diff --git a/DEPS b/DEPS
index 2c4d01f..a05a110c 100644
--- a/DEPS
+++ b/DEPS
@@ -28,6 +28,7 @@
 
 gclient_gn_args_file = 'src/build/config/gclient_args.gni'
 gclient_gn_args = [
+  'build_with_chromium',
   'checkout_android',
   'checkout_android_native_support',
   'checkout_libaom',
@@ -37,6 +38,11 @@
 
 
 vars = {
+  # Variable that can be used to support multiple build scenarios, like having
+  # Chromium specific targets in a client project's GN file or sync dependencies
+  # conditionally etc.
+  'build_with_chromium': True,
+
   # By default, we should check out everything needed to run on the main
   # chromium waterfalls. This var can be also be set to "small", in order
   # to skip things are not strictly needed to build chromium for development
@@ -96,11 +102,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': 'cf9086ce1ebd9efa27e96cfb09d9c72aac68aca1',
+  'skia_revision': '8c8e10c7d9c5db8b8ffe34b8cb956ac6c8a0e6c0',
   # 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': '2cf653be06593ee542932a6fde0f715d7dcf8bbe',
+  'v8_revision': 'ea8869dd155f98ec0c17137e5a2fea7fed170649',
   # 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.
@@ -108,7 +114,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '95fb2a1774124cd9169ed8694071ab7a96a915a4',
+  'angle_revision': '86ce210a675952554c1d84c4464b1a992162866c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -120,7 +126,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'a7b65b85bba95fb8757cbd407fd38d71304128ab',
+  'pdfium_revision': '8f7ee98e2c622c21f452cd9fd5956fe85bcb2b7c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
diff --git a/WATCHLISTS b/WATCHLISTS
index 26bc70f..2cb73d2 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -2059,7 +2059,6 @@
     'filebrowse': ['rginda+watch@chromium.org'],
     'filesapp': ['filesapp-reviews@chromium.org',
                  'fukino+watch@chromium.org',
-                 'oka+watch@chromium.org',
                  'yamaguchi+watch@chromium.org'],
     'fsp': ['mtomasz+watch@chromium.org'],
     'fuchsia': ['sergeyu@chromium.org', 'wez@chromium.org'],
@@ -2341,7 +2340,6 @@
     'views': ['tfarina@chromium.org'],
     'virtual_keyboard': ['blakeo+virtualkb@chromium.org',
                          'dfaden+virtualkb@google.com',
-                         'oka+watchvk@chromium.org',
                          'yhanada+watchvk@chromium.org'],
     'virtual_reality': ['feature-vr-reviews@chromium.org'],
     'wake_lock': ['mattreynolds+watch@chromium.org'],
diff --git a/android_webview/java/DEPS b/android_webview/java/DEPS
index b3dc856..32f7f7dd 100644
--- a/android_webview/java/DEPS
+++ b/android_webview/java/DEPS
@@ -10,7 +10,6 @@
 
   "-content/public/android/java",
   "+content/public/android/java/src/org/chromium/content_public",
-  "!content/public/android/java/src/org/chromium/content/browser/ChildProcessCreationParams.java",
   "!content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java",
 
   "+device/gamepad/android/java",
diff --git a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
index dd19639..f97fa5e 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwBrowserProcess.java
@@ -31,9 +31,9 @@
 import org.chromium.base.library_loader.LibraryProcessType;
 import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.components.minidump_uploader.CrashFileManager;
-import org.chromium.content.browser.ChildProcessCreationParams;
 import org.chromium.content.browser.ChildProcessLauncherHelper;
 import org.chromium.content_public.browser.BrowserStartupController;
+import org.chromium.content_public.browser.ChildProcessCreationParams;
 import org.chromium.policy.CombinedPolicyProvider;
 
 import java.io.File;
@@ -92,9 +92,9 @@
         final boolean isExternalService = true;
         final boolean bindToCaller = true;
         final boolean ignoreVisibilityForImportance = true;
-        ChildProcessCreationParams.set(new ChildProcessCreationParams(getWebViewPackageName(),
-                isExternalService, LibraryProcessType.PROCESS_WEBVIEW_CHILD, bindToCaller,
-                ignoreVisibilityForImportance));
+        ChildProcessCreationParams.set(getWebViewPackageName(), isExternalService,
+                LibraryProcessType.PROCESS_WEBVIEW_CHILD, bindToCaller,
+                ignoreVisibilityForImportance);
     }
 
     /**
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 15a2bfd..a891e04 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -562,6 +562,8 @@
     "system/audio/unified_volume_view.h",
     "system/audio/volume_view.cc",
     "system/audio/volume_view.h",
+    "system/bluetooth/bluetooth_detailed_view.cc",
+    "system/bluetooth/bluetooth_detailed_view.h",
     "system/bluetooth/bluetooth_feature_pod_controller.cc",
     "system/bluetooth/bluetooth_feature_pod_controller.h",
     "system/bluetooth/bluetooth_notification_controller.cc",
@@ -625,6 +627,8 @@
     "system/keyboard_brightness/keyboard_brightness_controller.h",
     "system/keyboard_brightness/tray_keyboard_brightness.cc",
     "system/keyboard_brightness/tray_keyboard_brightness.h",
+    "system/keyboard_brightness/unified_keyboard_brightness_slider_controller.cc",
+    "system/keyboard_brightness/unified_keyboard_brightness_slider_controller.h",
     "system/keyboard_brightness_control_delegate.h",
     "system/locale/locale_notification_controller.cc",
     "system/locale/locale_notification_controller.h",
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index d4c8932..52f04c8 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -166,6 +166,7 @@
     "system_tray_battery_x.icon",
     "system_tray_caps_lock.icon",
     "system_tray_cast.icon",
+    "system_tray_do_not_disturb.icon",
     "system_tray_night_light.icon",
     "system_tray_recording.icon",
     "system_tray_rotation_lock_auto.icon",
diff --git a/ash/resources/vector_icons/system_tray_do_not_disturb.icon b/ash/resources/vector_icons/system_tray_do_not_disturb.icon
new file mode 100644
index 0000000..67e79dc
--- /dev/null
+++ b/ash/resources/vector_icons/system_tray_do_not_disturb.icon
@@ -0,0 +1,23 @@
+// Copyright 2018 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.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 10, 2,
+R_CUBIC_TO, -4.42f, 0, -8, 3.58f, -8, 8,
+R_CUBIC_TO, 0, 4.42f, 3.58f, 8, 8, 8,
+R_CUBIC_TO, 4.42f, 0, 8, -3.58f, 8, -8,
+R_CUBIC_TO, 0, -4.42f, -3.58f, -8, -8, -8,
+CLOSE,
+R_MOVE_TO, 0, 14,
+R_CUBIC_TO, -3.31f, 0, -6, -2.69f, -6, -6,
+R_CUBIC_TO, 0, -3.31f, 2.69f, -6, 6, -6,
+R_CUBIC_TO, 3.31f, 0, 6, 2.69f, 6, 6,
+R_CUBIC_TO, 0, 3.31f, -2.69f, 6, -6, 6,
+CLOSE,
+MOVE_TO, 7, 9,
+R_H_LINE_TO, 6,
+R_ARC_TO, 1, 1, 0, 0, 1, 0, 2,
+H_LINE_TO, 7,
+R_ARC_TO, 1, 1, 0, 1, 1, 0, -2,
+CLOSE
\ No newline at end of file
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index f715114..4de3ca3 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -43,12 +43,16 @@
 
 constexpr int kVoiceInteractionAnimationDelayMs = 200;
 constexpr int kVoiceInteractionAnimationHideDelayMs = 500;
-
-}  // namespace
-
 constexpr uint8_t kVoiceInteractionRunningAlpha = 255;     // 100% alpha
 constexpr uint8_t kVoiceInteractionNotRunningAlpha = 138;  // 54% alpha
 
+bool IsAssistantEnabled() {
+  return chromeos::switches::IsVoiceInteractionEnabled() ||
+         chromeos::switches::IsAssistantEnabled();
+}
+
+}  // namespace
+
 AppListButton::AppListButton(InkDropButtonListener* listener,
                              ShelfView* shelf_view,
                              Shelf* shelf)
@@ -75,7 +79,7 @@
   // session has already started. This could happen when an external monitor
   // is plugged in.
   if (Shell::Get()->session_controller()->IsActiveUserSessionStarted() &&
-      chromeos::switches::IsVoiceInteractionEnabled()) {
+      IsAssistantEnabled()) {
     InitializeVoiceInteractionOverlay();
   }
 }
@@ -130,21 +134,19 @@
       if (UseVoiceInteractionStyle()) {
         base::RecordAction(base::UserMetricsAction(
             "VoiceInteraction.Started.AppListButtonLongPress"));
-        Shell::Get()->app_list_controller()->StartVoiceInteractionSession();
         assistant_overlay_->BurstAnimation();
         event->SetHandled();
-      } else if (chromeos::switches::IsAssistantEnabled()) {
-        // TODO: Handle overlay animation similarly to above. Also needs to
-        // factor in Assistant enabled state.
-        Shell::Get()->assistant_controller()->StartInteraction();
-        event->SetHandled();
+        if (chromeos::switches::IsAssistantEnabled()) {
+          Shell::Get()->assistant_controller()->StartInteraction();
+        } else {
+          Shell::Get()->app_list_controller()->StartVoiceInteractionSession();
+        }
       } else {
         ImageButton::OnGestureEvent(event);
       }
       return;
     case ui::ET_GESTURE_LONG_TAP:
-      if (UseVoiceInteractionStyle() ||
-          chromeos::switches::IsAssistantEnabled()) {
+      if (UseVoiceInteractionStyle()) {
         // Also consume the long tap event. This happens after the user long
         // presses and lifts the finger. We already handled the long press
         // ignore the long tap to avoid bringing up the context menu again.
@@ -355,7 +357,7 @@
   // Initialize voice interaction overlay when primary user session becomes
   // active.
   if (Shell::Get()->session_controller()->IsUserPrimary() &&
-      !assistant_overlay_ && chromeos::switches::IsVoiceInteractionEnabled()) {
+      !assistant_overlay_ && IsAssistantEnabled()) {
     InitializeVoiceInteractionOverlay();
   }
 }
@@ -371,7 +373,8 @@
       (alignment == SHELF_ALIGNMENT_BOTTOM ||
        alignment == SHELF_ALIGNMENT_BOTTOM_LOCKED) &&
       state == mojom::VoiceInteractionState::STOPPED &&
-      Shell::Get()->voice_interaction_controller()->setup_completed();
+      Shell::Get()->voice_interaction_controller()->setup_completed() &&
+      chromeos::switches::IsVoiceInteractionEnabled();
   assistant_overlay_->StartAnimation(show_icon);
 }
 
diff --git a/ash/system/bluetooth/bluetooth_detailed_view.cc b/ash/system/bluetooth/bluetooth_detailed_view.cc
new file mode 100644
index 0000000..8acae00
--- /dev/null
+++ b/ash/system/bluetooth/bluetooth_detailed_view.cc
@@ -0,0 +1,434 @@
+// Copyright 2018 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 "ash/system/bluetooth/bluetooth_detailed_view.h"
+
+#include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/tray/hover_highlight_view.h"
+#include "ash/system/tray/system_tray_controller.h"
+#include "ash/system/tray/tray_info_label.h"
+#include "ash/system/tray/tray_popup_item_style.h"
+#include "ash/system/tray/tray_popup_utils.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/controls/button/toggle_button.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/scroll_view.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace ash {
+namespace tray {
+namespace {
+
+const int kUpdateFrequencyMs = 1000;
+const int kDisabledPanelLabelBaselineY = 20;
+
+// Updates bluetooth device |device| in the |list|. If it is new, append to the
+// end of the |list|; otherwise, keep it at the same place, but update the data
+// with new device info provided by |device|.
+void UpdateBluetoothDeviceListHelper(BluetoothDeviceList* list,
+                                     const BluetoothDeviceInfo& device) {
+  for (BluetoothDeviceList::iterator it = list->begin(); it != list->end();
+       ++it) {
+    if ((*it).address == device.address) {
+      *it = device;
+      return;
+    }
+  }
+
+  list->push_back(device);
+}
+
+// Removes the obsolete BluetoothDevices from |list|, if they are not in the
+// |new_device_address_list|.
+void RemoveObsoleteBluetoothDevicesFromList(
+    BluetoothDeviceList* device_list,
+    const std::set<std::string>& new_device_address_list) {
+  for (BluetoothDeviceList::iterator it = device_list->begin();
+       it != device_list->end(); ++it) {
+    if (!new_device_address_list.count((*it).address)) {
+      it = device_list->erase(it);
+      if (it == device_list->end())
+        return;
+    }
+  }
+}
+
+// Returns corresponding device type icons for given Bluetooth device types and
+// connection states.
+const gfx::VectorIcon& GetBluetoothDeviceIcon(
+    device::BluetoothDeviceType device_type,
+    bool connected) {
+  switch (device_type) {
+    case device::BluetoothDeviceType::COMPUTER:
+      return ash::kSystemMenuComputerIcon;
+    case device::BluetoothDeviceType::PHONE:
+      return ash::kSystemMenuPhoneIcon;
+    case device::BluetoothDeviceType::AUDIO:
+    case device::BluetoothDeviceType::CAR_AUDIO:
+      return ash::kSystemMenuHeadsetIcon;
+    case device::BluetoothDeviceType::VIDEO:
+      return ash::kSystemMenuVideocamIcon;
+    case device::BluetoothDeviceType::JOYSTICK:
+    case device::BluetoothDeviceType::GAMEPAD:
+      return ash::kSystemMenuGamepadIcon;
+    case device::BluetoothDeviceType::KEYBOARD:
+    case device::BluetoothDeviceType::KEYBOARD_MOUSE_COMBO:
+      return ash::kSystemMenuKeyboardIcon;
+    case device::BluetoothDeviceType::TABLET:
+      return ash::kSystemMenuTabletIcon;
+    case device::BluetoothDeviceType::MOUSE:
+      return ash::kSystemMenuMouseIcon;
+    case device::BluetoothDeviceType::MODEM:
+    case device::BluetoothDeviceType::PERIPHERAL:
+      return ash::kSystemMenuBluetoothIcon;
+    default:
+      return connected ? ash::kSystemMenuBluetoothConnectedIcon
+                       : ash::kSystemMenuBluetoothIcon;
+  }
+}
+
+views::View* CreateDisabledPanel() {
+  views::View* container = new views::View;
+  auto box_layout =
+      std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical);
+  box_layout->set_main_axis_alignment(
+      views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
+  container->SetLayoutManager(std::move(box_layout));
+
+  TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL);
+  style.set_color_style(TrayPopupItemStyle::ColorStyle::DISABLED);
+
+  views::ImageView* image_view = new views::ImageView;
+  image_view->SetImage(gfx::CreateVectorIcon(kSystemMenuBluetoothDisabledIcon,
+                                             style.GetIconColor()));
+  image_view->SetVerticalAlignment(views::ImageView::TRAILING);
+  container->AddChildView(image_view);
+
+  views::Label* label = new views::Label(
+      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_DISABLED));
+  style.SetupLabel(label);
+  label->SetBorder(views::CreateEmptyBorder(
+      kDisabledPanelLabelBaselineY - label->GetBaseline(), 0, 0, 0));
+  container->AddChildView(label);
+
+  // Make top padding of the icon equal to the height of the label so that the
+  // icon is vertically aligned to center of the container.
+  image_view->SetBorder(
+      views::CreateEmptyBorder(label->GetPreferredSize().height(), 0, 0, 0));
+  return container;
+}
+
+}  // namespace
+
+BluetoothDetailedView::BluetoothDetailedView(DetailedViewDelegate* delegate,
+                                             LoginStatus login)
+    : TrayDetailedView(delegate),
+      login_(login),
+      toggle_(nullptr),
+      settings_(nullptr),
+      disabled_panel_(nullptr) {
+  CreateItems();
+}
+
+BluetoothDetailedView::~BluetoothDetailedView() {
+  // Stop discovering bluetooth devices when exiting BT detailed view.
+  BluetoothStopDiscovering();
+}
+
+void BluetoothDetailedView::Update() {
+  // Update immediately for initial device list and
+  // when bluetooth is disabled.
+  if (device_map_.empty() ||
+      !Shell::Get()->tray_bluetooth_helper()->GetBluetoothEnabled()) {
+    timer_.Stop();
+    DoUpdate();
+    return;
+  }
+
+  // Return here since an update is already queued.
+  if (timer_.IsRunning())
+    return;
+
+  // Update the detailed view after kUpdateFrequencyMs.
+  timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kUpdateFrequencyMs),
+               this, &BluetoothDetailedView::DoUpdate);
+}
+
+void BluetoothDetailedView::CreateItems() {
+  CreateScrollableList();
+  CreateTitleRow(IDS_ASH_STATUS_TRAY_BLUETOOTH);
+}
+
+void BluetoothDetailedView::BluetoothStartDiscovering() {
+  TrayBluetoothHelper* helper = Shell::Get()->tray_bluetooth_helper();
+  if (helper->HasBluetoothDiscoverySession()) {
+    ShowLoadingIndicator();
+    return;
+  }
+  HideLoadingIndicator();
+  if (helper->GetBluetoothEnabled())
+    helper->StartBluetoothDiscovering();
+}
+
+void BluetoothDetailedView::BluetoothStopDiscovering() {
+  TrayBluetoothHelper* helper = Shell::Get()->tray_bluetooth_helper();
+  if (helper && helper->HasBluetoothDiscoverySession()) {
+    helper->StopBluetoothDiscovering();
+    HideLoadingIndicator();
+  }
+}
+
+void BluetoothDetailedView::UpdateBluetoothDeviceList() {
+  std::set<std::string> new_connecting_devices;
+  std::set<std::string> new_connected_devices;
+  std::set<std::string> new_paired_not_connected_devices;
+  std::set<std::string> new_discovered_not_paired_devices;
+
+  BluetoothDeviceList list =
+      Shell::Get()->tray_bluetooth_helper()->GetAvailableBluetoothDevices();
+  for (const auto& device : list) {
+    if (device.connecting) {
+      new_connecting_devices.insert(device.address);
+      UpdateBluetoothDeviceListHelper(&connecting_devices_, device);
+    } else if (device.connected && device.paired) {
+      new_connected_devices.insert(device.address);
+      UpdateBluetoothDeviceListHelper(&connected_devices_, device);
+    } else if (device.paired) {
+      new_paired_not_connected_devices.insert(device.address);
+      UpdateBluetoothDeviceListHelper(&paired_not_connected_devices_, device);
+    } else {
+      new_discovered_not_paired_devices.insert(device.address);
+      UpdateBluetoothDeviceListHelper(&discovered_not_paired_devices_, device);
+    }
+  }
+  RemoveObsoleteBluetoothDevicesFromList(&connecting_devices_,
+                                         new_connecting_devices);
+  RemoveObsoleteBluetoothDevicesFromList(&connected_devices_,
+                                         new_connected_devices);
+  RemoveObsoleteBluetoothDevicesFromList(&paired_not_connected_devices_,
+                                         new_paired_not_connected_devices);
+  RemoveObsoleteBluetoothDevicesFromList(&discovered_not_paired_devices_,
+                                         new_discovered_not_paired_devices);
+}
+
+void BluetoothDetailedView::UpdateHeaderEntry() {
+  const bool is_bluetooth_enabled =
+      Shell::Get()->tray_bluetooth_helper()->GetBluetoothEnabled();
+  if (toggle_)
+    toggle_->SetIsOn(is_bluetooth_enabled, true);
+}
+
+void BluetoothDetailedView::UpdateDeviceScrollList() {
+  std::string focused_device_address = GetFocusedDeviceAddress();
+
+  device_map_.clear();
+  scroll_content()->RemoveAllChildViews(true);
+
+  TrayBluetoothHelper* helper = Shell::Get()->tray_bluetooth_helper();
+  const bool bluetooth_enabled = helper->GetBluetoothEnabled();
+  const bool bluetooth_available = helper->GetBluetoothAvailable();
+
+  // If Bluetooth is disabled, show a panel which only indicates that it is
+  // disabled, instead of the scroller with Bluetooth devices.
+  if (bluetooth_enabled) {
+    HideDisabledPanel();
+  } else {
+    ShowDisabledPanel();
+    return;
+  }
+
+  // Add paired devices and their section header to the list.
+  bool has_paired_devices = !connected_devices_.empty() ||
+                            !connecting_devices_.empty() ||
+                            !paired_not_connected_devices_.empty();
+  if (has_paired_devices) {
+    AddScrollListSubHeader(IDS_ASH_STATUS_TRAY_BLUETOOTH_PAIRED_DEVICES);
+    AppendSameTypeDevicesToScrollList(connected_devices_, true, true,
+                                      bluetooth_enabled);
+    AppendSameTypeDevicesToScrollList(connecting_devices_, true, false,
+                                      bluetooth_enabled);
+    AppendSameTypeDevicesToScrollList(paired_not_connected_devices_, false,
+                                      false, bluetooth_enabled);
+  }
+
+  // Add unpaired devices to the list. If at least one paired device is
+  // present, also add a section header above the unpaired devices.
+  if (!discovered_not_paired_devices_.empty()) {
+    if (has_paired_devices)
+      AddScrollListSubHeader(IDS_ASH_STATUS_TRAY_BLUETOOTH_UNPAIRED_DEVICES);
+    AppendSameTypeDevicesToScrollList(discovered_not_paired_devices_, false,
+                                      false, bluetooth_enabled);
+  }
+
+  // Show user Bluetooth state if there is no bluetooth devices in list.
+  if (device_map_.empty() && bluetooth_available && bluetooth_enabled) {
+    scroll_content()->AddChildView(new TrayInfoLabel(
+        nullptr /* delegate */, IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERING));
+  }
+
+  // Focus the device which was focused before the device-list update.
+  if (!focused_device_address.empty())
+    FocusDeviceByAddress(focused_device_address);
+
+  scroll_content()->InvalidateLayout();
+}
+
+void BluetoothDetailedView::AppendSameTypeDevicesToScrollList(
+    const BluetoothDeviceList& list,
+    bool highlight,
+    bool checked,
+    bool enabled) {
+  for (const auto& device : list) {
+    const gfx::VectorIcon& icon =
+        GetBluetoothDeviceIcon(device.device_type, device.connected);
+    HoverHighlightView* container =
+        AddScrollListItem(icon, device.display_name);
+    if (device.connected)
+      SetupConnectedScrollListItem(container);
+    else if (device.connecting)
+      SetupConnectingScrollListItem(container);
+    device_map_[container] = device.address;
+  }
+}
+
+bool BluetoothDetailedView::FoundDevice(
+    const std::string& device_address,
+    const BluetoothDeviceList& device_list) const {
+  for (const auto& device : device_list) {
+    if (device.address == device_address)
+      return true;
+  }
+  return false;
+}
+
+void BluetoothDetailedView::UpdateClickedDevice(
+    const std::string& device_address,
+    views::View* item_container) {
+  if (FoundDevice(device_address, paired_not_connected_devices_)) {
+    HoverHighlightView* container =
+        static_cast<HoverHighlightView*>(item_container);
+    SetupConnectingScrollListItem(container);
+    scroll_content()->SizeToPreferredSize();
+    scroller()->Layout();
+  }
+}
+
+void BluetoothDetailedView::HandleViewClicked(views::View* view) {
+  TrayBluetoothHelper* helper = Shell::Get()->tray_bluetooth_helper();
+  if (!helper->GetBluetoothEnabled())
+    return;
+
+  std::map<views::View*, std::string>::iterator find;
+  find = device_map_.find(view);
+  if (find == device_map_.end())
+    return;
+
+  const std::string device_address = find->second;
+  if (FoundDevice(device_address, connecting_devices_))
+    return;
+
+  UpdateClickedDevice(device_address, view);
+  helper->ConnectToBluetoothDevice(device_address);
+}
+
+void BluetoothDetailedView::HandleButtonPressed(views::Button* sender,
+                                                const ui::Event& event) {
+  if (sender == toggle_) {
+    Shell::Get()->tray_bluetooth_helper()->SetBluetoothEnabled(
+        toggle_->is_on());
+  } else if (sender == settings_) {
+    ShowSettings();
+  } else {
+    NOTREACHED();
+  }
+}
+
+void BluetoothDetailedView::CreateExtraTitleRowButtons() {
+  if (login_ == LoginStatus::LOCKED)
+    return;
+
+  DCHECK(!toggle_);
+  DCHECK(!settings_);
+
+  tri_view()->SetContainerVisible(TriView::Container::END, true);
+
+  toggle_ =
+      TrayPopupUtils::CreateToggleButton(this, IDS_ASH_STATUS_TRAY_BLUETOOTH);
+  tri_view()->AddView(TriView::Container::END, toggle_);
+
+  settings_ = CreateSettingsButton(IDS_ASH_STATUS_TRAY_BLUETOOTH_SETTINGS);
+  tri_view()->AddView(TriView::Container::END, settings_);
+}
+
+void BluetoothDetailedView::ShowSettings() {
+  if (TrayPopupUtils::CanOpenWebUISettings()) {
+    Shell::Get()->system_tray_controller()->ShowBluetoothSettings();
+    CloseBubble();
+  }
+}
+
+void BluetoothDetailedView::ShowLoadingIndicator() {
+  // Setting a value of -1 gives progress_bar an infinite-loading behavior.
+  ShowProgress(-1, true);
+}
+
+void BluetoothDetailedView::HideLoadingIndicator() {
+  ShowProgress(0, false);
+}
+
+void BluetoothDetailedView::ShowDisabledPanel() {
+  DCHECK(scroller());
+  if (!disabled_panel_) {
+    disabled_panel_ = CreateDisabledPanel();
+    // Insert |disabled_panel_| before the scroller, since the scroller will
+    // have unnecessary bottom border when it is not the last child.
+    AddChildViewAt(disabled_panel_, GetIndexOf(scroller()));
+    // |disabled_panel_| need to fill the remaining space below the title row
+    // so that the inner contents of |disabled_panel_| are placed properly.
+    box_layout()->SetFlexForView(disabled_panel_, 1);
+  }
+  disabled_panel_->SetVisible(true);
+  scroller()->SetVisible(false);
+}
+
+void BluetoothDetailedView::HideDisabledPanel() {
+  DCHECK(scroller());
+  if (disabled_panel_)
+    disabled_panel_->SetVisible(false);
+  scroller()->SetVisible(true);
+}
+
+std::string BluetoothDetailedView::GetFocusedDeviceAddress() const {
+  for (auto& view_and_address : device_map_) {
+    if (view_and_address.first->HasFocus())
+      return view_and_address.second;
+  }
+  return std::string();
+}
+
+void BluetoothDetailedView::FocusDeviceByAddress(
+    const std::string& address) const {
+  for (auto& view_and_address : device_map_) {
+    if (view_and_address.second == address) {
+      view_and_address.first->RequestFocus();
+      return;
+    }
+  }
+}
+
+void BluetoothDetailedView::DoUpdate() {
+  BluetoothStartDiscovering();
+  UpdateBluetoothDeviceList();
+
+  // Update UI.
+  UpdateDeviceScrollList();
+  UpdateHeaderEntry();
+  Layout();
+}
+
+}  // namespace tray
+}  // namespace ash
diff --git a/ash/system/bluetooth/bluetooth_detailed_view.h b/ash/system/bluetooth/bluetooth_detailed_view.h
new file mode 100644
index 0000000..cba77e38
--- /dev/null
+++ b/ash/system/bluetooth/bluetooth_detailed_view.h
@@ -0,0 +1,93 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_BLUETOOTH_BLUETOOTH_DETAILED_VIEW_H_
+#define ASH_SYSTEM_BLUETOOTH_BLUETOOTH_DETAILED_VIEW_H_
+
+#include "ash/login_status.h"
+#include "ash/system/bluetooth/tray_bluetooth_helper.h"
+#include "ash/system/tray/tray_detailed_view.h"
+
+namespace views {
+class ToggleButton;
+}  // namespace views
+
+namespace ash {
+namespace tray {
+
+class BluetoothDetailedView : public TrayDetailedView {
+ public:
+  BluetoothDetailedView(DetailedViewDelegate* delegate, LoginStatus login);
+
+  ~BluetoothDetailedView() override;
+
+  void Update();
+
+ private:
+  void CreateItems();
+
+  void BluetoothStartDiscovering();
+
+  void BluetoothStopDiscovering();
+
+  void UpdateBluetoothDeviceList();
+  void UpdateHeaderEntry();
+  void UpdateDeviceScrollList();
+  void AppendSameTypeDevicesToScrollList(const BluetoothDeviceList& list,
+                                         bool highlight,
+                                         bool checked,
+                                         bool enabled);
+
+  // Returns true if the device with |device_id| is found in |device_list|.
+  bool FoundDevice(const std::string& device_id,
+                   const BluetoothDeviceList& device_list) const;
+
+  // Updates UI of the clicked bluetooth device to show it is being connected
+  // or disconnected if such an operation is going to be performed underway.
+  void UpdateClickedDevice(const std::string& device_id,
+                           views::View* item_container);
+
+  // TrayDetailedView:
+  void HandleViewClicked(views::View* view) override;
+  void HandleButtonPressed(views::Button* sender,
+                           const ui::Event& event) override;
+  void CreateExtraTitleRowButtons() override;
+
+  void ShowSettings();
+  void ShowLoadingIndicator();
+  void HideLoadingIndicator();
+  void ShowDisabledPanel();
+  void HideDisabledPanel();
+
+  std::string GetFocusedDeviceAddress() const;
+  void FocusDeviceByAddress(const std::string& address) const;
+  void DoUpdate();
+
+  // TODO(jamescook): Don't cache this.
+  LoginStatus login_;
+
+  std::map<views::View*, std::string> device_map_;
+
+  BluetoothDeviceList connected_devices_;
+  BluetoothDeviceList connecting_devices_;
+  BluetoothDeviceList paired_not_connected_devices_;
+  BluetoothDeviceList discovered_not_paired_devices_;
+
+  views::ToggleButton* toggle_;
+  views::Button* settings_;
+
+  // The container of the message "Bluetooth is disabled" and an icon. It should
+  // be shown instead of Bluetooth device list when Bluetooth is disabled.
+  views::View* disabled_panel_;
+
+  // Timer used to limit the update frequency.
+  base::OneShotTimer timer_;
+
+  DISALLOW_COPY_AND_ASSIGN(BluetoothDetailedView);
+};
+
+}  // namespace tray
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_BLUETOOTH_BLUETOOTH_DETAILED_VIEW_H_
diff --git a/ash/system/bluetooth/tray_bluetooth.cc b/ash/system/bluetooth/tray_bluetooth.cc
index fdcb0f5..b07bac0a 100644
--- a/ash/system/bluetooth/tray_bluetooth.cc
+++ b/ash/system/bluetooth/tray_bluetooth.cc
@@ -15,18 +15,15 @@
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/system/bluetooth/bluetooth_detailed_view.h"
 #include "ash/system/bluetooth/tray_bluetooth_helper.h"
-#include "ash/system/tray/hover_highlight_view.h"
 #include "ash/system/tray/system_tray.h"
-#include "ash/system/tray/system_tray_controller.h"
 #include "ash/system/tray/system_tray_item_detailed_view_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_detailed_view.h"
-#include "ash/system/tray/tray_info_label.h"
 #include "ash/system/tray/tray_item_more.h"
 #include "ash/system/tray/tray_popup_item_style.h"
-#include "ash/system/tray/tray_popup_utils.h"
 #include "ash/system/tray/tri_view.h"
 #include "device/bluetooth/bluetooth_common.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -35,88 +32,12 @@
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/vector_icon_types.h"
-#include "ui/views/controls/button/toggle_button.h"
-#include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/progress_bar.h"
-#include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/separator.h"
-#include "ui/views/layout/box_layout.h"
 
 namespace ash {
 namespace tray {
-namespace {
-
-const int kUpdateFrequencyMs = 1000;
-
-// Updates bluetooth device |device| in the |list|. If it is new, append to the
-// end of the |list|; otherwise, keep it at the same place, but update the data
-// with new device info provided by |device|.
-void UpdateBluetoothDeviceListHelper(BluetoothDeviceList* list,
-                                     const BluetoothDeviceInfo& device) {
-  for (BluetoothDeviceList::iterator it = list->begin(); it != list->end();
-       ++it) {
-    if ((*it).address == device.address) {
-      *it = device;
-      return;
-    }
-  }
-
-  list->push_back(device);
-}
-
-// Removes the obsolete BluetoothDevices from |list|, if they are not in the
-// |new_list|.
-void RemoveObsoleteBluetoothDevicesFromList(
-    BluetoothDeviceList* list,
-    const std::set<std::string>& new_list) {
-  for (BluetoothDeviceList::iterator it = list->begin(); it != list->end();
-       ++it) {
-    if (new_list.find((*it).address) == new_list.end()) {
-      it = list->erase(it);
-      if (it == list->end())
-        return;
-    }
-  }
-}
-
-// Returns corresponding device type icons for given Bluetooth device types and
-// connection states.
-const gfx::VectorIcon& GetBluetoothDeviceIcon(
-    device::BluetoothDeviceType device_type,
-    bool connected) {
-  switch (device_type) {
-    case device::BluetoothDeviceType::COMPUTER:
-      return ash::kSystemMenuComputerIcon;
-    case device::BluetoothDeviceType::PHONE:
-      return ash::kSystemMenuPhoneIcon;
-    case device::BluetoothDeviceType::AUDIO:
-    case device::BluetoothDeviceType::CAR_AUDIO:
-      return ash::kSystemMenuHeadsetIcon;
-    case device::BluetoothDeviceType::VIDEO:
-      return ash::kSystemMenuVideocamIcon;
-    case device::BluetoothDeviceType::JOYSTICK:
-    case device::BluetoothDeviceType::GAMEPAD:
-      return ash::kSystemMenuGamepadIcon;
-    case device::BluetoothDeviceType::KEYBOARD:
-    case device::BluetoothDeviceType::KEYBOARD_MOUSE_COMBO:
-      return ash::kSystemMenuKeyboardIcon;
-    case device::BluetoothDeviceType::TABLET:
-      return ash::kSystemMenuTabletIcon;
-    case device::BluetoothDeviceType::MOUSE:
-      return ash::kSystemMenuMouseIcon;
-    case device::BluetoothDeviceType::MODEM:
-    case device::BluetoothDeviceType::PERIPHERAL:
-      return ash::kSystemMenuBluetoothIcon;
-    default:
-      return connected ? ash::kSystemMenuBluetoothConnectedIcon
-                       : ash::kSystemMenuBluetoothIcon;
-  }
-}
-
-const int kDisabledPanelLabelBaselineY = 20;
-
-}  // namespace
 
 class BluetoothDefaultView : public TrayItemMore {
  public:
@@ -184,368 +105,6 @@
   DISALLOW_COPY_AND_ASSIGN(BluetoothDefaultView);
 };
 
-class BluetoothDetailedView : public TrayDetailedView {
- public:
-  BluetoothDetailedView(DetailedViewDelegate* delegate, LoginStatus login)
-      : TrayDetailedView(delegate),
-        login_(login),
-        toggle_(nullptr),
-        settings_(nullptr),
-        disabled_panel_(nullptr) {
-    CreateItems();
-  }
-
-  ~BluetoothDetailedView() override {
-    // Stop discovering bluetooth devices when exiting BT detailed view.
-    BluetoothStopDiscovering();
-  }
-
-  void Update() {
-    // Update immediately for initial device list and
-    // when bluetooth is disabled.
-    if (device_map_.size() == 0 ||
-        !Shell::Get()->tray_bluetooth_helper()->GetBluetoothEnabled()) {
-      DoUpdate();
-      return;
-    }
-
-    // Return here since an update is already queued.
-    if (timer_.IsRunning())
-      return;
-
-    // Update the detailed view after kUpdateFrequencyMs.
-    timer_.Start(FROM_HERE,
-                 base::TimeDelta::FromMilliseconds(kUpdateFrequencyMs), this,
-                 &BluetoothDetailedView::DoUpdate);
-  }
-
- private:
-  void CreateItems() {
-    CreateScrollableList();
-    CreateTitleRow(IDS_ASH_STATUS_TRAY_BLUETOOTH);
-  }
-
-  void BluetoothStartDiscovering() {
-    TrayBluetoothHelper* helper = Shell::Get()->tray_bluetooth_helper();
-    if (helper->HasBluetoothDiscoverySession()) {
-      ShowLoadingIndicator();
-      return;
-    }
-    HideLoadingIndicator();
-    if (helper->GetBluetoothEnabled())
-      helper->StartBluetoothDiscovering();
-  }
-
-  void BluetoothStopDiscovering() {
-    TrayBluetoothHelper* helper = Shell::Get()->tray_bluetooth_helper();
-    if (helper && helper->HasBluetoothDiscoverySession()) {
-      helper->StopBluetoothDiscovering();
-      HideLoadingIndicator();
-    }
-  }
-
-  void UpdateBluetoothDeviceList() {
-    std::set<std::string> new_connecting_devices;
-    std::set<std::string> new_connected_devices;
-    std::set<std::string> new_paired_not_connected_devices;
-    std::set<std::string> new_discovered_not_paired_devices;
-
-    BluetoothDeviceList list =
-        Shell::Get()->tray_bluetooth_helper()->GetAvailableBluetoothDevices();
-    for (const auto& device : list) {
-      if (device.connecting) {
-        new_connecting_devices.insert(device.address);
-        UpdateBluetoothDeviceListHelper(&connecting_devices_, device);
-      } else if (device.connected && device.paired) {
-        new_connected_devices.insert(device.address);
-        UpdateBluetoothDeviceListHelper(&connected_devices_, device);
-      } else if (device.paired) {
-        new_paired_not_connected_devices.insert(device.address);
-        UpdateBluetoothDeviceListHelper(&paired_not_connected_devices_, device);
-      } else {
-        new_discovered_not_paired_devices.insert(device.address);
-        UpdateBluetoothDeviceListHelper(&discovered_not_paired_devices_,
-                                        device);
-      }
-    }
-    RemoveObsoleteBluetoothDevicesFromList(&connecting_devices_,
-                                           new_connecting_devices);
-    RemoveObsoleteBluetoothDevicesFromList(&connected_devices_,
-                                           new_connected_devices);
-    RemoveObsoleteBluetoothDevicesFromList(&paired_not_connected_devices_,
-                                           new_paired_not_connected_devices);
-    RemoveObsoleteBluetoothDevicesFromList(&discovered_not_paired_devices_,
-                                           new_discovered_not_paired_devices);
-  }
-
-  void UpdateHeaderEntry() {
-    const bool is_bluetooth_enabled =
-        Shell::Get()->tray_bluetooth_helper()->GetBluetoothEnabled();
-    if (toggle_)
-      toggle_->SetIsOn(is_bluetooth_enabled, true);
-  }
-
-  void UpdateDeviceScrollList() {
-    std::string focused_device_address = GetFocusedDeviceAddress();
-
-    device_map_.clear();
-    scroll_content()->RemoveAllChildViews(true);
-
-    TrayBluetoothHelper* helper = Shell::Get()->tray_bluetooth_helper();
-    const bool bluetooth_enabled = helper->GetBluetoothEnabled();
-    const bool bluetooth_available = helper->GetBluetoothAvailable();
-
-    // If Bluetooth is disabled, show a panel which only indicates that it is
-    // disabled, instead of the scroller with Bluetooth devices.
-    if (bluetooth_enabled) {
-      HideDisabledPanel();
-    } else {
-      ShowDisabledPanel();
-      return;
-    }
-
-    // Add paired devices and their section header to the list.
-    size_t num_paired_devices = connected_devices_.size() +
-                                connecting_devices_.size() +
-                                paired_not_connected_devices_.size();
-    if (num_paired_devices > 0) {
-      AddScrollListSubHeader(IDS_ASH_STATUS_TRAY_BLUETOOTH_PAIRED_DEVICES);
-      AppendSameTypeDevicesToScrollList(connected_devices_, true, true,
-                                        bluetooth_enabled);
-      AppendSameTypeDevicesToScrollList(connecting_devices_, true, false,
-                                        bluetooth_enabled);
-      AppendSameTypeDevicesToScrollList(paired_not_connected_devices_, false,
-                                        false, bluetooth_enabled);
-    }
-
-    // Add unpaired devices to the list. If at least one paired device is
-    // present, also add a section header above the unpaired devices.
-    if (discovered_not_paired_devices_.size() > 0) {
-      if (num_paired_devices > 0)
-        AddScrollListSubHeader(IDS_ASH_STATUS_TRAY_BLUETOOTH_UNPAIRED_DEVICES);
-      AppendSameTypeDevicesToScrollList(discovered_not_paired_devices_, false,
-                                        false, bluetooth_enabled);
-    }
-
-    // Show user Bluetooth state if there is no bluetooth devices in list.
-    if (device_map_.size() == 0 && bluetooth_available && bluetooth_enabled) {
-      scroll_content()->AddChildView(new TrayInfoLabel(
-          nullptr /* delegate */, IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERING));
-    }
-
-    // Focus the device which was focused before the device-list update.
-    if (!focused_device_address.empty())
-      FocusDeviceByAddress(focused_device_address);
-
-    scroll_content()->InvalidateLayout();
-  }
-
-  void AppendSameTypeDevicesToScrollList(const BluetoothDeviceList& list,
-                                         bool highlight,
-                                         bool checked,
-                                         bool enabled) {
-    for (const auto& device : list) {
-      const gfx::VectorIcon& icon =
-          GetBluetoothDeviceIcon(device.device_type, device.connected);
-      HoverHighlightView* container =
-          AddScrollListItem(icon, device.display_name);
-      if (device.connected)
-        SetupConnectedScrollListItem(container);
-      else if (device.connecting)
-        SetupConnectingScrollListItem(container);
-      device_map_[container] = device.address;
-    }
-  }
-
-  // Returns true if the device with |device_id| is found in |device_list|.
-  bool FoundDevice(const std::string& device_id,
-                   const BluetoothDeviceList& device_list) {
-    for (const auto& device : device_list) {
-      if (device.address == device_id)
-        return true;
-    }
-    return false;
-  }
-
-  // Updates UI of the clicked bluetooth device to show it is being connected
-  // or disconnected if such an operation is going to be performed underway.
-  void UpdateClickedDevice(const std::string& device_id,
-                           views::View* item_container) {
-    if (FoundDevice(device_id, paired_not_connected_devices_)) {
-      HoverHighlightView* container =
-          static_cast<HoverHighlightView*>(item_container);
-      SetupConnectingScrollListItem(container);
-      scroll_content()->SizeToPreferredSize();
-      scroller()->Layout();
-    }
-  }
-
-  // TrayDetailedView:
-  void HandleViewClicked(views::View* view) override {
-    TrayBluetoothHelper* helper = Shell::Get()->tray_bluetooth_helper();
-    if (!helper->GetBluetoothEnabled())
-      return;
-
-    std::map<views::View*, std::string>::iterator find;
-    find = device_map_.find(view);
-    if (find == device_map_.end())
-      return;
-
-    const std::string device_id = find->second;
-    if (FoundDevice(device_id, connecting_devices_))
-      return;
-
-    UpdateClickedDevice(device_id, view);
-    helper->ConnectToBluetoothDevice(device_id);
-  }
-
-  void HandleButtonPressed(views::Button* sender,
-                           const ui::Event& event) override {
-    if (sender == toggle_) {
-      Shell::Get()->tray_bluetooth_helper()->SetBluetoothEnabled(
-          toggle_->is_on());
-    } else if (sender == settings_) {
-      ShowSettings();
-    } else {
-      NOTREACHED();
-    }
-  }
-
-  void CreateExtraTitleRowButtons() override {
-    if (login_ == LoginStatus::LOCKED)
-      return;
-
-    DCHECK(!toggle_);
-    DCHECK(!settings_);
-
-    tri_view()->SetContainerVisible(TriView::Container::END, true);
-
-    toggle_ =
-        TrayPopupUtils::CreateToggleButton(this, IDS_ASH_STATUS_TRAY_BLUETOOTH);
-    tri_view()->AddView(TriView::Container::END, toggle_);
-
-    settings_ = CreateSettingsButton(IDS_ASH_STATUS_TRAY_BLUETOOTH_SETTINGS);
-    tri_view()->AddView(TriView::Container::END, settings_);
-  }
-
-  void ShowSettings() {
-    if (TrayPopupUtils::CanOpenWebUISettings()) {
-      Shell::Get()->system_tray_controller()->ShowBluetoothSettings();
-      CloseBubble();
-    }
-  }
-
-  void ShowLoadingIndicator() {
-    // Setting a value of -1 gives progress_bar an infinite-loading behavior.
-    ShowProgress(-1, true);
-  }
-
-  void HideLoadingIndicator() { ShowProgress(0, false); }
-
-  void ShowDisabledPanel() {
-    DCHECK(scroller());
-    if (!disabled_panel_) {
-      disabled_panel_ = CreateDisabledPanel();
-      // Insert |disabled_panel_| before the scroller, since the scroller will
-      // have unnecessary bottom border when it is not the last child.
-      AddChildViewAt(disabled_panel_, GetIndexOf(scroller()));
-      // |disabled_panel_| need to fill the remaining space below the title row
-      // so that the inner contents of |disabled_panel_| are placed properly.
-      box_layout()->SetFlexForView(disabled_panel_, 1);
-    }
-    disabled_panel_->SetVisible(true);
-    scroller()->SetVisible(false);
-  }
-
-  void HideDisabledPanel() {
-    DCHECK(scroller());
-    if (disabled_panel_)
-      disabled_panel_->SetVisible(false);
-    scroller()->SetVisible(true);
-  }
-
-  views::View* CreateDisabledPanel() {
-    views::View* container = new views::View;
-    auto box_layout =
-        std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical);
-    box_layout->set_main_axis_alignment(
-        views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
-    container->SetLayoutManager(std::move(box_layout));
-
-    TrayPopupItemStyle style(
-        TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL);
-    style.set_color_style(TrayPopupItemStyle::ColorStyle::DISABLED);
-
-    views::ImageView* image_view = new views::ImageView;
-    image_view->SetImage(gfx::CreateVectorIcon(kSystemMenuBluetoothDisabledIcon,
-                                               style.GetIconColor()));
-    image_view->SetVerticalAlignment(views::ImageView::TRAILING);
-    container->AddChildView(image_view);
-
-    views::Label* label = new views::Label(
-        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_DISABLED));
-    style.SetupLabel(label);
-    label->SetBorder(views::CreateEmptyBorder(
-        kDisabledPanelLabelBaselineY - label->GetBaseline(), 0, 0, 0));
-    container->AddChildView(label);
-
-    // Make top padding of the icon equal to the height of the label so that the
-    // icon is vertically aligned to center of the container.
-    image_view->SetBorder(
-        views::CreateEmptyBorder(label->GetPreferredSize().height(), 0, 0, 0));
-    return container;
-  }
-
-  std::string GetFocusedDeviceAddress() {
-    for (auto& view_and_address : device_map_) {
-      if (view_and_address.first->HasFocus())
-        return view_and_address.second;
-    }
-    return std::string();
-  }
-
-  void FocusDeviceByAddress(const std::string& address) {
-    for (auto& view_and_address : device_map_) {
-      if (view_and_address.second == address) {
-        view_and_address.first->RequestFocus();
-        return;
-      }
-    }
-  }
-
-  void DoUpdate() {
-    BluetoothStartDiscovering();
-    UpdateBluetoothDeviceList();
-
-    // Update UI.
-    UpdateDeviceScrollList();
-    UpdateHeaderEntry();
-    Layout();
-  }
-
-  // TODO(jamescook): Don't cache this.
-  LoginStatus login_;
-
-  std::map<views::View*, std::string> device_map_;
-
-  BluetoothDeviceList connected_devices_;
-  BluetoothDeviceList connecting_devices_;
-  BluetoothDeviceList paired_not_connected_devices_;
-  BluetoothDeviceList discovered_not_paired_devices_;
-
-  views::ToggleButton* toggle_;
-  views::Button* settings_;
-
-  // The container of the message "Bluetooth is disabled" and an icon. It should
-  // be shown instead of Bluetooth device list when Bluetooth is disabled.
-  views::View* disabled_panel_;
-
-  // Timer used to limit the update frequency.
-  base::OneShotTimer timer_;
-
-  DISALLOW_COPY_AND_ASSIGN(BluetoothDetailedView);
-};
-
 }  // namespace tray
 
 TrayBluetooth::TrayBluetooth(SystemTray* system_tray)
diff --git a/ash/system/brightness/unified_brightness_view.cc b/ash/system/brightness/unified_brightness_view.cc
index f51bd42c..2536b0c 100644
--- a/ash/system/brightness/unified_brightness_view.cc
+++ b/ash/system/brightness/unified_brightness_view.cc
@@ -19,15 +19,15 @@
                         IDS_ASH_STATUS_TRAY_BRIGHTNESS),
       model_(model) {
   model_->AddObserver(this);
-  OnBrightnessChanged();
+  OnDisplayBrightnessChanged(false /* by_user */);
 }
 
 UnifiedBrightnessView::~UnifiedBrightnessView() {
   model_->RemoveObserver(this);
 }
 
-void UnifiedBrightnessView::OnBrightnessChanged() {
-  slider()->SetValue(model_->brightness());
+void UnifiedBrightnessView::OnDisplayBrightnessChanged(bool by_user) {
+  slider()->SetValue(model_->display_brightness());
 }
 
 }  // namespace ash
diff --git a/ash/system/brightness/unified_brightness_view.h b/ash/system/brightness/unified_brightness_view.h
index 3909fe66..596574b0 100644
--- a/ash/system/brightness/unified_brightness_view.h
+++ b/ash/system/brightness/unified_brightness_view.h
@@ -22,7 +22,7 @@
   ~UnifiedBrightnessView() override;
 
   // UnifiedSystemTrayModel::Observer:
-  void OnBrightnessChanged() override;
+  void OnDisplayBrightnessChanged(bool by_user) override;
 
  private:
   UnifiedSystemTrayModel* const model_;
diff --git a/ash/system/ime_menu/ime_list_view.cc b/ash/system/ime_menu/ime_list_view.cc
index f932474c..624d805 100644
--- a/ash/system/ime_menu/ime_list_view.cc
+++ b/ash/system/ime_menu/ime_list_view.cc
@@ -6,6 +6,7 @@
 
 #include "ash/ime/ime_controller.h"
 #include "ash/ime/ime_switch_type.h"
+#include "ash/public/cpp/ash_features.h"
 #include "ash/public/interfaces/ime_info.mojom.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
@@ -49,7 +50,8 @@
                   const base::string16& id,
                   const base::string16& label,
                   bool selected,
-                  const SkColor button_color)
+                  const SkColor button_color,
+                  bool use_unified_theme)
       : ActionableView(nullptr, TrayPopupInkDropStyle::FILL_BOUNDS),
         ime_list_view_(list_view),
         selected_(selected) {
@@ -82,8 +84,8 @@
     // The label shows the IME full name.
     auto* label_view = TrayPopupUtils::CreateDefaultLabel();
     label_view->SetText(label);
-    TrayPopupItemStyle style(
-        TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL);
+    TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL,
+                             use_unified_theme);
     style.SetupLabel(label_view);
 
     label_view->SetHorizontalAlignment(gfx::ALIGN_LEFT);
@@ -144,7 +146,7 @@
   views::ToggleButton* toggle() const { return toggle_; }
   bool is_toggled() const { return toggle_->is_on(); }
 
-  void Init(views::ButtonListener* listener) {
+  void Init(views::ButtonListener* listener, bool use_unified_theme) {
     TrayPopupUtils::ConfigureAsStickyHeader(this);
     SetLayoutManager(std::make_unique<views::FillLayout>());
 
@@ -161,8 +163,8 @@
     auto* label = TrayPopupUtils::CreateDefaultLabel();
     label->SetText(ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
         IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD));
-    TrayPopupItemStyle style(
-        TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL);
+    TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL,
+                             use_unified_theme);
     style.SetupLabel(label);
     tri_view->AddView(TriView::Container::CENTER, label);
 
@@ -181,10 +183,14 @@
 };
 
 ImeListView::ImeListView(DetailedViewDelegate* delegate)
+    : ImeListView(delegate, features::IsSystemTrayUnifiedEnabled()) {}
+
+ImeListView::ImeListView(DetailedViewDelegate* delegate, bool use_unified_theme)
     : TrayDetailedView(delegate),
       last_item_selected_with_keyboard_(false),
       should_focus_ime_after_selection_with_keyboard_(false),
-      current_ime_view_(nullptr) {}
+      current_ime_view_(nullptr),
+      use_unified_theme_(use_unified_theme) {}
 
 ImeListView::~ImeListView() = default;
 
@@ -249,8 +255,9 @@
   DCHECK(ime_map_.empty());
   for (size_t i = 0; i < list.size(); i++) {
     const bool selected = current_ime_id == list[i].id;
-    views::View* ime_view = new ImeListItemView(
-        this, list[i].short_name, list[i].name, selected, gfx::kGoogleGreen700);
+    views::View* ime_view =
+        new ImeListItemView(this, list[i].short_name, list[i].name, selected,
+                            gfx::kGoogleGreen700, use_unified_theme_);
     scroll_content()->AddChildView(ime_view);
     ime_map_[ime_view] = list[i].id;
 
@@ -265,9 +272,9 @@
 
       // Adds the property items.
       for (size_t i = 0; i < property_list.size(); i++) {
-        ImeListItemView* property_view =
-            new ImeListItemView(this, base::string16(), property_list[i].label,
-                                property_list[i].checked, kMenuIconColor);
+        ImeListItemView* property_view = new ImeListItemView(
+            this, base::string16(), property_list[i].label,
+            property_list[i].checked, kMenuIconColor, use_unified_theme_);
         scroll_content()->AddChildView(property_view);
         property_map_[property_view] = property_list[i].key;
       }
@@ -284,7 +291,7 @@
 void ImeListView::PrependKeyboardStatusRow() {
   DCHECK(!keyboard_status_row_);
   keyboard_status_row_ = new KeyboardStatusRow;
-  keyboard_status_row_->Init(this);
+  keyboard_status_row_->Init(this, use_unified_theme_);
   scroll_content()->AddChildViewAt(keyboard_status_row_, 0);
 }
 
diff --git a/ash/system/ime_menu/ime_list_view.h b/ash/system/ime_menu/ime_list_view.h
index 547ea6f46..2b62b34 100644
--- a/ash/system/ime_menu/ime_list_view.h
+++ b/ash/system/ime_menu/ime_list_view.h
@@ -32,7 +32,9 @@
     HIDE_SINGLE_IME
   };
 
+  // The former uses default for |use_unified_theme|.
   explicit ImeListView(DetailedViewDelegate* delegate);
+  ImeListView(DetailedViewDelegate* delegate, bool use_unified_theme);
 
   ~ImeListView() override;
 
@@ -111,6 +113,8 @@
   // The item view of the current selected IME.
   views::View* current_ime_view_;
 
+  const bool use_unified_theme_;
+
   DISALLOW_COPY_AND_ASSIGN(ImeListView);
 };
 
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index 48407a1..16ea7f7 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -122,7 +122,8 @@
     title_label->SetBorder(
         views::CreateEmptyBorder(0, kMenuEdgeEffectivePadding, 1, 0));
     title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::TITLE);
+    TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::TITLE,
+                             false /* use_unified_theme */);
     style.SetupLabel(title_label);
 
     AddChildView(title_label);
@@ -254,7 +255,7 @@
 // height depending on the number of IMEs in the list.
 class ImeMenuListView : public ImeListView, public DetailedViewDelegate {
  public:
-  ImeMenuListView() : ImeListView(this) {
+  ImeMenuListView() : ImeListView(this, false /* use_unified_theme */) {
     set_should_focus_ime_after_selection_with_keyboard(true);
   }
 
diff --git a/ash/system/keyboard_brightness/tray_keyboard_brightness.cc b/ash/system/keyboard_brightness/tray_keyboard_brightness.cc
index 4b3e11dd..9468a5ca 100644
--- a/ash/system/keyboard_brightness/tray_keyboard_brightness.cc
+++ b/ash/system/keyboard_brightness/tray_keyboard_brightness.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
@@ -14,6 +15,7 @@
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_popup_utils.h"
 #include "ash/system/tray/tri_view.h"
+#include "ash/system/unified/unified_slider_view.h"
 #include "ash/wm/tablet_mode/tablet_mode_observer.h"
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
@@ -36,26 +38,6 @@
 namespace ash {
 namespace tray {
 
-namespace {
-
-// A slider that ignores inputs.
-class ReadOnlySlider : public views::Slider {
- public:
-  ReadOnlySlider() : Slider(nullptr) {}
-
- private:
-  // views::View:
-  bool OnMousePressed(const ui::MouseEvent& event) override { return false; }
-  bool OnMouseDragged(const ui::MouseEvent& event) override { return false; }
-  void OnMouseReleased(const ui::MouseEvent& event) override {}
-  bool OnKeyPressed(const ui::KeyEvent& event) override { return false; }
-
-  // ui::EventHandler:
-  void OnGestureEvent(ui::GestureEvent* event) override {}
-};
-
-}  // namespace
-
 class KeyboardBrightnessView : public TabletModeObserver, public views::View {
  public:
   explicit KeyboardBrightnessView(double initial_percent);
@@ -158,6 +140,9 @@
       power_manager::BacklightBrightnessChange_Cause_USER_REQUEST)
     return;
 
+  if (features::IsSystemTrayUnifiedEnabled())
+    return;
+
   if (brightness_view_ && brightness_view_->visible())
     SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
   else
diff --git a/ash/system/keyboard_brightness/unified_keyboard_brightness_slider_controller.cc b/ash/system/keyboard_brightness/unified_keyboard_brightness_slider_controller.cc
new file mode 100644
index 0000000..c9d72f2
--- /dev/null
+++ b/ash/system/keyboard_brightness/unified_keyboard_brightness_slider_controller.cc
@@ -0,0 +1,72 @@
+// Copyright 2018 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 "ash/system/keyboard_brightness/unified_keyboard_brightness_slider_controller.h"
+
+#include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/system/unified/unified_system_tray_model.h"
+
+namespace ash {
+
+namespace {
+
+class UnifiedKeyboardBrightnessView : public UnifiedSliderView,
+                                      public UnifiedSystemTrayModel::Observer {
+ public:
+  UnifiedKeyboardBrightnessView(
+      UnifiedKeyboardBrightnessSliderController* controller,
+      UnifiedSystemTrayModel* model)
+      : UnifiedSliderView(controller,
+                          kSystemMenuKeyboardBrightnessIcon,
+                          IDS_ASH_STATUS_TRAY_BRIGHTNESS,
+                          true /* readonly*/),
+        model_(model) {
+    model_->AddObserver(this);
+    OnKeyboardBrightnessChanged(false /* by_user */);
+  }
+
+  ~UnifiedKeyboardBrightnessView() override { model_->RemoveObserver(this); }
+
+  // UnifiedSystemTrayModel::Observer:
+  void OnKeyboardBrightnessChanged(bool by_user) override {
+    slider()->SetValue(model_->keyboard_brightness());
+  }
+
+ private:
+  UnifiedSystemTrayModel* const model_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnifiedKeyboardBrightnessView);
+};
+
+}  // namespace
+
+UnifiedKeyboardBrightnessSliderController::
+    UnifiedKeyboardBrightnessSliderController(UnifiedSystemTrayModel* model)
+    : model_(model) {}
+
+UnifiedKeyboardBrightnessSliderController::
+    ~UnifiedKeyboardBrightnessSliderController() = default;
+
+views::View* UnifiedKeyboardBrightnessSliderController::CreateView() {
+  DCHECK(!slider_);
+  slider_ = new UnifiedKeyboardBrightnessView(this, model_);
+  return slider_;
+}
+
+void UnifiedKeyboardBrightnessSliderController::ButtonPressed(
+    views::Button* sender,
+    const ui::Event& event) {
+  // This slider is read-only.
+}
+
+void UnifiedKeyboardBrightnessSliderController::SliderValueChanged(
+    views::Slider* sender,
+    float value,
+    float old_value,
+    views::SliderChangeReason reason) {
+  // This slider is read-only.
+}
+
+}  // namespace ash
diff --git a/ash/system/keyboard_brightness/unified_keyboard_brightness_slider_controller.h b/ash/system/keyboard_brightness/unified_keyboard_brightness_slider_controller.h
new file mode 100644
index 0000000..5a6eda32
--- /dev/null
+++ b/ash/system/keyboard_brightness/unified_keyboard_brightness_slider_controller.h
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_KEYBOARD_BRIGHTNESS_UNIFIED_KEYBOARD_BRIGHTNESS_SLIDER_CONTROLLER_H_
+#define ASH_SYSTEM_KEYBOARD_BRIGHTNESS_UNIFIED_KEYBOARD_BRIGHTNESS_SLIDER_CONTROLLER_H_
+
+#include "ash/system/unified/unified_slider_view.h"
+
+namespace ash {
+
+class UnifiedSystemTrayModel;
+
+// Controller of a read-only slider showing keyboard brightness.
+class UnifiedKeyboardBrightnessSliderController : public UnifiedSliderListener {
+ public:
+  explicit UnifiedKeyboardBrightnessSliderController(
+      UnifiedSystemTrayModel* model);
+  ~UnifiedKeyboardBrightnessSliderController() override;
+
+  // UnifiedSliderListener:
+  views::View* CreateView() override;
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+  void SliderValueChanged(views::Slider* sender,
+                          float value,
+                          float old_value,
+                          views::SliderChangeReason reason) override;
+
+ private:
+  UnifiedSystemTrayModel* const model_;
+  UnifiedSliderView* slider_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(UnifiedKeyboardBrightnessSliderController);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_KEYBOARD_BRIGHTNESS_UNIFIED_KEYBOARD_BRIGHTNESS_SLIDER_CONTROLLER_H_
diff --git a/ash/system/message_center/arc/arc_notification_content_view.cc b/ash/system/message_center/arc/arc_notification_content_view.cc
index 113b9eb..59ec668 100644
--- a/ash/system/message_center/arc/arc_notification_content_view.cc
+++ b/ash/system/message_center/arc/arc_notification_content_view.cc
@@ -92,8 +92,17 @@
     // TODO(sarakato): Use a better tigger (eg. focusing EditText on
     // notification) than clicking (b/78604162).
     if (event->type() == ui::ET_MOUSE_PRESSED ||
-        event->type() == ui::ET_GESTURE_TAP)
+        event->type() == ui::ET_GESTURE_TAP) {
+      // Remove the focus from the currently focused view-control in the message
+      // center before activating the window of ARC notification, so that
+      // unexpected key handling doesn't happen (b/74415372).
+      // Focusing notification surface window doesn't steal the focus from
+      // the focucued view control in the message center, so that input events
+      // handles on both side wrongly without this.
+      owner_->GetFocusManager()->ClearFocus();
+
       owner_->Activate();
+    }
 
     views::Widget* widget = owner_->GetWidget();
     if (!widget)
diff --git a/ash/system/palette/common_palette_tool.cc b/ash/system/palette/common_palette_tool.cc
index 33d85a5..f1e214e 100644
--- a/ash/system/palette/common_palette_tool.cc
+++ b/ash/system/palette/common_palette_tool.cc
@@ -76,7 +76,7 @@
 views::View* CommonPaletteTool::CreateDefaultView(const base::string16& name) {
   gfx::ImageSkia icon =
       CreateVectorIcon(GetPaletteIcon(), kMenuIconSize, gfx::kChromeIconGrey);
-  highlight_view_ = new HoverHighlightView(this);
+  highlight_view_ = new HoverHighlightView(this, false /* use_unified_theme */);
   highlight_view_->AddIconAndLabel(icon, name);
   return highlight_view_;
 }
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index d4cb635..5fe91cb0 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -95,7 +95,8 @@
         new views::Label(l10n_util::GetStringUTF16(IDS_ASH_STYLUS_TOOLS_TITLE));
     title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
     AddChildView(title_label);
-    TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::TITLE);
+    TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::TITLE,
+                             false /* use_unified_theme */);
     style.SetupLabel(title_label);
     layout_ptr->SetFlexForView(title_label, 1);
     help_button_ = new SystemMenuButton(this, kSystemMenuHelpIcon,
diff --git a/ash/system/palette/tools/metalayer_mode.cc b/ash/system/palette/tools/metalayer_mode.cc
index 2d48dc6..50d0fc8 100644
--- a/ash/system/palette/tools/metalayer_mode.cc
+++ b/ash/system/palette/tools/metalayer_mode.cc
@@ -206,7 +206,8 @@
 
   highlight_view_->SetEnabled(selectable());
 
-  TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL);
+  TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL,
+                           false /* use_unified_theme */);
   style.set_color_style(highlight_view_->enabled()
                             ? TrayPopupItemStyle::ColorStyle::ACTIVE
                             : TrayPopupItemStyle::ColorStyle::DISABLED);
diff --git a/ash/system/tray/hover_highlight_view.cc b/ash/system/tray/hover_highlight_view.cc
index bb84df7..0e623c30 100644
--- a/ash/system/tray/hover_highlight_view.cc
+++ b/ash/system/tray/hover_highlight_view.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/tray/hover_highlight_view.h"
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/tray/tray_popup_utils.h"
@@ -20,8 +21,13 @@
 namespace ash {
 
 HoverHighlightView::HoverHighlightView(ViewClickListener* listener)
+    : HoverHighlightView(listener, features::IsSystemTrayUnifiedEnabled()) {}
+
+HoverHighlightView::HoverHighlightView(ViewClickListener* listener,
+                                       bool use_unified_theme)
     : ActionableView(nullptr, TrayPopupInkDropStyle::FILL_BOUNDS),
-      listener_(listener) {
+      listener_(listener),
+      use_unified_theme_(use_unified_theme) {
   set_notify_enter_exit_on_child(true);
   SetInkDropMode(InkDropHostView::InkDropMode::ON);
 }
@@ -76,7 +82,8 @@
     tri_view_->AddView(TriView::Container::CENTER, sub_text_label_);
   }
 
-  TrayPopupItemStyle sub_style(TrayPopupItemStyle::FontStyle::CAPTION);
+  TrayPopupItemStyle sub_style(TrayPopupItemStyle::FontStyle::CAPTION,
+                               use_unified_theme_);
   sub_style.set_color_style(TrayPopupItemStyle::ColorStyle::INACTIVE);
   sub_style.SetupLabel(sub_text_label_);
   sub_text_label_->SetText(sub_text);
@@ -130,7 +137,7 @@
   text_label_ = TrayPopupUtils::CreateDefaultLabel();
   text_label_->SetText(text);
   text_label_->SetEnabled(enabled());
-  TrayPopupItemStyle style(font_style);
+  TrayPopupItemStyle style(font_style, use_unified_theme_);
   style.SetupLabel(text_label_);
   tri_view_->AddView(TriView::Container::CENTER, text_label_);
   // By default, END container is invisible, so labels in the CENTER should have
@@ -158,7 +165,8 @@
   text_label_ = TrayPopupUtils::CreateDefaultLabel();
   text_label_->SetText(text);
 
-  TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL);
+  TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL,
+                           use_unified_theme_);
   style.SetupLabel(text_label_);
   tri_view_->AddView(TriView::Container::CENTER, text_label_);
 
diff --git a/ash/system/tray/hover_highlight_view.h b/ash/system/tray/hover_highlight_view.h
index c797a4f..f4316a42 100644
--- a/ash/system/tray/hover_highlight_view.h
+++ b/ash/system/tray/hover_highlight_view.h
@@ -38,7 +38,9 @@
   };
 
   // If |listener| is null then no action is taken on click.
+  // The former uses default for |use_unified_theme|.
   explicit HoverHighlightView(ViewClickListener* listener);
+  HoverHighlightView(ViewClickListener* listener, bool use_unified_theme);
   ~HoverHighlightView() override;
 
   // Convenience function for populating the view with an icon and a label. This
@@ -136,6 +138,7 @@
   views::View* right_view_ = nullptr;
   TriView* tri_view_ = nullptr;
   bool expandable_ = false;
+  const bool use_unified_theme_;
   AccessibilityState accessibility_state_ = AccessibilityState::DEFAULT;
 
   DISALLOW_COPY_AND_ASSIGN(HoverHighlightView);
diff --git a/ash/system/tray/system_menu_button.cc b/ash/system/tray/system_menu_button.cc
index 7ec1b44..c3efdbbd 100644
--- a/ash/system/tray/system_menu_button.cc
+++ b/ash/system/tray/system_menu_button.cc
@@ -52,13 +52,9 @@
 
 void SystemMenuButton::SetVectorIcon(const gfx::VectorIcon& icon) {
   SetImage(views::Button::STATE_NORMAL,
-           gfx::CreateVectorIcon(icon, features::IsSystemTrayUnifiedEnabled()
-                                           ? kUnifiedMenuIconColor
-                                           : kMenuIconColor));
+           gfx::CreateVectorIcon(icon, kMenuIconColor));
   SetImage(views::Button::STATE_DISABLED,
-           gfx::CreateVectorIcon(icon, features::IsSystemTrayUnifiedEnabled()
-                                           ? kUnifiedMenuIconColorDisabled
-                                           : kMenuIconColorDisabled));
+           gfx::CreateVectorIcon(icon, kMenuIconColorDisabled));
 }
 
 SystemMenuButton::~SystemMenuButton() = default;
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index febf12d..5f42fe62 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -174,6 +174,11 @@
 constexpr int kUnifiedFeaturePodMaxItemsInCollapsed = 5;
 constexpr int kUnifiedNotificationSeparatorThickness = 1;
 
+// Gap between the buttons on the top shortcut row, other than the
+// expand/collapse button.
+constexpr int kUnifiedTopShortcutButtonDefaultSpacing = 16;
+constexpr int kUnifiedTopShortcutButtonMinSpacing = 4;
+
 }  // namespace ash
 
 #endif  // ASH_SYSTEM_TRAY_TRAY_CONSTANTS_H_
diff --git a/ash/system/tray/tray_popup_item_style.cc b/ash/system/tray/tray_popup_item_style.cc
index 23794106..0b95f982 100644
--- a/ash/system/tray/tray_popup_item_style.cc
+++ b/ash/system/tray/tray_popup_item_style.cc
@@ -22,10 +22,10 @@
 }  // namespace
 
 // static
-SkColor TrayPopupItemStyle::GetIconColor(ColorStyle color_style) {
-  const SkColor kBaseIconColor = features::IsSystemTrayUnifiedEnabled()
-                                     ? kUnifiedMenuIconColor
-                                     : gfx::kChromeIconGrey;
+SkColor TrayPopupItemStyle::GetIconColor(ColorStyle color_style,
+                                         bool use_unified_theme) {
+  const SkColor kBaseIconColor =
+      use_unified_theme ? kUnifiedMenuIconColor : gfx::kChromeIconGrey;
   switch (color_style) {
     case ColorStyle::ACTIVE:
       return kBaseIconColor;
@@ -41,7 +41,13 @@
 }
 
 TrayPopupItemStyle::TrayPopupItemStyle(FontStyle font_style)
-    : font_style_(font_style), color_style_(ColorStyle::ACTIVE) {
+    : TrayPopupItemStyle(font_style, features::IsSystemTrayUnifiedEnabled()) {}
+
+TrayPopupItemStyle::TrayPopupItemStyle(FontStyle font_style,
+                                       bool use_unified_theme)
+    : font_style_(font_style),
+      color_style_(ColorStyle::ACTIVE),
+      use_unified_theme_(use_unified_theme) {
   if (font_style_ == FontStyle::SYSTEM_INFO)
     color_style_ = ColorStyle::INACTIVE;
 }
@@ -49,7 +55,7 @@
 TrayPopupItemStyle::~TrayPopupItemStyle() = default;
 
 SkColor TrayPopupItemStyle::GetTextColor() const {
-  const SkColor kBaseTextColor = features::IsSystemTrayUnifiedEnabled()
+  const SkColor kBaseTextColor = use_unified_theme_
                                      ? kUnifiedMenuTextColor
                                      : SkColorSetA(SK_ColorBLACK, 0xDE);
 
@@ -68,7 +74,7 @@
 }
 
 SkColor TrayPopupItemStyle::GetIconColor() const {
-  return GetIconColor(color_style_);
+  return GetIconColor(color_style_, use_unified_theme_);
 }
 
 void TrayPopupItemStyle::SetupLabel(views::Label* label) const {
diff --git a/ash/system/tray/tray_popup_item_style.h b/ash/system/tray/tray_popup_item_style.h
index cc70c302..5734adb 100644
--- a/ash/system/tray/tray_popup_item_style.h
+++ b/ash/system/tray/tray_popup_item_style.h
@@ -17,6 +17,7 @@
 // Central style provider for the system tray menu. Makes it easier to ensure
 // all visuals are consistent and easily updated in one spot instead of being
 // defined in multiple places throughout the code.
+// TODO(tetsui): Clean up this class after UnifiedSystemTray is launched.
 class TrayPopupItemStyle {
  public:
   // The different visual styles that a row can have.
@@ -52,9 +53,13 @@
 
   static constexpr double kInactiveIconAlpha = 0.54;
 
-  static SkColor GetIconColor(ColorStyle color_style);
+  static SkColor GetIconColor(ColorStyle color_style,
+                              bool use_unified_theme = false);
 
+  // The first constructor initializes |use_unified_theme_| with default. See
+  // the comment below.
   explicit TrayPopupItemStyle(FontStyle font_style);
+  TrayPopupItemStyle(FontStyle font_style, bool use_unified_theme);
   ~TrayPopupItemStyle();
 
   void set_color_style(ColorStyle color_style) { color_style_ = color_style; }
@@ -75,6 +80,11 @@
 
   ColorStyle color_style_;
 
+  // Use base colors for UnifiedSystemTray. If IsSystemTrayUnifiedEnabled() is
+  // true, the value is true by default.
+  // TODO(tetsui): Clean up this after UnifiedSystemTray is launched.
+  const bool use_unified_theme_;
+
   DISALLOW_COPY_AND_ASSIGN(TrayPopupItemStyle);
 };
 
diff --git a/ash/system/tray/tray_popup_utils.cc b/ash/system/tray/tray_popup_utils.cc
index 63558f2..b1e86b5 100644
--- a/ash/system/tray/tray_popup_utils.cc
+++ b/ash/system/tray/tray_popup_utils.cc
@@ -160,14 +160,15 @@
 views::Label* TrayPopupUtils::CreateDefaultLabel() {
   views::Label* label = new views::Label();
   label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  // Frequently the label will paint to a layer that's non-opaque, so subpixel
-  // rendering won't work unless we explicitly set a background. See
-  // crbug.com/686363
-  label->SetBackground(
-      features::IsSystemTrayUnifiedEnabled()
-          ? views::CreateSolidBackground(kUnifiedMenuBackgroundColor)
-          : views::CreateThemedSolidBackground(
-                label, ui::NativeTheme::kColorId_BubbleBackground));
+  if (features::IsSystemTrayUnifiedEnabled()) {
+    label->SetSubpixelRenderingEnabled(false);
+  } else {
+    // Frequently the label will paint to a layer that's non-opaque, so subpixel
+    // rendering won't work unless we explicitly set a background. See
+    // https://crbug.com/686363
+    label->SetBackground(views::CreateThemedSolidBackground(
+        label, ui::NativeTheme::kColorId_BubbleBackground));
+  }
   return label;
 }
 
diff --git a/ash/system/unified/notification_counter_view.cc b/ash/system/unified/notification_counter_view.cc
index 796f116..3cc8c60 100644
--- a/ash/system/unified/notification_counter_view.cc
+++ b/ash/system/unified/notification_counter_view.cc
@@ -33,8 +33,12 @@
 
 QuietModeView::QuietModeView() : TrayItemView(nullptr) {
   CreateImageView();
-  image_view()->SetImage(gfx::CreateVectorIcon(
-      kNotificationCenterDoNotDisturbOnIcon, kTrayIconSize, kTrayIconColor));
+  // TODO(yamaguchi): Add this check when new style of the system tray is
+  // implemented, so that icon resizing will not happen here.
+  // DCHECK_EQ(kTrayIconSize,
+  //     gfx::GetDefaultSizeOfVectorIcon(kSystemTrayDoNotDisturbIcon));
+  image_view()->SetImage(gfx::CreateVectorIcon(kSystemTrayDoNotDisturbIcon,
+                                               kTrayIconSize, kTrayIconColor));
   Update();
 }
 
diff --git a/ash/system/unified/top_shortcuts_view.cc b/ash/system/unified/top_shortcuts_view.cc
index e53aa042..c00141e 100644
--- a/ash/system/unified/top_shortcuts_view.cc
+++ b/ash/system/unified/top_shortcuts_view.cc
@@ -39,21 +39,97 @@
   AddChildView(CreateUserAvatarView(0 /* user_index */));
 }
 
+class TopShortcutButtonContainer : public views::View {
+ public:
+  TopShortcutButtonContainer();
+  ~TopShortcutButtonContainer() override;
+
+  // views::View:
+  void Layout() override;
+  gfx::Size CalculatePreferredSize() const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TopShortcutButtonContainer);
+};
+
+// Buttons are equally spaced by the default value, but the gap will be
+// narrowed evenly when the parent view is not large enough.
+void TopShortcutButtonContainer::Layout() {
+  gfx::Rect child_area(GetContentsBounds());
+
+  int total_horizontal_size = 0;
+  int num_visible = 0;
+  for (int i = 0; i < child_count(); i++) {
+    const views::View* child = child_at(i);
+    if (!child->visible())
+      continue;
+    int child_horizontal_size = child->GetPreferredSize().width();
+    if (child_horizontal_size == 0)
+      continue;
+    total_horizontal_size += child_horizontal_size;
+    num_visible++;
+  }
+  int spacing = std::max(kUnifiedTopShortcutButtonMinSpacing,
+                         std::min(kUnifiedTopShortcutButtonDefaultSpacing,
+                                  (child_area.width() - total_horizontal_size) /
+                                      (num_visible - 1)));
+
+  int horizontal_position = child_area.x();
+  for (int i = 0; i < child_count(); i++) {
+    views::View* child = child_at(i);
+    if (!child->visible())
+      continue;
+    gfx::Rect bounds(child_area);
+    bounds.set_x(horizontal_position);
+    bounds.set_width(child->GetPreferredSize().width());
+    child->SetBoundsRect(bounds);
+    horizontal_position += child->GetPreferredSize().width() + spacing;
+  }
+}
+
+gfx::Size TopShortcutButtonContainer::CalculatePreferredSize() const {
+  int total_horizontal_size = 0;
+  int max_height = 0;
+  int num_visible = 0;
+  for (int i = 0; i < child_count(); i++) {
+    const views::View* child = child_at(i);
+    if (!child->visible())
+      continue;
+    int child_horizontal_size = child->GetPreferredSize().width();
+    if (child_horizontal_size == 0)
+      continue;
+    total_horizontal_size += child_horizontal_size;
+    num_visible++;
+    max_height = std::max(child->GetPreferredSize().height(), max_height);
+  }
+  int width =
+      (num_visible == 0)
+          ? 0
+          : total_horizontal_size +
+                (num_visible - 1) * kUnifiedTopShortcutButtonDefaultSpacing;
+  return gfx::Size(width, max_height);
+}
+
+TopShortcutButtonContainer::~TopShortcutButtonContainer() = default;
+
+TopShortcutButtonContainer::TopShortcutButtonContainer() = default;
+
 }  // namespace
 
 TopShortcutsView::TopShortcutsView(UnifiedSystemTrayController* controller)
-    : controller_(controller) {
+    : controller_(controller), container_(new TopShortcutButtonContainer()) {
   DCHECK(controller_);
 
   auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::kHorizontal, kUnifiedTopShortcutPadding,
       kUnifiedTopShortcutSpacing));
   layout->set_cross_axis_alignment(views::BoxLayout::CROSS_AXIS_ALIGNMENT_END);
+  AddChildView(container_);
 
   if (Shell::Get()->session_controller()->login_status() !=
       LoginStatus::NOT_LOGGED_IN) {
     user_avatar_button_ = new UserAvatarButton(this);
-    AddChildView(user_avatar_button_);
+    container_->AddChildView(user_avatar_button_);
   }
 
   // Show the buttons in this row as disabled if the user is at the login
@@ -62,30 +138,28 @@
   const bool can_show_web_ui = TrayPopupUtils::CanOpenWebUISettings();
 
   sign_out_button_ = new SignOutButton(this);
-  AddChildView(sign_out_button_);
+  container_->AddChildView(sign_out_button_);
 
   lock_button_ = new TopShortcutButton(this, kSystemMenuLockIcon,
                                        IDS_ASH_STATUS_TRAY_LOCK);
   lock_button_->SetEnabled(can_show_web_ui &&
                            Shell::Get()->session_controller()->CanLockScreen());
-  AddChildView(lock_button_);
+  container_->AddChildView(lock_button_);
 
   settings_button_ = new TopShortcutButton(this, kSystemMenuSettingsIcon,
                                            IDS_ASH_STATUS_TRAY_SETTINGS);
   settings_button_->SetEnabled(can_show_web_ui);
-  AddChildView(settings_button_);
+  container_->AddChildView(settings_button_);
 
   bool reboot = Shell::Get()->shutdown_controller()->reboot_on_shutdown();
   power_button_ = new TopShortcutButton(
       this, kSystemMenuPowerIcon,
       reboot ? IDS_ASH_STATUS_TRAY_REBOOT : IDS_ASH_STATUS_TRAY_SHUTDOWN);
-  AddChildView(power_button_);
+  container_->AddChildView(power_button_);
 
-  // |collapse_button_| should be right-aligned, so we need spacing between
-  // other buttons and |collapse_button_|.
-  views::View* spacing = new views::View;
-  AddChildView(spacing);
-  layout->SetFlexForView(spacing, 1);
+  // |collapse_button_| should be right-aligned, so we make the buttons
+  // container flex occupying all remaining space.
+  layout->SetFlexForView(container_, 1);
 
   collapse_button_ = new CollapseButton(this);
   AddChildView(collapse_button_);
diff --git a/ash/system/unified/top_shortcuts_view.h b/ash/system/unified/top_shortcuts_view.h
index 514df17..0484889b 100644
--- a/ash/system/unified/top_shortcuts_view.h
+++ b/ash/system/unified/top_shortcuts_view.h
@@ -38,6 +38,7 @@
   // Owned by views hierarchy.
   views::Button* user_avatar_button_ = nullptr;
   SignOutButton* sign_out_button_ = nullptr;
+  views::View* const container_;
   TopShortcutButton* lock_button_ = nullptr;
   TopShortcutButton* settings_button_ = nullptr;
   TopShortcutButton* power_button_ = nullptr;
diff --git a/ash/system/unified/unified_slider_bubble_controller.cc b/ash/system/unified/unified_slider_bubble_controller.cc
index 6f78c3c..0e163616 100644
--- a/ash/system/unified/unified_slider_bubble_controller.cc
+++ b/ash/system/unified/unified_slider_bubble_controller.cc
@@ -6,6 +6,7 @@
 
 #include "ash/system/audio/unified_volume_slider_controller.h"
 #include "ash/system/brightness/unified_brightness_slider_controller.h"
+#include "ash/system/keyboard_brightness/unified_keyboard_brightness_slider_controller.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/unified_system_tray.h"
 
@@ -62,8 +63,14 @@
   ShowBubble(SLIDER_TYPE_VOLUME);
 }
 
-void UnifiedSliderBubbleController::OnBrightnessChanged() {
-  ShowBubble(SLIDER_TYPE_DISPLAY_BRIGHTNESS);
+void UnifiedSliderBubbleController::OnDisplayBrightnessChanged(bool by_user) {
+  if (by_user)
+    ShowBubble(SLIDER_TYPE_DISPLAY_BRIGHTNESS);
+}
+
+void UnifiedSliderBubbleController::OnKeyboardBrightnessChanged(bool by_user) {
+  if (by_user)
+    ShowBubble(SLIDER_TYPE_KEYBOARD_BRIGHTNESS);
 }
 
 void UnifiedSliderBubbleController::ShowBubble(SliderType slider_type) {
@@ -128,6 +135,11 @@
       slider_controller_ =
           std::make_unique<UnifiedBrightnessSliderController>(tray_->model());
       return;
+    case SLIDER_TYPE_KEYBOARD_BRIGHTNESS:
+      slider_controller_ =
+          std::make_unique<UnifiedKeyboardBrightnessSliderController>(
+              tray_->model());
+      return;
   }
 }
 
diff --git a/ash/system/unified/unified_slider_bubble_controller.h b/ash/system/unified/unified_slider_bubble_controller.h
index 11a2414..3c6b27e 100644
--- a/ash/system/unified/unified_slider_bubble_controller.h
+++ b/ash/system/unified/unified_slider_bubble_controller.h
@@ -36,11 +36,15 @@
   void OnOutputMuteChanged(bool mute_on, bool system_adjust) override;
 
   // UnifiedSystemTrayModel::Observer:
-  void OnBrightnessChanged() override;
+  void OnDisplayBrightnessChanged(bool by_user) override;
+  void OnKeyboardBrightnessChanged(bool by_user) override;
 
  private:
-  // TODO(tetsui): Implement SLIDER_TYPE_KEYBOARD_BRIGHTNESS.
-  enum SliderType { SLIDER_TYPE_VOLUME = 0, SLIDER_TYPE_DISPLAY_BRIGHTNESS };
+  enum SliderType {
+    SLIDER_TYPE_VOLUME = 0,
+    SLIDER_TYPE_DISPLAY_BRIGHTNESS,
+    SLIDER_TYPE_KEYBOARD_BRIGHTNESS
+  };
 
   // Show a slider of |slider_type|. If the slider of same type is already
   // shown, it just extends the auto close timer.
diff --git a/ash/system/unified/unified_slider_view.cc b/ash/system/unified/unified_slider_view.cc
index 1ea27b2..0e63db6 100644
--- a/ash/system/unified/unified_slider_view.cc
+++ b/ash/system/unified/unified_slider_view.cc
@@ -13,6 +13,35 @@
 
 namespace ash {
 
+namespace {
+
+views::Slider* CreateSlider(UnifiedSliderListener* listener, bool readonly) {
+  if (readonly)
+    return new ReadOnlySlider();
+
+  return new views::Slider(listener);
+}
+
+}  // namespace
+
+ReadOnlySlider::ReadOnlySlider() : Slider(nullptr) {}
+
+bool ReadOnlySlider::OnMousePressed(const ui::MouseEvent& event) {
+  return false;
+}
+
+bool ReadOnlySlider::OnMouseDragged(const ui::MouseEvent& event) {
+  return false;
+}
+
+void ReadOnlySlider::OnMouseReleased(const ui::MouseEvent& event) {}
+
+bool ReadOnlySlider::OnKeyPressed(const ui::KeyEvent& event) {
+  return false;
+}
+
+void ReadOnlySlider::OnGestureEvent(ui::GestureEvent* event) {}
+
 UnifiedSliderButton::UnifiedSliderButton(views::ButtonListener* listener,
                                          const gfx::VectorIcon& icon,
                                          int accessible_name_id)
@@ -46,9 +75,10 @@
 
 UnifiedSliderView::UnifiedSliderView(UnifiedSliderListener* listener,
                                      const gfx::VectorIcon& icon,
-                                     int accessible_name_id)
+                                     int accessible_name_id,
+                                     bool readonly)
     : button_(new UnifiedSliderButton(listener, icon, accessible_name_id)),
-      slider_(new views::Slider(listener)) {
+      slider_(CreateSlider(listener, readonly)) {
   auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::kHorizontal, kUnifiedMenuItemPadding,
       kUnifiedTopShortcutSpacing));
diff --git a/ash/system/unified/unified_slider_view.h b/ash/system/unified/unified_slider_view.h
index c6cf08f..dce8c76 100644
--- a/ash/system/unified/unified_slider_view.h
+++ b/ash/system/unified/unified_slider_view.h
@@ -23,6 +23,25 @@
   ~UnifiedSliderListener() override = default;
 };
 
+// A slider that ignores inputs.
+// TODO(tetsui): Move to anonymous namespace.
+class ReadOnlySlider : public views::Slider {
+ public:
+  ReadOnlySlider();
+
+ private:
+  // views::View:
+  bool OnMousePressed(const ui::MouseEvent& event) override;
+  bool OnMouseDragged(const ui::MouseEvent& event) override;
+  void OnMouseReleased(const ui::MouseEvent& event) override;
+  bool OnKeyPressed(const ui::KeyEvent& event) override;
+
+  // ui::EventHandler:
+  void OnGestureEvent(ui::GestureEvent* event) override;
+
+  DISALLOW_COPY_AND_ASSIGN(ReadOnlySlider);
+};
+
 // A button used in a slider row of UnifiedSystemTray. The button is togglable.
 class UnifiedSliderButton : public TopShortcutButton {
  public:
@@ -51,9 +70,11 @@
 // left side and a slider on the right side.
 class UnifiedSliderView : public views::View {
  public:
+  // If |readonly| is set, the slider will not accept any user events.
   UnifiedSliderView(UnifiedSliderListener* listener,
                     const gfx::VectorIcon& icon,
-                    int accessible_name_id);
+                    int accessible_name_id,
+                    bool readonly = false);
   ~UnifiedSliderView() override;
 
  protected:
diff --git a/ash/system/unified/unified_system_tray_model.cc b/ash/system/unified/unified_system_tray_model.cc
index a5e489e2..2cad65b 100644
--- a/ash/system/unified/unified_system_tray_model.cc
+++ b/ash/system/unified/unified_system_tray_model.cc
@@ -23,6 +23,8 @@
   // chromeos::PowerManagerClient::Observer:
   void ScreenBrightnessChanged(
       const power_manager::BacklightBrightnessChange& change) override;
+  void KeyboardBrightnessChanged(
+      const power_manager::BacklightBrightnessChange& change) override;
 
   UnifiedSystemTrayModel* const owner_;
 
@@ -49,14 +51,26 @@
 void UnifiedSystemTrayModel::DBusObserver::HandleInitialBrightness(
     base::Optional<double> percent) {
   if (percent.has_value())
-    owner_->BrightnessChanged(percent.value() / 100.);
+    owner_->DisplayBrightnessChanged(percent.value() / 100.,
+                                     false /* by_user */);
 }
 
 void UnifiedSystemTrayModel::DBusObserver::ScreenBrightnessChanged(
     const power_manager::BacklightBrightnessChange& change) {
   Shell::Get()->metrics()->RecordUserMetricsAction(
       UMA_STATUS_AREA_BRIGHTNESS_CHANGED);
-  owner_->BrightnessChanged(change.percent() / 100.);
+  owner_->DisplayBrightnessChanged(
+      change.percent() / 100.,
+      change.cause() ==
+          power_manager::BacklightBrightnessChange_Cause_USER_REQUEST);
+}
+
+void UnifiedSystemTrayModel::DBusObserver::KeyboardBrightnessChanged(
+    const power_manager::BacklightBrightnessChange& change) {
+  owner_->KeyboardBrightnessChanged(
+      change.percent() / 100.,
+      change.cause() ==
+          power_manager::BacklightBrightnessChange_Cause_USER_REQUEST);
 }
 
 UnifiedSystemTrayModel::UnifiedSystemTrayModel()
@@ -72,10 +86,18 @@
   observers_.RemoveObserver(observer);
 }
 
-void UnifiedSystemTrayModel::BrightnessChanged(float brightness) {
-  brightness_ = brightness;
+void UnifiedSystemTrayModel::DisplayBrightnessChanged(float brightness,
+                                                      bool by_user) {
+  display_brightness_ = brightness;
   for (auto& observer : observers_)
-    observer.OnBrightnessChanged();
+    observer.OnDisplayBrightnessChanged(by_user);
+}
+
+void UnifiedSystemTrayModel::KeyboardBrightnessChanged(float brightness,
+                                                       bool by_user) {
+  keyboard_brightness_ = brightness;
+  for (auto& observer : observers_)
+    observer.OnKeyboardBrightnessChanged(by_user);
 }
 
 }  // namespace ash
diff --git a/ash/system/unified/unified_system_tray_model.h b/ash/system/unified/unified_system_tray_model.h
index dbb40ed..6b6ad630 100644
--- a/ash/system/unified/unified_system_tray_model.h
+++ b/ash/system/unified/unified_system_tray_model.h
@@ -20,7 +20,9 @@
    public:
     virtual ~Observer() {}
 
-    virtual void OnBrightnessChanged() = 0;
+    // |by_user| is true when brightness is changed by user action.
+    virtual void OnDisplayBrightnessChanged(bool by_user) {}
+    virtual void OnKeyboardBrightnessChanged(bool by_user) {}
   };
 
   UnifiedSystemTrayModel();
@@ -30,7 +32,8 @@
   void RemoveObserver(Observer* observer);
 
   bool expanded_on_open() const { return expanded_on_open_; }
-  float brightness() const { return brightness_; }
+  float display_brightness() const { return display_brightness_; }
+  float keyboard_brightness() const { return keyboard_brightness_; }
 
   void set_expanded_on_open(bool expanded_on_open) {
     expanded_on_open_ = expanded_on_open;
@@ -39,14 +42,18 @@
  private:
   class DBusObserver;
 
-  void BrightnessChanged(float brightness);
+  void DisplayBrightnessChanged(float brightness, bool by_user);
+  void KeyboardBrightnessChanged(float brightness, bool by_user);
 
   // If UnifiedSystemTray bubble is expanded on its open. It's expanded by
   // default, and if a user collapses manually, it remembers previous state.
   bool expanded_on_open_ = true;
 
-  // The last value of the brightness slider. Between 0.0 and 1.0.
-  float brightness_ = 1.f;
+  // The last value of the display brightness slider. Between 0.0 and 1.0.
+  float display_brightness_ = 1.f;
+
+  // The last value of the keyboard brightness slider. Between 0.0 and 1.0.
+  float keyboard_brightness_ = 1.f;
 
   std::unique_ptr<DBusObserver> dbus_observer_;
 
diff --git a/base/profiler/native_stack_sampler.h b/base/profiler/native_stack_sampler.h
index ebd7c3c4..baf64e5 100644
--- a/base/profiler/native_stack_sampler.h
+++ b/base/profiler/native_stack_sampler.h
@@ -52,8 +52,8 @@
 
   virtual ~NativeStackSampler();
 
-  // Creates a stack sampler that records samples for |thread_handle|. Returns
-  // null if this platform does not support stack sampling.
+  // Creates a stack sampler that records samples for thread with |thread_id|.
+  // Returns null if this platform does not support stack sampling.
   static std::unique_ptr<NativeStackSampler> Create(
       PlatformThreadId thread_id,
       AnnotateCallback annotator,
diff --git a/build/toolchain/win/setup_toolchain.py b/build/toolchain/win/setup_toolchain.py
index 9bc56fe..7405c61 100644
--- a/build/toolchain/win/setup_toolchain.py
+++ b/build/toolchain/win/setup_toolchain.py
@@ -241,8 +241,12 @@
       # _LoadToolchainEnv() above.
       include = [p.replace('"', r'\"') for p in env['INCLUDE'].split(';') if p]
 
-      # Make include path relative to builddir.
-      include = map(os.path.relpath, include)
+      # Make include path relative to builddir when cwd and sdk in same drive.
+      try:
+        include = map(os.path.relpath, include)
+      except ValueError:
+        pass
+
       include_I = ' '.join(['"/I' + i + '"' for i in include])
       include_imsvc = ' '.join(['"-imsvc' + i + '"' for i in include])
 
diff --git a/build_overrides/build.gni b/build_overrides/build.gni
index 590729d..f18c9d85 100644
--- a/build_overrides/build.gni
+++ b/build_overrides/build.gni
@@ -2,9 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# Variable that can be used to support multiple build scenarios, like having
-# Chromium specific targets in a client project's GN file etc.
-build_with_chromium = true
+import("//build/config/gclient_args.gni")
 
 # Uncomment these to specify a different NDK location and version in
 # non-Chromium builds.
diff --git a/cc/paint/oop_pixeltest.cc b/cc/paint/oop_pixeltest.cc
index cb3168a7..b66c3e2 100644
--- a/cc/paint/oop_pixeltest.cc
+++ b/cc/paint/oop_pixeltest.cc
@@ -8,6 +8,7 @@
 #include "base/command_line.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "cc/base/completion_event.h"
 #include "cc/base/region.h"
 #include "cc/layers/recording_source.h"
 #include "cc/paint/display_item_list.h"
@@ -28,6 +29,7 @@
 #include "gpu/ipc/gl_in_process_context.h"
 #include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkGraphics.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/gpu/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/GrContext.h"
@@ -99,6 +101,7 @@
     bool preclear = false;
     SkColor preclear_color;
     ImageDecodeCache* image_cache = nullptr;
+    std::vector<scoped_refptr<DisplayItemList>> additional_lists;
   };
 
   SkBitmap Raster(scoped_refptr<DisplayItemList> display_item_list,
@@ -151,6 +154,12 @@
         display_item_list.get(), &image_provider, options.content_size,
         options.full_raster_rect, options.playback_rect, options.post_translate,
         options.post_scale, options.requires_clear);
+    for (const auto& list : options.additional_lists) {
+      raster_implementation->RasterCHROMIUM(
+          list.get(), &image_provider, options.content_size,
+          options.full_raster_rect, options.playback_rect,
+          options.post_translate, options.post_scale, options.requires_clear);
+    }
     raster_implementation->EndRasterCHROMIUM();
 
     // Produce a mailbox and insert an ordering barrier (assumes the raster
@@ -1200,10 +1209,12 @@
   ExpectEquals(actual, expected);
 }
 
-scoped_refptr<PaintTextBlob> buildTextBlob() {
+scoped_refptr<PaintTextBlob> BuildTextBlob(
+    PaintTypeface typeface = PaintTypeface()) {
   SkFontStyle style;
-  PaintTypeface typeface =
-      PaintTypeface::FromFamilyNameAndFontStyle("monospace", style);
+  if (!typeface) {
+    typeface = PaintTypeface::FromFamilyNameAndFontStyle("monospace", style);
+  }
 
   PaintFont font;
   font.SetTypeface(typeface);
@@ -1235,7 +1246,7 @@
   PaintFlags flags;
   flags.setStyle(PaintFlags::kFill_Style);
   flags.setColor(SK_ColorGREEN);
-  display_item_list->push<DrawTextBlobOp>(buildTextBlob(), 0u, 0u, flags);
+  display_item_list->push<DrawTextBlobOp>(BuildTextBlob(), 0u, 0u, flags);
   display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
   display_item_list->Finalize();
 
@@ -1256,7 +1267,7 @@
   PaintFlags flags;
   flags.setStyle(PaintFlags::kFill_Style);
   flags.setColor(SK_ColorGREEN);
-  paint_record->push<DrawTextBlobOp>(buildTextBlob(), 0u, 0u, flags);
+  paint_record->push<DrawTextBlobOp>(BuildTextBlob(), 0u, 0u, flags);
   auto paint_record_shader = PaintShader::MakePaintRecord(
       paint_record, SkRect::MakeWH(25, 25), SkShader::kRepeat_TileMode,
       SkShader::kRepeat_TileMode, nullptr);
@@ -1287,7 +1298,7 @@
   PaintFlags flags;
   flags.setStyle(PaintFlags::kFill_Style);
   flags.setColor(SK_ColorGREEN);
-  paint_record->push<DrawTextBlobOp>(buildTextBlob(), 0u, 0u, flags);
+  paint_record->push<DrawTextBlobOp>(BuildTextBlob(), 0u, 0u, flags);
   auto paint_record_filter =
       sk_make_sp<RecordPaintFilter>(paint_record, SkRect::MakeWH(100, 100));
 
@@ -1305,6 +1316,57 @@
   ExpectEquals(actual, expected);
 }
 
+void ClearFontCache(CompletionEvent* event) {
+  SkGraphics::PurgeFontCache();
+  event->Signal();
+}
+
+TEST_F(OopPixelTest, DrawTextMultipleRasterCHROMIUM) {
+  RasterOptions options;
+  options.resource_size = gfx::Size(100, 100);
+  options.content_size = options.resource_size;
+  options.full_raster_rect = gfx::Rect(options.content_size);
+  options.playback_rect = options.full_raster_rect;
+  options.color_space = gfx::ColorSpace::CreateSRGB();
+
+  auto sk_typeface_1 = SkTypeface::MakeFromName("monospace", SkFontStyle());
+  auto sk_typeface_2 = SkTypeface::MakeFromName("roboto", SkFontStyle());
+
+  auto display_item_list = base::MakeRefCounted<DisplayItemList>();
+  display_item_list->StartPaint();
+  PaintFlags flags;
+  flags.setStyle(PaintFlags::kFill_Style);
+  flags.setColor(SK_ColorGREEN);
+  display_item_list->push<DrawTextBlobOp>(
+      BuildTextBlob(PaintTypeface::FromSkTypeface(sk_typeface_1)), 0u, 0u,
+      flags);
+  display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
+  display_item_list->Finalize();
+
+  // Create another list with a different typeface.
+  auto display_item_list_2 = base::MakeRefCounted<DisplayItemList>();
+  display_item_list_2->StartPaint();
+  display_item_list_2->push<DrawTextBlobOp>(
+      BuildTextBlob(PaintTypeface::FromSkTypeface(sk_typeface_2)), 0u, 0u,
+      flags);
+  display_item_list_2->EndPaintOfUnpaired(options.full_raster_rect);
+  display_item_list_2->Finalize();
+
+  // Raster both these lists with 2 RasterCHROMIUM commands between a single
+  // Begin/EndRaster sequence.
+  options.additional_lists = {display_item_list_2};
+  Raster(display_item_list, options);
+
+  // Clear skia's font cache. No entries should remain since the service
+  // should unpin everything.
+  EXPECT_GT(SkGraphics::GetFontCacheUsed(), 0u);
+  CompletionEvent event;
+  raster_context_provider_->ExecuteOnGpuThread(
+      base::BindOnce(&ClearFontCache, &event));
+  event.Wait();
+  EXPECT_EQ(SkGraphics::GetFontCacheUsed(), 0u);
+}
+
 INSTANTIATE_TEST_CASE_P(P, OopImagePixelTest, ::testing::Bool());
 
 }  // namespace
diff --git a/cc/test/test_in_process_context_provider.cc b/cc/test/test_in_process_context_provider.cc
index c5dfb836d..8a62b28 100644
--- a/cc/test/test_in_process_context_provider.cc
+++ b/cc/test/test_in_process_context_provider.cc
@@ -172,4 +172,11 @@
   return gpu_feature_info_;
 }
 
+void TestInProcessContextProvider::ExecuteOnGpuThread(base::OnceClosure task) {
+  DCHECK(raster_context_);
+  raster_context_->GetCommandBufferForTest()
+      ->service_for_testing()
+      ->ScheduleTask(std::move(task));
+}
+
 }  // namespace cc
diff --git a/cc/test/test_in_process_context_provider.h b/cc/test/test_in_process_context_provider.h
index 0daece7..032a5a5c 100644
--- a/cc/test/test_in_process_context_provider.h
+++ b/cc/test/test_in_process_context_provider.h
@@ -54,6 +54,8 @@
   void AddObserver(viz::ContextLostObserver* obs) override {}
   void RemoveObserver(viz::ContextLostObserver* obs) override {}
 
+  void ExecuteOnGpuThread(base::OnceClosure task);
+
  protected:
   friend class base::RefCountedThreadSafe<TestInProcessContextProvider>;
   ~TestInProcessContextProvider() override;
diff --git a/chrome/VERSION b/chrome/VERSION
index fa2d9743..2320086 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=69
 MINOR=0
-BUILD=3446
+BUILD=3447
 PATCH=0
diff --git a/chrome/android/java/DEPS b/chrome/android/java/DEPS
index 369c9a1..10a43436 100644
--- a/chrome/android/java/DEPS
+++ b/chrome/android/java/DEPS
@@ -20,7 +20,6 @@
 
   "-content/public/android",
   "+content/public/android/java/src/org/chromium/content_public",
-  "!content/public/android/java/src/org/chromium/content/browser/ChildProcessCreationParams.java",
   "!content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java",
 
   "+device/gamepad/android/java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
index 8223aae0..0a83cbe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/MonochromeApplication.java
@@ -9,7 +9,7 @@
 
 import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.library_loader.LibraryProcessType;
-import org.chromium.content.browser.ChildProcessCreationParams;
+import org.chromium.content_public.browser.ChildProcessCreationParams;
 
 /**
  * This is Application class for Monochrome.
@@ -28,8 +28,7 @@
         // created and set in all processes.
         boolean bindToCaller = false;
         boolean ignoreVisibilityForImportance = false;
-        ChildProcessCreationParams.set(new ChildProcessCreationParams(getPackageName(),
-                true /* isExternalService */, LibraryProcessType.PROCESS_CHILD, bindToCaller,
-                ignoreVisibilityForImportance));
+        ChildProcessCreationParams.set(getPackageName(), true /* isExternalService */,
+                LibraryProcessType.PROCESS_CHILD, bindToCaller, ignoreVisibilityForImportance);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
index b61210a..a503507d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanel.java
@@ -414,6 +414,7 @@
     protected void destroyOverlayPanelContent() {
         // It is possible that an OverlayPanelContent was never created for this panel.
         if (mContent != null) {
+            mActivity.getCompositorViewHolder().removeView(getContainerView());
             mContent.destroy();
             mContent = null;
         }
@@ -793,19 +794,17 @@
           // In either case the screen's viewport width or height will certainly change.
             mViewportWidth = width;
             mViewportHeight = height;
-            resizePanelContentViewCore(width, height);
+            resizePanelContentView(width, height);
             onLayoutChanged(width, height, visibleViewportOffsetY);
         }
     }
 
     /**
-     * Resize the panel's ContentViewCore manually since it is not attached to the view hierarchy.
-     * TODO(mdjones): Remove the need for this method by supporting multiple ContentViewCores
-     * existing simultaneously in the view hierarchy.
+     * Resize the panel's ContentView. Apply adjusted bar size to the height.
      * @param width The new width in dp.
      * @param height The new height in dp.
      */
-    protected void resizePanelContentViewCore(float width, float height) {
+    protected void resizePanelContentView(float width, float height) {
         if (!isShowing()) return;
         OverlayPanelContent panelContent = getOverlayPanelContent();
         int widthPx = (int) (width / mPxToDp);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
index 933f827..6db72eb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
@@ -366,6 +366,7 @@
         int viewHeight = mContentViewHeight - (mSubtractBarHeight ? mBarHeightPx : 0);
         onPhysicalBackingSizeChanged(mContentViewWidth, viewHeight);
         mWebContents.setSize(mContentViewWidth, viewHeight);
+        mActivity.getCompositorViewHolder().addView(mContainerView, 1);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
index cf54e2e..9dcf1a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/locale/LocaleManager.java
@@ -85,7 +85,7 @@
 
     // LocaleManager is a singleton and it should not have strong reference to UI objects.
     // SnackbarManager is owned by ChromeActivity and is not null as long as the activity is alive.
-    private WeakReference<SnackbarManager> mSnackbarManager;
+    private WeakReference<SnackbarManager> mSnackbarManager = new WeakReference<>(null);
     private LocaleTemplateUrlLoader mLocaleTemplateUrlLoader;
 
     private SnackbarController mSnackbarController = new SnackbarController() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelEventFilterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelEventFilterTest.java
index cf0909fd..6bf66b8b8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelEventFilterTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelEventFilterTest.java
@@ -162,7 +162,7 @@
         }
 
         @Override
-        protected void resizePanelContentViewCore(float width, float height) {}
+        protected void resizePanelContentView(float width, float height) {}
 
         @Override
         protected void animatePanelTo(float height, long duration) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrInputTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrInputTest.java
index ea56f09..274244c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrInputTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrInputTest.java
@@ -66,8 +66,7 @@
  */
 @RunWith(ParameterizedRunner.class)
 @UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
-@CommandLineFlags.
-Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "enable-webvr", "enable-gamepad-extensions"})
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "enable-webvr"})
 @MinAndroidSdkLevel(Build.VERSION_CODES.KITKAT) // WebVR and WebXR are only supported on K+
 public class WebVrInputTest {
     @ClassParameter
@@ -255,6 +254,18 @@
         });
     }
 
+    private void spamScreenTaps(final View view, final int x, final int y, final int iterations) {
+        // Tap the screen a bunch of times.
+        // Android doesn't seem to like sending touch events too quickly, so have a short delay
+        // between events.
+        for (int i = 0; i < iterations; i++) {
+            long downTime = sendScreenTouchDown(view, x, y);
+            SystemClock.sleep(100);
+            sendScreenTouchUp(view, x, y, downTime);
+            SystemClock.sleep(100);
+        }
+    }
+
     /**
      * Tests that screen touches are still registered when the viewer is Cardboard.
      */
@@ -314,14 +325,7 @@
                                               .getPresentationViewForTesting();
 
         // Tap the screen a bunch of times and make sure that they're all registered.
-        // Android doesn't seem to like sending touch events too quickly, so have a short delay
-        // between events.
-        for (int i = 0; i < numIterations; i++) {
-            long downTime = sendScreenTouchDown(presentationView, x, y);
-            SystemClock.sleep(100);
-            sendScreenTouchUp(presentationView, x, y, downTime);
-            SystemClock.sleep(100);
-        }
+        spamScreenTaps(presentationView, x, y, numIterations);
 
         XrTestFramework.waitOnJavaScriptStep(mXrTestFramework.getFirstTabWebContents());
         XrTestFramework.endTest(mXrTestFramework.getFirstTabWebContents());
@@ -628,4 +632,139 @@
         controller.pressReleaseAppButton();
         assertAppButtonEffect(true /* shouldHaveExited */, framework);
     }
+
+    /**
+     * Verifies that a Gamepad API gamepad is not returned when using WebXR and a Daydream View if
+     * WebXRGamepadSupport is not explicitly enabled. Correctness testing for
+     * https://crbug.com/830935.
+     */
+    @Test
+    @MediumTest
+    @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM)
+    @CommandLineFlags.Remove({"enable-webvr"})
+    @CommandLineFlags.Add({"enable-features=WebXR"})
+    @VrActivityRestriction({VrActivityRestriction.SupportedActivity.ALL})
+    public void testWebXrGamepadNotReturnedWithoutGamepadSupport() throws InterruptedException {
+        webxrGamepadSupportImpl(
+                0 /* numExpectedGamepads */, true /* webxrPresent */, true /* daydream */);
+    }
+
+    /**
+     * Verifies that a Gamepad API gamepad is not returned when using WebXR and Cardboard if
+     * WebXRGamepadSupport is not explicitly enabled. Correctness testing for
+     * https://crbug.com/830935.
+     */
+    @Test
+    @MediumTest
+    @Restriction(RESTRICTION_TYPE_VIEWER_NON_DAYDREAM)
+    @CommandLineFlags.Remove({"enable-webvr"})
+    @CommandLineFlags.Add({"enable-features=WebXR"})
+    @VrActivityRestriction({VrActivityRestriction.SupportedActivity.ALL})
+    public void testWebXrGamepadNotReturnedWithoutGamepadSupport_Cardboard()
+            throws InterruptedException {
+        webxrGamepadSupportImpl(
+                0 /* numExpectedGamepads */, true /* webxrPresent */, false /* daydream */);
+    }
+
+    /**
+     * Verifies that a Gamepad API gamepad is returned when using WebXR  and Daydream View if
+     * WebXRGamepadSupport is explicitly enabled. Correctness testing for https://crbug.com/830935.
+     */
+    @Test
+    @MediumTest
+    @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM)
+    @CommandLineFlags.Remove({"enable-webvr"})
+    @CommandLineFlags.Add({"enable-features=WebXR,WebXRGamepadSupport"})
+    @VrActivityRestriction({VrActivityRestriction.SupportedActivity.ALL})
+    public void testWebXrGamepadReturnedWithGamepadSupport() throws InterruptedException {
+        webxrGamepadSupportImpl(
+                1 /* numExpectedGamepads */, true /* webxrPresent */, true /* daydream */);
+    }
+
+    /**
+     * Verifies that a Gamepad API gamepad is returned when using WebXR and Cardboard if
+     * WebXRGamepadSupport is explicitly enabled. Correctness testing for https://crbug.com/830935.
+     */
+    @Test
+    @MediumTest
+    @Restriction(RESTRICTION_TYPE_VIEWER_NON_DAYDREAM)
+    @CommandLineFlags.Remove({"enable-webvr"})
+    @CommandLineFlags.Add({"enable-features=WebXR,WebXRGamepadSupport"})
+    @VrActivityRestriction({VrActivityRestriction.SupportedActivity.ALL})
+    public void testWebXrGamepadReturnedWithGamepadSupport_Cardboard()
+            throws InterruptedException {
+        webxrGamepadSupportImpl(
+                1 /* numExpectedGamepads */, true /* webxrPresent */, false /* daydream */);
+    }
+
+    /**
+     * Verifies that a Gamepad API gamepad is not returned when not using WebXR, WebVR, or the
+     * WebXRGamepadSupport feature with Daydream View. Correctness testing for
+     * https://crbug.com/830935.
+     */
+    @Test
+    @MediumTest
+    @Restriction(RESTRICTION_TYPE_VIEWER_DAYDREAM)
+    @CommandLineFlags.Remove({"enable-webvr"})
+    @VrActivityRestriction({VrActivityRestriction.SupportedActivity.ALL})
+    public void testWebXrGamepadNotReturnedWithoutAnyFeatures() throws InterruptedException {
+        webxrGamepadSupportImpl(
+                0 /* numExpectedGamepads */, false /* webxrPresent */, true /* daydream */);
+    }
+
+    /**
+     * Verifies that a Gamepad API gamepad is not returned when not using WebXR, WebVR, or the
+     * WebXRGamepadSupport feature with Cardboard. Correctness testing for https://crbug.com/830935.
+     */
+    @Test
+    @MediumTest
+    @Restriction(RESTRICTION_TYPE_VIEWER_NON_DAYDREAM)
+    @CommandLineFlags.Remove({"enable-webvr"})
+    @VrActivityRestriction({VrActivityRestriction.SupportedActivity.ALL})
+    public void testWebXrGamepadNotReturnedWithoutAnyFeatures_Cardboard()
+            throws InterruptedException {
+        webxrGamepadSupportImpl(
+                0 /* numExpectedGamepads */, false /* webxrPresent */, false /* daydream */);
+    }
+
+    private void webxrGamepadSupportImpl(int numExpectedGamepads, boolean webxrPresent,
+            boolean daydream) throws InterruptedException {
+        if (webxrPresent) {
+            mXrTestFramework.loadUrlAndAwaitInitialization(
+                    XrTestFramework.getFileUrlForHtmlTestFile("test_webxr_gamepad_support"),
+                    PAGE_LOAD_TIMEOUT_S);
+            TransitionUtils.enterPresentationOrFail(mXrTestFramework);
+        } else {
+            mXrTestFramework.loadUrlAndAwaitInitialization(
+                    XrTestFramework.getFileUrlForHtmlTestFile("test_webxr_gamepad_support_nowebxr"),
+                    PAGE_LOAD_TIMEOUT_S);
+        }
+
+        // Spam input to make sure the Gamepad API registers the gamepad if it should.
+        int numIterations = 10;
+        if (daydream) {
+            EmulatedVrController controller = new EmulatedVrController(mTestRule.getActivity());
+            for (int i = 0; i < numIterations; i++) {
+                controller.performControllerClick();
+            }
+        } else {
+            int x = mXrTestFramework.getFirstTabContentView().getWidth() / 2;
+            int y = mXrTestFramework.getFirstTabContentView().getHeight() / 2;
+
+            View presentationView;
+            if (webxrPresent) {
+                presentationView = ((VrShellImpl) TestVrShellDelegate.getVrShellForTesting())
+                                           .getPresentationViewForTesting();
+            } else {
+                presentationView = mTestRule.getActivity().getWindow().getDecorView();
+            }
+
+            spamScreenTaps(presentationView, x, y, numIterations);
+        }
+
+        XrTestFramework.executeStepAndWait("stepAssertNumGamepadsMatchesExpectation("
+                        + String.valueOf(numExpectedGamepads) + ")",
+                mXrTestFramework.getFirstTabWebContents());
+        XrTestFramework.endTest(mXrTestFramework.getFirstTabWebContents());
+    }
 }
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index e7064e0..7343bd6 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-69.0.3444.0_rc-r1.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-69.0.3446.0_rc-r1.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 757f94b..7e64395 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7241,10 +7241,10 @@
         More options
       </message>
       <message name="IDS_SYNC_CONFIRMATION_UNITY_MORE_OPTIONS_TITLE" desc="Title of the sync confirmation more options dialog">
-        Sync and personalization options
+        Control sync, personalization, and more
       </message>
       <message name="IDS_SYNC_CONFIRMATION_UNITY_MORE_OPTIONS_SUBTITLE" desc="Subitle of the sync confirmation more options dialog">
-        Features related to sync and personalization are now under a single control. Turning this on may change your current settings.
+        The settings that control sync, personalization, and other Google services in Chrome have changed. This may affect your current settings.
       </message>
       <message name="IDS_SYNC_CONFIRMATION_UNITY_OPTIONS_REVIEW_SETTINGS_TITLE" desc="Title of the sync confirmation option to review settings">
         Review your settings on the next screen
@@ -7256,14 +7256,11 @@
         The data you sync to Google and the features you use will not change
       </message>
       <message name="IDS_SYNC_CONFIRMATION_UNITY_OPTIONS_USE_DEFAULT_TITLE" desc="Title of the sync confirmation option to use the default settings">
-        Yes, turn on sync and personalization
+        Turn on sync, personalization, and other Google services
       </message>
       <message name="IDS_SYNC_CONFIRMATION_UNITY_OPTIONS_USE_DEFAULT_SUBTITLE" desc="Subtitle of the sync confirmation option to use the default settings">
         You can customize this anytime in Chrome Settings
       </message>
-      <message name="IDS_SYNC_CONFIRMATION_UNITY_OPTIONS_CONFIRM_BUTTON_LABEL" desc="Label of the sync confirmation options confirm button">
-        Ok
-      </message>
       <message name="IDS_SYNC_CONFIRMATION_UNITY_OPTIONS_BACK_BUTTON_LABEL" desc="Label of the sync confirmation options button to go to the previous screen">
         Back
       </message>
@@ -9635,6 +9632,9 @@
       <message name="IDS_TOOLTIP_TAB_ALERT_STATE_USB_CONNECTED" desc="Extra tool tip text, when tab is connected to a USB device.">
         This tab is connected to a USB device.
       </message>
+      <message name="IDS_TOOLTIP_TAB_ALERT_STATE_PIP_PLAYING" desc="Extra tool tip text, when tab is playing a video in Picture-in-Picture.">
+        This tab is playing a video in Picture-in-Picture.
+      </message>
 
       <!-- Tab accessibility labels -->
       <message name="IDS_TAB_AX_LABEL_MEDIA_RECORDING_FORMAT" desc="Accessibility label text, when the tab is recording media. Example: 'Google Hangouts - Camera or microphone recording'">
@@ -9643,6 +9643,9 @@
       <message name="IDS_TAB_AX_LABEL_TAB_CAPTURING_FORMAT" desc="Accessibility label text, when the tab content is being captured and shared using screen sharing. Example: 'Google Hangouts - Tab content shared'">
         <ph name="WINDOW_TITLE">$1<ex>Google Hangouts</ex></ph> - Tab content shared
       </message>
+        <message name="IDS_TAB_AX_LABEL_PIP_PLAYING_FORMAT" desc="Accessibility label text, when the tab is playing a persistent video in Picture-in-Picture. Example: 'Google Hangouts - Video playing in Picture-in-Picture'">
+        <ph name="WINDOW_TITLE">$1<ex>Google Hangouts</ex></ph> - Video playing in Picture-in-Picture
+      </message>
       <message name="IDS_TAB_AX_LABEL_AUDIO_PLAYING_FORMAT" desc="Accessibility label text, when the tab is playing audio. Example: 'Google Play Music - Audio playing'">
         <ph name="WINDOW_TITLE">$1<ex>Google Play Music</ex></ph> - Audio playing
       </message>
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index b931900..8506565 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -74,6 +74,7 @@
     "photo_camera.icon",
     "picture_in_picture_control_background.icon",
     "play_arrow.icon",
+    "picture_in_picture_alt.icon",
     "profile_switcher_outline.icon",
     "reload_touch.icon",
     "remove.icon",
diff --git a/chrome/app/vector_icons/picture_in_picture_alt.icon b/chrome/app/vector_icons/picture_in_picture_alt.icon
new file mode 100644
index 0000000..eeaa252
--- /dev/null
+++ b/chrome/app/vector_icons/picture_in_picture_alt.icon
@@ -0,0 +1,28 @@
+// Copyright 2018 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.
+
+// Set to Google Blue 600
+PATH_COLOR_ARGB, 0xFF, 0x1A, 0x73, 0xE8,
+MOVE_TO, 38, 22,
+H_LINE_TO, 22,
+R_V_LINE_TO, 11.99f,
+R_H_LINE_TO, 16,
+V_LINE_TO, 22,
+CLOSE,
+R_MOVE_TO, 8, 16,
+V_LINE_TO, 9.96f,
+CUBIC_TO, 46, 7.76f, 44.2f, 6, 42, 6,
+H_LINE_TO, 6,
+CUBIC_TO, 3.8f, 6, 2, 7.76f, 2, 9.96f,
+V_LINE_TO, 38,
+R_CUBIC_TO, 0, 2.2f, 1.8f, 4, 4, 4,
+R_H_LINE_TO, 36,
+R_CUBIC_TO, 2.2f, 0, 4, -1.8f, 4, -4,
+CLOSE,
+R_MOVE_TO, -4, 0.04f,
+H_LINE_TO, 6,
+V_LINE_TO, 9.94f,
+R_H_LINE_TO, 36,
+R_V_LINE_TO, 28.1f,
+CLOSE
\ No newline at end of file
diff --git a/chrome/browser/android/vr/arcore_device/arcore.h b/chrome/browser/android/vr/arcore_device/arcore.h
index 97bd97d9..0f47308 100644
--- a/chrome/browser/android/vr/arcore_device/arcore.h
+++ b/chrome/browser/android/vr/arcore_device/arcore.h
@@ -21,6 +21,8 @@
  public:
   virtual ~ARCore() = default;
 
+  // Initializes the runtime and returns whether it was successful.
+  // If successful, the runtime must be paused when this method returns.
   virtual bool Initialize() = 0;
 
   virtual void SetDisplayGeometry(
@@ -38,6 +40,9 @@
       const mojom::XRRayPtr& ray,
       const gfx::Size& image_size,
       std::vector<mojom::XRHitResultPtr>* hit_results) = 0;
+
+  virtual void Pause() = 0;
+  virtual void Resume() = 0;
 };
 
 }  // namespace device
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device.cc b/chrome/browser/android/vr/arcore_device/arcore_device.cc
index 3fc2103..048643e 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_device.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_device.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/android/vr/arcore_device/arcore_gl.h"
 #include "chrome/browser/android/vr/arcore_device/arcore_gl_thread.h"
 #include "chrome/browser/android/vr/mailbox_to_surface_bridge.h"
-#include "ui/display/display.h"
 
 using base::android::JavaRef;
 
@@ -72,9 +71,38 @@
 }
 
 ARCoreDevice::~ARCoreDevice() {
-  if (arcore_gl_thread_) {
+  if (arcore_gl_thread_)
     arcore_gl_thread_->Stop();
-  }
+}
+
+void ARCoreDevice::PauseTracking() {
+  DCHECK(IsOnMainThread());
+
+  if (is_paused_)
+    return;
+
+  is_paused_ = true;
+
+  if (!is_arcore_gl_thread_initialized_)
+    return;
+
+  PostTaskToGlThread(base::BindOnce(
+      &ARCoreGl::Pause, arcore_gl_thread_->GetARCoreGl()->GetWeakPtr()));
+}
+
+void ARCoreDevice::ResumeTracking() {
+  DCHECK(IsOnMainThread());
+
+  if (!is_paused_)
+    return;
+
+  is_paused_ = false;
+
+  if (!is_arcore_gl_thread_initialized_)
+    return;
+
+  PostTaskToGlThread(base::BindOnce(
+      &ARCoreGl::Resume, arcore_gl_thread_->GetARCoreGl()->GetWeakPtr()));
 }
 
 void ARCoreDevice::OnMailboxBridgeReady() {
@@ -91,17 +119,19 @@
 }
 
 void ARCoreDevice::OnARCoreGlThreadInitialized(bool success) {
+  DCHECK(IsOnMainThread());
+
   if (!success) {
     DLOG(ERROR) << "Failed to initialize ARCoreDevice/GL system!";
     return;
   }
 
   is_arcore_gl_thread_initialized_ = true;
-}
 
-void ARCoreDevice::PostTaskToGlThread(base::OnceClosure task) {
-  arcore_gl_thread_->GetARCoreGl()->GetGlThreadTaskRunner()->PostTask(
-      FROM_HERE, std::move(task));
+  if (!is_paused_) {
+    PostTaskToGlThread(base::BindOnce(
+        &ARCoreGl::Resume, arcore_gl_thread_->GetARCoreGl()->GetWeakPtr()));
+  }
 }
 
 void ARCoreDevice::RequestSession(
@@ -111,6 +141,10 @@
   std::move(callback).Run(true);
 }
 
+bool ARCoreDevice::ShouldPauseAndResumeOnFocusChange() {
+  return true;
+}
+
 void ARCoreDevice::OnMagicWindowFrameDataRequest(
     const gfx::Size& frame_size,
     display::Display::Rotation display_rotation,
@@ -118,13 +152,11 @@
   TRACE_EVENT0("gpu", __FUNCTION__);
   DCHECK(IsOnMainThread());
 
-  // Check if ARCoreGl is ready.
-  // TODO(https://crbug.com/837944): Delay callback until ready.
-  if (!is_arcore_gl_thread_initialized_) {
-    // It is not safe to access arcore_gl_thread_->GetARCoreGl() until we are
-    // sure it has finished initializing / writing to that member variable.
-    // is_initialized_ is set by a callback we pass to the ARCoreGlThread
-    // constructor that is then run back here on the main thread.
+  // TODO(ijamardo): Do we need to queue requests to avoid breaking
+  // applications?
+  // TODO(https://crbug.com/837944): Ensure is_arcore_gl_thread_initialized_
+  // is always true by blocking requestDevice()'s callback until it is true
+  if (is_paused_ || !is_arcore_gl_thread_initialized_) {
     std::move(callback).Run(nullptr);
     return;
   }
@@ -145,6 +177,12 @@
       std::move(ray), CreateMainThreadCallback(std::move(callback))));
 }
 
+void ARCoreDevice::PostTaskToGlThread(base::OnceClosure task) {
+  DCHECK(IsOnMainThread());
+  arcore_gl_thread_->GetARCoreGl()->GetGlThreadTaskRunner()->PostTask(
+      FROM_HERE, std::move(task));
+}
+
 bool ARCoreDevice::IsOnMainThread() {
   return main_thread_task_runner_->BelongsToCurrentThread();
 }
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device.h b/chrome/browser/android/vr/arcore_device/arcore_device.h
index 88640000..8f251e1 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_device.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_device.h
@@ -26,6 +26,8 @@
   ~ARCoreDevice() override;
 
   // VRDeviceBase implementation.
+  void PauseTracking() override;
+  void ResumeTracking() override;
   void RequestSession(
       VRDisplayImpl* display,
       mojom::VRDisplayHost::RequestSessionCallback callback) override;
@@ -35,13 +37,16 @@
   }
 
  private:
+  // VRDeviceBase implementation
+  bool ShouldPauseAndResumeOnFocusChange() override;
   void OnMagicWindowFrameDataRequest(
       const gfx::Size& frame_size,
-      display::Display::Rotation frame_rotation,
+      display::Display::Rotation display_rotation,
       mojom::VRMagicWindowProvider::GetFrameDataCallback callback) override;
   void RequestHitTest(
       mojom::XRRayPtr ray,
       mojom::VRMagicWindowProvider::RequestHitTestCallback callback) override;
+
   void OnMailboxBridgeReady();
   void OnARCoreGlThreadInitialized(bool success);
 
@@ -63,12 +68,19 @@
   void PostTaskToGlThread(base::OnceClosure task);
 
   bool IsOnMainThread();
+
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
   std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge_;
   std::unique_ptr<ARCoreGlThread> arcore_gl_thread_;
 
   bool is_arcore_gl_thread_initialized_ = false;
 
+  // This object is not paused when it is created. Although it is not
+  // necessarily running during initialization, it is not paused. If it is
+  // paused before initialization completes, then the underlying runtime will
+  // not be resumed.
+  bool is_paused_ = false;
+
   // Must be last.
   base::WeakPtrFactory<ARCoreDevice> weak_ptr_factory_;
   DISALLOW_COPY_AND_ASSIGN(ARCoreDevice);
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.cc b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
index c62643ac..74b80c6 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
@@ -128,7 +128,6 @@
 
   if (!arcore_->Initialize()) {
     DLOG(ERROR) << "ARCore failed to initialize";
-
     return false;
   }
 
@@ -136,6 +135,7 @@
     DLOG(ERROR) << "ARImageTransport failed to initialize";
     return false;
   }
+
   // Set the texture on ARCore to render the camera.
   arcore_->SetCameraTexture(ar_image_transport_->GetCameraTextureId());
 
@@ -264,6 +264,18 @@
   std::move(callback).Run(std::move(frame_data));
 }
 
+void ARCoreGl::Pause() {
+  DCHECK(IsOnGlThread());
+  DCHECK(is_initialized_);
+  arcore_->Pause();
+}
+
+void ARCoreGl::Resume() {
+  DCHECK(IsOnGlThread());
+  DCHECK(is_initialized_);
+  arcore_->Resume();
+}
+
 bool ARCoreGl::IsOnGlThread() const {
   return gl_thread_task_runner_->BelongsToCurrentThread();
 }
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.h b/chrome/browser/android/vr/arcore_device/arcore_gl.h
index 34c74a4..da9e5d4 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_gl.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_gl.h
@@ -51,6 +51,8 @@
   void ProduceFrame(const gfx::Size& frame_size,
                     display::Display::Rotation display_rotation,
                     mojom::VRMagicWindowProvider::GetFrameDataCallback);
+  void Pause();
+  void Resume();
 
   const scoped_refptr<base::SingleThreadTaskRunner>& GetGlThreadTaskRunner() {
     return gl_thread_task_runner_;
diff --git a/chrome/browser/android/vr/arcore_device/arcore_impl.cc b/chrome/browser/android/vr/arcore_device/arcore_impl.cc
index 85e0ee9..3a505091 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_impl.cc
+++ b/chrome/browser/android/vr/arcore_device/arcore_impl.cc
@@ -83,13 +83,16 @@
     return false;
   }
 
-  ArFrame_create(session.get(), arcore_frame_.receive());
-  if (!arcore_frame_.is_valid()) {
+  internal::ScopedArCoreObject<ArFrame*> frame;
+
+  ArFrame_create(session.get(), frame.receive());
+  if (!frame.is_valid()) {
     DLOG(ERROR) << "ArFrame_create failed";
     return false;
   }
 
-  // Success, we now have a valid session.
+  // Success, we now have a valid session and a valid frame.
+  arcore_frame_ = std::move(frame);
   arcore_session_ = std::move(session);
   return true;
 }
@@ -131,14 +134,6 @@
   DCHECK(arcore_frame_.is_valid());
 
   ArStatus status;
-  if (!is_tracking_) {
-    status = ArSession_resume(arcore_session_.get());
-    if (status != AR_SUCCESS) {
-      DLOG(ERROR) << "ArSession_resume failed: " << status;
-      return nullptr;
-    }
-    is_tracking_ = true;
-  }
 
   status = ArSession_update(arcore_session_.get(), arcore_frame_.get());
   if (status != AR_SUCCESS) {
@@ -182,6 +177,22 @@
   return pose;
 }
 
+void ARCoreImpl::Pause() {
+  DCHECK(IsOnGlThread());
+  DCHECK(arcore_session_.is_valid());
+  ArStatus status = ArSession_pause(arcore_session_.get());
+  DLOG_IF(ERROR, status != AR_SUCCESS)
+      << "ArSession_pause failed: status = " << status;
+}
+
+void ARCoreImpl::Resume() {
+  DCHECK(IsOnGlThread());
+  DCHECK(arcore_session_.is_valid());
+  ArStatus status = ArSession_resume(arcore_session_.get());
+  DLOG_IF(ERROR, status != AR_SUCCESS)
+      << "ArSession_resume failed: status = " << status;
+}
+
 gfx::Transform ARCoreImpl::GetProjectionMatrix(float near, float far) {
   DCHECK(IsOnGlThread());
   DCHECK(arcore_session_.is_valid());
diff --git a/chrome/browser/android/vr/arcore_device/arcore_impl.h b/chrome/browser/android/vr/arcore_device/arcore_impl.h
index bc06c57..dbcaa0b 100644
--- a/chrome/browser/android/vr/arcore_device/arcore_impl.h
+++ b/chrome/browser/android/vr/arcore_device/arcore_impl.h
@@ -79,6 +79,8 @@
       const base::span<const float> uvs) override;
   gfx::Transform GetProjectionMatrix(float near, float far) override;
   mojom::VRPosePtr Update() override;
+  void Pause() override;
+  void Resume() override;
 
   bool RequestHitTest(const mojom::XRRayPtr& ray,
                       const gfx::Size& image_size,
@@ -105,9 +107,6 @@
   internal::ScopedArCoreObject<ArSession*> arcore_session_;
   internal::ScopedArCoreObject<ArFrame*> arcore_frame_;
 
-  // TODO(https://crbug.com/838513): replace this with more sophisticated logic.
-  bool is_tracking_ = false;
-
   // Must be last.
   base::WeakPtrFactory<ARCoreImpl> weak_ptr_factory_;
   DISALLOW_COPY_AND_ASSIGN(ARCoreImpl);
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.cc b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
index aa2fb73..29414df 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.cc
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.cc
@@ -13,21 +13,26 @@
 
 namespace device {
 
-FakeARCore::FakeARCore() = default;
+FakeARCore::FakeARCore()
+    : gl_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
+
 FakeARCore::~FakeARCore() = default;
 
 bool FakeARCore::Initialize() {
+  DCHECK(IsOnGlThread());
   return true;
 }
 
 void FakeARCore::SetDisplayGeometry(
     const gfx::Size& frame_size,
     display::Display::Rotation display_rotation) {
+  DCHECK(IsOnGlThread());
   display_rotation_ = display_rotation;
   frame_size_ = frame_size;
 }
 
 void FakeARCore::SetCameraTexture(GLuint texture) {
+  DCHECK(IsOnGlThread());
   // We need a GL_TEXTURE_EXTERNAL_OES to be compatible with the real ARCore.
   // The content doesn't really matter, just create an AHardwareBuffer-backed
   // GLImage and bind it to the texture.
@@ -217,6 +222,7 @@
 }
 
 gfx::Transform FakeARCore::GetProjectionMatrix(float near, float far) {
+  DCHECK(IsOnGlThread());
   // Get a projection matrix matching the current screen orientation and
   // aspect. Currently, this uses a hardcoded FOV angle for the smaller screen
   // dimension, and adjusts the other angle to preserve the aspect. A better
@@ -248,6 +254,7 @@
 }
 
 mojom::VRPosePtr FakeARCore::Update() {
+  DCHECK(IsOnGlThread());
   // 1m up from the origin, neutral orientation facing forward.
   mojom::VRPosePtr pose = mojom::VRPose::New();
   pose->orientation.emplace(4);
@@ -271,4 +278,16 @@
   return false;
 }
 
+void FakeARCore::Pause() {
+  DCHECK(IsOnGlThread());
+}
+
+void FakeARCore::Resume() {
+  DCHECK(IsOnGlThread());
+}
+
+bool FakeARCore::IsOnGlThread() const {
+  return gl_thread_task_runner_->BelongsToCurrentThread();
+}
+
 }  // namespace device
diff --git a/chrome/browser/android/vr/arcore_device/fake_arcore.h b/chrome/browser/android/vr/arcore_device/fake_arcore.h
index 05f0754..4d69764 100644
--- a/chrome/browser/android/vr/arcore_device/fake_arcore.h
+++ b/chrome/browser/android/vr/arcore_device/fake_arcore.h
@@ -34,6 +34,8 @@
       const base::span<const float> uvs) override;
   gfx::Transform GetProjectionMatrix(float near, float far) override;
   mojom::VRPosePtr Update() override;
+  void Pause() override;
+  void Resume() override;
 
   bool RequestHitTest(const mojom::XRRayPtr& ray,
                       const gfx::Size& image_size,
@@ -42,6 +44,10 @@
   void SetCameraAspect(float aspect) { camera_aspect_ = aspect; }
 
  private:
+  bool IsOnGlThread() const;
+
+  scoped_refptr<base::SingleThreadTaskRunner> gl_thread_task_runner_;
+
   float camera_aspect_ = 1.0f;
   display::Display::Rotation display_rotation_ =
       display::Display::Rotation::ROTATE_0;
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index cdb97fc7..c4bc663d 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -718,6 +718,10 @@
       kCrostiniCroshBuiltinAppId);
   std::string vm_name_param = net::EscapeQueryParamValue(
       base::StringPrintf("--vm_name=%s", vm_name.c_str()), false);
+  std::string owner_id_param = net::EscapeQueryParamValue(
+      base::StringPrintf("--owner_id=%s",
+                         CryptohomeIdForProfile(profile).c_str()),
+      false);
   std::string lxd_dir =
       net::EscapeQueryParamValue("LXD_DIR=/mnt/stateful/lxd", false);
   std::string lxd_conf =
@@ -726,17 +730,10 @@
       extensions::ExtensionRegistry::Get(profile)->GetInstalledExtension(
           kCrostiniCroshBuiltinAppId);
 
-  std::vector<base::StringPiece> pieces = {vsh_crosh,
-                                           vm_name_param,
-                                           "--",
-                                           lxd_dir,
-                                           lxd_conf,
-                                           "run_container.sh",
-                                           "--container_name",
-                                           container_name,
-                                           "--user",
-                                           container_username,
-                                           "--shell"};
+  std::vector<base::StringPiece> pieces = {
+      vsh_crosh,      vm_name_param, owner_id_param,     "--",
+      lxd_dir,        lxd_conf,      "run_container.sh", "--container_name",
+      container_name, "--user",      container_username, "--shell"};
 
   GURL vsh_in_crosh_url(base::JoinString(pieces, "&args[]="));
 
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
index 550b6eb..9368299a 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
@@ -7,17 +7,21 @@
 
 #include "base/macros.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
 #include "chrome/browser/chromeos/file_manager/file_watcher.h"
 #include "chrome/browser/chromeos/file_manager/mount_test_util.h"
 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
 #include "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/cros_disks_client.h"
 #include "chromeos/disks/mock_disk_mount_manager.h"
 #include "components/drive/file_change.h"
+#include "components/prefs/pref_service.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/install_warning.h"
 #include "google_apis/drive/test_util.h"
@@ -471,3 +475,16 @@
 
   ASSERT_TRUE(RunComponentExtensionTest("file_browser/recent_test"));
 }
+
+IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, Crostini) {
+  // TODO(joelhockey): Setting prefs and features to allow crostini is not
+  // ideal.  It would be better if the crostini interface allowed for testing
+  // without such tight coupling.
+  browser()->profile()->GetPrefs()->SetBoolean(
+      crostini::prefs::kCrostiniEnabled, true);
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures(
+      {features::kCrostini, features::kExperimentalCrostiniUI}, {});
+
+  ASSERT_TRUE(RunComponentExtensionTest("file_browser/crostini_test"));
+}
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
index ff0e42ad..f6c3b4fc 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -9,8 +9,11 @@
 #include <set>
 #include <utility>
 
+#include "base/base64.h"
 #include "base/command_line.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/strcat.h"
+#include "base/task_scheduler/post_task.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
 #include "chrome/browser/chromeos/drive/file_system_util.h"
@@ -29,7 +32,6 @@
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/common/extensions/api/file_manager_private_internal.h"
 #include "chromeos/chromeos_switches.h"
-#include "chromeos/components/drivefs/mojom/drivefs.mojom.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state_handler.h"
 #include "components/drive/drive_app_registry.h"
@@ -558,13 +560,15 @@
 
   // Creates an instance and starts the process.
   static void Start(base::FilePath local_path,
+                    bool want_thumbnail,
                     Profile* const profile,
                     ResultCallback callback) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     SingleEntryPropertiesGetterForDriveFs* instance =
         new SingleEntryPropertiesGetterForDriveFs(std::move(local_path),
-                                                  profile, std::move(callback));
+                                                  want_thumbnail, profile,
+                                                  std::move(callback));
     instance->StartProcess();
 
     // The instance will be destroyed by itself.
@@ -572,10 +576,12 @@
 
  private:
   SingleEntryPropertiesGetterForDriveFs(base::FilePath local_path,
+                                        bool want_thumbnail,
                                         Profile* const profile,
                                         ResultCallback callback)
       : callback_(std::move(callback)),
         local_path_(std::move(local_path)),
+        want_thumbnail_(want_thumbnail),
         running_profile_(profile),
         properties_(std::make_unique<EntryProperties>()),
         weak_ptr_factory_(this) {
@@ -604,7 +610,7 @@
     }
 
     drivefs_interface->GetMetadata(
-        local_path_,
+        local_path_, want_thumbnail_,
         mojo::WrapCallbackWithDefaultInvokeIfNotRun(
             base::BindOnce(
                 &SingleEntryPropertiesGetterForDriveFs::OnGetFileInfo,
@@ -669,6 +675,34 @@
             std::make_unique<int32_t>(metadata->image_metadata->rotation);
       }
     }
+    if (metadata->thumbnail) {
+      base::PostTaskAndReplyWithResult(
+          FROM_HERE,
+          base::BindOnce(&SingleEntryPropertiesGetterForDriveFs::
+                             MakeThumbnailDataUrlOnSequence,
+                         std::move(*metadata->thumbnail)),
+          base::BindOnce(
+              &SingleEntryPropertiesGetterForDriveFs::SetThumbnailAndComplete,
+              weak_ptr_factory_.GetWeakPtr()));
+      return;
+    }
+
+    CompleteGetEntryProperties(drive::FILE_ERROR_OK);
+  }
+
+  static std::string MakeThumbnailDataUrlOnSequence(
+      const std::vector<uint8_t>& png_data) {
+    std::string encoded;
+    base::Base64Encode(
+        base::StringPiece(reinterpret_cast<const char*>(png_data.data()),
+                          png_data.size()),
+        &encoded);
+    return base::StrCat({"data:image/png;base64,", encoded});
+  }
+
+  void SetThumbnailAndComplete(std::string thumbnail_data_url) {
+    properties_->thumbnail_url =
+        std::make_unique<std::string>(std::move(thumbnail_data_url));
 
     CompleteGetEntryProperties(drive::FILE_ERROR_OK);
   }
@@ -685,6 +719,7 @@
   // Given parameters.
   ResultCallback callback_;
   const base::FilePath local_path_;
+  const bool want_thumbnail_;
   Profile* const running_profile_;
 
   // Values used in the process.
@@ -741,7 +776,10 @@
         break;
       case storage::kFileSystemTypeNativeLocal:
         SingleEntryPropertiesGetterForDriveFs::Start(
-            file_system_url.path(), GetProfile(),
+            file_system_url.path(),
+            names_as_set.count(
+                api::file_manager_private::ENTRY_PROPERTY_NAME_THUMBNAILURL),
+            GetProfile(),
             base::BindOnce(
                 &FileManagerPrivateInternalGetEntryPropertiesFunction::
                     CompleteGetEntryProperties,
@@ -798,56 +836,68 @@
       file_system_context->CrackURL(url);
 
   switch (file_system_url.type()) {
-    case storage::kFileSystemTypeDrive: {
-      drive::FileSystemInterface* const file_system =
-          drive::util::GetFileSystemByProfile(GetProfile());
-      if (!file_system)  // |file_system| is NULL if Drive is disabled.
-        return false;
+    case storage::kFileSystemTypeDrive:
+      return RunAsyncForDrive(url, params->pin);
 
-      const base::FilePath drive_path =
-          drive::util::ExtractDrivePath(file_manager::util::GetLocalPathFromURL(
-              render_frame_host(), GetProfile(), url));
-      if (params->pin) {
-        file_system->Pin(
-            drive_path,
-            base::Bind(
-                &FileManagerPrivateInternalPinDriveFileFunction::OnPinStateSet,
-                this));
-      } else {
-        file_system->Unpin(
-            drive_path,
-            base::Bind(
-                &FileManagerPrivateInternalPinDriveFileFunction::OnPinStateSet,
-                this));
-      }
-      return true;
-    }
-    case storage::kFileSystemTypeNativeLocal: {
-      drive::DriveIntegrationService* integration_service =
-          drive::DriveIntegrationServiceFactory::FindForProfile(GetProfile());
-      if (!integration_service || !integration_service->IsMounted() ||
-          !integration_service->GetMountPointPath().IsParent(
-              file_system_url.path())) {
-        return false;
-      }
+    case storage::kFileSystemTypeNativeLocal:
+      return RunAsyncForDriveFs(file_system_url, params->pin);
 
-      auto* drivefs_interface = integration_service->GetDriveFsInterface();
-      if (!drivefs_interface)
-        return false;
-
-      drivefs_interface->SetPinned(
-          file_system_url.path(), params->pin,
-          mojo::WrapCallbackWithDefaultInvokeIfNotRun(
-              base::BindOnce(&FileManagerPrivateInternalPinDriveFileFunction::
-                                 OnPinStateSet,
-                             this),
-              drive::FILE_ERROR_SERVICE_UNAVAILABLE));
-      return true;
-    }
-    default: { return false; }
+    default:
+      return false;
   }
 }
 
+bool FileManagerPrivateInternalPinDriveFileFunction::RunAsyncForDrive(
+    const GURL& url,
+    bool pin) {
+  drive::FileSystemInterface* const file_system =
+      drive::util::GetFileSystemByProfile(GetProfile());
+  if (!file_system)  // |file_system| is NULL if Drive is disabled.
+    return false;
+
+  const base::FilePath drive_path =
+      drive::util::ExtractDrivePath(file_manager::util::GetLocalPathFromURL(
+          render_frame_host(), GetProfile(), url));
+  if (pin) {
+    file_system->Pin(
+        drive_path,
+        base::Bind(
+            &FileManagerPrivateInternalPinDriveFileFunction::OnPinStateSet,
+            this));
+  } else {
+    file_system->Unpin(
+        drive_path,
+        base::Bind(
+            &FileManagerPrivateInternalPinDriveFileFunction::OnPinStateSet,
+            this));
+  }
+  return true;
+}
+
+bool FileManagerPrivateInternalPinDriveFileFunction::RunAsyncForDriveFs(
+    const storage::FileSystemURL& file_system_url,
+    bool pin) {
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::FindForProfile(GetProfile());
+  if (!integration_service || !integration_service->IsMounted() ||
+      !integration_service->GetMountPointPath().IsParent(
+          file_system_url.path())) {
+    return false;
+  }
+
+  auto* drivefs_interface = integration_service->GetDriveFsInterface();
+  if (!drivefs_interface)
+    return false;
+
+  drivefs_interface->SetPinned(
+      file_system_url.path(), pin,
+      mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+          base::BindOnce(
+              &FileManagerPrivateInternalPinDriveFileFunction::OnPinStateSet,
+              this),
+          drive::FILE_ERROR_SERVICE_UNAVAILABLE));
+  return true;
+}
 void FileManagerPrivateInternalPinDriveFileFunction::OnPinStateSet(
     drive::FileError error) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -1325,6 +1375,25 @@
   const std::unique_ptr<Params> params(Params::Create(*args_));
   EXTENSION_FUNCTION_VALIDATE(params);
 
+  scoped_refptr<storage::FileSystemContext> file_system_context =
+      file_manager::util::GetFileSystemContextForRenderFrameHost(
+          GetProfile(), render_frame_host());
+  const GURL url = GURL(params->url);
+  const storage::FileSystemURL file_system_url =
+      file_system_context->CrackURL(url);
+
+  switch (file_system_url.type()) {
+    case storage::kFileSystemTypeDrive:
+      return RunAsyncForDrive(url);
+    case storage::kFileSystemTypeNativeLocal:
+      return RunAsyncForDriveFs(file_system_url);
+    default:
+      return false;
+  }
+}
+
+bool FileManagerPrivateInternalGetDownloadUrlFunction::RunAsyncForDrive(
+    const GURL& url) {
   // Start getting the file info.
   drive::FileSystemInterface* const file_system =
       drive::util::GetFileSystemByProfile(GetProfile());
@@ -1337,7 +1406,7 @@
   }
 
   const base::FilePath path = file_manager::util::GetLocalPathFromURL(
-      render_frame_host(), GetProfile(), GURL(params->url));
+      render_frame_host(), GetProfile(), url);
   if (!drive::util::IsUnderDriveMountPoint(path)) {
     SetError("The given file is not in Drive.");
     // Intentionally returns a blank.
@@ -1360,20 +1429,27 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   if (error != drive::FILE_ERROR_OK) {
+    OnGotDownloadUrl(GURL());
+    return;
+  }
+
+  DriveApiUrlGenerator url_generator(
+      (GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction)),
+      (GURL(google_apis::DriveApiUrlGenerator::kBaseThumbnailUrlForProduction)),
+      google_apis::GetTeamDrivesIntegrationSwitch());
+  OnGotDownloadUrl(url_generator.GenerateDownloadFileUrl(entry->resource_id()));
+}
+
+void FileManagerPrivateInternalGetDownloadUrlFunction::OnGotDownloadUrl(
+    GURL download_url) {
+  if (download_url.is_empty()) {
     SetError("Download Url for this item is not available.");
     // Intentionally returns a blank.
     SetResult(std::make_unique<base::Value>(std::string()));
     SendResponse(false);
     return;
   }
-
-  DriveApiUrlGenerator url_generator(
-      (GURL(google_apis::DriveApiUrlGenerator::kBaseUrlForProduction)),
-      (GURL(
-          google_apis::DriveApiUrlGenerator::kBaseThumbnailUrlForProduction)),
-      google_apis::GetTeamDrivesIntegrationSwitch());
-  download_url_ = url_generator.GenerateDownloadFileUrl(entry->resource_id());
-
+  download_url_ = std::move(download_url);
   ProfileOAuth2TokenService* oauth2_token_service =
       ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
   SigninManagerBase* signin_manager =
@@ -1402,11 +1478,40 @@
     return;
   }
 
-  const std::string url =
-      download_url_.Resolve("?alt=media&access_token=" + access_token).spec();
-  SetResult(std::make_unique<base::Value>(url));
+  SetResult(std::make_unique<base::Value>(
+      download_url_.Resolve("?alt=media&access_token=" + access_token).spec()));
 
   SendResponse(true);
 }
 
+bool FileManagerPrivateInternalGetDownloadUrlFunction::RunAsyncForDriveFs(
+    const storage::FileSystemURL& file_system_url) {
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::FindForProfile(GetProfile());
+  if (!integration_service || !integration_service->IsMounted() ||
+      !integration_service->GetMountPointPath().IsParent(
+          file_system_url.path())) {
+    return false;
+  }
+
+  auto* drivefs_interface = integration_service->GetDriveFsInterface();
+  if (!drivefs_interface)
+    return false;
+
+  drivefs_interface->GetMetadata(
+      file_system_url.path(), false,
+      mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+          base::BindOnce(
+              &FileManagerPrivateInternalGetDownloadUrlFunction::OnGotMetadata,
+              this),
+          drive::FILE_ERROR_SERVICE_UNAVAILABLE, nullptr));
+  return true;
+}
+
+void FileManagerPrivateInternalGetDownloadUrlFunction::OnGotMetadata(
+    drive::FileError error,
+    drivefs::mojom::FileMetadataPtr metadata) {
+  OnGotDownloadUrl(metadata ? GURL(metadata->download_url) : GURL());
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
index 3641869..c488ce2 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
@@ -16,6 +16,7 @@
 #include "base/files/file.h"
 #include "chrome/browser/chromeos/extensions/file_manager/private_api_base.h"
 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
+#include "chromeos/components/drivefs/mojom/drivefs.mojom.h"
 #include "components/drive/chromeos/file_system_interface.h"
 #include "components/drive/file_errors.h"
 
@@ -98,7 +99,11 @@
   bool RunAsync() override;
 
  private:
-  // Callback for RunAsync().
+  bool RunAsyncForDrive(const GURL& url, bool pin);
+  bool RunAsyncForDriveFs(const storage::FileSystemURL& file_system_url,
+                          bool pin);
+
+  // Callback for RunAsyncForDrive() and RunAsyncForDriveFs.
   void OnPinStateSet(drive::FileError error);
 };
 
@@ -268,14 +273,23 @@
   // ChromeAsyncExtensionFunction overrides.
   bool RunAsync() override;
 
+ private:
+  bool RunAsyncForDrive(const GURL& url);
+
   void OnGetResourceEntry(drive::FileError error,
                           std::unique_ptr<drive::ResourceEntry> entry);
 
+  void OnGotDownloadUrl(GURL download_url);
+
   // Callback with an |access_token|, called by
   // drive::DriveReadonlyTokenFetcher.
   void OnTokenFetched(google_apis::DriveApiErrorCode code,
                       const std::string& access_token);
 
+  bool RunAsyncForDriveFs(const storage::FileSystemURL& file_system_url);
+  void OnGotMetadata(drive::FileError error,
+                     drivefs::mojom::FileMetadataPtr metadata);
+
  private:
   GURL download_url_;
   std::unique_ptr<google_apis::AuthService> auth_service_;
diff --git a/chrome/browser/chromeos/file_manager/open_with_browser.cc b/chrome/browser/chromeos/file_manager/open_with_browser.cc
index f35379bd..b19f340 100644
--- a/chrome/browser/chromeos/file_manager/open_with_browser.cc
+++ b/chrome/browser/chromeos/file_manager/open_with_browser.cc
@@ -193,7 +193,7 @@
           integration_service->GetDriveFsInterface() &&
           integration_service->GetMountPointPath().IsParent(file_path)) {
         integration_service->GetDriveFsInterface()->GetMetadata(
-            file_path,
+            file_path, false,
             base::BindOnce(&OpenHostedDriveFsFile, file_path, profile));
         return true;
       }
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
index c5bfee6..de1f576 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl.cc
@@ -502,8 +502,8 @@
   // happens after activating the 3rd party IME.
   // So here to record the 3rd party IME to be activated, and activate it
   // when SetEnabledExtensionImes happens later.
-  if (MethodAwaitsExtensionLoad(descriptor->id()))
-    pending_input_method_id = descriptor->id();
+  if (MethodAwaitsExtensionLoad(input_method_id))
+    pending_input_method_id = input_method_id;
 
   if (descriptor->id() != current_input_method.id()) {
     previous_input_method = current_input_method;
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index 73b8d262..dc60144 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -1082,17 +1082,6 @@
 }
 
 void UserSessionManager::OnUserNetworkPolicyParsed(bool send_password) {
-  // Sanity check that we only send the password for enterprise users. See
-  // https://crbug.com/386606.
-  const bool is_enterprise_managed = g_browser_process->platform_part()
-                                         ->browser_policy_connector_chromeos()
-                                         ->IsEnterpriseManaged();
-  if (!is_enterprise_managed) {
-    LOG(WARNING) << "Attempting to save user password for non enterprise user.";
-    user_context_.GetMutablePasswordKey()->ClearSecret();
-    return;
-  }
-
   if (send_password) {
     if (user_context_.GetPasswordKey()->GetSecret().size() > 0) {
       DBusThreadManager::Get()->GetSessionManagerClient()->SaveLoginPassword(
diff --git a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc b/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc
index 165ea02..c7f8ce4 100644
--- a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc
+++ b/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc
@@ -31,7 +31,7 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/user_activity/user_activity_detector.h"
-#include "ui/events/base_event_utils.cc"
+#include "ui/events/base_event_utils.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/gfx/geometry/point.h"
 
diff --git a/chrome/browser/content_settings/host_content_settings_map_factory.cc b/chrome/browser/content_settings/host_content_settings_map_factory.cc
index 8a2ebce..eae14e50 100644
--- a/chrome/browser/content_settings/host_content_settings_map_factory.cc
+++ b/chrome/browser/content_settings/host_content_settings_map_factory.cc
@@ -89,7 +89,8 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   // These must be registered before before the HostSettings are passed over to
   // the IOThread.  Simplest to do this on construction.
-  ExtensionService::RegisterContentSettings(settings_map.get(), profile);
+  extensions::ExtensionService::RegisterContentSettings(settings_map.get(),
+                                                        profile);
 #endif // BUILDFLAG(ENABLE_EXTENSIONS)
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
   SupervisedUserSettingsService* supervised_service =
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index 8f7580f..00f02e1 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <memory>
 #include <set>
+#include <utility>
 #include <vector>
 
 #include "base/files/file_util.h"
@@ -16,6 +17,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/path_service.h"
 #include "base/rand_util.h"
+#include "base/synchronization/lock.h"
 #include "base/test/histogram_tester.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/values.h"
@@ -33,11 +35,14 @@
 #include "components/prefs/pref_service.h"
 #include "components/proxy_config/proxy_config_dictionary.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/simple_url_loader_test_helper.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/api/declarative_net_request/constants.h"
 #include "extensions/browser/api/declarative_net_request/ruleset_manager.h"
@@ -54,10 +59,16 @@
 #include "extensions/common/extension_id.h"
 #include "extensions/common/url_pattern.h"
 #include "extensions/test/extension_test_message_listener.h"
+#include "ipc/ipc_message.h"
+#include "net/base/net_errors.h"
 #include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/http_request.h"
 #include "net/test/spawned_test_server/spawned_test_server.h"
 #include "net/test/test_data_directory.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "services/network/public/cpp/features.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 
 namespace extensions {
 namespace declarative_net_request {
@@ -128,6 +139,10 @@
                          .AppendASCII("declarative_net_request");
     embedded_test_server()->ServeFilesFromDirectory(test_root_path);
 
+    embedded_test_server()->RegisterRequestMonitor(
+        base::BindRepeating(&DeclarativeNetRequestBrowserTest::MonitorRequest,
+                            base::Unretained(this)));
+
     ASSERT_TRUE(embedded_test_server()->Start());
 
     // Map all hosts to localhost.
@@ -290,7 +305,21 @@
     EXPECT_EQ(expected_patterns, patterns);
   }
 
+  std::set<GURL> GetAndResetRequestsToServer() {
+    base::AutoLock lock(requests_to_server_lock_);
+    std::set<GURL> results = requests_to_server_;
+    requests_to_server_.clear();
+    return results;
+  }
+
  private:
+  // Handler to monitor the requests which reach the EmbeddedTestServer. This
+  // will be run on the EmbeddedTestServer's IO thread.
+  void MonitorRequest(const net::test_server::HttpRequest& request) {
+    base::AutoLock lock(requests_to_server_lock_);
+    requests_to_server_.insert(request.GetURL());
+  }
+
   void UpdateWhitelistedPages(const ExtensionId& extension_id,
                               const std::vector<std::string>& patterns,
                               const std::string& function_name) {
@@ -317,6 +346,13 @@
   bool has_background_script_ = false;
   std::unique_ptr<ExtensionTestMessageListener> background_page_ready_listener_;
 
+  // Requests observed by the EmbeddedTestServer. This is accessed on both the
+  // UI and the EmbeddedTestServer's IO thread. Access is protected by
+  // |requests_to_server_lock_|.
+  std::set<GURL> requests_to_server_;
+
+  base::Lock requests_to_server_lock_;
+
   DISALLOW_COPY_AND_ASSIGN(DeclarativeNetRequestBrowserTest);
 };
 
@@ -1226,9 +1262,9 @@
   EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
 }
 
-// Tests the pages whitelisting API.
+// Tests the page whitelisting API.
 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,
-                       PRE_PageWhitelistingAPI) {
+                       PRE_PageWhitelistingAPI_PersistedAcrossSessions) {
   // This is not tested for unpacked extensions since the unpacked extension
   // directory won't be persisted across browser sessions.
   ASSERT_EQ(ExtensionLoadType::PACKED, GetParam());
@@ -1253,7 +1289,7 @@
 // Tests that the pages whitelisted using the page whitelisting API are
 // persisted across browser sessions.
 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed,
-                       PageWhitelistingAPI) {
+                       PageWhitelistingAPI_PersistedAcrossSessions) {
   // This is not tested for unpacked extensions since the unpacked extension
   // directory won't be persisted across browser sessions.
   ASSERT_EQ(ExtensionLoadType::PACKED, GetParam());
@@ -1283,6 +1319,238 @@
   VerifyGetWhitelistedPages(dnr_extension->id(), {});
 }
 
+// Tests that the page whitelisting API performs pattern matching correctly.
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
+                       PageWhitelistingAPI_Patterns) {
+  // Load an extension which blocks requests to "script.js".
+  set_has_background_script(true);
+  std::vector<TestRule> rules;
+  TestRule rule = CreateGenericRule();
+  rule.condition->url_filter = std::string("script.js");
+  rules.push_back(rule);
+  ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));
+
+  // We'll be whitelisting these patterns subsequently.
+  const std::vector<std::string> whitelisted_page_patterns = {
+      "http://google.com:*/pages_with_script/page*.html",
+      "http://*/*index.html",
+      "http://example.com:*/pages_with_script/page2.html"};
+
+  struct TestCase {
+    std::string hostname;
+    std::string path;
+    bool expect_script_whitelisted_with_rules;
+  } test_cases[] = {
+      {"yahoo.com", "/pages_with_script/page.html", false},
+      {"yahoo.com", "/pages_with_script/index.html", true},
+      {"example.com", "/pages_with_script/page.html", false},
+      {"example.com", "/pages_with_script/page2.html", true},
+      {"google.com", "/pages_with_script/page.html", true},
+      {"google.com", "/pages_with_script/page2.html", true},
+  };
+
+  const GURL script_url =
+      embedded_test_server()->GetURL("/pages_with_script/script.js");
+  auto verify_script_load = [this, script_url](bool expect_script_load) {
+    // The page should have loaded correctly.
+    EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
+    EXPECT_EQ(expect_script_load, WasFrameWithScriptLoaded(GetMainFrame()));
+
+    // The EmbeddedTestServer sees requests after the hostname has been
+    // resolved.
+    bool did_see_script_request =
+        base::ContainsKey(GetAndResetRequestsToServer(), script_url);
+    EXPECT_EQ(expect_script_load, did_see_script_request);
+  };
+
+  // "script.js" should have been blocked for all pages initially.
+  for (const auto& test_case : test_cases) {
+    GURL url =
+        embedded_test_server()->GetURL(test_case.hostname, test_case.path);
+    SCOPED_TRACE(base::StringPrintf(
+        "Testing %s with no page whitelisting rules", url.spec().c_str()));
+
+    ui_test_utils::NavigateToURL(browser(), url);
+
+    verify_script_load(false);
+  }
+
+  // Now whitelist |whitelisted_page_patterns| and ensure the test expectations
+  // are met.
+  AddWhitelistedPages(last_loaded_extension_id(), whitelisted_page_patterns);
+  for (const auto& test_case : test_cases) {
+    GURL url =
+        embedded_test_server()->GetURL(test_case.hostname, test_case.path);
+    SCOPED_TRACE(base::StringPrintf("Testing %s with page whitelisting rules",
+                                    url.spec().c_str()));
+
+    ui_test_utils::NavigateToURL(browser(), url);
+
+    verify_script_load(test_case.expect_script_whitelisted_with_rules);
+  }
+
+  // Now remove the |whitelisted_page_patterns| and ensure that all requests to
+  // "script.js" are blocked again.
+  RemoveWhitelistedPages(last_loaded_extension_id(), whitelisted_page_patterns);
+  for (const auto& test_case : test_cases) {
+    GURL url =
+        embedded_test_server()->GetURL(test_case.hostname, test_case.path);
+    SCOPED_TRACE(base::StringPrintf(
+        "Testing %s with all page whitelisting rules removed",
+        url.spec().c_str()));
+
+    ui_test_utils::NavigateToURL(browser(), url);
+
+    verify_script_load(false);
+  }
+}
+
+// Tests the page whitelisting API for subresource requests.
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
+                       PageWhitelistingAPI_Resources) {
+  // Load an extension which blocks all requests.
+  set_has_background_script(true);
+  std::vector<TestRule> rules;
+
+  // This blocks all subresource requests. By default the main-frame resource
+  // type is excluded.
+  TestRule rule = CreateGenericRule();
+  rule.id = kMinValidID;
+  rule.condition->url_filter = std::string("*");
+  rules.push_back(rule);
+
+  // Also block main frame requests.
+  rule.id = kMinValidID + 1;
+  rule.condition->resource_types = std::vector<std::string>({"main_frame"});
+  rules.push_back(rule);
+
+  ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));
+
+  const GURL url = embedded_test_server()->GetURL("/whitelisting_api.html");
+
+  auto verify_page_load = [this](bool expect_load) {
+    EXPECT_EQ(expect_load, WasFrameWithScriptLoaded(GetMainFrame()));
+    EXPECT_EQ(
+        expect_load ? content::PAGE_TYPE_NORMAL : content::PAGE_TYPE_ERROR,
+        GetPageType());
+
+    // We will be loading "whitelisting_api.html" for the test. The following
+    // lists the set of requests we expect to see if the page is loaded.
+    static const std::vector<std::string> page_resources_path = {
+        // Main frame request.
+        "/whitelisting_api.html",
+
+        // Main frame subrources.
+        "/subresources/style.css", "/subresources/script.js",
+        "/subresources/image.png", "/subresources/ping.mp3",
+        "/child_frame_with_subresources.html",
+
+        // Child frame subresources.
+        "/iframe_subresources/style.css", "/iframe_subresources/script.js",
+        "/iframe_subresources/image.png", "/iframe_subresources/ping.mp3",
+        "/iframe_subresources/subframe.html",
+    };
+
+    const std::set<GURL> requests_seen = GetAndResetRequestsToServer();
+
+    for (const auto& path : page_resources_path) {
+      GURL expected_request_url = embedded_test_server()->GetURL(path);
+
+      // Request to |expected_requested_url| should be seen by the server iff we
+      // expect the page to load.
+      if (expect_load) {
+        EXPECT_TRUE(base::ContainsKey(requests_seen, expected_request_url))
+            << expected_request_url.spec()
+            << " was not requested from the server.";
+      } else {
+        EXPECT_FALSE(base::ContainsKey(requests_seen, expected_request_url))
+            << expected_request_url.spec() << " request seen unexpectedly.";
+      }
+    }
+  };
+
+  // Initially the request for the page should be blocked. No request should
+  // reach the server.
+  ui_test_utils::NavigateToURL(browser(), url);
+  verify_page_load(false /*expect_load*/);
+
+  // Next we whitelist |url|. All requests with |url| as the top level frame
+  // should be whitelisted.
+  AddWhitelistedPages(last_loaded_extension_id(), {url.spec()});
+
+  // Ensure that no requests made by the page load are blocked.
+  ui_test_utils::NavigateToURL(browser(), url);
+  verify_page_load(true /*expect_load*/);
+
+  // Remove |url| from whitelist.
+  RemoveWhitelistedPages(last_loaded_extension_id(), {url.spec()});
+
+  // Ensure that requests are blocked.
+  ui_test_utils::NavigateToURL(browser(), url);
+  verify_page_load(false /*expect_load*/);
+}
+
+// Tests that requests which can't be mapped to a render frame (e.g. non-
+// navigation browser requests) are not affected by the page whitelisting API.
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
+                       PageWhitelistingAPI_BrowserRequests) {
+  // Load an extension which blocks all requests.
+  set_has_background_script(true);
+  std::vector<TestRule> rules;
+  TestRule rule = CreateGenericRule();
+  rule.condition->url_filter = std::string("*");
+  rules.push_back(rule);
+  ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules(rules));
+
+  // Whitelist requests served from "google.com/*" pages.
+  AddWhitelistedPages(last_loaded_extension_id(), {"http://google.com:*/*"});
+
+  const GURL url = embedded_test_server()->GetURL(
+      "google.com", "/pages_with_script/index.html");
+
+  // A navigation to |url| should not be blocked as google.com/* pages have been
+  // whitelisted. This will cause two network requests to index.html and
+  // script.js.
+  ui_test_utils::NavigateToURL(browser(), url);
+  EXPECT_TRUE(WasFrameWithScriptLoaded(GetMainFrame()));
+  EXPECT_EQ(content::PAGE_TYPE_NORMAL, GetPageType());
+  std::set<GURL> requests_seen = GetAndResetRequestsToServer();
+
+  // The EmbeddedTestServer sees requests after the hostname has been resolved.
+  EXPECT_TRUE(base::ContainsKey(
+      requests_seen,
+      embedded_test_server()->GetURL("/pages_with_script/index.html")));
+  EXPECT_TRUE(base::ContainsKey(
+      requests_seen,
+      embedded_test_server()->GetURL("/pages_with_script/script.js")));
+
+  // But a non-navigation browser initiated resource request should still be
+  // blocked. This is because such a request can't be mapped to a top level
+  // frame and hence won't be considered for whitelisting by the page
+  // whitelisting API.
+  auto request = std::make_unique<network::ResourceRequest>();
+  request->url = embedded_test_server()->GetURL("google.com",
+                                                "/pages_with_script/script.js");
+  request->resource_type = content::ResourceType::RESOURCE_TYPE_SCRIPT;
+  request->render_frame_id = MSG_ROUTING_NONE;
+
+  auto loader = network::SimpleURLLoader::Create(std::move(request),
+                                                 TRAFFIC_ANNOTATION_FOR_TESTS);
+  content::SimpleURLLoaderTestHelper loader_helper;
+  loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      content::BrowserContext::GetDefaultStoragePartition(profile())
+          ->GetURLLoaderFactoryForBrowserProcess()
+          .get(),
+      loader_helper.GetCallback());
+  loader_helper.WaitForCallback();
+
+  EXPECT_FALSE(loader_helper.response_body());
+  EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, loader->NetError());
+  EXPECT_FALSE(base::ContainsKey(
+      GetAndResetRequestsToServer(),
+      embedded_test_server()->GetURL("/pages_with_script/script.js")));
+}
+
 // Test fixture to verify that host permissions for the request url and the
 // request initiator are properly checked. Loads an example.com url with four
 // sub-frames named frame_[1..4] from hosts frame_[1..4].com. The initiator for
diff --git a/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
index ff1ca03..97f94d8 100644
--- a/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/ruleset_manager_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/optional.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/extensions/api/declarative_net_request/dnr_test_base.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
@@ -163,7 +164,8 @@
       ASSERT_NO_FATAL_FAILURE(CreateMatcherForRules(
           {rule_one}, std::to_string(mask) + "_one", &matcher));
       extension_id_one = last_loaded_extension()->id();
-      manager->AddRuleset(extension_id_one, std::move(matcher));
+      manager->AddRuleset(extension_id_one, std::move(matcher),
+                          URLPatternSet());
     }
     if (mask & kEnableRulesetTwo) {
       ++expected_matcher_count;
@@ -171,7 +173,8 @@
       ASSERT_NO_FATAL_FAILURE(CreateMatcherForRules(
           {rule_two}, std::to_string(mask) + "_two", &matcher));
       extension_id_two = last_loaded_extension()->id();
-      manager->AddRuleset(extension_id_two, std::move(matcher));
+      manager->AddRuleset(extension_id_two, std::move(matcher),
+                          URLPatternSet());
     }
 
     ASSERT_EQ(expected_matcher_count, manager->GetMatcherCountForTest());
@@ -202,7 +205,8 @@
   std::unique_ptr<RulesetMatcher> matcher;
   ASSERT_NO_FATAL_FAILURE(
       CreateMatcherForRules({rule_one}, "test_extension", &matcher));
-  manager->AddRuleset(last_loaded_extension()->id(), std::move(matcher));
+  manager->AddRuleset(last_loaded_extension()->id(), std::move(matcher),
+                      URLPatternSet());
 
   std::unique_ptr<net::URLRequest> request =
       GetRequestForURL("http://example.com");
@@ -249,7 +253,8 @@
   ASSERT_NO_FATAL_FAILURE(
       CreateMatcherForRules({rule}, "test_extension", &matcher,
                             {"*://example.com/*", "*://abc.com/*"}));
-  manager->AddRuleset(last_loaded_extension()->id(), std::move(matcher));
+  manager->AddRuleset(last_loaded_extension()->id(), std::move(matcher),
+                      URLPatternSet());
 
   // Create a request to "example.com" with an empty initiator. It should be
   // redirected to "google.com".
@@ -311,7 +316,7 @@
         std::vector<std::string>({URLPattern::kAllUrlsPattern}),
         true /* has_background_script*/));
     extension_1 = last_loaded_extension();
-    manager->AddRuleset(extension_1->id(), std::move(matcher));
+    manager->AddRuleset(extension_1->id(), std::move(matcher), URLPatternSet());
   }
 
   // Add another extension with a background page which redirects all requests
@@ -328,7 +333,7 @@
         std::vector<std::string>({URLPattern::kAllUrlsPattern}),
         true /* has_background_script*/));
     extension_2 = last_loaded_extension();
-    manager->AddRuleset(extension_2->id(), std::move(matcher));
+    manager->AddRuleset(extension_2->id(), std::move(matcher), URLPatternSet());
   }
 
   EXPECT_EQ(2u, manager->GetMatcherCountForTest());
@@ -369,6 +374,171 @@
                               false /*is_incognito_context*/, &redirect_url));
 }
 
+TEST_P(RulesetManagerTest, PageWhitelistingAPI) {
+  RulesetManager* manager = info_map()->GetRulesetManager();
+  ASSERT_TRUE(manager);
+
+  // Add an extension which blocks all requests except for requests from
+  // http://google.com/whitelist* which are whitelisted.
+  {
+    std::unique_ptr<RulesetMatcher> matcher;
+
+    // This blocks all subresource requests. By default the main-frame resource
+    // type is excluded.
+    TestRule rule1 = CreateGenericRule();
+    rule1.id = kMinValidID;
+    rule1.condition->url_filter = std::string("*");
+
+    // Also block main frame requests.
+    TestRule rule2 = CreateGenericRule();
+    rule2.id = kMinValidID + 1;
+    rule2.condition->url_filter = std::string("*");
+    rule2.condition->resource_types = std::vector<std::string>({"main_frame"});
+
+    ASSERT_NO_FATAL_FAILURE(CreateMatcherForRules(
+        {rule1, rule2}, "test extension", &matcher,
+        std::vector<std::string>({URLPattern::kAllUrlsPattern}),
+        true /* background_script */));
+
+    URLPatternSet pattern_set(
+        {URLPattern(URLPattern::SCHEME_ALL, "http://google.com/whitelist*")});
+    manager->AddRuleset(last_loaded_extension()->id(), std::move(matcher),
+                        std::move(pattern_set));
+  }
+
+  constexpr int kDummyFrameRoutingId = 2;
+  constexpr int kDummyFrameId = 3;
+  constexpr int kDummyParentFrameId = 1;
+  constexpr int kDummyTabId = 5;
+  constexpr int kDummyWindowId = 7;
+  constexpr char kWhitelistedPageURL[] = "http://google.com/whitelist123";
+
+  struct FrameDataParams {
+    int frame_id;
+    int parent_frame_id;
+    std::string last_committed_main_frame_url;
+    base::Optional<std::string> pending_main_frame_url;
+  };
+  struct TestCase {
+    std::string url;
+    content::ResourceType type;
+    base::Optional<std::string> initiator;
+    int frame_routing_id;
+    base::Optional<FrameDataParams> frame_data_params;
+    bool expect_blocked_with_whitelist;
+  } test_cases[] = {
+      // Main frame requests. Whitelisted based on request url.
+      {kWhitelistedPageURL, content::RESOURCE_TYPE_MAIN_FRAME, base::nullopt,
+       MSG_ROUTING_NONE,
+       FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
+                        ExtensionApiFrameIdMap::kInvalidFrameId,
+                        "http://google.com/xyz", base::nullopt}),
+       false},
+      {"http://google.com/xyz", content::RESOURCE_TYPE_MAIN_FRAME,
+       base::nullopt, MSG_ROUTING_NONE,
+       FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
+                        ExtensionApiFrameIdMap::kInvalidFrameId,
+                        kWhitelistedPageURL, base::nullopt}),
+       true},
+
+      // Non-navigation browser or service worker request. Not whitelisted,
+      // since the request doesn't correspond to a frame.
+      {"http://google.com/xyz", content::RESOURCE_TYPE_SCRIPT, base::nullopt,
+       MSG_ROUTING_NONE, base::nullopt, true},
+
+      // Renderer requests - with no |pending_main_frame_url|. Whitelisted based
+      // on the |last_committed_main_frame_url|.
+      {kWhitelistedPageURL, content::RESOURCE_TYPE_SCRIPT, "http://google.com",
+       kDummyFrameRoutingId,
+       FrameDataParams({kDummyFrameId, kDummyParentFrameId,
+                        "http://google.com/xyz", base::nullopt}),
+       true},
+      {"http://google.com/xyz", content::RESOURCE_TYPE_SCRIPT,
+       "http://google.com", kDummyFrameRoutingId,
+       FrameDataParams({kDummyFrameId, kDummyParentFrameId, kWhitelistedPageURL,
+                        base::nullopt}),
+       false},
+
+      // Renderer requests with |pending_main_frame_url|. This only happens for
+      // main frame subresource requests.
+
+      // Here we'll determine "http://example.com/xyz" to be the main frame url
+      // due to the origin.
+      {"http://example.com/script.js", content::RESOURCE_TYPE_SCRIPT,
+       "http://example.com", kDummyFrameRoutingId,
+       FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
+                        ExtensionApiFrameIdMap::kInvalidFrameId,
+                        kWhitelistedPageURL, "http://example.com/xyz"}),
+       true},
+
+      // Here we'll determine |kWhitelistedPageURL| to be the main
+      // frame url due to the origin.
+      {"http://example.com/script.js", content::RESOURCE_TYPE_SCRIPT,
+       "http://google.com", kDummyFrameRoutingId,
+       FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
+                        ExtensionApiFrameIdMap::kInvalidFrameId,
+                        kWhitelistedPageURL, "http://yahoo.com/xyz"}),
+       false},
+
+      // In these cases both |pending_main_frame_url| and
+      // |last_committed_main_frame_url| will be tested since we won't be able
+      // to determine the correct top level frame url using origin.
+      {"http://example.com/script.js", content::RESOURCE_TYPE_SCRIPT,
+       "http://google.com", kDummyFrameRoutingId,
+       FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
+                        ExtensionApiFrameIdMap::kInvalidFrameId,
+                        "http://google.com/abc", kWhitelistedPageURL}),
+       false},
+      {"http://example.com/script.js", content::RESOURCE_TYPE_SCRIPT,
+       base::nullopt, kDummyFrameRoutingId,
+       FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
+                        ExtensionApiFrameIdMap::kInvalidFrameId,
+                        kWhitelistedPageURL, "http://google.com/abc"}),
+       false},
+      {"http://example.com/script.js", content::RESOURCE_TYPE_SCRIPT,
+       base::nullopt, kDummyFrameRoutingId,
+       FrameDataParams({ExtensionApiFrameIdMap::kTopFrameId,
+                        ExtensionApiFrameIdMap::kInvalidFrameId,
+                        "http://yahoo.com/abc",
+                        "http://yahoo.com/whitelist123"}),
+       true},
+  };
+
+  for (size_t i = 0; i < base::size(test_cases); ++i) {
+    const TestCase test_case = test_cases[i];
+    SCOPED_TRACE(base::StringPrintf("Testing case number %zu with url %s",
+                                    i + 1, test_case.url.c_str()));
+
+    WebRequestInfo info;
+    info.url = GURL(test_case.url);
+    ASSERT_TRUE(info.url.is_valid());
+    info.type = test_case.type;
+
+    if (test_case.initiator)
+      info.initiator = url::Origin::Create(GURL(*test_case.initiator));
+
+    info.frame_id = test_case.frame_routing_id;
+
+    if (test_case.frame_data_params) {
+      const FrameDataParams& params = *test_case.frame_data_params;
+      info.frame_data = ExtensionApiFrameIdMap::FrameData(
+          params.frame_id, params.parent_frame_id, kDummyTabId, kDummyWindowId,
+          GURL(params.last_committed_main_frame_url));
+      if (params.pending_main_frame_url)
+        info.frame_data->pending_main_frame_url =
+            GURL(*params.pending_main_frame_url);
+    }
+
+    GURL redirect_url;
+    RulesetManager::Action expected_action =
+        test_case.expect_blocked_with_whitelist ? RulesetManager::Action::BLOCK
+                                                : RulesetManager::Action::NONE;
+    EXPECT_EQ(expected_action,
+              manager->EvaluateRequest(info, false /*is_incognito_context*/,
+                                       &redirect_url));
+  }
+}
+
 INSTANTIATE_TEST_CASE_P(,
                         RulesetManagerTest,
                         ::testing::Values(ExtensionLoadType::PACKED,
diff --git a/chrome/browser/geolocation/geolocation_permission_context_android.cc b/chrome/browser/geolocation/geolocation_permission_context_android.cc
index 2c8ab722..4ddd336 100644
--- a/chrome/browser/geolocation/geolocation_permission_context_android.cc
+++ b/chrome/browser/geolocation/geolocation_permission_context_android.cc
@@ -188,6 +188,8 @@
         content::WebContents::FromRenderFrameHost(
             content::RenderFrameHost::FromID(id.render_process_id(),
                                              id.render_frame_id()));
+    if (!web_contents)
+      return;
 
     // Only show the location settings dialog if the tab for |web_contents| is
     // user-interactable (i.e. is the current tab, and Chrome is active and not
diff --git a/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc b/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
index 655e36e..91eb6b43 100644
--- a/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
+++ b/chrome/browser/media_galleries/fileapi/media_file_system_backend.cc
@@ -91,7 +91,7 @@
     Profile* profile =
         Profile::FromBrowserContext(web_contents->GetBrowserContext());
 
-    ExtensionService* extension_service =
+    extensions::ExtensionService* extension_service =
         extensions::ExtensionSystem::Get(profile)->extension_service();
     const extensions::Extension* extension =
         extension_service->GetExtensionById(storage_domain,
diff --git a/chrome/browser/media_galleries/media_galleries_test_util.cc b/chrome/browser/media_galleries/media_galleries_test_util.cc
index 150fde59..aa6c1e1 100644
--- a/chrome/browser/media_galleries/media_galleries_test_util.cc
+++ b/chrome/browser/media_galleries/media_galleries_test_util.cc
@@ -79,7 +79,7 @@
       extensions::Extension::ENABLED,
       syncer::StringOrdinal::CreateInitialOrdinal(),
       std::string());
-  ExtensionService* extension_service =
+  extensions::ExtensionService* extension_service =
       extensions::ExtensionSystem::Get(profile)->extension_service();
   extension_service->AddExtension(extension.get());
   extension_service->EnableExtension(extension->id());
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc b/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc
index db6926c..24df751 100644
--- a/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc
+++ b/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc
@@ -451,9 +451,9 @@
   // Primary verifier returns an error status.
   net::CertVerifyResult primary_result;
   primary_result.verified_cert = cert_chain_1_;
-  primary_result.cert_status = net::CERT_STATUS_REVOKED;
+  primary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
   scoped_refptr<MockCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_REVOKED,
+      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
                                                primary_result);
 
   net::CertVerifyResult secondary_result;
@@ -476,7 +476,7 @@
   EXPECT_TRUE(request);
 
   error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::ERR_CERT_REVOKED));
+  EXPECT_THAT(error, IsError(net::ERR_CERT_DATE_INVALID));
 
   verify_proc2->WaitForVerifyCall();
 
@@ -492,7 +492,7 @@
   ASSERT_TRUE(report.ParseFromString(full_reports[0]));
 
   ASSERT_EQ(1, report.cert_error_size());
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_REVOKED,
+  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_DATE_INVALID,
             report.cert_error()[0]);
   EXPECT_EQ(0, report.cert_status_size());
 
@@ -524,10 +524,10 @@
 
   // Trial verifier returns an error status.
   net::CertVerifyResult secondary_result;
-  secondary_result.cert_status = net::CERT_STATUS_REVOKED;
+  secondary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
   secondary_result.verified_cert = cert_chain_1_;
   scoped_refptr<MockCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_REVOKED,
+      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
                                                secondary_result);
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
@@ -568,7 +568,7 @@
   const chrome_browser_ssl::TrialVerificationInfo& trial_info =
       report.features_info().trial_verification_info();
   ASSERT_EQ(1, trial_info.cert_error_size());
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_REVOKED,
+  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_DATE_INVALID,
             trial_info.cert_error()[0]);
   EXPECT_EQ(0, trial_info.cert_status_size());
 
@@ -738,9 +738,9 @@
   // Primary verifier returns an error status.
   net::CertVerifyResult primary_result;
   primary_result.verified_cert = cert_chain_1_;
-  primary_result.cert_status = net::CERT_STATUS_REVOKED;
+  primary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
   scoped_refptr<MockCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_REVOKED,
+      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
                                                primary_result);
 
   // Trial verifier has ok status.
@@ -777,9 +777,9 @@
 
   // Both callbacks should be called with same error code.
   error = callback_1.WaitForResult();
-  EXPECT_THAT(error, IsError(net::ERR_CERT_REVOKED));
+  EXPECT_THAT(error, IsError(net::ERR_CERT_DATE_INVALID));
   error = callback_2.WaitForResult();
-  EXPECT_THAT(error, IsError(net::ERR_CERT_REVOKED));
+  EXPECT_THAT(error, IsError(net::ERR_CERT_DATE_INVALID));
 
   // Trial verifier should run.
   verify_proc2->WaitForVerifyCall();
@@ -796,7 +796,7 @@
   ASSERT_TRUE(report.ParseFromString(full_reports[0]));
 
   ASSERT_EQ(1, report.cert_error_size());
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_REVOKED,
+  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_DATE_INVALID,
             report.cert_error()[0]);
   EXPECT_EQ(0, report.cert_status_size());
 
@@ -826,9 +826,9 @@
   // Primary verifier returns an error status.
   net::CertVerifyResult primary_result;
   primary_result.verified_cert = cert_chain_1_;
-  primary_result.cert_status = net::CERT_STATUS_REVOKED;
+  primary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
   scoped_refptr<MockCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_REVOKED,
+      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
                                                primary_result);
 
   // Trial verifier has ok status.
@@ -871,7 +871,7 @@
   ASSERT_TRUE(report.ParseFromString(full_reports[0]));
 
   ASSERT_EQ(1, report.cert_error_size());
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_REVOKED,
+  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_DATE_INVALID,
             report.cert_error()[0]);
   EXPECT_EQ(0, report.cert_status_size());
 
@@ -899,9 +899,9 @@
   // Primary verifier returns an error status.
   net::CertVerifyResult primary_result;
   primary_result.verified_cert = cert_chain_1_;
-  primary_result.cert_status = net::CERT_STATUS_REVOKED;
+  primary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
   scoped_refptr<MockCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_REVOKED,
+      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
                                                primary_result);
 
   auto verifier = std::make_unique<TrialComparisonCertVerifier>(
@@ -943,9 +943,9 @@
   // Primary verifier returns an error status.
   net::CertVerifyResult primary_result;
   primary_result.verified_cert = cert_chain_1_;
-  primary_result.cert_status = net::CERT_STATUS_REVOKED;
+  primary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
   scoped_refptr<MockCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_REVOKED,
+      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
                                                primary_result);
 
   // Trial verifier has ok status.
@@ -971,7 +971,7 @@
 
   // Wait for primary verifier to finish.
   error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::ERR_CERT_REVOKED));
+  EXPECT_THAT(error, IsError(net::ERR_CERT_DATE_INVALID));
 
   // Delete the TrialComparisonCertVerifier.
   verifier.reset();
@@ -1009,10 +1009,10 @@
 
   // Trial verifier returns an error status.
   net::CertVerifyResult secondary_result;
-  secondary_result.cert_status = net::CERT_STATUS_REVOKED;
+  secondary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
   secondary_result.verified_cert = cert_chain_1_;
   scoped_refptr<MockCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_REVOKED,
+      base::MakeRefCounted<MockCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
                                                secondary_result);
 
   TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
index 1cbb17e..bc4523d 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
@@ -88,6 +88,45 @@
   ASSERT_TRUE(window_controller()->GetWindowForTesting()->IsVisible());
 }
 
+// Tests that when an active WebContents accurately tracks whether a video
+// is in Picture-in-Picture.
+IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
+                       TabIconUpdated) {
+  GURL test_page_url = ui_test_utils::GetTestUrl(
+      base::FilePath(base::FilePath::kCurrentDirectory),
+      base::FilePath(
+          FILE_PATH_LITERAL("media/picture-in-picture/window-size.html")));
+  ui_test_utils::NavigateToURL(browser(), test_page_url);
+
+  content::WebContents* active_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(active_web_contents);
+
+  // First test there is no video playing in Picture-in-Picture.
+  EXPECT_FALSE(active_web_contents->HasPictureInPictureVideo());
+
+  // Start playing video in Picture-in-Picture and retest with the
+  // opposite assertion.
+  SetUpWindowController(active_web_contents);
+  ASSERT_TRUE(window_controller());
+
+  EXPECT_TRUE(
+      content::ExecuteScript(active_web_contents, "enterPictureInPicture();"));
+
+  // Wait for title update to confirm and then test there is video playing in
+  // Picture-in-Picture.
+  base::string16 expected_title = base::ASCIIToUTF16("1");
+  EXPECT_EQ(expected_title,
+            content::TitleWatcher(active_web_contents, expected_title)
+                .WaitAndGetTitle());
+
+  EXPECT_TRUE(active_web_contents->HasPictureInPictureVideo());
+
+  // Stop video being played Picture-in-Picture and check if that's tracked.
+  window_controller()->Close();
+  EXPECT_FALSE(active_web_contents->HasPictureInPictureVideo());
+}
+
 #if !defined(OS_ANDROID)
 
 // Tests that when creating a Picture-in-Picture window a size is sent to the
@@ -254,4 +293,30 @@
   EXPECT_FALSE(in_picture_in_picture);
 }
 
+// Tests that we can enter Picture-in-Picture when a video is not preloaded,
+// using the metadata optimizations. This test is checking that there are no
+// crashes.
+IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
+                       EnterMetadataPosterOptimisation) {
+  GURL test_page_url = ui_test_utils::GetTestUrl(
+      base::FilePath(base::FilePath::kCurrentDirectory),
+      base::FilePath(FILE_PATH_LITERAL(
+          "media/picture-in-picture/player_metadata_poster.html")));
+  ui_test_utils::NavigateToURL(browser(), test_page_url);
+
+  content::WebContents* active_web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(active_web_contents);
+
+  SetUpWindowController(active_web_contents);
+
+  EXPECT_TRUE(
+      content::ExecuteScript(active_web_contents, "enterPictureInPicture();"));
+
+  base::string16 expected_title = base::ASCIIToUTF16("entered_pip");
+  EXPECT_EQ(expected_title,
+            content::TitleWatcher(active_web_contents, expected_title)
+                .WaitAndGetTitle());
+}
+
 #endif  // !defined(OS_LINUX) && !defined(OS_WIN)
diff --git a/chrome/browser/plugins/flash_permission_context.cc b/chrome/browser/plugins/flash_permission_context.cc
index 3352b9f..e2d9582 100644
--- a/chrome/browser/plugins/flash_permission_context.cc
+++ b/chrome/browser/plugins/flash_permission_context.cc
@@ -64,6 +64,8 @@
       content::WebContents::FromRenderFrameHost(
           content::RenderFrameHost::FromID(id.render_process_id(),
                                            id.render_frame_id()));
+  if (!web_contents)
+    return;
 
   if (PluginsEnterpriseSettingEnabled(
           HostContentSettingsMapFactory::GetForProfile(profile()))) {
diff --git a/chrome/browser/profiles/profile_window.cc b/chrome/browser/profiles/profile_window.cc
index 3bc51d3..3e4fcb86 100644
--- a/chrome/browser/profiles/profile_window.cc
+++ b/chrome/browser/profiles/profile_window.cc
@@ -77,13 +77,13 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 void BlockExtensions(Profile* profile) {
-  ExtensionService* extension_service =
+  extensions::ExtensionService* extension_service =
       extensions::ExtensionSystem::Get(profile)->extension_service();
   extension_service->BlockAllExtensions();
 }
 
 void UnblockExtensions(Profile* profile) {
-  ExtensionService* extension_service =
+  extensions::ExtensionService* extension_service =
       extensions::ExtensionSystem::Get(profile)->extension_service();
   extension_service->UnblockAllExtensions();
 }
diff --git a/chrome/browser/resources/md_user_manager/shared_styles.html b/chrome/browser/resources/md_user_manager/shared_styles.html
index 02eac8f..8056132 100644
--- a/chrome/browser/resources/md_user_manager/shared_styles.html
+++ b/chrome/browser/resources/md_user_manager/shared_styles.html
@@ -58,17 +58,6 @@
         -webkit-margin-start: 8px;
       }
 
-      paper-listbox {
-        --paper-listbox: {
-          padding: 0;
-        };
-      }
-
-      iron-overlay-backdrop {
-        --iron-overlay-backdrop-background-color: white;
-        --iron-overlay-backdrop-opacity: .8;
-      }
-
       .product-logo {
         content: -webkit-image-set(
             url(../../../app/theme/default_100_percent/%DISTRIBUTION%/product_logo_name_22.png) 1x,
diff --git a/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html b/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html
index 15d30d1..9b51f8c 100644
--- a/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html
+++ b/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html
@@ -266,17 +266,17 @@
                consent-description>
             $i18n{syncConfirmationOptionsSubtitle}
           </div>
-          <paper-radio-group selected="reviewSettings"
+          <paper-radio-group selected="noChanges"
               selectable="cr-radio-button">
-            <cr-radio-button name="reviewSettings">
-              $i18n{syncConfirmationOptionsReviewSettingsTitle}
-            </cr-radio-button>
             <cr-radio-button name="noChanges">
               $i18n{syncConfirmationOptionsMakeNoChangesTitle}
               <div class="subtitle-text">
                 $i18n{syncConfirmationOptionsMakeNoChangesSubtitle}
               </div>
             </cr-radio-button>
+            <cr-radio-button name="reviewSettings">
+              $i18n{syncConfirmationOptionsReviewSettingsTitle}
+            </cr-radio-button>
             <cr-radio-button name="defaultSettings">
               <div consent-description>
                 $i18n{syncConfirmationOptionsUseDefaultTitle}
diff --git a/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc b/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
index 0d69010..0592a8b 100644
--- a/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
+++ b/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
@@ -22,10 +22,10 @@
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/safe_browsing/db/database_manager.h"
 #include "components/subresource_filter/content/browser/content_ruleset_service.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
 #include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
+#include "components/subresource_filter/core/common/activation_decision.h"
 #include "components/subresource_filter/core/common/activation_level.h"
 #include "components/subresource_filter/core/common/activation_scope.h"
 #include "components/subresource_filter/core/common/activation_state.h"
@@ -46,11 +46,6 @@
       SubresourceFilterProfileContextFactory::GetForProfile(
           Profile::FromBrowserContext(web_contents->GetBrowserContext()));
   settings_manager_ = context->settings_manager();
-  subresource_filter::ContentSubresourceFilterDriverFactory::
-      CreateForWebContents(web_contents, this);
-
-  auto* driver_factory = subresource_filter::
-      ContentSubresourceFilterDriverFactory::FromWebContents(web_contents);
 
   subresource_filter::ContentRulesetService* ruleset_service =
       g_browser_process->subresource_filter_ruleset_service();
@@ -58,7 +53,7 @@
       ruleset_service ? ruleset_service->ruleset_dealer() : nullptr;
   throttle_manager_ = std::make_unique<
       subresource_filter::ContentSubresourceFilterThrottleManager>(
-      driver_factory, dealer, web_contents);
+      this, dealer, web_contents);
 }
 
 ChromeSubresourceFilterClient::~ChromeSubresourceFilterClient() {}
@@ -121,27 +116,36 @@
   }
 }
 
-bool ChromeSubresourceFilterClient::OnPageActivationComputed(
+subresource_filter::ActivationLevel
+ChromeSubresourceFilterClient::OnPageActivationComputed(
     content::NavigationHandle* navigation_handle,
-    bool activated) {
-  const GURL& url(navigation_handle->GetURL());
+    subresource_filter::ActivationLevel initial_activation_level,
+    subresource_filter::ActivationDecision* decision) {
   DCHECK(navigation_handle->IsInMainFrame());
 
-  if (url.SchemeIsHTTPOrHTTPS()) {
-    // With respect to persistent metadata, do not consider the site activated
-    // if it is forced via devtools.
-    settings_manager_->ResetSiteMetadataBasedOnActivation(
-        url, activated && !activated_via_devtools_);
+  subresource_filter::ActivationLevel effective_activation_level =
+      initial_activation_level;
+  if (activated_via_devtools_) {
+    effective_activation_level = subresource_filter::ActivationLevel::ENABLED;
+    *decision = subresource_filter::ActivationDecision::FORCED_ACTIVATION;
   }
 
-  // Return whether the activation should be whitelisted.
-  // Note: Could consider skipping this if forcing activation, but it isn't
-  // critical.
-  return whitelisted_hosts_.count(url.host()) ||
-         settings_manager_->GetSitePermission(url) == CONTENT_SETTING_ALLOW;
-  // TODO(csharrison): Consider setting the metadata to an empty dict here if
-  // the site is activated and not whitelisted. Need to be careful about various
-  // edge cases like DRYRUN activation.
+  const GURL& url(navigation_handle->GetURL());
+  if (url.SchemeIsHTTPOrHTTPS()) {
+    settings_manager_->ResetSiteMetadataBasedOnActivation(
+        url, effective_activation_level ==
+                 subresource_filter::ActivationLevel::ENABLED);
+  }
+
+  if (whitelisted_hosts_.count(url.host()) ||
+      settings_manager_->GetSitePermission(url) == CONTENT_SETTING_ALLOW) {
+    if (effective_activation_level ==
+        subresource_filter::ActivationLevel::ENABLED) {
+      *decision = subresource_filter::ActivationDecision::URL_WHITELISTED;
+    }
+    return subresource_filter::ActivationLevel::DISABLED;
+  }
+  return effective_activation_level;
 }
 
 void ChromeSubresourceFilterClient::WhitelistInCurrentWebContents(
@@ -155,10 +159,6 @@
   settings_manager_->WhitelistSite(top_level_url);
 }
 
-bool ChromeSubresourceFilterClient::ForceActivationInCurrentWebContents() {
-  return activated_via_devtools_;
-}
-
 void ChromeSubresourceFilterClient::ToggleForceActivationInCurrentWebContents(
     bool force_activation) {
   if (!activated_via_devtools_ && force_activation)
@@ -173,7 +173,6 @@
 }
 
 void ChromeSubresourceFilterClient::ShowUI(const GURL& url) {
-  DCHECK(!activated_via_devtools_);
 #if defined(OS_ANDROID)
   InfoBarService* infobar_service =
       InfoBarService::FromWebContents(web_contents());
diff --git a/chrome/browser/subresource_filter/chrome_subresource_filter_client.h b/chrome/browser/subresource_filter/chrome_subresource_filter_client.h
index f252e69..9725123 100644
--- a/chrome/browser/subresource_filter/chrome_subresource_filter_client.h
+++ b/chrome/browser/subresource_filter/chrome_subresource_filter_client.h
@@ -119,9 +119,10 @@
 
   // SubresourceFilterClient:
   void ShowNotification() override;
-  bool OnPageActivationComputed(content::NavigationHandle* navigation_handle,
-                                bool activated) override;
-  bool ForceActivationInCurrentWebContents() override;
+  subresource_filter::ActivationLevel OnPageActivationComputed(
+      content::NavigationHandle* navigation_handle,
+      subresource_filter::ActivationLevel initial_activation_level,
+      subresource_filter::ActivationDecision* decision) override;
 
   // Should be called by devtools in response to a protocol command to enable ad
   // blocking in this WebContents. Should only persist while devtools is
diff --git a/chrome/browser/subresource_filter/subresource_filter_abusive_unittest.cc b/chrome/browser/subresource_filter/subresource_filter_abusive_unittest.cc
index f7935c7..b3fb93e 100644
--- a/chrome/browser/subresource_filter/subresource_filter_abusive_unittest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_abusive_unittest.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
 #include "chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker.h"
 #include "components/safe_browsing/db/util.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
 #include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
diff --git a/chrome/browser/subresource_filter/subresource_filter_devtools_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_devtools_browsertest.cc
index 13c0ad6..7660151d 100644
--- a/chrome/browser/subresource_filter/subresource_filter_devtools_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_devtools_browsertest.cc
@@ -136,9 +136,9 @@
 }
 
 IN_PROC_BROWSER_TEST_F(SubresourceFilterDevtoolsBrowserTest,
-                       ForceActivation_NoSubresourceLogging) {
+                       ForceActivation_SubresourceLogging) {
   content::ConsoleObserverDelegate console_observer(web_contents(),
-                                                    "*show ads*");
+                                                    kActivationConsoleMessage);
   web_contents()->SetDelegate(&console_observer);
   const GURL url(
       GetTestUrl("subresource_filter/frame_with_included_script.html"));
@@ -149,7 +149,8 @@
 
   ui_test_utils::NavigateToURL(browser(), url);
   EXPECT_FALSE(WasParsedScriptElementLoaded(web_contents()->GetMainFrame()));
-  EXPECT_TRUE(console_observer.message().empty()) << console_observer.message();
+  EXPECT_FALSE(console_observer.message().empty());
+  console_observer.Wait();
 }
 
 // See crbug.com/813197, where agent hosts from subframes could send messages to
diff --git a/chrome/browser/subresource_filter/subresource_filter_test_harness.cc b/chrome/browser/subresource_filter/subresource_filter_test_harness.cc
index 86136d1..9064c71 100644
--- a/chrome/browser/subresource_filter/subresource_filter_test_harness.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_test_harness.cc
@@ -23,7 +23,6 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/safe_browsing/db/v4_protocol_manager_util.h"
 #include "components/subresource_filter/content/browser/content_ruleset_service.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
 #include "components/subresource_filter/core/browser/ruleset_service.h"
 #include "components/subresource_filter/core/common/activation_decision.h"
diff --git a/chrome/browser/subresource_filter/subresource_filter_unittest.cc b/chrome/browser/subresource_filter/subresource_filter_unittest.cc
index e9238c6..cbbbc6a 100644
--- a/chrome/browser/subresource_filter/subresource_filter_unittest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_unittest.cc
@@ -12,7 +12,6 @@
 #include "components/safe_browsing/db/util.h"
 #include "components/safe_browsing/db/v4_protocol_manager_util.h"
 #include "components/subresource_filter/content/browser/content_activation_list_utils.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
 #include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
@@ -169,7 +168,6 @@
 
   // Navigate initially, should be no activation.
   SimulateNavigateAndCommit(url, main_rfh());
-  EXPECT_FALSE(GetClient()->ForceActivationInCurrentWebContents());
   EXPECT_TRUE(CreateAndNavigateDisallowedSubframe(main_rfh()));
   EXPECT_EQ(nullptr, GetSettingsManager()->GetSiteMetadata(url));
 
@@ -179,18 +177,17 @@
   GetClient()->ToggleForceActivationInCurrentWebContents(true);
   histogram_tester.ExpectBucketCount(actions_histogram,
                                      kActionForcedActivationEnabled, 1);
-  EXPECT_TRUE(GetClient()->ForceActivationInCurrentWebContents());
 
   SimulateNavigateAndCommit(url, main_rfh());
   EXPECT_FALSE(CreateAndNavigateDisallowedSubframe(main_rfh()));
-  EXPECT_FALSE(GetClient()->did_show_ui_for_navigation());
-  EXPECT_EQ(nullptr, GetSettingsManager()->GetSiteMetadata(url));
+  EXPECT_TRUE(GetClient()->did_show_ui_for_navigation());
+  EXPECT_NE(nullptr, GetSettingsManager()->GetSiteMetadata(url));
   histogram_tester.ExpectBucketCount(
-      "SubresourceFilter.PageLoad.ForcedActivation.DisallowedLoad", true, 1);
+      "SubresourceFilter.PageLoad.ActivationDecision",
+      subresource_filter::ActivationDecision::FORCED_ACTIVATION, 1);
 
   // Simulate closing devtools.
   GetClient()->ToggleForceActivationInCurrentWebContents(false);
-  EXPECT_FALSE(GetClient()->ForceActivationInCurrentWebContents());
 
   SimulateNavigateAndCommit(url, main_rfh());
   EXPECT_TRUE(CreateAndNavigateDisallowedSubframe(main_rfh()));
@@ -208,9 +205,8 @@
   // Resource should be disallowed, since navigation commit had activation.
   EXPECT_FALSE(CreateAndNavigateDisallowedSubframe(main_rfh()));
 
-  // UI should not have shown though.
   histogram_tester.ExpectBucketCount("SubresourceFilter.Actions",
-                                     kActionUIShown, 0);
+                                     kActionUIShown, 1);
 }
 
 TEST_F(SubresourceFilterTest, NotifySafeBrowsing) {
diff --git a/chrome/browser/supervised_user/supervised_user_service.cc b/chrome/browser/supervised_user/supervised_user_service.cc
index 9ab9998..f8487c5 100644
--- a/chrome/browser/supervised_user/supervised_user_service.cc
+++ b/chrome/browser/supervised_user/supervised_user_service.cc
@@ -979,7 +979,7 @@
     return;
 
   ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
-  ExtensionService* service =
+  extensions::ExtensionService* service =
       ExtensionSystem::Get(profile_)->extension_service();
 
   ExtensionState state = GetExtensionState(*extension);
diff --git a/chrome/browser/sync_file_system/drive_backend/sync_engine.cc b/chrome/browser/sync_file_system/drive_backend/sync_engine.cc
index 5e0b91b..1ad7d997 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_engine.cc
+++ b/chrome/browser/sync_file_system/drive_backend/sync_engine.cc
@@ -209,7 +209,7 @@
   Profile* profile = Profile::FromBrowserContext(context);
   drive::DriveNotificationManager* notification_manager =
       drive::DriveNotificationManagerFactory::GetForBrowserContext(context);
-  ExtensionService* extension_service =
+  extensions::ExtensionService* extension_service =
       extensions::ExtensionSystem::Get(context)->extension_service();
   SigninManagerBase* signin_manager =
       SigninManagerFactory::GetForProfile(profile);
@@ -336,7 +336,8 @@
   worker_observer_.reset(new WorkerObserver(ui_task_runner_.get(),
                                             weak_ptr_factory_.GetWeakPtr()));
 
-  base::WeakPtr<ExtensionServiceInterface> extension_service_weak_ptr;
+  base::WeakPtr<extensions::ExtensionServiceInterface>
+      extension_service_weak_ptr;
   if (extension_service_)
     extension_service_weak_ptr = extension_service_->AsWeakPtr();
 
@@ -720,7 +721,7 @@
     const base::FilePath& sync_file_system_dir,
     TaskLogger* task_logger,
     drive::DriveNotificationManager* notification_manager,
-    ExtensionServiceInterface* extension_service,
+    extensions::ExtensionServiceInterface* extension_service,
     SigninManagerBase* signin_manager,
     OAuth2TokenService* token_service,
     net::URLRequestContextGetter* request_context,
diff --git a/chrome/browser/sync_file_system/drive_backend/sync_worker.cc b/chrome/browser/sync_file_system/drive_backend/sync_worker.cc
index e7274496..48ee63a 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_worker.cc
+++ b/chrome/browser/sync_file_system/drive_backend/sync_worker.cc
@@ -45,7 +45,8 @@
 
 SyncWorker::SyncWorker(
     const base::FilePath& base_dir,
-    const base::WeakPtr<ExtensionServiceInterface>& extension_service,
+    const base::WeakPtr<extensions::ExtensionServiceInterface>&
+        extension_service,
     leveldb::Env* env_override)
     : base_dir_(base_dir),
       env_override_(env_override),
@@ -411,11 +412,13 @@
 }
 
 void SyncWorker::QueryAppStatusOnUIThread(
-    const base::WeakPtr<ExtensionServiceInterface>& extension_service_ptr,
+    const base::WeakPtr<extensions::ExtensionServiceInterface>&
+        extension_service_ptr,
     const std::vector<std::string>* app_ids,
     AppStatusMap* status,
     const base::Closure& callback) {
-  ExtensionServiceInterface* extension_service = extension_service_ptr.get();
+  extensions::ExtensionServiceInterface* extension_service =
+      extension_service_ptr.get();
   if (!extension_service) {
     callback.Run();
     return;
diff --git a/chrome/browser/themes/theme_properties.cc b/chrome/browser/themes/theme_properties.cc
index 52e3c419..97bfb81 100644
--- a/chrome/browser/themes/theme_properties.cc
+++ b/chrome/browser/themes/theme_properties.cc
@@ -179,6 +179,8 @@
       return incognito ? gfx::kGoogleGrey600 : gfx::kGoogleGrey300;
     case ThemeProperties::COLOR_TAB_ALERT_RECORDING:
       return incognito ? gfx::kGoogleGrey400 : gfx::kGoogleRed600;
+    case ThemeProperties::COLOR_TAB_PIP_PLAYING:
+      return incognito ? gfx::kGoogleGrey400 : gfx::kGoogleBlue600;
     case ThemeProperties::COLOR_TAB_ALERT_CAPTURING:
       return incognito ? gfx::kGoogleGrey400 : gfx::kGoogleBlue600;
 
diff --git a/chrome/browser/themes/theme_properties.h b/chrome/browser/themes/theme_properties.h
index 13e75c0b..a953dce 100644
--- a/chrome/browser/themes/theme_properties.h
+++ b/chrome/browser/themes/theme_properties.h
@@ -129,6 +129,7 @@
     // The colors used by the various alert indicator icons in the tab.
     COLOR_TAB_ALERT_AUDIO,
     COLOR_TAB_ALERT_RECORDING,
+    COLOR_TAB_PIP_PLAYING,
     COLOR_TAB_ALERT_CAPTURING,
 
     // These colors don't have constant default values. They are derived from
diff --git a/chrome/browser/ui/cocoa/tabs/tab_view.mm b/chrome/browser/ui/cocoa/tabs/tab_view.mm
index 9021a2ce3..3933b104 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_view.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_view.mm
@@ -685,6 +685,8 @@
     case TabAlertState::MEDIA_RECORDING:
       return themeProvider->GetColor(
           ThemeProperties::COLOR_TAB_ALERT_RECORDING);
+    case TabAlertState::PIP_PLAYING:
+      return themeProvider->GetColor(ThemeProperties::COLOR_TAB_PIP_PLAYING);
     case TabAlertState::AUDIO_PLAYING:
     case TabAlertState::AUDIO_MUTING:
     case TabAlertState::TAB_CAPTURING:
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 3a8cfeae..7359c1e 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -81,7 +81,6 @@
 #include "components/history/core/browser/top_sites.h"
 #include "components/offline_pages/buildflags/buildflags.h"
 #include "components/password_manager/core/browser/password_manager.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
 #include "components/tracing/common/tracing_switches.h"
 #include "components/ukm/content/source_url_recorder.h"
diff --git a/chrome/browser/ui/tabs/tab_utils.cc b/chrome/browser/ui/tabs/tab_utils.cc
index 0241fb4..12da8290f 100644
--- a/chrome/browser/ui/tabs/tab_utils.cc
+++ b/chrome/browser/ui/tabs/tab_utils.cc
@@ -22,6 +22,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "content/public/browser/picture_in_picture_window_controller.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/url_constants.h"
@@ -176,6 +177,9 @@
   if (usb_tab_helper && usb_tab_helper->IsDeviceConnected())
     return TabAlertState::USB_CONNECTED;
 
+  if (contents->HasPictureInPictureVideo())
+    return TabAlertState::PIP_PLAYING;
+
   if (contents->WasRecentlyAudible()) {
     if (contents->IsAudioMuted())
       return TabAlertState::AUDIO_MUTING;
@@ -215,6 +219,9 @@
     case TabAlertState::USB_CONNECTED:
       icon = &kTabUsbConnectedIcon;
       break;
+    case TabAlertState::PIP_PLAYING:
+      icon = &kPictureInPictureAltIcon;
+      break;
     case TabAlertState::NONE:
       return gfx::Image();
   }
@@ -236,6 +243,7 @@
     case TabAlertState::TAB_CAPTURING:
     case TabAlertState::BLUETOOTH_CONNECTED:
     case TabAlertState::USB_CONNECTED:
+    case TabAlertState::PIP_PLAYING:
       return GetTabAlertIndicatorImage(alert_state, button_color);
   }
   NOTREACHED();
@@ -297,6 +305,10 @@
       result.append(
           l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ALERT_STATE_USB_CONNECTED));
       break;
+    case TabAlertState::PIP_PLAYING:
+      result.append(
+          l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_ALERT_STATE_PIP_PLAYING));
+      break;
     case TabAlertState::NONE:
       NOTREACHED();
       break;
@@ -338,6 +350,9 @@
     case TabAlertState::TAB_CAPTURING:
       return l10n_util::GetStringFUTF16(IDS_TAB_AX_LABEL_TAB_CAPTURING_FORMAT,
                                         title);
+    case TabAlertState::PIP_PLAYING:
+      return l10n_util::GetStringFUTF16(IDS_TAB_AX_LABEL_PIP_PLAYING_FORMAT,
+                                        title);
     case TabAlertState::NONE:
       return title;
   }
@@ -361,6 +376,7 @@
     case TabAlertState::TAB_CAPTURING:
     case TabAlertState::BLUETOOTH_CONNECTED:
     case TabAlertState::USB_CONNECTED:
+    case TabAlertState::PIP_PLAYING:
       // The new Audio Service implements muting separately from the tab audio
       // capture infrastructure; so the mute state can be toggled independently
       // at all times.
diff --git a/chrome/browser/ui/tabs/tab_utils.h b/chrome/browser/ui/tabs/tab_utils.h
index fb926e23..06428ab7 100644
--- a/chrome/browser/ui/tabs/tab_utils.h
+++ b/chrome/browser/ui/tabs/tab_utils.h
@@ -34,6 +34,7 @@
   AUDIO_MUTING,         // Tab audio is being muted.
   BLUETOOTH_CONNECTED,  // Tab is connected to a BT Device.
   USB_CONNECTED,        // Tab is connected to a USB device.
+  PIP_PLAYING,          // Tab contains a video in Picture-in-Picture mode.
 };
 
 enum class TabMutedReason {
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
index 64a80410..a9830da 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -119,6 +119,13 @@
   }
 }
 
+void BrowserNonClientFrameView::OnThemeChanged() {
+  // Adding or removing a frame image will change whether single tab mode is
+  // available.
+  if (MD::IsRefreshUi() && browser_view()->IsTabStripVisible())
+    browser_view()->tabstrip()->SingleTabModeChanged();
+}
+
 void BrowserNonClientFrameView::VisibilityChanged(views::View* starting_from,
                                                   bool is_visible) {
   // UpdateTaskbarDecoration() calls DrawTaskbarDecoration(), but that does
@@ -130,15 +137,15 @@
 }
 
 void BrowserNonClientFrameView::OnTabAdded(int index) {
-  if (MD::IsRefreshUi() && browser_view()->tabstrip()->tab_count() == 2) {
-    // We are exiting single-tab mode and need to repaint the frame.
+  if (IsSingleTabModeAvailable()) {
+    // We may be exiting single-tab mode and need to repaint the frame.
     SchedulePaint();
   }
 }
 
 void BrowserNonClientFrameView::OnTabRemoved(int index) {
-  if (MD::IsRefreshUi() && browser_view()->tabstrip()->tab_count() == 1) {
-    // We are entering single-tab mode and need to repaint the frame.
+  if (IsSingleTabModeAvailable()) {
+    // We may be entering single-tab mode and need to repaint the frame.
     SchedulePaint();
   }
 }
@@ -147,13 +154,16 @@
   return browser_view_->IsBrowserTypeNormal();
 }
 
-bool BrowserNonClientFrameView::ShouldPaintAsSingleTabMode() const {
-  // Single-tab mode is only available in Refresh. The special color we use for
+bool BrowserNonClientFrameView::IsSingleTabModeAvailable() const {
+  // Single-tab mode is only available in Refresh. The special color we use
   // won't be visible if there's a frame image, but since it's used to determine
   // constrast of other UI elements, the theme color should be used instead.
-  return MD::IsRefreshUi() && GetFrameImage().isNull() &&
-         browser_view()->IsTabStripVisible() &&
-         browser_view()->tabstrip()->tab_count() == 1;
+  return MD::IsRefreshUi() && GetFrameImage().isNull();
+}
+
+bool BrowserNonClientFrameView::ShouldPaintAsSingleTabMode() const {
+  return browser_view()->IsTabStripVisible() &&
+         browser_view()->tabstrip()->SingleTabMode();
 }
 
 SkColor BrowserNonClientFrameView::GetFrameColor(bool active) const {
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
index 718c180..6b60d9e 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
@@ -90,8 +90,13 @@
   // https://crbug.com/820485.
   virtual int GetTabStripLeftInset() const;
 
+  // Whether the special painting mode for one tab is allowed, regardless of how
+  // many tabs there are right now.
+  bool IsSingleTabModeAvailable() const;
+
   // views::NonClientFrameView:
   void ChildPreferredSizeChanged(views::View* child) override;
+  void OnThemeChanged() override;
   void VisibilityChanged(views::View* starting_from, bool is_visible) override;
 
   // TabStripObserver:
@@ -104,7 +109,7 @@
   // not.
   virtual bool ShouldPaintAsThemed() const;
 
-  // Whether the frame should be painted with a special mode for one tab.
+  // Whether the frame should be painted with the special mode for one tab.
   bool ShouldPaintAsSingleTabMode() const;
 
   // Compute aspects of the frame needed to paint the frame background.
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
index dd4f759..52f38b0c 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -346,6 +346,10 @@
                                                tabstrip_->stacked_layout());
 }
 
+bool BrowserTabStripController::IsSingleTabModeAvailable() {
+  return browser_view_->frame()->GetFrameView()->IsSingleTabModeAvailable();
+}
+
 void BrowserTabStripController::OnStartedDraggingTabs() {
   if (!immersive_reveal_lock_.get()) {
     // The top-of-window views should be revealed while the user is dragging
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
index f80fb29..dd81d59c 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
@@ -70,6 +70,7 @@
   void CreateNewTabWithLocation(const base::string16& loc) override;
   bool IsIncognito() override;
   void StackedLayoutMaybeChanged() override;
+  bool IsSingleTabModeAvailable() override;
   void OnStartedDraggingTabs() override;
   void OnStoppedDraggingTabs() override;
   SkColor GetToolbarTopSeparatorColor() const override;
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
index 1ece633..bf3c3046 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
@@ -128,6 +128,10 @@
 void FakeBaseTabStripController::StackedLayoutMaybeChanged() {
 }
 
+bool FakeBaseTabStripController::IsSingleTabModeAvailable() {
+  return false;
+}
+
 void FakeBaseTabStripController::OnStartedDraggingTabs() {
 }
 
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
index b0f9454..a7c3262 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
@@ -47,6 +47,7 @@
   void CreateNewTabWithLocation(const base::string16& loc) override;
   bool IsIncognito() override;
   void StackedLayoutMaybeChanged() override;
+  bool IsSingleTabModeAvailable() override;
   void OnStartedDraggingTabs() override;
   void OnStoppedDraggingTabs() override;
   SkColor GetToolbarTopSeparatorColor() const override;
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index bb83059..55f87ee 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -490,6 +490,8 @@
                  ? theme_provider->GetColor(
                        ThemeProperties::COLOR_TAB_ALERT_CAPTURING)
                  : button_color_;
+    case TabAlertState::PIP_PLAYING:
+      return theme_provider->GetColor(ThemeProperties::COLOR_TAB_PIP_PLAYING);
     case TabAlertState::BLUETOOTH_CONNECTED:
     case TabAlertState::USB_CONNECTED:
     case TabAlertState::NONE:
@@ -1343,7 +1345,7 @@
 
 void Tab::UpdateIconVisibility() {
   center_favicon_ = false;
-  showing_icon_ = showing_alert_indicator_ = showing_close_button_ = false;
+  showing_icon_ = showing_alert_indicator_ = false;
   extra_padding_before_content_ = false;
 
   const gfx::Size min_size(GetMinimumInactiveSize());
@@ -1371,10 +1373,6 @@
   const bool has_alert_icon =
       (alert_indicator_button_ ? alert_indicator_button_->showing_alert_state()
                                : data().alert_state) != TabAlertState::NONE;
-  const bool hide_inactive_close_button =
-      controller_->ShouldHideCloseButtonForInactiveTabs();
-  const bool show_close_button_on_hover =
-      controller_->ShouldShowCloseButtonOnHover();
 
   if (is_pinned) {
     // When the tab is pinned, we can show one of the two icons. Alert icon
@@ -1382,10 +1380,10 @@
     // tab is pinned.
     showing_alert_indicator_ = has_alert_icon;
     showing_icon_ = has_favicon && !has_alert_icon;
+    showing_close_button_ = false;
   } else {
+    showing_close_button_ = !controller_->ShouldHideCloseButtonForTab(this);
     if (is_active) {
-      // The close button is always visible for an active tab.
-      showing_close_button_ = true;
       available_width -= close_button_width;
 
       showing_alert_indicator_ =
@@ -1412,18 +1410,17 @@
       if (!showing_icon_ || !showing_alert_indicator_)
         extra_padding = 0;
 
-      // For an inactive tab, the close button will be visible only when
-      // it is not forced to hide and the total width can accomodate all 3
-      // icons. When favicon or alert button is not visible, its space
-      // will be occupied by the title of this tab.
+      // Show the close button if it's allowed to show on hover, even if it's
+      // forced to be hidden normally.
+      showing_close_button_ |= controller_->ShouldShowCloseButtonOnHover() &&
+                               hover_controller_.ShouldDraw();
+      // Always hide the close button if the total width can't accomodate all 3
+      // icons. When favicon or alert button is not visible, its space will be
+      // occupied by the title of this tab.
       int title_width =
           (!showing_icon_ + !showing_alert_indicator_) * favicon_width;
-      if ((!hide_inactive_close_button ||
-           (show_close_button_on_hover && hover_controller_.ShouldDraw())) &&
-          (title_width + close_button_width + extra_padding <=
-           available_width)) {
-        showing_close_button_ = true;
-      }
+      if (title_width + close_button_width + extra_padding > available_width)
+        showing_close_button_ = false;
 
       // If no other controls are visible, show favicon even though we
       // don't have enough space. We'll clip the favicon in PaintChildren().
diff --git a/chrome/browser/ui/views/tabs/tab.h b/chrome/browser/ui/views/tabs/tab.h
index f17a611..c8b64d9 100644
--- a/chrome/browser/ui/views/tabs/tab.h
+++ b/chrome/browser/ui/views/tabs/tab.h
@@ -60,6 +60,27 @@
   Tab(TabController* controller, gfx::AnimationContainer* container);
   ~Tab() override;
 
+  // views::View:
+  void ViewHierarchyChanged(
+      const ViewHierarchyChangedDetails& details) override;
+  void OnPaint(gfx::Canvas* canvas) override;
+  void PaintChildren(const views::PaintInfo& info) override;
+  void Layout() override;
+  void OnThemeChanged() override;
+  const char* GetClassName() const override;
+  bool GetTooltipText(const gfx::Point& p,
+                      base::string16* tooltip) const override;
+  bool GetTooltipTextOrigin(const gfx::Point& p,
+                            gfx::Point* origin) const override;
+  bool OnMousePressed(const ui::MouseEvent& event) override;
+  bool OnMouseDragged(const ui::MouseEvent& event) override;
+  void OnMouseReleased(const ui::MouseEvent& event) override;
+  void OnMouseCaptureLost() override;
+  void OnMouseEntered(const ui::MouseEvent& event) override;
+  void OnMouseMoved(const ui::MouseEvent& event) override;
+  void OnMouseExited(const ui::MouseEvent& event) override;
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+
   TabController* controller() const { return controller_; }
 
   // Used to set/check whether this Tab is being animated closed.
@@ -133,10 +154,6 @@
   // user to click to select/activate the tab.
   int GetWidthOfLargestSelectableRegion() const;
 
-  // Called when stacked layout changes and the close button may need to
-  // be updated.
-  void HideCloseButtonForInactiveTabsChanged() { Layout(); }
-
   // Returns the minimum possible size of a single unselected Tab.
   static gfx::Size GetMinimumInactiveSize();
 
@@ -187,27 +204,6 @@
   // views::MaskedTargeterDelegate:
   bool GetHitTestMask(gfx::Path* mask) const override;
 
-  // views::View:
-  void ViewHierarchyChanged(
-      const ViewHierarchyChangedDetails& details) override;
-  void OnPaint(gfx::Canvas* canvas) override;
-  void PaintChildren(const views::PaintInfo& info) override;
-  void Layout() override;
-  void OnThemeChanged() override;
-  const char* GetClassName() const override;
-  bool GetTooltipText(const gfx::Point& p,
-                      base::string16* tooltip) const override;
-  bool GetTooltipTextOrigin(const gfx::Point& p,
-                            gfx::Point* origin) const override;
-  bool OnMousePressed(const ui::MouseEvent& event) override;
-  bool OnMouseDragged(const ui::MouseEvent& event) override;
-  void OnMouseReleased(const ui::MouseEvent& event) override;
-  void OnMouseCaptureLost() override;
-  void OnMouseEntered(const ui::MouseEvent& event) override;
-  void OnMouseMoved(const ui::MouseEvent& event) override;
-  void OnMouseExited(const ui::MouseEvent& event) override;
-  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
-
   // ui::EventHandler:
   void OnGestureEvent(ui::GestureEvent* event) override;
 
diff --git a/chrome/browser/ui/views/tabs/tab_controller.h b/chrome/browser/ui/views/tabs/tab_controller.h
index f83db91401..47e28c4 100644
--- a/chrome/browser/ui/views/tabs/tab_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_controller.h
@@ -39,9 +39,8 @@
   // Returns true if multiple selection is supported.
   virtual bool SupportsMultipleSelection() = 0;
 
-  // Returns true if the close buttons of the inactive tabs are forced to be
-  // hidden.
-  virtual bool ShouldHideCloseButtonForInactiveTabs() = 0;
+  // Returns true if the close button for the given tab is forced to be hidden.
+  virtual bool ShouldHideCloseButtonForTab(Tab* tab) const = 0;
 
   // Returns true if the close button on an inactive tab should be shown on
   // mouse hover. This is predicated on ShouldHideCloseButtonForInactiveTabs()
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index ae512368..56ab43a 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -409,7 +409,11 @@
   }
 
   for (int i = 0; i < tab_count(); ++i)
-    tab_at(i)->HideCloseButtonForInactiveTabsChanged();
+    tab_at(i)->Layout();
+}
+
+bool TabStrip::SingleTabMode() const {
+  return controller_->IsSingleTabModeAvailable() && tab_count() == 1;
 }
 
 bool TabStrip::SizeTabButtonToTopOfTabStrip() {
@@ -429,6 +433,8 @@
 }
 
 void TabStrip::AddTabAt(int model_index, TabRendererData data, bool is_active) {
+  const bool was_single_tab_mode = SingleTabMode();
+
   Tab* tab = new Tab(this, animation_container_.get());
   AddChildView(tab);
   const bool pinned = data.pinned;
@@ -456,6 +462,9 @@
 
   SwapLayoutIfNecessary();
 
+  if (was_single_tab_mode)
+    SingleTabModeChanged();
+
   for (TabStripObserver& observer : observers_)
     observer.OnTabAdded(model_index);
 
@@ -525,6 +534,9 @@
   for (TabStripObserver& observer : observers_)
     observer.OnTabRemoved(model_index);
 
+  if (SingleTabMode())
+    SingleTabModeChanged();
+
   // Stop dragging when a new tab is removed and dragging a window. Doing
   // otherwise results in a confusing state if the user attempts to reattach. We
   // could allow this and make TabDragController update itself during the
@@ -781,7 +793,9 @@
   return touch_layout_ == nullptr;
 }
 
-bool TabStrip::ShouldHideCloseButtonForInactiveTabs() {
+bool TabStrip::ShouldHideCloseButtonForTab(Tab* tab) const {
+  if (tab->IsActive())
+    return SingleTabMode();
   return touch_layout_ != nullptr || MD::IsRefreshUi();
 }
 
@@ -2406,6 +2420,12 @@
     RemoveMessageLoopObserver();
 }
 
+void TabStrip::SingleTabModeChanged() {
+  const int active_tab_index = controller_->GetActiveIndex();
+  if (IsValidModelIndex(active_tab_index))
+    tab_at(active_tab_index)->Layout();
+}
+
 void TabStrip::ButtonPressed(views::Button* sender, const ui::Event& event) {
   if (sender == new_tab_button_) {
     base::RecordAction(UserMetricsAction("NewTab_Button"));
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index b49b8b4..bd08879e 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -124,6 +124,13 @@
   // Sets |stacked_layout_| and animates if necessary.
   void SetStackedLayout(bool stacked_layout);
 
+  // Whether the special painting mode for a single tab is enabled. This is only
+  // true if both the mode is available and we have exactly one tab.
+  bool SingleTabMode() const;
+
+  // Called when the value of SingleTabMode() changes.
+  void SingleTabModeChanged();
+
   // Returns the bounds of the new tab button.
   gfx::Rect new_tab_button_bounds() const { return new_tab_button_bounds_; }
 
@@ -219,7 +226,7 @@
   // TabController overrides:
   const ui::ListSelectionModel& GetSelectionModel() const override;
   bool SupportsMultipleSelection() override;
-  bool ShouldHideCloseButtonForInactiveTabs() override;
+  bool ShouldHideCloseButtonForTab(Tab* tab) const override;
   bool ShouldShowCloseButtonOnHover() override;
   bool MaySetClip() override;
   void SelectTab(Tab* tab) override;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_controller.h b/chrome/browser/ui/views/tabs/tab_strip_controller.h
index e466357..e4e120f9 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_strip_controller.h
@@ -102,6 +102,9 @@
   // Invoked if the stacked layout (on or off) might have changed.
   virtual void StackedLayoutMaybeChanged() = 0;
 
+  // Whether the special painting mode for one tab is allowed.
+  virtual bool IsSingleTabModeAvailable() = 0;
+
   // Notifies controller that the user started dragging this tabstrip's tabs.
   virtual void OnStartedDraggingTabs() = 0;
 
diff --git a/chrome/browser/ui/views/tabs/tab_unittest.cc b/chrome/browser/ui/views/tabs/tab_unittest.cc
index f764558..c3b1199e 100644
--- a/chrome/browser/ui/views/tabs/tab_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_unittest.cc
@@ -38,9 +38,7 @@
     return selection_model_;
   }
   bool SupportsMultipleSelection() override { return false; }
-  bool ShouldHideCloseButtonForInactiveTabs() override {
-    return false;
-  }
+  bool ShouldHideCloseButtonForTab(Tab* tab) const override { return false; }
   bool ShouldShowCloseButtonOnHover() override { return false; }
   bool MaySetClip() override { return false; }
   void SelectTab(Tab* tab) override {}
@@ -297,8 +295,9 @@
 
 TEST_F(TabTest, LayoutAndVisibilityOfElements) {
   static const TabAlertState kAlertStatesToTest[] = {
-      TabAlertState::NONE, TabAlertState::TAB_CAPTURING,
+      TabAlertState::NONE,          TabAlertState::TAB_CAPTURING,
       TabAlertState::AUDIO_PLAYING, TabAlertState::AUDIO_MUTING,
+      TabAlertState::PIP_PLAYING,
   };
 
   Widget widget;
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index c099e1ac..01ab13e 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -738,13 +738,9 @@
   UserContext user_context(user_type,
                            GetAccountId(email, gaia_id, AccountType::GOOGLE));
   user_context.SetKey(Key(password));
-  // Only save the password for enterprise users. See https://crbug.com/386606.
-  const bool is_enterprise_managed = g_browser_process->platform_part()
-                                         ->browser_policy_connector_chromeos()
-                                         ->IsEnterpriseManaged();
-  if (is_enterprise_managed) {
-    user_context.SetPasswordKey(Key(password));
-  }
+  // Save the user's plaintext password for possible authentication to a
+  // network. See https://crbug.com/386606 for details.
+  user_context.SetPasswordKey(Key(password));
   user_context.SetAuthCode(auth_code);
   user_context.SetAuthFlow(using_saml
                                ? UserContext::AUTH_FLOW_GAIA_WITH_SAML
@@ -857,6 +853,19 @@
       user ? UserContext(*user)
            : UserContext(CalculateUserType(account_id), account_id);
   user_context.SetKey(Key(password));
+  // Save the user's plaintext password for possible authentication to a
+  // network. If the user's OpenNetworkConfiguration policy contains a
+  // ${PASSWORD} variable, then the user's password will be used to authenticate
+  // to the specified network.
+  //
+  // The user's password needs to be saved in memory until the policy can be
+  // examined. When the policy comes in, if it does not contain the ${PASSWORD}
+  // variable, the user's password will be discarded. If it contains the
+  // password, it will be sent to the session manager, which will then save it
+  // in a keyring so it can be retrieved for authenticating to the network.
+  //
+  // More details can be found in https://crbug.com/386606
+  user_context.SetPasswordKey(Key(password));
   user_context.SetAuthFlow(using_saml
                                ? UserContext::AUTH_FLOW_GAIA_WITH_SAML
                                : UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML);
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 7a2891c..7fb22d7f 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -1229,13 +1229,9 @@
     user_context = UserContext(*user);
   }
   user_context.SetKey(Key(password));
-  // Only save the password for enterprise users. See https://crbug.com/386606.
-  const bool is_enterprise_managed = g_browser_process->platform_part()
-                                         ->browser_policy_connector_chromeos()
-                                         ->IsEnterpriseManaged();
-  if (is_enterprise_managed) {
-    user_context.SetPasswordKey(Key(password));
-  }
+  // Save the user's plaintext password for possible authentication to a
+  // network. See https://crbug.com/386606 for details.
+  user_context.SetPasswordKey(Key(password));
   user_context.SetIsUsingPin(authenticated_by_pin);
   if (account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY &&
       (user_context.GetUserType() !=
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc b/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
index 0fd7181..4352ef8 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
@@ -84,8 +84,7 @@
                       IDS_SYNC_CONFIRMATION_UNITY_OPTIONS_USE_DEFAULT_TITLE);
     AddStringResource(source, "syncConfirmationOptionsUseDefaultSubtitle",
                       IDS_SYNC_CONFIRMATION_UNITY_OPTIONS_USE_DEFAULT_SUBTITLE);
-    AddStringResource(source, "syncConfirmationOptionsConfirmLabel",
-                      IDS_SYNC_CONFIRMATION_UNITY_OPTIONS_CONFIRM_BUTTON_LABEL);
+    AddStringResource(source, "syncConfirmationOptionsConfirmLabel", IDS_OK);
     AddStringResource(source, "syncConfirmationOptionsBackLabel",
                       IDS_SYNC_CONFIRMATION_UNITY_OPTIONS_BACK_BUTTON_LABEL);
     AddStringResource(source, "syncConsentBumpTitle",
diff --git a/chrome/browser/vr/service/vr_display_host.cc b/chrome/browser/vr/service/vr_display_host.cc
index 1e3ce662..80b8a5f 100644
--- a/chrome/browser/vr/service/vr_display_host.cc
+++ b/chrome/browser/vr/service/vr_display_host.cc
@@ -42,6 +42,8 @@
                              device::mojom::VRServiceClient* service_client,
                              device::mojom::VRDisplayInfoPtr display_info)
     : browser_device_(device),
+      // TODO(https://crbug.com/846392): render_frame_host can be null because
+      // of a test, not because a VRDisplayHost can be created without it.
       in_focused_frame_(
           render_frame_host ? render_frame_host->GetView()->HasFocus() : false),
       render_frame_host_(render_frame_host),
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index d9523dc..77a93b9 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -66,7 +66,7 @@
 // Enables the built-in DNS resolver.
 const base::Feature kAsyncDns {
   "AsyncDns",
-#if defined(OS_CHROMEOS) || defined(OS_MACOSX)
+#if defined(OS_CHROMEOS) || defined(OS_MACOSX) || defined(OS_ANDROID)
       base::FEATURE_ENABLED_BY_DEFAULT
 #else
       base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/chrome/common/safe_browsing/rar_analyzer.cc b/chrome/common/safe_browsing/rar_analyzer.cc
index 5d73d41..d248fb92 100644
--- a/chrome/common/safe_browsing/rar_analyzer.cc
+++ b/chrome/common/safe_browsing/rar_analyzer.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include "base/files/file_path.h"
 #include "build/build_config.h"
 #include "chrome/common/safe_browsing/archive_analyzer_results.h"
 #include "chrome/common/safe_browsing/file_type_policies.h"
@@ -15,24 +16,21 @@
 namespace rar_analyzer {
 
 void AnalyzeRarFile(base::File rar_file,
-                    const base::FilePath& rar_file_path,
                     ArchiveAnalyzerResults* results) {
   auto archive = std::make_unique<third_party_unrar::Archive>();
   archive->SetFileHandle(rar_file.GetPlatformFile());
-  std::wstring wide_rar_file_path(rar_file_path.value().begin(),
-                                  rar_file_path.value().end());
-  if (!archive->Open(wide_rar_file_path.c_str())) {
+  if (!archive->Open(L"dummy.rar")) {
     results->success = false;
     // TODO(vakh): Log UMA here.
-    VLOG(1) << __FUNCTION__
-            << ": Unable to open rar_file: " << rar_file.GetPlatformFile();
+    DLOG(ERROR) << __FUNCTION__
+                << ": Unable to open rar_file: " << rar_file.GetPlatformFile();
     return;
   }
   if (!archive->IsArchive(/*EnableBroken=*/true)) {
     // TODO(vakh): Log UMA here.
     results->success = false;
-    VLOG(1) << __FUNCTION__
-            << ": !IsArchive: rar_file: " << rar_file.GetPlatformFile();
+    DLOG(ERROR) << __FUNCTION__
+                << ": !IsArchive: rar_file: " << rar_file.GetPlatformFile();
     return;
   }
   // TODO(vakh): Log UMA here.
@@ -43,7 +41,6 @@
        archive->ReadHeader() > 0 &&
        archive->GetHeaderType() != third_party_unrar::kUnrarEndarcHead;
        archive->SeekToNext()) {
-    VLOG(2) << __FUNCTION__ << ": FileName: " << archive->FileHead.FileName;
     std::wstring wide_filename(archive->FileHead.FileName);
 #if defined(OS_WIN)
     base::FilePath file_path(wide_filename);
@@ -51,15 +48,12 @@
     std::string filename(wide_filename.begin(), wide_filename.end());
     base::FilePath file_path(filename);
 #endif  // OS_WIN
-    VLOG(2) << __FUNCTION__ << ": file_path: " << file_path.value().c_str();
 
     bool is_executable =
         FileTypePolicies::GetInstance()->IsCheckedBinaryFile(file_path);
-    VLOG(2) << __FUNCTION__ << ": is_executable: " << is_executable;
     results->has_executable |= is_executable;
 
     bool is_archive = FileTypePolicies::GetInstance()->IsArchiveFile(file_path);
-    VLOG(2) << __FUNCTION__ << ": is_archive: " << is_archive;
     results->has_archive |= is_archive;
     if (is_archive) {
       archived_archive_filenames.insert(file_path.BaseName());
diff --git a/chrome/common/safe_browsing/rar_analyzer.h b/chrome/common/safe_browsing/rar_analyzer.h
index 289450e..c1a3b49 100644
--- a/chrome/common/safe_browsing/rar_analyzer.h
+++ b/chrome/common/safe_browsing/rar_analyzer.h
@@ -22,7 +22,6 @@
 #define CHROME_COMMON_SAFE_BROWSING_RAR_ANALYZER_H_
 
 #include "base/files/file.h"
-#include "base/files/file_path.h"
 
 namespace safe_browsing {
 
@@ -33,14 +32,11 @@
 // |rar_file| is a platform-agnostic handle to the file. Since |AnalyzeRarFile|
 // runs inside a sandbox, it isn't allowed to open file handles. So the file is
 // opened in |SandboxedRarAnalyzer|, which runs in the browser process, and the
-// handle is passed here. |rar_file_path| is the path to the same file. It is
-// required only because the unrar library expects it to be used with a
-// filename. The function populates the various fields in |results| based on the
-// results of parsing the rar file.
+// handle is passed here. The function populates the various fields in |results|
+// based on the results of parsing the rar file.
 // If the parsing fails for any reason, including crashing the sandbox process,
 // the browser process considers the file safe.
 void AnalyzeRarFile(base::File rar_file,
-                    const base::FilePath& rar_file_path,
                     ArchiveAnalyzerResults* results);
 
 }  // namespace rar_analyzer
diff --git a/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.cc b/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.cc
index 133755bd..f9531cd 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.cc
+++ b/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.cc
@@ -49,7 +49,7 @@
       &SandboxedRarAnalyzer::AnalyzeFileDone, base::Unretained(this),
       safe_browsing::ArchiveAnalyzerResults()));
   analyzer_ptr_->AnalyzeRarFile(
-      std::move(file), file_path_,
+      std::move(file),
       base::BindOnce(&SandboxedRarAnalyzer::AnalyzeFileDone, this));
 }
 
diff --git a/chrome/services/file_util/public/mojom/safe_archive_analyzer.mojom b/chrome/services/file_util/public/mojom/safe_archive_analyzer.mojom
index 650f037b..6983f7b 100644
--- a/chrome/services/file_util/public/mojom/safe_archive_analyzer.mojom
+++ b/chrome/services/file_util/public/mojom/safe_archive_analyzer.mojom
@@ -9,7 +9,6 @@
 module chrome.mojom;
 
 import "mojo/public/mojom/base/file.mojom";
-import "mojo/public/mojom/base/file_path.mojom";
 
 interface SafeArchiveAnalyzer {
   // Build flag FULL_SAFE_BROWSING: Analyze the |zip_file| for malicious
@@ -26,8 +25,7 @@
 
   // Build flag FULL_SAFE_BROWSING: Analyze the |rar_file| for malicious
   // download protection.
-  AnalyzeRarFile(mojo_base.mojom.File rar_file,
-                 mojo_base.mojom.FilePath rar_file_path)
+  AnalyzeRarFile(mojo_base.mojom.File rar_file)
       => (SafeArchiveAnalyzerResults results);
 };
 
diff --git a/chrome/services/file_util/safe_archive_analyzer.cc b/chrome/services/file_util/safe_archive_analyzer.cc
index 37cdb11..a13b42b1 100644
--- a/chrome/services/file_util/safe_archive_analyzer.cc
+++ b/chrome/services/file_util/safe_archive_analyzer.cc
@@ -46,13 +46,10 @@
 }
 
 void SafeArchiveAnalyzer::AnalyzeRarFile(base::File rar_file,
-                                         const base::FilePath& rar_file_path,
                                          AnalyzeRarFileCallback callback) {
   DCHECK(rar_file.IsValid());
-  DCHECK(!rar_file_path.value().empty());
 
   safe_browsing::ArchiveAnalyzerResults results;
-  safe_browsing::rar_analyzer::AnalyzeRarFile(std::move(rar_file),
-                                              rar_file_path, &results);
+  safe_browsing::rar_analyzer::AnalyzeRarFile(std::move(rar_file), &results);
   std::move(callback).Run(results);
 }
diff --git a/chrome/services/file_util/safe_archive_analyzer.h b/chrome/services/file_util/safe_archive_analyzer.h
index 4c5924e9..f21efa6 100644
--- a/chrome/services/file_util/safe_archive_analyzer.h
+++ b/chrome/services/file_util/safe_archive_analyzer.h
@@ -10,7 +10,6 @@
 
 namespace base {
 class File;
-class FilePath;
 }
 
 class SafeArchiveAnalyzer : public chrome::mojom::SafeArchiveAnalyzer {
@@ -27,7 +26,6 @@
   void AnalyzeDmgFile(base::File dmg_file,
                       AnalyzeDmgFileCallback callback) override;
   void AnalyzeRarFile(base::File rar_file,
-                      const base::FilePath& rar_file_path,
                       AnalyzeRarFileCallback callback) override;
 
   const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
diff --git a/chrome/test/data/extensions/api_test/file_browser/crostini_test/manifest.json b/chrome/test/data/extensions/api_test/file_browser/crostini_test/manifest.json
new file mode 100644
index 0000000..48bba8c9
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_browser/crostini_test/manifest.json
@@ -0,0 +1,16 @@
+{
+  // chrome-extension://pkplfbidichfdicaijlchgnapepdginl
+  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtDfX9dHNh948bt00YhZBm3P6E5QLaOt+v8kXVtibQfiPtOD2FTScB/f0wX/EQWVO7BkaSOsRkTPcPIgocyMPYr2FLgqGLFlYT9nQpKJZUFNF5oJ5rG6Nv7ppf4zEB3j6da1IBRTz2yOZ+6O1TMZxol/V62/QcqrJeggsHTEPGLdr9Ua4b1Ka0xKJnJngZljsbw93FI1o+P9dAh5BS6wTPiZI/vmJVjvMTkSTnaZ3n9Go2t7A0XLcSxLcVyuLAd2mAvSN0mIviOukdM66wr7llif71nKuUt+4qvlr/r9HfwzN6pA4jkwhtS1UD+3CmB+wsHwsnohNcuu4FIQ6rgq/7QIDAQAB",
+  "name": "chrome.fileManagerPrivate tests",
+  "version": "0.1",
+  "manifest_version": 2,
+  "description": "Tests of chrome.fileManagerPrivate.isCrostiniEnabled",
+  "app": {
+    "background": {
+      "scripts": ["test.js"]
+    }
+  },
+  "permissions": [
+    "fileManagerPrivate"
+  ]
+}
diff --git a/chrome/test/data/extensions/api_test/file_browser/crostini_test/test.js b/chrome/test/data/extensions/api_test/file_browser/crostini_test/test.js
new file mode 100644
index 0000000..90d7203
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_browser/crostini_test/test.js
@@ -0,0 +1,13 @@
+// Copyright 2018 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.
+
+// Run the tests.
+chrome.test.runTests([
+  function testIsCrostiniEnabled() {
+    chrome.fileManagerPrivate.isCrostiniEnabled(
+        chrome.test.callbackPass((enabled) => {
+          chrome.test.assertTrue(enabled);
+        }));
+  },
+]);
diff --git a/chrome/test/data/extensions/declarative_net_request/child_frame_with_subresources.html b/chrome/test/data/extensions/declarative_net_request/child_frame_with_subresources.html
new file mode 100644
index 0000000..1bcd9c7
--- /dev/null
+++ b/chrome/test/data/extensions/declarative_net_request/child_frame_with_subresources.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Child frame with subresources</title>
+</head>
+<body>
+
+  <link rel="stylesheet" type="text/css" href="iframe_subresources/style.css">
+  <script type="text/javascript" src="iframe_subresources/script.js"></script>
+  <img src="iframe_subresources/image.png">
+  <audio src="iframe_subresources/ping.mp3"></audio>
+  <iframe src="iframe_subresources/subframe.html"></iframe>
+
+</body>
+</html>
diff --git a/chrome/test/data/extensions/declarative_net_request/iframe_subresources/image.png b/chrome/test/data/extensions/declarative_net_request/iframe_subresources/image.png
new file mode 100644
index 0000000..85f38b2
--- /dev/null
+++ b/chrome/test/data/extensions/declarative_net_request/iframe_subresources/image.png
Binary files differ
diff --git a/chrome/test/data/extensions/declarative_net_request/iframe_subresources/ping.mp3 b/chrome/test/data/extensions/declarative_net_request/iframe_subresources/ping.mp3
new file mode 100644
index 0000000..75b83ba
--- /dev/null
+++ b/chrome/test/data/extensions/declarative_net_request/iframe_subresources/ping.mp3
Binary files differ
diff --git a/chrome/test/data/extensions/declarative_net_request/iframe_subresources/script.js b/chrome/test/data/extensions/declarative_net_request/iframe_subresources/script.js
new file mode 100644
index 0000000..171296e
--- /dev/null
+++ b/chrome/test/data/extensions/declarative_net_request/iframe_subresources/script.js
@@ -0,0 +1,5 @@
+// Copyright 2018 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.
+
+window.scriptExecuted = true;
diff --git a/chrome/test/data/extensions/declarative_net_request/iframe_subresources/style.css b/chrome/test/data/extensions/declarative_net_request/iframe_subresources/style.css
new file mode 100644
index 0000000..ec3e97d
--- /dev/null
+++ b/chrome/test/data/extensions/declarative_net_request/iframe_subresources/style.css
@@ -0,0 +1,8 @@
+/* Copyright 2018 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.
+ */
+
+body {
+    background-color: rgb(0, 0, 255);
+}
diff --git a/chrome/test/data/extensions/declarative_net_request/iframe_subresources/subframe.html b/chrome/test/data/extensions/declarative_net_request/iframe_subresources/subframe.html
new file mode 100644
index 0000000..c3a20e1
--- /dev/null
+++ b/chrome/test/data/extensions/declarative_net_request/iframe_subresources/subframe.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Subframe</title>
+</head>
+<body>
+</body>
+</html>
diff --git a/chrome/test/data/extensions/declarative_net_request/whitelisting_api.html b/chrome/test/data/extensions/declarative_net_request/whitelisting_api.html
new file mode 100644
index 0000000..ef9c2ee
--- /dev/null
+++ b/chrome/test/data/extensions/declarative_net_request/whitelisting_api.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Tests the page whitelisting API</title>
+</head>
+<body>
+
+  <link rel="stylesheet" type="text/css" href="subresources/style.css">
+  <script type="text/javascript" src="subresources/script.js"></script>
+  <img src="subresources/image.png">
+  <audio src="subresources/ping.mp3"></audio>
+  <iframe src="child_frame_with_subresources.html"></iframe>
+
+</body>
+</html>
diff --git a/chrome/test/data/media/picture-in-picture/player_metadata_poster.html b/chrome/test/data/media/picture-in-picture/player_metadata_poster.html
new file mode 100644
index 0000000..02978fd
--- /dev/null
+++ b/chrome/test/data/media/picture-in-picture/player_metadata_poster.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<video preload=metadata poster='../../banners/image-512px.png' src='../bigbuck.webm'></video>
+<script>
+  function enterPictureInPicture() {
+    document.querySelector('video').requestPictureInPicture().then(() => {
+      document.title = 'entered_pip';
+    });
+  }
+</script>
diff --git a/chrome/test/data/vr/e2e_test_files/html/test_webxr_gamepad_support.html b/chrome/test/data/vr/e2e_test_files/html/test_webxr_gamepad_support.html
new file mode 100644
index 0000000..0b3770a
--- /dev/null
+++ b/chrome/test/data/vr/e2e_test_files/html/test_webxr_gamepad_support.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<!--
+Tests that a Gamepad API gamepad is or is not returned under different
+circumstances.
+-->
+<html>
+  <head>
+    <link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css">
+  </head>
+  <body>
+    <canvas id="webgl-canvas"></canvas>
+    <script src="../../../../../../third_party/WebKit/LayoutTests/resources/testharness.js"></script>
+    <script src="../resources/webxr_e2e.js"></script>
+    <script src="../resources/webxr_boilerplate.js"></script>
+    <script>
+      var t = async_test("Gamepad returned while using WebXR when expected.");
+
+      // We apparently need to register a listener, otherwise all gamepads are
+      // always null.
+      window.addEventListener("gamepadconnected", function(e) {});
+
+      function stepAssertNumGamepadsMatchesExpectation(numExpectedGamepads) {
+        var numGamepads = 0;
+        for (gamepad of navigator.getGamepads()) {
+          if (gamepad !== null) {
+            numGamepads++;
+          }
+        }
+        t.step( () => {
+          assert_equals(numGamepads, numExpectedGamepads,
+              "Number of returned gamepads matches expectation");
+        });
+        t.done();
+      }
+    </script>
+  </body>
+</html>
\ No newline at end of file
diff --git a/chrome/test/data/vr/e2e_test_files/html/test_webxr_gamepad_support_nowebxr.html b/chrome/test/data/vr/e2e_test_files/html/test_webxr_gamepad_support_nowebxr.html
new file mode 100644
index 0000000..439e0b52
--- /dev/null
+++ b/chrome/test/data/vr/e2e_test_files/html/test_webxr_gamepad_support_nowebxr.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<!--
+Tests that a Gamepad API gamepad is or is not returned under different
+circumstances. Does not have WebXR code.
+-->
+<html>
+  <head>
+    <link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css">
+  </head>
+  <body>
+    <canvas id="webgl-canvas"></canvas>
+    <script src="../../../../../../third_party/WebKit/LayoutTests/resources/testharness.js"></script>
+    <script src="../resources/webxr_e2e.js"></script>
+    <script>
+      var t = async_test("Gamepad returned while using WebXR when expected.");
+
+      // We apparently need to register a listener, otherwise all gamepads are
+      // always null.
+      window.addEventListener("gamepadconnected", function(e) {});
+
+      function stepAssertNumGamepadsMatchesExpectation(numExpectedGamepads) {
+        var numGamepads = 0;
+        for (gamepad of navigator.getGamepads()) {
+          if (gamepad !== null) {
+            numGamepads++;
+          }
+        }
+        t.step( () => {
+          assert_equals(numGamepads, numExpectedGamepads,
+              "Number of returned gamepads matches expectation");
+        });
+        t.done();
+      }
+    </script>
+  </body>
+</html>
\ No newline at end of file
diff --git a/chromeos/components/drivefs/mojom/drivefs.mojom b/chromeos/components/drivefs/mojom/drivefs.mojom
index a005d02..2eb759f 100644
--- a/chromeos/components/drivefs/mojom/drivefs.mojom
+++ b/chromeos/components/drivefs/mojom/drivefs.mojom
@@ -20,8 +20,10 @@
 
 // Implemented by DriveFS, used from Chrome.
 interface DriveFs {
-  // Returns the metadata for |path|.
-  GetMetadata(mojo_base.mojom.FilePath path) => (
+  // Returns the metadata for |path|. Thumbnail requests may require requesting
+  // a thumbnail from the server so thumbnails are only populated for requests
+  // where |want_thumbnail| is true.
+  GetMetadata(mojo_base.mojom.FilePath path, bool want_thumbnail) => (
       FileError error, FileMetadata? metadata);
 
   // Sets the file at |path| to pinned or unpinned depending on the value of
@@ -90,6 +92,9 @@
   // A URL to open the file in the Drive website.
   string alternate_url;
 
+  // A URL to download the file.
+  string download_url;
+
   mojo_base.mojom.Time modification_time;
   mojo_base.mojom.Time modification_by_me_time;
 
@@ -102,6 +107,10 @@
 
   // May be present if the file is an image.
   ImageMetadata? image_metadata;
+
+  // The thumbnail as a PNG. It is only set if |want_thumbnail| is true in the
+  // request and the file has a thumbnail available.
+  array<uint8>? thumbnail;
 };
 
 struct ImageMetadata {
diff --git a/chromeos/dbus/cryptohome_client.cc b/chromeos/dbus/cryptohome_client.cc
index 9bb6655d..6d0945d 100644
--- a/chromeos/dbus/cryptohome_client.cc
+++ b/chromeos/dbus/cryptohome_client.cc
@@ -932,6 +932,46 @@
                          request, std::move(callback));
   }
 
+  void IsQuotaSupported(DBusMethodCallback<bool> callback) override {
+    dbus::MethodCall method_call(
+        cryptohome::kCryptohomeInterface,
+        cryptohome::kCryptohomeIsQuotaSupported);
+
+    proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&CryptohomeClientImpl::OnBoolMethod,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+
+  }
+
+  void GetCurrentSpaceForUid(const uid_t uid,
+                             DBusMethodCallback<int64_t> callback) override {
+    dbus::MethodCall method_call(cryptohome::kCryptohomeInterface,
+                                 cryptohome::kCryptohomeGetCurrentSpaceForUid);
+
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendUint32(uid);
+
+    proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&CryptohomeClientImpl::OnInt64DBusMethod,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
+  void GetCurrentSpaceForGid(const gid_t gid,
+                             DBusMethodCallback<int64_t> callback) override {
+    dbus::MethodCall method_call(cryptohome::kCryptohomeInterface,
+                                 cryptohome::kCryptohomeGetCurrentSpaceForGid);
+
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendUint32(gid);
+
+    proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&CryptohomeClientImpl::OnInt64DBusMethod,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
  protected:
   void Init(dbus::Bus* bus) override {
     proxy_ = bus->GetObjectProxy(
@@ -1053,6 +1093,21 @@
     std::move(callback).Run(result);
   }
 
+  void OnInt64DBusMethod(DBusMethodCallback<int64_t> callback,
+                          dbus::Response* response) {
+    if (!response) {
+      std::move(callback).Run(base::nullopt);
+      return;
+    }
+    int64_t value = 0;
+    dbus::MessageReader reader(response);
+    if (!reader.PopInt64(&value)) {
+      std::move(callback).Run(base::nullopt);
+      return;
+    }
+    std::move(callback).Run(value);
+  }
+
   // Handles responses for methods with a string value result.
   void OnStringMethod(DBusMethodCallback<std::string> callback,
                       dbus::Response* response) {
diff --git a/chromeos/dbus/cryptohome_client.h b/chromeos/dbus/cryptohome_client.h
index c66cb73..f4de86f 100644
--- a/chromeos/dbus/cryptohome_client.h
+++ b/chromeos/dbus/cryptohome_client.h
@@ -592,6 +592,18 @@
       const cryptohome::GetSupportedKeyPoliciesRequest& request,
       DBusMethodCallback<cryptohome::BaseReply> callback) = 0;
 
+  // Calls IsQuotaSupported to know whether quota is supported by cryptohome.
+  virtual void IsQuotaSupported(
+      DBusMethodCallback<bool> callback) = 0;
+
+  // Calls GetCurrentSpaceForUid to get the current disk space for a uid.
+  virtual void GetCurrentSpaceForUid(const uid_t uid,
+                                     DBusMethodCallback<int64_t> callback) = 0;
+
+  // Calls GetCurrentSpaceForGid to get the current disk space for a gid.
+  virtual void GetCurrentSpaceForGid(const gid_t gid,
+                                     DBusMethodCallback<int64_t> callback) = 0;
+
  protected:
   // Create() should be used instead.
   CryptohomeClient();
diff --git a/chromeos/dbus/fake_cryptohome_client.cc b/chromeos/dbus/fake_cryptohome_client.cc
index abb2617..27ad133 100644
--- a/chromeos/dbus/fake_cryptohome_client.cc
+++ b/chromeos/dbus/fake_cryptohome_client.cc
@@ -683,6 +683,17 @@
   ReturnProtobufMethodCallback(reply, std::move(callback));
 }
 
+void FakeCryptohomeClient::IsQuotaSupported(DBusMethodCallback<bool> callback) {
+}
+
+void FakeCryptohomeClient::GetCurrentSpaceForUid(
+    uid_t uid,
+    DBusMethodCallback<int64_t> callback) {}
+
+void FakeCryptohomeClient::GetCurrentSpaceForGid(
+    gid_t gid,
+    DBusMethodCallback<int64_t> callback) {}
+
 void FakeCryptohomeClient::SetServiceIsAvailable(bool is_available) {
   service_is_available_ = is_available;
   if (!is_available)
diff --git a/chromeos/dbus/fake_cryptohome_client.h b/chromeos/dbus/fake_cryptohome_client.h
index 97d67001..fe5e9360 100644
--- a/chromeos/dbus/fake_cryptohome_client.h
+++ b/chromeos/dbus/fake_cryptohome_client.h
@@ -206,6 +206,11 @@
   void GetSupportedKeyPolicies(
       const cryptohome::GetSupportedKeyPoliciesRequest& request,
       DBusMethodCallback<cryptohome::BaseReply> callback) override;
+  void IsQuotaSupported(DBusMethodCallback<bool> callback) override;
+  void GetCurrentSpaceForUid(uid_t uid,
+                             DBusMethodCallback<int64_t> callback) override;
+  void GetCurrentSpaceForGid(gid_t gid,
+                             DBusMethodCallback<int64_t> callback) override;
 
   /////////// Test helpers ////////////
 
diff --git a/chromeos/services/device_sync/device_sync_impl.cc b/chromeos/services/device_sync/device_sync_impl.cc
index b9839b3..22a6ea5c 100644
--- a/chromeos/services/device_sync/device_sync_impl.cc
+++ b/chromeos/services/device_sync/device_sync_impl.cc
@@ -49,7 +49,7 @@
     identity::IdentityManager* identity_manager,
     gcm::GCMDriver* gcm_driver,
     service_manager::Connector* connector,
-    cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
+    const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
     scoped_refptr<net::URLRequestContextGetter> url_request_context) {
   if (test_factory_instance_) {
     return test_factory_instance_->BuildInstance(
@@ -74,7 +74,7 @@
     identity::IdentityManager* identity_manager,
     gcm::GCMDriver* gcm_driver,
     service_manager::Connector* connector,
-    cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
+    const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
     scoped_refptr<net::URLRequestContextGetter> url_request_context) {
   return base::WrapUnique(new DeviceSyncImpl(
       identity_manager, gcm_driver, connector, gcm_device_info_provider,
@@ -103,7 +103,7 @@
     identity::IdentityManager* identity_manager,
     gcm::GCMDriver* gcm_driver,
     service_manager::Connector* connector,
-    cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
+    const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
     scoped_refptr<net::URLRequestContextGetter> url_request_context,
     base::Clock* clock,
     std::unique_ptr<PrefConnectionDelegate> pref_connection_delegate)
diff --git a/chromeos/services/device_sync/device_sync_impl.h b/chromeos/services/device_sync/device_sync_impl.h
index b9117f4e..de0ca741 100644
--- a/chromeos/services/device_sync/device_sync_impl.h
+++ b/chromeos/services/device_sync/device_sync_impl.h
@@ -67,7 +67,7 @@
         identity::IdentityManager* identity_manager,
         gcm::GCMDriver* gcm_driver,
         service_manager::Connector* connector,
-        cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
+        const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
         scoped_refptr<net::URLRequestContextGetter> url_request_context);
     static void SetInstanceForTesting(Factory* test_factory);
 
@@ -76,7 +76,7 @@
         identity::IdentityManager* identity_manager,
         gcm::GCMDriver* gcm_driver,
         service_manager::Connector* connector,
-        cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
+        const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
         scoped_refptr<net::URLRequestContextGetter> url_request_context);
 
    private:
@@ -135,7 +135,7 @@
       identity::IdentityManager* identity_manager,
       gcm::GCMDriver* gcm_driver,
       service_manager::Connector* connector,
-      cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
+      const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
       scoped_refptr<net::URLRequestContextGetter> url_request_context,
       base::Clock* clock,
       std::unique_ptr<PrefConnectionDelegate> pref_connection_delegate);
@@ -174,7 +174,7 @@
   identity::IdentityManager* identity_manager_;
   gcm::GCMDriver* gcm_driver_;
   service_manager::Connector* connector_;
-  cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider_;
+  const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider_;
   scoped_refptr<net::URLRequestContextGetter> url_request_context_;
   base::Clock* clock_;
   std::unique_ptr<PrefConnectionDelegate> pref_connection_delegate_;
diff --git a/chromeos/services/device_sync/device_sync_service.cc b/chromeos/services/device_sync/device_sync_service.cc
index 8241171d..5014da15 100644
--- a/chromeos/services/device_sync/device_sync_service.cc
+++ b/chromeos/services/device_sync/device_sync_service.cc
@@ -17,7 +17,7 @@
 DeviceSyncService::DeviceSyncService(
     identity::IdentityManager* identity_manager,
     gcm::GCMDriver* gcm_driver,
-    cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
+    const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
     scoped_refptr<net::URLRequestContextGetter> url_request_context)
     : identity_manager_(identity_manager),
       gcm_driver_(gcm_driver),
diff --git a/chromeos/services/device_sync/device_sync_service.h b/chromeos/services/device_sync/device_sync_service.h
index 78546e1..15902f0 100644
--- a/chromeos/services/device_sync/device_sync_service.h
+++ b/chromeos/services/device_sync/device_sync_service.h
@@ -42,7 +42,7 @@
   DeviceSyncService(
       identity::IdentityManager* identity_manager,
       gcm::GCMDriver* gcm_driver,
-      cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
+      const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
       scoped_refptr<net::URLRequestContextGetter> url_request_context);
   ~DeviceSyncService() override;
 
@@ -56,7 +56,7 @@
  private:
   identity::IdentityManager* identity_manager_;
   gcm::GCMDriver* gcm_driver_;
-  cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider_;
+  const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider_;
   scoped_refptr<net::URLRequestContextGetter> url_request_context_;
 
   std::unique_ptr<DeviceSyncBase> device_sync_;
diff --git a/chromeos/services/device_sync/device_sync_service_unittest.cc b/chromeos/services/device_sync/device_sync_service_unittest.cc
index 21219c64..1780311 100644
--- a/chromeos/services/device_sync/device_sync_service_unittest.cc
+++ b/chromeos/services/device_sync/device_sync_service_unittest.cc
@@ -420,7 +420,7 @@
         identity::IdentityManager* identity_manager,
         gcm::GCMDriver* gcm_driver,
         service_manager::Connector* connector,
-        cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
+        const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
         scoped_refptr<net::URLRequestContextGetter> url_request_context)
         override {
       return base::WrapUnique(new DeviceSyncImpl(
diff --git a/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc b/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
index 6a65ebf8..e5bb4bc 100644
--- a/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
+++ b/chromeos/services/device_sync/public/cpp/device_sync_client_impl_unittest.cc
@@ -59,7 +59,7 @@
       identity::IdentityManager* identity_manager,
       gcm::GCMDriver* gcm_driver,
       service_manager::Connector* connector,
-      cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
+      const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider,
       scoped_refptr<net::URLRequestContextGetter> url_request_context)
       override {
     EXPECT_TRUE(fake_device_sync_);
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 2fb9c33..e77341b70 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -4557,8 +4557,16 @@
 }
 
 // Tests that DeleteDisusedCreditCards deletes desired credit cards only.
+// Disabled on Android, see crbug.com/848227
+#if defined(OS_ANDROID)
+#define MAYBE_DeleteDisusedCreditCards_OnlyDeleteExpiredDisusedLocalCards \
+  DISABLED_DeleteDisusedCreditCards_OnlyDeleteExpiredDisusedLocalCards
+#else
+#define MAYBE_DeleteDisusedCreditCards_OnlyDeleteExpiredDisusedLocalCards \
+  DeleteDisusedCreditCards_OnlyDeleteExpiredDisusedLocalCards
+#endif
 TEST_F(PersonalDataManagerTest,
-       DeleteDisusedCreditCards_OnlyDeleteExpiredDisusedLocalCards) {
+       MAYBE_DeleteDisusedCreditCards_OnlyDeleteExpiredDisusedLocalCards) {
   // Enable the feature.
   base::test::ScopedFeatureList scoped_features;
   scoped_features.InitAndEnableFeature(kAutofillDeleteDisusedCreditCards);
diff --git a/components/exo/keyboard.cc b/components/exo/keyboard.cc
index 2d3b14a..49fc0d6 100644
--- a/components/exo/keyboard.cc
+++ b/components/exo/keyboard.cc
@@ -217,6 +217,10 @@
   if (!focus_)
     return;
 
+  // Ignore synthetic key repeat events.
+  if (event->is_repeat())
+    return;
+
   // Process reserved accelerators before sending it to client.
   if (ProcessAcceleratorIfReserved(focus_, event)) {
     // Discard a key press event if it's a reserved accelerator and it's
diff --git a/components/exo/seat.cc b/components/exo/seat.cc
index a8b36f9..f6c3244d 100644
--- a/components/exo/seat.cc
+++ b/components/exo/seat.cc
@@ -107,6 +107,9 @@
 // ui::EventHandler overrides:
 
 void Seat::OnKeyEvent(ui::KeyEvent* event) {
+  // Ignore synthetic key repeat events.
+  if (event->is_repeat())
+    return;
   switch (event->type()) {
     case ui::ET_KEY_PRESSED:
       pressed_keys_.insert(event->code());
diff --git a/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.cc b/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.cc
index c3f9a48..94d206ce 100644
--- a/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.cc
+++ b/components/ntp_snippets/contextual/contextual_suggestions_debugging_reporter.cc
@@ -70,10 +70,12 @@
   // Check if we've already sent an event with this url to the cache. If so,
   // remove it before adding another one.
   const std::string current_url = current_event_.url;
-  std::remove_if(events_.begin(), events_.end(),
-                 [current_url](ContextualSuggestionsDebuggingEvent event) {
-                   return current_url == event.url;
-                 });
+  auto itr =
+      std::remove_if(events_.begin(), events_.end(),
+                     [current_url](ContextualSuggestionsDebuggingEvent event) {
+                       return current_url == event.url;
+                     });
+  events_.erase(itr, events_.end());
   events_.push_back(current_event_);
 
   // If the cache is too large, then remove the least recently used.
@@ -81,4 +83,4 @@
     events_.erase(events_.begin());
 }
 
-}  // namespace contextual_suggestions
\ No newline at end of file
+}  // namespace contextual_suggestions
diff --git a/components/payments/core/payment_manifest_downloader.cc b/components/payments/core/payment_manifest_downloader.cc
index 6795f5c..6a19aee 100644
--- a/components/payments/core/payment_manifest_downloader.cc
+++ b/components/payments/core/payment_manifest_downloader.cc
@@ -73,6 +73,29 @@
   return GURL();
 }
 
+bool IsValidManifestUrl(const GURL& url) {
+  return url.is_valid() &&
+         (url.SchemeIs(url::kHttpsScheme) ||
+          (url.SchemeIs(url::kHttpScheme) && net::IsLocalhost(url)));
+}
+
+GURL ParseRedirectUrlFromResponseHeader(const net::URLFetcher* source) {
+  // Do not follow net::HTTP_MULTIPLE_CHOICES, net::HTTP_NOT_MODIFIED and
+  // net::HTTP_USE_PROXY redirects.
+  if (source->GetResponseCode() != net::HTTP_MOVED_PERMANENTLY &&
+      source->GetResponseCode() != net::HTTP_FOUND &&
+      source->GetResponseCode() != net::HTTP_SEE_OTHER &&
+      source->GetResponseCode() != net::HTTP_TEMPORARY_REDIRECT &&
+      source->GetResponseCode() != net::HTTP_PERMANENT_REDIRECT) {
+    return GURL();
+  }
+
+  if (!IsValidManifestUrl(source->GetURL()))
+    return GURL();
+
+  return source->GetURL();
+}
+
 std::string ParseResponseContent(const net::URLFetcher* source) {
   std::string content;
   if (source->GetResponseCode() != net::HTTP_OK) {
@@ -99,14 +122,17 @@
     const GURL& url,
     PaymentManifestDownloadCallback callback) {
   DCHECK(IsValidManifestUrl(url));
-  InitiateDownload(url, net::URLFetcher::HEAD, std::move(callback));
+  // Restrict number of redirects for efficiency and breaking circle.
+  InitiateDownload(url, net::URLFetcher::HEAD,
+                   /*allowed_number_of_redirects=*/3, std::move(callback));
 }
 
 void PaymentManifestDownloader::DownloadWebAppManifest(
     const GURL& url,
     PaymentManifestDownloadCallback callback) {
   DCHECK(IsValidManifestUrl(url));
-  InitiateDownload(url, net::URLFetcher::GET, std::move(callback));
+  InitiateDownload(url, net::URLFetcher::GET, /*allowed_number_of_redirects=*/0,
+                   std::move(callback));
 }
 
 PaymentManifestDownloader::Download::Download() {}
@@ -122,13 +148,25 @@
   downloads_.erase(download_it);
 
   if (download->request_type == net::URLFetcher::HEAD) {
+    // Manually follow some type of redirects.
+    if (download->allowed_number_of_redirects > 0) {
+      GURL redirect_url = ParseRedirectUrlFromResponseHeader(source);
+      if (!redirect_url.is_empty()) {
+        InitiateDownload(redirect_url, net::URLFetcher::HEAD,
+                         --download->allowed_number_of_redirects,
+                         std::move(download->callback));
+        return;
+      }
+    }
+
     GURL url = ParseResponseHeader(source);
     if (IsValidManifestUrl(url)) {
       InitiateDownload(url, net::URLFetcher::GET,
+                       /*allowed_number_of_redirects=*/0,
                        std::move(download->callback));
     } else {
-      // If the URL is empty, then ParseResponseHeader() has already printed an
-      // explanation.
+      // If the URL is empty, then ParseResponseHeader() has already printed
+      // an explanation.
       if (!url.is_empty())
         LOG(ERROR) << url << " is not a valid payment method manifest URL.";
       std::move(download->callback).Run(std::string());
@@ -141,6 +179,7 @@
 void PaymentManifestDownloader::InitiateDownload(
     const GURL& url,
     net::URLFetcher::RequestType request_type,
+    int allowed_number_of_redirects,
     PaymentManifestDownloadCallback callback) {
   DCHECK(IsValidManifestUrl(url));
 
@@ -178,6 +217,7 @@
   download->request_type = request_type;
   download->fetcher = std::move(fetcher);
   download->callback = std::move(callback);
+  download->allowed_number_of_redirects = allowed_number_of_redirects;
 
   const net::URLFetcher* identifier = download->fetcher.get();
   auto insert_result =
@@ -185,10 +225,4 @@
   DCHECK(insert_result.second);  // Whether the insert has succeeded.
 }
 
-bool PaymentManifestDownloader::IsValidManifestUrl(const GURL& url) {
-  return url.is_valid() &&
-         (url.SchemeIs(url::kHttpsScheme) ||
-          (url.SchemeIs(url::kHttpScheme) && net::IsLocalhost(url)));
-}
-
 }  // namespace payments
diff --git a/components/payments/core/payment_manifest_downloader.h b/components/payments/core/payment_manifest_downloader.h
index 0e2f38d..ef41decc 100644
--- a/components/payments/core/payment_manifest_downloader.h
+++ b/components/payments/core/payment_manifest_downloader.h
@@ -88,6 +88,7 @@
     Download();
     ~Download();
 
+    int allowed_number_of_redirects = 0;
     net::URLFetcher::RequestType request_type;
     std::unique_ptr<net::URLFetcher> fetcher;
     PaymentManifestDownloadCallback callback;
@@ -98,8 +99,8 @@
 
   void InitiateDownload(const GURL& url,
                         net::URLFetcher::RequestType request_type,
+                        int allowed_number_of_redirects,
                         PaymentManifestDownloadCallback callback);
-  bool IsValidManifestUrl(const GURL& url);
 
   scoped_refptr<net::URLRequestContextGetter> context_;
 
diff --git a/components/payments/core/payment_manifest_downloader_unittest.cc b/components/payments/core/payment_manifest_downloader_unittest.cc
index b84cfc4..99c3d6b 100644
--- a/components/payments/core/payment_manifest_downloader_unittest.cc
+++ b/components/payments/core/payment_manifest_downloader_unittest.cc
@@ -218,6 +218,145 @@
   fetcher()->delegate()->OnURLFetchComplete(fetcher());
 }
 
+TEST_F(PaymentMethodManifestDownloaderTest, 300IsUnsupportedRedirect) {
+  fetcher()->set_response_code(300);
+  fetcher()->set_url(GURL("https://alicepay.com"));
+
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+}
+
+TEST_F(PaymentMethodManifestDownloaderTest, 301And302AreSupportedRedirects) {
+  fetcher()->set_response_code(301);
+  fetcher()->set_url(GURL("https://alicepay.com"));
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+
+  EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://alicepay.com"));
+
+  fetcher()->set_response_code(302);
+  fetcher()->set_url(GURL("https://charliepay.com"));
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+
+  EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://charliepay.com"));
+
+  scoped_refptr<net::HttpResponseHeaders> headers(
+      new net::HttpResponseHeaders(std::string()));
+  headers->AddHeader("Link: <manifest.json>; rel=payment-method-manifest");
+  fetcher()->set_response_headers(headers);
+  fetcher()->set_response_code(200);
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+  fetcher()->SetResponseString("manifest content");
+  fetcher()->set_response_code(200);
+
+  EXPECT_CALL(*this, OnManifestDownload("manifest content"));
+
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+}
+
+TEST_F(PaymentMethodManifestDownloaderTest, 302And303AreSupportedRedirects) {
+  fetcher()->set_response_code(302);
+  fetcher()->set_url(GURL("https://alicepay.com"));
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+
+  EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://alicepay.com"));
+
+  fetcher()->set_response_code(303);
+  fetcher()->set_url(GURL("https://charliepay.com"));
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+
+  EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://charliepay.com"));
+
+  scoped_refptr<net::HttpResponseHeaders> headers(
+      new net::HttpResponseHeaders(std::string()));
+  headers->AddHeader("Link: <manifest.json>; rel=payment-method-manifest");
+  fetcher()->set_response_headers(headers);
+  fetcher()->set_response_code(200);
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+  fetcher()->SetResponseString("manifest content");
+  fetcher()->set_response_code(200);
+
+  EXPECT_CALL(*this, OnManifestDownload("manifest content"));
+
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+}
+
+TEST_F(PaymentMethodManifestDownloaderTest, 304IsUnsupportedRedirect) {
+  fetcher()->set_response_code(304);
+  fetcher()->set_url(GURL("https://alicepay.com"));
+
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+}
+
+TEST_F(PaymentMethodManifestDownloaderTest, 305IsUnsupportedRedirect) {
+  fetcher()->set_response_code(305);
+  fetcher()->set_url(GURL("https://alicepay.com"));
+
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+}
+
+TEST_F(PaymentMethodManifestDownloaderTest, 307And308AreSupportedRedirects) {
+  fetcher()->set_response_code(307);
+  fetcher()->set_url(GURL("https://alicepay.com"));
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+
+  EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://alicepay.com"));
+
+  fetcher()->set_response_code(308);
+  fetcher()->set_url(GURL("https://charliepay.com"));
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+
+  EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://charliepay.com"));
+
+  scoped_refptr<net::HttpResponseHeaders> headers(
+      new net::HttpResponseHeaders(std::string()));
+  headers->AddHeader("Link: <manifest.json>; rel=payment-method-manifest");
+  fetcher()->set_response_headers(headers);
+  fetcher()->set_response_code(200);
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+  fetcher()->SetResponseString("manifest content");
+  fetcher()->set_response_code(200);
+
+  EXPECT_CALL(*this, OnManifestDownload("manifest content"));
+
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+}
+
+TEST_F(PaymentMethodManifestDownloaderTest, NoMoreThanThreeRediects) {
+  fetcher()->set_response_code(301);
+  fetcher()->set_url(GURL("https://alicepay.com"));
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+
+  EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://alicepay.com"));
+
+  fetcher()->set_response_code(302);
+  fetcher()->set_url(GURL("https://charliepay.com"));
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+
+  EXPECT_EQ(fetcher()->GetOriginalURL(), GURL("https://charliepay.com"));
+
+  fetcher()->set_response_code(308);
+  fetcher()->set_url(GURL("https://davepay.com"));
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+}
+
+TEST_F(PaymentMethodManifestDownloaderTest, InvalidRedirectUrlIsFailure) {
+  fetcher()->set_response_code(308);
+  fetcher()->set_url(GURL("alicepay.com"));
+
+  EXPECT_CALL(*this, OnManifestDownload(std::string()));
+
+  fetcher()->delegate()->OnURLFetchComplete(fetcher());
+}
+
 class WebAppManifestDownloaderTest : public testing::Test {
  public:
   WebAppManifestDownloaderTest()
diff --git a/components/subresource_filter/content/browser/BUILD.gn b/components/subresource_filter/content/browser/BUILD.gn
index ae37f91f..7415092 100644
--- a/components/subresource_filter/content/browser/BUILD.gn
+++ b/components/subresource_filter/content/browser/BUILD.gn
@@ -12,8 +12,6 @@
     "content_activation_list_utils.h",
     "content_ruleset_service.cc",
     "content_ruleset_service.h",
-    "content_subresource_filter_driver_factory.cc",
-    "content_subresource_filter_driver_factory.h",
     "content_subresource_filter_throttle_manager.cc",
     "content_subresource_filter_throttle_manager.h",
     "navigation_console_logger.cc",
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc
deleted file mode 100644
index f99eeed2b..0000000
--- a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.cc
+++ /dev/null
@@ -1,132 +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.
-
-#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
-
-#include <utility>
-
-#include "base/metrics/histogram_macros.h"
-#include "base/time/time.h"
-#include "components/subresource_filter/content/browser/navigation_console_logger.h"
-#include "components/subresource_filter/content/browser/page_load_statistics.h"
-#include "components/subresource_filter/content/browser/subresource_filter_client.h"
-#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
-#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
-#include "components/subresource_filter/core/common/activation_list.h"
-#include "components/subresource_filter/core/common/activation_state.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/page_navigator.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/console_message_level.h"
-#include "net/base/net_errors.h"
-#include "url/gurl.h"
-
-DEFINE_WEB_CONTENTS_USER_DATA_KEY(
-    subresource_filter::ContentSubresourceFilterDriverFactory);
-
-namespace subresource_filter {
-
-// static
-void ContentSubresourceFilterDriverFactory::CreateForWebContents(
-    content::WebContents* web_contents,
-    SubresourceFilterClient* client) {
-  if (FromWebContents(web_contents))
-    return;
-  web_contents->SetUserData(
-      UserDataKey(), std::make_unique<ContentSubresourceFilterDriverFactory>(
-                         web_contents, client));
-}
-
-// static
-ContentSubresourceFilterDriverFactory::ContentSubresourceFilterDriverFactory(
-    content::WebContents* web_contents,
-    SubresourceFilterClient* client)
-    : content::WebContentsObserver(web_contents), client_(client) {}
-
-ContentSubresourceFilterDriverFactory::
-    ~ContentSubresourceFilterDriverFactory() {}
-
-void ContentSubresourceFilterDriverFactory::NotifyPageActivationComputed(
-    content::NavigationHandle* navigation_handle,
-    ActivationDecision activation_decision,
-    const Configuration& matched_configuration,
-    bool warning) {
-  DCHECK(navigation_handle->IsInMainFrame());
-  DCHECK(!navigation_handle->IsSameDocument());
-  if (navigation_handle->GetNetErrorCode() != net::OK)
-    return;
-
-  activation_decision_ = activation_decision;
-  matched_configuration_ = matched_configuration;
-  DCHECK_NE(activation_decision_, ActivationDecision::UNKNOWN);
-
-  ActivationLevel effective_activation_level =
-      matched_configuration_.activation_options.activation_level;
-
-  // ACTIVATION_DISABLED implies DISABLED activation level.
-  DCHECK(activation_decision_ != ActivationDecision::ACTIVATION_DISABLED ||
-         effective_activation_level == ActivationLevel::DISABLED);
-
-  // Ensure the matched config is in our config list. If it wasn't then this
-  // must be a forced activation via devtools.
-  bool forced_activation_via_devtools =
-      (matched_configuration == Configuration::MakeForForcedActivation());
-  DCHECK(activation_decision_ != ActivationDecision::ACTIVATED ||
-         HasEnabledConfiguration(matched_configuration) ||
-         forced_activation_via_devtools)
-      << matched_configuration;
-
-  if (warning && effective_activation_level == ActivationLevel::ENABLED) {
-    NavigationConsoleLogger::LogMessageOnCommit(
-        navigation_handle, content::CONSOLE_MESSAGE_LEVEL_WARNING,
-        kActivationWarningConsoleMessage);
-
-    // Do not disallow enforcement if activated via devtools.
-    if (!forced_activation_via_devtools) {
-      activation_decision_ = ActivationDecision::ACTIVATION_DISABLED;
-      effective_activation_level = ActivationLevel::DISABLED;
-    }
-  }
-
-  matched_configuration_.activation_options.activation_level =
-      effective_activation_level;
-  SubresourceFilterObserverManager::FromWebContents(web_contents())
-      ->NotifyPageActivationComputed(navigation_handle, activation_decision_,
-                                     matched_configuration_.GetActivationState(
-                                         effective_activation_level));
-}
-
-void ContentSubresourceFilterDriverFactory::OnFirstSubresourceLoadDisallowed() {
-  if (matched_configuration_.activation_conditions.forced_activation) {
-    UMA_HISTOGRAM_BOOLEAN(
-        "SubresourceFilter.PageLoad.ForcedActivation.DisallowedLoad", true);
-    return;
-  }
-  client_->ShowNotification();
-}
-
-void ContentSubresourceFilterDriverFactory::DidStartNavigation(
-    content::NavigationHandle* navigation_handle) {
-  if (navigation_handle->IsInMainFrame() &&
-      !navigation_handle->IsSameDocument()) {
-    activation_decision_ = ActivationDecision::UNKNOWN;
-  }
-}
-
-void ContentSubresourceFilterDriverFactory::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  if (!navigation_handle->IsInMainFrame() ||
-      navigation_handle->IsSameDocument() ||
-      !navigation_handle->HasCommitted()) {
-    return;
-  }
-
-  if (activation_decision_ == ActivationDecision::UNKNOWN) {
-    activation_decision_ = ActivationDecision::ACTIVATION_DISABLED;
-    matched_configuration_ = Configuration();
-  }
-}
-
-}  // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h b/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h
deleted file mode 100644
index 9d9240b..0000000
--- a/components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h
+++ /dev/null
@@ -1,103 +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.
-
-#ifndef COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_SUBRESOURCE_FILTER_DRIVER_FACTORY_H_
-#define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_SUBRESOURCE_FILTER_DRIVER_FACTORY_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
-#include "components/subresource_filter/core/browser/subresource_filter_features.h"
-#include "components/subresource_filter/core/common/activation_decision.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/browser/web_contents_user_data.h"
-
-namespace content {
-class WebContents;
-}  // namespace content
-
-namespace safe_browsing {
-class SafeBrowsingServiceTest;
-};
-
-namespace subresource_filter {
-
-class SubresourceFilterClient;
-enum class ActivationLevel;
-enum class ActivationList;
-
-// Controls the activation of subresource filtering for each page load in a
-// WebContents and is responsible for sending the activation signal to all the
-// per-frame SubresourceFilterAgents on the renderer side.
-class ContentSubresourceFilterDriverFactory
-    : public content::WebContentsUserData<
-          ContentSubresourceFilterDriverFactory>,
-      public content::WebContentsObserver,
-      public ContentSubresourceFilterThrottleManager::Delegate {
- public:
-  static void CreateForWebContents(content::WebContents* web_contents,
-                                   SubresourceFilterClient* client);
-
-  explicit ContentSubresourceFilterDriverFactory(
-      content::WebContents* web_contents,
-      SubresourceFilterClient* client);
-  ~ContentSubresourceFilterDriverFactory() override;
-
-  // This class will be notified of page level activation, before the associated
-  // navigation commits.
-  void NotifyPageActivationComputed(
-      content::NavigationHandle* navigation_handle,
-      ActivationDecision activation_decision,
-      const Configuration& matched_configuration,
-      bool warning);
-
-  // ContentSubresourceFilterThrottleManager::Delegate:
-  void OnFirstSubresourceLoadDisallowed() override;
-
-  SubresourceFilterClient* client() { return client_; }
-
- private:
-  friend class ContentSubresourceFilterDriverFactoryTest;
-  friend class safe_browsing::SafeBrowsingServiceTest;
-
-  // content::WebContentsObserver:
-  void DidStartNavigation(
-      content::NavigationHandle* navigation_handle) override;
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
-
-  // Must outlive this class.
-  SubresourceFilterClient* client_;
-
-  // The activation decision corresponding to the most recently _started_
-  // non-same-document navigation in the main frame.
-  //
-  // The value is reset to ActivationDecision::UNKNOWN at the start of each such
-  // navigation, and will not be assigned until the navigation successfully
-  // reaches the WillProcessResponse stage (or successfully finishes if
-  // throttles are not invoked). This means that after a cancelled or otherwise
-  // unsuccessful navigation, the value will be left at UNKNOWN indefinitely.
-  ActivationDecision activation_decision_ =
-      ActivationDecision::ACTIVATION_DISABLED;
-
-  // The Configuration corresponding to the most recently _committed_
-  // non-same-document navigation in the main frame.
-  //
-  // The value corresponding to the previous such navigation will be retained,
-  // and the new value not assigned until a subsequent navigation successfully
-  // reaches the WillProcessResponse stage (or successfully finishes if
-  // throttles are not invoked).
-  //
-  // Careful note: the Configuration may not entirely match up with
-  // a config in GetEnabledConfigurations() due to activation computation
-  // changing the config (e.g. for forcing devtools activation).
-  Configuration matched_configuration_;
-
-  DISALLOW_COPY_AND_ASSIGN(ContentSubresourceFilterDriverFactory);
-};
-
-}  // namespace subresource_filter
-
-#endif  // COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_CONTENT_SUBRESOURCE_FILTER_DRIVER_FACTORY_H_
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
index e6467a0e..cae6baee 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
@@ -17,6 +17,7 @@
 #include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
 #include "components/subresource_filter/content/browser/navigation_console_logger.h"
 #include "components/subresource_filter/content/browser/page_load_statistics.h"
+#include "components/subresource_filter/content/browser/subresource_filter_client.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
 #include "components/subresource_filter/content/common/subresource_filter_messages.h"
 #include "components/subresource_filter/content/common/subresource_filter_utils.h"
@@ -33,13 +34,13 @@
 
 ContentSubresourceFilterThrottleManager::
     ContentSubresourceFilterThrottleManager(
-        Delegate* delegate,
+        SubresourceFilterClient* client,
         VerifiedRulesetDealer::Handle* dealer_handle,
         content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
       scoped_observer_(this),
       dealer_handle_(dealer_handle),
-      delegate_(delegate),
+      client_(client),
       weak_ptr_factory_(this) {
   SubresourceFilterObserverManager::CreateForWebContents(web_contents);
   scoped_observer_.Add(
@@ -374,7 +375,7 @@
           ActivationLevel::ENABLED) {
     return;
   }
-  delegate_->OnFirstSubresourceLoadDisallowed();
+  client_->ShowNotification();
   current_committed_load_has_notified_disallowed_load_ = true;
 }
 
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h
index e8500b2..330f33d 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h
@@ -35,8 +35,9 @@
 
 class AsyncDocumentSubresourceFilter;
 class ActivationStateComputingNavigationThrottle;
-class SubresourceFilterObserverManager;
 class PageLoadStatistics;
+class SubresourceFilterObserverManager;
+class SubresourceFilterClient;
 struct DocumentLoadStatistics;
 
 // The ContentSubresourceFilterThrottleManager manages NavigationThrottles in
@@ -53,17 +54,8 @@
       public SubresourceFilterObserver,
       public SubframeNavigationFilteringThrottle::Delegate {
  public:
-  // It is expected that the Delegate outlives |this|, and manages the lifetime
-  // of this class.
-  class Delegate {
-   public:
-    // The embedder may be interested in displaying UI to the user when the
-    // first load is disallowed for a given page load.
-    virtual void OnFirstSubresourceLoadDisallowed() {}
-  };
-
   ContentSubresourceFilterThrottleManager(
-      Delegate* delegate,
+      SubresourceFilterClient* client,
       VerifiedRulesetDealer::Handle* dealer_handle,
       content::WebContents* web_contents);
   ~ContentSubresourceFilterThrottleManager() override;
@@ -197,7 +189,7 @@
 
   // These members outlive this class.
   VerifiedRulesetDealer::Handle* dealer_handle_;
-  Delegate* delegate_;
+  SubresourceFilterClient* client_;
 
   base::WeakPtrFactory<ContentSubresourceFilterThrottleManager>
       weak_ptr_factory_;
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
index 710d1ed..e9eac76 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/time/time.h"
 #include "components/subresource_filter/content/browser/async_document_subresource_filter.h"
+#include "components/subresource_filter/content/browser/subresource_filter_client.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
 #include "components/subresource_filter/content/common/subresource_filter_messages.h"
 #include "components/subresource_filter/core/common/activation_level.h"
@@ -113,7 +114,7 @@
 class ContentSubresourceFilterThrottleManagerTest
     : public content::RenderViewHostTestHarness,
       public content::WebContentsObserver,
-      public ContentSubresourceFilterThrottleManager::Delegate,
+      public SubresourceFilterClient,
       public ::testing::WithParamInterface<PageActivationNotificationTiming> {
  public:
   ContentSubresourceFilterThrottleManagerTest() {}
@@ -279,9 +280,13 @@
     }
   }
 
-  // ContentSubresourceFilterThrottleManager::Delegate:
-  void OnFirstSubresourceLoadDisallowed() override {
-    ++disallowed_notification_count_;
+  // SubresourceFilterClient:
+  void ShowNotification() override { ++disallowed_notification_count_; }
+  ActivationLevel OnPageActivationComputed(
+      content::NavigationHandle* navigation_handle,
+      ActivationLevel effective_activation_level,
+      ActivationDecision* decision) override {
+    return effective_activation_level;
   }
 
   ContentSubresourceFilterThrottleManager* throttle_manager() {
diff --git a/components/subresource_filter/content/browser/subresource_filter_client.h b/components/subresource_filter/content/browser/subresource_filter_client.h
index ced9043f..8424e0a4 100644
--- a/components/subresource_filter/content/browser/subresource_filter_client.h
+++ b/components/subresource_filter/content/browser/subresource_filter_client.h
@@ -6,6 +6,8 @@
 #define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_SUBRESOURCE_FILTER_CLIENT_H_
 
 #include "components/subresource_filter/content/browser/verified_ruleset_dealer.h"
+#include "components/subresource_filter/core/common/activation_decision.h"
+#include "components/subresource_filter/core/common/activation_level.h"
 #include "content/public/browser/web_contents.h"
 
 namespace content {
@@ -24,17 +26,17 @@
 
   // Called when the activation decision is otherwise completely computed by the
   // subresource filter. At this point, the embedder still has a chance to
-  // return false to suppress the activation. Returns whether the activation
-  // should be whitelisted for this navigation.
+  // alter the effective activation. Returns the effective activation for this
+  // navigation.
+  //
+  // Note: |decision| is guaranteed to be non-nullptr, and can be modified by
+  // the embedder if any decision changes.
   //
   // Precondition: The navigation must be a main frame navigation.
-  virtual bool OnPageActivationComputed(
+  virtual ActivationLevel OnPageActivationComputed(
       content::NavigationHandle* navigation_handle,
-      bool activated) = 0;
-
-  // Returns whether this navigation should be forced to be activated. This is
-  // currently only used for devtools.
-  virtual bool ForceActivationInCurrentWebContents() = 0;
+      ActivationLevel initial_activation_level,
+      subresource_filter::ActivationDecision* decision) = 0;
 };
 
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
index 1f148b4..d8a2950 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle.cc
@@ -13,14 +13,16 @@
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_event_argument.h"
 #include "components/subresource_filter/content/browser/content_activation_list_utils.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
+#include "components/subresource_filter/content/browser/navigation_console_logger.h"
 #include "components/subresource_filter/content/browser/subresource_filter_client.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
 #include "components/subresource_filter/content/browser/subresource_filter_safe_browsing_client.h"
+#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 #include "components/ukm/ukm_source.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/console_message_level.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "ui/base/page_transition_types.h"
@@ -129,32 +131,17 @@
                "SubresourceFilterSafeBrowsingActivationThrottle::NotifyResult");
   DCHECK(!check_results_.empty());
 
-  // For forced activation, the configuration is always the same, but we still
-  // need to compute the warning value base on the final check result.
-  bool forced_activation = client_->ForceActivationInCurrentWebContents();
   std::vector<ConfigResult> matched_configurations;
-  if (forced_activation) {
-    const auto& check_result = check_results_.back();
-    bool warning = false;
-    GetListForThreatTypeAndMetadata(check_result.threat_type,
-                                    check_result.threat_metadata, &warning);
+  for (const auto& current_result : check_results_) {
     matched_configurations.push_back(
-        ConfigResult(Configuration::MakeForForcedActivation(), warning,
-                     ActivationList::NONE));
-    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"), "ActivationForced");
-  } else {
-    // Otherwise loop over all the results, computing their configurations and
-    // associated warning booleans.
-    for (const auto& current_result : check_results_) {
-      matched_configurations.push_back(
-          GetHighestPriorityConfiguration(current_result));
-    }
+        GetHighestPriorityConfiguration(current_result));
   }
 
   // Get the activation decision and the matched configuration and warning.
   ConfigResult selection;
   ActivationDecision activation_decision =
       GetActivationDecision(matched_configurations, &selection);
+  DCHECK_NE(activation_decision, ActivationDecision::UNKNOWN);
   Configuration matched_configuration =
       selection.config.has_value() ? selection.config.value() : Configuration();
   bool warning = selection.warning;
@@ -169,31 +156,29 @@
                                         check_result.threat_type,
                                         check_result.threat_metadata);
 
-  // Check for whitelisted status last, so that the client gets an accurate
-  // indication of whether there would be activation otherwise.
-  // Note that the client is responsible for noticing if we're forcing
-  // activation.
-  bool whitelisted = client_->OnPageActivationComputed(
-      navigation_handle(),
-      !warning && matched_configuration.activation_options.activation_level ==
-                      ActivationLevel::ENABLED);
+  ActivationLevel activation_level =
+      matched_configuration.activation_options.activation_level;
 
-  // Only reset the activation decision reason if we would have activated.
-  if (whitelisted && activation_decision == ActivationDecision::ACTIVATED) {
-    TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("loading"), "ActivationWhitelisted");
-    activation_decision = ActivationDecision::URL_WHITELISTED;
-    matched_configuration = Configuration();
+  if (warning && activation_level == ActivationLevel::ENABLED) {
+    NavigationConsoleLogger::LogMessageOnCommit(
+        navigation_handle(), content::CONSOLE_MESSAGE_LEVEL_WARNING,
+        kActivationWarningConsoleMessage);
+    activation_level = ActivationLevel::DISABLED;
   }
 
-  LogMetricsOnChecksComplete(
-      selection.matched_list, activation_decision,
-      matched_configuration.activation_options.activation_level);
+  // Let the embedder get the last word when it comes to activation level.
+  // TODO(csharrison): Move all ActivationDecision code to the embedder.
+  activation_level = client_->OnPageActivationComputed(
+      navigation_handle(), activation_level, &activation_decision);
 
-  auto* driver_factory = ContentSubresourceFilterDriverFactory::FromWebContents(
-      navigation_handle()->GetWebContents());
-  DCHECK(driver_factory);
-  driver_factory->NotifyPageActivationComputed(
-      navigation_handle(), activation_decision, matched_configuration, warning);
+  LogMetricsOnChecksComplete(selection.matched_list, activation_decision,
+                             activation_level);
+
+  SubresourceFilterObserverManager::FromWebContents(
+      navigation_handle()->GetWebContents())
+      ->NotifyPageActivationComputed(
+          navigation_handle(), activation_decision,
+          matched_configuration.GetActivationState(activation_level));
 }
 
 void SubresourceFilterSafeBrowsingActivationThrottle::
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
index 27641e5..cb466dd 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
@@ -18,7 +18,7 @@
 #include "base/test/test_mock_time_task_runner.h"
 #include "build/build_config.h"
 #include "components/safe_browsing/db/test_database_manager.h"
-#include "components/subresource_filter/content/browser/content_subresource_filter_driver_factory.h"
+#include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
 #include "components/subresource_filter/content/browser/fake_safe_browsing_database_manager.h"
 #include "components/subresource_filter/content/browser/subresource_filter_client.h"
 #include "components/subresource_filter/content/browser/subresource_filter_observer_test_utils.h"
@@ -72,10 +72,17 @@
   MockSubresourceFilterClient() = default;
   ~MockSubresourceFilterClient() override = default;
 
-  bool OnPageActivationComputed(content::NavigationHandle* handle,
-                                bool activated) override {
+  ActivationLevel OnPageActivationComputed(
+      content::NavigationHandle* handle,
+      ActivationLevel effective_level,
+      ActivationDecision* decision) override {
     DCHECK(handle->IsInMainFrame());
-    return whitelisted_hosts_.count(handle->GetURL().host());
+    if (whitelisted_hosts_.count(handle->GetURL().host())) {
+      if (effective_level == subresource_filter::ActivationLevel::ENABLED)
+        *decision = subresource_filter::ActivationDecision::URL_WHITELISTED;
+      return ActivationLevel::DISABLED;
+    }
+    return effective_level;
   }
 
   MOCK_METHOD0(ShowNotification, void());
@@ -170,13 +177,9 @@
     auto* contents = RenderViewHostTestHarness::web_contents();
     client_ =
         std::make_unique<::testing::NiceMock<MockSubresourceFilterClient>>();
-    ContentSubresourceFilterDriverFactory::CreateForWebContents(contents,
-                                                                client_.get());
-    auto* driver_factory =
-        ContentSubresourceFilterDriverFactory::FromWebContents(contents);
     throttle_manager_ =
         std::make_unique<ContentSubresourceFilterThrottleManager>(
-            driver_factory, ruleset_dealer_.get(), contents);
+            client_.get(), ruleset_dealer_.get(), contents);
     fake_safe_browsing_database_ = new FakeSafeBrowsingDatabaseManager();
     NavigateAndCommit(GURL("https://test.com"));
     Observe(contents);
@@ -594,9 +597,9 @@
     // Whitelisting is only applied when the page will otherwise activate.
     client()->WhitelistInCurrentWebContents(url);
     ActivationDecision decision =
-        test_data.activation_level == ActivationLevel::DISABLED
-            ? test_data.activation_decision
-            : ActivationDecision::URL_WHITELISTED;
+        test_data.activation_level == ActivationLevel::ENABLED
+            ? ActivationDecision::URL_WHITELISTED
+            : test_data.activation_decision;
     SimulateNavigateAndCommit({url}, main_rfh());
     EXPECT_EQ(decision, *observer()->GetPageActivationForLastCommittedLoad());
   }
diff --git a/components/subresource_filter/core/browser/subresource_filter_features.cc b/components/subresource_filter/core/browser/subresource_filter_features.cc
index 8f17a00..ce87045 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features.cc
+++ b/components/subresource_filter/core/browser/subresource_filter_features.cc
@@ -289,16 +289,6 @@
 }
 
 // static
-Configuration Configuration::MakeForForcedActivation() {
-  // This is a strange configuration, but it is generated on-the-fly rather than
-  // via finch configs, and is separate from the standard activation computation
-  // (which is why scope is no_sites).
-  Configuration config(ActivationLevel::ENABLED, ActivationScope::NO_SITES);
-  config.activation_conditions.forced_activation = true;
-  return config;
-}
-
-// static
 Configuration Configuration::MakePresetForLiveRunForBetterAds() {
   Configuration config(ActivationLevel::ENABLED,
                        ActivationScope::ACTIVATION_LIST,
@@ -326,7 +316,6 @@
     return std::tie(config.activation_conditions.activation_scope,
                     config.activation_conditions.activation_list,
                     config.activation_conditions.priority,
-                    config.activation_conditions.forced_activation,
                     config.activation_options.activation_level,
                     config.activation_options.performance_measurement_rate,
                     config.general_settings.ruleset_flavor);
@@ -344,7 +333,6 @@
   value->SetString("activation_scope", StreamToString(activation_scope));
   value->SetString("activation_list", StreamToString(activation_list));
   value->SetInteger("priority", priority);
-  value->SetBoolean("forced_activation", forced_activation);
   return value;
 }
 
@@ -374,7 +362,6 @@
   // This bit keeps track of BAS enforcement-style logging, not warning logging.
   state.enable_logging =
       effective_activation_level == ActivationLevel::ENABLED &&
-      *this != Configuration::MakeForForcedActivation() &&
       base::FeatureList::IsEnabled(
           kSafeBrowsingSubresourceFilterExperimentalUI);
   return state;
diff --git a/components/subresource_filter/core/browser/subresource_filter_features.h b/components/subresource_filter/core/browser/subresource_filter_features.h
index 8fa16d7..11d8e3d 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features.h
+++ b/components/subresource_filter/core/browser/subresource_filter_features.h
@@ -64,11 +64,6 @@
     // otherwise satisfied. A greater value indicates higher priority.
     int priority = 0;
 
-    // This boolean is set to true for a navigation which has forced activation,
-    // despite other conditions not matching. It should never be possible to set
-    // this via variation params.
-    bool forced_activation = false;
-
     std::unique_ptr<base::trace_event::TracedValue> ToTracedValue() const;
   };
 
@@ -129,10 +124,6 @@
   static Configuration MakePresetForPerformanceTestingDryRunOnAllSites();
   static Configuration MakePresetForLiveRunForBetterAds();
 
-  // Not really a preset, but used as the configuration for forcing activation
-  // (e.g. via devtools).
-  static Configuration MakeForForcedActivation();
-
   ActivationConditions activation_conditions;
   ActivationOptions activation_options;
   GeneralSettings general_settings;
diff --git a/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc b/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc
index 0b8ed4f..2e119a0f 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc
+++ b/components/subresource_filter/core/browser/subresource_filter_features_unittest.cc
@@ -537,7 +537,6 @@
   EXPECT_EQ(ActivationScope::ACTIVATION_LIST,
             config.activation_conditions.activation_scope);
   EXPECT_EQ(800, config.activation_conditions.priority);
-  EXPECT_FALSE(config.activation_conditions.forced_activation);
   EXPECT_EQ(ActivationLevel::ENABLED,
             config.activation_options.activation_level);
   EXPECT_EQ(0.0, config.activation_options.performance_measurement_rate);
@@ -650,23 +649,6 @@
             config_list->lexicographically_greatest_ruleset_flavor());
 }
 
-TEST_F(SubresourceFilterFeaturesTest, ForcedActivation_NotConfigurable) {
-  ScopedExperimentalStateToggle scoped_experimental_state(
-      base::FeatureList::OVERRIDE_ENABLE_FEATURE,
-      {{kActivationLevelParameterName, kActivationLevelEnabled},
-       {kActivationScopeParameterName, kActivationScopeNoSites},
-       {"forced_activation", "true"}});
-
-  Configuration actual_configuration;
-  ExpectAndRetrieveExactlyOneExtraEnabledConfig(&actual_configuration);
-  EXPECT_EQ(ActivationLevel::ENABLED,
-            actual_configuration.activation_options.activation_level);
-  EXPECT_EQ(ActivationScope::NO_SITES,
-            actual_configuration.activation_conditions.activation_scope);
-
-  EXPECT_FALSE(actual_configuration.activation_conditions.forced_activation);
-}
-
 TEST_F(SubresourceFilterFeaturesTest, AdTagging_EnablesDryRun) {
   const Configuration dryrun =
       Configuration::MakePresetForPerformanceTestingDryRunOnAllSites();
diff --git a/components/subresource_filter/core/common/activation_decision.h b/components/subresource_filter/core/common/activation_decision.h
index 43d06711..557b084 100644
--- a/components/subresource_filter/core/common/activation_decision.h
+++ b/components/subresource_filter/core/common/activation_decision.h
@@ -27,8 +27,11 @@
   // activation conditions of any of enabled configurations.
   ACTIVATION_CONDITIONS_NOT_MET = 5,
 
+  // Activation was forced on the client (e.g. via devtools).
+  FORCED_ACTIVATION = 6,
+
   // Max value for enum.
-  ACTIVATION_DECISION_MAX = 6
+  ACTIVATION_DECISION_MAX = 7
 };
 
 }  // namespace subresource_filter
diff --git a/components/viz/PRESUBMIT.py b/components/viz/PRESUBMIT.py
index 705241b..c18ec999 100644
--- a/components/viz/PRESUBMIT.py
+++ b/components/viz/PRESUBMIT.py
@@ -19,10 +19,9 @@
   """git cl upload will call this hook after the issue is created/modified.
 
   This hook modifies the CL description in order to run extra GPU
-  tests (in particular, the WebGL 2.0 conformance tests) in addition
-  to the regular CQ try bots. This test suite is too large to run
-  against all Chromium commits, but should be run against changes
-  likely to affect these tests.
+  tests (in particular, on Android) in addition to the regular CQ try
+  bots. These tests don't yet run by default on
+  android_n5x_swarming_rel, but viz changes need to run them.
 
   When adding/removing tests here, ensure that both gpu/PRESUBMIT.py and
   ui/gl/PRESUBMIT.py are updated.
@@ -30,9 +29,6 @@
   return output_api.EnsureCQIncludeTrybotsAreAdded(
     cl,
     [
-      'luci.chromium.try:linux_optional_gpu_tests_rel',
-      'luci.chromium.try:mac_optional_gpu_tests_rel',
-      'luci.chromium.try:win_optional_gpu_tests_rel',
       'luci.chromium.try:android_optional_gpu_tests_rel',
     ],
     'Automatically added optional GPU tests to run on CQ.')
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index 9c39987..3ad6b29 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -1281,28 +1281,28 @@
       FILE_PATH_LITERAL("intersecting_blue_green.png"));
 }
 
-static inline SkColor GetSkiaOrGLColor(const SkColor& color) {
+static inline uint32_t GetSkiaOrGLColor(const SkColor& color) {
   return SkColorSetARGB(SkColorGetA(color), SkColorGetB(color),
                         SkColorGetG(color), SkColorGetR(color));
 }
 
 template <typename TypeParam>
-SkColor GetColor(const SkColor& color) {
+uint32_t GetColor(const SkColor& color) {
   return color;
 }
 
 template <>
-SkColor GetColor<GLRenderer>(const SkColor& color) {
+uint32_t GetColor<GLRenderer>(const SkColor& color) {
   return GetSkiaOrGLColor(color);
 }
 
 template <>
-SkColor GetColor<SkiaRenderer>(const SkColor& color) {
+uint32_t GetColor<SkiaRenderer>(const SkColor& color) {
   return GetSkiaOrGLColor(color);
 }
 
 template <>
-SkColor GetColor<cc::GLRendererWithExpandedViewport>(const SkColor& color) {
+uint32_t GetColor<cc::GLRendererWithExpandedViewport>(const SkColor& color) {
   return GetSkiaOrGLColor(color);
 }
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 545d28c..ef3cb6e 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1727,8 +1727,12 @@
     "web_package/signed_exchange_devtools_proxy.h",
     "web_package/signed_exchange_envelope.cc",
     "web_package/signed_exchange_envelope.h",
+    "web_package/signed_exchange_error.cc",
+    "web_package/signed_exchange_error.h",
     "web_package/signed_exchange_handler.cc",
     "web_package/signed_exchange_handler.h",
+    "web_package/signed_exchange_prologue.cc",
+    "web_package/signed_exchange_prologue.h",
     "web_package/signed_exchange_signature_header_field.cc",
     "web_package/signed_exchange_signature_header_field.h",
     "web_package/signed_exchange_signature_verifier.cc",
diff --git a/content/browser/background_fetch/background_fetch_data_manager.h b/content/browser/background_fetch/background_fetch_data_manager.h
index 86e3de6..7515625 100644
--- a/content/browser/background_fetch/background_fetch_data_manager.h
+++ b/content/browser/background_fetch/background_fetch_data_manager.h
@@ -154,9 +154,10 @@
 
   // TODO(rayankans): Move this function to MarkRequestCompleteTask after
   // non-persistent background fetch support is removed.
-  bool FillServiceWorkerResponse(const BackgroundFetchRequestInfo& request,
-                                 const url::Origin& origin,
-                                 ServiceWorkerResponse* response);
+  virtual bool FillServiceWorkerResponse(
+      const BackgroundFetchRequestInfo& request,
+      const url::Origin& origin,
+      ServiceWorkerResponse* response);
 
  private:
   FRIEND_TEST_ALL_PREFIXES(BackgroundFetchDataManagerTest, Cleanup);
diff --git a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
index 0b3808e..8e866b9 100644
--- a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
@@ -78,12 +78,21 @@
 }
 
 void AnnotateRequestInfoWithFakeDownloadManagerData(
-    BackgroundFetchRequestInfo* request_info) {
-  // Fill |request_info| with a failed result.
-  request_info->SetResult(std::make_unique<BackgroundFetchResult>(
-      base::Time::Now(), BackgroundFetchResult::FailureReason::UNKNOWN));
+    BackgroundFetchRequestInfo* request_info,
+    bool success = false) {
+  DCHECK(request_info);
+
   request_info->PopulateWithResponse(
       std::make_unique<BackgroundFetchResponse>(std::vector<GURL>(1), nullptr));
+
+  if (!success) {
+    // Fill |request_info| with a failed result.
+    request_info->SetResult(std::make_unique<BackgroundFetchResult>(
+        base::Time::Now(), BackgroundFetchResult::FailureReason::UNKNOWN));
+  } else {
+    request_info->SetResult(std::make_unique<BackgroundFetchResult>(
+        base::Time::Now(), base::FilePath(), 42u /* file_size */));
+  }
 }
 
 void GetNumUserData(base::Closure quit_closure,
@@ -135,7 +144,15 @@
                                    nullptr /* cache_storage_context */),
         browser_context_(browser_context) {}
 
+  bool FillServiceWorkerResponse(const BackgroundFetchRequestInfo& request,
+                                 const url::Origin& origin,
+                                 ServiceWorkerResponse* response) override {
+    return request.IsResultSuccess();
+  }
+
  private:
+  friend class BackgroundFetchDataManagerTest;
+
   void CreateCacheStorageManager() {
     StoragePartition* storage_partition =
         BrowserContext::GetDefaultStoragePartition(browser_context_);
@@ -146,17 +163,17 @@
     // Wait for ChromeBlobStorageContext to finish initializing.
     base::RunLoop().RunUntilIdle();
 
-    auto mock_quota_manager = base::MakeRefCounted<MockQuotaManager>(
+    mock_quota_manager_ = base::MakeRefCounted<MockQuotaManager>(
         storage_partition->GetPath().empty(), storage_partition->GetPath(),
         base::ThreadTaskRunnerHandle::Get().get(),
         base::MakeRefCounted<MockSpecialStoragePolicy>());
-    mock_quota_manager->SetQuota(GURL("https://example.com/"),
-                                 StorageType::kTemporary, 1024 * 1024 * 100);
+    mock_quota_manager_->SetQuota(GURL("https://example.com/"),
+                                  StorageType::kTemporary, 1024 * 1024 * 100);
 
     cache_manager_ = CacheStorageManager::Create(
         storage_partition->GetPath(), base::ThreadTaskRunnerHandle::Get(),
         base::MakeRefCounted<MockBGFQuotaManagerProxy>(
-            mock_quota_manager.get()));
+            mock_quota_manager_.get()));
     DCHECK(cache_manager_);
 
     cache_manager_->SetBlobParametersForCache(
@@ -170,6 +187,7 @@
     return cache_manager_.get();
   }
 
+  scoped_refptr<MockQuotaManager> mock_quota_manager_;
   std::unique_ptr<CacheStorageManager> cache_manager_;
   BrowserContext* browser_context_;
 
@@ -422,6 +440,38 @@
     return data;
   }
 
+  // Synchronous version of CacheStorageManager::HasCache().
+  bool HasCache(const std::string& cache_name) {
+    bool result = false;
+
+    base::RunLoop run_loop;
+    background_fetch_data_manager_->GetCacheStorageManager()->HasCache(
+        origin(), CacheStorageOwner::kBackgroundFetch, cache_name,
+        base::BindOnce(&BackgroundFetchDataManagerTest::DidFindCache,
+                       base::Unretained(this), run_loop.QuitClosure(),
+                       &result));
+    run_loop.Run();
+
+    return result;
+  }
+
+  // Synchronous version of CacheStorageManager::MatchCache().
+  bool MatchCache(const ServiceWorkerFetchRequest& request) {
+    bool result = false;
+    auto request_ptr = std::make_unique<ServiceWorkerFetchRequest>(request);
+
+    base::RunLoop run_loop;
+    background_fetch_data_manager_->GetCacheStorageManager()->MatchCache(
+        origin(), CacheStorageOwner::kBackgroundFetch, kExampleUniqueId,
+        std::move(request_ptr), nullptr /* match_params */,
+        base::BindOnce(&BackgroundFetchDataManagerTest::DidMatchCache,
+                       base::Unretained(this), run_loop.QuitClosure(),
+                       &result));
+    run_loop.Run();
+
+    return result;
+  }
+
   // Gets information about the number of background fetch requests by state.
   ResponseStateStats GetRequestStats(int64_t service_worker_registration_id) {
     ResponseStateStats stats;
@@ -545,6 +595,28 @@
     std::move(quit_closure).Run();
   }
 
+  void DidFindCache(base::OnceClosure quit_closure,
+                    bool* out_result,
+                    bool has_cache,
+                    blink::mojom::CacheStorageError error) {
+    DCHECK_EQ(error, blink::mojom::CacheStorageError::kSuccess);
+    *out_result = has_cache;
+    std::move(quit_closure).Run();
+  }
+
+  void DidMatchCache(base::OnceClosure quit_closure,
+                     bool* out_result,
+                     blink::mojom::CacheStorageError error,
+                     std::unique_ptr<ServiceWorkerResponse> response) {
+    if (error == blink::mojom::CacheStorageError::kSuccess) {
+      DCHECK(response);
+      *out_result = true;
+    } else {
+      *out_result = false;
+    }
+    std::move(quit_closure).Run();
+  }
+
   BackgroundFetchRegistrationStorage registration_storage_;
   std::unique_ptr<BackgroundFetchTestDataManager>
       background_fetch_data_manager_;
@@ -936,6 +1008,56 @@
                           2 /* completed_requests */}));
 }
 
+TEST_P(BackgroundFetchDataManagerTest, WriteToCache) {
+  // This test only applies to persistent storage.
+  if (registration_storage_ ==
+      BackgroundFetchRegistrationStorage::kNonPersistent)
+    return;
+
+  int64_t sw_id = RegisterServiceWorker();
+  ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
+
+  BackgroundFetchRegistrationId registration_id(
+      sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
+  ServiceWorkerFetchRequest request1;
+  request1.url = GURL(origin().GetURL().spec() + "1");
+  ServiceWorkerFetchRequest request2;
+  request2.url = GURL(origin().GetURL().spec() + "2");
+
+  BackgroundFetchOptions options;
+  blink::mojom::BackgroundFetchError error;
+
+  CreateRegistration(registration_id, {request1, request2}, options, &error);
+  EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
+
+  scoped_refptr<BackgroundFetchRequestInfo> request_info;
+  PopNextRequest(registration_id, &request_info);
+  ASSERT_TRUE(request_info);
+
+  AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
+                                                 true /* success */);
+  MarkRequestAsComplete(registration_id, request_info.get());
+
+  EXPECT_TRUE(HasCache(kExampleUniqueId));
+  EXPECT_FALSE(HasCache("foo"));
+
+  EXPECT_TRUE(MatchCache(request1));
+  EXPECT_FALSE(MatchCache(request2));
+
+  PopNextRequest(registration_id, &request_info);
+  ASSERT_TRUE(request_info);
+
+  AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
+                                                 true /* success */);
+  MarkRequestAsComplete(registration_id, request_info.get());
+  EXPECT_TRUE(MatchCache(request1));
+  EXPECT_TRUE(MatchCache(request2));
+
+  RestartDataManagerFromPersistentStorage();
+  EXPECT_TRUE(MatchCache(request1));
+  EXPECT_TRUE(MatchCache(request2));
+}
+
 TEST_P(BackgroundFetchDataManagerTest, GetSettledFetchesForRegistration) {
   // This test only applies to persistent storage.
   if (registration_storage_ ==
@@ -991,6 +1113,78 @@
   EXPECT_EQ(settled_fetches.size(), requests.size());
 }
 
+TEST_P(BackgroundFetchDataManagerTest, GetSettledFetchesFromCache) {
+  // This test only applies to persistent storage.
+  if (registration_storage_ ==
+      BackgroundFetchRegistrationStorage::kNonPersistent)
+    return;
+
+  int64_t sw_id = RegisterServiceWorker();
+  ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
+
+  BackgroundFetchRegistrationId registration_id(
+      sw_id, origin(), kExampleDeveloperId, kExampleUniqueId);
+  ServiceWorkerFetchRequest request1;
+  request1.url = GURL(origin().GetURL().spec() + "1");
+  ServiceWorkerFetchRequest request2;
+  request2.url = GURL(origin().GetURL().spec() + "2");
+
+  BackgroundFetchOptions options;
+  blink::mojom::BackgroundFetchError error;
+  CreateRegistration(registration_id, {request1, request2}, options, &error);
+  EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
+
+  bool succeeded = false;
+  std::vector<BackgroundFetchSettledFetch> settled_fetches;
+  // Nothing is downloaded yet.
+  GetSettledFetchesForRegistration(registration_id, &error, &succeeded,
+                                   &settled_fetches);
+  EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
+  EXPECT_TRUE(succeeded);
+  EXPECT_EQ(settled_fetches.size(), 0u);
+
+  scoped_refptr<BackgroundFetchRequestInfo> request_info;
+  PopNextRequest(registration_id, &request_info);
+  ASSERT_TRUE(request_info);
+  AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
+                                                 true /* success */);
+  MarkRequestAsComplete(registration_id, request_info.get());
+
+  GetSettledFetchesForRegistration(registration_id, &error, &succeeded,
+                                   &settled_fetches);
+  EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
+  EXPECT_TRUE(succeeded);
+  EXPECT_EQ(settled_fetches.size(), 1u);
+
+  PopNextRequest(registration_id, &request_info);
+  ASSERT_TRUE(request_info);
+  AnnotateRequestInfoWithFakeDownloadManagerData(request_info.get(),
+                                                 true /* success */);
+  MarkRequestAsComplete(registration_id, request_info.get());
+
+  GetSettledFetchesForRegistration(registration_id, &error, &succeeded,
+                                   &settled_fetches);
+  EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
+  EXPECT_TRUE(succeeded);
+  ASSERT_EQ(settled_fetches.size(), 2u);
+
+  // Sanity check that the responses are written to / read from the cache.
+  EXPECT_TRUE(MatchCache(request1));
+  EXPECT_TRUE(MatchCache(request2));
+  EXPECT_EQ(settled_fetches[0].response.cache_storage_cache_name,
+            kExampleUniqueId);
+  EXPECT_EQ(settled_fetches[1].response.cache_storage_cache_name,
+            kExampleUniqueId);
+
+  RestartDataManagerFromPersistentStorage();
+
+  GetSettledFetchesForRegistration(registration_id, &error, &succeeded,
+                                   &settled_fetches);
+  EXPECT_EQ(error, blink::mojom::BackgroundFetchError::NONE);
+  EXPECT_TRUE(succeeded);
+  EXPECT_EQ(settled_fetches.size(), 2u);
+}
+
 TEST_P(BackgroundFetchDataManagerTest, GetNumCompletedRequests) {
   int64_t sw_id = RegisterServiceWorker();
   ASSERT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, sw_id);
diff --git a/content/browser/background_fetch/background_fetch_request_info.cc b/content/browser/background_fetch/background_fetch_request_info.cc
index 25a471c..cc2055c 100644
--- a/content/browser/background_fetch/background_fetch_request_info.cc
+++ b/content/browser/background_fetch/background_fetch_request_info.cc
@@ -109,4 +109,10 @@
   return result_->response_time;
 }
 
+bool BackgroundFetchRequestInfo::IsResultSuccess() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(result_);
+  return result_->failure_reason == BackgroundFetchResult::FailureReason::NONE;
+}
+
 }  // namespace content
diff --git a/content/browser/background_fetch/background_fetch_request_info.h b/content/browser/background_fetch/background_fetch_request_info.h
index a8d1242..70ddbca 100644
--- a/content/browser/background_fetch/background_fetch_request_info.h
+++ b/content/browser/background_fetch/background_fetch_request_info.h
@@ -84,6 +84,9 @@
   // Returns the time at which the response was completed.
   const base::Time& GetResponseTime() const;
 
+  // Whether the BackgroundFetchResult was successful.
+  bool IsResultSuccess() const;
+
  private:
   friend class base::RefCountedDeleteOnSequence<BackgroundFetchRequestInfo>;
   friend class base::DeleteHelper<BackgroundFetchRequestInfo>;
diff --git a/content/browser/background_fetch/storage/get_settled_fetches_task.cc b/content/browser/background_fetch/storage/get_settled_fetches_task.cc
index 4bacc35..cdf6660 100644
--- a/content/browser/background_fetch/storage/get_settled_fetches_task.cc
+++ b/content/browser/background_fetch/storage/get_settled_fetches_task.cc
@@ -5,8 +5,8 @@
 #include "content/browser/background_fetch/storage/get_settled_fetches_task.h"
 
 #include "base/barrier_closure.h"
-#include "content/browser/background_fetch/background_fetch.pb.h"
 #include "content/browser/background_fetch/storage/database_helpers.h"
+#include "content/browser/cache_storage/cache_storage_manager.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 
 namespace content {
@@ -29,14 +29,39 @@
 GetSettledFetchesTask::~GetSettledFetchesTask() = default;
 
 void GetSettledFetchesTask::Start() {
+  base::RepeatingClosure barrier_closure = base::BarrierClosure(
+      2u, base::BindOnce(&GetSettledFetchesTask::GetResponses,
+                         weak_factory_.GetWeakPtr()));
+
+  cache_manager_->OpenCache(
+      registration_id_.origin(), CacheStorageOwner::kBackgroundFetch,
+      registration_id_.unique_id() /* cache_name */,
+      base::BindOnce(&GetSettledFetchesTask::DidOpenCache,
+                     weak_factory_.GetWeakPtr(), barrier_closure));
+
   service_worker_context()->GetRegistrationUserDataByKeyPrefix(
       registration_id_.service_worker_registration_id(),
       {CompletedRequestKeyPrefix(registration_id_.unique_id())},
       base::BindOnce(&GetSettledFetchesTask::DidGetCompletedRequests,
-                     weak_factory_.GetWeakPtr()));
+                     weak_factory_.GetWeakPtr(), barrier_closure));
+}
+
+void GetSettledFetchesTask::DidOpenCache(
+    base::OnceClosure done_closure,
+    CacheStorageCacheHandle handle,
+    blink::mojom::CacheStorageError error) {
+  if (error != blink::mojom::CacheStorageError::kSuccess) {
+    // TODO(crbug.com/780025): Log failures to UMA.
+    error_ = blink::mojom::BackgroundFetchError::STORAGE_ERROR;
+  } else {
+    DCHECK(handle.value());
+    handle_ = std::move(handle);
+  }
+  std::move(done_closure).Run();
 }
 
 void GetSettledFetchesTask::DidGetCompletedRequests(
+    base::OnceClosure done_closure,
     const std::vector<std::string>& data,
     ServiceWorkerStatusCode status) {
   switch (ToDatabaseStatus(status)) {
@@ -44,35 +69,44 @@
       break;
     // TODO(crbug.com/780025): Log failures to UMA.
     case DatabaseStatus::kFailed:
-      FinishTaskWithErrorCode(
-          blink::mojom::BackgroundFetchError::STORAGE_ERROR);
-      return;
+      error_ = blink::mojom::BackgroundFetchError::STORAGE_ERROR;
+      break;
     case DatabaseStatus::kNotFound:
       background_fetch_succeeded_ = false;
-      FinishTaskWithErrorCode(blink::mojom::BackgroundFetchError::INVALID_ID);
-      return;
+      error_ = blink::mojom::BackgroundFetchError::INVALID_ID;
+      break;
   }
 
-  // Nothing failed yet so the default state is success.
-  if (data.empty()) {
+  completed_requests_.reserve(data.size());
+  for (const std::string& serialized_completed_request : data) {
+    completed_requests_.emplace_back();
+    if (!completed_requests_.back().ParseFromString(
+            serialized_completed_request)) {
+      NOTREACHED()
+          << "Database is corrupt";  // TODO(crbug.com/780027): Nuke it.
+    }
+  }
+  std::move(done_closure).Run();
+}
+
+void GetSettledFetchesTask::GetResponses() {
+  if (error_ != blink::mojom::BackgroundFetchError::NONE) {
+    FinishTaskWithErrorCode(error_);
+    return;
+  }
+  if (completed_requests_.empty()) {
     FinishTaskWithErrorCode(blink::mojom::BackgroundFetchError::NONE);
     return;
   }
 
   base::RepeatingClosure barrier_closure = base::BarrierClosure(
-      data.size(),
+      completed_requests_.size(),
       base::BindOnce(&GetSettledFetchesTask::FinishTaskWithErrorCode,
                      weak_factory_.GetWeakPtr(),
                      blink::mojom::BackgroundFetchError::NONE));
 
-  settled_fetches_.reserve(data.size());
-  for (const std::string& serialized_completed_request : data) {
-    proto::BackgroundFetchCompletedRequest completed_request;
-    if (!completed_request.ParseFromString(serialized_completed_request)) {
-      NOTREACHED()
-          << "Database is corrupt";  // TODO(crbug.com/780027): Nuke it.
-    }
-
+  settled_fetches_.reserve(completed_requests_.size());
+  for (const auto& completed_request : completed_requests_) {
     settled_fetches_.emplace_back(BackgroundFetchSettledFetch());
     settled_fetches_.back().request =
         std::move(ServiceWorkerFetchRequest::ParseFromString(
@@ -81,13 +115,12 @@
       FillFailedResponse(&settled_fetches_.back().response, barrier_closure);
       continue;
     }
-    FillSuccessfulResponse(&settled_fetches_.back().response, barrier_closure);
+    FillSuccessfulResponse(&settled_fetches_.back(), barrier_closure);
   }
 }
 
-void GetSettledFetchesTask::FillFailedResponse(
-    ServiceWorkerResponse* response,
-    const base::RepeatingClosure& callback) {
+void GetSettledFetchesTask::FillFailedResponse(ServiceWorkerResponse* response,
+                                               base::OnceClosure callback) {
   DCHECK(response);
   background_fetch_succeeded_ = false;
   // TODO(rayankans): Fill failed response with error reports.
@@ -95,10 +128,31 @@
 }
 
 void GetSettledFetchesTask::FillSuccessfulResponse(
+    BackgroundFetchSettledFetch* settled_fetch,
+    base::OnceClosure callback) {
+  DCHECK(settled_fetch);
+  DCHECK(handle_.value());
+
+  auto request =
+      std::make_unique<ServiceWorkerFetchRequest>(settled_fetch->request);
+
+  handle_.value()->Match(
+      std::move(request), nullptr /* match_params */,
+      base::BindOnce(&GetSettledFetchesTask::DidMatchRequest,
+                     weak_factory_.GetWeakPtr(), &settled_fetch->response,
+                     std::move(callback)));
+}
+
+void GetSettledFetchesTask::DidMatchRequest(
     ServiceWorkerResponse* response,
-    const base::RepeatingClosure& callback) {
-  DCHECK(response);
-  // TODO(rayankans): Get the response stored in Cache Storage.
+    base::OnceClosure callback,
+    blink::mojom::CacheStorageError error,
+    std::unique_ptr<ServiceWorkerResponse> cache_response) {
+  if (error != blink::mojom::CacheStorageError::kSuccess) {
+    FillFailedResponse(response, std::move(callback));
+    return;
+  }
+  *response = std::move(*cache_response);
   std::move(callback).Run();
 }
 
diff --git a/content/browser/background_fetch/storage/get_settled_fetches_task.h b/content/browser/background_fetch/storage/get_settled_fetches_task.h
index 98cf7c9..94ce337 100644
--- a/content/browser/background_fetch/storage/get_settled_fetches_task.h
+++ b/content/browser/background_fetch/storage/get_settled_fetches_task.h
@@ -6,7 +6,9 @@
 #define CONTENT_BROWSER_BACKGROUND_FETCH_STORAGE_GET_SETTLED_FETCHES_TASK_H_
 
 #include "base/callback_forward.h"
+#include "content/browser/background_fetch/background_fetch.pb.h"
 #include "content/browser/background_fetch/storage/database_task.h"
+#include "content/browser/cache_storage/cache_storage_cache_handle.h"
 #include "content/common/service_worker/service_worker_status_code.h"
 #include "storage/browser/blob/blob_data_handle.h"
 
@@ -35,14 +37,26 @@
   void Start() override;
 
  private:
-  void DidGetCompletedRequests(const std::vector<std::string>& data,
+  void DidOpenCache(base::OnceClosure done_closure,
+                    CacheStorageCacheHandle handle,
+                    blink::mojom::CacheStorageError error);
+
+  void DidGetCompletedRequests(base::OnceClosure done_closure,
+                               const std::vector<std::string>& data,
                                ServiceWorkerStatusCode status);
 
-  void FillFailedResponse(ServiceWorkerResponse* response,
-                          const base::RepeatingClosure& callback);
+  void GetResponses();
 
-  void FillSuccessfulResponse(ServiceWorkerResponse* response,
-                              const base::RepeatingClosure& callback);
+  void FillFailedResponse(ServiceWorkerResponse* response,
+                          base::OnceClosure callback);
+
+  void FillSuccessfulResponse(BackgroundFetchSettledFetch* settled_fetch,
+                              base::OnceClosure callback);
+
+  void DidMatchRequest(ServiceWorkerResponse* response,
+                       base::OnceClosure callback,
+                       blink::mojom::CacheStorageError error,
+                       std::unique_ptr<ServiceWorkerResponse> cache_response);
 
   void FinishTaskWithErrorCode(blink::mojom::BackgroundFetchError error);
 
@@ -50,9 +64,16 @@
   CacheStorageManager* cache_manager_;
   SettledFetchesCallback settled_fetches_callback_;
 
+  // SettledFetchesCallback params.
   std::vector<BackgroundFetchSettledFetch> settled_fetches_;
   bool background_fetch_succeeded_ = true;
 
+  // Storage params.
+  CacheStorageCacheHandle handle_;
+  std::vector<proto::BackgroundFetchCompletedRequest> completed_requests_;
+  blink::mojom::BackgroundFetchError error_ =
+      blink::mojom::BackgroundFetchError::NONE;
+
   base::WeakPtrFactory<GetSettledFetchesTask> weak_factory_;  // Keep as last.
 
   DISALLOW_COPY_AND_ASSIGN(GetSettledFetchesTask);
diff --git a/content/browser/background_fetch/storage/mark_request_complete_task.cc b/content/browser/background_fetch/storage/mark_request_complete_task.cc
index 902fa07..c03d553 100644
--- a/content/browser/background_fetch/storage/mark_request_complete_task.cc
+++ b/content/browser/background_fetch/storage/mark_request_complete_task.cc
@@ -6,6 +6,7 @@
 
 #include "content/browser/background_fetch/background_fetch_data_manager.h"
 #include "content/browser/background_fetch/storage/database_helpers.h"
+#include "content/browser/cache_storage/cache_storage_manager.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "third_party/blink/public/mojom/blob/blob.mojom.h"
 
@@ -35,18 +36,50 @@
 }
 
 void MarkRequestCompleteTask::StoreResponse() {
-  ServiceWorkerResponse response;
+  auto response = std::make_unique<ServiceWorkerResponse>();
   bool is_response_valid = data_manager()->FillServiceWorkerResponse(
-      *request_info_, registration_id_.origin(), &response);
+      *request_info_, registration_id_.origin(), response.get());
 
   if (!is_response_valid) {
     // No point in caching the response, just do the request state transition.
+    CreateAndStoreCompletedRequest(/* succeeded = */ false);
+    return;
+  }
+  cache_manager_->OpenCache(
+      registration_id_.origin(), CacheStorageOwner::kBackgroundFetch,
+      registration_id_.unique_id() /* cache_name */,
+      base::BindOnce(&MarkRequestCompleteTask::DidOpenCache,
+                     weak_factory_.GetWeakPtr(), std::move(response)));
+}
+
+void MarkRequestCompleteTask::DidOpenCache(
+    std::unique_ptr<ServiceWorkerResponse> response,
+    CacheStorageCacheHandle handle,
+    blink::mojom::CacheStorageError error) {
+  if (error != blink::mojom::CacheStorageError::kSuccess) {
+    // TODO(crbug.com/780025): Log failures to UMA.
     CreateAndStoreCompletedRequest(false);
     return;
   }
-  // TODO(rayankans): Store the request/response pair in cache storage, and call
-  // `CreateAndStoreActiveRequest` via a callback.
-  CreateAndStoreCompletedRequest(true);
+  DCHECK(handle.value());
+
+  auto request = std::make_unique<ServiceWorkerFetchRequest>(
+      request_info_->fetch_request());
+
+  // We need to keep the handle refcounted while the write is happening,
+  // so it's passed along to the callback.
+  handle.value()->Put(
+      std::move(request), std::move(response),
+      base::BindOnce(&MarkRequestCompleteTask::DidWriteToCache,
+                     weak_factory_.GetWeakPtr(), std::move(handle)));
+}
+
+void MarkRequestCompleteTask::DidWriteToCache(
+    CacheStorageCacheHandle handle,
+    blink::mojom::CacheStorageError error) {
+  // TODO(crbug.com/780025): Log failures to UMA.
+  CreateAndStoreCompletedRequest(
+      /* succeeded = */ error == blink::mojom::CacheStorageError::kSuccess);
 }
 
 void MarkRequestCompleteTask::CreateAndStoreCompletedRequest(bool succeeded) {
@@ -107,4 +140,4 @@
 
 }  // namespace background_fetch
 
-}  // namespace content
\ No newline at end of file
+}  // namespace content
diff --git a/content/browser/background_fetch/storage/mark_request_complete_task.h b/content/browser/background_fetch/storage/mark_request_complete_task.h
index 80c0de57..a36c1f7f 100644
--- a/content/browser/background_fetch/storage/mark_request_complete_task.h
+++ b/content/browser/background_fetch/storage/mark_request_complete_task.h
@@ -10,6 +10,7 @@
 #include "content/browser/background_fetch/background_fetch.pb.h"
 #include "content/browser/background_fetch/background_fetch_request_info.h"
 #include "content/browser/background_fetch/storage/database_task.h"
+#include "content/browser/cache_storage/cache_storage_cache_handle.h"
 #include "content/common/service_worker/service_worker_status_code.h"
 
 namespace content {
@@ -39,6 +40,13 @@
  private:
   void StoreResponse();
 
+  void DidOpenCache(std::unique_ptr<ServiceWorkerResponse> response,
+                    CacheStorageCacheHandle handle,
+                    blink::mojom::CacheStorageError error);
+
+  void DidWriteToCache(CacheStorageCacheHandle handle,
+                       blink::mojom::CacheStorageError error);
+
   void CreateAndStoreCompletedRequest(bool succeeded);
 
   void DidStoreCompletedRequest(ServiceWorkerStatusCode status);
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 62c0ac8..420eb2d 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -32,6 +32,7 @@
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/web_package/signed_exchange_envelope.h"
+#include "content/browser/web_package/signed_exchange_error.h"
 #include "content/common/navigation_params.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
@@ -876,6 +877,46 @@
   return url.ReplaceComponents(replacements).spec();
 }
 
+String SignedExchangeErrorErrorFieldToString(SignedExchangeError::Field field) {
+  switch (field) {
+    case SignedExchangeError::Field::kSignatureSig:
+      return Network::SignedExchangeErrorFieldEnum::SignatureSig;
+    case SignedExchangeError::Field::kSignatureIintegrity:
+      return Network::SignedExchangeErrorFieldEnum::SignatureIntegrity;
+    case SignedExchangeError::Field::kSignatureCertUrl:
+      return Network::SignedExchangeErrorFieldEnum::SignatureCertUrl;
+    case SignedExchangeError::Field::kSignatureCertSha256:
+      return Network::SignedExchangeErrorFieldEnum::SignatureCertSha256;
+    case SignedExchangeError::Field::kSignatureValidityUrl:
+      return Network::SignedExchangeErrorFieldEnum::SignatureValidityUrl;
+    case SignedExchangeError::Field::kSignatureTimestamps:
+      return Network::SignedExchangeErrorFieldEnum::SignatureTimestamps;
+  }
+  NOTREACHED();
+  return "";
+}
+
+std::unique_ptr<Network::SignedExchangeError> BuildSignedExchangeError(
+    const SignedExchangeError& error) {
+  std::unique_ptr<Network::SignedExchangeError> signed_exchange_error =
+      Network::SignedExchangeError::Create().SetMessage(error.message).Build();
+  if (error.field) {
+    signed_exchange_error->SetSignatureIndex(error.field->first);
+    signed_exchange_error->SetErrorField(
+        SignedExchangeErrorErrorFieldToString(error.field->second));
+  }
+  return signed_exchange_error;
+}
+
+std::unique_ptr<Array<Network::SignedExchangeError>> BuildSignedExchangeErrors(
+    const std::vector<SignedExchangeError>& errors) {
+  std::unique_ptr<Array<Network::SignedExchangeError>> signed_exchange_errors =
+      Array<Network::SignedExchangeError>::create();
+  for (const auto& error : errors)
+    signed_exchange_errors->addItem(BuildSignedExchangeError(error));
+  return signed_exchange_errors;
+}
+
 }  // namespace
 
 class BackgroundSyncRestorer {
@@ -1654,7 +1695,7 @@
     const base::Optional<SignedExchangeEnvelope>& header,
     const scoped_refptr<net::X509Certificate>& certificate,
     const base::Optional<net::SSLInfo>& ssl_info,
-    const std::vector<std::string>& error_messages) {
+    const std::vector<SignedExchangeError>& errors) {
   if (!enabled_)
     return;
   std::unique_ptr<Network::SignedExchangeInfo> signed_exchange_info =
@@ -1713,12 +1754,8 @@
   }
   if (ssl_info)
     signed_exchange_info->SetSecurityDetails(BuildSecurityDetails(*ssl_info));
-  if (error_messages.size()) {
-    std::unique_ptr<Array<String>> errors = Array<String>::create();
-    for (const auto& message : error_messages)
-      errors->addItem(message);
-    signed_exchange_info->SetErrors(std::move(errors));
-  }
+  if (errors.size())
+    signed_exchange_info->SetErrors(BuildSignedExchangeErrors(errors));
 
   frontend_->SignedExchangeReceived(
       devtools_navigation_token ? devtools_navigation_token->ToString() : "",
diff --git a/content/browser/devtools/protocol/network_handler.h b/content/browser/devtools/protocol/network_handler.h
index 26c167c..1587e60 100644
--- a/content/browser/devtools/protocol/network_handler.h
+++ b/content/browser/devtools/protocol/network_handler.h
@@ -51,6 +51,7 @@
 class StoragePartition;
 struct GlobalRequestID;
 struct InterceptedRequestInfo;
+struct SignedExchangeError;
 
 namespace protocol {
 class BackgroundSyncRestorer;
@@ -169,7 +170,7 @@
       const base::Optional<SignedExchangeEnvelope>& header,
       const scoped_refptr<net::X509Certificate>& certificate,
       const base::Optional<net::SSLInfo>& ssl_info,
-      const std::vector<std::string>& error_messages);
+      const std::vector<SignedExchangeError>& errors);
 
   bool enabled() const { return enabled_; }
 
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 6e770aa..af69823 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -233,11 +233,11 @@
     const base::Optional<SignedExchangeEnvelope>& header,
     const scoped_refptr<net::X509Certificate>& certificate,
     const base::Optional<net::SSLInfo>& ssl_info,
-    const std::vector<std::string>& error_messages) {
+    const std::vector<SignedExchangeError>& errors) {
   DispatchToAgents(frame_tree_node,
                    &protocol::NetworkHandler::OnSignedExchangeReceived,
                    devtools_navigation_token, outer_request_url, outer_response,
-                   header, certificate, ssl_info, error_messages);
+                   header, certificate, ssl_info, errors);
 }
 
 // static
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h
index cbc6f28..fd72011 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.h
+++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -55,6 +55,7 @@
 class NavigationThrottle;
 class RenderFrameHostImpl;
 class SignedExchangeEnvelope;
+struct SignedExchangeError;
 
 class CONTENT_EXPORT RenderFrameDevToolsAgentHost
     : public DevToolsAgentHostImpl,
@@ -105,7 +106,7 @@
       const base::Optional<SignedExchangeEnvelope>& header,
       const scoped_refptr<net::X509Certificate>& certificate,
       const base::Optional<net::SSLInfo>& ssl_info,
-      const std::vector<std::string>& error_messages);
+      const std::vector<SignedExchangeError>& errors);
   static void OnSignedExchangeCertificateRequestSent(
       FrameTreeNode* frame_tree_node,
       const base::UnguessableToken& request_id,
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 8fea0bd8..1835ab3 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -1250,6 +1250,7 @@
 
   std::unique_ptr<network::ResourceRequest> new_request = CreateResourceRequest(
       request_info.get(), frame_tree_node_id, allow_download_);
+  new_request->transition_type = request_info->common_params.transition;
 
   if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
     DCHECK(!request_controller_);
diff --git a/content/browser/notifications/blink_notification_service_impl_unittest.cc b/content/browser/notifications/blink_notification_service_impl_unittest.cc
index a48f183..08d78173 100644
--- a/content/browser/notifications/blink_notification_service_impl_unittest.cc
+++ b/content/browser/notifications/blink_notification_service_impl_unittest.cc
@@ -274,8 +274,9 @@
   // PlatformNotificationContext::ReadNotificationData
   bool ReadNotificationData(const std::string& notification_id) {
     base::RunLoop run_loop;
-    notification_context_->ReadNotificationData(
+    notification_context_->ReadNotificationDataAndRecordInteraction(
         notification_id, GURL(kTestOrigin),
+        PlatformNotificationContext::Interaction::NONE,
         base::AdaptCallbackForRepeating(base::BindOnce(
             &BlinkNotificationServiceImplTest::DidReadNotificationData,
             base::Unretained(this), run_loop.QuitClosure())));
diff --git a/content/browser/notifications/notification_database.cc b/content/browser/notifications/notification_database.cc
index 87dca49..b5f571a 100644
--- a/content/browser/notifications/notification_database.cc
+++ b/content/browser/notifications/notification_database.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/files/file_util.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "content/browser/notifications/notification_database_data_conversions.h"
@@ -168,6 +169,39 @@
                                       notification_database_data);
 }
 
+NotificationDatabase::Status
+NotificationDatabase::ReadNotificationDataAndRecordInteraction(
+    const std::string& notification_id,
+    const GURL& origin,
+    PlatformNotificationContext::Interaction interaction,
+    NotificationDatabaseData* notification_database_data) {
+  Status status =
+      ReadNotificationData(notification_id, origin, notification_database_data);
+  if (status != STATUS_OK)
+    return status;
+
+  // Update the appropriate fields for UKM logging purposes.
+  switch (interaction) {
+    case PlatformNotificationContext::Interaction::CLOSED:
+      return status;
+    case PlatformNotificationContext::Interaction::NONE:
+      return status;
+    case PlatformNotificationContext::Interaction::ACTION_BUTTON_CLICKED:
+      notification_database_data->num_action_button_clicks += 1;
+      break;
+    case PlatformNotificationContext::Interaction::CLICKED:
+      notification_database_data->num_clicks += 1;
+      break;
+  }
+
+  // Write the changed values to the database.
+  status = WriteNotificationData(origin, *notification_database_data);
+  UMA_HISTOGRAM_ENUMERATION(
+      "Notifications.Database.ReadResultRecordInteraction", status,
+      NotificationDatabase::STATUS_COUNT);
+  return status;
+}
+
 NotificationDatabase::Status NotificationDatabase::ReadAllNotificationData(
     std::vector<NotificationDatabaseData>* notification_data_vector) const {
   return ReadAllNotificationDataInternal(
diff --git a/content/browser/notifications/notification_database.h b/content/browser/notifications/notification_database.h
index e85594f..219107e 100644
--- a/content/browser/notifications/notification_database.h
+++ b/content/browser/notifications/notification_database.h
@@ -14,6 +14,7 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/sequence_checker.h"
+#include "content/browser/notifications/platform_notification_context_impl.h"
 #include "content/common/content_export.h"
 
 class GURL;
@@ -90,6 +91,15 @@
       const GURL& origin,
       NotificationDatabaseData* notification_data) const;
 
+  // This function is identical to ReadNotificationData above, but also records
+  // an interaction with that notification in the database for UKM logging
+  // purposes.
+  Status ReadNotificationDataAndRecordInteraction(
+      const std::string& notification_id,
+      const GURL& origin,
+      PlatformNotificationContext::Interaction interaction,
+      NotificationDatabaseData* notification_data);
+
   // Reads all notification data for all origins from the database, and appends
   // the data to |notification_data_vector|. Returns the status code.
   Status ReadAllNotificationData(
diff --git a/content/browser/notifications/notification_database_unittest.cc b/content/browser/notifications/notification_database_unittest.cc
index b1eca237..da55e55 100644
--- a/content/browser/notifications/notification_database_unittest.cc
+++ b/content/browser/notifications/notification_database_unittest.cc
@@ -394,6 +394,44 @@
   }
 }
 
+TEST_F(NotificationDatabaseTest, ReadNotificationUpdateInteraction) {
+  std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
+  ASSERT_EQ(NotificationDatabase::STATUS_OK,
+            database->Open(true /* create_if_missing */));
+
+  GURL origin("https://example.com");
+
+  NotificationDatabaseData database_data, read_database_data;
+  database_data.notification_id = GenerateNotificationId();
+  database_data.notification_data.title = base::UTF8ToUTF16("My Notification");
+
+  ASSERT_EQ(NotificationDatabase::STATUS_OK,
+            database->WriteNotificationData(origin, database_data));
+
+  // Check that when a notification has an interaction, the appropriate field is
+  // updated on the read.
+  ASSERT_EQ(NotificationDatabase::STATUS_OK,
+            database->ReadNotificationDataAndRecordInteraction(
+                database_data.notification_id, origin,
+                PlatformNotificationContext::Interaction::CLICKED,
+                &read_database_data));
+  EXPECT_EQ(1, read_database_data.num_clicks);
+
+  ASSERT_EQ(NotificationDatabase::STATUS_OK,
+            database->ReadNotificationDataAndRecordInteraction(
+                database_data.notification_id, origin,
+                PlatformNotificationContext::Interaction::ACTION_BUTTON_CLICKED,
+                &read_database_data));
+  EXPECT_EQ(1, read_database_data.num_action_button_clicks);
+
+  ASSERT_EQ(NotificationDatabase::STATUS_OK,
+            database->ReadNotificationDataAndRecordInteraction(
+                database_data.notification_id, origin,
+                PlatformNotificationContext::Interaction::ACTION_BUTTON_CLICKED,
+                &read_database_data));
+  EXPECT_EQ(2, read_database_data.num_action_button_clicks);
+}
+
 TEST_F(NotificationDatabaseTest, DeleteInvalidNotificationData) {
   std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory());
   ASSERT_EQ(NotificationDatabase::STATUS_OK,
diff --git a/content/browser/notifications/notification_event_dispatcher_impl.cc b/content/browser/notifications/notification_event_dispatcher_impl.cc
index b91d29c..fbe442c 100644
--- a/content/browser/notifications/notification_event_dispatcher_impl.cc
+++ b/content/browser/notifications/notification_event_dispatcher_impl.cc
@@ -187,13 +187,14 @@
 void ReadNotificationDatabaseData(
     const std::string& notification_id,
     const GURL& origin,
+    PlatformNotificationContext::Interaction interaction,
     const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context,
     const scoped_refptr<PlatformNotificationContext>& notification_context,
     const NotificationOperationCallback& notification_read_callback,
     const NotificationDispatchCompleteCallback& dispatch_error_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  notification_context->ReadNotificationData(
-      notification_id, origin,
+  notification_context->ReadNotificationDataAndRecordInteraction(
+      notification_id, origin, interaction,
       base::Bind(&FindServiceWorkerRegistration, origin, service_worker_context,
                  notification_context, notification_read_callback,
                  dispatch_error_callback));
@@ -334,6 +335,7 @@
     BrowserContext* browser_context,
     const std::string& notification_id,
     const GURL& origin,
+    const PlatformNotificationContext::Interaction interaction,
     const NotificationOperationCallbackWithContext&
         notification_action_callback,
     const NotificationDispatchCompleteCallback& notification_error_callback) {
@@ -353,7 +355,7 @@
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
       base::BindOnce(
-          &ReadNotificationDatabaseData, notification_id, origin,
+          &ReadNotificationDatabaseData, notification_id, origin, interaction,
           service_worker_context, notification_context,
           base::Bind(notification_action_callback, notification_context),
           notification_error_callback));
@@ -387,8 +389,13 @@
   auto repeating_callback =
       base::AdaptCallbackForRepeating(std::move(dispatch_complete_callback));
 
+  PlatformNotificationContext::Interaction interaction =
+      action_index.has_value()
+          ? PlatformNotificationContext::Interaction::ACTION_BUTTON_CLICKED
+          : PlatformNotificationContext::Interaction::CLICKED;
+
   DispatchNotificationEvent(
-      browser_context, notification_id, origin,
+      browser_context, notification_id, origin, interaction,
       base::Bind(&DoDispatchNotificationClickEvent, action_index, reply,
                  repeating_callback),
       repeating_callback /* notification_error_callback */);
@@ -407,6 +414,7 @@
 
   DispatchNotificationEvent(
       browser_context, notification_id, origin,
+      PlatformNotificationContext::Interaction::CLOSED,
       base::Bind(&DoDispatchNotificationCloseEvent, notification_id, by_user,
                  repeating_callback),
       repeating_callback /* notification_error_callback */);
diff --git a/content/browser/notifications/platform_notification_context_impl.cc b/content/browser/notifications/platform_notification_context_impl.cc
index e174998..b5a2f94 100644
--- a/content/browser/notifications/platform_notification_context_impl.cc
+++ b/content/browser/notifications/platform_notification_context_impl.cc
@@ -154,27 +154,30 @@
       });
 }
 
-void PlatformNotificationContextImpl::ReadNotificationData(
+void PlatformNotificationContextImpl::ReadNotificationDataAndRecordInteraction(
     const std::string& notification_id,
     const GURL& origin,
+    const PlatformNotificationContext::Interaction interaction,
     const ReadResultCallback& callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   LazyInitialize(
       base::Bind(&PlatformNotificationContextImpl::DoReadNotificationData, this,
-                 notification_id, origin, callback),
+                 notification_id, origin, interaction, callback),
       base::Bind(callback, false /* success */, NotificationDatabaseData()));
 }
 
 void PlatformNotificationContextImpl::DoReadNotificationData(
     const std::string& notification_id,
     const GURL& origin,
+    Interaction interaction,
     const ReadResultCallback& callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
   NotificationDatabaseData database_data;
   NotificationDatabase::Status status =
-      database_->ReadNotificationData(notification_id, origin, &database_data);
+      database_->ReadNotificationDataAndRecordInteraction(
+          notification_id, origin, interaction, &database_data);
 
   UMA_HISTOGRAM_ENUMERATION("Notifications.Database.ReadResult", status,
                             NotificationDatabase::STATUS_COUNT);
diff --git a/content/browser/notifications/platform_notification_context_impl.h b/content/browser/notifications/platform_notification_context_impl.h
index a780c45..61c76f4 100644
--- a/content/browser/notifications/platform_notification_context_impl.h
+++ b/content/browser/notifications/platform_notification_context_impl.h
@@ -81,9 +81,11 @@
   }
 
   // PlatformNotificationContext implementation.
-  void ReadNotificationData(const std::string& notification_id,
-                            const GURL& origin,
-                            const ReadResultCallback& callback) override;
+  void ReadNotificationDataAndRecordInteraction(
+      const std::string& notification_id,
+      const GURL& origin,
+      Interaction interaction,
+      const ReadResultCallback& callback) override;
   void WriteNotificationData(const GURL& origin,
                              const NotificationDatabaseData& database_data,
                              const WriteResultCallback& callback) override;
@@ -136,6 +138,7 @@
   // IO thread when the operation has completed.
   void DoReadNotificationData(const std::string& notification_id,
                               const GURL& origin,
+                              Interaction interaction,
                               const ReadResultCallback& callback);
 
   // Updates the database (and the result callback) based on
diff --git a/content/browser/notifications/platform_notification_context_unittest.cc b/content/browser/notifications/platform_notification_context_unittest.cc
index d661a25..684fd2e7 100644
--- a/content/browser/notifications/platform_notification_context_unittest.cc
+++ b/content/browser/notifications/platform_notification_context_unittest.cc
@@ -145,8 +145,9 @@
   scoped_refptr<PlatformNotificationContextImpl> context =
       CreatePlatformNotificationContext();
 
-  context->ReadNotificationData(
+  context->ReadNotificationDataAndRecordInteraction(
       "invalid-notification-id", GURL("https://example.com"),
+      PlatformNotificationContext::Interaction::NONE,
       base::Bind(&PlatformNotificationContextTest::DidReadNotificationData,
                  base::Unretained(this)));
 
@@ -175,8 +176,8 @@
   ASSERT_TRUE(success());
   EXPECT_FALSE(notification_id().empty());
 
-  context->ReadNotificationData(
-      notification_id(), origin,
+  context->ReadNotificationDataAndRecordInteraction(
+      notification_id(), origin, PlatformNotificationContext::Interaction::NONE,
       base::Bind(&PlatformNotificationContextTest::DidReadNotificationData,
                  base::Unretained(this)));
 
@@ -297,8 +298,8 @@
   // The notification existed, so it should have been removed successfully.
   ASSERT_TRUE(success());
 
-  context->ReadNotificationData(
-      notification_id(), origin,
+  context->ReadNotificationDataAndRecordInteraction(
+      notification_id(), origin, PlatformNotificationContext::Interaction::NONE,
       base::Bind(&PlatformNotificationContextTest::DidReadNotificationData,
                  base::Unretained(this)));
 
@@ -366,8 +367,8 @@
   ASSERT_EQ(SERVICE_WORKER_OK, unregister_status);
 
   // And verify that the associated notification has indeed been dropped.
-  notification_context->ReadNotificationData(
-      notification_id(), origin,
+  notification_context->ReadNotificationDataAndRecordInteraction(
+      notification_id(), origin, PlatformNotificationContext::Interaction::NONE,
       base::Bind(&PlatformNotificationContextTest::DidReadNotificationData,
                  base::Unretained(this)));
 
@@ -401,8 +402,8 @@
   // Verify that reading notification data fails because the data does not
   // exist anymore. Deliberately omit RunUntilIdle(), since this is unlikely to
   // be the case when OnStorageWiped gets called in production.
-  context->ReadNotificationData(
-      notification_id(), origin,
+  context->ReadNotificationDataAndRecordInteraction(
+      notification_id(), origin, PlatformNotificationContext::Interaction::NONE,
       base::Bind(&PlatformNotificationContextTest::DidReadNotificationData,
                  base::Unretained(this)));
 
@@ -424,8 +425,9 @@
   OverrideTaskRunnerForTesting(context.get());
 
   // Trigger a read-operation to force creating the database.
-  context->ReadNotificationData(
+  context->ReadNotificationDataAndRecordInteraction(
       "invalid-notification-id", GURL("https://example.com"),
+      PlatformNotificationContext::Interaction::NONE,
       base::Bind(&PlatformNotificationContextTest::DidReadNotificationData,
                  base::Unretained(this)));
 
@@ -558,8 +560,9 @@
   ASSERT_TRUE(success());
   ASSERT_EQ(0u, notification_database_datas.size());
 
-  context->ReadNotificationData(
+  context->ReadNotificationDataAndRecordInteraction(
       notification_id(), origin,
+      PlatformNotificationContext::Interaction::CLOSED,
       base::Bind(&PlatformNotificationContextTest::DidReadNotificationData,
                  base::Unretained(this)));
 
diff --git a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
index b04c437b..e27cc29 100644
--- a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
+++ b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
@@ -47,16 +47,16 @@
   if (initiator_->IsBeingDestroyed())
     return;
 
+  initiator_->SetHasPictureInPictureVideo(false);
   OnLeavingPictureInPicture();
 }
 
 PictureInPictureWindowControllerImpl::PictureInPictureWindowControllerImpl(
     WebContents* initiator)
-    : initiator_(initiator) {
+    : initiator_(static_cast<WebContentsImpl* const>(initiator)) {
   DCHECK(initiator_);
 
-  media_web_contents_observer_ = static_cast<WebContentsImpl* const>(initiator_)
-                                     ->media_web_contents_observer();
+  media_web_contents_observer_ = initiator_->media_web_contents_observer();
 
   window_ =
       GetContentClient()->browser()->CreateWindowForPictureInPicture(this);
@@ -68,13 +68,16 @@
   DCHECK(surface_id_.is_valid());
 
   window_->Show();
+  initiator_->SetHasPictureInPictureVideo(true);
 
   return window_->GetBounds().size();
 }
 
 void PictureInPictureWindowControllerImpl::Close() {
   DCHECK(window_);
+
   window_->Hide();
+  initiator_->SetHasPictureInPictureVideo(false);
 
   surface_id_ = viz::SurfaceId();
 
diff --git a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h
index f4355c39..5b3dc96a 100644
--- a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h
+++ b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h
@@ -58,7 +58,7 @@
 
   std::unique_ptr<OverlayWindow> window_;
   std::unique_ptr<OverlaySurfaceEmbedder> embedder_;
-  WebContents* const initiator_;
+  WebContentsImpl* const initiator_;
 
   // Used to determine the state of the media player and route messages to
   // the corresponding media player with id |media_player_id_|.
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 7f53c05..c719f77 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -1447,6 +1447,19 @@
   return bluetooth_connected_device_count_ > 0;
 }
 
+bool WebContentsImpl::HasPictureInPictureVideo() const {
+  return has_picture_in_picture_video_;
+}
+
+void WebContentsImpl::SetHasPictureInPictureVideo(
+    bool has_picture_in_picture_video) {
+  // If status of |this| is already accurate, there is no need to update.
+  if (has_picture_in_picture_video == has_picture_in_picture_video_)
+    return;
+  has_picture_in_picture_video_ = has_picture_in_picture_video;
+  NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
+}
+
 bool WebContentsImpl::IsCrashed() const {
   switch (crashed_status_) {
     case base::TERMINATION_STATUS_PROCESS_CRASHED:
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index a412678..852eba6 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -356,6 +356,7 @@
   void SetAudioMuted(bool mute) override;
   bool IsCurrentlyAudible() override;
   bool IsConnectedToBluetoothDevice() const override;
+  bool HasPictureInPictureVideo() const override;
   bool IsCrashed() const override;
   void SetIsCrashed(base::TerminationStatus status, int error_code) override;
   base::TerminationStatus GetCrashedStatus() const override;
@@ -946,6 +947,10 @@
   // Picture-in-Picture mode has ended.
   void ExitPictureInPicture();
 
+  // Updates the tracking information for |this| to know if there is
+  // a video currently in Picture-in-Picture mode.
+  void SetHasPictureInPictureVideo(bool has_picture_in_picture_video);
+
 #if defined(OS_ANDROID)
   // Called by FindRequestManager when all of the find match rects are in.
   void NotifyFindMatchRectsReply(int version,
@@ -1660,6 +1665,8 @@
 
   size_t bluetooth_connected_device_count_;
 
+  bool has_picture_in_picture_video_ = false;
+
   // Notifies ResourceDispatcherHostImpl of various events related to loading.
   std::unique_ptr<LoaderIOThreadNotifier> loader_io_thread_notifier_;
 
diff --git a/content/browser/web_package/signed_exchange_devtools_proxy.cc b/content/browser/web_package/signed_exchange_devtools_proxy.cc
index 4c83fda..aed00e8 100644
--- a/content/browser/web_package/signed_exchange_devtools_proxy.cc
+++ b/content/browser/web_package/signed_exchange_devtools_proxy.cc
@@ -8,6 +8,7 @@
 #include "content/browser/devtools/render_frame_devtools_agent_host.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/web_package/signed_exchange_envelope.h"
+#include "content/browser/web_package/signed_exchange_error.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
@@ -76,14 +77,14 @@
     base::Optional<SignedExchangeEnvelope> header,
     scoped_refptr<net::X509Certificate> certificate,
     base::Optional<net::SSLInfo> ssl_info,
-    std::vector<std::string> error_messages) {
+    std::vector<SignedExchangeError> errors) {
   FrameTreeNode* frame_tree_node =
       FrameTreeNode::GloballyFindByID(frame_tree_node_id_getter.Run());
   if (!frame_tree_node)
     return;
   RenderFrameDevToolsAgentHost::OnSignedExchangeReceived(
       frame_tree_node, devtools_navigation_token, outer_request_url,
-      outer_response->head, header, certificate, ssl_info, error_messages);
+      outer_response->head, header, certificate, ssl_info, errors);
 }
 
 }  // namespace
@@ -106,10 +107,11 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 }
 
-void SignedExchangeDevToolsProxy::ReportErrorMessage(
-    const std::string& message) {
+void SignedExchangeDevToolsProxy::ReportError(
+    const std::string& message,
+    base::Optional<SignedExchangeError::FieldIndexPair> error_field) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  error_messages_.push_back(message);
+  errors_.push_back(SignedExchangeError(message, std::move(error_field)));
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
       base::BindOnce(&AddErrorMessageToConsoleOnUI, frame_tree_node_id_getter_,
@@ -181,7 +183,7 @@
       base::BindOnce(&OnSignedExchangeReceivedOnUI, frame_tree_node_id_getter_,
                      outer_request_url_, resource_response->DeepCopy(),
                      devtools_navigation_token_, header, certificate,
-                     std::move(ssl_info_opt), std::move(error_messages_)));
+                     std::move(ssl_info_opt), std::move(errors_)));
 }
 
 }  // namespace content
diff --git a/content/browser/web_package/signed_exchange_devtools_proxy.h b/content/browser/web_package/signed_exchange_devtools_proxy.h
index 1487748..ebbddd4 100644
--- a/content/browser/web_package/signed_exchange_devtools_proxy.h
+++ b/content/browser/web_package/signed_exchange_devtools_proxy.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/unguessable_token.h"
+#include "content/browser/web_package/signed_exchange_error.h"
 #include "content/common/content_export.h"
 #include "services/network/public/cpp/resource_response.h"
 
@@ -57,7 +58,10 @@
       bool report_raw_headers);
   ~SignedExchangeDevToolsProxy();
 
-  void ReportErrorMessage(const std::string& message);
+  void ReportError(
+      const std::string& message,
+      base::Optional<SignedExchangeError::FieldIndexPair> error_field);
+
   void CertificateRequestSent(const base::UnguessableToken& request_id,
                               const network::ResourceRequest& request);
   void CertificateResponseReceived(const base::UnguessableToken& request_id,
@@ -78,7 +82,7 @@
   const base::RepeatingCallback<int(void)> frame_tree_node_id_getter_;
   const base::Optional<const base::UnguessableToken> devtools_navigation_token_;
   const bool devtools_enabled_;
-  std::vector<std::string> error_messages_;
+  std::vector<SignedExchangeError> errors_;
 
   DISALLOW_COPY_AND_ASSIGN(SignedExchangeDevToolsProxy);
 };
diff --git a/content/browser/web_package/signed_exchange_envelope.cc b/content/browser/web_package/signed_exchange_envelope.cc
index e88a8a82..a82c8141 100644
--- a/content/browser/web_package/signed_exchange_envelope.cc
+++ b/content/browser/web_package/signed_exchange_envelope.cc
@@ -259,16 +259,6 @@
 
 }  // namespace
 
-constexpr size_t SignedExchangeEnvelope::kEncodedLengthInBytes;
-
-// static
-size_t SignedExchangeEnvelope::ParseEncodedLength(
-    base::span<const uint8_t> input) {
-  DCHECK_EQ(input.size(), SignedExchangeEnvelope::kEncodedLengthInBytes);
-  return static_cast<size_t>(input[0]) << 16 |
-         static_cast<size_t>(input[1]) << 8 | static_cast<size_t>(input[2]);
-}
-
 // static
 base::Optional<SignedExchangeEnvelope> SignedExchangeEnvelope::Parse(
     base::span<const uint8_t> input,
diff --git a/content/browser/web_package/signed_exchange_envelope.h b/content/browser/web_package/signed_exchange_envelope.h
index 960d447..63793ab 100644
--- a/content/browser/web_package/signed_exchange_envelope.h
+++ b/content/browser/web_package/signed_exchange_envelope.h
@@ -28,12 +28,6 @@
 // https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html
 class CONTENT_EXPORT SignedExchangeEnvelope {
  public:
-  static constexpr size_t kEncodedLengthInBytes = 3;
-  // Parse encoded length of the variable-length field in the signed exchange.
-  // Note: |input| must be pointing to a valid memory address that has at least
-  // |kEncodedLengthInBytes|.
-  static size_t ParseEncodedLength(base::span<const uint8_t> input);
-
   using HeaderMap = std::map<std::string, std::string>;
 
   // Parse headers from the application/signed-exchange;v=b0 format.
diff --git a/content/browser/web_package/signed_exchange_envelope_unittest.cc b/content/browser/web_package/signed_exchange_envelope_unittest.cc
index 5cf601b..7506b58 100644
--- a/content/browser/web_package/signed_exchange_envelope_unittest.cc
+++ b/content/browser/web_package/signed_exchange_envelope_unittest.cc
@@ -11,6 +11,7 @@
 #include "components/cbor/cbor_values.h"
 #include "components/cbor/cbor_writer.h"
 #include "content/browser/web_package/signed_exchange_consts.h"
+#include "content/browser/web_package/signed_exchange_prologue.h"
 #include "content/public/common/content_paths.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -54,22 +55,6 @@
 
 }  // namespace
 
-TEST(SignedExchangeEnvelopeTest, ParseEncodedLength) {
-  constexpr struct {
-    uint8_t bytes[SignedExchangeEnvelope::kEncodedLengthInBytes];
-    size_t expected;
-  } kTestCases[] = {
-      {{0x00, 0x00, 0x01}, 1u}, {{0x01, 0xe2, 0x40}, 123456u},
-  };
-
-  int test_element_index = 0;
-  for (const auto& test_case : kTestCases) {
-    SCOPED_TRACE(testing::Message() << "testing case " << test_element_index++);
-    EXPECT_EQ(SignedExchangeEnvelope::ParseEncodedLength(test_case.bytes),
-              test_case.expected);
-  }
-}
-
 TEST(SignedExchangeEnvelopeTest, ParseGoldenFile) {
   base::FilePath test_htxg_path;
   base::PathService::Get(content::DIR_TEST_DATA, &test_htxg_path);
@@ -80,15 +65,15 @@
   ASSERT_TRUE(base::ReadFileToString(test_htxg_path, &contents));
   auto* contents_bytes = reinterpret_cast<const uint8_t*>(contents.data());
 
-  ASSERT_GT(contents.size(), SignedExchangeEnvelope::kEncodedLengthInBytes);
+  ASSERT_GT(contents.size(), SignedExchangePrologue::kEncodedLengthInBytes);
   size_t header_size =
-      SignedExchangeEnvelope::ParseEncodedLength(base::make_span(
-          contents_bytes, SignedExchangeEnvelope::kEncodedLengthInBytes));
+      SignedExchangePrologue::ParseEncodedLength(base::make_span(
+          contents_bytes, SignedExchangePrologue::kEncodedLengthInBytes));
   ASSERT_GT(contents.size(),
-            SignedExchangeEnvelope::kEncodedLengthInBytes + header_size);
+            SignedExchangePrologue::kEncodedLengthInBytes + header_size);
 
   const auto cbor_bytes = base::make_span<const uint8_t>(
-      contents_bytes + SignedExchangeEnvelope::kEncodedLengthInBytes,
+      contents_bytes + SignedExchangePrologue::kEncodedLengthInBytes,
       header_size);
   const base::Optional<SignedExchangeEnvelope> header =
       SignedExchangeEnvelope::Parse(cbor_bytes, nullptr /* devtools_proxy */);
diff --git a/content/browser/web_package/signed_exchange_error.cc b/content/browser/web_package/signed_exchange_error.cc
new file mode 100644
index 0000000..9d71855f
--- /dev/null
+++ b/content/browser/web_package/signed_exchange_error.cc
@@ -0,0 +1,46 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/web_package/signed_exchange_error.h"
+
+namespace content {
+
+// static
+base::Optional<SignedExchangeError::Field>
+SignedExchangeError::GetFieldFromSignatureVerifierResult(
+    SignedExchangeSignatureVerifier::Result verify_result) {
+  switch (verify_result) {
+    case SignedExchangeSignatureVerifier::Result::kSuccess:
+      return base::nullopt;
+    case SignedExchangeSignatureVerifier::Result::kErrNoCertificate:
+      return base::nullopt;
+    case SignedExchangeSignatureVerifier::Result::kErrNoCertificateSHA256:
+      return Field::kSignatureCertSha256;
+    case SignedExchangeSignatureVerifier::Result::kErrCertificateSHA256Mismatch:
+      return Field::kSignatureCertSha256;
+    case SignedExchangeSignatureVerifier::Result::kErrInvalidSignatureFormat:
+      return base::nullopt;
+    case SignedExchangeSignatureVerifier::Result::
+        kErrSignatureVerificationFailed:
+      return Field::kSignatureSig;
+    case SignedExchangeSignatureVerifier::Result::kErrInvalidSignatureIntegrity:
+      return Field::kSignatureIintegrity;
+    case SignedExchangeSignatureVerifier::Result::kErrInvalidTimestamp:
+      return Field::kSignatureTimestamps;
+  }
+
+  NOTREACHED();
+  return base::nullopt;
+}
+
+SignedExchangeError::SignedExchangeError(const std::string& message,
+                                         base::Optional<FieldIndexPair> field)
+    : message(message), field(field) {}
+
+SignedExchangeError::SignedExchangeError(const SignedExchangeError& other) =
+    default;
+SignedExchangeError::SignedExchangeError(SignedExchangeError&& other) = default;
+SignedExchangeError::~SignedExchangeError() = default;
+
+}  // namespace content
diff --git a/content/browser/web_package/signed_exchange_error.h b/content/browser/web_package/signed_exchange_error.h
new file mode 100644
index 0000000..50856c6
--- /dev/null
+++ b/content/browser/web_package/signed_exchange_error.h
@@ -0,0 +1,50 @@
+// Copyright 2018 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 CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_ERROR_H_
+#define CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_ERROR_H_
+
+#include <string>
+#include <utility>
+
+#include "base/optional.h"
+#include "content/browser/web_package/signed_exchange_signature_verifier.h"
+
+namespace content {
+
+struct SignedExchangeError {
+ public:
+  enum class Field {
+    kSignatureSig,
+    kSignatureIintegrity,
+    kSignatureCertUrl,
+    kSignatureCertSha256,
+    kSignatureValidityUrl,
+    kSignatureTimestamps,
+  };
+
+  // |signature_index| will be used when we will support multiple signatures in
+  // a signed exchange header to indicate which signature is causing the error.
+  using FieldIndexPair = std::pair<int /* signature_index */, Field>;
+
+  static base::Optional<Field> GetFieldFromSignatureVerifierResult(
+      SignedExchangeSignatureVerifier::Result verify_result);
+
+  SignedExchangeError(const std::string& message,
+                      base::Optional<FieldIndexPair> field);
+
+  // Copy constructor.
+  SignedExchangeError(const SignedExchangeError& other);
+  // Move constructor.
+  SignedExchangeError(SignedExchangeError&& other);
+
+  ~SignedExchangeError();
+
+  std::string message;
+  base::Optional<FieldIndexPair> field;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_ERROR_H_
diff --git a/content/browser/web_package/signed_exchange_handler.cc b/content/browser/web_package/signed_exchange_handler.cc
index 2b66561..b6fc028 100644
--- a/content/browser/web_package/signed_exchange_handler.cc
+++ b/content/browser/web_package/signed_exchange_handler.cc
@@ -12,6 +12,7 @@
 #include "content/browser/web_package/signed_exchange_certificate_chain.h"
 #include "content/browser/web_package/signed_exchange_devtools_proxy.h"
 #include "content/browser/web_package/signed_exchange_envelope.h"
+#include "content/browser/web_package/signed_exchange_prologue.h"
 #include "content/browser/web_package/signed_exchange_signature_verifier.h"
 #include "content/browser/web_package/signed_exchange_utils.h"
 #include "content/public/browser/browser_thread.h"
@@ -107,7 +108,7 @@
   }
 
   // Triggering the read (asynchronously) for the encoded header length.
-  SetupBuffers(SignedExchangeEnvelope::kEncodedLengthInBytes);
+  SetupBuffers(SignedExchangePrologue::kEncodedLengthInBytes);
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(&SignedExchangeHandler::DoHeaderLoop,
                                 weak_factory_.GetWeakPtr()));
@@ -202,15 +203,15 @@
 
 bool SignedExchangeHandler::ParseHeadersLength() {
   TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"),
-                     "SignedExchangeHandler::ParseEncodedLength");
+                     "SignedExchangeHandler::ParseHeadersLength");
   DCHECK_EQ(state_, State::kReadingHeadersLength);
 
-  headers_length_ = SignedExchangeEnvelope::ParseEncodedLength(
+  headers_length_ = SignedExchangePrologue::ParseEncodedLength(
       base::make_span(reinterpret_cast<uint8_t*>(header_buf_->data()),
-                      SignedExchangeEnvelope::kEncodedLengthInBytes));
+                      SignedExchangePrologue::kEncodedLengthInBytes));
   if (headers_length_ == 0 || headers_length_ > kMaxHeadersCBORLength) {
     signed_exchange_utils::ReportErrorAndEndTraceEvent(
-        devtools_proxy_.get(), "SignedExchangeHandler::ParseEncodedLength",
+        devtools_proxy_.get(), "SignedExchangeHandler::ParseHeadersLength",
         base::StringPrintf("Invalid CBOR header length: %zu", headers_length_));
     return false;
   }
@@ -219,7 +220,7 @@
   SetupBuffers(headers_length_);
   state_ = State::kReadingHeaders;
   TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("loading"),
-                   "SignedExchangeHandler::ParseEncodedLength");
+                   "SignedExchangeHandler::ParseHeadersLength");
   return true;
 }
 
@@ -285,20 +286,28 @@
   if (!cert_chain) {
     signed_exchange_utils::ReportErrorAndEndTraceEvent(
         devtools_proxy_.get(), "SignedExchangeHandler::OnCertReceived",
-        "Failed to fetch the certificate.");
+        "Failed to fetch the certificate.",
+        std::make_pair(0 /* signature_index */,
+                       SignedExchangeError::Field::kSignatureCertUrl));
     RunErrorCallback(net::ERR_INVALID_SIGNED_EXCHANGE);
     return;
   }
 
   unverified_cert_chain_ = std::move(cert_chain);
 
-  if (SignedExchangeSignatureVerifier::Verify(
+  const SignedExchangeSignatureVerifier::Result verify_result =
+      SignedExchangeSignatureVerifier::Verify(
           *header_, unverified_cert_chain_->cert(), GetVerificationTime(),
-          devtools_proxy_.get()) !=
-      SignedExchangeSignatureVerifier::Result::kSuccess) {
+          devtools_proxy_.get());
+  if (verify_result != SignedExchangeSignatureVerifier::Result::kSuccess) {
+    base::Optional<SignedExchangeError::Field> error_field =
+        SignedExchangeError::GetFieldFromSignatureVerifierResult(verify_result);
     signed_exchange_utils::ReportErrorAndEndTraceEvent(
         devtools_proxy_.get(), "SignedExchangeHandler::OnCertReceived",
-        "Failed to verify the signed exchange header.");
+        "Failed to verify the signed exchange header.",
+        error_field ? base::make_optional(
+                          std::make_pair(0 /* signature_index */, *error_field))
+                    : base::nullopt);
     RunErrorCallback(net::ERR_INVALID_SIGNED_EXCHANGE);
     return;
   }
@@ -361,7 +370,9 @@
     signed_exchange_utils::ReportErrorAndEndTraceEvent(
         devtools_proxy_.get(), "SignedExchangeHandler::OnCertVerifyComplete",
         base::StringPrintf("Certificate verification error: %s",
-                           net::ErrorToShortString(result).c_str()));
+                           net::ErrorToShortString(result).c_str()),
+        std::make_pair(0 /* signature_index */,
+                       SignedExchangeError::Field::kSignatureCertUrl));
     RunErrorCallback(net::ERR_INVALID_SIGNED_EXCHANGE);
     return;
   }
@@ -372,7 +383,9 @@
         base::StringPrintf(
             "OCSP check failed. response status: %d, revocation status: %d",
             cert_verify_result_.ocsp_result.response_status,
-            cert_verify_result_.ocsp_result.revocation_status));
+            cert_verify_result_.ocsp_result.revocation_status),
+        std::make_pair(0 /* signature_index */,
+                       SignedExchangeError::Field::kSignatureCertUrl));
     RunErrorCallback(net::ERR_INVALID_SIGNED_EXCHANGE);
     return;
   }
diff --git a/content/browser/web_package/signed_exchange_prologue.cc b/content/browser/web_package/signed_exchange_prologue.cc
new file mode 100644
index 0000000..30f5c9a
--- /dev/null
+++ b/content/browser/web_package/signed_exchange_prologue.cc
@@ -0,0 +1,83 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/web_package/signed_exchange_prologue.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event.h"
+#include "content/browser/web_package/signed_exchange_utils.h"
+
+namespace content {
+
+namespace {
+
+constexpr char kSignedExchangeMagic[] = "sxg1-b1";
+constexpr size_t kMaximumSignatureHeaderFieldLength = 16 * 1024;
+constexpr size_t kMaximumCBORHeaderLength = 16 * 1024;
+
+}  // namespace
+
+constexpr size_t SignedExchangePrologue::kEncodedLengthInBytes;
+size_t SignedExchangePrologue::kEncodedPrologueInBytes =
+    sizeof(kSignedExchangeMagic) +
+    SignedExchangePrologue::kEncodedLengthInBytes * 2;
+
+// static
+size_t SignedExchangePrologue::ParseEncodedLength(
+    base::span<const uint8_t> input) {
+  DCHECK_EQ(input.size(), SignedExchangePrologue::kEncodedLengthInBytes);
+  return static_cast<size_t>(input[0]) << 16 |
+         static_cast<size_t>(input[1]) << 8 | static_cast<size_t>(input[2]);
+}
+
+// static
+base::Optional<SignedExchangePrologue> SignedExchangePrologue::Parse(
+    base::span<const uint8_t> input,
+    SignedExchangeDevToolsProxy* devtools_proxy) {
+  TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("loading"),
+                     "SignedExchangePrologue::Parse");
+
+  CHECK_EQ(input.size(), kEncodedPrologueInBytes);
+
+  const auto magic_string = input.subspan(0, sizeof(kSignedExchangeMagic));
+  const auto encoded_signature_header_field_length =
+      input.subspan(sizeof(kSignedExchangeMagic), kEncodedLengthInBytes);
+  const auto encoded_cbor_header_length =
+      input.subspan(sizeof(kSignedExchangeMagic) + kEncodedLengthInBytes,
+                    kEncodedLengthInBytes);
+
+  if (memcmp(magic_string.data(), kSignedExchangeMagic,
+             sizeof(kSignedExchangeMagic)) != 0) {
+    signed_exchange_utils::ReportErrorAndEndTraceEvent(
+        devtools_proxy, "SignedExchangePrologue::Parse", "Wrong magic string");
+    return base::nullopt;
+  }
+
+  size_t signature_header_field_length =
+      ParseEncodedLength(encoded_signature_header_field_length);
+  size_t cbor_header_length = ParseEncodedLength(encoded_cbor_header_length);
+
+  if (signature_header_field_length > kMaximumSignatureHeaderFieldLength) {
+    signed_exchange_utils::ReportErrorAndEndTraceEvent(
+        devtools_proxy, "SignedExchangePrologue::Parse",
+        base::StringPrintf("Signature header field too long: %zu",
+                           signature_header_field_length));
+    return base::nullopt;
+  }
+  if (cbor_header_length > kMaximumCBORHeaderLength) {
+    signed_exchange_utils::ReportErrorAndEndTraceEvent(
+        devtools_proxy, "SignedExchangePrologue::Parse",
+        base::StringPrintf("CBOR header too long: %zu", cbor_header_length));
+    return base::nullopt;
+  }
+
+  return SignedExchangePrologue(signature_header_field_length,
+                                cbor_header_length);
+}
+
+size_t SignedExchangePrologue::ComputeFollowingSectionsLength() const {
+  return signature_header_field_length_ + cbor_header_length_;
+}
+
+}  // namespace content
diff --git a/content/browser/web_package/signed_exchange_prologue.h b/content/browser/web_package/signed_exchange_prologue.h
new file mode 100644
index 0000000..22b52a2
--- /dev/null
+++ b/content/browser/web_package/signed_exchange_prologue.h
@@ -0,0 +1,72 @@
+// Copyright 2018 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 CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_PROLOGUE_H_
+#define CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_PROLOGUE_H_
+
+#include <string>
+
+#include "base/containers/span.h"
+#include "base/optional.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class SignedExchangeDevToolsProxy;
+
+// SignedExchangePrologue maps to the first bytes of
+// the "application/signed-exchange" format.
+// SignedExchangePrologue derives the lengths of the variable-length sections
+// following the prologue bytes.
+class CONTENT_EXPORT SignedExchangePrologue {
+ public:
+  // TODO(kouhei): Below should be made private (only friend from unittest)
+  // after the b1 migration.
+  static constexpr size_t kEncodedLengthInBytes = 3;
+  // Parse encoded length of the variable-length field in the signed exchange.
+  // Note: |input| must be pointing to a valid memory address that has at least
+  // |kEncodedLengthInBytes|.
+  static size_t ParseEncodedLength(base::span<const uint8_t> input);
+
+  // Size of the prologue bytes of the "application/signed-exchange" format
+  // which maps to this class.
+  static size_t kEncodedPrologueInBytes;
+
+  // Parses the first bytes of the "application/signed-exchange" format.
+  // |input| must be a valid span with length of kEncodedPrologueInBytes.
+  // If success, returns the result. Otherwise, returns nullopt and
+  // reports the error to |devtools_proxy|.
+  static base::Optional<SignedExchangePrologue> Parse(
+      base::span<const uint8_t> input,
+      SignedExchangeDevToolsProxy* devtools_proxy);
+
+  SignedExchangePrologue(size_t signature_header_field_length,
+                         size_t cbor_header_length)
+      : signature_header_field_length_(signature_header_field_length),
+        cbor_header_length_(cbor_header_length) {}
+  SignedExchangePrologue(const SignedExchangePrologue&) = default;
+  ~SignedExchangePrologue() = default;
+
+  size_t signature_header_field_length() const {
+    return signature_header_field_length_;
+  }
+  size_t cbor_header_length() const { return cbor_header_length_; }
+
+  size_t ComputeFollowingSectionsLength() const;
+
+ private:
+  // Corresponds to `sigLength` in the spec text.
+  // Encoded length of the Signature header field's value.
+  // https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#application-signed-exchange
+  size_t signature_header_field_length_;
+
+  // Corresponds to `headerLength` in the spec text.
+  // Length of the CBOR representation of the request and response headers.
+  // https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#application-signed-exchange
+  size_t cbor_header_length_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_WEB_PACKAGE_SIGNED_EXCHANGE_PROLOGUE_H_
diff --git a/content/browser/web_package/signed_exchange_prologue_unittest.cc b/content/browser/web_package/signed_exchange_prologue_unittest.cc
new file mode 100644
index 0000000..befb942e0
--- /dev/null
+++ b/content/browser/web_package/signed_exchange_prologue_unittest.cc
@@ -0,0 +1,63 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/web_package/signed_exchange_prologue.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+TEST(SignedExchangePrologueTest, ParseEncodedLength) {
+  constexpr struct {
+    uint8_t bytes[SignedExchangePrologue::kEncodedLengthInBytes];
+    size_t expected;
+  } kTestCases[] = {
+      {{0x00, 0x00, 0x01}, 1u}, {{0x01, 0xe2, 0x40}, 123456u},
+  };
+
+  int test_element_index = 0;
+  for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(testing::Message() << "testing case " << test_element_index++);
+    EXPECT_EQ(SignedExchangePrologue::ParseEncodedLength(test_case.bytes),
+              test_case.expected);
+  }
+}
+
+TEST(SignedExchangePrologueTest, Simple) {
+  uint8_t bytes[] = {'s',  'x',  'g',  '1',  '-',  'b',  '1',
+                     '\0', 0x00, 0x12, 0x34, 0x00, 0x23, 0x45};
+
+  auto prologue = SignedExchangePrologue::Parse(base::make_span(bytes),
+                                                nullptr /* devtools_proxy */);
+  EXPECT_TRUE(prologue);
+  EXPECT_EQ(0x1234u, prologue->signature_header_field_length());
+  EXPECT_EQ(0x2345u, prologue->cbor_header_length());
+  EXPECT_EQ(0x3579u, prologue->ComputeFollowingSectionsLength());
+}
+
+TEST(SignedExchangePrologueTest, WrongMagic) {
+  uint8_t bytes[] = {'s',  'x',  'g',  '!',  '-',  'b',  '1',
+                     '\0', 0x00, 0x12, 0x34, 0x00, 0x23, 0x45};
+
+  EXPECT_FALSE(SignedExchangePrologue::Parse(base::make_span(bytes),
+                                             nullptr /* devtools_proxy */));
+}
+
+TEST(SignedExchangePrologueTest, LongSignatureHeaderField) {
+  uint8_t bytes[] = {'s',  'x',  'g',  '1',  '-',  'b',  '1',
+                     '\0', 0xff, 0x12, 0x34, 0x00, 0x23, 0x45};
+
+  EXPECT_FALSE(SignedExchangePrologue::Parse(base::make_span(bytes),
+                                             nullptr /* devtools_proxy */));
+}
+
+TEST(SignedExchangePrologueTest, LongCBORHeader) {
+  uint8_t bytes[] = {'s',  'x',  'g',  '1',  '-',  'b',  '1',
+                     '\0', 0x00, 0x12, 0x34, 0xff, 0x23, 0x45};
+
+  EXPECT_FALSE(SignedExchangePrologue::Parse(base::make_span(bytes),
+                                             nullptr /* devtools_proxy */));
+}
+
+}  // namespace content
diff --git a/content/browser/web_package/signed_exchange_utils.cc b/content/browser/web_package/signed_exchange_utils.cc
index 4086fd6..4144465e 100644
--- a/content/browser/web_package/signed_exchange_utils.cc
+++ b/content/browser/web_package/signed_exchange_utils.cc
@@ -8,6 +8,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "content/browser/web_package/signed_exchange_devtools_proxy.h"
+#include "content/browser/web_package/signed_exchange_error.h"
 #include "content/browser/web_package/web_package_request_handler.h"
 #include "content/public/common/content_features.h"
 #include "services/network/public/cpp/resource_response.h"
@@ -16,11 +17,13 @@
 namespace content {
 namespace signed_exchange_utils {
 
-void ReportErrorAndEndTraceEvent(SignedExchangeDevToolsProxy* devtools_proxy,
-                                 const char* trace_event_name,
-                                 const std::string& error_message) {
+void ReportErrorAndEndTraceEvent(
+    SignedExchangeDevToolsProxy* devtools_proxy,
+    const char* trace_event_name,
+    const std::string& error_message,
+    base::Optional<SignedExchangeError::FieldIndexPair> error_field) {
   if (devtools_proxy)
-    devtools_proxy->ReportErrorMessage(error_message);
+    devtools_proxy->ReportError(error_message, std::move(error_field));
   TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("loading"), trace_event_name,
                    "error", error_message);
 }
diff --git a/content/browser/web_package/signed_exchange_utils.h b/content/browser/web_package/signed_exchange_utils.h
index b5c4220..5c118be 100644
--- a/content/browser/web_package/signed_exchange_utils.h
+++ b/content/browser/web_package/signed_exchange_utils.h
@@ -7,6 +7,9 @@
 
 #include <string>
 
+#include "base/optional.h"
+#include "content/browser/web_package/signed_exchange_error.h"
+
 class GURL;
 
 namespace network {
@@ -19,12 +22,15 @@
 
 namespace signed_exchange_utils {
 
-// Utility method to call SignedExchangeDevToolsProxy::ReportErrorMessage() and
+// Utility method to call SignedExchangeDevToolsProxy::ReportError() and
 // TRACE_EVENT_END() to report the error to both DevTools and about:tracing. If
 // |devtools_proxy| is nullptr, it just calls TRACE_EVENT_END().
-void ReportErrorAndEndTraceEvent(SignedExchangeDevToolsProxy* devtools_proxy,
-                                 const char* trace_event_name,
-                                 const std::string& error_message);
+void ReportErrorAndEndTraceEvent(
+    SignedExchangeDevToolsProxy* devtools_proxy,
+    const char* trace_event_name,
+    const std::string& error_message,
+    base::Optional<SignedExchangeError::FieldIndexPair> error_field =
+        base::nullopt);
 
 // Returns true when SignedHTTPExchange feature or SignedHTTPExchangeOriginTrial
 // feature is enabled.
diff --git a/content/browser/web_package/web_package_loader.cc b/content/browser/web_package/web_package_loader.cc
index ea80079..6ae1cb51 100644
--- a/content/browser/web_package/web_package_loader.cc
+++ b/content/browser/web_package/web_package_loader.cc
@@ -111,8 +111,9 @@
   // transport layer, and MUST NOT accept exchanges transferred over plain HTTP
   // without TLS. [spec text]
   if (!IsOriginSecure(outer_request_url)) {
-    devtools_proxy_->ReportErrorMessage(
-        "Signed exchange response from non secure origin is not supported.");
+    devtools_proxy_->ReportError(
+        "Signed exchange response from non secure origin is not supported.",
+        base::nullopt /* error_field */);
     // Calls OnSignedExchangeReceived() to show the outer response in DevTool's
     // Network panel and the error message in the Preview panel.
     devtools_proxy_->OnSignedExchangeReceived(base::nullopt /* header */,
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index a11fe0b..7518751 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -400,9 +400,6 @@
       "FeaturePolicyForPermissions",
       base::FeatureList::IsEnabled(features::kUseFeaturePolicyForPermissions));
 
-  if (base::FeatureList::IsEnabled(features::kKeyboardLockAPI))
-    WebRuntimeFeatures::EnableFeatureFromString("KeyboardLock", true);
-
   if (base::FeatureList::IsEnabled(features::kLazyFrameLoading))
     WebRuntimeFeatures::EnableLazyFrameLoading(true);
   if (base::FeatureList::IsEnabled(features::kLazyFrameVisibleLoadTimeMetrics))
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 7e875b1..60a55f4 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -110,7 +110,7 @@
     "java/src/org/chromium/content/browser/BackgroundSyncNetworkObserver.java",
     "java/src/org/chromium/content/browser/BindingManager.java",
     "java/src/org/chromium/content/browser/BrowserStartupControllerImpl.java",
-    "java/src/org/chromium/content/browser/ChildProcessCreationParams.java",
+    "java/src/org/chromium/content/browser/ChildProcessCreationParamsImpl.java",
     "java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java",
     "java/src/org/chromium/content/browser/ChildProcessRanking.java",
     "java/src/org/chromium/content/browser/ContentChildProcessConstants.java",
@@ -230,6 +230,7 @@
     "java/src/org/chromium/content_public/browser/ContentViewStatics.java",
     "java/src/org/chromium/content_public/browser/DeviceUtils.java",
     "java/src/org/chromium/content_public/browser/InputMethodManagerWrapper.java",
+    "java/src/org/chromium/content_public/browser/ChildProcessCreationParams.java",
     "java/src/org/chromium/content_public/browser/ContentVideoView.java",
     "java/src/org/chromium/content_public/browser/ContentVideoViewEmbedder.java",
     "java/src/org/chromium/content_public/browser/ContentViewCore.java",
diff --git a/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java b/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
index 884e076f..45e41e9 100644
--- a/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
+++ b/content/public/android/java/src/org/chromium/content/app/ContentChildProcessServiceDelegate.java
@@ -26,7 +26,7 @@
 import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.base.memory.MemoryPressureUma;
 import org.chromium.base.process_launcher.ChildProcessServiceDelegate;
-import org.chromium.content.browser.ChildProcessCreationParams;
+import org.chromium.content.browser.ChildProcessCreationParamsImpl;
 import org.chromium.content.browser.ContentChildProcessConstants;
 import org.chromium.content.common.IGpuProcessCallback;
 import org.chromium.content.common.SurfaceWrapper;
@@ -69,7 +69,8 @@
     @Override
     public void onServiceBound(Intent intent) {
         mLinkerParams = ChromiumLinkerParams.create(intent.getExtras());
-        mLibraryProcessType = ChildProcessCreationParams.getLibraryProcessType(intent.getExtras());
+        mLibraryProcessType =
+                ChildProcessCreationParamsImpl.getLibraryProcessType(intent.getExtras());
     }
 
     @Override
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessCreationParams.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessCreationParams.java
deleted file mode 100644
index 779ac244..0000000
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessCreationParams.java
+++ /dev/null
@@ -1,81 +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.
-
-package org.chromium.content.browser;
-
-import android.os.Bundle;
-
-import org.chromium.base.ContextUtils;
-import org.chromium.base.library_loader.LibraryProcessType;
-
-/**
- * Allows specifying the package name for looking up child services
- * configuration and classes into (if it differs from the application package
- * name, like in the case of Android WebView). Also allows specifying additional
- * child service binging flags.
- */
-public class ChildProcessCreationParams {
-    private static final String EXTRA_LIBRARY_PROCESS_TYPE =
-            "org.chromium.content.common.child_service_params.library_process_type";
-
-    private static ChildProcessCreationParams sParams;
-
-    /** Set params. This should be called once on start up. */
-    public static void set(ChildProcessCreationParams params) {
-        assert sParams == null;
-        sParams = params;
-    }
-
-    public static ChildProcessCreationParams get() {
-        return sParams;
-    }
-
-    // Members should all be immutable to avoid worrying about thread safety.
-    private final String mPackageNameForService;
-    private final boolean mIsSandboxedServiceExternal;
-    private final int mLibraryProcessType;
-    private final boolean mBindToCallerCheck;
-    // Use only the explicit WebContents.setImportance signal, and ignore other implicit
-    // signals in content.
-    private final boolean mIgnoreVisibilityForImportance;
-
-    public ChildProcessCreationParams(String packageNameForService,
-            boolean isExternalSandboxedService, int libraryProcessType, boolean bindToCallerCheck,
-            boolean ignoreVisibilityForImportance) {
-        mPackageNameForService = packageNameForService;
-        mIsSandboxedServiceExternal = isExternalSandboxedService;
-        mLibraryProcessType = libraryProcessType;
-        mBindToCallerCheck = bindToCallerCheck;
-        mIgnoreVisibilityForImportance = ignoreVisibilityForImportance;
-    }
-
-    public void addIntentExtras(Bundle extras) {
-        extras.putInt(EXTRA_LIBRARY_PROCESS_TYPE, mLibraryProcessType);
-    }
-
-    public static String getPackageNameForService() {
-        ChildProcessCreationParams params = ChildProcessCreationParams.get();
-        return params != null ? params.mPackageNameForService
-                              : ContextUtils.getApplicationContext().getPackageName();
-    }
-
-    public static boolean getIsSandboxedServiceExternal() {
-        ChildProcessCreationParams params = ChildProcessCreationParams.get();
-        return params != null && params.mIsSandboxedServiceExternal;
-    }
-
-    public static boolean getBindToCallerCheck() {
-        ChildProcessCreationParams params = ChildProcessCreationParams.get();
-        return params != null && params.mBindToCallerCheck;
-    }
-
-    public static boolean getIgnoreVisibilityForImportance() {
-        ChildProcessCreationParams params = ChildProcessCreationParams.get();
-        return params != null && params.mIgnoreVisibilityForImportance;
-    }
-
-    public static int getLibraryProcessType(Bundle extras) {
-        return extras.getInt(EXTRA_LIBRARY_PROCESS_TYPE, LibraryProcessType.PROCESS_CHILD);
-    }
-}
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessCreationParamsImpl.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessCreationParamsImpl.java
new file mode 100644
index 0000000..802b916
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessCreationParamsImpl.java
@@ -0,0 +1,69 @@
+// 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.
+
+package org.chromium.content.browser;
+
+import android.os.Bundle;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.library_loader.LibraryProcessType;
+
+/**
+ * Implementation of the interface {@link ChildProcessCreationParams}.
+ */
+public class ChildProcessCreationParamsImpl {
+    private static final String EXTRA_LIBRARY_PROCESS_TYPE =
+            "org.chromium.content.common.child_service_params.library_process_type";
+
+    // Members should all be immutable to avoid worrying about thread safety.
+    private static String sPackageNameForService;
+    private static boolean sIsSandboxedServiceExternal;
+    private static int sLibraryProcessType;
+    private static boolean sBindToCallerCheck;
+    // Use only the explicit WebContents.setImportance signal, and ignore other implicit
+    // signals in content.
+    private static boolean sIgnoreVisibilityForImportance;
+
+    private static boolean sInitialized;
+
+    private ChildProcessCreationParamsImpl() {}
+
+    /** Set params. This should be called once on start up. */
+    public static void set(String packageNameForService, boolean isExternalSandboxedService,
+            int libraryProcessType, boolean bindToCallerCheck,
+            boolean ignoreVisibilityForImportance) {
+        assert !sInitialized;
+        sPackageNameForService = packageNameForService;
+        sIsSandboxedServiceExternal = isExternalSandboxedService;
+        sLibraryProcessType = libraryProcessType;
+        sBindToCallerCheck = bindToCallerCheck;
+        sIgnoreVisibilityForImportance = ignoreVisibilityForImportance;
+        sInitialized = true;
+    }
+
+    public static void addIntentExtras(Bundle extras) {
+        if (sInitialized) extras.putInt(EXTRA_LIBRARY_PROCESS_TYPE, sLibraryProcessType);
+    }
+
+    public static String getPackageNameForService() {
+        return sInitialized ? sPackageNameForService
+                            : ContextUtils.getApplicationContext().getPackageName();
+    }
+
+    public static boolean getIsSandboxedServiceExternal() {
+        return sInitialized && sIsSandboxedServiceExternal;
+    }
+
+    public static boolean getBindToCallerCheck() {
+        return sInitialized && sBindToCallerCheck;
+    }
+
+    public static boolean getIgnoreVisibilityForImportance() {
+        return sInitialized && sIgnoreVisibilityForImportance;
+    }
+
+    public static int getLibraryProcessType(Bundle extras) {
+        return extras.getInt(EXTRA_LIBRARY_PROCESS_TYPE, LibraryProcessType.PROCESS_CHILD);
+    }
+}
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java
index 56ecb421..58fd459 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java
@@ -320,10 +320,10 @@
     @VisibleForTesting
     static ChildConnectionAllocator getConnectionAllocator(Context context, boolean sandboxed) {
         assert LauncherThread.runningOnLauncherThread();
-        final String packageName = ChildProcessCreationParams.getPackageNameForService();
-        boolean bindToCaller = ChildProcessCreationParams.getBindToCallerCheck();
+        final String packageName = ChildProcessCreationParamsImpl.getPackageNameForService();
+        boolean bindToCaller = ChildProcessCreationParamsImpl.getBindToCallerCheck();
         boolean bindAsExternalService =
-                sandboxed && ChildProcessCreationParams.getIsSandboxedServiceExternal();
+                sandboxed && ChildProcessCreationParamsImpl.getIsSandboxedServiceExternal();
 
         if (!sandboxed) {
             if (sPrivilegedChildConnectionAllocator == null) {
@@ -461,7 +461,7 @@
         }
 
         ChildProcessConnection connection = mLauncher.getConnection();
-        if (ChildProcessCreationParams.getIgnoreVisibilityForImportance()) {
+        if (ChildProcessCreationParamsImpl.getIgnoreVisibilityForImportance()) {
             foreground = false;
             boostForPendingViews = false;
         }
@@ -580,12 +580,9 @@
     }
 
     private static Bundle populateServiceBundle(Bundle bundle) {
-        ChildProcessCreationParams creationParams = ChildProcessCreationParams.get();
-        if (creationParams != null) {
-            creationParams.addIntentExtras(bundle);
-        }
+        ChildProcessCreationParamsImpl.addIntentExtras(bundle);
         bundle.putBoolean(ChildProcessConstants.EXTRA_BIND_TO_CALLER,
-                ChildProcessCreationParams.getBindToCallerCheck());
+                ChildProcessCreationParamsImpl.getBindToCallerCheck());
         ChromiumLinkerParams linkerParams = getLinkerParamsForNewConnection();
         if (linkerParams != null) linkerParams.populateBundle(bundle);
         return bundle;
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ChildProcessCreationParams.java b/content/public/android/java/src/org/chromium/content_public/browser/ChildProcessCreationParams.java
new file mode 100644
index 0000000..29ebb4f0
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content_public/browser/ChildProcessCreationParams.java
@@ -0,0 +1,23 @@
+// Copyright 2018 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.content_public.browser;
+
+import org.chromium.content.browser.ChildProcessCreationParamsImpl;
+
+/**
+ * Allows specifying the package name for looking up child services
+ * configuration and classes into (if it differs from the application package
+ * name, like in the case of Android WebView). Also allows specifying additional
+ * child service binging flags.
+ */
+public final class ChildProcessCreationParams {
+    /** Set params. This should be called once on start up. */
+    public static void set(String packageNameForService, boolean isExternalSandboxedService,
+            int libraryProcessType, boolean bindToCallerCheck,
+            boolean ignoreVisibilityForImportance) {
+        ChildProcessCreationParamsImpl.set(packageNameForService, isExternalSandboxedService,
+                libraryProcessType, bindToCallerCheck, ignoreVisibilityForImportance);
+    }
+}
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java
index 70311a47..1a76190a 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherHelperTest.java
@@ -149,9 +149,9 @@
 
         // Launch a service from this process. Since slot 0 is already bound by the Helper, it
         // will fail to start and the ChildProcessLauncher will retry and use the slot 1.
-        ChildProcessCreationParams.set(new ChildProcessCreationParams(context.getPackageName(),
-                false /* isExternalService */, LibraryProcessType.PROCESS_CHILD,
-                true /* bindToCallerCheck */, false /* ignoreVisibilityForImportance */));
+        ChildProcessCreationParamsImpl.set(context.getPackageName(), false /* isExternalService */,
+                LibraryProcessType.PROCESS_CHILD, true /* bindToCallerCheck */,
+                false /* ignoreVisibilityForImportance */);
         ChildProcessLauncherHelper launcher =
                 startSandboxedChildProcess(BLOCK_UNTIL_SETUP, true /* doSetupConnection */);
 
@@ -258,9 +258,9 @@
     @Feature({"ProcessManagement"})
     public void testWarmUpWithBindToCaller() {
         Context context = InstrumentationRegistry.getTargetContext();
-        ChildProcessCreationParams.set(new ChildProcessCreationParams(context.getPackageName(),
-                false /* isExternalService */, LibraryProcessType.PROCESS_CHILD,
-                true /* bindToCallerCheck */, false /* ignoreVisibilityForImportance */));
+        ChildProcessCreationParamsImpl.set(context.getPackageName(), false /* isExternalService */,
+                LibraryProcessType.PROCESS_CHILD, true /* bindToCallerCheck */,
+                false /* ignoreVisibilityForImportance */);
         testWarmUpImpl();
     }
 
diff --git a/content/public/browser/platform_notification_context.h b/content/public/browser/platform_notification_context.h
index 71da1d1..bea6143 100644
--- a/content/public/browser/platform_notification_context.h
+++ b/content/public/browser/platform_notification_context.h
@@ -38,12 +38,31 @@
 
   using DeleteResultCallback = base::Callback<void(bool /* success */)>;
 
+  // Reasons for updating a notification, triggering a read.
+  enum class Interaction {
+    // No interaction was taken with the notification.
+    NONE,
+
+    // An action button in the notification was clicked.
+    ACTION_BUTTON_CLICKED,
+
+    // The notification itself was clicked.
+    CLICKED,
+
+    // The notification was closed.
+    CLOSED
+  };
+
   // Reads the data associated with |notification_id| belonging to |origin|
   // from the database. |callback| will be invoked with the success status
   // and a reference to the notification database data when completed.
-  virtual void ReadNotificationData(const std::string& notification_id,
-                                    const GURL& origin,
-                                    const ReadResultCallback& callback) = 0;
+  // |interaction| is passed in for UKM logging purposes and does not
+  // otherwise affect the read.
+  virtual void ReadNotificationDataAndRecordInteraction(
+      const std::string& notification_id,
+      const GURL& origin,
+      Interaction interaction,
+      const ReadResultCallback& callback) = 0;
 
   // Reads all data associated with |service_worker_registration_id| belonging
   // to |origin| from the database. |callback| will be invoked with the success
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index e98ea1ee..a7efb7b 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -422,6 +422,9 @@
   // Device.
   virtual bool IsConnectedToBluetoothDevice() const = 0;
 
+  // Indicates whether a video is in Picture-in-Picture for |this|.
+  virtual bool HasPictureInPictureVideo() const = 0;
+
   // Indicates whether this tab should be considered crashed. The setter will
   // also notify the delegate when the flag is changed.
   virtual bool IsCrashed() const  = 0;
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 977f819c..b6a0838 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -224,7 +224,7 @@
 
 // Mojo-based Session Storage.
 const base::Feature kMojoSessionStorage{"MojoSessionStorage",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables/disables the video capture service.
 const base::Feature kMojoVideoCapture {
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.cc b/content/renderer/media/webrtc/mock_peer_connection_impl.cc
index ebc8190..d2ff26ec 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.cc
+++ b/content/renderer/media/webrtc/mock_peer_connection_impl.cc
@@ -162,7 +162,7 @@
   return {};
 }
 
-webrtc::RtpParameters FakeRtpSender::GetParameters() const {
+webrtc::RtpParameters FakeRtpSender::GetParameters() {
   NOTIMPLEMENTED();
   return webrtc::RtpParameters();
 }
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.h b/content/renderer/media/webrtc/mock_peer_connection_impl.h
index da06e71..87d3b22 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.h
+++ b/content/renderer/media/webrtc/mock_peer_connection_impl.h
@@ -31,7 +31,7 @@
   cricket::MediaType media_type() const override;
   std::string id() const override;
   std::vector<std::string> stream_ids() const override;
-  webrtc::RtpParameters GetParameters() const override;
+  webrtc::RtpParameters GetParameters() override;
   webrtc::RTCError SetParameters(
       const webrtc::RtpParameters& parameters) override;
   rtc::scoped_refptr<webrtc::DtmfSenderInterface> GetDtmfSender()
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index a1f2f35..30be8224 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -421,8 +421,7 @@
     const CommonNavigationParams& common_params,
     const RequestNavigationParams& request_params,
     std::unique_ptr<NavigationResponseOverrideParameters> response_override,
-    bool is_view_source_mode_enabled,
-    bool is_same_document_navigation) {
+    bool is_view_source_mode_enabled) {
   // Use the original navigation url to construct the WebURLRequest. The
   // WebURLloaderImpl will replay the redirects afterwards and will eventually
   // commit the final url.
@@ -455,7 +454,6 @@
     request.SetHTTPReferrer(web_referrer, common_params.referrer.policy);
   }
 
-  request.SetIsSameDocumentNavigation(is_same_document_navigation);
   request.SetPreviewsState(
       static_cast<WebURLRequest::PreviewsState>(common_params.previews_state));
 
@@ -3037,8 +3035,7 @@
       WebURLError::IsWebSecurityViolation::kFalse, common_params.url);
   WebURLRequest failed_request = CreateURLRequestForNavigation(
       common_params, request_params,
-      /*response_override=*/nullptr, frame_->IsViewSourceModeEnabled(),
-      false);  // is_same_document_navigation
+      /*response_override=*/nullptr, frame_->IsViewSourceModeEnabled());
 
   if (!ShouldDisplayErrorPageForFailedLoad(error_code, common_params.url)) {
     // The browser expects this frame to be loading an error page. Inform it
@@ -6339,7 +6336,7 @@
 
   WebURLRequest request = CreateURLRequestForNavigation(
       common_params, request_params, std::move(response_override),
-      frame_->IsViewSourceModeEnabled(), false /* is_same_document */);
+      frame_->IsViewSourceModeEnabled());
   request.SetFrameType(IsTopLevelNavigation(frame_)
                            ? network::mojom::RequestContextFrameType::kTopLevel
                            : network::mojom::RequestContextFrameType::kNested);
diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java
index bc1bf1a..bff99f8 100644
--- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java
+++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ChildProcessLauncherTestHelperService.java
@@ -20,8 +20,8 @@
 import org.chromium.base.library_loader.ProcessInitException;
 import org.chromium.base.process_launcher.ChildProcessConnection;
 import org.chromium.base.process_launcher.FileDescriptorInfo;
-import org.chromium.content.browser.ChildProcessCreationParams;
 import org.chromium.content.browser.ChildProcessLauncherHelper;
+import org.chromium.content_public.browser.ChildProcessCreationParams;
 
 /**
  * A Service that assists the ChildProcessLauncherTest that responds to one message, which
@@ -77,9 +77,8 @@
     private void doBindService(final Message msg) {
         String[] commandLine = { "_", "--" + BaseSwitches.RENDERER_WAIT_FOR_JAVA_DEBUGGER };
         final boolean bindToCaller = true;
-        ChildProcessCreationParams.set(new ChildProcessCreationParams(getPackageName(), false,
-                LibraryProcessType.PROCESS_CHILD, bindToCaller,
-                false /* ignoreVisibilityForImportance */));
+        ChildProcessCreationParams.set(getPackageName(), false, LibraryProcessType.PROCESS_CHILD,
+                bindToCaller, false /* ignoreVisibilityForImportance */);
         mProcessLauncher = ChildProcessLauncherTestUtils.startForTesting(true /* sandboxed */,
                 commandLine, new FileDescriptorInfo[0], true /* doSetupConnection */);
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index d1a3e35..d9db040 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1544,6 +1544,7 @@
     "../browser/web_package/signed_exchange_certificate_chain_unittest.cc",
     "../browser/web_package/signed_exchange_envelope_unittest.cc",
     "../browser/web_package/signed_exchange_handler_unittest.cc",
+    "../browser/web_package/signed_exchange_prologue_unittest.cc",
     "../browser/web_package/signed_exchange_signature_header_field_unittest.cc",
     "../browser/web_package/signed_exchange_signature_verifier_unittest.cc",
     "../browser/webrtc/webrtc_internals_message_handler_unittest.cc",
diff --git a/content/test/fuzzer/signed_exchange_envelope_fuzzer.cc b/content/test/fuzzer/signed_exchange_envelope_fuzzer.cc
index ea3d6d6..e0aa8a01 100644
--- a/content/test/fuzzer/signed_exchange_envelope_fuzzer.cc
+++ b/content/test/fuzzer/signed_exchange_envelope_fuzzer.cc
@@ -6,6 +6,7 @@
 #include "base/containers/span.h"
 #include "base/i18n/icu_util.h"
 #include "content/browser/web_package/signed_exchange_envelope.h"  // nogncheck
+#include "content/browser/web_package/signed_exchange_prologue.h"  // nogncheck
 
 namespace content {
 
@@ -18,14 +19,14 @@
 IcuEnvironment* env = new IcuEnvironment();
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  if (size < SignedExchangeEnvelope::kEncodedLengthInBytes)
+  if (size < SignedExchangePrologue::kEncodedLengthInBytes)
     return 0;
   auto encoded_length =
-      base::make_span(data, SignedExchangeEnvelope::kEncodedLengthInBytes);
+      base::make_span(data, SignedExchangePrologue::kEncodedLengthInBytes);
   size_t header_len =
-      SignedExchangeEnvelope::ParseEncodedLength(encoded_length);
-  data += SignedExchangeEnvelope::kEncodedLengthInBytes;
-  size -= SignedExchangeEnvelope::kEncodedLengthInBytes;
+      SignedExchangePrologue::ParseEncodedLength(encoded_length);
+  data += SignedExchangePrologue::kEncodedLengthInBytes;
+  size -= SignedExchangePrologue::kEncodedLengthInBytes;
 
   // Copy the header into a separate buffer so that out-of-bounds access can be
   // detected.
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index b252854d..0b087c19 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -722,7 +722,7 @@
         ['mac', 'no_passthrough', 'intel'], bug=679692)
     self.Fail('deqp/functional/gles3/fbomultisample*',
         ['mac', 'intel'], bug=641209)
-    self.Flaky('deqp/functional/gles3/shaderoperator/common_functions.html',
+    self.Fail('deqp/functional/gles3/shaderoperator/common_functions.html',
         ['mac', 'intel'], bug=820225)
     self.Fail('deqp/functional/gles3/texturefiltering/2d_combinations_01.html',
         ['mac', 'intel'], bug=606074)
diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h
index 69efd03..a0622f8 100644
--- a/device/bluetooth/bluetooth_device.h
+++ b/device/bluetooth/bluetooth_device.h
@@ -561,6 +561,20 @@
   std::vector<BluetoothRemoteGattService*> GetPrimaryServicesByUUID(
       const BluetoothUUID& service_uuid);
 
+#if defined(OS_CHROMEOS)
+  typedef base::Callback<void(device::BluetoothGattService::GattErrorCode)>
+      ExecuteWriteErrorCallback;
+  typedef base::Callback<void(device::BluetoothGattService::GattErrorCode)>
+      AbortWriteErrorCallback;
+  // Executes all the previous prepare writes in a reliable write session.
+  virtual void ExecuteWrite(
+      const base::Closure& callback,
+      const ExecuteWriteErrorCallback& error_callback) = 0;
+  // Aborts all the previous prepare writes in a reliable write session.
+  virtual void AbortWrite(const base::Closure& callback,
+                          const AbortWriteErrorCallback& error_callback) = 0;
+#endif
+
  protected:
   // BluetoothGattConnection is a friend to call Add/RemoveGattConnection.
   friend BluetoothGattConnection;
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic.h b/device/bluetooth/bluetooth_remote_gatt_characteristic.h
index 6191a43..3ac9de77 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic.h
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic.h
@@ -133,6 +133,19 @@
       const base::Closure& callback,
       const ErrorCallback& error_callback) = 0;
 
+#if defined(OS_CHROMEOS)
+  // Sends a prepare write request to a remote characteristic with the value
+  // |value|. |callback| is called to signal success and |error_callback| for
+  // failures. This method only applies to remote characteristics and will fail
+  // for those that are locally hosted.
+  // Callers should use BluetoothDevice::ExecuteWrite() to commit or
+  // BluetoothDevice::AbortWrite() to abort the change.
+  virtual void PrepareWriteRemoteCharacteristic(
+      const std::vector<uint8_t>& value,
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) = 0;
+#endif
+
   // Sends a write request to a remote characteristic with the value |value|
   // without waiting for a response. This method returns false to signal
   // failures. When attempting to write the remote characteristic true is
diff --git a/device/bluetooth/bluez/bluetooth_device_bluez.cc b/device/bluetooth/bluez/bluetooth_device_bluez.cc
index 6da4644..ff1f838 100644
--- a/device/bluetooth/bluez/bluetooth_device_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_device_bluez.cc
@@ -666,6 +666,26 @@
                  weak_ptr_factory_.GetWeakPtr(), error_callback));
 }
 
+#if defined(OS_CHROMEOS)
+void BluetoothDeviceBlueZ::ExecuteWrite(
+    const base::Closure& callback,
+    const ExecuteWriteErrorCallback& error_callback) {
+  bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->ExecuteWrite(
+      object_path_, callback,
+      base::Bind(&BluetoothDeviceBlueZ::OnExecuteWriteError,
+                 weak_ptr_factory_.GetWeakPtr(), error_callback));
+}
+
+void BluetoothDeviceBlueZ::AbortWrite(
+    const base::Closure& callback,
+    const AbortWriteErrorCallback& error_callback) {
+  bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->AbortWrite(
+      object_path_, callback,
+      base::Bind(&BluetoothDeviceBlueZ::OnAbortWriteError,
+                 weak_ptr_factory_.GetWeakPtr(), error_callback));
+}
+#endif
+
 void BluetoothDeviceBlueZ::UpdateServiceData() {
   bluez::BluetoothDeviceClient::Properties* properties =
       bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
@@ -863,6 +883,30 @@
   error_callback.Run(code);
 }
 
+#if defined(OS_CHROMEOS)
+void BluetoothDeviceBlueZ::OnExecuteWriteError(
+    const ExecuteWriteErrorCallback& error_callback,
+    const std::string& error_name,
+    const std::string& error_message) {
+  BLUETOOTH_LOG(EVENT) << object_path_.value()
+                       << ": Failed to execute write: " << error_name << ": "
+                       << error_message;
+  error_callback.Run(
+      BluetoothGattServiceBlueZ::DBusErrorToServiceError(error_name));
+}
+
+void BluetoothDeviceBlueZ::OnAbortWriteError(
+    const AbortWriteErrorCallback& error_callback,
+    const std::string& error_name,
+    const std::string& error_message) {
+  BLUETOOTH_LOG(EVENT) << object_path_.value()
+                       << ": Failed to abort write: " << error_name << ": "
+                       << error_message;
+  error_callback.Run(
+      BluetoothGattServiceBlueZ::DBusErrorToServiceError(error_name));
+}
+#endif
+
 void BluetoothDeviceBlueZ::ConnectInternal(
     bool after_pairing,
     const base::Closure& callback,
diff --git a/device/bluetooth/bluez/bluetooth_device_bluez.h b/device/bluetooth/bluez/bluetooth_device_bluez.h
index 9978b51..3cb5464b 100644
--- a/device/bluetooth/bluez/bluetooth_device_bluez.h
+++ b/device/bluetooth/bluez/bluetooth_device_bluez.h
@@ -104,6 +104,12 @@
   void Pair(device::BluetoothDevice::PairingDelegate* pairing_delegate,
             const base::Closure& callback,
             const ConnectErrorCallback& error_callback) override;
+#if defined(OS_CHROMEOS)
+  void ExecuteWrite(const base::Closure& callback,
+                    const ExecuteWriteErrorCallback& error_callback) override;
+  void AbortWrite(const base::Closure& callback,
+                  const AbortWriteErrorCallback& error_callback) override;
+#endif
 
   // Returns the complete list of service records discovered for on this
   // device via SDP. If called before discovery is complete, it may return
@@ -203,6 +209,16 @@
       const std::string& error_name,
       const std::string& error_message);
 
+#if defined(OS_CHROMEOS)
+  void OnExecuteWriteError(const ExecuteWriteErrorCallback& error_callback,
+                           const std::string& error_name,
+                           const std::string& error_message);
+
+  void OnAbortWriteError(const AbortWriteErrorCallback& error_callback,
+                         const std::string& error_name,
+                         const std::string& error_message);
+#endif
+
   // Internal method to initiate a connection to this device, and methods called
   // by dbus:: on completion of the D-Bus method call.
   void ConnectInternal(bool after_pairing,
diff --git a/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc b/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
index d2566e9a..5dfddcf 100644
--- a/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
+++ b/device/bluetooth/bluez/bluetooth_gatt_bluez_unittest.cc
@@ -24,6 +24,7 @@
 #include "device/bluetooth/bluetooth_remote_gatt_descriptor.h"
 #include "device/bluetooth/bluetooth_remote_gatt_service.h"
 #include "device/bluetooth/bluetooth_uuid.h"
+#include "device/bluetooth/bluez/bluetooth_device_bluez.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h"
 #include "device/bluetooth/dbus/fake_bluetooth_agent_manager_client.h"
@@ -1848,4 +1849,98 @@
   EXPECT_TRUE(update_sessions_[0]->IsActive());
 }
 
+#if defined(OS_CHROMEOS)
+TEST_F(BluetoothGattBlueZTest, ReliableWrite) {
+  fake_bluetooth_device_client_->CreateDevice(
+      dbus::ObjectPath(bluez::FakeBluetoothAdapterClient::kAdapterPath),
+      dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
+  BluetoothDeviceBlueZ* device = static_cast<BluetoothDeviceBlueZ*>(
+      adapter_->GetDevice(bluez::FakeBluetoothDeviceClient::kLowEnergyAddress));
+  ASSERT_TRUE(device);
+
+  TestBluetoothAdapterObserver observer(adapter_);
+
+  // Expose the fake Heart Rate service. This will asynchronously expose
+  // characteristics.
+  fake_bluetooth_gatt_service_client_->ExposeHeartRateService(
+      dbus::ObjectPath(bluez::FakeBluetoothDeviceClient::kLowEnergyPath));
+  ASSERT_EQ(1, observer.gatt_service_added_count());
+
+  BluetoothRemoteGattService* service =
+      device->GetGattService(observer.last_gatt_service_id());
+
+  // Run the message loop so that the characteristics appear.
+  base::RunLoop().Run();
+
+  // Request to start notifications.
+  service
+      ->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
+                              ->GetHeartRateMeasurementPath()
+                              .value())
+      ->StartNotifySession(
+          base::Bind(&BluetoothGattBlueZTest::NotifySessionCallback,
+                     base::Unretained(this)),
+          base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
+                     base::Unretained(this)));
+  base::RunLoop().Run();
+
+  // Obtain writable Heart Rate Control Point characteristic.
+  BluetoothRemoteGattCharacteristic* characteristic =
+      service->GetCharacteristic(fake_bluetooth_gatt_characteristic_client_
+                                     ->GetHeartRateControlPointPath()
+                                     .value());
+  std::vector<uint8_t> write_value = {0x01};
+
+  // Prepare 1000 writes.
+  success_callback_count_ = 0;
+  error_callback_count_ = 0;
+  observer.Reset();
+  for (int i = 0; i < 1000; ++i) {
+    characteristic->PrepareWriteRemoteCharacteristic(
+        write_value,
+        base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
+                   base::Unretained(this)),
+        base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
+                   base::Unretained(this)));
+  }
+  EXPECT_EQ(1000, success_callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
+
+  // Abort.
+  device->AbortWrite(base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
+                                base::Unretained(this)),
+                     base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
+                                base::Unretained(this)));
+  EXPECT_EQ(1001, success_callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
+
+  // Prepare another 1000 writes.
+  success_callback_count_ = 0;
+  error_callback_count_ = 0;
+  observer.Reset();
+  for (int i = 0; i < 1000; ++i) {
+    characteristic->PrepareWriteRemoteCharacteristic(
+        write_value,
+        base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
+                   base::Unretained(this)),
+        base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
+                   base::Unretained(this)));
+  }
+  EXPECT_EQ(1000, success_callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_EQ(0, observer.gatt_characteristic_value_changed_count());
+
+  // Execute.
+  device->ExecuteWrite(base::Bind(&BluetoothGattBlueZTest::SuccessCallback,
+                                  base::Unretained(this)),
+                       base::Bind(&BluetoothGattBlueZTest::ServiceErrorCallback,
+                                  base::Unretained(this)));
+  EXPECT_EQ(1001, success_callback_count_);
+  EXPECT_EQ(0, error_callback_count_);
+  EXPECT_EQ(1000, observer.gatt_characteristic_value_changed_count());
+}
+#endif
+
 }  // namespace bluez
diff --git a/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc b/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc
index 5f78621..6308b9d2 100644
--- a/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc
@@ -204,6 +204,25 @@
                      weak_ptr_factory_.GetWeakPtr(), error_callback));
 }
 
+#if defined(OS_CHROMEOS)
+void BluetoothRemoteGattCharacteristicBlueZ::PrepareWriteRemoteCharacteristic(
+    const std::vector<uint8_t>& value,
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  VLOG(1) << "Sending GATT characteristic prepare write request to "
+          << "characteristic: " << GetIdentifier()
+          << ", UUID: " << GetUUID().canonical_value()
+          << ", with value: " << value << ".";
+
+  bluez::BluezDBusManager::Get()
+      ->GetBluetoothGattCharacteristicClient()
+      ->PrepareWriteValue(
+          object_path(), value, callback,
+          base::Bind(&BluetoothRemoteGattCharacteristicBlueZ::OnWriteError,
+                     weak_ptr_factory_.GetWeakPtr(), error_callback));
+}
+#endif
+
 void BluetoothRemoteGattCharacteristicBlueZ::SubscribeToNotifications(
     device::BluetoothRemoteGattDescriptor* ccc_descriptor,
     const base::Closure& callback,
diff --git a/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h b/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h
index f40d317..45239fe3 100644
--- a/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h
+++ b/device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.h
@@ -64,6 +64,12 @@
   void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
                                  const base::Closure& callback,
                                  const ErrorCallback& error_callback) override;
+#if defined(OS_CHROMEOS)
+  void PrepareWriteRemoteCharacteristic(
+      const std::vector<uint8_t>& value,
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) override;
+#endif
 
  protected:
   void SubscribeToNotifications(
diff --git a/device/bluetooth/dbus/bluetooth_device_client.cc b/device/bluetooth/dbus/bluetooth_device_client.cc
index 9a69b3ef..4ca96ede 100644
--- a/device/bluetooth/dbus/bluetooth_device_client.cc
+++ b/device/bluetooth/dbus/bluetooth_device_client.cc
@@ -503,6 +503,52 @@
                        weak_ptr_factory_.GetWeakPtr(), error_callback));
   }
 
+  void ExecuteWrite(const dbus::ObjectPath& object_path,
+                    const base::Closure& callback,
+                    const ErrorCallback& error_callback) override {
+    dbus::MethodCall method_call(bluetooth_device::kBluetoothDeviceInterface,
+                                 bluetooth_device::kExecuteWrite);
+
+    dbus::ObjectProxy* object_proxy =
+        object_manager_->GetObjectProxy(object_path);
+    if (!object_proxy) {
+      error_callback.Run(kUnknownDeviceError, "");
+      return;
+    }
+
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendBool(true);
+    object_proxy->CallMethodWithErrorCallback(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&BluetoothDeviceClientImpl::OnSuccess,
+                       weak_ptr_factory_.GetWeakPtr(), callback),
+        base::BindOnce(&BluetoothDeviceClientImpl::OnError,
+                       weak_ptr_factory_.GetWeakPtr(), error_callback));
+  }
+
+  void AbortWrite(const dbus::ObjectPath& object_path,
+                  const base::Closure& callback,
+                  const ErrorCallback& error_callback) override {
+    dbus::MethodCall method_call(bluetooth_device::kBluetoothDeviceInterface,
+                                 bluetooth_device::kExecuteWrite);
+
+    dbus::ObjectProxy* object_proxy =
+        object_manager_->GetObjectProxy(object_path);
+    if (!object_proxy) {
+      error_callback.Run(kUnknownDeviceError, "");
+      return;
+    }
+
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendBool(false);
+    object_proxy->CallMethodWithErrorCallback(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&BluetoothDeviceClientImpl::OnSuccess,
+                       weak_ptr_factory_.GetWeakPtr(), callback),
+        base::BindOnce(&BluetoothDeviceClientImpl::OnError,
+                       weak_ptr_factory_.GetWeakPtr(), error_callback));
+  }
+
  protected:
   void Init(dbus::Bus* bus,
             const std::string& bluetooth_service_name) override {
diff --git a/device/bluetooth/dbus/bluetooth_device_client.h b/device/bluetooth/dbus/bluetooth_device_client.h
index d670b36..a59f424 100644
--- a/device/bluetooth/dbus/bluetooth_device_client.h
+++ b/device/bluetooth/dbus/bluetooth_device_client.h
@@ -242,6 +242,16 @@
                                  const ServiceRecordsCallback& callback,
                                  const ErrorCallback& error_callback) = 0;
 
+  // Executes all the privous prepare writes in a reliable write session.
+  virtual void ExecuteWrite(const dbus::ObjectPath& object_path,
+                            const base::Closure& callback,
+                            const ErrorCallback& error_callback) = 0;
+
+  // Aborts all the privous prepare writes in a reliable write session.
+  virtual void AbortWrite(const dbus::ObjectPath& object_path,
+                          const base::Closure& callback,
+                          const ErrorCallback& error_callback) = 0;
+
   // Creates the instance.
   static BluetoothDeviceClient* Create();
 
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
index 0633898a..95ed06fc 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.cc
@@ -140,6 +140,34 @@
                        weak_ptr_factory_.GetWeakPtr(), error_callback));
   }
 
+  void PrepareWriteValue(const dbus::ObjectPath& object_path,
+                         const std::vector<uint8_t>& value,
+                         const base::Closure& callback,
+                         const ErrorCallback& error_callback) override {
+    dbus::ObjectProxy* object_proxy =
+        object_manager_->GetObjectProxy(object_path);
+    if (!object_proxy) {
+      error_callback.Run(kUnknownCharacteristicError, "");
+      return;
+    }
+
+    dbus::MethodCall method_call(
+        bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface,
+        bluetooth_gatt_characteristic::kPrepareWriteValue);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendArrayOfBytes(value.data(), value.size());
+
+    base::DictionaryValue dict;
+    dbus::AppendValueData(&writer, dict);
+
+    object_proxy->CallMethodWithErrorCallback(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&BluetoothGattCharacteristicClientImpl::OnSuccess,
+                       weak_ptr_factory_.GetWeakPtr(), callback),
+        base::BindOnce(&BluetoothGattCharacteristicClientImpl::OnError,
+                       weak_ptr_factory_.GetWeakPtr(), error_callback));
+  }
+
   // BluetoothGattCharacteristicClient override.
   void StartNotify(const dbus::ObjectPath& object_path,
                    const base::Closure& callback,
diff --git a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h
index 85851c3..05e4214 100644
--- a/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h
+++ b/device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h
@@ -109,6 +109,14 @@
                           const base::Closure& callback,
                           const ErrorCallback& error_callback) = 0;
 
+  // Issues a request to prepare write the value of GATT characteristic with
+  // object path |object_path| with value |value|.
+  // Invokes |callback| on success and |error_callback| on failure.
+  virtual void PrepareWriteValue(const dbus::ObjectPath& object_path,
+                                 const std::vector<uint8_t>& value,
+                                 const base::Closure& callback,
+                                 const ErrorCallback& error_callback) = 0;
+
   // Starts a notification session from this characteristic with object path
   // |object_path| if it supports value notifications or indications. Invokes
   // |callback| on success and |error_callback| on failure.
diff --git a/device/bluetooth/dbus/fake_bluetooth_device_client.cc b/device/bluetooth/dbus/fake_bluetooth_device_client.cc
index ebe5e78..8dfcd20 100644
--- a/device/bluetooth/dbus/fake_bluetooth_device_client.cc
+++ b/device/bluetooth/dbus/fake_bluetooth_device_client.cc
@@ -29,6 +29,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "device/bluetooth/bluez/bluetooth_service_attribute_value_bluez.h"
+#include "device/bluetooth/dbus/bluetooth_gatt_characteristic_client.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
 #include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h"
 #include "device/bluetooth/dbus/fake_bluetooth_agent_manager_client.h"
@@ -625,6 +626,28 @@
   callback.Run(CreateFakeServiceRecords());
 }
 
+void FakeBluetoothDeviceClient::ExecuteWrite(
+    const dbus::ObjectPath& object_path,
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  for (const auto& prepare_write_request : prepare_write_requests_) {
+    bluez::BluezDBusManager::Get()
+        ->GetBluetoothGattCharacteristicClient()
+        ->WriteValue(prepare_write_request.first, prepare_write_request.second,
+                     base::DoNothing(), base::DoNothing());
+  }
+  prepare_write_requests_.clear();
+  callback.Run();
+}
+
+void FakeBluetoothDeviceClient::AbortWrite(
+    const dbus::ObjectPath& object_path,
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  prepare_write_requests_.clear();
+  callback.Run();
+}
+
 void FakeBluetoothDeviceClient::BeginDiscoverySimulation(
     const dbus::ObjectPath& adapter_path) {
   VLOG(1) << "starting discovery simulation";
@@ -1885,4 +1908,10 @@
     observer.DeviceAdded(device_path);
 }
 
+void FakeBluetoothDeviceClient::AddPrepareWriteRequest(
+    const dbus::ObjectPath& object_path,
+    const std::vector<uint8_t>& value) {
+  prepare_write_requests_.emplace_back(object_path, value);
+}
+
 }  // namespace bluez
diff --git a/device/bluetooth/dbus/fake_bluetooth_device_client.h b/device/bluetooth/dbus/fake_bluetooth_device_client.h
index cef8554..0b0d8e4 100644
--- a/device/bluetooth/dbus/fake_bluetooth_device_client.h
+++ b/device/bluetooth/dbus/fake_bluetooth_device_client.h
@@ -109,6 +109,12 @@
   void GetServiceRecords(const dbus::ObjectPath& object_path,
                          const ServiceRecordsCallback& callback,
                          const ErrorCallback& error_callback) override;
+  void ExecuteWrite(const dbus::ObjectPath& object_path,
+                    const base::Closure& callback,
+                    const ErrorCallback& error_callback) override;
+  void AbortWrite(const dbus::ObjectPath& object_path,
+                  const base::Closure& callback,
+                  const ErrorCallback& error_callback) override;
 
   void SetSimulationIntervalMs(int interval_ms);
 
@@ -186,6 +192,10 @@
       const std::map<std::string, std::vector<uint8_t>>& service_data,
       const std::map<uint16_t, std::vector<uint8_t>>& manufacturer_data);
 
+  // Adds a pending prepare write request to |object_path|.
+  void AddPrepareWriteRequest(const dbus::ObjectPath& object_path,
+                              const std::vector<uint8_t>& value);
+
   static const char kTestPinCode[];
   static const int kTestPassKey;
 
@@ -364,6 +374,10 @@
   // Controls the fake behavior to allow more extensive UI testing without
   // having to cycle the discovery simulation.
   bool delay_start_discovery_;
+
+  // Pending prepare write requests.
+  std::vector<std::pair<dbus::ObjectPath, std::vector<uint8_t>>>
+      prepare_write_requests_;
 };
 
 }  // namespace bluez
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
index 66fc0f1..cc329b2 100644
--- a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
+++ b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.cc
@@ -11,6 +11,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
+#include "device/bluetooth/dbus/fake_bluetooth_device_client.h"
 #include "device/bluetooth/dbus/fake_bluetooth_gatt_descriptor_client.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 
@@ -257,6 +258,7 @@
   } else if (value[0] == 1) {
     // TODO(jamuraa): make this happen when the callback happens
     calories_burned_ = 0;
+    ScheduleHeartRateMeasurementValueChange();
     completed_callback = callback;
   }
 
@@ -268,6 +270,46 @@
   completed_callback.Run();
 }
 
+void FakeBluetoothGattCharacteristicClient::PrepareWriteValue(
+    const dbus::ObjectPath& object_path,
+    const std::vector<uint8_t>& value,
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  if (!authenticated_) {
+    error_callback.Run(bluetooth_gatt_service::kErrorNotPaired, "Please login");
+    return;
+  }
+
+  if (!authorized_) {
+    error_callback.Run(bluetooth_gatt_service::kErrorNotAuthorized,
+                       "Authorize first");
+    return;
+  }
+
+  if (!IsHeartRateVisible()) {
+    error_callback.Run(kUnknownCharacteristicError, "");
+    return;
+  }
+
+  if (object_path.value() == heart_rate_measurement_path_) {
+    error_callback.Run(bluetooth_gatt_service::kErrorNotSupported,
+                       "Action not supported on this characteristic");
+    return;
+  }
+
+  if (object_path.value() != heart_rate_control_point_path_) {
+    error_callback.Run(bluetooth_gatt_service::kErrorNotPermitted,
+                       "Writes of this value are not allowed");
+    return;
+  }
+
+  DCHECK(heart_rate_control_point_properties_.get());
+  static_cast<FakeBluetoothDeviceClient*>(
+      bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient())
+      ->AddPrepareWriteRequest(object_path, value);
+  callback.Run();
+}
+
 void FakeBluetoothGattCharacteristicClient::StartNotify(
     const dbus::ObjectPath& object_path,
     const base::Closure& callback,
diff --git a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h
index 564e701..a86cbb1 100644
--- a/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h
+++ b/device/bluetooth/dbus/fake_bluetooth_gatt_characteristic_client.h
@@ -58,6 +58,10 @@
                   const std::vector<uint8_t>& value,
                   const base::Closure& callback,
                   const ErrorCallback& error_callback) override;
+  void PrepareWriteValue(const dbus::ObjectPath& object_path,
+                         const std::vector<uint8_t>& value,
+                         const base::Closure& callback,
+                         const ErrorCallback& error_callback) override;
   void StartNotify(const dbus::ObjectPath& object_path,
                    const base::Closure& callback,
                    const ErrorCallback& error_callback) override;
diff --git a/device/bluetooth/test/fake_peripheral.cc b/device/bluetooth/test/fake_peripheral.cc
index bf790f55..aa75c67 100644
--- a/device/bluetooth/test/fake_peripheral.cc
+++ b/device/bluetooth/test/fake_peripheral.cc
@@ -338,4 +338,17 @@
 void FakePeripheral::DisconnectGatt() {
 }
 
+#if defined(OS_CHROMEOS)
+void FakePeripheral::ExecuteWrite(
+    const base::Closure& callback,
+    const ExecuteWriteErrorCallback& error_callback) {
+  NOTIMPLEMENTED();
+}
+
+void FakePeripheral::AbortWrite(const base::Closure& callback,
+                                const AbortWriteErrorCallback& error_callback) {
+  NOTIMPLEMENTED();
+}
+#endif
+
 }  // namespace bluetooth
diff --git a/device/bluetooth/test/fake_peripheral.h b/device/bluetooth/test/fake_peripheral.h
index e9879063..8494216 100644
--- a/device/bluetooth/test/fake_peripheral.h
+++ b/device/bluetooth/test/fake_peripheral.h
@@ -113,6 +113,12 @@
       const GattConnectionCallback& callback,
       const ConnectErrorCallback& error_callback) override;
   bool IsGattServicesDiscoveryComplete() const override;
+#if defined(OS_CHROMEOS)
+  void ExecuteWrite(const base::Closure& callback,
+                    const ExecuteWriteErrorCallback& error_callback) override;
+  void AbortWrite(const base::Closure& callback,
+                  const AbortWriteErrorCallback& error_callback) override;
+#endif
 
  protected:
   void CreateGattConnectionImpl() override;
diff --git a/device/bluetooth/test/fake_remote_gatt_characteristic.cc b/device/bluetooth/test/fake_remote_gatt_characteristic.cc
index 507ad64..b0df348 100644
--- a/device/bluetooth/test/fake_remote_gatt_characteristic.cc
+++ b/device/bluetooth/test/fake_remote_gatt_characteristic.cc
@@ -183,6 +183,15 @@
                      value));
 }
 
+#if defined(OS_CHROMEOS)
+void FakeRemoteGattCharacteristic::PrepareWriteRemoteCharacteristic(
+    const std::vector<uint8_t>& value,
+    const base::Closure& callback,
+    const ErrorCallback& error_callback) {
+  NOTIMPLEMENTED();
+}
+#endif
+
 bool FakeRemoteGattCharacteristic::WriteWithoutResponse(
     base::span<const uint8_t> value) {
   if (properties_ & PROPERTY_WRITE_WITHOUT_RESPONSE) {
diff --git a/device/bluetooth/test/fake_remote_gatt_characteristic.h b/device/bluetooth/test/fake_remote_gatt_characteristic.h
index a952d43..db792af 100644
--- a/device/bluetooth/test/fake_remote_gatt_characteristic.h
+++ b/device/bluetooth/test/fake_remote_gatt_characteristic.h
@@ -92,6 +92,12 @@
   void WriteRemoteCharacteristic(const std::vector<uint8_t>& value,
                                  const base::Closure& callback,
                                  const ErrorCallback& error_callback) override;
+#if defined(OS_CHROMEOS)
+  void PrepareWriteRemoteCharacteristic(
+      const std::vector<uint8_t>& value,
+      const base::Closure& callback,
+      const ErrorCallback& error_callback) override;
+#endif
   bool WriteWithoutResponse(base::span<const uint8_t> value) override;
 
  protected:
diff --git a/device/bluetooth/test/mock_bluetooth_device.h b/device/bluetooth/test/mock_bluetooth_device.h
index 7e9ca84..a68a8d0 100644
--- a/device/bluetooth/test/mock_bluetooth_device.h
+++ b/device/bluetooth/test/mock_bluetooth_device.h
@@ -105,6 +105,14 @@
                      BluetoothRemoteGattService*(const std::string&));
   MOCK_METHOD0(CreateGattConnectionImpl, void());
   MOCK_METHOD0(DisconnectGatt, void());
+#if defined(OS_CHROMEOS)
+  MOCK_METHOD2(ExecuteWrite,
+               void(const base::Closure& callback,
+                    const ExecuteWriteErrorCallback& error_callback));
+  MOCK_METHOD2(AbortWrite,
+               void(const base::Closure& callback,
+                    const AbortWriteErrorCallback& error_callback));
+#endif
 
   // BluetoothDevice manages the lifetime of its BluetoothGATTServices.
   // This method takes ownership of the MockBluetoothGATTServices. This is only
diff --git a/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h
index ff419da..8473982e 100644
--- a/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h
+++ b/device/bluetooth/test/mock_bluetooth_gatt_characteristic.h
@@ -59,6 +59,12 @@
                void(const std::vector<uint8_t>&,
                     const base::Closure&,
                     const ErrorCallback&));
+#if defined(OS_CHROMEOS)
+  MOCK_METHOD3(PrepareWriteRemoteCharacteristic,
+               void(const std::vector<uint8_t>&,
+                    const base::Closure&,
+                    const ErrorCallback&));
+#endif
 
   void AddMockDescriptor(
       std::unique_ptr<MockBluetoothGattDescriptor> mock_descriptor);
diff --git a/device/vr/vr_device_base.cc b/device/vr/vr_device_base.cc
index 23abf3b..496ba604 100644
--- a/device/vr/vr_device_base.cc
+++ b/device/vr/vr_device_base.cc
@@ -103,6 +103,9 @@
 
 void VRDeviceBase::OnFrameFocusChanged(VRDisplayImpl* display) {
   UpdateListeningForActivate(display);
+  if (ShouldPauseAndResumeOnFocusChange()) {
+    display->InFocusedFrame() ? ResumeTracking() : PauseTracking();
+  }
 }
 
 void VRDeviceBase::SetVRDisplayInfo(mojom::VRDisplayInfoPtr display_info) {
@@ -116,7 +119,7 @@
     return;
 
   if (listener_)
-    listener_->OnChanged(display_info.Clone());
+    listener_->OnChanged(display_info_.Clone());
 }
 
 void VRDeviceBase::OnActivate(mojom::VRDisplayEventReason reason,
@@ -125,6 +128,10 @@
     listener_->OnActivate(reason, std::move(on_handled));
 }
 
+bool VRDeviceBase::ShouldPauseAndResumeOnFocusChange() {
+  return false;
+}
+
 void VRDeviceBase::OnListeningForActivate(bool listening) {}
 
 void VRDeviceBase::OnMagicWindowPoseRequest(
diff --git a/device/vr/vr_device_base.h b/device/vr/vr_device_base.h
index 005678a..f5161274 100644
--- a/device/vr/vr_device_base.h
+++ b/device/vr/vr_device_base.h
@@ -64,7 +64,17 @@
                   base::Callback<void(bool)> on_handled);
 
  private:
+  // Subclasses should implement these methods if they need to perform
+  // device-specific operations.
   virtual void UpdateListeningForActivate(VRDisplayImpl* display);
+
+  // TODO(https://crbug.com/845283): This method is a temporary solution
+  // until a XR related refactor lands. It allows to keep using the
+  // existing PauseTracking/ResumeTracking while not changing the
+  // existing VR functionality.
+  virtual bool ShouldPauseAndResumeOnFocusChange();
+
+  // TODO(https://crbug.com/842227): Rename methods to HandleOnXXX
   virtual void OnListeningForActivate(bool listening);
   virtual void OnMagicWindowPoseRequest(
       mojom::VRMagicWindowProvider::GetPoseCallback callback);
diff --git a/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc b/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc
index 0604c13..16e8bd6e 100644
--- a/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc
+++ b/extensions/browser/api/declarative_net_request/declarative_net_request_api.cc
@@ -39,6 +39,16 @@
   return false;
 }
 
+void UpdateWhitelistPagesOnIOThread(const ExtensionId& extension_id,
+                                    URLPatternSet whitelisted_pages,
+                                    InfoMap* info_map) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK(info_map);
+
+  info_map->GetRulesetManager()->UpdateWhitelistedPages(
+      extension_id, std::move(whitelisted_pages));
+}
+
 }  // namespace
 
 DeclarativeNetRequestUpdateWhitelistedPagesFunction::
@@ -85,7 +95,27 @@
   // Persist |new_set| as part of preferences.
   prefs->SetDNRWhitelistedPages(extension_id(), new_set);
 
-  return RespondNow(NoArguments());
+  // Update the new whitelist set on the IO thread.
+  base::OnceClosure updated_whitelist_pages_io_task = base::BindOnce(
+      &UpdateWhitelistPagesOnIOThread, extension_id(), std::move(new_set),
+      base::RetainedRef(ExtensionSystem::Get(browser_context())->info_map()));
+
+  base::OnceClosure updated_whitelisted_pages_ui_reply =
+      base::BindOnce(&DeclarativeNetRequestUpdateWhitelistedPagesFunction::
+                         OnWhitelistedPagesUpdated,
+                     this);
+  content::BrowserThread::PostTaskAndReply(
+      content::BrowserThread::IO, FROM_HERE,
+      std::move(updated_whitelist_pages_io_task),
+      std::move(updated_whitelisted_pages_ui_reply));
+
+  return RespondLater();
+}
+
+void DeclarativeNetRequestUpdateWhitelistedPagesFunction::
+    OnWhitelistedPagesUpdated() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  Respond(NoArguments());
 }
 
 bool DeclarativeNetRequestUpdateWhitelistedPagesFunction::PreRunValidation(
diff --git a/extensions/browser/api/declarative_net_request/declarative_net_request_api.h b/extensions/browser/api/declarative_net_request/declarative_net_request_api.h
index 753f75fd..62e40d88 100644
--- a/extensions/browser/api/declarative_net_request/declarative_net_request_api.h
+++ b/extensions/browser/api/declarative_net_request/declarative_net_request_api.h
@@ -33,6 +33,8 @@
   bool PreRunValidation(std::string* error) override;
 
  private:
+  void OnWhitelistedPagesUpdated();
+
   DISALLOW_COPY_AND_ASSIGN(DeclarativeNetRequestUpdateWhitelistedPagesFunction);
 };
 
diff --git a/extensions/browser/api/declarative_net_request/rules_monitor_service.cc b/extensions/browser/api/declarative_net_request/rules_monitor_service.cc
index 8e83e83..fe1809b9 100644
--- a/extensions/browser/api/declarative_net_request/rules_monitor_service.cc
+++ b/extensions/browser/api/declarative_net_request/rules_monitor_service.cc
@@ -38,11 +38,12 @@
 
 void LoadRulesetOnIOThread(ExtensionId extension_id,
                            std::unique_ptr<RulesetMatcher> ruleset_matcher,
+                           URLPatternSet whitelisted_pages,
                            InfoMap* info_map) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   DCHECK(info_map);
-  info_map->GetRulesetManager()->AddRuleset(extension_id,
-                                            std::move(ruleset_matcher));
+  info_map->GetRulesetManager()->AddRuleset(
+      extension_id, std::move(ruleset_matcher), std::move(whitelisted_pages));
 }
 
 void UnloadRulesetOnIOThread(ExtensionId extension_id, InfoMap* info_map) {
@@ -56,6 +57,7 @@
 void LoadRulesetOnFileTaskRunner(ExtensionId extension_id,
                                  int ruleset_checksum,
                                  base::FilePath indexed_file_path,
+                                 URLPatternSet whitelisted_pages,
                                  scoped_refptr<InfoMap> info_map) {
   base::AssertBlockingAllowed();
 
@@ -69,9 +71,10 @@
   if (result != RulesetMatcher::kLoadSuccess)
     return;
 
-  base::OnceClosure task = base::BindOnce(
-      &LoadRulesetOnIOThread, std::move(extension_id),
-      std::move(ruleset_matcher), base::RetainedRef(std::move(info_map)));
+  base::OnceClosure task =
+      base::BindOnce(&LoadRulesetOnIOThread, std::move(extension_id),
+                     std::move(ruleset_matcher), std::move(whitelisted_pages),
+                     base::RetainedRef(std::move(info_map)));
   content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
                                    std::move(task));
 }
@@ -126,12 +129,16 @@
   if (!prefs->GetDNRRulesetChecksum(extension->id(), &ruleset_checksum))
     return;
 
+  URLPatternSet whitelisted_pages =
+      prefs->GetDNRWhitelistedPages(extension->id());
+
   DCHECK(IsAPIAvailable());
   extensions_with_rulesets_.insert(extension);
 
   base::OnceClosure task = base::BindOnce(
       &LoadRulesetOnFileTaskRunner, extension->id(), ruleset_checksum,
       file_util::GetIndexedRulesetPath(extension->path()),
+      std::move(whitelisted_pages),
       base::WrapRefCounted(ExtensionSystem::Get(browser_context)->info_map()));
   file_task_runner_->PostTask(FROM_HERE, std::move(task));
 }
diff --git a/extensions/browser/api/declarative_net_request/ruleset_manager.cc b/extensions/browser/api/declarative_net_request/ruleset_manager.cc
index ba24ab6..44fc5ef 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_manager.cc
+++ b/extensions/browser/api/declarative_net_request/ruleset_manager.cc
@@ -10,6 +10,7 @@
 
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/optional.h"
 #include "base/stl_util.h"
 #include "components/web_cache/browser/web_cache_manager.h"
 #include "content/public/browser/browser_thread.h"
@@ -21,6 +22,7 @@
 #include "extensions/common/api/declarative_net_request/utils.h"
 #include "extensions/common/constants.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+#include "url/origin.h"
 
 namespace extensions {
 namespace declarative_net_request {
@@ -101,6 +103,104 @@
   }
 }
 
+// Returns true if |request| came from a page from the set of
+// |whitelisted_pages|. This necessitates finding the main frame url
+// corresponding to |request|. The logic behind how this is done is subtle and
+// as follows:
+//   - Requests made by the browser (not including navigation/frame requests) or
+//     service worker: These requests don't correspond to a render frame and
+//     hence they are not considered for whitelisting using the page
+//     whitelisting API.
+//   - Requests that correspond to a page: These include:
+//     - Main frame request: To check if it is whitelisted, check the request
+//       url against the set of whitelised pages.
+//     - Main frame subresource request: We might not be able to
+//       deterministically map a main frame subresource to the main frame url.
+//       This is because when a main frame subresource request reaches the
+//       browser, the main frame navigation would have been committed in the
+//       renderer, but the browser may not have been notified of the commit.
+//       Hence the FrameData for the request may not have the correct value for
+//       the |last_committed_main_frame_url|. To get around this we use
+//       FrameData's |pending_main_frame_url| which is populated in
+//       WebContentsObserver::ReadyToCommitNavigation. This happens before the
+//       renderer is asked to commit the navigation.
+//     - Subframe subresources: When a subframe subresource request reaches the
+//       browser, it is assured that the browser knows about its parent frame
+//       commit. For these requests, use the |last_committed_main_frame_url| and
+//       match it against the set of whitelisted pages.
+bool IsRequestPageWhitelisted(const WebRequestInfo& request,
+                              const URLPatternSet& whitelisted_pages) {
+  if (whitelisted_pages.is_empty())
+    return false;
+
+  // If this is a main frame request, |request.url| will be the main frame url.
+  if (request.type == content::RESOURCE_TYPE_MAIN_FRAME)
+    return whitelisted_pages.MatchesURL(request.url);
+
+  // This should happen for:
+  //  - Requests not corresponding to a render frame e.g. non-navigation
+  //    browser requests or service worker requests.
+  //  - Requests made by a render frame but when we don't have cached FrameData
+  //    for the request. This should occur rarely and is tracked by the
+  //    "Extensions.ExtensionFrameMapCacheHit" histogram
+  if (!request.frame_data)
+    return false;
+
+  const bool evaluate_pending_main_frame_url =
+      request.frame_data->pending_main_frame_url &&
+      *request.frame_data->pending_main_frame_url !=
+          request.frame_data->last_committed_main_frame_url;
+
+  if (!evaluate_pending_main_frame_url) {
+    return whitelisted_pages.MatchesURL(
+        request.frame_data->last_committed_main_frame_url);
+  }
+
+  // |pending_main_frame_url| should only be set for main-frame subresource
+  // loads.
+  DCHECK_EQ(ExtensionApiFrameIdMap::kTopFrameId, request.frame_data->frame_id);
+
+  // At this point, we are evaluating a main-frame subresource. There are two
+  // candidate main frame urls - |pending_main_frame_url| and
+  // |last_committed_main_frame_url|. To predict the correct main frame url,
+  // compare the request initiator (origin of the requesting frame i.e. origin
+  // of the main frame in this case) with the candidate urls' origins. If only
+  // one of the candidate url's origin matches the request initiator, we can be
+  // reasonably sure that it is the correct main frame url.
+  if (request.initiator) {
+    const bool initiator_matches_pending_url =
+        url::Origin::Create(*request.frame_data->pending_main_frame_url) ==
+        *request.initiator;
+    const bool initiator_matches_committed_url =
+        url::Origin::Create(
+            request.frame_data->last_committed_main_frame_url) ==
+        *request.initiator;
+
+    if (initiator_matches_pending_url && !initiator_matches_committed_url) {
+      // We predict that |pending_main_frame_url| is the actual main frame url.
+      return whitelisted_pages.MatchesURL(
+          *request.frame_data->pending_main_frame_url);
+    }
+
+    if (initiator_matches_committed_url && !initiator_matches_pending_url) {
+      // We predict that |last_committed_main_frame_url| is the actual main
+      // frame url.
+      return whitelisted_pages.MatchesURL(
+          request.frame_data->last_committed_main_frame_url);
+    }
+  }
+
+  // If we are not able to correctly predict the main frame url, simply test
+  // against both the possible URLs. This means a small proportion of main frame
+  // subresource requests might be incorrectly whitelisted by the page
+  // whitelisting API.
+  // TODO(karandeepb): Add UMA to see how often this happens.
+  return whitelisted_pages.MatchesURL(
+             request.frame_data->last_committed_main_frame_url) ||
+         whitelisted_pages.MatchesURL(
+             *request.frame_data->pending_main_frame_url);
+}
+
 }  // namespace
 
 RulesetManager::RulesetManager(const InfoMap* info_map) : info_map_(info_map) {
@@ -114,16 +214,16 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-void RulesetManager::AddRuleset(
-    const ExtensionId& extension_id,
-    std::unique_ptr<RulesetMatcher> ruleset_matcher) {
+void RulesetManager::AddRuleset(const ExtensionId& extension_id,
+                                std::unique_ptr<RulesetMatcher> ruleset_matcher,
+                                URLPatternSet whitelisted_pages) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(IsAPIAvailable());
 
   bool inserted;
-  std::tie(std::ignore, inserted) =
-      rulesets_.emplace(extension_id, info_map_->GetInstallTime(extension_id),
-                        std::move(ruleset_matcher));
+  std::tie(std::ignore, inserted) = rulesets_.emplace(
+      extension_id, info_map_->GetInstallTime(extension_id),
+      std::move(ruleset_matcher), std::move(whitelisted_pages));
   DCHECK(inserted) << "AddRuleset called twice in succession for "
                    << extension_id;
 
@@ -152,6 +252,30 @@
   ClearRendererCacheOnNavigation();
 }
 
+void RulesetManager::UpdateWhitelistedPages(const ExtensionId& extension_id,
+                                            URLPatternSet whitelisted_pages) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsAPIAvailable());
+
+  // This is O(n) but it's ok since the number of extensions will be small and
+  // we have to maintain the rulesets sorted in decreasing order of installation
+  // time.
+  auto iter =
+      std::find_if(rulesets_.begin(), rulesets_.end(),
+                   [&extension_id](const ExtensionRulesetData& ruleset) {
+                     return ruleset.extension_id == extension_id;
+                   });
+
+  // There must be ExtensionRulesetData corresponding to this |extension_id|.
+  DCHECK(iter != rulesets_.end());
+
+  iter->whitelisted_pages = std::move(whitelisted_pages);
+
+  // Clear the renderers' cache so that they take the updated whitelisted pages
+  // into account.
+  ClearRendererCacheOnNavigation();
+}
+
 RulesetManager::Action RulesetManager::EvaluateRequest(
     const WebRequestInfo& request,
     bool is_incognito_context,
@@ -232,10 +356,12 @@
 RulesetManager::ExtensionRulesetData::ExtensionRulesetData(
     const ExtensionId& extension_id,
     const base::Time& extension_install_time,
-    std::unique_ptr<RulesetMatcher> matcher)
+    std::unique_ptr<RulesetMatcher> matcher,
+    URLPatternSet whitelisted_pages)
     : extension_id(extension_id),
       extension_install_time(extension_install_time),
-      matcher(std::move(matcher)) {}
+      matcher(std::move(matcher)),
+      whitelisted_pages(std::move(whitelisted_pages)) {}
 RulesetManager::ExtensionRulesetData::~ExtensionRulesetData() = default;
 RulesetManager::ExtensionRulesetData::ExtensionRulesetData(
     ExtensionRulesetData&& other) = default;
@@ -284,6 +410,9 @@
     return false;
   }
 
+  if (IsRequestPageWhitelisted(request, ruleset.whitelisted_pages))
+    return false;
+
   const int tab_id = request.frame_data ? request.frame_data->tab_id
                                         : extension_misc::kUnknownTabId;
 
diff --git a/extensions/browser/api/declarative_net_request/ruleset_manager.h b/extensions/browser/api/declarative_net_request/ruleset_manager.h
index dd161d2..780719e 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_manager.h
+++ b/extensions/browser/api/declarative_net_request/ruleset_manager.h
@@ -13,6 +13,7 @@
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
 #include "extensions/common/extension_id.h"
+#include "extensions/common/url_pattern_set.h"
 
 class GURL;
 
@@ -50,12 +51,16 @@
   // Adds the ruleset for the given |extension_id|. Should not be called twice
   // in succession for an extension.
   void AddRuleset(const ExtensionId& extension_id,
-                  std::unique_ptr<RulesetMatcher> ruleset_matcher);
+                  std::unique_ptr<RulesetMatcher> ruleset_matcher,
+                  URLPatternSet whitelisted_pages);
 
   // Removes the ruleset for |extension_id|. Should be called only after a
   // corresponding AddRuleset.
   void RemoveRuleset(const ExtensionId& extension_id);
 
+  void UpdateWhitelistedPages(const ExtensionId& extension_id,
+                              URLPatternSet whitelisted_pages);
+
   // Returns the action to take for the given request. |redirect_url| will be
   // populated if the returned action is |REDIRECT|. Blocking rules have higher
   // priority than redirect rules. For determining the |redirect_url|, most
@@ -73,9 +78,10 @@
 
  private:
   struct ExtensionRulesetData {
-    ExtensionRulesetData(const ExtensionId&,
-                         const base::Time&,
-                         std::unique_ptr<RulesetMatcher>);
+    ExtensionRulesetData(const ExtensionId& extension_id,
+                         const base::Time& extension_install_time,
+                         std::unique_ptr<RulesetMatcher> matcher,
+                         URLPatternSet whitelisted_pages);
     ~ExtensionRulesetData();
     ExtensionRulesetData(ExtensionRulesetData&& other);
     ExtensionRulesetData& operator=(ExtensionRulesetData&& other);
@@ -83,6 +89,7 @@
     ExtensionId extension_id;
     base::Time extension_install_time;
     std::unique_ptr<RulesetMatcher> matcher;
+    URLPatternSet whitelisted_pages;
 
     bool operator<(const ExtensionRulesetData& other) const;
 
diff --git a/extensions/browser/api/web_request/web_request_info.cc b/extensions/browser/api/web_request/web_request_info.cc
index bc7969f..5ad45a4 100644
--- a/extensions/browser/api/web_request/web_request_info.cc
+++ b/extensions/browser/api/web_request/web_request_info.cc
@@ -367,6 +367,11 @@
     ExtensionApiFrameIdMap::FrameData data;
     bool was_cached = ExtensionApiFrameIdMap::Get()->GetCachedFrameDataOnIO(
         render_process_id, frame_id, &data);
+    // TODO(crbug.com/843762): Investigate when |was_cached| can be false. It
+    // seems we are not tracking all WebContents or that the corresponding
+    // render frame was destroyed. Track where this can occur, this should help
+    // in minimizing IO->UI->IO thread that the web request API performs to
+    // fetch the frame data.
     if (was_cached)
       frame_data = data;
   }
diff --git a/gpu/command_buffer/client/cmd_buffer_helper.cc b/gpu/command_buffer/client/cmd_buffer_helper.cc
index 6aa4374..b0718114 100644
--- a/gpu/command_buffer/client/cmd_buffer_helper.cc
+++ b/gpu/command_buffer/client/cmd_buffer_helper.cc
@@ -62,8 +62,8 @@
         total_entry_count_ /
         ((curr_get == last_flush_put_) ? kAutoFlushSmall : kAutoFlushBig);
 
-    int32_t pending =
-        (put_ + total_entry_count_ - last_flush_put_) % total_entry_count_;
+    int32_t pending = (put_ + total_entry_count_ - last_ordering_barrier_put_) %
+                      total_entry_count_;
 
     if (pending > 0 && pending >= limit) {
       // Time to force flush.
@@ -315,7 +315,14 @@
   // Try to get 'count' entries without flushing.
   CalcImmediateEntries(count);
   if (immediate_entry_count_ < count) {
-    // Try again with a shallow Flush().
+    // Update cached_get_offset_ and try again.
+    UpdateCachedState(command_buffer_->GetLastState());
+    CalcImmediateEntries(count);
+  }
+
+  if (immediate_entry_count_ < count) {
+    // Try again with a shallow Flush(). Flush can change immediate_entry_count_
+    // because of the auto flush logic.
     FlushLazy();
     CalcImmediateEntries(count);
     if (immediate_entry_count_ < count) {
diff --git a/gpu/command_buffer/client/cmd_buffer_helper_test.cc b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
index bc100f65..389b625 100644
--- a/gpu/command_buffer/client/cmd_buffer_helper_test.cc
+++ b/gpu/command_buffer/client/cmd_buffer_helper_test.cc
@@ -334,6 +334,34 @@
   EXPECT_EQ(error::kNoError, GetError());
 }
 
+// Checks that automatic flushing treats ordering barriers as flushes.
+TEST_F(CommandBufferHelperTest,
+       TestCalcImmediateEntriesAutoFlushingOrderingBarrier) {
+  // Check that auto flush happens without an ordering barrier.
+  AddUniqueCommandWithExpect(error::kNoError, kAutoFlushSmall - 1);
+  EXPECT_EQ(0, command_buffer_->FlushCount());
+  AddUniqueCommandWithExpect(error::kNoError, 1);
+  // Auto flush should be triggered by going past the threshold.
+  EXPECT_EQ(1, command_buffer_->FlushCount());
+  helper_->Finish();
+  EXPECT_EQ(2, command_buffer_->FlushCount());
+
+  // Check that an ordering barrier prevents auto flush.
+  AddUniqueCommandWithExpect(error::kNoError, kAutoFlushSmall - 1);
+  EXPECT_EQ(2, command_buffer_->FlushCount());
+  helper_->OrderingBarrier();
+  EXPECT_EQ(3, command_buffer_->FlushCount());
+  AddUniqueCommandWithExpect(error::kNoError, 1);
+  // Adding a command should not have caused a flush because there was an
+  // ordering barrier.
+  EXPECT_EQ(3, command_buffer_->FlushCount());
+
+  // Check that the commands did happen.
+  helper_->Finish();
+  Mock::VerifyAndClearExpectations(api_mock_.get());
+  EXPECT_EQ(error::kNoError, GetError());
+}
+
 // Checks immediate_entry_count_ calc when automatic flushing is enabled, and
 // we allocate commands over the immediate_entry_count_ size.
 TEST_F(CommandBufferHelperTest, TestCalcImmediateEntriesOverFlushLimit) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index bc5007c9..bbe3137 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -12336,15 +12336,6 @@
                    weak_ptr_factory_.GetWeakPtr(), c.swap_id()),
         base::DoNothing());
   } else {
-    // TODO(sunnyps): Remove Alias calls after crbug.com/724999 is fixed.
-    gl::GLContext* current = gl::GLContext::GetCurrent();
-    base::debug::Alias(&current);
-    gl::GLContext* real_current = gl::GLContext::GetRealCurrentForDebugging();
-    base::debug::Alias(&real_current);
-    gl::GLContext* context = context_.get();
-    base::debug::Alias(&context);
-    bool is_current = context_->IsCurrent(surface_.get());
-    base::debug::Alias(&is_current);
     client_->OnSwapBuffers(c.swap_id(), c.flags);
     FinishSwapBuffers(surface_->PostSubBuffer(c.x, c.y, c.width, c.height,
                                               base::DoNothing()));
@@ -15893,15 +15884,6 @@
                    weak_ptr_factory_.GetWeakPtr(), swap_id),
         base::DoNothing());
   } else {
-    // TODO(sunnyps): Remove Alias calls after crbug.com/724999 is fixed.
-    gl::GLContext* current = gl::GLContext::GetCurrent();
-    base::debug::Alias(&current);
-    gl::GLContext* real_current = gl::GLContext::GetRealCurrentForDebugging();
-    base::debug::Alias(&real_current);
-    gl::GLContext* context = context_.get();
-    base::debug::Alias(&context);
-    bool is_current = context_->IsCurrent(surface_.get());
-    base::debug::Alias(&is_current);
     client_->OnSwapBuffers(swap_id, flags);
     FinishSwapBuffers(surface_->SwapBuffers(base::DoNothing()));
   }
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 789b663..d3af093f 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -3058,12 +3058,15 @@
       return;
     }
 
+    std::vector<SkDiscardableHandleId> new_locked_handles;
     if (!font_manager_.Deserialize(font_buffer_memory, font_shm_size,
-                                   &locked_handles_)) {
+                                   &new_locked_handles)) {
       LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glRasterCHROMIUM",
                          "Invalid font buffer.");
       return;
     }
+    locked_handles_.insert(locked_handles_.end(), new_locked_handles.begin(),
+                           new_locked_handles.end());
   }
 
   char* paint_buffer_memory = GetSharedMemoryAs<char*>(
diff --git a/gpu/command_buffer/service/service_font_manager.cc b/gpu/command_buffer/service/service_font_manager.cc
index f6221fd..b4c172c 100644
--- a/gpu/command_buffer/service/service_font_manager.cc
+++ b/gpu/command_buffer/service/service_font_manager.cc
@@ -86,16 +86,22 @@
 
 ServiceFontManager::ServiceFontManager(Client* client)
     : client_(client), weak_factory_(this) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   strike_client_ = std::make_unique<SkStrikeClient>(
       sk_make_sp<SkiaDiscardableManager>(weak_factory_.GetWeakPtr()));
 }
 
-ServiceFontManager::~ServiceFontManager() = default;
+ServiceFontManager::~ServiceFontManager() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
 
 bool ServiceFontManager::Deserialize(
     const volatile char* memory,
     size_t memory_size,
     std::vector<SkDiscardableHandleId>* locked_handles) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(locked_handles->empty());
+
   // All new handles.
   Deserializer deserializer(memory, memory_size);
   size_t new_handles_created;
@@ -143,6 +149,8 @@
 
 bool ServiceFontManager::AddHandle(SkDiscardableHandleId handle_id,
                                    ServiceDiscardableHandle handle) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
   if (discardable_handle_map_.find(handle_id) != discardable_handle_map_.end())
     return false;
   discardable_handle_map_[handle_id] = std::move(handle);
@@ -151,6 +159,8 @@
 
 bool ServiceFontManager::Unlock(
     const std::vector<SkDiscardableHandleId>& handles) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
   for (auto handle_id : handles) {
     auto it = discardable_handle_map_.find(handle_id);
     if (it == discardable_handle_map_.end())
@@ -161,6 +171,8 @@
 }
 
 bool ServiceFontManager::DeleteHandle(SkDiscardableHandleId handle_id) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
   auto it = discardable_handle_map_.find(handle_id);
   if (it == discardable_handle_map_.end()) {
     LOG(ERROR) << "Tried to delete invalid SkDiscardableHandleId: "
diff --git a/gpu/command_buffer/service/service_font_manager.h b/gpu/command_buffer/service/service_font_manager.h
index 455747d..f46e723 100644
--- a/gpu/command_buffer/service/service_font_manager.h
+++ b/gpu/command_buffer/service/service_font_manager.h
@@ -7,6 +7,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
 #include "gpu/command_buffer/common/discardable_handle.h"
 #include "gpu/gpu_gles2_export.h"
 #include "third_party/skia/src/core/SkRemoteGlyphCache.h"
@@ -42,6 +43,7 @@
   std::unique_ptr<SkStrikeClient> strike_client_;
   base::flat_map<SkDiscardableHandleId, ServiceDiscardableHandle>
       discardable_handle_map_;
+  THREAD_CHECKER(thread_checker_);
   base::WeakPtrFactory<ServiceFontManager> weak_factory_;
 };
 
diff --git a/gpu/ipc/in_process_command_buffer.h b/gpu/ipc/in_process_command_buffer.h
index e5148c40..04c3ebd 100644
--- a/gpu/ipc/in_process_command_buffer.h
+++ b/gpu/ipc/in_process_command_buffer.h
@@ -261,6 +261,8 @@
     gles2::FramebufferCompletenessCache framebuffer_completeness_cache_;
   };
 
+  Service* service_for_testing() const { return service_.get(); }
+
  private:
   struct InitializeOnGpuThreadParams {
     bool is_offscreen;
diff --git a/gpu/ipc/raster_in_process_context.cc b/gpu/ipc/raster_in_process_context.cc
index d45bfdd..a9c307c 100644
--- a/gpu/ipc/raster_in_process_context.cc
+++ b/gpu/ipc/raster_in_process_context.cc
@@ -125,4 +125,9 @@
   return command_buffer_->GetTransferCacheForTest();
 }
 
+InProcessCommandBuffer* RasterInProcessContext::GetCommandBufferForTest()
+    const {
+  return command_buffer_.get();
+}
+
 }  // namespace gpu
diff --git a/gpu/ipc/raster_in_process_context.h b/gpu/ipc/raster_in_process_context.h
index ee9be759..2241e32 100644
--- a/gpu/ipc/raster_in_process_context.h
+++ b/gpu/ipc/raster_in_process_context.h
@@ -55,6 +55,7 @@
 
   // Test only functions.
   ServiceTransferCache* GetTransferCacheForTest() const;
+  InProcessCommandBuffer* GetCommandBufferForTest() const;
 
  private:
   std::unique_ptr<CommandBufferHelper> helper_;
diff --git a/gpu/ipc/service/direct_composition_child_surface_win.cc b/gpu/ipc/service/direct_composition_child_surface_win.cc
index c4b008a..f0853161 100644
--- a/gpu/ipc/service/direct_composition_child_surface_win.cc
+++ b/gpu/ipc/service/direct_composition_child_surface_win.cc
@@ -76,15 +76,6 @@
   if (!default_surface_) {
     DLOG(ERROR) << "eglCreatePbufferSurface failed with error "
                 << ui::GetLastEGLErrorString();
-    // It is likely that restoring the context will fail, so call Restore here
-    // to avoid the assert during ScopedReleaseCurrent destruction.
-    ignore_result(release_current.Restore());
-    return false;
-  }
-
-  if (!release_current.Restore()) {
-    DLOG(ERROR) << "Failed to restore context with error "
-                << ui::GetLastEGLErrorString();
     return false;
   }
 
@@ -243,6 +234,7 @@
   // PresentationCallback is handled by DirectCompositionSurfaceWin. The child
   // surface doesn't need provide presentation feedback.
   DCHECK(!callback);
+  ui::ScopedReleaseCurrent release_current;
   if (!ReleaseDrawTexture(false /* will_discard */))
     return gfx::SwapResult::SWAP_FAILED;
   return gfx::SwapResult::SWAP_ACK;
@@ -302,9 +294,6 @@
       (!use_dcomp_surface_ && !swap_chain_)) {
     if (!InitializeSurface()) {
       DLOG(ERROR) << "InitializeSurface failed";
-      // It is likely that restoring the context will fail, so call Restore here
-      // to avoid the assert during ScopedReleaseCurrent destruction.
-      ignore_result(release_current.Restore());
       return false;
     }
   }
@@ -355,15 +344,6 @@
   if (!real_surface_) {
     DLOG(ERROR) << "eglCreatePbufferFromClientBuffer failed with error "
                 << ui::GetLastEGLErrorString();
-    // It is likely that restoring the context will fail, so call Restore here
-    // to avoid the assert during ScopedReleaseCurrent destruction.
-    ignore_result(release_current.Restore());
-    return false;
-  }
-
-  if (!release_current.Restore()) {
-    DLOG(ERROR) << "Failed to restore context with error "
-                << ui::GetLastEGLErrorString();
     return false;
   }
 
diff --git a/gpu/ipc/service/direct_composition_child_surface_win.h b/gpu/ipc/service/direct_composition_child_surface_win.h
index 664e271..29af065 100644
--- a/gpu/ipc/service/direct_composition_child_surface_win.h
+++ b/gpu/ipc/service/direct_composition_child_surface_win.h
@@ -50,9 +50,6 @@
 
   uint64_t dcomp_surface_serial() const { return dcomp_surface_serial_; }
 
-  EGLSurface default_surface_for_debugging() { return default_surface_; }
-  EGLSurface real_surface_for_debugging() { return real_surface_; }
-
  protected:
   ~DirectCompositionChildSurfaceWin() override;
 
diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc
index a13977d..46e4d2a1a 100644
--- a/gpu/ipc/service/direct_composition_surface_win.cc
+++ b/gpu/ipc/service/direct_composition_surface_win.cc
@@ -34,7 +34,6 @@
 #include "ui/gl/gl_image_memory.h"
 #include "ui/gl/gl_surface_egl.h"
 #include "ui/gl/gl_surface_presentation_helper.h"
-#include "ui/gl/scoped_make_current.h"
 
 #ifndef EGL_ANGLE_flexible_surface_compatibility
 #define EGL_ANGLE_flexible_surface_compatibility 1
@@ -1493,24 +1492,17 @@
     const PresentationCallback& callback) {
   gl::GLSurfacePresentationHelper::ScopedSwapBuffers scoped_swap_buffers(
       presentation_helper_.get(), callback);
-  ui::ScopedReleaseCurrent release_current;
 
   child_window_.ClearInvalidContents();
 
-  bool failed = false;
-
   if (root_surface_->SwapBuffers(PresentationCallback()) ==
       gfx::SwapResult::SWAP_FAILED)
-    failed = true;
+    scoped_swap_buffers.set_result(gfx::SwapResult::SWAP_FAILED);
 
   if (!layer_tree_->CommitAndClearPendingOverlays())
-    failed = true;
-
-  if (!release_current.Restore())
-    failed = true;
-
-  if (failed) {
     scoped_swap_buffers.set_result(gfx::SwapResult::SWAP_FAILED);
+
+  if (scoped_swap_buffers.result() == gfx::SwapResult::SWAP_FAILED) {
     base::UmaHistogramSparse("GPU.DirectComposition.SwapBuffersLastError",
                              ::GetLastError());
   }
@@ -1590,39 +1582,8 @@
 }
 
 bool DirectCompositionSurfaceWin::SetDrawRectangle(const gfx::Rect& rectangle) {
-  if (root_surface_) {
-    // TODO(sunnyps): Remove after https://crbug.com/724999 is fixed.
-    CHECK(gl::GLContext::GetCurrent());
-    CHECK(gl::GLContext::GetCurrent()->IsCurrent(this));
-    bool was_surface_current = gl::GLSurface::GetCurrent() == this;
-    base::debug::Alias(&was_surface_current);
-    EGLSurface default_surface = root_surface_->default_surface_for_debugging();
-    base::debug::Alias(&default_surface);
-    EGLSurface old_real_surface = root_surface_->real_surface_for_debugging();
-    base::debug::Alias(&old_real_surface);
-    EGLSurface old_surface_handle = GetHandle();
-    base::debug::Alias(&old_surface_handle);
-    EGLSurface old_egl_current_surface = eglGetCurrentSurface(EGL_DRAW);
-    base::debug::Alias(&old_egl_current_surface);
-
-    bool succeeded = root_surface_->SetDrawRectangle(rectangle);
-
-    // TODO(sunnyps): Remove after https://crbug.com/724999 is fixed.
-    if (succeeded) {
-      bool is_surface_current = gl::GLSurface::GetCurrent() == this;
-      base::debug::Alias(&is_surface_current);
-      EGLSurface new_real_surface = root_surface_->real_surface_for_debugging();
-      base::debug::Alias(&new_real_surface);
-      EGLSurface new_surface_handle = GetHandle();
-      base::debug::Alias(&new_surface_handle);
-      EGLSurface new_egl_current_surface = eglGetCurrentSurface(EGL_DRAW);
-      base::debug::Alias(&new_egl_current_surface);
-      CHECK(gl::GLContext::GetCurrent());
-      CHECK(gl::GLContext::GetCurrent()->IsCurrent(this));
-    }
-
-    return succeeded;
-  }
+  if (root_surface_)
+    return root_surface_->SetDrawRectangle(rectangle);
   return false;
 }
 
diff --git a/infra/config/branch/cq.cfg b/infra/config/branch/cq.cfg
index 3dd85907..e0556da1 100644
--- a/infra/config/branch/cq.cfg
+++ b/infra/config/branch/cq.cfg
@@ -96,7 +96,7 @@
       name: "master.tryserver.chromium.win"
       builders {
         name: "win7_chromium_rel_ng"
-        equivalent_to { bucket: "luci.chromium.try" percentage: 0 }
+        equivalent_to { bucket: "luci.chromium.try" percentage: 100 }
       }
       builders {
         name: "win7_chromium_rel_loc_exp"
diff --git a/ios/build/bots/OWNERS b/ios/build/bots/OWNERS
index 1939b56c..e39778a 100644
--- a/ios/build/bots/OWNERS
+++ b/ios/build/bots/OWNERS
@@ -1,4 +1,3 @@
-huangml@chromium.org
 justincohen@chromium.org
 kkhorimoto@chromium.org
 rohitrao@chromium.org
diff --git a/ios/chrome/test/OWNERS b/ios/chrome/test/OWNERS
index 8e32f79..a659271 100644
--- a/ios/chrome/test/OWNERS
+++ b/ios/chrome/test/OWNERS
@@ -1,4 +1,3 @@
-huangml@chromium.org
 shenghuazhang@chromium.org
 eugenebut@chromium.org
 
diff --git a/ios/chrome/test/earl_grey/OWNERS b/ios/chrome/test/earl_grey/OWNERS
index 8e32f79..a659271 100644
--- a/ios/chrome/test/earl_grey/OWNERS
+++ b/ios/chrome/test/earl_grey/OWNERS
@@ -1,4 +1,3 @@
-huangml@chromium.org
 shenghuazhang@chromium.org
 eugenebut@chromium.org
 
diff --git a/ios/testing/earl_grey/OWNERS b/ios/testing/earl_grey/OWNERS
index 8e32f79..a659271 100644
--- a/ios/testing/earl_grey/OWNERS
+++ b/ios/testing/earl_grey/OWNERS
@@ -1,4 +1,3 @@
-huangml@chromium.org
 shenghuazhang@chromium.org
 eugenebut@chromium.org
 
diff --git a/ios/third_party/earl_grey/OWNERS b/ios/third_party/earl_grey/OWNERS
index 2f581c4..3558d71 100644
--- a/ios/third_party/earl_grey/OWNERS
+++ b/ios/third_party/earl_grey/OWNERS
@@ -1,4 +1,3 @@
-huangml@chromium.org
 justincohen@chromium.org
 rohitrao@chromium.org
 
diff --git a/ios/third_party/fishhook/OWNERS b/ios/third_party/fishhook/OWNERS
index 2f581c4..3558d71 100644
--- a/ios/third_party/fishhook/OWNERS
+++ b/ios/third_party/fishhook/OWNERS
@@ -1,4 +1,3 @@
-huangml@chromium.org
 justincohen@chromium.org
 rohitrao@chromium.org
 
diff --git a/ios/third_party/ochamcrest/OWNERS b/ios/third_party/ochamcrest/OWNERS
index 2f581c4..3558d71 100644
--- a/ios/third_party/ochamcrest/OWNERS
+++ b/ios/third_party/ochamcrest/OWNERS
@@ -1,4 +1,3 @@
-huangml@chromium.org
 justincohen@chromium.org
 rohitrao@chromium.org
 
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 9123780..98fc9d4 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -420,6 +420,13 @@
 void WebMediaPlayerImpl::OnSurfaceIdUpdated(viz::SurfaceId surface_id) {
   pip_surface_id_ = surface_id;
 
+  // If there was a request to enter Picture-in-Picture while the pipeline was
+  // suspended, this call should trigger Picture-in-Picture.
+  if (enter_pip_callback_) {
+    EnterPictureInPicture(std::move(*enter_pip_callback_));
+    enter_pip_callback_.reset();
+  }
+
   // TODO(726619): Handle the behavior when Picture-in-Picture mode is
   // disabled.
   // The viz::SurfaceId may be updated when the video begins playback or when
@@ -806,7 +813,17 @@
 
 void WebMediaPlayerImpl::EnterPictureInPicture(
     blink::WebMediaPlayer::PipWindowOpenedCallback callback) {
-  DCHECK(pip_surface_id_.is_valid());
+  // When the pipeline is suspended, there will be no valid surface. In this
+  // case, resuming the pipeline will auto-trigger Picture-in-Picture.
+  if (!pip_surface_id_.is_valid()) {
+    DCHECK(pipeline_controller_.IsSuspended());
+    enter_pip_callback_ = std::move(callback);
+
+    // This will trigger the pipeline to resume now that the player is pending
+    // to enter in Picture-in-Picture.
+    UpdatePlayState();
+    return;
+  }
 
   // Notifies the browser process that the player should now be in
   // Picture-in-Picture mode.
@@ -2688,6 +2705,11 @@
   result.is_suspended = is_remote || must_suspend || idle_suspended ||
                         background_suspended || can_stay_suspended;
 
+  // When Picture-in-Picture has been triggered, the pipeline needs to be
+  // resumed.
+  if (enter_pip_callback_ && !must_suspend && !is_remote)
+    result.is_suspended = false;
+
   DVLOG(3) << __func__ << ": is_remote=" << is_remote
            << ", must_suspend=" << must_suspend
            << ", idle_suspended=" << idle_suspended
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 9dc4c55..4d3b7a18 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -427,6 +427,7 @@
   //   - is_idle_, must_suspend_,
   //   - paused_, ended_,
   //   - pending_suspend_resume_cycle_,
+  //   - enter_pip_callback_,
   void UpdatePlayState();
 
   // Methods internal to UpdatePlayState().
@@ -923,6 +924,13 @@
   // route the video to be shown in the Picture-in-Picture window.
   viz::SurfaceId pip_surface_id_;
 
+  // Sets when entering Picture-in-Picture was delayed because no
+  // |pip_surface_id_| was available. This happens when the
+  // PreloadMetadataSuspend optimization is enabled and the player wouldn't
+  // create the surface until playback.
+  base::Optional<blink::WebMediaPlayer::PipWindowOpenedCallback>
+      enter_pip_callback_;
+
   DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImpl);
 };
 
diff --git a/media/gpu/android/image_reader_gl_owner.cc b/media/gpu/android/image_reader_gl_owner.cc
index 6a36337..6595a21 100644
--- a/media/gpu/android/image_reader_gl_owner.cc
+++ b/media/gpu/android/image_reader_gl_owner.cc
@@ -104,7 +104,7 @@
 
   // Delete texture
   ui::ScopedMakeCurrent scoped_make_current(context_.get(), surface_.get());
-  if (scoped_make_current.Succeeded()) {
+  if (context_->IsCurrent(surface_.get())) {
     glDeleteTextures(1, &texture_id_);
     DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
   }
diff --git a/media/gpu/android/surface_texture_gl_owner.cc b/media/gpu/android/surface_texture_gl_owner.cc
index 6550bd94..483934c 100644
--- a/media/gpu/android/surface_texture_gl_owner.cc
+++ b/media/gpu/android/surface_texture_gl_owner.cc
@@ -56,7 +56,7 @@
   if (!context_->IsCurrent(nullptr)) {
     scoped_make_current =
         std::make_unique<ui::ScopedMakeCurrent>(context_.get(), surface_.get());
-    if (!scoped_make_current->Succeeded())
+    if (!context_->IsCurrent(surface_.get()))
       return;
   }
 
diff --git a/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl b/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl
index fb535fe..a91260e 100644
--- a/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/fuzzing.tmpl
@@ -114,9 +114,9 @@
 {%-   elif kind|is_enum_kind -%}
 {{generate_or_mutate_enum(obj, operation, kind, name)}}
 {%-   elif kind|is_struct_kind -%}
-{{build_call(obj, operation, 'Struct', kind.module.namespace ~ '.' ~ kind.name, kind.is_nullable|to_js_boolean)}}
+{{build_call(obj, operation, 'Struct', name, kind.module.namespace ~ '.' ~ kind.name, kind.is_nullable|to_js_boolean)}}
 {%-   elif kind|is_union_kind -%}
-{{build_call(obj, operation, 'Union', kind.module.namespace ~ '.' ~ kind.name, kind.is_nullable|to_js_boolean)}}
+{{build_call(obj, operation, 'Union', name, kind.module.namespace ~ '.' ~ kind.name, kind.is_nullable|to_js_boolean)}}
 {%-   elif kind|is_array_kind -%}
 {{generate_or_mutate_array(obj, operation, kind, name)}}
 {%-   elif kind|is_map_kind -%}
diff --git a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
index 499c786..518cd906 100644
--- a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
@@ -64,7 +64,7 @@
     {
       field: "{{field.name}}",
 
-      mutator: function() { return {{generate_or_mutate('mutator_', 'mutate', field.kind, 'this.' ~ field.name)|indent(6)}}; },
+      mutator: function(val) { return {{generate_or_mutate('mutator_', 'mutate', field.kind, 'val.' ~ field.name)|indent(6)}}; },
     },
 {%-     endfor %}
   ];
diff --git a/net/base/port_util.cc b/net/base/port_util.cc
index 83c98213..f9bb531 100644
--- a/net/base/port_util.cc
+++ b/net/base/port_util.cc
@@ -58,6 +58,7 @@
     143,     // imap2
     179,     // BGP
     389,     // ldap
+    427,     // SLP (Also used by Apple Filing Protocol)
     465,     // smtp+ssl
     512,     // print / exec
     513,     // login
@@ -68,6 +69,7 @@
     531,     // chat
     532,     // netnews
     540,     // uucp
+    548,     // AFP (Apple Filing Protocol)
     556,     // remotefs
     563,     // nntp+ssl
     587,     // stmp?
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 3ac10e3..bea1edd 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -5232,9 +5232,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device4",
+              "id": "build211-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5264,9 +5264,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device4",
+              "id": "build212-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5298,9 +5298,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device4",
+              "id": "build212-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5330,9 +5330,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device4",
+              "id": "build212-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5364,9 +5364,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device4",
+              "id": "build212-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5396,9 +5396,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device5",
+              "id": "build213-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5430,9 +5430,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device5",
+              "id": "build213-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5462,9 +5462,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device4",
+              "id": "build213-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5496,9 +5496,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device4",
+              "id": "build213-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5528,9 +5528,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device2",
+              "id": "build213-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5562,9 +5562,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device2",
+              "id": "build213-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5594,9 +5594,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device3",
+              "id": "build211-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5628,9 +5628,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device3",
+              "id": "build211-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5660,9 +5660,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device5",
+              "id": "build212-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5694,9 +5694,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device5",
+              "id": "build212-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5726,9 +5726,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device3",
+              "id": "build211-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5760,9 +5760,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device3",
+              "id": "build211-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5792,9 +5792,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device6",
+              "id": "build213-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5826,9 +5826,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device6",
+              "id": "build213-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5858,9 +5858,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device5",
+              "id": "build213-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5892,9 +5892,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device5",
+              "id": "build213-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5924,9 +5924,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device7",
+              "id": "build211-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5958,9 +5958,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device7",
+              "id": "build211-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -5990,9 +5990,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device6",
+              "id": "build212-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6024,9 +6024,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device6",
+              "id": "build212-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6047,9 +6047,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device1",
+              "id": "build212-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6079,9 +6079,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device5",
+              "id": "build212-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6113,9 +6113,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device5",
+              "id": "build212-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6145,9 +6145,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device2",
+              "id": "build212-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6179,9 +6179,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device2",
+              "id": "build212-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6211,9 +6211,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device2",
+              "id": "build212-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6245,9 +6245,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device2",
+              "id": "build212-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6277,9 +6277,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device2",
+              "id": "build213-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6311,9 +6311,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device2",
+              "id": "build213-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6334,9 +6334,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device2",
+              "id": "build211-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6366,9 +6366,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device2",
+              "id": "build212-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6400,9 +6400,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device2",
+              "id": "build212-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6432,9 +6432,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device6",
+              "id": "build213-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6466,9 +6466,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device6",
+              "id": "build213-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6498,9 +6498,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device1",
+              "id": "build213-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6530,9 +6530,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device1",
+              "id": "build213-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6564,9 +6564,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device1",
+              "id": "build213-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6587,9 +6587,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device7",
+              "id": "build212-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6619,9 +6619,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device6",
+              "id": "build212-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6653,9 +6653,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device6",
+              "id": "build212-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6685,9 +6685,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device7",
+              "id": "build212-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6719,9 +6719,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device7",
+              "id": "build212-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6751,9 +6751,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device3",
+              "id": "build212-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6785,9 +6785,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device3",
+              "id": "build212-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6817,9 +6817,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device1",
+              "id": "build212-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6851,9 +6851,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device1",
+              "id": "build212-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6883,9 +6883,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device5",
+              "id": "build212-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6917,9 +6917,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device5",
+              "id": "build212-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6949,9 +6949,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device7",
+              "id": "build211-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -6981,9 +6981,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device6",
+              "id": "build211-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7015,9 +7015,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device6",
+              "id": "build211-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7047,9 +7047,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device4",
+              "id": "build212-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7081,9 +7081,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device4",
+              "id": "build212-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7113,9 +7113,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device4",
+              "id": "build213-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7147,9 +7147,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device4",
+              "id": "build213-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7179,9 +7179,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device6",
+              "id": "build213-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7213,9 +7213,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device6",
+              "id": "build213-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7245,9 +7245,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device5",
+              "id": "build213-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7279,9 +7279,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device5",
+              "id": "build213-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7311,9 +7311,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device4",
+              "id": "build212-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7345,9 +7345,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device4",
+              "id": "build212-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7377,9 +7377,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device6",
+              "id": "build212-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7411,9 +7411,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device6",
+              "id": "build212-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7443,9 +7443,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device3",
+              "id": "build212-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7477,9 +7477,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device3",
+              "id": "build212-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7509,9 +7509,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device6",
+              "id": "build212-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7543,9 +7543,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device6",
+              "id": "build212-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7575,9 +7575,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device5",
+              "id": "build213-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7609,9 +7609,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device5",
+              "id": "build213-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7641,9 +7641,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device1",
+              "id": "build212-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7675,9 +7675,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device1",
+              "id": "build212-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7707,9 +7707,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device7",
+              "id": "build211-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7741,9 +7741,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device7",
+              "id": "build211-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7773,9 +7773,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device3",
+              "id": "build212-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7807,9 +7807,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device3",
+              "id": "build212-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7839,9 +7839,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device6",
+              "id": "build213-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7873,9 +7873,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device6",
+              "id": "build213-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7905,9 +7905,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device2",
+              "id": "build211-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7939,9 +7939,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device2",
+              "id": "build211-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -7971,9 +7971,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device5",
+              "id": "build212-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8005,9 +8005,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device5",
+              "id": "build212-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8037,9 +8037,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device3",
+              "id": "build212-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8071,9 +8071,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device3",
+              "id": "build212-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8103,9 +8103,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device2",
+              "id": "build211-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8137,9 +8137,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device2",
+              "id": "build211-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8169,9 +8169,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device3",
+              "id": "build213-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8203,9 +8203,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device3",
+              "id": "build213-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8235,9 +8235,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device2",
+              "id": "build213-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8269,9 +8269,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device2",
+              "id": "build213-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8301,9 +8301,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device2",
+              "id": "build213-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8335,9 +8335,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device2",
+              "id": "build213-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8367,9 +8367,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device6",
+              "id": "build211-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8401,9 +8401,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device6",
+              "id": "build211-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8433,9 +8433,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device4",
+              "id": "build213-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8467,9 +8467,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device4",
+              "id": "build213-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8499,9 +8499,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device1",
+              "id": "build212-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8533,9 +8533,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device1",
+              "id": "build212-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8565,9 +8565,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device5",
+              "id": "build213-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8599,9 +8599,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device5",
+              "id": "build213-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8631,9 +8631,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device7",
+              "id": "build213-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8665,9 +8665,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device7",
+              "id": "build213-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8697,9 +8697,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device3",
+              "id": "build212-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8731,9 +8731,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device3",
+              "id": "build212-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8763,9 +8763,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device7",
+              "id": "build213-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8797,9 +8797,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device7",
+              "id": "build213-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8829,9 +8829,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device2",
+              "id": "build212-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8863,9 +8863,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device2",
+              "id": "build212-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8895,9 +8895,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device1",
+              "id": "build213-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8929,9 +8929,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device1",
+              "id": "build213-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8961,9 +8961,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device5",
+              "id": "build212-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -8995,9 +8995,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device5",
+              "id": "build212-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9027,9 +9027,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device2",
+              "id": "build212-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9059,9 +9059,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device4",
+              "id": "build211-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9093,9 +9093,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device4",
+              "id": "build211-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9127,9 +9127,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device2",
+              "id": "build212-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9159,9 +9159,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device2",
+              "id": "build212-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9191,9 +9191,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device4",
+              "id": "build211-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9225,9 +9225,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device4",
+              "id": "build211-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9259,9 +9259,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device2",
+              "id": "build212-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9291,9 +9291,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device4",
+              "id": "build212-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9325,9 +9325,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device4",
+              "id": "build212-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9357,9 +9357,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device5",
+              "id": "build213-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9391,9 +9391,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device5",
+              "id": "build213-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9423,9 +9423,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device7",
+              "id": "build212-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9457,9 +9457,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device7",
+              "id": "build212-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9489,9 +9489,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device1",
+              "id": "build211-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9523,9 +9523,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device1",
+              "id": "build211-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9555,9 +9555,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device1",
+              "id": "build212-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9589,9 +9589,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device1",
+              "id": "build212-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9621,9 +9621,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device1",
+              "id": "build212-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9655,9 +9655,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device1",
+              "id": "build212-b7--device1",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9687,9 +9687,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device6",
+              "id": "build211-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9721,9 +9721,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device6",
+              "id": "build211-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9753,9 +9753,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device6",
+              "id": "build212-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9787,9 +9787,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device6",
+              "id": "build212-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9819,9 +9819,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device4",
+              "id": "build213-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9853,9 +9853,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device4",
+              "id": "build213-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9885,9 +9885,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device5",
+              "id": "build212-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9919,9 +9919,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build74-b1--device5",
+              "id": "build212-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9951,9 +9951,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device3",
+              "id": "build213-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -9985,9 +9985,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device3",
+              "id": "build213-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10017,9 +10017,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device3",
+              "id": "build213-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10051,9 +10051,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device3",
+              "id": "build213-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10083,9 +10083,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device6",
+              "id": "build213-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10117,9 +10117,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device6",
+              "id": "build213-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10140,9 +10140,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device2",
+              "id": "build211-b7--device2",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10172,9 +10172,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device5",
+              "id": "build211-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10204,9 +10204,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device4",
+              "id": "build211-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10238,9 +10238,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device4",
+              "id": "build211-b7--device4",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10272,9 +10272,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device5",
+              "id": "build211-b7--device5",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10304,9 +10304,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build75-b1--device7",
+              "id": "build213-b7--device7",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10336,9 +10336,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device3",
+              "id": "build211-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10370,9 +10370,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device3",
+              "id": "build211-b7--device3",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10402,9 +10402,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device6",
+              "id": "build211-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
@@ -10436,9 +10436,9 @@
               "device_os": "MMB29Q",
               "device_os_flavor": "google",
               "device_type": "bullhead",
-              "id": "build73-b1--device6",
+              "id": "build211-b7--device6",
               "os": "Android",
-              "pool": "Chrome-perf"
+              "pool": "chrome.tests.perf"
             }
           ],
           "expiration": 36000,
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index 68a59e08..19ab1080 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -703,6 +703,18 @@
         "test": "browser_tests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_browser_tests.filter"
+        ],
+        "name": "network_service_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -757,6 +769,16 @@
         "test": "components_browsertests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_browsertests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -776,6 +798,17 @@
       },
       {
         "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+        ],
+        "name": "network_service_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-features=VizDisplayCompositor",
           "--test-launcher-filter-file=../../testing/buildbot/filters/viz.content_browsertests.filter"
         ],
@@ -864,6 +897,16 @@
         "test": "extensions_browsertests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -936,6 +979,16 @@
         "test": "interactive_ui_tests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1246,6 +1299,18 @@
         "test": "boringssl_ssl_tests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_browser_tests.filter"
+        ],
+        "name": "network_service_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1294,6 +1359,16 @@
         "test": "chromedriver_unittests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_browsertests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1312,6 +1387,17 @@
         "test": "content_browsertests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+        ],
+        "name": "network_service_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_browsertests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1383,6 +1469,16 @@
         "test": "events_unittests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1456,6 +1552,16 @@
         "test": "interactive_ui_tests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
diff --git a/testing/buildbot/filters/mash.browser_tests.filter b/testing/buildbot/filters/mash.browser_tests.filter
index 90325bc..a7691b6 100644
--- a/testing/buildbot/filters/mash.browser_tests.filter
+++ b/testing/buildbot/filters/mash.browser_tests.filter
@@ -232,6 +232,11 @@
 # http://crbug.com/755328
 -WebViewTest.*
 
+# Crashes in RenderProcessHostImpl::CreateEmbeddedFrameSinkProvider(). Refer
+# crbug.com/848039.
+-DeclarativeNetRequestBrowserTest.PageWhitelistingAPI_Resources/0
+-DeclarativeNetRequestBrowserTest.PageWhitelistingAPI_Resources/1
+
 # Sending invalid FrameSinkIds crbug.com/796999
 -WebviewLoginTest.AllowNewUser
 -EnterpriseEnrollmentTest.TestAuthCodeGetsProperlyReceivedFromGaia
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index f5bc4a47..3c411bd 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -136,6 +136,8 @@
 # http://crbug.com/841002
 -ExtensionWebRequestApiTest.WebRequestURLLoaderInterception
 -ExtensionWebRequestApiTest.WebRequestClientsGoogleComProtection
+-DeclarativeNetRequestBrowserTest.PageWhitelistingAPI_BrowserRequests/0
+-DeclarativeNetRequestBrowserTest.PageWhitelistingAPI_BrowserRequests/1
 # Note WebRequestUnloadImmediately is disabled on Linux
 -ExtensionWebRequestApiTest.WebRequestUnloadImmediately
 # http://crbug.com/841827
diff --git a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
index 3e7440c..a5623d2 100644
--- a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
@@ -3,7 +3,6 @@
 # failures. Otherwise please reach out to network-service-dev@.
 
 # https://crbug.com/715640
--ServiceWorkerBrowserTest.Reload
 -ServiceWorkerNavigationPreloadTest.CanceledByInterceptor
 -ServiceWorkerNavigationPreloadTest.RespondWithNavigationPreloadWithMimeSniffing
 -ServiceWorkerV8CodeCacheForCacheStorageTest.V8CacheOnCacheStorage
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index d27acb3..03bd1e5 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -2052,7 +2052,7 @@
     'win_specific_chromium_gtests',
   ],
 
-  'chromium_win_gtests': [
+  'chromium_win7_gtests': [
     'aura_gtests',
     'chromium_gtests',
     'chromium_gtests_for_devices_with_graphical_output',
@@ -2070,6 +2070,25 @@
     'win_specific_chromium_gtests',
   ],
 
+  'chromium_win10_gtests': [
+    'aura_gtests',
+    'chromium_gtests',
+    'chromium_gtests_for_devices_with_graphical_output',
+    'network_service_gtests',
+    'non_android_chromium_gtests',
+    'non_android_and_cast_and_chromeos_chromium_gtests',
+    'non_android_and_cast_and_chromeos_and_clang_and_mac_fyi_chromium_gtests',
+    'non_android_and_clang_linux_mac_chromium_gtests',
+    'non_android_and_clang_linux_win_chromium_gtests',
+    'non_android_and_clang_mac_win_chromium_gtests',
+    'non_android_and_clang_win_chromium_gtests',
+    'non_linux_chromium_gtests',
+    'non_mac_non_clang_win_chromium_gtests',
+    'viz_gtests',
+    'vr_platform_specific_non_clang_win_chromium_gtests',
+    'win_specific_chromium_gtests',
+  ],
+
   'marshmallow_isolated_scripts': [
     'components_perftests_isolated_scripts',
     'monochrome_apk_checker_isolated_script',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 6288ac0..f0af8eb 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2034,13 +2034,13 @@
       },
       'Win 7 Tests x64 (1)': {
         'test_suites': {
-          'gtest_tests': 'chromium_win_gtests',
+          'gtest_tests': 'chromium_win7_gtests',
           'isolated_scripts': 'chromium_rel_isolated_scripts',
         },
       },
       'Win10 Tests x64': {
         'test_suites': {
-          'gtest_tests': 'chromium_win_gtests',
+          'gtest_tests': 'chromium_win10_gtests',
           'isolated_scripts': 'win_specific_isolated_scripts',
         },
       },
@@ -2051,20 +2051,20 @@
       },
       'Win7 Tests (1)': {
         'test_suites': {
-          'gtest_tests': 'chromium_win_gtests',
+          'gtest_tests': 'chromium_win7_gtests',
           'isolated_scripts': 'chromium_rel_isolated_scripts',
           'scripts': 'chromium_scripts',
         },
       },
       'Win7 Tests (dbg)(1)': {
         'test_suites': {
-          'gtest_tests': 'chromium_win_gtests',
+          'gtest_tests': 'chromium_win7_gtests',
           'isolated_scripts': 'chromium_dbg_isolated_scripts',
         },
       },
       'Win10 Tests x64 (dbg)': {
         'test_suites': {
-          'gtest_tests': 'chromium_win_gtests',
+          'gtest_tests': 'chromium_win10_gtests',
           'isolated_scripts': 'chromium_dbg_isolated_scripts',
         },
       },
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index e34ad36..3eb5367 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -162,7 +162,6 @@
 crbug.com/591099 css3/flexbox/intrinsic-width-orthogonal-writing-mode.html [ Failure ]
 crbug.com/591099 css3/flexbox/line-wrapping.html [ Failure ]
 crbug.com/591099 css3/flexbox/scrollbars-auto.html [ Failure ]
-crbug.com/591099 css3/flexbox/scrollbars.html [ Failure ]
 crbug.com/714962 css3/masking/clip-path-reference-box-inline.html [ Failure ]
 crbug.com/591099 css3/selectors3/html/css3-modsel-167.html [ Failure ]
 crbug.com/591099 css3/selectors3/html/css3-modsel-167a.html [ Failure ]
@@ -708,19 +707,6 @@
 crbug.com/591099 fast/replaced/preferred-widths.html [ Failure ]
 crbug.com/591099 fast/replaced/table-replaced-element.html [ Failure ]
 crbug.com/591099 fast/ruby/position-after.html [ Failure ]
-crbug.com/591099 fast/ruby/ruby-empty-rt.html [ Failure ]
-crbug.com/591099 fast/ruby/ruby-run-break.html [ Failure ]
-crbug.com/591099 fast/ruby/ruby-runs.html [ Failure ]
-crbug.com/591099 fast/ruby/ruby-simple-rp.html [ Failure ]
-crbug.com/591099 fast/ruby/ruby-simple.html [ Failure ]
-crbug.com/591099 fast/ruby/ruby-trailing.html [ Failure ]
-crbug.com/591099 fast/ruby/rubyDOM-insert-rt.html [ Failure ]
-crbug.com/591099 fast/ruby/rubyDOM-insert-text1.html [ Failure ]
-crbug.com/591099 fast/ruby/rubyDOM-insert-text2.html [ Failure ]
-crbug.com/591099 fast/ruby/rubyDOM-insert-text3.html [ Failure ]
-crbug.com/591099 fast/ruby/rubyDOM-remove-rt1.html [ Failure ]
-crbug.com/591099 fast/ruby/rubyDOM-remove-rt2.html [ Failure ]
-crbug.com/591099 fast/ruby/rubyDOM-remove-text1.html [ Failure ]
 crbug.com/591099 fast/scrolling/content-box-smaller-than-scrollbar.html [ Failure ]
 crbug.com/591099 fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Crash ]
 crbug.com/591099 fast/scrolling/jquery-rtl-scroll-type.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index d4e8b8a..edf910bd 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -169,11 +169,6 @@
 crbug.com/736052 compositing/overflow/composited-scroll-with-fractional-translation.html [ Failure ]
 crbug.com/736052 virtual/prefer_compositing_to_lcd_text/compositing/overflow/composited-scroll-with-fractional-translation.html [ Failure ]
 
-
-# Not relevant, because SPv175 is launching imminently.
-
-crbug.com/842668 [ Win7 ] paint/invalidation/svg/zoom-foreignObject.svg [ Failure ]
-
 # Sheriff 2018/05/25
 crbug.com/846749 [ Mac ] virtual/stable/http/tests/navigation/navigation-interrupted-by-fragment.html  [ Pass Failure ]
 
@@ -4520,9 +4515,6 @@
 crbug.com/807838 virtual/service-worker-servicification/external/wpt/service-workers/service-worker/worker-in-sandboxed-iframe-by-csp-fetch-event.https.html [ Crash Pass ]
 crbug.com/830472 external/wpt/service-workers/service-worker/navigation-preload/broken-chunked-encoding.https.html [ Failure ]
 
-# SVGViewSpec is not updated when the referenced element's values are mutated.
-crbug.com/843192 svg/animations/viewspec-checkaspectparams.html [ Failure ]
-
 # Sheriff faulures 2017-12-12
 crbug.com/794180 http/tests/devtools/layers/layer-compositing-reasons.js [ Failure Pass ]
 
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/gc-with-event-listener.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/gc-with-event-listener.html
new file mode 100644
index 0000000..25cf9017
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/notifications/gc-with-event-listener.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/testdriver.js"></script>
+<script src="../../../resources/testdriver-vendor.js"></script>
+<script src="../../../external/wpt/bluetooth/resources/bluetooth-helpers.js"></script>
+<script>
+'use strict';
+bluetooth_test(async () => {
+  let expectingEvent = false;
+  let eventHandler;
+  let eventPromise = new Promise((resolve, reject) => {
+    eventHandler = () => {
+      if (expectingEvent) {
+        resolve();
+      } else {
+        reject('Event fired before garbage collection.');
+      }
+    };
+  });
+
+  await (async () => {
+    await setBluetoothFakeAdapter('HeartRateAdapter');
+    let device = await requestDeviceWithTrustedClick({
+        filters: [{services: ['heart_rate']}]});
+    let gattServer = await device.gatt.connect();
+    let service = await gattServer.getPrimaryService('heart_rate');
+    let characteristic =
+        await service.getCharacteristic('heart_rate_measurement');
+    await characteristic.startNotifications();
+
+    characteristic.addEventListener(
+        'characteristicvaluechanged', eventHandler, { once: true });
+
+    // The characteristic must hold the last reference to the event handler.
+    eventHandler = undefined;
+  })();
+
+  // At this point all the variables above should be out of scope and so this
+  // will free them.
+  GCController.collect();
+
+  // The event listener should still be active.
+  expectingEvent = true;
+  await eventPromise;
+}, 'Event listeners remain registered after garbage collection.');
+</script>
diff --git a/third_party/WebKit/LayoutTests/editing/spelling/cold_mode_nested_editables.html b/third_party/WebKit/LayoutTests/editing/spelling/cold_mode_nested_editables.html
new file mode 100644
index 0000000..72bf8cc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/editing/spelling/cold_mode_nested_editables.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../assert_selection.js"></script>
+<script src="spellcheck_test.js"></script>
+<script>
+const longCorrectText = ' word '.repeat(20);
+
+// crbug.com/848026
+spellcheck_test(
+    [
+      `<div contenteditable>${longCorrectText}`,
+      '<span contenteditable="false">zz ',
+      '<span contenteditable id="target">zz</span>',
+      ' zz</span>',
+      `${longCorrectText}</div>`
+    ].join(''),
+    document => document.getElementById('target').focus(),
+    [
+      `<div contenteditable>${longCorrectText}`,
+      '<span contenteditable="false">zz ',
+      '<span contenteditable id="target">#zz#</span>',
+      ' zz</span>',
+      `${longCorrectText}</div>`
+    ].join(''),
+    {
+      title: 'Cold mode checks text in nested editables correctly without crashing',
+      needsFullCheck: true
+    }
+);
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resource-timing/resource_timing_buffer_full_eventually.html b/third_party/WebKit/LayoutTests/external/wpt/resource-timing/resource_timing_buffer_full_eventually.html
new file mode 100644
index 0000000..7ca8237
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/resource-timing/resource_timing_buffer_full_eventually.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8" />
+<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
+<title>This test validates that resource timing implementations have a finite number of entries in their buffer.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+    const t = async_test("Finite resource timing entries buffer size");
+    performance.onresourcetimingbufferfull = t.step_func_done(function() {
+    });
+    window.onload = t.step_func(
+        function() {
+            // Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
+            let counter = performance.getEntriesByType("resource").length;
+            function appendScript() {
+                const src = "resources/empty.js?" + counter;
+                const script = document.createElement('script');
+                script.type = 'text/javascript';
+                script.onload = function() { ++counter; appendScript()};
+                script.src = src;
+                document.body.appendChild(script);
+            }
+            appendScript();
+        });
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-helper.js b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-helper.js
index b378579d..b1dd5e48 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-helper.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-helper.js
@@ -199,7 +199,7 @@
       // There is ongoing discussion on w3c/webrtc-pc#1213
       // that there should be an empty candidate string event
       // for end of candidate for each m= section.
-      if(candidate) {
+      if(candidate && remotePc.signalingState !== 'closed') {
         remotePc.addIceCandidate(candidate);
       }
     });
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-empty-rt-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-empty-rt-expected.txt
new file mode 100644
index 0000000..ea13202
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-empty-rt-expected.txt
@@ -0,0 +1,28 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 451x19
+          text run at (0,0) width 451: "The following is a test for having a <rt> immediately following another."
+      LayoutNGBlockFlow (anonymous) at (0,36) size 769x40
+        LayoutBR {BR} at (0,0) size 0x19
+        LayoutBR {BR} at (0,20) size 0x19
+      LayoutBlockFlow {P} at (0,92) size 784x32
+        LayoutText {#text} at (0,12) size 82x19
+          text run at (0,12) width 82: "<ruby> uses "
+        LayoutRuby (inline) {RUBY} at (0,0) size 42x19
+          LayoutRubyRun (anonymous) at (82,12) size 27x20
+            LayoutRubyText {RT} at (0,-12) size 27x12
+              LayoutText {#text} at (4,0) size 19x12
+                text run at (4,0) width 19: "ruby"
+            LayoutRubyBase (anonymous) at (0,0) size 27x20
+              LayoutText {#text} at (0,0) size 27x19
+                text run at (0,0) width 27: "<rt>"
+          LayoutRubyRun (anonymous) at (109,29) size 15x0
+            LayoutRubyText {RT} at (0,-12) size 15x12
+              LayoutText {#text} at (0,0) size 15x12
+                text run at (0,0) width 15: "text"
+        LayoutText {#text} at (124,12) size 163x19
+          text run at (124,12) width 163: " to contain the annotation."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-run-break-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-run-break-expected.txt
new file mode 100644
index 0000000..7c7ef6b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-run-break-expected.txt
@@ -0,0 +1,54 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x584
+      LayoutNGBlockFlow (anonymous) at (0,0) size 769x80
+        LayoutText {#text} at (0,0) size 767x59
+          text run at (0,0) width 752: "This is a test for multiple ruby runs and line breaks. There is a div with a blue border and 10px padding. This contains a"
+          text run at (0,20) width 497: "single <ruby> markup in the text contained in the block, broken across 2 lines. "
+          text run at (497,20) width 270: "Part of it should be on the first line, the rest"
+          text run at (0,40) width 110: "on the other line. "
+        LayoutBR {BR} at (110,55) size 0x0
+        LayoutBR {BR} at (0,60) size 0x19
+      LayoutBlockFlow {DIV} at (0,80) size 284x77 [border: (2px solid #0000FF)]
+        LayoutText {#text} at (12,14) size 155x19
+          text run at (12,14) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 260x50
+          LayoutRubyRun (anonymous) at (163,14) size 24x20
+            LayoutRubyText {RT} at (0,-12) size 24x12
+              LayoutText {#text} at (0,0) size 24x12
+                text run at (0,0) width 24: "Hyper"
+            LayoutRubyBase (anonymous) at (0,0) size 24x20
+              LayoutText {#text} at (6,0) size 12x19
+                text run at (6,0) width 12: "H"
+          LayoutRubyRun (anonymous) at (187,14) size 17x20
+            LayoutRubyText {RT} at (0,-12) size 17x12
+              LayoutText {#text} at (0,0) size 17x12
+                text run at (0,0) width 17: "Text"
+            LayoutRubyBase (anonymous) at (0,0) size 17x20
+              LayoutText {#text} at (3,0) size 11x19
+                text run at (3,0) width 11: "T"
+          LayoutRubyRun (anonymous) at (204,14) size 30x20
+            LayoutRubyText {RT} at (0,-12) size 30x12
+              LayoutText {#text} at (0,0) size 30x12
+                text run at (0,0) width 30: "Markup"
+            LayoutRubyBase (anonymous) at (0,0) size 30x20
+              LayoutText {#text} at (8,0) size 14x19
+                text run at (8,0) width 14: "M"
+          LayoutRubyRun (anonymous) at (234,14) size 38x20
+            LayoutRubyText {RT} at (0,-12) size 38x12
+              LayoutText {#text} at (0,0) size 38x12
+                text run at (0,0) width 38: "Language"
+            LayoutRubyBase (anonymous) at (0,0) size 38x20
+              LayoutText {#text} at (14,0) size 10x19
+                text run at (14,0) width 10: "L"
+          LayoutRubyRun (anonymous) at (12,45) size 29x20
+            LayoutRubyText {RT} at (0,-12) size 29x12
+              LayoutText {#text} at (0,0) size 29x12
+                text run at (0,0) width 29: "Level 5"
+            LayoutRubyBase (anonymous) at (0,0) size 29x20
+              LayoutText {#text} at (10,0) size 9x19
+                text run at (10,0) width 9: "5"
+        LayoutText {#text} at (37,45) size 42x19
+          text run at (37,45) width 42: " specs."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-runs-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-runs-expected.txt
new file mode 100644
index 0000000..39df261
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-runs-expected.txt
@@ -0,0 +1,51 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutNGBlockFlow (anonymous) at (0,0) size 769x40
+        LayoutText {#text} at (0,0) size 227x19
+          text run at (0,0) width 227: "This is a test for multiple ruby runs. "
+        LayoutBR {BR} at (227,15) size 0x0
+        LayoutBR {BR} at (0,20) size 0x19
+      LayoutBlockFlow {P} at (0,56) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 130x19
+          LayoutRubyRun (anonymous) at (151,12) size 24x20
+            LayoutRubyText {RT} at (0,-12) size 24x12
+              LayoutText {#text} at (0,0) size 24x12
+                text run at (0,0) width 24: "Hyper"
+            LayoutRubyBase (anonymous) at (0,0) size 24x20
+              LayoutText {#text} at (6,0) size 12x19
+                text run at (6,0) width 12: "H"
+          LayoutRubyRun (anonymous) at (175,12) size 17x20
+            LayoutRubyText {RT} at (0,-12) size 17x12
+              LayoutText {#text} at (0,0) size 17x12
+                text run at (0,0) width 17: "Text"
+            LayoutRubyBase (anonymous) at (0,0) size 17x20
+              LayoutText {#text} at (3,0) size 11x19
+                text run at (3,0) width 11: "T"
+          LayoutRubyRun (anonymous) at (192,12) size 30x20
+            LayoutRubyText {RT} at (0,-12) size 30x12
+              LayoutText {#text} at (0,0) size 30x12
+                text run at (0,0) width 30: "Markup"
+            LayoutRubyBase (anonymous) at (0,0) size 30x20
+              LayoutText {#text} at (8,0) size 14x19
+                text run at (8,0) width 14: "M"
+          LayoutRubyRun (anonymous) at (222,12) size 38x20
+            LayoutRubyText {RT} at (0,-12) size 38x12
+              LayoutText {#text} at (0,0) size 38x12
+                text run at (0,0) width 38: "Language"
+            LayoutRubyBase (anonymous) at (0,0) size 38x20
+              LayoutText {#text} at (14,0) size 10x19
+                text run at (14,0) width 10: "L"
+          LayoutRubyRun (anonymous) at (260,12) size 29x20
+            LayoutRubyText {RT} at (0,-12) size 29x12
+              LayoutText {#text} at (0,0) size 29x12
+                text run at (0,0) width 29: "Level 5"
+            LayoutRubyBase (anonymous) at (0,0) size 29x20
+              LayoutText {#text} at (10,0) size 9x19
+                text run at (10,0) width 9: "5"
+        LayoutText {#text} at (285,12) size 42x19
+          text run at (285,12) width 42: " specs."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-simple-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-simple-expected.txt
new file mode 100644
index 0000000..7f6fb2f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-simple-expected.txt
@@ -0,0 +1,23 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutNGBlockFlow (anonymous) at (0,0) size 769x40
+        LayoutText {#text} at (0,0) size 224x19
+          text run at (0,0) width 224: "This is a initial test for simple ruby. "
+        LayoutBR {BR} at (224,15) size 0x0
+        LayoutBR {BR} at (0,20) size 0x19
+      LayoutBlockFlow {P} at (0,56) size 784x32
+        LayoutText {#text} at (0,12) size 161x19
+          text run at (0,12) width 161: "Ruby is often used in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 117x19
+          LayoutRubyRun (anonymous) at (161,12) size 117x20
+            LayoutRubyText {RT} at (0,-12) size 117x12
+              LayoutText {#text} at (41,0) size 35x12
+                text run at (41,0) width 35: "Nihongo"
+            LayoutRubyBase (anonymous) at (0,0) size 117x20
+              LayoutText {#text} at (0,0) size 117x19
+                text run at (0,0) width 117: "Japanese language"
+        LayoutText {#text} at (278,12) size 4x19
+          text run at (278,12) width 4: "."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-simple-rp-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-simple-rp-expected.txt
new file mode 100644
index 0000000..e7fffcb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-simple-rp-expected.txt
@@ -0,0 +1,25 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutNGBlockFlow (anonymous) at (0,0) size 769x60
+        LayoutText {#text} at (0,0) size 760x39
+          text run at (0,0) width 333: "This is a test for simple ruby that contains <rp> tags. "
+          text run at (333,0) width 427: "Contents of the <rp> tags (opening and closing brackets) should not"
+          text run at (0,20) width 202: "show when ruby is suppoorted. "
+        LayoutBR {BR} at (202,35) size 0x0
+        LayoutBR {BR} at (0,40) size 0x19
+      LayoutBlockFlow {P} at (0,76) size 784x32
+        LayoutText {#text} at (0,12) size 161x19
+          text run at (0,12) width 161: "Ruby is often used in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 117x19
+          LayoutRubyRun (anonymous) at (161,12) size 117x20
+            LayoutRubyText {RT} at (0,-12) size 117x12
+              LayoutText {#text} at (41,0) size 35x12
+                text run at (41,0) width 35: "Nihongo"
+            LayoutRubyBase (anonymous) at (0,0) size 117x20
+              LayoutText {#text} at (0,0) size 117x19
+                text run at (0,0) width 117: "Japanese language"
+        LayoutText {#text} at (278,12) size 4x19
+          text run at (278,12) width 4: "."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-trailing-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-trailing-expected.txt
new file mode 100644
index 0000000..99e0809
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/ruby-trailing-expected.txt
@@ -0,0 +1,28 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 590x19
+          text run at (0,0) width 590: "The following is a test for having a trailing base within a <ruby> with no associated ruby text."
+      LayoutNGBlockFlow (anonymous) at (0,36) size 769x40
+        LayoutBR {BR} at (0,0) size 0x19
+        LayoutBR {BR} at (0,20) size 0x19
+      LayoutBlockFlow {P} at (0,92) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 118x19
+          LayoutRubyRun (anonymous) at (151,12) size 114x20
+            LayoutRubyText {RT} at (0,-12) size 114x12
+              LayoutText {#text} at (0,0) size 114x12
+                text run at (0,0) width 114: "Hyper-text Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 114x20
+              LayoutText {#text} at (34,0) size 46x19
+                text run at (34,0) width 46: "HTML"
+          LayoutRubyRun (anonymous) at (265,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (273,12) size 4x19
+          text run at (273,12) width 4: "."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-rt-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-rt-expected.txt
new file mode 100644
index 0000000..d89465e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-rt-expected.txt
@@ -0,0 +1,66 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 574x19
+          text run at (0,0) width 574: "The following is a test for DOM manipulation within <ruby>: Inserting a new <rt> element"
+      LayoutBlockFlow {P} at (0,36) size 784x20
+        LayoutText {#text} at (0,0) size 436x19
+          text run at (0,0) width 436: "Both lines should look identical (the first line is the one manipulated)."
+      LayoutNGBlockFlow (anonymous) at (0,72) size 769x40
+        LayoutBR {BR} at (0,0) size 0x19
+        LayoutBR {BR} at (0,20) size 0x19
+      LayoutBlockFlow {P} at (0,128) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 116x19
+          LayoutRubyRun (anonymous) at (151,12) size 42x20
+            LayoutRubyText {RT} at (0,-12) size 42x12
+              LayoutText {#text} at (0,0) size 42x12
+                text run at (0,0) width 42: "Hyper-text"
+            LayoutRubyBase (anonymous) at (0,0) size 42x20
+              LayoutInline {SPAN} at (0,0) size 22x19
+                LayoutText {#text} at (10,0) size 22x19
+                  text run at (10,0) width 22: "HT"
+          LayoutRubyRun (anonymous) at (193,12) size 70x20
+            LayoutRubyText {RT} at (0,-12) size 70x12
+              LayoutText {#text} at (0,0) size 70x12
+                text run at (0,0) width 70: "Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 70x20
+              LayoutInline {SPAN} at (0,0) size 24x19
+                LayoutText {#text} at (23,0) size 24x19
+                  text run at (23,0) width 24: "ML"
+          LayoutRubyRun (anonymous) at (263,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (271,12) size 36x19
+          text run at (271,12) width 36: " spec."
+      LayoutBlockFlow {P} at (0,176) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 116x19
+          LayoutRubyRun (anonymous) at (151,12) size 42x20
+            LayoutRubyText {RT} at (0,-12) size 42x12
+              LayoutText {#text} at (0,0) size 42x12
+                text run at (0,0) width 42: "Hyper-text"
+            LayoutRubyBase (anonymous) at (0,0) size 42x20
+              LayoutInline {SPAN} at (0,0) size 22x19
+                LayoutText {#text} at (10,0) size 22x19
+                  text run at (10,0) width 22: "HT"
+          LayoutRubyRun (anonymous) at (193,12) size 70x20
+            LayoutRubyText {RT} at (0,-12) size 70x12
+              LayoutText {#text} at (0,0) size 70x12
+                text run at (0,0) width 70: "Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 70x20
+              LayoutInline {SPAN} at (0,0) size 24x19
+                LayoutText {#text} at (23,0) size 24x19
+                  text run at (23,0) width 24: "ML"
+          LayoutRubyRun (anonymous) at (263,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (271,12) size 36x19
+          text run at (271,12) width 36: " spec."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-text1-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-text1-expected.txt
new file mode 100644
index 0000000..47c26bd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-text1-expected.txt
@@ -0,0 +1,62 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 656x19
+          text run at (0,0) width 656: "The following is a test for DOM manipulation within <ruby>: Inserting a new text before a <rt> element"
+      LayoutBlockFlow {P} at (0,36) size 784x20
+        LayoutText {#text} at (0,0) size 436x19
+          text run at (0,0) width 436: "Both lines should look identical (the first line is the one manipulated)."
+      LayoutNGBlockFlow (anonymous) at (0,72) size 769x40
+        LayoutBR {BR} at (0,0) size 0x19
+        LayoutBR {BR} at (0,20) size 0x19
+      LayoutBlockFlow {P} at (0,128) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 116x19
+          LayoutRubyRun (anonymous) at (151,12) size 42x20
+            LayoutRubyText {RT} at (0,-12) size 42x12
+              LayoutText {#text} at (0,0) size 42x12
+                text run at (0,0) width 42: "Hyper-text"
+            LayoutRubyBase (anonymous) at (0,0) size 42x20
+              LayoutText {#text} at (10,0) size 22x19
+                text run at (10,0) width 22: "HT"
+          LayoutRubyRun (anonymous) at (193,12) size 70x20
+            LayoutRubyText {RT} at (0,-12) size 70x12
+              LayoutText {#text} at (0,0) size 70x12
+                text run at (0,0) width 70: "Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 70x20
+              LayoutText {#text} at (23,0) size 24x19
+                text run at (23,0) width 24: "ML"
+          LayoutRubyRun (anonymous) at (263,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (271,12) size 36x19
+          text run at (271,12) width 36: " spec."
+      LayoutBlockFlow {P} at (0,176) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 116x19
+          LayoutRubyRun (anonymous) at (151,12) size 42x20
+            LayoutRubyText {RT} at (0,-12) size 42x12
+              LayoutText {#text} at (0,0) size 42x12
+                text run at (0,0) width 42: "Hyper-text"
+            LayoutRubyBase (anonymous) at (0,0) size 42x20
+              LayoutText {#text} at (10,0) size 22x19
+                text run at (10,0) width 22: "HT"
+          LayoutRubyRun (anonymous) at (193,12) size 70x20
+            LayoutRubyText {RT} at (0,-12) size 70x12
+              LayoutText {#text} at (0,0) size 70x12
+                text run at (0,0) width 70: "Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 70x20
+              LayoutText {#text} at (23,0) size 24x19
+                text run at (23,0) width 24: "ML"
+          LayoutRubyRun (anonymous) at (263,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (271,12) size 36x19
+          text run at (271,12) width 36: " spec."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-text2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-text2-expected.txt
new file mode 100644
index 0000000..a00a4e0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-text2-expected.txt
@@ -0,0 +1,53 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutBlockFlow {P} at (0,0) size 784x40
+        LayoutText {#text} at (0,0) size 733x39
+          text run at (0,0) width 733: "The following is a test for DOM manipulation within <ruby>: Inserting a new text before a <rt> element that already"
+          text run at (0,20) width 83: "contains text."
+      LayoutBlockFlow {P} at (0,56) size 784x20
+        LayoutText {#text} at (0,0) size 436x19
+          text run at (0,0) width 436: "Both lines should look identical (the first line is the one manipulated)."
+      LayoutNGBlockFlow (anonymous) at (0,92) size 769x40
+        LayoutBR {BR} at (0,0) size 0x19
+        LayoutBR {BR} at (0,20) size 0x19
+      LayoutBlockFlow {P} at (0,148) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 118x19
+          LayoutRubyRun (anonymous) at (151,12) size 114x20
+            LayoutRubyText {RT} at (0,-12) size 114x12
+              LayoutText {#text} at (0,0) size 114x12
+                text run at (0,0) width 114: "Hyper-text Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 114x20
+              LayoutInline {SPAN} at (0,0) size 22x19
+                LayoutText {#text} at (34,0) size 22x19
+                  text run at (34,0) width 22: "HT"
+              LayoutText {#text} at (56,0) size 24x19
+                text run at (56,0) width 24: "ML"
+          LayoutRubyRun (anonymous) at (265,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (273,12) size 36x19
+          text run at (273,12) width 36: " spec."
+      LayoutBlockFlow {P} at (0,196) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 118x19
+          LayoutRubyRun (anonymous) at (151,12) size 114x20
+            LayoutRubyText {RT} at (0,-12) size 114x12
+              LayoutText {#text} at (0,0) size 114x12
+                text run at (0,0) width 114: "Hyper-text Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 114x20
+              LayoutText {#text} at (34,0) size 46x19
+                text run at (34,0) width 46: "HTML"
+              LayoutInline {SPAN} at (0,0) size 0x19
+          LayoutRubyRun (anonymous) at (265,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (273,12) size 36x19
+          text run at (273,12) width 36: " spec."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-text3-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-text3-expected.txt
new file mode 100644
index 0000000..3be9ee7e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-insert-text3-expected.txt
@@ -0,0 +1,54 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 736x19
+          text run at (0,0) width 736: "The following is a test for DOM manipulation within <ruby>: Inserting a new text before another text of a ruby base."
+      LayoutBlockFlow {P} at (0,36) size 784x20
+        LayoutText {#text} at (0,0) size 436x19
+          text run at (0,0) width 436: "Both lines should look identical (the first line is the one manipulated)."
+      LayoutNGBlockFlow (anonymous) at (0,72) size 769x40
+        LayoutBR {BR} at (0,0) size 0x19
+        LayoutBR {BR} at (0,20) size 0x19
+      LayoutBlockFlow {P} at (0,128) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 118x19
+          LayoutRubyRun (anonymous) at (151,12) size 114x20
+            LayoutRubyText {RT} at (0,-12) size 114x12
+              LayoutText {#text} at (0,0) size 114x12
+                text run at (0,0) width 114: "Hyper-text Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 114x20
+              LayoutText {#text} at (34,0) size 22x19
+                text run at (34,0) width 22: "HT"
+              LayoutInline {SPAN} at (0,0) size 24x19
+                LayoutText {#text} at (56,0) size 24x19
+                  text run at (56,0) width 24: "ML"
+          LayoutRubyRun (anonymous) at (265,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (273,12) size 36x19
+          text run at (273,12) width 36: " spec."
+      LayoutBlockFlow {P} at (0,176) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 118x19
+          LayoutRubyRun (anonymous) at (151,12) size 114x20
+            LayoutRubyText {RT} at (0,-12) size 114x12
+              LayoutText {#text} at (0,0) size 114x12
+                text run at (0,0) width 114: "Hyper-text Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 114x20
+              LayoutText {#text} at (34,0) size 22x19
+                text run at (34,0) width 22: "HT"
+              LayoutInline {SPAN} at (0,0) size 24x19
+                LayoutText {#text} at (56,0) size 24x19
+                  text run at (56,0) width 24: "ML"
+          LayoutRubyRun (anonymous) at (265,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (273,12) size 36x19
+          text run at (273,12) width 36: " spec."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-rt1-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-rt1-expected.txt
new file mode 100644
index 0000000..03bfbbc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-rt1-expected.txt
@@ -0,0 +1,48 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 763x19
+          text run at (0,0) width 763: "The following is a test for DOM manipulation within <ruby>: Removing the last <rt>, leaving the base without ruby text."
+      LayoutBlockFlow {P} at (0,36) size 784x20
+        LayoutText {#text} at (0,0) size 436x19
+          text run at (0,0) width 436: "Both lines should look identical (the first line is the one manipulated)."
+      LayoutNGBlockFlow (anonymous) at (0,72) size 769x40
+        LayoutBR {BR} at (0,0) size 0x19
+        LayoutBR {BR} at (0,20) size 0x19
+      LayoutBlockFlow {P} at (0,128) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 118x19
+          LayoutRubyRun (anonymous) at (151,12) size 114x20
+            LayoutRubyText {RT} at (0,-12) size 114x12
+              LayoutText {#text} at (0,0) size 114x12
+                text run at (0,0) width 114: "Hyper-text Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 114x20
+              LayoutText {#text} at (34,0) size 46x19
+                text run at (34,0) width 46: "HTML"
+          LayoutRubyRun (anonymous) at (265,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (273,12) size 36x19
+          text run at (273,12) width 36: " spec."
+      LayoutBlockFlow {P} at (0,176) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 118x19
+          LayoutRubyRun (anonymous) at (151,12) size 114x20
+            LayoutRubyText {RT} at (0,-12) size 114x12
+              LayoutText {#text} at (0,0) size 114x12
+                text run at (0,0) width 114: "Hyper-text Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 114x20
+              LayoutText {#text} at (34,0) size 46x19
+                text run at (34,0) width 46: "HTML"
+          LayoutRubyRun (anonymous) at (265,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (273,12) size 36x19
+          text run at (273,12) width 36: " spec."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-rt2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-rt2-expected.txt
new file mode 100644
index 0000000..fbcc3f6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-rt2-expected.txt
@@ -0,0 +1,51 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutBlockFlow {P} at (0,0) size 784x40
+        LayoutText {#text} at (0,0) size 764x39
+          text run at (0,0) width 764: "The following is a test for DOM manipulation within <ruby>: Removing a <rt>, forcing a merge with the right neighbour"
+          text run at (0,20) width 21: "run"
+      LayoutBlockFlow {P} at (0,56) size 784x20
+        LayoutText {#text} at (0,0) size 436x19
+          text run at (0,0) width 436: "Both lines should look identical (the first line is the one manipulated)."
+      LayoutNGBlockFlow (anonymous) at (0,92) size 769x40
+        LayoutBR {BR} at (0,0) size 0x19
+        LayoutBR {BR} at (0,20) size 0x19
+      LayoutBlockFlow {P} at (0,148) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 118x19
+          LayoutRubyRun (anonymous) at (151,12) size 114x20
+            LayoutRubyText {RT} at (0,-12) size 114x12
+              LayoutText {#text} at (0,0) size 114x12
+                text run at (0,0) width 114: "Hyper-text Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 114x20
+              LayoutText {#text} at (34,0) size 22x19
+                text run at (34,0) width 22: "HT"
+              LayoutText {#text} at (56,0) size 24x19
+                text run at (56,0) width 24: "ML"
+          LayoutRubyRun (anonymous) at (265,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (273,12) size 36x19
+          text run at (273,12) width 36: " spec."
+      LayoutBlockFlow {P} at (0,196) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 118x19
+          LayoutRubyRun (anonymous) at (151,12) size 114x20
+            LayoutRubyText {RT} at (0,-12) size 114x12
+              LayoutText {#text} at (0,0) size 114x12
+                text run at (0,0) width 114: "Hyper-text Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 114x20
+              LayoutText {#text} at (34,0) size 46x19
+                text run at (34,0) width 46: "HTML"
+          LayoutRubyRun (anonymous) at (265,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (273,12) size 36x19
+          text run at (273,12) width 36: " spec."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-text1-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-text1-expected.txt
new file mode 100644
index 0000000..f511e6f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/ruby/rubyDOM-remove-text1-expected.txt
@@ -0,0 +1,50 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 800x600
+    LayoutBlockFlow {BODY} at (8,8) size 784x576
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 761x19
+          text run at (0,0) width 761: "The following is a test for DOM manipulation within <ruby>: Removing a ruby base object, leaving the base non-empty."
+      LayoutBlockFlow {P} at (0,36) size 784x20
+        LayoutText {#text} at (0,0) size 436x19
+          text run at (0,0) width 436: "Both lines should look identical (the first line is the one manipulated)."
+      LayoutNGBlockFlow (anonymous) at (0,72) size 769x40
+        LayoutBR {BR} at (0,0) size 0x19
+        LayoutBR {BR} at (0,20) size 0x19
+      LayoutBlockFlow {P} at (0,128) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 118x19
+          LayoutRubyRun (anonymous) at (151,12) size 114x20
+            LayoutRubyText {RT} at (0,-12) size 114x12
+              LayoutText {#text} at (0,0) size 114x12
+                text run at (0,0) width 114: "Hyper-text Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 114x20
+              LayoutInline {SPAN} at (0,0) size 46x19
+                LayoutText {#text} at (34,0) size 46x19
+                  text run at (34,0) width 46: "HTML"
+          LayoutRubyRun (anonymous) at (265,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (273,12) size 36x19
+          text run at (273,12) width 36: " spec."
+      LayoutBlockFlow {P} at (0,176) size 784x32
+        LayoutText {#text} at (0,12) size 155x19
+          text run at (0,12) width 155: "<ruby> is defined in the "
+        LayoutRuby (inline) {RUBY} at (0,0) size 118x19
+          LayoutRubyRun (anonymous) at (151,12) size 114x20
+            LayoutRubyText {RT} at (0,-12) size 114x12
+              LayoutText {#text} at (0,0) size 114x12
+                text run at (0,0) width 114: "Hyper-text Markup Language"
+            LayoutRubyBase (anonymous) at (0,0) size 114x20
+              LayoutInline {SPAN} at (0,0) size 46x19
+                LayoutText {#text} at (34,0) size 46x19
+                  text run at (34,0) width 46: "HTML"
+          LayoutRubyRun (anonymous) at (265,12) size 8x20
+            LayoutRubyBase (anonymous) at (0,0) size 8x20
+              LayoutText {#text} at (0,0) size 8x19
+                text run at (0,0) width 8: "5"
+        LayoutText {#text} at (273,12) size 36x19
+          text run at (273,12) width 36: " spec."
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fullscreen/full-screen-line-boxes-crash-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fullscreen/full-screen-line-boxes-crash-expected.txt
deleted file mode 100644
index fe55528d..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fullscreen/full-screen-line-boxes-crash-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-PASS.WebKit didn't crash.
-EVENT(webkitfullscreenchange)
-END OF TEST
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-limited-run-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-limited-run-expected.txt
index 8728486..b9b42c8 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-limited-run-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-limited-run-expected.txt
@@ -12,20 +12,23 @@
 
 =============== Audits run ===============
 bootup-time
-consistently-interactive
 critical-request-chains
 dom-size
+efficient-animated-content
 estimated-input-latency
-first-interactive
+first-contentful-paint
+first-cpu-idle
 first-meaningful-paint
 font-display
-link-blocking-first-paint
+interactive
 mainthread-work-breakdown
+metrics
+network-requests
 offscreen-images
 redirects
+render-blocking-resources
 screenshot-thumbnails
-script-blocking-first-paint
-speed-index-metric
+speed-index
 time-to-first-byte
 total-byte-weight
 unminified-css
@@ -34,8 +37,9 @@
 user-timings
 uses-long-cache-ttl
 uses-optimized-images
+uses-rel-preconnect
 uses-rel-preload
-uses-request-compression
 uses-responsive-images
+uses-text-compression
 uses-webp-images
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-successful-run-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-successful-run-expected.txt
index 0b08b676..4cd7bf54 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-successful-run-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-successful-run-expected.txt
@@ -16,7 +16,6 @@
 Loading page & waiting for onload
 Retrieving trace
 Retrieving devtoolsLog and network records
-Retrieving: URL
 Retrieving: Scripts
 Retrieving: CSSUsage
 Retrieving: Viewport
@@ -44,6 +43,7 @@
 Retrieving: Hreflang
 Retrieving: EmbeddedContent
 Retrieving: Canonical
+Retrieving: RobotsTxt
 Retrieving: Fonts
 Loading page & waiting for onload
 Retrieving devtoolsLog and network records
@@ -62,18 +62,19 @@
 Evaluating: Responds with a 200 when offline
 Evaluating: Has a `<meta name="viewport">` tag with `width` or `initial-scale`
 Evaluating: Contains some content when JavaScript is not available
-Evaluating: First meaningful paint
+Evaluating: First Contentful Paint
+Evaluating: First Meaningful Paint
 Evaluating: Page load is fast enough on 3G
-Evaluating: Perceptual Speed Index
+Evaluating: Speed Index
 Evaluating: Screenshot Thumbnails
 Evaluating: Estimated Input Latency
 Evaluating: No browser errors logged to the console
 Evaluating: Keep server response times low (TTFB)
-Evaluating: First Interactive (beta)
-Evaluating: Consistently Interactive (beta)
+Evaluating: First CPU Idle
+Evaluating: Time to Interactive
 Evaluating: User Timing marks and measures
 Evaluating: Critical Request Chains
-Evaluating: Avoids page redirects
+Evaluating: Avoid multiple page redirects
 Evaluating: User can be prompted to Install the Web App
 Evaluating: Configured for a custom splash screen
 Evaluating: Address bar matches brand colors
@@ -81,10 +82,13 @@
 Evaluating: Content is sized correctly for the viewport
 Evaluating: Displays images with correct aspect ratio
 Evaluating: Avoids deprecated APIs
-Evaluating: Main thread work breakdown
+Evaluating: Minimizes main thread work
 Evaluating: JavaScript boot-up time
 Evaluating: Preload key requests
+Evaluating: Avoid multiple, costly round trips to any origin
 Evaluating: All text remains visible during webfont loads
+Evaluating: Network Requests
+Evaluating: Metrics
 Evaluating: Site works cross-browser
 Evaluating: Page transitions don't feel like they block on the network
 Evaluating: Each page has a URL
@@ -135,26 +139,26 @@
 Evaluating: Visual order on the page follows DOM order
 Evaluating: Uses efficient cache policy on static assets
 Evaluating: Avoids enormous network payloads
-Evaluating: Offscreen images
+Evaluating: Defer offscreen images
+Evaluating: Eliminate render-blocking resources
 Evaluating: Minify CSS
 Evaluating: Minify JavaScript
-Evaluating: Unused CSS rules
+Evaluating: Defer unused CSS
 Evaluating: Serve images in next-gen formats
-Evaluating: Optimize images
+Evaluating: Efficiently encode images
 Evaluating: Enable text compression
 Evaluating: Properly size images
+Evaluating: Use video formats for animated content
 Evaluating: Avoids Application Cache
 Evaluating: Avoids an excessive DOM size
-Evaluating: Opens external anchors using `rel="noopener"`
+Evaluating: Links to cross-origin destinations are safe
 Evaluating: Avoids requesting the geolocation permission on page load
-Evaluating: Reduce render-blocking stylesheets
 Evaluating: Avoids `document.write()`
 Evaluating: Avoids Mutation Events in its own scripts
 Evaluating: Avoids front-end JavaScript libraries with known security vulnerabilities
 Evaluating: Avoids WebSQL DB
 Evaluating: Avoids requesting the notification permission on page load
 Evaluating: Allows users to paste into password fields
-Evaluating: Reduce render-blocking scripts
 Evaluating: Uses HTTP/2 for its own resources
 Evaluating: Uses passive listeners to improve scrolling performance
 Evaluating: Document has a meta description
@@ -162,6 +166,7 @@
 Evaluating: Document uses legible font sizes
 Evaluating: Links have descriptive text
 Evaluating: Page isn’t blocked from indexing
+Evaluating: robots.txt is valid
 Evaluating: Document has a valid `hreflang`
 Evaluating: Document avoids plugins
 Evaluating: Document has a valid `rel=canonical`
@@ -171,7 +176,7 @@
 
 =============== Lighthouse Results ===============
 URL: http://127.0.0.1:8000/devtools/resources/inspected-page.html
-Version: 2.9.1
+Version: 3.0.0-beta.0
 
 
 accesskeys: not-applicable
@@ -189,9 +194,8 @@
 bypass: not-applicable
 canonical: not-applicable
 color-contrast: not-applicable
-consistently-interactive: flaky
 content-width: fail
-critical-request-chains: informative
+critical-request-chains: not-applicable
 custom-controls-labels: manual
 custom-controls-roles: manual
 definition-list: not-applicable
@@ -200,10 +204,12 @@
 document-title: fail
 dom-size: numeric
 duplicate-id: not-applicable
+efficient-animated-content: flaky
 errors-in-console: pass
 estimated-input-latency: flaky
 external-anchors-use-rel-noopener: pass
-first-interactive: flaky
+first-contentful-paint: flaky
+first-cpu-idle: flaky
 first-meaningful-paint: flaky
 focus-traps: manual
 focusable-controls: manual
@@ -219,24 +225,26 @@
 image-alt: not-applicable
 image-aspect-ratio: pass
 input-image-alt: not-applicable
+interactive: flaky
 is-crawlable: pass
 is-on-https: pass
 label: not-applicable
 layout-table: not-applicable
-link-blocking-first-paint: informative
 link-name: not-applicable
 link-text: pass
 list: not-applicable
 listitem: not-applicable
 load-fast-enough-for-pwa: flaky
 logical-tab-order: manual
-mainthread-work-breakdown: informative
+mainthread-work-breakdown: numeric
 managed-focus: manual
 manifest-short-name-length: fail
 meta-description: fail
 meta-refresh: not-applicable
 meta-viewport: not-applicable
+metrics: flaky
 mobile-friendly: manual
+network-requests: informative
 no-document-write: pass
 no-mutation-events: pass
 no-vulnerable-libraries: pass
@@ -252,17 +260,18 @@
 pwa-page-transitions: manual
 redirects: flaky
 redirects-http: fail
+render-blocking-resources: flaky
+robots-txt: not-applicable
 screenshot-thumbnails: flaky
-script-blocking-first-paint: informative
 service-worker: fail
-speed-index-metric: flaky
+speed-index: flaky
 splash-screen: fail
 structured-data: manual
 tabindex: not-applicable
 td-headers-attr: not-applicable
 th-has-data-cells: not-applicable
 themed-omnibox: fail
-time-to-first-byte: informative
+time-to-first-byte: pass
 total-byte-weight: numeric
 unminified-css: flaky
 unminified-javascript: flaky
@@ -273,9 +282,10 @@
 uses-long-cache-ttl: numeric
 uses-optimized-images: flaky
 uses-passive-event-listeners: pass
+uses-rel-preconnect: numeric
 uses-rel-preload: flaky
-uses-request-compression: informative
 uses-responsive-images: flaky
+uses-text-compression: flaky
 uses-webp-images: flaky
 valid-lang: not-applicable
 video-caption: not-applicable
@@ -286,5 +296,5 @@
 without-javascript: fail
 works-offline: fail
 
-# of .lh-audit divs: 109
+# of .lh-audit divs: 110
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-successful-run.js b/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-successful-run.js
index 6838df5..895b3d6 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-successful-run.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/audits2/audits2-successful-run.js
@@ -9,10 +9,10 @@
     // metrics
     'first-contentful-paint',
     'first-meaningful-paint',
-    'first-interactive',
-    'consistently-interactive',
+    'first-cpu-idle',
+    'interactive',
     'estimated-input-latency',
-    'speed-index-metric',
+    'speed-index',
     'metrics',
     'screenshot-thumbnails',
     // misc trace-based audits
@@ -47,7 +47,7 @@
 
   var results = await Audits2TestRunner.waitForResults();
   TestRunner.addResult(`\n=============== Lighthouse Results ===============`);
-  TestRunner.addResult(`URL: ${results.url}`);
+  TestRunner.addResult(`URL: ${results.finalUrl}`);
   TestRunner.addResult(`Version: ${results.lighthouseVersion}`);
   TestRunner.addResult('\n');
 
@@ -56,18 +56,12 @@
 
     if (FLAKY_AUDITS.includes(auditName)) {
       TestRunner.addResult(`${auditName}: flaky`);
-    } else if (audit.notApplicable) {
-      TestRunner.addResult(`${auditName}: not-applicable`);
-    } else if (audit.manual) {
-      TestRunner.addResult(`${auditName}: manual`);
-    } else if (audit.informative) {
-      TestRunner.addResult(`${auditName}: informative`);
-    } else if (audit.error) {
-      TestRunner.addResult(`${auditName}: ERROR ${audit.debugString}`);
-    } else if (audit.scoringMode === 'binary') {
+    } else if (audit.scoreDisplayMode === 'error') {
+      TestRunner.addResult(`${auditName}: ERROR ${audit.errorMessage}`);
+    } else if (audit.scoreDisplayMode === 'binary') {
       TestRunner.addResult(`${auditName}: ${audit.score ? 'pass' : 'fail'}`);
     } else {
-      TestRunner.addResult(`${auditName}: ${audit.scoringMode}`);
+      TestRunner.addResult(`${auditName}: ${audit.scoreDisplayMode}`);
     }
   });
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/htxg/htxg-cert-not-found-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/htxg/htxg-cert-not-found-expected.txt
index b7097fad..561a356 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/htxg/htxg-cert-not-found-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/htxg/htxg-cert-not-found-expected.txt
@@ -9,8 +9,8 @@
   SignedExchangeInfo
     Request URL: https://www.127.0.0.1/not_found_cert.html
     Certificate URL: http://localhost:8000/loading/htxg/resources/not_found_cert.pem.cbor
-    Error: Invalid reponse code: 404
-    Error: Failed to fetch the certificate.
+    Error: {"message":"Invalid reponse code: 404"}
+    Error: {"message":"Failed to fetch the certificate.","signatureIndex":0,"errorField":"signatureCertUrl"}
 * http://localhost:8000/loading/htxg/resources/not_found_cert.pem.cbor
   failed: false
   statusCode: 404
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/htxg/htxg-navigation-expired-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/htxg/htxg-navigation-expired-expected.txt
index 95d6eb69..e2f32ef 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/htxg/htxg-navigation-expired-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/htxg/htxg-navigation-expired-expected.txt
@@ -9,8 +9,8 @@
   SignedExchangeInfo
     Request URL: https://www.127.0.0.1/test.html
     Certificate URL: http://localhost:8000/loading/htxg/resources/127.0.0.1.pem.cbor
-    Error: Invalid timestamp. creation_time: 1522540800, expires_time: 1523145600, verification_time: 1523318460
-    Error: Failed to verify the signed exchange header.
+    Error: {"message":"Invalid timestamp. creation_time: 1522540800, expires_time: 1523145600, verification_time: 1523318460"}
+    Error: {"message":"Failed to verify the signed exchange header.","signatureIndex":0,"errorField":"signatureTimestamps"}
 * http://localhost:8000/loading/htxg/resources/127.0.0.1.pem.cbor
   failed: false
   statusCode: 200
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/htxg/htxg-prefetch-expired-expected.txt b/third_party/WebKit/LayoutTests/http/tests/devtools/htxg/htxg-prefetch-expired-expected.txt
index 3977cba..5c9f3152 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/htxg/htxg-prefetch-expired-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/htxg/htxg-prefetch-expired-expected.txt
@@ -7,8 +7,8 @@
   SignedExchangeInfo
     Request URL: https://www.127.0.0.1/test.html
     Certificate URL: http://localhost:8000/loading/htxg/resources/127.0.0.1.pem.cbor
-    Error: Invalid timestamp. creation_time: 1522540800, expires_time: 1523145600, verification_time: 1523318460
-    Error: Failed to verify the signed exchange header.
+    Error: {"message":"Invalid timestamp. creation_time: 1522540800, expires_time: 1523145600, verification_time: 1523318460"}
+    Error: {"message":"Failed to verify the signed exchange header.","signatureIndex":0,"errorField":"signatureTimestamps"}
 * http://localhost:8000/loading/htxg/resources/127.0.0.1.pem.cbor
   failed: false
   statusCode: 200
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/emulation/emulation-user-agent-override-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/emulation/emulation-user-agent-override-expected.txt
index d7655eef..107a6ca 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/emulation/emulation-user-agent-override-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/emulation/emulation-user-agent-override-expected.txt
@@ -3,5 +3,6 @@
 User-Agent: Test UA
 navigator.language == en-uk
 Accept-Language: en-uk,en
+accept-language: ko
 navigator.platform == new_platform
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/emulation/emulation-user-agent-override.js b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/emulation/emulation-user-agent-override.js
index 968462b1..40f2664 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/emulation/emulation-user-agent-override.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/emulation/emulation-user-agent-override.js
@@ -11,6 +11,9 @@
   testRunner.log('navigator.language == ' + await session.evaluate('navigator.language'));
   await printHeader('Accept-Language');
 
+  // Do not override explicit Accept-Language header.
+  await printHeaderWithLang('Accept-Language');
+
   // Platform
   await dp.Emulation.setUserAgentOverride({userAgent: '', platform: 'new_platform'});
   testRunner.log('navigator.platform == ' + await session.evaluate('navigator.platform'));
@@ -24,5 +27,14 @@
     }
   }
 
+  async function printHeaderWithLang(name) {
+    const url = testRunner.url('resources/echo-headers.php');
+    const headers = await session.evaluateAsync(`fetch("${url}", { headers: {"accept-language": "ko"}}).then(r => r.text())`);
+    for (const header of headers.split('\n')) {
+      if (header.toLowerCase().startsWith(name.toLowerCase()))
+        testRunner.log(header);
+    }
+  }
+
   testRunner.completeTest();
 })
diff --git a/third_party/WebKit/LayoutTests/http/tests/performance-timing/resource_timing_buffer_full_250.html b/third_party/WebKit/LayoutTests/http/tests/performance-timing/resource_timing_buffer_full_250.html
new file mode 100644
index 0000000..53d9ea5e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/performance-timing/resource_timing_buffer_full_250.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8" />
+<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
+<title>This test validates that resource timing implementations have a finite number of entries in their buffer.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+    const t = async_test("Default resource timing entries buffer size");
+    const default_buffer_size = 250;
+    performance.onresourcetimingbufferfull = t.step_func_done(function() {
+        assert_equals(performance.getEntriesByType("resource").length, default_buffer_size);
+    });
+    window.onload = t.step_func(
+        function() {
+            // Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
+            let counter = performance.getEntriesByType("resource").length;
+            function appendScript() {
+                if (counter > default_buffer_size) {
+                    t.step_timeout(function() {
+                        assert_unreached("Got no buffer full event at " + counter + " resources");
+                    }, 100);
+                    return;
+                }
+                const src = "../loading/resources/empty.js?" + counter;
+                const script = document.createElement('script');
+                script.type = 'text/javascript';
+                script.onload = function() { ++counter; appendScript()};
+                script.src = src;
+                document.body.appendChild(script);
+            }
+            appendScript();
+        });
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/platform/linux/printing/avoid-setting-header-offset-on-header-expected.png b/third_party/WebKit/LayoutTests/platform/linux/printing/avoid-setting-header-offset-on-header-expected.png
new file mode 100644
index 0000000..7b5e367
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/printing/avoid-setting-header-offset-on-header-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/printing/avoid-setting-header-offset-on-header-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/printing/avoid-setting-header-offset-on-header-expected.txt
new file mode 100644
index 0000000..0091d8e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/printing/avoid-setting-header-offset-on-header-expected.txt
@@ -0,0 +1,218 @@
+layer at (0,0) size 800x600 scrollHeight 975
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x975 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 1046x975
+    LayoutBlockFlow {BODY} at (0,0) size 1046x975
+      LayoutTable {TABLE} at (0,0) size 174x975
+        LayoutTableSection {THEAD} at (0,0) size 174x44
+          LayoutTableRow {TR} at (0,0) size 174x44
+            LayoutTableCell {TD} at (0,10) size 58x24 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 54x19
+                text run at (2,2) width 54: "header 1"
+            LayoutTableCell {TD} at (58,10) size 58x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 54x19
+                text run at (2,2) width 54: "header 2"
+            LayoutTableCell {TD} at (116,10) size 58x24 [border: (1px solid #000000)] [r=0 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 54x19
+                text run at (2,2) width 54: "header 3"
+        LayoutTableSection {TBODY} at (0,44) size 174x931
+          LayoutTableRow {TR} at (0,0) size 174x44
+            LayoutTableCell {TD} at (0,10) size 58x24 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "1-1"
+            LayoutTableCell {TD} at (58,10) size 58x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "2-1"
+            LayoutTableCell {TD} at (116,10) size 58x24 [border: (1px solid #000000)] [r=0 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "3-1"
+          LayoutTableRow {TR} at (0,44) size 174x44
+            LayoutTableCell {TD} at (0,54) size 58x24 [border: (1px solid #000000)] [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "1-2"
+            LayoutTableCell {TD} at (58,54) size 58x24 [border: (1px solid #000000)] [r=1 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "2-2"
+            LayoutTableCell {TD} at (116,54) size 58x24 [border: (1px solid #000000)] [r=1 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "3-2"
+          LayoutTableRow {TR} at (0,88) size 174x44
+            LayoutTableCell {TD} at (0,98) size 58x24 [border: (1px solid #000000)] [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "1-3"
+            LayoutTableCell {TD} at (58,98) size 58x24 [border: (1px solid #000000)] [r=2 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "2-3"
+            LayoutTableCell {TD} at (116,98) size 58x24 [border: (1px solid #000000)] [r=2 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "3-3"
+          LayoutTableRow {TR} at (0,132) size 174x44
+            LayoutTableCell {TD} at (0,142) size 58x24 [border: (1px solid #000000)] [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "1-4"
+            LayoutTableCell {TD} at (58,142) size 58x24 [border: (1px solid #000000)] [r=3 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "2-4"
+            LayoutTableCell {TD} at (116,142) size 58x24 [border: (1px solid #000000)] [r=3 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "3-4"
+          LayoutTableRow {TR} at (0,176) size 174x44
+            LayoutTableCell {TD} at (0,186) size 58x24 [border: (1px solid #000000)] [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "1-5"
+            LayoutTableCell {TD} at (58,186) size 58x24 [border: (1px solid #000000)] [r=4 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "2-5"
+            LayoutTableCell {TD} at (116,186) size 58x24 [border: (1px solid #000000)] [r=4 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "3-5"
+          LayoutTableRow {TR} at (0,220) size 174x44
+            LayoutTableCell {TD} at (0,230) size 58x24 [border: (1px solid #000000)] [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "1-6"
+            LayoutTableCell {TD} at (58,230) size 58x24 [border: (1px solid #000000)] [r=5 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "2-6"
+            LayoutTableCell {TD} at (116,230) size 58x24 [border: (1px solid #000000)] [r=5 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "3-6"
+          LayoutTableRow {TR} at (0,264) size 174x44
+            LayoutTableCell {TD} at (0,274) size 58x24 [border: (1px solid #000000)] [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "1-7"
+            LayoutTableCell {TD} at (58,274) size 58x24 [border: (1px solid #000000)] [r=6 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "2-7"
+            LayoutTableCell {TD} at (116,274) size 58x24 [border: (1px solid #000000)] [r=6 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "3-7"
+          LayoutTableRow {TR} at (0,308) size 174x44
+            LayoutTableCell {TD} at (0,318) size 58x24 [border: (1px solid #000000)] [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "1-8"
+            LayoutTableCell {TD} at (58,318) size 58x24 [border: (1px solid #000000)] [r=7 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "2-8"
+            LayoutTableCell {TD} at (116,318) size 58x24 [border: (1px solid #000000)] [r=7 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "3-8"
+          LayoutTableRow {TR} at (0,352) size 174x44
+            LayoutTableCell {TD} at (0,362) size 58x24 [border: (1px solid #000000)] [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "1-9"
+            LayoutTableCell {TD} at (58,362) size 58x24 [border: (1px solid #000000)] [r=8 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "2-9"
+            LayoutTableCell {TD} at (116,362) size 58x24 [border: (1px solid #000000)] [r=8 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 21x19
+                text run at (2,2) width 21: "3-9"
+          LayoutTableRow {TR} at (0,396) size 174x44
+            LayoutTableCell {TD} at (0,406) size 58x24 [border: (1px solid #000000)] [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "1-10"
+            LayoutTableCell {TD} at (58,406) size 58x24 [border: (1px solid #000000)] [r=9 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "2-10"
+            LayoutTableCell {TD} at (116,406) size 58x24 [border: (1px solid #000000)] [r=9 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "3-10"
+          LayoutTableRow {TR} at (0,440) size 174x44
+            LayoutTableCell {TD} at (0,450) size 58x24 [border: (1px solid #000000)] [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 28x19
+                text run at (2,2) width 28: "1-11"
+            LayoutTableCell {TD} at (58,450) size 58x24 [border: (1px solid #000000)] [r=10 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 28x19
+                text run at (2,2) width 28: "2-11"
+            LayoutTableCell {TD} at (116,450) size 58x24 [border: (1px solid #000000)] [r=10 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 28x19
+                text run at (2,2) width 28: "3-11"
+          LayoutTableRow {TR} at (0,484) size 174x44
+            LayoutTableCell {TD} at (0,494) size 58x24 [border: (1px solid #000000)] [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "1-12"
+            LayoutTableCell {TD} at (58,494) size 58x24 [border: (1px solid #000000)] [r=11 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "2-12"
+            LayoutTableCell {TD} at (116,494) size 58x24 [border: (1px solid #000000)] [r=11 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "3-12"
+          LayoutTableRow {TR} at (0,528) size 174x44
+            LayoutTableCell {TD} at (0,538) size 58x24 [border: (1px solid #000000)] [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "1-13"
+            LayoutTableCell {TD} at (58,538) size 58x24 [border: (1px solid #000000)] [r=12 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "2-13"
+            LayoutTableCell {TD} at (116,538) size 58x24 [border: (1px solid #000000)] [r=12 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "3-13"
+          LayoutTableRow {TR} at (0,572) size 174x44
+            LayoutTableCell {TD} at (0,582) size 58x24 [border: (1px solid #000000)] [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "1-14"
+            LayoutTableCell {TD} at (58,582) size 58x24 [border: (1px solid #000000)] [r=13 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "2-14"
+            LayoutTableCell {TD} at (116,582) size 58x24 [border: (1px solid #000000)] [r=13 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "3-14"
+          LayoutTableRow {TR} at (0,616) size 174x44
+            LayoutTableCell {TD} at (0,626) size 58x24 [border: (1px solid #000000)] [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "1-15"
+            LayoutTableCell {TD} at (58,626) size 58x24 [border: (1px solid #000000)] [r=14 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "2-15"
+            LayoutTableCell {TD} at (116,626) size 58x24 [border: (1px solid #000000)] [r=14 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "3-15"
+          LayoutTableRow {TR} at (0,660) size 174x44
+            LayoutTableCell {TD} at (0,670) size 58x24 [border: (1px solid #000000)] [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "1-16"
+            LayoutTableCell {TD} at (58,670) size 58x24 [border: (1px solid #000000)] [r=15 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "2-16"
+            LayoutTableCell {TD} at (116,670) size 58x24 [border: (1px solid #000000)] [r=15 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "3-16"
+          LayoutTableRow {TR} at (0,704) size 174x44
+            LayoutTableCell {TD} at (0,714) size 58x24 [border: (1px solid #000000)] [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "1-17"
+            LayoutTableCell {TD} at (58,714) size 58x24 [border: (1px solid #000000)] [r=16 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "2-17"
+            LayoutTableCell {TD} at (116,714) size 58x24 [border: (1px solid #000000)] [r=16 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "3-17"
+          LayoutTableRow {TR} at (0,799) size 174x44
+            LayoutTableCell {TD} at (0,809) size 58x24 [border: (1px solid #000000)] [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "1-18"
+            LayoutTableCell {TD} at (58,809) size 58x24 [border: (1px solid #000000)] [r=17 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "2-18"
+            LayoutTableCell {TD} at (116,809) size 58x24 [border: (1px solid #000000)] [r=17 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "3-18"
+          LayoutTableRow {TR} at (0,843) size 174x44
+            LayoutTableCell {TD} at (0,853) size 58x24 [border: (1px solid #000000)] [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "1-19"
+            LayoutTableCell {TD} at (58,853) size 58x24 [border: (1px solid #000000)] [r=18 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "2-19"
+            LayoutTableCell {TD} at (116,853) size 58x24 [border: (1px solid #000000)] [r=18 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "3-19"
+          LayoutTableRow {TR} at (0,887) size 174x44
+            LayoutTableCell {TD} at (0,897) size 58x24 [border: (1px solid #000000)] [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "1-20"
+            LayoutTableCell {TD} at (58,897) size 58x24 [border: (1px solid #000000)] [r=19 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "2-20"
+            LayoutTableCell {TD} at (116,897) size 58x24 [border: (1px solid #000000)] [r=19 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "3-20"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/printing/thead-tfoot-containing-svg-repeat-expected.png b/third_party/WebKit/LayoutTests/platform/linux/printing/thead-tfoot-containing-svg-repeat-expected.png
new file mode 100644
index 0000000..d87b8b9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/printing/thead-tfoot-containing-svg-repeat-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/printing/thead-tfoot-containing-svg-repeat-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/printing/thead-tfoot-containing-svg-repeat-expected.txt
new file mode 100644
index 0000000..19dad7c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/printing/thead-tfoot-containing-svg-repeat-expected.txt
@@ -0,0 +1,155 @@
+layer at (0,0) size 800x600 scrollHeight 2256
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x2256 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 1046x2256
+    LayoutBlockFlow {BODY} at (8,8) size 1030x2240
+      LayoutBlockFlow (anonymous) at (0,0) size 1030x100
+        LayoutText {#text} at (0,8) size 969x84
+          text run at (0,8) width 969: "Passes if there is a green circle before each \"Header\" and a red circle before each"
+          text run at (0,58) width 110: "\"Footer\"."
+      LayoutTable {TABLE} at (0,100) size 138x2140
+        LayoutTableSection {THEAD} at (0,0) size 138x56
+          LayoutTableRow {TR} at (0,2) size 138x52
+            LayoutTableCell {TH} at (2,2) size 134x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (1,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#008000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (31,8) size 102x35
+                text run at (31,8) width 8: " "
+                text run at (39,8) width 94: "Header"
+        LayoutTableSection {TBODY} at (0,56) size 138x2030
+          LayoutTableRow {TR} at (0,0) size 138x52
+            LayoutTableCell {TD} at (2,0) size 134x52 [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "1"
+          LayoutTableRow {TR} at (0,54) size 138x52
+            LayoutTableCell {TD} at (2,54) size 134x52 [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "2"
+          LayoutTableRow {TR} at (0,108) size 138x52
+            LayoutTableCell {TD} at (2,108) size 134x52 [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "3"
+          LayoutTableRow {TR} at (0,162) size 138x52
+            LayoutTableCell {TD} at (2,162) size 134x52 [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "4"
+          LayoutTableRow {TR} at (0,216) size 138x52
+            LayoutTableCell {TD} at (2,216) size 134x52 [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "5"
+          LayoutTableRow {TR} at (0,270) size 138x52
+            LayoutTableCell {TD} at (2,270) size 134x52 [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "6"
+          LayoutTableRow {TR} at (0,324) size 138x52
+            LayoutTableCell {TD} at (2,324) size 134x52 [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "7"
+          LayoutTableRow {TR} at (0,378) size 138x52
+            LayoutTableCell {TD} at (2,378) size 134x52 [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "8"
+          LayoutTableRow {TR} at (0,432) size 138x52
+            LayoutTableCell {TD} at (2,432) size 134x52 [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "9"
+          LayoutTableRow {TR} at (0,486) size 138x52
+            LayoutTableCell {TD} at (2,486) size 134x52 [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "10"
+          LayoutTableRow {TR} at (0,691) size 138x52
+            LayoutTableCell {TD} at (2,691) size 134x52 [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 29x34
+                text run at (1,9) width 29: "11"
+          LayoutTableRow {TR} at (0,745) size 138x52
+            LayoutTableCell {TD} at (2,745) size 134x52 [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "12"
+          LayoutTableRow {TR} at (0,799) size 138x52
+            LayoutTableCell {TD} at (2,799) size 134x52 [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "13"
+          LayoutTableRow {TR} at (0,853) size 138x52
+            LayoutTableCell {TD} at (2,853) size 134x52 [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "14"
+          LayoutTableRow {TR} at (0,907) size 138x52
+            LayoutTableCell {TD} at (2,907) size 134x52 [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "15"
+          LayoutTableRow {TR} at (0,961) size 138x52
+            LayoutTableCell {TD} at (2,961) size 134x52 [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "16"
+          LayoutTableRow {TR} at (0,1015) size 138x52
+            LayoutTableCell {TD} at (2,1015) size 134x52 [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "17"
+          LayoutTableRow {TR} at (0,1069) size 138x52
+            LayoutTableCell {TD} at (2,1069) size 134x52 [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "18"
+          LayoutTableRow {TR} at (0,1123) size 138x52
+            LayoutTableCell {TD} at (2,1123) size 134x52 [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "19"
+          LayoutTableRow {TR} at (0,1177) size 138x52
+            LayoutTableCell {TD} at (2,1177) size 134x52 [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "20"
+          LayoutTableRow {TR} at (0,1231) size 138x52
+            LayoutTableCell {TD} at (2,1231) size 134x52 [r=20 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "21"
+          LayoutTableRow {TR} at (0,1285) size 138x52
+            LayoutTableCell {TD} at (2,1285) size 134x52 [r=21 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "22"
+          LayoutTableRow {TR} at (0,1490) size 138x52
+            LayoutTableCell {TD} at (2,1490) size 134x52 [r=22 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "23"
+          LayoutTableRow {TR} at (0,1544) size 138x52
+            LayoutTableCell {TD} at (2,1544) size 134x52 [r=23 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "24"
+          LayoutTableRow {TR} at (0,1598) size 138x52
+            LayoutTableCell {TD} at (2,1598) size 134x52 [r=24 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "25"
+          LayoutTableRow {TR} at (0,1652) size 138x52
+            LayoutTableCell {TD} at (2,1652) size 134x52 [r=25 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "26"
+          LayoutTableRow {TR} at (0,1706) size 138x52
+            LayoutTableCell {TD} at (2,1706) size 134x52 [r=26 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "27"
+          LayoutTableRow {TR} at (0,1760) size 138x52
+            LayoutTableCell {TD} at (2,1760) size 134x52 [r=27 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "28"
+          LayoutTableRow {TR} at (0,1814) size 138x52
+            LayoutTableCell {TD} at (2,1814) size 134x52 [r=28 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "29"
+          LayoutTableRow {TR} at (0,1868) size 138x52
+            LayoutTableCell {TD} at (2,1868) size 134x52 [r=29 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "30"
+          LayoutTableRow {TR} at (0,1922) size 138x52
+            LayoutTableCell {TD} at (2,1922) size 134x52 [r=30 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "31"
+          LayoutTableRow {TR} at (0,1976) size 138x52
+            LayoutTableCell {TD} at (2,1976) size 134x52 [r=31 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "32"
+        LayoutTableSection {TFOOT} at (0,2086) size 138x54
+          LayoutTableRow {TR} at (0,0) size 138x52
+            LayoutTableCell {TH} at (2,0) size 134x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (6,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#FF0000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (36,8) size 92x35
+                text run at (36,8) width 8: " "
+                text run at (44,8) width 84: "Footer"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png b/third_party/WebKit/LayoutTests/platform/linux/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
new file mode 100644
index 0000000..c7c9c38
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt
new file mode 100644
index 0000000..1a61e2eb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt
@@ -0,0 +1,157 @@
+layer at (0,0) size 800x600 scrollHeight 4368
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x4368 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 1046x4368
+    LayoutBlockFlow {BODY} at (8,8) size 1030x4352
+      LayoutBlockFlow (anonymous) at (0,0) size 1030x100
+        LayoutText {#text} at (0,8) size 969x84
+          text run at (0,8) width 969: "Passes if there is a green circle before each \"Header\" and a red circle before each"
+          text run at (0,58) width 110: "\"Footer\"."
+      LayoutBlockFlow {DIV} at (0,100) size 1030x1000
+      LayoutTable {TABLE} at (0,1100) size 138x2252
+        LayoutTableSection {THEAD} at (0,0) size 138x56
+          LayoutTableRow {TR} at (0,2) size 138x52
+            LayoutTableCell {TH} at (2,2) size 134x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (1,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#008000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (31,8) size 102x35
+                text run at (31,8) width 8: " "
+                text run at (39,8) width 94: "Header"
+        LayoutTableSection {TBODY} at (0,56) size 138x2142
+          LayoutTableRow {TR} at (0,0) size 138x52
+            LayoutTableCell {TD} at (2,0) size 134x52 [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "1"
+          LayoutTableRow {TR} at (0,54) size 138x52
+            LayoutTableCell {TD} at (2,54) size 134x52 [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "2"
+          LayoutTableRow {TR} at (0,108) size 138x52
+            LayoutTableCell {TD} at (2,108) size 134x52 [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "3"
+          LayoutTableRow {TR} at (0,162) size 138x52
+            LayoutTableCell {TD} at (2,162) size 134x52 [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "4"
+          LayoutTableRow {TR} at (0,216) size 138x52
+            LayoutTableCell {TD} at (2,216) size 134x52 [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "5"
+          LayoutTableRow {TR} at (0,270) size 138x52
+            LayoutTableCell {TD} at (2,270) size 134x52 [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "6"
+          LayoutTableRow {TR} at (0,324) size 138x52
+            LayoutTableCell {TD} at (2,324) size 134x52 [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "7"
+          LayoutTableRow {TR} at (0,490) size 138x52
+            LayoutTableCell {TD} at (2,490) size 134x52 [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "8"
+          LayoutTableRow {TR} at (0,544) size 138x52
+            LayoutTableCell {TD} at (2,544) size 134x52 [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "9"
+          LayoutTableRow {TR} at (0,598) size 138x52
+            LayoutTableCell {TD} at (2,598) size 134x52 [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "10"
+          LayoutTableRow {TR} at (0,652) size 138x52
+            LayoutTableCell {TD} at (2,652) size 134x52 [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 29x34
+                text run at (1,9) width 29: "11"
+          LayoutTableRow {TR} at (0,706) size 138x52
+            LayoutTableCell {TD} at (2,706) size 134x52 [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "12"
+          LayoutTableRow {TR} at (0,760) size 138x52
+            LayoutTableCell {TD} at (2,760) size 134x52 [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "13"
+          LayoutTableRow {TR} at (0,814) size 138x52
+            LayoutTableCell {TD} at (2,814) size 134x52 [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "14"
+          LayoutTableRow {TR} at (0,868) size 138x52
+            LayoutTableCell {TD} at (2,868) size 134x52 [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "15"
+          LayoutTableRow {TR} at (0,922) size 138x52
+            LayoutTableCell {TD} at (2,922) size 134x52 [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "16"
+          LayoutTableRow {TR} at (0,976) size 138x52
+            LayoutTableCell {TD} at (2,976) size 134x52 [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "17"
+          LayoutTableRow {TR} at (0,1030) size 138x52
+            LayoutTableCell {TD} at (2,1030) size 134x52 [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "18"
+          LayoutTableRow {TR} at (0,1084) size 138x52
+            LayoutTableCell {TD} at (2,1084) size 134x52 [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "19"
+          LayoutTableRow {TR} at (0,1289) size 138x52
+            LayoutTableCell {TD} at (2,1289) size 134x52 [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "20"
+          LayoutTableRow {TR} at (0,1343) size 138x52
+            LayoutTableCell {TD} at (2,1343) size 134x52 [r=20 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "21"
+          LayoutTableRow {TR} at (0,1397) size 138x52
+            LayoutTableCell {TD} at (2,1397) size 134x52 [r=21 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "22"
+          LayoutTableRow {TR} at (0,1451) size 138x52
+            LayoutTableCell {TD} at (2,1451) size 134x52 [r=22 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "23"
+          LayoutTableRow {TR} at (0,1505) size 138x52
+            LayoutTableCell {TD} at (2,1505) size 134x52 [r=23 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "24"
+          LayoutTableRow {TR} at (0,1559) size 138x52
+            LayoutTableCell {TD} at (2,1559) size 134x52 [r=24 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "25"
+          LayoutTableRow {TR} at (0,1613) size 138x52
+            LayoutTableCell {TD} at (2,1613) size 134x52 [r=25 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "26"
+          LayoutTableRow {TR} at (0,1667) size 138x52
+            LayoutTableCell {TD} at (2,1667) size 134x52 [r=26 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "27"
+          LayoutTableRow {TR} at (0,1721) size 138x52
+            LayoutTableCell {TD} at (2,1721) size 134x52 [r=27 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "28"
+          LayoutTableRow {TR} at (0,1775) size 138x52
+            LayoutTableCell {TD} at (2,1775) size 134x52 [r=28 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "29"
+          LayoutTableRow {TR} at (0,1829) size 138x52
+            LayoutTableCell {TD} at (2,1829) size 134x52 [r=29 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "30"
+          LayoutTableRow {TR} at (0,1883) size 138x52
+            LayoutTableCell {TD} at (2,1883) size 134x52 [r=30 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "31"
+          LayoutTableRow {TR} at (0,2088) size 138x52
+            LayoutTableCell {TD} at (2,2088) size 134x52 [r=31 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "32"
+        LayoutTableSection {TFOOT} at (0,2198) size 138x54
+          LayoutTableRow {TR} at (0,0) size 138x52
+            LayoutTableCell {TH} at (2,0) size 134x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (6,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#FF0000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (36,8) size 92x35
+                text run at (36,8) width 8: " "
+                text run at (44,8) width 84: "Footer"
+      LayoutBlockFlow {DIV} at (0,3352) size 1030x1000
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.png
new file mode 100644
index 0000000..7b5e367
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.txt
new file mode 100644
index 0000000..c51b213
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.txt
@@ -0,0 +1,218 @@
+layer at (0,0) size 800x600 scrollHeight 975
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x975 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutNGBlockFlow {HTML} at (0,0) size 1046x975
+    LayoutNGBlockFlow {BODY} at (0,0) size 1046x975
+      LayoutTable {TABLE} at (0,0) size 174x975
+        LayoutTableSection {THEAD} at (0,0) size 174x44
+          LayoutTableRow {TR} at (0,0) size 174x44
+            LayoutNGTableCell {TD} at (0,10) size 58x24 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 54x19
+                text run at (2,12) width 54: "header 1"
+            LayoutNGTableCell {TD} at (58,10) size 58x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 54x19
+                text run at (2,12) width 54: "header 2"
+            LayoutNGTableCell {TD} at (116,10) size 58x24 [border: (1px solid #000000)] [r=0 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 54x19
+                text run at (2,12) width 54: "header 3"
+        LayoutTableSection {TBODY} at (0,44) size 174x931
+          LayoutTableRow {TR} at (0,0) size 174x44
+            LayoutNGTableCell {TD} at (0,10) size 58x24 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "1-1"
+            LayoutNGTableCell {TD} at (58,10) size 58x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "2-1"
+            LayoutNGTableCell {TD} at (116,10) size 58x24 [border: (1px solid #000000)] [r=0 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "3-1"
+          LayoutTableRow {TR} at (0,44) size 174x44
+            LayoutNGTableCell {TD} at (0,54) size 58x24 [border: (1px solid #000000)] [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "1-2"
+            LayoutNGTableCell {TD} at (58,54) size 58x24 [border: (1px solid #000000)] [r=1 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "2-2"
+            LayoutNGTableCell {TD} at (116,54) size 58x24 [border: (1px solid #000000)] [r=1 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "3-2"
+          LayoutTableRow {TR} at (0,88) size 174x44
+            LayoutNGTableCell {TD} at (0,98) size 58x24 [border: (1px solid #000000)] [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "1-3"
+            LayoutNGTableCell {TD} at (58,98) size 58x24 [border: (1px solid #000000)] [r=2 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "2-3"
+            LayoutNGTableCell {TD} at (116,98) size 58x24 [border: (1px solid #000000)] [r=2 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "3-3"
+          LayoutTableRow {TR} at (0,132) size 174x44
+            LayoutNGTableCell {TD} at (0,142) size 58x24 [border: (1px solid #000000)] [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "1-4"
+            LayoutNGTableCell {TD} at (58,142) size 58x24 [border: (1px solid #000000)] [r=3 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "2-4"
+            LayoutNGTableCell {TD} at (116,142) size 58x24 [border: (1px solid #000000)] [r=3 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "3-4"
+          LayoutTableRow {TR} at (0,176) size 174x44
+            LayoutNGTableCell {TD} at (0,186) size 58x24 [border: (1px solid #000000)] [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "1-5"
+            LayoutNGTableCell {TD} at (58,186) size 58x24 [border: (1px solid #000000)] [r=4 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "2-5"
+            LayoutNGTableCell {TD} at (116,186) size 58x24 [border: (1px solid #000000)] [r=4 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "3-5"
+          LayoutTableRow {TR} at (0,220) size 174x44
+            LayoutNGTableCell {TD} at (0,230) size 58x24 [border: (1px solid #000000)] [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "1-6"
+            LayoutNGTableCell {TD} at (58,230) size 58x24 [border: (1px solid #000000)] [r=5 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "2-6"
+            LayoutNGTableCell {TD} at (116,230) size 58x24 [border: (1px solid #000000)] [r=5 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "3-6"
+          LayoutTableRow {TR} at (0,264) size 174x44
+            LayoutNGTableCell {TD} at (0,274) size 58x24 [border: (1px solid #000000)] [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "1-7"
+            LayoutNGTableCell {TD} at (58,274) size 58x24 [border: (1px solid #000000)] [r=6 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "2-7"
+            LayoutNGTableCell {TD} at (116,274) size 58x24 [border: (1px solid #000000)] [r=6 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "3-7"
+          LayoutTableRow {TR} at (0,308) size 174x44
+            LayoutNGTableCell {TD} at (0,318) size 58x24 [border: (1px solid #000000)] [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "1-8"
+            LayoutNGTableCell {TD} at (58,318) size 58x24 [border: (1px solid #000000)] [r=7 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "2-8"
+            LayoutNGTableCell {TD} at (116,318) size 58x24 [border: (1px solid #000000)] [r=7 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "3-8"
+          LayoutTableRow {TR} at (0,352) size 174x44
+            LayoutNGTableCell {TD} at (0,362) size 58x24 [border: (1px solid #000000)] [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "1-9"
+            LayoutNGTableCell {TD} at (58,362) size 58x24 [border: (1px solid #000000)] [r=8 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "2-9"
+            LayoutNGTableCell {TD} at (116,362) size 58x24 [border: (1px solid #000000)] [r=8 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 21x19
+                text run at (2,12) width 21: "3-9"
+          LayoutTableRow {TR} at (0,396) size 174x44
+            LayoutNGTableCell {TD} at (0,406) size 58x24 [border: (1px solid #000000)] [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "1-10"
+            LayoutNGTableCell {TD} at (58,406) size 58x24 [border: (1px solid #000000)] [r=9 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "2-10"
+            LayoutNGTableCell {TD} at (116,406) size 58x24 [border: (1px solid #000000)] [r=9 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "3-10"
+          LayoutTableRow {TR} at (0,440) size 174x44
+            LayoutNGTableCell {TD} at (0,450) size 58x24 [border: (1px solid #000000)] [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 28x19
+                text run at (2,12) width 28: "1-11"
+            LayoutNGTableCell {TD} at (58,450) size 58x24 [border: (1px solid #000000)] [r=10 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 28x19
+                text run at (2,12) width 28: "2-11"
+            LayoutNGTableCell {TD} at (116,450) size 58x24 [border: (1px solid #000000)] [r=10 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 28x19
+                text run at (2,12) width 28: "3-11"
+          LayoutTableRow {TR} at (0,484) size 174x44
+            LayoutNGTableCell {TD} at (0,494) size 58x24 [border: (1px solid #000000)] [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "1-12"
+            LayoutNGTableCell {TD} at (58,494) size 58x24 [border: (1px solid #000000)] [r=11 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "2-12"
+            LayoutNGTableCell {TD} at (116,494) size 58x24 [border: (1px solid #000000)] [r=11 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "3-12"
+          LayoutTableRow {TR} at (0,528) size 174x44
+            LayoutNGTableCell {TD} at (0,538) size 58x24 [border: (1px solid #000000)] [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "1-13"
+            LayoutNGTableCell {TD} at (58,538) size 58x24 [border: (1px solid #000000)] [r=12 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "2-13"
+            LayoutNGTableCell {TD} at (116,538) size 58x24 [border: (1px solid #000000)] [r=12 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "3-13"
+          LayoutTableRow {TR} at (0,572) size 174x44
+            LayoutNGTableCell {TD} at (0,582) size 58x24 [border: (1px solid #000000)] [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "1-14"
+            LayoutNGTableCell {TD} at (58,582) size 58x24 [border: (1px solid #000000)] [r=13 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "2-14"
+            LayoutNGTableCell {TD} at (116,582) size 58x24 [border: (1px solid #000000)] [r=13 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "3-14"
+          LayoutTableRow {TR} at (0,616) size 174x44
+            LayoutNGTableCell {TD} at (0,626) size 58x24 [border: (1px solid #000000)] [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "1-15"
+            LayoutNGTableCell {TD} at (58,626) size 58x24 [border: (1px solid #000000)] [r=14 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "2-15"
+            LayoutNGTableCell {TD} at (116,626) size 58x24 [border: (1px solid #000000)] [r=14 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "3-15"
+          LayoutTableRow {TR} at (0,660) size 174x44
+            LayoutNGTableCell {TD} at (0,670) size 58x24 [border: (1px solid #000000)] [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "1-16"
+            LayoutNGTableCell {TD} at (58,670) size 58x24 [border: (1px solid #000000)] [r=15 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "2-16"
+            LayoutNGTableCell {TD} at (116,670) size 58x24 [border: (1px solid #000000)] [r=15 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "3-16"
+          LayoutTableRow {TR} at (0,704) size 174x44
+            LayoutNGTableCell {TD} at (0,714) size 58x24 [border: (1px solid #000000)] [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "1-17"
+            LayoutNGTableCell {TD} at (58,714) size 58x24 [border: (1px solid #000000)] [r=16 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "2-17"
+            LayoutNGTableCell {TD} at (116,714) size 58x24 [border: (1px solid #000000)] [r=16 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "3-17"
+          LayoutTableRow {TR} at (0,799) size 174x44
+            LayoutNGTableCell {TD} at (0,809) size 58x24 [border: (1px solid #000000)] [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "1-18"
+            LayoutNGTableCell {TD} at (58,809) size 58x24 [border: (1px solid #000000)] [r=17 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "2-18"
+            LayoutNGTableCell {TD} at (116,809) size 58x24 [border: (1px solid #000000)] [r=17 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "3-18"
+          LayoutTableRow {TR} at (0,843) size 174x44
+            LayoutNGTableCell {TD} at (0,853) size 58x24 [border: (1px solid #000000)] [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "1-19"
+            LayoutNGTableCell {TD} at (58,853) size 58x24 [border: (1px solid #000000)] [r=18 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "2-19"
+            LayoutNGTableCell {TD} at (116,853) size 58x24 [border: (1px solid #000000)] [r=18 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "3-19"
+          LayoutTableRow {TR} at (0,887) size 174x44
+            LayoutNGTableCell {TD} at (0,897) size 58x24 [border: (1px solid #000000)] [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "1-20"
+            LayoutNGTableCell {TD} at (58,897) size 58x24 [border: (1px solid #000000)] [r=19 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "2-20"
+            LayoutNGTableCell {TD} at (116,897) size 58x24 [border: (1px solid #000000)] [r=19 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "3-20"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png
new file mode 100644
index 0000000..024c399
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.txt
new file mode 100644
index 0000000..b1a8f9d5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.txt
@@ -0,0 +1,153 @@
+layer at (0,0) size 800x600 scrollHeight 2264
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x2264 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutNGBlockFlow {HTML} at (0,0) size 1046x2264
+    LayoutNGBlockFlow {BODY} at (8,8) size 1030x2248
+      LayoutNGBlockFlow (anonymous) at (0,0) size 1030x100
+        LayoutText {#text} at (0,8) size 969x84
+          text run at (0,8) width 969: "Passes if there is a green circle before each \"Header\" and a red circle before each"
+          text run at (0,58) width 110: "\"Footer\"."
+      LayoutTable {TABLE} at (0,100) size 138x2148
+        LayoutTableSection {THEAD} at (0,0) size 138x56
+          LayoutTableRow {TR} at (0,2) size 138x52
+            LayoutNGTableCell {TH} at (2,2) size 134x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (1,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#008000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (31,8) size 102x35
+                text run at (31,8) width 102: " Header"
+        LayoutTableSection {TBODY} at (0,56) size 138x2038
+          LayoutTableRow {TR} at (0,0) size 138x52
+            LayoutNGTableCell {TD} at (2,0) size 134x52 [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "1"
+          LayoutTableRow {TR} at (0,54) size 138x52
+            LayoutNGTableCell {TD} at (2,54) size 134x52 [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "2"
+          LayoutTableRow {TR} at (0,108) size 138x52
+            LayoutNGTableCell {TD} at (2,108) size 134x52 [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "3"
+          LayoutTableRow {TR} at (0,162) size 138x52
+            LayoutNGTableCell {TD} at (2,162) size 134x52 [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "4"
+          LayoutTableRow {TR} at (0,216) size 138x52
+            LayoutNGTableCell {TD} at (2,216) size 134x52 [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "5"
+          LayoutTableRow {TR} at (0,270) size 138x52
+            LayoutNGTableCell {TD} at (2,270) size 134x52 [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "6"
+          LayoutTableRow {TR} at (0,324) size 138x52
+            LayoutNGTableCell {TD} at (2,324) size 134x52 [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "7"
+          LayoutTableRow {TR} at (0,378) size 138x52
+            LayoutNGTableCell {TD} at (2,378) size 134x52 [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "8"
+          LayoutTableRow {TR} at (0,432) size 138x52
+            LayoutNGTableCell {TD} at (2,432) size 134x52 [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "9"
+          LayoutTableRow {TR} at (0,486) size 138x52
+            LayoutNGTableCell {TD} at (2,486) size 134x52 [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "10"
+          LayoutTableRow {TR} at (0,699) size 138x52
+            LayoutNGTableCell {TD} at (2,699) size 134x52 [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 29x34
+                text run at (1,9) width 29: "11"
+          LayoutTableRow {TR} at (0,753) size 138x52
+            LayoutNGTableCell {TD} at (2,753) size 134x52 [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "12"
+          LayoutTableRow {TR} at (0,807) size 138x52
+            LayoutNGTableCell {TD} at (2,807) size 134x52 [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "13"
+          LayoutTableRow {TR} at (0,861) size 138x52
+            LayoutNGTableCell {TD} at (2,861) size 134x52 [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "14"
+          LayoutTableRow {TR} at (0,915) size 138x52
+            LayoutNGTableCell {TD} at (2,915) size 134x52 [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "15"
+          LayoutTableRow {TR} at (0,969) size 138x52
+            LayoutNGTableCell {TD} at (2,969) size 134x52 [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "16"
+          LayoutTableRow {TR} at (0,1023) size 138x52
+            LayoutNGTableCell {TD} at (2,1023) size 134x52 [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "17"
+          LayoutTableRow {TR} at (0,1077) size 138x52
+            LayoutNGTableCell {TD} at (2,1077) size 134x52 [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "18"
+          LayoutTableRow {TR} at (0,1131) size 138x52
+            LayoutNGTableCell {TD} at (2,1131) size 134x52 [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "19"
+          LayoutTableRow {TR} at (0,1185) size 138x52
+            LayoutNGTableCell {TD} at (2,1185) size 134x52 [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "20"
+          LayoutTableRow {TR} at (0,1239) size 138x52
+            LayoutNGTableCell {TD} at (2,1239) size 134x52 [r=20 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "21"
+          LayoutTableRow {TR} at (0,1293) size 138x52
+            LayoutNGTableCell {TD} at (2,1293) size 134x52 [r=21 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "22"
+          LayoutTableRow {TR} at (0,1498) size 138x52
+            LayoutNGTableCell {TD} at (2,1498) size 134x52 [r=22 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "23"
+          LayoutTableRow {TR} at (0,1552) size 138x52
+            LayoutNGTableCell {TD} at (2,1552) size 134x52 [r=23 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "24"
+          LayoutTableRow {TR} at (0,1606) size 138x52
+            LayoutNGTableCell {TD} at (2,1606) size 134x52 [r=24 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "25"
+          LayoutTableRow {TR} at (0,1660) size 138x52
+            LayoutNGTableCell {TD} at (2,1660) size 134x52 [r=25 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "26"
+          LayoutTableRow {TR} at (0,1714) size 138x52
+            LayoutNGTableCell {TD} at (2,1714) size 134x52 [r=26 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "27"
+          LayoutTableRow {TR} at (0,1768) size 138x52
+            LayoutNGTableCell {TD} at (2,1768) size 134x52 [r=27 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "28"
+          LayoutTableRow {TR} at (0,1822) size 138x52
+            LayoutNGTableCell {TD} at (2,1822) size 134x52 [r=28 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "29"
+          LayoutTableRow {TR} at (0,1876) size 138x52
+            LayoutNGTableCell {TD} at (2,1876) size 134x52 [r=29 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "30"
+          LayoutTableRow {TR} at (0,1930) size 138x52
+            LayoutNGTableCell {TD} at (2,1930) size 134x52 [r=30 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "31"
+          LayoutTableRow {TR} at (0,1984) size 138x52
+            LayoutNGTableCell {TD} at (2,1984) size 134x52 [r=31 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "32"
+        LayoutTableSection {TFOOT} at (0,2094) size 138x54
+          LayoutTableRow {TR} at (0,0) size 138x52
+            LayoutNGTableCell {TH} at (2,0) size 134x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (6.50,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#FF0000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (36,8) size 92x35
+                text run at (36,8) width 92: " Footer"
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
new file mode 100644
index 0000000..4085344
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt
new file mode 100644
index 0000000..67655a6e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt
@@ -0,0 +1,155 @@
+layer at (0,0) size 800x600 scrollHeight 4376
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x4376 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutNGBlockFlow {HTML} at (0,0) size 1046x4376
+    LayoutNGBlockFlow {BODY} at (8,8) size 1030x4360
+      LayoutNGBlockFlow (anonymous) at (0,0) size 1030x100
+        LayoutText {#text} at (0,8) size 969x84
+          text run at (0,8) width 969: "Passes if there is a green circle before each \"Header\" and a red circle before each"
+          text run at (0,58) width 110: "\"Footer\"."
+      LayoutNGBlockFlow {DIV} at (0,100) size 1030x1000
+      LayoutTable {TABLE} at (0,1100) size 138x2260
+        LayoutTableSection {THEAD} at (0,0) size 138x56
+          LayoutTableRow {TR} at (0,2) size 138x52
+            LayoutNGTableCell {TH} at (2,2) size 134x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (1,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#008000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (31,8) size 102x35
+                text run at (31,8) width 102: " Header"
+        LayoutTableSection {TBODY} at (0,56) size 138x2150
+          LayoutTableRow {TR} at (0,0) size 138x52
+            LayoutNGTableCell {TD} at (2,0) size 134x52 [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "1"
+          LayoutTableRow {TR} at (0,54) size 138x52
+            LayoutNGTableCell {TD} at (2,54) size 134x52 [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "2"
+          LayoutTableRow {TR} at (0,108) size 138x52
+            LayoutNGTableCell {TD} at (2,108) size 134x52 [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "3"
+          LayoutTableRow {TR} at (0,162) size 138x52
+            LayoutNGTableCell {TD} at (2,162) size 134x52 [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "4"
+          LayoutTableRow {TR} at (0,216) size 138x52
+            LayoutNGTableCell {TD} at (2,216) size 134x52 [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "5"
+          LayoutTableRow {TR} at (0,270) size 138x52
+            LayoutNGTableCell {TD} at (2,270) size 134x52 [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "6"
+          LayoutTableRow {TR} at (0,324) size 138x52
+            LayoutNGTableCell {TD} at (2,324) size 134x52 [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "7"
+          LayoutTableRow {TR} at (0,498) size 138x52
+            LayoutNGTableCell {TD} at (2,498) size 134x52 [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "8"
+          LayoutTableRow {TR} at (0,552) size 138x52
+            LayoutNGTableCell {TD} at (2,552) size 134x52 [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "9"
+          LayoutTableRow {TR} at (0,606) size 138x52
+            LayoutNGTableCell {TD} at (2,606) size 134x52 [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "10"
+          LayoutTableRow {TR} at (0,660) size 138x52
+            LayoutNGTableCell {TD} at (2,660) size 134x52 [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 29x34
+                text run at (1,9) width 29: "11"
+          LayoutTableRow {TR} at (0,714) size 138x52
+            LayoutNGTableCell {TD} at (2,714) size 134x52 [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "12"
+          LayoutTableRow {TR} at (0,768) size 138x52
+            LayoutNGTableCell {TD} at (2,768) size 134x52 [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "13"
+          LayoutTableRow {TR} at (0,822) size 138x52
+            LayoutNGTableCell {TD} at (2,822) size 134x52 [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "14"
+          LayoutTableRow {TR} at (0,876) size 138x52
+            LayoutNGTableCell {TD} at (2,876) size 134x52 [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "15"
+          LayoutTableRow {TR} at (0,930) size 138x52
+            LayoutNGTableCell {TD} at (2,930) size 134x52 [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "16"
+          LayoutTableRow {TR} at (0,984) size 138x52
+            LayoutNGTableCell {TD} at (2,984) size 134x52 [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "17"
+          LayoutTableRow {TR} at (0,1038) size 138x52
+            LayoutNGTableCell {TD} at (2,1038) size 134x52 [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "18"
+          LayoutTableRow {TR} at (0,1092) size 138x52
+            LayoutNGTableCell {TD} at (2,1092) size 134x52 [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "19"
+          LayoutTableRow {TR} at (0,1297) size 138x52
+            LayoutNGTableCell {TD} at (2,1297) size 134x52 [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "20"
+          LayoutTableRow {TR} at (0,1351) size 138x52
+            LayoutNGTableCell {TD} at (2,1351) size 134x52 [r=20 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "21"
+          LayoutTableRow {TR} at (0,1405) size 138x52
+            LayoutNGTableCell {TD} at (2,1405) size 134x52 [r=21 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "22"
+          LayoutTableRow {TR} at (0,1459) size 138x52
+            LayoutNGTableCell {TD} at (2,1459) size 134x52 [r=22 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "23"
+          LayoutTableRow {TR} at (0,1513) size 138x52
+            LayoutNGTableCell {TD} at (2,1513) size 134x52 [r=23 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "24"
+          LayoutTableRow {TR} at (0,1567) size 138x52
+            LayoutNGTableCell {TD} at (2,1567) size 134x52 [r=24 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "25"
+          LayoutTableRow {TR} at (0,1621) size 138x52
+            LayoutNGTableCell {TD} at (2,1621) size 134x52 [r=25 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "26"
+          LayoutTableRow {TR} at (0,1675) size 138x52
+            LayoutNGTableCell {TD} at (2,1675) size 134x52 [r=26 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "27"
+          LayoutTableRow {TR} at (0,1729) size 138x52
+            LayoutNGTableCell {TD} at (2,1729) size 134x52 [r=27 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "28"
+          LayoutTableRow {TR} at (0,1783) size 138x52
+            LayoutNGTableCell {TD} at (2,1783) size 134x52 [r=28 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "29"
+          LayoutTableRow {TR} at (0,1837) size 138x52
+            LayoutNGTableCell {TD} at (2,1837) size 134x52 [r=29 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "30"
+          LayoutTableRow {TR} at (0,1891) size 138x52
+            LayoutNGTableCell {TD} at (2,1891) size 134x52 [r=30 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "31"
+          LayoutTableRow {TR} at (0,2096) size 138x52
+            LayoutNGTableCell {TD} at (2,2096) size 134x52 [r=31 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "32"
+        LayoutTableSection {TFOOT} at (0,2206) size 138x54
+          LayoutTableRow {TR} at (0,0) size 138x52
+            LayoutNGTableCell {TH} at (2,0) size 134x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (6.50,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#FF0000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (36,8) size 92x35
+                text run at (36,8) width 92: " Footer"
+      LayoutNGBlockFlow {DIV} at (0,3360) size 1030x1000
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/printing/avoid-setting-header-offset-on-header-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/printing/avoid-setting-header-offset-on-header-expected.png
new file mode 100644
index 0000000..85f4b25
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/printing/avoid-setting-header-offset-on-header-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.png
new file mode 100644
index 0000000..85f4b25
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/printing/avoid-setting-header-offset-on-header-expected.png b/third_party/WebKit/LayoutTests/platform/mac/printing/avoid-setting-header-offset-on-header-expected.png
new file mode 100644
index 0000000..75c9158f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/printing/avoid-setting-header-offset-on-header-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/printing/avoid-setting-header-offset-on-header-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/printing/avoid-setting-header-offset-on-header-expected.txt
new file mode 100644
index 0000000..925ffae
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/printing/avoid-setting-header-offset-on-header-expected.txt
@@ -0,0 +1,218 @@
+layer at (0,0) size 800x600 scrollHeight 975
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x975 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 1046x975
+    LayoutBlockFlow {BODY} at (0,0) size 1046x975
+      LayoutTable {TABLE} at (0,0) size 177x975
+        LayoutTableSection {THEAD} at (0,0) size 177x44
+          LayoutTableRow {TR} at (0,0) size 177x44
+            LayoutTableCell {TD} at (0,11) size 59x22 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 55x18
+                text run at (2,2) width 55: "header 1"
+            LayoutTableCell {TD} at (59,11) size 59x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 55x18
+                text run at (2,2) width 55: "header 2"
+            LayoutTableCell {TD} at (118,11) size 59x22 [border: (1px solid #000000)] [r=0 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 55x18
+                text run at (2,2) width 55: "header 3"
+        LayoutTableSection {TBODY} at (0,44) size 177x931
+          LayoutTableRow {TR} at (0,0) size 177x44
+            LayoutTableCell {TD} at (0,11) size 59x22 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "1-1"
+            LayoutTableCell {TD} at (59,11) size 59x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "2-1"
+            LayoutTableCell {TD} at (118,11) size 59x22 [border: (1px solid #000000)] [r=0 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "3-1"
+          LayoutTableRow {TR} at (0,44) size 177x44
+            LayoutTableCell {TD} at (0,55) size 59x22 [border: (1px solid #000000)] [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "1-2"
+            LayoutTableCell {TD} at (59,55) size 59x22 [border: (1px solid #000000)] [r=1 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "2-2"
+            LayoutTableCell {TD} at (118,55) size 59x22 [border: (1px solid #000000)] [r=1 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "3-2"
+          LayoutTableRow {TR} at (0,88) size 177x44
+            LayoutTableCell {TD} at (0,99) size 59x22 [border: (1px solid #000000)] [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "1-3"
+            LayoutTableCell {TD} at (59,99) size 59x22 [border: (1px solid #000000)] [r=2 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "2-3"
+            LayoutTableCell {TD} at (118,99) size 59x22 [border: (1px solid #000000)] [r=2 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "3-3"
+          LayoutTableRow {TR} at (0,132) size 177x44
+            LayoutTableCell {TD} at (0,143) size 59x22 [border: (1px solid #000000)] [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "1-4"
+            LayoutTableCell {TD} at (59,143) size 59x22 [border: (1px solid #000000)] [r=3 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "2-4"
+            LayoutTableCell {TD} at (118,143) size 59x22 [border: (1px solid #000000)] [r=3 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "3-4"
+          LayoutTableRow {TR} at (0,176) size 177x44
+            LayoutTableCell {TD} at (0,187) size 59x22 [border: (1px solid #000000)] [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "1-5"
+            LayoutTableCell {TD} at (59,187) size 59x22 [border: (1px solid #000000)] [r=4 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "2-5"
+            LayoutTableCell {TD} at (118,187) size 59x22 [border: (1px solid #000000)] [r=4 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "3-5"
+          LayoutTableRow {TR} at (0,220) size 177x44
+            LayoutTableCell {TD} at (0,231) size 59x22 [border: (1px solid #000000)] [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "1-6"
+            LayoutTableCell {TD} at (59,231) size 59x22 [border: (1px solid #000000)] [r=5 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "2-6"
+            LayoutTableCell {TD} at (118,231) size 59x22 [border: (1px solid #000000)] [r=5 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "3-6"
+          LayoutTableRow {TR} at (0,264) size 177x44
+            LayoutTableCell {TD} at (0,275) size 59x22 [border: (1px solid #000000)] [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "1-7"
+            LayoutTableCell {TD} at (59,275) size 59x22 [border: (1px solid #000000)] [r=6 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "2-7"
+            LayoutTableCell {TD} at (118,275) size 59x22 [border: (1px solid #000000)] [r=6 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "3-7"
+          LayoutTableRow {TR} at (0,308) size 177x44
+            LayoutTableCell {TD} at (0,319) size 59x22 [border: (1px solid #000000)] [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "1-8"
+            LayoutTableCell {TD} at (59,319) size 59x22 [border: (1px solid #000000)] [r=7 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "2-8"
+            LayoutTableCell {TD} at (118,319) size 59x22 [border: (1px solid #000000)] [r=7 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "3-8"
+          LayoutTableRow {TR} at (0,352) size 177x44
+            LayoutTableCell {TD} at (0,363) size 59x22 [border: (1px solid #000000)] [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "1-9"
+            LayoutTableCell {TD} at (59,363) size 59x22 [border: (1px solid #000000)] [r=8 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "2-9"
+            LayoutTableCell {TD} at (118,363) size 59x22 [border: (1px solid #000000)] [r=8 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x18
+                text run at (2,2) width 22: "3-9"
+          LayoutTableRow {TR} at (0,396) size 177x44
+            LayoutTableCell {TD} at (0,407) size 59x22 [border: (1px solid #000000)] [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "1-10"
+            LayoutTableCell {TD} at (59,407) size 59x22 [border: (1px solid #000000)] [r=9 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "2-10"
+            LayoutTableCell {TD} at (118,407) size 59x22 [border: (1px solid #000000)] [r=9 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "3-10"
+          LayoutTableRow {TR} at (0,440) size 177x44
+            LayoutTableCell {TD} at (0,451) size 59x22 [border: (1px solid #000000)] [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x18
+                text run at (2,2) width 29: "1-11"
+            LayoutTableCell {TD} at (59,451) size 59x22 [border: (1px solid #000000)] [r=10 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x18
+                text run at (2,2) width 29: "2-11"
+            LayoutTableCell {TD} at (118,451) size 59x22 [border: (1px solid #000000)] [r=10 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x18
+                text run at (2,2) width 29: "3-11"
+          LayoutTableRow {TR} at (0,484) size 177x44
+            LayoutTableCell {TD} at (0,495) size 59x22 [border: (1px solid #000000)] [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "1-12"
+            LayoutTableCell {TD} at (59,495) size 59x22 [border: (1px solid #000000)] [r=11 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "2-12"
+            LayoutTableCell {TD} at (118,495) size 59x22 [border: (1px solid #000000)] [r=11 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "3-12"
+          LayoutTableRow {TR} at (0,528) size 177x44
+            LayoutTableCell {TD} at (0,539) size 59x22 [border: (1px solid #000000)] [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "1-13"
+            LayoutTableCell {TD} at (59,539) size 59x22 [border: (1px solid #000000)] [r=12 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "2-13"
+            LayoutTableCell {TD} at (118,539) size 59x22 [border: (1px solid #000000)] [r=12 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "3-13"
+          LayoutTableRow {TR} at (0,572) size 177x44
+            LayoutTableCell {TD} at (0,583) size 59x22 [border: (1px solid #000000)] [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "1-14"
+            LayoutTableCell {TD} at (59,583) size 59x22 [border: (1px solid #000000)] [r=13 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "2-14"
+            LayoutTableCell {TD} at (118,583) size 59x22 [border: (1px solid #000000)] [r=13 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "3-14"
+          LayoutTableRow {TR} at (0,616) size 177x44
+            LayoutTableCell {TD} at (0,627) size 59x22 [border: (1px solid #000000)] [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "1-15"
+            LayoutTableCell {TD} at (59,627) size 59x22 [border: (1px solid #000000)] [r=14 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "2-15"
+            LayoutTableCell {TD} at (118,627) size 59x22 [border: (1px solid #000000)] [r=14 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "3-15"
+          LayoutTableRow {TR} at (0,660) size 177x44
+            LayoutTableCell {TD} at (0,671) size 59x22 [border: (1px solid #000000)] [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "1-16"
+            LayoutTableCell {TD} at (59,671) size 59x22 [border: (1px solid #000000)] [r=15 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "2-16"
+            LayoutTableCell {TD} at (118,671) size 59x22 [border: (1px solid #000000)] [r=15 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "3-16"
+          LayoutTableRow {TR} at (0,704) size 177x44
+            LayoutTableCell {TD} at (0,715) size 59x22 [border: (1px solid #000000)] [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "1-17"
+            LayoutTableCell {TD} at (59,715) size 59x22 [border: (1px solid #000000)] [r=16 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "2-17"
+            LayoutTableCell {TD} at (118,715) size 59x22 [border: (1px solid #000000)] [r=16 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "3-17"
+          LayoutTableRow {TR} at (0,799) size 177x44
+            LayoutTableCell {TD} at (0,810) size 59x22 [border: (1px solid #000000)] [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "1-18"
+            LayoutTableCell {TD} at (59,810) size 59x22 [border: (1px solid #000000)] [r=17 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "2-18"
+            LayoutTableCell {TD} at (118,810) size 59x22 [border: (1px solid #000000)] [r=17 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "3-18"
+          LayoutTableRow {TR} at (0,843) size 177x44
+            LayoutTableCell {TD} at (0,854) size 59x22 [border: (1px solid #000000)] [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "1-19"
+            LayoutTableCell {TD} at (59,854) size 59x22 [border: (1px solid #000000)] [r=18 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "2-19"
+            LayoutTableCell {TD} at (118,854) size 59x22 [border: (1px solid #000000)] [r=18 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "3-19"
+          LayoutTableRow {TR} at (0,887) size 177x44
+            LayoutTableCell {TD} at (0,898) size 59x22 [border: (1px solid #000000)] [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "1-20"
+            LayoutTableCell {TD} at (59,898) size 59x22 [border: (1px solid #000000)] [r=19 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "2-20"
+            LayoutTableCell {TD} at (118,898) size 59x22 [border: (1px solid #000000)] [r=19 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x18
+                text run at (2,2) width 30: "3-20"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/printing/thead-tfoot-containing-svg-repeat-expected.png b/third_party/WebKit/LayoutTests/platform/mac/printing/thead-tfoot-containing-svg-repeat-expected.png
new file mode 100644
index 0000000..85ab9ebb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/printing/thead-tfoot-containing-svg-repeat-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/printing/thead-tfoot-containing-svg-repeat-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/printing/thead-tfoot-containing-svg-repeat-expected.txt
new file mode 100644
index 0000000..7fe2a97
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/printing/thead-tfoot-containing-svg-repeat-expected.txt
@@ -0,0 +1,155 @@
+layer at (0,0) size 800x600 scrollHeight 2256
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x2256 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 1046x2256
+    LayoutBlockFlow {BODY} at (8,8) size 1030x2240
+      LayoutBlockFlow (anonymous) at (0,0) size 1030x100
+        LayoutText {#text} at (0,7) size 972x86
+          text run at (0,7) width 972: "Passes if there is a green circle before each \"Header\" and a red circle before each"
+          text run at (0,57) width 111: "\"Footer\"."
+      LayoutTable {TABLE} at (0,100) size 139x2140
+        LayoutTableSection {THEAD} at (0,0) size 139x56
+          LayoutTableRow {TR} at (0,2) size 139x52
+            LayoutTableCell {TH} at (2,2) size 135x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (1.27,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#008000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (31,8) size 103x36
+                text run at (31,8) width 8: " "
+                text run at (38,8) width 96: "Header"
+        LayoutTableSection {TBODY} at (0,56) size 139x2030
+          LayoutTableRow {TR} at (0,0) size 139x52
+            LayoutTableCell {TD} at (2,0) size 135x52 [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "1"
+          LayoutTableRow {TR} at (0,54) size 139x52
+            LayoutTableCell {TD} at (2,54) size 135x52 [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "2"
+          LayoutTableRow {TR} at (0,108) size 139x52
+            LayoutTableCell {TD} at (2,108) size 135x52 [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "3"
+          LayoutTableRow {TR} at (0,162) size 139x52
+            LayoutTableCell {TD} at (2,162) size 135x52 [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "4"
+          LayoutTableRow {TR} at (0,216) size 139x52
+            LayoutTableCell {TD} at (2,216) size 135x52 [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "5"
+          LayoutTableRow {TR} at (0,270) size 139x52
+            LayoutTableCell {TD} at (2,270) size 135x52 [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "6"
+          LayoutTableRow {TR} at (0,324) size 139x52
+            LayoutTableCell {TD} at (2,324) size 135x52 [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "7"
+          LayoutTableRow {TR} at (0,378) size 139x52
+            LayoutTableCell {TD} at (2,378) size 135x52 [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "8"
+          LayoutTableRow {TR} at (0,432) size 139x52
+            LayoutTableCell {TD} at (2,432) size 135x52 [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "9"
+          LayoutTableRow {TR} at (0,486) size 139x52
+            LayoutTableCell {TD} at (2,486) size 135x52 [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "10"
+          LayoutTableRow {TR} at (0,691) size 139x52
+            LayoutTableCell {TD} at (2,691) size 135x52 [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 29x36
+                text run at (1,8) width 29: "11"
+          LayoutTableRow {TR} at (0,745) size 139x52
+            LayoutTableCell {TD} at (2,745) size 135x52 [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "12"
+          LayoutTableRow {TR} at (0,799) size 139x52
+            LayoutTableCell {TD} at (2,799) size 135x52 [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "13"
+          LayoutTableRow {TR} at (0,853) size 139x52
+            LayoutTableCell {TD} at (2,853) size 135x52 [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "14"
+          LayoutTableRow {TR} at (0,907) size 139x52
+            LayoutTableCell {TD} at (2,907) size 135x52 [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "15"
+          LayoutTableRow {TR} at (0,961) size 139x52
+            LayoutTableCell {TD} at (2,961) size 135x52 [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "16"
+          LayoutTableRow {TR} at (0,1015) size 139x52
+            LayoutTableCell {TD} at (2,1015) size 135x52 [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "17"
+          LayoutTableRow {TR} at (0,1069) size 139x52
+            LayoutTableCell {TD} at (2,1069) size 135x52 [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "18"
+          LayoutTableRow {TR} at (0,1123) size 139x52
+            LayoutTableCell {TD} at (2,1123) size 135x52 [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "19"
+          LayoutTableRow {TR} at (0,1177) size 139x52
+            LayoutTableCell {TD} at (2,1177) size 135x52 [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "20"
+          LayoutTableRow {TR} at (0,1231) size 139x52
+            LayoutTableCell {TD} at (2,1231) size 135x52 [r=20 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "21"
+          LayoutTableRow {TR} at (0,1285) size 139x52
+            LayoutTableCell {TD} at (2,1285) size 135x52 [r=21 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "22"
+          LayoutTableRow {TR} at (0,1490) size 139x52
+            LayoutTableCell {TD} at (2,1490) size 135x52 [r=22 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "23"
+          LayoutTableRow {TR} at (0,1544) size 139x52
+            LayoutTableCell {TD} at (2,1544) size 135x52 [r=23 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "24"
+          LayoutTableRow {TR} at (0,1598) size 139x52
+            LayoutTableCell {TD} at (2,1598) size 135x52 [r=24 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "25"
+          LayoutTableRow {TR} at (0,1652) size 139x52
+            LayoutTableCell {TD} at (2,1652) size 135x52 [r=25 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "26"
+          LayoutTableRow {TR} at (0,1706) size 139x52
+            LayoutTableCell {TD} at (2,1706) size 135x52 [r=26 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "27"
+          LayoutTableRow {TR} at (0,1760) size 139x52
+            LayoutTableCell {TD} at (2,1760) size 135x52 [r=27 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "28"
+          LayoutTableRow {TR} at (0,1814) size 139x52
+            LayoutTableCell {TD} at (2,1814) size 135x52 [r=28 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "29"
+          LayoutTableRow {TR} at (0,1868) size 139x52
+            LayoutTableCell {TD} at (2,1868) size 135x52 [r=29 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "30"
+          LayoutTableRow {TR} at (0,1922) size 139x52
+            LayoutTableCell {TD} at (2,1922) size 135x52 [r=30 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "31"
+          LayoutTableRow {TR} at (0,1976) size 139x52
+            LayoutTableCell {TD} at (2,1976) size 135x52 [r=31 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "32"
+        LayoutTableSection {TFOOT} at (0,2086) size 139x54
+          LayoutTableRow {TR} at (0,0) size 139x52
+            LayoutTableCell {TH} at (2,0) size 135x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (6.28,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#FF0000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (36,8) size 93x36
+                text run at (36,8) width 8: " "
+                text run at (43,8) width 86: "Footer"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png b/third_party/WebKit/LayoutTests/platform/mac/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
new file mode 100644
index 0000000..a55759f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt
new file mode 100644
index 0000000..fd5e603b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt
@@ -0,0 +1,157 @@
+layer at (0,0) size 800x600 scrollHeight 4368
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x4368 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 1046x4368
+    LayoutBlockFlow {BODY} at (8,8) size 1030x4352
+      LayoutBlockFlow (anonymous) at (0,0) size 1030x100
+        LayoutText {#text} at (0,7) size 972x86
+          text run at (0,7) width 972: "Passes if there is a green circle before each \"Header\" and a red circle before each"
+          text run at (0,57) width 111: "\"Footer\"."
+      LayoutBlockFlow {DIV} at (0,100) size 1030x1000
+      LayoutTable {TABLE} at (0,1100) size 139x2252
+        LayoutTableSection {THEAD} at (0,0) size 139x56
+          LayoutTableRow {TR} at (0,2) size 139x52
+            LayoutTableCell {TH} at (2,2) size 135x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (1.27,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#008000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (31,8) size 103x36
+                text run at (31,8) width 8: " "
+                text run at (38,8) width 96: "Header"
+        LayoutTableSection {TBODY} at (0,56) size 139x2142
+          LayoutTableRow {TR} at (0,0) size 139x52
+            LayoutTableCell {TD} at (2,0) size 135x52 [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "1"
+          LayoutTableRow {TR} at (0,54) size 139x52
+            LayoutTableCell {TD} at (2,54) size 135x52 [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "2"
+          LayoutTableRow {TR} at (0,108) size 139x52
+            LayoutTableCell {TD} at (2,108) size 135x52 [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "3"
+          LayoutTableRow {TR} at (0,162) size 139x52
+            LayoutTableCell {TD} at (2,162) size 135x52 [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "4"
+          LayoutTableRow {TR} at (0,216) size 139x52
+            LayoutTableCell {TD} at (2,216) size 135x52 [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "5"
+          LayoutTableRow {TR} at (0,270) size 139x52
+            LayoutTableCell {TD} at (2,270) size 135x52 [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "6"
+          LayoutTableRow {TR} at (0,324) size 139x52
+            LayoutTableCell {TD} at (2,324) size 135x52 [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "7"
+          LayoutTableRow {TR} at (0,490) size 139x52
+            LayoutTableCell {TD} at (2,490) size 135x52 [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "8"
+          LayoutTableRow {TR} at (0,544) size 139x52
+            LayoutTableCell {TD} at (2,544) size 135x52 [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "9"
+          LayoutTableRow {TR} at (0,598) size 139x52
+            LayoutTableCell {TD} at (2,598) size 135x52 [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "10"
+          LayoutTableRow {TR} at (0,652) size 139x52
+            LayoutTableCell {TD} at (2,652) size 135x52 [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 29x36
+                text run at (1,8) width 29: "11"
+          LayoutTableRow {TR} at (0,706) size 139x52
+            LayoutTableCell {TD} at (2,706) size 135x52 [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "12"
+          LayoutTableRow {TR} at (0,760) size 139x52
+            LayoutTableCell {TD} at (2,760) size 135x52 [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "13"
+          LayoutTableRow {TR} at (0,814) size 139x52
+            LayoutTableCell {TD} at (2,814) size 135x52 [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "14"
+          LayoutTableRow {TR} at (0,868) size 139x52
+            LayoutTableCell {TD} at (2,868) size 135x52 [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "15"
+          LayoutTableRow {TR} at (0,922) size 139x52
+            LayoutTableCell {TD} at (2,922) size 135x52 [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "16"
+          LayoutTableRow {TR} at (0,976) size 139x52
+            LayoutTableCell {TD} at (2,976) size 135x52 [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "17"
+          LayoutTableRow {TR} at (0,1030) size 139x52
+            LayoutTableCell {TD} at (2,1030) size 135x52 [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "18"
+          LayoutTableRow {TR} at (0,1084) size 139x52
+            LayoutTableCell {TD} at (2,1084) size 135x52 [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "19"
+          LayoutTableRow {TR} at (0,1289) size 139x52
+            LayoutTableCell {TD} at (2,1289) size 135x52 [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "20"
+          LayoutTableRow {TR} at (0,1343) size 139x52
+            LayoutTableCell {TD} at (2,1343) size 135x52 [r=20 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "21"
+          LayoutTableRow {TR} at (0,1397) size 139x52
+            LayoutTableCell {TD} at (2,1397) size 135x52 [r=21 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "22"
+          LayoutTableRow {TR} at (0,1451) size 139x52
+            LayoutTableCell {TD} at (2,1451) size 135x52 [r=22 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "23"
+          LayoutTableRow {TR} at (0,1505) size 139x52
+            LayoutTableCell {TD} at (2,1505) size 135x52 [r=23 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "24"
+          LayoutTableRow {TR} at (0,1559) size 139x52
+            LayoutTableCell {TD} at (2,1559) size 135x52 [r=24 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "25"
+          LayoutTableRow {TR} at (0,1613) size 139x52
+            LayoutTableCell {TD} at (2,1613) size 135x52 [r=25 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "26"
+          LayoutTableRow {TR} at (0,1667) size 139x52
+            LayoutTableCell {TD} at (2,1667) size 135x52 [r=26 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "27"
+          LayoutTableRow {TR} at (0,1721) size 139x52
+            LayoutTableCell {TD} at (2,1721) size 135x52 [r=27 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "28"
+          LayoutTableRow {TR} at (0,1775) size 139x52
+            LayoutTableCell {TD} at (2,1775) size 135x52 [r=28 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "29"
+          LayoutTableRow {TR} at (0,1829) size 139x52
+            LayoutTableCell {TD} at (2,1829) size 135x52 [r=29 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "30"
+          LayoutTableRow {TR} at (0,1883) size 139x52
+            LayoutTableCell {TD} at (2,1883) size 135x52 [r=30 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "31"
+          LayoutTableRow {TR} at (0,2088) size 139x52
+            LayoutTableCell {TD} at (2,2088) size 135x52 [r=31 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "32"
+        LayoutTableSection {TFOOT} at (0,2198) size 139x54
+          LayoutTableRow {TR} at (0,0) size 139x52
+            LayoutTableCell {TH} at (2,0) size 135x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (6.28,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#FF0000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (36,8) size 93x36
+                text run at (36,8) width 8: " "
+                text run at (43,8) width 86: "Footer"
+      LayoutBlockFlow {DIV} at (0,3352) size 1030x1000
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.txt
new file mode 100644
index 0000000..e9138ba7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.txt
@@ -0,0 +1,218 @@
+layer at (0,0) size 800x600 scrollHeight 975
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x975 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutNGBlockFlow {HTML} at (0,0) size 1046x975
+    LayoutNGBlockFlow {BODY} at (0,0) size 1046x975
+      LayoutTable {TABLE} at (0,0) size 177x975
+        LayoutTableSection {THEAD} at (0,0) size 177x44
+          LayoutTableRow {TR} at (0,0) size 177x44
+            LayoutNGTableCell {TD} at (0,11) size 59x22 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 55x18
+                text run at (2,13) width 55: "header 1"
+            LayoutNGTableCell {TD} at (59,11) size 59x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 55x18
+                text run at (2,13) width 55: "header 2"
+            LayoutNGTableCell {TD} at (118,11) size 59x22 [border: (1px solid #000000)] [r=0 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 55x18
+                text run at (2,13) width 55: "header 3"
+        LayoutTableSection {TBODY} at (0,44) size 177x931
+          LayoutTableRow {TR} at (0,0) size 177x44
+            LayoutNGTableCell {TD} at (0,11) size 59x22 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "1-1"
+            LayoutNGTableCell {TD} at (59,11) size 59x22 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "2-1"
+            LayoutNGTableCell {TD} at (118,11) size 59x22 [border: (1px solid #000000)] [r=0 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "3-1"
+          LayoutTableRow {TR} at (0,44) size 177x44
+            LayoutNGTableCell {TD} at (0,55) size 59x22 [border: (1px solid #000000)] [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "1-2"
+            LayoutNGTableCell {TD} at (59,55) size 59x22 [border: (1px solid #000000)] [r=1 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "2-2"
+            LayoutNGTableCell {TD} at (118,55) size 59x22 [border: (1px solid #000000)] [r=1 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "3-2"
+          LayoutTableRow {TR} at (0,88) size 177x44
+            LayoutNGTableCell {TD} at (0,99) size 59x22 [border: (1px solid #000000)] [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "1-3"
+            LayoutNGTableCell {TD} at (59,99) size 59x22 [border: (1px solid #000000)] [r=2 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "2-3"
+            LayoutNGTableCell {TD} at (118,99) size 59x22 [border: (1px solid #000000)] [r=2 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "3-3"
+          LayoutTableRow {TR} at (0,132) size 177x44
+            LayoutNGTableCell {TD} at (0,143) size 59x22 [border: (1px solid #000000)] [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "1-4"
+            LayoutNGTableCell {TD} at (59,143) size 59x22 [border: (1px solid #000000)] [r=3 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "2-4"
+            LayoutNGTableCell {TD} at (118,143) size 59x22 [border: (1px solid #000000)] [r=3 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "3-4"
+          LayoutTableRow {TR} at (0,176) size 177x44
+            LayoutNGTableCell {TD} at (0,187) size 59x22 [border: (1px solid #000000)] [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "1-5"
+            LayoutNGTableCell {TD} at (59,187) size 59x22 [border: (1px solid #000000)] [r=4 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "2-5"
+            LayoutNGTableCell {TD} at (118,187) size 59x22 [border: (1px solid #000000)] [r=4 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "3-5"
+          LayoutTableRow {TR} at (0,220) size 177x44
+            LayoutNGTableCell {TD} at (0,231) size 59x22 [border: (1px solid #000000)] [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "1-6"
+            LayoutNGTableCell {TD} at (59,231) size 59x22 [border: (1px solid #000000)] [r=5 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "2-6"
+            LayoutNGTableCell {TD} at (118,231) size 59x22 [border: (1px solid #000000)] [r=5 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "3-6"
+          LayoutTableRow {TR} at (0,264) size 177x44
+            LayoutNGTableCell {TD} at (0,275) size 59x22 [border: (1px solid #000000)] [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "1-7"
+            LayoutNGTableCell {TD} at (59,275) size 59x22 [border: (1px solid #000000)] [r=6 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "2-7"
+            LayoutNGTableCell {TD} at (118,275) size 59x22 [border: (1px solid #000000)] [r=6 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "3-7"
+          LayoutTableRow {TR} at (0,308) size 177x44
+            LayoutNGTableCell {TD} at (0,319) size 59x22 [border: (1px solid #000000)] [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "1-8"
+            LayoutNGTableCell {TD} at (59,319) size 59x22 [border: (1px solid #000000)] [r=7 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "2-8"
+            LayoutNGTableCell {TD} at (118,319) size 59x22 [border: (1px solid #000000)] [r=7 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "3-8"
+          LayoutTableRow {TR} at (0,352) size 177x44
+            LayoutNGTableCell {TD} at (0,363) size 59x22 [border: (1px solid #000000)] [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "1-9"
+            LayoutNGTableCell {TD} at (59,363) size 59x22 [border: (1px solid #000000)] [r=8 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "2-9"
+            LayoutNGTableCell {TD} at (118,363) size 59x22 [border: (1px solid #000000)] [r=8 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 22x18
+                text run at (2,13) width 22: "3-9"
+          LayoutTableRow {TR} at (0,396) size 177x44
+            LayoutNGTableCell {TD} at (0,407) size 59x22 [border: (1px solid #000000)] [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "1-10"
+            LayoutNGTableCell {TD} at (59,407) size 59x22 [border: (1px solid #000000)] [r=9 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "2-10"
+            LayoutNGTableCell {TD} at (118,407) size 59x22 [border: (1px solid #000000)] [r=9 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "3-10"
+          LayoutTableRow {TR} at (0,440) size 177x44
+            LayoutNGTableCell {TD} at (0,451) size 59x22 [border: (1px solid #000000)] [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 29x18
+                text run at (2,13) width 29: "1-11"
+            LayoutNGTableCell {TD} at (59,451) size 59x22 [border: (1px solid #000000)] [r=10 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 29x18
+                text run at (2,13) width 29: "2-11"
+            LayoutNGTableCell {TD} at (118,451) size 59x22 [border: (1px solid #000000)] [r=10 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 29x18
+                text run at (2,13) width 29: "3-11"
+          LayoutTableRow {TR} at (0,484) size 177x44
+            LayoutNGTableCell {TD} at (0,495) size 59x22 [border: (1px solid #000000)] [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "1-12"
+            LayoutNGTableCell {TD} at (59,495) size 59x22 [border: (1px solid #000000)] [r=11 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "2-12"
+            LayoutNGTableCell {TD} at (118,495) size 59x22 [border: (1px solid #000000)] [r=11 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "3-12"
+          LayoutTableRow {TR} at (0,528) size 177x44
+            LayoutNGTableCell {TD} at (0,539) size 59x22 [border: (1px solid #000000)] [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "1-13"
+            LayoutNGTableCell {TD} at (59,539) size 59x22 [border: (1px solid #000000)] [r=12 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "2-13"
+            LayoutNGTableCell {TD} at (118,539) size 59x22 [border: (1px solid #000000)] [r=12 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "3-13"
+          LayoutTableRow {TR} at (0,572) size 177x44
+            LayoutNGTableCell {TD} at (0,583) size 59x22 [border: (1px solid #000000)] [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "1-14"
+            LayoutNGTableCell {TD} at (59,583) size 59x22 [border: (1px solid #000000)] [r=13 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "2-14"
+            LayoutNGTableCell {TD} at (118,583) size 59x22 [border: (1px solid #000000)] [r=13 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "3-14"
+          LayoutTableRow {TR} at (0,616) size 177x44
+            LayoutNGTableCell {TD} at (0,627) size 59x22 [border: (1px solid #000000)] [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "1-15"
+            LayoutNGTableCell {TD} at (59,627) size 59x22 [border: (1px solid #000000)] [r=14 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "2-15"
+            LayoutNGTableCell {TD} at (118,627) size 59x22 [border: (1px solid #000000)] [r=14 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "3-15"
+          LayoutTableRow {TR} at (0,660) size 177x44
+            LayoutNGTableCell {TD} at (0,671) size 59x22 [border: (1px solid #000000)] [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "1-16"
+            LayoutNGTableCell {TD} at (59,671) size 59x22 [border: (1px solid #000000)] [r=15 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "2-16"
+            LayoutNGTableCell {TD} at (118,671) size 59x22 [border: (1px solid #000000)] [r=15 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "3-16"
+          LayoutTableRow {TR} at (0,704) size 177x44
+            LayoutNGTableCell {TD} at (0,715) size 59x22 [border: (1px solid #000000)] [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "1-17"
+            LayoutNGTableCell {TD} at (59,715) size 59x22 [border: (1px solid #000000)] [r=16 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "2-17"
+            LayoutNGTableCell {TD} at (118,715) size 59x22 [border: (1px solid #000000)] [r=16 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "3-17"
+          LayoutTableRow {TR} at (0,799) size 177x44
+            LayoutNGTableCell {TD} at (0,810) size 59x22 [border: (1px solid #000000)] [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "1-18"
+            LayoutNGTableCell {TD} at (59,810) size 59x22 [border: (1px solid #000000)] [r=17 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "2-18"
+            LayoutNGTableCell {TD} at (118,810) size 59x22 [border: (1px solid #000000)] [r=17 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "3-18"
+          LayoutTableRow {TR} at (0,843) size 177x44
+            LayoutNGTableCell {TD} at (0,854) size 59x22 [border: (1px solid #000000)] [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "1-19"
+            LayoutNGTableCell {TD} at (59,854) size 59x22 [border: (1px solid #000000)] [r=18 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "2-19"
+            LayoutNGTableCell {TD} at (118,854) size 59x22 [border: (1px solid #000000)] [r=18 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "3-19"
+          LayoutTableRow {TR} at (0,887) size 177x44
+            LayoutNGTableCell {TD} at (0,898) size 59x22 [border: (1px solid #000000)] [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "1-20"
+            LayoutNGTableCell {TD} at (59,898) size 59x22 [border: (1px solid #000000)] [r=19 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "2-20"
+            LayoutNGTableCell {TD} at (118,898) size 59x22 [border: (1px solid #000000)] [r=19 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,13) size 30x18
+                text run at (2,13) width 30: "3-20"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png
new file mode 100644
index 0000000..493bcff
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.txt
new file mode 100644
index 0000000..2a2e2e0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.txt
@@ -0,0 +1,153 @@
+layer at (0,0) size 800x600 scrollHeight 2264
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x2264 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutNGBlockFlow {HTML} at (0,0) size 1046x2264
+    LayoutNGBlockFlow {BODY} at (8,8) size 1030x2248
+      LayoutNGBlockFlow (anonymous) at (0,0) size 1030x100
+        LayoutText {#text} at (0,7) size 972x86
+          text run at (0,7) width 972: "Passes if there is a green circle before each \"Header\" and a red circle before each"
+          text run at (0,57) width 111: "\"Footer\"."
+      LayoutTable {TABLE} at (0,100) size 139x2148
+        LayoutTableSection {THEAD} at (0,0) size 139x56
+          LayoutTableRow {TR} at (0,2) size 139x52
+            LayoutNGTableCell {TH} at (2,2) size 135x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (1.27,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#008000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (31,8) size 103x36
+                text run at (31,8) width 103: " Header"
+        LayoutTableSection {TBODY} at (0,56) size 139x2038
+          LayoutTableRow {TR} at (0,0) size 139x52
+            LayoutNGTableCell {TD} at (2,0) size 135x52 [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "1"
+          LayoutTableRow {TR} at (0,54) size 139x52
+            LayoutNGTableCell {TD} at (2,54) size 135x52 [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "2"
+          LayoutTableRow {TR} at (0,108) size 139x52
+            LayoutNGTableCell {TD} at (2,108) size 135x52 [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "3"
+          LayoutTableRow {TR} at (0,162) size 139x52
+            LayoutNGTableCell {TD} at (2,162) size 135x52 [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "4"
+          LayoutTableRow {TR} at (0,216) size 139x52
+            LayoutNGTableCell {TD} at (2,216) size 135x52 [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "5"
+          LayoutTableRow {TR} at (0,270) size 139x52
+            LayoutNGTableCell {TD} at (2,270) size 135x52 [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "6"
+          LayoutTableRow {TR} at (0,324) size 139x52
+            LayoutNGTableCell {TD} at (2,324) size 135x52 [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "7"
+          LayoutTableRow {TR} at (0,378) size 139x52
+            LayoutNGTableCell {TD} at (2,378) size 135x52 [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "8"
+          LayoutTableRow {TR} at (0,432) size 139x52
+            LayoutNGTableCell {TD} at (2,432) size 135x52 [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "9"
+          LayoutTableRow {TR} at (0,486) size 139x52
+            LayoutNGTableCell {TD} at (2,486) size 135x52 [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "10"
+          LayoutTableRow {TR} at (0,699) size 139x52
+            LayoutNGTableCell {TD} at (2,699) size 135x52 [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 29x36
+                text run at (1,8) width 29: "11"
+          LayoutTableRow {TR} at (0,753) size 139x52
+            LayoutNGTableCell {TD} at (2,753) size 135x52 [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "12"
+          LayoutTableRow {TR} at (0,807) size 139x52
+            LayoutNGTableCell {TD} at (2,807) size 135x52 [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "13"
+          LayoutTableRow {TR} at (0,861) size 139x52
+            LayoutNGTableCell {TD} at (2,861) size 135x52 [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "14"
+          LayoutTableRow {TR} at (0,915) size 139x52
+            LayoutNGTableCell {TD} at (2,915) size 135x52 [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "15"
+          LayoutTableRow {TR} at (0,969) size 139x52
+            LayoutNGTableCell {TD} at (2,969) size 135x52 [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "16"
+          LayoutTableRow {TR} at (0,1023) size 139x52
+            LayoutNGTableCell {TD} at (2,1023) size 135x52 [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "17"
+          LayoutTableRow {TR} at (0,1077) size 139x52
+            LayoutNGTableCell {TD} at (2,1077) size 135x52 [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "18"
+          LayoutTableRow {TR} at (0,1131) size 139x52
+            LayoutNGTableCell {TD} at (2,1131) size 135x52 [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "19"
+          LayoutTableRow {TR} at (0,1185) size 139x52
+            LayoutNGTableCell {TD} at (2,1185) size 135x52 [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "20"
+          LayoutTableRow {TR} at (0,1239) size 139x52
+            LayoutNGTableCell {TD} at (2,1239) size 135x52 [r=20 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "21"
+          LayoutTableRow {TR} at (0,1293) size 139x52
+            LayoutNGTableCell {TD} at (2,1293) size 135x52 [r=21 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "22"
+          LayoutTableRow {TR} at (0,1498) size 139x52
+            LayoutNGTableCell {TD} at (2,1498) size 135x52 [r=22 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "23"
+          LayoutTableRow {TR} at (0,1552) size 139x52
+            LayoutNGTableCell {TD} at (2,1552) size 135x52 [r=23 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "24"
+          LayoutTableRow {TR} at (0,1606) size 139x52
+            LayoutNGTableCell {TD} at (2,1606) size 135x52 [r=24 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "25"
+          LayoutTableRow {TR} at (0,1660) size 139x52
+            LayoutNGTableCell {TD} at (2,1660) size 135x52 [r=25 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "26"
+          LayoutTableRow {TR} at (0,1714) size 139x52
+            LayoutNGTableCell {TD} at (2,1714) size 135x52 [r=26 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "27"
+          LayoutTableRow {TR} at (0,1768) size 139x52
+            LayoutNGTableCell {TD} at (2,1768) size 135x52 [r=27 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "28"
+          LayoutTableRow {TR} at (0,1822) size 139x52
+            LayoutNGTableCell {TD} at (2,1822) size 135x52 [r=28 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "29"
+          LayoutTableRow {TR} at (0,1876) size 139x52
+            LayoutNGTableCell {TD} at (2,1876) size 135x52 [r=29 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "30"
+          LayoutTableRow {TR} at (0,1930) size 139x52
+            LayoutNGTableCell {TD} at (2,1930) size 135x52 [r=30 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "31"
+          LayoutTableRow {TR} at (0,1984) size 139x52
+            LayoutNGTableCell {TD} at (2,1984) size 135x52 [r=31 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "32"
+        LayoutTableSection {TFOOT} at (0,2094) size 139x54
+          LayoutTableRow {TR} at (0,0) size 139x52
+            LayoutNGTableCell {TH} at (2,0) size 135x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (6.53,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#FF0000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (36,8) size 93x36
+                text run at (36,8) width 93: " Footer"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
new file mode 100644
index 0000000..49dbebe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt
new file mode 100644
index 0000000..5948ae3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt
@@ -0,0 +1,155 @@
+layer at (0,0) size 800x600 scrollHeight 4376
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x4376 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutNGBlockFlow {HTML} at (0,0) size 1046x4376
+    LayoutNGBlockFlow {BODY} at (8,8) size 1030x4360
+      LayoutNGBlockFlow (anonymous) at (0,0) size 1030x100
+        LayoutText {#text} at (0,7) size 972x86
+          text run at (0,7) width 972: "Passes if there is a green circle before each \"Header\" and a red circle before each"
+          text run at (0,57) width 111: "\"Footer\"."
+      LayoutNGBlockFlow {DIV} at (0,100) size 1030x1000
+      LayoutTable {TABLE} at (0,1100) size 139x2260
+        LayoutTableSection {THEAD} at (0,0) size 139x56
+          LayoutTableRow {TR} at (0,2) size 139x52
+            LayoutNGTableCell {TH} at (2,2) size 135x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (1.27,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#008000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (31,8) size 103x36
+                text run at (31,8) width 103: " Header"
+        LayoutTableSection {TBODY} at (0,56) size 139x2150
+          LayoutTableRow {TR} at (0,0) size 139x52
+            LayoutNGTableCell {TD} at (2,0) size 135x52 [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "1"
+          LayoutTableRow {TR} at (0,54) size 139x52
+            LayoutNGTableCell {TD} at (2,54) size 135x52 [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "2"
+          LayoutTableRow {TR} at (0,108) size 139x52
+            LayoutNGTableCell {TD} at (2,108) size 135x52 [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "3"
+          LayoutTableRow {TR} at (0,162) size 139x52
+            LayoutNGTableCell {TD} at (2,162) size 135x52 [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "4"
+          LayoutTableRow {TR} at (0,216) size 139x52
+            LayoutNGTableCell {TD} at (2,216) size 135x52 [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "5"
+          LayoutTableRow {TR} at (0,270) size 139x52
+            LayoutNGTableCell {TD} at (2,270) size 135x52 [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "6"
+          LayoutTableRow {TR} at (0,324) size 139x52
+            LayoutNGTableCell {TD} at (2,324) size 135x52 [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "7"
+          LayoutTableRow {TR} at (0,498) size 139x52
+            LayoutNGTableCell {TD} at (2,498) size 135x52 [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "8"
+          LayoutTableRow {TR} at (0,552) size 139x52
+            LayoutNGTableCell {TD} at (2,552) size 135x52 [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 15x36
+                text run at (1,8) width 15: "9"
+          LayoutTableRow {TR} at (0,606) size 139x52
+            LayoutNGTableCell {TD} at (2,606) size 135x52 [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "10"
+          LayoutTableRow {TR} at (0,660) size 139x52
+            LayoutNGTableCell {TD} at (2,660) size 135x52 [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 29x36
+                text run at (1,8) width 29: "11"
+          LayoutTableRow {TR} at (0,714) size 139x52
+            LayoutNGTableCell {TD} at (2,714) size 135x52 [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "12"
+          LayoutTableRow {TR} at (0,768) size 139x52
+            LayoutNGTableCell {TD} at (2,768) size 135x52 [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "13"
+          LayoutTableRow {TR} at (0,822) size 139x52
+            LayoutNGTableCell {TD} at (2,822) size 135x52 [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "14"
+          LayoutTableRow {TR} at (0,876) size 139x52
+            LayoutNGTableCell {TD} at (2,876) size 135x52 [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "15"
+          LayoutTableRow {TR} at (0,930) size 139x52
+            LayoutNGTableCell {TD} at (2,930) size 135x52 [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "16"
+          LayoutTableRow {TR} at (0,984) size 139x52
+            LayoutNGTableCell {TD} at (2,984) size 135x52 [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "17"
+          LayoutTableRow {TR} at (0,1038) size 139x52
+            LayoutNGTableCell {TD} at (2,1038) size 135x52 [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "18"
+          LayoutTableRow {TR} at (0,1092) size 139x52
+            LayoutNGTableCell {TD} at (2,1092) size 135x52 [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "19"
+          LayoutTableRow {TR} at (0,1297) size 139x52
+            LayoutNGTableCell {TD} at (2,1297) size 135x52 [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "20"
+          LayoutTableRow {TR} at (0,1351) size 139x52
+            LayoutNGTableCell {TD} at (2,1351) size 135x52 [r=20 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "21"
+          LayoutTableRow {TR} at (0,1405) size 139x52
+            LayoutNGTableCell {TD} at (2,1405) size 135x52 [r=21 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "22"
+          LayoutTableRow {TR} at (0,1459) size 139x52
+            LayoutNGTableCell {TD} at (2,1459) size 135x52 [r=22 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "23"
+          LayoutTableRow {TR} at (0,1513) size 139x52
+            LayoutNGTableCell {TD} at (2,1513) size 135x52 [r=23 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "24"
+          LayoutTableRow {TR} at (0,1567) size 139x52
+            LayoutNGTableCell {TD} at (2,1567) size 135x52 [r=24 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "25"
+          LayoutTableRow {TR} at (0,1621) size 139x52
+            LayoutNGTableCell {TD} at (2,1621) size 135x52 [r=25 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "26"
+          LayoutTableRow {TR} at (0,1675) size 139x52
+            LayoutNGTableCell {TD} at (2,1675) size 135x52 [r=26 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "27"
+          LayoutTableRow {TR} at (0,1729) size 139x52
+            LayoutNGTableCell {TD} at (2,1729) size 135x52 [r=27 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "28"
+          LayoutTableRow {TR} at (0,1783) size 139x52
+            LayoutNGTableCell {TD} at (2,1783) size 135x52 [r=28 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "29"
+          LayoutTableRow {TR} at (0,1837) size 139x52
+            LayoutNGTableCell {TD} at (2,1837) size 135x52 [r=29 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "30"
+          LayoutTableRow {TR} at (0,1891) size 139x52
+            LayoutNGTableCell {TD} at (2,1891) size 135x52 [r=30 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "31"
+          LayoutTableRow {TR} at (0,2096) size 139x52
+            LayoutNGTableCell {TD} at (2,2096) size 135x52 [r=31 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,8) size 30x36
+                text run at (1,8) width 30: "32"
+        LayoutTableSection {TFOOT} at (0,2206) size 139x54
+          LayoutTableRow {TR} at (0,0) size 139x52
+            LayoutNGTableCell {TH} at (2,0) size 135x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (6.53,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#FF0000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (36,8) size 93x36
+                text run at (36,8) width 93: " Footer"
+      LayoutNGBlockFlow {DIV} at (0,3360) size 1030x1000
diff --git a/third_party/WebKit/LayoutTests/platform/win/printing/avoid-setting-header-offset-on-header-expected.png b/third_party/WebKit/LayoutTests/platform/win/printing/avoid-setting-header-offset-on-header-expected.png
new file mode 100644
index 0000000..a803c97
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/printing/avoid-setting-header-offset-on-header-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/printing/avoid-setting-header-offset-on-header-expected.txt b/third_party/WebKit/LayoutTests/platform/win/printing/avoid-setting-header-offset-on-header-expected.txt
new file mode 100644
index 0000000..a247503
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/printing/avoid-setting-header-offset-on-header-expected.txt
@@ -0,0 +1,218 @@
+layer at (0,0) size 800x600 scrollHeight 975
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x975 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 1046x975
+    LayoutBlockFlow {BODY} at (0,0) size 1046x975
+      LayoutTable {TABLE} at (0,0) size 171x975
+        LayoutTableSection {THEAD} at (0,0) size 171x44
+          LayoutTableRow {TR} at (0,0) size 171x44
+            LayoutTableCell {TD} at (0,10) size 57x24 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 53x19
+                text run at (2,2) width 53: "header 1"
+            LayoutTableCell {TD} at (57,10) size 57x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 53x19
+                text run at (2,2) width 53: "header 2"
+            LayoutTableCell {TD} at (114,10) size 57x24 [border: (1px solid #000000)] [r=0 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 53x19
+                text run at (2,2) width 53: "header 3"
+        LayoutTableSection {TBODY} at (0,44) size 171x931
+          LayoutTableRow {TR} at (0,0) size 171x44
+            LayoutTableCell {TD} at (0,10) size 57x24 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "1-1"
+            LayoutTableCell {TD} at (57,10) size 57x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "2-1"
+            LayoutTableCell {TD} at (114,10) size 57x24 [border: (1px solid #000000)] [r=0 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "3-1"
+          LayoutTableRow {TR} at (0,44) size 171x44
+            LayoutTableCell {TD} at (0,54) size 57x24 [border: (1px solid #000000)] [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "1-2"
+            LayoutTableCell {TD} at (57,54) size 57x24 [border: (1px solid #000000)] [r=1 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "2-2"
+            LayoutTableCell {TD} at (114,54) size 57x24 [border: (1px solid #000000)] [r=1 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "3-2"
+          LayoutTableRow {TR} at (0,88) size 171x44
+            LayoutTableCell {TD} at (0,98) size 57x24 [border: (1px solid #000000)] [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "1-3"
+            LayoutTableCell {TD} at (57,98) size 57x24 [border: (1px solid #000000)] [r=2 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "2-3"
+            LayoutTableCell {TD} at (114,98) size 57x24 [border: (1px solid #000000)] [r=2 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "3-3"
+          LayoutTableRow {TR} at (0,132) size 171x44
+            LayoutTableCell {TD} at (0,142) size 57x24 [border: (1px solid #000000)] [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "1-4"
+            LayoutTableCell {TD} at (57,142) size 57x24 [border: (1px solid #000000)] [r=3 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "2-4"
+            LayoutTableCell {TD} at (114,142) size 57x24 [border: (1px solid #000000)] [r=3 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "3-4"
+          LayoutTableRow {TR} at (0,176) size 171x44
+            LayoutTableCell {TD} at (0,186) size 57x24 [border: (1px solid #000000)] [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "1-5"
+            LayoutTableCell {TD} at (57,186) size 57x24 [border: (1px solid #000000)] [r=4 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "2-5"
+            LayoutTableCell {TD} at (114,186) size 57x24 [border: (1px solid #000000)] [r=4 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "3-5"
+          LayoutTableRow {TR} at (0,220) size 171x44
+            LayoutTableCell {TD} at (0,230) size 57x24 [border: (1px solid #000000)] [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "1-6"
+            LayoutTableCell {TD} at (57,230) size 57x24 [border: (1px solid #000000)] [r=5 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "2-6"
+            LayoutTableCell {TD} at (114,230) size 57x24 [border: (1px solid #000000)] [r=5 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "3-6"
+          LayoutTableRow {TR} at (0,264) size 171x44
+            LayoutTableCell {TD} at (0,274) size 57x24 [border: (1px solid #000000)] [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "1-7"
+            LayoutTableCell {TD} at (57,274) size 57x24 [border: (1px solid #000000)] [r=6 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "2-7"
+            LayoutTableCell {TD} at (114,274) size 57x24 [border: (1px solid #000000)] [r=6 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "3-7"
+          LayoutTableRow {TR} at (0,308) size 171x44
+            LayoutTableCell {TD} at (0,318) size 57x24 [border: (1px solid #000000)] [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "1-8"
+            LayoutTableCell {TD} at (57,318) size 57x24 [border: (1px solid #000000)] [r=7 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "2-8"
+            LayoutTableCell {TD} at (114,318) size 57x24 [border: (1px solid #000000)] [r=7 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "3-8"
+          LayoutTableRow {TR} at (0,352) size 171x44
+            LayoutTableCell {TD} at (0,362) size 57x24 [border: (1px solid #000000)] [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "1-9"
+            LayoutTableCell {TD} at (57,362) size 57x24 [border: (1px solid #000000)] [r=8 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "2-9"
+            LayoutTableCell {TD} at (114,362) size 57x24 [border: (1px solid #000000)] [r=8 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 22x19
+                text run at (2,2) width 22: "3-9"
+          LayoutTableRow {TR} at (0,396) size 171x44
+            LayoutTableCell {TD} at (0,406) size 57x24 [border: (1px solid #000000)] [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "1-10"
+            LayoutTableCell {TD} at (57,406) size 57x24 [border: (1px solid #000000)] [r=9 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "2-10"
+            LayoutTableCell {TD} at (114,406) size 57x24 [border: (1px solid #000000)] [r=9 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "3-10"
+          LayoutTableRow {TR} at (0,440) size 171x44
+            LayoutTableCell {TD} at (0,450) size 57x24 [border: (1px solid #000000)] [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "1-11"
+            LayoutTableCell {TD} at (57,450) size 57x24 [border: (1px solid #000000)] [r=10 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "2-11"
+            LayoutTableCell {TD} at (114,450) size 57x24 [border: (1px solid #000000)] [r=10 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 29x19
+                text run at (2,2) width 29: "3-11"
+          LayoutTableRow {TR} at (0,484) size 171x44
+            LayoutTableCell {TD} at (0,494) size 57x24 [border: (1px solid #000000)] [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "1-12"
+            LayoutTableCell {TD} at (57,494) size 57x24 [border: (1px solid #000000)] [r=11 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "2-12"
+            LayoutTableCell {TD} at (114,494) size 57x24 [border: (1px solid #000000)] [r=11 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "3-12"
+          LayoutTableRow {TR} at (0,528) size 171x44
+            LayoutTableCell {TD} at (0,538) size 57x24 [border: (1px solid #000000)] [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "1-13"
+            LayoutTableCell {TD} at (57,538) size 57x24 [border: (1px solid #000000)] [r=12 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "2-13"
+            LayoutTableCell {TD} at (114,538) size 57x24 [border: (1px solid #000000)] [r=12 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "3-13"
+          LayoutTableRow {TR} at (0,572) size 171x44
+            LayoutTableCell {TD} at (0,582) size 57x24 [border: (1px solid #000000)] [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "1-14"
+            LayoutTableCell {TD} at (57,582) size 57x24 [border: (1px solid #000000)] [r=13 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "2-14"
+            LayoutTableCell {TD} at (114,582) size 57x24 [border: (1px solid #000000)] [r=13 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "3-14"
+          LayoutTableRow {TR} at (0,616) size 171x44
+            LayoutTableCell {TD} at (0,626) size 57x24 [border: (1px solid #000000)] [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "1-15"
+            LayoutTableCell {TD} at (57,626) size 57x24 [border: (1px solid #000000)] [r=14 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "2-15"
+            LayoutTableCell {TD} at (114,626) size 57x24 [border: (1px solid #000000)] [r=14 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "3-15"
+          LayoutTableRow {TR} at (0,660) size 171x44
+            LayoutTableCell {TD} at (0,670) size 57x24 [border: (1px solid #000000)] [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "1-16"
+            LayoutTableCell {TD} at (57,670) size 57x24 [border: (1px solid #000000)] [r=15 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "2-16"
+            LayoutTableCell {TD} at (114,670) size 57x24 [border: (1px solid #000000)] [r=15 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "3-16"
+          LayoutTableRow {TR} at (0,704) size 171x44
+            LayoutTableCell {TD} at (0,714) size 57x24 [border: (1px solid #000000)] [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "1-17"
+            LayoutTableCell {TD} at (57,714) size 57x24 [border: (1px solid #000000)] [r=16 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "2-17"
+            LayoutTableCell {TD} at (114,714) size 57x24 [border: (1px solid #000000)] [r=16 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "3-17"
+          LayoutTableRow {TR} at (0,799) size 171x44
+            LayoutTableCell {TD} at (0,809) size 57x24 [border: (1px solid #000000)] [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "1-18"
+            LayoutTableCell {TD} at (57,809) size 57x24 [border: (1px solid #000000)] [r=17 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "2-18"
+            LayoutTableCell {TD} at (114,809) size 57x24 [border: (1px solid #000000)] [r=17 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "3-18"
+          LayoutTableRow {TR} at (0,843) size 171x44
+            LayoutTableCell {TD} at (0,853) size 57x24 [border: (1px solid #000000)] [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "1-19"
+            LayoutTableCell {TD} at (57,853) size 57x24 [border: (1px solid #000000)] [r=18 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "2-19"
+            LayoutTableCell {TD} at (114,853) size 57x24 [border: (1px solid #000000)] [r=18 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "3-19"
+          LayoutTableRow {TR} at (0,887) size 171x44
+            LayoutTableCell {TD} at (0,897) size 57x24 [border: (1px solid #000000)] [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "1-20"
+            LayoutTableCell {TD} at (57,897) size 57x24 [border: (1px solid #000000)] [r=19 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "2-20"
+            LayoutTableCell {TD} at (114,897) size 57x24 [border: (1px solid #000000)] [r=19 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,2) size 30x19
+                text run at (2,2) width 30: "3-20"
diff --git a/third_party/WebKit/LayoutTests/platform/win/printing/thead-tfoot-containing-svg-repeat-expected.png b/third_party/WebKit/LayoutTests/platform/win/printing/thead-tfoot-containing-svg-repeat-expected.png
new file mode 100644
index 0000000..82c9a1d7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/printing/thead-tfoot-containing-svg-repeat-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/printing/thead-tfoot-containing-svg-repeat-expected.txt b/third_party/WebKit/LayoutTests/platform/win/printing/thead-tfoot-containing-svg-repeat-expected.txt
new file mode 100644
index 0000000..5dc632d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/printing/thead-tfoot-containing-svg-repeat-expected.txt
@@ -0,0 +1,155 @@
+layer at (0,0) size 800x600 scrollHeight 2256
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x2256 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 1046x2256
+    LayoutBlockFlow {BODY} at (8,8) size 1030x2240
+      LayoutBlockFlow (anonymous) at (0,0) size 1030x100
+        LayoutText {#text} at (0,8) size 958x84
+          text run at (0,8) width 958: "Passes if there is a green circle before each \"Header\" and a red circle before each"
+          text run at (0,58) width 110: "\"Footer\"."
+      LayoutTable {TABLE} at (0,100) size 136x2140
+        LayoutTableSection {THEAD} at (0,0) size 136x56
+          LayoutTableRow {TR} at (0,2) size 136x52
+            LayoutTableCell {TH} at (2,2) size 132x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (1,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#008000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (31,8) size 100x35
+                text run at (31,8) width 8: " "
+                text run at (39,8) width 92: "Header"
+        LayoutTableSection {TBODY} at (0,56) size 136x2030
+          LayoutTableRow {TR} at (0,0) size 136x52
+            LayoutTableCell {TD} at (2,0) size 132x52 [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "1"
+          LayoutTableRow {TR} at (0,54) size 136x52
+            LayoutTableCell {TD} at (2,54) size 132x52 [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "2"
+          LayoutTableRow {TR} at (0,108) size 136x52
+            LayoutTableCell {TD} at (2,108) size 132x52 [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "3"
+          LayoutTableRow {TR} at (0,162) size 136x52
+            LayoutTableCell {TD} at (2,162) size 132x52 [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "4"
+          LayoutTableRow {TR} at (0,216) size 136x52
+            LayoutTableCell {TD} at (2,216) size 132x52 [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "5"
+          LayoutTableRow {TR} at (0,270) size 136x52
+            LayoutTableCell {TD} at (2,270) size 132x52 [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "6"
+          LayoutTableRow {TR} at (0,324) size 136x52
+            LayoutTableCell {TD} at (2,324) size 132x52 [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "7"
+          LayoutTableRow {TR} at (0,378) size 136x52
+            LayoutTableCell {TD} at (2,378) size 132x52 [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "8"
+          LayoutTableRow {TR} at (0,432) size 136x52
+            LayoutTableCell {TD} at (2,432) size 132x52 [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "9"
+          LayoutTableRow {TR} at (0,486) size 136x52
+            LayoutTableCell {TD} at (2,486) size 132x52 [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "10"
+          LayoutTableRow {TR} at (0,691) size 136x52
+            LayoutTableCell {TD} at (2,691) size 132x52 [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 29x34
+                text run at (1,9) width 29: "11"
+          LayoutTableRow {TR} at (0,745) size 136x52
+            LayoutTableCell {TD} at (2,745) size 132x52 [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "12"
+          LayoutTableRow {TR} at (0,799) size 136x52
+            LayoutTableCell {TD} at (2,799) size 132x52 [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "13"
+          LayoutTableRow {TR} at (0,853) size 136x52
+            LayoutTableCell {TD} at (2,853) size 132x52 [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "14"
+          LayoutTableRow {TR} at (0,907) size 136x52
+            LayoutTableCell {TD} at (2,907) size 132x52 [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "15"
+          LayoutTableRow {TR} at (0,961) size 136x52
+            LayoutTableCell {TD} at (2,961) size 132x52 [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "16"
+          LayoutTableRow {TR} at (0,1015) size 136x52
+            LayoutTableCell {TD} at (2,1015) size 132x52 [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "17"
+          LayoutTableRow {TR} at (0,1069) size 136x52
+            LayoutTableCell {TD} at (2,1069) size 132x52 [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "18"
+          LayoutTableRow {TR} at (0,1123) size 136x52
+            LayoutTableCell {TD} at (2,1123) size 132x52 [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "19"
+          LayoutTableRow {TR} at (0,1177) size 136x52
+            LayoutTableCell {TD} at (2,1177) size 132x52 [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "20"
+          LayoutTableRow {TR} at (0,1231) size 136x52
+            LayoutTableCell {TD} at (2,1231) size 132x52 [r=20 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "21"
+          LayoutTableRow {TR} at (0,1285) size 136x52
+            LayoutTableCell {TD} at (2,1285) size 132x52 [r=21 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "22"
+          LayoutTableRow {TR} at (0,1490) size 136x52
+            LayoutTableCell {TD} at (2,1490) size 132x52 [r=22 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "23"
+          LayoutTableRow {TR} at (0,1544) size 136x52
+            LayoutTableCell {TD} at (2,1544) size 132x52 [r=23 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "24"
+          LayoutTableRow {TR} at (0,1598) size 136x52
+            LayoutTableCell {TD} at (2,1598) size 132x52 [r=24 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "25"
+          LayoutTableRow {TR} at (0,1652) size 136x52
+            LayoutTableCell {TD} at (2,1652) size 132x52 [r=25 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "26"
+          LayoutTableRow {TR} at (0,1706) size 136x52
+            LayoutTableCell {TD} at (2,1706) size 132x52 [r=26 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "27"
+          LayoutTableRow {TR} at (0,1760) size 136x52
+            LayoutTableCell {TD} at (2,1760) size 132x52 [r=27 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "28"
+          LayoutTableRow {TR} at (0,1814) size 136x52
+            LayoutTableCell {TD} at (2,1814) size 132x52 [r=28 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "29"
+          LayoutTableRow {TR} at (0,1868) size 136x52
+            LayoutTableCell {TD} at (2,1868) size 132x52 [r=29 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "30"
+          LayoutTableRow {TR} at (0,1922) size 136x52
+            LayoutTableCell {TD} at (2,1922) size 132x52 [r=30 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "31"
+          LayoutTableRow {TR} at (0,1976) size 136x52
+            LayoutTableCell {TD} at (2,1976) size 132x52 [r=31 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "32"
+        LayoutTableSection {TFOOT} at (0,2086) size 136x54
+          LayoutTableRow {TR} at (0,0) size 136x52
+            LayoutTableCell {TH} at (2,0) size 132x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (5,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#FF0000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (35,8) size 92x35
+                text run at (35,8) width 8: " "
+                text run at (43,8) width 84: "Footer"
diff --git a/third_party/WebKit/LayoutTests/platform/win/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png b/third_party/WebKit/LayoutTests/platform/win/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
new file mode 100644
index 0000000..62190e4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt b/third_party/WebKit/LayoutTests/platform/win/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt
new file mode 100644
index 0000000..1940bd8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt
@@ -0,0 +1,157 @@
+layer at (0,0) size 800x600 scrollHeight 4368
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x4368 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutBlockFlow {HTML} at (0,0) size 1046x4368
+    LayoutBlockFlow {BODY} at (8,8) size 1030x4352
+      LayoutBlockFlow (anonymous) at (0,0) size 1030x100
+        LayoutText {#text} at (0,8) size 958x84
+          text run at (0,8) width 958: "Passes if there is a green circle before each \"Header\" and a red circle before each"
+          text run at (0,58) width 110: "\"Footer\"."
+      LayoutBlockFlow {DIV} at (0,100) size 1030x1000
+      LayoutTable {TABLE} at (0,1100) size 136x2252
+        LayoutTableSection {THEAD} at (0,0) size 136x56
+          LayoutTableRow {TR} at (0,2) size 136x52
+            LayoutTableCell {TH} at (2,2) size 132x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (1,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#008000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (31,8) size 100x35
+                text run at (31,8) width 8: " "
+                text run at (39,8) width 92: "Header"
+        LayoutTableSection {TBODY} at (0,56) size 136x2142
+          LayoutTableRow {TR} at (0,0) size 136x52
+            LayoutTableCell {TD} at (2,0) size 132x52 [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "1"
+          LayoutTableRow {TR} at (0,54) size 136x52
+            LayoutTableCell {TD} at (2,54) size 132x52 [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "2"
+          LayoutTableRow {TR} at (0,108) size 136x52
+            LayoutTableCell {TD} at (2,108) size 132x52 [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "3"
+          LayoutTableRow {TR} at (0,162) size 136x52
+            LayoutTableCell {TD} at (2,162) size 132x52 [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "4"
+          LayoutTableRow {TR} at (0,216) size 136x52
+            LayoutTableCell {TD} at (2,216) size 132x52 [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "5"
+          LayoutTableRow {TR} at (0,270) size 136x52
+            LayoutTableCell {TD} at (2,270) size 132x52 [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "6"
+          LayoutTableRow {TR} at (0,324) size 136x52
+            LayoutTableCell {TD} at (2,324) size 132x52 [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "7"
+          LayoutTableRow {TR} at (0,490) size 136x52
+            LayoutTableCell {TD} at (2,490) size 132x52 [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "8"
+          LayoutTableRow {TR} at (0,544) size 136x52
+            LayoutTableCell {TD} at (2,544) size 132x52 [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "9"
+          LayoutTableRow {TR} at (0,598) size 136x52
+            LayoutTableCell {TD} at (2,598) size 132x52 [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "10"
+          LayoutTableRow {TR} at (0,652) size 136x52
+            LayoutTableCell {TD} at (2,652) size 132x52 [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 29x34
+                text run at (1,9) width 29: "11"
+          LayoutTableRow {TR} at (0,706) size 136x52
+            LayoutTableCell {TD} at (2,706) size 132x52 [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "12"
+          LayoutTableRow {TR} at (0,760) size 136x52
+            LayoutTableCell {TD} at (2,760) size 132x52 [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "13"
+          LayoutTableRow {TR} at (0,814) size 136x52
+            LayoutTableCell {TD} at (2,814) size 132x52 [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "14"
+          LayoutTableRow {TR} at (0,868) size 136x52
+            LayoutTableCell {TD} at (2,868) size 132x52 [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "15"
+          LayoutTableRow {TR} at (0,922) size 136x52
+            LayoutTableCell {TD} at (2,922) size 132x52 [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "16"
+          LayoutTableRow {TR} at (0,976) size 136x52
+            LayoutTableCell {TD} at (2,976) size 132x52 [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "17"
+          LayoutTableRow {TR} at (0,1030) size 136x52
+            LayoutTableCell {TD} at (2,1030) size 132x52 [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "18"
+          LayoutTableRow {TR} at (0,1084) size 136x52
+            LayoutTableCell {TD} at (2,1084) size 132x52 [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "19"
+          LayoutTableRow {TR} at (0,1289) size 136x52
+            LayoutTableCell {TD} at (2,1289) size 132x52 [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "20"
+          LayoutTableRow {TR} at (0,1343) size 136x52
+            LayoutTableCell {TD} at (2,1343) size 132x52 [r=20 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "21"
+          LayoutTableRow {TR} at (0,1397) size 136x52
+            LayoutTableCell {TD} at (2,1397) size 132x52 [r=21 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "22"
+          LayoutTableRow {TR} at (0,1451) size 136x52
+            LayoutTableCell {TD} at (2,1451) size 132x52 [r=22 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "23"
+          LayoutTableRow {TR} at (0,1505) size 136x52
+            LayoutTableCell {TD} at (2,1505) size 132x52 [r=23 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "24"
+          LayoutTableRow {TR} at (0,1559) size 136x52
+            LayoutTableCell {TD} at (2,1559) size 132x52 [r=24 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "25"
+          LayoutTableRow {TR} at (0,1613) size 136x52
+            LayoutTableCell {TD} at (2,1613) size 132x52 [r=25 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "26"
+          LayoutTableRow {TR} at (0,1667) size 136x52
+            LayoutTableCell {TD} at (2,1667) size 132x52 [r=26 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "27"
+          LayoutTableRow {TR} at (0,1721) size 136x52
+            LayoutTableCell {TD} at (2,1721) size 132x52 [r=27 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "28"
+          LayoutTableRow {TR} at (0,1775) size 136x52
+            LayoutTableCell {TD} at (2,1775) size 132x52 [r=28 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "29"
+          LayoutTableRow {TR} at (0,1829) size 136x52
+            LayoutTableCell {TD} at (2,1829) size 132x52 [r=29 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "30"
+          LayoutTableRow {TR} at (0,1883) size 136x52
+            LayoutTableCell {TD} at (2,1883) size 132x52 [r=30 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "31"
+          LayoutTableRow {TR} at (0,2088) size 136x52
+            LayoutTableCell {TD} at (2,2088) size 132x52 [r=31 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "32"
+        LayoutTableSection {TFOOT} at (0,2198) size 136x54
+          LayoutTableRow {TR} at (0,0) size 136x52
+            LayoutTableCell {TH} at (2,0) size 132x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (5,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#FF0000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (35,8) size 92x35
+                text run at (35,8) width 8: " "
+                text run at (43,8) width 84: "Footer"
+      LayoutBlockFlow {DIV} at (0,3352) size 1030x1000
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.txt
new file mode 100644
index 0000000..0ad17a08
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/avoid-setting-header-offset-on-header-expected.txt
@@ -0,0 +1,218 @@
+layer at (0,0) size 800x600 scrollHeight 975
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x975 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutNGBlockFlow {HTML} at (0,0) size 1046x975
+    LayoutNGBlockFlow {BODY} at (0,0) size 1046x975
+      LayoutTable {TABLE} at (0,0) size 171x975
+        LayoutTableSection {THEAD} at (0,0) size 171x44
+          LayoutTableRow {TR} at (0,0) size 171x44
+            LayoutNGTableCell {TD} at (0,10) size 57x24 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 53x19
+                text run at (2,12) width 53: "header 1"
+            LayoutNGTableCell {TD} at (57,10) size 57x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 53x19
+                text run at (2,12) width 53: "header 2"
+            LayoutNGTableCell {TD} at (114,10) size 57x24 [border: (1px solid #000000)] [r=0 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 53x19
+                text run at (2,12) width 53: "header 3"
+        LayoutTableSection {TBODY} at (0,44) size 171x931
+          LayoutTableRow {TR} at (0,0) size 171x44
+            LayoutNGTableCell {TD} at (0,10) size 57x24 [border: (1px solid #000000)] [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "1-1"
+            LayoutNGTableCell {TD} at (57,10) size 57x24 [border: (1px solid #000000)] [r=0 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "2-1"
+            LayoutNGTableCell {TD} at (114,10) size 57x24 [border: (1px solid #000000)] [r=0 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "3-1"
+          LayoutTableRow {TR} at (0,44) size 171x44
+            LayoutNGTableCell {TD} at (0,54) size 57x24 [border: (1px solid #000000)] [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "1-2"
+            LayoutNGTableCell {TD} at (57,54) size 57x24 [border: (1px solid #000000)] [r=1 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "2-2"
+            LayoutNGTableCell {TD} at (114,54) size 57x24 [border: (1px solid #000000)] [r=1 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "3-2"
+          LayoutTableRow {TR} at (0,88) size 171x44
+            LayoutNGTableCell {TD} at (0,98) size 57x24 [border: (1px solid #000000)] [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "1-3"
+            LayoutNGTableCell {TD} at (57,98) size 57x24 [border: (1px solid #000000)] [r=2 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "2-3"
+            LayoutNGTableCell {TD} at (114,98) size 57x24 [border: (1px solid #000000)] [r=2 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "3-3"
+          LayoutTableRow {TR} at (0,132) size 171x44
+            LayoutNGTableCell {TD} at (0,142) size 57x24 [border: (1px solid #000000)] [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "1-4"
+            LayoutNGTableCell {TD} at (57,142) size 57x24 [border: (1px solid #000000)] [r=3 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "2-4"
+            LayoutNGTableCell {TD} at (114,142) size 57x24 [border: (1px solid #000000)] [r=3 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "3-4"
+          LayoutTableRow {TR} at (0,176) size 171x44
+            LayoutNGTableCell {TD} at (0,186) size 57x24 [border: (1px solid #000000)] [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "1-5"
+            LayoutNGTableCell {TD} at (57,186) size 57x24 [border: (1px solid #000000)] [r=4 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "2-5"
+            LayoutNGTableCell {TD} at (114,186) size 57x24 [border: (1px solid #000000)] [r=4 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "3-5"
+          LayoutTableRow {TR} at (0,220) size 171x44
+            LayoutNGTableCell {TD} at (0,230) size 57x24 [border: (1px solid #000000)] [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "1-6"
+            LayoutNGTableCell {TD} at (57,230) size 57x24 [border: (1px solid #000000)] [r=5 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "2-6"
+            LayoutNGTableCell {TD} at (114,230) size 57x24 [border: (1px solid #000000)] [r=5 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "3-6"
+          LayoutTableRow {TR} at (0,264) size 171x44
+            LayoutNGTableCell {TD} at (0,274) size 57x24 [border: (1px solid #000000)] [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "1-7"
+            LayoutNGTableCell {TD} at (57,274) size 57x24 [border: (1px solid #000000)] [r=6 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "2-7"
+            LayoutNGTableCell {TD} at (114,274) size 57x24 [border: (1px solid #000000)] [r=6 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "3-7"
+          LayoutTableRow {TR} at (0,308) size 171x44
+            LayoutNGTableCell {TD} at (0,318) size 57x24 [border: (1px solid #000000)] [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "1-8"
+            LayoutNGTableCell {TD} at (57,318) size 57x24 [border: (1px solid #000000)] [r=7 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "2-8"
+            LayoutNGTableCell {TD} at (114,318) size 57x24 [border: (1px solid #000000)] [r=7 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "3-8"
+          LayoutTableRow {TR} at (0,352) size 171x44
+            LayoutNGTableCell {TD} at (0,362) size 57x24 [border: (1px solid #000000)] [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "1-9"
+            LayoutNGTableCell {TD} at (57,362) size 57x24 [border: (1px solid #000000)] [r=8 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "2-9"
+            LayoutNGTableCell {TD} at (114,362) size 57x24 [border: (1px solid #000000)] [r=8 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 22x19
+                text run at (2,12) width 22: "3-9"
+          LayoutTableRow {TR} at (0,396) size 171x44
+            LayoutNGTableCell {TD} at (0,406) size 57x24 [border: (1px solid #000000)] [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "1-10"
+            LayoutNGTableCell {TD} at (57,406) size 57x24 [border: (1px solid #000000)] [r=9 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "2-10"
+            LayoutNGTableCell {TD} at (114,406) size 57x24 [border: (1px solid #000000)] [r=9 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "3-10"
+          LayoutTableRow {TR} at (0,440) size 171x44
+            LayoutNGTableCell {TD} at (0,450) size 57x24 [border: (1px solid #000000)] [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "1-11"
+            LayoutNGTableCell {TD} at (57,450) size 57x24 [border: (1px solid #000000)] [r=10 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "2-11"
+            LayoutNGTableCell {TD} at (114,450) size 57x24 [border: (1px solid #000000)] [r=10 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 29x19
+                text run at (2,12) width 29: "3-11"
+          LayoutTableRow {TR} at (0,484) size 171x44
+            LayoutNGTableCell {TD} at (0,494) size 57x24 [border: (1px solid #000000)] [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "1-12"
+            LayoutNGTableCell {TD} at (57,494) size 57x24 [border: (1px solid #000000)] [r=11 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "2-12"
+            LayoutNGTableCell {TD} at (114,494) size 57x24 [border: (1px solid #000000)] [r=11 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "3-12"
+          LayoutTableRow {TR} at (0,528) size 171x44
+            LayoutNGTableCell {TD} at (0,538) size 57x24 [border: (1px solid #000000)] [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "1-13"
+            LayoutNGTableCell {TD} at (57,538) size 57x24 [border: (1px solid #000000)] [r=12 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "2-13"
+            LayoutNGTableCell {TD} at (114,538) size 57x24 [border: (1px solid #000000)] [r=12 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "3-13"
+          LayoutTableRow {TR} at (0,572) size 171x44
+            LayoutNGTableCell {TD} at (0,582) size 57x24 [border: (1px solid #000000)] [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "1-14"
+            LayoutNGTableCell {TD} at (57,582) size 57x24 [border: (1px solid #000000)] [r=13 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "2-14"
+            LayoutNGTableCell {TD} at (114,582) size 57x24 [border: (1px solid #000000)] [r=13 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "3-14"
+          LayoutTableRow {TR} at (0,616) size 171x44
+            LayoutNGTableCell {TD} at (0,626) size 57x24 [border: (1px solid #000000)] [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "1-15"
+            LayoutNGTableCell {TD} at (57,626) size 57x24 [border: (1px solid #000000)] [r=14 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "2-15"
+            LayoutNGTableCell {TD} at (114,626) size 57x24 [border: (1px solid #000000)] [r=14 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "3-15"
+          LayoutTableRow {TR} at (0,660) size 171x44
+            LayoutNGTableCell {TD} at (0,670) size 57x24 [border: (1px solid #000000)] [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "1-16"
+            LayoutNGTableCell {TD} at (57,670) size 57x24 [border: (1px solid #000000)] [r=15 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "2-16"
+            LayoutNGTableCell {TD} at (114,670) size 57x24 [border: (1px solid #000000)] [r=15 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "3-16"
+          LayoutTableRow {TR} at (0,704) size 171x44
+            LayoutNGTableCell {TD} at (0,714) size 57x24 [border: (1px solid #000000)] [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "1-17"
+            LayoutNGTableCell {TD} at (57,714) size 57x24 [border: (1px solid #000000)] [r=16 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "2-17"
+            LayoutNGTableCell {TD} at (114,714) size 57x24 [border: (1px solid #000000)] [r=16 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "3-17"
+          LayoutTableRow {TR} at (0,799) size 171x44
+            LayoutNGTableCell {TD} at (0,809) size 57x24 [border: (1px solid #000000)] [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "1-18"
+            LayoutNGTableCell {TD} at (57,809) size 57x24 [border: (1px solid #000000)] [r=17 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "2-18"
+            LayoutNGTableCell {TD} at (114,809) size 57x24 [border: (1px solid #000000)] [r=17 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "3-18"
+          LayoutTableRow {TR} at (0,843) size 171x44
+            LayoutNGTableCell {TD} at (0,853) size 57x24 [border: (1px solid #000000)] [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "1-19"
+            LayoutNGTableCell {TD} at (57,853) size 57x24 [border: (1px solid #000000)] [r=18 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "2-19"
+            LayoutNGTableCell {TD} at (114,853) size 57x24 [border: (1px solid #000000)] [r=18 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "3-19"
+          LayoutTableRow {TR} at (0,887) size 171x44
+            LayoutNGTableCell {TD} at (0,897) size 57x24 [border: (1px solid #000000)] [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "1-20"
+            LayoutNGTableCell {TD} at (57,897) size 57x24 [border: (1px solid #000000)] [r=19 c=1 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "2-20"
+            LayoutNGTableCell {TD} at (114,897) size 57x24 [border: (1px solid #000000)] [r=19 c=2 rs=1 cs=1]
+              LayoutText {#text} at (2,12) size 30x19
+                text run at (2,12) width 30: "3-20"
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png
new file mode 100644
index 0000000..d182b47
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.txt
new file mode 100644
index 0000000..32fbfb8e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-expected.txt
@@ -0,0 +1,153 @@
+layer at (0,0) size 800x600 scrollHeight 2264
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x2264 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutNGBlockFlow {HTML} at (0,0) size 1046x2264
+    LayoutNGBlockFlow {BODY} at (8,8) size 1030x2248
+      LayoutNGBlockFlow (anonymous) at (0,0) size 1030x100
+        LayoutText {#text} at (0,8) size 958x84
+          text run at (0,8) width 958: "Passes if there is a green circle before each \"Header\" and a red circle before each"
+          text run at (0,58) width 110: "\"Footer\"."
+      LayoutTable {TABLE} at (0,100) size 136x2148
+        LayoutTableSection {THEAD} at (0,0) size 136x56
+          LayoutTableRow {TR} at (0,2) size 136x52
+            LayoutNGTableCell {TH} at (2,2) size 132x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (1,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#008000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (31,8) size 100x35
+                text run at (31,8) width 100: " Header"
+        LayoutTableSection {TBODY} at (0,56) size 136x2038
+          LayoutTableRow {TR} at (0,0) size 136x52
+            LayoutNGTableCell {TD} at (2,0) size 132x52 [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "1"
+          LayoutTableRow {TR} at (0,54) size 136x52
+            LayoutNGTableCell {TD} at (2,54) size 132x52 [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "2"
+          LayoutTableRow {TR} at (0,108) size 136x52
+            LayoutNGTableCell {TD} at (2,108) size 132x52 [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "3"
+          LayoutTableRow {TR} at (0,162) size 136x52
+            LayoutNGTableCell {TD} at (2,162) size 132x52 [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "4"
+          LayoutTableRow {TR} at (0,216) size 136x52
+            LayoutNGTableCell {TD} at (2,216) size 132x52 [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "5"
+          LayoutTableRow {TR} at (0,270) size 136x52
+            LayoutNGTableCell {TD} at (2,270) size 132x52 [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "6"
+          LayoutTableRow {TR} at (0,324) size 136x52
+            LayoutNGTableCell {TD} at (2,324) size 132x52 [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "7"
+          LayoutTableRow {TR} at (0,378) size 136x52
+            LayoutNGTableCell {TD} at (2,378) size 132x52 [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "8"
+          LayoutTableRow {TR} at (0,432) size 136x52
+            LayoutNGTableCell {TD} at (2,432) size 132x52 [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "9"
+          LayoutTableRow {TR} at (0,486) size 136x52
+            LayoutNGTableCell {TD} at (2,486) size 132x52 [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "10"
+          LayoutTableRow {TR} at (0,699) size 136x52
+            LayoutNGTableCell {TD} at (2,699) size 132x52 [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 29x34
+                text run at (1,9) width 29: "11"
+          LayoutTableRow {TR} at (0,753) size 136x52
+            LayoutNGTableCell {TD} at (2,753) size 132x52 [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "12"
+          LayoutTableRow {TR} at (0,807) size 136x52
+            LayoutNGTableCell {TD} at (2,807) size 132x52 [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "13"
+          LayoutTableRow {TR} at (0,861) size 136x52
+            LayoutNGTableCell {TD} at (2,861) size 132x52 [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "14"
+          LayoutTableRow {TR} at (0,915) size 136x52
+            LayoutNGTableCell {TD} at (2,915) size 132x52 [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "15"
+          LayoutTableRow {TR} at (0,969) size 136x52
+            LayoutNGTableCell {TD} at (2,969) size 132x52 [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "16"
+          LayoutTableRow {TR} at (0,1023) size 136x52
+            LayoutNGTableCell {TD} at (2,1023) size 132x52 [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "17"
+          LayoutTableRow {TR} at (0,1077) size 136x52
+            LayoutNGTableCell {TD} at (2,1077) size 132x52 [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "18"
+          LayoutTableRow {TR} at (0,1131) size 136x52
+            LayoutNGTableCell {TD} at (2,1131) size 132x52 [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "19"
+          LayoutTableRow {TR} at (0,1185) size 136x52
+            LayoutNGTableCell {TD} at (2,1185) size 132x52 [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "20"
+          LayoutTableRow {TR} at (0,1239) size 136x52
+            LayoutNGTableCell {TD} at (2,1239) size 132x52 [r=20 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "21"
+          LayoutTableRow {TR} at (0,1293) size 136x52
+            LayoutNGTableCell {TD} at (2,1293) size 132x52 [r=21 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "22"
+          LayoutTableRow {TR} at (0,1498) size 136x52
+            LayoutNGTableCell {TD} at (2,1498) size 132x52 [r=22 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "23"
+          LayoutTableRow {TR} at (0,1552) size 136x52
+            LayoutNGTableCell {TD} at (2,1552) size 132x52 [r=23 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "24"
+          LayoutTableRow {TR} at (0,1606) size 136x52
+            LayoutNGTableCell {TD} at (2,1606) size 132x52 [r=24 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "25"
+          LayoutTableRow {TR} at (0,1660) size 136x52
+            LayoutNGTableCell {TD} at (2,1660) size 132x52 [r=25 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "26"
+          LayoutTableRow {TR} at (0,1714) size 136x52
+            LayoutNGTableCell {TD} at (2,1714) size 132x52 [r=26 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "27"
+          LayoutTableRow {TR} at (0,1768) size 136x52
+            LayoutNGTableCell {TD} at (2,1768) size 132x52 [r=27 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "28"
+          LayoutTableRow {TR} at (0,1822) size 136x52
+            LayoutNGTableCell {TD} at (2,1822) size 132x52 [r=28 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "29"
+          LayoutTableRow {TR} at (0,1876) size 136x52
+            LayoutNGTableCell {TD} at (2,1876) size 132x52 [r=29 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "30"
+          LayoutTableRow {TR} at (0,1930) size 136x52
+            LayoutNGTableCell {TD} at (2,1930) size 132x52 [r=30 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "31"
+          LayoutTableRow {TR} at (0,1984) size 136x52
+            LayoutNGTableCell {TD} at (2,1984) size 132x52 [r=31 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "32"
+        LayoutTableSection {TFOOT} at (0,2094) size 136x54
+          LayoutTableRow {TR} at (0,0) size 136x52
+            LayoutNGTableCell {TH} at (2,0) size 132x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (5.50,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#FF0000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (35,8) size 92x35
+                text run at (35,8) width 92: " Footer"
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
new file mode 100644
index 0000000..ef6a78a2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt
new file mode 100644
index 0000000..9a89c41
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/layout_ng_experimental/printing/thead-tfoot-containing-svg-repeat-extra-pages-expected.txt
@@ -0,0 +1,155 @@
+layer at (0,0) size 800x600 scrollHeight 4376
+  LayoutView at (0,0) size 1046x799
+layer at (0,0) size 1046x4376 backgroundClip at (0,0) size 800x600 clip at (0,0) size 800x600
+  LayoutNGBlockFlow {HTML} at (0,0) size 1046x4376
+    LayoutNGBlockFlow {BODY} at (8,8) size 1030x4360
+      LayoutNGBlockFlow (anonymous) at (0,0) size 1030x100
+        LayoutText {#text} at (0,8) size 958x84
+          text run at (0,8) width 958: "Passes if there is a green circle before each \"Header\" and a red circle before each"
+          text run at (0,58) width 110: "\"Footer\"."
+      LayoutNGBlockFlow {DIV} at (0,100) size 1030x1000
+      LayoutTable {TABLE} at (0,1100) size 136x2260
+        LayoutTableSection {THEAD} at (0,0) size 136x56
+          LayoutTableRow {TR} at (0,2) size 136x52
+            LayoutNGTableCell {TH} at (2,2) size 132x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (1,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#008000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (31,8) size 100x35
+                text run at (31,8) width 100: " Header"
+        LayoutTableSection {TBODY} at (0,56) size 136x2150
+          LayoutTableRow {TR} at (0,0) size 136x52
+            LayoutNGTableCell {TD} at (2,0) size 132x52 [r=0 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "1"
+          LayoutTableRow {TR} at (0,54) size 136x52
+            LayoutNGTableCell {TD} at (2,54) size 132x52 [r=1 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "2"
+          LayoutTableRow {TR} at (0,108) size 136x52
+            LayoutNGTableCell {TD} at (2,108) size 132x52 [r=2 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "3"
+          LayoutTableRow {TR} at (0,162) size 136x52
+            LayoutNGTableCell {TD} at (2,162) size 132x52 [r=3 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "4"
+          LayoutTableRow {TR} at (0,216) size 136x52
+            LayoutNGTableCell {TD} at (2,216) size 132x52 [r=4 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "5"
+          LayoutTableRow {TR} at (0,270) size 136x52
+            LayoutNGTableCell {TD} at (2,270) size 132x52 [r=5 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "6"
+          LayoutTableRow {TR} at (0,324) size 136x52
+            LayoutNGTableCell {TD} at (2,324) size 132x52 [r=6 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "7"
+          LayoutTableRow {TR} at (0,498) size 136x52
+            LayoutNGTableCell {TD} at (2,498) size 132x52 [r=7 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "8"
+          LayoutTableRow {TR} at (0,552) size 136x52
+            LayoutNGTableCell {TD} at (2,552) size 132x52 [r=8 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 15x34
+                text run at (1,9) width 15: "9"
+          LayoutTableRow {TR} at (0,606) size 136x52
+            LayoutNGTableCell {TD} at (2,606) size 132x52 [r=9 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "10"
+          LayoutTableRow {TR} at (0,660) size 136x52
+            LayoutNGTableCell {TD} at (2,660) size 132x52 [r=10 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 29x34
+                text run at (1,9) width 29: "11"
+          LayoutTableRow {TR} at (0,714) size 136x52
+            LayoutNGTableCell {TD} at (2,714) size 132x52 [r=11 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "12"
+          LayoutTableRow {TR} at (0,768) size 136x52
+            LayoutNGTableCell {TD} at (2,768) size 132x52 [r=12 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "13"
+          LayoutTableRow {TR} at (0,822) size 136x52
+            LayoutNGTableCell {TD} at (2,822) size 132x52 [r=13 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "14"
+          LayoutTableRow {TR} at (0,876) size 136x52
+            LayoutNGTableCell {TD} at (2,876) size 132x52 [r=14 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "15"
+          LayoutTableRow {TR} at (0,930) size 136x52
+            LayoutNGTableCell {TD} at (2,930) size 132x52 [r=15 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "16"
+          LayoutTableRow {TR} at (0,984) size 136x52
+            LayoutNGTableCell {TD} at (2,984) size 132x52 [r=16 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "17"
+          LayoutTableRow {TR} at (0,1038) size 136x52
+            LayoutNGTableCell {TD} at (2,1038) size 132x52 [r=17 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "18"
+          LayoutTableRow {TR} at (0,1092) size 136x52
+            LayoutNGTableCell {TD} at (2,1092) size 132x52 [r=18 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "19"
+          LayoutTableRow {TR} at (0,1297) size 136x52
+            LayoutNGTableCell {TD} at (2,1297) size 132x52 [r=19 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "20"
+          LayoutTableRow {TR} at (0,1351) size 136x52
+            LayoutNGTableCell {TD} at (2,1351) size 132x52 [r=20 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "21"
+          LayoutTableRow {TR} at (0,1405) size 136x52
+            LayoutNGTableCell {TD} at (2,1405) size 132x52 [r=21 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "22"
+          LayoutTableRow {TR} at (0,1459) size 136x52
+            LayoutNGTableCell {TD} at (2,1459) size 132x52 [r=22 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "23"
+          LayoutTableRow {TR} at (0,1513) size 136x52
+            LayoutNGTableCell {TD} at (2,1513) size 132x52 [r=23 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "24"
+          LayoutTableRow {TR} at (0,1567) size 136x52
+            LayoutNGTableCell {TD} at (2,1567) size 132x52 [r=24 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "25"
+          LayoutTableRow {TR} at (0,1621) size 136x52
+            LayoutNGTableCell {TD} at (2,1621) size 132x52 [r=25 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "26"
+          LayoutTableRow {TR} at (0,1675) size 136x52
+            LayoutNGTableCell {TD} at (2,1675) size 132x52 [r=26 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "27"
+          LayoutTableRow {TR} at (0,1729) size 136x52
+            LayoutNGTableCell {TD} at (2,1729) size 132x52 [r=27 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "28"
+          LayoutTableRow {TR} at (0,1783) size 136x52
+            LayoutNGTableCell {TD} at (2,1783) size 132x52 [r=28 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "29"
+          LayoutTableRow {TR} at (0,1837) size 136x52
+            LayoutNGTableCell {TD} at (2,1837) size 132x52 [r=29 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "30"
+          LayoutTableRow {TR} at (0,1891) size 136x52
+            LayoutNGTableCell {TD} at (2,1891) size 132x52 [r=30 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "31"
+          LayoutTableRow {TR} at (0,2096) size 136x52
+            LayoutNGTableCell {TD} at (2,2096) size 132x52 [r=31 c=0 rs=1 cs=1]
+              LayoutText {#text} at (1,9) size 30x34
+                text run at (1,9) width 30: "32"
+        LayoutTableSection {TFOOT} at (0,2206) size 136x54
+          LayoutTableRow {TR} at (0,0) size 136x52
+            LayoutNGTableCell {TH} at (2,0) size 132x52 [r=0 c=0 rs=1 cs=1]
+              LayoutSVGRoot {svg} at (5.50,6) size 30x30
+                LayoutSVGEllipse {circle} at (0,0) size 30x30 [fill={[type=SOLID] [color=#FF0000]}] [cx=15.00] [cy=15.00] [r=15.00]
+              LayoutText {#text} at (35,8) size 92x35
+                text run at (35,8) width 92: " Footer"
+      LayoutNGBlockFlow {DIV} at (0,3360) size 1030x1000
diff --git a/third_party/WebKit/LayoutTests/platform/win7/paint/invalidation/svg/zoom-foreignObject-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/invalidation/svg/zoom-foreignObject-expected.png
deleted file mode 100644
index 093fffa..0000000
--- a/third_party/WebKit/LayoutTests/platform/win7/paint/invalidation/svg/zoom-foreignObject-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/printing/avoid-setting-header-offset-on-header-expected.html b/third_party/WebKit/LayoutTests/printing/avoid-setting-header-offset-on-header-expected.html
deleted file mode 100644
index 34daf50..0000000
--- a/third_party/WebKit/LayoutTests/printing/avoid-setting-header-offset-on-header-expected.html
+++ /dev/null
@@ -1,133 +0,0 @@
-<!DOCTYPE html>
-<body>
-<style>
-body {
-	margin: 0px;
-}
-td {
-  border: 1px solid black;
-  height: 40px;
-}
-.header {
-  break-inside: avoid;
-}
-</style>
-<script>
-if (window.testRunner)
-    testRunner.setPrinting();
-</script>
-<!-- crbug.com/702605: Header uses offset intended for rows. The first header row should be at the top of the first page. -->
-<table cellspacing="0">
-    <tr>
-      <td>header 1</td>
-      <td>header 2</td>
-      <td>header 3</td>
-    </tr>
-    <tr>
-      <td>1-1</td>
-      <td>2-1</td>
-      <td>3-1</td>
-    </tr>
-    <tr>
-      <td>1-2</td>
-      <td>2-2</td>
-      <td>3-2</td>
-    </tr>
-    <tr>
-      <td>1-3</td>
-      <td>2-3</td>
-      <td>3-3</td>
-    </tr>
-    <tr>
-      <td>1-4</td>
-      <td>2-4</td>
-      <td>3-4</td>
-    </tr>
-    <tr>
-      <td>1-5</td>
-      <td>2-5</td>
-      <td>3-5</td>
-    </tr>
-    <tr>
-      <td>1-6</td>
-      <td>2-6</td>
-      <td>3-6</td>
-    </tr>
-    <tr>
-      <td>1-7</td>
-      <td>2-7</td>
-      <td>3-7</td>
-    </tr>
-    <tr>
-      <td>1-8</td>
-      <td>2-8</td>
-      <td>3-8</td>
-    </tr>
-    <tr>
-      <td>1-9</td>
-      <td>2-9</td>
-      <td>3-9</td>
-    </tr>
-    <tr>
-      <td>1-10</td>
-      <td>2-10</td>
-      <td>3-10</td>
-    </tr>
-    <tr>
-      <td>1-11</td>
-      <td>2-11</td>
-      <td>3-11</td>
-    </tr>
-    <tr>
-      <td>1-12</td>
-      <td>2-12</td>
-      <td>3-12</td>
-    </tr>
-    <tr>
-      <td>1-13</td>
-      <td>2-13</td>
-      <td>3-13</td>
-    </tr>
-    <tr>
-      <td>1-14</td>
-      <td>2-14</td>
-      <td>3-14</td>
-    </tr>
-    <tr>
-      <td>1-15</td>
-      <td>2-15</td>
-      <td>3-15</td>
-    </tr>
-    <tr>
-      <td>1-16</td>
-      <td>2-16</td>
-      <td>3-16</td>
-    </tr>
-    <tr>
-      <td>1-17</td>
-      <td>2-17</td>
-      <td>3-17</td>
-    </tr>
-    <tr class="header">
-      <td>header 1</td>
-      <td>header 2</td>
-      <td>header 3</td>
-    </tr>
-    <tr>
-      <td>1-18</td>
-      <td>2-18</td>
-      <td>3-18</td>
-    </tr>
-    <tr>
-      <td>1-19</td>
-      <td>2-19</td>
-      <td>3-19</td>
-    </tr>
-    <tr>
-      <td>1-20</td>
-      <td>2-20</td>
-      <td>3-20</td>
-    </tr>
-  </tbody>
-  </table>
-</body>
diff --git a/third_party/WebKit/LayoutTests/printing/thead-tfoot-containing-svg-repeat-extra-pages.html b/third_party/WebKit/LayoutTests/printing/thead-tfoot-containing-svg-repeat-extra-pages.html
new file mode 100644
index 0000000..d4293a25
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/printing/thead-tfoot-containing-svg-repeat-extra-pages.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<style>
+  body {
+    font-size: 30px;
+    line-height: 50px;
+  }
+</style>
+<script>
+  if (window.testRunner)
+    testRunner.setPrinting();
+</script>
+Passes if there is a green circle before each "Header" and a red circle before each "Footer".
+<div style="height: 1000px"></div>
+<table>
+  <thead>
+    <tr>
+      <th>
+        <svg height="30" width="30">
+          <circle cx="15" cy="15" r="15" fill="green" />
+        </svg>
+        Header
+      </th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>1</td>
+    </tr>
+    <tr>
+      <td>2</td>
+    </tr>
+    <tr>
+      <td>3</td>
+    </tr>
+    <tr>
+      <td>4</td>
+    </tr>
+    <tr>
+      <td>5</td>
+    </tr>
+    <tr>
+      <td>6</td>
+    </tr>
+    <tr>
+      <td>7</td>
+    </tr>
+    <tr>
+      <td>8</td>
+    </tr>
+    <tr>
+      <td>9</td>
+    </tr>
+    <tr>
+      <td>10</td>
+    </tr>
+    <tr>
+      <td>11</td>
+    </tr>
+    <tr>
+      <td>12</td>
+    </tr>
+    <tr>
+      <td>13</td>
+    </tr>
+    <tr>
+      <td>14</td>
+    </tr>
+    <tr>
+      <td>15</td>
+    </tr>
+    <tr>
+      <td>16</td>
+    </tr>
+    <tr>
+      <td>17</td>
+    </tr>
+    <tr>
+      <td>18</td>
+    </tr>
+    <tr>
+      <td>19</td>
+    </tr>
+    <tr>
+      <td>20</td>
+    </tr>
+    <tr>
+      <td>21</td>
+    </tr>
+    <tr>
+      <td>22</td>
+    </tr>
+    <tr>
+      <td>23</td>
+    </tr>
+    <tr>
+      <td>24</td>
+    </tr>
+    <tr>
+      <td>25</td>
+    </tr>
+    <tr>
+      <td>26</td>
+    </tr>
+    <tr>
+      <td>27</td>
+    </tr>
+    <tr>
+      <td>28</td>
+    </tr>
+    <tr>
+      <td>29</td>
+    </tr>
+    <tr>
+      <td>30</td>
+    </tr>
+    <tr>
+      <td>31</td>
+    </tr>
+    <tr>
+      <td>32</td>
+    </tr>
+  </tbody>
+  <tfoot>
+    <tr>
+      <th>
+        <svg height="30" width="30">
+          <circle cx="15" cy="15" r="15" fill="red" />
+        </svg>
+        Footer
+      </th>
+    </tr>
+  </tfoot>
+</table>
+<div style="height: 1000px"></div>
diff --git a/third_party/WebKit/LayoutTests/printing/thead-tfoot-containing-svg-repeat.html b/third_party/WebKit/LayoutTests/printing/thead-tfoot-containing-svg-repeat.html
new file mode 100644
index 0000000..134baa0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/printing/thead-tfoot-containing-svg-repeat.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<style>
+  body {
+    font-size: 30px;
+    line-height: 50px;
+  }
+</style>
+<script>
+  if (window.testRunner)
+    testRunner.setPrinting();
+</script>
+Passes if there is a green circle before each "Header" and a red circle before each "Footer".
+<table>
+  <thead>
+    <tr>
+      <th>
+        <svg height="30" width="30">
+          <circle cx="15" cy="15" r="15" fill="green" />
+        </svg>
+        Header
+      </th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>1</td>
+    </tr>
+    <tr>
+      <td>2</td>
+    </tr>
+    <tr>
+      <td>3</td>
+    </tr>
+    <tr>
+      <td>4</td>
+    </tr>
+    <tr>
+      <td>5</td>
+    </tr>
+    <tr>
+      <td>6</td>
+    </tr>
+    <tr>
+      <td>7</td>
+    </tr>
+    <tr>
+      <td>8</td>
+    </tr>
+    <tr>
+      <td>9</td>
+    </tr>
+    <tr>
+      <td>10</td>
+    </tr>
+    <tr>
+      <td>11</td>
+    </tr>
+    <tr>
+      <td>12</td>
+    </tr>
+    <tr>
+      <td>13</td>
+    </tr>
+    <tr>
+      <td>14</td>
+    </tr>
+    <tr>
+      <td>15</td>
+    </tr>
+    <tr>
+      <td>16</td>
+    </tr>
+    <tr>
+      <td>17</td>
+    </tr>
+    <tr>
+      <td>18</td>
+    </tr>
+    <tr>
+      <td>19</td>
+    </tr>
+    <tr>
+      <td>20</td>
+    </tr>
+    <tr>
+      <td>21</td>
+    </tr>
+    <tr>
+      <td>22</td>
+    </tr>
+    <tr>
+      <td>23</td>
+    </tr>
+    <tr>
+      <td>24</td>
+    </tr>
+    <tr>
+      <td>25</td>
+    </tr>
+    <tr>
+      <td>26</td>
+    </tr>
+    <tr>
+      <td>27</td>
+    </tr>
+    <tr>
+      <td>28</td>
+    </tr>
+    <tr>
+      <td>29</td>
+    </tr>
+    <tr>
+      <td>30</td>
+    </tr>
+    <tr>
+      <td>31</td>
+    </tr>
+    <tr>
+      <td>32</td>
+    </tr>
+  </tbody>
+  <tfoot>
+    <tr>
+      <th>
+        <svg height="30" width="30">
+          <circle cx="15" cy="15" r="15" fill="red" />
+        </svg>
+        Footer
+      </th>
+    </tr>
+  </tfoot>
+</table>
diff --git a/third_party/blink/public/platform/web_url_request.h b/third_party/blink/public/platform/web_url_request.h
index 318e7c01..5faad36 100644
--- a/third_party/blink/public/platform/web_url_request.h
+++ b/third_party/blink/public/platform/web_url_request.h
@@ -340,12 +340,6 @@
 
   BLINK_PLATFORM_EXPORT void SetNavigationStartTime(base::TimeTicks);
 
-  // PlzNavigate: specify that the request was intended to be loaded as a same
-  // document navigation. No network requests should be made and the request
-  // should be dropped if a different document was loaded in the frame
-  // in-between.
-  BLINK_PLATFORM_EXPORT void SetIsSameDocumentNavigation(bool);
-
   // If this request was created from an anchor with a download attribute, this
   // is the value provided there.
   BLINK_PLATFORM_EXPORT base::Optional<WebString> GetSuggestedFilename() const;
diff --git a/third_party/blink/renderer/build/scripts/core/css/make_media_features.py b/third_party/blink/renderer/build/scripts/core/css/make_media_features.py
index b77ebe8..487e6b3 100755
--- a/third_party/blink/renderer/build/scripts/core/css/make_media_features.py
+++ b/third_party/blink/renderer/build/scripts/core/css/make_media_features.py
@@ -8,10 +8,10 @@
 import sys
 sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
 
+from blinkbuild.name_style_converter import NameStyleConverter
 import media_feature_symbol
 import json5_generator
 import template_expander
-import name_utilities
 
 
 class MakeMediaFeaturesWriter(json5_generator.Writer):
@@ -21,7 +21,7 @@
     }
     filters = {
         'symbol': media_feature_symbol.getMediaFeatureSymbolWithSuffix(''),
-        'upper_first_letter': name_utilities.upper_first_letter,
+        'to_function_name': lambda symbol: NameStyleConverter(symbol).to_function_name(),
     }
 
     def __init__(self, json5_file_path, output_dir):
diff --git a/third_party/blink/renderer/build/scripts/core/css/templates/media_features.h.tmpl b/third_party/blink/renderer/build/scripts/core/css/templates/media_features.h.tmpl
index ad7696f2..4f7fe5d 100644
--- a/third_party/blink/renderer/build/scripts/core/css/templates/media_features.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/templates/media_features.h.tmpl
@@ -9,7 +9,7 @@
 #define CSS_MEDIAQUERY_NAMES_FOR_EACH_MEDIAFEATURE(macro) \
     {% for entry in entries %}
     {% set constant_prefix = entry | symbol %}
-    {% set method_prefix = constant_prefix | upper_first_letter %}
+    {% set method_prefix = constant_prefix | to_function_name %}
     macro({{constant_prefix}}, {{method_prefix}}){% if not loop.last %} \
     {% endif %}
     {% endfor %}
diff --git a/third_party/blink/renderer/build/scripts/json5_generator.py b/third_party/blink/renderer/build/scripts/json5_generator.py
index 4ef8c97..2a4ed7d 100644
--- a/third_party/blink/renderer/build/scripts/json5_generator.py
+++ b/third_party/blink/renderer/build/scripts/json5_generator.py
@@ -249,7 +249,6 @@
         self.gperf_path = gperf_path
 
     def get_file_basename(self, name):
-        # Use NameStyleConverter instead of name_utilities for consistency.
         return NameStyleConverter(name).to_snake_case()
 
     def make_header_guard(self, path):
diff --git a/third_party/blink/renderer/build/scripts/name_utilities.py b/third_party/blink/renderer/build/scripts/name_utilities.py
index 1c10e5e..d6afcc0 100644
--- a/third_party/blink/renderer/build/scripts/name_utilities.py
+++ b/third_party/blink/renderer/build/scripts/name_utilities.py
@@ -27,16 +27,8 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 import os.path
-import re
 
-from blinkbuild.name_style_converter import NameStyleConverter, tokenize_name
-
-
-def upper_first_letter(name):
-    """Return name with first letter uppercased."""
-    if not name:
-        return ''
-    return name[0].upper() + name[1:]
+from blinkbuild.name_style_converter import NameStyleConverter
 
 
 def script_name(entry):
@@ -69,43 +61,3 @@
 
 def enum_for_css_property_alias(property_name):
     return 'CSSPropertyAlias' + property_name.to_upper_camel_case()
-
-# FIXME: Merge these with the above methods.
-# and update all the generators to use these ones.
-# FIXME: Switch external callers of these methods to use the high level
-# API below instead.
-
-
-def naming_style(f):
-    """Decorator for name utility functions.
-
-    Wraps a name utility function in a function that takes one or more names,
-    splits them into a list of words, and passes the list to the utility function.
-    """
-    def inner(name_or_names):
-        names = name_or_names if isinstance(name_or_names, list) else [name_or_names]
-        words = []
-        for name in names:
-            if name:
-                words.extend(tokenize_name(name))
-        return f(words)
-    return inner
-
-
-@naming_style
-def _upper_camel_case(words):
-    return ''.join(upper_first_letter(word) for word in words)
-
-
-@naming_style
-def snake_case(words):
-    return '_'.join(word.lower() for word in words)
-
-
-# Use these high level naming functions which describe the semantics of the name,
-# rather than a particular style.
-
-
-@naming_style
-def enum_type_name(words):
-    return _upper_camel_case(words)
diff --git a/third_party/blink/renderer/core/dom/accessibility_role.idl b/third_party/blink/renderer/core/dom/accessibility_role.idl
index 057c4f1f4..cda5216e 100644
--- a/third_party/blink/renderer/core/dom/accessibility_role.idl
+++ b/third_party/blink/renderer/core/dom/accessibility_role.idl
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // ARIA reflection
-// https://github.com/w3c/aria/pull/708/files
+// https://w3c.github.io/aria/#idl-interface
 [
     NoInterfaceObject,
     RuntimeEnabled=AccessibilityObjectModel
diff --git a/third_party/blink/renderer/core/dom/aria_attributes.idl b/third_party/blink/renderer/core/dom/aria_attributes.idl
index b8aee48..4867c90 100644
--- a/third_party/blink/renderer/core/dom/aria_attributes.idl
+++ b/third_party/blink/renderer/core/dom/aria_attributes.idl
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // ARIA reflection
-// https://github.com/w3c/aria/pull/708/files
+// https://w3c.github.io/aria/#idl-interface
 [
     NoInterfaceObject,
     RuntimeEnabled=AccessibilityObjectModel
diff --git a/third_party/blink/renderer/core/dom/container_node.cc b/third_party/blink/renderer/core/dom/container_node.cc
index 7067c93..c83fecbd 100644
--- a/third_party/blink/renderer/core/dom/container_node.cc
+++ b/third_party/blink/renderer/core/dom/container_node.cc
@@ -1340,7 +1340,7 @@
 
 void ContainerNode::RecalcDescendantStylesForReattach() {
   for (Node* child = lastChild(); child; child = child->previousSibling()) {
-    if (child->IsElementNode())
+    if (child->IsElementNode() && !child->NeedsReattachLayoutTree())
       ToElement(child)->RecalcStyleForReattach();
   }
 }
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 9c177c8..42929bff 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2327,6 +2327,7 @@
 }
 
 void Element::RecalcStyleForReattach() {
+  DCHECK(!GetNonAttachedStyle());
   if (HasCustomStyleCallbacks())
     WillRecalcStyle(kReattach);
 
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder.cc b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
index a06f199..2a00fbd 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder.cc
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
@@ -128,6 +128,7 @@
 ComputedStyle& LayoutTreeBuilderForElement::Style() const {
   if (!style_) {
     DCHECK(!node_->GetNonAttachedStyle());
+    DCHECK(node_->IsPseudoElement());
     style_ = node_->StyleForLayoutObject();
   }
   return *style_;
diff --git a/third_party/blink/renderer/core/editing/spellcheck/cold_mode_spell_check_requester.cc b/third_party/blink/renderer/core/editing/spellcheck/cold_mode_spell_check_requester.cc
index e94c80c..f7c9068 100644
--- a/third_party/blink/renderer/core/editing/spellcheck/cold_mode_spell_check_requester.cc
+++ b/third_party/blink/renderer/core/editing/spellcheck/cold_mode_spell_check_requester.cc
@@ -63,10 +63,11 @@
   if (position.IsNull())
     return nullptr;
 
-  const Element* element = RootEditableElementOf(position);
-  if (!element || !element->isConnected())
+  const ContainerNode* root = HighestEditableRoot(position);
+  if (!root || !root->isConnected() || !root->IsElementNode())
     return nullptr;
 
+  const Element* element = ToElement(root);
   if (!element->IsSpellCheckingEnabled() ||
       !SpellChecker::IsSpellCheckingEnabledAt(position))
     return nullptr;
diff --git a/third_party/blink/renderer/core/editing/spellcheck/spell_checker.cc b/third_party/blink/renderer/core/editing/spellcheck/spell_checker.cc
index f5f87c90..ddead938 100644
--- a/third_party/blink/renderer/core/editing/spellcheck/spell_checker.cc
+++ b/third_party/blink/renderer/core/editing/spellcheck/spell_checker.cc
@@ -783,7 +783,8 @@
   }
   HTMLElement* element =
       Traversal<HTMLElement>::FirstAncestorOrSelf(*position.AnchorNode());
-  return element && element->IsSpellCheckingEnabled();
+  return element && element->IsSpellCheckingEnabled() &&
+         HasEditableStyle(*element);
 }
 
 STATIC_ASSERT_ENUM(kWebTextDecorationTypeSpelling, kTextDecorationTypeSpelling);
diff --git a/third_party/blink/renderer/core/html/html_plugin_element.cc b/third_party/blink/renderer/core/html/html_plugin_element.cc
index fa30c17..29ca435 100644
--- a/third_party/blink/renderer/core/html/html_plugin_element.cc
+++ b/third_party/blink/renderer/core/html/html_plugin_element.cc
@@ -707,17 +707,15 @@
   }
 }
 
-void HTMLPlugInElement::DidRecalcStyle(StyleRecalcChange change) {
-  if (change != kReattach)
-    return;
-  ComputedStyle* style = GetNonAttachedStyle();
-  if (!style || style->Display() == EDisplay::kNone)
-    return;
-  if (!IsImageType())
-    return;
-  if (!image_loader_)
-    image_loader_ = HTMLImageLoader::Create(this);
-  image_loader_->UpdateFromElement();
+scoped_refptr<ComputedStyle> HTMLPlugInElement::CustomStyleForLayoutObject() {
+  scoped_refptr<ComputedStyle> style = OriginalStyleForLayoutObject();
+  if (IsImageType() && !GetLayoutObject() && style &&
+      LayoutObjectIsNeeded(*style)) {
+    if (!image_loader_)
+      image_loader_ = HTMLImageLoader::Create(this);
+    image_loader_->UpdateFromElement();
+  }
+  return style;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_plugin_element.h b/third_party/blink/renderer/core/html/html_plugin_element.h
index d405d29..62e6624 100644
--- a/third_party/blink/renderer/core/html/html_plugin_element.h
+++ b/third_party/blink/renderer/core/html/html_plugin_element.h
@@ -170,7 +170,7 @@
   bool IsFocusableStyle() const final;
   bool IsKeyboardFocusable() const final;
   void DidAddUserAgentShadowRoot(ShadowRoot&) final;
-  void DidRecalcStyle(StyleRecalcChange) final;
+  scoped_refptr<ComputedStyle> CustomStyleForLayoutObject() final;
 
   // HTMLElement overrides:
   bool HasCustomFocusLogic() const override;
diff --git a/third_party/blink/renderer/core/html/image_document.cc b/third_party/blink/renderer/core/html/image_document.cc
index 90c49f4d..a41b413 100644
--- a/third_party/blink/renderer/core/html/image_document.cc
+++ b/third_party/blink/renderer/core/html/image_document.cc
@@ -454,7 +454,8 @@
   //   of the frame.
   // * Images smaller in either dimension are centered along that axis.
   int viewport_width =
-      GetFrame()->GetPage()->GetVisualViewport().Size().Width();
+      GetFrame()->GetPage()->GetVisualViewport().Size().Width() /
+      GetFrame()->PageZoomFactor();
 
   // For huge images, minimum-scale=0.1 is still too big on small screens.
   // Set the <div> width so that the image will shrink to fit the width of the
diff --git a/third_party/blink/renderer/core/html/image_document.h b/third_party/blink/renderer/core/html/image_document.h
index 1b057b1..6732122 100644
--- a/third_party/blink/renderer/core/html/image_document.h
+++ b/third_party/blink/renderer/core/html/image_document.h
@@ -96,6 +96,9 @@
 
   enum ShrinkToFitMode { kViewport, kDesktop };
   ShrinkToFitMode shrink_to_fit_mode_;
+
+  FRIEND_TEST_ALL_PREFIXES(ImageDocumentViewportTest, ZoomForDSFScaleImage);
+  FRIEND_TEST_ALL_PREFIXES(ImageDocumentViewportTest, DivWidthWithZoomForDSF);
 };
 
 DEFINE_DOCUMENT_TYPE_CASTS(ImageDocument);
diff --git a/third_party/blink/renderer/core/html/image_document_test.cc b/third_party/blink/renderer/core/html/image_document_test.cc
index abd0fde6..7e92dd9 100644
--- a/third_party/blink/renderer/core/html/image_document_test.cc
+++ b/third_party/blink/renderer/core/html/image_document_test.cc
@@ -310,5 +310,99 @@
   EXPECT_EQ(175, rect->y());
 }
 
+TEST_F(ImageDocumentViewportTest, ZoomForDSFScaleImage) {
+  v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
+  SimRequest request("https://example.com/test.jpg", "image/jpeg");
+  LoadURL("https://example.com/test.jpg");
+
+  Vector<unsigned char> jpeg = JpegImage();
+  Vector<char> data = Vector<char>();
+  data.Append(jpeg.data(), jpeg.size());
+  request.Complete(data);
+
+  HTMLImageElement* img = GetDocument().ImageElement();
+
+  // no zoom
+  WebView().Resize(IntSize(100, 100));
+  WebView().SetZoomFactorForDeviceScaleFactor(1.f);
+  Compositor().BeginFrame();
+  EXPECT_EQ(50u, img->width());
+  EXPECT_EQ(50u, img->height());
+  EXPECT_EQ(100, GetDocument().CalculateDivWidth());
+  EXPECT_EQ(1.f, GetVisualViewport().Scale());
+  EXPECT_EQ(100, GetVisualViewport().Width());
+  EXPECT_EQ(100, GetVisualViewport().Height());
+
+  // zoom-for-dsf = 4. WebView size is in physical pixel(400*400), image and
+  // visual viewport should be same in CSS pixel, as no dsf applied.
+  // This simulates running on two phones with different screen densities but
+  // same (physical) screen size, image document should displayed the same.
+  WebView().Resize(IntSize(400, 400));
+  WebView().SetZoomFactorForDeviceScaleFactor(4.f);
+  Compositor().BeginFrame();
+  EXPECT_EQ(50u, img->width());
+  EXPECT_EQ(50u, img->height());
+  EXPECT_EQ(100, GetDocument().CalculateDivWidth());
+  EXPECT_EQ(1.f, GetVisualViewport().Scale());
+  EXPECT_EQ(100, GetVisualViewport().Width());
+  EXPECT_EQ(100, GetVisualViewport().Height());
+}
+
+// Tests that with zoom factor for device scale factor, image with different
+// size fit in the viewport correctly.
+TEST_F(ImageDocumentViewportTest, DivWidthWithZoomForDSF) {
+  v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
+  SimRequest request("https://example.com/test.jpg", "image/jpeg");
+  LoadURL("https://example.com/test.jpg");
+
+  Vector<unsigned char> jpeg = JpegImage();
+  Vector<char> data = Vector<char>();
+  data.Append(jpeg.data(), jpeg.size());
+  request.Complete(data);
+
+  HTMLImageElement* img = GetDocument().ImageElement();
+
+  WebView().SetZoomFactorForDeviceScaleFactor(2.f);
+
+  // Image smaller then webview size, visual viewport is not zoomed, and image
+  // will be centered in the viewport.
+  WebView().Resize(IntSize(200, 200));
+  Compositor().BeginFrame();
+  EXPECT_EQ(50u, img->width());
+  EXPECT_EQ(50u, img->height());
+  EXPECT_EQ(100, GetDocument().CalculateDivWidth());
+  EXPECT_EQ(1.f, GetVisualViewport().Scale());
+  EXPECT_EQ(100, GetVisualViewport().Width());
+  EXPECT_EQ(100, GetVisualViewport().Height());
+  DOMRect* rect = img->getBoundingClientRect();
+  EXPECT_EQ(25, rect->x());
+  EXPECT_EQ(25, rect->y());
+
+  // Image wider than webview size, image should fill the visual viewport, and
+  // visual viewport zoom out to 0.5.
+  WebView().Resize(IntSize(50, 50));
+  Compositor().BeginFrame();
+  EXPECT_EQ(50u, img->width());
+  EXPECT_EQ(50u, img->height());
+  EXPECT_EQ(50, GetDocument().CalculateDivWidth());
+  EXPECT_EQ(0.5f, GetVisualViewport().Scale());
+  EXPECT_EQ(50, GetVisualViewport().Width());
+  EXPECT_EQ(50, GetVisualViewport().Height());
+
+  // When image is more than 10X wider than webview, shrink the image to fit the
+  // width of the screen.
+  WebView().Resize(IntSize(4, 20));
+  Compositor().BeginFrame();
+  EXPECT_EQ(20u, img->width());
+  EXPECT_EQ(20u, img->height());
+  EXPECT_EQ(20, GetDocument().CalculateDivWidth());
+  EXPECT_EQ(0.1f, GetVisualViewport().Scale());
+  EXPECT_EQ(20, GetVisualViewport().Width());
+  EXPECT_EQ(100, GetVisualViewport().Height());
+  rect = img->getBoundingClientRect();
+  EXPECT_EQ(0, rect->x());
+  EXPECT_EQ(40, rect->y());
+}
+
 #undef MAYBE
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 9e05e09..f5325d47 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -3709,6 +3709,26 @@
       # Signed exchange response signature.
       array of SignedExchangeSignature signatures
 
+  # Field type for a signed exchange related error.
+  experimental type SignedExchangeErrorField extends string
+    enum
+      signatureSig
+      signatureIntegrity
+      signatureCertUrl
+      signatureCertSha256
+      signatureValidityUrl
+      signatureTimestamps
+
+  # Information about a signed exchange response.
+  experimental type SignedExchangeError extends object
+    properties
+      # Error message.
+      string message
+      # The index of the signature which caused the error.
+      optional integer signatureIndex
+      # The field which caused the error.
+      optional SignedExchangeErrorField errorField
+
   # Information about a signed exchange response.
   experimental type SignedExchangeInfo extends object
     properties
@@ -3719,7 +3739,7 @@
       # Security details for the signed exchange header.
       optional SecurityDetails securityDetails
       # Errors occurred while handling the signed exchagne.
-      optional array of string errors
+      optional array of SignedExchangeError errors
 
   # Tells whether clearing browser cache is supported.
   deprecated command canClearBrowserCache
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
index 53b520a..2e50e4c 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
@@ -379,7 +379,8 @@
   String accept_lang_override;
   state_->getString(EmulationAgentState::kAcceptLanguageOverride,
                     &accept_lang_override);
-  if (!accept_lang_override.IsEmpty()) {
+  if (!accept_lang_override.IsEmpty() &&
+      request.HttpHeaderField("Accept-Language").IsEmpty()) {
     request.SetHTTPHeaderField("Accept-Language",
                                AtomicString(accept_lang_override));
   }
diff --git a/third_party/blink/renderer/core/layout/layout_table_section.cc b/third_party/blink/renderer/core/layout/layout_table_section.cc
index 78990e1..7cd66cf 100644
--- a/third_party/blink/renderer/core/layout/layout_table_section.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_section.cc
@@ -2046,10 +2046,6 @@
   if (GetPaginationBreakability() == kAllowAnyBreaks)
     return false;
 
-  // TODO(rhogan): Sections can be self-painting.
-  if (HasSelfPaintingLayer())
-    return false;
-
   // If we don't know the page height yet, just assume we fit.
   if (!IsPageLogicalHeightKnown())
     return true;
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
index 19c0da2..aa08a9e4 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
@@ -74,34 +74,7 @@
   // Add overflow from the last layout cycle.
   if (Base::ChildrenInline()) {
     if (const NGPhysicalBoxFragment* physical_fragment = CurrentFragment()) {
-      // LayoutOverflow is only computed if overflow is not hidden
-      if (physical_fragment->Style().OverflowX() != EOverflow::kHidden ||
-          physical_fragment->Style().OverflowY() != EOverflow::kHidden) {
-        // inline-end LayoutOverflow padding spec is still undecided:
-        // https://github.com/w3c/csswg-drafts/issues/129
-        // For backwards compatibility, if container clips overflow,
-        // padding is added to the inline-end for inline children.
-        base::Optional<NGPhysicalBoxStrut> padding_strut;
-        if (Base::HasOverflowClip()) {
-          padding_strut =
-              NGBoxStrut(LayoutUnit(), Base::PaddingEnd(), LayoutUnit(),
-                         LayoutUnit())
-                  .ConvertToPhysical(Base::StyleRef().GetWritingMode(),
-                                     Base::StyleRef().Direction());
-        }
-        NGPhysicalOffsetRect children_overflow;
-        for (const auto& child : physical_fragment->Children()) {
-          NGPhysicalOffsetRect child_scrollable_overflow =
-              child->ScrollableOverflow();
-          child_scrollable_overflow.offset += child->Offset();
-          if (child->IsLineBox() && padding_strut) {
-            child_scrollable_overflow.Expand(*padding_strut);
-          }
-          children_overflow.Unite(child_scrollable_overflow);
-        }
-        Base::AddLayoutOverflow(children_overflow.ToLayoutFlippedRect(
-            physical_fragment->Style(), physical_fragment->Size()));
-      }
+      AddScrollingOverflowFromChildren();
       Base::AddSelfVisualOverflow(
           physical_fragment->SelfVisualRect().ToLayoutFlippedRect(
               physical_fragment->Style(), physical_fragment->Size()));
@@ -120,6 +93,59 @@
 }
 
 template <typename Base>
+void LayoutNGMixin<Base>::AddScrollingOverflowFromChildren() {
+  const NGPhysicalBoxFragment* physical_fragment = CurrentFragment();
+  DCHECK(physical_fragment);
+  // LayoutOverflow is only computed if overflow is not hidden
+  if (physical_fragment->Style().OverflowX() == EOverflow::kHidden &&
+      physical_fragment->Style().OverflowY() == EOverflow::kHidden)
+    return;
+  // inline-end LayoutOverflow padding spec is still undecided:
+  // https://github.com/w3c/csswg-drafts/issues/129
+  // For backwards compatibility, if container clips overflow,
+  // padding is added to the inline-end for inline children.
+  base::Optional<NGPhysicalBoxStrut> padding_strut;
+  if (Base::HasOverflowClip()) {
+    padding_strut =
+        NGBoxStrut(LayoutUnit(), Base::PaddingEnd(), LayoutUnit(), LayoutUnit())
+            .ConvertToPhysical(Base::StyleRef().GetWritingMode(),
+                               Base::StyleRef().Direction());
+  }
+
+  NGPhysicalOffsetRect children_overflow;
+  for (const auto& child : physical_fragment->Children()) {
+    NGPhysicalOffsetRect child_scrollable_overflow =
+        child->ScrollableOverflow();
+    child_scrollable_overflow.offset += child->Offset();
+    if (child->IsLineBox() && padding_strut) {
+      child_scrollable_overflow.Expand(*padding_strut);
+    }
+    children_overflow.Unite(child_scrollable_overflow);
+  }
+
+  // LayoutOverflow takes flipped blocks coordinates, adjust as needed.
+  LayoutRect children_flipped_overflow = children_overflow.ToLayoutFlippedRect(
+      physical_fragment->Style(), physical_fragment->Size());
+  if (physical_fragment->Style().IsFlippedBlocksWritingMode()) {
+    // Legacy overflow coordinate system for flipped blocks is broken.
+    // It coordinates are "flipped blocks pretending scrollbar does
+    // not exist. This is the scrollbar adjustment.
+    // For details, see comments in LayoutBox::NoOverflowRect
+    LayoutObject* layout_object = physical_fragment->GetLayoutObject();
+    if (layout_object && layout_object->IsBox()) {
+      const LayoutBox* box = ToLayoutBox(layout_object);
+      if (!box->ShouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
+        LayoutUnit right_scrollbar_width =
+            LayoutUnit(box->VerticalScrollbarWidth());
+        children_flipped_overflow.SetX(children_flipped_overflow.X() -
+                                       right_scrollbar_width);
+      }
+    }
+  }
+  Base::AddLayoutOverflow(children_flipped_overflow);
+}
+
+template <typename Base>
 void LayoutNGMixin<Base>::AddOutlineRects(
     Vector<LayoutRect>& rects,
     const LayoutPoint& additional_offset,
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
index 3f7511f..61475fb 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
@@ -76,6 +76,10 @@
 
   void AddOverflowFromChildren() override;
 
+ private:
+  void AddScrollingOverflowFromChildren();
+
+ protected:
   void AddOutlineRects(
       Vector<LayoutRect>&,
       const LayoutPoint& additional_offset,
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index fe189a843..f0ae35b 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -718,7 +718,6 @@
   // embedder figure out what to do with it. Navigations to filesystem URLs are
   // always blocked here.
   if (frame_->IsMainFrame() &&
-      !request.GetResourceRequest().IsSameDocumentNavigation() &&
       !frame_->Client()->AllowContentInitiatedDataUrlNavigations(
           request.OriginDocument()->Url()) &&
       (url.ProtocolIs("filesystem") ||
@@ -943,14 +942,6 @@
     return;
   }
 
-  // PlzNavigate
-  // If the loader classifies this navigation as a different document navigation
-  // while the browser intended the navigation to be same-document, it means
-  // that a different navigation must have committed while the IPC was sent.
-  // This navigation is no more same-document. The navigation is simply dropped.
-  if (request.GetResourceRequest().IsSameDocumentNavigation())
-    return;
-
   StartLoad(request, new_load_type, policy, history_item, check_with_client);
 }
 
@@ -1621,7 +1612,6 @@
     provisional_document_loader_->SetItemForHistoryNavigation(history_item);
   }
 
-  DCHECK(!frame_load_request.GetResourceRequest().IsSameDocumentNavigation());
   frame_->GetFrameScheduler()->DidStartProvisionalLoad(frame_->IsMainFrame());
 
   // TODO(ananta):
diff --git a/third_party/blink/renderer/core/paint/adjust_paint_offset_scope.cc b/third_party/blink/renderer/core/paint/adjust_paint_offset_scope.cc
index aa4b69e..f0e438c5 100644
--- a/third_party/blink/renderer/core/paint/adjust_paint_offset_scope.cc
+++ b/third_party/blink/renderer/core/paint/adjust_paint_offset_scope.cc
@@ -45,8 +45,7 @@
     return true;
   }
 
-  if (box.IsTableSection() &&
-      (!old_paint_info_.IsPrinting() || box.FirstFragment().NextFragment())) {
+  if (box.IsTableSection()) {
     const auto& section = ToLayoutTableSection(box);
     if (section.IsRepeatingHeaderGroup() || section.IsRepeatingFooterGroup()) {
       adjusted_paint_offset_ = fragment->PaintOffset();
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index ebb4d668..2160c1a1 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -252,8 +252,11 @@
   return false;
 }
 
-IntPoint ApplyPaintOffsetTranslation(const LayoutObject& object,
-                                     LayoutPoint& paint_offset) {
+void FragmentPaintPropertyTreeBuilder::UpdateForPaintOffsetTranslation(
+    base::Optional<IntPoint>& paint_offset_translation) {
+  if (!NeedsPaintOffsetTranslation(object_))
+    return;
+
   // We should use the same subpixel paint offset values for snapping
   // regardless of whether a transform is present. If there is a transform
   // we round the paint offset but keep around the residual fractional
@@ -261,35 +264,21 @@
   // called "subpixel accumulation". For more information, see
   // PaintLayer::subpixelAccumulation() and
   // PaintLayerPainter::paintFragmentByApplyingTransform.
-  IntPoint paint_offset_translation = RoundedIntPoint(paint_offset);
+  paint_offset_translation = RoundedIntPoint(context_.current.paint_offset);
   LayoutPoint fractional_paint_offset =
-      LayoutPoint(paint_offset - paint_offset_translation);
+      LayoutPoint(context_.current.paint_offset - *paint_offset_translation);
   if (fractional_paint_offset != LayoutPoint()) {
     // If the object has a non-translation transform, discard the fractional
     // paint offset which can't be transformed by the transform.
     TransformationMatrix matrix;
-    object.StyleRef().ApplyTransform(
+    object_.StyleRef().ApplyTransform(
         matrix, LayoutSize(), ComputedStyle::kExcludeTransformOrigin,
         ComputedStyle::kIncludeMotionPath,
         ComputedStyle::kIncludeIndependentTransformProperties);
     if (!matrix.IsIdentityOrTranslation())
       fractional_paint_offset = LayoutPoint();
   }
-  paint_offset = fractional_paint_offset;
-  return paint_offset_translation;
-}
-
-void FragmentPaintPropertyTreeBuilder::UpdateForPaintOffsetTranslation(
-    base::Optional<IntPoint>& paint_offset_translation) {
-  if (!NeedsPaintOffsetTranslation(object_))
-    return;
-
-  paint_offset_translation =
-      ApplyPaintOffsetTranslation(object_, context_.current.paint_offset);
-  if (object_.IsLayoutView()) {
-    context_.absolute_position.paint_offset = context_.current.paint_offset;
-    context_.fixed_position.paint_offset = context_.current.paint_offset;
-  }
+  context_.current.paint_offset = fractional_paint_offset;
 }
 
 void FragmentPaintPropertyTreeBuilder::UpdatePaintOffsetTranslation(
@@ -1321,27 +1310,32 @@
     OnClear(properties_->ClearCssClipFixedPosition());
 }
 
-static LayoutRect BoundingRectInPaginationContainer(
+static LayoutRect MapLocalRectToAncestorLayer(
     const LayoutBox& box,
-    const LayoutRect& local_bounding_rect,
-    const PaintLayer& enclosing_pagination_layer) {
+    const LayoutRect& local_rect,
+    const PaintLayer& ancestor_layer) {
   TransformState transform_state(TransformState::kApplyTransformDirection,
-                                 FloatPoint(local_bounding_rect.Location()));
-  box.MapLocalToAncestor(&enclosing_pagination_layer.GetLayoutObject(),
-                         transform_state, kApplyContainerFlip);
+                                 FloatPoint(local_rect.Location()));
+  box.MapLocalToAncestor(&ancestor_layer.GetLayoutObject(), transform_state,
+                         kApplyContainerFlip);
   transform_state.Flatten();
   return LayoutRect(LayoutPoint(transform_state.LastPlanarPoint()),
-                    local_bounding_rect.Size());
+                    local_rect.Size());
+}
+
+static bool IsRepeatingTableSection(const LayoutObject& object) {
+  if (!object.IsTableSection())
+    return false;
+  const auto& section = ToLayoutTableSection(object);
+  return section.IsRepeatingHeaderGroup() || section.IsRepeatingFooterGroup();
 }
 
 static LayoutRect BoundingBoxInPaginationContainer(
     const LayoutObject& object,
-    const PaintLayer& enclosing_pagination_layer,
-    bool& should_repeat_in_fragments) {
+    const PaintLayer& enclosing_pagination_layer) {
   // Non-boxes that have no layer paint in the space of their containing block.
   if (!object.IsBox() && !object.HasLayer()) {
     const LayoutBox& containining_block = *object.ContainingBlock();
-    should_repeat_in_fragments = false;
     LayoutRect bounds_rect;
     // For non-SVG we can get a more accurate result
     // with LocalVisualRect, instead of falling back to the bounds of the
@@ -1353,12 +1347,10 @@
       bounds_rect = LayoutRect(SVGLayoutSupport::LocalVisualRect(object));
     }
 
-    return BoundingRectInPaginationContainer(containining_block, bounds_rect,
-                                             enclosing_pagination_layer);
+    return MapLocalRectToAncestorLayer(containining_block, bounds_rect,
+                                       enclosing_pagination_layer);
   }
 
-  should_repeat_in_fragments = false;
-
   // The special path for layers ensures that the bounding box also covers
   // contents visual overflow, so that the fragments will cover all fragments of
   // contents except for self-painting layers, because we initiate fragment
@@ -1373,24 +1365,21 @@
   // Compute the bounding box without transforms.
   // The object is guaranteed to be a box due to the logic above.
   const LayoutBox& box = ToLayoutBox(object);
-  auto bounding_box = BoundingRectInPaginationContainer(
-      box, box.BorderBoxRect(), enclosing_pagination_layer);
+  auto bounding_box = MapLocalRectToAncestorLayer(box, box.BorderBoxRect(),
+                                                  enclosing_pagination_layer);
 
-  if (!object.IsTableSection())
+  if (!IsRepeatingTableSection(object))
     return bounding_box;
+
   const auto& section = ToLayoutTableSection(object);
-  if (!section.IsRepeatingHeaderGroup() && !section.IsRepeatingFooterGroup())
-    return bounding_box;
-
   const auto& table = *section.Table();
-  should_repeat_in_fragments = true;
 
   if (section.IsRepeatingHeaderGroup()) {
     // Now bounding_box covers the original header. Expand it to intersect
     // with all fragments containing the original and repeatings, i.e. to
     // intersect any fragment containing any row.
     if (const auto* bottom_section = table.BottomNonEmptySection()) {
-      bounding_box.Unite(BoundingRectInPaginationContainer(
+      bounding_box.Unite(MapLocalRectToAncestorLayer(
           *bottom_section, bottom_section->BorderBoxRect(),
           enclosing_pagination_layer));
     }
@@ -1402,9 +1391,9 @@
   // fragment containing any row first.
   const auto* top_section = table.TopNonEmptySection();
   if (top_section) {
-    bounding_box.Unite(BoundingRectInPaginationContainer(
-        *top_section, top_section->BorderBoxRect(),
-        enclosing_pagination_layer));
+    bounding_box.Unite(MapLocalRectToAncestorLayer(*top_section,
+                                                   top_section->BorderBoxRect(),
+                                                   enclosing_pagination_layer));
     // However, the first fragment intersecting the expanded bounding_box may
     // not have enough space to contain the repeating footer. Exclude the
     // total height of the first row and repeating footers from the top of
@@ -1555,6 +1544,9 @@
           -ToLayoutBox(parent_row)->PhysicalLocation());
     }
   }
+
+  context_.current.paint_offset.Move(
+      context_.repeating_paint_offset_adjustment);
 }
 
 void FragmentPaintPropertyTreeBuilder::SetNeedsPaintPropertyUpdateIfNeeded() {
@@ -1794,12 +1786,9 @@
        !parent_composited_layer->EnclosingPaginationLayer())) {
     // |object_| establishes the top level composited layer under the
     // pagination layer.
-    bool should_repeat_in_fragments = false;
     FragmentainerIterator iterator(
         ToLayoutFlowThread(enclosing_pagination_layer->GetLayoutObject()),
-        BoundingBoxInPaginationContainer(object_, *enclosing_pagination_layer,
-                                         should_repeat_in_fragments));
-    DCHECK(!should_repeat_in_fragments);
+        BoundingBoxInPaginationContainer(object_, *enclosing_pagination_layer));
     if (!iterator.AtEnd()) {
       first_fragment.SetPaginationOffset(
           ToLayoutPoint(iterator.PaginationOffset()));
@@ -1815,8 +1804,9 @@
   }
 }
 
-void PaintPropertyTreeBuilder::UpdateRepeatingPaintOffsetAdjustment() {
-  if (!context_.is_repeating_in_flow_thread)
+void PaintPropertyTreeBuilder::
+    UpdateRepeatingTableSectionPaintOffsetAdjustment() {
+  if (!context_.is_repeating_table_section)
     return;
 
   if (object_.IsTableSection()) {
@@ -1824,6 +1814,13 @@
       UpdateRepeatingTableHeaderPaintOffsetAdjustment();
     else if (ToLayoutTableSection(object_).IsRepeatingFooterGroup())
       UpdateRepeatingTableFooterPaintOffsetAdjustment();
+  } else if (!context_.painting_layer->EnclosingPaginationLayer()) {
+    // When repeating a table section in paged media, paint_offset is inherited
+    // by descendants, so we only need to adjust point offset for the table
+    // section.
+    for (auto& fragment_context : context_.fragments) {
+      fragment_context.repeating_paint_offset_adjustment = LayoutSize();
+    }
   }
 
   // Otherwise the object is a descendant of the object which initiated the
@@ -1837,21 +1834,34 @@
     UpdateRepeatingTableHeaderPaintOffsetAdjustment() {
   const auto& section = ToLayoutTableSection(object_);
   DCHECK(section.IsRepeatingHeaderGroup());
-  auto& flow_thread = ToLayoutFlowThread(
-      context_.painting_layer->EnclosingPaginationLayer()->GetLayoutObject());
-  // TODO(crbug.com/757947): This shouldn't be possible but happens to
-  // column-spanners in nested multi-col contexts.
-  if (!flow_thread.IsPageLogicalHeightKnown())
-    return;
 
-  auto original_offset_in_flow_thread =
-      context_.repeating_bounding_box_in_flow_thread.Y();
-  auto fragment_height =
-      flow_thread.PageLogicalHeightForOffset(original_offset_in_flow_thread);
-  auto original_offset_in_fragment =
-      fragment_height -
-      flow_thread.PageRemainingLogicalHeightForOffset(
-          original_offset_in_flow_thread, LayoutBox::kAssociateWithLatterPage);
+  LayoutUnit fragment_height;
+  LayoutUnit original_offset_in_flow_thread =
+      context_.repeating_table_section_bounding_box.Y();
+  LayoutUnit original_offset_in_fragment;
+  const LayoutFlowThread* flow_thread = nullptr;
+  if (const auto* pagination_layer =
+          context_.painting_layer->EnclosingPaginationLayer()) {
+    flow_thread = &ToLayoutFlowThread(pagination_layer->GetLayoutObject());
+    // TODO(crbug.com/757947): This shouldn't be possible but happens to
+    // column-spanners in nested multi-col contexts.
+    if (!flow_thread->IsPageLogicalHeightKnown())
+      return;
+
+    fragment_height =
+        flow_thread->PageLogicalHeightForOffset(original_offset_in_flow_thread);
+    original_offset_in_fragment =
+        fragment_height - flow_thread->PageRemainingLogicalHeightForOffset(
+                              original_offset_in_flow_thread,
+                              LayoutBox::kAssociateWithLatterPage);
+  } else {
+    // The containing LayoutView serves as the virtual pagination container
+    // for repeating table section in paged media.
+    fragment_height = object_.View()->PageLogicalHeight();
+    original_offset_in_fragment =
+        IntMod(original_offset_in_flow_thread, fragment_height);
+  }
+
   // This is total height of repeating headers seen by the table - height of
   // this header (which is the lowest repeating header seen by this table.
   auto repeating_offset_in_fragment =
@@ -1879,8 +1889,11 @@
     // Calculate the offset of the next fragment in flow thread. It's used to
     // get the height of that fragment.
     fragment_offset_in_flow_thread += fragment_height;
-    fragment_height =
-        flow_thread.PageLogicalHeightForOffset(fragment_offset_in_flow_thread);
+
+    if (flow_thread) {
+      fragment_height = flow_thread->PageLogicalHeightForOffset(
+          fragment_offset_in_flow_thread);
+    }
   }
 }
 
@@ -1888,22 +1901,34 @@
     UpdateRepeatingTableFooterPaintOffsetAdjustment() {
   const auto& section = ToLayoutTableSection(object_);
   DCHECK(section.IsRepeatingFooterGroup());
-  auto& flow_thread = ToLayoutFlowThread(
-      context_.painting_layer->EnclosingPaginationLayer()->GetLayoutObject());
-  // TODO(crbug.com/757947): This shouldn't be possible but happens to
-  // column-spanners in nested multi-col contexts.
-  if (!flow_thread.IsPageLogicalHeightKnown())
-    return;
 
-  auto original_offset_in_flow_thread =
-      context_.repeating_bounding_box_in_flow_thread.MaxY() -
+  LayoutUnit fragment_height;
+  LayoutUnit original_offset_in_flow_thread =
+      context_.repeating_table_section_bounding_box.MaxY() -
       section.LogicalHeight();
-  auto fragment_height =
-      flow_thread.PageLogicalHeightForOffset(original_offset_in_flow_thread);
-  auto original_offset_in_fragment =
-      fragment_height -
-      flow_thread.PageRemainingLogicalHeightForOffset(
-          original_offset_in_flow_thread, LayoutBox::kAssociateWithLatterPage);
+  LayoutUnit original_offset_in_fragment;
+  const LayoutFlowThread* flow_thread = nullptr;
+  if (const auto* pagination_layer =
+          context_.painting_layer->EnclosingPaginationLayer()) {
+    flow_thread = &ToLayoutFlowThread(pagination_layer->GetLayoutObject());
+    // TODO(crbug.com/757947): This shouldn't be possible but happens to
+    // column-spanners in nested multi-col contexts.
+    if (!flow_thread->IsPageLogicalHeightKnown())
+      return;
+
+    fragment_height =
+        flow_thread->PageLogicalHeightForOffset(original_offset_in_flow_thread);
+    original_offset_in_fragment =
+        fragment_height - flow_thread->PageRemainingLogicalHeightForOffset(
+                              original_offset_in_flow_thread,
+                              LayoutBox::kAssociateWithLatterPage);
+  } else {
+    // The containing LayoutView serves as the virtual pagination container
+    // for repeating table section in paged media.
+    fragment_height = object_.View()->PageLogicalHeight();
+    original_offset_in_fragment =
+        IntMod(original_offset_in_flow_thread, fragment_height);
+  }
 
   const auto& table = *section.Table();
   // TODO(crbug.com/798153): This keeps the existing behavior of repeating
@@ -1936,8 +1961,11 @@
     // Calculate the offset of the previous fragment in flow thread. It's used
     // to get the height of that fragment.
     fragment_offset_in_flow_thread -= fragment_height;
-    fragment_height =
-        flow_thread.PageLogicalHeightForOffset(fragment_offset_in_flow_thread);
+
+    if (flow_thread) {
+      fragment_height = flow_thread->PageLogicalHeightForOffset(
+          fragment_offset_in_flow_thread);
+    }
   }
 }
 
@@ -2106,19 +2134,18 @@
   const auto& flow_thread =
       ToLayoutFlowThread(enclosing_pagination_layer->GetLayoutObject());
   LayoutRect object_bounding_box_in_flow_thread;
-  if (context_.is_repeating_in_flow_thread) {
+  if (context_.is_repeating_table_section) {
     // The object is a descendant of a repeating object. It should use the
     // repeating bounding box to repeat in the same fragments as its
     // repeating ancestor.
     object_bounding_box_in_flow_thread =
-        context_.repeating_bounding_box_in_flow_thread;
+        context_.repeating_table_section_bounding_box;
   } else {
-    bool should_repeat_in_fragments = false;
-    object_bounding_box_in_flow_thread = BoundingBoxInPaginationContainer(
-        object_, *enclosing_pagination_layer, should_repeat_in_fragments);
-    if (should_repeat_in_fragments) {
-      context_.is_repeating_in_flow_thread = true;
-      context_.repeating_bounding_box_in_flow_thread =
+    object_bounding_box_in_flow_thread =
+        BoundingBoxInPaginationContainer(object_, *enclosing_pagination_layer);
+    if (IsRepeatingTableSection(object_)) {
+      context_.is_repeating_table_section = true;
+      context_.repeating_table_section_bounding_box =
           object_bounding_box_in_flow_thread;
     }
   }
@@ -2195,6 +2222,26 @@
     object_.GetMutableForPainting().SetSubtreeNeedsPaintPropertyUpdate();
 }
 
+bool PaintPropertyTreeBuilder::ObjectIsRepeatingTableSectionInPagedMedia()
+    const {
+  if (!IsRepeatingTableSection(object_))
+    return false;
+
+  // The table section repeats in the pagination layer instead of paged media.
+  if (context_.painting_layer->EnclosingPaginationLayer())
+    return false;
+
+  if (!object_.View()->PageLogicalHeight())
+    return false;
+
+  // TODO(crbug.com/619094): Figure out the correct behavior for repeating
+  // objects in paged media with vertical writing modes.
+  if (!object_.View()->IsHorizontalWritingMode())
+    return false;
+
+  return true;
+}
+
 void PaintPropertyTreeBuilder::
     CreateFragmentContextsForRepeatingFixedPosition() {
   DCHECK(object_.IsFixedPositionObjectInPagedMedia());
@@ -2214,9 +2261,66 @@
   }
 }
 
-void PaintPropertyTreeBuilder::CreateFragmentDataForRepeatingFixedPosition(
+void PaintPropertyTreeBuilder::
+    CreateFragmentContextsForRepeatingTableSectionInPagedMedia() {
+  DCHECK(ObjectIsRepeatingTableSectionInPagedMedia());
+
+  // The containing LayoutView serves as the virtual pagination container
+  // for repeating table section in paged media.
+  LayoutView* view = object_.View();
+  context_.repeating_table_section_bounding_box =
+      BoundingBoxInPaginationContainer(object_, *view->Layer());
+
+  auto page_height = view->PageLogicalHeight();
+  const auto& bounding_box = context_.repeating_table_section_bounding_box;
+  int first_page = floorf(bounding_box.Y() / page_height);
+  int last_page = ceilf(bounding_box.MaxY() / page_height) - 1;
+  context_.fragments.resize(last_page - first_page + 1);
+  for (int page = first_page; page <= last_page; page++) {
+    if (page > first_page)
+      context_.fragments[page - first_page] = context_.fragments[0];
+    context_.fragments[page - first_page].logical_top_in_flow_thread =
+        page * page_height;
+  }
+
+  // We should also create fragments for the painting layer so that it will
+  // initiate painting of the repeating fragments of the table section.
+  const auto& painting_object = context_.painting_layer->GetLayoutObject();
+  if (painting_object == object_)
+    return;
+
+  int page_count = ceilf(view->DocumentRect().Height() / page_height);
+  auto* fragment = &painting_object.GetMutableForPainting().FirstFragment();
+  // Check if we have created the fragments of the painting layer for another
+  // repeating table section.
+  if (fragment->NextFragment()) {
+#if DCHECK_IS_ON()
+    int fragment_count = 1;
+    while ((fragment = fragment->NextFragment()))
+      fragment_count++;
+    DCHECK_EQ(fragment_count, page_count);
+#endif
+    return;
+  }
+
+  for (int page = 1; page < page_count; page++) {
+    auto* new_fragment = &fragment->EnsureNextFragment();
+    new_fragment->SetLocalBorderBoxProperties(
+        fragment->LocalBorderBoxProperties());
+    new_fragment->SetLogicalTopInFlowThread(page * page_height);
+    fragment = new_fragment;
+  }
+}
+
+bool PaintPropertyTreeBuilder::IsRepeatingInPagedMedia() const {
+  return context_.is_repeating_fixed_position ||
+         (context_.is_repeating_table_section &&
+          !context_.painting_layer->EnclosingPaginationLayer());
+}
+
+void PaintPropertyTreeBuilder::CreateFragmentDataForRepeatingInPagedMedia(
     bool needs_paint_properties) {
-  DCHECK(context_.is_repeating_fixed_position);
+  DCHECK(IsRepeatingInPagedMedia());
 
   FragmentData* fragment_data = nullptr;
   for (auto& fragment_context : context_.fragments) {
@@ -2248,19 +2352,23 @@
   // Need of fragmentation clip will be determined in CreateFragmentContexts().
 
   if (object_.IsFixedPositionObjectInPagedMedia()) {
-    // This flag applies to the fixed-position object itself and descendants.
+    // This flag applies to the object itself and descendants.
     context_.is_repeating_fixed_position = true;
     CreateFragmentContextsForRepeatingFixedPosition();
+  } else if (ObjectIsRepeatingTableSectionInPagedMedia()) {
+    context_.is_repeating_table_section = true;
+    CreateFragmentContextsForRepeatingTableSectionInPagedMedia();
   }
 
-  if (context_.is_repeating_fixed_position) {
-    CreateFragmentDataForRepeatingFixedPosition(needs_paint_properties);
+  if (IsRepeatingInPagedMedia()) {
+    CreateFragmentDataForRepeatingInPagedMedia(needs_paint_properties);
   } else if (context_.painting_layer->ShouldFragmentCompositedBounds()) {
     CreateFragmentContextsInFlowThread(needs_paint_properties);
   } else {
     InitSingleFragmentFromParent(needs_paint_properties);
     UpdateCompositedLayerPaginationOffset();
-    context_.is_repeating_in_flow_thread = false;
+    context_.is_repeating_fixed_position = false;
+    context_.is_repeating_table_section = false;
   }
 
   if (object_.IsSVGHiddenContainer()) {
@@ -2285,7 +2393,7 @@
         context_.has_svg_hidden_container_ancestor);
   }
 
-  UpdateRepeatingPaintOffsetAdjustment();
+  UpdateRepeatingTableSectionPaintOffsetAdjustment();
 
   return needs_paint_properties != had_paint_properties;
 }
@@ -2293,6 +2401,7 @@
 bool PaintPropertyTreeBuilder::ObjectTypeMightNeedPaintProperties() const {
   return object_.IsBoxModelObject() || object_.IsSVG() ||
          context_.painting_layer->EnclosingPaginationLayer() ||
+         context_.is_repeating_table_section ||
          context_.is_repeating_fixed_position;
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.h b/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
index 5024594..be55f83 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
@@ -101,9 +101,9 @@
   // in the flow thread.
   LayoutUnit logical_top_in_flow_thread;
 
-  // A repeating object paints at multiple places in the flow thread, once in
-  // each fragment. The repeated paintings need to add an adjustment to the
-  // calculated paint offset to paint at the desired place.
+  // A repeating object paints at multiple places, once in each fragment.
+  // The repeated paintings need to add an adjustment to the calculated paint
+  // offset to paint at the desired place.
   LayoutSize repeating_paint_offset_adjustment;
 };
 
@@ -137,10 +137,10 @@
 
   PaintLayer* painting_layer = nullptr;
 
-  // In a fragmented context, some objects (e.g. repeating table headers and
-  // footers) and their descendants in paint order) repeatedly paint in all
-  // fragments after the fragment where the object first appears.
-  bool is_repeating_in_flow_thread = false;
+  // In a fragmented context, repeating table headers and footers and their
+  // descendants in paint order repeatedly paint in all fragments after the
+  // fragment where the object first appears.
+  bool is_repeating_table_section = false;
 
   // When printing, fixed-position objects and their descendants need to repeat
   // in each page.
@@ -150,9 +150,9 @@
   // ancestor.
   bool has_svg_hidden_container_ancestor = false;
 
-  // The physical bounding box of all appearances of the repeating object
-  // in the flow thread.
-  LayoutRect repeating_bounding_box_in_flow_thread;
+  // The physical bounding box of all appearances of the repeating table section
+  // in the flow thread or the paged LayoutView.
+  LayoutRect repeating_table_section_bounding_box;
 };
 
 // Creates paint property tree nodes for non-local effects in the layout tree.
@@ -193,13 +193,17 @@
                      LayoutUnit logical_top_in_flow_thread) const;
   ALWAYS_INLINE void CreateFragmentContextsInFlowThread(
       bool needs_paint_properties);
+  ALWAYS_INLINE bool IsRepeatingInPagedMedia() const;
+  ALWAYS_INLINE bool ObjectIsRepeatingTableSectionInPagedMedia() const;
   ALWAYS_INLINE void CreateFragmentContextsForRepeatingFixedPosition();
-  ALWAYS_INLINE void CreateFragmentDataForRepeatingFixedPosition(
+  ALWAYS_INLINE void
+  CreateFragmentContextsForRepeatingTableSectionInPagedMedia();
+  ALWAYS_INLINE void CreateFragmentDataForRepeatingInPagedMedia(
       bool needs_paint_properties);
   // Returns whether ObjectPaintProperties were allocated or deleted.
   ALWAYS_INLINE bool UpdateFragments();
   ALWAYS_INLINE void UpdatePaintingLayer();
-  ALWAYS_INLINE void UpdateRepeatingPaintOffsetAdjustment();
+  ALWAYS_INLINE void UpdateRepeatingTableSectionPaintOffsetAdjustment();
   ALWAYS_INLINE void UpdateRepeatingTableHeaderPaintOffsetAdjustment();
   ALWAYS_INLINE void UpdateRepeatingTableFooterPaintOffsetAdjustment();
 
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index c1913ca..e751203 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -6,6 +6,8 @@
 
 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
 #include "third_party/blink/renderer/core/layout/layout_image.h"
+#include "third_party/blink/renderer/core/layout/layout_table_cell.h"
+#include "third_party/blink/renderer/core/layout/layout_table_section.h"
 #include "third_party/blink/renderer/core/layout/layout_tree_as_text.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
 #include "third_party/blink/renderer/core/paint/object_paint_properties.h"
@@ -5464,6 +5466,89 @@
   EXPECT_EQ(1u, NumFragments(fixed_child));
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, RepeatingTableSectionInPagedMedia) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      body { margin: 0; }
+      tr { height: 100px; }
+      div { height: 500px; }
+    </style>
+    <div></div>
+    <table style="border-spacing: 0">
+      <thead id="head"><tr><th>Header</th></tr></thead>
+      <tbody>
+        <tr><td></td></tr>
+        <tr><td></td></tr>
+        <tr><td></td></tr>
+        <tr><td></td></tr>
+      </tbody>
+      <tfoot id="foot"><tr><th>Footer</th></tr></tfoot>
+    </table>
+    <div></div>
+  )HTML");
+
+  const auto* head = ToLayoutTableSection(GetLayoutObjectByElementId("head"));
+  const auto* foot = ToLayoutTableSection(GetLayoutObjectByElementId("foot"));
+  EXPECT_FALSE(head->IsRepeatingHeaderGroup());
+  EXPECT_EQ(1u, NumFragments(head));
+  EXPECT_EQ(1u, NumFragments(head->FirstRow()));
+  EXPECT_EQ(1u, NumFragments(head->FirstRow()->FirstCell()));
+  EXPECT_FALSE(foot->IsRepeatingFooterGroup());
+  EXPECT_EQ(1u, NumFragments(foot));
+  EXPECT_EQ(1u, NumFragments(foot->FirstRow()));
+  EXPECT_EQ(1u, NumFragments(foot->FirstRow()->FirstCell()));
+
+  FloatSize page_size(300, 400);
+  GetFrame().StartPrinting(page_size, page_size, 1);
+  GetDocument().View()->UpdateLifecyclePhasesForPrinting();
+
+  // "fixed" should create fragments to repeat in each printed page.
+  EXPECT_TRUE(head->IsRepeatingHeaderGroup());
+  EXPECT_TRUE(foot->IsRepeatingFooterGroup());
+  auto check_fragments = [&](const LayoutObject* object) {
+    ASSERT_EQ(3u, NumFragments(object));
+    for (int i = 0; i < 3; i++) {
+      EXPECT_EQ(LayoutUnit((i + 1) * 400),
+                FragmentAt(object, i).LogicalTopInFlowThread());
+    }
+  };
+  check_fragments(head);
+  check_fragments(head->FirstRow());
+  check_fragments(head->FirstRow()->FirstCell());
+  check_fragments(foot);
+  check_fragments(foot->FirstRow());
+  check_fragments(foot->FirstRow());
+
+  // The first header is at its normal flow location (0, 100px) in its page.
+  // The other repeated ones are at the top of the their pages.
+  EXPECT_EQ(LayoutPoint(0, 500), FragmentAt(head, 0).PaintOffset());
+  EXPECT_EQ(LayoutPoint(0, 800), FragmentAt(head, 1).PaintOffset());
+  EXPECT_EQ(LayoutPoint(0, 1200), FragmentAt(head, 2).PaintOffset());
+  // The last footer is at its normal flow location (0, 200px) in its page.
+  // The other repeated ones are at the bottom of their pages.
+  EXPECT_EQ(LayoutPoint(0, 700), FragmentAt(foot, 0).PaintOffset());
+  EXPECT_EQ(LayoutPoint(0, 1100), FragmentAt(foot, 1).PaintOffset());
+  EXPECT_EQ(LayoutPoint(0, 1400), FragmentAt(foot, 2).PaintOffset());
+
+  const auto& painting_layer_object = head->PaintingLayer()->GetLayoutObject();
+  ASSERT_EQ(5u, NumFragments(&painting_layer_object));
+  for (int i = 0; i < 3; i++) {
+    const auto& fragment = FragmentAt(&painting_layer_object, i);
+    EXPECT_EQ(LayoutUnit(i * 400), fragment.LogicalTopInFlowThread());
+  }
+
+  GetFrame().EndPrinting();
+  GetDocument().View()->UpdateAllLifecyclePhases();
+  EXPECT_FALSE(head->IsRepeatingHeaderGroup());
+  EXPECT_EQ(1u, NumFragments(head));
+  EXPECT_EQ(1u, NumFragments(head->FirstRow()));
+  EXPECT_EQ(1u, NumFragments(head->FirstRow()->FirstCell()));
+  EXPECT_FALSE(foot->IsRepeatingFooterGroup());
+  EXPECT_EQ(1u, NumFragments(foot));
+  EXPECT_EQ(1u, NumFragments(foot->FirstRow()));
+  EXPECT_EQ(1u, NumFragments(foot->FirstRow()->FirstCell()));
+}
+
 TEST_P(PaintPropertyTreeBuilderTest, ImageWithInvertFilter) {
   SetBodyInnerHTML(R"HTML(
     <img id='img' src='x'>
diff --git a/third_party/blink/renderer/core/paint/svg_paint_context.cc b/third_party/blink/renderer/core/paint/svg_paint_context.cc
index f3c56a6..e2645a5 100644
--- a/third_party/blink/renderer/core/paint/svg_paint_context.cc
+++ b/third_party/blink/renderer/core/paint/svg_paint_context.cc
@@ -123,22 +123,25 @@
   if (object_.IsSVGRoot())
     return;
 
-  if (const auto* properties = object_.FirstFragment().PaintProperties()) {
-    // MaskClip() implies Effect(), thus we don't need to check MaskClip().
-    if (properties->Effect() || properties->ClipPathClip()) {
-      auto& paint_controller = GetPaintInfo().context.GetPaintController();
-      PropertyTreeState state = paint_controller.CurrentPaintChunkProperties();
-      if (const auto* effect = properties->Effect())
-        state.SetEffect(effect);
-      if (const auto* mask_clip = properties->MaskClip())
-        state.SetClip(mask_clip);
-      else if (const auto* clip_path_clip = properties->ClipPathClip())
-        state.SetClip(clip_path_clip);
-      scoped_paint_chunk_properties_.emplace(
-          paint_controller, state, object_,
-          DisplayItem::PaintPhaseToSVGEffectType(GetPaintInfo().phase));
-    }
-  }
+  const auto* fragment = GetPaintInfo().FragmentToPaint(object_);
+  if (!fragment)
+    return;
+  const auto* properties = fragment->PaintProperties();
+  // MaskClip() implies Effect(), thus we don't need to check MaskClip().
+  if (!properties || (!properties->Effect() && !properties->ClipPathClip()))
+    return;
+
+  auto& paint_controller = GetPaintInfo().context.GetPaintController();
+  PropertyTreeState state = paint_controller.CurrentPaintChunkProperties();
+  if (const auto* effect = properties->Effect())
+    state.SetEffect(effect);
+  if (const auto* mask_clip = properties->MaskClip())
+    state.SetClip(mask_clip);
+  else if (const auto* clip_path_clip = properties->ClipPathClip())
+    state.SetClip(clip_path_clip);
+  scoped_paint_chunk_properties_.emplace(
+      paint_controller, state, object_,
+      DisplayItem::PaintPhaseToSVGEffectType(GetPaintInfo().phase));
 }
 
 void SVGPaintContext::ApplyCompositingIfNecessary() {
diff --git a/third_party/blink/renderer/core/paint/svg_paint_context.h b/third_party/blink/renderer/core/paint/svg_paint_context.h
index e2e98fb..8a60c27 100644
--- a/third_party/blink/renderer/core/paint/svg_paint_context.h
+++ b/third_party/blink/renderer/core/paint/svg_paint_context.h
@@ -55,7 +55,10 @@
                       const AffineTransform& transform)
       : TransformRecorder(paint_info.context, object, transform) {
     if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled()) {
-      const auto* properties = object.FirstFragment().PaintProperties();
+      const auto* fragment = paint_info.FragmentToPaint(object);
+      if (!fragment)
+        return;
+      const auto* properties = fragment->PaintProperties();
       if (!properties)
         return;
 
diff --git a/third_party/blink/renderer/core/paint/table_section_painter.cc b/third_party/blink/renderer/core/paint/table_section_painter.cc
index d6f03ff..19126bb 100644
--- a/third_party/blink/renderer/core/paint/table_section_painter.cc
+++ b/third_party/blink/renderer/core/paint/table_section_painter.cc
@@ -26,10 +26,7 @@
     const PaintInfo& paint_info,
     const LayoutPoint& paint_offset,
     ItemToPaint item_to_paint) {
-  if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled() &&
-      // TODO(wangxianzhu): Use the PaintPropertyTreeBuilder path for printing.
-      (!paint_info.IsPrinting() ||
-       layout_table_section_.FirstFragment().NextFragment()))
+  if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
     return;
 
   if (!layout_table_section_.IsRepeatingHeaderGroup())
@@ -97,10 +94,7 @@
     const PaintInfo& paint_info,
     const LayoutPoint& paint_offset,
     ItemToPaint item_to_paint) {
-  if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled() &&
-      // TODO(wangxianzhu): Use the PaintPropertyTreeBuilder path for printing.
-      (!paint_info.IsPrinting() ||
-       layout_table_section_.FirstFragment().NextFragment()))
+  if (RuntimeEnabledFeatures::SlimmingPaintV175Enabled())
     return;
 
   if (!layout_table_section_.IsRepeatingFooterGroup())
@@ -177,6 +171,9 @@
 
 void TableSectionPainter::Paint(const PaintInfo& paint_info,
                                 const LayoutPoint& paint_offset) {
+  if (!paint_info.FragmentToPaint(layout_table_section_))
+    return;
+
   // TODO(crbug.com/805514): Paint mask for table section.
   if (paint_info.phase == PaintPhase::kMask)
     return;
diff --git a/third_party/blink/renderer/core/style/fill_layer.cc b/third_party/blink/renderer/core/style/fill_layer.cc
index 7c0a92d..16d68469 100644
--- a/third_party/blink/renderer/core/style/fill_layer.cc
+++ b/third_party/blink/renderer/core/style/fill_layer.cc
@@ -364,16 +364,6 @@
   return Clip() == ThisOrNextLayersClipMax();
 }
 
-bool FillLayer::ContainsImage(StyleImage* s) const {
-  if (!s)
-    return false;
-  if (image_ && *s == *image_)
-    return true;
-  if (next_)
-    return next_->ContainsImage(s);
-  return false;
-}
-
 bool FillLayer::ImagesAreLoaded() const {
   const FillLayer* curr;
   for (curr = this; curr; curr = curr->Next()) {
diff --git a/third_party/blink/renderer/core/style/fill_layer.h b/third_party/blink/renderer/core/style/fill_layer.h
index 1d68e66..d5bed9d2 100644
--- a/third_party/blink/renderer/core/style/fill_layer.h
+++ b/third_party/blink/renderer/core/style/fill_layer.h
@@ -214,7 +214,6 @@
 
   bool VisuallyEqual(const FillLayer&) const;
 
-  bool ContainsImage(StyleImage*) const;
   bool ImagesAreLoaded() const;
 
   bool HasImage() const {
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index a5f46167..0d1aa197 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -84,7 +84,7 @@
 
 using PerformanceObserverVector = HeapVector<Member<PerformanceObserver>>;
 
-static const size_t kDefaultResourceTimingBufferSize = 150;
+static const size_t kDefaultResourceTimingBufferSize = 250;
 static const size_t kDefaultFrameTimingBufferSize = 150;
 constexpr size_t kDefaultEventTimingBufferSize = 150;
 
diff --git a/third_party/blink/renderer/devtools/BUILD.gn b/third_party/blink/renderer/devtools/BUILD.gn
index 84eb9d0..bd71028 100644
--- a/third_party/blink/renderer/devtools/BUILD.gn
+++ b/third_party/blink/renderer/devtools/BUILD.gn
@@ -54,6 +54,7 @@
   "front_end/audits2/lighthouse/renderer/dom.js",
   "front_end/audits2/lighthouse/renderer/details-renderer.js",
   "front_end/audits2/lighthouse/renderer/category-renderer.js",
+  "front_end/audits2/lighthouse/renderer/performance-category-renderer.js",
   "front_end/audits2/lighthouse/renderer/crc-details-renderer.js",
   "front_end/audits2/lighthouse/renderer/report-renderer.js",
   "front_end/audits2/lighthouse/renderer/util.js",
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/Audits2Controller.js b/third_party/blink/renderer/devtools/front_end/audits2/Audits2Controller.js
index a33681c..843e716 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/Audits2Controller.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2/Audits2Controller.js
@@ -254,20 +254,40 @@
   },
   {
     setting: Common.settings.createSetting('audits2.throttling', 'default'),
-    description: ls`Apply network and CPU throttling during performance auditing`,
     setFlags: (flags, value) => {
-      flags.disableNetworkThrottling = value === 'off';
-      flags.disableCpuThrottling = value === 'off';
+      switch (value) {
+        case 'devtools':
+          flags.throttlingMethod = 'devtools';
+          break;
+        case 'off':
+          flags.throttlingMethod = 'provided';
+          break;
+        default:
+          flags.throttlingMethod = 'simulate';
+      }
     },
     options: [
-      {label: ls`Fast 3G with 4x CPU Slowdown`, value: 'default'},
-      {label: ls`No throttling`, value: 'off'},
+      {
+        label: ls`Simulated Fast 3G, 4x CPU Slowdown`,
+        value: 'default',
+        title: 'Throttling is simulated, resulting in faster audit runs with similar measurement accuracy'
+      },
+      {
+        label: ls`Applied Fast 3G, 4x CPU Slowdown`,
+        value: 'devtools',
+        title: 'Typical DevTools throttling, with actual traffic shaping and CPU slowdown applied'
+      },
+      {
+        label: ls`No throttling`,
+        value: 'off',
+        title: 'No network or CPU throttling used. (Useful when not evaluating performance)'
+      },
     ],
   },
   {
     setting: Common.settings.createSetting('audits2.clear_storage', true),
     title: ls`Clear storage`,
-    description: ls`Reset storage (localStorage, IndexedDB, etc) to a clean baseline before auditing`,
+    description: ls`Reset storage (localStorage, IndexedDB, etc) before auditing. (Good for performance & PWA testing)`,
     setFlags: (flags, value) => {
       flags.disableStorageReset = !value;
     },
@@ -279,4 +299,4 @@
   AuditProgressChanged: Symbol('AuditProgressChanged'),
   RequestAuditStart: Symbol('RequestAuditStart'),
   RequestAuditCancel: Symbol('RequestAuditCancel'),
-};
\ No newline at end of file
+};
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/Audits2Panel.js b/third_party/blink/renderer/devtools/front_end/audits2/Audits2Panel.js
index 07e3402..109d719b7 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/Audits2Panel.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2/Audits2Panel.js
@@ -118,8 +118,9 @@
 
   /**
    * @param {!ReportRenderer.ReportJSON} lighthouseResult
+   * @param {!ReportRenderer.RunnerResultArtifacts=} artifacts
    */
-  _renderReport(lighthouseResult) {
+  _renderReport(lighthouseResult, artifacts) {
     this.contentElement.classList.toggle('in-progress', false);
     this._startView.hideWidget();
     this._statusView.hide();
@@ -137,8 +138,7 @@
 
     const dom = new DOM(/** @type {!Document} */ (this._auditResultsElement.ownerDocument));
     const detailsRenderer = new Audits2.DetailsRenderer(dom);
-    const categoryRenderer = new Audits2.CategoryRenderer(dom, detailsRenderer);
-    categoryRenderer.setTraceArtifact(lighthouseResult);
+    const categoryRenderer = new CategoryRenderer(dom, detailsRenderer);
     const renderer = new Audits2.ReportRenderer(dom, categoryRenderer);
 
     const templatesHTML = Runtime.cachedResources['audits2/lighthouse/templates.html'];
@@ -147,20 +147,22 @@
       return;
 
     renderer.setTemplateContext(templatesDOM);
-    renderer.renderReport(lighthouseResult, reportContainer);
+    const el = renderer.renderReport(lighthouseResult, reportContainer);
+    Audits2.ReportRenderer.addViewTraceButton(el, artifacts);
 
     this._cachedRenderedReports.set(lighthouseResult, reportContainer);
   }
 
   /**
    * @param {!ReportRenderer.ReportJSON} lighthouseResult
+   * @param {!ReportRenderer.RunnerResultArtifacts=} artifacts
    */
-  _buildReportUI(lighthouseResult) {
+  _buildReportUI(lighthouseResult, artifacts) {
     if (lighthouseResult === null)
       return;
 
     const optionElement = new Audits2.ReportSelector.Item(
-        lighthouseResult, () => this._renderReport(lighthouseResult), this._renderStartView.bind(this));
+        lighthouseResult, () => this._renderReport(lighthouseResult, artifacts), this._renderStartView.bind(this));
     this._reportSelector.prepend(optionElement);
     this._refreshToolbarUI();
     this._renderReport(lighthouseResult);
@@ -208,21 +210,21 @@
 
       this._renderStatusView(inspectedURL);
 
-      const lighthouseResult = await this._protocolService.startLighthouse(inspectedURL, categoryIDs, flags);
+      const lighthouseResponse = await this._protocolService.startLighthouse(inspectedURL, categoryIDs, flags);
 
-      if (lighthouseResult && lighthouseResult.fatal) {
-        const error = new Error(lighthouseResult.message);
-        error.stack = lighthouseResult.stack;
+      if (lighthouseResponse && lighthouseResponse.fatal) {
+        const error = new Error(lighthouseResponse.message);
+        error.stack = lighthouseResponse.stack;
         throw error;
       }
 
-      if (!lighthouseResult)
+      if (!lighthouseResponse)
         throw new Error('Auditing failed to produce a result');
 
       Host.userMetrics.actionTaken(Host.UserMetrics.Action.Audits2Finished);
 
       await this._resetEmulationAndProtocolConnection();
-      this._buildReportUI(lighthouseResult);
+      this._buildReportUI(lighthouseResponse.lhr, lighthouseResponse.artifacts);
     } catch (err) {
       if (err instanceof Error)
         this._statusView.renderBugReport(err);
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/Audits2ProtocolService.js b/third_party/blink/renderer/devtools/front_end/audits2/Audits2ProtocolService.js
index 4c68a1f..99c2b81 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/Audits2ProtocolService.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2/Audits2ProtocolService.js
@@ -28,7 +28,7 @@
    * @param {string} auditURL
    * @param {!Array<string>} categoryIDs
    * @param {!Object} flags
-   * @return {!Promise<!ReportRenderer.ReportJSON>}
+   * @return {!Promise<!ReportRenderer.RunnerResult>}
    */
   startLighthouse(auditURL, categoryIDs, flags) {
     return this._send('start', {url: auditURL, categoryIDs, flags});
@@ -80,7 +80,7 @@
   /**
    * @param {string} method
    * @param {!Object=} params
-   * @return {!Promise<!ReportRenderer.ReportJSON>}
+   * @return {!Promise<!ReportRenderer.RunnerResult>}
    */
   _send(method, params) {
     if (!this._backendPromise)
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/Audits2ReportRenderer.js b/third_party/blink/renderer/devtools/front_end/audits2/Audits2ReportRenderer.js
index 0460011..62581c6 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/Audits2ReportRenderer.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2/Audits2ReportRenderer.js
@@ -7,21 +7,22 @@
  */
 Audits2.ReportRenderer = class extends ReportRenderer {
   /**
-   * Provides empty element for left nav
-   * @override
-   * @returns {!DocumentFragment}
+   * @param {!Element} el Parent element to render the report into.
+   * @param {!ReportRenderer.RunnerResultArtifacts=} artifacts
    */
-  _renderReportNav() {
-    return createDocumentFragment();
-  }
+  static addViewTraceButton(el, artifacts) {
+    if (!artifacts || !artifacts.traces || !artifacts.traces.defaultPass)
+      return;
 
-  /**
-   * @param {!ReportRenderer.ReportJSON} report
-   * @override
-   * @return {!DocumentFragment}
-   */
-  _renderReportHeader(report) {
-    return createDocumentFragment();
+    const defaultPassTrace = artifacts.traces.defaultPass;
+    const timelineButton = UI.createTextButton(Common.UIString('View Trace'), onViewTraceClick, 'view-trace');
+    el.querySelector('.lh-metric-column').appendChild(timelineButton);
+    return el;
+
+    async function onViewTraceClick() {
+      await UI.inspectorView.showPanel('timeline');
+      Timeline.TimelinePanel.instance().loadFromEvents(defaultPassTrace.traceEvents);
+    }
   }
 };
 
@@ -33,48 +34,6 @@
   }
 }
 
-Audits2.CategoryRenderer = class extends CategoryRenderer {
-  /**
-   * @override
-   * @param {!DOM} dom
-   * @param {!DetailsRenderer} detailsRenderer
-   */
-  constructor(dom, detailsRenderer) {
-    super(dom, detailsRenderer);
-    this._defaultPassTrace = null;
-  }
-
-  /**
-   * @param {!ReportRenderer.ReportJSON} lhr
-   */
-  setTraceArtifact(lhr) {
-    if (!lhr.artifacts || !lhr.artifacts.traces || !lhr.artifacts.traces.defaultPass)
-      return;
-    this._defaultPassTrace = lhr.artifacts.traces.defaultPass;
-  }
-
-  /**
-   * @override
-   * @param {!ReportRenderer.CategoryJSON} category
-   * @param {!Object<string, !ReportRenderer.GroupJSON>} groups
-   * @return {!Element}
-   */
-  renderPerformanceCategory(category, groups) {
-    const defaultPassTrace = this._defaultPassTrace;
-    const element = super.renderPerformanceCategory(category, groups);
-    if (!defaultPassTrace)
-      return element;
-
-    const timelineButton = UI.createTextButton(Common.UIString('View Trace'), onViewTraceClick, 'view-trace');
-    element.querySelector('.lh-audit-group').prepend(timelineButton);
-    return element;
-
-    async function onViewTraceClick() {
-      await UI.inspectorView.showPanel('timeline');
-      Timeline.TimelinePanel.instance().loadFromEvents(defaultPassTrace.traceEvents);
-    }
-  }
-};
 
 Audits2.DetailsRenderer = class extends DetailsRenderer {
   /**
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/Audits2ReportSelector.js b/third_party/blink/renderer/devtools/front_end/audits2/Audits2ReportSelector.js
index fda21c7..59261aa 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/Audits2ReportSelector.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2/Audits2ReportSelector.js
@@ -111,8 +111,8 @@
     this._renderReport = renderReport;
     this._showLandingCallback = showLandingCallback;
 
-    const url = new Common.ParsedURL(lighthouseResult.url);
-    const timestamp = lighthouseResult.generatedTime;
+    const url = new Common.ParsedURL(lighthouseResult.finalUrl);
+    const timestamp = lighthouseResult.fetchTime;
     this._element = createElement('option');
     this._element.label = `${new Date(timestamp).toLocaleTimeString()} - ${url.domain()}`;
   }
@@ -135,9 +135,9 @@
   }
 
   download() {
-    const url = new Common.ParsedURL(this._lighthouseResult.url).domain();
-    const timestamp = this._lighthouseResult.generatedTime;
+    const url = new Common.ParsedURL(this._lighthouseResult.finalUrl).domain();
+    const timestamp = this._lighthouseResult.fetchTime;
     const fileName = `${url}-${new Date(timestamp).toISO8601Compact()}.json`;
     Workspace.fileManager.save(fileName, JSON.stringify(this._lighthouseResult), true);
   }
-};
\ No newline at end of file
+};
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/RadioSetting.js b/third_party/blink/renderer/devtools/front_end/audits2/RadioSetting.js
index 15a146e..805b337 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/RadioSetting.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2/RadioSetting.js
@@ -16,13 +16,15 @@
     this._radioElements = [];
     for (const option of this._options) {
       const fragment = UI.Fragment.build`
-        <label class="audits2-radio">
+        <label $="label" class="audits2-radio">
           <input $="input" type="radio" value=${option.value} name=${setting.name}>
           ${option.label}
         </label>
       `;
 
       this.element.appendChild(fragment.element());
+      if (option.title)
+        UI.Tooltip.install(fragment.$('label'), option.title);
       const radioElement = fragment.$('input');
       radioElement.addEventListener('change', this._valueChanged.bind(this));
       this._radioElements.push(radioElement);
@@ -57,4 +59,4 @@
     const selectedRadio = this._radioElements.find(radio => radio.checked);
     this._setting.set(selectedRadio.value);
   }
-};
\ No newline at end of file
+};
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/audits2Panel.css b/third_party/blink/renderer/devtools/front_end/audits2/audits2Panel.css
index 01305bb..377b457 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/audits2Panel.css
+++ b/third_party/blink/renderer/devtools/front_end/audits2/audits2Panel.css
@@ -11,7 +11,7 @@
 
 .lh-root {
   --report-menu-width: 0;
-  user-select: initial;
+  user-select: text;
   --lh-bg-color: #fff;
   background-color: var(--lh-bg-color);
 }
@@ -40,8 +40,7 @@
   position: relative;
 }
 button.view-trace {
-  position: absolute;
-  right: 0;
+  margin: 10px;
 }
 
 .audits2-results-container {
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/category-renderer.js b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/category-renderer.js
index c361200f..1646e6ee 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/category-renderer.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/category-renderer.js
@@ -7,233 +7,185 @@
 
 /* globals self, Util */
 
+/** @typedef {import('./dom.js')} DOM */
+/** @typedef {import('./report-renderer.js')} ReportRenderer */
+/** @typedef {import('./report-renderer.js').AuditJSON} AuditJSON */
+/** @typedef {import('./report-renderer.js').CategoryJSON} CategoryJSON */
+/** @typedef {import('./report-renderer.js').GroupJSON} GroupJSON */
+/** @typedef {import('./details-renderer.js')} DetailsRenderer */
+/** @typedef {import('./util.js')} Util */
+
 class CategoryRenderer {
   /**
-   * @param {!DOM} dom
-   * @param {!DetailsRenderer} detailsRenderer
+   * @param {DOM} dom
+   * @param {DetailsRenderer} detailsRenderer
    */
   constructor(dom, detailsRenderer) {
-    /** @private {!DOM} */
-    this._dom = dom;
-    /** @private {!DetailsRenderer} */
-    this._detailsRenderer = detailsRenderer;
-    /** @private {!Document|!Element} */
-    this._templateContext = this._dom.document();
+    /** @type {DOM} */
+    this.dom = dom;
+    /** @type {DetailsRenderer} */
+    this.detailsRenderer = detailsRenderer;
+    /** @type {ParentNode} */
+    this.templateContext = this.dom.document();
 
-    this._detailsRenderer.setTemplateContext(this._templateContext);
+    this.detailsRenderer.setTemplateContext(this.templateContext);
   }
 
   /**
-   * @param {!ReportRenderer.AuditJSON} audit
-   * @return {!Element}
+   * @param {AuditJSON} audit
+   * @param {number} index
+   * @return {Element}
    */
-  _renderAuditScore(audit) {
-    const tmpl = this._dom.cloneTemplate('#tmpl-lh-audit-score', this._templateContext);
+  renderAudit(audit, index) {
+    const tmpl = this.dom.cloneTemplate('#tmpl-lh-audit', this.templateContext);
+    return this.populateAuditValues(audit, index, tmpl);
+  }
 
-    const scoringMode = audit.result.scoringMode;
-    const description = audit.result.helpText;
-    let title = audit.result.description;
+  /**
+   * Populate an DOM tree with audit details. Used by renderAudit and renderOpportunity
+   * @param {AuditJSON} audit
+   * @param {number} index
+   * @param {DocumentFragment} tmpl
+   * @return {Element}
+   */
+  populateAuditValues(audit, index, tmpl) {
+    const auditEl = this.dom.find('.lh-audit', tmpl);
+    auditEl.id = audit.result.id;
+    const scoreDisplayMode = audit.result.scoreDisplayMode;
 
     if (audit.result.displayValue) {
-      title += `:  ${audit.result.displayValue}`;
+      const displayValue = Util.formatDisplayValue(audit.result.displayValue);
+      this.dom.find('.lh-audit__display-text', auditEl).textContent = displayValue;
     }
 
-    if (audit.result.debugString) {
-      const debugStrEl = tmpl.appendChild(this._dom.createElement('div', 'lh-debug'));
-      debugStrEl.textContent = audit.result.debugString;
-    }
+    const titleEl = this.dom.find('.lh-audit__title', auditEl);
+    titleEl.appendChild(this.dom.convertMarkdownCodeSnippets(audit.result.title));
+    this.dom.find('.lh-audit__description', auditEl)
+        .appendChild(this.dom.convertMarkdownLinkSnippets(audit.result.description));
 
-    // Append audit details to header section so the entire audit is within a <details>.
-    const header = /** @type {!HTMLDetailsElement} */ (this._dom.find('.lh-score__header', tmpl));
-    if (audit.result.details) {
-      header.appendChild(this._detailsRenderer.render(audit.result.details));
+    const header = /** @type {HTMLDetailsElement} */ (this.dom.find('details', auditEl));
+    if (audit.result.details && audit.result.details.type) {
+      const elem = this.detailsRenderer.render(audit.result.details);
+      elem.classList.add('lh-details');
+      header.appendChild(elem);
     }
+    this.dom.find('.lh-audit__index', auditEl).textContent = `${index + 1}`;
 
-    const scoreEl = this._dom.find('.lh-score', tmpl);
-    if (audit.result.informative) {
-      scoreEl.classList.add('lh-score--informative');
-    }
-    if (audit.result.manual) {
-      scoreEl.classList.add('lh-score--manual');
-    }
+    // Add chevron SVG to the end of the summary
+    this.dom.find('.lh-chevron-container', auditEl).appendChild(this._createChevron());
+    this._setRatingClass(auditEl, audit.result.score, scoreDisplayMode);
 
-    return this._populateScore(tmpl, audit.score, scoringMode, title, description);
+    if (audit.result.scoreDisplayMode === 'error') {
+      auditEl.classList.add(`lh-audit--error`);
+      const textEl = this.dom.find('.lh-audit__display-text', auditEl);
+      textEl.textContent = 'Error!';
+      textEl.classList.add('tooltip-boundary');
+      const tooltip = this.dom.createChildOf(textEl, 'div', 'tooltip tooltip--error');
+      tooltip.textContent = audit.result.errorMessage || 'Report error: no audit information';
+    } else if (audit.result.explanation) {
+      const explEl = this.dom.createChildOf(titleEl, 'div', 'lh-audit-explanation');
+      explEl.textContent = audit.result.explanation;
+    }
+    const warnings = audit.result.warnings;
+    if (!warnings || warnings.length === 0) return auditEl;
+
+    // Add list of warnings or singular warning
+    const warningsEl = this.dom.createChildOf(titleEl, 'div', 'lh-warnings');
+    if (warnings.length === 1) {
+      warningsEl.textContent = `Warning: ${warnings.join('')}`;
+    } else {
+      warningsEl.textContent = 'Warnings: ';
+      const warningsUl = this.dom.createChildOf(warningsEl, 'ul');
+      for (const warning of warnings) {
+        const item = this.dom.createChildOf(warningsUl, 'li');
+        item.textContent = warning;
+      }
+    }
+    return auditEl;
   }
 
   /**
-   * @param {!DocumentFragment|!Element} element DOM node to populate with values.
-   * @param {number} score
-   * @param {string} scoringMode
-   * @param {string} title
-   * @param {string} description
-   * @return {!Element}
+   * @return {!HTMLElement}
    */
-  _populateScore(element, score, scoringMode, title, description) {
-    // Fill in the blanks.
-    const valueEl = this._dom.find('.lh-score__value', element);
-    valueEl.textContent = Util.formatNumber(score);
-    valueEl.classList.add(`lh-score__value--${Util.calculateRating(score)}`,
-        `lh-score__value--${scoringMode}`);
-
-    this._dom.find('.lh-score__title', element).appendChild(
-        this._dom.convertMarkdownCodeSnippets(title));
-    this._dom.find('.lh-score__description', element)
-        .appendChild(this._dom.convertMarkdownLinkSnippets(description));
-
-    return /** @type {!Element} **/ (element);
+  _createChevron() {
+    const chevronTmpl = this.dom.cloneTemplate('#tmpl-lh-chevron', this.templateContext);
+    const chevronEl = this.dom.find('.lh-chevron', chevronTmpl);
+    return chevronEl;
   }
 
   /**
-   * @param {!ReportRenderer.CategoryJSON} category
-   * @return {!Element}
+   * @param {!Element} element DOM node to populate with values.
+   * @param {number|null} score
+   * @param {string} scoreDisplayMode
+   * @return {Element}
    */
-  _renderCategoryScore(category) {
-    const tmpl = this._dom.cloneTemplate('#tmpl-lh-category-score', this._templateContext);
-    const score = Math.round(category.score);
+  _setRatingClass(element, score, scoreDisplayMode) {
+    const rating = Util.calculateRating(score, scoreDisplayMode);
+    element.classList.add(`lh-audit--${rating}`, `lh-audit--${scoreDisplayMode}`);
+    return element;
+  }
 
-    const gaugeContainerEl = this._dom.find('.lh-score__gauge', tmpl);
+  /**
+   * @param {CategoryJSON} category
+   * @return {Element}
+   */
+  renderCategoryHeader(category) {
+    const tmpl = this.dom.cloneTemplate('#tmpl-lh-category-header', this.templateContext);
+
+    const gaugeContainerEl = this.dom.find('.lh-score__gauge', tmpl);
     const gaugeEl = this.renderScoreGauge(category);
     gaugeContainerEl.appendChild(gaugeEl);
 
-    return this._populateScore(tmpl, score, 'numeric', category.name, category.description);
-  }
-
-  /**
-   * @param {!ReportRenderer.AuditJSON} audit
-   * @return {!Element}
-   */
-  _renderAudit(audit) {
-    const element = this._dom.createElement('div', 'lh-audit');
-    element.appendChild(this._renderAuditScore(audit));
-    return element;
-  }
-
-  /**
-   * @param {!ReportRenderer.AuditJSON} audit
-   * @param {number} scale
-   * @return {!Element}
-   */
-  _renderTimelineMetricAudit(audit, scale) {
-    const tmpl = this._dom.cloneTemplate('#tmpl-lh-timeline-metric', this._templateContext);
-    const element = this._dom.find('.lh-timeline-metric', tmpl);
-    element.classList.add(`lh-timeline-metric--${Util.calculateRating(audit.score)}`);
-
-    const titleEl = this._dom.find('.lh-timeline-metric__title', tmpl);
-    titleEl.textContent = audit.result.description;
-
-    const valueEl = this._dom.find('.lh-timeline-metric__value', tmpl);
-    valueEl.textContent = audit.result.displayValue;
-
-    const descriptionEl = this._dom.find('.lh-timeline-metric__description', tmpl);
-    descriptionEl.appendChild(this._dom.convertMarkdownLinkSnippets(audit.result.helpText));
-
-    if (typeof audit.result.rawValue !== 'number') {
-      const debugStrEl = this._dom.createChildOf(element, 'div', 'lh-debug');
-      debugStrEl.textContent = audit.result.debugString || 'Report error: no metric information';
-      return element;
+    this.dom.find('.lh-category-header__title', tmpl).appendChild(
+      this.dom.convertMarkdownCodeSnippets(category.title));
+    if (category.description) {
+      const descEl = this.dom.convertMarkdownLinkSnippets(category.description);
+      this.dom.find('.lh-category-header__description', tmpl).appendChild(descEl);
     }
 
-    const sparklineBarEl = this._dom.find('.lh-sparkline__bar', tmpl);
-    sparklineBarEl.style.width = `${audit.result.rawValue / scale * 100}%`;
-
-    return element;
-  }
-
-  /**
-   * @param {!ReportRenderer.AuditJSON} audit
-   * @param {number} scale
-   * @return {!Element}
-   */
-  _renderPerfHintAudit(audit, scale) {
-    const extendedInfo = /** @type {!CategoryRenderer.PerfHintExtendedInfo}
-        */ (audit.result.extendedInfo);
-    const tooltipAttrs = {title: audit.result.displayValue};
-
-    const element = this._dom.createElement('details', [
-      'lh-perf-hint',
-      `lh-perf-hint--${Util.calculateRating(audit.score)}`,
-      'lh-expandable-details',
-    ].join(' '));
-
-    const summary = this._dom.createChildOf(element, 'summary', 'lh-perf-hint__summary ' +
-        'lh-expandable-details__summary');
-    const titleEl = this._dom.createChildOf(summary, 'div', 'lh-perf-hint__title');
-    titleEl.textContent = audit.result.description;
-
-    this._dom.createChildOf(summary, 'div', 'lh-toggle-arrow', {title: 'See resources'});
-
-    if (!extendedInfo || typeof audit.result.rawValue !== 'number') {
-      const debugStrEl = this._dom.createChildOf(summary, 'div', 'lh-debug');
-      debugStrEl.textContent = audit.result.debugString || 'Report error: no extended information';
-      return element;
-    }
-
-    const sparklineContainerEl = this._dom.createChildOf(summary, 'div', 'lh-perf-hint__sparkline',
-        tooltipAttrs);
-    const sparklineEl = this._dom.createChildOf(sparklineContainerEl, 'div', 'lh-sparkline');
-    const sparklineBarEl = this._dom.createChildOf(sparklineEl, 'div', 'lh-sparkline__bar');
-    sparklineBarEl.style.width = audit.result.rawValue / scale * 100 + '%';
-
-    const statsEl = this._dom.createChildOf(summary, 'div', 'lh-perf-hint__stats', tooltipAttrs);
-    const statsMsEl = this._dom.createChildOf(statsEl, 'div', 'lh-perf-hint__primary-stat');
-    statsMsEl.textContent = Util.formatMilliseconds(audit.result.rawValue);
-
-    if (extendedInfo.value.wastedKb) {
-      const statsKbEl = this._dom.createChildOf(statsEl, 'div', 'lh-perf-hint__secondary-stat');
-      statsKbEl.textContent = Util.formatNumber(extendedInfo.value.wastedKb) + ' KB';
-    }
-
-    const descriptionEl = this._dom.createChildOf(element, 'div', 'lh-perf-hint__description');
-    descriptionEl.appendChild(this._dom.convertMarkdownLinkSnippets(audit.result.helpText));
-
-    if (audit.result.debugString) {
-      const debugStrEl = this._dom.createChildOf(summary, 'div', 'lh-debug');
-      debugStrEl.textContent = audit.result.debugString;
-    }
-
-    if (audit.result.details) {
-      element.appendChild(this._detailsRenderer.render(audit.result.details));
-    }
-
-    return element;
+    return /** @type {Element} */ (tmpl.firstElementChild);
   }
 
   /**
    * Renders the group container for a group of audits. Individual audit elements can be added
    * directly to the returned element.
-   * @param {!ReportRenderer.GroupJSON} group
-   * @param {{expandable: boolean}} opts
-   * @return {!Element}
+   * @param {GroupJSON} group
+   * @param {{expandable: boolean, itemCount?: number}} opts
+   * @return {Element}
    */
-  _renderAuditGroup(group, opts) {
+  renderAuditGroup(group, opts) {
     const expandable = opts.expandable;
-    const element = this._dom.createElement(expandable ? 'details' : 'div', 'lh-audit-group');
-    const summmaryEl = this._dom.createChildOf(element, 'summary', 'lh-audit-group__summary');
-    const headerEl = this._dom.createChildOf(summmaryEl, 'div', 'lh-audit-group__header');
-    this._dom.createChildOf(summmaryEl, 'div',
-      `lh-toggle-arrow  ${expandable ? '' : ' lh-toggle-arrow-unexpandable'}`, {
-        title: 'See audits',
-      });
+    const groupEl = this.dom.createElement(expandable ? 'details' : 'div', 'lh-audit-group');
+    const summmaryEl = this.dom.createChildOf(groupEl, 'summary', 'lh-audit-group__summary');
+    const headerEl = this.dom.createChildOf(summmaryEl, 'div', 'lh-audit-group__header');
+    const itemCountEl = this.dom.createChildOf(summmaryEl, 'div', 'lh-audit-group__itemcount');
+    if (expandable) {
+      const chevronEl = summmaryEl.appendChild(this._createChevron());
+      chevronEl.title = 'Show audits';
+    }
 
     if (group.description) {
-      const auditGroupDescription = this._dom.createElement('div', 'lh-audit-group__description');
-      auditGroupDescription.appendChild(this._dom.convertMarkdownLinkSnippets(group.description));
-      element.appendChild(auditGroupDescription);
+      const auditGroupDescription = this.dom.createElement('div', 'lh-audit-group__description');
+      auditGroupDescription.appendChild(this.dom.convertMarkdownLinkSnippets(group.description));
+      groupEl.appendChild(auditGroupDescription);
     }
     headerEl.textContent = group.title;
 
-    return element;
+    if (opts.itemCount) {
+      itemCountEl.textContent = `${opts.itemCount} audits`;
+    }
+    return groupEl;
   }
 
   /**
    * Find the total number of audits contained within a section.
    * Accounts for nested subsections like Accessibility.
-   * @param {!Array<!Element>} elements
+   * @param {Array<Element>} elements
    * @return {number}
    */
   _getTotalAuditsLength(elements) {
     // Create a scratch element to append sections to so we can reuse querySelectorAll().
-    const scratch = this._dom.createElement('div');
+    const scratch = this.dom.createElement('div');
     elements.forEach(function(element) {
       scratch.appendChild(element);
     });
@@ -246,283 +198,193 @@
   }
 
   /**
-   * @param {!Array<!Element>} elements
-   * @return {!Element}
+   * @param {Array<Element>} elements
+   * @return {Element}
    */
-  _renderPassedAuditsSection(elements) {
-    const passedElem = this._renderAuditGroup({
-      title: `${this._getTotalAuditsLength(elements)} Passed Audits`,
-    }, {expandable: true});
+  _renderFailedAuditsSection(elements) {
+    const failedElem = this.dom.createElement('div');
+    failedElem.classList.add('lh-failed-audits');
+    elements.forEach(elem => failedElem.appendChild(elem));
+    return failedElem;
+  }
+
+  /**
+   * @param {Array<Element>} elements
+   * @return {Element}
+   */
+  renderPassedAuditsSection(elements) {
+    const passedElem = this.renderAuditGroup({
+      title: `Passed audits`,
+    }, {expandable: true, itemCount: this._getTotalAuditsLength(elements)});
     passedElem.classList.add('lh-passed-audits');
     elements.forEach(elem => passedElem.appendChild(elem));
     return passedElem;
   }
 
   /**
-   * @param {!Array<!Element>} elements
-   * @return {!Element}
+   * @param {Array<Element>} elements
+   * @return {Element}
    */
   _renderNotApplicableAuditsSection(elements) {
-    const notApplicableElem = this._renderAuditGroup({
-      title: `${this._getTotalAuditsLength(elements)} Not Applicable Audits`,
-    }, {expandable: true});
-    notApplicableElem.classList.add('lh-audit-group--notapplicable');
+    const notApplicableElem = this.renderAuditGroup({
+      title: `Not applicable`,
+    }, {expandable: true, itemCount: this._getTotalAuditsLength(elements)});
+    notApplicableElem.classList.add('lh-audit-group--not-applicable');
     elements.forEach(elem => notApplicableElem.appendChild(elem));
     return notApplicableElem;
   }
 
   /**
-   * @param {!Array<!ReportRenderer.AuditJSON>} manualAudits
-   * @param {!Object<string, !ReportRenderer.GroupJSON>} groupDefinitions
-   * @param {!Element} element Parent container to add the manual audits to.
+   * @param {Array<AuditJSON>} manualAudits
+   * @param {string} manualDescription
+   * @return {Element}
    */
-  _renderManualAudits(manualAudits, groupDefinitions, element) {
-    const auditsGroupedByGroup = /** @type {!Object<string,
-        !Array<!ReportRenderer.AuditJSON>>} */ ({});
-    manualAudits.forEach(audit => {
-      const group = auditsGroupedByGroup[audit.group] || [];
-      group.push(audit);
-      auditsGroupedByGroup[audit.group] = group;
+  _renderManualAudits(manualAudits, manualDescription) {
+    const group = {title: 'Additional items to manually check', description: manualDescription};
+    const auditGroupElem = this.renderAuditGroup(group,
+        {expandable: true, itemCount: manualAudits.length});
+    auditGroupElem.classList.add('lh-audit-group--manual');
+    manualAudits.forEach((audit, i) => {
+      auditGroupElem.appendChild(this.renderAudit(audit, i));
     });
-
-    Object.keys(auditsGroupedByGroup).forEach(groupId => {
-      const group = groupDefinitions[groupId];
-      const auditGroupElem = this._renderAuditGroup(group, {expandable: true});
-      auditGroupElem.classList.add('lh-audit-group--manual');
-
-      auditsGroupedByGroup[groupId].forEach(audit => {
-        auditGroupElem.appendChild(this._renderAudit(audit));
-      });
-
-      element.appendChild(auditGroupElem);
-    });
+    return auditGroupElem;
   }
 
   /**
-   * @param {!Document|!Element} context
+   * @param {ParentNode} context
    */
   setTemplateContext(context) {
-    this._templateContext = context;
-    this._detailsRenderer.setTemplateContext(context);
+    this.templateContext = context;
+    this.detailsRenderer.setTemplateContext(context);
   }
 
   /**
-   * @param {!ReportRenderer.CategoryJSON} category
-   * @return {!DocumentFragment}
+   * @param {CategoryJSON} category
+   * @return {DocumentFragment}
    */
   renderScoreGauge(category) {
-    const tmpl = this._dom.cloneTemplate('#tmpl-lh-gauge', this._templateContext);
-    this._dom.find('.lh-gauge__wrapper', tmpl).href = `#${category.id}`;
-    this._dom.find('.lh-gauge__label', tmpl).textContent = category.name;
+    const tmpl = this.dom.cloneTemplate('#tmpl-lh-gauge', this.templateContext);
+    const wrapper = /** @type {HTMLAnchorElement} */ (this.dom.find('.lh-gauge__wrapper', tmpl));
+    wrapper.href = `#${category.id}`;
+    wrapper.classList.add(`lh-gauge__wrapper--${Util.calculateRating(category.score)}`);
 
-    const score = Math.round(category.score);
-    const fillRotation = Math.floor((score / 100) * 180);
+    // Cast `null` to 0
+    const numericScore = Number(category.score);
+    const gauge = this.dom.find('.lh-gauge', tmpl);
+    // 329 is ~= 2 * Math.PI * gauge radius (53)
+    // https://codepen.io/xgad/post/svg-radial-progress-meters
+    // score of 50: `stroke-dasharray: 164.5 329`;
+    /** @type {?SVGCircleElement} */
+    const gaugeArc = gauge.querySelector('.lh-gauge-arc');
+    if (gaugeArc) {
+      gaugeArc.style.strokeDasharray = `${numericScore * 329} 329`;
+    }
 
-    const gauge = this._dom.find('.lh-gauge', tmpl);
-    gauge.setAttribute('data-progress', score); // .dataset not supported in jsdom.
-    gauge.classList.add(`lh-gauge--${Util.calculateRating(score)}`);
+    const scoreOutOf100 = Math.round(numericScore * 100);
+    const percentageEl = this.dom.find('.lh-gauge__percentage', tmpl);
+    percentageEl.textContent = scoreOutOf100.toString();
+    if (category.score === null) {
+      percentageEl.textContent = '?';
+      percentageEl.title = 'Errors occurred while auditing';
+    }
 
-    this._dom.findAll('.lh-gauge__fill', gauge).forEach(el => {
-      el.style.transform = `rotate(${fillRotation}deg)`;
-    });
-
-    this._dom.find('.lh-gauge__mask--full', gauge).style.transform =
-        `rotate(${fillRotation}deg)`;
-    this._dom.find('.lh-gauge__fill--fix', gauge).style.transform =
-        `rotate(${fillRotation * 2}deg)`;
-    this._dom.find('.lh-gauge__percentage', gauge).textContent = score;
-
+    this.dom.find('.lh-gauge__label', tmpl).textContent = category.title;
     return tmpl;
   }
 
   /**
-   * @param {!ReportRenderer.CategoryJSON} category
-   * @param {!Object<string, !ReportRenderer.GroupJSON>} groups
-   * @return {!Element}
+   * @param {CategoryJSON} category
+   * @param {Object<string, GroupJSON>} groupDefinitions
+   * @return {Element}
    */
-  render(category, groups) {
-    switch (category.id) {
-      case 'performance':
-        return this.renderPerformanceCategory(category, groups);
-      case 'accessibility':
-        return this._renderAccessibilityCategory(category, groups);
-      default:
-        return this._renderDefaultCategory(category, groups);
-    }
-  }
+  render(category, groupDefinitions) {
+    const element = this.dom.createElement('div', 'lh-category');
+    this.createPermalinkSpan(element, category.id);
+    element.appendChild(this.renderCategoryHeader(category));
 
-  /**
-   * @param {!ReportRenderer.CategoryJSON} category
-   * @param {!Object<string, !ReportRenderer.GroupJSON>} groupDefinitions
-   * @return {!Element}
-   */
-  _renderDefaultCategory(category, groupDefinitions) {
-    const element = this._dom.createElement('div', 'lh-category');
-    this._createPermalinkSpan(element, category.id);
-    element.appendChild(this._renderCategoryScore(category));
+    const auditRefs = category.auditRefs;
+    const manualAudits = auditRefs.filter(audit => audit.result.scoreDisplayMode === 'manual');
+    const nonManualAudits = auditRefs.filter(audit => !manualAudits.includes(audit));
 
-    const manualAudits = category.audits.filter(audit => audit.result.manual);
-    const nonManualAudits = category.audits.filter(audit => !manualAudits.includes(audit));
-    const passedAudits = nonManualAudits.filter(audit => audit.score === 100 &&
-        !audit.result.debugString);
-    const nonPassedAudits = nonManualAudits.filter(audit => !passedAudits.includes(audit));
+    /** @type {Object<string, {passed: Array<AuditJSON>, failed: Array<AuditJSON>, notApplicable: Array<AuditJSON>}>} */
+    const auditsGroupedByGroup = {};
+    const auditsUngrouped = {passed: [], failed: [], notApplicable: []};
 
-    const nonPassedElem = this._renderAuditGroup({
-      title: `${nonPassedAudits.length} Failed Audits`,
-    }, {expandable: false});
-    nonPassedElem.classList.add('lh-failed-audits');
-    nonPassedAudits.forEach(audit => nonPassedElem.appendChild(this._renderAudit(audit)));
-    element.appendChild(nonPassedElem);
+    nonManualAudits.forEach(auditRef => {
+      let group;
 
-    // Create a passed section if there are passing audits.
-    if (passedAudits.length) {
-      const passedElem = this._renderPassedAuditsSection(
-        passedAudits.map(audit => this._renderAudit(audit))
-      );
-      element.appendChild(passedElem);
-    }
+      if (auditRef.group) {
+        const groupId = auditRef.group;
 
-    // Render manual audits after passing.
-    this._renderManualAudits(manualAudits, groupDefinitions, element);
-
-    return element;
-  }
-
-  /**
-   * @param {!ReportRenderer.CategoryJSON} category
-   * @param {!Object<string, !ReportRenderer.GroupJSON>} groups
-   * @return {!Element}
-   */
-  renderPerformanceCategory(category, groups) {
-    const element = this._dom.createElement('div', 'lh-category');
-    this._createPermalinkSpan(element, category.id);
-    element.appendChild(this._renderCategoryScore(category));
-
-    const metricAudits = category.audits.filter(audit => audit.group === 'perf-metric');
-    const metricAuditsEl = this._renderAuditGroup(groups['perf-metric'], {expandable: false});
-    const timelineContainerEl = this._dom.createChildOf(metricAuditsEl, 'div',
-        'lh-timeline-container');
-    const timelineEl = this._dom.createChildOf(timelineContainerEl, 'div', 'lh-timeline');
-
-    let perfTimelineScale = 0;
-    metricAudits.forEach(audit => {
-      if (typeof audit.result.rawValue === 'number' && audit.result.rawValue) {
-        perfTimelineScale = Math.max(perfTimelineScale, audit.result.rawValue);
-      }
-    });
-
-    const thumbnailAudit = category.audits.find(audit => audit.id === 'screenshot-thumbnails');
-    const thumbnailResult = thumbnailAudit && thumbnailAudit.result;
-    if (thumbnailResult && thumbnailResult.details) {
-      const thumbnailDetails = /** @type {!DetailsRenderer.FilmstripDetails} */
-          (thumbnailResult.details);
-      perfTimelineScale = Math.max(perfTimelineScale, thumbnailDetails.scale);
-      const filmstripEl = this._detailsRenderer.render(thumbnailDetails);
-      timelineEl.appendChild(filmstripEl);
-    }
-
-    metricAudits.forEach(item => {
-      if (item.id === 'speed-index-metric' || item.id === 'estimated-input-latency') {
-        return metricAuditsEl.appendChild(this._renderAudit(item));
-      }
-
-      timelineEl.appendChild(this._renderTimelineMetricAudit(item, perfTimelineScale));
-    });
-
-    metricAuditsEl.open = true;
-    element.appendChild(metricAuditsEl);
-
-    const hintAudits = category.audits
-        .filter(audit => audit.group === 'perf-hint' && audit.score < 100)
-        .sort((auditA, auditB) => auditB.result.rawValue - auditA.result.rawValue);
-    if (hintAudits.length) {
-      const maxWaste = Math.max(...hintAudits.map(audit => audit.result.rawValue));
-      const scale = Math.ceil(maxWaste / 1000) * 1000;
-      const hintAuditsEl = this._renderAuditGroup(groups['perf-hint'], {expandable: false});
-      hintAudits.forEach(item => hintAuditsEl.appendChild(this._renderPerfHintAudit(item, scale)));
-      hintAuditsEl.open = true;
-      element.appendChild(hintAuditsEl);
-    }
-
-    const infoAudits = category.audits
-        .filter(audit => audit.group === 'perf-info' && audit.score < 100);
-    if (infoAudits.length) {
-      const infoAuditsEl = this._renderAuditGroup(groups['perf-info'], {expandable: false});
-      infoAudits.forEach(item => infoAuditsEl.appendChild(this._renderAudit(item)));
-      infoAuditsEl.open = true;
-      element.appendChild(infoAuditsEl);
-    }
-
-    const passedElements = category.audits
-        .filter(audit => (audit.group === 'perf-hint' || audit.group === 'perf-info') &&
-            audit.score === 100)
-        .map(audit => this._renderAudit(audit));
-
-    if (!passedElements.length) return element;
-
-    const passedElem = this._renderPassedAuditsSection(passedElements);
-    element.appendChild(passedElem);
-    return element;
-  }
-
-  /**
-   * @param {!ReportRenderer.CategoryJSON} category
-   * @param {!Object<string, !ReportRenderer.GroupJSON>} groupDefinitions
-   * @return {!Element}
-   */
-  _renderAccessibilityCategory(category, groupDefinitions) {
-    const element = this._dom.createElement('div', 'lh-category');
-    this._createPermalinkSpan(element, category.id);
-    element.appendChild(this._renderCategoryScore(category));
-
-    const manualAudits = category.audits.filter(audit => audit.result.manual);
-    const nonManualAudits = category.audits.filter(audit => !manualAudits.includes(audit));
-    const auditsGroupedByGroup = /** @type {!Object<string,
-        {passed: !Array<!ReportRenderer.AuditJSON>,
-        failed: !Array<!ReportRenderer.AuditJSON>,
-        notApplicable: !Array<!ReportRenderer.AuditJSON>}>} */ ({});
-    nonManualAudits.forEach(audit => {
-      const groupId = audit.group;
-      const groups = auditsGroupedByGroup[groupId] || {passed: [], failed: [], notApplicable: []};
-
-      if (audit.result.notApplicable) {
-        groups.notApplicable.push(audit);
-      } else if (audit.score === 100) {
-        groups.passed.push(audit);
+        if (auditsGroupedByGroup[groupId]) {
+          group = auditsGroupedByGroup[groupId];
+        } else {
+          group = {passed: [], failed: [], notApplicable: []};
+          auditsGroupedByGroup[groupId] = group;
+        }
       } else {
-        groups.failed.push(audit);
+        group = auditsUngrouped;
       }
 
-      auditsGroupedByGroup[groupId] = groups;
+      if (auditRef.result.scoreDisplayMode === 'not-applicable') {
+        group.notApplicable.push(auditRef);
+      } else if (Util.showAsPassed(auditRef.result)) {
+        group.passed.push(auditRef);
+      } else {
+        group.failed.push(auditRef);
+      }
     });
 
-    const passedElements = /** @type {!Array<!Element>} */ ([]);
-    const notApplicableElements = /** @type {!Array<!Element>} */ ([]);
+    const failedElements = /** @type {Array<Element>} */ ([]);
+    const passedElements = /** @type {Array<Element>} */ ([]);
+    const notApplicableElements = /** @type {Array<Element>} */ ([]);
+
+    auditsUngrouped.failed.forEach((/** @type {AuditJSON} */ audit, i) =>
+      failedElements.push(this.renderAudit(audit, i)));
+    auditsUngrouped.passed.forEach((/** @type {AuditJSON} */ audit, i) =>
+      passedElements.push(this.renderAudit(audit, i)));
+    auditsUngrouped.notApplicable.forEach((/** @type {AuditJSON} */ audit, i) =>
+      notApplicableElements.push(this.renderAudit(audit, i)));
+
     Object.keys(auditsGroupedByGroup).forEach(groupId => {
       const group = groupDefinitions[groupId];
       const groups = auditsGroupedByGroup[groupId];
+
       if (groups.failed.length) {
-        const auditGroupElem = this._renderAuditGroup(group, {expandable: false});
-        groups.failed.forEach(item => auditGroupElem.appendChild(this._renderAudit(item)));
-        auditGroupElem.open = true;
-        element.appendChild(auditGroupElem);
+        const auditGroupElem = this.renderAuditGroup(group, {expandable: false});
+        groups.failed.forEach((item, i) => auditGroupElem.appendChild(this.renderAudit(item, i)));
+        auditGroupElem.classList.add('lh-audit-group--unadorned');
+        failedElements.push(auditGroupElem);
       }
 
       if (groups.passed.length) {
-        const auditGroupElem = this._renderAuditGroup(group, {expandable: true});
-        groups.passed.forEach(item => auditGroupElem.appendChild(this._renderAudit(item)));
+        const auditGroupElem = this.renderAuditGroup(group, {expandable: true});
+        groups.passed.forEach((item, i) => auditGroupElem.appendChild(this.renderAudit(item, i)));
+        auditGroupElem.classList.add('lh-audit-group--unadorned');
         passedElements.push(auditGroupElem);
       }
 
       if (groups.notApplicable.length) {
-        const auditGroupElem = this._renderAuditGroup(group, {expandable: true});
-        groups.notApplicable.forEach(item => auditGroupElem.appendChild(this._renderAudit(item)));
+        const auditGroupElem = this.renderAuditGroup(group, {expandable: true});
+        groups.notApplicable.forEach((item, i) =>
+            auditGroupElem.appendChild(this.renderAudit(item, i)));
+        auditGroupElem.classList.add('lh-audit-group--unadorned');
         notApplicableElements.push(auditGroupElem);
       }
     });
 
+    if (failedElements.length) {
+      const failedElem = this._renderFailedAuditsSection(failedElements);
+      element.appendChild(failedElem);
+    }
+
+    if (manualAudits.length) {
+      const manualEl = this._renderManualAudits(manualAudits, category.manualDescription);
+      element.appendChild(manualEl);
+    }
+
     if (passedElements.length) {
-      const passedElem = this._renderPassedAuditsSection(passedElements);
+      const passedElem = this.renderPassedAuditsSection(passedElements);
       element.appendChild(passedElem);
     }
 
@@ -531,19 +393,16 @@
       element.appendChild(notApplicableElem);
     }
 
-    // Render manual audits after passing.
-    this._renderManualAudits(manualAudits, groupDefinitions, element);
-
     return element;
   }
 
   /**
    * Create a non-semantic span used for hash navigation of categories
-   * @param {!Element} element
+   * @param {Element} element
    * @param {string} id
    */
-  _createPermalinkSpan(element, id) {
-    const permalinkEl = this._dom.createChildOf(element, 'span', 'lh-permalink');
+  createPermalinkSpan(element, id) {
+    const permalinkEl = this.dom.createChildOf(element, 'span', 'lh-permalink');
     permalinkEl.id = id;
   }
 }
@@ -553,14 +412,3 @@
 } else {
   self.CategoryRenderer = CategoryRenderer;
 }
-
-
-/**
- * @typedef {{
- *     value: {
- *       wastedMs: (number|undefined),
- *       wastedKb: (number|undefined),
- *     }
- * }}
- */
-CategoryRenderer.PerfHintExtendedInfo; // eslint-disable-line no-unused-expressions
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/crc-details-renderer.js b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/crc-details-renderer.js
index 822b2f96..5c5fe795 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/crc-details-renderer.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/crc-details-renderer.js
@@ -12,11 +12,13 @@
 
 /* globals self Util */
 
+/** @typedef {import('./dom.js')} DOM */
+
 class CriticalRequestChainRenderer {
   /**
    * Create render context for critical-request-chain tree display.
-   * @param {!Object<string, !CriticalRequestChainRenderer.CRCNode>} tree
-   * @return {{tree: !Object<string, !CriticalRequestChainRenderer.CRCNode>, startTime: number, transferSize: number}}
+   * @param {LH.Audit.SimpleCriticalRequestNode} tree
+   * @return {{tree: LH.Audit.SimpleCriticalRequestNode, startTime: number, transferSize: number}}
    */
   static initTree(tree) {
     let startTime = 0;
@@ -34,13 +36,13 @@
    * parent. Calculates if this node is the last child, whether it has any
    * children itself and what the tree looks like all the way back up to the root,
    * so the tree markers can be drawn correctly.
-   * @param {!Object<string, !CriticalRequestChainRenderer.CRCNode>} parent
+   * @param {LH.Audit.SimpleCriticalRequestNode} parent
    * @param {string} id
    * @param {number} startTime
    * @param {number} transferSize
-   * @param {!Array<boolean>=} treeMarkers
+   * @param {Array<boolean>=} treeMarkers
    * @param {boolean=} parentIsLastChild
-   * @return {!CriticalRequestChainRenderer.CRCSegment}
+   * @return {CRCSegment}
    */
   static createSegment(parent, id, startTime, transferSize, treeMarkers, parentIsLastChild) {
     const node = parent[id];
@@ -68,10 +70,10 @@
 
   /**
    * Creates the DOM for a tree segment.
-   * @param {!DOM} dom
-   * @param {!DocumentFragment} tmpl
-   * @param {!CriticalRequestChainRenderer.CRCSegment} segment
-   * @return {!Node}
+   * @param {DOM} dom
+   * @param {DocumentFragment} tmpl
+   * @param {CRCSegment} segment
+   * @return {Node}
    */
   static createChainNode(dom, tmpl, segment) {
     const chainsEl = dom.cloneTemplate('#tmpl-lh-crc__chains', tmpl);
@@ -110,14 +112,14 @@
     const {file, hostname} = Util.parseURL(segment.node.request.url);
     const treevalEl = dom.find('.crc-node__tree-value', chainsEl);
     dom.find('.crc-node__tree-file', treevalEl).textContent = `${file}`;
-    dom.find('.crc-node__tree-hostname', treevalEl).textContent = `(${hostname})`;
+    dom.find('.crc-node__tree-hostname', treevalEl).textContent = hostname ? `(${hostname})` : '';
 
     if (!segment.hasChildren) {
       const span = dom.createElement('span', 'crc-node__chain-duration');
       span.textContent = ' - ' + Util.chainDuration(
           segment.node.request.startTime, segment.node.request.endTime) + 'ms, ';
       const span2 = dom.createElement('span', 'crc-node__chain-duration');
-      span2.textContent = Util.formatBytesToKB(segment.node.request.transferSize);
+      span2.textContent = Util.formatBytesToKB(segment.node.request.transferSize, 0.01);
 
       treevalEl.appendChild(span);
       treevalEl.appendChild(span2);
@@ -128,52 +130,48 @@
 
   /**
    * Recursively builds a tree from segments.
-   * @param {!DOM} dom
-   * @param {!DocumentFragment} tmpl
-   * @param {!CriticalRequestChainRenderer.CRCSegment} segment
-   * @param {!Element} detailsEl Parent details element.
-   * @param {!CriticalRequestChainRenderer.CRCDetailsJSON} details
+   * @param {DOM} dom
+   * @param {DocumentFragment} tmpl
+   * @param {CRCSegment} segment
+   * @param {Element} elem Parent element.
+   * @param {CRCDetailsJSON} details
    */
-  static buildTree(dom, tmpl, segment, detailsEl, details) {
-    detailsEl.appendChild(CriticalRequestChainRenderer.createChainNode(dom, tmpl, segment));
+  static buildTree(dom, tmpl, segment, elem, details) {
+    elem.appendChild(CriticalRequestChainRenderer.createChainNode(dom, tmpl, segment));
 
     for (const key of Object.keys(segment.node.children)) {
       const childSegment = CriticalRequestChainRenderer.createSegment(segment.node.children, key,
          segment.startTime, segment.transferSize, segment.treeMarkers, segment.isLastChild);
-      CriticalRequestChainRenderer.buildTree(dom, tmpl, childSegment, detailsEl, details);
+      CriticalRequestChainRenderer.buildTree(dom, tmpl, childSegment, elem, details);
     }
   }
 
   /**
-   * @param {!DOM} dom
-   * @param {!Node} templateContext
-   * @param {!CriticalRequestChainRenderer.CRCDetailsJSON} details
-   * @return {!Node}
+   * @param {DOM} dom
+   * @param {ParentNode} templateContext
+   * @param {CRCDetailsJSON} details
+   * @return {Element}
    */
   static render(dom, templateContext, details) {
     const tmpl = dom.cloneTemplate('#tmpl-lh-crc', templateContext);
+    const containerEl = dom.find('.lh-crc', tmpl);
 
     // Fill in top summary.
     dom.find('.lh-crc__longest_duration', tmpl).textContent =
         Util.formatNumber(details.longestChain.duration) + 'ms';
-    dom.find('.lh-crc__longest_length', tmpl).textContent = details.longestChain.length;
+    dom.find('.lh-crc__longest_length', tmpl).textContent = details.longestChain.length.toString();
     dom.find('.lh-crc__longest_transfersize', tmpl).textContent =
         Util.formatBytesToKB(details.longestChain.transferSize);
 
-    const detailsEl = dom.find('.lh-details', tmpl);
-    detailsEl.open = true;
-
-    dom.find('.lh-details > summary', tmpl).textContent = details.header.text;
-
     // Construct visual tree.
     const root = CriticalRequestChainRenderer.initTree(details.chains);
     for (const key of Object.keys(root.tree)) {
       const segment = CriticalRequestChainRenderer.createSegment(root.tree, key,
           root.startTime, root.transferSize);
-      CriticalRequestChainRenderer.buildTree(dom, tmpl, segment, detailsEl, details);
+      CriticalRequestChainRenderer.buildTree(dom, tmpl, segment, containerEl, details);
     }
 
-    return tmpl;
+    return dom.find('.lh-crc-container', tmpl);
   }
 }
 
@@ -185,44 +183,19 @@
 }
 
 /** @typedef {{
- *     type: string,
- *     header: {text: string},
- *     longestChain: {duration: number, length: number, transferSize: number},
- *     chains: !Object<string, !CriticalRequestChainRenderer.CRCNode>
- * }}
+      type: string,
+      header: {text: string},
+      longestChain: {duration: number, length: number, transferSize: number},
+      chains: LH.Audit.SimpleCriticalRequestNode
+  }} CRCDetailsJSON
  */
-CriticalRequestChainRenderer.CRCDetailsJSON; // eslint-disable-line no-unused-expressions
 
 /** @typedef {{
- *     endTime: number,
- *     responseReceivedTime: number,
- *     startTime: number,
- *     transferSize: number,
- *     url: string
- * }}
+      node: LH.Audit.SimpleCriticalRequestNode[string],
+      isLastChild: boolean,
+      hasChildren: boolean,
+      startTime: number,
+      transferSize: number,
+      treeMarkers: Array<boolean>
+  }} CRCSegment
  */
-CriticalRequestChainRenderer.CRCRequest; // eslint-disable-line no-unused-expressions
-
-/**
- * Record type so children can circularly have CRCNode values.
- * @struct
- * @record
- */
-CriticalRequestChainRenderer.CRCNode = function() {};
-
-/** @type {!Object<string, !CriticalRequestChainRenderer.CRCNode>} */
-CriticalRequestChainRenderer.CRCNode.prototype.children; // eslint-disable-line no-unused-expressions
-
-/** @type {!CriticalRequestChainRenderer.CRCRequest} */
-CriticalRequestChainRenderer.CRCNode.prototype.request; // eslint-disable-line no-unused-expressions
-
-/** @typedef {{
- *     node: !CriticalRequestChainRenderer.CRCNode,
- *     isLastChild: boolean,
- *     hasChildren: boolean,
- *     startTime: number,
- *     transferSize: number,
- *     treeMarkers: !Array<boolean>
- * }}
- */
-CriticalRequestChainRenderer.CRCSegment; // eslint-disable-line no-unused-expressions
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/details-renderer.js b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/details-renderer.js
index 21f31d3..b908209 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/details-renderer.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/details-renderer.js
@@ -7,72 +7,104 @@
 
 /* globals self CriticalRequestChainRenderer Util URL */
 
+/** @typedef {import('./dom.js')} DOM */
+/** @typedef {import('./crc-details-renderer.js')} CRCDetailsJSON */
+
 class DetailsRenderer {
   /**
-   * @param {!DOM} dom
+   * @param {DOM} dom
    */
   constructor(dom) {
-    /** @private {!DOM} */
+    /** @type {DOM} */
     this._dom = dom;
-    /** @private {!Document|!Element} */
+    /** @type {ParentNode} */
     this._templateContext; // eslint-disable-line no-unused-expressions
   }
 
   /**
-   * @param {!Document|!Element} context
+   * @param {ParentNode} context
    */
   setTemplateContext(context) {
     this._templateContext = context;
   }
 
   /**
-   * @param {!DetailsRenderer.DetailsJSON} details
-   * @return {!Node}
+   * @param {DetailsJSON} details
+   * @return {Element}
    */
   render(details) {
     switch (details.type) {
       case 'text':
-        return this._renderText(details);
+        return this._renderText(/** @type {StringDetailsJSON} */ (details));
       case 'url':
-        return this._renderTextURL(details);
+        return this._renderTextURL(/** @type {StringDetailsJSON} */ (details));
+      case 'bytes':
+        return this._renderBytes(/** @type {NumericUnitDetailsJSON} */ (details));
+      case 'ms':
+        // eslint-disable-next-line max-len
+        return this._renderMilliseconds(/** @type {NumericUnitDetailsJSON} */ (details));
       case 'link':
-        return this._renderLink(/** @type {!DetailsRenderer.LinkDetailsJSON} */ (details));
+        // @ts-ignore - TODO(bckenny): Fix type hierarchy
+        return this._renderLink(/** @type {LinkDetailsJSON} */ (details));
       case 'thumbnail':
-        return this._renderThumbnail(/** @type {!DetailsRenderer.ThumbnailDetails} */ (details));
+        return this._renderThumbnail(/** @type {ThumbnailDetails} */ (details));
       case 'filmstrip':
-        return this._renderFilmstrip(/** @type {!DetailsRenderer.FilmstripDetails} */ (details));
-      case 'cards':
-        return this._renderCards(/** @type {!DetailsRenderer.CardsDetailsJSON} */ (details));
+        // @ts-ignore - TODO(bckenny): Fix type hierarchy
+        return this._renderFilmstrip(/** @type {FilmstripDetails} */ (details));
       case 'table':
-        return this._renderTable(/** @type {!DetailsRenderer.TableDetailsJSON} */ (details));
+        // @ts-ignore - TODO(bckenny): Fix type hierarchy
+        return this._renderTable(/** @type {TableDetailsJSON} */ (details));
       case 'code':
         return this._renderCode(details);
       case 'node':
-        return this.renderNode(/** @type {!DetailsRenderer.NodeDetailsJSON} */(details));
+        return this.renderNode(/** @type {NodeDetailsJSON} */(details));
       case 'criticalrequestchain':
         return CriticalRequestChainRenderer.render(this._dom, this._templateContext,
-          /** @type {!CriticalRequestChainRenderer.CRCDetailsJSON} */ (details));
-      case 'list':
-        return this._renderList(/** @type {!DetailsRenderer.ListDetailsJSON} */ (details));
-      default:
+          // @ts-ignore - TODO(bckenny): Fix type hierarchy
+          /** @type {CRCDetailsJSON} */ (details));
+      default: {
         throw new Error(`Unknown type: ${details.type}`);
+      }
     }
   }
 
   /**
-   * @param {!DetailsRenderer.DetailsJSON} text
-   * @return {!Element}
+   * @param {NumericUnitDetailsJSON} details
+   * @return {Element}
+   */
+  _renderBytes(details) {
+    // TODO: handle displayUnit once we have something other than 'kb'
+    const value = Util.formatBytesToKB(details.value, details.granularity);
+    return this._renderText({type: 'text', value});
+  }
+
+  /**
+   * @param {NumericUnitDetailsJSON} details
+   * @return {Element}
+   */
+  _renderMilliseconds(details) {
+    let value = Util.formatMilliseconds(details.value, details.granularity);
+    if (details.displayUnit === 'duration') {
+      value = Util.formatDuration(details.value);
+    }
+
+    return this._renderText({type: 'text', value});
+  }
+
+  /**
+   * @param {StringDetailsJSON} text
+   * @return {HTMLElement}
    */
   _renderTextURL(text) {
-    const url = text.text || '';
+    const url = text.value;
 
     let displayedPath;
     let displayedHost;
     let title;
     try {
       const parsed = Util.parseURL(url);
-      displayedPath = parsed.file;
-      displayedHost = `(${parsed.hostname})`;
+      displayedPath = parsed.file === '/' ? parsed.origin : parsed.file;
+      displayedHost = parsed.file === '/' ? '' : `(${parsed.hostname})`;
       title = url;
     } catch (/** @type {!Error} */ e) {
       if (!(e instanceof TypeError)) {
@@ -81,15 +113,15 @@
       displayedPath = url;
     }
 
-    const element = this._dom.createElement('div', 'lh-text__url');
+    const element = /** @type {HTMLElement} */ (this._dom.createElement('div', 'lh-text__url'));
     element.appendChild(this._renderText({
-      text: displayedPath,
+      value: displayedPath,
       type: 'text',
     }));
 
     if (displayedHost) {
       const hostElem = this._renderText({
-        text: displayedHost,
+        value: displayedHost,
         type: 'text',
       });
       hostElem.classList.add('lh-text__url-host');
@@ -101,18 +133,21 @@
   }
 
   /**
-   * @param {!DetailsRenderer.LinkDetailsJSON} details
-   * @return {!Element}
+   * @param {LinkDetailsJSON} details
+   * @return {Element}
    */
   _renderLink(details) {
     const allowedProtocols = ['https:', 'http:'];
     const url = new URL(details.url);
     if (!allowedProtocols.includes(url.protocol)) {
-      // Fall back to text if protocol not allowed.
-      return this._renderText(details);
+      // Fall back to just the link text if protocol not allowed.
+      return this._renderText({
+        type: 'text',
+        value: details.text,
+      });
     }
 
-    const a = /** @type {!HTMLAnchorElement} */ (this._dom.createElement('a'));
+    const a = /** @type {HTMLAnchorElement} */ (this._dom.createElement('a'));
     a.rel = 'noopener';
     a.target = '_blank';
     a.textContent = details.text;
@@ -122,100 +157,104 @@
   }
 
   /**
-   * @param {!DetailsRenderer.DetailsJSON} text
-   * @return {!Element}
+   * @param {StringDetailsJSON} text
+   * @return {Element}
    */
   _renderText(text) {
     const element = this._dom.createElement('div', 'lh-text');
-    element.textContent = text.text;
+    element.textContent = text.value;
     return element;
   }
 
   /**
    * Create small thumbnail with scaled down image asset.
    * If the supplied details doesn't have an image/* mimeType, then an empty span is returned.
-   * @param {!DetailsRenderer.ThumbnailDetails} value
-   * @return {!Element}
+   * @param {ThumbnailDetails} details
+   * @return {Element}
    */
-  _renderThumbnail(value) {
-    if (/^image/.test(value.mimeType) === false) {
-      return this._dom.createElement('span');
-    }
-
-    const element = this._dom.createElement('img', 'lh-thumbnail');
-    element.src = value.url;
+  _renderThumbnail(details) {
+    const element = /** @type {HTMLImageElement}*/ (this._dom.createElement('img', 'lh-thumbnail'));
+    /** @type {string} */
+    // @ts-ignore - type should have a value if we get here.
+    const strValue = details.value;
+    element.src = strValue;
+    element.title = strValue;
     element.alt = '';
-    element.title = value.url;
     return element;
   }
 
   /**
-   * @param {!DetailsRenderer.ListDetailsJSON} list
-   * @return {!Element}
-   */
-  _renderList(list) {
-    if (!list.items.length) return this._dom.createElement('span');
-
-    const element = this._dom.createElement('details', 'lh-details');
-    element.open = true;
-    if (list.header) {
-      const summary = this._dom.createElement('summary', 'lh-list__header');
-      summary.textContent = list.header.text;
-      element.appendChild(summary);
-    }
-
-    const itemsElem = this._dom.createChildOf(element, 'div', 'lh-list__items');
-    for (const item of list.items) {
-      const itemElem = this._dom.createChildOf(itemsElem, 'span', 'lh-list__item');
-      itemElem.appendChild(this.render(item));
-    }
-    return element;
-  }
-
-  /**
-   * @param {!DetailsRenderer.TableDetailsJSON} details
-   * @return {!Element}
+   * @param {TableDetailsJSON} details
+   * @return {Element}
    */
   _renderTable(details) {
     if (!details.items.length) return this._dom.createElement('span');
 
-    const element = this._dom.createElement('details', 'lh-details');
-    element.open = true;
-    if (details.header) {
-      element.appendChild(this._dom.createElement('summary')).textContent = details.header;
-    }
-
-    const tableElem = this._dom.createChildOf(element, 'table', 'lh-table');
+    const tableElem = this._dom.createElement('table', 'lh-table');
     const theadElem = this._dom.createChildOf(tableElem, 'thead');
     const theadTrElem = this._dom.createChildOf(theadElem, 'tr');
 
-    for (const heading of details.itemHeaders) {
+    for (const heading of details.headings) {
       const itemType = heading.itemType || 'text';
       const classes = `lh-table-column--${itemType}`;
-      this._dom.createChildOf(theadTrElem, 'th', classes).appendChild(this.render(heading));
+      this._dom.createChildOf(theadTrElem, 'th', classes).appendChild(this.render({
+        type: 'text',
+        value: heading.text || '',
+      }));
     }
 
     const tbodyElem = this._dom.createChildOf(tableElem, 'tbody');
     for (const row of details.items) {
       const rowElem = this._dom.createChildOf(tbodyElem, 'tr');
-      for (const columnItem of row) {
-        const classes = `lh-table-column--${columnItem.type}`;
-        this._dom.createChildOf(rowElem, 'td', classes).appendChild(this.render(columnItem));
+      for (const heading of details.headings) {
+        const key = /** @type {keyof DetailsJSON} */ (heading.key);
+        // TODO(bckenny): type should be naturally inferred here.
+        const value = /** @type {number|string|DetailsJSON|undefined} */ (row[key]);
+
+        if (typeof value === 'undefined' || value === null) {
+          this._dom.createChildOf(rowElem, 'td', 'lh-table-column--empty');
+          continue;
+        }
+        // handle nested types like code blocks in table rows.
+        // @ts-ignore - TODO(bckenny): narrow first
+        if (value.type) {
+          const valueAsDetails = /** @type {DetailsJSON} */ (value);
+          const classes = `lh-table-column--${valueAsDetails.type}`;
+          this._dom.createChildOf(rowElem, 'td', classes).appendChild(this.render(valueAsDetails));
+          continue;
+        }
+
+        // build new details item to render
+        const item = {
+          value: /** @type {number|string} */ (value),
+          type: heading.itemType,
+          displayUnit: heading.displayUnit,
+          granularity: heading.granularity,
+        };
+
+        /** @type {string|undefined} */
+        // @ts-ignore - TODO(bckenny): handle with refactoring above
+        const valueType = value.type;
+        const classes = `lh-table-column--${valueType || heading.itemType}`;
+        this._dom.createChildOf(rowElem, 'td', classes).appendChild(this.render(item));
       }
     }
-    return element;
+    return tableElem;
   }
 
   /**
-   * @param {!DetailsRenderer.NodeDetailsJSON} item
-   * @return {!Element}
+   * @param {NodeDetailsJSON} item
+   * @return {Element}
    * @protected
    */
   renderNode(item) {
-    const element = this._dom.createElement('span', 'lh-node');
-    element.textContent = item.snippet;
-    element.title = item.selector;
-    if (item.text) element.setAttribute('data-text', item.text);
+    const element = /** @type {HTMLSpanElement} */ (this._dom.createElement('span', 'lh-node'));
+    if (item.snippet) {
+      element.textContent = item.snippet;
+    }
+    if (item.selector) {
+      element.title = item.selector;
+    }
     if (item.path) element.setAttribute('data-path', item.path);
     if (item.selector) element.setAttribute('data-selector', item.selector);
     if (item.snippet) element.setAttribute('data-snippet', item.snippet);
@@ -223,71 +262,29 @@
   }
 
   /**
-   * @param {!DetailsRenderer.CardsDetailsJSON} details
-   * @return {!Element}
-   */
-  _renderCards(details) {
-    const element = this._dom.createElement('details', 'lh-details');
-    element.open = true;
-    if (details.header) {
-      element.appendChild(this._dom.createElement('summary')).textContent = details.header.text;
-    }
-
-    const cardsParent = this._dom.createElement('div', 'lh-scorecards');
-    for (const item of details.items) {
-      const card = cardsParent.appendChild(
-          this._dom.createElement('div', 'lh-scorecard', {title: item.snippet}));
-      const titleEl = this._dom.createElement('div', 'lh-scorecard__title');
-      const valueEl = this._dom.createElement('div', 'lh-scorecard__value');
-      const targetEl = this._dom.createElement('div', 'lh-scorecard__target');
-
-      card.appendChild(titleEl).textContent = item.title;
-      card.appendChild(valueEl).textContent = item.value;
-
-      if (item.target) {
-        card.appendChild(targetEl).textContent = `target: ${item.target}`;
-      }
-    }
-
-    element.appendChild(cardsParent);
-    return element;
-  }
-
-  /**
-   * @param {!DetailsRenderer.FilmstripDetails} details
-   * @return {!Element}
+   * @param {FilmstripDetails} details
+   * @return {Element}
    */
   _renderFilmstrip(details) {
     const filmstripEl = this._dom.createElement('div', 'lh-filmstrip');
 
     for (const thumbnail of details.items) {
       const frameEl = this._dom.createChildOf(filmstripEl, 'div', 'lh-filmstrip__frame');
-
-      let timing = Util.formatMilliseconds(thumbnail.timing, 1);
-      if (thumbnail.timing > 1000) {
-        timing = Util.formatNumber(thumbnail.timing / 1000) + ' s';
-      }
-
-      const timingEl = this._dom.createChildOf(frameEl, 'div', 'lh-filmstrip__timestamp');
-      timingEl.textContent = timing;
-
-      const base64data = thumbnail.data;
       this._dom.createChildOf(frameEl, 'img', 'lh-filmstrip__thumbnail', {
-        src: `data:image/jpeg;base64,${base64data}`,
-        alt: `Screenshot at ${timing}`,
+        src: `data:image/jpeg;base64,${thumbnail.data}`,
+        alt: `Screenshot`,
       });
     }
-
     return filmstripEl;
   }
 
   /**
-   * @param {!DetailsRenderer.DetailsJSON} details
-   * @return {!Element}
+   * @param {DetailsJSON} details
+   * @return {Element}
    */
   _renderCode(details) {
     const pre = this._dom.createElement('pre', 'lh-code');
-    pre.textContent = details.text;
+    pre.textContent = /** @type {string} */ (details.value);
     return pre;
   }
 }
@@ -298,91 +295,84 @@
   self.DetailsRenderer = DetailsRenderer;
 }
 
+// TODO, what's the diff between DetailsJSON and NumericUnitDetailsJSON?
 /**
  * @typedef {{
- *     type: string,
- *     text: (string|undefined)
- * }}
+      type: string,
+      value: (string|number|undefined),
+      summary?: OpportunitySummary,
+      granularity?: number,
+      displayUnit?: string
+  }} DetailsJSON
  */
-DetailsRenderer.DetailsJSON; // eslint-disable-line no-unused-expressions
 
 /**
  * @typedef {{
- *     type: string,
- *     header: ({text: string}|undefined),
- *     items: !Array<{type: string, text: (string|undefined)}>
- * }}
+      type: string,
+      value: string,
+      granularity?: number,
+      displayUnit?: string,
+  }} StringDetailsJSON
  */
-DetailsRenderer.ListDetailsJSON; // eslint-disable-line no-unused-expressions
 
 /**
  * @typedef {{
- *     type: string,
- *     text: (string|undefined),
- *     path: (string|undefined),
- *     selector: (string|undefined),
- *     snippet:(string|undefined)
- * }}
+      type: string,
+      value: number,
+      granularity?: number,
+      displayUnit?: string,
+  }} NumericUnitDetailsJSON
  */
-DetailsRenderer.NodeDetailsJSON; // eslint-disable-line no-unused-expressions
-
-/** @typedef {{
- *     type: string,
- *     header: ({text: string}|undefined),
- *     items: !Array<{title: string, value: string, snippet: (string|undefined), target: string}>
- * }}
- */
-DetailsRenderer.CardsDetailsJSON; // eslint-disable-line no-unused-expressions
 
 /**
  * @typedef {{
- *     type: string,
- *     itemType: (string|undefined),
- *     text: (string|undefined)
- * }}
+      type: string,
+      path?: string,
+      selector?: string,
+      snippet?: string
+  }} NodeDetailsJSON
  */
-DetailsRenderer.TableHeaderJSON; // eslint-disable-line no-unused-expressions
 
 /**
  * @typedef {{
- *     type: string,
- *     text: (string|undefined),
- *     path: (string|undefined),
- *     selector: (string|undefined),
- *     snippet:(string|undefined)
- * }}
+      itemType: string,
+      key: string,
+      text?: string,
+      granularity?: number,
+      displayUnit?: string,
+  }} TableHeaderJSON
  */
-DetailsRenderer.NodeDetailsJSON; // eslint-disable-line no-unused-expressions
 
 /** @typedef {{
- *     type: string,
- *     header: ({text: string}|undefined),
- *     items: !Array<!Array<!DetailsRenderer.DetailsJSON>>,
- *     itemHeaders: !Array<!DetailsRenderer.TableHeaderJSON>
- * }}
+      type: string,
+      items: Array<DetailsJSON>,
+      headings: Array<TableHeaderJSON>
+  }} TableDetailsJSON
  */
-DetailsRenderer.TableDetailsJSON; // eslint-disable-line no-unused-expressions
 
 /** @typedef {{
- *     type: string,
- *     url: ({text: string}|undefined),
- *     mimeType: ({text: string}|undefined)
- * }}
+      type: string,
+      value?: string,
+  }} ThumbnailDetails
  */
-DetailsRenderer.ThumbnailDetails; // eslint-disable-line no-unused-expressions
 
 /** @typedef {{
- *     type: string,
- *     url: string,
- *     text: string
- * }}
+      type: string,
+      text: string,
+      url: string
+  }} LinkDetailsJSON
  */
-DetailsRenderer.LinkDetailsJSON; // eslint-disable-line no-unused-expressions
 
 /** @typedef {{
- *     type: string,
- *     scale: number,
- *     items: !Array<{timing: number, timestamp: number, data: string}>,
- * }}
+      type: string,
+      scale: number,
+      items: Array<{timing: number, timestamp: number, data: string}>,
+  }} FilmstripDetails
  */
-DetailsRenderer.FilmstripDetails; // eslint-disable-line no-unused-expressions
+
+
+/** @typedef {{
+      wastedMs?: number,
+      wastedBytes?: number
+  }} OpportunitySummary
+ */
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/dom.js b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/dom.js
index a12f794e..69ce25b 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/dom.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/dom.js
@@ -9,20 +9,21 @@
 
 class DOM {
   /**
-   * @param {!Document} document
+   * @param {Document} document
    */
   constructor(document) {
-    /** @private {!Document} */
+    /** @type {Document} */
     this._document = document;
   }
 
+  // TODO(bckenny): can pass along `createElement`'s inferred type
   /**
    * @param {string} name
    * @param {string=} className
-   * @param {!Object<string, (string|undefined)>=} attrs Attribute key/val pairs.
+   * @param {Object<string, (string|undefined)>=} attrs Attribute key/val pairs.
    *     Note: if an attribute key has an undefined value, this method does not
    *     set the attribute on the node.
-   * @return {!Element}
+   * @return {Element}
    */
   createElement(name, className, attrs = {}) {
     const element = this._document.createElement(name);
@@ -39,13 +40,20 @@
   }
 
   /**
-   * @param {!Element} parentElem
+   * @return {DocumentFragment}
+   */
+  createFragment() {
+    return this._document.createDocumentFragment();
+  }
+
+  /**
+   * @param {Element} parentElem
    * @param {string} elementName
    * @param {string=} className
-   * @param {!Object<string, (string|undefined)>=} attrs Attribute key/val pairs.
+   * @param {Object<string, (string|undefined)>=} attrs Attribute key/val pairs.
    *     Note: if an attribute key has an undefined value, this method does not
    *     set the attribute on the node.
-   * @return {!Element}
+   * @return {Element}
    */
   createChildOf(parentElem, elementName, className, attrs) {
     const element = this.createElement(elementName, className, attrs);
@@ -55,8 +63,8 @@
 
   /**
    * @param {string} selector
-   * @param {!Node} context
-   * @return {!DocumentFragment} A clone of the template content.
+   * @param {ParentNode} context
+   * @return {DocumentFragment} A clone of the template content.
    * @throws {Error}
    */
   cloneTemplate(selector, context) {
@@ -65,15 +73,14 @@
       throw new Error(`Template not found: template${selector}`);
     }
 
-    const clone = /** @type {!DocumentFragment} */ (this._document.importNode(
-        template.content, true));
+    const clone = this._document.importNode(template.content, true);
 
     // Prevent duplicate styles in the DOM. After a template has been stamped
     // for the first time, remove the clone's styles so they're not re-added.
     if (template.hasAttribute('data-stamped')) {
       this.findAll('style', clone).forEach(style => style.remove());
     }
-    template.setAttribute('data-stamped', true);
+    template.setAttribute('data-stamped', 'true');
 
     return clone;
   }
@@ -89,7 +96,7 @@
 
   /**
    * @param {string} text
-   * @return {!Element}
+   * @return {Element}
    */
   convertMarkdownLinkSnippets(text) {
     const element = this.createElement('span');
@@ -104,7 +111,7 @@
 
       // Append link if there are any.
       if (linkText && linkHref) {
-        const a = /** @type {!HTMLAnchorElement} */ (this.createElement('a'));
+        const a = /** @type {HTMLAnchorElement} */ (this.createElement('a'));
         a.rel = 'noopener';
         a.target = '_blank';
         a.textContent = linkText;
@@ -118,7 +125,7 @@
 
   /**
    * @param {string} text
-   * @return {!Element}
+   * @return {Element}
    */
   convertMarkdownCodeSnippets(text) {
     const element = this.createElement('span');
@@ -129,7 +136,7 @@
       const [preambleText, codeText] = parts.splice(0, 2);
       element.appendChild(this._document.createTextNode(preambleText));
       if (codeText) {
-        const pre = /** @type {!HTMLPreElement} */ (this.createElement('code'));
+        const pre = /** @type {HTMLPreElement} */ (this.createElement('code'));
         pre.textContent = codeText;
         element.appendChild(pre);
       }
@@ -139,20 +146,29 @@
   }
 
   /**
-   * @return {!Document}
+   * @return {Document}
    */
   document() {
     return this._document;
   }
 
   /**
+   * TODO(paulirish): import and conditionally apply the DevTools frontend subclasses instead of this
+   * @return {boolean}
+   */
+  isDevTools() {
+    return !!this._document.querySelector('.lh-devtools');
+  }
+
+  /**
    * Guaranteed context.querySelector. Always returns an element or throws if
    * nothing matches query.
    * @param {string} query
-   * @param {!Node} context
-   * @return {!Element}
+   * @param {ParentNode} context
+   * @return {HTMLElement}
    */
   find(query, context) {
+    /** @type {?HTMLElement} */
     const result = context.querySelector(query);
     if (result === null) {
       throw new Error(`query ${query} not found`);
@@ -163,8 +179,8 @@
   /**
    * Helper for context.querySelectorAll. Returns an Array instead of a NodeList.
    * @param {string} query
-   * @param {!Node} context
-   * @return {!Array<!Element>}
+   * @param {ParentNode} context
+   * @return {Array<HTMLElement>}
    */
   findAll(query, context) {
     return Array.from(context.querySelectorAll(query));
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/performance-category-renderer.js b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/performance-category-renderer.js
new file mode 100644
index 0000000..71b1667
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/performance-category-renderer.js
@@ -0,0 +1,203 @@
+/**
+ * @license Copyright 2018 Google Inc. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+ */
+'use strict';
+
+/* globals self, Util, CategoryRenderer */
+
+/** @typedef {import('./dom.js')} DOM */
+/** @typedef {import('./report-renderer.js').CategoryJSON} CategoryJSON */
+/** @typedef {import('./report-renderer.js').GroupJSON} GroupJSON */
+/** @typedef {import('./report-renderer.js').AuditJSON} AuditJSON */
+/** @typedef {import('./details-renderer.js').OpportunitySummary} OpportunitySummary */
+/** @typedef {import('./details-renderer.js').FilmstripDetails} FilmstripDetails */
+
+class PerformanceCategoryRenderer extends CategoryRenderer {
+  /**
+   * @param {AuditJSON} audit
+   * @return {Element}
+   */
+  _renderMetric(audit) {
+    const tmpl = this.dom.cloneTemplate('#tmpl-lh-metric', this.templateContext);
+    const element = this.dom.find('.lh-metric', tmpl);
+    element.id = audit.result.id;
+    const rating = Util.calculateRating(audit.result.score, audit.result.scoreDisplayMode);
+    element.classList.add(`lh-metric--${rating}`);
+
+    const titleEl = this.dom.find('.lh-metric__title', tmpl);
+    titleEl.textContent = audit.result.title;
+
+    const valueEl = this.dom.find('.lh-metric__value', tmpl);
+    valueEl.textContent = Util.formatDisplayValue(audit.result.displayValue);
+
+    const descriptionEl = this.dom.find('.lh-metric__description', tmpl);
+    descriptionEl.appendChild(this.dom.convertMarkdownLinkSnippets(audit.result.description));
+
+    if (audit.result.scoreDisplayMode === 'error') {
+      descriptionEl.textContent = '';
+      valueEl.textContent = 'Error!';
+      const tooltip = this.dom.createChildOf(descriptionEl, 'span');
+      tooltip.textContent = audit.result.errorMessage || 'Report error: no metric information';
+    }
+
+    return element;
+  }
+
+  /**
+   * @param {AuditJSON} audit
+   * @param {number} index
+   * @param {number} scale
+   * @return {Element}
+   */
+  _renderOpportunity(audit, index, scale) {
+    const oppTmpl = this.dom.cloneTemplate('#tmpl-lh-opportunity', this.templateContext);
+    const element = this.populateAuditValues(audit, index, oppTmpl);
+    element.id = audit.result.id;
+
+    const details = audit.result.details;
+    if (!details) {
+      return element;
+    }
+    const summaryInfo = /** @type {OpportunitySummary} */ (details.summary);
+    if (!summaryInfo || !summaryInfo.wastedMs || audit.result.scoreDisplayMode === 'error') {
+      return element;
+    }
+
+    // Overwrite the displayValue with opportunity's wastedMs
+    const displayEl = this.dom.find('.lh-audit__display-text', element);
+    const sparklineWidthPct = `${summaryInfo.wastedMs / scale * 100}%`;
+    this.dom.find('.lh-sparkline__bar', element).style.width = sparklineWidthPct;
+    displayEl.textContent = Util.formatSeconds(summaryInfo.wastedMs, 0.01);
+
+    // Set [title] tooltips
+    if (audit.result.displayValue) {
+      const displayValue = Util.formatDisplayValue(audit.result.displayValue);
+      this.dom.find('.lh-load-opportunity__sparkline', element).title = displayValue;
+      displayEl.title = displayValue;
+    }
+
+    return element;
+  }
+
+  /**
+   * Get an audit's wastedMs to sort the opportunity by, and scale the sparkline width
+   * Opportunties with an error won't have a summary object, so MIN_VALUE is returned to keep any
+   * erroring opportunities last in sort order.
+   * @param {AuditJSON} audit
+   * @return {number}
+   */
+  _getWastedMs(audit) {
+    if (
+      audit.result.details &&
+      audit.result.details.summary &&
+      typeof audit.result.details.summary.wastedMs === 'number'
+    ) {
+      return audit.result.details.summary.wastedMs;
+    } else {
+      return Number.MIN_VALUE;
+    }
+  }
+
+  /**
+   * @param {CategoryJSON} category
+   * @param {Object<string, GroupJSON>} groups
+   * @return {Element}
+   * @override
+   */
+  render(category, groups) {
+    const element = this.dom.createElement('div', 'lh-category');
+    this.createPermalinkSpan(element, category.id);
+    element.appendChild(this.renderCategoryHeader(category));
+
+    // Metrics
+    const metricAudits = category.auditRefs.filter(audit => audit.group === 'metrics');
+    const metricAuditsEl = this.renderAuditGroup(groups['metrics'], {expandable: false});
+
+    const keyMetrics = metricAudits.filter(a => a.weight >= 3);
+    const otherMetrics = metricAudits.filter(a => a.weight < 3);
+
+    const metricsBoxesEl = this.dom.createChildOf(metricAuditsEl, 'div', 'lh-metric-container');
+    const metricsColumn1El = this.dom.createChildOf(metricsBoxesEl, 'div', 'lh-metric-column');
+    const metricsColumn2El = this.dom.createChildOf(metricsBoxesEl, 'div', 'lh-metric-column');
+
+    keyMetrics.forEach(item => {
+      metricsColumn1El.appendChild(this._renderMetric(item));
+    });
+    otherMetrics.forEach(item => {
+      metricsColumn2El.appendChild(this._renderMetric(item));
+    });
+    const estValuesEl = this.dom.createChildOf(metricsColumn2El, 'div',
+        'lh-metrics__disclaimer lh-metrics__disclaimer');
+    estValuesEl.textContent = 'Values are estimated and may vary.';
+
+    metricAuditsEl.classList.add('lh-audit-group--metrics');
+    element.appendChild(metricAuditsEl);
+
+    // Filmstrip
+    const timelineEl = this.dom.createChildOf(element, 'div', 'lh-filmstrip-container');
+    const thumbnailAudit = category.auditRefs.find(audit => audit.id === 'screenshot-thumbnails');
+    const thumbnailResult = thumbnailAudit && thumbnailAudit.result;
+    if (thumbnailResult && thumbnailResult.details) {
+      timelineEl.id = thumbnailResult.id;
+      const filmstripEl = this.detailsRenderer.render(thumbnailResult.details);
+      timelineEl.appendChild(filmstripEl);
+    }
+
+    // Opportunities
+    const opportunityAudits = category.auditRefs
+        .filter(audit => audit.group === 'load-opportunities' && !Util.showAsPassed(audit.result))
+        .sort((auditA, auditB) => this._getWastedMs(auditB) - this._getWastedMs(auditA));
+
+    if (opportunityAudits.length) {
+      // Scale the sparklines relative to savings, minimum 2s to not overstate small savings
+      const minimumScale = 2000;
+      const wastedMsValues = opportunityAudits.map(audit => this._getWastedMs(audit));
+      const maxWaste = Math.max(...wastedMsValues);
+      const scale = Math.max(Math.ceil(maxWaste / 1000) * 1000, minimumScale);
+      const groupEl = this.renderAuditGroup(groups['load-opportunities'], {expandable: false});
+      const tmpl = this.dom.cloneTemplate('#tmpl-lh-opportunity-header', this.templateContext);
+      const headerEl = this.dom.find('.lh-load-opportunity__header', tmpl);
+      groupEl.appendChild(headerEl);
+      opportunityAudits.forEach((item, i) =>
+          groupEl.appendChild(this._renderOpportunity(item, i, scale)));
+      groupEl.classList.add('lh-audit-group--opportunities');
+      element.appendChild(groupEl);
+    }
+
+    // Diagnostics
+    const diagnosticAudits = category.auditRefs
+        .filter(audit => audit.group === 'diagnostics' && !Util.showAsPassed(audit.result))
+        .sort((a, b) => {
+          const scoreA = a.result.scoreDisplayMode === 'informative' ? 100 : Number(a.result.score);
+          const scoreB = b.result.scoreDisplayMode === 'informative' ? 100 : Number(b.result.score);
+          return scoreA - scoreB;
+        });
+
+    if (diagnosticAudits.length) {
+      const groupEl = this.renderAuditGroup(groups['diagnostics'], {expandable: false});
+      diagnosticAudits.forEach((item, i) => groupEl.appendChild(this.renderAudit(item, i)));
+      groupEl.classList.add('lh-audit-group--diagnostics');
+      element.appendChild(groupEl);
+    }
+
+    // Passed audits
+    const passedElements = category.auditRefs
+        .filter(audit => (audit.group === 'load-opportunities' || audit.group === 'diagnostics') &&
+            Util.showAsPassed(audit.result))
+        .map((audit, i) => this.renderAudit(audit, i));
+
+    if (!passedElements.length) return element;
+
+    const passedElem = this.renderPassedAuditsSection(passedElements);
+    element.appendChild(passedElem);
+    return element;
+  }
+}
+
+if (typeof module !== 'undefined' && module.exports) {
+  module.exports = PerformanceCategoryRenderer;
+} else {
+  self.PerformanceCategoryRenderer = PerformanceCategoryRenderer;
+}
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js
index 92ca071..011dfe8 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/report-renderer.js
@@ -12,119 +12,120 @@
  * Dummy text for ensuring report robustness: </script> pre$`post %%LIGHTHOUSE_JSON%%
  */
 
-/* globals self, Util */
+/** @typedef {import('./dom.js')} DOM */
+/** @typedef {import('./details-renderer.js').DetailsJSON} DetailsJSON */
+
+/* globals self, Util, DetailsRenderer, CategoryRenderer, PerformanceCategoryRenderer */
 
 class ReportRenderer {
   /**
-   * @param {!DOM} dom
-   * @param {!CategoryRenderer} categoryRenderer
+   * @param {DOM} dom
    */
-  constructor(dom, categoryRenderer) {
-    /** @private {!DOM} */
+  constructor(dom) {
+    /** @type {DOM} */
     this._dom = dom;
-    /** @private {!CategoryRenderer} */
-    this._categoryRenderer = categoryRenderer;
-    /** @private {!Document|!Element} */
+    /** @type {ParentNode} */
     this._templateContext = this._dom.document();
   }
 
   /**
-   * @param {!ReportRenderer.ReportJSON} report
-   * @param {!Element} container Parent element to render the report into.
+   * @param {ReportJSON} report
+   * @param {Element} container Parent element to render the report into.
    */
   renderReport(report, container) {
-    container.textContent = ''; // Remove previous report.
-    const element = container.appendChild(this._renderReport(report));
+    // If any mutations happen to the report within the renderers, we want the original object untouched
+    const clone = /** @type {ReportJSON} */ (JSON.parse(JSON.stringify(report)));
 
-    return /** @type {!Element} **/ (element);
+    // TODO(phulce): we all agree this is technical debt we should fix
+    if (typeof clone.categories !== 'object') throw new Error('No categories provided.');
+    clone.reportCategories = Object.values(clone.categories);
+    ReportRenderer.smooshAuditResultsIntoCategories(clone.audits, clone.reportCategories);
+
+    container.textContent = ''; // Remove previous report.
+    container.appendChild(this._renderReport(clone));
+    return /** @type {Element} **/ (container);
   }
 
   /**
    * Define a custom element for <templates> to be extracted from. For example:
    *     this.setTemplateContext(new DOMParser().parseFromString(htmlStr, 'text/html'))
-   * @param {!Document|!Element} context
+   * @param {ParentNode} context
    */
   setTemplateContext(context) {
     this._templateContext = context;
-    this._categoryRenderer.setTemplateContext(context);
   }
 
   /**
-   * @param {!ReportRenderer.ReportJSON} report
-   * @return {!DocumentFragment}
+   * @param {ReportJSON} report
+   * @return {DocumentFragment}
    */
   _renderReportHeader(report) {
-    const header = this._dom.cloneTemplate('#tmpl-lh-heading', this._templateContext);
-    this._dom.find('.lh-config__timestamp', header).textContent =
-        Util.formatDateTime(report.generatedTime);
-    const url = this._dom.find('.lh-metadata__url', header);
-    url.href = report.url;
-    url.textContent = report.url;
+    const el = this._dom.cloneTemplate('#tmpl-lh-heading', this._templateContext);
+    const domFragment = this._dom.cloneTemplate('#tmpl-lh-scores-wrapper', this._templateContext);
+    const placeholder = this._dom.find('.lh-scores-wrapper-placeholder', el);
+    /** @type {HTMLDivElement} */ (placeholder.parentNode).replaceChild(domFragment, placeholder);
 
-    this._dom.find('.lh-env__item__ua', header).textContent = report.userAgent;
+    this._dom.find('.lh-config__timestamp', el).textContent =
+        Util.formatDateTime(report.fetchTime);
+    this._dom.find('.lh-product-info__version', el).textContent = report.lighthouseVersion;
+    const metadataUrl = /** @type {HTMLAnchorElement} */ (this._dom.find('.lh-metadata__url', el));
+    const toolbarUrl = /** @type {HTMLAnchorElement}*/ (this._dom.find('.lh-toolbar__url', el));
+    metadataUrl.href = metadataUrl.textContent = report.finalUrl;
+    toolbarUrl.href = toolbarUrl.textContent = report.finalUrl;
 
-    const env = this._dom.find('.lh-env__items', header);
-    report.runtimeConfig.environment.forEach(runtime => {
-      const item = this._dom.cloneTemplate('#tmpl-lh-env__items', env);
-      this._dom.find('.lh-env__name', item).textContent = runtime.name;
-      this._dom.find('.lh-env__description', item).textContent = runtime.description;
-      this._dom.find('.lh-env__enabled', item).textContent =
-          runtime.enabled ? 'Enabled' : 'Disabled';
-      env.appendChild(item);
-    });
-
-    return header;
+    const emulationDescriptions = Util.getEmulationDescriptions(report.configSettings || {});
+    this._dom.find('.lh-config__emulation', el).textContent = emulationDescriptions.summary;
+    return el;
   }
 
   /**
-   * @param {!ReportRenderer.ReportJSON} report
-   * @return {!DocumentFragment}
+   * @return {Element}
+   */
+  _renderReportShortHeader() {
+    const shortHeaderContainer = this._dom.createElement('div', 'lh-header-container');
+    const wrapper = this._dom.cloneTemplate('#tmpl-lh-scores-wrapper', this._templateContext);
+    shortHeaderContainer.appendChild(wrapper);
+    return shortHeaderContainer;
+  }
+
+
+  /**
+   * @param {ReportJSON} report
+   * @return {DocumentFragment}
    */
   _renderReportFooter(report) {
     const footer = this._dom.cloneTemplate('#tmpl-lh-footer', this._templateContext);
+
+    const env = this._dom.find('.lh-env__items', footer);
+    env.id = 'runtime-settings';
+    const envValues = Util.getEnvironmentDisplayValues(report.configSettings || {});
+    [
+      {name: 'URL', description: report.finalUrl},
+      {name: 'Fetch time', description: Util.formatDateTime(report.fetchTime)},
+      ...envValues,
+      {name: 'User agent', description: report.userAgent},
+    ].forEach(runtime => {
+      const item = this._dom.cloneTemplate('#tmpl-lh-env__items', env);
+      this._dom.find('.lh-env__name', item).textContent = `${runtime.name}:`;
+      this._dom.find('.lh-env__description', item).textContent = runtime.description;
+      env.appendChild(item);
+    });
+
     this._dom.find('.lh-footer__version', footer).textContent = report.lighthouseVersion;
-    this._dom.find('.lh-footer__timestamp', footer).textContent =
-        Util.formatDateTime(report.generatedTime);
     return footer;
   }
 
   /**
-   * @param {!ReportRenderer.ReportJSON} report
-   * @return {!DocumentFragment}
-   */
-  _renderReportNav(report) {
-    const leftNav = this._dom.cloneTemplate('#tmpl-lh-leftnav', this._templateContext);
-
-    this._dom.find('.leftnav__header__version', leftNav).textContent =
-        `Version: ${report.lighthouseVersion}`;
-
-    const nav = this._dom.find('.lh-leftnav', leftNav);
-    for (const category of report.reportCategories) {
-      const itemsTmpl = this._dom.cloneTemplate('#tmpl-lh-leftnav__items', leftNav);
-
-      const navItem = this._dom.find('.lh-leftnav__item', itemsTmpl);
-      navItem.href = `#${category.id}`;
-
-      this._dom.find('.leftnav-item__category', navItem).textContent = category.name;
-      const score = this._dom.find('.leftnav-item__score', navItem);
-      score.classList.add(`lh-score__value--${Util.calculateRating(category.score)}`);
-      score.textContent = Math.round(category.score);
-      nav.appendChild(navItem);
-    }
-    return leftNav;
-  }
-
-  /**
    * Returns a div with a list of top-level warnings, or an empty div if no warnings.
-   * @param {!ReportRenderer.ReportJSON} report
-   * @return {!Node}
+   * @param {ReportJSON} report
+   * @return {Node}
    */
   _renderReportWarnings(report) {
     if (!report.runWarnings || report.runWarnings.length === 0) {
       return this._dom.createElement('div');
     }
 
-    const container = this._dom.cloneTemplate('#tmpl-lh-run-warnings', this._templateContext);
+    const container = this._dom.cloneTemplate('#tmpl-lh-warnings--toplevel', this._templateContext);
     const warnings = this._dom.find('ul', container);
     for (const warningString of report.runWarnings) {
       const warning = warnings.appendChild(this._dom.createElement('li'));
@@ -135,13 +136,23 @@
   }
 
   /**
-   * @param {!ReportRenderer.ReportJSON} report
-   * @return {!Element}
+   * @param {ReportJSON} report
+   * @return {DocumentFragment}
    */
   _renderReport(report) {
+    let header;
+    const headerContainer = this._dom.createElement('div');
+    if (this._dom.isDevTools()) {
+      headerContainer.classList.add('lh-header-plain');
+      header = this._renderReportShortHeader();
+    } else {
+      headerContainer.classList.add('lh-header-sticky');
+      header = this._renderReportHeader(report);
+    }
+    headerContainer.appendChild(header);
+    const scoresContainer = this._dom.find('.lh-scores-container', headerContainer);
+
     const container = this._dom.createElement('div', 'lh-container');
-    container.appendChild(this._renderReportHeader(report)); // sticky header goes at the top.
-    container.appendChild(this._renderReportNav(report));
     const reportSection = container.appendChild(this._dom.createElement('div', 'lh-report'));
 
     reportSection.appendChild(this._renderReportWarnings(report));
@@ -149,20 +160,58 @@
     let scoreHeader;
     const isSoloCategory = report.reportCategories.length === 1;
     if (!isSoloCategory) {
-      scoreHeader = reportSection.appendChild(this._dom.createElement('div', 'lh-scores-header'));
+      scoreHeader = this._dom.createElement('div', 'lh-scores-header');
+    } else {
+      headerContainer.classList.add('lh-header--solo-category');
     }
 
+    const detailsRenderer = new DetailsRenderer(this._dom);
+    const categoryRenderer = new CategoryRenderer(this._dom, detailsRenderer);
+    categoryRenderer.setTemplateContext(this._templateContext);
+    const perfCategoryRenderer = new PerformanceCategoryRenderer(this._dom, detailsRenderer);
+    perfCategoryRenderer.setTemplateContext(this._templateContext);
+
     const categories = reportSection.appendChild(this._dom.createElement('div', 'lh-categories'));
+
     for (const category of report.reportCategories) {
       if (scoreHeader) {
-        scoreHeader.appendChild(this._categoryRenderer.renderScoreGauge(category));
+        scoreHeader.appendChild(categoryRenderer.renderScoreGauge(category));
       }
-      categories.appendChild(this._categoryRenderer.render(category, report.reportGroups));
+
+      let renderer = categoryRenderer;
+      if (category.id === 'performance') {
+        renderer = perfCategoryRenderer;
+      }
+      categories.appendChild(renderer.render(category, report.categoryGroups));
+    }
+
+    if (scoreHeader) {
+      const scoreScale = this._dom.cloneTemplate('#tmpl-lh-scorescale', this._templateContext);
+      scoresContainer.appendChild(scoreHeader);
+      scoresContainer.appendChild(scoreScale);
     }
 
     reportSection.appendChild(this._renderReportFooter(report));
 
-    return container;
+    const reportFragment = this._dom.createFragment();
+    reportFragment.appendChild(headerContainer);
+    reportFragment.appendChild(container);
+
+    return reportFragment;
+  }
+
+  /**
+   * Place the AuditResult into the auditDfn (which has just weight & group)
+   * @param {Object<string, LH.Audit.Result>} audits
+   * @param {Array<CategoryJSON>} reportCategories
+   */
+  static smooshAuditResultsIntoCategories(audits, reportCategories) {
+    for (const category of reportCategories) {
+      category.auditRefs.forEach(auditMeta => {
+        const result = audits[auditMeta.id];
+        auditMeta.result = result;
+      });
+    }
   }
 }
 
@@ -174,65 +223,46 @@
 
 /**
  * @typedef {{
- *     id: string,
- *     weight: number,
- *     score: number,
- *     group: string,
- *     result: {
- *       rawValue: (number|undefined),
- *       description: string,
- *       informative: boolean,
- *       manual: boolean,
- *       notApplicable: boolean,
- *       debugString: string,
- *       displayValue: string,
- *       helpText: string,
- *       score: (number|boolean),
- *       scoringMode: string,
- *       extendedInfo: Object,
- *       details: (!DetailsRenderer.DetailsJSON|undefined)
- *     }
- * }}
+      id: string,
+      score: (number|null),
+      weight: number,
+      group?: string,
+      result: LH.Audit.Result
+  }} AuditJSON
  */
-ReportRenderer.AuditJSON; // eslint-disable-line no-unused-expressions
 
 /**
  * @typedef {{
- *     name: string,
- *     id: string,
- *     weight: number,
- *     score: number,
- *     description: string,
- *     audits: !Array<!ReportRenderer.AuditJSON>
- * }}
+      title: string,
+      id: string,
+      score: (number|null),
+      description?: string,
+      manualDescription: string,
+      auditRefs: Array<AuditJSON>
+  }} CategoryJSON
  */
-ReportRenderer.CategoryJSON; // eslint-disable-line no-unused-expressions
 
 /**
  * @typedef {{
- *     title: string,
- *     description: (string|undefined),
- * }}
+      title: string,
+      description?: string,
+  }} GroupJSON
  */
-ReportRenderer.GroupJSON; // eslint-disable-line no-unused-expressions
 
 /**
  * @typedef {{
- *     lighthouseVersion: string,
- *     userAgent: string,
- *     generatedTime: string,
- *     timing: {total: number},
- *     initialUrl: string,
- *     url: string,
- *     runWarnings: (!Array<string>|undefined),
- *     artifacts: {traces: {defaultPass: {traceEvents: !Array}}},
- *     reportCategories: !Array<!ReportRenderer.CategoryJSON>,
- *     reportGroups: !Object<string, !ReportRenderer.GroupJSON>,
- *     runtimeConfig: {
- *       blockedUrlPatterns: !Array<string>,
- *       extraHeaders: !Object,
- *       environment: !Array<{description: string, enabled: boolean, name: string}>
- *     }
- * }}
+      lighthouseVersion: string,
+      userAgent: string,
+      fetchTime: string,
+      timing: {total: number},
+      requestedUrl: string,
+      finalUrl: string,
+      runWarnings?: Array<string>,
+      artifacts: {traces: {defaultPass: {traceEvents: Array}}},
+      audits: Object<string, LH.Audit.Result>,
+      categories: Object<string, CategoryJSON>,
+      reportCategories: Array<CategoryJSON>,
+      categoryGroups: Object<string, GroupJSON>,
+      configSettings: LH.Config.Settings,
+  }} ReportJSON
  */
-ReportRenderer.ReportJSON; // eslint-disable-line no-unused-expressions
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/util.js b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/util.js
index f979251..87fc3a73 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/util.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/renderer/util.js
@@ -9,20 +9,106 @@
 
 const ELLIPSIS = '\u2026';
 const NBSP = '\xa0';
+const PASS_THRESHOLD = 0.75;
 
 const RATINGS = {
-  PASS: {label: 'pass', minScore: 75},
-  AVERAGE: {label: 'average', minScore: 45},
+  PASS: {label: 'pass', minScore: PASS_THRESHOLD},
+  AVERAGE: {label: 'average', minScore: 0.45},
   FAIL: {label: 'fail'},
+  ERROR: {label: 'error'},
 };
 
 class Util {
+  static get PASS_THRESHOLD() {
+    return PASS_THRESHOLD;
+  }
+
+  static get MS_DISPLAY_VALUE() {
+    return `%10d${NBSP}ms`;
+  }
+
   /**
-   * Convert a score to a rating label.
-   * @param {number} score
+   * @param {string|Array<string|number>=} displayValue
    * @return {string}
    */
-  static calculateRating(score) {
+  static formatDisplayValue(displayValue) {
+    if (typeof displayValue === 'string') return displayValue;
+    if (!displayValue) return '';
+
+    const replacementRegex = /%([0-9]*(\.[0-9]+)?d|s)/;
+    const template = /** @type {string} */ (displayValue[0]);
+    if (typeof template !== 'string') {
+      // First value should always be the format string, but we don't want to fail to build
+      // a report, return a placeholder.
+      return 'UNKNOWN';
+    }
+
+    let output = template;
+    for (const replacement of displayValue.slice(1)) {
+      if (!replacementRegex.test(output)) {
+        // eslint-disable-next-line no-console
+        console.warn('Too many replacements given');
+        break;
+      }
+
+      output = output.replace(replacementRegex, match => {
+        const granularity = Number(match.match(/[0-9.]+/)) || 1;
+        return match === '%s' ?
+          replacement.toLocaleString() :
+          (Math.round(Number(replacement) / granularity) * granularity).toLocaleString();
+      });
+    }
+
+    if (replacementRegex.test(output)) {
+      // eslint-disable-next-line no-console
+      console.warn('Not enough replacements given');
+    }
+
+    return output;
+  }
+
+  /**
+   * Used to determine if the "passed" for the purposes of showing up in the "failed" or "passed"
+   * sections of the report.
+   *
+   * @param {{score: (number|null), scoreDisplayMode: string}} audit
+   * @return {boolean}
+   */
+  static showAsPassed(audit) {
+    switch (audit.scoreDisplayMode) {
+      case 'manual':
+      case 'not-applicable':
+        return true;
+      case 'error':
+      case 'informative':
+        return false;
+      case 'numeric':
+      case 'binary':
+      default:
+        // Numeric audits that are within PASS_THRESHOLD will still show up with failing.
+        // For opportunities, we want to have them show up with other failing for contrast.
+        // For diagnostics, we sort by score so they'll be lowest priority.
+        return Number(audit.score) === 1;
+    }
+  }
+
+  /**
+   * Convert a score to a rating label.
+   * @param {number|null} score
+   * @param {string=} scoreDisplayMode
+   * @return {string}
+   */
+  static calculateRating(score, scoreDisplayMode) {
+    // Handle edge cases first, manual and not applicable receive 'pass', errored audits receive 'error'
+    if (scoreDisplayMode === 'manual' || scoreDisplayMode === 'not-applicable') {
+      return RATINGS.PASS.label;
+    } else if (scoreDisplayMode === 'error') {
+      return RATINGS.ERROR.label;
+    } else if (score === null) {
+      return RATINGS.FAIL.label;
+    }
+
+    // At this point, we're rating a standard binary/numeric audit
     let rating = RATINGS.FAIL.label;
     if (score >= RATINGS.PASS.minScore) {
       rating = RATINGS.PASS.label;
@@ -35,20 +121,21 @@
   /**
    * Format number.
    * @param {number} number
-   * @param {number=} decimalPlaces Number of decimal places to include. Defaults to 1.
+   * @param {number=} granularity Number of decimal places to include. Defaults to 0.1.
    * @return {string}
    */
-  static formatNumber(number, decimalPlaces = 1) {
-    return number.toLocaleString(undefined, {maximumFractionDigits: decimalPlaces});
+  static formatNumber(number, granularity = 0.1) {
+    const coarseValue = Math.round(number / granularity) * granularity;
+    return coarseValue.toLocaleString();
   }
 
   /**
    * @param {number} size
-   * @param {number=} decimalPlaces Number of decimal places to include. Defaults to 2.
+   * @param {number=} granularity Controls how coarse the displayed value is, defaults to .01
    * @return {string}
    */
-  static formatBytesToKB(size, decimalPlaces = 2) {
-    const kbs = (size / 1024).toLocaleString(undefined, {maximumFractionDigits: decimalPlaces});
+  static formatBytesToKB(size, granularity = 0.1) {
+    const kbs = (Math.round(size / 1024 / granularity) * granularity).toLocaleString();
     return `${kbs}${NBSP}KB`;
   }
 
@@ -63,6 +150,16 @@
   }
 
   /**
+   * @param {number} ms
+   * @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1
+   * @return {string}
+   */
+  static formatSeconds(ms, granularity = 0.1) {
+    const coarseTime = Math.round(ms / 1000 / granularity) * granularity;
+    return `${coarseTime.toLocaleString()}${NBSP}s`;
+  }
+
+  /**
    * Format time.
    * @param {string} date
    * @return {string}
@@ -84,19 +181,19 @@
     return formatter.format(new Date(date));
   }
   /**
-   * Converts a time in seconds into a duration string, i.e. `1d 2h 13m 52s`
-   * @param {number} timeInSeconds
-   * @param {string=} zeroLabel
+   * Converts a time in milliseconds into a duration string, i.e. `1d 2h 13m 52s`
+   * @param {number} timeInMilliseconds
    * @return {string}
    */
-  static formatDuration(timeInSeconds, zeroLabel = 'None') {
-    if (timeInSeconds === 0) {
-      return zeroLabel;
+  static formatDuration(timeInMilliseconds) {
+    let timeInSeconds = timeInMilliseconds / 1000;
+    if (Math.round(timeInSeconds) === 0) {
+      return 'None';
     }
 
-    /** @type {!Array<string>} */
+    /** @type {Array<string>} */
     const parts = [];
-    const unitLabels = /** @type {!Object<string, number>} */ ({
+    const unitLabels = /** @type {Object<string, number>} */ ({
       d: 60 * 60 * 24,
       h: 60 * 60,
       m: 60,
@@ -116,12 +213,14 @@
   }
 
   /**
-   * @param {!URL} parsedUrl
-   * @param {{numPathParts: (number|undefined), preserveQuery: (boolean|undefined), preserveHost: (boolean|undefined)}=} options
+   * @param {URL} parsedUrl
+   * @param {{numPathParts?: number, preserveQuery?: boolean, preserveHost?: boolean}=} options
    * @return {string}
    */
   static getURLDisplayName(parsedUrl, options) {
-    options = options || {};
+    // Closure optional properties aren't optional in tsc, so fallback needs undefined  values.
+    options = options || {numPathParts: undefined, preserveQuery: undefined,
+      preserveHost: undefined};
     const numPathParts = options.numPathParts !== undefined ? options.numPathParts : 2;
     const preserveQuery = options.preserveQuery !== undefined ? options.preserveQuery : true;
     const preserveHost = options.preserveHost || false;
@@ -151,7 +250,7 @@
     name = name.replace(/([a-f0-9]{7})[a-f0-9]{13}[a-f0-9]*/g, `$1${ELLIPSIS}`);
     // Also elide other hash-like mixed-case strings
     name = name.replace(/([a-zA-Z0-9-_]{9})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9-_]{10,}/g,
-        `$1${ELLIPSIS}`);
+      `$1${ELLIPSIS}`);
     // Also elide long number sequences
     name = name.replace(/(\d{3})\d{6,}/g, `$1${ELLIPSIS}`);
     // Merge any adjacent ellipses
@@ -173,8 +272,8 @@
       const dotIndex = name.lastIndexOf('.');
       if (dotIndex >= 0) {
         name = name.slice(0, MAX_LENGTH - 1 - (name.length - dotIndex)) +
-            // Show file extension
-            `${ELLIPSIS}${name.slice(dotIndex)}`;
+          // Show file extension
+          `${ELLIPSIS}${name.slice(dotIndex)}`;
       } else {
         name = name.slice(0, MAX_LENGTH - 1) + ELLIPSIS;
       }
@@ -184,13 +283,17 @@
   }
 
   /**
-   * Split a URL into a file and hostname for easy display.
+   * Split a URL into a file, hostname and origin for easy display.
    * @param {string} url
-   * @return {{file: string, hostname: string}}
+   * @return {{file: string, hostname: string, origin: string}}
    */
   static parseURL(url) {
     const parsedUrl = new URL(url);
-    return {file: Util.getURLDisplayName(parsedUrl), hostname: parsedUrl.hostname};
+    return {
+      file: Util.getURLDisplayName(parsedUrl),
+      hostname: parsedUrl.hostname,
+      origin: parsedUrl.origin,
+    };
   }
 
   /**
@@ -201,11 +304,82 @@
   static chainDuration(startTime, endTime) {
     return Util.formatNumber((endTime - startTime) * 1000);
   }
+
+  /**
+   * @param {LH.Config.Settings} settings
+   * @return {Array<{name: string, description: string}>}
+   */
+  static getEnvironmentDisplayValues(settings) {
+    const emulationDesc = Util.getEmulationDescriptions(settings);
+
+    return [
+      {
+        name: 'Device',
+        description: emulationDesc.deviceEmulation,
+      },
+      {
+        name: 'Network throttling',
+        description: emulationDesc.networkThrottling,
+      },
+      {
+        name: 'CPU throttling',
+        description: emulationDesc.cpuThrottling,
+      },
+    ];
+  }
+
+  /**
+   * @param {LH.Config.Settings} settings
+   * @return {{deviceEmulation: string, networkThrottling: string, cpuThrottling: string, summary: string}}
+   */
+  static getEmulationDescriptions(settings) {
+    let cpuThrottling;
+    let networkThrottling;
+    let summary;
+
+    const throttling = settings.throttling;
+
+    switch (settings.throttlingMethod) {
+      case 'provided':
+        cpuThrottling = 'Provided by environment';
+        networkThrottling = 'Provided by environment';
+        summary = 'No throttling applied';
+        break;
+      case 'devtools': {
+        const {cpuSlowdownMultiplier, requestLatencyMs} = throttling;
+        cpuThrottling = `${Util.formatNumber(cpuSlowdownMultiplier)}x slowdown (DevTools)`;
+        networkThrottling = `${Util.formatNumber(requestLatencyMs)}${NBSP}ms HTTP RTT, ` +
+          `${Util.formatNumber(throttling.downloadThroughputKbps)}${NBSP}Kbps down, ` +
+          `${Util.formatNumber(throttling.uploadThroughputKbps)}${NBSP}Kbps up (DevTools)`;
+        summary = 'Throttled Fast 3G network';
+        break;
+      }
+      case 'simulate': {
+        const {cpuSlowdownMultiplier, rttMs, throughputKbps} = throttling;
+        cpuThrottling = `${Util.formatNumber(cpuSlowdownMultiplier)}x slowdown (Simulated)`;
+        networkThrottling = `${Util.formatNumber(rttMs)}${NBSP}ms TCP RTT, ` +
+          `${Util.formatNumber(throughputKbps)}${NBSP}Kbps throughput (Simulated)`;
+        summary = 'Simulated Fast 3G network';
+        break;
+      }
+      default:
+        cpuThrottling = 'Unknown';
+        networkThrottling = 'Unknown';
+        summary = 'Unknown';
+    }
+
+    const deviceEmulation = settings.disableDeviceEmulation ? 'No emulation' : 'Emulated Nexus 5X';
+    return {
+      deviceEmulation,
+      cpuThrottling,
+      networkThrottling,
+      summary: `${deviceEmulation}, ${summary}`,
+    };
+  }
 }
 
 if (typeof module !== 'undefined' && module.exports) {
   module.exports = Util;
 } else {
-  // @ts-ignore
   self.Util = Util;
 }
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/report-styles.css b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/report-styles.css
index e56bc6e3..c3c9a43c 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/report-styles.css
+++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/report-styles.css
@@ -7,10 +7,13 @@
 .lh-vars {
   --text-font-family: Roboto, Helvetica, Arial, sans-serif;
   --monospace-font-family: 'Menlo', 'dejavu sans mono', 'Consolas', 'Lucida Console', monospace;
+  --body-background-color: #fff;
   --body-font-size: 14px;
   --body-line-height: 18px;
-  --subheader-font-size: 16px;
+  --subheader-font-size: 14px;
   --subheader-line-height: 20px;
+  --subheader-color: hsl(206, 6%, 25%);
+  --header-bg-color: #f1f3f4;
   --header-font-size: 20px;
   --header-line-height: 24px;
   --title-font-size: 24px;
@@ -18,25 +21,31 @@
   --caption-font-size: 12px;
   --caption-line-height: 16px;
   --default-padding: 12px;
-  --section-padding: 20px;
+  --section-padding: 16px;
   --section-indent: 16px;
   --audit-group-indent: 16px;
+  --audit-item-gap: 5px;
   --audit-indent: 16px;
+  --text-indent: 8px;
   --expandable-indent: 20px;
   --secondary-text-color: #565656;
   /*--accent-color: #3879d9;*/
-  --fail-color: #df332f;
-  --pass-color: #2b882f;
+  --fail-color: hsl(1, 73%, 45%);
+  --average-color: hsl(31, 100%, 45%); /* md orange 800 */
+  --pass-color: hsl(139, 70%, 30%);
   --informative-color: #0c50c7;
-  --manual-color: #757575;
-  --average-color: #ef6c00; /* md orange 800 */
+  --medium-75-gray: #757575;
+  --medium-50-gray: hsl(210, 17%, 98%);
+  --medium-100-gray: hsl(200, 12%, 95%);
   --warning-color: #ffab00; /* md amber a700 */
   --report-border-color: #ccc;
   --report-secondary-border-color: #ebebeb;
   --metric-timeline-rule-color: #b3b3b3;
+  --display-value-gray: hsl(216, 5%, 39%);
   --report-width: calc(60 * var(--body-font-size));
-  --report-menu-width: calc(20 * var(--body-font-size));
-  --report-content-width: calc(var(--report-width) + var(--report-menu-width));
+  --report-content-width: calc(var(--report-width));
+  --report-header-height: 161px;
+  --report-header-color: #202124;
   --navitem-font-size: var(--body-font-size);
   --navitem-line-height: var(--body-line-height);
   --navitem-hpadding: var(--body-font-size);
@@ -44,21 +53,34 @@
   --lh-score-highlight-bg: hsla(0, 0%, 90%, 0.2);
   --lh-score-icon-background-size: 24px;
   --lh-score-margin: 12px;
-  --lh-table-header-bg: hsla(0, 0%, 50%, 0.4);
+  --lh-table-header-bg: #f8f9fa;
   --lh-table-higlight-bg: hsla(0, 0%, 75%, 0.1);
   --lh-sparkline-height: 5px;
   --lh-sparkline-thin-height: 3px;
   --lh-filmstrip-thumbnail-width: 60px;
-  --lh-audit-score-width: calc(5 * var(--body-font-size));
+  --lh-score-icon-width: calc(var(--body-font-size) / 14 * 16);
   --lh-category-score-width: calc(5 * var(--body-font-size));
   --lh-audit-vpadding: 8px;
+  --lh-audit-index-width: 18px;
   --lh-audit-hgap: 12px;
-  --lh-audit-group-vpadding: 12px;
+  --lh-audit-group-vpadding: 8px;
   --lh-section-vpadding: 12px;
-  --pass-icon-url: url('data:image/svg+xml;utf8,<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path stroke="%23007F04" stroke-width="1.5" d="M1 5.75l3.5 3.5 6.5-6.5" fill="none" fill-rule="evenodd"/></svg>');
-  --fail-icon-url: url('data:image/svg+xml;utf8,<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><g stroke="%23EE1D0A" stroke-width="1.5" fill="none" fill-rule="evenodd"><path d="M2 10l8-8M10 10L2 2"/></g></svg>');
-  --collapsed-icon-url: url('data:image/svg+xml;utf8,<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="none" d="M0 0h12v12H0z"/><path fill="hsl(0, 0%, 60%)" d="M3 2l6 4-6 4z"/></g></svg>');
-  --expanded-icon-url: url('data:image/svg+xml;utf8,<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="none" d="M0 0h12v12H0z"/><path fill="hsl(0, 0%, 60%)" d="M10 3L6 9 2 3z"/></g></svg>');
+  --chevron-size: 12px;
+
+  /* Voodoo magic here to get narrow columns. 0 doesn't size the column like our friend 1px does */
+  --bytes-col-width: 1px;
+
+  --pass-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><title>check</title><path fill="hsl(139, 70%, 30%)" d="M24 4C12.95 4 4 12.95 4 24c0 11.04 8.95 20 20 20 11.04 0 20-8.96 20-20 0-11.05-8.96-20-20-20zm-4 30L10 24l2.83-2.83L20 28.34l15.17-15.17L38 16 20 34z"/></svg>');
+  --average-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><title>info</title><path fill="hsl(31, 100%, 45%)" d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm2 30h-4V22h4v12zm0-16h-4v-4h4v4z"/></svg>');
+  --fail-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><title>warn</title><path fill="hsl(1, 73%, 45%)" d="M2 42h44L24 4 2 42zm24-6h-4v-4h4v4zm0-8h-4v-8h4v8z"/></svg>');
+
+  --av-timer-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><path d="M0 0h48v48H0z" fill="none"/><path d="M22 34c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2zm0-28v8h4v-3.84c6.78.97 12 6.79 12 13.84 0 7.73-6.27 14-14 14s-14-6.27-14-14c0-3.36 1.18-6.43 3.15-8.85L24 26l2.83-2.83-13.6-13.6-.02.04C8.84 12.89 6 18.11 6 24c0 9.94 8.04 18 17.99 18S42 33.94 42 24 33.94 6 23.99 6H22zm14 18c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2zm-24 0c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2z" fill="hsl(216, 5%, 39%)"/></svg>');
+  --photo-filter-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><path fill="none" d="M0 0h48v48H0V0z"/><path d="M38.04 20v18H10V10h18V6H10.04c-2.2 0-4 1.8-4 4v28c0 2.2 1.8 4 4 4h28c2.2 0 4-1.8 4-4V20h-4zM34 20l1.88-4.12L40 14l-4.12-1.88L34 8l-1.88 4.12L28 14l4.12 1.88zm-7.5 1.5L24 16l-2.5 5.5L16 24l5.5 2.5L24 32l2.5-5.5L32 24z" fill="hsl(216, 5%, 39%)"/></svg>');
+  --visibility-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><path d="M0 0h48v48H0z" fill="none"/><path d="M24 9C14 9 5.46 15.22 2 24c3.46 8.78 12 15 22 15 10.01 0 18.54-6.22 22-15-3.46-8.78-11.99-15-22-15zm0 25c-5.52 0-10-4.48-10-10s4.48-10 10-10 10 4.48 10 10-4.48 10-10 10zm0-16c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6z" fill="hsl(216, 5%, 39%)"/></svg>');
+  --check-circle-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><path d="M0 0h48v48H0z" fill="none"/><path d="M24 4C12.95 4 4 12.95 4 24c0 11.04 8.95 20 20 20 11.04 0 20-8.96 20-20 0-11.05-8.96-20-20-20zm-4 30L10 24l2.83-2.83L20 28.34l15.17-15.17L38 16 20 34z" fill="hsl(216, 5%, 39%)"/></svg>');
+  --check-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><path d="M0 0h48v48H0z" fill="none"/><path d="M18 32.34L9.66 24l-2.83 2.83L18 38l24-24-2.83-2.83z"/></svg>');
+  --search-icon-url: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><path d="M31 28h-1.59l-.55-.55C30.82 25.18 32 22.23 32 19c0-7.18-5.82-13-13-13S6 11.82 6 19s5.82 13 13 13c3.23 0 6.18-1.18 8.45-3.13l.55.55V31l10 9.98L40.98 38 31 28zm-12 0a9 9 0 1 1 .001-18.001A9 9 0 0 1 19 28z" fill="hsl(216, 5%, 39%)"/><path d="M0 0h48v48H0z" fill="none" /></svg>');
+  --remove-circle-icon-url: url('data:image/svg+xml;utf8,<svg height="24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M7 11v2h10v-2H7zm5-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" fill="hsl(216, 5%, 39%)"/></svg>');
 }
 
 .lh-vars.lh-devtools {
@@ -75,15 +97,15 @@
   --caption-font-size: 11px;
   --caption-line-height: 14px;
   --default-padding: 12px;
-  --section-padding: 16px;
-  --section-indent: 16px;
+  --section-padding: 12px;
+  --section-indent: 8px;
   --audit-group-indent: 16px;
   --audit-indent: 16px;
   --expandable-indent: 16px;
 
   --lh-audit-vpadding: 4px;
   --lh-audit-hgap: 12px;
-  --lh-audit-group-vpadding: 8px;
+  --lh-audit-group-vpadding: 12px;
   --lh-section-vpadding: 8px;
 }
 
@@ -101,37 +123,43 @@
   font-size: var(--body-font-size);
   margin: 0;
   line-height: var(--body-line-height);
-  background: #f5f5f5;
+  background: var(--body-background-color);
   scroll-behavior: smooth;
 }
 
 .lh-root :focus {
     outline: -webkit-focus-ring-color auto 3px;
 }
+.lh-root summary:focus {
+    outline: 1px solid hsl(217, 89%, 61%);
+}
+
 
 .lh-root [hidden] {
   display: none !important;
 }
 
-a {
+.lh-audit-group a,
+.lh-category-header__description a {
   color: #0c50c7;
 }
 
-summary {
-  cursor: pointer;
+
+.lh-audit__description,
+.lh-load-opportunity__description,
+.lh-details {
+  --inner-audit-left-padding: calc(var(--text-indent) + var(--lh-audit-index-width) + 2 * var(--audit-item-gap));
+  --inner-audit-right-padding: calc(var(--text-indent) + 2px);
+  margin-left: var(--inner-audit-left-padding);
+  margin-right: var(--inner-audit-right-padding);
 }
 
 .lh-details {
   font-size: var(--body-font-size);
   margin-top: var(--default-padding);
-}
-
-.lh-details[open] summary {
   margin-bottom: var(--default-padding);
-}
-
-.lh-details summary::-webkit-details-marker {
-  color: #9e9e9e;
+  /* whatever the .lh-details side margins are */
+  width: calc(100% - var(--inner-audit-left-padding) - var(--inner-audit-right-padding));
 }
 
 .lh-details.flex .lh-code {
@@ -167,20 +195,6 @@
   background-image: url('data:image/svg+xml;utf8,<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>');
 }
 
-/* List */
-.lh-list {
-  font-size: smaller;
-  margin-top: var(--default-padding);
-}
-
-.lh-list__items {
-  padding-left: var(--default-padding);
-}
-
-.lh-list__item {
-  margin-bottom: 2px;
-}
-
 /* Node */
 .lh-node {
   display: block;
@@ -188,195 +202,101 @@
   word-break: break-word;
   color: hsl(174, 100%, 27%);
 }
-span.lh-node:hover {
+.lh-node:hover {
     background: hsl(0, 0%, 98%);
     border-radius: 2px;
 }
 
-/* Card */
-.lh-scorecards {
-  display: flex;
-  flex-wrap: wrap;
-}
-.lh-scorecard {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  flex: 0 0 calc(12 * var(--body-font-size));
-  flex-direction: column;
-  padding: var(--default-padding);
-  padding-top: calc(32px + var(--default-padding));
-  border-radius: 3px;
-  margin-right: var(--default-padding);
-  position: relative;
-  line-height: inherit;
-  border: 1px solid #ebebeb;
-}
-.lh-scorecard__title {
-  background-color: #eee;
-  position: absolute;
-  top: 0;
-  right: 0;
-  left: 0;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  padding: calc(var(--default-padding) / 2);
-}
-.lh-scorecard__value {
-  font-size: calc(1.6 * var(--body-font-size));
-}
-.lh-scorecard__target {
-  margin-top: calc(var(--default-padding) / 2);
-}
-
 /* Score */
 
-.lh-score {
-  display: flex;
-  align-items: flex-start;
-}
-
-.lh-score__value {
-  flex: none;
+.lh-audit__score-icon {
   margin-left: var(--lh-score-margin);
-  width: calc(var(--lh-audit-score-width) - var(--lh-score-margin));
-  position: relative;
-  font-weight: bold;
-  top: 1px;
-  text-align: right;
+  width: var(--lh-score-icon-width);
+  height: var(--lh-score-icon-width);
+  background: none no-repeat center center / contain;
 }
 
-.lh-score__value::after {
-  content: '';
-  position: absolute;
-  right: 0;
-  top: 0;
-  bottom: 0;
-  border-radius: inherit;
-  width: 16px;
-}
-
-.lh-score--informative .lh-score__value {
-  color: var(--informative-color);
-  border-radius: 50%;
-  top: 3px;
-}
-
-.lh-score--informative .lh-score__value::after {
-  display: none;
-  background: url('data:image/svg+xml;utf8,<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>info</title><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z" fill="hsl(218, 89%, 41%)"/></svg>') no-repeat 50% 50%;
-  background-size: var(--lh-score-icon-background-size);
-}
-
-.lh-score--manual .lh-score__value::after {
-  background: url('data:image/svg+xml;utf8,<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><title>manual</title><path d="M2 5h8v2H2z" fill="hsl(0, 0%, 100%)" fill-rule="evenodd"/></svg>') no-repeat 50% 50%;
-  background-size: 18px;
-  background-color: var(--manual-color);
-  width: 20px;
-  height:  20px;
-  position: relative;
-}
-
-.lh-score__value--binary {
-  color: transparent !important;
-}
-
-/* No icon for audits with number scores. */
-.lh-score__value:not(.lh-score__value--binary)::after {
-  content: none;
-}
-
-.lh-score__value--pass {
+.lh-audit--pass .lh-audit__display-text {
   color: var(--pass-color);
 }
-
-.lh-score__value--pass::after {
-  background: var(--pass-icon-url) no-repeat center center / 12px 12px;
+.lh-audit--pass .lh-audit__score-icon {
+  background-image: var(--pass-icon-url);
 }
 
-.lh-score__value--average {
+.lh-audit--average .lh-audit__display-text {
   color: var(--average-color);
 }
-
-.lh-score__value--average::after {
-  background: none;
-  content: '!';
-  color: var(--average-color);
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  font-weight: 500;
-  font-size: 15px;
+.lh-audit--average .lh-audit__score-icon {
+  background-image: var(--average-icon-url);
 }
 
-.lh-score__value--fail {
+.lh-audit--fail .lh-audit__display-text {
   color: var(--fail-color);
 }
-
-.lh-score__value--fail::after {
-  background: var(--fail-icon-url) no-repeat center center / 12px 12px;
+.lh-audit--fail .lh-audit__score-icon {
+  background-image: var(--fail-icon-url);
 }
 
-.lh-score__description {
-  font-size: var(--body-font-size);
-  color: var(--secondary-text-color);
-  line-height: var(--body-line-height);
+.lh-audit--informative .lh-audit__display-text {
+  color: var(--display-value-gray);
 }
 
-.lh-score__snippet {
-  align-items: center;
-  justify-content: space-between;
-  /*outline: none;*/
+.lh-audit--informative .lh-audit__score-icon,
+.lh-audit--manual .lh-audit__score-icon {
+  visibility: hidden;
 }
-
-.lh-score__snippet::-moz-list-bullet {
+.lh-audit--error .lh-audit__score-icon {
   display: none;
 }
 
-.lh-score__title {
+.lh-category-header__description,
+.lh-audit__description   {
+  color: var(--secondary-text-color);
+}
+
+.lh-category-header__description  {
+  font-size: var(--body-font-size);
+  margin: calc(var(--default-padding) / 2) 0 var(--default-padding);
+}
+
+
+.lh-audit__index,
+.lh-audit__title,
+.lh-audit__display-text,
+.lh-audit__score-icon,
+.lh-load-opportunity__sparkline,
+.lh-chevron-container {
+  margin: 0 var(--audit-item-gap);
+}
+.lh-audit__index {
+  margin-left: 0;
+}
+.lh-chevron-container {
+  margin-right: 0;
+}
+
+
+.lh-audit__header .lh-audit__index {
+  width: var(--lh-audit-index-width);
+}
+
+.lh-audit__title {
   flex: 1;
 }
 
-.lh-toggle-arrow {
-  background: var(--collapsed-icon-url) no-repeat center center / 12px 12px;
-  background-color: transparent;
-  width: 12px;
-  height: 12px;
-  flex: none;
-  transition: transform 150ms ease-in-out;
-  cursor: pointer;
-  border: none;
-  order: -1;
-  margin-right: calc(var(--expandable-indent) - 12px);
-  align-self: flex-start;
-}
-
-.lh-toggle-arrow-unexpandable {
-  visibility: hidden;
-}
-
 /* Expandable Details (Audit Groups, Audits) */
-
-.lh-score__header {
-  order: -1;
-  flex: 1;
-}
-
-.lh-expandable-details {
-  padding-left: var(--expandable-indent);
-}
-
-.lh-expandable-details__summary {
+.lh-audit__header {
   display: flex;
-  align-items: center;
+  padding: var(--lh-audit-vpadding) var(--text-indent);
   cursor: pointer;
-  margin-left: calc(0px - var(--expandable-indent));
 }
 
-.lh-audit-group[open] > .lh-audit-group__summary > .lh-toggle-arrow,
-.lh-expandable-details[open] > .lh-expandable-details__summary > .lh-toggle-arrow {
-  background-image: var(--expanded-icon-url);
+.lh-audit--load-opportunity .lh-audit__header {
+  display: block;
+}
+
+.lh-audit__header:hover {
+  background-color: #F8F9FA;
 }
 
 .lh-audit-group__summary::-webkit-details-marker,
@@ -384,200 +304,176 @@
   display: none;
 }
 
-.lh-score__snippet .lh-toggle-arrow {
-  margin-top: calc((var(--body-line-height) - 12px) / 2);
-}
 
-/* Perf Timeline */
+/* Perf Metric */
 
-.lh-timeline-container {
-  overflow: hidden;
-  border-top: 1px solid var(--metric-timeline-rule-color);
-}
-
-.lh-timeline {
-  padding: 0;
-  padding-bottom: 0;
-  width: calc(var(--lh-filmstrip-thumbnail-width) * 10 + var(--default-padding) * 2);
-}
-
-.lh-narrow .lh-timeline-container {
-  width: calc(100vw - var(--section-padding) * 2);
-  overflow-x: scroll;
-}
-
-.lh-devtools .lh-timeline-container {
-  width: 100%;
-  overflow-x: scroll;
-}
-
-/* Perf Timeline Metric */
-
-.lh-timeline-metric {
-  position: relative;
-  margin-bottom: calc(2 * var(--lh-audit-vpadding));
-  padding-top: var(--lh-audit-vpadding);
-  border-top: 1px solid var(--report-secondary-border-color);
-}
-
-.lh-timeline-metric__header {
+.lh-metric-container {
   display: flex;
 }
 
-.lh-timeline-metric__details {
+.lh-metric-column {
+  flex: 1;
+}
+.lh-metric-column:first-of-type {
+  margin-right: 20px;
+}
+
+.lh-metric {
+  border-bottom: 1px solid var(--report-secondary-border-color);
+}
+
+.lh-metric__innerwrap {
+  display: flex;
+  justify-content: space-between;
+  padding: 11px var(--text-indent);
+}
+
+.lh-metric__details {
   order: -1;
 }
 
-.lh-timeline-metric__title {
+.lh-metric__title {
   font-size: var(--body-font-size);
   line-height: var(--body-line-height);
   display: flex;
 }
 
-.lh-timeline-metric__name {
+.lh-metric__name {
   flex: 1;
 }
 
-.lh-timeline-metric__description {
+.lh-metrics__disclaimer {
+  color: var(--medium-75-gray);
+  text-align: right;
+  margin: var(--lh-section-vpadding) 0;
+  padding: 0 var(--text-indent);
+}
+
+.lh-metric__description {
   color: var(--secondary-text-color);
 }
 
-.lh-timeline-metric__value {
-  width: var(--lh-audit-score-width);
-  text-align: right;
+.lh-metric__value {
+  white-space: nowrap; /* No wrapping between metric value and the icon */
 }
 
-.lh-timeline-metric--pass .lh-timeline-metric__value {
+
+.lh-metric .lh-metric__value::after {
+  content: '';
+  width: var(--lh-score-icon-width);
+  height: var(--lh-score-icon-width);
+  background-size: contain;
+  display: inline-block;
+  vertical-align: text-bottom;
+  margin-left: calc(var(--body-font-size) / 2);
+}
+
+.lh-metric--pass .lh-metric__value {
   color: var(--pass-color);
 }
-
-.lh-timeline-metric--average .lh-timeline-metric__value {
-  color: var(--average-color);
+.lh-metric--pass .lh-metric__value::after {
+  background: var(--pass-icon-url) no-repeat 50% 50%;
 }
 
-.lh-timeline-metric--fail .lh-timeline-metric__value {
+
+.lh-metric--average .lh-metric__value {
+  color: var(--average-color);
+}
+.lh-metric--average .lh-metric__value::after {
+  background: var(--average-icon-url) no-repeat 50% 50%;
+}
+
+
+.lh-metric--fail .lh-metric__value {
+  color: var(--fail-color);
+}
+.lh-metric--fail .lh-metric__value::after {
+  background: var(--fail-icon-url) no-repeat 50% 50%;
+}
+
+.lh-metric--error .lh-metric__value,
+.lh-metric--error .lh-metric__description {
   color: var(--fail-color);
 }
 
-.lh-timeline-metric__sparkline {
-  position: absolute;
-  left: 0;
-  right: 0;
-  top: -1px;
-  height: 3px;
-  width: 100%;
+/* Hide icon if there was an error */
+.lh-metric--error .lh-metric__value::after {
+  display: none;
 }
 
-.lh-timeline-metric__sparkline .lh-sparkline__bar {
-  float: none;
-}
+/* Perf load opportunity */
 
-.lh-timeline-metric--pass .lh-sparkline__bar {
-  background: var(--pass-color);
-}
-
-.lh-timeline-metric--average .lh-sparkline__bar {
-  background: var(--average-color);
-}
-
-.lh-timeline-metric--fail .lh-sparkline__bar {
-  background: var(--fail-color);
-}
-
-.lh-timeline-metric .lh-debug {
-  margin-left: var(--expandable-indent);
-}
-
-/* Perf Hint */
-
-.lh-perf-hint {
-  padding-top: var(--lh-audit-vpadding);
-  padding-bottom: var(--lh-audit-vpadding);
-  border-top: 1px solid var(--report-secondary-border-color);
-}
-
-.lh-perf-hint:last-of-type {
-  border-bottom: none;
-}
-
-.lh-perf-hint__summary {
+.lh-load-opportunity__cols {
   display: flex;
   align-items: flex-start;
-  flex-wrap: wrap;
-  min-height: calc(var(--body-line-height) + var(--caption-line-height));
 }
 
-.lh-perf-hint__summary .lh-toggle-arrow {
-  margin-top: calc((var(--subheader-line-height) - 12px) / 2);
+.lh-load-opportunity__header .lh-load-opportunity__col {
+  background-color: var(--medium-50-gray);
+  color: var(--medium-75-gray);
+  text-align: center;
+  display: unset;
+  line-height: calc(2.3 * var(--body-font-size));
 }
 
-.lh-perf-hint__summary .lh-debug {
-  width: calc(100% - var(--expandable-indent));
-  margin: 0 var(--expandable-indent);
+.lh-load-opportunity__col {
+  display: flex;
+  justify-content: space-between;
 }
 
-.lh-perf-hint__title {
-  font-size: var(--body-font-size);
-  flex: 10;
+.lh-load-opportunity__col--one {
+  flex: 5;
+  margin-right: 2px;
+}
+.lh-load-opportunity__col--two {
+  flex: 4;
 }
 
-.lh-perf-hint__sparkline {
-  flex: 0 0 50%;
+.lh-audit--load-opportunity .lh-audit__display-text {
+  text-align: right;
+  flex: 0 0 calc(3 * var(--body-font-size));
+}
+
+
+/* Sparkline */
+
+.lh-load-opportunity__sparkline {
+  flex: 1;
   margin-top: calc((var(--body-line-height) - var(--lh-sparkline-height)) / 2);
 }
 
-.lh-perf-hint__sparkline .lh-sparkline {
+.lh-sparkline {
+  height: var(--lh-sparkline-height);
   width: 100%;
+}
+
+.lh-sparkline__bar {
+  height: 100%;
   float: right;
-  margin: 0;
 }
 
-.lh-perf-hint__stats {
-  text-align: right;
-  flex: 0 0 var(--lh-audit-score-width);
-}
-
-.lh-perf-hint__primary-stat {
-  font-size: var(--body-font-size);
-  line-height: var(--body-line-height);
-}
-
-.lh-perf-hint__secondary-stat {
-  font-size: var(--caption-font-size);
-  line-height: var(--caption-line-height);
-}
-
-.lh-perf-hint__description {
-  color: var(--secondary-text-color);
-  margin-top: calc(var(--default-padding) / 2);
-}
-
-.lh-perf-hint--pass .lh-perf-hint__stats {
-  color: var(--pass-color);
-}
-
-.lh-perf-hint--pass .lh-sparkline__bar {
+.lh-audit--pass .lh-sparkline__bar {
   background: var(--pass-color);
 }
 
-.lh-perf-hint--average .lh-sparkline__bar {
+.lh-audit--average .lh-sparkline__bar {
   background: var(--average-color);
 }
 
-.lh-perf-hint--average .lh-perf-hint__stats {
-  color: var(--average-color);
-}
-
-.lh-perf-hint--fail .lh-sparkline__bar {
+.lh-audit--fail .lh-sparkline__bar {
   background: var(--fail-color);
 }
 
-.lh-perf-hint--fail .lh-perf-hint__stats {
-  color: var(--fail-color);
-}
+
 
 /* Filmstrip */
 
+.lh-filmstrip-container {
+  padding: 0 var(--expandable-indent);
+  margin: 0 auto;
+}
+
+
 .lh-filmstrip {
   display: flex;
   flex-direction: row;
@@ -590,137 +486,117 @@
   position: relative;
 }
 
-.lh-filmstrip__timestamp {
-  margin-bottom: calc(0.5 * var(--caption-line-height));
-  font-size: var(--caption-font-size);
-  line-height: var(--caption-line-height);
-  padding-top: 1px;
-  padding-right: 6px;
-}
-
-.lh-filmstrip__timestamp::before {
-  content: '';
-  height: 7px;
-  width: 2px;
-  background: var(--metric-timeline-rule-color);
-  position: absolute;
-  right: 0;
-  top: -2px;
-}
-
 .lh-filmstrip__thumbnail {
   border: 1px solid var(--report-secondary-border-color);
   max-height: 100px;
   max-width: 60px;
 }
 
-/* Sparkline */
-
-.lh-sparkline {
-  margin: 5px;
-  height: var(--lh-sparkline-height);
-  width: 100%;
-}
-
-.lh-sparkline--thin {
-  height: calc(var(--lh-sparkline-height) / 2);
-}
-
-.lh-sparkline__bar {
-  background: var(--warning-color);
-  height: 100%;
-  float: right;
-  position: relative;
-}
-
-/* correlate metric end location with sparkline */
-.lh-timeline-metric:hover .lh-sparkline__bar::after {
-  content: '';
-  height: 100vh;
-  width: 2px;
-  background: inherit;
-  position: absolute;
-  right: 0;
-  bottom: 0;
-  opacity: 0;
-  animation: fadeIn 150ms;
-  animation-fill-mode: forwards;
-}
-
 /* Audit */
 
 .lh-audit {
-  margin-bottom: var(--lh-audit-vpadding);
-  padding-top: var(--lh-audit-vpadding);
-  border-top: 1px solid var(--report-secondary-border-color);
+  border-bottom: 1px solid var(--report-secondary-border-color);
 }
 
-.lh-audit:last-of-type {
+.lh-audit:last-child  {
   border-bottom: none;
 }
 
-.lh-audit > .lh-score {
-  font-size: var(--body-font-size);
-}
-
-.lh-audit .lh-debug {
-  margin-left: var(--expandable-indent);
-  margin-right: var(--lh-audit-score-width);
+.lh-audit--error .lh-audit__display-text {
+  color: var(--fail-color);
 }
 
 /* Audit Group */
 
 .lh-audit-group {
-  padding-top: var(--lh-audit-group-vpadding);
-  border-top: 1px solid var(--report-secondary-border-color);
-  padding-left: var(--expandable-indent);
+  padding: var(--lh-audit-group-vpadding) 0;
+  border-bottom: 1px solid var(--report-secondary-border-color);
+}
+
+.lh-audit-group:last-child {
+  border-bottom: none;
 }
 
 .lh-audit-group__header {
   font-size: var(--subheader-font-size);
   line-height: var(--subheader-line-height);
+  color: var(--subheader-color);
+  flex: 1;
+  font-weight: bold;
+}
+
+.lh-audit-group__header::before {
+  content: '';
+  width: calc(var(--subheader-font-size) / 14 * 24);
+  height: calc(var(--subheader-font-size) / 14 * 24);
+  margin-right: calc(var(--subheader-font-size) / 2);
+  background: var(--medium-100-gray) none no-repeat center / 16px;
+  display: inline-block;
+  border-radius: 50%;
+  vertical-align: middle;
+}
+
+/* A11y/Seo groups within Passed don't get an icon */
+.lh-audit-group--unadorned .lh-audit-group__header::before {
+  content: none;
+}
+
+
+.lh-audit-group--manual .lh-audit-group__header::before {
+  background-image: var(--search-icon-url);
+}
+.lh-passed-audits .lh-audit-group__header::before {
+  background-image: var(--check-icon-url);
+}
+.lh-audit-group--diagnostics .lh-audit-group__header::before {
+  background-image: var(--search-icon-url);
+}
+.lh-audit-group--opportunities .lh-audit-group__header::before {
+  background-image: var(--photo-filter-icon-url);
+}
+.lh-audit-group--metrics .lh-audit-group__header::before {
+  background-image: var(--av-timer-icon-url);
+}
+.lh-audit-group--not-applicable .lh-audit-group__header::before {
+  background-image: var(--remove-circle-icon-url);
+}
+
+/* Removing too much whitespace */
+.lh-audit-group--metrics {
+  margin-top: -28px;
+  border-bottom: none;
 }
 
 .lh-audit-group__summary {
   display: flex;
-  align-items: center;
-  margin-bottom: var(--lh-audit-group-vpadding);
-  margin-left: calc(0px - var(--expandable-indent));
+  justify-content: space-between;
+  padding-right: var(--text-indent);
 }
 
-.lh-audit-group__summary .lh-toggle-arrow {
-  margin-top: calc((var(--subheader-line-height) - 12px) / 2);
+.lh-audit-group__itemcount {
+  color: var(--display-value-gray);
+  margin: 0 10px;
 }
 
 .lh-audit-group__description {
   font-size: var(--body-font-size);
-  color: var(--secondary-text-color);
-  margin-top: calc(0px - var(--lh-audit-group-vpadding));
-  margin-bottom: var(--lh-audit-group-vpadding);
-  line-height: var(--body-line-height);
+  color: var(--medium-75-gray);
+  margin: var(--lh-audit-group-vpadding) 0;
 }
 
+.lh-audit-group--unadorned .lh-audit-group__description {
+  margin-top: 0;
+}
 
-.lh-debug {
-  font-size: var(--caption-font-size);
+.lh-audit-explanation {
+  margin: var(--lh-audit-vpadding) 0 calc(var(--lh-audit-vpadding) / 2);
   line-height: var(--caption-line-height);
+}
+
+.lh-audit--fail .lh-audit-explanation {
   color: var(--fail-color);
-  margin-top: 3px;
 }
 
-.lh-debug::before {
-  display: none;
-  content: '';
-  background: url('data:image/svg+xml;utf8,<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>warn</title><path d="M0 0h24v24H0z" fill="none"/><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" fill="hsl(40, 100%, 50%)"/></svg>') no-repeat 50% 50%;
-  background-size: contain;
-  width: 20px;
-  height: 20px;
-  position: relative;
-  margin-right: calc(var(--default-padding) / 2);
-  top: 5px;
-}
-
-
 /* Report */
 
 .lh-container {
@@ -730,10 +606,27 @@
   margin: 0 auto;
 }
 
+.lh-header-sticky {
+  position: sticky;
+  top: 0;
+  width: 100%;
+  z-index: 2;
+  will-change: transform;
+}
+.lh-header-plain {
+  margin-top: var(--section-padding);
+}
+
+.lh-header-container {
+  display: block;
+  margin: 0 auto;
+  max-width: var(--report-content-width);
+  position: relative;
+  word-wrap: break-word;
+}
+
 .lh-report {
-  margin-left: var(--report-menu-width);
   background-color: #fff;
-  padding-top: var(--report-header-height);
 }
 @media screen {
   .lh-report {
@@ -745,51 +638,106 @@
   font-size: large;
 }
 
-.lh-text {
-  white-space: nowrap;
-}
-
 .lh-code {
   white-space: normal;
   margin-top: 0;
+  font-size: 85%;
+  word-break: break-word;
 }
 
-.lh-run-warnings {
-  font-size: var(--body-font-size);
+.lh-warnings {
+  --item-margin: calc(var(--body-line-height) / 6);
+  border: 1px solid var(--average-color);
+  border-radius: 4px;
+  margin: var(--lh-audit-vpadding) 0;
+  padding: calc(var(--lh-audit-vpadding) / 2) var(--lh-audit-vpadding);
+}
+
+.lh-warnings--toplevel {
+  --item-margin: calc(var(--header-line-height) / 4);
+  color: var(--secondary-text-color);
   margin: var(--section-padding);
   padding: var(--section-padding);
-  background-color: hsla(40, 100%, 91%, 1);
-  color: var(--secondary-text-color);
 }
-.lh-run-warnings li {
-  margin-bottom: calc(var(--header-line-height) / 2);
+.lh-warnings ul {
+  padding-left: calc(var(--section-padding) * 2);
+  margin: 0;
 }
-.lh-run-warnings::before {
-  display: inline-block;
+.lh-warnings li {
+  margin: var(--item-margin) 0;
+}
+.lh-warnings li:last-of-type {
+  margin-bottom: 0;
 }
 
 .lh-scores-header {
   display: flex;
   justify-content: center;
   overflow-x: hidden;
-  padding: var(--section-padding);
-  border-bottom: 1px solid var(--report-border-color);
+  position: relative;
+  padding: var(--section-indent) calc(var(--section-indent) / 2) calc(var(--section-indent) * 2);
 }
 .lh-scores-header__solo {
   padding: 0;
   border: 0;
 }
 
+.lh-scorescale {
+  color: var(--medium-75-gray);
+  padding: 0 calc(var(--section-indent) * 1.5) 0;
+  text-align: right;
+  transform-origin: bottom right;
+  will-change: opacity; /* opacity is changed on scroll */
+}
+
+.lh-scorescale-range {
+  margin-left: 10px;
+}
+
+.lh-scorescale-range::before {
+  content: '';
+  width: var(--body-font-size);
+  height: calc(var(--body-font-size) * .60);
+  border-radius: 4px;
+  display: inline-block;
+  margin: 0 5px;
+}
+
+.lh-scorescale-range--pass::before {
+  background-color: var(--pass-color);
+}
+
+.lh-scorescale-range--average::before {
+  background-color: var(--average-color);
+}
+
+.lh-scorescale-range--fail::before {
+  background-color: var(--fail-color);
+}
+
+/* Hide category score gauages if it's a single category report */
+.lh-header--solo-category .lh-scores-wrapper {
+  display: none;
+}
+
+
 .lh-categories {
   width: 100%;
   overflow: hidden;
 }
 
 .lh-category {
+  --circle-size: calc(2.5 * var(--header-font-size));
+
   padding: var(--section-padding);
   border-top: 1px solid var(--report-border-color);
 }
 
+.lh-category:first-of-type {
+  padding-top: calc(2 * var(--section-padding));
+  border: none;
+}
+
 /* section hash link jump should preserve fixed header
    https://css-tricks.com/hash-tag-links-padding/
 */
@@ -800,36 +748,30 @@
   visibility: hidden;
 }
 
-.lh-category:first-of-type {
-  border: none;
-}
-
-.lh-category > .lh-score {
+.lh-category-header {
   font-size: var(--header-font-size);
-  padding-bottom: var(--lh-section-vpadding);
+  min-height: var(--circle-size);
+  margin-bottom: var(--lh-section-vpadding);
 }
 
-.lh-category > .lh-score .lh-score__value,
-.lh-category > .lh-score .lh-score__gauge .lh-gauge__label {
+.lh-category-header__title {
+  line-height: 24px;
+}
+
+.lh-category-header .lh-score__gauge .lh-gauge__label {
   display: none;
 }
 
+
+.lh-category-header .lh-score__gauge {
+  float: right;
+}
+
 .lh-category .lh-score__gauge {
   margin-left: var(--section-indent);
-  flex-basis: var(--circle-size);
-  flex-shrink: 0;
 }
 
-.lh-category .lh-score__gauge .lh-gauge {
-  --circle-size: calc(2.5 * var(--header-font-size));
-}
-
-/* Category snippet shouldnt have pointer cursor. */
-.lh-category > .lh-score .lh-score__snippet {
-  cursor: initial;
-}
-
-.lh-category > .lh-score .lh-score__title {
+.lh-category-header .lh-audit__title {
   font-size: var(--header-font-size);
   line-height: var(--header-line-height);
 }
@@ -900,16 +842,16 @@
 
 .lh-table {
   --image-preview-size: 24px;
-  border: 1px solid var(--report-secondary-border-color);
   border-collapse: collapse;
-  width: 100%;
-
-  --url-col-max-width: 450px;
 }
 
 .lh-table thead {
   background: var(--lh-table-header-bg);
 }
+.lh-table thead th {
+  color: var(--medium-75-gray);
+  font-weight: normal;
+}
 
 .lh-table tbody tr:nth-child(even) {
   background-color: var(--lh-table-higlight-bg);
@@ -920,7 +862,9 @@
   padding: 8px 6px;
 }
 
-.lh-table-column--text {
+.lh-table-column--text,
+.lh-table-column--bytes,
+.lh-table-column--ms {
   text-align: right;
 }
 
@@ -932,7 +876,13 @@
   text-align: left;
   min-width: 250px;
   white-space: nowrap;
-  max-width: var(--url-col-max-width);
+  max-width: 0;
+}
+
+/* Keep bytes columns narrow if they follow the URL column */
+.lh-table-column--url + th.lh-table-column--bytes,
+.lh-table-column--url + .lh-table-column--bytes + th.lh-table-column--bytes {
+  width: var(--bytes-col-width);
 }
 
 .lh-table-column--code {
@@ -950,7 +900,7 @@
 }
 
 .lh-text__url > .lh-text, .lh-text__url-host {
-  display: inline;
+  display: inline-block;
 }
 
 .lh-text__url-host {
@@ -965,4 +915,103 @@
   object-fit: contain;
 }
 
-/*# sourceURL=report.styles.css */
+/* Chevron
+   https://codepen.io/paulirish/pen/LmzEmK
+ */
+.lh-chevron {
+  --chevron-angle: 42deg;
+  width: var(--chevron-size);
+  height: var(--chevron-size);
+  margin-top: calc((var(--body-line-height) - 12px) / 2);
+}
+
+.lh-chevron__lines {
+  transition: transform 0.4s;
+  transform: translateY(var(--body-line-height));
+}
+.lh-chevron__line {
+ stroke: var(--display-value-gray);
+ stroke-width: var(--chevron-size);
+ stroke-linecap: square;
+ transform-origin: 50%;
+ transform: rotate(var(--chevron-angle));
+ transition: transform 300ms, stroke 300ms;
+}
+
+.lh-audit-group > .lh-audit-group__summary > .lh-chevron .lh-chevron__line-right,
+.lh-audit-group[open] > .lh-audit-group__summary > .lh-chevron .lh-chevron__line-left,
+.lh-audit > .lh-expandable-details .lh-chevron__line-right,
+.lh-audit > .lh-expandable-details[open] .lh-chevron__line-left {
+ transform: rotate(calc(var(--chevron-angle) * -1));
+}
+
+.lh-audit-group[open] > .lh-audit-group__summary > .lh-chevron .lh-chevron__line-right,
+.lh-audit > .lh-expandable-details[open] .lh-chevron__line-right {
+  transform: rotate(var(--chevron-angle));
+}
+
+.lh-audit-group[open] > .lh-audit-group__summary > .lh-chevron .lh-chevron__lines,
+.lh-audit > .lh-expandable-details[open] .lh-chevron__lines {
+ transform: translateY(calc(var(--chevron-size) * -1));
+}
+
+
+
+/* Tooltip */
+.tooltip-boundary {
+  position: relative;
+}
+
+.tooltip {
+  position: absolute;
+  display: none; /* Don't retain these layers when not needed */
+  opacity: 0;
+  background: #ffffff;
+  min-width: 246px;
+  max-width: 275px;
+  padding: 15px;
+  border-radius: 5px;
+  text-align: initial;
+}
+/* shrink tooltips to not be cutoff on left edge of narrow viewports
+   45vw is chosen to be ~= width of the left column of metrics
+*/
+@media screen and (max-width: 535px) {
+  .tooltip {
+    min-width: 45vw;
+    padding: 3vw;
+  }
+}
+
+.tooltip-boundary:hover {
+  background-color: #F8F9FA;
+}
+
+.tooltip-boundary:hover .tooltip {
+  display: block;
+  animation: fadeInTooltip 250ms;
+  animation-fill-mode: forwards;
+  animation-delay: 850ms;
+  bottom: 100%;
+  z-index: 1;
+  will-change: opacity;
+  right: 0;
+}
+
+.tooltip::before {
+  content: "";
+  border: solid transparent;
+  border-bottom-color: #fff;
+  border-width: 10px;
+  position: absolute;
+  bottom: -20px;
+  right: 6px;
+  transform: rotate(180deg);
+  pointer-events: none;
+}
+
+@keyframes fadeInTooltip {
+  0% { opacity: 0; }
+  75% { opacity: 1; }
+  100% { opacity: 1;  filter: drop-shadow(1px 0px 1px #aaa) drop-shadow(0px 2px 4px hsla(206, 6%, 25%, 0.15)); }
+}
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/templates.html b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/templates.html
index 3a2bc90..770432f 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/templates.html
+++ b/third_party/blink/renderer/devtools/front_end/audits2/lighthouse/templates.html
@@ -1,192 +1,244 @@
 <!-- Lighthouse run warnings -->
-<template id="tmpl-lh-run-warnings">
-  <div class="lh-run-warnings lh-debug">
+<template id="tmpl-lh-warnings--toplevel">
+  <div class="lh-warnings lh-warnings--toplevel">
     <strong>There were issues affecting this run of Lighthouse:</strong>
     <ul></ul>
   </div>
 </template>
 
-<!-- Lighthouse category score -->
-<template id="tmpl-lh-category-score">
-  <div class="lh-score">
-    <div class="lh-score__value"><!-- fill me --></div>
-    <div class="lh-score__gauge"></div>
-    <div class="lh-score__header">
-      <div class="lh-score__snippet">
-        <span class="lh-score__title"><!-- fill me --></span>
-      </div>
-      <div class="lh-score__description"><!-- fill me --></div>
-    </div>
+<!-- Lighthouse score scale -->
+<template id="tmpl-lh-scorescale">
+  <div class="lh-scorescale">
+    <span class="lh-scorescale-label">Score scale:</span>
+    <span class="lh-scorescale-range lh-scorescale-range--fail">0-44</span>
+    <span class="lh-scorescale-range lh-scorescale-range--average">45-74</span>
+    <span class="lh-scorescale-range lh-scorescale-range--pass">75-100</span>
   </div>
 </template>
 
-<!-- Lighthouse audit score -->
-<template id="tmpl-lh-audit-score">
-  <div class="lh-score">
-    <div class="lh-score__value"><!-- fill me --></div>
-    <details class="lh-score__header lh-expandable-details">
-      <summary class="lh-score__snippet lh-expandable-details__summary">
-        <span class="lh-score__title"><!-- fill me --></span>
-        <div class="lh-toggle-arrow" title="See audits"></div>
+<!-- Toggle arrow chevron -->
+<template id="tmpl-lh-chevron">
+  <svg class="lh-chevron" title="See audits" xmlns="http://www.w3.org/2000/svg"  viewbox="0 0 100 100">
+    <g class="lh-chevron__lines">
+      <path class="lh-chevron__line lh-chevron__line-left" d="M10 50h40" stroke="#707173"/>
+      <path class="lh-chevron__line lh-chevron__line-right" d="M90 50H50" stroke="#707173"/>
+    </g>
+  </svg>
+</template>
+
+<!-- Lighthouse category header -->
+<template id="tmpl-lh-category-header">
+  <div class="lh-category-header">
+    <div class="lh-score__gauge"></div>
+    <span class="lh-category-header__title"></span>
+    <div class="lh-category-header__description"></div>
+  </div>
+</template>
+
+<!-- Lighthouse audit -->
+<template id="tmpl-lh-audit">
+  <div class="lh-audit">
+    <details class="lh-expandable-details">
+      <summary class="lh-audit__header lh-expandable-details__summary">
+        <span class="lh-audit__index"></span>
+        <span class="lh-audit__title"></span>
+        <span class="lh-audit__display-text"></span>
+        <div class="lh-audit__score-icon"></div>
+        <div class="lh-chevron-container"></div>
       </summary>
-      <div class="lh-score__description"><!-- fill me --></div>
+      <div class="lh-audit__description"></div>
     </details>
   </div>
 </template>
 
-<!-- Lighthouse timeline metric -->
-<template id="tmpl-lh-timeline-metric">
-  <div class="lh-timeline-metric">
-    <div class="lh-timeline-metric__sparkline">
-      <div class="lh-sparkline__bar"></div>
-    </div>
-    <div class="lh-timeline-metric__header">
-      <div class="lh-timeline-metric__value"><!-- fill me --></div>
-      <details class="lh-timeline-metric__details lh-expandable-details">
-        <summary class="lh-timeline-metric__summary lh-expandable-details__summary">
-          <span class="lh-timeline-metric__title"><!-- fill me --></span>
-          <div class="lh-toggle-arrow" title="See audits"></div>
-        </summary>
-        <div class="lh-timeline-metric__description"><!-- fill me --></div>
-      </details>
+<!-- Lighthouse perf metric -->
+<template id="tmpl-lh-metric">
+  <div class="lh-metric">
+    <div class="lh-metric__innerwrap tooltip-boundary">
+      <span class="lh-metric__title"></span>
+      <div class="lh-metric__value"></div>
+      <div class="lh-metric__description tooltip"></div>
     </div>
   </div>
 </template>
 
-<!-- Lighthouse left nav -->
-<template id="tmpl-lh-leftnav">
+<!-- Lighthouse perf opportunity -->
+<template id="tmpl-lh-opportunity">
+  <div class="lh-audit lh-audit--load-opportunity">
+    <details class="lh-expandable-details">
+      <summary class="lh-audit__header lh-expandable-details__summary">
+        <div class="lh-load-opportunity__cols">
+          <div class="lh-load-opportunity__col lh-load-opportunity__col--one">
+            <span class="lh-audit__index"></span>
+            <div class="lh-audit__title"></div>
+          </div>
+          <div class="lh-load-opportunity__col lh-load-opportunity__col--two">
+            <div class="lh-load-opportunity__sparkline">
+              <div class="lh-sparkline"><div class="lh-sparkline__bar"></div></div>
+            </div>
+            <div class="lh-audit__display-text"></div>
+            <div class="lh-chevron-container" title="See resources"></div>
+          </div>
+        </div>
+      </summary>
+      <div class="lh-audit__description"></div>
+    </details>
+  </div>
+</template>
+
+
+<!-- Lighthouse perf opportunity header -->
+<template id="tmpl-lh-opportunity-header">
+  <div class="lh-load-opportunity__header lh-load-opportunity__cols">
+    <div class="lh-load-opportunity__col lh-load-opportunity__col--one">
+      Resource to optimize
+    </div>
+    <div class="lh-load-opportunity__col lh-load-opportunity__col--two">
+      Estimated Savings
+    </div>
+  </div>
+</template>
+
+
+<!-- Lighthouse score container -->
+<template id="tmpl-lh-scores-wrapper">
   <style>
-    .lh-leftnav {
-      width: var(--report-menu-width);
-      border-right: 1px solid var(--report-border-color);
-      position: fixed;
+    .lh-scores-wrapper__background,
+    .lh-scores-wrapper__shadow {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
       height: 100%;
-      background: #fff;
-      will-change: transform; /* prevent excessive paints */
-      z-index: 2;
+      background: white;
+      border-radius: 8px;
     }
-    .lh-leftnav__item {
-      padding: var(--navitem-vpadding) var(--navitem-hpadding);
-      color: var(--secondary-text-color);
-      font-size: var(--navitem-font-size);
-      line-height: var(--navitem-line-height);
-      display: flex;
-      justify-content: space-between;
-      text-decoration: none;
-      color: inherit;
+    .lh-scores-wrapper__shadow {
+      border-radius: 0;
+      box-shadow: rgba(0, 0, 0, 0.2) 0px 3px 0px -2px
     }
-    .leftnav-item__score {
-      background: transparent;
-    }
-    .leftnav-item__score::after {
-      content: '';
-    }
-    .leftnav-item__score.lh-score__value--pass {
-      color: var(--pass-color);
-    }
-    .leftnav-item__score.lh-score__value--average {
-      color: var(--average-color);
-    }
-    .leftnav-item__score.lh-score__value--fail {
-      color: var(--fail-color);
-    }
-    .leftnav__header {
-      padding: 0 20px;
-      margin-bottom: var(--navitem-vpadding);
-      height: 115px;
-      font-size: 18px;
-      display: flex;
-      flex-direction: column;
-      justify-content: center;
-      background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZoAAADeCAYAAAAEuMatAABPH0lEQVR4Ae3dBZyk1ZX38d+59z5PSfv0uMPg7iQB4oRkIbrZbGSz7Js368mbrLtE1903nuzGVuIGgQgxCBbIAAPjru1d8sg9b091fxhBMsN0T1V13+/ncz5VNRaFP+ee89zi8QRBEARBEARBEARBEARBIARB0BTZuru7Be0HOoFYlcirAqgIiaoMGKtbAaWNBYFkD9/JzAuC4H3/57KeZKB0Wb/jea9YVrowFbNC0KVAj4Cr50olzTACCKCy11j/QLG/tlmEb5bXp18GdtFmgkDSy3lS7qxzLKsWzSfVcWCMIAiOzxfvuIZV3S+lp/hSUk5DYSzzCIcIkORKJcsQDqOg3gBgUj8qyP8YlU8DnyEI2oTsfc4ynkjvm65eaJ9z8cuMdQvwmoCpgAwiOlH2AERDCONABZUxICUIgob6R7/ynOz7D/+yjlRuEE8BryAccixBczQBUXCj+Xc73difhcAJ2oFUn1PmcV19dnfxZ1/yM2CXkucJIEeWGsCD1IAKSBUvIyCDGD0AphFEUz9fb9QcEATZvetPSd/7uT/2ef56CpFgDIecYNBMUSOAYES+YJ37XeA+WlQQyN6Xr+bxzP+XX3upJEPPIPdVnpwB5LBXEBQa5UHGgFGQUXIzBDIETFakg6B1IGMWCILq337+NfnajX8lxcISjDBl2oPmcCIMl7fXfh/4R1pQEMjwhX0cLfq5a1eUXn3lGxgZtSCeE2MeLcEAOZABOaIJyDAwAAxh4iFERqaCaQzrhwGlxQWBfv3+qP4/t/xZVo9+RZxhykkJGgCNDEZ4rxV5C1ChtQSho1nO0fr/6NWvMPN6Lif3dWaGABzVCZmpSoAaUMdSRRgGBkEGcbof0RGgAlIBMoKgSe5ev6KcRoVC18Z1C0758Hve7p39SZwBOMlBc4hFPmeM3AgM0iKCwEmqHK7nRef1miXzz6JST5k5etir50gCFIAiOX3AUkBAIUUQqaIyBhMlOlGMAIMoQzg9gGUMJQFSQDlOQbB7sLsDm5f/4+tXdW7b1rM0sqxQdInAotTbRV+9/8z+pEp3LnQV8aU/tz+39PSC70kkotly9MWo/9SY6XtNWIVuFYFsePHZHG78rTc+/fxz6jdQI6U1CWAOKwV8o+RgSRUYRhhBGUAZQnQIzBCqw+BrIYACgA994+m9Y2OF00yUnXLHnlWrvn3XmhVdsEpFVwBLKnWzMMuwIkxSEIFyQfEiGFL+2Pw6L+E/qVFGEYCmdjQABWrcaZ73wG8W/uPm3fPjnRbdirLJCI+IMAgoJ1EQyIpfyJnCaZ25fOqtf/36nrR6FpiE9iOPE0QeSBBJUFJEx0GGEBkE9qN+GBgDxhEZAZRgVvnSHWeVXSlddd8jq9d89vbzzi0V9FxVOXvTvu4VWUqXCOWCgUIBlENElMejQBXhzfLX/LK8m+qhkGmJoBGUAhU+ZH6Pf+U36aChKjAK7BR4QIW1ueUhYKsxbAH2MUOCQC55hTKFC/vW9r3/Tz76C4wXSoBn9pAnKIOQg1SBg1UBHQUZRBgAOYD3gwg1aFRK0NLW7Z7fsXt/39L33HTVeXGUP21guPviezb2L7OWJc7SFztAaYic8lRUEJ4nX+Ov5KcxeDIcQMsEDYAhx+L5Q/cRviYvpIjyBKoi7B4fZHNW415V+a6HtcBuYIBpEASy6gplCl/9t387/7SF219LEtWZO44MHhAmKQ2SIkxtwukoMAIyBAwjcgCfDwFZo0664LaHTu/64t1nnke9+MzP337uBanlTIFzvacIYA0UI0WZHhlCHwO817yMNTxEjRJAywUNQEyNHXIab7Zf4YD0EaE8EWMBYQqKss5Y1lq4R+EbKfJDYATwBMFxcvNW8ah0xK5iIcrcolMFkPMYKih9QD9gaNAcyFFyRGogI8AwRgbI/QDCKDCKyBAwzrQJ/ufWS1099pd86juXPX3z7r6nZXn89OFxWWYNrqOoOB5LmT4Z8Fp5L2ewlgqdtLKEIqv0QV6tf8/fyR8R8cR8zuEEOMvnnJXCjwO5ovtR7rQi30G402V8BxgjCI6BXH6DAvCMNevkz3//Yz8fj8kKkJTgyQjA46xoWwBEEqAO1EArIMPAIEYOkPv9wDhQmXr1BE/qextWnvuhW64+f/POeS9Yv2PeVRksLxcoOwMIGFFOhgThNDbwPvNiuhgmwwG0bEcDYMmoSwe/aG9ho5xKAWUaeOPZauCutCZf3rOOO1S4H1CC4HHIolUKwOtf84P+v/iNj7+RkVInkBNM53Hc4T+mIBXQcZAxiMeBySCSxkzoAFADkkbNQVv29/R84JZrTt+8e97131q79PkYLoosncZA7JRmUIQa8LvyB/y0/DPjdAK0fNCA0MEwn7Rv4S/MuyijTBcVEEAMVRV+oMLtqvIZ4F5gkCCYIhf8uALwyivvOeMPbvyv11MpekCZSYE5VCqAAh5BQXJgMnxERlAdBIanPg+hfgDVFPDMIg9tX1z87D1nPfPOh8684f7Ni5/rvZwLUC4orSBBWM5OPmReSB8HyHBtEjQQkTDAYt4U3cxOlhChzLBHVLnVeG4e7JObgRGCOc2NlGm48fnfWUg9ioAaMy3wUwUIj1IABOgCelA1gAFyIJ0skyCMggwh/gCqEyUjj86FoEKb+M7Dqwp/d8fVz9r24MIbSAo/tmsgXl0uYouRAkoryYBnyK0sZCdVOmgnGRGL2cIl+nU2y2uImHGni3A6lp/vqOpWb/nynsXyhTp8ExgimHPcvnk0dEWVhSCeoNl0qjyHSKNUC0AR6AFdgWJABEhAqqBVvKsiZnJF2+T7yQr70KgC1BBt+rU9P9i+oPjNe8666LaHlr30we2LXpqmnB1FIAK9nUorUoQIz/Pkc4DQbhQhw/Ec/2m+Yn8SxSAoM02BOGEl8HOnbNSf8ykbxg9wi4p8TOD7wDhzQuCWfRdWnlkR63QB4AlakR56fVyCahnoQFIBBAAPmBRE6iBjiB/BuxGIRoAhxA+QR/ungigDUmbIr9/03FOHHu77iVsfWPby2HIlQCGCUlFpdSmwWrazhnVkRLSjjIg1ej+djDBML5aTQ4WGXIAiazpXsgb05wTuxvNFKcgnpcBawDNrBXL5jcrPveBrpTdee/P/Y7wYFgFmJzlsJmSYlCPkqOSIVEGGER3EuwF8NAwMoYxQYBio8xR8/BsXlbfumn/DR799/ktG6/bFkdJdLihKe6kgvEBu5m/lddQoAgLQNjMaAINHEd7t3sPNcgNFlJYg1FX4qop8PEr57Oyc5wTOD0BhWHuBCFBmo0CBfLKERykCWFS7QHtQVkNisIkH6igJSp2IKsgAogOo3U9eGAYqCOPAKEf59HfPP+O9X73iVQ9unPdTKGd2laA7VgCU9mOg8dyMJQOEduQxdDDKGl3LFxtB0yKUgijXC3q9wmbj+S9V+ShwL7NG4Mgh7ZBefAiaNjQDt2jLoQCCMjllMuYBy0EEvEGq+VTQVIARjB1Mk6jy/YdPWfr7n7/2RYPbup9jnfbP68ypp5YkAxHBCIiAESaJIrQ2RXDASjbgsbQzwVPWYQytyRtW55bfENU3E3MzVt6rwteAUYK25rIesOQ9gANSggB0qiYJRxOMdwhd1Wph2cM7F527bseiC/YNdS99zXn34s8Xxusx47UCo9UiY7WDFTNaKTJUKTFWLZB7g5+ozINXEMAYpoJIaSUGWC5byLG0s5SYFWygm4wEh0VpNaIAFKnzYtCJ4h6FD8WpfBTYR9CWXDQEL7/qni6qBfukQRMEAhifgeZDo539D25dcvEjuxZfPBEgfaA46/EIAnQV6/SUapj+IUDJvWlU5i311DbCZqRSYvhg8NQKVA4G00SNVYtU6hGqNCiThEPdkIgy01KEjEkCdDLMyaAzfHzWowcQlCpgESLAoTwV3gtJDtZAZJVpJTQIXHywfKS/Jrl8RHM+DKwjaCtyyQuV7/7du14ex/kVQJ2jBYGgGJ+j6IGhrkVrty29aP2uhReN1wpdzipGPD+KAIgCIAKCYmSyEEgyO1GOWhJRT10jfIbGywxO1ESQUU0d9SSi2vh5g+pU6BxeAJx4ECUI8xnkeXyOpbINUF4qH6OL4RntanJVMq/MFEPOKPO41byCGmU2s4Zv6I8xQgcRyvHIvFBwGacu2sS+4QXsHemd+VsbBIADAh8zKv8KrCVoC/K6X97p3vuWD766GKVnAylBcDibZ6joroG+ZfdvXnbl5j3zz03SqGBNjjHK9DgUPiL66PsGARTqWcR4bbLrqUzUwSAaHi9PVImRarERTlluSHNLltMgAuY4u6AEYTWb+DPzc5zH9/E4QKhRQhFmmjBzFMHgialiUIScb3Mtv+3/hSH6j7mz8SqU4grv+Mm38dwLb2HrvlX84cf/mLs2nk/BKSfJuCqfVZW/B75H0NLkNW/a2fH+t3zotUVXXwmSEwQA1mcounugZ8n9m5dfuWnPgguT1EXOekSUk01EMVMloiiC9wfL4FWoJlFjHjRSKTJaK07OhyZq4nPjOC7NLKrgG3XUUZxRBFCEFOWP5Dd5lfwb4/Qx23UwxN/r2/lnfQsllGNRqQsvvuwW/vINb4U8gtIoX/r2T/LWD7yTcgFAOYkyVfkE8A/A7QQtyWW5iVHKIEoAhIBBNN83ETD3bFz19C17+8+vp67gjCdyOc2iKuQHiyMJYIzSWazTXaqxfP5kGOVeGt1NmrmJspMdUKV0MHgar9WDnVESUZnqkNIccqBb6pxu7ieVInAokWYrT8xF8j1U38KxEgOj1S7II3AJoAxXulGawono64BXCHxCkb8FfkDQUuR3fmv9oj964yd/puDSMqDMTYFRj/Hp0Ejn/B9sXHHlwzsXX1JLXCma6mDakYgicMRxXONVlCw31NKoETjVg4GTxAxVyoyOxfxK9Te5OPkmtawAaQZpDl7BCFjDbNLBKJ/hdfym/wfKKMdCAVXPG5/3YW648lNs2H42f/6ZX2fX4AKcVZpsTOAjxjQC52GCliB619+eyryR14I65p5AVLF5MjZe7vnBppVXrtu2+NLxetzlrMeIMlsdCqBDCwkopMScyoM8N/8spl4nq+ZQT9BaSj5Sw+8agiwHYQYICDPKyKH/XWPqjNHFm/3HuIOnUUCPq8OsZ1CMlCQTjNAImRYyCLzHO/kbYDdNFYhu/aOLyM0NgGOOCQHj06QeFddtW3reDzYvv3pwrGOBszlGlLlL8Ahnyf2ca+6lJFUQQUUoZGP4u9eh1QREmHbqwecgwkwQYLxeppoUEFF2sZz/0F/kq/pCIkBQjpeqtHTHK45NQzvkr8cH+AAwTlMEolvecTXePwewzClhk2z9jkVn3b1h9bP2DXetEFGs8QQAQoajSIUCdRTwavixp9/N/Pm7IQcQpp/C4B6ojoIYppUo2Ix//tKNfPS7z6cjVoboY5QCRQCU2cpYEOFuQd4BfJrjpB30qNIjUGRSAowCBwiOiejmt12HckUImjkyhxGf7RvsWXz3+tXXbNi94CJVcNYTPJbHoAgAuTe86nnfY9GqPaAWVJl2YiCrw96tkCUgMu1B8+7/eTP/dut1dBXAABZlLokyPm28vAu4kyfgu+jP+rjCC9dV53GONSxVZZ4IJSbVUYaBnSiPqHKLV24DdhI8LgdSBhWC2UsAmyfVaqF8z4aVz3pg67Kn1ZKo7GyOGJ5AYPAcouA9ZB48M8SDRFDug4HdIExv0KhifUYMRChzURLxMtDnCfKP3vCXwABTagu4uL6AnykrL1FhNYA9+iraQxYBZwDPFvhZa9iP50uqfBC4leBxg4bZLTwPs2H74jPvePjU5+8f6Vwa2ZzI5bSiQKHcA2NDkNRAhGB68xboAv0d43mJyeV3h5awTubzK7HwUx05HQoIx0mZj/B6EV4twmeBdwL3EjQ4lCIgBLNx2J8MT6wr377u1Gdt2LXgYo+Y2GW0sEAVrJ0Mm6QKCDMgUMBzbjqPTxfnMaDQjwflhEUKP64511W28Bfe86dAEoJGNEYJZhPjM/XG3Ld+9ZV3r1/17PFa3OOsx4knaJOwKXXAqIN82rfQAgUMZIuFvB9B6UeZPgpi6Ow6g7chXK7KzwK753jQMMuCJnQxBwa7F333oTXXbtk7/xwjvh2PyULQRAVwMeRVgukPmXQZ+D4gZ8b4FARu8I7Pbt3OK4GtzFEOJAYlaP9ZjM+NTHQxT7vnkVXPHU/izsjmBG0sLkK9AgjBNBHIlgq+F8hPUq55Ll9R5n+yUW4A9jAHORQHCG0tPHg5ONzV/50HTnvBxj0LznPG0+4hEwi4AiggBNPBQ7ZEyOcBOSeNerC9XObm8T6ElwEZc4wDNQTtyfgcFX1w87KLvrduzbXj1UJvGPbPEgJYByjBNPDgeyCfD3hOOvWNul7ht4B3Mcc4wBC0H5cnlWqh4ztrz3jBup2LLjNomMXMNiKNOkGBAhFkiwQE8DSNgd/0yufn2g3TDhFBlaBNiHpsnm7dvXDNtx847cf2j3QueZIbloMgUMjmgZaAnKZS6Eb4A+CVzCFhPtNOrM99buWuB0951l0bVj0nz20cu5xgFlLA+xOd0QQKWgDfJ+BpCQIvF8MzgW8yRzi8KiC0tsBlyeh4ufub9591/cbd88+LrMfZnGAWy1NOTCAKebegMa0UNCaHX8wM95cMw4CfC0dnHlVDSwvPxmzbveDU2x444/qBkY4lc6GLCRTSOgjBU6WgFnwXLUUB4/mJ2PNMEWoC6wVuQ7kF+C6zkAP1tKbAaK6ov/fh1U+fuKfsusybeI4M/AP1kNQAIXjqNAZfAjytxgJLMwWFU4EXAG+zwje85W+AzzGLOIQcpQWFBzBr9Tj+9trTrntw29IrrPU445kDAjFQG4P8hL4qINCpkLGAp2UJjzJeeY5mPAflw1h+HdjHLOBAUtBwsWYribL64GD3gq/df9ZLdg70rpljXUwgQHUMcg/G8AQCzyHCIXroVUtCuxEA4adRzspyXgVsoc050DrQRcsI85ituxas+fp9Z71spFrqD0/4zzEikNahMgJGCB6HBwxoGXwn+JKgEQ2iQB1MVbEjoBHtS7kisnwmHeF6YAdtzKEkBK0RMqLZ2k0rLv3W2tNfnHsTz8WQCQRGDkCeghiCo3jwXZD3C74TMBxBATrAi5AnoAJ42pfhQp/wntoOXgYk7dzRJDRXYHyuKnz3gdOfc+/Glc8VVKzxzDGBMVAZhfFhQDhMoIBAtkjIFwAG8I16QuoApa1pDoWFvKi0hJ8F/ok25RBTQb0CQnDy2TxLkjj6+n1n3vDwzsWXOBOe8p+TxEBah6E9oAoiBEeFzFIhnwco4PnRlFlBc8hzfgvhk8C+dn1gswIoQTNCJh2vljq/es85r9i6r/+MyOUISvMEXs0T/7gIGAGEaSUCSQ0GdkKegDVMOwGMoGLIgRzhaAYQlJajU7cu9wM5c9UK8bwC+DfakEO0gtIE4VLM/YM9C2/9wdmv3DvUtaz5ty4HRjwFk/B4cjFInkEtA2+YPgK1MRjci9brIMcwVJCncDONKNiMOB2nm1E6UQ5RFKFGGY9tzVuX+wHPnKbCq9s3aKwbJ8sITiKX1Xbt61/51XvPeeVIpbSg+evLQeojTu3cwrVLv0nuHQACIAKAihDtSknv2AtpBiJMH0VrCdkdD0Oag/Ckqt6TAsLxe3n9d3mReRuS8ShLTpUyb7cf4CE5nwilZRjI5wtICBrgfIFlwI42DJp0hEyU4OSIstq2ifXlm+8991XVetzd3PvKAhFFAGs8TnIilxIhAKgCuQfvIVd8LYPUQy4gTB9jIFG0kkGW/8gQ0zTDe0U4fmWp0SEKemTQVOjAkaAt2M34MuAJoFuFs9oyaP7s6y8e+ZUrv5LHJo0AZeYELq9v3Lb43FvuO+fHk8yWQsic1AfgAMXIZIkoAGnuqGeWSlZmODHQMUYuRUgydKLIPZr7Q4EjAobpZQSt1EEVjAHhyRnhqfJYHkvIcShCSxHIOwED5AQQAYtpQ+62HSuSt6jUgRKgBDMWMg9tWX7xN+4/4yW5N4Vwncz0E0DMkUHiveD1YBnSzDFSLTI6UePVmNFakZFKkbFagX3VLq5KDvATV27Gz18GWU6DMEVAhBkhgg6NQZaDs0wJDGhJwDMlUKVIG3IlmyagM3p7X3jaP68/sGnFZd/44ZkvQ8WGZ2ROIEhEpwoE5VECWW4Yqxao1GOqScxItcBwpcToeInhaolqPSLNLVluJkoeveHFCFQF8B72DcHC5WCEk0IEkhS/fxiMcJhAAMdhAoE6bcgZpA4yDgjB9BIU45O1G1de/s0fnvFSBGueNGQCEQA9LFR4tEPJvVBPo4ly1DPXCJShgyEyXn60M6klEbXUkUxUmoMRkEPVIICzekTzYBAwAjsH0NU1pFQA75lx1uB3DaDDVTCGKYGCGlDhkCATwx7akJNIEzSvAIZgehmf3rd+1dO//eDpP4aoNaIcEkwFCOZgGcWrkOV2srw5GBqNIBmplBrHXaO1AuMTVanFjNdjkswggB7R8RyqQqTHP5Sv1sk37sadtwoEUGaOEbSekm/YxWMF4kEAJUBAc4aTAzxAG3J33r9UudZVcLkhmM7LMev3r1/59G89ePr1ImrmYMgc6kamCkBVUAWvQiWJGKsWGas3AmSi4smjrkZ3UqSWOLwKuRe8BwWMHOpSIqtMO2PwW/fh+7swy/ohzZkxIuTrd6JDFXCGwwQCKJADjkBAPWtdDztoQ871wKc3Xjj66gtvU5ICJygQYGom860HTn+xiIoRneUzE4DJV+HQ5zS3jNViKvVCIzTGapMzk6GxcmMoX6nHjV+TZpYkE1TBmENBIgCiWHOwTmI6eiX/4VYoxpj+rukPGxFwQr5xD37THp7gP1zgQaqgRUDDkXJ9Fx+nTbn6Ltj5YHGIyzUlOeFONbB5/cGpwT+CGNFZtBoMcth6cJabRkjUs4gks42OZGi8fLAaQVKtxxMVUUliaqnF50fNSmSqgNgpLcPI5AOUd2/AXXQKZn4P5B5UOWFTKZpv2E3+4DYahODxKJhRxfdJyFzYVjX8D23KVQ2YNX6ElAxwIWhO7AvL1m1efuFEyLwUsEa0PWcmRhEUr2bq2MqQedvoQEarxcbcZOJ1alZSaAziD75muQCgOlkcdszljIKhfdjJeU1253rsmcuxKxeAtZDnPCUiYA1aT/HrtpNv2QcCiPAEAgN2DPIaaBHwzFmJ5y86lrOXNuU6lkOn+CGYCpqnLDzxv2nH4rO+PhEyCq4FQ+bILS4UAAUEIVeoJVEjMMarxcaR13C12JiXTA3jSb0hb6wGG3IPHD4zMYo1yqxiDGQ5+Q83o/uGsWuWIH2dYAAPqAfliclhSZvl+O37G52MDo+BtRyDIAV3QEmXC3ORCkSGr3QY3kMbc/McfHn9paOvPueu8c64WsIbguPksmT77vmn3nLv2a/Mc1O0xrfMarCgGFEUoZ5Z6knUCJTqRI1MBcnkcVeJetZYC278fJo/9u+VwiTTCBXmBpFG+d2D+P0jmAU9mGXzkJ4OpBhDZEAB1aN+D5Bk6HiC7h/B7ziAHxoD5XhCJrBgBsB2QN4H5MwdBiTlIfH8dAI12phLKvBIpS9PE7ePAguBnOMSrvrfvb9/2c33nPeqehp1nKxrZUSOnJmAkuWWqQcSG6ExVi0wWikx2JiZFKgmcWNuMvm8iUUfM9g/9HrUzCSwBrzH7xpolJQLSFcJKRehFCOxo0EVradotY6OTdRoBerpVGIbMARPgdulqBN8F5Az+1mQcYh2aJlElgF7aWNO1tNQNPk+wAIpwTGHzPBoZ9/BTqZSj3uczWfkKXgjCnJoNVhVSHLbWAkeO1jVIsOVIqPVwkQVD1YjVLyXRuUKqiACZqqcVYKnMmcRALSaoON1GgwgQoMCKPjD/gt3lhMQCJBBtFVJlwq+F9BGzT4GUDAD4HYrkrIS0f8GuRbY2L5B42lYu/b0PZc9424lNxyDwPqsXi8Uv3rv2T8+0TEsjI4zZARAjlwJFhQEBMi8aWxs1dJoagg/GSgjjWF8ufF5qoMhzQzZYU/BGwMCMDXYNwTTbSqxn3hNzxBMJwFyiLYr+biQ94MWOURpX8IkDzIG9gDYEaXBAHCqop/0yHXAAdqQy4WG/3rw9AOXPev2cepRDHieWGDU+8zYiZB5xc6B3tWxy580TI5+eNFMXaeSZo4ks40aqxcad3KN1A52JxOv40VqU9etVBtPwYPIVHHke2OUgiEIZjehwR5Q7DD4TvBdgi8CBlRAaDMeJAVTBRnVxiseMBxB4FKDfsyovAyo0GacUxpKqe73uR01sOBJgiYQnYD/+v1n3rBx98JzYpcBIKJHBIki5I3trIMlVJP40WtURiuNMGmsB1eSyZlJNXGgNCgcCieOuE4lCAID5GCGwAwrCKgFDO1FQXLAA8ok06jHJXCtyfWfXcYbAE8bcXFdAfjy7efVX3vdyr1nrN6+iCTiMQJB1OCy2n0Pn3rV+p2LrywXEqZQSx3j1UIjOMZrcePZkpFqibHGVfSFxtPxuZep4AFVMHLYMb5RjlEQBDJVUySjPcmhOhaZ48aJ2gC8gzbiakUeFTu/Gbhgzv9fVxFQAcyhH9c6pfruu3549iWfvf3C68brMaNTYTI8PvnAYpJaktyRpIbcHxkkIjSIKJGlBQUGD9SJKfB4shxUAWFGGQEB9InHgxOlyFN+KJcjWHIyHIKnbQlzgtDwNlFZD3yMNuGMClP4yNev2PIHr/9Uwuy+NFWYZB4twQAZUAcSIptgzSjKAdBBusoH6JAD7/i7Z57xpW+f87bhasEAiDz+avDUFfRBGxGgoh3s4HQqdPAYCj09OZEDVWaMAEkqZDlPqFY2pIBwfERgvOao1CKMKFMw5NQoUaMDIWgDoqLvSXLZCnybNiDn3KBM4fzVO6OP/+E/vpGx4nKElPZ2eJAI4B8tLznoGCoDGB0hZQjPCDCI6DAdpWEg5TB37Ty9+6ff+fJbNOOyMC+ZvQTlaKqQ5cJvvWGQs9ck1BNhpkRWueehIht2uCdZQReeCmeVBzYWeHBDTOR4HErQVjYoPBvYTotzh1+1/tC2JemB8d4t/XZ8Jd7S4g4dbSkCCJMEUESqwDDoOCmjwADIAIYBhnSIROuIJkDO0QarHO7OTSvNL3zs+vdIzmVxCJlZzWM4mgLWgnUCGBBhxhgFDNooZVoJGAMKqADa5qdPwRoR3q/Ii4E6LcwhwuE+e9OlP/yZF996pdStAErzCMBRQWIapQCSAOOI1ihQRWQYZB/4ARIGQSvAOGiVCE+D0tDPcfm/7/6pt2d59Kq5+7R86GjiWImsoqqgzBxVQJGpmk6C4owigKgSzALKtaL6V8CbaGFOvHK4j9125fb/88LbtiF6CirpSR7EW8CACkKGkjZeY6kBQygDCIP06jCiYyhDGB0FasyQt7//xT8+Xo1+p7usKHNRoAjOeVwjaGhbChijiBDMJsIvA3cD76dFOYQj7Bvt8A9tX37XWadsOYV6NF1dzZEdCQqgh90AmYKMgQ6BDONLI8DBGsDoAGUdATLAA5BwUrzjo88/7QP/e+k/9HaqUdpLkgkCRE45QYFC5MA52p41IKJMJ++FegJewVmIoxBmTfCXGLkXuJsW5DDC0f7iw1ev/dff3XxlZHQlXpLj3whXATFTr4KQA1NHWTKOxiMIA6gOYO0A3Z3DoAlQAzJawL++75LyP3/y0g8sXswSlLahCEkGK/r34L1lx8D8E3zYM1DAWW2UqtC2VLAWjIACwolLUqFYUC48c4zOzpRde4us21zGWbBGOTkCEfryTD+wa508Bxigxbgda3mMHWvPSn7r515w8xnLv/jTjJcdQgYIU47qTgyCAlNBIXXUjTVCRMwBvB8kYxiRCqpjwNgRWZLkUBmg1Xzq3vP+on8+V6NKO0kz5bVX/y8/+4J/w6vwL196E//zvRefwG3MgSrEUx2N0r4UsFYRAVVATjxkli+q80e/uJ5nXDIINmdsLOaTX1jKP3xsJV4FI8rMC1TBGC5Yfq7+A/A6Woxbfq7yeF79zms2/O/v3PXF1Wt2v4jUxkA+WeJBKogMgg7hoyHUjQIjiA6jdgCo8yhLu/n5v3rda9bt7PqleZ2KajuFjLC49wC/cO2/M3/eThDPm174L9x83/Op1EoYowTHTxUiB1Gbz2hQsAaMQK6cEK+Cs57ffeNGnnH5fsgsJI7Ocs4bfnIzewciPvDppZRLnFzBa9Nxvg38My3EpeM8oRf//ltv/9Nf+d+9jNpTERlD9AA+HkRdBUiBjFlmy76+0773yLK/6i0rqrQXgdw7kiwGkwNKksZ4b0A4AYFzOlG0PWMUkenoZuC8NVWedsEQeAteaMgEYuGFV+/nEzctIc/lZB+hhc4m5h1JVb4F3EeLcFkuPJlf/8sf3wRsYg5QT4zjX4pllghKu4mssm+kh7/5/Fv55Rf9UyNg/v6Lb2asVghHZyfc0SjOKr7dt86sRYQTHtKoh45yTqHgwXMUoaOUUyp4RsctJ1GgYCzzSt3670bluUCFFuA6ugim5J7fRvT5tLHYKV+4+3nc9tBVqAqj1UJTlwFUBQARbf+gcUo9EdqRGosKdGYDRMwjoYCgJ9DhwZZdRXbvK7B4aRUS4RDPI1vLDAxbirFy8gXqudKj7wbeSgtwXhUIRLka4bdpf41gqdSLyNT7Zq5YR/bQ+3buqiIH1tBmBDUWUOLxAboGNtAxvJWSXs8YBU5E5JSde2Pe8z/L+f1fegSJcyYpe3aXed//rsAaECFoEoU3Gy83AV+kyZx4Ya5TodOL/iNQYpawRpu+mPDMs2/nLTf8Lbka/v7zb+FbD13RnmEjUIg9SpsQwYtF1FMc20PngY2Uhndg8xqIwZkMPCesVFA+/uXFHBiOecXz9tDVnbJpa5kPf3YpD28pU4iVoKmMN/q3InInsJcmcmqZ88aL+q6OCheqMA0Cr0JXqcrvv/JdLFvyCAj89sv+gtf/wwcYrnTirNJOBCjGCtoOAeMwPqU8sp2u/esbQWN8hheLNxEGTyzptKw3i0DklC9/ex5fmag4gmodIkcImRahcHqk+hfAjU1eplHmMoe8IK/ySypMk0CByGV0FschiwDoKIwT2RSl/YhAIVaU1qQiYCwmS+gc3krnwEaKY3tBPdoIGAdTRJRIUqaLyGRnowqqQkdJCVqHACm83iBfAD5Jk7gcYa6q7qdHO/UvSiWmMW8DIzAw1sW/3/xGfunH/hFV4X23/h/2jvYRGdpSK/4TuopBxWKzKh0HNtF5YD1xdRDUo8aBGB5PbFJUpj+MRZSg9SgI6J/1WfkGsIcmcH2WOcsu5I9S5QJVgmm/JRg+/I3X8I0HngkKW/YvI7KKtHNHo7QEFYuKIUrG6BjcQsfAJqLa0KHhvxieiAAFk4ICAhB4FbwHY8CIMtsI4GH1/lz/FPg/NIHbnytz0f7N8rwFp+ibUGZAIALWwMa9yxAgsiAA7Ro0BW2JFWUQotownQObKQ9uJqqPosagxnFslILJmBTkXjAC/V1DDFV6yf3sfcBU4MahnfIZ4NOcZG54pzDXdPTTOREyfw5EzJhARCm42RGahQhQmmIqRIgrA435S3loKy6tomLwNuJ4CBwxowlLK2P81kv+mqed9W1uf/jp/PlnfoXhSs9sDRuZt1L/DOQbwCAnkZu3kjnH57wJ5RKC4JgoxYKiJ3uDzFhEtTHYn1xR3o7N6/ipAf9TFZGiBJU6/NTVn+cl13wU0iIvfuZ/sm7Hmfz7La+ho8Cs5DPOUOG3gd/iJHJ5ztxiOcegv6UEwY+mj3Y0Hj1J8xdvY0RzyiO76dr3CMWx3YetKDtOlCNlUhBFCQigFlAil6DKrCaqb/bCfwPf5yRxijJXjOzDdHbxp1KglyA4RgKNjgZlxqiZDJhiMkDf7g1UNhygo7bniBXl6aE4MiAoxfCZ79/AFWu+z0Vn3M4P7n0hn73reooRs5ooJYG/3LNIrgUSTgK3d6EwVxQjXhlV9cVKEBwjBZGZu8rH2wg1jrhygAWbv8Oy9V+gsm0F++rnoNaAGKaTAFYzILBGOTA6j//3gb+lv3sfB0bmU88cziqzmQoIPHPeADcC7zk5M5oB5oRinT4VfZtaguC4OAvOgSrTRPDWocZRHNnFwo3fYNGGWykObQeTomY1ahyQMt0UsJJjhGAqbNLcsvPAYowBZ5S5opDo74nwOWA3M8wVU2VOsLxVlbMIguOgCnGsWKOATEPARCDSeP5l4fpbWbDpNuLR3WAcuBhEmFmCJcOhKIKghAeMFWOZeyyr6qP8LvD/mGGuPs6sVx2Rs3qW6VtQguC4qApx5DEG9EQCxsWI+sb9Y4sfuZn5W76DrQyCdRCVOFkUsOKxeDIsc1egHlyZ/ytePgjczQxyUVGY7Uq9/F6W0kMQHCcF4miqo1GOi4ppBIzxGT277mPJRMDM2/Z9TG0EXAGiIiefYMmxkpOqRZjLAhHK4vhD4GXMICeO2U24Jkv11TwFQaAKhUgx5ngDpoBLK/RtuYOl675Ez64fIGl1KmBKNIsqOMlxkqEaEwSq+lLgBuDzzBCnqsxahkhy/hjBcYIUQEGEOSRQnepoLOixPAPjYqLaKAs33dY4Iuva8wD4DGzcCJhmUwRnPFY8KCAQBAp/LCq3AhVmgEOF2ao6yMvLffpcPE+ZKtQTwTmInFJPBfXh+zbm1tEZGDmWFeUB5j/y3YmA+Sod+x8B9eBiMI5W4iRvFEEwxSiX1or6U8C/MwNcvajMRuOdUuqp6++gJxYyWS68/Ln7eNWLdtE/r86mbR188H+X8937u8P3oc8FCoVDM5rHX1Ee3cPCTd9k0SO3UBzeCgjYCBBajQKWyaBRDgkCl/Mb1ZJ8Ehhimrk0FmajZQO8rlriIlWesloi3PiSXfzOLz5Cgworlla54txh/t+fns037+qlVFCC2b7e7LEWcn/kinJ5aBuL1t/C/E23URjZBcaBjQGhdQmWvFHKpCBQAZdyWlfK/wX+imnmukaYdayntyb66xiesjwXFs5L+ZmX7gAEEgNTip2TP37H/d14bzBGCWb31pkxkJoCeN+44LKxorz5W7jKAJipFeU2oIAjx5ATBIdTAUHfIiofAfYyjZww+9QtP2XhTE5AlsOKRTX6ulPIhSOosLLxczn7Bg3GEMxSquAKESZy9Gy9n4WPfJX5Ww+uKA+BmxrwtxXBkGPwIATBERRW5OjPA+9gGrkMZTYxnl5n+FUVTogxMDgSUU8MxY4ccg4RZWjMMV41GCGYzSwsGl3H2V/7AMUN9yFp5dCKctvyODIeTxAY4Ze95z3AbqaJQ5lVROQNKnoKJyhyyqadRb7y7QW86sXbwFjwAk4B4dO3LmJ4zFIuKsHspAhxDOft/SKlka+B6W0ETPsTXPg65ycWLDoYNsAfME2cEWYNL9Kj8ItME2fhbz6yEms91z9rH8VSxsBQzEc+s5z/umkxpQKzXTg2M9ARZ+BKoIbZIibjCQWB8EaQfwZ2MQ0cIswWBm4EPW36gkYZqzre9i+n8YFPL6e7K2XfQMyOPQWiSBEhmOWcKJ1RFVSYTSJJEeGJBMFihJ8F3j5NQcOs4A09NtdfUqaXs4pX2LSjiGoRYyCeEyETKOAkp2xq4IXZQoFIwtFZ8ORU9Y058i/APk6Qy5RZwRpepjlnMgOMTIbL3BNYySnNsqABIZYMEZ5QEAisMJbXAH/PCXLG0vb21Ijnj+mb4m5Qz7QIAgUMOUVqgDCbxCYlCJ6MKCj6i3sXynuBCifA7VlI2+sf4kVxlcvUM62CwOIpzLKgUaAgKUHwZFTA5py1bDs/AXyIE+CWbaetqSBi9Jc80y8IDDkFnX0dTWRShCD40bzTX1DkY0DCU+RyR3tTrhR4LtMsCBQQ9UR+tgWNEJERBMdEeRpWnw3cxFPkMEo7k1zeCDhmQBA4UqxPQZhVnKYEwbEynp8/oaAxnrY1ulNWdyzWV4ow7YJAgbJUEfGzbkZjJUNQguBYeOX6eUbOA37IU+B6RWhXbon+dAI9zIAgUKDLVTEos43B4yQnUxcCJ/iRBApDXt8A/CpPgRvySjvKPB3G8JPCzAgCVeiJK4goKLOCqEd8huCxeDKC4Ngo/DiedwCDHCennrZUynhuvcA5KDMiCBTojiogHlRmQcB48qhIre8UtsqZ1IcjBCUIjoUKKwuZ3AB8hOPk4kxoR2msbxRlxgSBBzpdFVBA2jZg0Jw87mS8dxXjfavJu/pIBgyqIEIQHBNRSGJ9AyL/ASjHwSUF2o7COaI8nxkUBKrQYaogSrsRnwNKVuhuhMvYvNVkhS5AcXlGJAYhIgiO09UevRL4HsfBeZR2EyfyijSmLMqMCoKSVAEPWFqfIt4DSlrqY2zeqYz3riCPO2BqNgOgBqwFhDYQqAoAIkqzCTjNedVxB43mtBeRUhbrT4oyo4JAgRLtcHSmiM9BDPWO+Yz1r6HSs4w8KjV+XHzG0Ywo1kDuQQhaVT0VjAHvQUSInTZ/Nd7wciPydmCIY+QiK7QTD89COY8ZFgQKFLUK+JYOGDWOWtcSRvtPo9azhNzEiOaYPOWJiIA1Sp4LCEELSjLhGWfexY3P+RC1pMQ/f+UXeGTXKURWaSphdZZyHfAJjpHLUtqKi/Q1npMjCCKtAdqCA36PGkeldxVj/adS61yEGjsZMD7lR5gKGoIWleXCgu5B/vAn3snKFWtBlN7SCG/8139GVZp6jGYE6jV97XEFTVpT2kWlLIvmwbWcDEEgEOU1UG2ZgBHNyV2RavcyxuafRq3cD2IOHZEdCwURMEYBIWg9uUJXcZwlvbugVgaXsWrBFiKXk+YOoXm8h2I31xRgNbCZY+B6umkfRZ7rKywRZlYQKIIViKUOSEsETBZ1UOld2ehgklLfoeG/ZhwPBYwBawkrzi0qsrDtwGL+5/ZX8MqrPkFWL/CRb76OauKIndJs6unLM7kO+DeOgcsToV10JPqaNAKUGRUECsQCJUlApWkryoKSxl2Mz1vdWFNOit0IiviMEyEyFTQErciIknvHn336N7jl/udRSwrcu/k8Iqu0iiTSVx9z0CSR0g6sl9UKzxBlVlIVVMEYJWg+VShaT9nVOal0MkTMRKWlHsbnncJ43yqyyRXlxo9PB0GxRglalzWKqvCtBy9FBAqR0kpEuRzkHOABfgQnKrSDzPI84+mfrYO/3EMhUqqJUIxaYmc+bJzZlLKtA4YZpx7yFGze2CA7sPAK0r7FZIetKE8nY8AaQAVQgtYkohRjWlWH8bwQeGDWfE2AoC9VZp/MC/O7Bnnri/+O5Qs3cfNdN/Cxb78SmrpZEqhCbNLJjsYLM0chq4MrMLz0Qg6cex2bRq9idE8nkWSYPGUmiIAxoDx1QaBWX6xe/hbwPAnnhZZnhGUqXI3Oxm4GfuY5H+alz/wPyGIuXXUfa7efxZ3rz6cY00xhRmMyimaGgkZzyBKISgysega7z7iWoaUXYYoF/P3SCBixyoxQEAPWKKBND/R6ImQ5GAPFOBwftxNVnqY5pwPr2v5mAF/gOnL6mIVEYGnfLvAOkhKURpnXMYQSND1oJKUgCSBMG59BnuALPQyccs1EwLyA4YVn402EzevYtIpoETAz/MVnYG1zj85yL3gPF505xtLFNUZGHfc+1M14zVCIlLYQFK3R639k0LT6QDDOReqi1ymzlMLHv/NKLjr1Hnq7DvD9e6/jro0XEzmCJlIgIqUgdVABmY6ASck6+tm36ir2nH4tY/PXAGDyFJvVTvrZvzWK0hzeC3Hk+fUbN/Gya/dQLKXgLXf8oJc//MfT2La7SBzCpi0oXBfJkx+fOSdCK6sYXWAyns0sVYiU7667jBv/4QMs6dvND7eeS6VewFklaC5HRsQJdjR5CupJuhaz99RnsWfNsxvPwogqJksApVmMBRGaolqHN7x8F69+yXbILSQODFxx8QF+942GN/3J2WiYU7YHy9N3bNCVwGaegNuxSWll3QvlilKPLvQ5zOaw2bp/KZv2LCWOCCHTAhSwpIgmIBwnfTRgaj0r2HPac9k7ETAHr4kRzRvdSytwTZrR5F7o6fQ8/8r9gEAuNHggt1xx/jCnr6zwwMaOMKdsA6p09S3l6U8aNH1LaWmurNf7nFkvsjpRBM2ioHrYew9RWod8CLQAGBALGECefEUZqCw4g12nP58Dq55GvTz/0PFYi9AmdjSqUIg9pWIOCEdQKERKuehRpU0ErsSLgY/xBJwr0bKMl06vejUzJAjUAwomAhuDLYEpCJGF8bifz5Vfy7xsiFXZVvrzfZT8IGBAYkBoUIW8DjZmdMn57DrzOg6suIIs7sLkSWPA33JUcFYx0pwHEQ8MOR7e0sGpp44Chkc5z/adJbbsLIY5ZTtRno5IHzDI43AgtKrhbs7sGuEMhGkXhIARIOqCeL4Q94DrBAyI0DDEIv5KfwUBYvWsTjdxcf1erqp9k/OT+8HXwAu4AgMrn8aeM17A4NKLyKMSJjsYMJXWPhq0IAIKCCePCIiBf//vFVxwxihLl40DAihp3fHPn1jJnoGIclFpE4GwMk64CPgaj8PFCS2rXOFpCDFtJvdCmoM1tNLdRMHhx2LdUFoO8TzBRICC6mGvgAARk3IMD8VruH+iPtXxEi6r3cWra//N6kUR209/IQMLz8Xb+NEV5XbgDJMUkJM/l3xoU5mff/u5vO76XaxeMc7wcMz/3rKI2+7qDSHTbhSTW572hEGTW1qWy/Q6lfa7TqYUJ5yxZBv7Ruazf7SHglOUoNnUgzFQPkUoLwOJQD1ozo8kQKwQA5lEfKP8NO6YqCsXJTx3YY0uWyfNarQNBWMUEVClKQqxsmlHiT/8p1MpxlBPwQgUC0r7CbzRFwB/wuNw3mirzmfmqeEStL06md6OYd792j/kmnNuY/2u0/id/3wXD2w/vQWu9g4hYwvQebpQWACaN+opMUBJIQe+sTlm+5DwE+emLOrISHKhHRx+dNZMkdOJAq9CqaCI0KYChQuNyjJgR9tcqpkZvUyURbSRegrPPe+bXHPBVyEtcdqp9/CKKz/N2m2/gSIIStCkkClC95kQzQPNmBYWsBY2DkV8+Ae9vP6CIRZ3tk/YWAMitAQjStsLenOjzwD+i6O43CgtyculiDraiAiMVLtALbgEUMaqXShB0yiYCLrOEKI+0IxpV7Cwa8zysR/2cONFQ/QVPKmn5VmryPQNaIJAQC5/3KABoQWJiD6DNlOI4Btrr+bfv/RLvOiyL/DDTRfxn996Nc4QupkmUYXOU4S4HzRjxhQsbBtxfOahbl53/hBGwCstSxWcDUdV0ysQ1SuACEg5jBNVWo0aehQuF23Db8VTx99+8Rf4l5t+niQXjEBkFeVkCzSHwgIoLQbNmXGxhR/ujbljR4lrVlWoZ0IrsyFoplkgcImKLDx6TuMQodWo5yKDLqQNGVEKDlSF2CoAStAMxkF5pYAAnhkngBG4bUsH5y6s013w5J6WJQLGMG2CQKEL1UseGzSqtJoCPD0BEdqXiBI0j+ZQXCJEXaCek8YZ2Fsx3LO7yAvXjJHkglfwKqiC0jrksK8LFmG6BMHVwOc4jKMFiXARylMTBArGQmERIJx0kYE7dpQpO09sob+c0V/K6Yg8CmReaAUi4CzTKghUuIijOBVai5euOnq28NQFgSkz2c3kzVkbHqwa/vfBbkTACI2gOXN+nYsX11jenTbCxitNZ4yiTJ8gEOV0PPOB/UxxktNajC4BziAIjpVyiDB1xYyABTxNYQQQUCZr77hlx1iZ7+8ocdXKKs9ePUZktHndjYaOZsYEq3CsOiJocLQUUTlPVQsEwZNQD3gQe7AO/Zjmk+U6QQyop6nksC7HArVc+PL6MttHHK88e4SuQt6UsFFABKxTQABlmgSBQeVC4C6mOFRoJap6OcGTCQEjU7cu9wuuA0x02LclVyAZVGwBUFqOFbBucg069T381PlDlCJP7oVmsAZUmV5BoHoZ8P6W3ToT5QIVguAxNAfbAR0rIZ4vGMckZZLQUFoqAGhOyyo6WLc/4qYNnbz8rBF8E3JRRDDWoEy7ILigZbfOjJcONXoqjyMIIRP3Q9fpgi3/iEsxDaC0vNjC7TtKnLewxhn9yUm8I01QY1HxdMkoTroAwzQKghWozAMGWu7oLDe6WmApRwtCyPRBz9mCONCMJ6e0BSNQz+FbWztYMy89aQGDeopje+k58DDl8Tob7fOoaBGDMk2CYKEYPR24HcCJUVpFlLIyjehGaQgC1anr/U8TJALNmVUiA1uGI/aMWRZ15swIEVQs4jNKwzvpPLCB0uhOnE9ItA8RBWU6BUExyzn10aDJclqGWDkdVYLgUR6KS8B1gubMOiIwlghbhmOWd1fImT4qBhWL8QkdQzvoPPAIhfF9GJ/jjWuU9YrBo0yvIBAjpzHFiRFaRS6cI8qkIFAwMRQXCeqZlQTIFfZXLF4B4cSJwRuDTauNgOkY2Eihsh9UUWMbAQOgCE4yLDkoIEybIDCiZzHFGVFahnI2QTBFPcTdgikwq1mBSmLIvOCsnlAHgxhsUqFraAudBzYS14ZQpmYzwmM0gkY8yjQLAuVsFRyQOaU1uFzKip7qDUETeS8T1TpXyLvyoQcvZzPlqVNjUQxRbZjOwc10TJSrjwHgjeOJKIIjx+CZAUGw0ov0AvudF6EVpJaVRukmaAqvQq0GnWWlsytj/1CEVyjGStMoiAMM4Jm1vEIpUqwoelwbZAaAuDpE54FNlIe24NIKKgY1lmNhJMdJxgwIgk6Tc2ojaExOSxDR1UAHJ13gPYDyhpfv5seevZeuUsb23UXe898ruWNtV1PDRhVQZjUR6C9lWAM5P5oaB+opjO2j6+AG2fAObF5DZWr+cpxiSZgBQVCY+vv6HU5EaQUCKxQsJ11QT4W3vHYrv/j6zUwSVq8a44IzR/nld57LnQ80L2x8Cujs7mY6ImV5T0rmBTH6I1aUc0oju+gcmAwYyTMwBm8inqqCpCjTLwgUVgO0zowmlZVprIhyEgVpJpyytMZrfmw3qEBqmGTo7k14/Yt3cPeDZ+IVjHByGchGFc0FDKDMOpmH1X0Zyzszcg/OPP6KsvUppeFtdB1YT2FsL+Iz1FjUOk6UI2O6BYEA3rMSwKmnJWSOlaKcZEGWw8oldXq7MsiFI8nEz9Xo6coZHXcYq5xMYiAbm6yoB1SZVRQwwFUrKjirJLkcETDeTq4ol4d3NI7I4vEDgJ8MGOOYLpGkTLcgUMAalgM4a2gB4lR0GcpJFlihMfiv1gylTg+ewygDQxGVqsUITaE5VHcqUY/Mutvs6xlcuqTOuQvqpF4A8DZqlEvG6TmwlY7BLcTVAfTR4b9jmp3kZYBwglBPQBWcg2KsiDBrKSxFKDkVmk6ML5PLYk66IIqUdZvLfP3OebzouTvBOfCAU/DCp29dRK0ulIpKM4iB+j6o90NxMfiMWaGew4runOvPGEWA3MSoGLoHN7Fo407qG8YoZSMogjeOmeQkZ+YF1bpw5uoq11wySKGY8+AjnXzr3j6YepxgllqoaIdTlGarDkix2MVSMZxkgchkvfu9p5Dm8OwrB+gqerbvjPnwp5bzpW/Np1hQmkYAhdENiikIUR9oDihtySskOazqyXj1+SN0dTqSXOjcv55F629h4ZavMbLvPHZkF854wAAIEEnGzApqdeG6Zwzwx7+0nr7+KgA+t/zXF5bwp+87FVUQYTZaVEil7Aqp0Gx5Kn0q2iMEzeCsMjQS8bt/dwanrajS1ZGxY0+BHftiSoUWaO0FfAIjDygda4TiQsCAekBpeQqoQuohtvC0FXWuOzOh23lKOx5iybovMW/7nZjaCDgldzGaGyBnmoX15ibIcmHZooTf/7kN9PXXIHEAGKf85Et2sHZjF5/48kLKRWUWKuYq/S5XodmiBSxTxRA0jXOKKjy8pYRXcJaW+j++GPAJjD6kJAegtERw3YCACCBMqyQFryCcGCOTVYqU8+alXLLSc3b3CL0772X+uluYt+MuSKtgY4hKIAknWyQpCDMkSFK49OxhFiyoQ2p5VCYQw9UXD/DpWxfgVTCizDZZzDKXxTSdKIsImk4ECrHSsgwNtT1Q36+4TiZKMDGIZVqoghG48oIaXR0e709s0aJgPf2dwpI+Yb4ZZuHOO5l3+0307F4LeQKu0AiYZookm8FLNQNVsJYnZI1iBJTZyaALnUFpNoWFBMExEgsopCOQDiso00YVIgfXvniMJfMzslxO6A6y3BZw4weYt/l7LHz4Zsr714PPwcbgirSCiJRg5hRiuOehLoYGY3r765AYGqwCyvfu76Val7Y9OvMqpBkgEFsQUQ6nqgucqtJsgixUjkMQCIgw7VTBRAoGqpkhzzluOvVdL8WRvSzcfBuLHrmF0uBmwICNwDlahU51NJEoiiBo697Fl4AIFCMQ0baagW7eUeQvPria3/+FDZQ6EiYJX7x1CZ+6ZRHFuH1DxpmcS864jyQp8sPtZ4OCCIeRBQ4kdDRBMAWFOAJrAeW4eBujYhoPWC7a8DUWTlQ8uhtEwBVpRYpgG0GTkmhMK8q9UIrrXHfhbVSTIt966Kq2m2cUC8r/3rKQrbtKPPfKA8RFz9p1nXzl2wtIMmmEUbtRBMj5rZf9Ba+6+uNkueNvPverfOjrr8VZEJQpCwwtwAvzaAFBoEAhVkSUYyIG7woTVaRzYCNrbn8PF375d1l+z38Sj+8DVwAb08ocOZFkKK3He8GahD985bv48zf8Cn//c2/iTS/8F9KMtlOMlbse6OLd71nN2/7xVP7nqwvJPW0ZMgB5Dsv79/LyKz4DanBRwque/t/0livknkkCqvQ6VZpOlE6EpgsCVaEQ+R89nBUhtwWMz+je+wBLHvoK87bdga0NgW0M+NvmP6+TqaBRQGgpaQ5nLNrNtRfcArlDTMZLLv8c//7V/0s9LSCibTav0YmaHbsXIjBaLbN/tJ8lS9aDydk1uIRa6jBMUUAoO4QmE2PQDqX5gkAV4lgx5skDxmZ1+rd9n8WP3Dy5opxUplaUy7QbQ44lR2k91sC+kXls3HsKZ53+PQAe2nEmlSTGGaVdCe3PGmVgrIff/eg7ef1zPkStXubfbv55amlMZJXDFB1NJlBSKNMigiCOwIg+ZoPM2xiXjLNg27dY/PBN9O66D/LkUAfTpiweR9a6DxOPd/JHn/wDXnfNR6kmZT74tRtRFUBpriCyyh3rL+LeTRfhFTIPBacohwiUndBcAkWFDlpAEKhCIVKMARTUOHIbE9cGGwP+JetuomP/w+CzyYBxRdqZIhjxWPEorSl2yg+3nslvfPhtKFBwEFtFCVrlfx+vgsjke+UxSo4mU7QAlFrlvFoEQAnmcNDECs6R2YjC6B7mb/oWi9Z/jfLABkDAxuAcs4WhETQt/zezyAkCQKuFTGBEeRKFVgiaSJC4Fb5l0hqmtkAEZxQlmItBExUs5epeFjz8NRZMBExhZCeIAVdkNjIohpxWJyhtKYgdTScRUKCJ0ly45uzvc8MVn2HX/uV88OuvZ6xWxhqCOSazcNaBW7ngy/+AHRoEE4ErMnsJIh5BUYJgRkSOJhOIgJgmSTJhzaJt/PXP/Bod3XvBZhSiOn/26bdgjRLMLWpgyeiDWLsbXC+znQIGj8UTBDMkNjRbk4Mm97Codw8dPXvx1R5IC5y9/CGMIZhjFCESiF0GJmKuEJTIJAhBMCMiQ/NZwNEkkYWHdpzO/euejimNkGcFPvv9G8g9wRwUCZRNDRDmCgVKUiMIZog1NJmCaf6efg9vfv/f8Jvv/1ve+I8f4LN3Xk/BKcEcXAQwSperg86loBFKUscQBDPCOppMwLTCQ2EHRnv57B3XYQRiByIEc4wCkcnocFVQw9whlEwVEYJgJhhH85lWeQLZWYK5HjSS02Frc6yjgYIkCMz0VwUEIWjCtT9B4CSnaOqQC8dFOUQf53OD8BjKkUQopoZC6ojE84QElEkqAPrYzwIKqChPToglQVAUYZoFgTiCIGhQwJFRliqoHBkEhz4fWQgIYISalamC1CmpUfKp8gdLFDUKoiQGcgEz9T4VsCi5y/lObZBNtW3E3gNgVbC5AcB4wXmDUcH6g2UO/djUZ+uFKDdEmcUdfM0doqACioKAF300hIxRSmSIF1QABESZLkHgaD5PEDSjiVYQBXJQD5qDoYDzOVVxJNaS2cmQGImVfSXYV1b2d+SMFXOqsSePcnKXkUQ5SexJo4xarNScklgltZ7MKrmdChvjwSgCgGJQhElGPE48t4449td3gXiekBfIDW6qotzi/KHPcWYppG6ibKNKqaNUjykljnI9oqtaaFR3NaazbqknVTSuQx4juWASAQU1gAGVqfcCoByHIPBy0Us9TXY5cAfTLQhUjggSD6iZLBHAebLOAbRnN653F65zP/0927h26acYLtXY36GMFjOSQoZzKc5mWJtOdSegoqiAoFgFg2JRppoWBBAVBEBpECbpobA7QiQ5Xxw+n021hSA5T0oO/0OPei+H/7wCwmFnaeAFUcGoNDqfqNJFPj4fV+0iGu8hGp2HrXQTjfcRjfbhKj1EI/3YpAAK4gEFODqIlCA4StoKQXMpcCdB8BSJCuoBD6qThUIeQVauYnp2E3Xtw3bvxS7YSLz0IQp92ykVR+gojBAVhzGFCiauYiQn80UiBTNVqIAeHg7CTIkl40vD57GpthgkY8Yc3ZmIB+NB/KH3KpAWkaSAyQrYtIAb7yUeWExxcDHx4BKikXlElV5cpQs31odNQAVo1KH3agCUYE6qt0LQXAzczY8UBALKZOVANvVSBDqHiUpDaP827Mr7KC9eR0fXHsqd+3A9uxvdSsHVKOIxKqgaUEER8FPvMYcCpUkKJuNLQ+excSpomk50KnwA/NRnPRRGWYSt9GCrnUQHQ2hwEcV9KylNVGFwSePHG1W3iIJaUAMqc+gYLqi2QtBcANwFOILgKOIFMvAecgsaK6Y4Sr70YeIV99HZu43SvK24JQ9T7N1BVzxKwSYgHgGMN6h34C2qgkdoYY2g+crQuayvLQHJaHmHh47kIBw6R0sj4pEFFBrdz2KK+1dQ2rOK4oEVuGoHkllMBmqYCiAlmJVGWyFozgLuJHz5WaACGaiCB0Qg7R6Fxesp9m+iOBEm8cp76Vn4CD3lfdjiGNYmRCqQOdQ7vBoUoV0VJOOmkXN4pLoEJKetiYLJD1UeIUkRk5Qp7l9KefepFPespjiwbOLzcqKxTlAmayp8VJS2Fww4mk1JEeohaOYgL6Cg+WRlRWDRdgrzdhCvvpPO075Lb/8GCr27iMqDlCTD5g4/FShkEaQxGS0pfO+KCuSuUQ2iqE3Jy0OMrz7A+Jp7AEEqXVPHbkvo2H4GHdvOojjxPh5eiK2aqW4HkDbteoKkFTqalcD3gCUEs5yABzLIPeRlj+3eg5x6Nx2n3E7vwocpLn2Acu8OOkyCqIC3jdKJ8gizXUEybhk5i4eqy0By5gSTH7aIoKBgR+dT3L+M0t5T6Nx6Dh07zsCN9+CqEQqoa6MFg2Cro9lEE1TqBLOTCqTgFdSBdowhp9xF54r76FjzXbpW3k1v+QDW1bCAZDE+dXgi5iIFDMqc4m2jDpeXhxk/ZT/jp/6A/Zd/HlProLTnFDq3nUV55xl0bD+LaLQbyQUEvANEaUlB6mg2lQSoE8wScsQDkHkBdPWDlFffQ/ncm5i/9Id09OxqrBTH3qBZAX/wtd5BTgAg4W+YR4WP4uMq46fcx/iauyEpNVapO3aeRtfGi+nYfnqj87F1QQ2TZQGUlhDUHU0nVdBx2legAgpkkCn4vmGi+ZspnPtV+k+/je5l99PRtZeCF/zUBpjWOsgQfrQQNIGACqQxEIN4su79DPfuYfi8b2LGeynuW9E4Yut+5DJKe1cRjXaCCGpb4IgtqDiaTJWawChCOwoBk0BmQDoqmDV3MO/8r9B12rfoXfQwHa6KKJDF+EawBNNwnXmgBvKD5QAa3U5l5QNUVq1l7zM+RXEiaDq2nU3PI5fSsfVcXKWMeEGjJm2xBVVHk4moIlRQ2kAgXvAZKJCX69izv0fvWd+k57yvMH/eBuLiKDaL8AfDJS8TnNyts3DMBrVFm6ktXc+BS25q3GLQs/5SujZdSOeW83DjRZCT+dxOIFBxQvPFhtFaDkLQumuqoBkkJcWdeg+dl36KBWd+na4F6ykXRjBpgdw7tNZJRvDUCQYFlOApyqLJEiXp38m+ieDZd8XnKexfTvdE6PSuu7KxUOCqFu9m+o62wAvjcuHLPE2n/JvAz9F6QsDUIY3ALNxG8cIv0H/el+k75Xa641HwDs0ivFqmRxBLzu3jp3DX2GoQzzQJxIPNwWaQxo2g6XnoSvoefAaFgcVIBj4KgTMTBD7ojNJ0CntoHUEm5DloZ43okptYfP5EuJzzVXo792BRNC2Q1ztpG+HoLFADmXm006mseIDKyrXsuep/6N54EX1rr6Z7/WW4aoQ3oE6ZHoHCPqe0At0DQhMFXtAMvIJfvIPOi77I/Cs+2lhHLkZVSErkB4tgJikgBDP/bFfh0UWCofO/ztDZ36G4dzXz7ns2vQ9dSXHfYhBQd+ILBIHucaA0ncouhGYJAZNAVlSiM+6m7+kfZdE5N9HTvwmXOfI8Jq91cvIERjwnSaAGkhKIUlu8kZ3L1rH3Gf9L9yNX0H/v8+ncdiYmOaGNtUDNbocamk2F3YJyEgVe8An4jhR74VdZds0HWXjm1+hwFchj/Jwb6oejs3Cha9SorGOYgUu/yMAFtza21frveR49D18xeawWh8A5XiLsdSI0XVHYU1e8giGYWbngM8g7a5Se/jn6n/FhFq2+nZKtomk5zF5C0AS5myxRRs+4g9E1dzeuvOm/+zp6H3wG0VgBHx3renSQ5exxWU7TeWHYGPYDCwlmjgc/byelSz7L4qf/J/1L7ydWJc+K5HkHrSEwBK3zQHIRUMZXPNCovU/7DP33PJ++tc/AjfeBEDy5cTE6+P8BW01E00mieO8AAAAASUVORK5CYII=) no-repeat 150% 100%;
-      background-color: #2238b3;
-      background-size: 205px;
-      background-blend-mode: luminosity;
-    }
-    .leftnav__header__title {
-      font-family: var(--text-font-family);
-      font-size: var(--title-font-size);
-      line-height: var(--title-line-height);
-      font-weight: 300;
-      color: #fff;
-      margin: 0;
-      padding: 0;
-    }
-    .leftnav__header__version {
-      color: #aab3ed;
-      font-family: var(--text-font-family);
-      font-size: var(--body-font-size);
-      line-height: var(--body-line-height);
-    }
-    @media screen and (max-width: 964px) {
-      .lh-leftnav {
-        display: none;
-      }
-    }
-    @media print {
-      .lh-leftnav {
-        display: none;
-      }
+    .lh-scores-container {
+      padding-bottom: calc(var(--section-indent) / 2);
+      position: relative;
+      width: 100%;
     }
   </style>
-  <nav class="lh-leftnav">
-    <div class="leftnav__header">
-      <h1 class="leftnav__header__title">Lighthouse</h1>
-      <div class="leftnav__header__version"><!-- fill me --></div>
+  <div class="lh-scores-wrapper">
+    <div class="lh-scores-container">
+      <div class="lh-scores-wrapper__background"></div>
+      <div class="lh-scores-wrapper__shadow"></div>
     </div>
-    <template id="tmpl-lh-leftnav__items">
-      <a href="#" class="lh-leftnav__item">
-        <span class="leftnav-item__category"><!-- fill me --></span>
-        <span class="leftnav-item__score"><!-- fill me --></span>
-      </a>
-    </template>
-  </nav>
+  </div>
 </template>
 
+
 <!-- Lighthouse header -->
 <template id="tmpl-lh-heading">
   <style>
     :root {
-      --report-header-height: 58px;
-      --report-header-bg-color: #fafafa;
+      --report-header-overlap-top: 30px;
     }
-    .lh-header {
-      display: flex;
+    .lh-header-bg {
+      background-color: var(--header-bg-color);
       height: var(--report-header-height);
       left: 0;
-      right: 0;
-      max-width: 100%; /* support text-overflow on url */
-      border-bottom: 1px solid var(--report-secondary-border-color);
-      position: fixed;
-      z-index: 1;
+      position: absolute;
+      top: 0;
+      width: 100%;
       will-change: transform;
-      background-color: var(--report-header-bg-color);
-      margin-left: var(--report-menu-width);
-      align-items: center;
-      padding: 0 calc(var(--default-padding) * 2);
+    }
+    .lh-lighthouse {
+      position: absolute;
+      top: var(--report-header-height);
+      right: 50%;
+      transform: translate3d(calc(var(--report-content-width) / 2), -100%, 0);
+      opacity: 1;
+      transform-origin: bottom right;
+      will-change: transform, opacity;
+    }
+    .lh-header {
+      width: 100%;
+      height: var(--report-header-height);
+      max-width: 100%; /* support text-overflow on url */
+      position: relative;
     }
     .lh-metadata {
       flex: 1 1 0;
-      padding-right: calc(var(--default-padding) / 2);
+      padding: calc(var(--section-padding) / 2);
+      padding-left: var(--section-indent);
       line-height: 20px;
-      color: var(--secondary-text-color);
-      overflow-x: hidden;
+      color: var(--report-header-color);
+      z-index: 1;
+      position: relative;
     }
     .lh-metadata__results {
-      overflow: hidden;
       text-overflow: ellipsis;
       white-space: nowrap;
     }
     .lh-metadata__url {
       color: currentColor;
     }
+    .lh-scores-wrapper {
+      margin-top: -30px;
+      transform: translateZ(1px);
+    }
+    .lh-scores-wrapper__shadow {
+      opacity: 0;
+    }
+    .lh-scores-wrapper__background,
+    .lh-scores-wrapper__shadow {
+      box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.1);
+      border-radius: 8px;
+      will-change: opacity, transform;
+      transform-origin: top;
+    }
+
+    .lh-product-info, .lh-toolbar__metadata {
+      align-items: center;
+      white-space: nowrap;
+      color: #5F6369;
+      display: flex;
+      font-size: calc(var(--body-font-size) * 0.9);
+      margin-left: var(--section-indent);
+      opacity: 0;
+      transform: translateY(-50%);
+      will-change: opacity;
+    }
+    .lh-product-info__icon {
+      height: 20px;
+      margin-right: var(--default-padding);
+    }
+    .lh-toolbar {
+      height: 50px;
+      position: absolute;
+      top: 25px;
+      will-change: transform;
+      display: flex;
+      width: calc(100% - 70px);  /* give room for export */
+    }
+    .lh-toolbar__metadata {
+      overflow: hidden;
+      text-overflow: ellipsis;
+      width: 100%;
+    }
+    .lh-toolbar__url {
+      color: currentColor;
+      display: block;
+      white-space: nowrap;
+      margin-right: 2px;
+    }
     .lh-export {
-      position: relative;
+      position: absolute;
+      right: var(--section-indent);
+      transform: translateY(0);
+      top: calc(var(--section-padding) / 2);
+      will-change: transform;
+      z-index: 2;
     }
     .lh-export__button {
       background-color: #fff;
-      border: 1px solid var(--report-border-color);
-      border-radius: 3px;
+      border: 1px solid #dadada;
+      border-radius: 2px;
       cursor: pointer;
       outline: none;
       height: 32px;
@@ -194,6 +246,7 @@
       background-repeat: no-repeat;
       background-size: 20px;
       background-position: 50% 50%;
+      will-change: transform;
     }
     .lh-export__button:focus,
     .lh-export__button.active {
@@ -249,68 +302,21 @@
       display: none;
     }
     .lh-config {
-      display: flex;
-    }
-    .lh-env {
-      padding: var(--default-padding) 0 var(--default-padding) calc(var(--default-padding) * 2);
-      left: 0;
-      top: 100%;
-      position: absolute;
-      width: 100%;
-      background-color: var(--report-header-bg-color);
-      border-top: 1px solid var(--report-secondary-border-color);
-      border-bottom: 1px solid var(--report-secondary-border-color);
-    }
-    .lh-env__title {
-      font-size: var(--header-font-size);
-    }
-    .lh-env__items {
-      margin: var(--default-padding) 0 0 0;
-    }
-    .lh-config__timestamp {
-      margin-right: 6px;
-    }
-    .lh-config__settings-toggle {
-      margin-left: 6px;
-    }
-    .lh-config__timestamp,
-    .lh-config__settings-toggle summary {
       color: var(--secondary-text-color);
     }
-    .lh-config__settings-toggle summary {
-      display: flex;
-      align-items: center;
+    .lh-config__timestamp {
+      font-size: var(--caption-font-size);
+      display: block;
     }
-    .lh-config__settings-toggle .lh-toggle-arrow {
-      width: 16px;
-      height: 16px;
-      margin-left: 2px;
-    }
-    .lh-config__settings-toggle[open] .lh-toggle-arrow {
-      transform: rotateZ(90deg);
-    }
-    .lh-config__settings-toggle summary::-moz-list-bullet {
-      display: none;
-    }
-    .lh-config__settings-toggle summary::-webkit-details-marker {
-      display: none;
-    }
-    @media screen and (min-width: 965px) {
-      .lh-header {
-        width: var(--report-width);
-        right: initial;
-        left: initial;
-      }
+    a.lh-config__emulation {
+      color: inherit;
+      text-decoration: none;
     }
     @media screen and (max-width: 964px) {
       .lh-export__dropdown {
         right: 0;
         left: initial;
       }
-      .lh-header {
-        padding: 0 var(--default-padding);
-        margin-left: 0;
-      }
     }
     @media print {
       .lh-header {
@@ -318,36 +324,73 @@
         margin-left: 0;
       }
     }
+/*
+    TODO: Enable animating the clouds
+    .lh-lighthouse__clouds {
+      animation: panacross 30s linear infinite;
+      animation-play-state: paused;
+    }
+    @keyframes panacross {
+      0% { transform: translateX(0px); }
+      77% { transform: translateX(-680px); }
+      77.0001% { transform: translateX(195px); }
+      100% { transform: translateX(0px); }
+    } */
   </style>
-  <div class="lh-header">
-    <div class="lh-metadata">
-      <div class="lh-metadata__results">Results for: <a href="" class="lh-metadata__url" target="_blank" rel="noopener"><!-- fill me --></a></div>
-      <div class="lh-config">
-        <span class="lh-config__timestamp"><!-- fill me --></span> &bullet;
-        <details class="lh-config__settings-toggle">
-          <summary>
-            <span>Runtime settings</span>
-            <span class="lh-toggle-arrow" title="See report's runtime settings"></span>
-          </summary>
-          <div class="lh-env">
-            <div class="lh-env__title">Runtime environment</div>
-            <ul class="lh-env__items">
-              <li class="lh-env__item">
-                <span class="lh-env__name">User agent:</span>
-                <b class="lh-env__item__ua"><!-- fill me --></b>
-              </li>
-              <template id="tmpl-lh-env__items">
-                <li class="lh-env__item">
-                  <span class="lh-env__name"><!-- fill me --></span>
-                  <span class="lh-env__description"><!-- fill me --></span>:
-                  <b class="lh-env__enabled"><!-- fill me --></b>
-                </li>
-              </template>
-            </ul>
-          </div>
-        </details>
+  <div class="lh-header-bg"></div>
+  <div class="lh-lighthouse">
+    <svg width="217" height="148" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+      <defs>
+        <mask id="a" x="-56" y="-54" width="284" height="202" maskUnits="userSpaceOnUse">
+          <path d="M-56-54h284v202H-56z" fill="#fff"/>
+        </mask>
+        <linearGradient id="b" x1="-525.16" y1="560.08" x2="-524.23" y2="560.08" gradientTransform="matrix(91 0 0 -77 47797 43181)" gradientUnits="userSpaceOnUse">
+          <stop offset="0" stop-color="#f1f3f4"/>
+          <stop offset="1" stop-color="#fff"/>
+        </linearGradient>
+      </defs>
+      <g mask="url(#a)">
+        <path d="M95 47h24v2H95z" fill="#ec5548"/>
+        <path d="M98 49h18v11H98z" fill="#fbc21b"/>
+        <path d="M95 59h24v7H95z" fill="#ec5548"/>
+        <path d="M97.63 66h19.74l2.63 47H95z" fill="#fff"/>
+        <path d="M107 38a10 10 0 0 1 10 10v1H97v-1a10 10 0 0 1 10-10zM96.77 82.23l21-10.7.63 11.87-22.31 11.87zM95 110.8L119.1 98l.9 14H95z" fill="#ec5548"/>
+        <path d="M0 148a177.58 177.58 0 0 1 217 0z" fill="#e8eaed"/>
+        <path d="M103 49a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5z" fill="#fef0c8"/>
+        <path d="M7 16l91 33.18v10L7 93z" fill="url(#b)"/>
+      </g>
+      <g mask="url(#a)" class="lh-lighthouse__clouds">
+        <path d="M60 .19A9.77 9.77 0 0 1 61.93 0a9.44 9.44 0 0 1 9.24 7.83A7.24 7.24 0 0 1 79 14.45v.73A7.37 7.37 0 0 1 76.2 21h-31a7.44 7.44 0 0 1-1.2-4.09 7.31 7.31 0 0 1 7.26-7.36 6.84 6.84 0 0 1 1.28.1v-.11A9.51 9.51 0 0 1 60 .19m79.78 22.31h-17.9a4.37 4.37 0 0 1-.63-2.25 4.2 4.2 0 0 1 4.16-4.25 4.37 4.37 0 0 1 .72.06V16a5.35 5.35 0 0 1 10.64-1h.33a4.2 4.2 0 0 1 4.15 4.25 4.29 4.29 0 0 1-1.47 3.25zM163 62h-24.15a5.1 5.1 0 0 1-.85-2.81 5.65 5.65 0 0 1 6.59-5.19v-.08a7.07 7.07 0 0 1 7.24-6.92 7.15 7.15 0 0 1 7.17 5.64h.44a5.46 5.46 0 0 1 5.6 5.32A5.19 5.19 0 0 1 163 62z" fill="#fff"/>
+      </g>
+    </svg>
+  </div>
+
+  <div class="lh-header-container">
+    <div class="lh-header">
+      <div class="lh-metadata">
+        <div class="lh-metadata__results"><a href="" class="lh-metadata__url" target="_blank" rel="noopener"></a></div>
+        <div class="lh-config">
+          <span class="lh-config__timestamp"></span>
+          <a href="#runtime-settings" class="lh-config__emulation"></a>
+        </div>
       </div>
     </div>
+
+    <div class="lh-scores-wrapper-placeholder"></div>
+
+    <div class="lh-toolbar">
+      <div class="lh-product-info">
+        <img src="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==" alt="" class="lh-product-info__icon" />
+        <span class="lh-product-info__name">Lighthouse</span>&nbsp;
+        <span class="lh-product-info__version"></span>
+      </div>
+
+      <div class="lh-toolbar__metadata">
+        <a href="" class="lh-toolbar__url" target="_blank" rel="noopener"></a>
+        <span class="lh-toggle-arrow" title="See report's runtime settings"></span>
+      </div>
+    </div>
+
     <div class="lh-export">
       <button class="report-icon report-icon--share lh-export__button" title="Export report"></button>
       <div class="lh-export__dropdown">
@@ -360,200 +403,227 @@
         <a href="#" class="report-icon report-icon--open lh-export--gist" data-action="save-gist">Save as Gist</a>
       </div>
     </div>
-  </div>
 </template>
 
+
 <!-- Lighthouse footer -->
 <template id="tmpl-lh-footer">
   <style>
     .lh-footer {
-      min-height: 90px;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      background-color: var(--report-header-bg-color);
+      background-color: var(--header-bg-color);
       border-top: 1px solid var(--report-secondary-border-color);
+      padding: var(--section-indent) calc(var(--default-padding) * 2);
     }
-
-    .lh-footer span {
+    .lh-footer .lh-generated {
       text-align: center;
+      border-top: 1px solid var(--report-border-color);
+      padding-top: var(--default-padding);
+    }
+    .lh-env {
+      padding: var(--default-padding) 0;
+    }
+    .lh-env__items {
+      padding-left: 16px;
+    }
+    span.lh-env__name {
+      font-weight: bold;
+      color: var(--secondary-text-color);
+    }
+    span.lh-env__description {
+      font-family: var(--monospace-font-family);
+      font-size: var(--caption-font-size);
+      padding-left: 5px;
     }
   </style>
   <footer class="lh-footer">
-    <span>
-      Generated by <b>Lighthouse</b> <span class="lh-footer__version"><!-- fill me --></span> on
-      <span class="lh-footer__timestamp"><!-- fill me --></span> |
+    <div class="lh-env">
+      <div class="lh-env__title">Runtime settings</div>
+      <ul class="lh-env__items">
+        <template id="tmpl-lh-env__items">
+          <li class="lh-env__item">
+            <span class="lh-env__name"></span>
+            <span class="lh-env__description"></span>
+          </li>
+        </template>
+      </ul>
+    </div>
+
+    <div class="lh-generated">
+      Generated by <b>Lighthouse</b> <span class="lh-footer__version"></span> |
       <a href="https://github.com/GoogleChrome/Lighthouse/issues" target="_blank" rel="noopener">File an issue</a>
-    </span>
+    </div>
   </footer>
 </template>
 
 <!-- Lighthouse score gauge -->
 <template id="tmpl-lh-gauge">
   <style>
-    .lh-gauge {
-      --circle-size: calc(2.5 * var(--header-font-size));
+    .lh-gauge__wrapper {
+      --circle-size: calc(3 * var(--header-font-size));
       --circle-size-half: calc(var(--circle-size) / 2);
-      --circle-background: #ccc;
-      --circle-border-width: 2px;
+      --circle-background: hsl(216, 12%, 92%);
+      --circle-border-width: 9;
       --inset-size: calc(var(--circle-size) - 2 * var(--circle-border-width));
-      --inset-color: #fff;
       --transition-length: 1s;
-      width: var(--circle-size);
-      height: var(--circle-size);
-      background-color: var(--circle-background);
-      border-radius: 50%;
     }
-    .lh-gauge--pass {
+
+    .lh-gauge__wrapper--pass,
+    .lh-gauge__wrapper--pass .lh-gauge {
       --circle-color: var(--pass-color);
       color: var(--circle-color);
     }
-    .lh-gauge--average {
+
+    .lh-gauge__wrapper--average,
+    .lh-gauge__wrapper--average .lh-gauge {
       --circle-color: var(--average-color);
       color: var(--circle-color);
     }
-    .lh-gauge--fail {
+
+    .lh-gauge__wrapper--fail,
+    .lh-gauge__wrapper--fail .lh-gauge {
       --circle-color: var(--fail-color);
       color: var(--circle-color);
     }
-    .lh-gauge__mask,
-    .lh-gauge__fill {
-      width: var(--circle-size);
-      height: var(--circle-size);
-      position: absolute;
-      transition: transform var(--transition-length);
-      border-radius: 50%;
+
+    .lh-gauge {
+        max-width: 360px;
+        max-height: 360px;
+        stroke-linecap: round;
+        width: var(--circle-size);
+        height: var(--circle-size);
     }
-    .lh-gauge__mask {
-      clip: rect(0px, var(--circle-size), var(--circle-size), var(--circle-size-half));
+
+    .lh-gauge-base {
+        fill: none;
+        stroke: var(--circle-background);
+        stroke-width: var(--circle-border-width);
     }
-    .lh-gauge__mask .lh-gauge__fill {
-      clip: rect(0px, var(--circle-size-half), var(--circle-size), 0px);
-      background-color: var(--circle-color);
-      backface-visibility: hidden;
+    .lh-gauge-arc {
+        fill: none;
+        stroke: var(--circle-color);
+        stroke-width: var(--circle-border-width);
+        animation: load-gauge var(--transition-length) ease forwards;
+        animation-delay: 250ms;
     }
+
+    @keyframes load-gauge {
+      from { stroke-dasharray: 0 329; }
+    }
+
     .lh-gauge__percentage {
       --spacer: calc((var(--circle-size) - var(--inset-size)) / 2);
       width: var(--inset-size);
       height: var(--inset-size);
       position: absolute;
-      margin-left: var(--spacer);
-      margin-top: var(--spacer);
-      background-color: var(--inset-color);
       border-radius: inherit;
-      display: flex;
-      align-items: center;
-      justify-content: center;
       font-size: var(--header-font-size);
+      text-align: center;
+      top: calc(var(--circle-size) / 3);
     }
+
     .lh-gauge__wrapper {
       display: inline-flex;
       align-items: center;
       flex-direction: column;
       text-decoration: none;
-      color: inherit;
       flex: 1;
       min-width: auto;
       position: relative;
+
+      /* Contain the layout style paint & layers during animation*/
+      contain: content;
+      will-change: opacity; /* Only using for layer promotion */
     }
-    .lh-scores-header .lh-gauge__wrapper {
-      width: calc(10.5 * var(--body-font-size));
-    }
+
     .lh-gauge__label {
       font-size: var(--body-font-size);
       line-height: var(--body-line-height);
       margin-top: calc(0.5 * var(--body-line-height));
       text-align: center;
+      color: black;
     }
+
   </style>
   <a href="#" class="lh-gauge__wrapper">
-    <div class="lh-gauge" data-progress="0">
-      <div class="lh-gauge__circle">
-        <div class="lh-gauge__mask lh-gauge__mask--full">
-          <div class="lh-gauge__fill"></div>
-        </div>
-        <div class="lh-gauge__mask lh-gauge__mask--half">
-          <div class="lh-gauge__fill"></div>
-          <div class="lh-gauge__fill lh-gauge__fill--fix"></div>
-        </div>
-      </div>
-      <div class="lh-gauge__percentage"></div>
-    </div>
-    <div class="lh-gauge__label"><!-- fill me --></div>
+    <svg viewBox="0 0 120 120" class="lh-gauge" fill="none" stroke-width="2">
+      <circle class="lh-gauge-base" r="53" cx="60" cy="60"></circle>
+      <circle class="lh-gauge-arc" transform="rotate(-90 60 60)" stroke-dasharray="0 329" stroke-dashoffset="0" r="53" cx="60" cy="60"></circle>
+    </svg>
+    <div class="lh-gauge__percentage"></div>
+    <div class="lh-gauge__label"></div>
   </a>
 </template>
 
 <!-- Lighthouse crtiical request chains component -->
 <template id="tmpl-lh-crc">
-  <style>
-    .lh-crc .tree-marker {
-      width: 12px;
-      height: 26px;
-      display: block;
-      float: left;
-      background-position: top left;
-    }
-    .lh-crc .horiz-down {
-      background: url('data:image/svg+xml;utf8,<svg width="16" height="26" viewBox="0 0 16 26" xmlns="http://www.w3.org/2000/svg"><g fill="%23D8D8D8" fill-rule="evenodd"><path d="M16 12v2H-2v-2z"/><path d="M9 12v14H7V12z"/></g></svg>');
-    }
-    .lh-crc .right {
-      background: url('data:image/svg+xml;utf8,<svg width="16" height="26" viewBox="0 0 16 26" xmlns="http://www.w3.org/2000/svg"><path d="M16 12v2H0v-2z" fill="%23D8D8D8" fill-rule="evenodd"/></svg>');
-    }
-    .lh-crc .up-right {
-      background: url('data:image/svg+xml;utf8,<svg width="16" height="26" viewBox="0 0 16 26" xmlns="http://www.w3.org/2000/svg"><path d="M7 0h2v14H7zm2 12h7v2H9z" fill="%23D8D8D8" fill-rule="evenodd"/></svg>');
-    }
-    .lh-crc .vert-right {
-      background: url('data:image/svg+xml;utf8,<svg width="16" height="26" viewBox="0 0 16 26" xmlns="http://www.w3.org/2000/svg"><path d="M7 0h2v27H7zm2 12h7v2H9z" fill="%23D8D8D8" fill-rule="evenodd"/></svg>');
-    }
-    .lh-crc .vert {
-      background: url('data:image/svg+xml;utf8,<svg width="16" height="26" viewBox="0 0 16 26" xmlns="http://www.w3.org/2000/svg"><path d="M7 0h2v26H7z" fill="%23D8D8D8" fill-rule="evenodd"/></svg>');
-    }
-    .lh-crc .crc-tree {
-      font-size: 14px;
-      width: 100%;
-      overflow-x: auto;
-    }
-    .lh-crc .crc-node {
-      height: 26px;
-      line-height: 26px;
-      white-space: nowrap;
-    }
-    .lh-crc .crc-node__tree-value {
-      margin-left: 10px;
-    }
-    .lh-crc .crc-node__chain-duration {
-      font-weight: 700;
-    }
-    .lh-crc .crc-node__tree-hostname {
-      color: #595959;
-    }
-    .lh-crc .crc-initial-nav {
-      color: #595959;
-      font-style: italic;
-    }
-  </style>
-  <div class="lh-score__description">
-    Longest chain: <b class="lh-crc__longest_duration"><!-- fill me: longestChain.duration --></b>
-    over <b class="lh-crc__longest_length"><!-- fill me: longestChain.length --></b> requests, totalling
-    <b class="lh-crc__longest_transfersize"><!-- fill me: longestChain.length --></b>
-  </div>
-  <div class="lh-crc">
-    <details class="lh-details">
-      <summary><!-- fill me --></summary>
+  <div class="lh-crc-container">
+    <style>
+      .lh-crc .tree-marker {
+        width: 12px;
+        height: 26px;
+        display: block;
+        float: left;
+        background-position: top left;
+      }
+      .lh-crc .horiz-down {
+        background: url('data:image/svg+xml;utf8,<svg width="16" height="26" viewBox="0 0 16 26" xmlns="http://www.w3.org/2000/svg"><g fill="%23D8D8D8" fill-rule="evenodd"><path d="M16 12v2H-2v-2z"/><path d="M9 12v14H7V12z"/></g></svg>');
+      }
+      .lh-crc .right {
+        background: url('data:image/svg+xml;utf8,<svg width="16" height="26" viewBox="0 0 16 26" xmlns="http://www.w3.org/2000/svg"><path d="M16 12v2H0v-2z" fill="%23D8D8D8" fill-rule="evenodd"/></svg>');
+      }
+      .lh-crc .up-right {
+        background: url('data:image/svg+xml;utf8,<svg width="16" height="26" viewBox="0 0 16 26" xmlns="http://www.w3.org/2000/svg"><path d="M7 0h2v14H7zm2 12h7v2H9z" fill="%23D8D8D8" fill-rule="evenodd"/></svg>');
+      }
+      .lh-crc .vert-right {
+        background: url('data:image/svg+xml;utf8,<svg width="16" height="26" viewBox="0 0 16 26" xmlns="http://www.w3.org/2000/svg"><path d="M7 0h2v27H7zm2 12h7v2H9z" fill="%23D8D8D8" fill-rule="evenodd"/></svg>');
+      }
+      .lh-crc .vert {
+        background: url('data:image/svg+xml;utf8,<svg width="16" height="26" viewBox="0 0 16 26" xmlns="http://www.w3.org/2000/svg"><path d="M7 0h2v26H7z" fill="%23D8D8D8" fill-rule="evenodd"/></svg>');
+      }
+      .lh-crc .crc-tree {
+        font-size: 14px;
+        width: 100%;
+        overflow-x: auto;
+      }
+      .lh-crc .crc-node {
+        height: 26px;
+        line-height: 26px;
+        white-space: nowrap;
+      }
+      .lh-crc .crc-node__tree-value {
+        margin-left: 10px;
+      }
+      .lh-crc .crc-node__chain-duration {
+        font-weight: 700;
+      }
+      .lh-crc .crc-node__tree-hostname {
+        color: #595959;
+      }
+      .lh-crc .crc-initial-nav {
+        color: #595959;
+        font-style: italic;
+      }
+    </style>
+    <div>
+      Longest chain: <b class="lh-crc__longest_duration"><!-- fill me: longestChain.duration --></b>
+      over <b class="lh-crc__longest_length"><!-- fill me: longestChain.length --></b> requests, totalling
+      <b class="lh-crc__longest_transfersize"><!-- fill me: longestChain.length --></b>
+    </div>
+    <div class="lh-crc">
       <div class="crc-initial-nav">Initial Navigation</div>
       <!-- stamp for each chain -->
       <template id="tmpl-lh-crc__chains">
         <div class="crc-node">
           <span class="crc-node__tree-marker">
-            <!-- fill me -->
+
           </span>
           <span class="crc-node__tree-value">
             <span class="crc-node__tree-file"><!-- fill me: node.request.url.file --></span>
             <span class="crc-node__tree-hostname">(<!-- fill me: node.request.url.host -->)</span>
-            <!-- fill me -->
+
           </span>
         </div>
       </template>
-    </details>
+    </div>
   </div>
 </template>
diff --git a/third_party/blink/renderer/devtools/front_end/audits2/module.json b/third_party/blink/renderer/devtools/front_end/audits2/module.json
index 41ee0851..a99df715 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2/module.json
+++ b/third_party/blink/renderer/devtools/front_end/audits2/module.json
@@ -22,6 +22,7 @@
         "lighthouse/renderer/util.js",
         "lighthouse/renderer/dom.js",
         "lighthouse/renderer/category-renderer.js",
+        "lighthouse/renderer/performance-category-renderer.js",
         "lighthouse/renderer/details-renderer.js",
         "lighthouse/renderer/crc-details-renderer.js",
         "lighthouse/renderer/report-renderer.js",
@@ -40,5 +41,14 @@
         "audits2Panel.css",
         "lighthouse/report-styles.css",
         "lighthouse/templates.html"
-    ]
+    ],
+    "skip_compilation": [
+      "lighthouse/renderer/util.js",
+      "lighthouse/renderer/dom.js",
+      "lighthouse/renderer/category-renderer.js",
+      "lighthouse/renderer/performance-category-renderer.js",
+      "lighthouse/renderer/details-renderer.js",
+      "lighthouse/renderer/crc-details-renderer.js",
+      "lighthouse/renderer/report-renderer.js"
+  ]
 }
diff --git a/third_party/blink/renderer/devtools/front_end/audits2_worker/Audits2Service.js b/third_party/blink/renderer/devtools/front_end/audits2_worker/Audits2Service.js
index f19bee6..be445dd 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2_worker/Audits2Service.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2_worker/Audits2Service.js
@@ -37,7 +37,7 @@
   }
 
   /**
-   * @return {!Promise<!ReportRenderer.ReportJSON>}
+   * @return {!Promise<!ReportRenderer.RunnerResult>}
    */
   start(params) {
     if (Runtime.queryParam('isUnderTest'))
@@ -49,7 +49,7 @@
 
     return Promise.resolve()
         .then(_ => self.runLighthouseInWorker(this, params.url, {flags: params.flags}, params.categoryIDs))
-        .then(/** @type {!ReportRenderer.ReportJSON} */ result => {
+        .then(/** @type {!ReportRenderer.RunnerResult} */ result => {
           // Keep all artifacts on the result, no trimming
           return result;
         })
diff --git a/third_party/blink/renderer/devtools/front_end/audits2_worker/lighthouse/lighthouse-background.js b/third_party/blink/renderer/devtools/front_end/audits2_worker/lighthouse/lighthouse-background.js
index c7b59ac2..21f7dcab 100644
--- a/third_party/blink/renderer/devtools/front_end/audits2_worker/lighthouse/lighthouse-background.js
+++ b/third_party/blink/renderer/devtools/front_end/audits2_worker/lighthouse/lighthouse-background.js
@@ -1,5 +1,5 @@
-// lighthouse, browserified. 2.9.1 (f9171e0133fa0bffd8cd419ac3ec91861d5d5713)
-require=function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f;}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e);},l,l.exports,e,t,n,r);}return n[o].exports;}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s;}({"../audits/accessibility/accesskeys":[function(require,module,exports){
+// lighthouse, browserified. 3.0.0-beta.0 (a194e972771bcf85071a796142d50d5bfbe3cc7b)
+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){
 
 
 
@@ -506,10 +506,10 @@
 return{
 name:'document-title',
 description:'Document has a `<title>` element',
-failureDescription:'Document does not have a `<title>` element',
-helpText:'Screen reader users use page titles to get an overview of the contents of '+
-'the page. '+
-'[Learn more](https://dequeuniversity.com/rules/axe/2.2/document-title?application=lighthouse).',
+failureDescription:'Document doesn\'t have a `<title>` element',
+helpText:'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).',
 requiredArtifacts:['Accessibility']};
 
 }}
@@ -680,7 +680,7 @@
 description:'Image elements have `[alt]` attributes',
 failureDescription:'Image elements do not have `[alt]` attributes',
 helpText:'Informative elements should aim for short, descriptive alternate text. '+
-'Decorative elements can be ignored with an empty alt attribute.'+
+'Decorative elements can be ignored with an empty alt attribute. '+
 '[Learn more](https://dequeuniversity.com/rules/axe/2.2/image-alt?application=lighthouse).',
 requiredArtifacts:['Accessibility']};
 
@@ -925,7 +925,7 @@
 name:'custom-controls-labels',
 helpText:'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 controls have associated labels'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -955,7 +955,7 @@
 name:'custom-controls-roles',
 helpText:'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 controls have ARIA roles'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -985,7 +985,7 @@
 name:'focus-traps',
 helpText:'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:'User focus is not accidentally trapped in a region'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -1015,7 +1015,7 @@
 name:'focusable-controls',
 helpText:'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:'Interactive controls are keyboard focusable'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -1045,7 +1045,7 @@
 name:'heading-levels',
 helpText:'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 don\'t skip levels'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -1075,7 +1075,7 @@
 name:'logical-tab-order',
 helpText:'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:'The page has a logical tab order'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -1105,7 +1105,7 @@
 name:'managed-focus',
 helpText:'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:'The user\'s focus is directed to new content added to the page'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -1136,7 +1136,7 @@
 name:'offscreen-content-hidden',
 helpText:'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 from assistive technology'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -1166,7 +1166,7 @@
 name:'use-landmarks',
 helpText:'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:'HTML5 landmark elements are used to improve navigation'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -1196,7 +1196,7 @@
 name:'visual-order-follows-dom',
 helpText:'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:'Visual order on the page follows DOM order'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -1530,9 +1530,8 @@
 
 const Audit=require('./audit');
 const WebInspector=require('../lib/web-inspector');
-const Util=require('../report/v2/renderer/util');
+const Util=require('../report/html/renderer/util');
 const{groupIdToName,taskToGroup}=require('../lib/task-groups');
-const THRESHOLD_IN_MS=10;
 
 class BootupTime extends Audit{
 
@@ -1540,12 +1539,13 @@
 
 static get meta(){
 return{
-category:'Performance',
 name:'bootup-time',
 description:'JavaScript boot-up time',
 failureDescription:'JavaScript boot-up time is too high',
-helpText:'Consider reducing the time spent parsing, compiling and executing JS. '+
-'You may find delivering smaller JS payloads helps with this.',
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
+helpText:'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).',
 requiredArtifacts:['traces']};
 
 }
@@ -1553,9 +1553,24 @@
 
 
 
+static get defaultOptions(){
+return{
+
+
+scorePODR:600,
+scoreMedian:3500,
+thresholdInMs:50};
+
+}
+
+
+
+
+
 
 static getExecutionTimingsByURL(timelineModel){
 const bottomUpByURL=timelineModel.bottomUpGroupBy('URL');
+
 const result=new Map();
 
 bottomUpByURL.children.forEach((perUrlNode,url)=>{
@@ -1564,6 +1579,7 @@
 return;
 }
 
+
 const taskGroups={};
 perUrlNode.children.forEach(perTaskPerUrlNode=>{
 
@@ -1583,24 +1599,34 @@
 
 
 
-static audit(artifacts){
+
+static async audit(artifacts,context){
+const settings=context.settings||{};
 const trace=artifacts.traces[BootupTime.DEFAULT_PASS];
-return artifacts.requestDevtoolsTimelineModel(trace).then(devtoolsTimelineModel=>{
+const devtoolsTimelineModel=await artifacts.requestDevtoolsTimelineModel(trace);
 const executionTimings=BootupTime.getExecutionTimingsByURL(devtoolsTimelineModel);
 let totalBootupTime=0;
+
 const extendedInfo={};
 
 const headings=[
 {key:'url',itemType:'url',text:'URL'},
-{key:'scripting',itemType:'text',text:groupIdToName.scripting},
-{key:'scriptParseCompile',itemType:'text',text:groupIdToName.scriptParseCompile}];
+{key:'scripting',granularity:1,itemType:'ms',text:groupIdToName.scripting},
+{key:'scriptParseCompile',granularity:1,itemType:'ms',
+text:groupIdToName.scriptParseCompile}];
 
 
+const multiplier=settings.throttlingMethod==='simulate'?
+settings.throttling.cpuSlowdownMultiplier:1;
 
 const results=Array.from(executionTimings).
 map(([url,groups])=>{
 
-totalBootupTime+=Object.keys(groups).reduce((sum,name)=>sum+=groups[name],0);
+for(const[name,value]of Object.entries(groups)){
+groups[name]=value*multiplier;
+totalBootupTime+=value*multiplier;
+}
+
 extendedInfo[url]=groups;
 
 const scriptingTotal=groups[groupIdToName.scripting]||0;
@@ -1610,31 +1636,131 @@
 sum:scriptingTotal+parseCompileTotal,
 
 
-scripting:Util.formatMilliseconds(scriptingTotal,1),
-scriptParseCompile:Util.formatMilliseconds(parseCompileTotal,1)};
+scripting:scriptingTotal,
+scriptParseCompile:parseCompileTotal};
 
 }).
-filter(result=>result.sum>=THRESHOLD_IN_MS).
+filter(result=>result.sum>=context.options.thresholdInMs).
 sort((a,b)=>b.sum-a.sum);
 
-const tableDetails=BootupTime.makeTableDetails(headings,results);
+const summary={wastedMs:totalBootupTime};
+const details=BootupTime.makeTableDetails(headings,results,summary);
+
+const score=Audit.computeLogNormalScore(
+totalBootupTime,
+context.options.scorePODR,
+context.options.scoreMedian);
+
 
 return{
-score:totalBootupTime<2000,
+score,
 rawValue:totalBootupTime,
-displayValue:Util.formatMilliseconds(totalBootupTime),
-details:tableDetails,
+displayValue:[Util.MS_DISPLAY_VALUE,totalBootupTime],
+details,
 extendedInfo:{
 value:extendedInfo}};
 
 
-});
 }}
 
 
 module.exports=BootupTime;
 
-},{"../lib/task-groups":36,"../lib/web-inspector":42,"../report/v2/renderer/util":43,"./audit":2}],"../audits/byte-efficiency/offscreen-images":[function(require,module,exports){
+},{"../lib/task-groups":42,"../lib/web-inspector":47,"../report/html/renderer/util":48,"./audit":2}],"../audits/byte-efficiency/efficient-animated-content":[function(require,module,exports){
+
+
+
+
+
+
+
+
+'use strict';
+
+const WebInspector=require('../../lib/web-inspector');
+const ByteEfficiencyAudit=require('./byte-efficiency-audit');
+
+
+
+const GIF_BYTE_THRESHOLD=100*1024;
+
+class EfficientAnimatedContent extends ByteEfficiencyAudit{
+
+
+
+static get meta(){
+return{
+name:'efficient-animated-content',
+scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
+description:'Use video formats for animated content',
+helpText:'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/)',
+requiredArtifacts:['devtoolsLogs']};
+
+}
+
+
+
+
+
+
+
+static getPercentSavings(bytes){
+return Math.round(29.1*Math.log10(bytes)-100.7)/100;
+}
+
+
+
+
+
+
+static audit_(artifacts,networkRecords){
+const unoptimizedContent=networkRecords.filter(
+record=>record._mimeType==='image/gif'&&
+record._resourceType===WebInspector.resourceTypes.Image&&
+(record._resourceSize||0)>GIF_BYTE_THRESHOLD);
+
+
+
+const results=unoptimizedContent.map(record=>{
+const resourceSize=record._resourceSize||0;
+return{
+url:record.url,
+totalBytes:resourceSize,
+wastedBytes:Math.round(resourceSize*
+EfficientAnimatedContent.getPercentSavings(resourceSize))};
+
+});
+
+const headings=[
+{key:'url',itemType:'url',text:'URL'},
+{
+key:'totalBytes',
+itemType:'bytes',
+displayUnit:'kb',
+granularity:1,
+text:'Transfer Size'},
+
+{
+key:'wastedBytes',
+itemType:'bytes',
+displayUnit:'kb',
+granularity:1,
+text:'Byte Savings'}];
+
+
+
+return{
+results,
+headings};
+
+}}
+
+
+module.exports=EfficientAnimatedContent;
+
+},{"../../lib/web-inspector":47,"./byte-efficiency-audit":3}],"../audits/byte-efficiency/offscreen-images":[function(require,module,exports){
 
 
 
@@ -1656,6 +1782,8 @@
 const IGNORE_THRESHOLD_IN_BYTES=2048;
 const IGNORE_THRESHOLD_IN_PERCENT=75;
 
+
+
 class OffscreenImages extends ByteEfficiencyAudit{
 
 
@@ -1663,10 +1791,11 @@
 static get meta(){
 return{
 name:'offscreen-images',
-description:'Offscreen images',
-informative:true,
-helpText:'Consider lazy-loading offscreen and hidden images to improve page load speed '+
-'and time to interactive. '+
+description:'Defer offscreen images',
+scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
+helpText:
+'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).',
 requiredArtifacts:['ImageUsage','ViewportDimensions','traces','devtoolsLogs']};
 
@@ -1695,6 +1824,10 @@
 
 
 static computeWaste(image,viewportDimensions){
+if(!image.networkRecord){
+return null;
+}
+
 const url=URL.elideDataURI(image.src);
 const totalPixels=image.clientWidth*image.clientHeight;
 const visiblePixels=this.computeVisiblePixels(image.clientRect,viewportDimensions);
@@ -1709,11 +1842,6 @@
 
 return{
 url,
-preview:{
-type:'thumbnail',
-url:image.networkRecord.url,
-mimeType:image.networkRecord.mimeType},
-
 requestStartTime:image.networkRecord.startTime,
 totalBytes,
 wastedBytes,
@@ -1725,51 +1853,83 @@
 
 
 
-static audit_(artifacts){
+
+
+
+
+
+
+static computeWasteWithTTIGraph(results,graph,simulator){
+return ByteEfficiencyAudit.computeWasteWithTTIGraph(results,graph,simulator,
+{includeLoad:false});
+}
+
+
+
+
+
+
+
+static audit_(artifacts,networkRecords,context){
 const images=artifacts.ImageUsage;
 const viewportDimensions=artifacts.ViewportDimensions;
 const trace=artifacts.traces[ByteEfficiencyAudit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[ByteEfficiencyAudit.DEFAULT_PASS];
 
-let debugString;
+
+const warnings=[];
 const resultsMap=images.reduce((results,image)=>{
-if(!image.networkRecord){
+const processed=OffscreenImages.computeWaste(image,viewportDimensions);
+if(processed===null){
 return results;
 }
 
-const processed=OffscreenImages.computeWaste(image,viewportDimensions);
 if(processed instanceof Error){
-debugString=processed.message;
+warnings.push(processed.message);
+
 Sentry.captureException(processed,{tags:{audit:this.meta.name},level:'warning'});
 return results;
 }
 
 
-const existing=results.get(processed.preview.url);
+const existing=results.get(processed.url);
 if(!existing||existing.wastedBytes>processed.wastedBytes){
-results.set(processed.preview.url,processed);
+results.set(processed.url,processed);
 }
 
 return results;
 },new Map());
 
-return artifacts.requestFirstInteractive(trace).then(firstInteractive=>{
-const ttiTimestamp=firstInteractive.timestamp/1000000;
+const settings=context.settings;
+return artifacts.requestFirstCPUIdle({trace,devtoolsLog,settings}).then(firstInteractive=>{
+
+
+
+const ttiTimestamp=firstInteractive.timestamp?firstInteractive.timestamp/1e6:Infinity;
+
 const results=Array.from(resultsMap.values()).filter(item=>{
-const isWasteful=item.wastedBytes>IGNORE_THRESHOLD_IN_BYTES&&
+const isWasteful=
+item.wastedBytes>IGNORE_THRESHOLD_IN_BYTES&&
 item.wastedPercent>IGNORE_THRESHOLD_IN_PERCENT;
 const loadedEarly=item.requestStartTime<ttiTimestamp;
 return isWasteful&&loadedEarly;
 });
 
 const headings=[
-{key:'preview',itemType:'thumbnail',text:''},
+{key:'url',itemType:'thumbnail',text:''},
 {key:'url',itemType:'url',text:'URL'},
-{key:'totalKb',itemType:'text',text:'Original'},
-{key:'potentialSavings',itemType:'text',text:'Potential Savings'}];
+{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'},
+{
+key:'wastedBytes',
+itemType:'bytes',
+displayUnit:'kb',
+granularity:1,
+text:'Potential Savings'}];
+
 
 
 return{
-debugString,
+warnings,
 results,
 headings};
 
@@ -1779,7 +1939,243 @@
 
 module.exports=OffscreenImages;
 
-},{"../../lib/sentry":33,"../../lib/url-shim":41,"./byte-efficiency-audit":3}],"../audits/byte-efficiency/total-byte-weight":[function(require,module,exports){
+},{"../../lib/sentry":39,"../../lib/url-shim":"url","./byte-efficiency-audit":3}],"../audits/byte-efficiency/render-blocking-resources":[function(require,module,exports){
+
+
+
+
+
+
+
+
+
+'use strict';
+
+const Audit=require('../audit');
+const Node=require('../../lib/dependency-graph/node');
+const ByteEfficiencyAudit=require('./byte-efficiency-audit');
+const UnusedCSS=require('./unused-css-rules');
+const WebInspector=require('../../lib/web-inspector');
+
+const Simulator=require('../../lib/dependency-graph/simulator/simulator');
+const NetworkNode=require('../../lib/dependency-graph/network-node.js');
+
+
+
+
+
+const MINIMUM_WASTED_MS=50;
+
+
+
+
+
+
+function getNodesAndTimingByUrl(nodeTimings){
+
+const urlMap={};
+const nodes=Array.from(nodeTimings.keys());
+nodes.forEach(node=>{
+if(node.type!=='network')return;
+const networkNode=node;
+const nodeTiming=nodeTimings.get(node);
+if(!nodeTiming)return;
+
+urlMap[networkNode.record.url]={node,nodeTiming};
+});
+
+return urlMap;
+}
+
+class RenderBlockingResources extends Audit{
+
+
+
+static get meta(){
+return{
+name:'render-blocking-resources',
+description:'Eliminate render-blocking resources',
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
+helpText:
+'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).',
+
+
+
+requiredArtifacts:['URL','TagsBlockingFirstPaint','traces']};
+
+}
+
+
+
+
+
+
+static async computeResults(artifacts,context){
+const trace=artifacts.traces[Audit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const simulatorData={devtoolsLog,settings:context.settings};
+const traceOfTab=await artifacts.requestTraceOfTab(trace);
+const simulator=await artifacts.requestLoadSimulator(simulatorData);
+const wastedCssBytes=await RenderBlockingResources.computeWastedCSSBytes(artifacts,context);
+
+const metricSettings={throttlingMethod:'simulate'};
+const metricComputationData={trace,devtoolsLog,simulator,settings:metricSettings};
+
+const fcpSimulation=await artifacts.requestFirstContentfulPaint(metricComputationData);
+const fcpTsInMs=traceOfTab.timestamps.firstContentfulPaint/1000;
+
+const nodesByUrl=getNodesAndTimingByUrl(fcpSimulation.optimisticEstimate.nodeTimings);
+
+const results=[];
+const deferredNodeIds=new Set();
+for(const resource of artifacts.TagsBlockingFirstPaint){
+
+if(resource.endTime*1000>fcpTsInMs)continue;
+
+if(!nodesByUrl[resource.tag.url])continue;
+
+const{node,nodeTiming}=nodesByUrl[resource.tag.url];
+
+
+
+
+node.traverse(node=>deferredNodeIds.add(node.id));
+
+
+
+const wastedMs=Math.round(nodeTiming.endTime-nodeTiming.startTime);
+if(wastedMs<MINIMUM_WASTED_MS)continue;
+
+results.push({
+url:resource.tag.url,
+totalBytes:resource.transferSize,
+wastedMs});
+
+}
+
+if(!results.length){
+return{results,wastedMs:0};
+}
+
+const wastedMs=RenderBlockingResources.estimateSavingsWithGraphs(
+simulator,
+fcpSimulation.optimisticGraph,
+deferredNodeIds,
+wastedCssBytes);
+
+
+return{results,wastedMs};
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static estimateSavingsWithGraphs(simulator,fcpGraph,deferredIds,wastedCssBytesByUrl){
+const originalEstimate=simulator.simulate(fcpGraph).timeInMs;
+
+let totalChildNetworkBytes=0;
+const minimalFCPGraph=fcpGraph.cloneWithRelationships(node=>{
+
+const canDeferRequest=deferredIds.has(node.id);
+if(node.type!==Node.TYPES.NETWORK)return!canDeferRequest;
+
+const networkNode=node;
+
+const isStylesheet=
+networkNode.record._resourceType===WebInspector.resourceTypes.Stylesheet;
+if(canDeferRequest&&isStylesheet){
+
+const wastedBytes=wastedCssBytesByUrl.get(networkNode.record.url)||0;
+totalChildNetworkBytes+=(networkNode.record._transferSize||0)-wastedBytes;
+}
+return!canDeferRequest;
+});
+
+
+const originalTransferSize=minimalFCPGraph.record._transferSize;
+const safeTransferSize=originalTransferSize||0;
+minimalFCPGraph.record._transferSize=safeTransferSize+totalChildNetworkBytes;
+const estimateAfterInline=simulator.simulate(minimalFCPGraph).timeInMs;
+minimalFCPGraph.record._transferSize=originalTransferSize;
+return Math.round(Math.max(originalEstimate-estimateAfterInline,0));
+}
+
+
+
+
+
+
+static async computeWastedCSSBytes(artifacts,context){
+const wastedBytesByUrl=new Map();
+try{
+
+const results=await UnusedCSS.audit(artifacts,context);
+
+for(const item of results.details.items){
+wastedBytesByUrl.set(item.url,item.wastedBytes);
+}
+}catch(_){}
+
+return wastedBytesByUrl;
+}
+
+
+
+
+
+
+static async audit(artifacts,context){
+const{results,wastedMs}=await RenderBlockingResources.computeResults(artifacts,context);
+
+let displayValue='';
+if(results.length>1){
+displayValue=`${results.length} resources delayed first paint by ${wastedMs}ms`;
+}else if(results.length===1){
+displayValue=`${results.length} resource delayed first paint by ${wastedMs}ms`;
+}
+
+const headings=[
+{key:'url',itemType:'url',text:'URL'},
+{
+key:'totalBytes',
+itemType:'bytes',
+displayUnit:'kb',
+granularity:0.01,
+text:'Size (KB)'},
+
+{key:'wastedMs',itemType:'ms',text:'Download Time (ms)',granularity:1}];
+
+
+const summary={wastedMs};
+const details=Audit.makeTableDetails(headings,results,summary);
+
+return{
+displayValue,
+score:ByteEfficiencyAudit.scoreForWastedMs(wastedMs),
+rawValue:wastedMs,
+details};
+
+}}
+
+
+module.exports=RenderBlockingResources;
+
+},{"../../lib/dependency-graph/network-node.js":24,"../../lib/dependency-graph/node":25,"../../lib/dependency-graph/simulator/simulator":28,"../../lib/web-inspector":47,"../audit":2,"./byte-efficiency-audit":3,"./unused-css-rules":"../audits/byte-efficiency/unused-css-rules"}],"../audits/byte-efficiency/total-byte-weight":[function(require,module,exports){
 
 
 
@@ -1789,11 +2185,6 @@
 
 const ByteEfficiencyAudit=require('./byte-efficiency-audit');
 
-
-
-const SCORING_POINT_OF_DIMINISHING_RETURNS=2500*1024;
-const SCORING_MEDIAN=4000*1024;
-
 class TotalByteWeight extends ByteEfficiencyAudit{
 
 
@@ -1803,11 +2194,11 @@
 name:'total-byte-weight',
 description:'Avoids enormous network payloads',
 failureDescription:'Has enormous network payloads',
+scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
 helpText:
 '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).',
-scoringMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
 requiredArtifacts:['devtoolsLogs']};
 
 }
@@ -1815,25 +2206,39 @@
 
 
 
+static get defaultOptions(){
+return{
 
-static audit(artifacts){
+
+scorePODR:2500*1024,
+scoreMedian:4000*1024};
+
+}
+
+
+
+
+
+
+static async audit(artifacts,context){
 const devtoolsLogs=artifacts.devtoolsLogs[ByteEfficiencyAudit.DEFAULT_PASS];
-return Promise.all([
+const[networkRecords,networkThroughput]=await Promise.all([
 artifacts.requestNetworkRecords(devtoolsLogs),
-artifacts.requestNetworkThroughput(devtoolsLogs)]).
-then(([networkRecords,networkThroughput])=>{
+artifacts.requestNetworkThroughput(devtoolsLogs)]);
+
+
 let totalBytes=0;
+
 let results=[];
 networkRecords.forEach(record=>{
 
 
-if(record.scheme==='data'||!record.finished)return;
+if(record.parsedURL.scheme==='data'||!record.finished)return;
 
 const result={
 url:record.url,
 totalBytes:record.transferSize,
-totalKb:this.bytesToKbString(record.transferSize),
-totalMs:this.bytesToMsString(record.transferSize,networkThroughput)};
+totalMs:ByteEfficiencyAudit.bytesToMs(record.transferSize,networkThroughput)};
 
 
 totalBytes+=result.totalBytes;
@@ -1842,21 +2247,22 @@
 const totalCompletedRequests=results.length;
 results=results.sort((itemA,itemB)=>itemB.totalBytes-itemA.totalBytes).slice(0,10);
 
-
-
-
-
-
 const score=ByteEfficiencyAudit.computeLogNormalScore(
 totalBytes,
-SCORING_POINT_OF_DIMINISHING_RETURNS,
-SCORING_MEDIAN);
+context.options.scorePODR,
+context.options.scoreMedian);
 
 
 const headings=[
 {key:'url',itemType:'url',text:'URL'},
-{key:'totalKb',itemType:'text',text:'Total Size'},
-{key:'totalMs',itemType:'text',text:'Transfer Time'}];
+{
+key:'totalBytes',
+itemType:'bytes',
+displayUnit:'kb',
+granularity:1,
+text:'Total Size'},
+
+{key:'totalMs',itemType:'ms',text:'Transfer Time'}];
 
 
 const tableDetails=ByteEfficiencyAudit.makeTableDetails(headings,results);
@@ -1864,7 +2270,10 @@
 return{
 score,
 rawValue:totalBytes,
-displayValue:`Total size was ${ByteEfficiencyAudit.bytesToKbString(totalBytes)}`,
+displayValue:[
+'Total size was %d\xa0KB',
+totalBytes/1024],
+
 extendedInfo:{
 value:{
 results,
@@ -1873,7 +2282,6 @@
 
 details:tableDetails};
 
-});
 }}
 
 
@@ -1904,8 +2312,8 @@
 return{
 name:'unminified-css',
 description:'Minify CSS',
-informative:true,
-helpText:'Minifying CSS files can reduce network payload sizes.'+
+scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
+helpText:'Minifying CSS files can reduce network payload sizes. '+
 '[Learn more](https://developers.google.com/speed/docs/insights/MinifyResources).',
 requiredArtifacts:['CSSUsage','devtoolsLogs']};
 
@@ -1982,10 +2390,11 @@
 const content=stylesheet.content;
 const totalTokenLength=UnminifiedCSS.computeTokenLength(content);
 
+
 let url=stylesheet.header.sourceURL;
 if(!url||url===pageUrl){
 const contentPreview=UnusedCSSRules.determineContentPreview(stylesheet.content);
-url={type:'code',text:contentPreview};
+url={type:'code',value:contentPreview};
 }
 
 const totalBytes=ByteEfficiencyAudit.estimateTransferSize(networkRecord,content.length,
@@ -2005,6 +2414,7 @@
 
 
 
+
 static audit_(artifacts,networkRecords){
 const pageUrl=artifacts.URL.finalUrl;
 const results=[];
@@ -2018,7 +2428,8 @@
 
 
 if(result.wastedPercent<IGNORE_THRESHOLD_IN_PERCENT||
-result.wastedBytes<IGNORE_THRESHOLD_IN_BYTES)continue;
+result.wastedBytes<IGNORE_THRESHOLD_IN_BYTES||
+!Number.isFinite(result.wastedBytes))continue;
 results.push(result);
 }
 
@@ -2026,8 +2437,9 @@
 results,
 headings:[
 {key:'url',itemType:'url',text:'URL'},
-{key:'totalKb',itemType:'text',text:'Original'},
-{key:'potentialSavings',itemType:'text',text:'Potential Savings'}]};
+{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'},
+{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1,
+text:'Potential Savings'}]};
 
 
 }}
@@ -2044,6 +2456,7 @@
 'use strict';
 
 const ByteEfficiencyAudit=require('./byte-efficiency-audit');
+
 const esprima=require('esprima');
 
 const IGNORE_THRESHOLD_IN_PERCENT=10;
@@ -2067,7 +2480,8 @@
 return{
 name:'unminified-javascript',
 description:'Minify JavaScript',
-informative:true,
+
+scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
 helpText:'Minifying JavaScript files can reduce payload sizes and script parse time. '+
 '[Learn more](https://developers.google.com/speed/docs/insights/MinifyResources).',
 requiredArtifacts:['Scripts','devtoolsLogs']};
@@ -2078,6 +2492,7 @@
 
 
 
+
 static computeWaste(scriptContent,networkRecord){
 const contentLength=scriptContent.length;
 let totalTokenLength=0;
@@ -2108,9 +2523,11 @@
 
 
 
+
 static audit_(artifacts,networkRecords){
+
 const results=[];
-let debugString;
+const warnings=[];
 for(const requestId of Object.keys(artifacts.Scripts)){
 const scriptContent=artifacts.Scripts[requestId];
 const networkRecord=networkRecords.find(record=>record.requestId===requestId);
@@ -2121,20 +2538,22 @@
 
 
 if(result.wastedPercent<IGNORE_THRESHOLD_IN_PERCENT||
-result.wastedBytes<IGNORE_THRESHOLD_IN_BYTES)continue;
+result.wastedBytes<IGNORE_THRESHOLD_IN_BYTES||
+!Number.isFinite(result.wastedBytes))continue;
 results.push(result);
 }catch(err){
-debugString=`Unable to process ${networkRecord._url}: ${err.message}`;
+warnings.push(`Unable to process ${networkRecord._url}: ${err.message}`);
 }
 }
 
 return{
 results,
-debugString,
+warnings,
 headings:[
 {key:'url',itemType:'url',text:'URL'},
-{key:'totalKb',itemType:'text',text:'Original'},
-{key:'potentialSavings',itemType:'text',text:'Potential Savings'}]};
+{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'},
+{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1,
+text:'Potential Savings'}]};
 
 
 }}
@@ -2142,7 +2561,7 @@
 
 module.exports=UnminifiedJavaScript;
 
-},{"./byte-efficiency-audit":3,"esprima":130}],"../audits/byte-efficiency/unused-css-rules":[function(require,module,exports){
+},{"./byte-efficiency-audit":3,"esprima":136}],"../audits/byte-efficiency/unused-css-rules":[function(require,module,exports){
 
 
 
@@ -2155,6 +2574,8 @@
 const IGNORE_THRESHOLD_IN_BYTES=2048;
 const PREVIEW_LENGTH=100;
 
+
+
 class UnusedCSSRules extends ByteEfficiencyAudit{
 
 
@@ -2162,11 +2583,11 @@
 static get meta(){
 return{
 name:'unused-css-rules',
-description:'Unused CSS rules',
-informative:true,
+description:'Defer unused CSS',
+scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
 helpText:'Remove unused rules from stylesheets to reduce unnecessary '+
 'bytes consumed by network activity. '+
-'[Learn more](https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery)',
+'[Learn more](https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery).',
 requiredArtifacts:['CSSUsage','URL','devtoolsLogs']};
 
 }
@@ -2277,14 +2698,11 @@
 
 
 static mapSheetToResult(stylesheetInfo,pageUrl){
-if(stylesheetInfo.isDuplicate){
-return null;
-}
 
 let url=stylesheetInfo.header.sourceURL;
 if(!url||url===pageUrl){
 const contentPreview=UnusedCSSRules.determineContentPreview(stylesheetInfo.content);
-url={type:'code',text:contentPreview};
+url={type:'code',value:contentPreview};
 }
 
 const usage=UnusedCSSRules.computeUsage(stylesheetInfo);
@@ -2311,8 +2729,9 @@
 
 const headings=[
 {key:'url',itemType:'url',text:'URL'},
-{key:'totalKb',itemType:'text',text:'Original'},
-{key:'potentialSavings',itemType:'text',text:'Potential Savings'}];
+{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'},
+{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1,
+text:'Potential Savings'}];
 
 
 return{
@@ -2345,7 +2764,7 @@
 return{
 name:'unused-javascript',
 description:'Unused JavaScript',
-informative:true,
+scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
 helpText:'Remove unused JavaScript to reduce bytes consumed by network activity.',
 requiredArtifacts:['JsUsage','devtoolsLogs']};
 
@@ -2417,7 +2836,9 @@
 
 
 
+
 static audit_(artifacts,networkRecords){
+
 const scriptsByUrl=new Map();
 for(const script of artifacts.JsUsage){
 const scripts=scriptsByUrl.get(script.url)||[];
@@ -2425,6 +2846,7 @@
 scriptsByUrl.set(script.url,scripts);
 }
 
+
 const results=[];
 for(const[url,scripts]of scriptsByUrl.entries()){
 const networkRecord=networkRecords.find(record=>record.url===url);
@@ -2439,8 +2861,9 @@
 results,
 headings:[
 {key:'url',itemType:'url',text:'URL'},
-{key:'totalKb',itemType:'text',text:'Original'},
-{key:'potentialSavings',itemType:'text',text:'Potential Savings'}]};
+{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'},
+{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1,
+text:'Potential Savings'}]};
 
 
 }}
@@ -2457,33 +2880,28 @@
 'use strict';
 
 const assert=require('assert');
+
 const parseCacheControl=require('parse-cache-control');
-const ByteEfficiencyAudit=require('./byte-efficiency-audit');
-const formatDuration=require('../../report/v2/renderer/util.js').formatDuration;
+const Audit=require('../audit');
 const WebInspector=require('../../lib/web-inspector');
 const URL=require('../../lib/url-shim');
 
 
 const IGNORE_THRESHOLD_IN_PERCENT=0.925;
 
-
-const SCORING_POINT_OF_DIMINISHING_RETURNS=4;
-const SCORING_MEDIAN=768;
-
-class CacheHeaders extends ByteEfficiencyAudit{
+class CacheHeaders extends Audit{
 
 
 
 static get meta(){
 return{
-category:'Caching',
 name:'uses-long-cache-ttl',
 description:'Uses efficient cache policy on static assets',
 failureDescription:'Uses inefficient cache policy on static assets',
 helpText:
 'A long cache lifetime can speed up repeat visits to your page. '+
 '[Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#cache-control).',
-scoringMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
 requiredArtifacts:['devtoolsLogs']};
 
 }
@@ -2491,6 +2909,19 @@
 
 
 
+static get defaultOptions(){
+return{
+
+
+
+scorePODR:4*1024,
+scoreMedian:128*1024};
+
+}
+
+
+
+
 
 
 
@@ -2554,14 +2985,16 @@
 if(cacheControl){
 
 if(cacheControl['no-cache']||cacheControl['no-store'])return 0;
-if(Number.isFinite(cacheControl['max-age']))return Math.max(cacheControl['max-age'],0);
+const maxAge=cacheControl['max-age'];
+if(maxAge!==undefined&&Number.isFinite(maxAge))return Math.max(maxAge,0);
 }else if((headers.get('pragma')||'').includes('no-cache')){
 
 return 0;
 }
 
-if(headers.has('expires')){
-const expires=new Date(headers.get('expires')).getTime();
+const expiresHeaders=headers.get('expires');
+if(expiresHeaders){
+const expires=new Date(expiresHeaders).getTime();
 
 if(!expires)return 0;
 return Math.max(0,Math.ceil((expires-Date.now())/1000));
@@ -2608,8 +3041,9 @@
 
 
 
-static audit(artifacts){
-const devtoolsLogs=artifacts.devtoolsLogs[ByteEfficiencyAudit.DEFAULT_PASS];
+
+static audit(artifacts,context){
+const devtoolsLogs=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
 return artifacts.requestNetworkRecords(devtoolsLogs).then(records=>{
 const results=[];
 let queryStringCount=0;
@@ -2618,8 +3052,9 @@
 for(const record of records){
 if(!CacheHeaders.isCacheableAsset(record))continue;
 
+
 const headers=new Map();
-for(const header of record._responseHeaders){
+for(const header of record._responseHeaders||[]){
 headers.set(header.name.toLowerCase(),header.value);
 }
 
@@ -2637,10 +3072,8 @@
 if(cacheHitProbability>IGNORE_THRESHOLD_IN_PERCENT)continue;
 
 const url=URL.elideDataURI(record._url);
-const totalBytes=record._transferSize;
-const totalKb=ByteEfficiencyAudit.bytesToKbString(totalBytes);
+const totalBytes=record._transferSize||0;
 const wastedBytes=(1-cacheHitProbability)*totalBytes;
-const cacheLifetimeDisplay=formatDuration(cacheLifetimeInSeconds);
 
 totalWastedBytes+=wastedBytes;
 if(url.includes('?'))queryStringCount++;
@@ -2648,36 +3081,32 @@
 results.push({
 url,
 cacheControl,
-cacheLifetimeInSeconds,
-cacheLifetimeDisplay,
+cacheLifetimeMs:cacheLifetimeInSeconds*1000,
 cacheHitProbability,
-totalKb,
 totalBytes,
 wastedBytes});
 
 }
 
 results.sort(
-(a,b)=>a.cacheLifetimeInSeconds-b.cacheLifetimeInSeconds||b.totalBytes-a.totalBytes);
+(a,b)=>a.cacheLifetimeMs-b.cacheLifetimeMs||b.totalBytes-a.totalBytes);
 
 
-
-
-
-
-const score=ByteEfficiencyAudit.computeLogNormalScore(
-totalWastedBytes/1024,
-SCORING_POINT_OF_DIMINISHING_RETURNS,
-SCORING_MEDIAN);
+const score=Audit.computeLogNormalScore(
+totalWastedBytes,
+context.options.scorePODR,
+context.options.scoreMedian);
 
 
 const headings=[
 {key:'url',itemType:'url',text:'URL'},
-{key:'cacheLifetimeDisplay',itemType:'text',text:'Cache TTL'},
-{key:'totalKb',itemType:'text',text:'Size (KB)'}];
+{key:'cacheLifetimeMs',itemType:'ms',text:'Cache TTL',displayUnit:'duration'},
+{key:'totalBytes',itemType:'bytes',text:'Size (KB)',displayUnit:'kb',
+granularity:1}];
 
 
-const tableDetails=ByteEfficiencyAudit.makeTableDetails(headings,results);
+const summary={wastedBytes:totalWastedBytes};
+const details=Audit.makeTableDetails(headings,results,summary);
 
 return{
 score,
@@ -2689,7 +3118,7 @@
 queryStringCount}},
 
 
-details:tableDetails};
+details};
 
 });
 }}
@@ -2697,7 +3126,7 @@
 
 module.exports=CacheHeaders;
 
-},{"../../lib/url-shim":41,"../../lib/web-inspector":42,"../../report/v2/renderer/util.js":43,"./byte-efficiency-audit":3,"assert":47,"parse-cache-control":141}],"../audits/byte-efficiency/uses-optimized-images":[function(require,module,exports){
+},{"../../lib/url-shim":"url","../../lib/web-inspector":47,"../audit":2,"assert":53,"parse-cache-control":147}],"../audits/byte-efficiency/uses-optimized-images":[function(require,module,exports){
 
 
 
@@ -2721,8 +3150,8 @@
 static get meta(){
 return{
 name:'uses-optimized-images',
-description:'Optimize images',
-informative:true,
+description:'Efficiently encode images',
+scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
 helpText:'Optimized images load faster and consume less cellular data. '+
 '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/optimize-images).',
 requiredArtifacts:['OptimizedImages','devtoolsLogs']};
@@ -2733,9 +3162,8 @@
 
 
 
-
-static computeSavings(image,type){
-const bytes=image.originalSize-image[type+'Size'];
+static computeSavings(image){
+const bytes=image.originalSize-image.jpegSize;
 const percent=100*bytes/image.originalSize;
 return{bytes,percent};
 }
@@ -2747,45 +3175,40 @@
 static audit_(artifacts){
 const images=artifacts.OptimizedImages;
 
-const failedImages=[];
+
 const results=[];
-images.forEach(image=>{
+const warnings=[];
+for(const image of images){
 if(image.failed){
-failedImages.push(image);
-return;
+warnings.push(`Unable to decode ${URL.getURLDisplayName(image.url)}`);
+continue;
 }else if(/(jpeg|bmp)/.test(image.mimeType)===false||
 image.originalSize<image.jpegSize+IGNORE_THRESHOLD_IN_BYTES){
-return;
+continue;
 }
 
 const url=URL.elideDataURI(image.url);
-const jpegSavings=UsesOptimizedImages.computeSavings(image,'jpeg');
+const jpegSavings=UsesOptimizedImages.computeSavings(image);
 
 results.push({
 url,
 fromProtocol:image.fromProtocol,
 isCrossOrigin:!image.isSameOrigin,
-preview:{url:image.url,mimeType:image.mimeType,type:'thumbnail'},
 totalBytes:image.originalSize,
 wastedBytes:jpegSavings.bytes});
 
-});
-
-let debugString;
-if(failedImages.length){
-const urls=failedImages.map(image=>URL.getURLDisplayName(image.url));
-debugString=`Lighthouse was unable to decode some of your images: ${urls.join(', ')}`;
 }
 
 const headings=[
-{key:'preview',itemType:'thumbnail',text:''},
+{key:'url',itemType:'thumbnail',text:''},
 {key:'url',itemType:'url',text:'URL'},
-{key:'totalKb',itemType:'text',text:'Original'},
-{key:'potentialSavings',itemType:'text',text:'Potential Savings'}];
+{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'},
+{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1,
+text:'Potential Savings'}];
 
 
 return{
-debugString,
+warnings,
 results,
 headings};
 
@@ -2794,7 +3217,138 @@
 
 module.exports=UsesOptimizedImages;
 
-},{"../../lib/url-shim":41,"./byte-efficiency-audit":3}],"../audits/byte-efficiency/uses-request-compression":[function(require,module,exports){
+},{"../../lib/url-shim":"url","./byte-efficiency-audit":3}],"../audits/byte-efficiency/uses-responsive-images":[function(require,module,exports){
+
+
+
+
+
+
+
+
+
+
+
+
+
+'use strict';
+
+const ByteEfficiencyAudit=require('./byte-efficiency-audit');
+const Sentry=require('../../lib/sentry');
+const URL=require('../../lib/url-shim');
+
+const IGNORE_THRESHOLD_IN_BYTES=2048;
+
+class UsesResponsiveImages extends ByteEfficiencyAudit{
+
+
+
+static get meta(){
+return{
+name:'uses-responsive-images',
+description:'Properly size images',
+scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
+helpText:
+'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).',
+requiredArtifacts:['ImageUsage','ViewportDimensions','devtoolsLogs']};
+
+}
+
+
+
+
+
+
+static computeWaste(image,DPR){
+
+if(!image.networkRecord){
+return null;
+}
+
+const url=URL.elideDataURI(image.src);
+const actualPixels=image.naturalWidth*image.naturalHeight;
+const usedPixels=image.clientWidth*image.clientHeight*Math.pow(DPR,2);
+const wastedRatio=1-usedPixels/actualPixels;
+const totalBytes=image.networkRecord.resourceSize;
+const wastedBytes=Math.round(totalBytes*wastedRatio);
+
+
+
+if(!usedPixels){
+return null;
+}
+
+if(!Number.isFinite(wastedRatio)){
+return new Error(`Invalid image sizing information ${url}`);
+}
+
+return{
+url,
+totalBytes,
+wastedBytes,
+wastedPercent:100*wastedRatio};
+
+}
+
+
+
+
+
+static audit_(artifacts){
+const images=artifacts.ImageUsage;
+const DPR=artifacts.ViewportDimensions.devicePixelRatio;
+
+
+const warnings=[];
+
+const resultsMap=new Map();
+images.forEach(image=>{
+
+if(!image.networkRecord||image.networkRecord.mimeType==='image/svg+xml'){
+return;
+}
+
+const processed=UsesResponsiveImages.computeWaste(image,DPR);
+if(!processed)return;
+
+if(processed instanceof Error){
+warnings.push(processed.message);
+
+Sentry.captureException(processed,{tags:{audit:this.meta.name},level:'warning'});
+return;
+}
+
+
+const existing=resultsMap.get(processed.url);
+if(!existing||existing.wastedBytes>processed.wastedBytes){
+resultsMap.set(processed.url,processed);
+}
+});
+
+const results=Array.from(resultsMap.values()).
+filter(item=>item.wastedBytes>IGNORE_THRESHOLD_IN_BYTES);
+
+const headings=[
+{key:'url',itemType:'thumbnail',text:''},
+{key:'url',itemType:'url',text:'URL'},
+{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'},
+{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1,
+text:'Potential Savings'}];
+
+
+return{
+warnings,
+results,
+headings};
+
+}}
+
+
+module.exports=UsesResponsiveImages;
+
+},{"../../lib/sentry":39,"../../lib/url-shim":"url","./byte-efficiency-audit":3}],"../audits/byte-efficiency/uses-text-compression":[function(require,module,exports){
 
 
 
@@ -2818,8 +3372,8 @@
 
 static get meta(){
 return{
-name:'uses-request-compression',
-informative:true,
+name:'uses-text-compression',
+scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
 description:'Enable text compression',
 helpText:'Text-based responses should be served with compression (gzip, deflate or brotli)'+
 ' to minimize total network bytes.'+
@@ -2832,10 +3386,10 @@
 
 
 
-
 static audit_(artifacts){
 const uncompressedResponses=artifacts.ResponseCompression;
 
+
 const results=[];
 uncompressedResponses.forEach(record=>{
 const originalSize=record.resourceSize;
@@ -2868,8 +3422,9 @@
 
 const headings=[
 {key:'url',itemType:'url',text:'Uncompressed resource URL'},
-{key:'totalKb',itemType:'text',text:'Original'},
-{key:'potentialSavings',itemType:'text',text:'GZIP Savings'}];
+{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'},
+{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1,
+text:'GZIP Savings'}];
 
 
 return{
@@ -2881,136 +3436,7 @@
 
 module.exports=ResponsesAreCompressed;
 
-},{"../../lib/url-shim":41,"./byte-efficiency-audit":3}],"../audits/byte-efficiency/uses-responsive-images":[function(require,module,exports){
-
-
-
-
-
-
-
-
-
-
-
-
-
-'use strict';
-
-const ByteEfficiencyAudit=require('./byte-efficiency-audit');
-const Sentry=require('../../lib/sentry');
-const URL=require('../../lib/url-shim');
-
-const IGNORE_THRESHOLD_IN_BYTES=2048;
-const WASTEFUL_THRESHOLD_IN_BYTES=25*1024;
-
-class UsesResponsiveImages extends ByteEfficiencyAudit{
-
-
-
-static get meta(){
-return{
-name:'uses-responsive-images',
-description:'Properly size images',
-informative:true,
-helpText:
-'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).',
-requiredArtifacts:['ImageUsage','ViewportDimensions','devtoolsLogs']};
-
-}
-
-
-
-
-
-
-static computeWaste(image,DPR){
-const url=URL.elideDataURI(image.src);
-const actualPixels=image.naturalWidth*image.naturalHeight;
-const usedPixels=image.clientWidth*image.clientHeight*Math.pow(DPR,2);
-const wastedRatio=1-usedPixels/actualPixels;
-const totalBytes=image.networkRecord.resourceSize;
-const wastedBytes=Math.round(totalBytes*wastedRatio);
-
-
-
-if(!usedPixels){
-return null;
-}
-
-if(!Number.isFinite(wastedRatio)){
-return new Error(`Invalid image sizing information ${url}`);
-}
-
-return{
-url,
-preview:{
-type:'thumbnail',
-url:image.networkRecord.url,
-mimeType:image.networkRecord.mimeType},
-
-totalBytes,
-wastedBytes,
-wastedPercent:100*wastedRatio,
-isWasteful:wastedBytes>WASTEFUL_THRESHOLD_IN_BYTES};
-
-}
-
-
-
-
-
-static audit_(artifacts){
-const images=artifacts.ImageUsage;
-const DPR=artifacts.ViewportDimensions.devicePixelRatio;
-
-let debugString;
-const resultsMap=new Map();
-images.forEach(image=>{
-
-if(!image.networkRecord||image.networkRecord.mimeType==='image/svg+xml'){
-return;
-}
-
-const processed=UsesResponsiveImages.computeWaste(image,DPR);
-if(!processed)return;
-
-if(processed instanceof Error){
-debugString=processed.message;
-Sentry.captureException(processed,{tags:{audit:this.meta.name},level:'warning'});
-return;
-}
-
-
-const existing=resultsMap.get(processed.preview.url);
-if(!existing||existing.wastedBytes>processed.wastedBytes){
-resultsMap.set(processed.preview.url,processed);
-}
-});
-
-const results=Array.from(resultsMap.values()).
-filter(item=>item.wastedBytes>IGNORE_THRESHOLD_IN_BYTES);
-
-const headings=[
-{key:'preview',itemType:'thumbnail',text:''},
-{key:'url',itemType:'url',text:'URL'},
-{key:'totalKb',itemType:'text',text:'Original'},
-{key:'potentialSavings',itemType:'text',text:'Potential Savings'}];
-
-
-return{
-debugString,
-results,
-headings};
-
-}}
-
-
-module.exports=UsesResponsiveImages;
-
-},{"../../lib/sentry":33,"../../lib/url-shim":41,"./byte-efficiency-audit":3}],"../audits/byte-efficiency/uses-webp-images":[function(require,module,exports){
+},{"../../lib/url-shim":"url","./byte-efficiency-audit":3}],"../audits/byte-efficiency/uses-webp-images":[function(require,module,exports){
 
 
 
@@ -3022,7 +3448,6 @@
 'use strict';
 
 const ByteEfficiencyAudit=require('./byte-efficiency-audit');
-const OptimizedImages=require('./uses-optimized-images');
 const URL=require('../../lib/url-shim');
 
 const IGNORE_THRESHOLD_IN_BYTES=8192;
@@ -3035,7 +3460,7 @@
 return{
 name:'uses-webp-images',
 description:'Serve images in next-gen formats',
-informative:true,
+scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
 helpText:'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).',
@@ -3047,47 +3472,52 @@
 
 
 
+static computeSavings(image){
+const bytes=image.originalSize-image.webpSize;
+const percent=100*bytes/image.originalSize;
+return{bytes,percent};
+}
+
+
+
+
+
 static audit_(artifacts){
 const images=artifacts.OptimizedImages;
 
-const failedImages=[];
+
 const results=[];
-images.forEach(image=>{
+const warnings=[];
+for(const image of images){
 if(image.failed){
-failedImages.push(image);
-return;
+warnings.push(`Unable to decode ${URL.getURLDisplayName(image.url)}`);
+continue;
 }else if(image.originalSize<image.webpSize+IGNORE_THRESHOLD_IN_BYTES){
-return;
+continue;
 }
 
 const url=URL.elideDataURI(image.url);
-const webpSavings=OptimizedImages.computeSavings(image,'webp');
+const webpSavings=UsesWebPImages.computeSavings(image);
 
 results.push({
 url,
 fromProtocol:image.fromProtocol,
 isCrossOrigin:!image.isSameOrigin,
-preview:{url:image.url,mimeType:image.mimeType,type:'thumbnail'},
 totalBytes:image.originalSize,
 wastedBytes:webpSavings.bytes});
 
-});
-
-let debugString;
-if(failedImages.length){
-const urls=failedImages.map(image=>URL.getURLDisplayName(image.url));
-debugString=`Lighthouse was unable to decode some of your images: ${urls.join(', ')}`;
 }
 
 const headings=[
-{key:'preview',itemType:'thumbnail',text:''},
+{key:'url',itemType:'thumbnail',text:''},
 {key:'url',itemType:'url',text:'URL'},
-{key:'totalKb',itemType:'text',text:'Original'},
-{key:'potentialSavings',itemType:'text',text:'Potential Savings'}];
+{key:'totalBytes',itemType:'bytes',displayUnit:'kb',granularity:1,text:'Original'},
+{key:'wastedBytes',itemType:'bytes',displayUnit:'kb',granularity:1,
+text:'Potential Savings'}];
 
 
 return{
-debugString,
+warnings,
 results,
 headings};
 
@@ -3096,293 +3526,7 @@
 
 module.exports=UsesWebPImages;
 
-},{"../../lib/url-shim":41,"./byte-efficiency-audit":3,"./uses-optimized-images":"../audits/byte-efficiency/uses-optimized-images"}],"../audits/cache-start-url":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const Audit=require('./audit');
-
-class CacheStartUrl extends Audit{
-
-
-
-static get meta(){
-return{
-name:'cache-start-url',
-description:'Cache contains start_url from manifest (alpha)',
-failureDescription:'Cache does not contain start_url from manifest (alpha)',
-requiredArtifacts:['CacheContents','Manifest','URL']};
-
-}
-
-
-
-
-
-static audit(artifacts){
-if(!artifacts.Manifest||!artifacts.Manifest.value){
-
-return{
-rawValue:false};
-
-}
-
-const manifest=artifacts.Manifest.value;
-if(!(manifest.start_url&&manifest.start_url.value)){
-return{
-rawValue:false,
-debugString:'start_url not present in Manifest'};
-
-}
-
-
-const startURL=manifest.start_url.value;
-
-const altStartURL=startURL.
-replace(/\?utm_([^=]*)=([^&]|$)*/,'').
-replace(/\?$/,'');
-
-
-
-
-
-
-const cacheContents=artifacts.CacheContents;
-const cacheHasStartUrl=cacheContents.find(req=>{
-return startURL===req||altStartURL===req;
-});
-
-return{
-rawValue:cacheHasStartUrl!==undefined};
-
-}}
-
-
-module.exports=CacheStartUrl;
-
-},{"./audit":2}],"../audits/consistently-interactive":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util');
-const NetworkRecorder=require('../lib/network-recorder');
-const TracingProcessor=require('../lib/traces/tracing-processor');
-const LHError=require('../lib/errors');
-
-
-
-const SCORING_POINT_OF_DIMINISHING_RETURNS=1700;
-const SCORING_MEDIAN=10000;
-
-const REQUIRED_QUIET_WINDOW=5000;
-const ALLOWED_CONCURRENT_REQUESTS=2;
-
-
-
-
-
-
-
-class ConsistentlyInteractiveMetric extends Audit{
-
-
-
-static get meta(){
-return{
-name:'consistently-interactive',
-description:'Consistently Interactive (beta)',
-helpText:'Consistently Interactive marks the time at which the page is '+
-'fully interactive. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/consistently-interactive).',
-scoringMode:Audit.SCORING_MODES.NUMERIC,
-requiredArtifacts:['traces','devtoolsLogs']};
-
-}
-
-
-
-
-
-
-
-
-static _findNetworkQuietPeriods(networkRecords,traceOfTab){
-const traceEndTsInMs=traceOfTab.timestamps.traceEnd/1000;
-return NetworkRecorder.findNetworkQuietPeriods(networkRecords,
-ALLOWED_CONCURRENT_REQUESTS,traceEndTsInMs);
-}
-
-
-
-
-
-
-
-static _findCPUQuietPeriods(longTasks,traceOfTab){
-const navStartTsInMs=traceOfTab.timestamps.navigationStart/1000;
-const traceEndTsInMs=traceOfTab.timestamps.traceEnd/1000;
-if(longTasks.length===0){
-return[{start:0,end:traceEndTsInMs}];
-}
-
-const quietPeriods=[];
-longTasks.forEach((task,index)=>{
-if(index===0){
-quietPeriods.push({
-start:0,
-end:task.start+navStartTsInMs});
-
-}
-
-if(index===longTasks.length-1){
-quietPeriods.push({
-start:task.end+navStartTsInMs,
-end:traceEndTsInMs});
-
-}else{
-quietPeriods.push({
-start:task.end+navStartTsInMs,
-end:longTasks[index+1].start+navStartTsInMs});
-
-}
-});
-
-return quietPeriods;
-}
-
-
-
-
-
-
-
-
-
-
-static findOverlappingQuietPeriods(longTasks,networkRecords,traceOfTab){
-const FMPTsInMs=traceOfTab.timestamps.firstMeaningfulPaint/1000;
-
-const isLongEnoughQuietPeriod=period=>
-period.end>FMPTsInMs+REQUIRED_QUIET_WINDOW&&
-period.end-period.start>=REQUIRED_QUIET_WINDOW;
-const networkQuietPeriods=this._findNetworkQuietPeriods(networkRecords,traceOfTab).
-filter(isLongEnoughQuietPeriod);
-const cpuQuietPeriods=this._findCPUQuietPeriods(longTasks,traceOfTab).
-filter(isLongEnoughQuietPeriod);
-
-const cpuQueue=cpuQuietPeriods.slice();
-const networkQueue=networkQuietPeriods.slice();
-
-
-let cpuCandidate=cpuQueue.shift();
-let networkCandidate=networkQueue.shift();
-while(cpuCandidate&&networkCandidate){
-if(cpuCandidate.start>=networkCandidate.start){
-
-if(networkCandidate.end>=cpuCandidate.start+REQUIRED_QUIET_WINDOW){
-return{
-cpuQuietPeriod:cpuCandidate,
-networkQuietPeriod:networkCandidate,
-cpuQuietPeriods,
-networkQuietPeriods};
-
-}else{
-networkCandidate=networkQueue.shift();
-}
-}else{
-
-if(cpuCandidate.end>=networkCandidate.start+REQUIRED_QUIET_WINDOW){
-return{
-cpuQuietPeriod:cpuCandidate,
-networkQuietPeriod:networkCandidate,
-cpuQuietPeriods,
-networkQuietPeriods};
-
-}else{
-cpuCandidate=cpuQueue.shift();
-}
-}
-}
-
-throw new LHError(
-cpuCandidate?
-LHError.errors.NO_TTI_NETWORK_IDLE_PERIOD:
-LHError.errors.NO_TTI_CPU_IDLE_PERIOD);
-
-}
-
-
-
-
-
-static audit(artifacts){
-const trace=artifacts.traces[Audit.DEFAULT_PASS];
-const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
-const computedArtifacts=[
-artifacts.requestNetworkRecords(devtoolsLog),
-artifacts.requestTraceOfTab(trace)];
-
-
-return Promise.all(computedArtifacts).
-then(([networkRecords,traceOfTab])=>{
-if(!traceOfTab.timestamps.firstMeaningfulPaint){
-throw new LHError(LHError.errors.NO_FMP);
-}
-
-if(!traceOfTab.timestamps.domContentLoaded){
-throw new LHError(LHError.errors.NO_DCL);
-}
-
-const longTasks=TracingProcessor.getMainThreadTopLevelEvents(traceOfTab).
-filter(event=>event.duration>=50);
-const quietPeriodInfo=this.findOverlappingQuietPeriods(longTasks,networkRecords,
-traceOfTab);
-const cpuQuietPeriod=quietPeriodInfo.cpuQuietPeriod;
-
-const timestamp=Math.max(
-cpuQuietPeriod.start,
-traceOfTab.timestamps.firstMeaningfulPaint/1000,
-traceOfTab.timestamps.domContentLoaded/1000)*
-1000;
-const timeInMs=(timestamp-traceOfTab.timestamps.navigationStart)/1000;
-const extendedInfo=Object.assign(quietPeriodInfo,{timestamp,timeInMs});
-
-return{
-score:Audit.computeLogNormalScore(
-timeInMs,
-SCORING_POINT_OF_DIMINISHING_RETURNS,
-SCORING_MEDIAN),
-
-rawValue:timeInMs,
-displayValue:Util.formatMilliseconds(timeInMs),
-extendedInfo:{
-value:extendedInfo}};
-
-
-});
-}}
-
-
-module.exports=ConsistentlyInteractiveMetric;
-
-
-
-
-
-
-
-let TimePeriod;
-
-},{"../lib/errors":28,"../lib/network-recorder":32,"../lib/traces/tracing-processor":40,"../report/v2/renderer/util":43,"./audit":2}],"../audits/content-width":[function(require,module,exports){
+},{"../../lib/url-shim":"url","./byte-efficiency-audit":3}],"../audits/content-width":[function(require,module,exports){
 
 
 
@@ -3419,11 +3563,16 @@
 
 return{
 rawValue:widthsMatch,
-debugString:this.createDebugString(widthsMatch,artifacts.ViewportDimensions)};
+explanation:this.createExplanation(widthsMatch,artifacts.ViewportDimensions)};
 
 }
 
-static createDebugString(match,artifact){
+
+
+
+
+
+static createExplanation(match,artifact){
 if(match){
 return'';
 }
@@ -3444,7 +3593,7 @@
 'use strict';
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util');
+const Util=require('../report/html/renderer/util');
 
 class CriticalRequestChains extends Audit{
 
@@ -3454,17 +3603,29 @@
 return{
 name:'critical-request-chains',
 description:'Critical Request Chains',
-informative:true,
+scoreDisplayMode:Audit.SCORING_MODES.INFORMATIVE,
 helpText:'The Critical Request Chains below show you what resources are '+
 'issued 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).',
-requiredArtifacts:['devtoolsLogs']};
+requiredArtifacts:['devtoolsLogs','URL']};
 
 }
 
+
+
+
+
+
+
 static _traverse(tree,cb){
+
+
+
+
+
+
 function walk(node,depth,startTime,transferSize=0){
 const children=Object.keys(node);
 if(children.length===0){
@@ -3497,6 +3658,7 @@
 
 
 
+
 static _getLongestChain(tree){
 const longest={
 duration:0,
@@ -3519,35 +3681,49 @@
 
 
 
-static flattenRequests(tree){
-const flattendChains={};
-const chainMap=new Map();
-CriticalRequestChains._traverse(tree,opts=>{
-let chain;
-if(chainMap.has(opts.id)){
-chain=chainMap.get(opts.id);
-}else{
-chain={};
-flattendChains[opts.id]=chain;
-}
 
+static flattenRequests(tree){
+
+const flattendChains={};
+
+const chainMap=new Map();
+
+
+function flatten(opts){
 const request=opts.node.request;
-chain.request={
+const simpleRequest={
 url:request.url,
 startTime:request.startTime,
 endTime:request.endTime,
-responseReceivedTime:request.responseReceivedTime,
+_responseReceivedTime:request._responseReceivedTime,
 transferSize:request.transferSize};
 
-chain.children={};
-Object.keys(opts.node.children).forEach(chainId=>{
-const childChain={};
+
+let chain=chainMap.get(opts.id);
+if(chain){
+chain.request=simpleRequest;
+}else{
+chain={
+request:simpleRequest,
+children:{}};
+
+flattendChains[opts.id]=chain;
+}
+
+for(const chainId of Object.keys(opts.node.children)){
+
+const childChain={
+request:{},
+children:{}};
+
 chainMap.set(chainId,childChain);
 chain.children[chainId]=childChain;
-});
+}
 
 chainMap.set(opts.id,chain);
-});
+}
+
+CriticalRequestChains._traverse(tree,flatten);
 
 return flattendChains;
 }
@@ -3558,9 +3734,14 @@
 
 
 static audit(artifacts){
-const devtoolsLogs=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
-return artifacts.requestCriticalRequestChains(devtoolsLogs).then(chains=>{
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const URL=artifacts.URL;
+return artifacts.requestCriticalRequestChains({devtoolsLog,URL}).then(chains=>{
 let chainCount=0;
+
+
+
+
 function walk(node,depth){
 const children=Object.keys(node);
 
@@ -3589,7 +3770,8 @@
 
 return{
 rawValue:chainCount===0,
-displayValue:Util.formatNumber(chainCount),
+notApplicable:chainCount===0,
+displayValue:chainCount?`${Util.formatNumber(chainCount)} chains found`:'',
 extendedInfo:{
 value:{
 chains:flattenedChains,
@@ -3609,7 +3791,7 @@
 
 module.exports=CriticalRequestChains;
 
-},{"../report/v2/renderer/util":43,"./audit":2}],"../audits/deprecations":[function(require,module,exports){
+},{"../report/html/renderer/util":48,"./audit":2}],"../audits/deprecations":[function(require,module,exports){
 
 
 
@@ -3624,7 +3806,7 @@
 
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util');
+const Util=require('../report/html/renderer/util');
 
 class Deprecations extends Audit{
 
@@ -3650,16 +3832,15 @@
 
 const deprecations=entries.filter(log=>log.entry.source==='deprecation').map(log=>{
 return{
-type:'code',
-text:log.entry.text,
-url:log.entry.url,
+value:log.entry.text,
+url:log.entry.url||'',
 source:log.entry.source,
 lineNumber:log.entry.lineNumber};
 
 });
 
 const headings=[
-{key:'text',itemType:'code',text:'Deprecation / Warning'},
+{key:'value',itemType:'code',text:'Deprecation / Warning'},
 {key:'url',itemType:'url',text:'URL'},
 {key:'lineNumber',itemType:'text',text:'Line'}];
 
@@ -3685,7 +3866,7 @@
 
 module.exports=Deprecations;
 
-},{"../report/v2/renderer/util":43,"./audit":2}],"../audits/dobetterweb/appcache-manifest":[function(require,module,exports){
+},{"../report/html/renderer/util":48,"./audit":2}],"../audits/dobetterweb/appcache-manifest":[function(require,module,exports){
 
 
 
@@ -3721,12 +3902,11 @@
 
 static audit(artifacts){
 const usingAppcache=artifacts.AppCacheManifest!==null;
-const debugString=usingAppcache?
-`Found <html manifest="${artifacts.AppCacheManifest}">.`:'';
+const displayValue=usingAppcache?`Found "${artifacts.AppCacheManifest}"`:'';
 
 return{
 rawValue:!usingAppcache,
-debugString};
+displayValue};
 
 }}
 
@@ -3749,16 +3929,12 @@
 'use strict';
 
 const Audit=require('../audit');
-const Util=require('../../report/v2/renderer/util.js');
+const Util=require('../../report/html/renderer/util.js');
 
 const MAX_DOM_NODES=1500;
 const MAX_DOM_TREE_WIDTH=60;
 const MAX_DOM_TREE_DEPTH=32;
 
-
-const SCORING_POINT_OF_DIMINISHING_RETURNS=2400;
-const SCORING_MEDIAN=3000;
-
 class DOMSize extends Audit{
 static get MAX_DOM_NODES(){
 return MAX_DOM_NODES;
@@ -3777,8 +3953,8 @@
 `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/fundamentals/performance/rendering/).',
-scoringMode:Audit.SCORING_MODES.NUMERIC,
+'and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://developers.google.com/web/tools/lighthouse/audits/dom-size).',
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
 requiredArtifacts:['DOMStats']};
 
 }
@@ -3786,67 +3962,71 @@
 
 
 
+static get defaultOptions(){
+return{
 
-static audit(artifacts){
+
+
+scorePODR:700,
+scoreMedian:1400};
+
+}
+
+
+
+
+
+
+
+static audit(artifacts,context){
 const stats=artifacts.DOMStats;
 
-
-
-
-
-
-
-const depthSnippet=stats.depth.pathToElement.reduce((str,curr,i)=>{
-return`${str}\n`+'  '.repeat(i)+`${curr} >`;
-},'').replace(/>$/g,'').trim();
-const widthSnippet='Element with most children:\n'+
-stats.width.pathToElement[stats.width.pathToElement.length-1];
-
-
-
-
-
 const score=Audit.computeLogNormalScore(
 stats.totalDOMNodes,
-SCORING_POINT_OF_DIMINISHING_RETURNS,
-SCORING_MEDIAN);
+context.options.scorePODR,
+context.options.scoreMedian);
 
 
-const cards=[{
-title:'Total DOM Nodes',
-value:Util.formatNumber(stats.totalDOMNodes),
-target:`< ${Util.formatNumber(MAX_DOM_NODES)} nodes`},
+const headings=[
+{key:'totalNodes',itemType:'text',text:'Total DOM Nodes'},
+{key:'depth',itemType:'text',text:'Maximum DOM Depth'},
+{key:'width',itemType:'text',text:'Maximum Children'}];
+
+
+const items=[
 {
-title:'DOM Depth',
-value:Util.formatNumber(stats.depth.max),
-snippet:depthSnippet,
-target:`< ${Util.formatNumber(MAX_DOM_TREE_DEPTH)}`},
+totalNodes:Util.formatNumber(stats.totalDOMNodes),
+depth:Util.formatNumber(stats.depth.max),
+width:Util.formatNumber(stats.width.max)},
+
 {
-title:'Maximum Children',
-value:Util.formatNumber(stats.width.max),
-snippet:widthSnippet,
-target:`< ${Util.formatNumber(MAX_DOM_TREE_WIDTH)} nodes`}];
+totalNodes:'',
+depth:{
+type:'code',
+value:stats.depth.snippet},
+
+width:{
+type:'code',
+value:stats.width.snippet}}];
+
+
 
 
 return{
 score,
 rawValue:stats.totalDOMNodes,
-displayValue:`${Util.formatNumber(stats.totalDOMNodes)} nodes`,
+displayValue:['%d nodes',stats.totalDOMNodes],
 extendedInfo:{
-value:cards},
+value:items},
 
-details:{
-type:'cards',
-header:{type:'text',text:'View details'},
-items:cards}};
-
+details:Audit.makeTableDetails(headings,items)};
 
 }}
 
 
 module.exports=DOMSize;
 
-},{"../../report/v2/renderer/util.js":43,"../audit":2}],"../audits/dobetterweb/external-anchors-use-rel-noopener":[function(require,module,exports){
+},{"../../report/html/renderer/util.js":48,"../audit":2}],"../audits/dobetterweb/external-anchors-use-rel-noopener":[function(require,module,exports){
 
 
 
@@ -3864,10 +4044,10 @@
 static get meta(){
 return{
 name:'external-anchors-use-rel-noopener',
-description:'Opens external anchors using `rel="noopener"`',
-failureDescription:'Does not open external anchors using `rel="noopener"`',
-helpText:'Open new tabs using `rel="noopener"` to improve performance and '+
-'prevent security vulnerabilities. '+
+description:'Links to cross-origin destinations are safe',
+failureDescription:'Links to cross-origin destinations are unsafe',
+helpText:'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).',
 requiredArtifacts:['URL','AnchorsWithNoRelNoopener']};
 
@@ -3878,7 +4058,8 @@
 
 
 static audit(artifacts){
-let debugString;
+
+const warnings=[];
 const pageHost=new URL(artifacts.URL.finalUrl).host;
 
 
@@ -3889,9 +4070,9 @@
 try{
 return new URL(anchor.href).host!==pageHost;
 }catch(err){
-debugString='Lighthouse was unable to determine the destination '+
-'of some anchor tags. If they are not used as hyperlinks, '+
-'consider removing the _blank target.';
+
+warnings.push('Unable to determine the destination for anchor tag. '+
+'If not used as a hyperlink, consider removing target=_blank.');
 return true;
 }
 }).
@@ -3924,14 +4105,14 @@
 value:failingAnchors},
 
 details,
-debugString};
+warnings};
 
 }}
 
 
 module.exports=ExternalAnchorsUseRelNoopenerAudit;
 
-},{"../../lib/url-shim":41,"../audit":2}],"../audits/dobetterweb/geolocation-on-start":[function(require,module,exports){
+},{"../../lib/url-shim":"url","../audit":2}],"../audits/dobetterweb/geolocation-on-start":[function(require,module,exports){
 
 
 
@@ -3975,6 +4156,8 @@
 {key:'url',itemType:'url',text:'URL'},
 {key:'label',itemType:'text',text:'Location'}];
 
+
+
 const details=ViolationAudit.makeTableDetails(headings,results);
 
 return{
@@ -3989,125 +4172,7 @@
 
 module.exports=GeolocationOnStart;
 
-},{"../violation-audit":6}],"../audits/dobetterweb/link-blocking-first-paint":[function(require,module,exports){
-
-
-
-
-
-
-
-
-
-
-'use strict';
-
-const Audit=require('../audit');
-const Util=require('../../report/v2/renderer/util.js');
-const scoreForWastedMs=require('../byte-efficiency/byte-efficiency-audit').scoreForWastedMs;
-
-
-
-
-
-const LOAD_THRESHOLD_IN_MS=50;
-
-class LinkBlockingFirstPaintAudit extends Audit{
-
-
-
-static get meta(){
-return{
-name:'link-blocking-first-paint',
-description:'Reduce render-blocking stylesheets',
-informative:true,
-helpText:'External stylesheets are blocking the first paint of your page. Consider '+
-'delivering critical CSS via `<style>` tags and deferring non-critical '+
-'styles. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources).',
-requiredArtifacts:['TagsBlockingFirstPaint','traces']};
-
-}
-
-
-
-
-
-
-
-
-
-
-static computeAuditResultForTags(artifacts,tagFilter,endTimeMax=Infinity,
-loadDurationThreshold=0){
-const artifact=artifacts.TagsBlockingFirstPaint;
-
-const filtered=artifact.filter(item=>{
-return item.tag.tagName===tagFilter&&
-(item.endTime-item.startTime)*1000>=loadDurationThreshold&&
-item.endTime*1000<endTimeMax;
-});
-
-const startTime=filtered.length===0?0:
-filtered.reduce((t,item)=>Math.min(t,item.startTime),Number.MAX_VALUE);
-let endTime=0;
-
-const results=filtered.map(item=>{
-endTime=Math.max(item.endTime,endTime);
-
-return{
-url:item.tag.url,
-totalKb:Util.formatBytesToKB(item.transferSize),
-totalMs:Util.formatMilliseconds(Math.round((item.endTime-startTime)*1000),1)};
-
-});
-
-const rawDelayTime=Math.round((endTime-startTime)*1000);
-const delayTime=Util.formatMilliseconds(rawDelayTime,1);
-let displayValue='';
-if(results.length>1){
-displayValue=`${results.length} resources delayed first paint by ${delayTime}`;
-}else if(results.length===1){
-displayValue=`${results.length} resource delayed first paint by ${delayTime}`;
-}
-
-const headings=[
-{key:'url',itemType:'url',text:'URL'},
-{key:'totalKb',itemType:'text',text:'Size (KB)'},
-{key:'totalMs',itemType:'text',text:'Delayed Paint By (ms)'}];
-
-
-const tableDetails=Audit.makeTableDetails(headings,results);
-
-return{
-displayValue,
-score:scoreForWastedMs(rawDelayTime),
-rawValue:rawDelayTime,
-extendedInfo:{
-value:{
-wastedMs:delayTime,
-results}},
-
-
-details:tableDetails};
-
-}
-
-
-
-
-
-static audit(artifacts){
-const trace=artifacts.traces[Audit.DEFAULT_PASS];
-return artifacts.requestTraceOfTab(trace).then(traceOfTab=>{
-const fcpTsInMs=traceOfTab.timestamps.firstContentfulPaint/1000;
-return this.computeAuditResultForTags(artifacts,'LINK',fcpTsInMs,LOAD_THRESHOLD_IN_MS);
-});
-}}
-
-
-module.exports=LinkBlockingFirstPaintAudit;
-
-},{"../../report/v2/renderer/util.js":43,"../audit":2,"../byte-efficiency/byte-efficiency-audit":3}],"../audits/dobetterweb/no-document-write":[function(require,module,exports){
+},{"../violation-audit":6}],"../audits/dobetterweb/no-document-write":[function(require,module,exports){
 
 
 
@@ -4149,6 +4214,7 @@
 {key:'url',itemType:'url',text:'URL'},
 {key:'label',itemType:'text',text:'Location'}];
 
+
 const details=ViolationAudit.makeTableDetails(headings,results);
 
 return{
@@ -4215,7 +4281,8 @@
 
 
 static audit(artifacts){
-let debugString;
+
+const warnings=[];
 const listeners=artifacts.EventListeners;
 
 const results=listeners.filter(loc=>{
@@ -4224,7 +4291,7 @@
 
 if(!URL.isValid(loc.url)&&isMutationEvent){
 sameHost=true;
-debugString=URL.INVALID_URL_DEBUG_STRING;
+warnings.push(URL.INVALID_URL_DEBUG_STRING);
 }
 
 return sameHost&&isMutationEvent;
@@ -4249,14 +4316,14 @@
 
 
 details,
-debugString};
+warnings};
 
 }}
 
 
 module.exports=NoMutationEventsAudit;
 
-},{"../../lib/event-helpers":29,"../../lib/url-shim":41,"../audit":2}],"../audits/dobetterweb/no-vulnerable-libraries":[function(require,module,exports){
+},{"../../lib/event-helpers":34,"../../lib/url-shim":"url","../audit":2}],"../audits/dobetterweb/no-vulnerable-libraries":[function(require,module,exports){
 
 
 
@@ -4274,10 +4341,14 @@
 const Audit=require('../audit');
 const Sentry=require('../../lib/sentry');
 const semver=require('semver');
+
 const snykDatabase=require('../../../third-party/snyk/snapshot.json');
 
 const SEMVER_REGEX=/^(\d+\.\d+\.\d+)[^-0-9]+/;
 
+
+
+
 class NoVulnerableLibrariesAudit extends Audit{
 
 
@@ -4325,7 +4396,8 @@
 
 if(/^\d+\.\d+$/.test(version))return`${version}.0`;
 
-if(SEMVER_REGEX.test(version))return version.match(SEMVER_REGEX)[1];
+const versionMatch=version.match(SEMVER_REGEX);
+if(versionMatch)return versionMatch[1];
 
 return version;
 }
@@ -4335,34 +4407,35 @@
 
 
 
-static getVulns(lib,snykDB){
-const vulns=[];
-if(!snykDB.npm[lib.npmPkgName]){
-return vulns;
+static getVulnerabilities(normalizedVersion,lib){
+const snykDB=NoVulnerableLibrariesAudit.snykDB;
+if(!lib.npmPkgName||!snykDB.npm[lib.npmPkgName]){
+return[];
 }
 
 try{
-semver.satisfies(lib.version,'*');
+semver.satisfies(normalizedVersion,'*');
 }catch(err){
 err.pkgName=lib.npmPkgName;
 
+
 Sentry.captureException(err,{level:'warning'});
-return vulns;
+return[];
 }
 
-lib.pkgLink=`https://snyk.io/vuln/npm:${lib.npmPkgName}?lh@${lib.version}`;
 const snykInfo=snykDB.npm[lib.npmPkgName];
-snykInfo.forEach(vuln=>{
-if(semver.satisfies(lib.version,vuln.semver.vulnerable[0])){
+const vulns=snykInfo.
+filter(vuln=>semver.satisfies(normalizedVersion,vuln.semver.vulnerable[0])).
 
-vulns.push({
+map(vuln=>{
+return{
 severity:vuln.severity,
 numericSeverity:this.severityMap[vuln.severity],
-library:`${lib.name}@${lib.version}`,
-url:'https://snyk.io/vuln/'+vuln.id});
+library:`${lib.name}@${normalizedVersion}`,
+url:'https://snyk.io/vuln/'+vuln.id};
 
-}
 });
+
 return vulns;
 }
 
@@ -4370,8 +4443,8 @@
 
 
 
-static highestSeverity(vulns){
-const sortedVulns=vulns.
+static highestSeverity(vulnerabilities){
+const sortedVulns=vulnerabilities.
 sort((a,b)=>b.numericSeverity-a.numericSeverity);
 return sortedVulns[0].severity;
 }
@@ -4381,37 +4454,52 @@
 
 
 static audit(artifacts){
-const libraries=artifacts.JSLibraries;
-if(!libraries.length){
+const foundLibraries=artifacts.JSLibraries;
+if(!foundLibraries.length){
 return{
 rawValue:true};
 
 }
 
 let totalVulns=0;
-const finalVulns=libraries.map(lib=>{
-lib.version=this.normalizeVersion(lib.version);
-lib.vulns=this.getVulns(lib,this.snykDB);
-if(lib.vulns.length>0){
-lib.vulnCount=lib.vulns.length;
-lib.highestSeverity=this.highestSeverity(lib.vulns).replace(/^\w/,l=>l.toUpperCase());
-totalVulns+=lib.vulnCount;
-lib.detectedLib={};
-lib.detectedLib.text=lib.name+'@'+lib.version;
-lib.detectedLib.url=lib.pkgLink;
-lib.detectedLib.type='link';
+
+const vulnerabilityResults=[];
+
+const libraryVulns=foundLibraries.map(lib=>{
+const version=this.normalizeVersion(lib.version)||'';
+const vulns=this.getVulnerabilities(version,lib);
+const vulnCount=vulns.length;
+totalVulns+=vulnCount;
+
+let highestSeverity;
+if(vulns.length>0){
+highestSeverity=this.highestSeverity(vulns).replace(/^\w/,l=>l.toUpperCase());
+
+vulnerabilityResults.push({
+highestSeverity,
+vulnCount,
+detectedLib:{
+text:lib.name+'@'+version,
+url:`https://snyk.io/vuln/npm:${lib.npmPkgName}?lh@${version}`,
+type:'link'}});
+
+
 }
-return lib;
-}).
-filter(obj=>{
-return obj.vulns.length>0;
+
+return{
+name:lib.name,
+npmPkgName:lib.npmPkgName,
+version,
+vulns,
+highestSeverity};
+
 });
 
 let displayValue='';
 if(totalVulns>1){
-displayValue=`${totalVulns} vulnerabilities detected.`;
+displayValue=`${totalVulns} vulnerabilities detected`;
 }else if(totalVulns===1){
-displayValue=`${totalVulns} vulnerability was detected.`;
+displayValue=`${totalVulns} vulnerability detected`;
 }
 
 const headings=[
@@ -4419,14 +4507,14 @@
 {key:'vulnCount',itemType:'text',text:'Vulnerability Count'},
 {key:'highestSeverity',itemType:'text',text:'Highest Severity'}];
 
-const details=Audit.makeTableDetails(headings,finalVulns);
+const details=Audit.makeTableDetails(headings,vulnerabilityResults,{});
 
 return{
 rawValue:totalVulns===0,
 displayValue,
 extendedInfo:{
-jsLibs:libraries,
-vulnerabilities:finalVulns},
+jsLibs:libraryVulns,
+vulnerabilities:vulnerabilityResults},
 
 details};
 
@@ -4435,7 +4523,7 @@
 
 module.exports=NoVulnerableLibrariesAudit;
 
-},{"../../../third-party/snyk/snapshot.json":147,"../../lib/sentry":33,"../audit":2,"semver":142}],"../audits/dobetterweb/no-websql":[function(require,module,exports){
+},{"../../../third-party/snyk/snapshot.json":155,"../../lib/sentry":39,"../audit":2,"semver":150}],"../audits/dobetterweb/no-websql":[function(require,module,exports){
 
 
 
@@ -4472,12 +4560,12 @@
 
 static audit(artifacts){
 const db=artifacts.WebSQL;
-const debugString=db?
-`Found database "${db.name}", version: ${db.version}.`:'';
+const displayValue=db?
+`Found "${db.name}" (v${db.version})`:'';
 
 return{
 rawValue:!db,
-debugString};
+displayValue};
 
 }}
 
@@ -4527,6 +4615,7 @@
 {key:'url',itemType:'url',text:'URL'},
 {key:'label',itemType:'text',text:'Location'}];
 
+
 const details=ViolationAudit.makeTableDetails(headings,results);
 
 return{
@@ -4561,7 +4650,7 @@
 description:'Allows users to paste into password fields',
 failureDescription:'Prevents users to paste into password fields',
 helpText:'Preventing password pasting undermines good security policy. '+
-'[Learn more](https://www.ncsc.gov.uk/blog-post/let-them-paste-passwords)',
+'[Learn more](https://www.ncsc.gov.uk/blog-post/let-them-paste-passwords).',
 requiredArtifacts:['PasswordInputsWithPreventedPaste']};
 
 }
@@ -4573,76 +4662,31 @@
 static audit(artifacts){
 const passwordInputsWithPreventedPaste=artifacts.PasswordInputsWithPreventedPaste;
 
+
+const items=[];
+passwordInputsWithPreventedPaste.forEach(input=>{
+items.push({
+node:{type:'node',snippet:input.snippet}});
+
+});
+
+const headings=[
+{key:'node',itemType:'node',text:'Failing Elements'}];
+
+
 return{
 rawValue:passwordInputsWithPreventedPaste.length===0,
 extendedInfo:{
 value:passwordInputsWithPreventedPaste},
 
-details:{
-type:'list',
-header:{
-type:'text',
-text:'Password inputs that prevent pasting into'},
-
-items:passwordInputsWithPreventedPaste.map(input=>({
-type:'text',
-text:input.snippet}))}};
-
-
+details:Audit.makeTableDetails(headings,items)};
 
 }}
 
 
 module.exports=PasswordInputsCanBePastedIntoAudit;
 
-},{"../audit":2}],"../audits/dobetterweb/script-blocking-first-paint":[function(require,module,exports){
-
-
-
-
-
-
-
-
-
-
-'use strict';
-
-const Audit=require('../audit');
-const LinkBlockingFirstPaintAudit=require('./link-blocking-first-paint');
-
-class ScriptBlockingFirstPaint extends Audit{
-
-
-
-static get meta(){
-return{
-name:'script-blocking-first-paint',
-description:'Reduce render-blocking scripts',
-informative:true,
-helpText:'Script elements are blocking the first paint of your page. Consider inlining '+
-'critical scripts and deferring non-critical ones. '+
-'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources).',
-requiredArtifacts:['TagsBlockingFirstPaint','traces']};
-
-}
-
-
-
-
-
-static audit(artifacts){
-const trace=artifacts.traces[Audit.DEFAULT_PASS];
-return artifacts.requestTraceOfTab(trace).then(traceOfTab=>{
-const fcpTsInMs=traceOfTab.timestamps.firstContentfulPaint/1000;
-return LinkBlockingFirstPaintAudit.computeAuditResultForTags(artifacts,'SCRIPT',fcpTsInMs);
-});
-}}
-
-
-module.exports=ScriptBlockingFirstPaint;
-
-},{"../audit":2,"./link-blocking-first-paint":"../audits/dobetterweb/link-blocking-first-paint"}],"../audits/dobetterweb/uses-http2":[function(require,module,exports){
+},{"../audit":2}],"../audits/dobetterweb/uses-http2":[function(require,module,exports){
 
 
 
@@ -4658,7 +4702,7 @@
 
 const URL=require('../../lib/url-shim');
 const Audit=require('../audit');
-const Util=require('../../report/v2/renderer/util.js');
+const Util=require('../../report/html/renderer/util.js');
 
 class UsesHTTP2Audit extends Audit{
 
@@ -4684,6 +4728,7 @@
 return artifacts.requestNetworkRecords(devtoolsLogs).then(networkRecords=>{
 const finalHost=new URL(artifacts.URL.finalUrl).host;
 
+const seenURLs=new Set();
 
 const resources=networkRecords.filter(record=>{
 
@@ -4694,16 +4739,20 @@
 }).map(record=>{
 return{
 protocol:record.protocol,
-url:record.url};
+url:record._url};
 
+}).filter(record=>{
+if(seenURLs.has(record.url))return false;
+seenURLs.add(record.url);
+return true;
 });
 
 let displayValue='';
 if(resources.length>1){
 displayValue=
-`${Util.formatNumber(resources.length)} requests were not handled over HTTP/2`;
+`${Util.formatNumber(resources.length)} requests not served via HTTP/2`;
 }else if(resources.length===1){
-displayValue=`${resources.length} request was not handled over HTTP/2`;
+displayValue=`${resources.length} request not served via HTTP/2`;
 }
 
 const headings=[
@@ -4728,7 +4777,7 @@
 
 module.exports=UsesHTTP2Audit;
 
-},{"../../lib/url-shim":41,"../../report/v2/renderer/util.js":43,"../audit":2}],"../audits/dobetterweb/uses-passive-event-listeners":[function(require,module,exports){
+},{"../../lib/url-shim":"url","../../report/html/renderer/util.js":48,"../audit":2}],"../audits/dobetterweb/uses-passive-event-listeners":[function(require,module,exports){
 
 
 
@@ -4771,6 +4820,7 @@
 {key:'url',itemType:'url',text:'URL'},
 {key:'label',itemType:'text',text:'Location'}];
 
+
 const details=ViolationAudit.makeTableDetails(headings,results);
 
 return{
@@ -4822,6 +4872,7 @@
 static audit(artifacts){
 const consoleEntries=artifacts.ChromeConsoleMessages;
 const runtimeExceptions=artifacts.RuntimeExceptions;
+
 const consoleRows=
 consoleEntries.filter(log=>log.entry&&log.entry.level==='error').
 map(item=>{
@@ -4856,7 +4907,7 @@
 const numErrors=tableRows.length;
 
 return{
-score:numErrors===0,
+score:Number(numErrors===0),
 rawValue:numErrors,
 details};
 
@@ -4874,14 +4925,6 @@
 'use strict';
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util');
-const TracingProcessor=require('../lib/traces/tracing-processor');
-const LHError=require('../lib/errors');
-
-
-
-const SCORING_POINT_OF_DIMINISHING_RETURNS=50;
-const SCORING_MEDIAN=100;
 
 class EstimatedInputLatency extends Audit{
 
@@ -4892,24 +4935,25 @@
 name:'estimated-input-latency',
 description:'Estimated Input Latency',
 helpText:'The score above is an estimate of how long your app takes to respond to user '+
-'input, in milliseconds. There is a 90% probability that a user encounters this amount '+
-'of latency, or less. 10% of the time a user can expect additional latency. If your '+
+'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).',
-scoringMode:Audit.SCORING_MODES.NUMERIC,
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
 requiredArtifacts:['traces']};
 
 }
 
-static calculate(tabTrace){
-const startTime=tabTrace.timings.firstMeaningfulPaint;
-if(!startTime){
-throw new LHError(LHError.errors.NO_FMP);
+
+
+
+static get defaultOptions(){
+return{
+
+scorePODR:50,
+scoreMedian:100};
+
 }
 
-const latencyPercentiles=TracingProcessor.getRiskToResponsiveness(tabTrace,startTime);
-const ninetieth=latencyPercentiles.find(result=>result.percentile===0.9);
-const rawValue=parseFloat(ninetieth.time.toFixed(1));
 
 
 
@@ -4917,39 +4961,28 @@
 
 
 
-const score=Audit.computeLogNormalScore(
-ninetieth.time,
-SCORING_POINT_OF_DIMINISHING_RETURNS,
-SCORING_MEDIAN);
 
+static async audit(artifacts,context){
+const trace=artifacts.traces[Audit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const metricComputationData={trace,devtoolsLog,settings:context.settings};
+const metricResult=await artifacts.requestEstimatedInputLatency(metricComputationData);
 
 return{
-score,
-rawValue,
-displayValue:Util.formatMilliseconds(rawValue,1),
-extendedInfo:{
-value:latencyPercentiles}};
+score:Audit.computeLogNormalScore(
+metricResult.timing,
+context.options.scorePODR,
+context.options.scoreMedian),
 
+rawValue:metricResult.timing,
+displayValue:['%d\xa0ms',metricResult.timing]};
 
-}
-
-
-
-
-
-
-
-static audit(artifacts){
-const trace=artifacts.traces[this.DEFAULT_PASS];
-
-return artifacts.requestTraceOfTab(trace).
-then(EstimatedInputLatency.calculate);
 }}
 
 
 module.exports=EstimatedInputLatency;
 
-},{"../lib/errors":28,"../lib/traces/tracing-processor":40,"../report/v2/renderer/util":43,"./audit":2}],"../audits/first-interactive":[function(require,module,exports){
+},{"./audit":2}],"../audits/first-contentful-paint":[function(require,module,exports){
 
 
 
@@ -4958,25 +4991,84 @@
 'use strict';
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util.js');
+const Util=require('../report/html/renderer/util.js');
 
-
-
-const SCORING_POINT_OF_DIMINISHING_RETURNS=1700;
-const SCORING_MEDIAN=10000;
-
-class FirstInteractiveMetric extends Audit{
+class FirstContentfulPaint extends Audit{
 
 
 
 static get meta(){
 return{
-name:'first-interactive',
-description:'First Interactive (beta)',
-helpText:'First Interactive marks the time at which the page is '+
-'minimally interactive. '+
+name:'first-contentful-paint',
+description:'First Contentful Paint',
+helpText:'First contentful paint marks the time at which the first text/image is painted. '+
+`[Learn more](https://developers.google.com/web/fundamentals/performance/user-centric-performance-metrics#first_paint_and_first_contentful_paint).`,
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
+requiredArtifacts:['traces','devtoolsLogs']};
+
+}
+
+
+
+
+static get defaultOptions(){
+return{
+
+
+
+scorePODR:2900,
+scoreMedian:4000};
+
+}
+
+
+
+
+
+
+static async audit(artifacts,context){
+const trace=artifacts.traces[Audit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const metricComputationData={trace,devtoolsLog,settings:context.settings};
+const metricResult=await artifacts.requestFirstContentfulPaint(metricComputationData);
+
+return{
+score:Audit.computeLogNormalScore(
+metricResult.timing,
+context.options.scorePODR,
+context.options.scoreMedian),
+
+rawValue:metricResult.timing,
+displayValue:[Util.MS_DISPLAY_VALUE,metricResult.timing]};
+
+}}
+
+
+module.exports=FirstContentfulPaint;
+
+},{"../report/html/renderer/util.js":48,"./audit":2}],"../audits/first-cpu-idle":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Audit=require('./audit');
+const Util=require('../report/html/renderer/util.js');
+
+class FirstCPUIdle extends Audit{
+
+
+
+static get meta(){
+return{
+name:'first-cpu-idle',
+description:'First CPU Idle',
+helpText:'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).',
-scoringMode:Audit.SCORING_MODES.NUMERIC,
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
 requiredArtifacts:['traces']};
 
 }
@@ -4984,33 +5076,45 @@
 
 
 
+static get defaultOptions(){
+return{
+
+
+
+scorePODR:2900,
+scoreMedian:6500};
+
+}
+
+
+
+
 
 
 
 
-static audit(artifacts){
+
+static async audit(artifacts,context){
 const trace=artifacts.traces[Audit.DEFAULT_PASS];
-return artifacts.requestFirstInteractive(trace).
-then(firstInteractive=>{
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const metricComputationData={trace,devtoolsLog,settings:context.settings};
+const metricResult=await artifacts.requestFirstCPUIdle(metricComputationData);
+
 return{
 score:Audit.computeLogNormalScore(
-firstInteractive.timeInMs,
-SCORING_POINT_OF_DIMINISHING_RETURNS,
-SCORING_MEDIAN),
+metricResult.timing,
+context.options.scorePODR,
+context.options.scoreMedian),
 
-rawValue:firstInteractive.timeInMs,
-displayValue:Util.formatMilliseconds(firstInteractive.timeInMs),
-extendedInfo:{
-value:firstInteractive}};
+rawValue:metricResult.timing,
+displayValue:[Util.MS_DISPLAY_VALUE,metricResult.timing]};
 
-
-});
 }}
 
 
-module.exports=FirstInteractiveMetric;
+module.exports=FirstCPUIdle;
 
-},{"../report/v2/renderer/util.js":43,"./audit":2}],"../audits/first-meaningful-paint":[function(require,module,exports){
+},{"../report/html/renderer/util.js":48,"./audit":2}],"../audits/first-meaningful-paint":[function(require,module,exports){
 
 
 
@@ -5019,14 +5123,7 @@
 'use strict';
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util');
-const LHError=require('../lib/errors');
-
-
-
-const SCORING_POINT_OF_DIMINISHING_RETURNS=1600;
-const SCORING_MEDIAN=4000;
-
+const Util=require('../report/html/renderer/util');
 
 class FirstMeaningfulPaint extends Audit{
 
@@ -5035,10 +5132,10 @@
 static get meta(){
 return{
 name:'first-meaningful-paint',
-description:'First meaningful paint',
-helpText:'First meaningful paint measures when the primary content of a page is visible. '+
+description:'First Meaningful Paint',
+helpText:'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).',
-scoringMode:Audit.SCORING_MODES.NUMERIC,
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
 requiredArtifacts:['traces']};
 
 }
@@ -5046,90 +5143,45 @@
 
 
 
+static get defaultOptions(){
+return{
 
 
 
+scorePODR:2000,
+scoreMedian:4000};
 
-static audit(artifacts){
-const trace=artifacts.traces[this.DEFAULT_PASS];
-return artifacts.requestTraceOfTab(trace).then(tabTrace=>{
-if(!tabTrace.firstMeaningfulPaintEvt){
-throw new LHError(LHError.errors.NO_FMP);
 }
 
 
 
-if(!tabTrace.navigationStartEvt){
-throw new LHError(LHError.errors.NO_NAVSTART);
-}
 
-const result=this.calculateScore(tabTrace);
+
+
+
+
+
+static async audit(artifacts,context){
+const trace=artifacts.traces[Audit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const metricComputationData={trace,devtoolsLog,settings:context.settings};
+const metricResult=await artifacts.requestFirstMeaningfulPaint(metricComputationData);
 
 return{
-score:result.score,
-rawValue:parseFloat(result.duration),
-displayValue:Util.formatMilliseconds(result.duration),
-debugString:result.debugString,
-extendedInfo:{
-value:result.extendedInfo}};
+score:Audit.computeLogNormalScore(
+metricResult.timing,
+context.options.scorePODR,
+context.options.scoreMedian),
 
-
-});
-}
-
-static calculateScore(traceOfTab){
-
-const extendedInfo={
-timestamps:{
-navStart:traceOfTab.timestamps.navigationStart,
-fCP:traceOfTab.timestamps.firstContentfulPaint,
-fMP:traceOfTab.timestamps.firstMeaningfulPaint,
-onLoad:traceOfTab.timestamps.onLoad,
-endOfTrace:traceOfTab.timestamps.traceEnd},
-
-timings:{
-navStart:0,
-fCP:traceOfTab.timings.firstContentfulPaint,
-fMP:traceOfTab.timings.firstMeaningfulPaint,
-onLoad:traceOfTab.timings.onLoad,
-endOfTrace:traceOfTab.timings.traceEnd},
-
-fmpFellBack:traceOfTab.fmpFellBack};
-
-
-Object.keys(extendedInfo.timings).forEach(key=>{
-const val=extendedInfo.timings[key];
-if(typeof val!=='number'||Number.isNaN(val)){
-extendedInfo.timings[key]=undefined;
-extendedInfo.timestamps[key]=undefined;
-}else{
-extendedInfo.timings[key]=parseFloat(extendedInfo.timings[key].toFixed(3));
-}
-});
-
-
-
-
-
-const firstMeaningfulPaint=traceOfTab.timings.firstMeaningfulPaint;
-const score=Audit.computeLogNormalScore(
-firstMeaningfulPaint,
-SCORING_POINT_OF_DIMINISHING_RETURNS,
-SCORING_MEDIAN);
-
-
-return{
-score,
-duration:firstMeaningfulPaint.toFixed(1),
-rawValue:firstMeaningfulPaint,
-extendedInfo};
+rawValue:metricResult.timing,
+displayValue:[Util.MS_DISPLAY_VALUE,metricResult.timing]};
 
 }}
 
 
 module.exports=FirstMeaningfulPaint;
 
-},{"../lib/errors":28,"../report/v2/renderer/util":43,"./audit":2}],"../audits/font-display":[function(require,module,exports){
+},{"../report/html/renderer/util":48,"./audit":2}],"../audits/font-display":[function(require,module,exports){
 
 
 
@@ -5138,7 +5190,6 @@
 'use strict';
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util');
 const WebInspector=require('../lib/web-inspector');
 const allowedFontFaceDisplays=['block','fallback','optional','swap'];
 
@@ -5150,7 +5201,7 @@
 return{
 name:'font-display',
 description:'All text remains visible during webfont loads',
-failureDescription:'Avoid invisible text while webfonts are loading',
+failureDescription:'Text is invisible while webfonts are loading',
 helpText:'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).',
@@ -5180,29 +5231,29 @@
 filter(fontRecord=>{
 
 return!!fontsWithoutProperDisplay.find(fontFace=>{
-return fontFace.src.find(src=>fontRecord.url===src);
+return!!fontFace.src&&!!fontFace.src.find(src=>fontRecord.url===src);
 });
 }).
 
 map(record=>{
 
 
-const wastedTime=Math.min((record._endTime-record._startTime)*1000,3000);
+const wastedTime=Math.min((record.endTime-record.startTime)*1000,3000);
 
 return{
 url:record.url,
-wastedTime:Util.formatMilliseconds(wastedTime,1)};
+wastedTime};
 
 });
 
 const headings=[
 {key:'url',itemType:'url',text:'Font URL'},
-{key:'wastedTime',itemType:'text',text:'Font download time'}];
+{key:'wastedTime',itemType:'ms',granularity:1,text:'Font download time'}];
 
 const details=Audit.makeTableDetails(headings,results);
 
 return{
-score:results.length===0,
+score:Number(results.length===0),
 rawValue:results.length===0,
 details};
 
@@ -5212,7 +5263,7 @@
 
 module.exports=FontDisplay;
 
-},{"../lib/web-inspector":42,"../report/v2/renderer/util":43,"./audit":2}],"../audits/image-aspect-ratio":[function(require,module,exports){
+},{"../lib/web-inspector":47,"./audit":2}],"../audits/image-aspect-ratio":[function(require,module,exports){
 
 
 
@@ -5231,6 +5282,8 @@
 const URL=require('../lib/url-shim');
 const THRESHOLD=0.05;
 
+
+
 class ImageAspectRatio extends Audit{
 
 
@@ -5262,11 +5315,6 @@
 
 return{
 url,
-preview:{
-type:'thumbnail',
-url:image.networkRecord.url,
-mimeType:image.networkRecord.mimeType},
-
 displayedAspectRatio:`${image.width} x ${image.height}
         (${displayedAspectRatio.toFixed(2)})`,
 actualAspectRatio:`${image.naturalWidth} x ${image.naturalHeight}
@@ -5282,7 +5330,9 @@
 static audit(artifacts){
 const images=artifacts.ImageUsage;
 
-let debugString;
+
+const warnings=[];
+
 const results=[];
 images.filter(image=>{
 
@@ -5294,9 +5344,10 @@
 image.height&&
 !image.usesObjectFit;
 }).forEach(image=>{
-const processed=ImageAspectRatio.computeAspectRatios(image);
+const wellDefinedImage=image;
+const processed=ImageAspectRatio.computeAspectRatios(wellDefinedImage);
 if(processed instanceof Error){
-debugString=processed.message;
+warnings.push(processed.message);
 return;
 }
 
@@ -5304,7 +5355,7 @@
 });
 
 const headings=[
-{key:'preview',itemType:'thumbnail',text:''},
+{key:'url',itemType:'thumbnail',text:''},
 {key:'url',itemType:'url',text:'URL'},
 {key:'displayedAspectRatio',itemType:'text',text:'Aspect Ratio (Displayed)'},
 {key:'actualAspectRatio',itemType:'text',text:'Aspect Ratio (Actual)'}];
@@ -5312,7 +5363,7 @@
 
 return{
 rawValue:results.length===0,
-debugString,
+warnings,
 details:Audit.makeTableDetails(headings,results)};
 
 }}
@@ -5320,7 +5371,89 @@
 
 module.exports=ImageAspectRatio;
 
-},{"../lib/url-shim":41,"./audit":2}],"../audits/is-on-https":[function(require,module,exports){
+},{"../lib/url-shim":"url","./audit":2}],"../audits/interactive":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Audit=require('./audit');
+const Util=require('../report/html/renderer/util');
+
+
+
+
+
+
+
+class InteractiveMetric extends Audit{
+
+
+
+static get meta(){
+return{
+name:'interactive',
+description:'Time to Interactive',
+helpText:'Interactive marks the time at which the page is fully interactive. '+
+'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/consistently-interactive).',
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
+requiredArtifacts:['traces','devtoolsLogs']};
+
+}
+
+
+
+
+static get defaultOptions(){
+return{
+
+
+
+scorePODR:2900,
+scoreMedian:7300};
+
+}
+
+
+
+
+
+
+static async audit(artifacts,context){
+const trace=artifacts.traces[Audit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const metricComputationData={trace,devtoolsLog,settings:context.settings};
+const metricResult=await artifacts.requestInteractive(metricComputationData);
+const timeInMs=metricResult.timing;
+const extendedInfo={
+timeInMs,
+timestamp:metricResult.timestamp,
+
+optimistic:metricResult.optimisticEstimate&&metricResult.optimisticEstimate.timeInMs,
+
+pessimistic:metricResult.pessimisticEstimate&&metricResult.pessimisticEstimate.timeInMs};
+
+
+return{
+score:Audit.computeLogNormalScore(
+timeInMs,
+context.options.scorePODR,
+context.options.scoreMedian),
+
+rawValue:timeInMs,
+displayValue:[Util.MS_DISPLAY_VALUE,timeInMs],
+extendedInfo:{
+value:extendedInfo}};
+
+
+}}
+
+
+module.exports=InteractiveMetric;
+
+},{"../report/html/renderer/util":48,"./audit":2}],"../audits/is-on-https":[function(require,module,exports){
 
 
 
@@ -5330,9 +5463,9 @@
 
 const Audit=require('./audit');
 const URL=require('../lib/url-shim');
-const Util=require('../report/v2/renderer/util');
+const Util=require('../report/html/renderer/util');
 
-const SECURE_SCHEMES=['data','https','wss','blob','chrome','chrome-extension'];
+const SECURE_SCHEMES=['data','https','wss','blob','chrome','chrome-extension','about'];
 const SECURE_DOMAINS=['localhost','127.0.0.1'];
 
 class HTTPS extends Audit{
@@ -5358,9 +5491,9 @@
 
 
 static isSecureRecord(record){
-return SECURE_SCHEMES.includes(record.scheme)||
+return SECURE_SCHEMES.includes(record.parsedURL.scheme)||
 SECURE_SCHEMES.includes(record.protocol)||
-SECURE_DOMAINS.includes(record.domain);
+SECURE_DOMAINS.includes(record.parsedURL.host);
 }
 
 
@@ -5370,28 +5503,30 @@
 static audit(artifacts){
 const devtoolsLogs=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
 return artifacts.requestNetworkRecords(devtoolsLogs).then(networkRecords=>{
-const insecureRecords=networkRecords.
+const insecureURLs=networkRecords.
 filter(record=>!HTTPS.isSecureRecord(record)).
-map(record=>({url:URL.elideDataURI(record.url)}));
+map(record=>URL.elideDataURI(record.url));
 
 let displayValue='';
-if(insecureRecords.length>1){
-displayValue=`${Util.formatNumber(insecureRecords.length)} insecure requests found`;
-}else if(insecureRecords.length===1){
-displayValue=`${insecureRecords.length} insecure request found`;
+if(insecureURLs.length>1){
+displayValue=`${Util.formatNumber(insecureURLs.length)} insecure requests found`;
+}else if(insecureURLs.length===1){
+displayValue=`${insecureURLs.length} insecure request found`;
 }
 
+const items=Array.from(new Set(insecureURLs)).map(url=>({url}));
+
+const headings=[
+{key:'url',itemType:'url',text:'Insecure URL'}];
+
+
 return{
-rawValue:insecureRecords.length===0,
+rawValue:items.length===0,
 displayValue,
 extendedInfo:{
-value:insecureRecords},
+value:items},
 
-details:{
-type:'list',
-header:{type:'text',text:'Insecure URLs:'},
-items:insecureRecords.map(record=>({type:'url',text:record.url}))}};
-
+details:Audit.makeTableDetails(headings,items)};
 
 });
 }}
@@ -5399,7 +5534,7 @@
 
 module.exports=HTTPS;
 
-},{"../lib/url-shim":41,"../report/v2/renderer/util":43,"./audit":2}],"../audits/load-fast-enough-for-pwa":[function(require,module,exports){
+},{"../lib/url-shim":"url","../report/html/renderer/util":48,"./audit":2}],"../audits/load-fast-enough-for-pwa":[function(require,module,exports){
 
 
 
@@ -5413,17 +5548,13 @@
 
 
 
+const isDeepEqual=require('lodash.isequal');
 const Audit=require('./audit');
-const URL=require('../lib/url-shim');
-const Emulation=require('../lib/emulation');
-const Sentry=require('../lib/sentry');
-const Util=require('../report/v2/renderer/util.js');
+const mobile3GThrottling=require('../config/constants').throttling.mobile3G;
 
 
 
-const MAXIMUM_TTFI=10*1000;
-
-const WHITELISTED_STATUS_CODES=[307];
+const MAXIMUM_TTI=10*1000;
 
 class LoadFastEnough4Pwa extends Audit{
 
@@ -5434,7 +5565,8 @@
 name:'load-fast-enough-for-pwa',
 description:'Page load is fast enough on 3G',
 failureDescription:'Page load is not fast enough on 3G',
-helpText:'A fast page load over a 3G network ensures a good mobile user experience. '+
+helpText:
+'A fast page load over a 3G network ensures a good mobile user experience. '+
 '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/fast-3g).',
 requiredArtifacts:['traces','devtoolsLogs']};
 
@@ -5444,112 +5576,48 @@
 
 
 
-static audit(artifacts){
-const devtoolsLogs=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
-return artifacts.requestNetworkRecords(devtoolsLogs).then(networkRecords=>{
-const firstRequestLatenciesByOrigin=new Map();
-networkRecords.forEach(record=>{
 
-
-const fromCache=record._fromDiskCache||record._fromMemoryCache;
-const origin=URL.getOrigin(record._url);
-if(!origin||!record._timing||fromCache||
-WHITELISTED_STATUS_CODES.includes(record.statusCode)||!record.finished){
-return;
-}
-
-
-
-if(record._startTime<record._issueTime){
-return;
-}
-
-
-const latency=record._timing.receiveHeadersEnd-record._timing.sendEnd;
-const latencyInfo={
-url:record._url,
-startTime:record._startTime,
-origin,
-latency};
-
-
-
-
-const existing=firstRequestLatenciesByOrigin.get(origin);
-if(!existing||latencyInfo.startTime<existing.startTime){
-firstRequestLatenciesByOrigin.set(origin,latencyInfo);
-}
-});
-
-let firstRequestLatencies=Array.from(firstRequestLatenciesByOrigin.values());
-const latency3gMin=Emulation.settings.TYPICAL_MOBILE_THROTTLING_METRICS.targetLatency-10;
-const areLatenciesAll3G=firstRequestLatencies.every(val=>val.latency>latency3gMin);
-firstRequestLatencies=firstRequestLatencies.map(item=>({
-url:item.url,
-latency:Util.formatNumber(item.latency,2)}));
-
-
+static async audit(artifacts,context){
 const trace=artifacts.traces[Audit.DEFAULT_PASS];
-return artifacts.requestFirstInteractive(trace).then(firstInteractive=>{
-const timeToFirstInteractive=firstInteractive.timeInMs;
-const isFast=timeToFirstInteractive<MAXIMUM_TTFI;
-
-const extendedInfo={
-value:{areLatenciesAll3G,firstRequestLatencies,isFast,timeToFirstInteractive}};
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
 
 
-const details=Audit.makeTableDetails([
-{key:'url',itemType:'url',text:'URL'},
-{key:'latency',itemType:'text',text:'Latency (ms)'}],
-firstRequestLatencies);
 
-if(!isFast){
-return{
-rawValue:false,
+const settingOverrides={throttlingMethod:'simulate',throttling:mobile3GThrottling};
+const settings=
+context.settings.throttlingMethod!=='provided'&&
+isDeepEqual(context.settings.throttling,mobile3GThrottling)?
+context.settings:
+Object.assign({},context.settings,settingOverrides);
 
-debugString:`First Interactive was at ${Util.formatMilliseconds(timeToFirstInteractive)}. More details in the "Performance" section.`,
-extendedInfo};
+const metricComputationData={trace,devtoolsLog,settings};
+const tti=await artifacts.requestInteractive(metricComputationData);
 
-}
-
-if(!areLatenciesAll3G){
-const sentryContext=Sentry.getContext();
-const hadThrottlingEnabled=sentryContext&&sentryContext.extra&&
-sentryContext.extra.networkThrottling;
-
-if(hadThrottlingEnabled){
+const score=Number(tti.timing<MAXIMUM_TTI);
 
 
-const violatingLatency=firstRequestLatencies.
-find(item=>Number(item.latency)<latency3gMin);
-Sentry.captureMessage('Network request latencies were not realistic',{
-tags:{audit:this.meta.name},
-extra:{violatingLatency},
-level:'warning'});
+let displayValue;
 
+let explanation;
+if(!score){
+displayValue=[`Interactive at %d\xa0s`,tti.timing/1000];
+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.';
 }
 
 return{
-rawValue:true,
+score,
+displayValue,
+explanation,
+rawValue:tti.timing};
 
-debugString:`First Interactive was found at ${Util.formatMilliseconds(timeToFirstInteractive)}; however, the network request latencies were not sufficiently realistic, so the performance measurements cannot be trusted.`,
-extendedInfo,
-details};
-
-}
-
-return{
-rawValue:true,
-extendedInfo};
-
-});
-});
 }}
 
 
 module.exports=LoadFastEnough4Pwa;
 
-},{"../lib/emulation":27,"../lib/sentry":33,"../lib/url-shim":41,"../report/v2/renderer/util.js":43,"./audit":2}],"../audits/mainthread-work-breakdown":[function(require,module,exports){
+},{"../config/constants":8,"./audit":2,"lodash.isequal":144}],"../audits/mainthread-work-breakdown":[function(require,module,exports){
 
 
 
@@ -5563,21 +5631,21 @@
 'use strict';
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util');
+const Util=require('../report/html/renderer/util');
 
 const{taskToGroup}=require('../lib/task-groups');
 
-class PageExecutionTimings extends Audit{
+class MainThreadWorkBreakdown extends Audit{
 
 
 
 static get meta(){
 return{
-category:'Performance',
 name:'mainthread-work-breakdown',
-description:'Main thread work breakdown',
-informative:true,
-helpText:'Consider reducing the time spent parsing, compiling and executing JS.'+
+description:'Minimizes main thread work',
+failureDescription:'Has significant main thread work',
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
+helpText:'Consider reducing the time spent parsing, compiling and executing JS. '+
 'You may find delivering smaller JS payloads helps with this.',
 requiredArtifacts:['traces']};
 
@@ -5586,6 +5654,17 @@
 
 
 
+static get defaultOptions(){
+return{
+
+scorePODR:1500,
+scoreMedian:4000};
+
+}
+
+
+
+
 
 static getExecutionTimingsByCategory(timelineModel){
 const bottomUpByName=timelineModel.bottomUpGroupBy('EventName');
@@ -5601,19 +5680,24 @@
 
 
 
-static audit(artifacts){
-const trace=artifacts.traces[PageExecutionTimings.DEFAULT_PASS];
 
-return artifacts.requestDevtoolsTimelineModel(trace).
-then(devtoolsTimelineModel=>{
-const executionTimings=PageExecutionTimings.getExecutionTimingsByCategory(
+static async audit(artifacts,context){
+const settings=context.settings||{};
+const trace=artifacts.traces[MainThreadWorkBreakdown.DEFAULT_PASS];
+
+const devtoolsTimelineModel=await artifacts.requestDevtoolsTimelineModel(trace);
+const executionTimings=MainThreadWorkBreakdown.getExecutionTimingsByCategory(
 devtoolsTimelineModel);
 
 let totalExecutionTime=0;
 
+const multiplier=settings.throttlingMethod==='simulate'?
+settings.throttling.cpuSlowdownMultiplier:1;
+
 const extendedInfo={};
 const categoryTotals={};
 const results=Array.from(executionTimings).map(([eventName,duration])=>{
+duration*=multiplier;
 totalExecutionTime+=duration;
 extendedInfo[eventName]=duration;
 const groupName=taskToGroup[eventName];
@@ -5624,34 +5708,40 @@
 return{
 category:eventName,
 group:groupName,
-duration:Util.formatMilliseconds(duration,1)};
+duration:duration};
 
 });
 
 const headings=[
 {key:'group',itemType:'text',text:'Category'},
 {key:'category',itemType:'text',text:'Work'},
-{key:'duration',itemType:'text',text:'Time spent'}];
+{key:'duration',itemType:'ms',granularity:1,text:'Time spent'}];
+
 
 results.stableSort((a,b)=>categoryTotals[b.group]-categoryTotals[a.group]);
-const tableDetails=PageExecutionTimings.makeTableDetails(headings,results);
+const tableDetails=MainThreadWorkBreakdown.makeTableDetails(headings,results);
+
+const score=Audit.computeLogNormalScore(
+totalExecutionTime,
+context.options.scorePODR,
+context.options.scoreMedian);
+
 
 return{
-score:totalExecutionTime<3000,
+score,
 rawValue:totalExecutionTime,
-displayValue:Util.formatMilliseconds(totalExecutionTime),
+displayValue:[Util.MS_DISPLAY_VALUE,totalExecutionTime],
 details:tableDetails,
 extendedInfo:{
 value:extendedInfo}};
 
 
-});
 }}
 
 
-module.exports=PageExecutionTimings;
+module.exports=MainThreadWorkBreakdown;
 
-},{"../lib/task-groups":36,"../report/v2/renderer/util":43,"./audit":2}],"../audits/manifest-short-name-length":[function(require,module,exports){
+},{"../lib/task-groups":42,"../report/html/renderer/util":48,"./audit":2}],"../audits/manifest-short-name-length":[function(require,module,exports){
 
 
 
@@ -5690,17 +5780,17 @@
 
 }
 
-const hasShortName=manifestValues.allChecks.find(i=>i.id==='hasShortName').passing;
-if(!hasShortName){
+const hasShortName=manifestValues.allChecks.find(i=>i.id==='hasShortName');
+if(!hasShortName||!hasShortName.passing){
 return{
 rawValue:false,
-debugString:'No short_name found in manifest.'};
+explanation:'No short_name found in manifest.'};
 
 }
 
-const isShortEnough=manifestValues.allChecks.find(i=>i.id==='shortNameLength').passing;
+const isShortEnough=manifestValues.allChecks.find(i=>i.id==='shortNameLength');
 return{
-rawValue:isShortEnough};
+rawValue:!!isShortEnough&&isShortEnough.passing};
 
 });
 }}
@@ -5733,7 +5823,7 @@
 helpText:'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).',
 description:'Site works cross-browser'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -5763,7 +5853,7 @@
 helpText:'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).',
 description:'Each page has a URL'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -5793,13 +5883,156 @@
 helpText:'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).',
 description:'Page transitions don\'t feel like they block on the network'},
-super.meta);
+super.partialMeta);
 }}
 
 
 module.exports=PWAPageTransitions;
 
-},{"./manual-audit":4}],"../audits/mixed-content":[function(require,module,exports){
+},{"./manual-audit":4}],"../audits/metrics":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Audit=require('./audit');
+
+class Metrics extends Audit{
+
+
+
+static get meta(){
+return{
+name:'metrics',
+scoreDisplayMode:Audit.SCORING_MODES.INFORMATIVE,
+description:'Metrics',
+helpText:'Collects all available metrics.',
+requiredArtifacts:['traces','devtoolsLogs']};
+
+}
+
+
+
+
+
+
+static async audit(artifacts,context){
+const trace=artifacts.traces[Audit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const metricComputationData={trace,devtoolsLog,settings:context.settings};
+
+const traceOfTab=await artifacts.requestTraceOfTab(trace);
+const speedline=await artifacts.requestSpeedline(trace);
+const firstContentfulPaint=await artifacts.requestFirstContentfulPaint(metricComputationData);
+const firstMeaningfulPaint=await artifacts.requestFirstMeaningfulPaint(metricComputationData);
+const firstCPUIdle=await artifacts.requestFirstCPUIdle(metricComputationData);
+const interactive=await artifacts.requestInteractive(metricComputationData);
+const speedIndex=await artifacts.requestSpeedIndex(metricComputationData);
+const estimatedInputLatency=await artifacts.requestEstimatedInputLatency(metricComputationData);
+
+
+const metrics={
+
+firstContentfulPaint:firstContentfulPaint.timing,
+firstContentfulPaintTs:firstContentfulPaint.timestamp,
+firstMeaningfulPaint:firstMeaningfulPaint.timing,
+firstMeaningfulPaintTs:firstMeaningfulPaint.timestamp,
+firstCPUIdle:firstCPUIdle.timing,
+firstCPUIdleTs:firstCPUIdle.timestamp,
+interactive:interactive.timing,
+interactiveTs:interactive.timestamp,
+speedIndex:speedIndex.timing,
+speedIndexTs:speedIndex.timestamp,
+estimatedInputLatency:estimatedInputLatency.timing,
+estimatedInputLatencyTs:estimatedInputLatency.timestamp,
+
+
+observedNavigationStart:traceOfTab.timings.navigationStart,
+observedNavigationStartTs:traceOfTab.timestamps.navigationStart,
+observedFirstPaint:traceOfTab.timings.firstPaint,
+observedFirstPaintTs:traceOfTab.timestamps.firstPaint,
+observedFirstContentfulPaint:traceOfTab.timings.firstContentfulPaint,
+observedFirstContentfulPaintTs:traceOfTab.timestamps.firstContentfulPaint,
+observedFirstMeaningfulPaint:traceOfTab.timings.firstMeaningfulPaint,
+observedFirstMeaningfulPaintTs:traceOfTab.timestamps.firstMeaningfulPaint,
+observedTraceEnd:traceOfTab.timings.traceEnd,
+observedTraceEndTs:traceOfTab.timestamps.traceEnd,
+observedLoad:traceOfTab.timings.load,
+observedLoadTs:traceOfTab.timestamps.load,
+observedDomContentLoaded:traceOfTab.timings.domContentLoaded,
+observedDomContentLoadedTs:traceOfTab.timestamps.domContentLoaded,
+
+
+observedFirstVisualChange:speedline.first,
+observedFirstVisualChangeTs:(speedline.first+speedline.beginning)*1000,
+observedLastVisualChange:speedline.complete,
+observedLastVisualChangeTs:(speedline.complete+speedline.beginning)*1000,
+observedSpeedIndex:speedline.speedIndex,
+observedSpeedIndexTs:(speedline.speedIndex+speedline.beginning)*1000};
+
+
+for(const[name,value]of Object.entries(metrics)){
+const key=name;
+if(typeof value!=='undefined'){
+metrics[key]=Math.round(value);
+}
+}
+
+
+const details={items:[metrics]};
+
+return{
+score:1,
+rawValue:interactive.timing,
+details};
+
+}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+module.exports=Metrics;
+
+},{"./audit":2}],"../audits/mixed-content":[function(require,module,exports){
+
 
 
 
@@ -5809,7 +6042,7 @@
 
 const Audit=require('./audit');
 const URL=require('../lib/url-shim');
-const Util=require('../report/v2/renderer/util');
+const Util=require('../report/html/renderer/util');
 
 
 
@@ -5823,10 +6056,8 @@
 
 static get meta(){
 return{
-category:'Mixed Content',
 name:'mixed-content',
 description:'All resources loaded are secure',
-informative:true,
 failureDescription:'Some insecure resources can be upgraded to HTTPS',
 helpText:`Mixed content warnings can prevent you from upgrading to HTTPS.
       This audit shows which insecure resources this page uses that can be
@@ -5949,12 +6180,11 @@
 const details=Audit.makeTableDetails(headings,upgradeableResources);
 
 const totalRecords=defaultRecords.length;
-const score=100*(
-secureRecords.length+0.5*upgradeableResources.length)/
-totalRecords;
+const score=(secureRecords.length+0.5*upgradeableResources.length)/totalRecords;
 
 return{
-rawValue:score,
+rawValue:upgradeableResources.length===0,
+score,
 displayValue:displayValue,
 details};
 
@@ -5964,7 +6194,7 @@
 
 module.exports=MixedContent;
 
-},{"../lib/url-shim":41,"../report/v2/renderer/util":43,"./audit":2}],"../audits/predictive-perf":[function(require,module,exports){
+},{"../lib/url-shim":"url","../report/html/renderer/util":48,"./audit":2}],"../audits/network-requests":[function(require,module,exports){
 
 
 
@@ -5973,37 +6203,92 @@
 'use strict';
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util.js');
-const LoadSimulator=require('../lib/dependency-graph/simulator/simulator.js');
-const Node=require('../lib/dependency-graph/node.js');
-const WebInspector=require('../lib/web-inspector');
+const URL=require('../lib/url-shim');
+
+class NetworkRequests extends Audit{
+
+
+
+static get meta(){
+return{
+name:'network-requests',
+scoreDisplayMode:Audit.SCORING_MODES.INFORMATIVE,
+description:'Network Requests',
+helpText:'Lists the network requests that were made during page load.',
+requiredArtifacts:['devtoolsLogs']};
+
+}
+
+
+
+
+
+static audit(artifacts){
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+return artifacts.requestNetworkRecords(devtoolsLog).then(records=>{
+const earliestStartTime=records.reduce(
+(min,record)=>Math.min(min,record.startTime),
+Infinity);
+
+
+const results=records.map(record=>{
+return{
+url:URL.elideDataURI(record.url),
+startTime:(record.startTime-earliestStartTime)*1000,
+endTime:(record.endTime-earliestStartTime)*1000,
+transferSize:record.transferSize,
+statusCode:record.statusCode,
+mimeType:record._mimeType,
+resourceType:record._resourceType&&record._resourceType._name};
+
+});
+
+const headings=[
+{key:'url',itemType:'url',text:'URL'},
+{key:'startTime',itemType:'ms',granularity:1,text:'Start Time'},
+{key:'endTime',itemType:'ms',granularity:1,text:'End Time'},
+{
+key:'transferSize',
+itemType:'bytes',
+displayUnit:'kb',
+granularity:1,
+text:'Transfer Size'},
+
+{key:'statusCode',itemType:'text',text:'Status Code'},
+{key:'mimeType',itemType:'text',text:'MIME Type'},
+{key:'resourceType',itemType:'text',text:'Resource Type'}];
+
+
+const tableDetails=Audit.makeTableDetails(headings,results);
+
+return{
+score:1,
+rawValue:results.length,
+extendedInfo:{value:results},
+details:tableDetails};
+
+});
+}}
+
+
+module.exports=NetworkRequests;
+
+},{"../lib/url-shim":"url","./audit":2}],"../audits/predictive-perf":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Audit=require('./audit');
+const Util=require('../report/html/renderer/util');
 
 
 
 const SCORING_POINT_OF_DIMINISHING_RETURNS=1700;
 const SCORING_MEDIAN=10000;
 
-
-const CRITICAL_LONG_TASK_THRESHOLD=20;
-
-const COEFFICIENTS={
-FCP:{
-intercept:1440,
-optimistic:-1.75,
-pessimistic:2.73},
-
-FMP:{
-intercept:1532,
-optimistic:-.30,
-pessimistic:1.33},
-
-TTCI:{
-intercept:1582,
-optimistic:.97,
-pessimistic:.49}};
-
-
-
 class PredictivePerf extends Audit{
 
 
@@ -6015,7 +6300,7 @@
 helpText:
 'Predicted performance evaluates how your site will perform under '+
 'a 3G connection on a mobile device.',
-scoringMode:Audit.SCORING_MODES.NUMERIC,
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
 requiredArtifacts:['traces','devtoolsLogs']};
 
 }
@@ -6024,223 +6309,63 @@
 
 
 
-
-static getScriptUrls(dependencyGraph,condition){
-const scriptUrls=new Set();
-
-dependencyGraph.traverse(node=>{
-if(node.type===Node.TYPES.CPU)return;
-if(node.record._resourceType!==WebInspector.resourceTypes.Script)return;
-if(condition&&!condition(node))return;
-scriptUrls.add(node.record.url);
-});
-
-return scriptUrls;
-}
-
-
-
-
-
-
-static getOptimisticFCPGraph(dependencyGraph,traceOfTab){
-const fcp=traceOfTab.timestamps.firstContentfulPaint;
-const blockingScriptUrls=PredictivePerf.getScriptUrls(dependencyGraph,node=>{
-return(
-node.endTime<=fcp&&node.hasRenderBlockingPriority()&&node.initiatorType!=='script');
-
-});
-
-return dependencyGraph.cloneWithRelationships(node=>{
-if(node.endTime>fcp)return false;
-
-if(node.type===Node.TYPES.CPU)return node.isEvaluateScriptFor(blockingScriptUrls);
-
-return node.hasRenderBlockingPriority()&&node.initiatorType!=='script';
-});
-}
-
-
-
-
-
-
-static getPessimisticFCPGraph(dependencyGraph,traceOfTab){
-const fcp=traceOfTab.timestamps.firstContentfulPaint;
-const blockingScriptUrls=PredictivePerf.getScriptUrls(dependencyGraph,node=>{
-return node.endTime<=fcp&&node.hasRenderBlockingPriority();
-});
-
-return dependencyGraph.cloneWithRelationships(node=>{
-if(node.endTime>fcp)return false;
-
-if(node.type===Node.TYPES.CPU)return node.isEvaluateScriptFor(blockingScriptUrls);
-
-return node.hasRenderBlockingPriority();
-});
-}
-
-
-
-
-
-
-static getOptimisticFMPGraph(dependencyGraph,traceOfTab){
-const fmp=traceOfTab.timestamps.firstMeaningfulPaint;
-const requiredScriptUrls=PredictivePerf.getScriptUrls(dependencyGraph,node=>{
-return(
-node.endTime<=fmp&&node.hasRenderBlockingPriority()&&node.initiatorType!=='script');
-
-});
-
-return dependencyGraph.cloneWithRelationships(node=>{
-if(node.endTime>fmp)return false;
-
-if(node.type===Node.TYPES.CPU)return node.isEvaluateScriptFor(requiredScriptUrls);
-
-return node.hasRenderBlockingPriority()&&node.initiatorType!=='script';
-});
-}
-
-
-
-
-
-
-static getPessimisticFMPGraph(dependencyGraph,traceOfTab){
-const fmp=traceOfTab.timestamps.firstMeaningfulPaint;
-const requiredScriptUrls=PredictivePerf.getScriptUrls(dependencyGraph,node=>{
-return node.endTime<=fmp&&node.hasRenderBlockingPriority();
-});
-
-return dependencyGraph.cloneWithRelationships(node=>{
-if(node.endTime>fmp)return false;
-
-
-if(node.type===Node.TYPES.CPU){
-return node.didPerformLayout()||node.isEvaluateScriptFor(requiredScriptUrls);
-}
-
-
-return node.hasRenderBlockingPriority();
-});
-}
-
-
-
-
-
-static getOptimisticTTCIGraph(dependencyGraph){
-
-const minimumCpuTaskDuration=CRITICAL_LONG_TASK_THRESHOLD*1000;
-
-return dependencyGraph.cloneWithRelationships(node=>{
-
-if(node.type===Node.TYPES.CPU)return node.event.dur>minimumCpuTaskDuration;
-
-const isImage=node.record._resourceType===WebInspector.resourceTypes.Image;
-const isScript=node.record._resourceType===WebInspector.resourceTypes.Script;
-return!isImage&&(isScript||
-node.record.priority()==='High'||
-node.record.priority()==='VeryHigh');
-});
-}
-
-
-
-
-
-static getPessimisticTTCIGraph(dependencyGraph){
-return dependencyGraph;
-}
-
-
-
-
-
-static getLastLongTaskEndTime(nodeTiming){
-return Array.from(nodeTiming.entries()).
-filter(([node,timing])=>node.type===Node.TYPES.CPU&&
-timing.endTime-timing.startTime>50).
-map(([_,timing])=>timing.endTime).
-reduce((max,x)=>Math.max(max,x),0);
-}
-
-
-
-
-
-static audit(artifacts){
+static async audit(artifacts){
 const trace=artifacts.traces[Audit.DEFAULT_PASS];
-const devtoolsLogs=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
-return Promise.all([
-artifacts.requestPageDependencyGraph(trace,devtoolsLogs),
-artifacts.requestTraceOfTab(trace)]).
-then(([graph,traceOfTab])=>{
-const graphs={
-optimisticFCP:PredictivePerf.getOptimisticFCPGraph(graph,traceOfTab),
-pessimisticFCP:PredictivePerf.getPessimisticFCPGraph(graph,traceOfTab),
-optimisticFMP:PredictivePerf.getOptimisticFMPGraph(graph,traceOfTab),
-pessimisticFMP:PredictivePerf.getPessimisticFMPGraph(graph,traceOfTab),
-optimisticTTCI:PredictivePerf.getOptimisticTTCIGraph(graph,traceOfTab),
-pessimisticTTCI:PredictivePerf.getPessimisticTTCIGraph(graph,traceOfTab)};
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
 
 
-const values={};
-Object.keys(graphs).forEach(key=>{
-const estimate=new LoadSimulator(graphs[key]).simulate();
-const lastLongTaskEnd=PredictivePerf.getLastLongTaskEndTime(estimate.nodeTiming);
+const settings={};
+const fcp=await artifacts.requestLanternFirstContentfulPaint({trace,devtoolsLog,settings});
+const fmp=await artifacts.requestLanternFirstMeaningfulPaint({trace,devtoolsLog,settings});
+const tti=await artifacts.requestLanternInteractive({trace,devtoolsLog,settings});
+const ttfcpui=await artifacts.requestLanternFirstCPUIdle({trace,devtoolsLog,settings});
+const si=await artifacts.requestLanternSpeedIndex({trace,devtoolsLog,settings});
+const eil=await artifacts.requestLanternEstimatedInputLatency({trace,devtoolsLog,settings});
 
-switch(key){
-case'optimisticFCP':
-case'pessimisticFCP':
-case'optimisticFMP':
-case'pessimisticFMP':
-values[key]=estimate.timeInMs;
-break;
-case'optimisticTTCI':
-values[key]=Math.max(values.optimisticFMP,lastLongTaskEnd);
-break;
-case'pessimisticTTCI':
-values[key]=Math.max(values.pessimisticFMP,lastLongTaskEnd);
-break;}
+const values={
+roughEstimateOfFCP:fcp.timing,
+optimisticFCP:fcp.optimisticEstimate.timeInMs,
+pessimisticFCP:fcp.pessimisticEstimate.timeInMs,
 
-});
+roughEstimateOfFMP:fmp.timing,
+optimisticFMP:fmp.optimisticEstimate.timeInMs,
+pessimisticFMP:fmp.pessimisticEstimate.timeInMs,
 
-values.roughEstimateOfFCP=COEFFICIENTS.FCP.intercept+
-COEFFICIENTS.FCP.optimistic*values.optimisticFCP+
-COEFFICIENTS.FCP.pessimistic*values.pessimisticFCP;
-values.roughEstimateOfFMP=COEFFICIENTS.FMP.intercept+
-COEFFICIENTS.FMP.optimistic*values.optimisticFMP+
-COEFFICIENTS.FMP.pessimistic*values.pessimisticFMP;
-values.roughEstimateOfTTCI=COEFFICIENTS.TTCI.intercept+
-COEFFICIENTS.TTCI.optimistic*values.optimisticTTCI+
-COEFFICIENTS.TTCI.pessimistic*values.pessimisticTTCI;
+roughEstimateOfTTI:tti.timing,
+optimisticTTI:tti.optimisticEstimate.timeInMs,
+pessimisticTTI:tti.pessimisticEstimate.timeInMs,
 
+roughEstimateOfTTFCPUI:ttfcpui.timing,
+optimisticTTFCPUI:ttfcpui.optimisticEstimate.timeInMs,
+pessimisticTTFCPUI:ttfcpui.pessimisticEstimate.timeInMs,
 
+roughEstimateOfSI:si.timing,
+optimisticSI:si.optimisticEstimate.timeInMs,
+pessimisticSI:si.pessimisticEstimate.timeInMs,
 
-values.roughEstimateOfFMP=Math.max(values.roughEstimateOfFCP,values.roughEstimateOfFMP);
-values.roughEstimateOfTTCI=Math.max(values.roughEstimateOfFMP,values.roughEstimateOfTTCI);
+roughEstimateOfEIL:eil.timing,
+optimisticEIL:eil.optimisticEstimate.timeInMs,
+pessimisticEIL:eil.pessimisticEstimate.timeInMs};
+
 
 const score=Audit.computeLogNormalScore(
-values.roughEstimateOfTTCI,
+values.roughEstimateOfTTI,
 SCORING_POINT_OF_DIMINISHING_RETURNS,
 SCORING_MEDIAN);
 
 
 return{
 score,
-rawValue:values.roughEstimateOfTTCI,
-displayValue:Util.formatMilliseconds(values.roughEstimateOfTTCI),
-extendedInfo:{value:values}};
+rawValue:values.roughEstimateOfTTI,
+displayValue:Util.formatMilliseconds(values.roughEstimateOfTTI),
+details:{items:[values]}};
 
-});
 }}
 
 
 module.exports=PredictivePerf;
 
-},{"../lib/dependency-graph/node.js":22,"../lib/dependency-graph/simulator/simulator.js":23,"../lib/web-inspector":42,"../report/v2/renderer/util.js":43,"./audit":2}],"../audits/redirects-http":[function(require,module,exports){
+},{"../report/html/renderer/util":48,"./audit":2}],"../audits/redirects-http":[function(require,module,exports){
 
 
 
@@ -6271,8 +6396,7 @@
 
 static audit(artifacts){
 return{
-rawValue:artifacts.HTTPRedirect.value,
-debugString:artifacts.HTTPRedirect.debugString};
+rawValue:artifacts.HTTPRedirect.value};
 
 }}
 
@@ -6288,7 +6412,6 @@
 'use strict';
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util');
 const UnusedBytes=require('./byte-efficiency/byte-efficiency-audit');
 
 class Redirects extends Audit{
@@ -6298,10 +6421,10 @@
 static get meta(){
 return{
 name:'redirects',
-description:'Avoids page redirects',
-failureDescription:'Has multiple page redirects',
+description:'Avoid multiple page redirects',
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
 helpText:'Redirects introduce additional delays before the page can be loaded. [Learn more](https://developers.google.com/speed/docs/insights/AvoidRedirects).',
-requiredArtifacts:['URL','devtoolsLogs']};
+requiredArtifacts:['URL','devtoolsLogs','traces']};
 
 }
 
@@ -6309,9 +6432,28 @@
 
 
 
-static audit(artifacts){
-return artifacts.requestMainResource(artifacts.devtoolsLogs[Audit.DEFAULT_PASS]).
-then(mainResource=>{
+
+static async audit(artifacts,context){
+const settings=context.settings;
+const trace=artifacts.traces[Audit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+
+const traceOfTab=await artifacts.requestTraceOfTab(trace);
+const networkRecords=await artifacts.requestNetworkRecords(devtoolsLog);
+const mainResource=await artifacts.requestMainResource({URL:artifacts.URL,devtoolsLog});
+
+const metricComputationData={trace,devtoolsLog,traceOfTab,networkRecords,settings};
+const metricResult=await artifacts.requestLanternInteractive(metricComputationData);
+
+
+const nodeTimingsByUrl=new Map();
+for(const[node,timing]of metricResult.pessimisticEstimate.nodeTimings.entries()){
+if(node.type==='network'){
+const networkNode=node;
+nodeTimingsByUrl.set(networkNode.record.url,timing);
+}
+}
+
 
 const redirectRequests=Array.from(mainResource.redirects||[]);
 
@@ -6325,7 +6467,7 @@
 if(redirectRequests.length>1){
 pageRedirects.push({
 url:`(Initial: ${redirectRequests[0].url})`,
-wastedMs:'n/a'});
+wastedMs:0});
 
 }
 
@@ -6333,26 +6475,34 @@
 const initialRequest=redirectRequests[i-1];
 const redirectedRequest=redirectRequests[i];
 
-const wastedMs=(redirectedRequest.startTime-initialRequest.startTime)*1000;
+const initialTiming=nodeTimingsByUrl.get(initialRequest.url);
+const redirectedTiming=nodeTimingsByUrl.get(redirectedRequest.url);
+if(!initialTiming||!redirectedTiming){
+throw new Error('Could not find redirects in graph');
+}
+
+
+const wastedMs=redirectedTiming.startTime-initialTiming.startTime;
 totalWastedMs+=wastedMs;
 
 pageRedirects.push({
 url:redirectedRequest.url,
-wastedMs:Util.formatMilliseconds(wastedMs,1)});
+wastedMs});
 
 }
 
 const headings=[
 {key:'url',itemType:'text',text:'Redirected URL'},
-{key:'wastedMs',itemType:'text',text:'Time for Redirect'}];
+{key:'wastedMs',itemType:'ms',text:'Time for Redirect'}];
 
-const details=Audit.makeTableDetails(headings,pageRedirects);
+const summary={wastedMs:totalWastedMs};
+const details=Audit.makeTableDetails(headings,pageRedirects,summary);
 
 return{
 
-score:redirectRequests.length<=2?100:UnusedBytes.scoreForWastedMs(totalWastedMs),
+score:redirectRequests.length<=2?1:UnusedBytes.scoreForWastedMs(totalWastedMs),
 rawValue:totalWastedMs,
-displayValue:Util.formatMilliseconds(totalWastedMs,1),
+displayValue:['%d\xa0ms',totalWastedMs],
 extendedInfo:{
 value:{
 wastedMs:totalWastedMs}},
@@ -6360,13 +6510,12 @@
 
 details};
 
-});
 }}
 
 
 module.exports=Redirects;
 
-},{"../report/v2/renderer/util":43,"./audit":2,"./byte-efficiency/byte-efficiency-audit":3}],"../audits/screenshot-thumbnails":[function(require,module,exports){
+},{"./audit":2,"./byte-efficiency/byte-efficiency-audit":3}],"../audits/screenshot-thumbnails":[function(require,module,exports){
 
 
 
@@ -6375,13 +6524,14 @@
 'use strict';
 
 const Audit=require('./audit');
-const TTFI=require('./first-interactive');
-const TTCI=require('./consistently-interactive');
+const LHError=require('../lib/errors');
 const jpeg=require('jpeg-js');
 
 const NUMBER_OF_THUMBNAILS=10;
 const THUMBNAIL_WIDTH=120;
 
+
+
 class ScreenshotThumbnails extends Audit{
 
 
@@ -6389,10 +6539,10 @@
 static get meta(){
 return{
 name:'screenshot-thumbnails',
-informative:true,
+scoreDisplayMode:Audit.SCORING_MODES.INFORMATIVE,
 description:'Screenshot Thumbnails',
 helpText:'This is what the load of your site looked like.',
-requiredArtifacts:['traces']};
+requiredArtifacts:['traces','devtoolsLogs']};
 
 }
 
@@ -6436,27 +6586,42 @@
 
 
 
-static audit(artifacts){
+
+static async audit(artifacts,context){
 const trace=artifacts.traces[Audit.DEFAULT_PASS];
+
 const cachedThumbnails=new Map();
 
-return Promise.all([
-artifacts.requestSpeedline(trace),
-TTFI.audit(artifacts).catch(()=>({rawValue:0})),
-TTCI.audit(artifacts).catch(()=>({rawValue:0}))]).
-then(([speedline,ttfi,ttci])=>{
+const speedline=await artifacts.requestSpeedline(trace);
+
+
+let minimumTimelineDuration=context.options.minimumTimelineDuration||3000;
+
+if(context.settings.throttlingMethod!=='simulate'){
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const metricComputationData={trace,devtoolsLog,settings:context.settings};
+const tti=artifacts.requestInteractive(metricComputationData);
+try{
+minimumTimelineDuration=Math.max((await tti).timing,minimumTimelineDuration);
+}catch(_){}
+}
+
 const thumbnails=[];
 const analyzedFrames=speedline.frames.filter(frame=>!frame.isProgressInterpolated());
 const maxFrameTime=
 speedline.complete||
 Math.max(...speedline.frames.map(frame=>frame.getTimeStamp()-speedline.beginning));
+const timelineEnd=Math.max(maxFrameTime,minimumTimelineDuration);
 
-
-const timelineEnd=Math.max(maxFrameTime,ttfi.rawValue,ttci.rawValue);
+if(!analyzedFrames.length||!Number.isFinite(timelineEnd)){
+throw new LHError(LHError.errors.INVALID_SPEEDLINE);
+}
 
 for(let i=1;i<=NUMBER_OF_THUMBNAILS;i++){
 const targetTimestamp=speedline.beginning+timelineEnd*i/NUMBER_OF_THUMBNAILS;
 
+
+
 let frameForTimestamp=null;
 if(i===NUMBER_OF_THUMBNAILS){
 frameForTimestamp=analyzedFrames[analyzedFrames.length-1];
@@ -6483,7 +6648,7 @@
 }
 
 return{
-score:100,
+score:1,
 rawValue:thumbnails.length>0,
 details:{
 type:'filmstrip',
@@ -6491,13 +6656,12 @@
 items:thumbnails}};
 
 
-});
 }}
 
 
 module.exports=ScreenshotThumbnails;
 
-},{"./audit":2,"./consistently-interactive":"../audits/consistently-interactive","./first-interactive":"../audits/first-interactive","jpeg-js":134}],"../audits/seo/canonical":[function(require,module,exports){
+},{"../lib/errors":33,"./audit":2,"jpeg-js":140}],"../audits/seo/canonical":[function(require,module,exports){
 
 
 
@@ -6564,9 +6728,8 @@
 description:'Document has a valid `rel=canonical`',
 failureDescription:'Document does not have a valid `rel=canonical`',
 helpText:'Canonical links suggest which URL to show in search results. '+
-'Read more in [Use canonical URLs]'+
-'(https://support.google.com/webmasters/answer/139066).',
-requiredArtifacts:['Canonical','Hreflang']};
+'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/canonical).',
+requiredArtifacts:['Canonical','Hreflang','URL']};
 
 }
 
@@ -6575,22 +6738,31 @@
 
 
 static audit(artifacts){
-const devtoolsLogs=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
 
-return artifacts.requestMainResource(devtoolsLogs).
+return artifacts.requestMainResource({devtoolsLog,URL:artifacts.URL}).
 then(mainResource=>{
 const baseURL=new URL(mainResource.url);
+
 let canonicals=[];
+
 let hreflangs=[];
 
-mainResource.responseHeaders.
+mainResource._responseHeaders&&mainResource._responseHeaders.
 filter(h=>h.name.toLowerCase()===LINK_HEADER).
 forEach(h=>{
 canonicals=canonicals.concat(getCanonicalLinksFromHeader(h.value));
 hreflangs=hreflangs.concat(getHreflangsFromHeader(h.value));
 });
 
-canonicals=canonicals.concat(artifacts.Canonical);
+for(const canonical of artifacts.Canonical){
+if(canonical!==null){
+canonicals.push(canonical);
+}
+}
+
+
+canonicals=Array.from(new Set(canonicals));
 
 artifacts.Hreflang.forEach(({href})=>hreflangs.push(href));
 
@@ -6608,7 +6780,7 @@
 if(canonicals.length>1){
 return{
 rawValue:false,
-debugString:`Multiple URLs (${canonicals.join(', ')})`};
+explanation:`Multiple conflicting URLs (${canonicals.join(', ')})`};
 
 }
 
@@ -6617,14 +6789,14 @@
 if(!isValidRelativeOrAbsoluteURL(canonical)){
 return{
 rawValue:false,
-debugString:`Invalid URL (${canonical})`};
+explanation:`Invalid URL (${canonical})`};
 
 }
 
 if(!URL.isValid(canonical)){
 return{
 rawValue:false,
-debugString:`Relative URL (${canonical})`};
+explanation:`Relative URL (${canonical})`};
 
 }
 
@@ -6635,7 +6807,7 @@
 baseURL.href!==canonicalURL.href){
 return{
 rawValue:false,
-debugString:`Points to another hreflang location (${baseURL.href})`};
+explanation:`Points to another hreflang location (${baseURL.href})`};
 
 }
 
@@ -6644,7 +6816,7 @@
 if(getPrimaryDomain(canonicalURL)!==getPrimaryDomain(baseURL)){
 return{
 rawValue:false,
-debugString:`Points to a different domain (${canonicalURL})`};
+explanation:`Points to a different domain (${canonicalURL})`};
 
 }
 
@@ -6653,7 +6825,7 @@
 canonicalURL.pathname==='/'&&baseURL.pathname!=='/'){
 return{
 rawValue:false,
-debugString:'Points to a root of the same origin'};
+explanation:'Points to a root of the same origin'};
 
 }
 
@@ -6666,8 +6838,7 @@
 
 module.exports=Canonical;
 
-},{"../../lib/url-shim":41,"../audit":2,"http-link-header":131}],"../audits/seo/font-size":[function(require,module,exports){
-(function(global){
+},{"../../lib/url-shim":"url","../audit":2,"http-link-header":137}],"../audits/seo/font-size":[function(require,module,exports){
 
 
 
@@ -6675,17 +6846,22 @@
 
 'use strict';
 
+
+
+
 const URL=require('../../lib/url-shim');
 const Audit=require('../audit');
 const ViewportAudit=require('../viewport');
-const CSSStyleDeclaration=require('../../lib/web-inspector').CSSStyleDeclaration;
-const MINIMAL_PERCENTAGE_OF_LEGIBLE_TEXT=75;
+const WebInspector=require('../../lib/web-inspector');
+const CSSStyleDeclaration=WebInspector.CSSStyleDeclaration;
+const MINIMAL_PERCENTAGE_OF_LEGIBLE_TEXT=60;
 
 
 
 
 
 function getUniqueFailingRules(fontSizeArtifact){
+
 const failingRules=new Map();
 
 fontSizeArtifact.forEach(({cssRule,fontSize,textLength,node})=>{
@@ -6704,14 +6880,14 @@
 }
 });
 
-return failingRules.valuesArray();
+return[...failingRules.values()];
 }
 
 
 
 
 
-function getAttributeMap(attributes){
+function getAttributeMap(attributes=[]){
 const map=new Map();
 
 for(let i=0;i<attributes.length;i+=2){
@@ -6736,8 +6912,11 @@
 
 if(attributeMap.has('id')){
 return'#'+attributeMap.get('id');
-}else if(attributeMap.has('class')){
-return'.'+attributeMap.get('class').split(/\s+/).join('.');
+}else{
+const attrClass=attributeMap.get('class');
+if(attrClass){
+return'.'+attrClass.split(/\s+/).join('.');
+}
 }
 
 return node.localName.toLowerCase();
@@ -6748,7 +6927,8 @@
 
 
 function nodeToTableNode(node){
-const attributesString=node.attributes.map((value,idx)=>
+const attributes=node.attributes||[];
+const attributesString=attributes.map((value,idx)=>
 idx%2===0?` ${value}`:`="${value}"`).
 join('');
 
@@ -6772,13 +6952,13 @@
 styleDeclaration.type===CSSStyleDeclaration.Type.Inline)
 {
 return{
-source:baseURL,
-selector:nodeToTableNode(node)};
+selector:nodeToTableNode(node),
+source:baseURL};
 
 }
 
 if(styleDeclaration.parentRule&&
-styleDeclaration.parentRule.origin===global.CSSAgent.StyleSheetOrigin.USER_AGENT){
+styleDeclaration.parentRule.origin==='user-agent'){
 return{
 selector:styleDeclaration.parentRule.selectors.map(item=>item.text).join(', '),
 source:'User Agent Stylesheet'};
@@ -6819,6 +6999,7 @@
 }
 
 return{
+selector:'',
 source:'Unknown'};
 
 }
@@ -6847,9 +7028,9 @@
 name:'font-size',
 description:'Document uses legible font sizes',
 failureDescription:'Document doesn\'t use legible font sizes',
-helpText:'Font sizes less than 16px are too small to be legible and require mobile '+
-'visitors to “pinch to zoom” in order to read. Strive to have >75% of page text ≥16px. '+
-'[Learn more](https://developers.google.com/speed/docs/insights/UseLegibleFontSizes).',
+helpText:'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).',
 requiredArtifacts:['FontSize','URL','Viewport']};
 
 }
@@ -6863,7 +7044,7 @@
 if(!hasViewportSet){
 return{
 rawValue:false,
-debugString:'Text is illegible because of a missing viewport config'};
+explanation:'Text is illegible because of a missing viewport config'};
 
 }
 
@@ -6913,25 +7094,27 @@
 
 tableData.push({
 source:'Add\'l illegible text',
-selector:null,
+selector:'',
 coverage:`${percentageOfUnanalyzedFailingText.toFixed(2)}%`,
-fontSize:'< 16px'});
+fontSize:'< 12px'});
 
 }
 
 if(percentageOfPassingText>0){
 tableData.push({
 source:'Legible text',
-selector:null,
+selector:'',
 coverage:`${percentageOfPassingText.toFixed(2)}%`,
-fontSize:'≥ 16px'});
+fontSize:'≥ 12px'});
 
 }
 
+
+const displayValue=['%.1d% legible text',percentageOfPassingText];
 const details=Audit.makeTableDetails(headings,tableData);
 const passed=percentageOfPassingText>=MINIMAL_PERCENTAGE_OF_LEGIBLE_TEXT;
-let debugString=null;
 
+let explanation;
 if(!passed){
 const percentageOfFailingText=parseFloat((100-percentageOfPassingText).toFixed(2));
 let disclaimer='';
@@ -6942,21 +7125,21 @@
 disclaimer=` (based on ${percentageOfVisitedText.toFixed()}% sample)`;
 }
 
-debugString=`${percentageOfFailingText}% of text is too small${disclaimer}.`;
+explanation=`${percentageOfFailingText}% of text is too small${disclaimer}.`;
 }
 
 return{
 rawValue:passed,
 details,
-debugString};
+displayValue,
+explanation};
 
 }}
 
 
 module.exports=FontSize;
 
-}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{"../../lib/url-shim":41,"../../lib/web-inspector":42,"../audit":2,"../viewport":"../audits/viewport"}],"../audits/seo/hreflang":[function(require,module,exports){
+},{"../../lib/url-shim":"url","../../lib/web-inspector":47,"../audit":2,"../viewport":"../audits/viewport"}],"../audits/seo/hreflang":[function(require,module,exports){
 (function(global){
 
 
@@ -6976,11 +7159,17 @@
 
 
 
+
 function importValidLangs(){
+
 const axeCache=global.axe;
+
 global.axe={utils:{}};
+
 require('axe-core/lib/commons/utils/valid-langs.js');
+
 const validLangs=global.axe.utils.validLangs();
+
 global.axe=axeCache;
 
 return validLangs;
@@ -7008,7 +7197,7 @@
 const linkHeader=LinkHeader.parse(headerValue);
 
 return linkHeader.get('rel','alternate').
-every(link=>link.hreflang&&isValidHreflang(link.hreflang));
+every(link=>!!link.hreflang&&isValidHreflang(link.hreflang));
 }
 
 class Hreflang extends Audit{
@@ -7020,10 +7209,10 @@
 name:'hreflang',
 description:'Document has a valid `hreflang`',
 failureDescription:'Document doesn\'t have a valid `hreflang`',
-helpText:'hreflang allows crawlers to discover alternate translations of the '+
-'page content. [Learn more]'+
-'(https://support.google.com/webmasters/answer/189077).',
-requiredArtifacts:['Hreflang']};
+helpText:'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).',
+requiredArtifacts:['Hreflang','URL']};
 
 }
 
@@ -7032,9 +7221,10 @@
 
 
 static audit(artifacts){
-const devtoolsLogs=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const URL=artifacts.URL;
 
-return artifacts.requestMainResource(devtoolsLogs).
+return artifacts.requestMainResource({devtoolsLog,URL}).
 then(mainResource=>{
 
 const invalidHreflangs=[];
@@ -7052,7 +7242,7 @@
 });
 }
 
-mainResource.responseHeaders.
+mainResource._responseHeaders&&mainResource._responseHeaders.
 filter(h=>h.name.toLowerCase()===LINK_HEADER&&!headerHasValidHreflangs(h.value)).
 forEach(h=>invalidHreflangs.push({source:`${h.name}: ${h.value}`}));
 
@@ -7072,7 +7262,7 @@
 module.exports=Hreflang;
 
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{"../audit":2,"axe-core/lib/commons/utils/valid-langs.js":92,"http-link-header":131}],"../audits/seo/http-status-code":[function(require,module,exports){
+},{"../audit":2,"axe-core/lib/commons/utils/valid-langs.js":98,"http-link-header":137}],"../audits/seo/http-status-code":[function(require,module,exports){
 
 
 
@@ -7096,7 +7286,7 @@
 helpText:'Pages with unsuccessful HTTP status codes may not be indexed properly. '+
 '[Learn more]'+
 '(https://developers.google.com/web/tools/lighthouse/audits/successful-http-code).',
-requiredArtifacts:['devtoolsLogs']};
+requiredArtifacts:['devtoolsLogs','URL']};
 
 }
 
@@ -7105,9 +7295,10 @@
 
 
 static audit(artifacts){
-const devtoolsLogs=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const URL=artifacts.URL;
 
-return artifacts.requestMainResource(devtoolsLogs).
+return artifacts.requestMainResource({devtoolsLog,URL}).
 then(mainResource=>{
 const statusCode=mainResource.statusCode;
 
@@ -7137,6 +7328,8 @@
 'use strict';
 
 const Audit=require('../audit');
+const robotsParser=require('robots-parser');
+const URL=require('../../lib/url-shim');
 const BLOCKLIST=new Set([
 'noindex',
 'none']);
@@ -7194,9 +7387,10 @@
 name:'is-crawlable',
 description:'Page isn’t blocked from indexing',
 failureDescription:'Page is blocked from indexing',
-helpText:'The "Robots" directives tell crawlers how your content should be indexed. '+
-'[Learn more](https://developers.google.com/search/reference/robots_meta_tag).',
-requiredArtifacts:['MetaRobots']};
+helpText:'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).',
+requiredArtifacts:['MetaRobots','RobotsTxt','URL']};
 
 }
 
@@ -7205,8 +7399,11 @@
 
 
 static audit(artifacts){
-return artifacts.requestMainResource(artifacts.devtoolsLogs[Audit.DEFAULT_PASS]).
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+
+return artifacts.requestMainResource({devtoolsLog,URL:artifacts.URL}).
 then(mainResource=>{
+
 const blockingDirectives=[];
 
 if(artifacts.MetaRobots){
@@ -7222,13 +7419,27 @@
 }
 }
 
-mainResource.responseHeaders.
+mainResource._responseHeaders&&mainResource._responseHeaders.
 filter(h=>h.name.toLowerCase()===ROBOTS_HEADER&&!hasUserAgent(h.value)&&
 hasBlockingDirective(h.value)).
 forEach(h=>blockingDirectives.push({source:`${h.name}: ${h.value}`}));
 
+if(artifacts.RobotsTxt.content){
+const robotsFileUrl=new URL('/robots.txt',mainResource.url);
+const robotsTxt=robotsParser(robotsFileUrl.href,artifacts.RobotsTxt.content);
+
+if(!robotsTxt.isAllowed(mainResource.url)){
+blockingDirectives.push({
+source:{
+type:'url',
+value:robotsFileUrl.href}});
+
+
+}
+}
+
 const headings=[
-{key:'source',itemType:'code',text:'Source'}];
+{key:'source',itemType:'code',text:'Blocking Directive Source'}];
 
 const details=Audit.makeTableDetails(headings,blockingDirectives);
 
@@ -7242,7 +7453,7 @@
 
 module.exports=IsCrawlable;
 
-},{"../audit":2}],"../audits/seo/link-text":[function(require,module,exports){
+},{"../../lib/url-shim":"url","../audit":2,"robots-parser":149}],"../audits/seo/link-text":[function(require,module,exports){
 
 
 
@@ -7270,12 +7481,11 @@
 
 static get meta(){
 return{
-category:'Content Best Practices',
 name:'link-text',
 description:'Links have descriptive text',
 failureDescription:'Links do not have descriptive text',
 helpText:'Descriptive link text helps search engines understand your content. '+
-'[Learn more](https://webmasters.googleblog.com/2008/10/importance-of-link-architecture.html).',
+'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/descriptive-link-text).',
 requiredArtifacts:['URL','CrawlableLinks']};
 
 }
@@ -7302,7 +7512,7 @@
 {key:'text',itemType:'text',text:'Link Text'}];
 
 
-const details=Audit.makeTableDetails(headings,failingLinks);
+const details=Audit.makeTableDetails(headings,failingLinks,{});
 let displayValue;
 
 if(failingLinks.length){
@@ -7320,7 +7530,7 @@
 
 module.exports=LinkText;
 
-},{"../../lib/url-shim":41,"../audit":2}],"../audits/seo/manual/mobile-friendly":[function(require,module,exports){
+},{"../../lib/url-shim":"url","../audit":2}],"../audits/seo/manual/mobile-friendly":[function(require,module,exports){
 
 
 
@@ -7343,7 +7553,7 @@
 name:'mobile-friendly',
 helpText:'Take the [Mobile-Friendly Test](https://search.google.com/test/mobile-friendly) to check for audits not covered by Lighthouse, like sizing tap targets appropriately. [Learn more](https://developers.google.com/search/mobile-sites/).',
 description:'Page is mobile friendly'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -7372,7 +7582,7 @@
 name:'structured-data',
 helpText:'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:'Structured data is valid'},
-super.meta);
+super.partialMeta);
 }}
 
 
@@ -7399,7 +7609,7 @@
 failureDescription:'Document does not have a meta description',
 helpText:'Meta descriptions may be included in search results to concisely summarize '+
 'page content. '+
-'[Learn more](https://support.google.com/webmasters/answer/35624?hl=en#1).',
+'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/description).',
 requiredArtifacts:['MetaDescription']};
 
 }
@@ -7418,7 +7628,7 @@
 if(artifacts.MetaDescription.trim().length===0){
 return{
 rawValue:false,
-debugString:'Description text is empty.'};
+explanation:'Description text is empty.'};
 
 }
 
@@ -7469,6 +7679,7 @@
 
 
 
+
 function isPluginType(type){
 type=type.trim().toLowerCase();
 
@@ -7481,13 +7692,18 @@
 
 
 
+
 function isPluginURL(url){
 try{
 
 const filePath=new URL(url,'http://example.com').pathname;
 const parts=filePath.split('.');
 
-return parts.length>1&&FILE_EXTENSION_BLOCKLIST.has(parts.pop().trim().toLowerCase());
+if(parts.length<2){
+return false;
+}
+const part=parts.pop();
+return FILE_EXTENSION_BLOCKLIST.has(part.trim().toLowerCase());
 }catch(e){
 return false;
 }
@@ -7502,8 +7718,9 @@
 name:'plugins',
 description:'Document avoids plugins',
 failureDescription:'Document uses plugins',
-helpText:'Most mobile devices do not support plugins, and many desktop browsers restrict '+
-'them.',
+helpText:'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).',
 requiredArtifacts:['EmbeddedContent']};
 
 }
@@ -7544,7 +7761,9 @@
 }).
 map(plugin=>{
 const tagName=plugin.tagName.toLowerCase();
-const attributes=['src','data','code','type'].
+
+const attributeKeys=['src','data','code','type'];
+const attributes=attributeKeys.
 reduce((result,attr)=>{
 if(plugin[attr]!==null){
 result+=` ${attr}="${plugin[attr]}"`;
@@ -7579,7 +7798,239 @@
 
 module.exports=Plugins;
 
-},{"../../lib/url-shim":41,"../audit":2}],"../audits/service-worker":[function(require,module,exports){
+},{"../../lib/url-shim":"url","../audit":2}],"../audits/seo/robots-txt":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+
+
+
+
+
+
+
+const Audit=require('../audit');
+const URL=require('../../lib/url-shim');
+
+const HTTP_CLIENT_ERROR_CODE_LOW=400;
+const HTTP_SERVER_ERROR_CODE_LOW=500;
+
+const DIRECTIVE_SITEMAP='sitemap';
+const DIRECTIVE_USER_AGENT='user-agent';
+const DIRECTIVE_ALLOW='allow';
+const DIRECTIVE_DISALLOW='disallow';
+const DIRECTIVES_GROUP_MEMBERS=new Set([DIRECTIVE_ALLOW,DIRECTIVE_DISALLOW]);
+const DIRECTIVE_SAFELIST=new Set([
+DIRECTIVE_USER_AGENT,DIRECTIVE_DISALLOW,
+DIRECTIVE_ALLOW,DIRECTIVE_SITEMAP,
+'crawl-delay',
+'clean-param','host',
+'request-rate','visit-time','noindex']);
+
+const SITEMAP_VALID_PROTOCOLS=new Set(['https:','http:','ftp:']);
+
+
+
+
+
+
+function verifyDirective(directiveName,directiveValue){
+if(!DIRECTIVE_SAFELIST.has(directiveName)){
+throw new Error('Unknown directive');
+}
+
+if(directiveName===DIRECTIVE_SITEMAP){
+let sitemapUrl;
+
+try{
+sitemapUrl=new URL(directiveValue);
+}catch(e){
+throw new Error('Invalid sitemap URL');
+}
+
+if(!SITEMAP_VALID_PROTOCOLS.has(sitemapUrl.protocol)){
+throw new Error('Invalid sitemap URL protocol');
+}
+}
+
+if(directiveName===DIRECTIVE_USER_AGENT&&!directiveValue){
+throw new Error('No user-agent specified');
+}
+
+if(directiveName===DIRECTIVE_ALLOW||directiveName===DIRECTIVE_DISALLOW){
+if(directiveValue!==''&&directiveValue[0]!=='/'&&directiveValue[0]!=='*'){
+throw new Error('Pattern should either be empty, start with "/" or "*"');
+}
+
+const dollarIndex=directiveValue.indexOf('$');
+
+if(dollarIndex!==-1&&dollarIndex!==directiveValue.length-1){
+throw new Error('"$" should only be used at the end of the pattern');
+}
+}
+}
+
+
+
+
+
+
+function parseLine(line){
+const hashIndex=line.indexOf('#');
+
+if(hashIndex!==-1){
+line=line.substr(0,hashIndex);
+}
+
+line=line.trim();
+
+if(line.length===0){
+return null;
+}
+
+const colonIndex=line.indexOf(':');
+
+if(colonIndex===-1){
+throw new Error('Syntax not understood');
+}
+
+const directiveName=line.slice(0,colonIndex).trim().toLowerCase();
+const directiveValue=line.slice(colonIndex+1).trim();
+
+verifyDirective(directiveName,directiveValue);
+
+return{
+directive:directiveName,
+value:directiveValue};
+
+}
+
+
+
+
+
+function validateRobots(content){
+
+
+
+const errors=[];
+let inGroup=false;
+
+content.
+split(/\r\n|\r|\n/).
+forEach((line,index)=>{
+let parsedLine;
+
+try{
+parsedLine=parseLine(line);
+}catch(e){
+errors.push({
+index:(index+1).toString(),
+line:line,
+message:e.message.toString()});
+
+}
+
+if(!parsedLine){
+return;
+}
+
+
+
+if(parsedLine.directive===DIRECTIVE_USER_AGENT){
+inGroup=true;
+}else if(!inGroup&&DIRECTIVES_GROUP_MEMBERS.has(parsedLine.directive)){
+errors.push({
+index:(index+1).toString(),
+line:line,
+message:'No user-agent specified'});
+
+}
+});
+
+return errors;
+}
+
+class RobotsTxt extends Audit{
+
+
+
+static get meta(){
+return{
+name:'robots-txt',
+description:'robots.txt is valid',
+failureDescription:'robots.txt is not valid',
+helpText:'If your robots.txt file is malformed, crawlers may not be able to understand '+
+'how you want your website to be crawled or indexed.',
+requiredArtifacts:['RobotsTxt']};
+
+}
+
+
+
+
+
+static audit(artifacts){
+const{
+status,
+content}=
+artifacts.RobotsTxt;
+
+if(!status){
+return{
+rawValue:false,
+explanation:'Lighthouse was unable to download your robots.txt file'};
+
+}
+
+if(status>=HTTP_SERVER_ERROR_CODE_LOW){
+return{
+rawValue:false,
+displayValue:`request for robots.txt returned HTTP${status}`};
+
+}else if(status>=HTTP_CLIENT_ERROR_CODE_LOW||content===''){
+return{
+rawValue:true,
+notApplicable:true};
+
+}
+
+
+if(content===null){
+throw new Error(`Status ${status} was valid, but content was null`);
+}
+
+const validationErrors=validateRobots(content);
+
+const headings=[
+{key:'index',itemType:'text',text:'Line #'},
+{key:'line',itemType:'code',text:'Content'},
+{key:'message',itemType:'code',text:'Error'}];
+
+
+const details=Audit.makeTableDetails(headings,validationErrors,{});
+let displayValue;
+
+if(validationErrors.length){
+displayValue=validationErrors.length>1?
+`${validationErrors.length} errors found`:'1 error found';
+}
+
+return{
+rawValue:validationErrors.length===0,
+details,
+displayValue};
+
+}}
+
+
+module.exports=RobotsTxt;
+
+},{"../../lib/url-shim":"url","../audit":2}],"../audits/service-worker":[function(require,module,exports){
 
 
 
@@ -7628,7 +8079,7 @@
 
 module.exports=ServiceWorker;
 
-},{"../lib/url-shim":41,"./audit":2}],"../audits/speed-index-metric":[function(require,module,exports){
+},{"../lib/url-shim":"url","./audit":2}],"../audits/speed-index":[function(require,module,exports){
 
 
 
@@ -7637,105 +8088,64 @@
 'use strict';
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util');
-const LHError=require('../lib/errors');
+const Util=require('../report/html/renderer/util');
 
-
-
-const SCORING_POINT_OF_DIMINISHING_RETURNS=1250;
-const SCORING_MEDIAN=5500;
-
-class SpeedIndexMetric extends Audit{
+class SpeedIndex extends Audit{
 
 
 
 static get meta(){
 return{
-name:'speed-index-metric',
-description:'Perceptual Speed Index',
+name:'speed-index',
+description:'Speed Index',
 helpText:'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).',
-scoringMode:Audit.SCORING_MODES.NUMERIC,
-requiredArtifacts:['traces']};
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
+requiredArtifacts:['traces','devtoolsLogs']};
 
 }
 
 
 
 
-
-
-
-static audit(artifacts){
-const trace=artifacts.traces[this.DEFAULT_PASS];
-
-
-return artifacts.requestSpeedline(trace).then(speedline=>{
-if(speedline.frames.length===0){
-throw new LHError(LHError.errors.NO_SPEEDLINE_FRAMES);
-}
-
-if(speedline.perceptualSpeedIndex===0){
-throw new LHError(LHError.errors.SPEEDINDEX_OF_ZERO);
-}
-
-let visuallyReadyInMs=undefined;
-speedline.frames.forEach(frame=>{
-if(frame.getPerceptualProgress()>=85&&typeof visuallyReadyInMs==='undefined'){
-visuallyReadyInMs=frame.getTimeStamp()-speedline.beginning;
-}
-});
-
-
-
-
-
-
-
-const score=Audit.computeLogNormalScore(
-speedline.perceptualSpeedIndex,
-SCORING_POINT_OF_DIMINISHING_RETURNS,
-SCORING_MEDIAN);
-
-
-const extendedInfo={
-timings:{
-firstVisualChange:speedline.first,
-visuallyReady:visuallyReadyInMs,
-visuallyComplete:speedline.complete,
-perceptualSpeedIndex:speedline.perceptualSpeedIndex},
-
-timestamps:{
-firstVisualChange:(speedline.first+speedline.beginning)*1000,
-visuallyReady:(visuallyReadyInMs+speedline.beginning)*1000,
-visuallyComplete:(speedline.complete+speedline.beginning)*1000,
-perceptualSpeedIndex:(speedline.perceptualSpeedIndex+speedline.beginning)*1000},
-
-frames:speedline.frames.map(frame=>{
+static get defaultOptions(){
 return{
-timestamp:frame.getTimeStamp(),
-progress:frame.getPerceptualProgress()};
-
-})};
 
 
-const rawValue=Math.round(speedline.perceptualSpeedIndex);
+
+scorePODR:2900,
+scoreMedian:5800};
+
+}
+
+
+
+
+
+
+
+
+static async audit(artifacts,context){
+const trace=artifacts.traces[Audit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
+const metricComputationData={trace,devtoolsLog,settings:context.settings};
+const metricResult=await artifacts.requestSpeedIndex(metricComputationData);
 
 return{
-score,
-rawValue,
-displayValue:Util.formatNumber(rawValue),
-extendedInfo:{
-value:extendedInfo}};
+score:Audit.computeLogNormalScore(
+metricResult.timing,
+context.options.scorePODR,
+context.options.scoreMedian),
 
+rawValue:metricResult.timing,
+displayValue:[Util.MS_DISPLAY_VALUE,metricResult.timing]};
 
-});
 }}
 
 
-module.exports=SpeedIndexMetric;
+module.exports=SpeedIndex;
 
-},{"../lib/errors":28,"../report/v2/renderer/util":43,"./audit":2}],"../audits/splash-screen":[function(require,module,exports){
+},{"../report/html/renderer/util":48,"./audit":2}],"../audits/splash-screen":[function(require,module,exports){
 
 
 
@@ -7774,8 +8184,12 @@
 
 }
 
+
+
+
+
 static assessManifest(manifestValues,failures){
-if(manifestValues.isParseFailure){
+if(manifestValues.isParseFailure&&manifestValues.parseFailureReason){
 failures.push(manifestValues.parseFailureReason);
 return;
 }
@@ -7797,7 +8211,11 @@
 }
 
 
+
+
+
 static audit_(artifacts){
+
 const failures=[];
 
 return artifacts.requestManifestValues(artifacts.Manifest).then(manifestValues=>{
@@ -7849,6 +8267,10 @@
 
 }
 
+
+
+
+
 static assessMetaThemecolor(themeColorMeta,failures){
 if(themeColorMeta===null){
 failures.push('No `<meta name="theme-color">` tag found');
@@ -7857,19 +8279,28 @@
 }
 }
 
+
+
+
+
 static assessManifest(manifestValues,failures){
-if(manifestValues.isParseFailure){
+if(manifestValues.isParseFailure&&manifestValues.parseFailureReason){
 failures.push(manifestValues.parseFailureReason);
 return;
 }
 
 const themeColorCheck=manifestValues.allChecks.find(i=>i.id==='hasThemeColor');
-if(!themeColorCheck.passing){
+if(themeColorCheck&&!themeColorCheck.passing){
 failures.push(themeColorCheck.failureText);
 }
 }
 
+
+
+
+
 static audit_(artifacts){
+
 const failures=[];
 
 return artifacts.requestManifestValues(artifacts.Manifest).then(manifestValues=>{
@@ -7887,7 +8318,7 @@
 
 module.exports=ThemedOmnibox;
 
-},{"../lib/web-inspector":42,"./multi-check-audit":5}],"../audits/time-to-first-byte":[function(require,module,exports){
+},{"../lib/web-inspector":47,"./multi-check-audit":5}],"../audits/time-to-first-byte":[function(require,module,exports){
 
 
 
@@ -7896,7 +8327,7 @@
 'use strict';
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util');
+const Util=require('../report/html/renderer/util');
 
 const TTFB_THRESHOLD=600;
 
@@ -7908,13 +8339,15 @@
 return{
 name:'time-to-first-byte',
 description:'Keep server response times low (TTFB)',
-informative:true,
 helpText:'Time To First Byte identifies the time at which your server sends a response.'+
 ' [Learn more](https://developers.google.com/web/tools/chrome-devtools/network-performance/issues).',
 requiredArtifacts:['devtoolsLogs','URL']};
 
 }
 
+
+
+
 static caclulateTTFB(record){
 const timing=record._timing;
 
@@ -7930,28 +8363,34 @@
 
 return artifacts.requestNetworkRecords(devtoolsLogs).
 then(networkRecords=>{
-let debugString='';
+let displayValue='';
 
 const finalUrl=artifacts.URL.finalUrl;
 const finalUrlRequest=networkRecords.find(record=>record._url===finalUrl);
+if(!finalUrlRequest){
+throw new Error(`finalUrl '${finalUrl} not found in network records.`);
+}
 const ttfb=TTFBMetric.caclulateTTFB(finalUrlRequest);
 const passed=ttfb<TTFB_THRESHOLD;
 
 if(!passed){
-debugString=`Root document took ${Util.formatMilliseconds(ttfb,1)} `+
-'to get the first byte.';
+displayValue=`Root document took ${Util.formatMilliseconds(ttfb,1)} `;
 }
 
 return{
 rawValue:ttfb,
-score:passed,
-displayValue:Util.formatMilliseconds(ttfb),
-extendedInfo:{
-value:{
+score:Number(passed),
+displayValue,
+details:{
+summary:{
 wastedMs:ttfb-TTFB_THRESHOLD}},
 
 
-debugString};
+extendedInfo:{
+value:{
+wastedMs:ttfb-TTFB_THRESHOLD}}};
+
+
 
 });
 }}
@@ -7959,7 +8398,7 @@
 
 module.exports=TTFBMetric;
 
-},{"../report/v2/renderer/util":43,"./audit":2}],"../audits/user-timings":[function(require,module,exports){
+},{"../report/html/renderer/util":48,"./audit":2}],"../audits/user-timings":[function(require,module,exports){
 
 
 
@@ -7968,7 +8407,9 @@
 'use strict';
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util');
+
+
+
 
 class UserTimings extends Audit{
 
@@ -7977,12 +8418,12 @@
 static get meta(){
 return{
 name:'user-timings',
+scoreDisplayMode:Audit.SCORING_MODES.INFORMATIVE,
 description:'User Timing marks and measures',
 helpText:'Consider instrumenting your app with the User Timing API to create custom, '+
 'real-world measurements of key user experiences. '+
 '[Learn more](https://developers.google.com/web/tools/lighthouse/audits/user-timing).',
-requiredArtifacts:['traces'],
-informative:true};
+requiredArtifacts:['traces']};
 
 }
 
@@ -7991,6 +8432,7 @@
 
 
 static filterTrace(tabTrace){
+
 const userTimings=[];
 const measuresStartTimes={};
 
@@ -8031,7 +8473,8 @@
 isMark:false,
 args:ut.args,
 startTime:measuresStartTimes[ut.name],
-endTime:ut.ts});
+endTime:ut.ts,
+duration:ut.ts-measuresStartTimes[ut.name]});
 
 }
 });
@@ -8041,7 +8484,7 @@
 ut.startTime=(ut.startTime-tabTrace.navigationStartEvt.ts)/1000;
 if(!ut.isMark){
 ut.endTime=(ut.endTime-tabTrace.navigationStartEvt.ts)/1000;
-ut.duration=ut.endTime-ut.startTime;
+ut.duration=ut.duration/1000;
 }
 });
 
@@ -8060,8 +8503,8 @@
 
 
 
-static excludeBlacklisted(timing){
-return UserTimings.blacklistedPrefixes.every(prefix=>!timing.name.startsWith(prefix));
+static excludeBlacklisted(evt){
+return UserTimings.blacklistedPrefixes.every(prefix=>!evt.name.startsWith(prefix));
 }
 
 
@@ -8077,13 +8520,12 @@
 return{
 name:item.name,
 timingType:item.isMark?'Mark':'Measure',
-time:Util.formatMilliseconds(time,0.001),
-timeAsNumber:time};
+time};
 
 }).sort((itemA,itemB)=>{
 if(itemA.timingType===itemB.timingType){
 
-return itemA.timeAsNumber-itemB.timeAsNumber;
+return itemA.time-itemB.time;
 }else if(itemA.timingType==='Measure'){
 
 return-1;
@@ -8095,7 +8537,7 @@
 const headings=[
 {key:'name',itemType:'text',text:'Name'},
 {key:'timingType',itemType:'text',text:'Type'},
-{key:'time',itemType:'text',text:'Time'}];
+{key:'time',itemType:'ms',granularity:0.01,text:'Time'}];
 
 
 const details=Audit.makeTableDetails(headings,tableRows);
@@ -8103,7 +8545,8 @@
 return{
 
 rawValue:userTimings.length===0,
-displayValue:userTimings.length,
+notApplicable:userTimings.length===0,
+displayValue:userTimings.length?`${userTimings.length}`:'',
 extendedInfo:{
 value:userTimings},
 
@@ -8115,7 +8558,7 @@
 
 module.exports=UserTimings;
 
-},{"../report/v2/renderer/util":43,"./audit":2}],"../audits/uses-rel-preload":[function(require,module,exports){
+},{"./audit":2}],"../audits/uses-rel-preconnect":[function(require,module,exports){
 
 
 
@@ -8125,7 +8568,175 @@
 'use strict';
 
 const Audit=require('./audit');
-const Util=require('../report/v2/renderer/util');
+const UnusedBytes=require('./byte-efficiency/byte-efficiency-audit');
+
+
+
+
+const PRECONNECT_SOCKET_MAX_IDLE=15;
+
+const IGNORE_THRESHOLD_IN_MS=50;
+
+class UsesRelPreconnectAudit extends Audit{
+
+
+
+static get meta(){
+return{
+name:'uses-rel-preconnect',
+description:'Avoid multiple, costly round trips to any origin',
+helpText:
+'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).`,
+requiredArtifacts:['devtoolsLogs','URL'],
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC};
+
+}
+
+
+
+
+
+
+static hasValidTiming(record){
+return record._timing&&record._timing.connectEnd>0&&record._timing.connectStart>0;
+}
+
+
+
+
+
+
+static hasAlreadyConnectedToOrigin(record){
+return(
+record._timing.dnsEnd-record._timing.dnsStart===0&&
+record._timing.connectEnd-record._timing.connectStart===0);
+
+}
+
+
+
+
+
+
+
+static socketStartTimeIsBelowThreshold(record,mainResource){
+return Math.max(0,record.startTime-mainResource.endTime)<PRECONNECT_SOCKET_MAX_IDLE;
+}
+
+
+
+
+
+
+static async audit(artifacts,context){
+const devtoolsLog=artifacts.devtoolsLogs[UsesRelPreconnectAudit.DEFAULT_PASS];
+const URL=artifacts.URL;
+const settings=context.settings;
+let maxWasted=0;
+
+const[networkRecords,mainResource,loadSimulator]=await Promise.all([
+artifacts.requestNetworkRecords(devtoolsLog),
+artifacts.requestMainResource({devtoolsLog,URL}),
+artifacts.requestLoadSimulator({devtoolsLog,settings})]);
+
+
+const{rtt,additionalRttByOrigin}=loadSimulator.getOptions();
+
+
+const origins=new Map();
+networkRecords.
+forEach(record=>{
+if(
+
+!UsesRelPreconnectAudit.hasValidTiming(record)||
+
+record.initiatorRequest()===mainResource||
+
+!record.parsedURL||!record.parsedURL.securityOrigin()||
+
+mainResource.parsedURL.securityOrigin()===record.parsedURL.securityOrigin()||
+
+UsesRelPreconnectAudit.hasAlreadyConnectedToOrigin(record)||
+
+!UsesRelPreconnectAudit.socketStartTimeIsBelowThreshold(record,mainResource))
+{
+return;
+}
+
+const securityOrigin=record.parsedURL.securityOrigin();
+const records=origins.get(securityOrigin)||[];
+records.push(record);
+origins.set(securityOrigin,records);
+});
+
+
+let results=[];
+origins.forEach(records=>{
+
+
+const firstRecordOfOrigin=records.reduce((firstRecord,record)=>{
+return record.startTime<firstRecord.startTime?record:firstRecord;
+});
+
+const securityOrigin=firstRecordOfOrigin.parsedURL.securityOrigin();
+
+
+
+
+const additionalRtt=additionalRttByOrigin.get(securityOrigin)||0;
+let connectionTime=rtt+additionalRtt;
+
+if(firstRecordOfOrigin.parsedURL.scheme==='https')connectionTime=connectionTime*2;
+
+const timeBetweenMainResourceAndDnsStart=
+firstRecordOfOrigin.startTime*1000-
+mainResource.endTime*1000+
+firstRecordOfOrigin._timing.dnsStart;
+
+const wastedMs=Math.min(connectionTime,timeBetweenMainResourceAndDnsStart);
+if(wastedMs<IGNORE_THRESHOLD_IN_MS)return;
+
+maxWasted=Math.max(wastedMs,maxWasted);
+results.push({
+url:securityOrigin,
+wastedMs:wastedMs});
+
+});
+
+results=results.
+sort((a,b)=>b.wastedMs-a.wastedMs);
+
+const headings=[
+{key:'url',itemType:'url',text:'Origin'},
+{key:'wastedMs',itemType:'ms',text:'Potential Savings'}];
+
+const summary={wastedMs:maxWasted};
+const details=Audit.makeTableDetails(headings,results,summary);
+
+return{
+score:UnusedBytes.scoreForWastedMs(maxWasted),
+rawValue:maxWasted,
+displayValue:['Potential savings of %10d\xa0ms',maxWasted],
+extendedInfo:{
+value:results},
+
+details};
+
+}}
+
+
+module.exports=UsesRelPreconnectAudit;
+
+},{"./audit":2,"./byte-efficiency/byte-efficiency-audit":3}],"../audits/uses-rel-preload":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Audit=require('./audit');
 const UnusedBytes=require('./byte-efficiency/byte-efficiency-audit');
 const THRESHOLD_IN_MS=100;
 
@@ -8135,19 +8746,28 @@
 
 static get meta(){
 return{
-category:'Performance',
 name:'uses-rel-preload',
 description:'Preload key requests',
-informative:true,
 helpText:'Consider using <link rel=preload> to prioritize fetching late-discovered '+
-'resources sooner [Learn more](https://developers.google.com/web/updates/2016/03/link-rel-preload).',
-requiredArtifacts:['devtoolsLogs','traces'],
-scoringMode:Audit.SCORING_MODES.NUMERIC};
+'resources sooner. [Learn more](https://developers.google.com/web/updates/2016/03/link-rel-preload).',
+requiredArtifacts:['devtoolsLogs','traces','URL'],
+scoreDisplayMode:Audit.SCORING_MODES.NUMERIC};
 
 }
 
+
+
+
+
+
 static _flattenRequests(chains,maxLevel,minLevel=0){
+
 const requests=[];
+
+
+
+
+
 const flatten=(chains,level)=>{
 Object.keys(chains).forEach(chain=>{
 if(chains[chain]){
@@ -8172,62 +8792,134 @@
 
 
 
-static audit(artifacts){
-const devtoolsLogs=artifacts.devtoolsLogs[UsesRelPreloadAudit.DEFAULT_PASS];
 
-return Promise.all([
-artifacts.requestCriticalRequestChains(devtoolsLogs),
-artifacts.requestMainResource(devtoolsLogs)]).
-then(([critChains,mainResource])=>{
+
+
+static computeWasteWithGraph(urls,graph,simulator){
+if(!urls.size){
+return{wastedMs:0,results:[]};
+}
+
+
+
+const simulationBeforeChanges=simulator.simulate(graph,{flexibleOrdering:true});
+const modifiedGraph=graph.cloneWithRelationships();
+
+
+const nodesToPreload=[];
+
+let mainDocumentNode=null;
+modifiedGraph.traverse(node=>{
+if(node.type!=='network')return;
+
+const networkNode=node;
+if(node.isMainDocument()){
+mainDocumentNode=networkNode;
+}else if(networkNode.record&&urls.has(networkNode.record.url)){
+nodesToPreload.push(networkNode);
+}
+});
+
+if(!mainDocumentNode){
+
+throw new Error('Could not find main document node');
+}
+
+
+
+for(const node of nodesToPreload){
+node.removeAllDependencies();
+node.addDependency(mainDocumentNode);
+}
+
+
+const simulationAfterChanges=simulator.simulate(modifiedGraph,{flexibleOrdering:true});
+const originalNodesByRecord=Array.from(simulationBeforeChanges.nodeTimings.keys()).
+
+reduce((map,node)=>map.set(node.record,node),new Map());
+
 const results=[];
-let maxWasted=0;
+for(const node of nodesToPreload){
+const originalNode=originalNodesByRecord.get(node.record);
+const timingAfter=simulationAfterChanges.nodeTimings.get(node);
+const timingBefore=simulationBeforeChanges.nodeTimings.get(originalNode);
+
+const wastedMs=Math.round(timingBefore.endTime-timingAfter.endTime);
+if(wastedMs<THRESHOLD_IN_MS)continue;
+results.push({url:node.record.url,wastedMs});
+}
+
+if(!results.length){
+return{wastedMs:0,results};
+}
+
+return{
+
+
+wastedMs:Math.max(...results.map(item=>item.wastedMs)),
+results};
+
+}
+
+
+
+
+
+
+static async audit(artifacts,context){
+const trace=artifacts.traces[UsesRelPreloadAudit.DEFAULT_PASS];
+const devtoolsLog=artifacts.devtoolsLogs[UsesRelPreloadAudit.DEFAULT_PASS];
+const URL=artifacts.URL;
+const simulatorOptions={trace,devtoolsLog,settings:context.settings};
+
+const[critChains,mainResource,graph,simulator]=await Promise.all([
+
+artifacts.requestCriticalRequestChains({devtoolsLog,URL}),
+artifacts.requestMainResource({devtoolsLog,URL}),
+artifacts.requestPageDependencyGraph({trace,devtoolsLog}),
+artifacts.requestLoadSimulator(simulatorOptions)]);
+
+
 
 const mainResourceIndex=mainResource.redirects?mainResource.redirects.length:0;
 
 const criticalRequests=UsesRelPreloadAudit._flattenRequests(critChains,
 3+mainResourceIndex,2+mainResourceIndex);
-criticalRequests.forEach(request=>{
-const networkRecord=request;
+
+
+const urls=new Set();
+for(const networkRecord of criticalRequests){
 if(!networkRecord._isLinkPreload&&networkRecord.protocol!=='data'){
-
-const wastedMs=Math.min(request._startTime-mainResource._endTime,
-request._endTime-request._startTime)*1000;
-
-if(wastedMs>=THRESHOLD_IN_MS){
-maxWasted=Math.max(wastedMs,maxWasted);
-results.push({
-url:request.url,
-wastedMs:Util.formatMilliseconds(wastedMs)});
-
+urls.add(networkRecord._url);
 }
 }
-});
 
+const{results,wastedMs}=UsesRelPreloadAudit.computeWasteWithGraph(urls,graph,simulator);
 
 results.sort((a,b)=>b.wastedMs-a.wastedMs);
 
 const headings=[
 {key:'url',itemType:'url',text:'URL'},
-{key:'wastedMs',itemType:'text',text:'Potential Savings'}];
+{key:'wastedMs',itemType:'ms',text:'Potential Savings',granularity:10}];
 
-const details=Audit.makeTableDetails(headings,results);
+const summary={wastedMs};
+const details=Audit.makeTableDetails(headings,results,summary);
 
 return{
-score:UnusedBytes.scoreForWastedMs(maxWasted),
-rawValue:maxWasted,
-displayValue:Util.formatMilliseconds(maxWasted),
+score:UnusedBytes.scoreForWastedMs(wastedMs),
+rawValue:wastedMs,
+displayValue:['Potential savings of %10d\xa0ms',wastedMs],
 extendedInfo:{
 value:results},
 
 details};
 
-});
 }}
 
 
 module.exports=UsesRelPreloadAudit;
 
-},{"../report/v2/renderer/util":43,"./audit":2,"./byte-efficiency/byte-efficiency-audit":3}],"../audits/viewport":[function(require,module,exports){
+},{"./audit":2,"./byte-efficiency/byte-efficiency-audit":3}],"../audits/viewport":[function(require,module,exports){
 
 
 
@@ -8261,35 +8953,34 @@
 static audit(artifacts){
 if(artifacts.Viewport===null){
 return{
-debugString:'No viewport meta tag found',
+explanation:'No viewport meta tag found',
 rawValue:false};
 
 }
 
-let debugString='';
+const warnings=[];
 const parsedProps=Parser.parseMetaViewPortContent(artifacts.Viewport);
 
 if(Object.keys(parsedProps.unknownProperties).length){
-debugString+=`Invalid properties found: ${JSON.stringify(parsedProps.unknownProperties)}. `;
+warnings.push(`Invalid properties found: ${JSON.stringify(parsedProps.unknownProperties)}`);
 }
 if(Object.keys(parsedProps.invalidValues).length){
-debugString+=`Invalid values found: ${JSON.stringify(parsedProps.invalidValues)}. `;
+warnings.push(`Invalid values found: ${JSON.stringify(parsedProps.invalidValues)}`);
 }
-debugString=debugString.trim();
 
 const viewportProps=parsedProps.validProperties;
 const hasMobileViewport=viewportProps.width||viewportProps['initial-scale'];
 
 return{
 rawValue:!!hasMobileViewport,
-debugString};
+warnings};
 
 }}
 
 
 module.exports=Viewport;
 
-},{"./audit":2,"metaviewport-parser":139}],"../audits/webapp-install-banner":[function(require,module,exports){
+},{"./audit":2,"metaviewport-parser":145}],"../audits/webapp-install-banner":[function(require,module,exports){
 
 
 
@@ -8336,13 +9027,17 @@
 
 }
 
-static assessManifest(artifacts,result){
-const{manifestValues,failures}=result;
-if(manifestValues.isParseFailure){
-failures.push(manifestValues.parseFailureReason);
-return;
+
+
+
+
+static assessManifest(manifestValues){
+if(manifestValues.isParseFailure&&manifestValues.parseFailureReason){
+return[manifestValues.parseFailureReason];
 }
 
+
+const failures=[];
 const bannerCheckIds=[
 'hasName',
 'hasShortName',
@@ -8357,40 +9052,76 @@
 failures.push(item.failureText);
 }
 });
+
+return failures;
 }
 
 
-static assessServiceWorker(artifacts,result){
+
+
+
+static assessServiceWorker(artifacts){
+const failures=[];
 const hasServiceWorker=SWAudit.audit(artifacts).rawValue;
 if(!hasServiceWorker){
-result.failures.push('Site does not register a service worker');
-}
+failures.push('Site does not register a service worker');
 }
 
-static assessOfflineStartUrl(artifacts,result){
+return failures;
+}
+
+
+
+
+
+static assessOfflineStartUrl(artifacts){
+const failures=[];
+const warnings=[];
 const hasOfflineStartUrl=artifacts.StartUrl.statusCode===200;
 
 if(!hasOfflineStartUrl){
-result.failures.push('Service worker does not successfully serve the manifest\'s start_url');
-if(artifacts.StartUrl.debugString)result.failures.push(artifacts.StartUrl.debugString);
+failures.push('Service worker does not successfully serve the manifest\'s start_url');
+
+if(artifacts.StartUrl.debugString){
+failures.push(artifacts.StartUrl.debugString);
+}
 }
 
 if(artifacts.StartUrl.debugString){
-result.warnings.push(artifacts.StartUrl.debugString);
+warnings.push(artifacts.StartUrl.debugString);
 }
+
+return{failures,warnings};
 }
 
+
+
+
+
 static audit_(artifacts){
-const failures=[];
-const warnings=[];
+
+let offlineFailures=[];
+
+let offlineWarnings=[];
 
 return artifacts.requestManifestValues(artifacts.Manifest).then(manifestValues=>{
-const result={warnings,failures,manifestValues};
-WebappInstallBanner.assessManifest(artifacts,result);
-WebappInstallBanner.assessServiceWorker(artifacts,result);
-WebappInstallBanner.assessOfflineStartUrl(artifacts,result);
+const manifestFailures=WebappInstallBanner.assessManifest(manifestValues);
+const swFailures=WebappInstallBanner.assessServiceWorker(artifacts);
+if(!swFailures.length){
+const{failures,warnings}=WebappInstallBanner.assessOfflineStartUrl(artifacts);
+offlineFailures=failures;
+offlineWarnings=warnings;
+}
 
-return result;
+return{
+warnings:offlineWarnings,
+failures:[
+...manifestFailures,
+...swFailures,
+...offlineFailures],
+
+manifestValues};
+
 });
 }}
 
@@ -8433,7 +9164,7 @@
 if(artifact.value.trim()===''){
 return{
 rawValue:false,
-debugString:'The page body should render some content if its scripts are not available.'};
+explanation:'The page body should render some content if its scripts are not available.'};
 
 }
 
@@ -8477,1328 +9208,25 @@
 
 
 static audit(artifacts){
-let debugString;
+const warnings=[];
 const passed=artifacts.Offline===200;
 if(!passed&&
-!URL.equalWithExcludedFragments(artifacts.URL.initialUrl,artifacts.URL.finalUrl)){
-debugString='WARNING: You may be failing this check because your test URL '+
-`(${artifacts.URL.initialUrl}) was redirected to "${artifacts.URL.finalUrl}". `+
-'Try testing the second URL directly.';
+!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.');
 }
 
 return{
 rawValue:passed,
-debugString};
+warnings};
 
 }}
 
 
 module.exports=WorksOffline;
 
-},{"../lib/url-shim":41,"./audit":2}],"./gather/computed/computed-artifact":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const ArbitraryEqualityMap=require('../../lib/arbitrary-equality-map');
-
-class ComputedArtifact{
-
-
-
-constructor(allComputedArtifacts){
-
-this._cache=new ArbitraryEqualityMap();
-this._cache.setEqualityFn(ArbitraryEqualityMap.deepEquals);
-
-
-this._allComputedArtifacts=allComputedArtifacts;
-}
-
-get requiredNumberOfArtifacts(){
-return 1;
-}
-
-
-
-
-
-
-
-
-
-
-
-compute_(artifact,allComputedArtifacts){
-throw new Error('compute_() not implemented for computed artifact '+this.name);
-}
-
-
-
-
-
-_assertCorrectNumberOfArtifacts(artifacts){
-const actual=artifacts.length;
-const expected=this.requiredNumberOfArtifacts;
-if(actual!==expected){
-const className=this.constructor.name;
-throw new Error(`${className} requires ${expected} artifacts but ${actual} were given`);
-}
-}
-
-
-
-
-
-
-
-
-request(...artifacts){
-this._assertCorrectNumberOfArtifacts(artifacts);
-if(this._cache.has(artifacts)){
-return Promise.resolve(this._cache.get(artifacts));
-}
-
-const artifactPromise=Promise.resolve().
-then(_=>this.compute_(...artifacts,this._allComputedArtifacts));
-this._cache.set(artifacts,artifactPromise);
-
-return artifactPromise;
-}}
-
-
-module.exports=ComputedArtifact;
-
-},{"../../lib/arbitrary-equality-map":17}],"./gather/computed/critical-request-chains":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const ComputedArtifact=require('./computed-artifact');
-const WebInspector=require('../../lib/web-inspector');
-const assert=require('assert');
-
-class CriticalRequestChains extends ComputedArtifact{
-get name(){
-return'CriticalRequestChains';
-}
-
-
-
-
-
-
-
-
-static isCritical(request,mainResource){
-assert.ok(mainResource,'mainResource not provided');
-const resourceTypeCategory=request._resourceType&&request._resourceType._category;
-
-
-const isIframe=request._resourceType===WebInspector.resourceTypes.Document&&
-request.frameId!==mainResource.frameId;
-
-
-
-const nonCriticalResourceTypes=[
-WebInspector.resourceTypes.Image._category,
-WebInspector.resourceTypes.XHR._category];
-
-if(nonCriticalResourceTypes.includes(resourceTypeCategory)||
-isIframe||
-request.mimeType&&request.mimeType.startsWith('image/')){
-return false;
-}
-
-return['VeryHigh','High','Medium'].includes(request.priority());
-}
-
-static extractChain([networkRecords,mainResource]){
-networkRecords=networkRecords.filter(req=>req.finished);
-
-
-const requestIdToRequests=new Map();
-for(const request of networkRecords){
-requestIdToRequests.set(request.requestId,request);
-}
-
-
-
-const criticalRequests=networkRecords.filter(request=>
-CriticalRequestChains.isCritical(request,mainResource));
-
-
-const criticalRequestChains={};
-for(const request of criticalRequests){
-
-
-
-const ancestors=[];
-let ancestorRequest=request.initiatorRequest();
-let node=criticalRequestChains;
-while(ancestorRequest){
-const ancestorIsCritical=CriticalRequestChains.isCritical(ancestorRequest,mainResource);
-
-
-
-
-
-if(!ancestorIsCritical||ancestors.includes(ancestorRequest.requestId)){
-
-
-ancestors.length=0;
-node=undefined;
-break;
-}
-ancestors.push(ancestorRequest.requestId);
-ancestorRequest=ancestorRequest.initiatorRequest();
-}
-
-
-
-let ancestor=ancestors.pop();
-while(ancestor){
-const parentRequest=requestIdToRequests.get(ancestor);
-const parentRequestId=parentRequest.requestId;
-if(!node[parentRequestId]){
-node[parentRequestId]={
-request:parentRequest,
-children:{}};
-
-}
-
-
-ancestor=ancestors.pop();
-node=node[parentRequestId].children;
-}
-
-if(!node){
-continue;
-}
-
-
-if(node[request.requestId]){
-continue;
-}
-
-
-node[request.requestId]={
-request,
-children:{}};
-
-}
-
-return criticalRequestChains;
-}
-
-
-
-
-
-
-compute_(devtoolsLog,artifacts){
-return Promise.all([
-artifacts.requestNetworkRecords(devtoolsLog),
-artifacts.requestMainResource(devtoolsLog)]).
-
-then(CriticalRequestChains.extractChain);
-}}
-
-
-module.exports=CriticalRequestChains;
-
-},{"../../lib/web-inspector":42,"./computed-artifact":"./gather/computed/computed-artifact","assert":47}],"./gather/computed/dtm-model":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const ComputedArtifact=require('./computed-artifact');
-const DTM=require('../../lib/traces/devtools-timeline-model');
-
-class DevtoolsTimelineModel extends ComputedArtifact{
-get name(){
-return'DevtoolsTimelineModel';
-}
-
-
-
-
-
-compute_(trace){
-return Promise.resolve(new DTM(trace));
-}}
-
-
-module.exports=DevtoolsTimelineModel;
-
-},{"../../lib/traces/devtools-timeline-model":37,"./computed-artifact":"./gather/computed/computed-artifact"}],"./gather/computed/first-interactive":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const ComputedArtifact=require('./computed-artifact');
-const TracingProcessor=require('../../lib/traces/tracing-processor');
-const LHError=require('../../lib/errors');
-
-const LONG_TASK_THRESHOLD=50;
-
-const MAX_TASK_CLUSTER_DURATION=250;
-const MIN_TASK_CLUSTER_PADDING=1000;
-const MIN_TASK_CLUSTER_FMP_DISTANCE=5000;
-
-const MAX_QUIET_WINDOW_SIZE=5000;
-
-
-const EXPONENTIATION_COEFFICIENT=-Math.log(3-1)/15;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-class FirstInteractive extends ComputedArtifact{
-get name(){
-return'FirstInteractive';
-}
-
-
-
-
-
-static getRequiredWindowSizeInMs(t){
-const tInSeconds=t/1000;
-const exponentiationComponent=Math.exp(EXPONENTIATION_COEFFICIENT*tInSeconds);
-return(4*exponentiationComponent+1)*1000;
-}
-
-
-
-
-
-
-
-
-
-
-static getTaskClustersInWindow(tasks,startIndex,windowEnd){
-const clusters=[];
-
-let previousTaskEndTime=-Infinity;
-let currentCluster=null;
-
-
-
-
-
-const clusteringWindowEnd=windowEnd+MIN_TASK_CLUSTER_PADDING;
-const isInClusteringWindow=task=>task.start<clusteringWindowEnd;
-for(let i=startIndex;i<tasks.length;i++){
-if(!isInClusteringWindow(tasks[i])){
-break;
-}
-
-const task=tasks[i];
-
-
-if(task.start-previousTaskEndTime>MIN_TASK_CLUSTER_PADDING){
-currentCluster=[];
-clusters.push(currentCluster);
-}
-
-currentCluster.push(task);
-previousTaskEndTime=task.end;
-}
-
-return clusters.
-
-map(tasks=>{
-const start=tasks[0].start;
-const end=tasks[tasks.length-1].end;
-const duration=end-start;
-return{start,end,duration};
-}).
-
-filter(cluster=>cluster.start<windowEnd);
-}
-
-
-
-
-
-
-
-
-
-
-static findQuietWindow(FMP,traceEnd,longTasks){
-
-if(longTasks.length===0||
-longTasks[0].start>FMP+FirstInteractive.getRequiredWindowSizeInMs(0)){
-return FMP;
-}
-
-const isTooCloseToFMP=cluster=>cluster.start<FMP+MIN_TASK_CLUSTER_FMP_DISTANCE;
-const isTooLong=cluster=>cluster.duration>MAX_TASK_CLUSTER_DURATION;
-const isBadCluster=cluster=>isTooCloseToFMP(cluster)||isTooLong(cluster);
-
-
-
-for(let i=0;i<longTasks.length;i++){
-const windowStart=longTasks[i].end;
-const windowSize=FirstInteractive.getRequiredWindowSizeInMs(windowStart-FMP);
-const windowEnd=windowStart+windowSize;
-
-
-if(windowEnd>traceEnd){
-throw new LHError(LHError.errors.NO_FCPUI_IDLE_PERIOD);
-}
-
-
-if(i+1<longTasks.length&&
-longTasks[i+1].start-windowStart<=MIN_TASK_CLUSTER_PADDING){
-continue;
-}
-
-const taskClusters=FirstInteractive.getTaskClustersInWindow(longTasks,i+1,windowEnd);
-const hasBadTaskClusters=taskClusters.some(isBadCluster);
-
-if(!hasBadTaskClusters){
-return windowStart;
-}
-}
-
-throw new LHError(LHError.errors.NO_FCPUI_IDLE_PERIOD);
-}
-
-
-
-
-
-computeWithArtifacts(traceOfTab){
-const navStart=traceOfTab.timestamps.navigationStart;
-const FMP=traceOfTab.timings.firstMeaningfulPaint;
-const DCL=traceOfTab.timings.domContentLoaded;
-const traceEnd=traceOfTab.timings.traceEnd;
-
-if(traceEnd-FMP<MAX_QUIET_WINDOW_SIZE){
-throw new LHError(LHError.errors.FMP_TOO_LATE_FOR_FCPUI);
-}
-
-if(!FMP||!DCL){
-throw new LHError(FMP?LHError.errors.NO_DCL:LHError.errors.NO_FMP);
-}
-
-const longTasksAfterFMP=TracingProcessor.getMainThreadTopLevelEvents(traceOfTab,FMP).
-filter(evt=>evt.duration>=LONG_TASK_THRESHOLD);
-const firstInteractive=FirstInteractive.findQuietWindow(FMP,traceEnd,longTasksAfterFMP);
-
-const valueInMs=Math.max(firstInteractive,DCL);
-return{
-timeInMs:valueInMs,
-timestamp:valueInMs*1000+navStart};
-
-}
-
-
-
-
-
-
-compute_(trace,artifacts){
-return artifacts.requestTraceOfTab(trace).then(traceOfTab=>{
-return this.computeWithArtifacts(traceOfTab);
-});
-}}
-
-
-module.exports=FirstInteractive;
-
-},{"../../lib/errors":28,"../../lib/traces/tracing-processor":40,"./computed-artifact":"./gather/computed/computed-artifact"}],"./gather/computed/main-resource":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const ComputedArtifact=require('./computed-artifact');
-const HTTP_REDIRECT_CODE_LOW=300;
-const HTTP_REDIRECT_CODE_HIGH=399;
-
-
-
-
-
-class MainResource extends ComputedArtifact{
-get name(){
-return'MainResource';
-}
-
-
-
-
-
-isMainResource(request){
-return request.statusCode<HTTP_REDIRECT_CODE_LOW||
-request.statusCode>HTTP_REDIRECT_CODE_HIGH;
-}
-
-
-
-
-
-
-compute_(devtoolsLog,artifacts){
-return artifacts.requestNetworkRecords(devtoolsLog).
-then(requests=>{
-const mainResource=requests.find(this.isMainResource);
-
-if(!mainResource){
-throw new Error('Unable to identify the main resource');
-}
-
-return mainResource;
-});
-}}
-
-
-module.exports=MainResource;
-
-},{"./computed-artifact":"./gather/computed/computed-artifact"}],"./gather/computed/manifest-values":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const ComputedArtifact=require('./computed-artifact');
-const icons=require('../../lib/icons');
-
-const PWA_DISPLAY_VALUES=['minimal-ui','fullscreen','standalone'];
-
-
-
-const SUGGESTED_SHORTNAME_LENGTH=12;
-
-class ManifestValues extends ComputedArtifact{
-get name(){
-return'ManifestValues';
-}
-
-static get validityIds(){
-return['hasManifest','hasParseableManifest'];
-}
-
-static get manifestChecks(){
-return[
-{
-id:'hasStartUrl',
-failureText:'Manifest does not contain a `start_url`',
-validate:manifest=>!!manifest.value.start_url.value},
-
-{
-id:'hasIconsAtLeast192px',
-failureText:'Manifest does not have icons at least 192px',
-validate:manifest=>icons.doExist(manifest.value)&&
-icons.sizeAtLeast(192,manifest.value).length>0},
-
-{
-id:'hasIconsAtLeast512px',
-failureText:'Manifest does not have icons at least 512px',
-validate:manifest=>icons.doExist(manifest.value)&&
-icons.sizeAtLeast(512,manifest.value).length>0},
-
-{
-id:'hasPWADisplayValue',
-failureText:'Manifest\'s `display` value is not one of: '+PWA_DISPLAY_VALUES.join(' | '),
-validate:manifest=>PWA_DISPLAY_VALUES.includes(manifest.value.display.value)},
-
-{
-id:'hasBackgroundColor',
-failureText:'Manifest does not have `background_color`',
-validate:manifest=>!!manifest.value.background_color.value},
-
-{
-id:'hasThemeColor',
-failureText:'Manifest does not have `theme_color`',
-validate:manifest=>!!manifest.value.theme_color.value},
-
-{
-id:'hasShortName',
-failureText:'Manifest does not have `short_name`',
-validate:manifest=>!!manifest.value.short_name.value},
-
-{
-id:'shortNameLength',
-failureText:'Manifest `short_name` will be truncated when displayed on the homescreen',
-validate:manifest=>!!manifest.value.short_name.value&&
-manifest.value.short_name.value.length<=SUGGESTED_SHORTNAME_LENGTH},
-
-{
-id:'hasName',
-failureText:'Manifest does not have `name`',
-validate:manifest=>!!manifest.value.name.value}];
-
-
-}
-
-
-
-
-
-
-compute_(manifest){
-
-let parseFailureReason;
-
-if(manifest===null){
-parseFailureReason='No manifest was fetched';
-}
-if(manifest&&manifest.value===undefined){
-parseFailureReason='Manifest failed to parse as valid JSON';
-}
-if(parseFailureReason){
-return{
-isParseFailure:true,
-parseFailureReason,
-allChecks:[]};
-
-}
-
-
-const remainingChecks=ManifestValues.manifestChecks.map(item=>{
-item.passing=item.validate(manifest);
-return item;
-});
-
-return{
-isParseFailure:false,
-parseFailureReason,
-allChecks:remainingChecks};
-
-}}
-
-
-module.exports=ManifestValues;
-
-},{"../../lib/icons":30,"./computed-artifact":"./gather/computed/computed-artifact"}],"./gather/computed/network-records":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const ComputedArtifact=require('./computed-artifact');
-const NetworkRecorder=require('../../lib/network-recorder');
-
-class NetworkRecords extends ComputedArtifact{
-get name(){
-return'NetworkRecords';
-}
-
-
-
-
-
-compute_(devtoolsLog){
-return NetworkRecorder.recordsFromLogs(devtoolsLog);
-}}
-
-
-module.exports=NetworkRecords;
-
-},{"../../lib/network-recorder":32,"./computed-artifact":"./gather/computed/computed-artifact"}],"./gather/computed/network-throughput":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const ComputedArtifact=require('./computed-artifact');
-
-class NetworkThroughput extends ComputedArtifact{
-get name(){
-return'NetworkThroughput';
-}
-
-
-
-
-
-
-
-
-
-static getThroughput(networkRecords){
-let totalBytes=0;
-const timeBoundaries=networkRecords.reduce((boundaries,record)=>{
-const scheme=record.parsedURL&&record.parsedURL.scheme;
-if(scheme==='data'||record.failed||!record.finished||
-record.statusCode>300||!record.transferSize){
-return boundaries;
-}
-
-totalBytes+=record.transferSize;
-boundaries.push({time:record.responseReceivedTime,isStart:true});
-boundaries.push({time:record.endTime,isStart:false});
-return boundaries;
-},[]).sort((a,b)=>a.time-b.time);
-
-if(!timeBoundaries.length){
-return Infinity;
-}
-
-let inflight=0;
-let currentStart=0;
-let totalDuration=0;
-timeBoundaries.forEach(boundary=>{
-if(boundary.isStart){
-if(inflight===0){
-currentStart=boundary.time;
-}
-inflight++;
-}else{
-inflight--;
-if(inflight===0){
-totalDuration+=boundary.time-currentStart;
-}
-}
-});
-
-return totalBytes/totalDuration;
-}
-
-
-
-
-
-
-compute_(devtoolsLog,artifacts){
-return artifacts.requestNetworkRecords(devtoolsLog).
-then(NetworkThroughput.getThroughput);
-}}
-
-
-module.exports=NetworkThroughput;
-
-},{"./computed-artifact":"./gather/computed/computed-artifact"}],"./gather/computed/page-dependency-graph":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const ComputedArtifact=require('./computed-artifact');
-const NetworkNode=require('../../lib/dependency-graph/network-node');
-const CPUNode=require('../../lib/dependency-graph/cpu-node');
-const TracingProcessor=require('../../lib/traces/tracing-processor');
-const WebInspector=require('../../lib/web-inspector');
-
-
-const MINIMUM_TASK_DURATION_OF_INTEREST=10;
-
-
-const IGNORED_MIME_TYPES_REGEX=/^video/;
-
-class PageDependencyGraphArtifact extends ComputedArtifact{
-get name(){
-return'PageDependencyGraph';
-}
-
-get requiredNumberOfArtifacts(){
-return 2;
-}
-
-
-
-
-
-static getNetworkInitiators(record){
-if(!record._initiator)return[];
-if(record._initiator.url)return[record._initiator.url];
-if(record._initiator.type==='script'){
-const frames=record._initiator.stack.callFrames;
-return Array.from(new Set(frames.map(frame=>frame.url))).filter(Boolean);
-}
-
-return[];
-}
-
-
-
-
-
-static getNetworkNodeOutput(networkRecords){
-const nodes=[];
-const idToNodeMap=new Map();
-const urlToNodeMap=new Map();
-
-networkRecords.forEach(record=>{
-if(IGNORED_MIME_TYPES_REGEX.test(record.mimeType))return;
-const node=new NetworkNode(record);
-nodes.push(node);
-
-const list=urlToNodeMap.get(record.url)||[];
-list.push(node);
-
-idToNodeMap.set(record.requestId,node);
-urlToNodeMap.set(record.url,list);
-});
-
-return{nodes,idToNodeMap,urlToNodeMap};
-}
-
-
-
-
-
-static getCPUNodes(traceOfTab){
-const nodes=[];
-let i=0;
-
-const minimumEvtDur=MINIMUM_TASK_DURATION_OF_INTEREST*1000;
-while(i<traceOfTab.mainThreadEvents.length){
-const evt=traceOfTab.mainThreadEvents[i];
-
-
-if(
-!TracingProcessor.isScheduleableTask(evt)||
-!evt.dur||
-evt.dur<minimumEvtDur)
-{
-i++;
-continue;
-}
-
-
-const children=[];
-i++;
-for(
-const endTime=evt.ts+evt.dur;
-i<traceOfTab.mainThreadEvents.length&&traceOfTab.mainThreadEvents[i].ts<endTime;
-i++)
-{
-children.push(traceOfTab.mainThreadEvents[i]);
-}
-
-nodes.push(new CPUNode(evt,children));
-}
-
-return nodes;
-}
-
-
-
-
-
-static linkNetworkNodes(rootNode,networkNodeOutput){
-networkNodeOutput.nodes.forEach(node=>{
-const initiators=PageDependencyGraphArtifact.getNetworkInitiators(node.record);
-if(initiators.length){
-initiators.forEach(initiator=>{
-const parentCandidates=networkNodeOutput.urlToNodeMap.get(initiator)||[rootNode];
-
-const parent=parentCandidates.length===1?parentCandidates[0]:rootNode;
-node.addDependency(parent);
-});
-}else if(node!==rootNode){
-rootNode.addDependent(node);
-}
-
-const redirects=Array.from(node.record.redirects||[]);
-redirects.push(node.record);
-
-for(let i=1;i<redirects.length;i++){
-const redirectNode=networkNodeOutput.idToNodeMap.get(redirects[i-1].requestId);
-const actualNode=networkNodeOutput.idToNodeMap.get(redirects[i].requestId);
-actualNode.addDependency(redirectNode);
-}
-});
-}
-
-
-
-
-
-
-static linkCPUNodes(rootNode,networkNodeOutput,cpuNodes){
-function addDependentNetworkRequest(cpuNode,reqId){
-const networkNode=networkNodeOutput.idToNodeMap.get(reqId);
-if(!networkNode||
-networkNode.record._resourceType!==WebInspector.resourceTypes.XHR)return;
-cpuNode.addDependent(networkNode);
-}
-
-function addDependencyOnUrl(cpuNode,url){
-if(!url)return;
-const candidates=networkNodeOutput.urlToNodeMap.get(url)||[];
-
-let minCandidate=null;
-let minDistance=Infinity;
-
-candidates.forEach(candidate=>{
-const distance=cpuNode.startTime-candidate.endTime;
-if(distance>0&&distance<minDistance){
-minCandidate=candidate;
-minDistance=distance;
-}
-});
-
-if(!minCandidate)return;
-cpuNode.addDependency(minCandidate);
-}
-
-const timers=new Map();
-for(const node of cpuNodes){
-for(const evt of node.childEvents){
-if(!evt.args.data)continue;
-
-const url=evt.args.data.url;
-const stackTraceUrls=(evt.args.data.stackTrace||[]).map(l=>l.url).filter(Boolean);
-
-switch(evt.name){
-case'TimerInstall':
-timers.set(evt.args.data.timerId,node);
-stackTraceUrls.forEach(url=>addDependencyOnUrl(node,url));
-break;
-case'TimerFire':{
-const installer=timers.get(evt.args.data.timerId);
-if(!installer)break;
-installer.addDependent(node);
-break;
-}
-
-case'InvalidateLayout':
-case'ScheduleStyleRecalculation':
-stackTraceUrls.forEach(url=>addDependencyOnUrl(node,url));
-break;
-
-case'EvaluateScript':
-addDependencyOnUrl(node,url);
-stackTraceUrls.forEach(url=>addDependencyOnUrl(node,url));
-break;
-
-case'XHRReadyStateChange':
-
-if(evt.args.data.readyState!==4)break;
-
-addDependencyOnUrl(node,url);
-stackTraceUrls.forEach(url=>addDependencyOnUrl(node,url));
-break;
-
-case'FunctionCall':
-case'v8.compile':
-addDependencyOnUrl(node,url);
-break;
-
-case'ParseAuthorStyleSheet':
-addDependencyOnUrl(node,evt.args.data.styleSheetUrl);
-break;
-
-case'ResourceSendRequest':
-addDependentNetworkRequest(node,evt.args.data.requestId,evt);
-stackTraceUrls.forEach(url=>addDependencyOnUrl(node,url));
-break;}
-
-}
-
-if(node.getNumberOfDependencies()===0){
-node.addDependency(rootNode);
-}
-}
-}
-
-
-
-
-
-
-static createGraph(traceOfTab,networkRecords){
-const networkNodeOutput=PageDependencyGraphArtifact.getNetworkNodeOutput(networkRecords);
-const cpuNodes=PageDependencyGraphArtifact.getCPUNodes(traceOfTab);
-
-const rootRequest=networkRecords.reduce((min,r)=>min.startTime<r.startTime?min:r);
-const rootNode=networkNodeOutput.idToNodeMap.get(rootRequest.requestId);
-
-PageDependencyGraphArtifact.linkNetworkNodes(rootNode,networkNodeOutput,networkRecords);
-PageDependencyGraphArtifact.linkCPUNodes(rootNode,networkNodeOutput,cpuNodes);
-
-if(NetworkNode.hasCycle(rootNode)){
-throw new Error('Invalid dependency graph created, cycle detected');
-}
-
-return rootNode;
-}
-
-
-
-
-
-static printGraph(rootNode,widthInCharacters=100){
-function padRight(str,target,padChar=' '){
-return str+padChar.repeat(Math.max(target-str.length,0));
-}
-
-const nodes=[];
-rootNode.traverse(node=>nodes.push(node));
-nodes.sort((a,b)=>a.startTime-b.startTime);
-
-const min=nodes[0].startTime;
-const max=nodes.reduce((max,node)=>Math.max(max,node.endTime),0);
-
-const totalTime=max-min;
-const timePerCharacter=totalTime/widthInCharacters;
-nodes.forEach(node=>{
-const offset=Math.round((node.startTime-min)/timePerCharacter);
-const length=Math.ceil((node.endTime-node.startTime)/timePerCharacter);
-const bar=padRight('',offset)+padRight('',length,'=');
-
-const displayName=node.record?node.record._url:node.type;
-
-console.log(padRight(bar,widthInCharacters),`| ${displayName.slice(0,30)}`);
-});
-}
-
-
-
-
-
-
-
-compute_(trace,devtoolsLog,artifacts){
-const promises=[
-artifacts.requestTraceOfTab(trace),
-artifacts.requestNetworkRecords(devtoolsLog)];
-
-
-return Promise.all(promises).then(([traceOfTab,networkRecords])=>{
-return PageDependencyGraphArtifact.createGraph(traceOfTab,networkRecords);
-});
-}}
-
-
-module.exports=PageDependencyGraphArtifact;
-
-
-
-
-
-
-
-
-PageDependencyGraphArtifact.NetworkNodeOutput;
-
-},{"../../lib/dependency-graph/cpu-node":20,"../../lib/dependency-graph/network-node":21,"../../lib/traces/tracing-processor":40,"../../lib/web-inspector":42,"./computed-artifact":"./gather/computed/computed-artifact"}],"./gather/computed/pushed-requests":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const ComputedArtifact=require('./computed-artifact');
-
-class PushedRequests extends ComputedArtifact{
-get name(){
-return'PushedRequests';
-}
-
-
-
-
-
-
-
-compute_(devtoolsLog,artifacts){
-return artifacts.requestNetworkRecords(devtoolsLog).then(records=>{
-const pushedRecords=records.filter(r=>r._timing&&!!r._timing.pushStart);
-return pushedRecords;
-});
-}}
-
-
-module.exports=PushedRequests;
-
-},{"./computed-artifact":"./gather/computed/computed-artifact"}],"./gather/computed/screenshots":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const ComputedArtifact=require('./computed-artifact');
-
-class ScreenshotFilmstrip extends ComputedArtifact{
-get name(){
-return'Screenshots';
-}
-
-fetchScreenshot(frame){
-return frame.
-imageDataPromise().
-then(data=>'data:image/jpg;base64,'+data);
-}
-
-
-
-
-
-
-compute_(trace,artifacts){
-return artifacts.requestDevtoolsTimelineModel(trace).then(model=>{
-const filmStripFrames=model.filmStripModel().frames();
-const frameFetches=filmStripFrames.map(frame=>this.fetchScreenshot(frame));
-
-return Promise.all(frameFetches).then(images=>{
-const result=filmStripFrames.map((frame,i)=>({
-timestamp:frame.timestamp,
-datauri:images[i]}));
-
-return result;
-});
-});
-}}
-
-
-module.exports=ScreenshotFilmstrip;
-
-},{"./computed-artifact":"./gather/computed/computed-artifact"}],"./gather/computed/speedline":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const ComputedArtifact=require('./computed-artifact');
-const speedline=require('speedline');
-const LHError=require('../../lib/errors');
-
-class Speedline extends ComputedArtifact{
-get name(){
-return'Speedline';
-}
-
-
-
-
-compute_(trace,computedArtifacts){
-
-
-return computedArtifacts.requestTraceOfTab(trace).then(traceOfTab=>{
-
-
-const traceEvents=trace.traceEvents.slice();
-
-
-const navStart=traceOfTab.timestamps.navigationStart;
-return speedline(traceEvents,{
-timeOrigin:navStart,
-fastMode:true,
-include:'perceptualSpeedIndex'});
-
-}).catch(err=>{
-if(/No screenshots found in trace/.test(err.message)){
-throw new LHError(LHError.errors.NO_SCREENSHOTS);
-}
-
-throw err;
-});
-}}
-
-
-module.exports=Speedline;
-
-},{"../../lib/errors":28,"./computed-artifact":"./gather/computed/computed-artifact","speedline":144}],"./gather/computed/trace-of-tab":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-
-
-
-
-
-
-
-
-
-
-
-const ComputedArtifact=require('./computed-artifact');
-const log=require('lighthouse-logger');
-const LHError=require('../../lib/errors');
-const Sentry=require('../../lib/sentry');
-
-
-
-
-const WebInspector=require('../../lib/web-inspector');
-
-class TraceOfTab extends ComputedArtifact{
-get name(){
-return'TraceOfTab';
-}
-
-
-
-
-
-
-
-compute_(trace){
-
-
-const keyEvents=trace.traceEvents.
-filter(e=>{
-return e.cat.includes('blink.user_timing')||
-e.cat.includes('loading')||
-e.cat.includes('devtools.timeline')||
-e.name==='TracingStartedInPage';
-}).
-stableSort((event0,event1)=>event0.ts-event1.ts);
-
-
-
-const startedInPageEvt=keyEvents.find(e=>e.name==='TracingStartedInPage');
-if(!startedInPageEvt)throw new LHError(LHError.errors.NO_TRACING_STARTED);
-
-const frameEvents=keyEvents.filter(e=>e.args.frame===startedInPageEvt.args.data.page);
-
-
-const navigationStart=frameEvents.filter(e=>e.name==='navigationStart').pop();
-if(!navigationStart)throw new LHError(LHError.errors.NO_NAVSTART);
-
-
-const firstPaint=frameEvents.find(e=>e.name==='firstPaint'&&e.ts>navigationStart.ts);
-
-
-const firstContentfulPaint=frameEvents.find(
-e=>e.name==='firstContentfulPaint'&&e.ts>navigationStart.ts);
-
-
-
-let firstMeaningfulPaint=frameEvents.find(
-e=>e.name==='firstMeaningfulPaint'&&e.ts>navigationStart.ts);
-
-let fmpFellBack=false;
-
-
-
-
-
-if(!firstMeaningfulPaint){
-
-Sentry.captureMessage('No firstMeaningfulPaint found, using fallback',{level:'warning'});
-
-const fmpCand='firstMeaningfulPaintCandidate';
-fmpFellBack=true;
-log.verbose('trace-of-tab',`No firstMeaningfulPaint found, falling back to last ${fmpCand}`);
-const lastCandidate=frameEvents.filter(e=>e.name===fmpCand).pop();
-if(!lastCandidate){
-log.verbose('trace-of-tab','No `firstMeaningfulPaintCandidate` events found in trace');
-}
-firstMeaningfulPaint=lastCandidate;
-}
-
-const onLoad=frameEvents.find(e=>e.name==='loadEventEnd'&&e.ts>navigationStart.ts);
-const domContentLoaded=frameEvents.find(
-e=>e.name==='domContentLoadedEventEnd'&&e.ts>navigationStart.ts);
-
-
-
-
-const processEvents=trace.traceEvents.
-filter(e=>e.pid===startedInPageEvt.pid).
-stableSort((event0,event1)=>event0.ts-event1.ts);
-
-const mainThreadEvents=processEvents.
-filter(e=>e.tid===startedInPageEvt.tid);
-
-const traceEnd=trace.traceEvents.reduce((max,evt)=>{
-return max.ts>evt.ts?max:evt;
-});
-
-const metrics={
-navigationStart,
-firstPaint,
-firstContentfulPaint,
-firstMeaningfulPaint,
-traceEnd:{ts:traceEnd.ts+(traceEnd.dur||0)},
-onLoad,
-domContentLoaded};
-
-
-const timings={};
-const timestamps={};
-
-Object.keys(metrics).forEach(metric=>{
-timestamps[metric]=metrics[metric]&&metrics[metric].ts;
-timings[metric]=(timestamps[metric]-navigationStart.ts)/1000;
-});
-
-return{
-timings,
-timestamps,
-processEvents,
-mainThreadEvents,
-startedInPageEvt,
-navigationStartEvt:navigationStart,
-firstPaintEvt:firstPaint,
-firstContentfulPaintEvt:firstContentfulPaint,
-firstMeaningfulPaintEvt:firstMeaningfulPaint,
-onLoadEvt:onLoad,
-fmpFellBack};
-
-}}
-
-
-module.exports=TraceOfTab;
-
-},{"../../lib/errors":28,"../../lib/sentry":33,"../../lib/web-inspector":42,"./computed-artifact":"./gather/computed/computed-artifact","lighthouse-logger":137}],"./gatherers/accessibility":[function(require,module,exports){
+},{"../lib/url-shim":"url","./audit":2}],"../gather/gatherers/accessibility":[function(require,module,exports){
 
 
 
@@ -9810,13 +9238,17 @@
 
 const Gatherer=require('./gatherer');
 
-const axeLibSource="/*! aXe v2.6.1\n * Copyright (c) 2017 Deque Systems, Inc.\n *\n * Your use of this Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * This entire copyright notice must appear in every copy of this file you\n * distribute or in any file that contains substantial portions of this source\n * code.\n */\n!function a(window){function b(a){this.name=\"SupportError\",this.cause=a.cause,this.message=\"`\"+a.cause+\"` - feature unsupported in your environment.\",a.ruleId&&(this.ruleId=a.ruleId,this.message+=\" Skipping \"+this.ruleId+\" rule.\"),this.stack=(new Error).stack}function c(a){\"use strict\";var b;return a?(b=axe.utils.clone(a),b.commons=a.commons):b={},b.reporter=b.reporter||null,b.rules=b.rules||[],b.checks=b.checks||[],b.data=Object.assign({checks:{},rules:{}},b.data),b}function d(a,b,c){\"use strict\";var d,e;for(d=0,e=a.length;d<e;d++)b[c](a[d])}function e(a){this.brand=\"axe\",this.application=\"axeAPI\",this.tagExclude=[\"experimental\"],this.defaultConfig=a,this._init()}function f(a,b,c){var d=a.brand,e=a.application;return axe.constants.helpUrlBase+d+\"/\"+(c||axe.version.substring(0,axe.version.lastIndexOf(\".\")))+\"/\"+b+\"?application=\"+e}function g(a){\"use strict\";this.id=a.id,this.data=null,this.relatedNodes=[],this.result=null}function h(a){\"use strict\";return\"string\"==typeof a?new Function(\"return \"+a+\";\")():a}function i(a){a&&(this.id=a.id,this.configure(a))}function j(a,b){\"use strict\";if(!axe.utils.isHidden(b)){axe.utils.findBy(a,\"node\",b)||a.push({node:b,include:[],exclude:[]})}}function k(a,b,c){\"use strict\";a.frames=a.frames||[];var d,e,f=document.querySelectorAll(c.shift());a:for(var g=0,h=f.length;g<h;g++){e=f[g];for(var i=0,j=a.frames.length;i<j;i++)if(a.frames[i].node===e){a.frames[i][b].push(c);break a}d={node:e,include:[],exclude:[]},c&&d[b].push(c),a.frames.push(d)}}function l(a){\"use strict\";if(a&&\"object\"===(void 0===a?\"undefined\":ha(a))||a instanceof NodeList){if(a instanceof Node)return{include:[a],exclude:[]};if(a.hasOwnProperty(\"include\")||a.hasOwnProperty(\"exclude\"))return{include:a.include&&+a.include.length?a.include:[document],exclude:a.exclude||[]};if(a.length===+a.length)return{include:a,exclude:[]}}return\"string\"==typeof a?{include:[a],exclude:[]}:{include:[document],exclude:[]}}function m(a,b){\"use strict\";for(var c,d=[],e=0,f=a[b].length;e<f;e++){if(\"string\"==typeof(c=a[b][e])){d=d.concat(axe.utils.toArray(document.querySelectorAll(c)));break}!c||!c.length||c instanceof Node?d.push(c):c.length>1?k(a,b,c):d=d.concat(axe.utils.toArray(document.querySelectorAll(c[0])))}return d.filter(function(a){return a})}function n(a){\"use strict\";if(0===a.include.length){if(0===a.frames.length){var b=axe.utils.respondable.isInFrame()?\"frame\":\"page\";return new Error(\"No elements found for include in \"+b+\" Context\")}a.frames.forEach(function(a,b){if(0===a.include.length)return new Error(\"No elements found for include in Context of frame \"+b)})}}function o(a){\"use strict\";var b=this;this.frames=[],this.initiator=!a||\"boolean\"!=typeof a.initiator||a.initiator,this.page=!1,a=l(a),this.exclude=a.exclude,this.include=a.include,this.include=m(this,\"include\"),this.exclude=m(this,\"exclude\"),axe.utils.select(\"frame, iframe\",this).forEach(function(a){fa(a,b)&&j(b.frames,a)}),1===this.include.length&&this.include[0]===document&&(this.page=!0);var c=n(this);if(c instanceof Error)throw c}function p(a){\"use strict\";this.id=a.id,this.result=axe.constants.NA,this.pageLevel=a.pageLevel,this.impact=null,this.nodes=[]}function q(a,b){\"use strict\";this._audit=b,this.id=a.id,this.selector=a.selector||\"*\",this.excludeHidden=\"boolean\"!=typeof a.excludeHidden||a.excludeHidden,this.enabled=\"boolean\"!=typeof a.enabled||a.enabled,this.pageLevel=\"boolean\"==typeof a.pageLevel&&a.pageLevel,this.any=a.any||[],this.all=a.all||[],this.none=a.none||[],this.tags=a.tags||[],a.matches&&(this.matches=h(a.matches))}function r(a){\"use strict\";return axe.utils.getAllChecks(a).map(function(b){var c=a._audit.checks[b.id||b];return c&&\"function\"==typeof c.after?c:null}).filter(Boolean)}function s(a,b){\"use strict\";var c=[];return a.forEach(function(a){axe.utils.getAllChecks(a).forEach(function(a){a.id===b&&c.push(a)})}),c}function t(a){\"use strict\";return a.filter(function(a){return!0!==a.filtered})}function u(a){\"use strict\";var b=[\"any\",\"all\",\"none\"],c=a.nodes.filter(function(a){var c=0;return b.forEach(function(b){a[b]=t(a[b]),c+=a[b].length}),c>0});return a.pageLevel&&c.length&&(c=[c.reduce(function(a,c){if(a)return b.forEach(function(b){a[b].push.apply(a[b],c[b])}),a})]),c}function v(a,b){\"use strict\";if(!axe._audit)throw new Error(\"No audit configured\");var c=axe.utils.queue(),d=[];Object.keys(axe.plugins).forEach(function(a){c.defer(function(b){var c=function(a){d.push(a),b()};try{axe.plugins[a].cleanup(b,c)}catch(a){c(a)}})}),axe.utils.toArray(document.querySelectorAll(\"frame, iframe\")).forEach(function(a){c.defer(function(b,c){return axe.utils.sendCommandToFrame(a,{command:\"cleanup-plugin\"},b,c)})}),c.then(function(c){0===d.length?a(c):b(d)}).catch(b)}function w(a){\"use strict\";var b;if(!(b=axe._audit))throw new Error(\"No audit configured\");a.reporter&&(\"function\"==typeof a.reporter||ka[a.reporter])&&(b.reporter=a.reporter),a.checks&&a.checks.forEach(function(a){b.addCheck(a)}),a.rules&&a.rules.forEach(function(a){b.addRule(a)}),void 0!==a.branding?b.setBranding(a.branding):b._constructHelpUrls(),a.tagExclude&&(b.tagExclude=a.tagExclude)}function x(a,b,c){\"use strict\";var d=c,e=function(a){a instanceof Error==!1&&(a=new Error(a)),c(a)},f=a&&a.context||{};f.hasOwnProperty(\"include\")&&!f.include.length&&(f.include=[document]);var g=a&&a.options||{};switch(a.command){case\"rules\":return A(f,g,d,e);case\"cleanup-plugin\":return v(d,e);default:if(axe._audit&&axe._audit.commands&&axe._audit.commands[a.command])return axe._audit.commands[a.command](a,c)}}function y(a){\"use strict\";this._run=a.run,this._collect=a.collect,this._registry={},a.commands.forEach(function(a){axe._audit.registerCommand(a)})}function z(){\"use strict\";var a=axe._audit;if(!a)throw new Error(\"No audit configured\");a.resetRulesAndChecks()}function A(a,b,c,d){\"use strict\";try{a=new o(a)}catch(a){return d(a)}var e=axe.utils.queue(),f=axe._audit;b.performanceTimer&&axe.utils.performanceTimer.auditStart(),a.frames.length&&!1!==b.iframes&&e.defer(function(c,d){axe.utils.collectResultsFromFrames(a,b,\"rules\",null,c,d)});var g=void 0;e.defer(function(c,d){b.restoreScroll&&(g=axe.utils.getScrollState()),f.run(a,b,c,d)}),e.then(function(e){try{g&&axe.utils.setScrollState(g),b.performanceTimer&&axe.utils.performanceTimer.auditEnd();var h=axe.utils.mergeResults(e.map(function(a){return{results:a}}));a.initiator&&(h=f.after(h,b),h.forEach(axe.utils.publishMetaData),h=h.map(axe.utils.finalizeRuleResult));try{c(h)}catch(a){axe.log(a)}}catch(a){d(a)}}).catch(d)}function B(a){\"use strict\";switch(!0){case\"string\"==typeof a:case Array.isArray(a):case Node&&a instanceof Node:case NodeList&&a instanceof NodeList:return!0;case\"object\"!==(void 0===a?\"undefined\":ha(a)):return!1;case void 0!==a.include:case void 0!==a.exclude:case\"number\"==typeof a.length:return!0;default:return!1}}function C(a,b,c){\"use strict\";var d=new TypeError(\"axe.run arguments are invalid\");if(!B(a)){if(void 0!==c)throw d;c=b,b=a,a=document}if(\"object\"!==(void 0===b?\"undefined\":ha(b))){if(void 0!==c)throw d;c=b,b={}}if(\"function\"!=typeof c&&void 0!==c)throw d;return{context:a,options:b,callback:c||la}}function D(a,b){\"use strict\";[\"any\",\"all\",\"none\"].forEach(function(c){Array.isArray(a[c])&&a[c].filter(function(a){return Array.isArray(a.relatedNodes)}).forEach(function(a){a.relatedNodes=a.relatedNodes.map(function(a){var c={html:a.source};return b.elementRef&&!a.fromFrame&&(c.element=a.element),(!1!==b.selectors||a.fromFrame)&&(c.target=a.selector),b.xpath&&(c.xpath=a.xpath),c})})})}function E(a,b){return ra.reduce(function(c,d){return c[d]=(a[d]||[]).map(function(a){return b(a,d)}),c},{})}function F(a,b,c){var d=Object.assign({},b);d.nodes=(d[c]||[]).concat(),axe.constants.resultGroups.forEach(function(a){delete d[a]}),a[c].push(d)}function G(a,b,c){\"use strict\";var d=window.getComputedStyle(a,null),e=!1;return!!d&&(b.forEach(function(a){d.getPropertyValue(a.property)===a.value&&(e=!0)}),!!e||!(a.nodeName.toUpperCase()===c.toUpperCase()||!a.parentNode)&&G(a.parentNode,b,c))}function H(a,b){\"use strict\";return new Error(a+\": \"+axe.utils.getSelector(b))}function I(a,b,c,d,e,f){\"use strict\";var g=axe.utils.queue();a.frames.forEach(function(e){var f={options:b,command:c,parameter:d,context:{initiator:!1,page:a.page,include:e.include||[],exclude:e.exclude||[]}};g.defer(function(a,b){var c=e.node;axe.utils.sendCommandToFrame(c,f,function(b){if(b)return a({results:b,frameElement:c,frame:axe.utils.getSelector(c)});a(null)},b)})}),g.then(function(a){e(axe.utils.mergeResults(a,b))}).catch(f)}function J(a,b){if(b=b||300,a.length>b){var c=a.indexOf(\">\");a=a.substring(0,c+1)}return a}function K(a){var b=a.outerHTML;return b||\"function\"!=typeof XMLSerializer||(b=(new XMLSerializer).serializeToString(a)),J(b||\"\")}function L(a,b,c){this._fromFrame=!!c,this.spec=c||{},b&&b.absolutePaths&&(this._options={toRoot:!0}),this.source=void 0!==this.spec.source?this.spec.source:K(a),this._element=a}function M(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"\";return 0!==a.length&&(a.match(/[0-9]/g)||\"\").length>=a.length/2}function N(a,b){return[a.substring(0,b),a.substring(b)]}function O(a){var b=a,c=\"\",d=\"\",e=\"\",f=\"\",g=\"\",h=\"\";if(a.includes(\"#\")){var i=N(a,a.indexOf(\"#\")),j=sa(i,2);a=j[0],h=j[1]}if(a.includes(\"?\")){var k=N(a,a.indexOf(\"?\")),l=sa(k,2);a=l[0],g=l[1]}if(a.includes(\"://\")){var m=a.split(\"://\"),n=sa(m,2);c=n[0],a=n[1];var o=N(a,a.indexOf(\"/\")),p=sa(o,2);d=p[0],a=p[1]}else if(\"//\"===a.substr(0,2)){a=a.substr(2);var q=N(a,a.indexOf(\"/\")),r=sa(q,2);d=r[0],a=r[1]}if(\"www.\"===d.substr(0,4)&&(d=d.substr(4)),d&&d.includes(\":\")){var s=N(d,d.indexOf(\":\")),t=sa(s,2);d=t[0],e=t[1]}return f=a,{original:b,protocol:c,domain:d,port:e,path:f,query:g,hash:h}}function P(a){if(Array.isArray(a)){for(var b=0,c=Array(a.length);b<a.length;b++)c[b]=a[b];return c}return Array.from(a)}function Q(a){return![\"focus\",\"hover\",\"hidden\",\"visible\",\"dirty\",\"touched\",\"valid\",\"disable\",\"enable\",\"active\",\"col-\"].find(function(b){return a.includes(b)})}function R(a){return a.classList&&0!==a.classList.length?(a.parentNode&&Array.from(a.parentNode.children||\"\")||[]).reduce(function(b,c){return a===c?b:b.filter(function(a){return!c.classList.contains(a)})},Array.from(a.classList).filter(Q)):[]}function S(a,b){var c=a.parentNode&&Array.from(a.parentNode.children||\"\")||[];if(c.find(function(c){return c!==a&&axe.utils.matchesSelector(c,b)}))return\":nth-child(\"+(1+c.indexOf(a))+\")\";return\"\"}function T(a,b){void 0===ua&&(ua=axe.utils.isXHTML(document));var c=ta(ua?a.localName:a.nodeName.toLowerCase()),d=Array.from(a.classList)||[],e={nodeName:c,classList:d,isCustomElm:c.includes(\"-\"),isCommonElm:va.includes(c),distinctClassList:R(a)};return[wa.getCustomElm,wa.getElmRoleProp,wa.getUncommonElm,wa.getElmNameProp,wa.getDistinctClass,wa.getFileRefProp,wa.getCommonName].reduce(function(c,d){if(c.length===b)return c;var f=d(a,e);return f&&(f[0].match(/[a-z]/)?c.unshift(f):c.push(f)),c},[])}function U(a,b){var c,d;if(!a)return[];if(!b&&9===a.nodeType)return b=[{str:\"html\"}];if(b=b||[],a.parentNode&&a.parentNode!==a&&(b=U(a.parentNode,b)),a.previousSibling){d=1,c=a.previousSibling;do{1===c.nodeType&&c.nodeName===a.nodeName&&d++,c=c.previousSibling}while(c);1===d&&(d=null)}else if(a.nextSibling){c=a.nextSibling;do{1===c.nodeType&&c.nodeName===a.nodeName?(d=1,c=null):(d=null,c=c.previousSibling)}while(c)}if(1===a.nodeType){var e={};e.str=a.nodeName.toLowerCase();var f=a.getAttribute&&axe.utils.escapeSelector(a.getAttribute(\"id\"));f&&1===a.ownerDocument.querySelectorAll(\"#\"+f).length&&(e.id=a.getAttribute(\"id\")),d>1&&(e.count=d),b.push(e)}return b}function V(a){return a.reduce(function(a,b){return b.id?\"/\"+b.str+\"[@id='\"+b.id+\"']\":a+\"/\"+b.str+(b.count>0?\"[\"+b.count+\"]\":\"\")},\"\")}function W(a){\"use strict\";if(xa&&xa.parentNode)return void 0===xa.styleSheet?xa.appendChild(document.createTextNode(a)):xa.styleSheet.cssText+=a,xa;if(a){var b=document.head||document.getElementsByTagName(\"head\")[0];return xa=document.createElement(\"style\"),xa.type=\"text/css\",void 0===xa.styleSheet?xa.appendChild(document.createTextNode(a)):xa.styleSheet.cssText=a,b.appendChild(xa),xa}}function X(a,b,c,d){\"use strict\";var e=axe.utils.getXpath(c),f={element:c,selector:d,xpath:e};a.forEach(function(a){a.node=axe.utils.DqElement.fromFrame(a.node,b,f);var c=axe.utils.getAllChecks(a);c.length&&c.forEach(function(a){a.relatedNodes=a.relatedNodes.map(function(a){return axe.utils.DqElement.fromFrame(a,b,f)})})})}function Y(a,b){\"use strict\";for(var c,d,e=b[0].node,f=0,g=a.length;f<g;f++)if(d=a[f].node,(c=axe.utils.nodeSorter(d.element,e.element))>0||0===c&&e.selector.length<d.selector.length)return void a.splice.apply(a,[f,0].concat(b));a.push.apply(a,b)}function Z(a){\"use strict\";return a&&a.results?Array.isArray(a.results)?a.results.length?a.results:null:[a.results]:null}function $(a,b){function c(a){return a.incomplete&&a.incomplete.default?a.incomplete.default:ia.incompleteFallbackMessage()}if(!a||!a.missingData)return c(b);try{var d=b.incomplete[a.missingData[0].reason];if(!d)throw new Error;return d}catch(d){return\"string\"==typeof a.missingData?b.incomplete[a.missingData]:c(b)}}function _(a,b){\"use strict\";return function(c){var d=a[c.id]||{},e=d.messages||{},f=Object.assign({},d);delete f.messages,void 0===c.result?\"object\"===ha(e.incomplete)?f.message=function(){return $(c.data,e)}:f.message=e.incomplete:f.message=c.result===b?e.pass:e.fail,axe.utils.extendMetaData(c,f)}}function aa(a,b){\"use strict\";var c,d,e=axe._audit&&axe._audit.tagExclude?axe._audit.tagExclude:[];return b.hasOwnProperty(\"include\")||b.hasOwnProperty(\"exclude\")?(c=b.include||[],c=Array.isArray(c)?c:[c],d=b.exclude||[],d=Array.isArray(d)?d:[d],d=d.concat(e.filter(function(a){return-1===c.indexOf(a)}))):(c=Array.isArray(b)?b:[b],d=e.filter(function(a){return-1===c.indexOf(a)})),!!(c.some(function(b){return-1!==a.tags.indexOf(b)})||0===c.length&&!1!==a.enabled)&&d.every(function(b){return-1===a.tags.indexOf(b)})}function ba(a){var b=window.getComputedStyle(a),c=\"visible\"===b.getPropertyValue(\"overflow-y\"),d=\"visible\"===b.getPropertyValue(\"overflow-x\");if(!c&&a.scrollHeight>a.clientHeight||!d&&a.scrollWidth>a.clientWidth)return{elm:a,top:a.scrollTop,left:a.scrollLeft}}function ca(a,b,c){if(a===window)return a.scroll(b,c);a.scrollTop=b,a.scrollLeft=c}function da(a){return Array.from(a.children).reduce(function(a,b){var c=ba(b);return c&&a.push(c),a.concat(da(b))},[])}function ea(a){\"use strict\";return a.sort(function(a,b){return axe.utils.contains(a,b)?1:-1})[0]}function fa(a,b){\"use strict\";var c=b.include&&ea(b.include.filter(function(b){return axe.utils.contains(b,a)})),d=b.exclude&&ea(b.exclude.filter(function(b){return axe.utils.contains(b,a)}));return!!(!d&&c||d&&axe.utils.contains(d,c))}function ga(a,b,c){\"use strict\";for(var d=0,e=b.length;d<e;d++)-1===a.indexOf(b[d])&&fa(b[d],c)&&a.push(b[d])}var document=window.document,ha=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a},axe=axe||{};axe.version=\"2.6.1\",\"function\"==typeof define&&define.amd&&define([],function(){\"use strict\";return axe}),\"object\"===(\"undefined\"==typeof module?\"undefined\":ha(module))&&module.exports&&\"function\"==typeof a.toString&&(axe.source=\"(\"+a.toString()+')(typeof window === \"object\" ? window : this);',module.exports=axe),\"function\"==typeof window.getComputedStyle&&(window.axe=axe);var commons;b.prototype=Object.create(Error.prototype),b.prototype.constructor=b;var utils=axe.utils={},ia={},ha=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};e.prototype._init=function(){var a=c(this.defaultConfig);axe.commons=commons=a.commons,this.reporter=a.reporter,this.commands={},this.rules=[],this.checks={},d(a.rules,this,\"addRule\"),d(a.checks,this,\"addCheck\"),this.data={},this.data.checks=a.data&&a.data.checks||{},this.data.rules=a.data&&a.data.rules||{},this.data.failureSummaries=a.data&&a.data.failureSummaries||{},this.data.incompleteFallbackMessage=a.data&&a.data.incompleteFallbackMessage||\"\",this._constructHelpUrls()},e.prototype.registerCommand=function(a){\"use strict\";this.commands[a.id]=a.callback},e.prototype.addRule=function(a){\"use strict\";a.metadata&&(this.data.rules[a.id]=a.metadata);var b=this.getRule(a.id);b?b.configure(a):this.rules.push(new q(a,this))},e.prototype.addCheck=function(a){\"use strict\";var b=a.metadata;\"object\"===(void 0===b?\"undefined\":ha(b))&&(this.data.checks[a.id]=b,\"object\"===ha(b.messages)&&Object.keys(b.messages).filter(function(a){return b.messages.hasOwnProperty(a)&&\"string\"==typeof b.messages[a]}).forEach(function(a){0===b.messages[a].indexOf(\"function\")&&(b.messages[a]=new Function(\"return \"+b.messages[a]+\";\")())})),this.checks[a.id]?this.checks[a.id].configure(a):this.checks[a.id]=new i(a)},e.prototype.run=function(a,b,c,d){\"use strict\";this.validateOptions(b);var e=axe.utils.queue();this.rules.forEach(function(c){if(axe.utils.ruleShouldRun(c,a,b)){if(b.performanceTimer){var d=\"mark_rule_end_\"+c.id,f=\"mark_rule_start_\"+c.id;axe.utils.performanceTimer.mark(f)}e.defer(function(e,g){c.run(a,b,function(a){b.performanceTimer&&(axe.utils.performanceTimer.mark(d),axe.utils.performanceTimer.measure(\"rule_\"+c.id,f,d)),e(a)},function(a){if(b.debug)g(a);else{var d=Object.assign(new p(c),{result:axe.constants.CANTTELL,description:\"An error occured while running this rule\",message:a.message,stack:a.stack,error:a});e(d)}})})}}),e.then(function(a){c(a.filter(function(a){return!!a}))}).catch(d)},e.prototype.after=function(a,b){\"use strict\";var c=this.rules;return a.map(function(a){return axe.utils.findBy(c,\"id\",a.id).after(a,b)})},e.prototype.getRule=function(a){return this.rules.find(function(b){return b.id===a})},e.prototype.validateOptions=function(a){\"use strict\";var b=this;if(\"object\"===ha(a.runOnly)){var c=a.runOnly;if(\"rule\"===c.type&&Array.isArray(c.value))c.value.forEach(function(a){if(!b.getRule(a))throw new Error(\"unknown rule `\"+a+\"` in options.runOnly\")});else if(Array.isArray(c.value)&&c.value.length>0){var d=[].concat(c.value);if(b.rules.forEach(function(a){var b,c,e;if(d)for(c=0,e=a.tags.length;c<e;c++)-1!==(b=d.indexOf(a.tags[c]))&&d.splice(b,1)}),0!==d.length)throw new Error(\"could not find tags `\"+d.join(\"`, `\")+\"`\")}}return\"object\"===ha(a.rules)&&Object.keys(a.rules).forEach(function(a){if(!b.getRule(a))throw new Error(\"unknown rule `\"+a+\"` in options.rules\")}),a},e.prototype.setBranding=function(a){\"use strict\";var b={brand:this.brand,application:this.application};a&&a.hasOwnProperty(\"brand\")&&a.brand&&\"string\"==typeof a.brand&&(this.brand=a.brand),a&&a.hasOwnProperty(\"application\")&&a.application&&\"string\"==typeof a.application&&(this.application=a.application),this._constructHelpUrls(b)},e.prototype._constructHelpUrls=function(){var a=this,b=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,c=(axe.version.match(/^[1-9][0-9]*\\.[0-9]+/)||[\"x.y\"])[0];this.rules.forEach(function(d){a.data.rules[d.id]||(a.data.rules[d.id]={});var e=a.data.rules[d.id];(\"string\"!=typeof e.helpUrl||b&&e.helpUrl===f(b,d.id,c))&&(e.helpUrl=f(a,d.id,c))})},e.prototype.resetRulesAndChecks=function(){\"use strict\";this._init()},i.prototype.enabled=!0,i.prototype.run=function(a,b,c,d){\"use strict\";b=b||{};var e=b.hasOwnProperty(\"enabled\")?b.enabled:this.enabled,f=b.options||this.options;if(e){var h,i=new g(this),j=axe.utils.checkHelper(i,b,c,d);try{h=this.evaluate.call(j,a,f)}catch(a){return void d(a)}j.isAsync||(i.result=h,setTimeout(function(){c(i)},0))}else c(null)},i.prototype.configure=function(a){var b=this;[\"options\",\"enabled\"].filter(function(b){return a.hasOwnProperty(b)}).forEach(function(c){return b[c]=a[c]}),[\"evaluate\",\"after\"].filter(function(b){return a.hasOwnProperty(b)}).forEach(function(c){return b[c]=h(a[c])})};var ha=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};q.prototype.matches=function(){\"use strict\";return!0},q.prototype.gather=function(a){\"use strict\";var b=axe.utils.select(this.selector,a);return this.excludeHidden?b.filter(function(a){return!axe.utils.isHidden(a)}):b},q.prototype.runChecks=function(a,b,c,d,e){\"use strict\";var f=this,g=axe.utils.queue();this[a].forEach(function(a){var d=f._audit.checks[a.id||a],e=axe.utils.getCheckOption(d,f.id,c);g.defer(function(a,c){d.run(b,e,a,c)})}),g.then(function(b){b=b.filter(function(a){return a}),d({type:a,results:b})}).catch(e)},q.prototype.run=function(a,c,d,e){var f=this,g=axe.utils.queue(),h=new p(this),i=void 0;try{i=this.gather(a).filter(function(a){return f.matches(a)})}catch(a){return void e(new b({cause:a,ruleId:this.id}))}c.performanceTimer&&axe.log(\"gather (\",i.length,\"):\",axe.utils.performanceTimer.timeElapsed()+\"ms\"),i.forEach(function(a){g.defer(function(b,d){var e=axe.utils.queue();e.defer(function(b,d){f.runChecks(\"any\",a,c,b,d)}),e.defer(function(b,d){f.runChecks(\"all\",a,c,b,d)}),e.defer(function(b,d){f.runChecks(\"none\",a,c,b,d)}),e.then(function(d){if(d.length){var e=!1,f={};d.forEach(function(a){var b=a.results.filter(function(a){return a});f[a.type]=b,b.length&&(e=!0)}),e&&(f.node=new axe.utils.DqElement(a,c),h.nodes.push(f))}b()}).catch(function(a){return d(a)})})}),g.then(function(){return d(h)}).catch(function(a){return e(a)})},q.prototype.after=function(a,b){\"use strict\";var c=r(this),d=this.id;return c.forEach(function(c){var e=s(a.nodes,c.id),f=axe.utils.getCheckOption(c,d,b),g=c.after(e,f);e.forEach(function(a){-1===g.indexOf(a)&&(a.filtered=!0)})}),a.nodes=u(a),a},q.prototype.configure=function(a){\"use strict\";a.hasOwnProperty(\"selector\")&&(this.selector=a.selector),a.hasOwnProperty(\"excludeHidden\")&&(this.excludeHidden=\"boolean\"!=typeof a.excludeHidden||a.excludeHidden),a.hasOwnProperty(\"enabled\")&&(this.enabled=\"boolean\"!=typeof a.enabled||a.enabled),a.hasOwnProperty(\"pageLevel\")&&(this.pageLevel=\"boolean\"==typeof a.pageLevel&&a.pageLevel),a.hasOwnProperty(\"any\")&&(this.any=a.any),a.hasOwnProperty(\"all\")&&(this.all=a.all),a.hasOwnProperty(\"none\")&&(this.none=a.none),a.hasOwnProperty(\"tags\")&&(this.tags=a.tags),a.hasOwnProperty(\"matches\")&&(\"string\"==typeof a.matches?this.matches=new Function(\"return \"+a.matches+\";\")():this.matches=a.matches)},function(axe){var a=[{name:\"NA\",value:\"inapplicable\",priority:0,group:\"inapplicable\"},{name:\"PASS\",value:\"passed\",priority:1,group:\"passes\"},{name:\"CANTTELL\",value:\"cantTell\",priority:2,group:\"incomplete\"},{name:\"FAIL\",value:\"failed\",priority:3,group:\"violations\"}],b={helpUrlBase:\"https://dequeuniversity.com/rules/\",results:[],resultGroups:[],resultGroupMap:{},impact:Object.freeze([\"minor\",\"moderate\",\"serious\",\"critical\"])};a.forEach(function(a){var c=a.name,d=a.value,e=a.priority,f=a.group;b[c]=d,b[c+\"_PRIO\"]=e,b[c+\"_GROUP\"]=f,b.results[e]=d,b.resultGroups[e]=f,b.resultGroupMap[d]=f}),Object.freeze(b.results),Object.freeze(b.resultGroups),Object.freeze(b.resultGroupMap),Object.freeze(b),Object.defineProperty(axe,\"constants\",{value:b,enumerable:!0,configurable:!1,writable:!1})}(axe);var ha=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};axe.log=function(){\"use strict\";\"object\"===(\"undefined\"==typeof console?\"undefined\":ha(console))&&console.log&&Function.prototype.apply.call(console.log,console,arguments)};var ha=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};axe.a11yCheck=function(a,b,c){\"use strict\";\"function\"==typeof b&&(c=b,b={}),b&&\"object\"===(void 0===b?\"undefined\":ha(b))||(b={});var d=axe._audit;if(!d)throw new Error(\"No audit configured\");b.reporter=b.reporter||d.reporter||\"v2\",b.performanceTimer&&axe.utils.performanceTimer.start();var e=axe.getReporter(b.reporter);axe._runRules(a,b,function(a){var d=e(a,b,c);void 0!==d&&(b.performanceTimer&&axe.utils.performanceTimer.end(),c(d))},axe.log)},axe.cleanup=v,axe.configure=w,axe.getRules=function(a){\"use strict\";a=a||[];var b=a.length?axe._audit.rules.filter(function(b){return!!a.filter(function(a){return-1!==b.tags.indexOf(a)}).length}):axe._audit.rules,c=axe._audit.data.rules||{};return b.map(function(a){var b=c[a.id]||{};return{ruleId:a.id,description:b.description,help:b.help,helpUrl:b.helpUrl,tags:a.tags}})},axe._load=function(a){\"use strict\";axe.utils.respondable.subscribe(\"axe.ping\",function(a,b,c){c({axe:!0})}),axe.utils.respondable.subscribe(\"axe.start\",x),axe._audit=new e(a)};var axe=axe||{};axe.plugins={},y.prototype.run=function(){\"use strict\";return this._run.apply(this,arguments)},y.prototype.collect=function(){\"use strict\";return this._collect.apply(this,arguments)},y.prototype.cleanup=function(a){\"use strict\";var b=axe.utils.queue(),c=this;Object.keys(this._registry).forEach(function(a){b.defer(function(b){c._registry[a].cleanup(b)})}),b.then(function(){a()})},y.prototype.add=function(a){\"use strict\";this._registry[a.id]=a},axe.registerPlugin=function(a){\"use strict\";axe.plugins[a.id]=new y(a)};var ja,ka={};axe.getReporter=function(a){\"use strict\";return\"string\"==typeof a&&ka[a]?ka[a]:\"function\"==typeof a?a:ja},axe.addReporter=function(a,b,c){\"use strict\";ka[a]=b,c&&(ja=b)},axe.reset=z,axe._runRules=A;var ha=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a},la=function(){};axe.run=function(a,b,c){\"use strict\";if(!axe._audit)throw new Error(\"No audit configured\");var d=C(a,b,c);a=d.context,b=d.options,c=d.callback,b.reporter=b.reporter||axe._audit.reporter||\"v1\",b.performanceTimer&&axe.utils.performanceTimer.start();var e=void 0,f=la,g=la;return window.Promise&&c===la&&(e=new Promise(function(a,b){f=b,g=a})),axe._runRules(a,b,function(a){var d=function(a){try{c(null,a)}catch(a){axe.log(a)}g(a)};b.performanceTimer&&axe.utils.performanceTimer.end();try{var e=axe.getReporter(b.reporter),h=e(a,b,d);void 0!==h&&d(h)}catch(a){c(a),f(a)}},function(a){c(a),f(a)}),e},ia.failureSummary=function(a){\"use strict\";var b={};return b.none=a.none.concat(a.all),b.any=a.any,Object.keys(b).map(function(a){if(b[a].length){var c=axe._audit.data.failureSummaries[a];return c&&\"function\"==typeof c.failureMessage?c.failureMessage(b[a].map(function(a){return a.message||\"\"})):void 0}}).filter(function(a){return void 0!==a}).join(\"\\n\\n\")},ia.incompleteFallbackMessage=function(){\"use strict\";return axe._audit.data.incompleteFallbackMessage()};var ha=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a},ma=axe.constants.resultGroups;ia.processAggregate=function(a,b){var c=axe.utils.aggregateResult(a);return c.timestamp=(new Date).toISOString(),c.url=window.location.href,ma.forEach(function(a){b.resultTypes&&!b.resultTypes.includes(a)&&(c[a]||[]).forEach(function(a){Array.isArray(a.nodes)&&a.nodes.length>0&&(a.nodes=[a.nodes[0]])}),c[a]=(c[a]||[]).map(function(a){return a=Object.assign({},a),Array.isArray(a.nodes)&&a.nodes.length>0&&(a.nodes=a.nodes.map(function(a){return\"object\"===ha(a.node)&&(a.html=a.node.source,b.elementRef&&!a.node.fromFrame&&(a.element=a.node.element),(!1!==b.selectors||a.node.fromFrame)&&(a.target=a.node.selector),b.xpath&&(a.xpath=a.node.xpath)),delete a.result,delete a.node,D(a,b),a})),ma.forEach(function(b){return delete a[b]}),delete a.pageLevel,delete a.result,a})}),c},axe.addReporter(\"na\",function(a,b,c){\"use strict\";\"function\"==typeof b&&(c=b,b={});var d=ia.processAggregate(a,b);c({violations:d.violations,passes:d.passes,incomplete:d.incomplete,inapplicable:d.inapplicable,timestamp:d.timestamp,url:d.url})}),axe.addReporter(\"no-passes\",function(a,b,c){\"use strict\";\"function\"==typeof b&&(c=b,b={}),b.resultTypes=[\"violations\"];var d=ia.processAggregate(a,b);c({violations:d.violations,timestamp:d.timestamp,url:d.url})}),axe.addReporter(\"raw\",function(a,b,c){\"use strict\";\"function\"==typeof b&&(c=b,b={}),c(a)}),axe.addReporter(\"v1\",function(a,b,c){\"use strict\";\"function\"==typeof b&&(c=b,b={});var d=ia.processAggregate(a,b);d.violations.forEach(function(a){return a.nodes.forEach(function(a){a.failureSummary=ia.failureSummary(a)})}),c({violations:d.violations,passes:d.passes,incomplete:d.incomplete,inapplicable:d.inapplicable,timestamp:d.timestamp,url:d.url})}),axe.addReporter(\"v2\",function(a,b,c){\"use strict\";\"function\"==typeof b&&(c=b,b={});var d=ia.processAggregate(a,b);c({violations:d.violations,passes:d.passes,incomplete:d.incomplete,inapplicable:d.inapplicable,timestamp:d.timestamp,url:d.url})},!0),axe.utils.aggregate=function(a,b,c){b=b.slice(),c&&b.push(c);var d=b.map(function(b){return a.indexOf(b)}).sort();return a[d.pop()]};var na=axe.constants,oa=na.CANTTELL_PRIO,pa=na.FAIL_PRIO,qa=[];qa[axe.constants.PASS_PRIO]=!0,qa[axe.constants.CANTTELL_PRIO]=null,qa[axe.constants.FAIL_PRIO]=!1;var ra=[\"any\",\"all\",\"none\"];axe.utils.aggregateChecks=function(a){var b=Object.assign({},a);E(b,function(a,b){var c=qa.indexOf(a.result);a.priority=-1!==c?c:axe.constants.CANTTELL_PRIO,\"none\"===b&&(a.priority=4-a.priority)});var c={all:b.all.reduce(function(a,b){return Math.max(a,b.priority)},0),none:b.none.reduce(function(a,b){return Math.max(a,b.priority)},0),any:b.any.reduce(function(a,b){return Math.min(a,b.priority)},4)%4};b.priority=Math.max(c.all,c.none,c.any);var d=[];return ra.forEach(function(a){b[a]=b[a].filter(function(d){return d.priority===b.priority&&d.priority===c[a]}),b[a].forEach(function(a){return d.push(a.impact)})}),[oa,pa].includes(b.priority)?b.impact=axe.utils.aggregate(axe.constants.impact,d):b.impact=null,E(b,function(a){delete a.result,delete a.priority}),b.result=axe.constants.results[b.priority],delete b.priority,b},function(){axe.utils.aggregateNodeResults=function(a){var b={};if((a=a.map(function(a){if(a.any&&a.all&&a.none)return axe.utils.aggregateChecks(a);if(Array.isArray(a.node))return axe.utils.finalizeRuleResult(a);throw new TypeError(\"Invalid Result type\")}))&&a.length){var c=a.map(function(a){return a.result});b.result=axe.utils.aggregate(axe.constants.results,c,b.result)}else b.result=\"inapplicable\";axe.constants.resultGroups.forEach(function(a){return b[a]=[]}),a.forEach(function(a){var c=axe.constants.resultGroupMap[a.result];b[c].push(a)});var d=axe.constants.FAIL_GROUP;if(0===b[d].length&&(d=axe.constants.CANTTELL_GROUP),b[d].length>0){var e=b[d].map(function(a){return a.impact});b.impact=axe.utils.aggregate(axe.constants.impact,e)||null}else b.impact=null;return b}}(),axe.utils.aggregateResult=function(a){var b={};return axe.constants.resultGroups.forEach(function(a){return b[a]=[]}),a.forEach(function(a){a.error?F(b,a,axe.constants.CANTTELL_GROUP):a.result===axe.constants.NA?F(b,a,axe.constants.NA_GROUP):axe.constants.resultGroups.forEach(function(c){Array.isArray(a[c])&&a[c].length>0&&F(b,a,c)})}),b},axe.utils.areStylesSet=G,axe.utils.checkHelper=function(a,b,c,d){\"use strict\";return{isAsync:!1,async:function(){return this.isAsync=!0,function(b){b instanceof Error==!1?(a.result=b,c(a)):d(b)}},data:function(b){a.data=b},relatedNodes:function(c){c=c instanceof Node?[c]:axe.utils.toArray(c),a.relatedNodes=c.map(function(a){return new axe.utils.DqElement(a,b)})}}};var ha=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};axe.utils.clone=function(a){\"use strict\";var b,c,d=a\n;if(null!==a&&\"object\"===(void 0===a?\"undefined\":ha(a)))if(Array.isArray(a))for(d=[],b=0,c=a.length;b<c;b++)d[b]=axe.utils.clone(a[b]);else{d={};for(b in a)d[b]=axe.utils.clone(a[b])}return d},axe.utils.sendCommandToFrame=function(a,b,c,d){\"use strict\";var e=a.contentWindow;if(!e)return axe.log(\"Frame does not have a content window\",a),void c(null);var f=setTimeout(function(){f=setTimeout(function(){var e=H(\"No response from frame\",a);b.debug?d(e):(axe.log(e),c(null))},0)},500);axe.utils.respondable(e,\"axe.ping\",null,void 0,function(){clearTimeout(f),f=setTimeout(function(){d(H(\"Axe in frame timed out\",a))},3e4),axe.utils.respondable(e,\"axe.start\",b,void 0,function(a){clearTimeout(f),a instanceof Error==!1?c(a):d(a)})})},axe.utils.collectResultsFromFrames=I,axe.utils.contains=function(a,b){\"use strict\";return\"function\"==typeof a.contains?a.contains(b):!!(16&a.compareDocumentPosition(b))},L.prototype={get selector(){return this.spec.selector||[axe.utils.getSelector(this.element,this._options)]},get xpath(){return this.spec.xpath||[axe.utils.getXpath(this.element)]},get element(){return this._element},get fromFrame(){return this._fromFrame},toJSON:function(){\"use strict\";return{selector:this.selector,source:this.source,xpath:this.xpath}}},L.fromFrame=function(a,b,c){return a.selector.unshift(c.selector),a.xpath.unshift(c.xpath),new axe.utils.DqElement(c.element,b,a)},axe.utils.DqElement=L,axe.utils.matchesSelector=function(){\"use strict\";function a(a){var b,c,d=a.Element.prototype,e=[\"matches\",\"matchesSelector\",\"mozMatchesSelector\",\"webkitMatchesSelector\",\"msMatchesSelector\"],f=e.length;for(b=0;b<f;b++)if(c=e[b],d[c])return c}var b;return function(c,d){return b&&c[b]||(b=a(c.ownerDocument.defaultView)),c[b](d)}}(),axe.utils.escapeSelector=function(a){\"use strict\";for(var b,c=String(a),d=c.length,e=-1,f=\"\",g=c.charCodeAt(0);++e<d;){if(0==(b=c.charCodeAt(e)))throw new Error(\"INVALID_CHARACTER_ERR\");b>=1&&b<=31||b>=127&&b<=159||0==e&&b>=48&&b<=57||1==e&&b>=48&&b<=57&&45==g?f+=\"\\\\\"+b.toString(16)+\" \":f+=(1!=e||45!=b||45!=g)&&(b>=128||45==b||95==b||b>=48&&b<=57||b>=65&&b<=90||b>=97&&b<=122)?c.charAt(e):\"\\\\\"+c.charAt(e)}return f},axe.utils.extendMetaData=function(a,b){Object.assign(a,b),Object.keys(b).filter(function(a){return\"function\"==typeof b[a]}).forEach(function(c){a[c]=null;try{a[c]=b[c](a)}catch(a){}})},axe.utils.finalizeRuleResult=function(a){return Object.assign(a,axe.utils.aggregateNodeResults(a.nodes)),delete a.nodes,a};var ha=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};axe.utils.findBy=function(a,b,c){if(Array.isArray(a))return a.find(function(a){return\"object\"===(void 0===a?\"undefined\":ha(a))&&a[b]===c})},axe.utils.getAllChecks=function(a){\"use strict\";return[].concat(a.any||[]).concat(a.all||[]).concat(a.none||[])},axe.utils.getCheckOption=function(a,b,c){var d=((c.rules&&c.rules[b]||{}).checks||{})[a.id],e=(c.checks||{})[a.id],f=a.enabled,g=a.options;return e&&(e.hasOwnProperty(\"enabled\")&&(f=e.enabled),e.hasOwnProperty(\"options\")&&(g=e.options)),d&&(d.hasOwnProperty(\"enabled\")&&(f=d.enabled),d.hasOwnProperty(\"options\")&&(g=d.options)),{enabled:f,options:g,absolutePaths:c.absolutePaths}};var sa=function(){function a(a,b){var c=[],d=!0,e=!1,f=void 0;try{for(var g,h=a[Symbol.iterator]();!(d=(g=h.next()).done)&&(c.push(g.value),!b||c.length!==b);d=!0);}catch(a){e=!0,f=a}finally{try{!d&&h.return&&h.return()}finally{if(e)throw f}}return c}return function(b,c){if(Array.isArray(b))return b;if(Symbol.iterator in Object(b))return a(b,c);throw new TypeError(\"Invalid attempt to destructure non-iterable instance\")}}();axe.utils.getFriendlyUriEnd=function(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"\",b=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!(a.length<=1||\"data:\"===a.substr(0,5)||\"javascript:\"===a.substr(0,11)||a.includes(\"?\"))){var c=b.currentDomain,d=b.maxLength,e=void 0===d?25:d,f=O(a),g=f.path,h=f.domain,i=f.hash,j=g.substr(g.substr(0,g.length-2).lastIndexOf(\"/\")+1);if(i)return j&&(j+i).length<=e?j+i:j.length<2&&i.length>2&&i.length<=e?i:void 0;if(h&&h.length<e&&g.length<=1)return h+g;if(g===\"/\"+j&&h&&c&&h!==c&&(h+g).length<=e)return h+g;var k=j.lastIndexOf(\".\");return(-1===k||k>1)&&(-1!==k||j.length>2)&&j.length<=e&&!j.match(/index(\\.[a-zA-Z]{2-4})?/)&&!M(j)?j:void 0}};var ta=axe.utils.escapeSelector,ua=void 0,va=[\"div\",\"span\",\"p\",\"b\",\"i\",\"u\",\"strong\",\"em\",\"h2\",\"h3\"],wa={getElmId:function(a){if(a.getAttribute(\"id\")){var b=\"#\"+ta(a.getAttribute(\"id\")||\"\");return b.match(/player_uid_/)||1!==document.querySelectorAll(b).length?void 0:b}},getCustomElm:function(a,b){var c=b.isCustomElm,d=b.nodeName;if(c)return d},getElmRoleProp:function(a){if(a.hasAttribute(\"role\"))return'[role=\"'+ta(a.getAttribute(\"role\"))+'\"]'},getUncommonElm:function(a,b){var c=b.isCommonElm,d=b.isCustomElm,e=b.nodeName;if(!c&&!d)return\"input\"===e&&a.hasAttribute(\"type\")&&(e+='[type=\"'+a.type+'\"]'),e},getElmNameProp:function(a){if(!a.hasAttribute(\"id\")&&a.name)return'[name=\"'+ta(a.name)+'\"]'},getDistinctClass:function(a,b){var c=b.distinctClassList;if(c.length>0&&c.length<3)return\".\"+c.map(ta).join(\".\")},getFileRefProp:function(a){var b=void 0;if(a.hasAttribute(\"href\"))b=\"href\";else{if(!a.hasAttribute(\"src\"))return;b=\"src\"}var c=axe.utils.getFriendlyUriEnd(a.getAttribute(b));if(c)return\"[\"+b+'$=\"'+encodeURI(c)+'\"]'},getCommonName:function(a,b){var c=b.nodeName;if(b.isCommonElm)return c}};axe.utils.getSelector=function a(b){var c=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!b)return\"\";var d=void 0,e=void 0,f=c.isUnique,g=void 0!==f&&f,h=wa.getElmId(b),i=c.featureCount,j=void 0===i?2:i,k=c.minDepth,l=void 0===k?1:k,m=c.toRoot,n=void 0!==m&&m,o=c.childSelectors,p=void 0===o?[]:o;h?(d=h,g=!0):(d=T(b,j).join(\"\"),d+=S(b,d),g=c.isUnique||1===document.querySelectorAll(d).length,g||b!==document.documentElement||(d+=\":root\"),e=0!==l||!g);var q=[d].concat(P(p));return b.parentElement&&(n||e)?a(b.parentNode,{toRoot:n,isUnique:g,childSelectors:q,featureCount:1,minDepth:l-1}):q.join(\" > \")},axe.utils.getXpath=function(a){return V(U(a))};var xa;axe.utils.injectStyle=W,axe.utils.isHidden=function(a,b){\"use strict\";if(9===a.nodeType)return!1;var c=window.getComputedStyle(a,null);return!c||!a.parentNode||\"none\"===c.getPropertyValue(\"display\")||!b&&\"hidden\"===c.getPropertyValue(\"visibility\")||\"true\"===a.getAttribute(\"aria-hidden\")||axe.utils.isHidden(a.parentNode,!0)},axe.utils.isXHTML=function(a){\"use strict\";return!!a.createElement&&\"A\"===a.createElement(\"A\").localName},axe.utils.mergeResults=function(a,b){\"use strict\";var c=[];return a.forEach(function(a){var d=Z(a);d&&d.length&&d.forEach(function(d){d.nodes&&a.frame&&X(d.nodes,b,a.frameElement,a.frame);var e=axe.utils.findBy(c,\"id\",d.id);e?d.nodes.length&&Y(e.nodes,d.nodes):c.push(d)})}),c},axe.utils.nodeSorter=function(a,b){\"use strict\";return a===b?0:4&a.compareDocumentPosition(b)?-1:1},utils.performanceTimer=function(){\"use strict\";function a(){if(window.performance&&window.performance)return window.performance.now()}var b=null,c=a();return{start:function(){this.mark(\"mark_axe_start\")},end:function(){this.mark(\"mark_axe_end\"),this.measure(\"axe\",\"mark_axe_start\",\"mark_axe_end\"),this.logMeasures(\"axe\")},auditStart:function(){this.mark(\"mark_audit_start\")},auditEnd:function(){this.mark(\"mark_audit_end\"),this.measure(\"audit_start_to_end\",\"mark_audit_start\",\"mark_audit_end\"),this.logMeasures()},mark:function(a){window.performance&&void 0!==window.performance.mark&&window.performance.mark(a)},measure:function(a,b,c){window.performance&&void 0!==window.performance.measure&&window.performance.measure(a,b,c)},logMeasures:function(a){function b(a){axe.log(\"Measure \"+a.name+\" took \"+a.duration+\"ms\")}if(window.performance&&void 0!==window.performance.getEntriesByType)for(var c=window.performance.getEntriesByType(\"measure\"),d=0;d<c.length;++d){var e=c[d];if(e.name===a)return void b(e);b(e)}},timeElapsed:function(){return a()-c},reset:function(){b||(b=a()),c=a()}}}(),\"function\"!=typeof Object.assign&&function(){Object.assign=function(a){\"use strict\";if(void 0===a||null===a)throw new TypeError(\"Cannot convert undefined or null to object\");for(var b=Object(a),c=1;c<arguments.length;c++){var d=arguments[c];if(void 0!==d&&null!==d)for(var e in d)d.hasOwnProperty(e)&&(b[e]=d[e])}return b}}(),Array.prototype.find||Object.defineProperty(Array.prototype,\"find\",{value:function(a){if(null===this)throw new TypeError(\"Array.prototype.find called on null or undefined\");if(\"function\"!=typeof a)throw new TypeError(\"predicate must be a function\");for(var b,c=Object(this),d=c.length>>>0,e=arguments[1],f=0;f<d;f++)if(b=c[f],a.call(e,b,f,c))return b}}),axe.utils.pollyfillElementsFromPoint=function(){if(document.elementsFromPoint)return document.elementsFromPoint;if(document.msElementsFromPoint)return document.msElementsFromPoint;var a=function(){var a=document.createElement(\"x\");return a.style.cssText=\"pointer-events:auto\",\"auto\"===a.style.pointerEvents}(),b=a?\"pointer-events\":\"visibility\",c=a?\"none\":\"hidden\",d=document.createElement(\"style\");return d.innerHTML=a?\"* { pointer-events: all }\":\"* { visibility: visible }\",function(a,e){var f,g,h,i=[],j=[];for(document.head.appendChild(d);(f=document.elementFromPoint(a,e))&&-1===i.indexOf(f);)i.push(f),j.push({value:f.style.getPropertyValue(b),priority:f.style.getPropertyPriority(b)}),f.style.setProperty(b,c,\"important\");for(g=j.length;h=j[--g];)i[g].style.setProperty(b,h.value?h.value:\"\",h.priority);return document.head.removeChild(d),i}},\"function\"==typeof window.addEventListener&&(document.elementsFromPoint=axe.utils.pollyfillElementsFromPoint()),Array.prototype.includes||Object.defineProperty(Array.prototype,\"includes\",{value:function(a){\"use strict\";var b=Object(this),c=parseInt(b.length,10)||0;if(0===c)return!1;var d,e=parseInt(arguments[1],10)||0;e>=0?d=e:(d=c+e)<0&&(d=0);for(var f;d<c;){if(f=b[d],a===f||a!==a&&f!==f)return!0;d++}return!1}}),Array.prototype.some||Object.defineProperty(Array.prototype,\"some\",{value:function(a){\"use strict\";if(null==this)throw new TypeError(\"Array.prototype.some called on null or undefined\");if(\"function\"!=typeof a)throw new TypeError;for(var b=Object(this),c=b.length>>>0,d=arguments.length>=2?arguments[1]:void 0,e=0;e<c;e++)if(e in b&&a.call(d,b[e],e,b))return!0;return!1}}),Array.from||Object.defineProperty(Array,\"from\",{value:function(){var a=Object.prototype.toString,b=function(b){return\"function\"==typeof b||\"[object Function]\"===a.call(b)},c=function(a){var b=Number(a);return isNaN(b)?0:0!==b&&isFinite(b)?(b>0?1:-1)*Math.floor(Math.abs(b)):b},d=Math.pow(2,53)-1,e=function(a){var b=c(a);return Math.min(Math.max(b,0),d)};return function(a){var c=this,d=Object(a);if(null==a)throw new TypeError(\"Array.from requires an array-like object - not null or undefined\");var f,g=arguments.length>1?arguments[1]:void 0;if(void 0!==g){if(!b(g))throw new TypeError(\"Array.from: when provided, the second argument must be a function\");arguments.length>2&&(f=arguments[2])}for(var h,i=e(d.length),j=b(c)?Object(new c(i)):new Array(i),k=0;k<i;)h=d[k],j[k]=g?void 0===f?g(h,k):g.call(f,h,k):h,k+=1;return j.length=i,j}}()}),String.prototype.includes||(String.prototype.includes=function(a,b){return\"number\"!=typeof b&&(b=0),!(b+a.length>this.length)&&-1!==this.indexOf(a,b)});var ha=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};axe.utils.publishMetaData=function(a){\"use strict\";var b=axe._audit.data.checks||{},c=axe._audit.data.rules||{},d=axe.utils.findBy(axe._audit.rules,\"id\",a.id)||{};a.tags=axe.utils.clone(d.tags||[]);var e=_(b,!0),f=_(b,!1);a.nodes.forEach(function(a){a.any.forEach(e),a.all.forEach(e),a.none.forEach(f)}),axe.utils.extendMetaData(a,axe.utils.clone(c[a.id]||{}))};var ha=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};!function(){\"use strict\";function a(){}function b(a){if(\"function\"!=typeof a)throw new TypeError(\"Queue methods require functions as arguments\")}function c(){function c(b){return function(c){g[b]=c,(i-=1)||j===a||(k=!0,j(g))}}function d(b){return j=a,m(b),g}function e(){for(var a=g.length;h<a;h++){var b=g[h];try{b.call(null,c(h),d)}catch(a){d(a)}}}var f,g=[],h=0,i=0,j=a,k=!1,l=function(a){f=a,setTimeout(function(){void 0!==f&&null!==f&&axe.log(\"Uncaught error (of queue)\",f)},1)},m=l,n={defer:function(a){if(\"object\"===(void 0===a?\"undefined\":ha(a))&&a.then&&a.catch){var c=a;a=function(a,b){c.then(a).catch(b)}}if(b(a),void 0===f){if(k)throw new Error(\"Queue already completed\");return g.push(a),++i,e(),n}},then:function(c){if(b(c),j!==a)throw new Error(\"queue `then` already set\");return f||(j=c,i||(k=!0,j(g))),n},catch:function(a){if(b(a),m!==l)throw new Error(\"queue `catch` already set\");return f?(a(f),f=null):m=a,n},abort:d};return n}axe.utils.queue=c}();var ha=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};!function(a){\"use strict\";function b(){var a=\"axe\",b=\"\";return void 0!==axe&&axe._audit&&!axe._audit.application&&(a=axe._audit.application),void 0!==axe&&(b=axe.version),a+\".\"+b}function c(a){if(\"object\"===(void 0===a?\"undefined\":ha(a))&&\"string\"==typeof a.uuid&&!0===a._respondable){var c=b();return a._source===c||\"axe.x.y.z\"===a._source||\"axe.x.y.z\"===c}return!1}function d(a,c,d,e,f,g){var h;d instanceof Error&&(h={name:d.name,message:d.message,stack:d.stack},d=void 0);var i={uuid:e,topic:c,message:d,error:h,_respondable:!0,_source:b(),_keepalive:f};\"function\"==typeof g&&(j[e]=g),a.postMessage(JSON.stringify(i),\"*\")}function e(a,b,c,e,f){d(a,b,c,ya.v1(),e,f)}function f(a,b,c){return function(e,f,g){d(a,b,e,c,f,g)}}function g(a,b,c){var d=b.topic,e=k[d];if(e){var g=f(a,null,b.uuid);e(b.message,c,g)}}function h(a){var b=a.message||\"Unknown error occurred\",c=l.includes(a.name)?a.name:\"Error\",d=window[c]||Error;return a.stack&&(b+=\"\\n\"+a.stack.replace(a.message,\"\")),new d(b)}function i(a){var b;if(\"string\"==typeof a){try{b=JSON.parse(a)}catch(a){}if(c(b))return\"object\"===ha(b.error)?b.error=h(b.error):b.error=void 0,b}}var j={},k={},l=Object.freeze([\"EvalError\",\"RangeError\",\"ReferenceError\",\"SyntaxError\",\"TypeError\",\"URIError\"]);e.subscribe=function(a,b){k[a]=b},e.isInFrame=function(a){return a=a||window,!!a.frameElement},\"function\"==typeof window.addEventListener&&window.addEventListener(\"message\",function(a){var b=i(a.data);if(b){var c=b.uuid,e=b._keepalive,h=j[c];if(h){h(b.error||b.message,e,f(a.source,b.topic,c)),e||delete j[c]}if(!b.error)try{g(a.source,b,e)}catch(e){d(a.source,b.topic,e,c,!1)}}},!1),a.respondable=e}(utils),axe.utils.ruleShouldRun=function(a,b,c){\"use strict\";var d=c.runOnly||{},e=(c.rules||{})[a.id];return!(a.pageLevel&&!b.page)&&(\"rule\"===d.type?-1!==d.values.indexOf(a.id):e&&\"boolean\"==typeof e.enabled?e.enabled:\"tag\"===d.type&&d.values?aa(a,d.values):aa(a,[]))},axe.utils.getScrollState=function(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:window,b=a.document.documentElement;return[void 0!==a.pageXOffset?{elm:a,top:a.pageYOffset,left:a.pageXOffset}:{elm:b,top:b.scrollTop,left:b.scrollLeft}].concat(da(document.body))},axe.utils.setScrollState=function(a){a.forEach(function(a){return ca(a.elm,a.top,a.left)})},axe.utils.select=function(a,b){\"use strict\";for(var c,d=[],e=0,f=b.include.length;e<f;e++)c=b.include[e],c.nodeType===c.ELEMENT_NODE&&axe.utils.matchesSelector(c,a)&&ga(d,[c],b),ga(d,c.querySelectorAll(a),b);return d.sort(axe.utils.nodeSorter)},axe.utils.toArray=function(a){\"use strict\";return Array.prototype.slice.call(a)};var ya;!function(a){function b(a,b,c){var d=b&&c||0,e=0;for(b=b||[],a.toLowerCase().replace(/[0-9a-f]{2}/g,function(a){e<16&&(b[d+e++]=l[a])});e<16;)b[d+e++]=0;return b}function c(a,b){var c=b||0,d=k;return d[a[c++]]+d[a[c++]]+d[a[c++]]+d[a[c++]]+\"-\"+d[a[c++]]+d[a[c++]]+\"-\"+d[a[c++]]+d[a[c++]]+\"-\"+d[a[c++]]+d[a[c++]]+\"-\"+d[a[c++]]+d[a[c++]]+d[a[c++]]+d[a[c++]]+d[a[c++]]+d[a[c++]]}function d(a,b,d){var e=b&&d||0,f=b||[];a=a||{};var g=null!=a.clockseq?a.clockseq:p,h=null!=a.msecs?a.msecs:(new Date).getTime(),i=null!=a.nsecs?a.nsecs:r+1,j=h-q+(i-r)/1e4;if(j<0&&null==a.clockseq&&(g=g+1&16383),(j<0||h>q)&&null==a.nsecs&&(i=0),i>=1e4)throw new Error(\"uuid.v1(): Can't create more than 10M uuids/sec\");q=h,r=i,p=g,h+=122192928e5;var k=(1e4*(268435455&h)+i)%4294967296;f[e++]=k>>>24&255,f[e++]=k>>>16&255,f[e++]=k>>>8&255,f[e++]=255&k;var l=h/4294967296*1e4&268435455;f[e++]=l>>>8&255,f[e++]=255&l,f[e++]=l>>>24&15|16,f[e++]=l>>>16&255,f[e++]=g>>>8|128,f[e++]=255&g;for(var m=a.node||o,n=0;n<6;n++)f[e+n]=m[n];return b||c(f)}function e(a,b,d){var e=b&&d||0;\"string\"==typeof a&&(b=\"binary\"==a?new j(16):null,a=null),a=a||{};var g=a.random||(a.rng||f)();if(g[6]=15&g[6]|64,g[8]=63&g[8]|128,b)for(var h=0;h<16;h++)b[e+h]=g[h];return b||c(g)}var f,g=a.crypto||a.msCrypto;if(!f&&g&&g.getRandomValues){var h=new Uint8Array(16);f=function(){return g.getRandomValues(h),h}}if(!f){var i=new Array(16);f=function(){for(var a,b=0;b<16;b++)0==(3&b)&&(a=4294967296*Math.random()),i[b]=a>>>((3&b)<<3)&255;return i}}for(var j=\"function\"==typeof a.Buffer?a.Buffer:Array,k=[],l={},m=0;m<256;m++)k[m]=(m+256).toString(16).substr(1),l[k[m]]=m;var n=f(),o=[1|n[0],n[1],n[2],n[3],n[4],n[5]],p=16383&(n[6]<<8|n[7]),q=0,r=0;ya=e,ya.v1=d,ya.v4=e,ya.parse=b,ya.unparse=c,ya.BufferClass=j}(window),axe._load({data:{rules:{accesskeys:{description:\"Ensures every accesskey attribute value is unique\",help:\"accesskey attribute value must be unique\"},\"area-alt\":{description:\"Ensures <area> elements of image maps have alternate text\",help:\"Active <area> elements must have alternate text\"},\"aria-allowed-attr\":{description:\"Ensures ARIA attributes are allowed for an element's role\",help:\"Elements must only use allowed ARIA attributes\"},\"aria-hidden-body\":{description:\"Ensures aria-hidden='true' is not present on the document body.\",help:\"aria-hidden='true' must not be present on the document body\"},\"aria-required-attr\":{description:\"Ensures elements with ARIA roles have all required ARIA attributes\",help:\"Required ARIA attributes must be provided\"},\"aria-required-children\":{description:\"Ensures elements with an ARIA role that require child roles contain them\",help:\"Certain ARIA roles must contain particular children\"},\"aria-required-parent\":{description:\"Ensures elements with an ARIA role that require parent roles are contained by them\",help:\"Certain ARIA roles must be contained by particular parents\"},\"aria-roles\":{description:\"Ensures all elements with a role attribute use a valid value\",help:\"ARIA roles used must conform to valid values\"},\"aria-valid-attr-value\":{description:\"Ensures all ARIA attributes have valid values\",help:\"ARIA attributes must conform to valid values\"},\"aria-valid-attr\":{description:\"Ensures attributes that begin with aria- are valid ARIA attributes\",help:\"ARIA attributes must conform to valid names\"},\"audio-caption\":{description:\"Ensures <audio> elements have captions\",help:\"<audio> elements must have a captions track\"},blink:{description:\"Ensures <blink> elements are not used\",help:\"<blink> elements are deprecated and must not be used\"},\"button-name\":{description:\"Ensures buttons have discernible text\",help:\"Buttons must have discernible text\"},bypass:{description:\"Ensures each page has at least one mechanism for a user to bypass navigation and jump straight to the content\",help:\"Page must have means to bypass repeated blocks\"},checkboxgroup:{description:'Ensures related <input type=\"checkbox\"> elements have a group and that that group designation is consistent',help:\"Checkbox inputs with the same name attribute value must be part of a group\"},\"color-contrast\":{description:\"Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds\",help:\"Elements must have sufficient color contrast\"},\"definition-list\":{description:\"Ensures <dl> elements are structured correctly\",help:\"<dl> elements must only directly contain properly-ordered <dt> and <dd> groups, <script> or <template> elements\"},dlitem:{description:\"Ensures <dt> and <dd> elements are contained by a <dl>\",help:\"<dt> and <dd> elements must be contained by a <dl>\"},\"document-title\":{description:\"Ensures each HTML document contains a non-empty <title> element\",help:\"Documents must have <title> element to aid in navigation\"},\"duplicate-id\":{description:\"Ensures every id attribute value is unique\",help:\"id attribute value must be unique\"},\"empty-heading\":{description:\"Ensures headings have discernible text\",help:\"Headings must not be empty\"},\"frame-title-unique\":{description:\"Ensures <iframe> and <frame> elements contain a unique title attribute\",help:\"Frames must have a unique title attribute\"},\"frame-title\":{description:\"Ensures <iframe> and <frame> elements contain a non-empty title attribute\",help:\"Frames must have title attribute\"},\"heading-order\":{description:\"Ensures the order of headings is semantically correct\",help:\"Heading levels should only increase by one\"},\"hidden-content\":{description:\"Informs users about hidden content.\",help:\"Hidden content on the page cannot be analyzed\"},\"href-no-hash\":{description:\"Ensures that href values are valid link references to promote only using anchors as links\",help:\"Anchors must only be used as links with valid URLs or URL fragments\"},\"html-has-lang\":{description:\"Ensures every HTML document has a lang attribute\",help:\"<html> element must have a lang attribute\"},\"html-lang-valid\":{description:\"Ensures the lang attribute of the <html> element has a valid value\",help:\"<html> element must have a valid value for the lang attribute\"},\"image-alt\":{description:\"Ensures <img> elements have alternate text or a role of none or presentation\",help:\"Images must have alternate text\"},\"image-redundant-alt\":{description:\"Ensure button and link text is not repeated as image alternative\",help:\"Text of buttons and links should not be repeated in the image alternative\"},\"input-image-alt\":{description:'Ensures <input type=\"image\"> elements have alternate text',help:\"Image buttons must have alternate text\"},\"label-title-only\":{description:\"Ensures that every form element is not solely labeled using the title or aria-describedby attributes\",help:\"Form elements should have a visible label\"},label:{description:\"Ensures every form element has a label\",help:\"Form elements must have labels\"},\"landmark-main-is-top-level\":{description:\"The main landmark should not be contained in another landmark\",help:\"Main landmark is not at top level\"},\"landmark-one-main\":{description:\"Ensures a navigation point to the primary content of the page. If the page contains iframes, each iframe should contain either no main landmarks or just one.\",help:\"Page must contain one main landmark.\"},\"layout-table\":{description:\"Ensures presentational <table> elements do not use <th>, <caption> elements or the summary attribute\",help:\"Layout tables must not use data table elements\"},\"link-in-text-block\":{description:\"Links can be distinguished without relying on color\",help:\"Links must be distinguished from surrounding text in a way that does not rely on color\"},\"link-name\":{description:\"Ensures links have discernible text\",help:\"Links must have discernible text\"},list:{description:\"Ensures that lists are structured correctly\",help:\"<ul> and <ol> must only directly contain <li>, <script> or <template> elements\"},listitem:{description:\"Ensures <li> elements are used semantically\",help:\"<li> elements must be contained in a <ul> or <ol>\"},marquee:{description:\"Ensures <marquee> elements are not used\",help:\"<marquee> elements are deprecated and must not be used\"},\"meta-refresh\":{description:'Ensures <meta http-equiv=\"refresh\"> is not used',help:\"Timed refresh must not exist\"},\"meta-viewport-large\":{description:'Ensures <meta name=\"viewport\"> can scale a significant amount',help:\"Users should be able to zoom and scale the text up to 500%\"},\"meta-viewport\":{description:'Ensures <meta name=\"viewport\"> does not disable text scaling and zooming',help:\"Zooming and scaling must not be disabled\"},\"object-alt\":{description:\"Ensures <object> elements have alternate text\",help:\"<object> elements must have alternate text\"},\"p-as-heading\":{description:\"Ensure p elements are not used to style headings\",help:\"Bold, italic text and font-size are not used to style p elements as a heading\"},radiogroup:{description:'Ensures related <input type=\"radio\"> elements have a group and that the group designation is consistent',help:\"Radio inputs with the same name attribute value must be part of a group\"},region:{description:\"Ensures all content is contained within a landmark region\",help:\"Content should be contained in a landmark region\"},\"scope-attr-valid\":{description:\"Ensures the scope attribute is used correctly on tables\",help:\"scope attribute should be used correctly\"},\"server-side-image-map\":{description:\"Ensures that server-side image maps are not used\",help:\"Server-side image maps must not be used\"},\"skip-link\":{description:\"Ensures the first link on the page is a skip link\",help:\"The page should have a skip link as its first link\"},tabindex:{description:\"Ensures tabindex attribute values are not greater than 0\",help:\"Elements should not have tabindex greater than zero\"},\"table-duplicate-name\":{description:\"Ensure that tables do not have the same summary and caption\",help:\"The <caption> element should not contain the same text as the summary attribute\"},\"table-fake-caption\":{description:\"Ensure that tables with a caption use the <caption> element.\",help:\"Data or header cells should not be used to give caption to a data table.\"},\"td-has-header\":{description:\"Ensure that each non-empty data cell in a large table has one or more table headers\",help:\"All non-empty td element in table larger than 3 by 3 must have an associated table header\"},\"td-headers-attr\":{description:\"Ensure that each cell in a table using the headers refers to another cell in that table\",help:\"All cells in a table element that use the headers attribute must only refer to other cells of that same table\"},\"th-has-data-cells\":{description:\"Ensure that each table header in a data table refers to data cells\",help:\"All th elements and elements with role=columnheader/rowheader must have data cells they describe\"},\"valid-lang\":{description:\"Ensures lang attributes have valid values\",help:\"lang attribute must have a valid value\"},\"video-caption\":{description:\"Ensures <video> elements have captions\",help:\"<video> elements must have captions\"},\"video-description\":{description:\"Ensures <video> elements have audio descriptions\",help:\"<video> elements must have an audio description track\"}},checks:{accesskeys:{impact:\"serious\",messages:{pass:function(a){return\"Accesskey attribute value is unique\"},fail:function(a){return\"Document has multiple elements with the same accesskey\"}}},\"non-empty-alt\":{impact:\"critical\",messages:{pass:function(a){return\"Element has a non-empty alt attribute\"},fail:function(a){return\"Element has no alt attribute or the alt attribute is empty\"}}},\"non-empty-title\":{impact:\"serious\",messages:{pass:function(a){return\"Element has a title attribute\"},fail:function(a){return\"Element has no title attribute or the title attribute is empty\"}}},\"aria-label\":{impact:\"serious\",messages:{pass:function(a){return\"aria-label attribute exists and is not empty\"},fail:function(a){return\"aria-label attribute does not exist or is empty\"}}},\"aria-labelledby\":{impact:\"serious\",messages:{pass:function(a){return\"aria-labelledby attribute exists and references elements that are visible to screen readers\"},fail:function(a){return\"aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty or not visible\"}}},\"aria-allowed-attr\":{impact:\"critical\",messages:{pass:function(a){return\"ARIA attributes are used correctly for the defined role\"},fail:function(a){var b=\"ARIA attribute\"+(a.data&&a.data.length>1?\"s are\":\" is\")+\" not allowed:\",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" \"+d;return b}}},\"aria-hidden-body\":{impact:\"critical\",messages:{pass:function(a){return\"No aria-hidden attribute is present on document body\"},fail:function(a){return\"aria-hidden=true should not be present on the document body\"}}},\"aria-required-attr\":{impact:\"critical\",messages:{pass:function(a){return\"All required ARIA attributes are present\"},fail:function(a){var b=\"Required ARIA attribute\"+(a.data&&a.data.length>1?\"s\":\"\")+\" not present:\",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" \"+d;return b}}},\"aria-required-children\":{impact:\"critical\",messages:{pass:function(a){return\"Required ARIA children are present\"},fail:function(a){var b=\"Required ARIA \"+(a.data&&a.data.length>1?\"children\":\"child\")+\" role not present:\",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" \"+d;return b}}},\"aria-required-parent\":{impact:\"critical\",messages:{pass:function(a){return\"Required ARIA parent role present\"},fail:function(a){var b=\"Required ARIA parent\"+(a.data&&a.data.length>1?\"s\":\"\")+\" role not present:\",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" \"+d;return b}}},invalidrole:{impact:\"critical\",messages:{pass:function(a){return\"ARIA role is valid\"},fail:function(a){return\"Role must be one of the valid ARIA roles\"}}},abstractrole:{impact:\"serious\",messages:{pass:function(a){return\"Abstract roles are not used\"},fail:function(a){return\"Abstract roles cannot be directly used\"}}},\"aria-valid-attr-value\":{impact:\"critical\",messages:{pass:function(a){return\"ARIA attribute values are valid\"},fail:function(a){var b=\"Invalid ARIA attribute value\"+(a.data&&a.data.length>1?\"s\":\"\")+\":\",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" \"+d;return b}}},\"aria-errormessage\":{impact:\"critical\",messages:{pass:function(a){return\"Uses a supported aria-errormessage technique\"},fail:function(a){var b=\"aria-errormessage value\"+(a.data&&a.data.length>1?\"s\":\"\")+\" \",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" `\"+d;return b+=\"` must use a technique to announce the message (e.g., aria-live, aria-describedby, role=alert, etc.)\"}}},\"aria-valid-attr\":{impact:\"critical\",messages:{pass:function(a){return\"ARIA attribute name\"+(a.data&&a.data.length>1?\"s\":\"\")+\" are valid\"},fail:function(a){var b=\"Invalid ARIA attribute name\"+(a.data&&a.data.length>1?\"s\":\"\")+\":\",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" \"+d;return b}}},caption:{impact:\"critical\",messages:{pass:function(a){return\"The multimedia element has a captions track\"},fail:function(a){return\"The multimedia element does not have a captions track\"},incomplete:function(a){return\"A captions track for this element could not be found\"}}},\"is-on-screen\":{impact:\"serious\",messages:{pass:function(a){return\"Element is not visible\"},fail:function(a){return\"Element is visible\"}}},\"non-empty-if-present\":{impact:\"critical\",messages:{pass:function(a){var b=\"Element \";return a.data?b+=\"has a non-empty value attribute\":b+=\"does not have a value attribute\",b},fail:function(a){return\"Element has a value attribute and the value attribute is empty\"}}},\"non-empty-value\":{impact:\"critical\",messages:{pass:function(a){return\"Element has a non-empty value attribute\"},fail:function(a){return\"Element has no value attribute or the value attribute is empty\"}}},\"button-has-visible-text\":{impact:\"critical\",messages:{pass:function(a){return\"Element has inner text that is visible to screen readers\"},fail:function(a){return\"Element does not have inner text that is visible to screen readers\"}}},\n\"role-presentation\":{impact:\"minor\",messages:{pass:function(a){return'Element\\'s default semantics were overriden with role=\"presentation\"'},fail:function(a){return'Element\\'s default semantics were not overridden with role=\"presentation\"'}}},\"role-none\":{impact:\"minor\",messages:{pass:function(a){return'Element\\'s default semantics were overriden with role=\"none\"'},fail:function(a){return'Element\\'s default semantics were not overridden with role=\"none\"'}}},\"focusable-no-name\":{impact:\"serious\",messages:{pass:function(a){return\"Element is not in tab order or has accessible text\"},fail:function(a){return\"Element is in tab order and does not have accessible text\"}}},\"internal-link-present\":{impact:\"serious\",messages:{pass:function(a){return\"Valid skip link found\"},fail:function(a){return\"No valid skip link found\"}}},\"header-present\":{impact:\"serious\",messages:{pass:function(a){return\"Page has a header\"},fail:function(a){return\"Page does not have a header\"}}},landmark:{impact:\"serious\",messages:{pass:function(a){return\"Page has a landmark region\"},fail:function(a){return\"Page does not have a landmark region\"}}},\"group-labelledby\":{impact:\"critical\",messages:{pass:function(a){return'All elements with the name \"'+a.data.name+'\" reference the same element with aria-labelledby'},fail:function(a){return'All elements with the name \"'+a.data.name+'\" do not reference the same element with aria-labelledby'}}},fieldset:{impact:\"critical\",messages:{pass:function(a){return\"Element is contained in a fieldset\"},fail:function(a){var b=\"\",c=a.data&&a.data.failureCode;return b+=\"no-legend\"===c?\"Fieldset does not have a legend as its first child\":\"empty-legend\"===c?\"Legend does not have text that is visible to screen readers\":\"mixed-inputs\"===c?\"Fieldset contains unrelated inputs\":\"no-group-label\"===c?\"ARIA group does not have aria-label or aria-labelledby\":\"group-mixed-inputs\"===c?\"ARIA group contains unrelated inputs\":\"Element does not have a containing fieldset or ARIA group\"}}},\"color-contrast\":{impact:\"serious\",messages:{pass:function(a){return\"Element has sufficient color contrast of \"+a.data.contrastRatio},fail:function(a){return\"Element has insufficient color contrast of \"+a.data.contrastRatio+\" (foreground color: \"+a.data.fgColor+\", background color: \"+a.data.bgColor+\", font size: \"+a.data.fontSize+\", font weight: \"+a.data.fontWeight+\"). Expected contrast ratio of \"+a.data.expectedContrastRatio},incomplete:{bgImage:\"Element's background color could not be determined due to a background image\",bgGradient:\"Element's background color could not be determined due to a background gradient\",imgNode:\"Element's background color could not be determined because element contains an image node\",bgOverlap:\"Element's background color could not be determined because it is overlapped by another element\",fgAlpha:\"Element's foreground color could not be determined because of alpha transparency\",elmPartiallyObscured:\"Element's background color could not be determined because it's partially obscured by another element\",elmPartiallyObscuring:\"Element's background color could not be determined because it partially overlaps other elements\",outsideViewport:\"Element's background color could not be determined because it's outside the viewport\",equalRatio:\"Element has a 1:1 contrast ratio with the background\",default:\"Unable to determine contrast ratio\"}}},\"structured-dlitems\":{impact:\"serious\",messages:{pass:function(a){return\"When not empty, element has both <dt> and <dd> elements\"},fail:function(a){return\"When not empty, element does not have at least one <dt> element followed by at least one <dd> element\"}}},\"only-dlitems\":{impact:\"serious\",messages:{pass:function(a){return\"List element only has direct children that are allowed inside <dt> or <dd> elements\"},fail:function(a){return\"List element has direct children that are not allowed inside <dt> or <dd> elements\"}}},dlitem:{impact:\"serious\",messages:{pass:function(a){return\"Description list item has a <dl> parent element\"},fail:function(a){return\"Description list item does not have a <dl> parent element\"}}},\"doc-has-title\":{impact:\"serious\",messages:{pass:function(a){return\"Document has a non-empty <title> element\"},fail:function(a){return\"Document does not have a non-empty <title> element\"}}},\"duplicate-id\":{impact:\"moderate\",messages:{pass:function(a){return\"Document has no elements that share the same id attribute\"},fail:function(a){return\"Document has multiple elements with the same id attribute: \"+a.data}}},\"has-visible-text\":{impact:\"minor\",messages:{pass:function(a){return\"Element has text that is visible to screen readers\"},fail:function(a){return\"Element does not have text that is visible to screen readers\"}}},\"unique-frame-title\":{impact:\"serious\",messages:{pass:function(a){return\"Element's title attribute is unique\"},fail:function(a){return\"Element's title attribute is not unique\"}}},\"heading-order\":{impact:\"moderate\",messages:{pass:function(a){return\"Heading order valid\"},fail:function(a){return\"Heading order invalid\"}}},\"hidden-content\":{impact:\"minor\",messages:{pass:function(a){return\"All content on the page has been analyzed.\"},fail:function(a){return\"There were problems analyzing the content on this page.\"},incomplete:function(a){return\"There is hidden content on the page that was not analyzed. You will need to trigger the display of this content in order to analyze it.\"}}},\"href-no-hash\":{impact:\"moderate\",messages:{pass:function(a){return\"Anchor does not have an href value of #\"},fail:function(a){return\"Anchor has an href value of #\"}}},\"has-lang\":{impact:\"serious\",messages:{pass:function(a){return\"The <html> element has a lang attribute\"},fail:function(a){return\"The <html> element does not have a lang attribute\"}}},\"valid-lang\":{impact:\"serious\",messages:{pass:function(a){return\"Value of lang attribute is included in the list of valid languages\"},fail:function(a){return\"Value of lang attribute not included in the list of valid languages\"}}},\"has-alt\":{impact:\"critical\",messages:{pass:function(a){return\"Element has an alt attribute\"},fail:function(a){return\"Element does not have an alt attribute\"}}},\"duplicate-img-label\":{impact:\"minor\",messages:{pass:function(a){return\"Element does not duplicate existing text in <img> alt text\"},fail:function(a){return\"Element contains <img> element with alt text that duplicates existing text\"}}},\"title-only\":{impact:\"serious\",messages:{pass:function(a){return\"Form element does not solely use title attribute for its label\"},fail:function(a){return\"Only title used to generate label for form element\"}}},\"implicit-label\":{impact:\"critical\",messages:{pass:function(a){return\"Form element has an implicit (wrapped) <label>\"},fail:function(a){return\"Form element does not have an implicit (wrapped) <label>\"}}},\"explicit-label\":{impact:\"critical\",messages:{pass:function(a){return\"Form element has an explicit <label>\"},fail:function(a){return\"Form element does not have an explicit <label>\"}}},\"help-same-as-label\":{impact:\"minor\",messages:{pass:function(a){return\"Help text (title or aria-describedby) does not duplicate label text\"},fail:function(a){return\"Help text (title or aria-describedby) text is the same as the label text\"}}},\"multiple-label\":{impact:\"serious\",messages:{pass:function(a){return\"Form element does not have multiple <label> elements\"},fail:function(a){return\"Form element has multiple <label> elements\"}}},\"main-is-top-level\":{impact:\"moderate\",messages:{pass:function(a){return\"The main landmark is at the top level.\"},fail:function(a){return\"The main landmark is contained in another landmark.\"}}},\"has-at-least-one-main\":{impact:\"moderate\",messages:{pass:function(a){return\"Document has at least one main landmark\"},fail:function(a){return\"Document has no main landmarks\"}}},\"has-no-more-than-one-main\":{impact:\"moderate\",messages:{pass:function(a){return\"Document has no more than one main landmark\"},fail:function(a){return\"Document has more than one main landmark\"}}},\"has-th\":{impact:\"serious\",messages:{pass:function(a){return\"Layout table does not use <th> elements\"},fail:function(a){return\"Layout table uses <th> elements\"}}},\"has-caption\":{impact:\"serious\",messages:{pass:function(a){return\"Layout table does not use <caption> element\"},fail:function(a){return\"Layout table uses <caption> element\"}}},\"has-summary\":{impact:\"serious\",messages:{pass:function(a){return\"Layout table does not use summary attribute\"},fail:function(a){return\"Layout table uses summary attribute\"}}},\"link-in-text-block\":{impact:\"serious\",messages:{pass:function(a){return\"Links can be distinguished from surrounding text in a way that does not rely on color\"},fail:function(a){return\"Links can not be distinguished from surrounding text in a way that does not rely on color\"},incomplete:{bgContrast:\"Element's contrast ratio could not be determined. Check for a distinct hover/focus style\",bgImage:\"Element's contrast ratio could not be determined due to a background image\",bgGradient:\"Element's contrast ratio could not be determined due to a background gradient\",imgNode:\"Element's contrast ratio could not be determined because element contains an image node\",bgOverlap:\"Element's contrast ratio could not be determined because of element overlap\",default:\"Unable to determine contrast ratio\"}}},\"only-listitems\":{impact:\"serious\",messages:{pass:function(a){return\"List element only has direct children that are allowed inside <li> elements\"},fail:function(a){return\"List element has direct children that are not allowed inside <li> elements\"}}},listitem:{impact:\"serious\",messages:{pass:function(a){return'List item has a <ul>, <ol> or role=\"list\" parent element'},fail:function(a){return'List item does not have a <ul>, <ol> or role=\"list\" parent element'}}},\"meta-refresh\":{impact:\"critical\",messages:{pass:function(a){return\"<meta> tag does not immediately refresh the page\"},fail:function(a){return\"<meta> tag forces timed refresh of page\"}}},\"meta-viewport-large\":{impact:\"minor\",messages:{pass:function(a){return\"<meta> tag does not prevent significant zooming on mobile devices\"},fail:function(a){return\"<meta> tag limits zooming on mobile devices\"}}},\"meta-viewport\":{impact:\"critical\",messages:{pass:function(a){return\"<meta> tag does not disable zooming on mobile devices\"},fail:function(a){return\"<meta> tag disables zooming on mobile devices\"}}},\"p-as-heading\":{impact:\"serious\",messages:{pass:function(a){return\"<p> elements are not styled as headings\"},fail:function(a){return\"Heading elements should be used instead of styled p elements\"}}},region:{impact:\"moderate\",messages:{pass:function(a){return\"Content contained by ARIA landmark\"},fail:function(a){return\"Content not contained by an ARIA landmark\"}}},\"html5-scope\":{impact:\"moderate\",messages:{pass:function(a){return\"Scope attribute is only used on table header elements (<th>)\"},fail:function(a){return\"In HTML 5, scope attributes may only be used on table header elements (<th>)\"}}},\"scope-value\":{impact:\"critical\",messages:{pass:function(a){return\"Scope attribute is used correctly\"},fail:function(a){return\"The value of the scope attribute may only be 'row' or 'col'\"}}},exists:{impact:\"minor\",messages:{pass:function(a){return\"Element does not exist\"},fail:function(a){return\"Element exists\"}}},\"skip-link\":{impact:\"moderate\",messages:{pass:function(a){return\"Valid skip link found\"},fail:function(a){return\"No valid skip link found\"}}},tabindex:{impact:\"serious\",messages:{pass:function(a){return\"Element does not have a tabindex greater than 0\"},fail:function(a){return\"Element has a tabindex greater than 0\"}}},\"same-caption-summary\":{impact:\"minor\",messages:{pass:function(a){return\"Content of summary attribute and <caption> are not duplicated\"},fail:function(a){return\"Content of summary attribute and <caption> element are identical\"}}},\"caption-faked\":{impact:\"serious\",messages:{pass:function(a){return\"The first row of a table is not used as a caption\"},fail:function(a){return\"The first row of the table should be a caption instead of a table cell\"}}},\"td-has-header\":{impact:\"critical\",messages:{pass:function(a){return\"All non-empty data cells have table headers\"},fail:function(a){return\"Some non-empty data cells do not have table headers\"}}},\"td-headers-attr\":{impact:\"serious\",messages:{pass:function(a){return\"The headers attribute is exclusively used to refer to other cells in the table\"},fail:function(a){return\"The headers attribute is not exclusively used to refer to other cells in the table\"}}},\"th-has-data-cells\":{impact:\"serious\",messages:{pass:function(a){return\"All table header cells refer to data cells\"},fail:function(a){return\"Not all table header cells refer to data cells\"},incomplete:function(a){return\"Table data cells are missing or empty\"}}},description:{impact:\"critical\",messages:{pass:function(a){return\"The multimedia element has an audio description track\"},fail:function(a){return\"The multimedia element does not have an audio description track\"},incomplete:function(a){return\"An audio description track for this element could not be found\"}}}},failureSummaries:{any:{failureMessage:function(a){var b=\"Fix any of the following:\",c=a;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\"\\n  \"+d.split(\"\\n\").join(\"\\n  \");return b}},none:{failureMessage:function(a){var b=\"Fix all of the following:\",c=a;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\"\\n  \"+d.split(\"\\n\").join(\"\\n  \");return b}}},incompleteFallbackMessage:function(a){return\"aXe couldn't tell the reason. Time to break out the element inspector!\"}},rules:[{id:\"accesskeys\",selector:\"[accesskey]\",excludeHidden:!1,tags:[\"wcag2a\",\"wcag211\",\"cat.keyboard\"],all:[],any:[],none:[\"accesskeys\"]},{id:\"area-alt\",selector:\"map area[href]\",excludeHidden:!1,tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag111\",\"section508\",\"section508.22.a\"],all:[],any:[\"non-empty-alt\",\"non-empty-title\",\"aria-label\",\"aria-labelledby\"],none:[]},{id:\"aria-allowed-attr\",matches:function(a){var b=a.getAttribute(\"role\");b||(b=axe.commons.aria.implicitRole(a));var c=axe.commons.aria.allowedAttr(b);if(b&&c){var d=/^aria-/;if(a.hasAttributes())for(var e=a.attributes,f=0,g=e.length;f<g;f++)if(d.test(e[f].name))return!0}return!1},tags:[\"cat.aria\",\"wcag2a\",\"wcag411\",\"wcag412\"],all:[],any:[\"aria-allowed-attr\"],none:[]},{id:\"aria-hidden-body\",selector:\"body\",excludeHidden:!1,tags:[\"cat.aria\",\"wcag2a\",\"wcag412\"],all:[],any:[\"aria-hidden-body\"],none:[]},{id:\"aria-required-attr\",selector:\"[role]\",tags:[\"cat.aria\",\"wcag2a\",\"wcag411\",\"wcag412\"],all:[],any:[\"aria-required-attr\"],none:[]},{id:\"aria-required-children\",selector:\"[role]\",tags:[\"cat.aria\",\"wcag2a\",\"wcag131\"],all:[],any:[\"aria-required-children\"],none:[]},{id:\"aria-required-parent\",selector:\"[role]\",tags:[\"cat.aria\",\"wcag2a\",\"wcag131\"],all:[],any:[\"aria-required-parent\"],none:[]},{id:\"aria-roles\",selector:\"[role]\",tags:[\"cat.aria\",\"wcag2a\",\"wcag131\",\"wcag411\",\"wcag412\"],all:[],any:[],none:[\"invalidrole\",\"abstractrole\"]},{id:\"aria-valid-attr-value\",matches:function(a){var b=/^aria-/;if(a.hasAttributes())for(var c=a.attributes,d=0,e=c.length;d<e;d++)if(b.test(c[d].name))return!0;return!1},tags:[\"cat.aria\",\"wcag2a\",\"wcag131\",\"wcag411\",\"wcag412\"],all:[{options:[],id:\"aria-valid-attr-value\"},\"aria-errormessage\"],any:[],none:[]},{id:\"aria-valid-attr\",matches:function(a){var b=/^aria-/;if(a.hasAttributes())for(var c=a.attributes,d=0,e=c.length;d<e;d++)if(b.test(c[d].name))return!0;return!1},tags:[\"cat.aria\",\"wcag2a\",\"wcag411\"],all:[],any:[{options:[],id:\"aria-valid-attr\"}],none:[]},{id:\"audio-caption\",selector:\"audio\",excludeHidden:!1,tags:[\"cat.time-and-media\",\"wcag2a\",\"wcag122\",\"section508\",\"section508.22.a\"],all:[],any:[],none:[\"caption\"]},{id:\"blink\",selector:\"blink\",excludeHidden:!1,tags:[\"cat.time-and-media\",\"wcag2a\",\"wcag222\",\"section508\",\"section508.22.j\"],all:[],any:[],none:[\"is-on-screen\"]},{id:\"button-name\",selector:'button, [role=\"button\"], input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"]',tags:[\"cat.name-role-value\",\"wcag2a\",\"wcag412\",\"section508\",\"section508.22.a\"],all:[],any:[\"non-empty-if-present\",\"non-empty-value\",\"button-has-visible-text\",\"aria-label\",\"aria-labelledby\",\"role-presentation\",\"role-none\"],none:[\"focusable-no-name\"]},{id:\"bypass\",selector:\"html\",pageLevel:!0,matches:function(a){return!!a.querySelector(\"a[href]\")},tags:[\"cat.keyboard\",\"wcag2a\",\"wcag241\",\"section508\",\"section508.22.o\"],all:[],any:[\"internal-link-present\",\"header-present\",\"landmark\"],none:[]},{id:\"checkboxgroup\",selector:\"input[type=checkbox][name]\",tags:[\"cat.forms\",\"best-practice\"],all:[],any:[\"group-labelledby\",\"fieldset\"],none:[]},{id:\"color-contrast\",matches:function(a){var b=a.nodeName.toUpperCase(),c=a.type,d=document;if(\"true\"===a.getAttribute(\"aria-disabled\")||axe.commons.dom.findUp(a,'[aria-disabled=\"true\"]'))return!1;if(\"INPUT\"===b)return-1===[\"hidden\",\"range\",\"color\",\"checkbox\",\"radio\",\"image\"].indexOf(c)&&!a.disabled;if(\"SELECT\"===b)return!!a.options.length&&!a.disabled;if(\"TEXTAREA\"===b)return!a.disabled;if(\"OPTION\"===b)return!1;if(\"BUTTON\"===b&&a.disabled||axe.commons.dom.findUp(a,\"button[disabled]\"))return!1;if(\"FIELDSET\"===b&&a.disabled||axe.commons.dom.findUp(a,\"fieldset[disabled]\"))return!1;var e=axe.commons.dom.findUp(a,\"label\");if(\"LABEL\"===b||e){var f=a;e&&(f=e);var g=f.htmlFor&&d.getElementById(f.htmlFor);if(g&&g.disabled)return!1;var g=f.querySelector('input:not([type=\"hidden\"]):not([type=\"image\"]):not([type=\"button\"]):not([type=\"submit\"]):not([type=\"reset\"]), select, textarea');if(g&&g.disabled)return!1}if(a.getAttribute(\"id\")){var h=axe.commons.utils.escapeSelector(a.getAttribute(\"id\")),i=d.querySelector('[aria-labelledby~=\"'+h+'\"]');if(i&&i.hasAttribute(\"disabled\"))return!1}if(\"\"===axe.commons.text.visible(a,!1,!0))return!1;var j,k,l=document.createRange(),m=a.childNodes,n=m.length;for(k=0;k<n;k++)j=m[k],3===j.nodeType&&\"\"!==axe.commons.text.sanitize(j.nodeValue)&&l.selectNodeContents(j);var o=l.getClientRects();for(n=o.length,k=0;k<n;k++)if(axe.commons.dom.visuallyOverlaps(o[k],a))return!0;return!1},excludeHidden:!1,options:{noScroll:!1},tags:[\"cat.color\",\"wcag2aa\",\"wcag143\"],all:[],any:[\"color-contrast\"],none:[]},{id:\"definition-list\",selector:\"dl:not([role])\",tags:[\"cat.structure\",\"wcag2a\",\"wcag131\"],all:[],any:[],none:[\"structured-dlitems\",\"only-dlitems\"]},{id:\"dlitem\",selector:\"dd:not([role]), dt:not([role])\",tags:[\"cat.structure\",\"wcag2a\",\"wcag131\"],all:[],any:[\"dlitem\"],none:[]},{id:\"document-title\",selector:\"html\",matches:function(a){return a.ownerDocument.defaultView.self===a.ownerDocument.defaultView.top},tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag242\"],all:[],any:[\"doc-has-title\"],none:[]},{id:\"duplicate-id\",selector:\"[id]\",excludeHidden:!1,tags:[\"cat.parsing\",\"wcag2a\",\"wcag411\"],all:[],any:[\"duplicate-id\"],none:[]},{id:\"empty-heading\",selector:'h1, h2, h3, h4, h5, h6, [role=\"heading\"]',enabled:!0,tags:[\"cat.name-role-value\",\"best-practice\"],all:[],any:[\"has-visible-text\",\"role-presentation\",\"role-none\"],none:[]},{id:\"frame-title-unique\",selector:\"frame[title]:not([title='']), iframe[title]:not([title=''])\",matches:function(a){var b=a.getAttribute(\"title\");return!!(b?axe.commons.text.sanitize(b).trim():\"\")},tags:[\"cat.text-alternatives\",\"best-practice\"],all:[],any:[],none:[\"unique-frame-title\"]},{id:\"frame-title\",selector:\"frame, iframe\",tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag241\",\"section508\",\"section508.22.i\"],all:[],any:[\"aria-label\",\"aria-labelledby\",\"non-empty-title\",\"role-presentation\",\"role-none\"],none:[]},{id:\"heading-order\",selector:\"h1,h2,h3,h4,h5,h6,[role=heading]\",enabled:!1,tags:[\"cat.semantics\",\"best-practice\"],all:[],any:[\"heading-order\"],none:[]},{id:\"hidden-content\",selector:\"*\",excludeHidden:!1,tags:[\"experimental\",\"review-item\"],all:[],any:[\"hidden-content\"],none:[],enabled:!1},{id:\"href-no-hash\",selector:\"a[href]\",enabled:!1,tags:[\"cat.semantics\",\"best-practice\"],all:[],any:[\"href-no-hash\"],none:[]},{id:\"html-has-lang\",selector:\"html\",tags:[\"cat.language\",\"wcag2a\",\"wcag311\"],all:[],any:[\"has-lang\"],none:[]},{id:\"html-lang-valid\",selector:\"html[lang]\",tags:[\"cat.language\",\"wcag2a\",\"wcag311\"],all:[],any:[],none:[\"valid-lang\"]},{id:\"image-alt\",selector:\"img, [role='img']\",tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag111\",\"section508\",\"section508.22.a\"],all:[],any:[\"has-alt\",\"aria-label\",\"aria-labelledby\",\"non-empty-title\",\"role-presentation\",\"role-none\"],none:[]},{id:\"image-redundant-alt\",selector:'button, [role=\"button\"], a[href], p, li, td, th',tags:[\"cat.text-alternatives\",\"best-practice\"],all:[],any:[],none:[\"duplicate-img-label\"]},{id:\"input-image-alt\",selector:'input[type=\"image\"]',tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag111\",\"section508\",\"section508.22.a\"],all:[],any:[\"non-empty-alt\",\"aria-label\",\"aria-labelledby\",\"non-empty-title\"],none:[]},{id:\"label-title-only\",selector:\"input:not([type='hidden']):not([type='image']):not([type='button']):not([type='submit']):not([type='reset']), select, textarea\",enabled:!1,tags:[\"cat.forms\",\"best-practice\"],all:[],any:[],none:[\"title-only\"]},{id:\"label\",selector:\"input:not([type='hidden']):not([type='image']):not([type='button']):not([type='submit']):not([type='reset']), select, textarea\",tags:[\"cat.forms\",\"wcag2a\",\"wcag332\",\"wcag131\",\"section508\",\"section508.22.n\"],all:[],any:[\"aria-label\",\"aria-labelledby\",\"implicit-label\",\"explicit-label\",\"non-empty-title\"],none:[\"help-same-as-label\",\"multiple-label\"]},{id:\"landmark-main-is-top-level\",selector:\"main,[role=main]\",tags:[\"best-practice\"],all:[],any:[\"main-is-top-level\"],none:[]},{id:\"landmark-one-main\",selector:\"html\",tags:[\"best-practice\"],all:[\"has-at-least-one-main\",\"has-no-more-than-one-main\"],any:[],none:[]},{id:\"layout-table\",selector:\"table\",matches:function(a){return!axe.commons.table.isDataTable(a)},tags:[\"cat.semantics\",\"wcag2a\",\"wcag131\"],all:[],any:[],none:[\"has-th\",\"has-caption\",\"has-summary\"]},{id:\"link-in-text-block\",selector:\"a[href]:not([role]), *[role=link]\",matches:function(a){return!!axe.commons.text.sanitize(a.textContent)&&!!axe.commons.dom.isVisible(a,!1)&&axe.commons.dom.isInTextBlock(a)},excludeHidden:!1,tags:[\"cat.color\",\"experimental\",\"wcag2a\",\"wcag141\"],all:[\"link-in-text-block\"],any:[],none:[]},{id:\"link-name\",selector:'a[href]:not([role=\"button\"]), [role=link][href]',tags:[\"cat.name-role-value\",\"wcag2a\",\"wcag111\",\"wcag412\",\"wcag244\",\"section508\",\"section508.22.a\"],all:[],any:[\"has-visible-text\",\"aria-label\",\"aria-labelledby\",\"role-presentation\",\"role-none\"],none:[\"focusable-no-name\"]},{id:\"list\",selector:\"ul:not([role]), ol:not([role])\",tags:[\"cat.structure\",\"wcag2a\",\"wcag131\"],all:[],any:[],none:[\"only-listitems\"]},{id:\"listitem\",selector:\"li:not([role])\",tags:[\"cat.structure\",\"wcag2a\",\"wcag131\"],all:[],any:[\"listitem\"],none:[]},{id:\"marquee\",selector:\"marquee\",excludeHidden:!1,tags:[\"cat.parsing\",\"wcag2a\",\"wcag222\"],all:[],any:[],none:[\"is-on-screen\"]},{id:\"meta-refresh\",selector:'meta[http-equiv=\"refresh\"]',excludeHidden:!1,tags:[\"cat.time\",\"wcag2a\",\"wcag2aaa\",\"wcag221\",\"wcag224\",\"wcag325\"],all:[],any:[\"meta-refresh\"],none:[]},{id:\"meta-viewport-large\",selector:'meta[name=\"viewport\"]',excludeHidden:!1,tags:[\"cat.sensory-and-visual-cues\",\"best-practice\"],all:[],any:[{options:{scaleMinimum:5,lowerBound:2},id:\"meta-viewport-large\"}],none:[]},{id:\"meta-viewport\",selector:'meta[name=\"viewport\"]',excludeHidden:!1,tags:[\"cat.sensory-and-visual-cues\",\"wcag2aa\",\"wcag144\"],all:[],any:[{options:{scaleMinimum:2},id:\"meta-viewport\"}],none:[]},{id:\"object-alt\",selector:\"object\",tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag111\",\"section508\",\"section508.22.a\"],all:[],any:[\"has-visible-text\",\"aria-label\",\"aria-labelledby\",\"non-empty-title\"],none:[]},{id:\"p-as-heading\",selector:\"p\",matches:function(a){var b=Array.from(a.parentNode.childNodes),c=a.textContent.trim(),d=/[.!?:;](?![.!?:;])/g;return!(0===c.length||(c.match(d)||[]).length>=2)&&0!==b.slice(b.indexOf(a)+1).filter(function(a){return\"P\"===a.nodeName.toUpperCase()&&\"\"!==a.textContent.trim()}).length},tags:[\"cat.semantics\",\"wcag2a\",\"wcag131\",\"experimental\"],all:[{options:{margins:[{weight:150,italic:!0},{weight:150,size:1.15},{italic:!0,size:1.15},{size:1.4}]},id:\"p-as-heading\"}],any:[],none:[]},{id:\"radiogroup\",selector:\"input[type=radio][name]\",tags:[\"cat.forms\",\"best-practice\"],all:[],any:[\"group-labelledby\",\"fieldset\"],none:[]},{id:\"region\",selector:\"html\",pageLevel:!0,enabled:!1,tags:[\"cat.keyboard\",\"best-practice\"],all:[],any:[\"region\"],none:[]},{id:\"scope-attr-valid\",selector:\"td[scope], th[scope]\",enabled:!0,tags:[\"cat.tables\",\"best-practice\"],all:[\"html5-scope\",\"scope-value\"],any:[],none:[]},{id:\"server-side-image-map\",selector:\"img[ismap]\",tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag211\",\"section508\",\"section508.22.f\"],all:[],any:[],none:[\"exists\"]},{id:\"skip-link\",selector:\"a[href]\",pageLevel:!0,enabled:!1,tags:[\"cat.keyboard\",\"best-practice\"],all:[],any:[\"skip-link\"],none:[]},{id:\"tabindex\",selector:\"[tabindex]\",tags:[\"cat.keyboard\",\"best-practice\"],all:[],any:[\"tabindex\"],none:[]},{id:\"table-duplicate-name\",selector:\"table\",tags:[\"cat.tables\",\"best-practice\"],all:[],any:[],none:[\"same-caption-summary\"]},{id:\"table-fake-caption\",selector:\"table\",matches:function(a){return axe.commons.table.isDataTable(a)},tags:[\"cat.tables\",\"experimental\",\"wcag2a\",\"wcag131\",\"section508\",\"section508.22.g\"],all:[\"caption-faked\"],any:[],none:[]},{id:\"td-has-header\",selector:\"table\",matches:function(a){if(axe.commons.table.isDataTable(a)){var b=axe.commons.table.toArray(a);return b.length>=3&&b[0].length>=3&&b[1].length>=3&&b[2].length>=3}return!1},tags:[\"cat.tables\",\"experimental\",\"wcag2a\",\"wcag131\",\"section508\",\"section508.22.g\"],all:[\"td-has-header\"],any:[],none:[]},{id:\"td-headers-attr\",selector:\"table\",tags:[\"cat.tables\",\"wcag2a\",\"wcag131\",\"section508\",\"section508.22.g\"],all:[\"td-headers-attr\"],any:[],none:[]},{id:\"th-has-data-cells\",selector:\"table\",matches:function(a){return axe.commons.table.isDataTable(a)},tags:[\"cat.tables\",\"wcag2a\",\"wcag131\",\"section508\",\"section508.22.g\"],all:[\"th-has-data-cells\"],any:[],none:[]},{id:\"valid-lang\",selector:\"[lang]:not(html), [xml\\\\:lang]:not(html)\",tags:[\"cat.language\",\"wcag2aa\",\"wcag312\"],all:[],any:[],none:[\"valid-lang\"]},{id:\"video-caption\",selector:\"video\",excludeHidden:!1,tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag122\",\"wcag123\",\"section508\",\"section508.22.a\"],all:[],any:[],none:[\"caption\"]},{id:\"video-description\",selector:\"video\",excludeHidden:!1,tags:[\"cat.text-alternatives\",\"wcag2aa\",\"wcag125\",\"section508\",\"section508.22.b\"],all:[],any:[],none:[\"description\"]}],checks:[{id:\"abstractrole\",evaluate:function(a,b){return\"abstract\"===axe.commons.aria.getRoleType(a.getAttribute(\"role\"))}},{id:\"aria-allowed-attr\",evaluate:function(a,b){var c,d,e,f=[],g=a.getAttribute(\"role\"),h=a.attributes;if(g||(g=axe.commons.aria.implicitRole(a)),e=axe.commons.aria.allowedAttr(g),g&&e)for(var i=0,j=h.length;i<j;i++)c=h[i],d=c.name,axe.commons.aria.validateAttr(d)&&-1===e.indexOf(d)&&f.push(d+'=\"'+c.nodeValue+'\"');return!f.length||(this.data(f),!1)}},{id:\"aria-hidden-body\",evaluate:function(a,b){return\"true\"!==a.getAttribute(\"aria-hidden\")}},{id:\"aria-errormessage\",evaluate:function(a,b){b=Array.isArray(b)?b:[];var c=a.getAttribute(\"aria-errormessage\"),d=a.hasAttribute(\"aria-errormessage\"),e=document;return!(-1===b.indexOf(c)&&d&&!function(){var b=c&&e.getElementById(c);if(b)return\"alert\"===b.getAttribute(\"role\")||\"assertive\"===b.getAttribute(\"aria-live\")||axe.utils.tokenList(a.getAttribute(\"aria-describedby\")||\"\").indexOf(c)>-1}())||(this.data(c),!1)}},{id:\"invalidrole\",evaluate:function(a,b){return!axe.commons.aria.isValidRole(a.getAttribute(\"role\"))}},{id:\"aria-required-attr\",evaluate:function(a,b){var c=[];if(a.hasAttributes()){var d,e=a.getAttribute(\"role\"),f=axe.commons.aria.requiredAttr(e);if(e&&f)for(var g=0,h=f.length;g<h;g++)d=f[g],a.getAttribute(d)||c.push(d)}return!c.length||(this.data(c),!1)}},{id:\"aria-required-children\",evaluate:function(a,b){function c(a,b,c){if(null===a)return!1;var d=f(b),e=['[role=\"'+b+'\"]'];return d&&(e=e.concat(d)),e=e.join(\",\"),c?g(a,e)||!!a.querySelector(e):!!a.querySelector(e)}function d(a,b){var d,e;for(d=0,e=a.length;d<e;d++)if(null!==a[d]&&c(a[d],b,!0))return!0;return!1}var e=axe.commons.aria.requiredOwned,f=axe.commons.aria.implicitNodes,g=axe.commons.utils.matchesSelector,h=axe.commons.dom.idrefs,i=a.getAttribute(\"role\"),j=e(i);if(!j)return!0;var k=!1,l=j.one;if(!l){var k=!0;l=j.all}var m=function(a,b,e,f){var g,i=b.length,j=[],k=h(a,\"aria-owns\");for(g=0;g<i;g++){var l=b[g];if(c(a,l)||d(k,l)){if(!e)return null}else e&&j.push(l)}if(\"combobox\"===f){var m=j.indexOf(\"textbox\"),n=[\"text\",\"search\",\"email\",\"url\",\"tel\"];m>=0&&\"INPUT\"===a.tagName&&n.includes(a.type)&&j.splice(m,1);var o=j.indexOf(\"listbox\"),p=a.getAttribute(\"aria-expanded\");o>=0&&(!p||\"false\"===p)&&j.splice(o,1)}return j.length?j:!e&&b.length?b:null}(a,l,k,i);return!m||(this.data(m),!1)}},{id:\"aria-required-parent\",evaluate:function(a,b){function c(a){return(axe.commons.aria.implicitNodes(a)||[]).concat('[role=\"'+a+'\"]').join(\",\")}function d(a,b,d){var e,f,g=a.getAttribute(\"role\"),h=[];if(b||(b=axe.commons.aria.requiredContext(g)),!b)return null;for(e=0,f=b.length;e<f;e++){if(d&&axe.utils.matchesSelector(a,c(b[e])))return null;if(axe.commons.dom.findUp(a,c(b[e])))return null;h.push(b[e])}return h}var e=d(a);if(!e)return!0;var f=function(a){for(var b=[],c=null;a;){if(a.getAttribute(\"id\")){var d=axe.commons.utils.escapeSelector(a.getAttribute(\"id\"));c=document.querySelector(\"[aria-owns~=\"+d+\"]\"),c&&b.push(c)}a=a.parentElement}return b.length?b:null}(a);if(f)for(var g=0,h=f.length;g<h;g++)if(!(e=d(f[g],e,!0)))return!0;return this.data(e),!1}},{id:\"aria-valid-attr-value\",evaluate:function(a,b){b=Array.isArray(b)?b:[];for(var c,d,e=[],f=/^aria-/,g=a.attributes,h=[\"aria-errormessage\"],i=0,j=g.length;i<j;i++)c=g[i],d=c.name,h.includes(d)||-1===b.indexOf(d)&&f.test(d)&&!axe.commons.aria.validateAttrValue(a,d)&&e.push(d+'=\"'+c.nodeValue+'\"');return!e.length||(this.data(e),!1)},options:[]},{id:\"aria-valid-attr\",evaluate:function(a,b){b=Array.isArray(b)?b:[];for(var c,d=[],e=/^aria-/,f=a.attributes,g=0,h=f.length;g<h;g++)c=f[g].name,-1===b.indexOf(c)&&e.test(c)&&!axe.commons.aria.validateAttr(c)&&d.push(c);return!d.length||(this.data(d),!1)},options:[]},{id:\"color-contrast\",evaluate:function(a,b){if(!axe.commons.dom.isVisible(a,!1))return!0;var c,d=!!(b||{}).noScroll,e=[],f=axe.commons.color.getBackgroundColor(a,e,d),g=axe.commons.color.getForegroundColor(a,d),h=window.getComputedStyle(a),i=parseFloat(h.getPropertyValue(\"font-size\")),j=h.getPropertyValue(\"font-weight\"),k=-1!==[\"bold\",\"bolder\",\"600\",\"700\",\"800\",\"900\"].indexOf(j),l=axe.commons.color.hasValidContrastRatio(f,g,i,k),m=Math.floor(100*l.contrastRatio)/100;null===f&&(c=axe.commons.color.incompleteData.get(\"bgColor\"));var n=!1;1===m&&(n=!0,c=axe.commons.color.incompleteData.set(\"bgColor\",\"equalRatio\"));var o={fgColor:g?g.toHexString():void 0,bgColor:f?f.toHexString():void 0,contrastRatio:l?m:void 0,fontSize:(72*i/96).toFixed(1)+\"pt\",fontWeight:k?\"bold\":\"normal\",missingData:c,expectedContrastRatio:l.expectedContrastRatio+\":1\"};return this.data(o),null===g||null===f||n?(c=null,axe.commons.color.incompleteData.clear(),void this.relatedNodes(e)):(l.isValid||this.relatedNodes(e),l.isValid)}},{id:\"link-in-text-block\",evaluate:function(a,b){function c(a,b){var c=a.getRelativeLuminance(),d=b.getRelativeLuminance();return(Math.max(c,d)+.05)/(Math.min(c,d)+.05)}function d(a){var b=window.getComputedStyle(a).getPropertyValue(\"display\");return-1!==f.indexOf(b)||\"table-\"===b.substr(0,6)}var e=axe.commons.color,f=[\"block\",\"list-item\",\"table\",\"flex\",\"grid\",\"inline-block\"];if(d(a))return!1;for(var g=a.parentNode;1===g.nodeType&&!d(g);)g=g.parentNode;if(this.relatedNodes([g]),e.elementIsDistinct(a,g))return!0;var h,i;if(h=e.getForegroundColor(a),i=e.getForegroundColor(g),h&&i){var j=c(h,i);if(1===j)return!0;if(j>=3)return axe.commons.color.incompleteData.set(\"fgColor\",\"bgContrast\"),this.data({\nmissingData:axe.commons.color.incompleteData.get(\"fgColor\")}),void axe.commons.color.incompleteData.clear();if(h=e.getBackgroundColor(a),i=e.getBackgroundColor(g),!h||!i||c(h,i)>=3){var k=void 0;return k=h&&i?\"bgContrast\":axe.commons.color.incompleteData.get(\"bgColor\"),axe.commons.color.incompleteData.set(\"fgColor\",k),this.data({missingData:axe.commons.color.incompleteData.get(\"fgColor\")}),void axe.commons.color.incompleteData.clear()}return!1}}},{id:\"fieldset\",evaluate:function(a,b){function c(a,b){return axe.commons.utils.toArray(a.querySelectorAll('select,textarea,button,input:not([name=\"'+b+'\"]):not([type=\"hidden\"])'))}function d(a,b){var d=a.firstElementChild;if(!d||\"LEGEND\"!==d.nodeName.toUpperCase())return h.relatedNodes([a]),g=\"no-legend\",!1;if(!axe.commons.text.accessibleText(d))return h.relatedNodes([d]),g=\"empty-legend\",!1;var e=c(a,b);return!e.length||(h.relatedNodes(e),g=\"mixed-inputs\",!1)}function e(a,b){var d=axe.commons.dom.idrefs(a,\"aria-labelledby\").some(function(a){return a&&axe.commons.text.accessibleText(a)}),e=a.getAttribute(\"aria-label\");if(!(d||e&&axe.commons.text.sanitize(e)))return h.relatedNodes(a),g=\"no-group-label\",!1;var f=c(a,b);return!f.length||(h.relatedNodes(f),g=\"group-mixed-inputs\",!1)}function f(a,b){return axe.commons.utils.toArray(a).filter(function(a){return a!==b})}var g,h=this,i={name:a.getAttribute(\"name\"),type:a.getAttribute(\"type\")},j=function(b){var c=axe.commons.utils.escapeSelector(a.name),i=document.querySelectorAll('input[type=\"'+axe.commons.utils.escapeSelector(a.type)+'\"][name=\"'+c+'\"]');if(i.length<2)return!0;var j=axe.commons.dom.findUp(b,\"fieldset\"),k=axe.commons.dom.findUp(b,'[role=\"group\"]'+(\"radio\"===a.type?',[role=\"radiogroup\"]':\"\"));return k||j?j?d(j,c):e(k,c):(g=\"no-group\",h.relatedNodes(f(i,b)),!1)}(a);return j||(i.failureCode=g),this.data(i),j},after:function(a,b){var c={};return a.filter(function(a){if(a.result)return!0;var b=a.data;if(b){if(c[b.type]=c[b.type]||{},!c[b.type][b.name])return c[b.type][b.name]=[b],!0;var d=c[b.type][b.name].some(function(a){return a.failureCode===b.failureCode});return d||c[b.type][b.name].push(b),!d}return!1})}},{id:\"group-labelledby\",evaluate:function(a,b){this.data({name:a.getAttribute(\"name\"),type:a.getAttribute(\"type\")});var c=document.querySelectorAll('input[type=\"'+axe.commons.utils.escapeSelector(a.type)+'\"][name=\"'+axe.commons.utils.escapeSelector(a.name)+'\"]');return c.length<=1||0!==[].map.call(c,function(a){var b=a.getAttribute(\"aria-labelledby\");return b?b.split(/\\s+/):[]}).reduce(function(a,b){return a.filter(function(a){return-1!==b.indexOf(a)})}).filter(function(a){var b=document.getElementById(a);return b&&axe.commons.text.accessibleText(b)}).length},after:function(a,b){var c={};return a.filter(function(a){var b=a.data;return!(!b||(c[b.type]=c[b.type]||{},c[b.type][b.name]))&&(c[b.type][b.name]=!0,!0)})}},{id:\"accesskeys\",evaluate:function(a,b){return axe.commons.dom.isVisible(a,!1)&&(this.data(a.getAttribute(\"accesskey\")),this.relatedNodes([a])),!0},after:function(a,b){var c={};return a.filter(function(a){if(!a.data)return!1;var b=a.data.toUpperCase();return c[b]?(c[b].relatedNodes.push(a.relatedNodes[0]),!1):(c[b]=a,a.relatedNodes=[],!0)}).map(function(a){return a.result=!!a.relatedNodes.length,a})}},{id:\"focusable-no-name\",evaluate:function(a,b){var c=a.getAttribute(\"tabindex\");return!!(axe.commons.dom.isFocusable(a)&&c>-1)&&!axe.commons.text.accessibleText(a)}},{id:\"has-at-least-one-main\",evaluate:function(a,b){var c=document.querySelectorAll(\"main,[role=main]\");return this.data(!!c[0]),!!c[0]},after:function(a,b){for(var c=!1,d=0;d<a.length&&!c;d++)c=a[d].data;for(var d=0;d<a.length;d++)a[d].result=c;return a}},{id:\"has-no-more-than-one-main\",evaluate:function(a,b){return document.querySelectorAll(\"main,[role=main]\").length<=1}},{id:\"main-is-top-level\",evaluate:function(a,b){for(var c=axe.commons.aria.getRolesByType(\"landmark\"),d=a.parentNode;d;){if(1===d.nodeType){var e=d.getAttribute(\"role\");if(e||\"form\"===d.tagName.toLowerCase()||(e=axe.commons.aria.implicitRole(d)),e&&c.includes(e))return!1}d=d.parentNode}return!0}},{id:\"tabindex\",evaluate:function(a,b){return a.tabIndex<=0}},{id:\"duplicate-img-label\",evaluate:function(a,b){var c=a.querySelectorAll(\"img\"),d=axe.commons.text.visible(a,!0).toLowerCase();if(\"\"===d)return!1;for(var e=0,f=c.length;e<f;e++){var g=c[e];if(axe.commons.text.accessibleText(g).toLowerCase()===d&&\"presentation\"!==g.getAttribute(\"role\")&&axe.commons.dom.isVisible(g))return!0}return!1}},{id:\"explicit-label\",evaluate:function(a,b){if(a.getAttribute(\"id\")){var c=axe.commons.utils.escapeSelector(a.getAttribute(\"id\")),d=document.querySelector('label[for=\"'+c+'\"]');if(d)return!!axe.commons.text.accessibleText(d)}return!1}},{id:\"help-same-as-label\",evaluate:function(a,b){var c=axe.commons.text.label(a),d=a.getAttribute(\"title\");if(!c)return!1;if(!d&&(d=\"\",a.getAttribute(\"aria-describedby\"))){d=axe.commons.dom.idrefs(a,\"aria-describedby\").map(function(a){return a?axe.commons.text.accessibleText(a):\"\"}).join(\"\")}return axe.commons.text.sanitize(d)===axe.commons.text.sanitize(c)},enabled:!1},{id:\"implicit-label\",evaluate:function(a,b){var c=axe.commons.dom.findUp(a,\"label\");return!!c&&!!axe.commons.text.accessibleText(c)}},{id:\"multiple-label\",evaluate:function(a,b){var c=axe.commons.utils.escapeSelector(a.getAttribute(\"id\")),d=Array.from(document.querySelectorAll('label[for=\"'+c+'\"]')),e=a.parentNode;for(d.length&&(d=d.filter(function(a,b){if(0===b&&!axe.commons.dom.isVisible(a,!0)||axe.commons.dom.isVisible(a,!0))return a}));e;)\"LABEL\"===e.tagName&&-1===d.indexOf(e)&&d.push(e),e=e.parentNode;return this.relatedNodes(d),d.length>1}},{id:\"title-only\",evaluate:function(a,b){return!(axe.commons.text.label(a)||!a.getAttribute(\"title\")&&!a.getAttribute(\"aria-describedby\"))}},{id:\"has-lang\",evaluate:function(a,b){return!!(a.getAttribute(\"lang\")||a.getAttribute(\"xml:lang\")||\"\").trim()}},{id:\"valid-lang\",evaluate:function(a,b){function c(a){return a.trim().split(\"-\")[0].toLowerCase()}var d,e;return d=(b||axe.commons.utils.validLangs()).map(c),e=[\"lang\",\"xml:lang\"].reduce(function(b,e){var f=a.getAttribute(e);if(\"string\"!=typeof f)return b;var g=c(f);return\"\"!==g&&-1===d.indexOf(g)&&b.push(e+'=\"'+a.getAttribute(e)+'\"'),b},[]),!!e.length&&(this.data(e),!0)}},{id:\"dlitem\",evaluate:function(a,b){return\"DL\"===a.parentNode.tagName.toUpperCase()}},{id:\"has-listitem\",evaluate:function(a,b){var c=a.children;if(0===c.length)return!0;for(var d=0;d<c.length;d++)if(\"LI\"===c[d].nodeName.toUpperCase())return!1;return!0}},{id:\"listitem\",evaluate:function(a,b){return-1!==[\"UL\",\"OL\"].indexOf(a.parentNode.nodeName.toUpperCase())||\"list\"===a.parentNode.getAttribute(\"role\")}},{id:\"only-dlitems\",evaluate:function(a,b){for(var c,d,e=[],f=a.childNodes,g=[\"STYLE\",\"META\",\"LINK\",\"MAP\",\"AREA\",\"SCRIPT\",\"DATALIST\",\"TEMPLATE\"],h=!1,i=0;i<f.length;i++){c=f[i];var d=c.nodeName.toUpperCase();1===c.nodeType&&\"DT\"!==d&&\"DD\"!==d&&-1===g.indexOf(d)?e.push(c):3===c.nodeType&&\"\"!==c.nodeValue.trim()&&(h=!0)}return e.length&&this.relatedNodes(e),!!e.length||h}},{id:\"only-listitems\",evaluate:function(a,b){for(var c,d,e=[],f=a.childNodes,g=[\"STYLE\",\"META\",\"LINK\",\"MAP\",\"AREA\",\"SCRIPT\",\"DATALIST\",\"TEMPLATE\"],h=!1,i=0;i<f.length;i++)c=f[i],d=c.nodeName.toUpperCase(),1===c.nodeType&&\"LI\"!==d&&-1===g.indexOf(d)?e.push(c):3===c.nodeType&&\"\"!==c.nodeValue.trim()&&(h=!0);return e.length&&this.relatedNodes(e),!!e.length||h}},{id:\"structured-dlitems\",evaluate:function(a,b){var c=a.children;if(!c||!c.length)return!1;for(var d,e=!1,f=!1,g=0;g<c.length;g++){if(d=c[g].nodeName.toUpperCase(),\"DT\"===d&&(e=!0),e&&\"DD\"===d)return!1;\"DD\"===d&&(f=!0)}return e||f}},{id:\"caption\",evaluate:function(a,b){var c=a.querySelectorAll(\"track\");if(c.length){for(var d=0;d<c.length;d++){var e=c[d].getAttribute(\"kind\");if(e&&\"captions\"===e)return!1}return!0}}},{id:\"description\",evaluate:function(a,b){var c=a.querySelectorAll(\"track\");if(c.length){for(var d=0;d<c.length;d++){var e=c[d].getAttribute(\"kind\");if(e&&\"descriptions\"===e)return!1}return!0}}},{id:\"meta-viewport-large\",evaluate:function(a,b){b=b||{};for(var c,d=a.getAttribute(\"content\")||\"\",e=d.split(/[;,]/),f={},g=b.scaleMinimum||2,h=b.lowerBound||!1,i=0,j=e.length;i<j;i++){c=e[i].split(\"=\");var k=c.shift().toLowerCase();k&&c.length&&(f[k.trim()]=c.shift().trim().toLowerCase())}return!!(h&&f[\"maximum-scale\"]&&parseFloat(f[\"maximum-scale\"])<h)||!(!h&&\"no\"===f[\"user-scalable\"])&&!(f[\"maximum-scale\"]&&parseFloat(f[\"maximum-scale\"])<g)},options:{scaleMinimum:5,lowerBound:2}},{id:\"meta-viewport\",evaluate:function(a,b){b=b||{};for(var c,d=a.getAttribute(\"content\")||\"\",e=d.split(/[;,]/),f={},g=b.scaleMinimum||2,h=b.lowerBound||!1,i=0,j=e.length;i<j;i++){c=e[i].split(\"=\");var k=c.shift().toLowerCase();k&&c.length&&(f[k.trim()]=c.shift().trim().toLowerCase())}return!!(h&&f[\"maximum-scale\"]&&parseFloat(f[\"maximum-scale\"])<h)||!(!h&&\"no\"===f[\"user-scalable\"])&&!(f[\"maximum-scale\"]&&parseFloat(f[\"maximum-scale\"])<g)},options:{scaleMinimum:2}},{id:\"header-present\",evaluate:function(a,b){return!!a.querySelector('h1, h2, h3, h4, h5, h6, [role=\"heading\"]')}},{id:\"heading-order\",evaluate:function(a,b){var c=a.getAttribute(\"aria-level\");if(null!==c)return this.data(parseInt(c,10)),!0;var d=a.tagName.match(/H(\\d)/);return!d||(this.data(parseInt(d[1],10)),!0)},after:function(a,b){if(a.length<2)return a;for(var c=a[0].data,d=1;d<a.length;d++)a[d].result&&a[d].data>c+1&&(a[d].result=!1),c=a[d].data;return a}},{id:\"href-no-hash\",evaluate:function(a,b){return\"#\"!==a.getAttribute(\"href\")}},{id:\"internal-link-present\",evaluate:function(a,b){return!!a.querySelector('a[href^=\"#\"]')}},{id:\"landmark\",evaluate:function(a,b){return a.getElementsByTagName(\"main\").length>0||!!a.querySelector('[role=\"main\"]')}},{id:\"meta-refresh\",evaluate:function(a,b){var c=a.getAttribute(\"content\")||\"\",d=c.split(/[;,]/);return\"\"===c||\"0\"===d[0]}},{id:\"p-as-heading\",evaluate:function(a,b){function c(a){for(var b=a,c=a.textContent.trim(),d=c;d===c&&void 0!==b;){var e=-1;if(a=b,0===a.children.length)return a;do{e++,d=a.children[e].textContent.trim()}while(\"\"===d&&e+1<a.children.length);b=a.children[e]}return a}function d(a){switch(a){case\"lighter\":return 100;case\"normal\":return 400;case\"bold\":return 700;case\"bolder\":return 900}return a=parseInt(a),isNaN(a)?400:a}function e(a){var b=window.getComputedStyle(c(a));return{fontWeight:d(b.getPropertyValue(\"font-weight\")),fontSize:parseInt(b.getPropertyValue(\"font-size\")),isItalic:\"italic\"===b.getPropertyValue(\"font-style\")}}function f(a,b,c){return c.reduce(function(c,d){return c||(!d.size||a.fontSize/d.size>b.fontSize)&&(!d.weight||a.fontWeight-d.weight>b.fontWeight)&&(!d.italic||a.isItalic&&!b.isItalic)},!1)}var g=Array.from(a.parentNode.children),h=g.indexOf(a);b=b||{};var i=b.margins||[],j=g.slice(h+1).find(function(a){return\"P\"===a.nodeName.toUpperCase()}),k=g.slice(0,h).reverse().find(function(a){return\"P\"===a.nodeName.toUpperCase()}),l=e(a),m=j?e(j):null,n=k?e(k):null;if(!m||!f(l,m,i))return!0;var o=axe.commons.dom.findUp(a,\"blockquote\");return!!(o&&\"BLOCKQUOTE\"===o.nodeName.toUpperCase()||n&&!f(l,n,i))&&void 0},options:{margins:[{weight:150,italic:!0},{weight:150,size:1.15},{italic:!0,size:1.15},{size:1.4}]}},{id:\"region\",evaluate:function(a,b){function c(a){return h&&axe.commons.dom.getElementByReference(h,\"href\")&&h===a}function d(a){return a.hasAttribute(\"role\")?g.includes(a.getAttribute(\"role\").toLowerCase()):i.some(function(b){return axe.utils.matchesSelector(a,b)})}function e(a){return d(a)?null:c(a)?f(a):axe.commons.dom.isVisible(a,!0)&&(axe.commons.text.visible(a,!0,!0)||axe.commons.dom.isVisualContent(a))?a:f(a)}function f(a){var b=axe.commons.utils.toArray(a.children);return 0===b.length?[]:b.map(e).filter(function(a){return null!==a}).reduce(function(a,b){return a.concat(b)},[])}var g=axe.commons.aria.getRolesByType(\"landmark\"),h=a.querySelector(\"a[href]\"),i=g.reduce(function(a,b){return a.concat(axe.commons.aria.implicitNodes(b))},[]).filter(function(a){return null!==a}),j=f(a);return this.relatedNodes(j),!j.length},after:function(a,b){return[a[0]]}},{id:\"skip-link\",evaluate:function(a,b){var c=axe.commons.dom.getElementByReference(a,\"href\");return!!c&&axe.commons.dom.isFocusable(c)},after:function(a,b){return[a[0]]}},{id:\"unique-frame-title\",evaluate:function(a,b){var c=axe.commons.text.sanitize(a.title).trim().toLowerCase();return this.data(c),!0},after:function(a,b){var c={};return a.forEach(function(a){c[a.data]=void 0!==c[a.data]?++c[a.data]:0}),a.forEach(function(a){a.result=!!c[a.data]}),a}},{id:\"aria-label\",evaluate:function(a,b){var c=a.getAttribute(\"aria-label\");return!!(c?axe.commons.text.sanitize(c).trim():\"\")}},{id:\"aria-labelledby\",evaluate:function(a,b){return(0,axe.commons.dom.idrefs)(a,\"aria-labelledby\").some(function(a){return a&&axe.commons.text.accessibleText(a,!0)})}},{id:\"button-has-visible-text\",evaluate:function(a,b){var c=a.nodeName.toUpperCase(),d=a.getAttribute(\"role\"),e=void 0;return(\"BUTTON\"===c||\"button\"===d&&\"INPUT\"!==c)&&(e=axe.commons.text.accessibleText(a),this.data(e),!!e)}},{id:\"doc-has-title\",evaluate:function(a,b){var c=document.title;return!!(c?axe.commons.text.sanitize(c).trim():\"\")}},{id:\"duplicate-id\",evaluate:function(a,b){if(!a.getAttribute(\"id\").trim())return!0;for(var c=axe.commons.utils.escapeSelector(a.getAttribute(\"id\")),d=document.querySelectorAll('[id=\"'+c+'\"]'),e=[],f=0;f<d.length;f++)d[f]!==a&&e.push(d[f]);return e.length&&this.relatedNodes(e),this.data(a.getAttribute(\"id\")),d.length<=1},after:function(a,b){var c=[];return a.filter(function(a){return-1===c.indexOf(a.data)&&(c.push(a.data),!0)})}},{id:\"exists\",evaluate:function(a,b){return!0}},{id:\"has-alt\",evaluate:function(a,b){var c=a.nodeName.toLowerCase();return a.hasAttribute(\"alt\")&&(\"img\"===c||\"input\"===c||\"area\"===c)}},{id:\"has-visible-text\",evaluate:function(a,b){return axe.commons.text.accessibleText(a).length>0}},{id:\"is-on-screen\",evaluate:function(a,b){return axe.commons.dom.isVisible(a,!1)&&!axe.commons.dom.isOffscreen(a)}},{id:\"non-empty-alt\",evaluate:function(a,b){var c=a.getAttribute(\"alt\");return!!(c?axe.commons.text.sanitize(c).trim():\"\")}},{id:\"non-empty-if-present\",evaluate:function(a,b){var c=a.nodeName.toUpperCase(),d=(a.getAttribute(\"type\")||\"\").toLowerCase(),e=a.getAttribute(\"value\");return this.data(e),\"INPUT\"===c&&-1!==[\"submit\",\"reset\"].indexOf(d)&&null===e}},{id:\"non-empty-title\",evaluate:function(a,b){var c=a.getAttribute(\"title\");return!!(c?axe.commons.text.sanitize(c).trim():\"\")}},{id:\"non-empty-value\",evaluate:function(a,b){var c=a.getAttribute(\"value\");return!!(c?axe.commons.text.sanitize(c).trim():\"\")}},{id:\"role-none\",evaluate:function(a,b){return\"none\"===a.getAttribute(\"role\")}},{id:\"role-presentation\",evaluate:function(a,b){return\"presentation\"===a.getAttribute(\"role\")}},{id:\"caption-faked\",evaluate:function(a,b){var c=axe.commons.table.toGrid(a),d=c[0];return c.length<=1||d.length<=1||a.rows.length<=1||d.reduce(function(a,b,c){return a||b!==d[c+1]&&void 0!==d[c+1]},!1)}},{id:\"has-caption\",evaluate:function(a,b){return!!a.caption}},{id:\"has-summary\",evaluate:function(a,b){return!!a.summary}},{id:\"has-th\",evaluate:function(a,b){for(var c,d,e=[],f=0,g=a.rows.length;f<g;f++){c=a.rows[f];for(var h=0,i=c.cells.length;h<i;h++)d=c.cells[h],\"TH\"!==d.nodeName.toUpperCase()&&-1===[\"rowheader\",\"columnheader\"].indexOf(d.getAttribute(\"role\"))||e.push(d)}return!!e.length&&(this.relatedNodes(e),!0)}},{id:\"html5-scope\",evaluate:function(a,b){return!axe.commons.dom.isHTML5(document)||\"TH\"===a.nodeName.toUpperCase()}},{id:\"same-caption-summary\",evaluate:function(a,b){return!(!a.summary||!a.caption)&&a.summary===axe.commons.text.accessibleText(a.caption)}},{id:\"scope-value\",evaluate:function(a,b){b=b||{};var c=a.getAttribute(\"scope\").toLowerCase();return-1!==([\"row\",\"col\",\"rowgroup\",\"colgroup\"]||b.values).indexOf(c)}},{id:\"td-has-header\",evaluate:function(a,b){var c=axe.commons.table,d=[];return c.getAllCells(a).forEach(function(a){if(axe.commons.dom.hasContent(a)&&c.isDataCell(a)&&!axe.commons.aria.label(a)){var b=c.getHeaders(a);(b=b.reduce(function(a,b){return a||null!==b&&!!axe.commons.dom.hasContent(b)},!1))||d.push(a)}}),!d.length||(this.relatedNodes(d),!1)}},{id:\"td-headers-attr\",evaluate:function(a,b){for(var c=[],d=0,e=a.rows.length;d<e;d++)for(var f=a.rows[d],g=0,h=f.cells.length;g<h;g++)c.push(f.cells[g]);var i=c.reduce(function(a,b){return b.getAttribute(\"id\")&&a.push(b.getAttribute(\"id\")),a},[]),j=c.reduce(function(a,b){var c,d,e=(b.getAttribute(\"headers\")||\"\").split(/\\s/).reduce(function(a,b){return b=b.trim(),b&&a.push(b),a},[]);return 0!==e.length&&(b.getAttribute(\"id\")&&(c=-1!==e.indexOf(b.getAttribute(\"id\").trim())),d=e.reduce(function(a,b){return a||-1===i.indexOf(b)},!1),(c||d)&&a.push(b)),a},[]);return!(j.length>0)||(this.relatedNodes(j),!1)}},{id:\"th-has-data-cells\",evaluate:function(a,b){var c=axe.commons.table,d=c.getAllCells(a),e=this,f=[];d.forEach(function(a){var b=a.getAttribute(\"headers\");b&&(f=f.concat(b.split(/\\s+/)));var c=a.getAttribute(\"aria-labelledby\");c&&(f=f.concat(c.split(/\\s+/)))});var g=d.filter(function(a){return\"\"!==axe.commons.text.sanitize(a.textContent)&&(\"TH\"===a.nodeName.toUpperCase()||-1!==[\"rowheader\",\"columnheader\"].indexOf(a.getAttribute(\"role\")))}),h=c.toGrid(a);return!!g.reduce(function(a,b){if(b.getAttribute(\"id\")&&f.includes(b.getAttribute(\"id\")))return!!a||a;var d=!1,g=c.getCellPosition(b,h);return c.isColumnHeader(b)&&(d=c.traverse(\"down\",g,h).reduce(function(a,b){return a||axe.commons.dom.hasContent(b)&&!c.isColumnHeader(b)},!1)),!d&&c.isRowHeader(b)&&(d=c.traverse(\"right\",g,h).reduce(function(a,b){return a||axe.commons.dom.hasContent(b)&&!c.isRowHeader(b)},!1)),d||e.relatedNodes(b),a&&d},!0)||void 0}},{id:\"hidden-content\",evaluate:function(a,b){var c=window.getComputedStyle(a);if(![\"SCRIPT\",\"HEAD\",\"TITLE\",\"NOSCRIPT\",\"STYLE\",\"TEMPLATE\"].includes(a.tagName.toUpperCase())&&axe.commons.dom.hasContent(a)){if(\"none\"===c.getPropertyValue(\"display\"))return;if(\"hidden\"===c.getPropertyValue(\"visibility\")){if(a.parentNode)var d=window.getComputedStyle(a.parentNode);if(!d||\"hidden\"!==d.getPropertyValue(\"visibility\"))return}}return!0}}],commons:function(){function a(a){return a.getPropertyValue(\"font-family\").split(/[,;]/g).map(function(a){return a.trim().toLowerCase()})}function b(b,c){var d=window.getComputedStyle(b);if(\"none\"!==d.getPropertyValue(\"background-image\"))return!0;if([\"border-bottom\",\"border-top\",\"outline\"].reduce(function(a,b){var c=new y.Color;return c.parseRgbString(d.getPropertyValue(b+\"-color\")),a||\"none\"!==d.getPropertyValue(b+\"-style\")&&parseFloat(d.getPropertyValue(b+\"-width\"))>0&&0!==c.alpha},!1))return!0;var e=window.getComputedStyle(c);if(a(d)[0]!==a(e)[0])return!0;var f=[\"text-decoration-line\",\"text-decoration-style\",\"font-weight\",\"font-style\",\"font-size\"].reduce(function(a,b){return a||d.getPropertyValue(b)!==e.getPropertyValue(b)},!1),g=d.getPropertyValue(\"text-decoration\");return g.split(\" \").length<3&&(f=f||g!==e.getPropertyValue(\"text-decoration\")),f}function c(a,b){var c=a.nodeName.toUpperCase();if(C.includes(c))return axe.commons.color.incompleteData.set(\"bgColor\",\"imgNode\"),!0;b=b||window.getComputedStyle(a);var d=b.getPropertyValue(\"background-image\"),e=\"none\"!==d;if(e){var f=/gradient/.test(d);axe.commons.color.incompleteData.set(\"bgColor\",f?\"bgGradient\":\"bgImage\")}return e}function d(a,b){b=b||window.getComputedStyle(a);var c=new y.Color;if(c.parseRgbString(b.getPropertyValue(\"background-color\")),0!==c.alpha){var d=b.getPropertyValue(\"opacity\");c.alpha=c.alpha*d}return c}function e(a,b){var c=a.getClientRects()[0],d=document.elementsFromPoint(c.left,c.top);if(d)for(var e=0;e<d.length;e++)if(d[e]!==a&&d[e]===b)return!0;return!1}function f(a,b,c){var f=0;if(a>0)for(var g=a-1;g>=0;g--){var h=b[g],i=window.getComputedStyle(h),j=d(h,i);j.alpha&&e(c,h)?f+=j.alpha:b.splice(g,1)}return f}function g(a,b,c){var d=a!==b&&!z.visuallyContains(a,b)&&0!==c.alpha;return d&&axe.commons.color.incompleteData.set(\"bgColor\",\"elmPartiallyObscured\"),d}function h(a,b){var c={TD:[\"TR\",\"TBODY\"],TH:[\"TR\",\"THEAD\"],INPUT:[\"LABEL\"]},d=a.map(function(a){return a.tagName}),e=a;for(var f in c)if(d.includes(f))for(var g in c[f])if(f.hasOwnProperty(g)){var h=axe.commons.dom.findUp(b,c[f][g]);if(h&&-1===a.indexOf(h)){var i=axe.commons.dom.visuallyOverlaps(b.getBoundingClientRect(),h);i&&e.splice(d.indexOf(f)+1,0,h)}b.tagName===c[f][g]&&-1===d.indexOf(b.tagName)&&e.splice(d.indexOf(f)+1,0,b)}return e}function i(a){var b=a.indexOf(document.body),e=a;return b>1&&!c(document.documentElement)&&0===d(document.documentElement).alpha&&(e.splice(b,1),e.splice(a.indexOf(document.documentElement),1),e.push(document.body)),e}function j(a,b){\"use strict\";var c=b(a);for(a=a.firstChild;a;)!1!==c&&j(a,b),a=a.nextSibling}function k(a){\"use strict\";var b=window.getComputedStyle(a).getPropertyValue(\"display\");return-1!==D.indexOf(b)||\"table-\"===b.substr(0,6)}function l(a){\"use strict\";var b=a.match(/rect\\s*\\(([0-9]+)px,?\\s*([0-9]+)px,?\\s*([0-9]+)px,?\\s*([0-9]+)px\\s*\\)/);return!(!b||5!==b.length)&&(b[3]-b[1]<=0&&b[2]-b[4]<=0)}function m(a){var b=null;if(a.getAttribute(\"id\")){var c=axe.utils.escapeSelector(a.getAttribute(\"id\"));if(b=document.querySelector('label[for=\"'+c+'\"]'))return b}return b=z.findUp(a,\"label\")}function n(a){return-1!==[\"button\",\"reset\",\"submit\"].indexOf(a.type)}function o(a){var b=a.nodeName.toUpperCase();return\"TEXTAREA\"===b||\"SELECT\"===b||\"INPUT\"===b&&\"hidden\"!==a.type.toLowerCase()}function p(a){return-1!==[\"BUTTON\",\"SUMMARY\",\"A\"].indexOf(a.nodeName.toUpperCase())}function q(a){return-1!==[\"TABLE\",\"FIGURE\"].indexOf(a.nodeName.toUpperCase())}function r(a){var b=a.nodeName.toUpperCase();if(\"INPUT\"===b)return!a.hasAttribute(\"type\")||-1!==G.indexOf(a.getAttribute(\"type\").toLowerCase())&&a.value?a.value:\"\";if(\"SELECT\"===b){var c=a.options;if(c&&c.length){for(var d=\"\",e=0;e<c.length;e++)c[e].selected&&(d+=\" \"+c[e].text);return B.sanitize(d)}return\"\"}return\"TEXTAREA\"===b&&a.value?a.value:\"\"}function s(a,b){var c=a.querySelector(b.toLowerCase());return c?B.accessibleText(c):\"\"}function t(a){if(!a)return!1;switch(a.nodeName.toUpperCase()){case\"SELECT\":case\"TEXTAREA\":return!0;case\"INPUT\":return!a.hasAttribute(\"type\")||-1!==G.indexOf(a.getAttribute(\"type\").toLowerCase());default:return!1}}function u(a){var b=a.nodeName.toUpperCase();return\"INPUT\"===b&&\"image\"===a.type.toLowerCase()||-1!==[\"IMG\",\"APPLET\",\"AREA\"].indexOf(b)}function v(a){return!!B.sanitize(a)}var commons={},w=commons.aria={},x=w.lookupTable={};x.attributes={\"aria-activedescendant\":{type:\"idref\"},\"aria-atomic\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-autocomplete\":{type:\"nmtoken\",values:[\"inline\",\"list\",\"both\",\"none\"]},\"aria-busy\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-checked\":{type:\"nmtoken\",values:[\"true\",\"false\",\"mixed\",\"undefined\"]},\"aria-colcount\":{type:\"int\"},\"aria-colindex\":{type:\"int\"},\"aria-colspan\":{type:\"int\"},\"aria-controls\":{type:\"idrefs\"},\"aria-current\":{type:\"nmtoken\",values:[\"page\",\"step\",\"location\",\"date\",\"time\",\"true\",\"false\"]},\"aria-describedby\":{type:\"idrefs\"},\"aria-disabled\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-dropeffect\":{type:\"nmtokens\",values:[\"copy\",\"move\",\"reference\",\"execute\",\"popup\",\"none\"]},\"aria-errormessage\":{type:\"idref\"},\"aria-expanded\":{type:\"nmtoken\",values:[\"true\",\"false\",\"undefined\"]},\"aria-flowto\":{type:\"idrefs\"},\"aria-grabbed\":{type:\"nmtoken\",values:[\"true\",\"false\",\"undefined\"]},\"aria-haspopup\":{type:\"nmtoken\",values:[\"true\",\"false\",\"menu\",\"listbox\",\"tree\",\"grid\",\"dialog\"]},\"aria-hidden\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-invalid\":{type:\"nmtoken\",values:[\"true\",\"false\",\"spelling\",\"grammar\"]},\"aria-keyshortcuts\":{type:\"string\"},\"aria-label\":{type:\"string\"},\"aria-labelledby\":{type:\"idrefs\"},\"aria-level\":{type:\"int\"},\"aria-live\":{type:\"nmtoken\",values:[\"off\",\"polite\",\"assertive\"]},\"aria-modal\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-multiline\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-multiselectable\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-orientation\":{type:\"nmtoken\",values:[\"horizontal\",\"vertical\"]},\"aria-owns\":{type:\"idrefs\"},\"aria-placeholder\":{type:\"string\"},\"aria-posinset\":{type:\"int\"},\"aria-pressed\":{type:\"nmtoken\",values:[\"true\",\"false\",\"mixed\",\"undefined\"]},\"aria-readonly\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-relevant\":{type:\"nmtokens\",values:[\"additions\",\"removals\",\"text\",\"all\"]},\"aria-required\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-rowcount\":{type:\"int\"},\"aria-rowindex\":{type:\"int\"},\"aria-rowspan\":{type:\"int\"},\"aria-selected\":{type:\"nmtoken\",values:[\"true\",\"false\",\"undefined\"]},\"aria-setsize\":{type:\"int\"},\"aria-sort\":{type:\"nmtoken\",values:[\"ascending\",\"descending\",\"other\",\"none\"]},\"aria-valuemax\":{type:\"decimal\"},\"aria-valuemin\":{type:\"decimal\"},\"aria-valuenow\":{type:\"decimal\"},\"aria-valuetext\":{type:\"string\"}},x.globalAttributes=[\"aria-atomic\",\"aria-busy\",\"aria-controls\",\"aria-current\",\"aria-describedby\",\"aria-disabled\",\"aria-dropeffect\",\"aria-flowto\",\"aria-grabbed\",\"aria-haspopup\",\"aria-hidden\",\"aria-invalid\",\"aria-keyshortcuts\",\"aria-label\",\"aria-labelledby\",\"aria-live\",\"aria-owns\",\"aria-relevant\"],x.role={alert:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},alertdialog:{type:\"widget\",attributes:{allowed:[\"aria-expanded\",\"aria-modal\"]},owned:null,nameFrom:[\"author\"],context:null},application:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},article:{type:\"structure\",attributes:{allowed:[\"aria-expanded\",\"aria-posinset\",\"aria-setsize\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"article\"]},banner:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"header\"]},button:{type:\"widget\",attributes:{allowed:[\"aria-expanded\",\"aria-pressed\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null,implicit:[\"button\",'input[type=\"button\"]','input[type=\"image\"]','input[type=\"reset\"]','input[type=\"submit\"]',\"summary\"]},cell:{type:\"structure\",attributes:{allowed:[\"aria-colindex\",\"aria-colspan\",\"aria-rowindex\",\"aria-rowspan\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"row\"],implicit:[\"td\",\"th\"]},checkbox:{type:\"widget\",attributes:{allowed:[\"aria-checked\",\"aria-required\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null,implicit:['input[type=\"checkbox\"]']},columnheader:{type:\"structure\",attributes:{allowed:[\"aria-colindex\",\"aria-colspan\",\"aria-expanded\",\"aria-rowindex\",\"aria-rowspan\",\"aria-required\",\"aria-readonly\",\"aria-selected\",\"aria-sort\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"row\"],implicit:[\"th\"]},combobox:{type:\"composite\",attributes:{allowed:[\"aria-expanded\",\"aria-autocomplete\",\"aria-required\",\"aria-activedescendant\",\"aria-orientation\"]},owned:{all:[\"listbox\",\"textbox\"]},nameFrom:[\"author\"],context:null},command:{nameFrom:[\"author\"],type:\"abstract\"},complementary:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"aside\"]},composite:{nameFrom:[\"author\"],type:\"abstract\"},contentinfo:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"footer\"]},definition:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"dd\",\"dfn\"]},dialog:{type:\"widget\",attributes:{allowed:[\"aria-expanded\",\"aria-modal\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"dialog\"]},directory:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null},document:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"body\"]},feed:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:{one:[\"article\"]},nameFrom:[\"author\"],context:null},form:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"form\"]},grid:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\",\"aria-colcount\",\"aria-level\",\"aria-multiselectable\",\"aria-readonly\",\"aria-rowcount\"]},owned:{one:[\"rowgroup\",\"row\"]},nameFrom:[\"author\"],context:null,implicit:[\"table\"]},gridcell:{type:\"widget\",attributes:{allowed:[\"aria-colindex\",\"aria-colspan\",\"aria-expanded\",\"aria-rowindex\",\"aria-rowspan\",\"aria-selected\",\"aria-readonly\",\"aria-required\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"row\"],implicit:[\"td\",\"th\"]},group:{type:\"structure\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"details\",\"optgroup\"]},heading:{type:\"structure\",attributes:{allowed:[\"aria-level\",\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null,implicit:[\"h1\",\"h2\",\"h3\",\"h4\",\"h5\",\"h6\"]},img:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"img\"]},input:{nameFrom:[\"author\"],type:\"abstract\"},landmark:{nameFrom:[\"author\"],type:\"abstract\"},link:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null,implicit:[\"a[href]\"]},list:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:{all:[\"listitem\"]},nameFrom:[\"author\"],context:null,implicit:[\"ol\",\"ul\",\"dl\"]},listbox:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-multiselectable\",\"aria-required\",\"aria-expanded\",\"aria-orientation\"]},owned:{all:[\"option\"]},nameFrom:[\"author\"],context:null,implicit:[\"select\"]},listitem:{type:\"structure\",attributes:{allowed:[\"aria-level\",\"aria-posinset\",\"aria-setsize\",\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"list\"],implicit:[\"li\",\"dt\"]},log:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},main:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"main\"]},marquee:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},math:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"math\"]},menu:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\",\"aria-orientation\"]},owned:{one:[\"menuitem\",\"menuitemradio\",\"menuitemcheckbox\"]},nameFrom:[\"author\"],context:null,implicit:['menu[type=\"context\"]']},menubar:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\",\"aria-orientation\"]},owned:null,nameFrom:[\"author\"],context:null},menuitem:{type:\"widget\",attributes:{allowed:[\"aria-posinset\",\"aria-setsize\",\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"menu\",\"menubar\"],implicit:['menuitem[type=\"command\"]']},menuitemcheckbox:{type:\"widget\",attributes:{allowed:[\"aria-checked\",\"aria-posinset\",\"aria-setsize\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"menu\",\"menubar\"],implicit:['menuitem[type=\"checkbox\"]']},menuitemradio:{type:\"widget\",attributes:{allowed:[\"aria-checked\",\"aria-selected\",\"aria-posinset\",\"aria-setsize\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"menu\",\"menubar\"],implicit:['menuitem[type=\"radio\"]']},navigation:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"nav\"]},none:{type:\"structure\",attributes:null,owned:null,nameFrom:[\"author\"],context:null},note:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},option:{type:\"widget\",attributes:{allowed:[\"aria-selected\",\"aria-posinset\",\"aria-setsize\",\"aria-checked\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"listbox\"],implicit:[\"option\"]},presentation:{type:\"structure\",attributes:null,owned:null,nameFrom:[\"author\"],context:null},progressbar:{type:\"widget\",attributes:{allowed:[\"aria-valuetext\",\"aria-valuenow\",\"aria-valuemax\",\"aria-valuemin\"]},owned:null,nameFrom:[\"author\"],\ncontext:null,implicit:[\"progress\"]},radio:{type:\"widget\",attributes:{allowed:[\"aria-checked\",\"aria-selected\",\"aria-posinset\",\"aria-setsize\",\"aria-required\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null,implicit:['input[type=\"radio\"]']},radiogroup:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-required\",\"aria-expanded\"]},owned:{all:[\"radio\"]},nameFrom:[\"author\"],context:null},range:{nameFrom:[\"author\"],type:\"abstract\"},region:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"section[aria-label]\",\"section[aria-labelledby]\",\"section[title]\"]},roletype:{type:\"abstract\"},row:{type:\"structure\",attributes:{allowed:[\"aria-activedescendant\",\"aria-colindex\",\"aria-expanded\",\"aria-level\",\"aria-selected\",\"aria-rowindex\"]},owned:{one:[\"cell\",\"columnheader\",\"rowheader\",\"gridcell\"]},nameFrom:[\"author\",\"contents\"],context:[\"rowgroup\",\"grid\",\"treegrid\",\"table\"],implicit:[\"tr\"]},rowgroup:{type:\"structure\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\"]},owned:{all:[\"row\"]},nameFrom:[\"author\",\"contents\"],context:[\"grid\",\"table\"],implicit:[\"tbody\",\"thead\",\"tfoot\"]},rowheader:{type:\"structure\",attributes:{allowed:[\"aria-colindex\",\"aria-colspan\",\"aria-expanded\",\"aria-rowindex\",\"aria-rowspan\",\"aria-required\",\"aria-readonly\",\"aria-selected\",\"aria-sort\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"row\"],implicit:[\"th\"]},scrollbar:{type:\"widget\",attributes:{required:[\"aria-controls\",\"aria-valuenow\",\"aria-valuemax\",\"aria-valuemin\"],allowed:[\"aria-valuetext\",\"aria-orientation\"]},owned:null,nameFrom:[\"author\"],context:null},search:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},searchbox:{type:\"widget\",attributes:{allowed:[\"aria-activedescendant\",\"aria-autocomplete\",\"aria-multiline\",\"aria-readonly\",\"aria-required\",\"aria-placeholder\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:['input[type=\"search\"]']},section:{nameFrom:[\"author\",\"contents\"],type:\"abstract\"},sectionhead:{nameFrom:[\"author\",\"contents\"],type:\"abstract\"},select:{nameFrom:[\"author\"],type:\"abstract\"},separator:{type:\"structure\",attributes:{allowed:[\"aria-expanded\",\"aria-orientation\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"hr\"]},slider:{type:\"widget\",attributes:{allowed:[\"aria-valuetext\",\"aria-orientation\"],required:[\"aria-valuenow\",\"aria-valuemax\",\"aria-valuemin\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:['input[type=\"range\"]']},spinbutton:{type:\"widget\",attributes:{allowed:[\"aria-valuetext\",\"aria-required\"],required:[\"aria-valuenow\",\"aria-valuemax\",\"aria-valuemin\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:['input[type=\"number\"]']},status:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"output\"]},structure:{type:\"abstract\"},switch:{type:\"widget\",attributes:{required:[\"aria-checked\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null},tab:{type:\"widget\",attributes:{allowed:[\"aria-selected\",\"aria-expanded\",\"aria-setsize\",\"aria-posinset\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"tablist\"]},table:{type:\"structure\",attributes:{allowed:[\"aria-colcount\",\"aria-rowcount\"]},owned:{one:[\"rowgroup\",\"row\"]},nameFrom:[\"author\"],context:null,implicit:[\"table\"]},tablist:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\",\"aria-level\",\"aria-multiselectable\",\"aria-orientation\"]},owned:{all:[\"tab\"]},nameFrom:[\"author\"],context:null},tabpanel:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},term:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null,implicit:[\"dt\"]},text:{type:\"structure\",owned:null,nameFrom:[\"author\",\"contents\"],context:null},textbox:{type:\"widget\",attributes:{allowed:[\"aria-activedescendant\",\"aria-autocomplete\",\"aria-multiline\",\"aria-readonly\",\"aria-required\",\"aria-placeholder\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:['input[type=\"text\"]','input[type=\"email\"]','input[type=\"password\"]','input[type=\"tel\"]','input[type=\"url\"]',\"input:not([type])\",\"textarea\"]},timer:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},toolbar:{type:\"structure\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:['menu[type=\"toolbar\"]']},tooltip:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null},tree:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-multiselectable\",\"aria-required\",\"aria-expanded\",\"aria-orientation\"]},owned:{all:[\"treeitem\"]},nameFrom:[\"author\"],context:null},treegrid:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-colcount\",\"aria-expanded\",\"aria-level\",\"aria-multiselectable\",\"aria-readonly\",\"aria-required\",\"aria-rowcount\",\"aria-orientation\"]},owned:{one:[\"rowgroup\",\"row\"]},nameFrom:[\"author\"],context:null},treeitem:{type:\"widget\",attributes:{allowed:[\"aria-checked\",\"aria-selected\",\"aria-expanded\",\"aria-level\",\"aria-posinset\",\"aria-setsize\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"group\",\"tree\"]},widget:{type:\"abstract\"},window:{nameFrom:[\"author\"],type:\"abstract\"}};var y={};commons.color=y;var z=commons.dom={},A=commons.table={},B=commons.text={};commons.utils=axe.utils;w.requiredAttr=function(a){\"use strict\";var b=w.lookupTable.role[a];return b&&b.attributes&&b.attributes.required||[]},w.allowedAttr=function(a){\"use strict\";var b=w.lookupTable.role[a],c=b&&b.attributes&&b.attributes.allowed||[],d=b&&b.attributes&&b.attributes.required||[];return c.concat(w.lookupTable.globalAttributes).concat(d)},w.validateAttr=function(a){\"use strict\";return!!w.lookupTable.attributes[a]},w.validateAttrValue=function(a,b){\"use strict\";var c,d,e=document,f=a.getAttribute(b),g=w.lookupTable.attributes[b];if(!g)return!0;switch(g.type){case\"boolean\":case\"nmtoken\":return\"string\"==typeof f&&-1!==g.values.indexOf(f.toLowerCase());case\"nmtokens\":return d=axe.utils.tokenList(f),d.reduce(function(a,b){return a&&-1!==g.values.indexOf(b)},0!==d.length);case\"idref\":return!(!f||!e.getElementById(f));case\"idrefs\":return d=axe.utils.tokenList(f),d.reduce(function(a,b){return!(!a||!e.getElementById(b))},0!==d.length);case\"string\":return!0;case\"decimal\":return!(!(c=f.match(/^[-+]?([0-9]*)\\.?([0-9]*)$/))||!c[1]&&!c[2]);case\"int\":return/^[-+]?[0-9]+$/.test(f)}},w.label=function(a){var b,c;return a.getAttribute(\"aria-labelledby\")&&(b=z.idrefs(a,\"aria-labelledby\"),c=b.map(function(a){return a?B.visible(a,!0):\"\"}).join(\" \").trim())?c:(c=a.getAttribute(\"aria-label\"),c&&(c=B.sanitize(c).trim())?c:null)},w.isValidRole=function(a){\"use strict\";return!!w.lookupTable.role[a]},w.getRolesWithNameFromContents=function(){return Object.keys(w.lookupTable.role).filter(function(a){return w.lookupTable.role[a].nameFrom&&-1!==w.lookupTable.role[a].nameFrom.indexOf(\"contents\")})},w.getRolesByType=function(a){return Object.keys(w.lookupTable.role).filter(function(b){return w.lookupTable.role[b].type===a})},w.getRoleType=function(a){var b=w.lookupTable.role[a];return b&&b.type||null},w.requiredOwned=function(a){\"use strict\";var b=null,c=w.lookupTable.role[a];return c&&(b=axe.utils.clone(c.owned)),b},w.requiredContext=function(a){\"use strict\";var b=null,c=w.lookupTable.role[a];return c&&(b=axe.utils.clone(c.context)),b},w.implicitNodes=function(a){\"use strict\";var b=null,c=w.lookupTable.role[a];return c&&c.implicit&&(b=axe.utils.clone(c.implicit)),b},w.implicitRole=function(a){\"use strict\";var b=function(b,c){var d=function(b){return axe.utils.matchesSelector(a,b)};return c.implicit&&c.implicit.some(d)&&b.push(c.name),b},c=Object.keys(w.lookupTable.role).map(function(a){var b=w.lookupTable.role[a];return{name:a,implicit:b&&b.implicit}}),d=c.reduce(b,[]);if(!d.length)return null;for(var e=a.attributes,f=[],g=0,h=e.length;g<h;g++){var i=e[g];i.name.match(/^aria-/)&&f.push(i.name)}return function(a,b){var c=function(a){return w.allowedAttr(a).reduce(function(a,c){return a+(b.indexOf(c)>-1?1:0)},0)};return a.map(function(a){return{score:c(a),name:a}}).sort(function(a,b){return b.score-a.score}).map(function(a){return a.name})}(d,f).shift()},y.Color=function(a,b,c,d){this.red=a,this.green=b,this.blue=c,this.alpha=d,this.toHexString=function(){var a=Math.round(this.red).toString(16),b=Math.round(this.green).toString(16),c=Math.round(this.blue).toString(16);return\"#\"+(this.red>15.5?a:\"0\"+a)+(this.green>15.5?b:\"0\"+b)+(this.blue>15.5?c:\"0\"+c)};var e=/^rgb\\((\\d+), (\\d+), (\\d+)\\)$/,f=/^rgba\\((\\d+), (\\d+), (\\d+), (\\d*(\\.\\d+)?)\\)/;this.parseRgbString=function(a){if(\"transparent\"===a)return this.red=0,this.green=0,this.blue=0,void(this.alpha=0);var b=a.match(e);return b?(this.red=parseInt(b[1],10),this.green=parseInt(b[2],10),this.blue=parseInt(b[3],10),void(this.alpha=1)):(b=a.match(f),b?(this.red=parseInt(b[1],10),this.green=parseInt(b[2],10),this.blue=parseInt(b[3],10),void(this.alpha=parseFloat(b[4]))):void 0)},this.getRelativeLuminance=function(){var a=this.red/255,b=this.green/255,c=this.blue/255;return.2126*(a<=.03928?a/12.92:Math.pow((a+.055)/1.055,2.4))+.7152*(b<=.03928?b/12.92:Math.pow((b+.055)/1.055,2.4))+.0722*(c<=.03928?c/12.92:Math.pow((c+.055)/1.055,2.4))}},y.flattenColors=function(a,b){var c=a.alpha,d=(1-c)*b.red+c*a.red,e=(1-c)*b.green+c*a.green,f=(1-c)*b.blue+c*a.blue,g=a.alpha+b.alpha*(1-a.alpha);return new y.Color(d,e,f,g)},y.getContrast=function(a,b){if(!b||!a)return null;b.alpha<1&&(b=y.flattenColors(b,a));var c=a.getRelativeLuminance(),d=b.getRelativeLuminance();return(Math.max(d,c)+.05)/(Math.min(d,c)+.05)},y.hasValidContrastRatio=function(a,b,c,d){var e=y.getContrast(a,b),f=d&&Math.ceil(72*c)/96<14||!d&&Math.ceil(72*c)/96<18,g=f?4.5:3;return{isValid:e>g,contrastRatio:e,expectedContrastRatio:g}},y.elementIsDistinct=b;var C=[\"IMG\",\"CANVAS\",\"OBJECT\",\"IFRAME\",\"VIDEO\",\"SVG\"];y.getCoords=function(a){var b=void 0,c=void 0;if(!(a.left>window.innerWidth||a.top>window.innerHeight))return b=Math.min(Math.ceil(a.left+a.width/2),window.innerWidth-1),c=Math.min(Math.ceil(a.top+a.height/2),window.innerHeight-1),{x:b,y:c}},y.getRectStack=function(a){var b=y.getCoords(a.getBoundingClientRect());if(b){var c=Array.from(a.getClientRects()),d=Array.from(document.elementsFromPoint(b.x,b.y));if(c&&c.length>1){var e=c.filter(function(a){return a.width&&a.width>0}).map(function(a){var b=y.getCoords(a);if(b)return Array.from(document.elementsFromPoint(b.x,b.y))});return e.splice(0,0,d),e}return[d]}return null},y.filteredRectStack=function(a){var b=y.getRectStack(a);if(b&&1===b.length)return b[0];if(b&&b.length>1){var c=b.shift(),d=void 0;return b.forEach(function(e,f){if(0!==f){var g=b[f-1],h=b[f];d=g.every(function(a,b){return a===h[b]})||c.includes(a)}}),d?b[0]:(axe.commons.color.incompleteData.set(\"bgColor\",\"elmPartiallyObscuring\"),null)}return axe.commons.color.incompleteData.set(\"bgColor\",\"outsideViewport\"),null},y.getBackgroundStack=function(a){var b=y.filteredRectStack(a);if(null===b)return null;b=h(b,a),b=z.reduceToElementsBelowFloating(b,a),b=i(b);var c=b.indexOf(a);return f(c,b,a)>=.99?(axe.commons.color.incompleteData.set(\"bgColor\",\"bgOverlap\"),null):-1!==c?b:null},y.getBackgroundColor=function(a){var b=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];if(!0!==(arguments.length>2&&void 0!==arguments[2]&&arguments[2])){var e=a.clientHeight-2>=2*window.innerHeight;a.scrollIntoView(e)}var f=[],h=y.getBackgroundStack(a);if((h||[]).some(function(e){var h=window.getComputedStyle(e),i=d(e,h);return g(a,e,i)||c(e,h)?(f=null,b.push(e),!0):0!==i.alpha&&(b.push(e),f.push(i),1===i.alpha)}),null!==f&&null!==h){f.push(new y.Color(255,255,255,1));return f.reduce(y.flattenColors)}return null},z.isOpaque=function(a){var b=window.getComputedStyle(a);return c(a,b)||1===d(a,b).alpha},y.getForegroundColor=function(a,b){var c=window.getComputedStyle(a),d=new y.Color;d.parseRgbString(c.getPropertyValue(\"color\"));var e=c.getPropertyValue(\"opacity\");if(d.alpha=d.alpha*e,1===d.alpha)return d;var f=y.getBackgroundColor(a,[],b);if(null===f){var g=axe.commons.color.incompleteData.get(\"bgColor\");return axe.commons.color.incompleteData.set(\"fgColor\",g),null}return y.flattenColors(d,f)},y.incompleteData=function(){var a={};return{set:function(b,c){if(\"string\"!=typeof b)throw new Error(\"Incomplete data: key must be a string\");return c&&(a[b]=c),a[b]},get:function(b){return a[b]},clear:function(){a={}}}}(),z.reduceToElementsBelowFloating=function(a,b){var c,d,e,f=[\"fixed\",\"sticky\"],g=[],h=!1;for(c=0;c<a.length;++c)d=a[c],d===b&&(h=!0),e=window.getComputedStyle(d),h||-1===f.indexOf(e.position)?g.push(d):g=[];return g},z.findUp=function(a,b){\"use strict\";var c,d=document.querySelectorAll(b);if(!d.length)return null;for(d=axe.utils.toArray(d),c=a.parentNode;c&&-1===d.indexOf(c);)c=c.parentNode;return c},z.getElementByReference=function(a,b){\"use strict\";var c,d=a.getAttribute(b),e=document;if(d&&\"#\"===d.charAt(0)){if(d=d.substring(1),c=e.getElementById(d))return c;if(c=e.getElementsByName(d),c.length)return c[0]}return null},z.getElementCoordinates=function(a){\"use strict\";var b=z.getScrollOffset(document),c=b.left,d=b.top,e=a.getBoundingClientRect();return{top:e.top+d,right:e.right+c,bottom:e.bottom+d,left:e.left+c,width:e.right-e.left,height:e.bottom-e.top}},z.getScrollOffset=function(a){\"use strict\";if(!a.nodeType&&a.document&&(a=a.document),9===a.nodeType){var b=a.documentElement,c=a.body;return{left:b&&b.scrollLeft||c&&c.scrollLeft||0,top:b&&b.scrollTop||c&&c.scrollTop||0}}return{left:a.scrollLeft,top:a.scrollTop}},z.getViewportSize=function(a){\"use strict\";var b,c=a.document,d=c.documentElement;return a.innerWidth?{width:a.innerWidth,height:a.innerHeight}:d?{width:d.clientWidth,height:d.clientHeight}:(b=c.body,{width:b.clientWidth,height:b.clientHeight})},z.hasContent=function(a){var b=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];if(a.textContent.trim()||w.label(a))return!0;for(var c=a.querySelectorAll(\"*\"),d=0;d<c.length;d++)if(-1===b.indexOf(c[d])&&w.label(c[d])||z.isVisualContent(c[d]))return!0;return!1},z.idrefs=function(a,b){\"use strict\";var c,d,e=document,f=[],g=a.getAttribute(b);if(g)for(g=axe.utils.tokenList(g),c=0,d=g.length;c<d;c++)f.push(e.getElementById(g[c]));return f},z.isFocusable=function(a){\"use strict\";if(z.isNativelyFocusable(a))return!0;var b=a.getAttribute(\"tabindex\");return!(!b||isNaN(parseInt(b,10)))},z.isNativelyFocusable=function(a){\"use strict\";if(!a||a.disabled||!z.isVisible(a)&&\"AREA\"!==a.nodeName.toUpperCase())return!1;switch(a.nodeName.toUpperCase()){case\"A\":case\"AREA\":if(a.href)return!0;break;case\"INPUT\":return\"hidden\"!==a.type;case\"TEXTAREA\":case\"SELECT\":case\"DETAILS\":case\"BUTTON\":return!0}return!1},z.isHTML5=function(a){var b=a.doctype;return null!==b&&(\"html\"===b.name&&!b.publicId&&!b.systemId)};var D=[\"block\",\"list-item\",\"table\",\"flex\",\"grid\",\"inline-block\"];z.isInTextBlock=function(a){\"use strict\";if(k(a))return!1;for(var b=a.parentNode;1===b.nodeType&&!k(b);)b=b.parentNode;var c=\"\",d=\"\",e=0;return j(b,function(b){if(2===e)return!1;if(3===b.nodeType&&(c+=b.nodeValue),1===b.nodeType){var f=(b.nodeName||\"\").toUpperCase();if(-1!==[\"BR\",\"HR\"].indexOf(f))0===e?(c=\"\",d=\"\"):e=2;else{if(\"none\"===b.style.display||\"hidden\"===b.style.overflow||-1===[\"\",null,\"none\"].indexOf(b.style.float)||-1===[\"\",null,\"relative\"].indexOf(b.style.position))return!1;if(\"A\"===f&&b.href||\"link\"===(b.getAttribute(\"role\")||\"\").toLowerCase())return b===a&&(e=1),d+=b.textContent,!1}}}),c=axe.commons.text.sanitize(c),d=axe.commons.text.sanitize(d),c.length>d.length},z.isNode=function(a){\"use strict\";return a instanceof Node},z.isOffscreen=function(a){\"use strict\";var b,c=document.documentElement,d=window.getComputedStyle(document.body||c).getPropertyValue(\"direction\"),e=z.getElementCoordinates(a);if(e.bottom<0&&function(a,b){for(a=a.parentNode;\"html\"!==a.nodeName.toLowerCase();){if(a.scrollTop&&(b+=a.scrollTop)>=0)return!1;a=a.parentNode}return!0}(a,e.bottom))return!0;if(0===e.left&&0===e.right)return!1;if(\"ltr\"===d){if(e.right<=0)return!0}else if(b=Math.max(c.scrollWidth,z.getViewportSize(window).width),e.left>=b)return!0;return!1},z.isVisible=function(a,b,c){\"use strict\";var d,e=a.nodeName.toUpperCase(),f=a.parentNode;return 9===a.nodeType||null!==(d=window.getComputedStyle(a,null))&&(!(\"none\"===d.getPropertyValue(\"display\")||\"STYLE\"===e.toUpperCase()||\"SCRIPT\"===e.toUpperCase()||!b&&l(d.getPropertyValue(\"clip\"))||!c&&(\"hidden\"===d.getPropertyValue(\"visibility\")||!b&&z.isOffscreen(a))||b&&\"true\"===a.getAttribute(\"aria-hidden\"))&&(!!f&&z.isVisible(f,b,!0)))};var E=[\"checkbox\",\"img\",\"radio\",\"range\",\"slider\",\"spinbutton\",\"textbox\"];z.isVisualContent=function(a){var b=a.getAttribute(\"role\");if(b)return-1!==E.indexOf(b);switch(a.tagName.toUpperCase()){case\"IMG\":case\"IFRAME\":case\"OBJECT\":case\"VIDEO\":case\"AUDIO\":case\"CANVAS\":case\"SVG\":case\"MATH\":case\"BUTTON\":case\"SELECT\":case\"TEXTAREA\":case\"KEYGEN\":case\"PROGRESS\":case\"METER\":return!0;case\"INPUT\":return\"hidden\"!==a.type;default:return!1}},z.visuallyContains=function(a,b){var c=a.getBoundingClientRect(),d={top:c.top+.01,bottom:c.bottom-.01,left:c.left+.01,right:c.right-.01},e=b.getBoundingClientRect(),f=e.top,g=e.left,h={top:f-b.scrollTop,bottom:f-b.scrollTop+b.scrollHeight,left:g-b.scrollLeft,right:g-b.scrollLeft+b.scrollWidth},i=window.getComputedStyle(b);return\"inline\"===i.getPropertyValue(\"display\")||!(d.left<h.left&&d.left<e.left||d.top<h.top&&d.top<e.top||d.right>h.right&&d.right>e.right||d.bottom>h.bottom&&d.bottom>e.bottom)&&(!(d.right>e.right||d.bottom>e.bottom)||(\"scroll\"===i.overflow||\"auto\"===i.overflow||\"hidden\"===i.overflow||b instanceof HTMLBodyElement||b instanceof HTMLHtmlElement))},z.visuallyOverlaps=function(a,b){var c=b.getBoundingClientRect(),d=c.top,e=c.left,f={top:d-b.scrollTop,bottom:d-b.scrollTop+b.scrollHeight,left:e-b.scrollLeft,right:e-b.scrollLeft+b.scrollWidth};if(a.left>f.right&&a.left>c.right||a.top>f.bottom&&a.top>c.bottom||a.right<f.left&&a.right<c.left||a.bottom<f.top&&a.bottom<c.top)return!1;var g=window.getComputedStyle(b);return!(a.left>c.right||a.top>c.bottom)||(\"scroll\"===g.overflow||\"auto\"===g.overflow||b instanceof HTMLBodyElement||b instanceof HTMLHtmlElement)},A.getAllCells=function(a){var b,c,d,e,f=[];for(b=0,d=a.rows.length;b<d;b++)for(c=0,e=a.rows[b].cells.length;c<e;c++)f.push(a.rows[b].cells[c]);return f},A.getCellPosition=function(a,b){var c,d;for(b||(b=A.toGrid(z.findUp(a,\"table\"))),c=0;c<b.length;c++)if(b[c]&&-1!==(d=b[c].indexOf(a)))return{x:d,y:c}},A.getHeaders=function(a){if(a.hasAttribute(\"headers\"))return commons.dom.idrefs(a,\"headers\");var b=commons.table.toGrid(commons.dom.findUp(a,\"table\")),c=commons.table.getCellPosition(a,b);return[].concat(A.traverse(\"left\",c,b).filter(function(a){return A.isRowHeader(a)}),A.traverse(\"up\",c,b).filter(function(a){return A.isColumnHeader(a)})).reverse()},A.getScope=function(a){var b=a.getAttribute(\"scope\"),c=a.getAttribute(\"role\");if(a instanceof Element==!1||-1===[\"TD\",\"TH\"].indexOf(a.nodeName.toUpperCase()))throw new TypeError(\"Expected TD or TH element\");if(\"columnheader\"===c)return\"col\";if(\"rowheader\"===c)return\"row\";if(\"col\"===b||\"row\"===b)return b;if(\"TH\"!==a.nodeName.toUpperCase())return!1;var d=A.toGrid(z.findUp(a,\"table\")),e=A.getCellPosition(a);return d[e.y].reduce(function(a,b){return a&&\"TH\"===b.nodeName.toUpperCase()},!0)?\"col\":d.map(function(a){return a[e.x]}).reduce(function(a,b){return a&&\"TH\"===b.nodeName.toUpperCase()},!0)?\"row\":\"auto\"},A.isColumnHeader=function(a){return-1!==[\"col\",\"auto\"].indexOf(A.getScope(a))},A.isDataCell=function(a){return!(!a.children.length&&!a.textContent.trim())&&\"TD\"===a.nodeName.toUpperCase()},A.isDataTable=function(a){var b=a.getAttribute(\"role\");if((\"presentation\"===b||\"none\"===b)&&!z.isFocusable(a))return!1;if(\"true\"===a.getAttribute(\"contenteditable\")||z.findUp(a,'[contenteditable=\"true\"]'))return!0;if(\"grid\"===b||\"treegrid\"===b||\"table\"===b)return!0;if(\"landmark\"===commons.aria.getRoleType(b))return!0;if(\"0\"===a.getAttribute(\"datatable\"))return!1;if(a.getAttribute(\"summary\"))return!0;if(a.tHead||a.tFoot||a.caption)return!0;for(var c=0,d=a.children.length;c<d;c++)if(\"COLGROUP\"===a.children[c].nodeName.toUpperCase())return!0;for(var e,f,g=0,h=a.rows.length,i=!1,j=0;j<h;j++){e=a.rows[j];for(var k=0,l=e.cells.length;k<l;k++){if(f=e.cells[k],\"TH\"===f.nodeName.toUpperCase())return!0;if(i||f.offsetWidth===f.clientWidth&&f.offsetHeight===f.clientHeight||(i=!0),f.getAttribute(\"scope\")||f.getAttribute(\"headers\")||f.getAttribute(\"abbr\"))return!0;if(-1!==[\"columnheader\",\"rowheader\"].indexOf(f.getAttribute(\"role\")))return!0;if(1===f.children.length&&\"ABBR\"===f.children[0].nodeName.toUpperCase())return!0;g++}}if(a.getElementsByTagName(\"table\").length)return!1;if(h<2)return!1;var m=a.rows[Math.ceil(h/2)];if(1===m.cells.length&&1===m.cells[0].colSpan)return!1;if(m.cells.length>=5)return!0;if(i)return!0;var n,o;for(j=0;j<h;j++){if(e=a.rows[j],n&&n!==window.getComputedStyle(e).getPropertyValue(\"background-color\"))return!0;if(n=window.getComputedStyle(e).getPropertyValue(\"background-color\"),o&&o!==window.getComputedStyle(e).getPropertyValue(\"background-image\"))return!0;o=window.getComputedStyle(e).getPropertyValue(\"background-image\")}return h>=20||!(z.getElementCoordinates(a).width>.95*z.getViewportSize(window).width)&&(!(g<10)&&!a.querySelector(\"object, embed, iframe, applet\"))},A.isHeader=function(a){if(A.isColumnHeader(a)||A.isRowHeader(a))return!0;if(a.getAttribute(\"id\")){var b=axe.utils.escapeSelector(a.getAttribute(\"id\"));return!!document.querySelector('[headers~=\"'+b+'\"]')}return!1},A.isRowHeader=function(a){return[\"row\",\"auto\"].includes(A.getScope(a))},A.toGrid=function(a){for(var b=[],c=a.rows,d=0,e=c.length;d<e;d++){var f=c[d].cells;b[d]=b[d]||[];for(var g=0,h=0,i=f.length;h<i;h++)for(var j=0;j<f[h].colSpan;j++){for(var k=0;k<f[h].rowSpan;k++){for(b[d+k]=b[d+k]||[];b[d+k][g];)g++;b[d+k][g]=f[h]}g++}}return b},A.toArray=A.toGrid,function(a){var b=function a(b,c,d,e){var f,g=d[c.y]?d[c.y][c.x]:void 0;return g?\"function\"==typeof e&&!0===(f=e(g,c,d))?[g]:(f=a(b,{x:c.x+b.x,y:c.y+b.y},d,e),f.unshift(g),f):[]};a.traverse=function(a,c,d,e){if(Array.isArray(c)&&(e=d,d=c,c={x:0,y:0}),\"string\"==typeof a)switch(a){case\"left\":a={x:-1,y:0};break;case\"up\":a={x:0,y:-1};break;case\"right\":a={x:1,y:0};break;case\"down\":a={x:0,y:1}}return b(a,{x:c.x+a.x,y:c.y+a.y},d,e)}}(A);var F={submit:\"Submit\",reset:\"Reset\"},G=[\"text\",\"search\",\"tel\",\"url\",\"email\",\"date\",\"time\",\"number\",\"range\",\"color\"],H=[\"A\",\"EM\",\"STRONG\",\"SMALL\",\"MARK\",\"ABBR\",\"DFN\",\"I\",\"B\",\"S\",\"U\",\"CODE\",\"VAR\",\"SAMP\",\"KBD\",\"SUP\",\"SUB\",\"Q\",\"CITE\",\"SPAN\",\"BDO\",\"BDI\",\"BR\",\"WBR\",\"INS\",\"DEL\",\"IMG\",\"EMBED\",\"OBJECT\",\"IFRAME\",\"MAP\",\"AREA\",\"SCRIPT\",\"NOSCRIPT\",\"RUBY\",\"VIDEO\",\"AUDIO\",\"INPUT\",\"TEXTAREA\",\"SELECT\",\"BUTTON\",\"LABEL\",\"OUTPUT\",\"DATALIST\",\"KEYGEN\",\"PROGRESS\",\"COMMAND\",\"CANVAS\",\"TIME\",\"METER\"];B.accessibleText=function(a,b){function c(a,b,c){for(var d,e=a.childNodes,f=\"\",h=0;h<e.length;h++)d=e[h],3===d.nodeType?f+=d.textContent:1===d.nodeType&&(-1===H.indexOf(d.nodeName.toUpperCase())&&(f+=\" \"),f+=g(e[h],b,c));return f}function d(a){return axe.commons.table.isDataTable(a)||1!==axe.commons.table.getAllCells(a).length?\"\":c(a,!1,!1).trim()}function e(a,b,e){var f=\"\",h=a.nodeName.toUpperCase();if(p(a)&&(f=c(a,!1,!1)||\"\",v(f)))return f;if(\"FIGURE\"===h&&(f=s(a,\"figcaption\"),v(f)))return f;if(\"TABLE\"===h){if(f=s(a,\"caption\"),v(f))return f;if(f=a.getAttribute(\"title\")||a.getAttribute(\"summary\")||d(a)||\"\",v(f))return f}if(u(a))return a.getAttribute(\"alt\")||\"\";if(o(a)&&!e){if(n(a))return a.value||a.title||F[a.type]||\"\";var i=m(a);if(i)return g(i,b,!0)}return\"\"}function f(a,b,c){return!b&&a.hasAttribute(\"aria-labelledby\")?B.sanitize(z.idrefs(a,\"aria-labelledby\").map(function(b){return a===b&&h.pop(),g(b,!0,a!==b)}).join(\" \")):c&&t(a)||!a.hasAttribute(\"aria-label\")?\"\":B.sanitize(a.getAttribute(\"aria-label\"))}var g,h=[];return g=function(a,b,d){\"use strict\";var g;if(null===a||-1!==h.indexOf(a))return\"\";if(!b&&!z.isVisible(a,!0))return\"\";h.push(a);var i=a.getAttribute(\"role\");return g=f(a,b,d),v(g)?g:(g=e(a,b,d),v(g)?g:d&&(g=r(a),v(g))?g:q(a)||i&&-1===w.getRolesWithNameFromContents().indexOf(i)||(g=c(a,b,d),!v(g))?a.hasAttribute(\"title\")?a.getAttribute(\"title\"):\"\":g)},B.sanitize(g(a,b))},B.label=function(a){var b,c;if(c=w.label(a))return c;if(a.getAttribute(\"id\")){var d=axe.commons.utils.escapeSelector(a.getAttribute(\"id\"));if(b=document.querySelector('label[for=\"'+d+'\"]'),c=b&&B.visible(b,!0))return c}return b=z.findUp(a,\"label\"),(c=b&&B.visible(b,!0))||null},B.sanitize=function(a){\"use strict\";return a.replace(/\\r\\n/g,\"\\n\").replace(/\\u00A0/g,\" \").replace(/[\\s]{2,}/g,\" \").trim()},B.visible=function(a,b,c){\"use strict\";var d,e,f=a.childNodes,g=f.length,h=\"\";for(d=0;d<g;d++)e=f[d],3===e.nodeType?e.nodeValue&&z.isVisible(a,b)&&(h+=e.nodeValue):c||(h+=B.visible(e,b));return B.sanitize(h)},axe.utils.toArray=function(a){\"use strict\";return Array.prototype.slice.call(a)},axe.utils.tokenList=function(a){\"use strict\";return a.trim().replace(/\\s{2,}/g,\" \").split(\" \")}\n;var I=[\"aa\",\"ab\",\"ae\",\"af\",\"ak\",\"am\",\"an\",\"ar\",\"as\",\"av\",\"ay\",\"az\",\"ba\",\"be\",\"bg\",\"bh\",\"bi\",\"bm\",\"bn\",\"bo\",\"br\",\"bs\",\"ca\",\"ce\",\"ch\",\"co\",\"cr\",\"cs\",\"cu\",\"cv\",\"cy\",\"da\",\"de\",\"dv\",\"dz\",\"ee\",\"el\",\"en\",\"eo\",\"es\",\"et\",\"eu\",\"fa\",\"ff\",\"fi\",\"fj\",\"fo\",\"fr\",\"fy\",\"ga\",\"gd\",\"gl\",\"gn\",\"gu\",\"gv\",\"ha\",\"he\",\"hi\",\"ho\",\"hr\",\"ht\",\"hu\",\"hy\",\"hz\",\"ia\",\"id\",\"ie\",\"ig\",\"ii\",\"ik\",\"in\",\"io\",\"is\",\"it\",\"iu\",\"iw\",\"ja\",\"ji\",\"jv\",\"jw\",\"ka\",\"kg\",\"ki\",\"kj\",\"kk\",\"kl\",\"km\",\"kn\",\"ko\",\"kr\",\"ks\",\"ku\",\"kv\",\"kw\",\"ky\",\"la\",\"lb\",\"lg\",\"li\",\"ln\",\"lo\",\"lt\",\"lu\",\"lv\",\"mg\",\"mh\",\"mi\",\"mk\",\"ml\",\"mn\",\"mo\",\"mr\",\"ms\",\"mt\",\"my\",\"na\",\"nb\",\"nd\",\"ne\",\"ng\",\"nl\",\"nn\",\"no\",\"nr\",\"nv\",\"ny\",\"oc\",\"oj\",\"om\",\"or\",\"os\",\"pa\",\"pi\",\"pl\",\"ps\",\"pt\",\"qu\",\"rm\",\"rn\",\"ro\",\"ru\",\"rw\",\"sa\",\"sc\",\"sd\",\"se\",\"sg\",\"sh\",\"si\",\"sk\",\"sl\",\"sm\",\"sn\",\"so\",\"sq\",\"sr\",\"ss\",\"st\",\"su\",\"sv\",\"sw\",\"ta\",\"te\",\"tg\",\"th\",\"ti\",\"tk\",\"tl\",\"tn\",\"to\",\"tr\",\"ts\",\"tt\",\"tw\",\"ty\",\"ug\",\"uk\",\"ur\",\"uz\",\"ve\",\"vi\",\"vo\",\"wa\",\"wo\",\"xh\",\"yi\",\"yo\",\"za\",\"zh\",\"zu\",\"aaa\",\"aab\",\"aac\",\"aad\",\"aae\",\"aaf\",\"aag\",\"aah\",\"aai\",\"aak\",\"aal\",\"aam\",\"aan\",\"aao\",\"aap\",\"aaq\",\"aas\",\"aat\",\"aau\",\"aav\",\"aaw\",\"aax\",\"aaz\",\"aba\",\"abb\",\"abc\",\"abd\",\"abe\",\"abf\",\"abg\",\"abh\",\"abi\",\"abj\",\"abl\",\"abm\",\"abn\",\"abo\",\"abp\",\"abq\",\"abr\",\"abs\",\"abt\",\"abu\",\"abv\",\"abw\",\"abx\",\"aby\",\"abz\",\"aca\",\"acb\",\"acd\",\"ace\",\"acf\",\"ach\",\"aci\",\"ack\",\"acl\",\"acm\",\"acn\",\"acp\",\"acq\",\"acr\",\"acs\",\"act\",\"acu\",\"acv\",\"acw\",\"acx\",\"acy\",\"acz\",\"ada\",\"adb\",\"add\",\"ade\",\"adf\",\"adg\",\"adh\",\"adi\",\"adj\",\"adl\",\"adn\",\"ado\",\"adp\",\"adq\",\"adr\",\"ads\",\"adt\",\"adu\",\"adw\",\"adx\",\"ady\",\"adz\",\"aea\",\"aeb\",\"aec\",\"aed\",\"aee\",\"aek\",\"ael\",\"aem\",\"aen\",\"aeq\",\"aer\",\"aes\",\"aeu\",\"aew\",\"aey\",\"aez\",\"afa\",\"afb\",\"afd\",\"afe\",\"afg\",\"afh\",\"afi\",\"afk\",\"afn\",\"afo\",\"afp\",\"afs\",\"aft\",\"afu\",\"afz\",\"aga\",\"agb\",\"agc\",\"agd\",\"age\",\"agf\",\"agg\",\"agh\",\"agi\",\"agj\",\"agk\",\"agl\",\"agm\",\"agn\",\"ago\",\"agp\",\"agq\",\"agr\",\"ags\",\"agt\",\"agu\",\"agv\",\"agw\",\"agx\",\"agy\",\"agz\",\"aha\",\"ahb\",\"ahg\",\"ahh\",\"ahi\",\"ahk\",\"ahl\",\"ahm\",\"ahn\",\"aho\",\"ahp\",\"ahr\",\"ahs\",\"aht\",\"aia\",\"aib\",\"aic\",\"aid\",\"aie\",\"aif\",\"aig\",\"aih\",\"aii\",\"aij\",\"aik\",\"ail\",\"aim\",\"ain\",\"aio\",\"aip\",\"aiq\",\"air\",\"ais\",\"ait\",\"aiw\",\"aix\",\"aiy\",\"aja\",\"ajg\",\"aji\",\"ajn\",\"ajp\",\"ajt\",\"aju\",\"ajw\",\"ajz\",\"akb\",\"akc\",\"akd\",\"ake\",\"akf\",\"akg\",\"akh\",\"aki\",\"akj\",\"akk\",\"akl\",\"akm\",\"ako\",\"akp\",\"akq\",\"akr\",\"aks\",\"akt\",\"aku\",\"akv\",\"akw\",\"akx\",\"aky\",\"akz\",\"ala\",\"alc\",\"ald\",\"ale\",\"alf\",\"alg\",\"alh\",\"ali\",\"alj\",\"alk\",\"all\",\"alm\",\"aln\",\"alo\",\"alp\",\"alq\",\"alr\",\"als\",\"alt\",\"alu\",\"alv\",\"alw\",\"alx\",\"aly\",\"alz\",\"ama\",\"amb\",\"amc\",\"ame\",\"amf\",\"amg\",\"ami\",\"amj\",\"amk\",\"aml\",\"amm\",\"amn\",\"amo\",\"amp\",\"amq\",\"amr\",\"ams\",\"amt\",\"amu\",\"amv\",\"amw\",\"amx\",\"amy\",\"amz\",\"ana\",\"anb\",\"anc\",\"and\",\"ane\",\"anf\",\"ang\",\"anh\",\"ani\",\"anj\",\"ank\",\"anl\",\"anm\",\"ann\",\"ano\",\"anp\",\"anq\",\"anr\",\"ans\",\"ant\",\"anu\",\"anv\",\"anw\",\"anx\",\"any\",\"anz\",\"aoa\",\"aob\",\"aoc\",\"aod\",\"aoe\",\"aof\",\"aog\",\"aoh\",\"aoi\",\"aoj\",\"aok\",\"aol\",\"aom\",\"aon\",\"aor\",\"aos\",\"aot\",\"aou\",\"aox\",\"aoz\",\"apa\",\"apb\",\"apc\",\"apd\",\"ape\",\"apf\",\"apg\",\"aph\",\"api\",\"apj\",\"apk\",\"apl\",\"apm\",\"apn\",\"apo\",\"app\",\"apq\",\"apr\",\"aps\",\"apt\",\"apu\",\"apv\",\"apw\",\"apx\",\"apy\",\"apz\",\"aqa\",\"aqc\",\"aqd\",\"aqg\",\"aql\",\"aqm\",\"aqn\",\"aqp\",\"aqr\",\"aqt\",\"aqz\",\"arb\",\"arc\",\"ard\",\"are\",\"arh\",\"ari\",\"arj\",\"ark\",\"arl\",\"arn\",\"aro\",\"arp\",\"arq\",\"arr\",\"ars\",\"art\",\"aru\",\"arv\",\"arw\",\"arx\",\"ary\",\"arz\",\"asa\",\"asb\",\"asc\",\"asd\",\"ase\",\"asf\",\"asg\",\"ash\",\"asi\",\"asj\",\"ask\",\"asl\",\"asn\",\"aso\",\"asp\",\"asq\",\"asr\",\"ass\",\"ast\",\"asu\",\"asv\",\"asw\",\"asx\",\"asy\",\"asz\",\"ata\",\"atb\",\"atc\",\"atd\",\"ate\",\"atg\",\"ath\",\"ati\",\"atj\",\"atk\",\"atl\",\"atm\",\"atn\",\"ato\",\"atp\",\"atq\",\"atr\",\"ats\",\"att\",\"atu\",\"atv\",\"atw\",\"atx\",\"aty\",\"atz\",\"aua\",\"aub\",\"auc\",\"aud\",\"aue\",\"auf\",\"aug\",\"auh\",\"aui\",\"auj\",\"auk\",\"aul\",\"aum\",\"aun\",\"auo\",\"aup\",\"auq\",\"aur\",\"aus\",\"aut\",\"auu\",\"auw\",\"aux\",\"auy\",\"auz\",\"avb\",\"avd\",\"avi\",\"avk\",\"avl\",\"avm\",\"avn\",\"avo\",\"avs\",\"avt\",\"avu\",\"avv\",\"awa\",\"awb\",\"awc\",\"awd\",\"awe\",\"awg\",\"awh\",\"awi\",\"awk\",\"awm\",\"awn\",\"awo\",\"awr\",\"aws\",\"awt\",\"awu\",\"awv\",\"aww\",\"awx\",\"awy\",\"axb\",\"axe\",\"axg\",\"axk\",\"axl\",\"axm\",\"axx\",\"aya\",\"ayb\",\"ayc\",\"ayd\",\"aye\",\"ayg\",\"ayh\",\"ayi\",\"ayk\",\"ayl\",\"ayn\",\"ayo\",\"ayp\",\"ayq\",\"ayr\",\"ays\",\"ayt\",\"ayu\",\"ayx\",\"ayy\",\"ayz\",\"aza\",\"azb\",\"azc\",\"azd\",\"azg\",\"azj\",\"azm\",\"azn\",\"azo\",\"azt\",\"azz\",\"baa\",\"bab\",\"bac\",\"bad\",\"bae\",\"baf\",\"bag\",\"bah\",\"bai\",\"baj\",\"bal\",\"ban\",\"bao\",\"bap\",\"bar\",\"bas\",\"bat\",\"bau\",\"bav\",\"baw\",\"bax\",\"bay\",\"baz\",\"bba\",\"bbb\",\"bbc\",\"bbd\",\"bbe\",\"bbf\",\"bbg\",\"bbh\",\"bbi\",\"bbj\",\"bbk\",\"bbl\",\"bbm\",\"bbn\",\"bbo\",\"bbp\",\"bbq\",\"bbr\",\"bbs\",\"bbt\",\"bbu\",\"bbv\",\"bbw\",\"bbx\",\"bby\",\"bbz\",\"bca\",\"bcb\",\"bcc\",\"bcd\",\"bce\",\"bcf\",\"bcg\",\"bch\",\"bci\",\"bcj\",\"bck\",\"bcl\",\"bcm\",\"bcn\",\"bco\",\"bcp\",\"bcq\",\"bcr\",\"bcs\",\"bct\",\"bcu\",\"bcv\",\"bcw\",\"bcy\",\"bcz\",\"bda\",\"bdb\",\"bdc\",\"bdd\",\"bde\",\"bdf\",\"bdg\",\"bdh\",\"bdi\",\"bdj\",\"bdk\",\"bdl\",\"bdm\",\"bdn\",\"bdo\",\"bdp\",\"bdq\",\"bdr\",\"bds\",\"bdt\",\"bdu\",\"bdv\",\"bdw\",\"bdx\",\"bdy\",\"bdz\",\"bea\",\"beb\",\"bec\",\"bed\",\"bee\",\"bef\",\"beg\",\"beh\",\"bei\",\"bej\",\"bek\",\"bem\",\"beo\",\"bep\",\"beq\",\"ber\",\"bes\",\"bet\",\"beu\",\"bev\",\"bew\",\"bex\",\"bey\",\"bez\",\"bfa\",\"bfb\",\"bfc\",\"bfd\",\"bfe\",\"bff\",\"bfg\",\"bfh\",\"bfi\",\"bfj\",\"bfk\",\"bfl\",\"bfm\",\"bfn\",\"bfo\",\"bfp\",\"bfq\",\"bfr\",\"bfs\",\"bft\",\"bfu\",\"bfw\",\"bfx\",\"bfy\",\"bfz\",\"bga\",\"bgb\",\"bgc\",\"bgd\",\"bge\",\"bgf\",\"bgg\",\"bgi\",\"bgj\",\"bgk\",\"bgl\",\"bgm\",\"bgn\",\"bgo\",\"bgp\",\"bgq\",\"bgr\",\"bgs\",\"bgt\",\"bgu\",\"bgv\",\"bgw\",\"bgx\",\"bgy\",\"bgz\",\"bha\",\"bhb\",\"bhc\",\"bhd\",\"bhe\",\"bhf\",\"bhg\",\"bhh\",\"bhi\",\"bhj\",\"bhk\",\"bhl\",\"bhm\",\"bhn\",\"bho\",\"bhp\",\"bhq\",\"bhr\",\"bhs\",\"bht\",\"bhu\",\"bhv\",\"bhw\",\"bhx\",\"bhy\",\"bhz\",\"bia\",\"bib\",\"bic\",\"bid\",\"bie\",\"bif\",\"big\",\"bij\",\"bik\",\"bil\",\"bim\",\"bin\",\"bio\",\"bip\",\"biq\",\"bir\",\"bit\",\"biu\",\"biv\",\"biw\",\"bix\",\"biy\",\"biz\",\"bja\",\"bjb\",\"bjc\",\"bjd\",\"bje\",\"bjf\",\"bjg\",\"bjh\",\"bji\",\"bjj\",\"bjk\",\"bjl\",\"bjm\",\"bjn\",\"bjo\",\"bjp\",\"bjq\",\"bjr\",\"bjs\",\"bjt\",\"bju\",\"bjv\",\"bjw\",\"bjx\",\"bjy\",\"bjz\",\"bka\",\"bkb\",\"bkc\",\"bkd\",\"bkf\",\"bkg\",\"bkh\",\"bki\",\"bkj\",\"bkk\",\"bkl\",\"bkm\",\"bkn\",\"bko\",\"bkp\",\"bkq\",\"bkr\",\"bks\",\"bkt\",\"bku\",\"bkv\",\"bkw\",\"bkx\",\"bky\",\"bkz\",\"bla\",\"blb\",\"blc\",\"bld\",\"ble\",\"blf\",\"blg\",\"blh\",\"bli\",\"blj\",\"blk\",\"bll\",\"blm\",\"bln\",\"blo\",\"blp\",\"blq\",\"blr\",\"bls\",\"blt\",\"blv\",\"blw\",\"blx\",\"bly\",\"blz\",\"bma\",\"bmb\",\"bmc\",\"bmd\",\"bme\",\"bmf\",\"bmg\",\"bmh\",\"bmi\",\"bmj\",\"bmk\",\"bml\",\"bmm\",\"bmn\",\"bmo\",\"bmp\",\"bmq\",\"bmr\",\"bms\",\"bmt\",\"bmu\",\"bmv\",\"bmw\",\"bmx\",\"bmy\",\"bmz\",\"bna\",\"bnb\",\"bnc\",\"bnd\",\"bne\",\"bnf\",\"bng\",\"bni\",\"bnj\",\"bnk\",\"bnl\",\"bnm\",\"bnn\",\"bno\",\"bnp\",\"bnq\",\"bnr\",\"bns\",\"bnt\",\"bnu\",\"bnv\",\"bnw\",\"bnx\",\"bny\",\"bnz\",\"boa\",\"bob\",\"boe\",\"bof\",\"bog\",\"boh\",\"boi\",\"boj\",\"bok\",\"bol\",\"bom\",\"bon\",\"boo\",\"bop\",\"boq\",\"bor\",\"bot\",\"bou\",\"bov\",\"bow\",\"box\",\"boy\",\"boz\",\"bpa\",\"bpb\",\"bpd\",\"bpg\",\"bph\",\"bpi\",\"bpj\",\"bpk\",\"bpl\",\"bpm\",\"bpn\",\"bpo\",\"bpp\",\"bpq\",\"bpr\",\"bps\",\"bpt\",\"bpu\",\"bpv\",\"bpw\",\"bpx\",\"bpy\",\"bpz\",\"bqa\",\"bqb\",\"bqc\",\"bqd\",\"bqf\",\"bqg\",\"bqh\",\"bqi\",\"bqj\",\"bqk\",\"bql\",\"bqm\",\"bqn\",\"bqo\",\"bqp\",\"bqq\",\"bqr\",\"bqs\",\"bqt\",\"bqu\",\"bqv\",\"bqw\",\"bqx\",\"bqy\",\"bqz\",\"bra\",\"brb\",\"brc\",\"brd\",\"brf\",\"brg\",\"brh\",\"bri\",\"brj\",\"brk\",\"brl\",\"brm\",\"brn\",\"bro\",\"brp\",\"brq\",\"brr\",\"brs\",\"brt\",\"bru\",\"brv\",\"brw\",\"brx\",\"bry\",\"brz\",\"bsa\",\"bsb\",\"bsc\",\"bse\",\"bsf\",\"bsg\",\"bsh\",\"bsi\",\"bsj\",\"bsk\",\"bsl\",\"bsm\",\"bsn\",\"bso\",\"bsp\",\"bsq\",\"bsr\",\"bss\",\"bst\",\"bsu\",\"bsv\",\"bsw\",\"bsx\",\"bsy\",\"bta\",\"btb\",\"btc\",\"btd\",\"bte\",\"btf\",\"btg\",\"bth\",\"bti\",\"btj\",\"btk\",\"btl\",\"btm\",\"btn\",\"bto\",\"btp\",\"btq\",\"btr\",\"bts\",\"btt\",\"btu\",\"btv\",\"btw\",\"btx\",\"bty\",\"btz\",\"bua\",\"bub\",\"buc\",\"bud\",\"bue\",\"buf\",\"bug\",\"buh\",\"bui\",\"buj\",\"buk\",\"bum\",\"bun\",\"buo\",\"bup\",\"buq\",\"bus\",\"but\",\"buu\",\"buv\",\"buw\",\"bux\",\"buy\",\"buz\",\"bva\",\"bvb\",\"bvc\",\"bvd\",\"bve\",\"bvf\",\"bvg\",\"bvh\",\"bvi\",\"bvj\",\"bvk\",\"bvl\",\"bvm\",\"bvn\",\"bvo\",\"bvp\",\"bvq\",\"bvr\",\"bvt\",\"bvu\",\"bvv\",\"bvw\",\"bvx\",\"bvy\",\"bvz\",\"bwa\",\"bwb\",\"bwc\",\"bwd\",\"bwe\",\"bwf\",\"bwg\",\"bwh\",\"bwi\",\"bwj\",\"bwk\",\"bwl\",\"bwm\",\"bwn\",\"bwo\",\"bwp\",\"bwq\",\"bwr\",\"bws\",\"bwt\",\"bwu\",\"bww\",\"bwx\",\"bwy\",\"bwz\",\"bxa\",\"bxb\",\"bxc\",\"bxd\",\"bxe\",\"bxf\",\"bxg\",\"bxh\",\"bxi\",\"bxj\",\"bxk\",\"bxl\",\"bxm\",\"bxn\",\"bxo\",\"bxp\",\"bxq\",\"bxr\",\"bxs\",\"bxu\",\"bxv\",\"bxw\",\"bxx\",\"bxz\",\"bya\",\"byb\",\"byc\",\"byd\",\"bye\",\"byf\",\"byg\",\"byh\",\"byi\",\"byj\",\"byk\",\"byl\",\"bym\",\"byn\",\"byo\",\"byp\",\"byq\",\"byr\",\"bys\",\"byt\",\"byv\",\"byw\",\"byx\",\"byy\",\"byz\",\"bza\",\"bzb\",\"bzc\",\"bzd\",\"bze\",\"bzf\",\"bzg\",\"bzh\",\"bzi\",\"bzj\",\"bzk\",\"bzl\",\"bzm\",\"bzn\",\"bzo\",\"bzp\",\"bzq\",\"bzr\",\"bzs\",\"bzt\",\"bzu\",\"bzv\",\"bzw\",\"bzx\",\"bzy\",\"bzz\",\"caa\",\"cab\",\"cac\",\"cad\",\"cae\",\"caf\",\"cag\",\"cah\",\"cai\",\"caj\",\"cak\",\"cal\",\"cam\",\"can\",\"cao\",\"cap\",\"caq\",\"car\",\"cas\",\"cau\",\"cav\",\"caw\",\"cax\",\"cay\",\"caz\",\"cba\",\"cbb\",\"cbc\",\"cbd\",\"cbe\",\"cbg\",\"cbh\",\"cbi\",\"cbj\",\"cbk\",\"cbl\",\"cbn\",\"cbo\",\"cbq\",\"cbr\",\"cbs\",\"cbt\",\"cbu\",\"cbv\",\"cbw\",\"cby\",\"cca\",\"ccc\",\"ccd\",\"cce\",\"ccg\",\"cch\",\"ccj\",\"ccl\",\"ccm\",\"ccn\",\"cco\",\"ccp\",\"ccq\",\"ccr\",\"ccs\",\"cda\",\"cdc\",\"cdd\",\"cde\",\"cdf\",\"cdg\",\"cdh\",\"cdi\",\"cdj\",\"cdm\",\"cdn\",\"cdo\",\"cdr\",\"cds\",\"cdy\",\"cdz\",\"cea\",\"ceb\",\"ceg\",\"cek\",\"cel\",\"cen\",\"cet\",\"cfa\",\"cfd\",\"cfg\",\"cfm\",\"cga\",\"cgc\",\"cgg\",\"cgk\",\"chb\",\"chc\",\"chd\",\"chf\",\"chg\",\"chh\",\"chj\",\"chk\",\"chl\",\"chm\",\"chn\",\"cho\",\"chp\",\"chq\",\"chr\",\"cht\",\"chw\",\"chx\",\"chy\",\"chz\",\"cia\",\"cib\",\"cic\",\"cid\",\"cie\",\"cih\",\"cik\",\"cim\",\"cin\",\"cip\",\"cir\",\"ciw\",\"ciy\",\"cja\",\"cje\",\"cjh\",\"cji\",\"cjk\",\"cjm\",\"cjn\",\"cjo\",\"cjp\",\"cjr\",\"cjs\",\"cjv\",\"cjy\",\"cka\",\"ckb\",\"ckh\",\"ckl\",\"ckn\",\"cko\",\"ckq\",\"ckr\",\"cks\",\"ckt\",\"cku\",\"ckv\",\"ckx\",\"cky\",\"ckz\",\"cla\",\"clc\",\"cld\",\"cle\",\"clh\",\"cli\",\"clj\",\"clk\",\"cll\",\"clm\",\"clo\",\"clt\",\"clu\",\"clw\",\"cly\",\"cma\",\"cmc\",\"cme\",\"cmg\",\"cmi\",\"cmk\",\"cml\",\"cmm\",\"cmn\",\"cmo\",\"cmr\",\"cms\",\"cmt\",\"cna\",\"cnb\",\"cnc\",\"cng\",\"cnh\",\"cni\",\"cnk\",\"cnl\",\"cno\",\"cns\",\"cnt\",\"cnu\",\"cnw\",\"cnx\",\"coa\",\"cob\",\"coc\",\"cod\",\"coe\",\"cof\",\"cog\",\"coh\",\"coj\",\"cok\",\"col\",\"com\",\"con\",\"coo\",\"cop\",\"coq\",\"cot\",\"cou\",\"cov\",\"cow\",\"cox\",\"coy\",\"coz\",\"cpa\",\"cpb\",\"cpc\",\"cpe\",\"cpf\",\"cpg\",\"cpi\",\"cpn\",\"cpo\",\"cpp\",\"cps\",\"cpu\",\"cpx\",\"cpy\",\"cqd\",\"cqu\",\"cra\",\"crb\",\"crc\",\"crd\",\"crf\",\"crg\",\"crh\",\"cri\",\"crj\",\"crk\",\"crl\",\"crm\",\"crn\",\"cro\",\"crp\",\"crq\",\"crr\",\"crs\",\"crt\",\"crv\",\"crw\",\"crx\",\"cry\",\"crz\",\"csa\",\"csb\",\"csc\",\"csd\",\"cse\",\"csf\",\"csg\",\"csh\",\"csi\",\"csj\",\"csk\",\"csl\",\"csm\",\"csn\",\"cso\",\"csq\",\"csr\",\"css\",\"cst\",\"csu\",\"csv\",\"csw\",\"csy\",\"csz\",\"cta\",\"ctc\",\"ctd\",\"cte\",\"ctg\",\"cth\",\"ctl\",\"ctm\",\"ctn\",\"cto\",\"ctp\",\"cts\",\"ctt\",\"ctu\",\"ctz\",\"cua\",\"cub\",\"cuc\",\"cug\",\"cuh\",\"cui\",\"cuj\",\"cuk\",\"cul\",\"cum\",\"cuo\",\"cup\",\"cuq\",\"cur\",\"cus\",\"cut\",\"cuu\",\"cuv\",\"cuw\",\"cux\",\"cvg\",\"cvn\",\"cwa\",\"cwb\",\"cwd\",\"cwe\",\"cwg\",\"cwt\",\"cya\",\"cyb\",\"cyo\",\"czh\",\"czk\",\"czn\",\"czo\",\"czt\",\"daa\",\"dac\",\"dad\",\"dae\",\"daf\",\"dag\",\"dah\",\"dai\",\"daj\",\"dak\",\"dal\",\"dam\",\"dao\",\"dap\",\"daq\",\"dar\",\"das\",\"dau\",\"dav\",\"daw\",\"dax\",\"day\",\"daz\",\"dba\",\"dbb\",\"dbd\",\"dbe\",\"dbf\",\"dbg\",\"dbi\",\"dbj\",\"dbl\",\"dbm\",\"dbn\",\"dbo\",\"dbp\",\"dbq\",\"dbr\",\"dbt\",\"dbu\",\"dbv\",\"dbw\",\"dby\",\"dcc\",\"dcr\",\"dda\",\"ddd\",\"dde\",\"ddg\",\"ddi\",\"ddj\",\"ddn\",\"ddo\",\"ddr\",\"dds\",\"ddw\",\"dec\",\"ded\",\"dee\",\"def\",\"deg\",\"deh\",\"dei\",\"dek\",\"del\",\"dem\",\"den\",\"dep\",\"deq\",\"der\",\"des\",\"dev\",\"dez\",\"dga\",\"dgb\",\"dgc\",\"dgd\",\"dge\",\"dgg\",\"dgh\",\"dgi\",\"dgk\",\"dgl\",\"dgn\",\"dgo\",\"dgr\",\"dgs\",\"dgt\",\"dgu\",\"dgw\",\"dgx\",\"dgz\",\"dha\",\"dhd\",\"dhg\",\"dhi\",\"dhl\",\"dhm\",\"dhn\",\"dho\",\"dhr\",\"dhs\",\"dhu\",\"dhv\",\"dhw\",\"dhx\",\"dia\",\"dib\",\"dic\",\"did\",\"dif\",\"dig\",\"dih\",\"dii\",\"dij\",\"dik\",\"dil\",\"dim\",\"din\",\"dio\",\"dip\",\"diq\",\"dir\",\"dis\",\"dit\",\"diu\",\"diw\",\"dix\",\"diy\",\"diz\",\"dja\",\"djb\",\"djc\",\"djd\",\"dje\",\"djf\",\"dji\",\"djj\",\"djk\",\"djl\",\"djm\",\"djn\",\"djo\",\"djr\",\"dju\",\"djw\",\"dka\",\"dkk\",\"dkl\",\"dkr\",\"dks\",\"dkx\",\"dlg\",\"dlk\",\"dlm\",\"dln\",\"dma\",\"dmb\",\"dmc\",\"dmd\",\"dme\",\"dmg\",\"dmk\",\"dml\",\"dmm\",\"dmn\",\"dmo\",\"dmr\",\"dms\",\"dmu\",\"dmv\",\"dmw\",\"dmx\",\"dmy\",\"dna\",\"dnd\",\"dne\",\"dng\",\"dni\",\"dnj\",\"dnk\",\"dnn\",\"dnr\",\"dnt\",\"dnu\",\"dnv\",\"dnw\",\"dny\",\"doa\",\"dob\",\"doc\",\"doe\",\"dof\",\"doh\",\"doi\",\"dok\",\"dol\",\"don\",\"doo\",\"dop\",\"doq\",\"dor\",\"dos\",\"dot\",\"dov\",\"dow\",\"dox\",\"doy\",\"doz\",\"dpp\",\"dra\",\"drb\",\"drc\",\"drd\",\"dre\",\"drg\",\"drh\",\"dri\",\"drl\",\"drn\",\"dro\",\"drq\",\"drr\",\"drs\",\"drt\",\"dru\",\"drw\",\"dry\",\"dsb\",\"dse\",\"dsh\",\"dsi\",\"dsl\",\"dsn\",\"dso\",\"dsq\",\"dta\",\"dtb\",\"dtd\",\"dth\",\"dti\",\"dtk\",\"dtm\",\"dtn\",\"dto\",\"dtp\",\"dtr\",\"dts\",\"dtt\",\"dtu\",\"dty\",\"dua\",\"dub\",\"duc\",\"dud\",\"due\",\"duf\",\"dug\",\"duh\",\"dui\",\"duj\",\"duk\",\"dul\",\"dum\",\"dun\",\"duo\",\"dup\",\"duq\",\"dur\",\"dus\",\"duu\",\"duv\",\"duw\",\"dux\",\"duy\",\"duz\",\"dva\",\"dwa\",\"dwl\",\"dwr\",\"dws\",\"dwu\",\"dww\",\"dwy\",\"dya\",\"dyb\",\"dyd\",\"dyg\",\"dyi\",\"dym\",\"dyn\",\"dyo\",\"dyu\",\"dyy\",\"dza\",\"dzd\",\"dze\",\"dzg\",\"dzl\",\"dzn\",\"eaa\",\"ebg\",\"ebk\",\"ebo\",\"ebr\",\"ebu\",\"ecr\",\"ecs\",\"ecy\",\"eee\",\"efa\",\"efe\",\"efi\",\"ega\",\"egl\",\"ego\",\"egx\",\"egy\",\"ehu\",\"eip\",\"eit\",\"eiv\",\"eja\",\"eka\",\"ekc\",\"eke\",\"ekg\",\"eki\",\"ekk\",\"ekl\",\"ekm\",\"eko\",\"ekp\",\"ekr\",\"eky\",\"ele\",\"elh\",\"eli\",\"elk\",\"elm\",\"elo\",\"elp\",\"elu\",\"elx\",\"ema\",\"emb\",\"eme\",\"emg\",\"emi\",\"emk\",\"emm\",\"emn\",\"emo\",\"emp\",\"ems\",\"emu\",\"emw\",\"emx\",\"emy\",\"ena\",\"enb\",\"enc\",\"end\",\"enf\",\"enh\",\"enl\",\"enm\",\"enn\",\"eno\",\"enq\",\"enr\",\"enu\",\"env\",\"enw\",\"enx\",\"eot\",\"epi\",\"era\",\"erg\",\"erh\",\"eri\",\"erk\",\"ero\",\"err\",\"ers\",\"ert\",\"erw\",\"ese\",\"esg\",\"esh\",\"esi\",\"esk\",\"esl\",\"esm\",\"esn\",\"eso\",\"esq\",\"ess\",\"esu\",\"esx\",\"esy\",\"etb\",\"etc\",\"eth\",\"etn\",\"eto\",\"etr\",\"ets\",\"ett\",\"etu\",\"etx\",\"etz\",\"euq\",\"eve\",\"evh\",\"evn\",\"ewo\",\"ext\",\"eya\",\"eyo\",\"eza\",\"eze\",\"faa\",\"fab\",\"fad\",\"faf\",\"fag\",\"fah\",\"fai\",\"faj\",\"fak\",\"fal\",\"fam\",\"fan\",\"fap\",\"far\",\"fat\",\"fau\",\"fax\",\"fay\",\"faz\",\"fbl\",\"fcs\",\"fer\",\"ffi\",\"ffm\",\"fgr\",\"fia\",\"fie\",\"fil\",\"fip\",\"fir\",\"fit\",\"fiu\",\"fiw\",\"fkk\",\"fkv\",\"fla\",\"flh\",\"fli\",\"fll\",\"fln\",\"flr\",\"fly\",\"fmp\",\"fmu\",\"fnb\",\"fng\",\"fni\",\"fod\",\"foi\",\"fom\",\"fon\",\"for\",\"fos\",\"fox\",\"fpe\",\"fqs\",\"frc\",\"frd\",\"frk\",\"frm\",\"fro\",\"frp\",\"frq\",\"frr\",\"frs\",\"frt\",\"fse\",\"fsl\",\"fss\",\"fub\",\"fuc\",\"fud\",\"fue\",\"fuf\",\"fuh\",\"fui\",\"fuj\",\"fum\",\"fun\",\"fuq\",\"fur\",\"fut\",\"fuu\",\"fuv\",\"fuy\",\"fvr\",\"fwa\",\"fwe\",\"gaa\",\"gab\",\"gac\",\"gad\",\"gae\",\"gaf\",\"gag\",\"gah\",\"gai\",\"gaj\",\"gak\",\"gal\",\"gam\",\"gan\",\"gao\",\"gap\",\"gaq\",\"gar\",\"gas\",\"gat\",\"gau\",\"gav\",\"gaw\",\"gax\",\"gay\",\"gaz\",\"gba\",\"gbb\",\"gbc\",\"gbd\",\"gbe\",\"gbf\",\"gbg\",\"gbh\",\"gbi\",\"gbj\",\"gbk\",\"gbl\",\"gbm\",\"gbn\",\"gbo\",\"gbp\",\"gbq\",\"gbr\",\"gbs\",\"gbu\",\"gbv\",\"gbw\",\"gbx\",\"gby\",\"gbz\",\"gcc\",\"gcd\",\"gce\",\"gcf\",\"gcl\",\"gcn\",\"gcr\",\"gct\",\"gda\",\"gdb\",\"gdc\",\"gdd\",\"gde\",\"gdf\",\"gdg\",\"gdh\",\"gdi\",\"gdj\",\"gdk\",\"gdl\",\"gdm\",\"gdn\",\"gdo\",\"gdq\",\"gdr\",\"gds\",\"gdt\",\"gdu\",\"gdx\",\"gea\",\"geb\",\"gec\",\"ged\",\"geg\",\"geh\",\"gei\",\"gej\",\"gek\",\"gel\",\"gem\",\"geq\",\"ges\",\"gev\",\"gew\",\"gex\",\"gey\",\"gez\",\"gfk\",\"gft\",\"gfx\",\"gga\",\"ggb\",\"ggd\",\"gge\",\"ggg\",\"ggk\",\"ggl\",\"ggn\",\"ggo\",\"ggr\",\"ggt\",\"ggu\",\"ggw\",\"gha\",\"ghc\",\"ghe\",\"ghh\",\"ghk\",\"ghl\",\"ghn\",\"gho\",\"ghr\",\"ghs\",\"ght\",\"gia\",\"gib\",\"gic\",\"gid\",\"gie\",\"gig\",\"gih\",\"gil\",\"gim\",\"gin\",\"gio\",\"gip\",\"giq\",\"gir\",\"gis\",\"git\",\"giu\",\"giw\",\"gix\",\"giy\",\"giz\",\"gji\",\"gjk\",\"gjm\",\"gjn\",\"gjr\",\"gju\",\"gka\",\"gke\",\"gkn\",\"gko\",\"gkp\",\"gku\",\"glc\",\"gld\",\"glh\",\"gli\",\"glj\",\"glk\",\"gll\",\"glo\",\"glr\",\"glu\",\"glw\",\"gly\",\"gma\",\"gmb\",\"gmd\",\"gme\",\"gmg\",\"gmh\",\"gml\",\"gmm\",\"gmn\",\"gmq\",\"gmu\",\"gmv\",\"gmw\",\"gmx\",\"gmy\",\"gmz\",\"gna\",\"gnb\",\"gnc\",\"gnd\",\"gne\",\"gng\",\"gnh\",\"gni\",\"gnk\",\"gnl\",\"gnm\",\"gnn\",\"gno\",\"gnq\",\"gnr\",\"gnt\",\"gnu\",\"gnw\",\"gnz\",\"goa\",\"gob\",\"goc\",\"god\",\"goe\",\"gof\",\"gog\",\"goh\",\"goi\",\"goj\",\"gok\",\"gol\",\"gom\",\"gon\",\"goo\",\"gop\",\"goq\",\"gor\",\"gos\",\"got\",\"gou\",\"gow\",\"gox\",\"goy\",\"goz\",\"gpa\",\"gpe\",\"gpn\",\"gqa\",\"gqi\",\"gqn\",\"gqr\",\"gqu\",\"gra\",\"grb\",\"grc\",\"grd\",\"grg\",\"grh\",\"gri\",\"grj\",\"grk\",\"grm\",\"gro\",\"grq\",\"grr\",\"grs\",\"grt\",\"gru\",\"grv\",\"grw\",\"grx\",\"gry\",\"grz\",\"gse\",\"gsg\",\"gsl\",\"gsm\",\"gsn\",\"gso\",\"gsp\",\"gss\",\"gsw\",\"gta\",\"gti\",\"gtu\",\"gua\",\"gub\",\"guc\",\"gud\",\"gue\",\"guf\",\"gug\",\"guh\",\"gui\",\"guk\",\"gul\",\"gum\",\"gun\",\"guo\",\"gup\",\"guq\",\"gur\",\"gus\",\"gut\",\"guu\",\"guv\",\"guw\",\"gux\",\"guz\",\"gva\",\"gvc\",\"gve\",\"gvf\",\"gvj\",\"gvl\",\"gvm\",\"gvn\",\"gvo\",\"gvp\",\"gvr\",\"gvs\",\"gvy\",\"gwa\",\"gwb\",\"gwc\",\"gwd\",\"gwe\",\"gwf\",\"gwg\",\"gwi\",\"gwj\",\"gwm\",\"gwn\",\"gwr\",\"gwt\",\"gwu\",\"gww\",\"gwx\",\"gxx\",\"gya\",\"gyb\",\"gyd\",\"gye\",\"gyf\",\"gyg\",\"gyi\",\"gyl\",\"gym\",\"gyn\",\"gyr\",\"gyy\",\"gza\",\"gzi\",\"gzn\",\"haa\",\"hab\",\"hac\",\"had\",\"hae\",\"haf\",\"hag\",\"hah\",\"hai\",\"haj\",\"hak\",\"hal\",\"ham\",\"han\",\"hao\",\"hap\",\"haq\",\"har\",\"has\",\"hav\",\"haw\",\"hax\",\"hay\",\"haz\",\"hba\",\"hbb\",\"hbn\",\"hbo\",\"hbu\",\"hca\",\"hch\",\"hdn\",\"hds\",\"hdy\",\"hea\",\"hed\",\"heg\",\"heh\",\"hei\",\"hem\",\"hgm\",\"hgw\",\"hhi\",\"hhr\",\"hhy\",\"hia\",\"hib\",\"hid\",\"hif\",\"hig\",\"hih\",\"hii\",\"hij\",\"hik\",\"hil\",\"him\",\"hio\",\"hir\",\"hit\",\"hiw\",\"hix\",\"hji\",\"hka\",\"hke\",\"hkk\",\"hks\",\"hla\",\"hlb\",\"hld\",\"hle\",\"hlt\",\"hlu\",\"hma\",\"hmb\",\"hmc\",\"hmd\",\"hme\",\"hmf\",\"hmg\",\"hmh\",\"hmi\",\"hmj\",\"hmk\",\"hml\",\"hmm\",\"hmn\",\"hmp\",\"hmq\",\"hmr\",\"hms\",\"hmt\",\"hmu\",\"hmv\",\"hmw\",\"hmx\",\"hmy\",\"hmz\",\"hna\",\"hnd\",\"hne\",\"hnh\",\"hni\",\"hnj\",\"hnn\",\"hno\",\"hns\",\"hnu\",\"hoa\",\"hob\",\"hoc\",\"hod\",\"hoe\",\"hoh\",\"hoi\",\"hoj\",\"hok\",\"hol\",\"hom\",\"hoo\",\"hop\",\"hor\",\"hos\",\"hot\",\"hov\",\"how\",\"hoy\",\"hoz\",\"hpo\",\"hps\",\"hra\",\"hrc\",\"hre\",\"hrk\",\"hrm\",\"hro\",\"hrp\",\"hrr\",\"hrt\",\"hru\",\"hrw\",\"hrx\",\"hrz\",\"hsb\",\"hsh\",\"hsl\",\"hsn\",\"hss\",\"hti\",\"hto\",\"hts\",\"htu\",\"htx\",\"hub\",\"huc\",\"hud\",\"hue\",\"huf\",\"hug\",\"huh\",\"hui\",\"huj\",\"huk\",\"hul\",\"hum\",\"huo\",\"hup\",\"huq\",\"hur\",\"hus\",\"hut\",\"huu\",\"huv\",\"huw\",\"hux\",\"huy\",\"huz\",\"hvc\",\"hve\",\"hvk\",\"hvn\",\"hvv\",\"hwa\",\"hwc\",\"hwo\",\"hya\",\"hyx\",\"iai\",\"ian\",\"iap\",\"iar\",\"iba\",\"ibb\",\"ibd\",\"ibe\",\"ibg\",\"ibh\",\"ibi\",\"ibl\",\"ibm\",\"ibn\",\"ibr\",\"ibu\",\"iby\",\"ica\",\"ich\",\"icl\",\"icr\",\"ida\",\"idb\",\"idc\",\"idd\",\"ide\",\"idi\",\"idr\",\"ids\",\"idt\",\"idu\",\"ifa\",\"ifb\",\"ife\",\"iff\",\"ifk\",\"ifm\",\"ifu\",\"ify\",\"igb\",\"ige\",\"igg\",\"igl\",\"igm\",\"ign\",\"igo\",\"igs\",\"igw\",\"ihb\",\"ihi\",\"ihp\",\"ihw\",\"iin\",\"iir\",\"ijc\",\"ije\",\"ijj\",\"ijn\",\"ijo\",\"ijs\",\"ike\",\"iki\",\"ikk\",\"ikl\",\"iko\",\"ikp\",\"ikr\",\"iks\",\"ikt\",\"ikv\",\"ikw\",\"ikx\",\"ikz\",\"ila\",\"ilb\",\"ilg\",\"ili\",\"ilk\",\"ill\",\"ilm\",\"ilo\",\"ilp\",\"ils\",\"ilu\",\"ilv\",\"ilw\",\"ima\",\"ime\",\"imi\",\"iml\",\"imn\",\"imo\",\"imr\",\"ims\",\"imy\",\"inb\",\"inc\",\"ine\",\"ing\",\"inh\",\"inj\",\"inl\",\"inm\",\"inn\",\"ino\",\"inp\",\"ins\",\"int\",\"inz\",\"ior\",\"iou\",\"iow\",\"ipi\",\"ipo\",\"iqu\",\"iqw\",\"ira\",\"ire\",\"irh\",\"iri\",\"irk\",\"irn\",\"iro\",\"irr\",\"iru\",\"irx\",\"iry\",\"isa\",\"isc\",\"isd\",\"ise\",\"isg\",\"ish\",\"isi\",\"isk\",\"ism\",\"isn\",\"iso\",\"isr\",\"ist\",\"isu\",\"itb\",\"itc\",\"itd\",\"ite\",\"iti\",\"itk\",\"itl\",\"itm\",\"ito\",\"itr\",\"its\",\"itt\",\"itv\",\"itw\",\"itx\",\"ity\",\"itz\",\"ium\",\"ivb\",\"ivv\",\"iwk\",\"iwm\",\"iwo\",\"iws\",\"ixc\",\"ixl\",\"iya\",\"iyo\",\"iyx\",\"izh\",\"izi\",\"izr\",\"izz\",\"jaa\",\"jab\",\"jac\",\"jad\",\"jae\",\"jaf\",\"jah\",\"jaj\",\"jak\",\"jal\",\"jam\",\"jan\",\"jao\",\"jaq\",\"jar\",\"jas\",\"jat\",\"jau\",\"jax\",\"jay\",\"jaz\",\"jbe\",\"jbi\",\"jbj\",\"jbk\",\"jbn\",\"jbo\",\"jbr\",\"jbt\",\"jbu\",\"jbw\",\"jcs\",\"jct\",\"jda\",\"jdg\",\"jdt\",\"jeb\",\"jee\",\"jeg\",\"jeh\",\"jei\",\"jek\",\"jel\",\"jen\",\"jer\",\"jet\",\"jeu\",\"jgb\",\"jge\",\"jgk\",\"jgo\",\"jhi\",\"jhs\",\"jia\",\"jib\",\"jic\",\"jid\",\"jie\",\"jig\",\"jih\",\"jii\",\"jil\",\"jim\",\"jio\",\"jiq\",\"jit\",\"jiu\",\"jiv\",\"jiy\",\"jje\",\"jjr\",\"jka\",\"jkm\",\"jko\",\"jkp\",\"jkr\",\"jku\",\"jle\",\"jls\",\"jma\",\"jmb\",\"jmc\",\"jmd\",\"jmi\",\"jml\",\"jmn\",\"jmr\",\"jms\",\"jmw\",\"jmx\",\"jna\",\"jnd\",\"jng\",\"jni\",\"jnj\",\"jnl\",\"jns\",\"job\",\"jod\",\"jog\",\"jor\",\"jos\",\"jow\",\"jpa\",\"jpr\",\"jpx\",\"jqr\",\"jra\",\"jrb\",\"jrr\",\"jrt\",\"jru\",\"jsl\",\"jua\",\"jub\",\"juc\",\"jud\",\"juh\",\"jui\",\"juk\",\"jul\",\"jum\",\"jun\",\"juo\",\"jup\",\"jur\",\"jus\",\"jut\",\"juu\",\"juw\",\"juy\",\"jvd\",\"jvn\",\"jwi\",\"jya\",\"jye\",\"jyy\",\"kaa\",\"kab\",\"kac\",\"kad\",\"kae\",\"kaf\",\"kag\",\"kah\",\"kai\",\"kaj\",\"kak\",\"kam\",\"kao\",\"kap\",\"kaq\",\"kar\",\"kav\",\"kaw\",\"kax\",\"kay\",\"kba\",\"kbb\",\"kbc\",\"kbd\",\"kbe\",\"kbf\",\"kbg\",\"kbh\",\"kbi\",\"kbj\",\"kbk\",\"kbl\",\"kbm\",\"kbn\",\"kbo\",\"kbp\",\"kbq\",\"kbr\",\"kbs\",\"kbt\",\"kbu\",\"kbv\",\"kbw\",\"kbx\",\"kby\",\"kbz\",\"kca\",\"kcb\",\"kcc\",\"kcd\",\"kce\",\"kcf\",\"kcg\",\"kch\",\"kci\",\"kcj\",\"kck\",\"kcl\",\"kcm\",\"kcn\",\"kco\",\"kcp\",\"kcq\",\"kcr\",\"kcs\",\"kct\",\"kcu\",\"kcv\",\"kcw\",\"kcx\",\"kcy\",\"kcz\",\"kda\",\"kdc\",\"kdd\",\"kde\",\"kdf\",\"kdg\",\"kdh\",\"kdi\",\"kdj\",\"kdk\",\"kdl\",\"kdm\",\"kdn\",\"kdo\",\"kdp\",\"kdq\",\"kdr\",\"kdt\",\"kdu\",\"kdv\",\"kdw\",\"kdx\",\"kdy\",\"kdz\",\"kea\",\"keb\",\"kec\",\"ked\",\"kee\",\"kef\",\"keg\",\"keh\",\"kei\",\"kej\",\"kek\",\"kel\",\"kem\",\"ken\",\"keo\",\"kep\",\"keq\",\"ker\",\"kes\",\"ket\",\"keu\",\"kev\",\"kew\",\"kex\",\"key\",\"kez\",\"kfa\",\"kfb\",\"kfc\",\"kfd\",\"kfe\",\"kff\",\"kfg\",\"kfh\",\"kfi\",\"kfj\",\"kfk\",\"kfl\",\"kfm\",\"kfn\",\"kfo\",\"kfp\",\"kfq\",\"kfr\",\"kfs\",\"kft\",\"kfu\",\"kfv\",\"kfw\",\"kfx\",\"kfy\",\"kfz\",\"kga\",\"kgb\",\"kgc\",\"kgd\",\"kge\",\"kgf\",\"kgg\",\"kgh\",\"kgi\",\"kgj\",\"kgk\",\"kgl\",\"kgm\",\"kgn\",\"kgo\",\"kgp\",\"kgq\",\"kgr\",\"kgs\",\"kgt\",\"kgu\",\"kgv\",\"kgw\",\"kgx\",\"kgy\",\"kha\",\"khb\",\"khc\",\"khd\",\"khe\",\"khf\",\"khg\",\"khh\",\"khi\",\"khj\",\"khk\",\"khl\",\"khn\",\"kho\",\"khp\",\"khq\",\"khr\",\"khs\",\"kht\",\"khu\",\"khv\",\"khw\",\"khx\",\"khy\",\"khz\",\"kia\",\"kib\",\"kic\",\"kid\",\"kie\",\"kif\",\"kig\",\"kih\",\"kii\",\"kij\",\"kil\",\"kim\",\"kio\",\"kip\",\"kiq\",\"kis\",\"kit\",\"kiu\",\"kiv\",\"kiw\",\"kix\",\"kiy\",\"kiz\",\"kja\",\"kjb\",\"kjc\",\"kjd\",\"kje\",\"kjf\",\"kjg\",\"kjh\",\"kji\",\"kjj\",\"kjk\",\"kjl\",\"kjm\",\"kjn\",\"kjo\",\"kjp\",\"kjq\",\"kjr\",\"kjs\",\"kjt\",\"kju\",\"kjv\",\"kjx\",\"kjy\",\"kjz\",\"kka\",\"kkb\",\"kkc\",\"kkd\",\"kke\",\"kkf\",\"kkg\",\"kkh\",\"kki\",\"kkj\",\"kkk\",\"kkl\",\"kkm\",\"kkn\",\"kko\",\"kkp\",\"kkq\",\"kkr\",\"kks\",\"kkt\",\"kku\",\"kkv\",\"kkw\",\"kkx\",\"kky\",\"kkz\",\"kla\",\"klb\",\"klc\",\"kld\",\"kle\",\"klf\",\"klg\",\"klh\",\"kli\",\"klj\",\"klk\",\"kll\",\"klm\",\"kln\",\"klo\",\"klp\",\"klq\",\"klr\",\"kls\",\"klt\",\"klu\",\"klv\",\"klw\",\"klx\",\"kly\",\"klz\",\"kma\",\"kmb\",\"kmc\",\"kmd\",\"kme\",\"kmf\",\"kmg\",\"kmh\",\"kmi\",\"kmj\",\"kmk\",\"kml\",\"kmm\",\"kmn\",\"kmo\",\"kmp\",\"kmq\",\"kmr\",\"kms\",\"kmt\",\"kmu\",\"kmv\",\"kmw\",\"kmx\",\"kmy\",\"kmz\",\"kna\",\"knb\",\"knc\",\"knd\",\"kne\",\"knf\",\"kng\",\"kni\",\"knj\",\"knk\",\"knl\",\"knm\",\"knn\",\"kno\",\"knp\",\"knq\",\"knr\",\"kns\",\"knt\",\"knu\",\"knv\",\"knw\",\"knx\",\"kny\",\"knz\",\"koa\",\"koc\",\"kod\",\"koe\",\"kof\",\"kog\",\"koh\",\"koi\",\"koj\",\"kok\",\"kol\",\"koo\",\"kop\",\"koq\",\"kos\",\"kot\",\"kou\",\"kov\",\"kow\",\"kox\",\"koy\",\"koz\",\"kpa\",\"kpb\",\"kpc\",\"kpd\",\"kpe\",\"kpf\",\"kpg\",\"kph\",\"kpi\",\"kpj\",\"kpk\",\"kpl\",\"kpm\",\"kpn\",\"kpo\",\"kpp\",\"kpq\",\"kpr\",\"kps\",\"kpt\",\"kpu\",\"kpv\",\"kpw\",\"kpx\",\"kpy\",\"kpz\",\"kqa\",\"kqb\",\"kqc\",\"kqd\",\"kqe\",\"kqf\",\"kqg\",\"kqh\",\"kqi\",\"kqj\",\"kqk\",\"kql\",\"kqm\",\"kqn\",\"kqo\",\"kqp\",\"kqq\",\"kqr\",\"kqs\",\"kqt\",\"kqu\",\"kqv\",\"kqw\",\"kqx\",\"kqy\",\"kqz\",\"kra\",\"krb\",\"krc\",\"krd\",\"kre\",\"krf\",\"krh\",\"kri\",\"krj\",\"krk\",\"krl\",\"krm\",\"krn\",\"kro\",\"krp\",\"krr\",\"krs\",\"krt\",\"kru\",\"krv\",\"krw\",\"krx\",\"kry\",\"krz\",\"ksa\",\"ksb\",\"ksc\",\"ksd\",\"kse\",\"ksf\",\"ksg\",\"ksh\",\"ksi\",\"ksj\",\"ksk\",\"ksl\",\"ksm\",\"ksn\",\"kso\",\"ksp\",\"ksq\",\"ksr\",\"kss\",\"kst\",\"ksu\",\"ksv\",\"ksw\",\"ksx\",\"ksy\",\"ksz\",\"kta\",\"ktb\",\"ktc\",\"ktd\",\"kte\",\"ktf\",\"ktg\",\"kth\",\"kti\",\"ktj\",\"ktk\",\"ktl\",\"ktm\",\"ktn\",\"kto\",\"ktp\",\"ktq\",\"ktr\",\"kts\",\"ktt\",\"ktu\",\"ktv\",\"ktw\",\"ktx\",\"kty\",\"ktz\",\"kub\",\"kuc\",\"kud\",\"kue\",\"kuf\",\"kug\",\"kuh\",\"kui\",\"kuj\",\"kuk\",\"kul\",\"kum\",\"kun\",\"kuo\",\"kup\",\"kuq\",\"kus\",\"kut\",\"kuu\",\"kuv\",\"kuw\",\"kux\",\"kuy\",\"kuz\",\"kva\",\"kvb\",\"kvc\",\"kvd\",\"kve\",\"kvf\",\"kvg\",\"kvh\",\"kvi\",\"kvj\",\"kvk\",\"kvl\",\"kvm\",\"kvn\",\"kvo\",\"kvp\",\"kvq\",\"kvr\",\"kvs\",\"kvt\",\"kvu\",\"kvv\",\"kvw\",\"kvx\",\"kvy\",\"kvz\",\"kwa\",\"kwb\",\"kwc\",\"kwd\",\"kwe\",\"kwf\",\"kwg\",\"kwh\",\"kwi\",\"kwj\",\"kwk\",\"kwl\",\"kwm\",\"kwn\",\"kwo\",\"kwp\",\"kwq\",\"kwr\",\"kws\",\"kwt\",\"kwu\",\"kwv\",\"kww\",\"kwx\",\"kwy\",\"kwz\",\"kxa\",\"kxb\",\"kxc\",\"kxd\",\"kxe\",\"kxf\",\"kxh\",\"kxi\",\"kxj\",\"kxk\",\"kxl\",\"kxm\",\"kxn\",\"kxo\",\"kxp\",\"kxq\",\"kxr\",\"kxs\",\"kxt\",\"kxu\",\"kxv\",\"kxw\",\"kxx\",\"kxy\",\"kxz\",\"kya\",\"kyb\",\"kyc\",\"kyd\",\"kye\",\"kyf\",\"kyg\",\"kyh\",\"kyi\",\"kyj\",\"kyk\",\"kyl\",\"kym\",\"kyn\",\"kyo\",\"kyp\",\"kyq\",\"kyr\",\"kys\",\"kyt\",\"kyu\",\"kyv\",\"kyw\",\"kyx\",\"kyy\",\"kyz\",\"kza\",\"kzb\",\"kzc\",\"kzd\",\"kze\",\"kzf\",\"kzg\",\"kzh\",\"kzi\",\"kzj\",\"kzk\",\"kzl\",\"kzm\",\"kzn\",\"kzo\",\"kzp\",\"kzq\",\"kzr\",\"kzs\",\"kzt\",\"kzu\",\"kzv\",\"kzw\",\"kzx\",\"kzy\",\"kzz\",\"laa\",\"lab\",\"lac\",\"lad\",\"lae\",\"laf\",\"lag\",\"lah\",\"lai\",\"laj\",\"lak\",\"lal\",\"lam\",\"lan\",\"lap\",\"laq\",\"lar\",\"las\",\"lau\",\"law\",\"lax\",\"lay\",\"laz\",\"lba\",\"lbb\",\"lbc\",\"lbe\",\"lbf\",\"lbg\",\"lbi\",\"lbj\",\"lbk\",\"lbl\",\"lbm\",\"lbn\",\"lbo\",\"lbq\",\"lbr\",\"lbs\",\"lbt\",\"lbu\",\"lbv\",\"lbw\",\"lbx\",\"lby\",\"lbz\",\"lcc\",\"lcd\",\"lce\",\"lcf\",\"lch\",\"lcl\",\"lcm\",\"lcp\",\"lcq\",\"lcs\",\"lda\",\"ldb\",\"ldd\",\"ldg\",\"ldh\",\"ldi\",\"ldj\",\"ldk\",\"ldl\",\"ldm\",\"ldn\",\"ldo\",\"ldp\",\"ldq\",\"lea\",\"leb\",\"lec\",\"led\",\"lee\",\"lef\",\"leg\",\"leh\",\"lei\",\"lej\",\"lek\",\"lel\",\"lem\",\"len\",\"leo\",\"lep\",\"leq\",\"ler\",\"les\",\"let\",\"leu\",\"lev\",\"lew\",\"lex\",\"ley\",\"lez\",\"lfa\",\"lfn\",\"lga\",\"lgb\",\"lgg\",\"lgh\",\"lgi\",\"lgk\",\"lgl\",\"lgm\",\"lgn\",\"lgq\",\"lgr\",\"lgt\",\"lgu\",\"lgz\",\"lha\",\"lhh\",\"lhi\",\"lhl\",\"lhm\",\"lhn\",\"lhp\",\"lhs\",\"lht\",\"lhu\",\"lia\",\"lib\",\"lic\",\"lid\",\"lie\",\"lif\",\"lig\",\"lih\",\"lii\",\"lij\",\"lik\",\"lil\",\"lio\",\"lip\",\"liq\",\"lir\",\"lis\",\"liu\",\"liv\",\"liw\",\"lix\",\"liy\",\"liz\",\"lja\",\"lje\",\"lji\",\"ljl\",\"ljp\",\"ljw\",\"ljx\",\"lka\",\"lkb\",\"lkc\",\"lkd\",\"lke\",\"lkh\",\"lki\",\"lkj\",\"lkl\",\"lkm\",\"lkn\",\"lko\",\"lkr\",\"lks\",\"lkt\",\"lku\",\"lky\",\"lla\",\"llb\",\"llc\",\"lld\",\"lle\",\"llf\",\"llg\",\"llh\",\"lli\",\"llj\",\"llk\",\"lll\",\"llm\",\"lln\",\"llo\",\"llp\",\"llq\",\"lls\",\"llu\",\"llx\",\"lma\",\"lmb\",\"lmc\",\"lmd\",\"lme\",\"lmf\",\"lmg\",\"lmh\",\"lmi\",\"lmj\",\"lmk\",\"lml\",\"lmm\",\"lmn\",\"lmo\",\"lmp\",\"lmq\",\"lmr\",\"lmu\",\"lmv\",\"lmw\",\"lmx\",\"lmy\",\"lmz\",\"lna\",\"lnb\",\"lnd\",\"lng\",\"lnh\",\"lni\",\"lnj\",\"lnl\",\"lnm\",\"lnn\",\"lno\",\"lns\",\"lnu\",\"lnw\",\"lnz\",\"loa\",\"lob\",\"loc\",\"loe\",\"lof\",\"log\",\"loh\",\"loi\",\"loj\",\"lok\",\"lol\",\"lom\",\"lon\",\"loo\",\"lop\",\"loq\",\"lor\",\"los\",\"lot\",\"lou\",\"lov\",\"low\",\"lox\",\"loy\",\"loz\",\"lpa\",\"lpe\",\"lpn\",\"lpo\",\"lpx\",\"lra\",\"lrc\",\"lre\",\"lrg\",\"lri\",\"lrk\",\"lrl\",\"lrm\",\"lrn\",\"lro\",\"lrr\",\"lrt\",\"lrv\",\"lrz\",\"lsa\",\"lsd\",\"lse\",\"lsg\",\"lsh\",\"lsi\",\"lsl\",\"lsm\",\"lso\",\"lsp\",\"lsr\",\"lss\",\"lst\",\"lsy\",\"ltc\",\"ltg\",\"lth\",\"lti\",\"ltn\",\"lto\",\"lts\",\"ltu\",\"lua\",\"luc\",\"lud\",\"lue\",\"luf\",\"lui\",\"luj\",\"luk\",\"lul\",\"lum\",\"lun\",\"luo\",\"lup\",\"luq\",\"lur\",\"lus\",\"lut\",\"luu\",\"luv\",\"luw\",\"luy\",\"luz\",\"lva\",\"lvk\",\"lvs\",\"lvu\",\"lwa\",\"lwe\",\"lwg\",\"lwh\",\"lwl\",\"lwm\",\"lwo\",\"lwt\",\"lwu\",\"lww\",\"lya\",\"lyg\",\"lyn\",\"lzh\",\"lzl\",\"lzn\",\"lzz\",\"maa\",\"mab\",\"mad\",\"mae\",\"maf\",\"mag\",\"mai\",\"maj\",\"mak\",\"mam\",\"man\",\"map\",\"maq\",\"mas\",\"mat\",\"mau\",\"mav\",\"maw\",\"max\",\"maz\",\"mba\",\"mbb\",\"mbc\",\"mbd\",\"mbe\",\"mbf\",\"mbh\",\"mbi\",\"mbj\",\"mbk\",\"mbl\",\"mbm\",\"mbn\",\"mbo\",\"mbp\",\"mbq\",\"mbr\",\"mbs\",\"mbt\",\"mbu\",\"mbv\",\"mbw\",\"mbx\",\"mby\",\"mbz\",\"mca\",\"mcb\",\"mcc\",\"mcd\",\"mce\",\"mcf\",\"mcg\",\"mch\",\"mci\",\"mcj\",\"mck\",\"mcl\",\"mcm\",\"mcn\",\"mco\",\"mcp\",\"mcq\",\"mcr\",\"mcs\",\"mct\",\"mcu\",\"mcv\",\"mcw\",\"mcx\",\"mcy\",\"mcz\",\"mda\",\"mdb\",\"mdc\",\"mdd\",\"mde\",\"mdf\",\"mdg\",\"mdh\",\"mdi\",\"mdj\",\"mdk\",\"mdl\",\"mdm\",\"mdn\",\"mdp\",\"mdq\",\"mdr\",\"mds\",\"mdt\",\"mdu\",\"mdv\",\"mdw\",\"mdx\",\"mdy\",\"mdz\",\"mea\",\"meb\",\"mec\",\"med\",\"mee\",\"mef\",\"meg\",\"meh\",\"mei\",\"mej\",\"mek\",\"mel\",\"mem\",\"men\",\"meo\",\"mep\",\"meq\",\"mer\",\"mes\",\"met\",\"meu\",\"mev\",\"mew\",\"mey\",\"mez\",\"mfa\",\"mfb\",\"mfc\",\"mfd\",\"mfe\",\"mff\",\"mfg\",\"mfh\",\"mfi\",\"mfj\",\"mfk\",\"mfl\",\"mfm\",\"mfn\",\"mfo\",\"mfp\",\"mfq\",\"mfr\",\"mfs\",\"mft\",\"mfu\",\"mfv\",\"mfw\",\"mfx\",\"mfy\",\"mfz\",\"mga\",\"mgb\",\"mgc\",\"mgd\",\"mge\",\"mgf\",\"mgg\",\"mgh\",\"mgi\",\"mgj\",\"mgk\",\"mgl\",\"mgm\",\"mgn\",\"mgo\",\"mgp\",\"mgq\",\"mgr\",\"mgs\",\"mgt\",\"mgu\",\"mgv\",\"mgw\",\"mgx\",\"mgy\",\"mgz\",\"mha\",\"mhb\",\"mhc\",\"mhd\",\"mhe\",\"mhf\",\"mhg\",\"mhh\",\"mhi\",\"mhj\",\"mhk\",\"mhl\",\"mhm\",\"mhn\",\"mho\",\"mhp\",\"mhq\",\"mhr\",\"mhs\",\"mht\",\"mhu\",\"mhw\",\"mhx\",\"mhy\",\"mhz\",\"mia\",\"mib\",\"mic\",\"mid\",\"mie\",\"mif\",\"mig\",\"mih\",\"mii\",\"mij\",\"mik\",\"mil\",\"mim\",\"min\",\"mio\",\"mip\",\"miq\",\"mir\",\"mis\",\"mit\",\"miu\",\"miw\",\"mix\",\"miy\",\"miz\",\"mja\",\"mjb\",\"mjc\",\"mjd\",\"mje\",\"mjg\",\"mjh\",\"mji\",\"mjj\",\"mjk\",\"mjl\",\"mjm\",\"mjn\",\"mjo\",\"mjp\",\"mjq\",\"mjr\",\"mjs\",\"mjt\",\"mju\",\"mjv\",\"mjw\",\"mjx\",\"mjy\",\"mjz\",\"mka\",\"mkb\",\"mkc\",\"mke\",\"mkf\",\"mkg\",\"mkh\",\"mki\",\"mkj\",\"mkk\",\"mkl\",\"mkm\",\"mkn\",\"mko\",\"mkp\",\"mkq\",\"mkr\",\"mks\",\"mkt\",\"mku\",\"mkv\",\"mkw\",\"mkx\",\"mky\",\"mkz\",\"mla\",\"mlb\",\"mlc\",\"mld\",\"mle\",\"mlf\",\"mlh\",\"mli\",\"mlj\",\"mlk\",\"mll\",\"mlm\",\"mln\",\"mlo\",\"mlp\",\"mlq\",\"mlr\",\"mls\",\"mlu\",\"mlv\",\"mlw\",\"mlx\",\"mlz\",\"mma\",\"mmb\",\"mmc\",\"mmd\",\"mme\",\"mmf\",\"mmg\",\"mmh\",\"mmi\",\"mmj\",\"mmk\",\"mml\",\"mmm\",\"mmn\",\"mmo\",\"mmp\",\"mmq\",\"mmr\",\"mmt\",\"mmu\",\"mmv\",\"mmw\",\"mmx\",\"mmy\",\"mmz\",\"mna\",\"mnb\",\"mnc\",\"mnd\",\"mne\",\"mnf\",\"mng\",\"mnh\",\"mni\",\"mnj\",\"mnk\",\"mnl\",\"mnm\",\"mnn\",\"mno\",\"mnp\",\"mnq\",\"mnr\",\"mns\",\"mnt\",\"mnu\",\"mnv\",\"mnw\",\"mnx\",\"mny\",\"mnz\",\"moa\",\"moc\",\"mod\",\"moe\",\"mof\",\"mog\",\"moh\",\"moi\",\"moj\",\"mok\",\"mom\",\"moo\",\"mop\",\"moq\",\"mor\",\"mos\",\"mot\",\"mou\",\"mov\",\"mow\",\"mox\",\"moy\",\"moz\",\"mpa\",\"mpb\",\"mpc\",\"mpd\",\"mpe\",\"mpg\",\"mph\",\"mpi\",\"mpj\",\"mpk\",\"mpl\",\"mpm\",\"mpn\",\"mpo\",\"mpp\",\"mpq\",\"mpr\",\"mps\",\"mpt\",\"mpu\",\"mpv\",\"mpw\",\"mpx\",\"mpy\",\"mpz\",\"mqa\",\"mqb\",\"mqc\",\"mqe\",\"mqf\",\"mqg\",\"mqh\",\"mqi\",\"mqj\",\"mqk\",\"mql\",\"mqm\",\"mqn\",\"mqo\",\"mqp\",\"mqq\",\"mqr\",\"mqs\",\"mqt\",\"mqu\",\"mqv\",\"mqw\",\"mqx\",\"mqy\",\"mqz\",\"mra\",\"mrb\",\"mrc\",\"mrd\",\"mre\",\"mrf\",\"mrg\",\"mrh\",\"mrj\",\"mrk\",\"mrl\",\"mrm\",\"mrn\",\"mro\",\"mrp\",\"mrq\",\"mrr\",\"mrs\",\"mrt\",\"mru\",\"mrv\",\"mrw\",\"mrx\",\"mry\",\"mrz\",\"msb\",\"msc\",\"msd\",\"mse\",\"msf\",\"msg\",\"msh\",\"msi\",\"msj\",\"msk\",\"msl\",\"msm\",\"msn\",\"mso\",\"msp\",\"msq\",\"msr\",\"mss\",\"mst\",\"msu\",\"msv\",\"msw\",\"msx\",\"msy\",\"msz\",\"mta\",\"mtb\",\"mtc\",\"mtd\",\"mte\",\"mtf\",\"mtg\",\"mth\",\"mti\",\"mtj\",\"mtk\",\"mtl\",\"mtm\",\"mtn\",\"mto\",\"mtp\",\"mtq\",\"mtr\",\"mts\",\"mtt\",\"mtu\",\"mtv\",\"mtw\",\"mtx\",\"mty\",\"mua\",\"mub\",\"muc\",\"mud\",\"mue\",\"mug\",\"muh\",\"mui\",\"muj\",\"muk\",\"mul\",\"mum\",\"mun\",\"muo\",\"mup\",\"muq\",\"mur\",\"mus\",\"mut\",\"muu\",\"muv\",\"mux\",\"muy\",\"muz\",\"mva\",\"mvb\",\"mvd\",\"mve\",\"mvf\",\"mvg\",\"mvh\",\"mvi\",\"mvk\",\"mvl\",\"mvm\",\"mvn\",\"mvo\",\"mvp\",\"mvq\",\"mvr\",\"mvs\",\"mvt\",\"mvu\",\"mvv\",\"mvw\",\"mvx\",\"mvy\",\"mvz\",\"mwa\",\"mwb\",\"mwc\",\"mwd\",\"mwe\",\"mwf\",\"mwg\",\"mwh\",\"mwi\",\"mwj\",\"mwk\",\"mwl\",\"mwm\",\"mwn\",\"mwo\",\"mwp\",\"mwq\",\"mwr\",\"mws\",\"mwt\",\"mwu\",\"mwv\",\"mww\",\"mwx\",\"mwy\",\"mwz\",\"mxa\",\"mxb\",\"mxc\",\"mxd\",\"mxe\",\"mxf\",\"mxg\",\"mxh\",\"mxi\",\"mxj\",\"mxk\",\"mxl\",\"mxm\",\"mxn\",\"mxo\",\"mxp\",\"mxq\",\"mxr\",\"mxs\",\"mxt\",\"mxu\",\"mxv\",\"mxw\",\"mxx\",\"mxy\",\"mxz\",\"myb\",\"myc\",\"myd\",\"mye\",\"myf\",\"myg\",\"myh\",\"myi\",\"myj\",\"myk\",\"myl\",\"mym\",\"myn\",\"myo\",\"myp\",\"myq\",\"myr\",\"mys\",\"myt\",\"myu\",\"myv\",\"myw\",\"myx\",\"myy\",\"myz\",\"mza\",\"mzb\",\"mzc\",\"mzd\",\"mze\",\"mzg\",\"mzh\",\"mzi\",\"mzj\",\"mzk\",\"mzl\",\"mzm\",\"mzn\",\"mzo\",\"mzp\",\"mzq\",\"mzr\",\"mzs\",\"mzt\",\"mzu\",\"mzv\",\"mzw\",\"mzx\",\"mzy\",\"mzz\",\"naa\",\"nab\",\"nac\",\"nad\",\"nae\",\"naf\",\"nag\",\"nah\",\"nai\",\"naj\",\"nak\",\"nal\",\"nam\",\"nan\",\"nao\",\"nap\",\"naq\",\"nar\",\"nas\",\"nat\",\"naw\",\"nax\",\"nay\",\"naz\",\"nba\",\"nbb\",\"nbc\",\"nbd\",\"nbe\",\"nbf\",\"nbg\",\"nbh\",\"nbi\",\"nbj\",\"nbk\",\"nbm\",\"nbn\",\"nbo\",\"nbp\",\"nbq\",\"nbr\",\"nbs\",\"nbt\",\"nbu\",\"nbv\",\"nbw\",\"nbx\",\"nby\",\"nca\",\"ncb\",\"ncc\",\"ncd\",\"nce\",\"ncf\",\"ncg\",\"nch\",\"nci\",\"ncj\",\"nck\",\"ncl\",\"ncm\",\"ncn\",\"nco\",\"ncp\",\"ncq\",\"ncr\",\"ncs\",\"nct\",\"ncu\",\"ncx\",\"ncz\",\"nda\",\"ndb\",\"ndc\",\"ndd\",\"ndf\",\"ndg\",\"ndh\",\"ndi\",\"ndj\",\"ndk\",\"ndl\",\"ndm\",\"ndn\",\"ndp\",\"ndq\",\"ndr\",\"nds\",\"ndt\",\"ndu\",\"ndv\",\"ndw\",\"ndx\",\"ndy\",\"ndz\",\"nea\",\"neb\",\"nec\",\"ned\",\"nee\",\"nef\",\"neg\",\"neh\",\"nei\",\"nej\",\"nek\",\"nem\",\"nen\",\"neo\",\"neq\",\"ner\",\"nes\",\"net\",\"neu\",\"nev\",\"new\",\"nex\",\"ney\",\"nez\",\"nfa\",\"nfd\",\"nfl\",\"nfr\",\"nfu\",\"nga\",\"ngb\",\"ngc\",\"ngd\",\"nge\",\"ngf\",\"ngg\",\"ngh\",\"ngi\",\"ngj\",\"ngk\",\"ngl\",\"ngm\",\"ngn\",\"ngo\",\"ngp\",\"ngq\",\"ngr\",\"ngs\",\"ngt\",\"ngu\",\"ngv\",\"ngw\",\"ngx\",\"ngy\",\"ngz\",\"nha\",\"nhb\",\"nhc\",\"nhd\",\"nhe\",\"nhf\",\"nhg\",\"nhh\",\"nhi\",\"nhk\",\"nhm\",\"nhn\",\"nho\",\"nhp\",\"nhq\",\"nhr\",\"nht\",\"nhu\",\"nhv\",\"nhw\",\"nhx\",\"nhy\",\"nhz\",\"nia\",\"nib\",\"nic\",\"nid\",\"nie\",\"nif\",\"nig\",\"nih\",\"nii\",\"nij\",\"nik\",\"nil\",\"nim\",\"nin\",\"nio\",\"niq\",\"nir\",\"nis\",\"nit\",\"niu\",\"niv\",\"niw\",\"nix\",\"niy\",\"niz\",\"nja\",\"njb\",\"njd\",\"njh\",\"nji\",\"njj\",\"njl\",\"njm\",\"njn\",\"njo\",\"njr\",\"njs\",\"njt\",\"nju\",\"njx\",\"njy\",\"njz\",\"nka\",\"nkb\",\"nkc\",\"nkd\",\"nke\",\"nkf\",\"nkg\",\"nkh\",\"nki\",\"nkj\",\"nkk\",\"nkm\",\"nkn\",\"nko\",\"nkp\",\"nkq\",\"nkr\",\"nks\",\"nkt\",\"nku\",\"nkv\",\"nkw\",\"nkx\",\"nkz\",\"nla\",\"nlc\",\"nle\",\"nlg\",\"nli\",\"nlj\",\"nlk\",\"nll\",\"nln\",\"nlo\",\"nlq\",\"nlr\",\"nlu\",\"nlv\",\"nlw\",\"nlx\",\"nly\",\"nlz\",\"nma\",\"nmb\",\"nmc\",\"nmd\",\"nme\",\"nmf\",\"nmg\",\"nmh\",\"nmi\",\"nmj\",\"nmk\",\"nml\",\"nmm\",\"nmn\",\"nmo\",\"nmp\",\"nmq\",\"nmr\",\"nms\",\"nmt\",\"nmu\",\"nmv\",\"nmw\",\"nmx\",\"nmy\",\"nmz\",\"nna\",\"nnb\",\"nnc\",\"nnd\",\"nne\",\"nnf\",\"nng\",\"nnh\",\"nni\",\"nnj\",\"nnk\",\"nnl\",\"nnm\",\"nnn\",\"nnp\",\"nnq\",\"nnr\",\"nns\",\"nnt\",\"nnu\",\"nnv\",\"nnw\",\"nnx\",\"nny\",\"nnz\",\"noa\",\"noc\",\"nod\",\"noe\",\"nof\",\"nog\",\"noh\",\"noi\",\"noj\",\"nok\",\"nol\",\"nom\",\"non\",\"noo\",\"nop\",\"noq\",\"nos\",\"not\",\"nou\",\"nov\",\"now\",\"noy\",\"noz\",\"npa\",\"npb\",\"npg\",\"nph\",\"npi\",\"npl\",\"npn\",\"npo\",\"nps\",\"npu\",\"npx\",\"npy\",\"nqg\",\"nqk\",\"nql\",\"nqm\",\"nqn\",\"nqo\",\"nqq\",\"nqy\",\"nra\",\"nrb\",\"nrc\",\"nre\",\"nrf\",\"nrg\",\"nri\",\"nrk\",\"nrl\",\"nrm\",\"nrn\",\"nrp\",\"nrr\",\"nrt\",\"nru\",\"nrx\",\"nrz\",\"nsa\",\"nsc\",\"nsd\",\"nse\",\"nsf\",\"nsg\",\"nsh\",\"nsi\",\"nsk\",\"nsl\",\"nsm\",\"nsn\",\"nso\",\"nsp\",\"nsq\",\"nsr\",\"nss\",\"nst\",\"nsu\",\"nsv\",\"nsw\",\"nsx\",\"nsy\",\"nsz\",\"ntd\",\"nte\",\"ntg\",\"nti\",\"ntj\",\"ntk\",\"ntm\",\"nto\",\"ntp\",\"ntr\",\"nts\",\"ntu\",\"ntw\",\"ntx\",\"nty\",\"ntz\",\"nua\",\"nub\",\"nuc\",\"nud\",\"nue\",\"nuf\",\"nug\",\"nuh\",\"nui\",\"nuj\",\"nuk\",\"nul\",\"num\",\"nun\",\"nuo\",\"nup\",\"nuq\",\"nur\",\"nus\",\"nut\",\"nuu\",\"nuv\",\"nuw\",\"nux\",\"nuy\",\"nuz\",\"nvh\",\"nvm\",\"nvo\",\"nwa\",\"nwb\",\"nwc\",\"nwe\",\"nwg\",\"nwi\",\"nwm\",\"nwo\",\"nwr\",\"nwx\",\"nwy\",\"nxa\",\"nxd\",\"nxe\",\"nxg\",\"nxi\",\"nxk\",\"nxl\",\"nxm\",\"nxn\",\"nxo\",\"nxq\",\"nxr\",\"nxu\",\"nxx\",\"nyb\",\"nyc\",\"nyd\",\"nye\",\"nyf\",\"nyg\",\"nyh\",\"nyi\",\"nyj\",\"nyk\",\"nyl\",\"nym\",\"nyn\",\"nyo\",\"nyp\",\"nyq\",\"nyr\",\"nys\",\"nyt\",\"nyu\",\"nyv\",\"nyw\",\"nyx\",\"nyy\",\"nza\",\"nzb\",\"nzi\",\"nzk\",\"nzm\",\"nzs\",\"nzu\",\"nzy\",\"nzz\",\"oaa\",\"oac\",\"oar\",\"oav\",\"obi\",\"obk\",\"obl\",\"obm\",\"obo\",\"obr\",\"obt\",\"obu\",\"oca\",\"och\",\"oco\",\"ocu\",\"oda\",\"odk\",\"odt\",\"odu\",\"ofo\",\"ofs\",\"ofu\",\"ogb\",\"ogc\",\"oge\",\"ogg\",\"ogo\",\"ogu\",\"oht\",\"ohu\",\"oia\",\"oin\",\"ojb\",\"ojc\",\"ojg\",\"ojp\",\"ojs\",\"ojv\",\"ojw\",\"oka\",\"okb\",\"okd\",\"oke\",\"okg\",\"okh\",\"oki\",\"okj\",\"okk\",\"okl\",\"okm\",\"okn\",\"oko\",\"okr\",\"oks\",\"oku\",\"okv\",\"okx\",\"ola\",\"old\",\"ole\",\"olk\",\"olm\",\"olo\",\"olr\",\"olt\",\"olu\",\"oma\",\"omb\",\"omc\",\"ome\",\"omg\",\"omi\",\"omk\",\"oml\",\"omn\",\"omo\",\"omp\",\"omq\",\"omr\",\"omt\",\"omu\",\"omv\",\"omw\",\"omx\",\"ona\",\"onb\",\"one\",\"ong\",\"oni\",\"onj\",\"onk\",\"onn\",\"ono\",\"onp\",\"onr\",\"ons\",\"ont\",\"onu\",\"onw\",\"onx\",\"ood\",\"oog\",\"oon\",\"oor\",\"oos\",\"opa\",\"opk\",\"opm\",\"opo\",\"opt\",\"opy\",\"ora\",\"orc\",\"ore\",\"org\",\"orh\",\"orn\",\"oro\",\"orr\",\"ors\",\"ort\",\"oru\",\"orv\",\"orw\",\"orx\",\"ory\",\"orz\",\"osa\",\"osc\",\"osi\",\"oso\",\"osp\",\"ost\",\"osu\",\"osx\",\"ota\",\"otb\",\"otd\",\"ote\",\"oti\",\"otk\",\"otl\",\"otm\",\"otn\",\"oto\",\"otq\",\"otr\",\"ots\",\"ott\",\"otu\",\"otw\",\"otx\",\"oty\",\"otz\",\"oua\",\"oub\",\"oue\",\"oui\",\"oum\",\"oun\",\"ovd\",\"owi\",\"owl\",\"oyb\",\"oyd\",\"oym\",\"oyy\",\"ozm\",\"paa\",\"pab\",\"pac\",\"pad\",\"pae\",\"paf\",\"pag\",\"pah\",\"pai\",\"pak\",\"pal\",\"pam\",\"pao\",\"pap\",\"paq\",\"par\",\"pas\",\"pat\",\"pau\",\"pav\",\"paw\",\"pax\",\"pay\",\"paz\",\"pbb\",\"pbc\",\"pbe\",\"pbf\",\"pbg\",\"pbh\",\"pbi\",\"pbl\",\"pbn\",\"pbo\",\"pbp\",\"pbr\",\"pbs\",\"pbt\",\"pbu\",\"pbv\",\"pby\",\"pbz\",\"pca\",\"pcb\",\"pcc\",\"pcd\",\"pce\",\"pcf\",\"pcg\",\"pch\",\"pci\",\"pcj\",\"pck\",\"pcl\",\"pcm\",\"pcn\",\"pcp\",\"pcr\",\"pcw\",\"pda\",\"pdc\",\"pdi\",\"pdn\",\"pdo\",\"pdt\",\"pdu\",\"pea\",\"peb\",\"ped\",\"pee\",\"pef\",\"peg\",\"peh\",\"pei\",\"pej\",\"pek\",\"pel\",\"pem\",\"peo\",\"pep\",\"peq\",\"pes\",\"pev\",\"pex\",\"pey\",\"pez\",\"pfa\",\"pfe\",\"pfl\",\"pga\",\"pgd\",\"pgg\",\"pgi\",\"pgk\",\"pgl\",\"pgn\",\"pgs\",\"pgu\",\"pgy\",\"pgz\",\"pha\",\"phd\",\"phg\",\"phh\",\"phi\",\"phk\",\"phl\",\"phm\",\"phn\",\"pho\",\"phq\",\"phr\",\"pht\",\"phu\",\"phv\",\"phw\",\"pia\",\"pib\",\"pic\",\"pid\",\"pie\",\"pif\",\"pig\",\"pih\",\"pii\",\"pij\",\"pil\",\"pim\",\"pin\",\"pio\",\"pip\",\"pir\",\"pis\",\"pit\",\"piu\",\"piv\",\"piw\",\"pix\",\"piy\",\"piz\",\"pjt\",\"pka\",\"pkb\",\"pkc\",\"pkg\",\"pkh\",\"pkn\",\"pko\",\"pkp\",\"pkr\",\"pks\",\"pkt\",\"pku\",\"pla\",\"plb\",\"plc\",\"pld\",\"ple\",\"plf\",\"plg\",\"plh\",\"plj\",\"plk\",\"pll\",\"pln\",\"plo\",\"plp\",\"plq\",\"plr\",\"pls\",\"plt\",\"plu\",\"plv\",\"plw\",\"ply\",\"plz\",\"pma\",\"pmb\",\"pmc\",\"pmd\",\"pme\",\"pmf\",\"pmh\",\"pmi\",\"pmj\",\"pmk\",\"pml\",\"pmm\",\"pmn\",\"pmo\",\"pmq\",\"pmr\",\"pms\",\"pmt\",\"pmu\",\"pmw\",\"pmx\",\"pmy\",\"pmz\",\"pna\",\"pnb\",\"pnc\",\"pne\",\"png\",\"pnh\",\"pni\",\"pnj\",\"pnk\",\"pnl\",\"pnm\",\"pnn\",\"pno\",\"pnp\",\"pnq\",\"pnr\",\"pns\",\"pnt\",\"pnu\",\"pnv\",\"pnw\",\"pnx\",\"pny\",\"pnz\",\"poc\",\"pod\",\"poe\",\"pof\",\"pog\",\"poh\",\"poi\",\"pok\",\"pom\",\"pon\",\"poo\",\"pop\",\"poq\",\"pos\",\"pot\",\"pov\",\"pow\",\"pox\",\"poy\",\"poz\",\"ppa\",\"ppe\",\"ppi\",\"ppk\",\"ppl\",\"ppm\",\"ppn\",\"ppo\",\"ppp\",\"ppq\",\"ppr\",\"pps\",\"ppt\",\"ppu\",\"pqa\",\"pqe\",\"pqm\",\"pqw\",\"pra\",\"prb\",\"prc\",\"prd\",\"pre\",\"prf\",\"prg\",\"prh\",\"pri\",\"prk\",\"prl\",\"prm\",\"prn\",\"pro\",\"prp\",\"prq\",\"prr\",\"prs\",\"prt\",\"pru\",\"prw\",\"prx\",\"pry\",\"prz\",\"psa\",\"psc\",\"psd\",\"pse\",\"psg\",\"psh\",\"psi\",\"psl\",\"psm\",\"psn\",\"pso\",\"psp\",\"psq\",\"psr\",\"pss\",\"pst\",\"psu\",\"psw\",\"psy\",\"pta\",\"pth\",\"pti\",\"ptn\",\"pto\",\"ptp\",\"ptq\",\"ptr\",\"ptt\",\"ptu\",\"ptv\",\"ptw\",\"pty\",\"pua\",\"pub\",\"puc\",\"pud\",\"pue\",\"puf\",\"pug\",\"pui\",\"puj\",\"puk\",\"pum\",\"puo\",\"pup\",\"puq\",\"pur\",\"put\",\"puu\",\"puw\",\"pux\",\"puy\",\"puz\",\"pwa\",\"pwb\",\"pwg\",\"pwi\",\"pwm\",\"pwn\",\"pwo\",\"pwr\",\"pww\",\"pxm\",\"pye\",\"pym\",\"pyn\",\"pys\",\"pyu\",\"pyx\",\"pyy\",\"pzn\",\"qaa..qtz\",\"qua\",\"qub\",\"quc\",\"qud\",\"quf\",\"qug\",\"quh\",\"qui\",\"quk\",\"qul\",\"qum\",\"qun\",\"qup\",\"quq\",\"qur\",\"qus\",\"quv\",\"quw\",\"qux\",\"quy\",\"quz\",\"qva\",\"qvc\",\"qve\",\"qvh\",\"qvi\",\"qvj\",\"qvl\",\"qvm\",\"qvn\",\"qvo\",\"qvp\",\"qvs\",\"qvw\",\"qvy\",\"qvz\",\"qwa\",\"qwc\",\"qwe\",\"qwh\",\"qwm\",\"qws\",\"qwt\",\"qxa\",\"qxc\",\"qxh\",\"qxl\",\"qxn\",\"qxo\",\"qxp\",\"qxq\",\"qxr\",\"qxs\",\"qxt\",\"qxu\",\"qxw\",\"qya\",\"qyp\",\"raa\",\"rab\",\"rac\",\"rad\",\"raf\",\"rag\",\"rah\",\"rai\",\"raj\",\"rak\",\"ral\",\"ram\",\"ran\",\"rao\",\"rap\",\"raq\",\"rar\",\"ras\",\"rat\",\"rau\",\"rav\",\"raw\",\"rax\",\"ray\",\"raz\",\"rbb\",\"rbk\",\"rbl\",\"rbp\",\"rcf\",\"rdb\",\"rea\",\"reb\",\"ree\",\"reg\",\"rei\",\"rej\",\"rel\",\"rem\",\"ren\",\"rer\",\"res\",\"ret\",\"rey\",\"rga\",\"rge\",\"rgk\",\"rgn\",\"rgr\",\"rgs\",\"rgu\",\"rhg\",\"rhp\",\"ria\",\"rie\",\"rif\",\"ril\",\"rim\",\"rin\",\"rir\",\"rit\",\"riu\",\"rjg\",\"rji\",\"rjs\",\"rka\",\"rkb\",\"rkh\",\"rki\",\"rkm\",\"rkt\",\"rkw\",\"rma\",\"rmb\",\"rmc\",\"rmd\",\"rme\",\"rmf\",\"rmg\",\"rmh\",\"rmi\",\"rmk\",\"rml\",\"rmm\",\"rmn\",\"rmo\",\"rmp\",\"rmq\",\"rmr\",\"rms\",\"rmt\",\"rmu\",\"rmv\",\"rmw\",\"rmx\",\"rmy\",\"rmz\",\"rna\",\"rnd\",\"rng\",\"rnl\",\"rnn\",\"rnp\",\"rnr\",\"rnw\",\"roa\",\"rob\",\"roc\",\"rod\",\"roe\",\"rof\",\"rog\",\"rol\",\"rom\",\"roo\",\"rop\",\"ror\",\"rou\",\"row\",\"rpn\",\"rpt\",\"rri\",\"rro\",\"rrt\",\"rsb\",\"rsi\",\"rsl\",\"rsm\",\"rtc\",\"rth\",\"rtm\",\"rts\",\"rtw\",\"rub\",\"ruc\",\"rue\",\"ruf\",\"rug\",\"ruh\",\"rui\",\"ruk\",\"ruo\",\"rup\",\"ruq\",\"rut\",\"ruu\",\"ruy\",\"ruz\",\"rwa\",\"rwk\",\"rwm\",\"rwo\",\"rwr\",\"rxd\",\"rxw\",\"ryn\",\"rys\",\"ryu\",\"rzh\",\"saa\",\"sab\",\"sac\",\"sad\",\"sae\",\"saf\",\"sah\",\"sai\",\"saj\",\"sak\",\"sal\",\"sam\",\"sao\",\"sap\",\"saq\",\"sar\",\"sas\",\"sat\",\"sau\",\"sav\",\"saw\",\"sax\",\"say\",\"saz\",\"sba\",\"sbb\",\"sbc\",\"sbd\",\"sbe\",\"sbf\",\"sbg\",\"sbh\",\"sbi\",\"sbj\",\"sbk\",\"sbl\",\"sbm\",\"sbn\",\"sbo\",\"sbp\",\"sbq\",\"sbr\",\"sbs\",\"sbt\",\"sbu\",\"sbv\",\"sbw\",\"sbx\",\"sby\",\"sbz\",\"sca\",\"scb\",\"sce\",\"scf\",\"scg\",\"sch\",\"sci\",\"sck\",\"scl\",\"scn\",\"sco\",\"scp\",\"scq\",\"scs\",\"sct\",\"scu\",\"scv\",\"scw\",\"scx\",\"sda\",\"sdb\",\"sdc\",\"sde\",\"sdf\",\"sdg\",\"sdh\",\"sdj\",\"sdk\",\"sdl\",\"sdm\",\"sdn\",\"sdo\",\"sdp\",\"sdr\",\"sds\",\"sdt\",\"sdu\",\"sdv\",\"sdx\",\"sdz\",\"sea\",\"seb\",\"sec\",\"sed\",\"see\",\"sef\",\"seg\",\"seh\",\"sei\",\"sej\",\"sek\",\"sel\",\"sem\",\"sen\",\"seo\",\"sep\",\"seq\",\"ser\",\"ses\",\"set\",\"seu\",\"sev\",\"sew\",\"sey\",\"sez\",\"sfb\",\"sfe\",\"sfm\",\"sfs\",\"sfw\",\"sga\",\"sgb\",\"sgc\",\"sgd\",\"sge\",\"sgg\",\"sgh\",\"sgi\",\"sgj\",\"sgk\",\"sgl\",\"sgm\",\"sgn\",\"sgo\",\"sgp\",\"sgr\",\"sgs\",\"sgt\",\"sgu\",\"sgw\",\"sgx\",\"sgy\",\"sgz\",\"sha\",\"shb\",\"shc\",\"shd\",\"she\",\"shg\",\"shh\",\"shi\",\"shj\",\"shk\",\"shl\",\"shm\",\"shn\",\"sho\",\"shp\",\"shq\",\"shr\",\"shs\",\"sht\",\"shu\",\"shv\",\"shw\",\"shx\",\"shy\",\"shz\",\"sia\",\"sib\",\"sid\",\"sie\",\"sif\",\"sig\",\"sih\",\"sii\",\"sij\",\"sik\",\"sil\",\"sim\",\"sio\",\"sip\",\"siq\",\"sir\",\"sis\",\"sit\",\"siu\",\"siv\",\"siw\",\"six\",\"siy\",\"siz\",\"sja\",\"sjb\",\"sjd\",\"sje\",\"sjg\",\"sjk\",\"sjl\",\"sjm\",\"sjn\",\"sjo\",\"sjp\",\"sjr\",\"sjs\",\"sjt\",\"sju\",\"sjw\",\"ska\",\"skb\",\"skc\",\"skd\",\"ske\",\"skf\",\"skg\",\"skh\",\"ski\",\"skj\",\"skk\",\"skm\",\"skn\",\"sko\",\"skp\",\"skq\",\"skr\",\"sks\",\"skt\",\"sku\",\"skv\",\"skw\",\"skx\",\"sky\",\"skz\",\"sla\",\"slc\",\"sld\",\"sle\",\"slf\",\"slg\",\"slh\",\"sli\",\"slj\",\"sll\",\"slm\",\"sln\",\"slp\",\"slq\",\"slr\",\"sls\",\"slt\",\"slu\",\"slw\",\"slx\",\"sly\",\"slz\",\"sma\",\"smb\",\"smc\",\"smd\",\"smf\",\"smg\",\"smh\",\"smi\",\"smj\",\"smk\",\"sml\",\"smm\",\"smn\",\"smp\",\"smq\",\"smr\",\"sms\",\"smt\",\"smu\",\"smv\",\"smw\",\"smx\",\"smy\",\"smz\",\"snb\",\"snc\",\"sne\",\"snf\",\"sng\",\"snh\",\"sni\",\"snj\",\"snk\",\"snl\",\"snm\",\"snn\",\"sno\",\"snp\",\"snq\",\"snr\",\"sns\",\"snu\",\"snv\",\"snw\",\"snx\",\"sny\",\"snz\",\"soa\",\"sob\",\"soc\",\"sod\",\"soe\",\"sog\",\"soh\",\"soi\",\"soj\",\"sok\",\"sol\",\"son\",\"soo\",\"sop\",\"soq\",\"sor\",\"sos\",\"sou\",\"sov\",\"sow\",\"sox\",\"soy\",\"soz\",\"spb\",\"spc\",\"spd\",\"spe\",\"spg\",\"spi\",\"spk\",\"spl\",\"spm\",\"spn\",\"spo\",\"spp\",\"spq\",\"spr\",\"sps\",\"spt\",\"spu\",\"spv\",\"spx\",\"spy\",\"sqa\",\"sqh\",\"sqj\",\"sqk\",\"sqm\",\"sqn\",\"sqo\",\"sqq\",\"sqr\",\"sqs\",\"sqt\",\"squ\",\"sra\",\"srb\",\"src\",\"sre\",\"srf\",\"srg\",\"srh\",\"sri\",\"srk\",\"srl\",\"srm\",\"srn\",\"sro\",\"srq\",\"srr\",\"srs\",\"srt\",\"sru\",\"srv\",\"srw\",\"srx\",\"sry\",\"srz\",\"ssa\",\"ssb\",\"ssc\",\"ssd\",\"sse\",\"ssf\",\"ssg\",\"ssh\",\"ssi\",\"ssj\",\"ssk\",\"ssl\",\"ssm\",\"ssn\",\"sso\",\"ssp\",\"ssq\",\"ssr\",\"sss\",\"sst\",\"ssu\",\"ssv\",\"ssx\",\"ssy\",\"ssz\",\"sta\",\"stb\",\"std\",\"ste\",\"stf\",\"stg\",\"sth\",\"sti\",\"stj\",\"stk\",\"stl\",\"stm\",\"stn\",\"sto\",\"stp\",\"stq\",\"str\",\"sts\",\"stt\",\"stu\",\"stv\",\"stw\",\"sty\",\"sua\",\"sub\",\"suc\",\"sue\",\"sug\",\"sui\",\"suj\",\"suk\",\"sul\",\"sum\",\"suq\",\"sur\",\"sus\",\"sut\",\"suv\",\"suw\",\"sux\",\"suy\",\"suz\",\"sva\",\"svb\",\"svc\",\"sve\",\"svk\",\"svm\",\"svr\",\"svs\",\"svx\",\"swb\",\"swc\",\"swf\",\"swg\",\"swh\",\"swi\",\"swj\",\"swk\",\"swl\",\"swm\",\"swn\",\"swo\",\"swp\",\"swq\",\"swr\",\"sws\",\"swt\",\"swu\",\"swv\",\"sww\",\"swx\",\"swy\",\"sxb\",\"sxc\",\"sxe\",\"sxg\",\"sxk\",\"sxl\",\"sxm\",\"sxn\",\"sxo\",\"sxr\",\"sxs\",\"sxu\",\"sxw\",\"sya\",\"syb\",\"syc\",\"syd\",\"syi\",\"syk\",\"syl\",\"sym\",\"syn\",\"syo\",\"syr\",\"sys\",\"syw\",\"syx\",\"syy\",\"sza\",\"szb\",\"szc\",\"szd\",\"sze\",\"szg\",\"szl\",\"szn\",\"szp\",\"szs\",\"szv\",\"szw\",\"taa\",\"tab\",\"tac\",\"tad\",\"tae\",\"taf\",\"tag\",\"tai\",\"taj\",\"tak\",\"tal\",\"tan\",\"tao\",\"tap\",\"taq\",\"tar\",\"tas\",\"tau\",\"tav\",\"taw\",\"tax\",\"tay\",\"taz\",\"tba\",\"tbb\",\"tbc\",\"tbd\",\"tbe\",\"tbf\",\"tbg\",\"tbh\",\"tbi\",\"tbj\",\"tbk\",\"tbl\",\"tbm\",\"tbn\",\"tbo\",\"tbp\",\"tbq\",\"tbr\",\"tbs\",\"tbt\",\"tbu\",\"tbv\",\"tbw\",\"tbx\",\"tby\",\"tbz\",\"tca\",\"tcb\",\"tcc\",\"tcd\",\"tce\",\"tcf\",\"tcg\",\"tch\",\"tci\",\"tck\",\"tcl\",\"tcm\",\"tcn\",\"tco\",\"tcp\",\"tcq\",\"tcs\",\"tct\",\"tcu\",\"tcw\",\"tcx\",\"tcy\",\"tcz\",\"tda\",\"tdb\",\"tdc\",\"tdd\",\"tde\",\"tdf\",\"tdg\",\"tdh\",\"tdi\",\"tdj\",\"tdk\",\"tdl\",\"tdm\",\"tdn\",\"tdo\",\"tdq\",\"tdr\",\"tds\",\"tdt\",\"tdu\",\"tdv\",\"tdx\",\"tdy\",\"tea\",\"teb\",\"tec\",\"ted\",\"tee\",\"tef\",\"teg\",\"teh\",\"tei\",\"tek\",\"tem\",\"ten\",\"teo\",\"tep\",\"teq\",\"ter\",\"tes\",\"tet\",\"teu\",\"tev\",\"tew\",\"tex\",\"tey\",\"tfi\",\"tfn\",\"tfo\",\"tfr\",\"tft\",\"tga\",\"tgb\",\"tgc\",\"tgd\",\"tge\",\"tgf\",\"tgg\",\"tgh\",\"tgi\",\"tgj\",\"tgn\",\"tgo\",\"tgp\",\"tgq\",\"tgr\",\"tgs\",\"tgt\",\"tgu\",\"tgv\",\"tgw\",\"tgx\",\"tgy\",\"tgz\",\"thc\",\"thd\",\"the\",\"thf\",\"thh\",\"thi\",\"thk\",\"thl\",\"thm\",\"thn\",\"thp\",\"thq\",\"thr\",\"ths\",\"tht\",\"thu\",\"thv\",\"thw\",\"thx\",\"thy\",\"thz\",\"tia\",\"tic\",\"tid\",\"tie\",\"tif\",\"tig\",\"tih\",\"tii\",\"tij\",\"tik\",\"til\",\"tim\",\"tin\",\"tio\",\"tip\",\"tiq\",\"tis\",\"tit\",\"tiu\",\"tiv\",\"tiw\",\"tix\",\"tiy\",\"tiz\",\"tja\",\"tjg\",\"tji\",\"tjl\",\"tjm\",\"tjn\",\"tjo\",\"tjs\",\"tju\",\"tjw\",\"tka\",\"tkb\",\"tkd\",\"tke\",\"tkf\",\"tkg\",\"tkk\",\"tkl\",\"tkm\",\"tkn\",\"tkp\",\"tkq\",\"tkr\",\"tks\",\"tkt\",\"tku\",\"tkv\",\"tkw\",\"tkx\",\"tkz\",\"tla\",\"tlb\",\"tlc\",\"tld\",\"tlf\",\"tlg\",\"tlh\",\"tli\",\"tlj\",\"tlk\",\"tll\",\"tlm\",\"tln\",\"tlo\",\"tlp\",\"tlq\",\"tlr\",\"tls\",\"tlt\",\"tlu\",\"tlv\",\"tlw\",\"tlx\",\"tly\",\"tma\",\"tmb\",\"tmc\",\"tmd\",\"tme\",\"tmf\",\"tmg\",\"tmh\",\"tmi\",\"tmj\",\"tmk\",\"tml\",\"tmm\",\"tmn\",\"tmo\",\"tmp\",\"tmq\",\"tmr\",\"tms\",\"tmt\",\"tmu\",\"tmv\",\"tmw\",\"tmy\",\"tmz\",\"tna\",\"tnb\",\"tnc\",\"tnd\",\"tne\",\"tnf\",\"tng\",\"tnh\",\"tni\",\"tnk\",\"tnl\",\"tnm\",\"tnn\",\"tno\",\"tnp\",\"tnq\",\"tnr\",\"tns\",\"tnt\",\"tnu\",\"tnv\",\"tnw\",\"tnx\",\"tny\",\"tnz\",\"tob\",\"toc\",\"tod\",\"toe\",\"tof\",\"tog\",\"toh\",\"toi\",\"toj\",\"tol\",\"tom\",\"too\",\"top\",\"toq\",\"tor\",\"tos\",\"tou\",\"tov\",\"tow\",\"tox\",\"toy\",\"toz\",\"tpa\",\"tpc\",\"tpe\",\"tpf\",\"tpg\",\"tpi\",\"tpj\",\"tpk\",\"tpl\",\"tpm\",\"tpn\",\"tpo\",\"tpp\",\"tpq\",\"tpr\",\"tpt\",\"tpu\",\"tpv\",\"tpw\",\"tpx\",\"tpy\",\"tpz\",\"tqb\",\"tql\",\"tqm\",\"tqn\",\"tqo\",\"tqp\",\"tqq\",\"tqr\",\"tqt\",\"tqu\",\"tqw\",\"tra\",\"trb\",\"trc\",\"trd\",\"tre\",\"trf\",\"trg\",\"trh\",\"tri\",\"trj\",\"trk\",\"trl\",\"trm\",\"trn\",\"tro\",\"trp\",\"trq\",\"trr\",\"trs\",\"trt\",\"tru\",\"trv\",\"trw\",\"trx\",\"try\",\"trz\",\"tsa\",\"tsb\",\"tsc\",\"tsd\",\"tse\",\"tsf\",\"tsg\",\"tsh\",\"tsi\",\"tsj\",\"tsk\",\"tsl\",\"tsm\",\"tsp\",\"tsq\",\"tsr\",\"tss\",\"tst\",\"tsu\",\"tsv\",\"tsw\",\"tsx\",\"tsy\",\"tsz\",\"tta\",\"ttb\",\"ttc\",\"ttd\",\"tte\",\"ttf\",\"ttg\",\"tth\",\"tti\",\"ttj\",\"ttk\",\"ttl\",\"ttm\",\"ttn\",\"tto\",\"ttp\",\"ttq\",\"ttr\",\"tts\",\"ttt\",\"ttu\",\"ttv\",\"ttw\",\"tty\",\"ttz\",\"tua\",\"tub\",\"tuc\",\"tud\",\"tue\",\"tuf\",\"tug\",\"tuh\",\"tui\",\"tuj\",\"tul\",\"tum\",\"tun\",\"tuo\",\"tup\",\"tuq\",\"tus\",\"tut\",\"tuu\",\"tuv\",\"tuw\",\"tux\",\"tuy\",\"tuz\",\"tva\",\"tvd\",\"tve\",\"tvk\",\"tvl\",\"tvm\",\"tvn\",\"tvo\",\"tvs\",\"tvt\",\"tvu\",\"tvw\",\"tvy\",\"twa\",\"twb\",\"twc\",\"twd\",\"twe\",\"twf\",\"twg\",\"twh\",\"twl\",\"twm\",\"twn\",\"two\",\"twp\",\"twq\",\"twr\",\"twt\",\"twu\",\"tww\",\"twx\",\"twy\",\"txa\",\"txb\",\"txc\",\"txe\",\"txg\",\"txh\",\"txi\",\"txj\",\"txm\",\"txn\",\"txo\",\"txq\",\"txr\",\"txs\",\"txt\",\"txu\",\"txx\",\"txy\",\"tya\",\"tye\",\"tyh\",\"tyi\",\"tyj\",\"tyl\",\"tyn\",\"typ\",\"tyr\",\"tys\",\"tyt\",\"tyu\",\"tyv\",\"tyx\",\"tyz\",\"tza\",\"tzh\",\"tzj\",\"tzl\",\"tzm\",\"tzn\",\"tzo\",\"tzx\",\"uam\",\"uan\",\"uar\",\"uba\",\"ubi\",\"ubl\",\"ubr\",\"ubu\",\"uby\",\"uda\",\"ude\",\"udg\",\"udi\",\"udj\",\"udl\",\"udm\",\"udu\",\"ues\",\"ufi\",\"uga\",\"ugb\",\"uge\",\"ugn\",\"ugo\",\"ugy\",\"uha\",\"uhn\",\"uis\",\"uiv\",\"uji\",\"uka\",\"ukg\",\"ukh\",\"ukk\",\"ukl\",\"ukp\",\"ukq\",\"uks\",\"uku\",\"ukw\",\"uky\",\"ula\",\"ulb\",\"ulc\",\"ule\",\"ulf\",\"uli\",\"ulk\",\"ull\",\"ulm\",\"uln\",\"ulu\",\"ulw\",\"uma\",\"umb\",\"umc\",\"umd\",\"umg\",\"umi\",\"umm\",\"umn\",\"umo\",\"ump\",\"umr\",\"ums\",\"umu\",\"una\",\"und\",\"une\",\"ung\",\"unk\",\"unm\",\"unn\",\"unp\",\"unr\",\"unu\",\"unx\",\"unz\",\"uok\",\"upi\",\"upv\",\"ura\",\"urb\",\"urc\",\"ure\",\"urf\",\"urg\",\"urh\",\"uri\",\"urj\",\"urk\",\"url\",\"urm\",\"urn\",\"uro\",\"urp\",\"urr\",\"urt\",\"uru\",\"urv\",\"urw\",\"urx\",\"ury\",\"urz\",\"usa\",\"ush\",\"usi\",\"usk\",\"usp\",\"usu\",\"uta\",\"ute\",\"utp\",\"utr\",\"utu\",\"uum\",\"uun\",\"uur\",\"uuu\",\"uve\",\"uvh\",\"uvl\",\"uwa\",\"uya\",\"uzn\",\"uzs\",\"vaa\",\"vae\",\"vaf\",\"vag\",\"vah\",\"vai\",\"vaj\",\"val\",\"vam\",\"van\",\"vao\",\"vap\",\"var\",\"vas\",\"vau\",\"vav\",\"vay\",\"vbb\",\"vbk\",\"vec\",\"ved\",\"vel\",\"vem\",\"veo\",\"vep\",\"ver\",\"vgr\",\"vgt\",\"vic\",\"vid\",\"vif\",\"vig\",\"vil\",\"vin\",\"vis\",\"vit\",\"viv\",\"vka\",\"vki\",\"vkj\",\"vkk\",\"vkl\",\"vkm\",\"vko\",\"vkp\",\"vkt\",\"vku\",\"vlp\",\"vls\",\"vma\",\"vmb\",\"vmc\",\"vmd\",\"vme\",\"vmf\",\"vmg\",\"vmh\",\"vmi\",\"vmj\",\"vmk\",\"vml\",\"vmm\",\"vmp\",\"vmq\",\"vmr\",\"vms\",\"vmu\",\"vmv\",\"vmw\",\"vmx\",\"vmy\",\"vmz\",\"vnk\",\"vnm\",\"vnp\",\"vor\",\"vot\",\"vra\",\"vro\",\"vrs\",\"vrt\",\"vsi\",\"vsl\",\"vsv\",\"vto\",\"vum\",\"vun\",\"vut\",\"vwa\",\"waa\",\"wab\",\"wac\",\"wad\",\"wae\",\"waf\",\"wag\",\"wah\",\"wai\",\"waj\",\"wak\",\"wal\",\"wam\",\"wan\",\"wao\",\"wap\",\"waq\",\"war\",\"was\",\"wat\",\"wau\",\"wav\",\"waw\",\"wax\",\"way\",\"waz\",\"wba\",\"wbb\",\"wbe\",\"wbf\",\"wbh\",\"wbi\",\"wbj\",\"wbk\",\"wbl\",\"wbm\",\"wbp\",\"wbq\",\"wbr\",\"wbs\",\"wbt\",\"wbv\",\"wbw\",\"wca\",\"wci\",\"wdd\",\"wdg\",\"wdj\",\"wdk\",\"wdu\",\"wdy\",\"wea\",\"wec\",\"wed\",\"weg\",\"weh\",\"wei\",\"wem\",\"wen\",\"weo\",\"wep\",\"wer\",\"wes\",\"wet\",\"weu\",\"wew\",\"wfg\",\"wga\",\"wgb\",\"wgg\",\"wgi\",\"wgo\",\"wgu\",\"wgw\",\"wgy\",\"wha\",\"whg\",\"whk\",\"whu\",\"wib\",\"wic\",\"wie\",\"wif\",\"wig\",\"wih\",\"wii\",\"wij\",\"wik\",\"wil\",\"wim\",\"win\",\"wir\",\"wit\",\"wiu\",\"wiv\",\"wiw\",\"wiy\",\"wja\",\"wji\",\"wka\",\"wkb\",\"wkd\",\"wkl\",\"wku\",\"wkw\",\"wky\",\"wla\",\"wlc\",\"wle\",\"wlg\",\"wli\",\"wlk\",\"wll\",\"wlm\",\"wlo\",\"wlr\",\"wls\",\"wlu\",\"wlv\",\"wlw\",\"wlx\",\"wly\",\"wma\",\"wmb\",\"wmc\",\"wmd\",\"wme\",\"wmh\",\"wmi\",\"wmm\",\"wmn\",\"wmo\",\"wms\",\"wmt\",\"wmw\",\"wmx\",\"wnb\",\"wnc\",\"wnd\",\"wne\",\"wng\",\"wni\",\"wnk\",\"wnm\",\"wnn\",\"wno\",\"wnp\",\"wnu\",\"wnw\",\"wny\",\"woa\",\"wob\",\"woc\",\"wod\",\"woe\",\"wof\",\"wog\",\"woi\",\"wok\",\"wom\",\"won\",\"woo\",\"wor\",\"wos\",\"wow\",\"woy\",\"wpc\",\"wra\",\"wrb\",\"wrd\",\"wrg\",\"wrh\",\"wri\",\"wrk\",\"wrl\",\"wrm\",\"wrn\",\"wro\",\"wrp\",\"wrr\",\"wrs\",\"wru\",\"wrv\",\"wrw\",\"wrx\",\"wry\",\"wrz\",\"wsa\",\"wsg\",\"wsi\",\"wsk\",\"wsr\",\"wss\",\"wsu\",\"wsv\",\"wtf\",\"wth\",\"wti\",\"wtk\",\"wtm\",\"wtw\",\"wua\",\"wub\",\"wud\",\"wuh\",\"wul\",\"wum\",\"wun\",\"wur\",\"wut\",\"wuu\",\"wuv\",\"wux\",\"wuy\",\"wwa\",\"wwb\",\"wwo\",\"wwr\",\"www\",\"wxa\",\"wxw\",\"wya\",\"wyb\",\"wyi\",\"wym\",\"wyr\",\"wyy\",\"xaa\",\"xab\",\"xac\",\"xad\",\"xae\",\"xag\",\"xai\",\"xaj\",\"xak\",\"xal\",\"xam\",\"xan\",\"xao\",\"xap\",\"xaq\",\"xar\",\"xas\",\"xat\",\"xau\",\"xav\",\"xaw\",\"xay\",\"xba\",\"xbb\",\"xbc\",\"xbd\",\"xbe\",\"xbg\",\"xbi\",\"xbj\",\"xbm\",\"xbn\",\"xbo\",\"xbp\",\"xbr\",\"xbw\",\"xbx\",\"xby\",\"xcb\",\"xcc\",\"xce\",\"xcg\",\"xch\",\"xcl\",\"xcm\",\"xcn\",\"xco\",\"xcr\",\"xct\",\"xcu\",\"xcv\",\"xcw\",\"xcy\",\"xda\",\"xdc\",\"xdk\",\"xdm\",\"xdo\",\"xdy\",\"xeb\",\"xed\",\"xeg\",\"xel\",\"xem\",\"xep\",\"xer\",\"xes\",\"xet\",\"xeu\",\"xfa\",\"xga\",\"xgb\",\"xgd\",\"xgf\",\"xgg\",\"xgi\",\"xgl\",\"xgm\",\"xgn\",\"xgr\",\"xgu\",\"xgw\",\"xha\",\"xhc\",\"xhd\",\"xhe\",\"xhr\",\"xht\",\"xhu\",\"xhv\",\"xia\",\"xib\",\"xii\",\"xil\",\"xin\",\"xip\",\"xir\",\"xis\",\"xiv\",\"xiy\",\"xjb\",\"xjt\",\"xka\",\"xkb\",\"xkc\",\"xkd\",\"xke\",\"xkf\",\"xkg\",\"xkh\",\"xki\",\"xkj\",\"xkk\",\"xkl\",\"xkn\",\"xko\",\"xkp\",\"xkq\",\"xkr\",\"xks\",\"xkt\",\"xku\",\"xkv\",\"xkw\",\"xkx\",\"xky\",\"xkz\",\"xla\",\"xlb\",\"xlc\",\"xld\",\"xle\",\"xlg\",\"xli\",\"xln\",\"xlo\",\"xlp\",\"xls\",\"xlu\",\"xly\",\"xma\",\"xmb\",\"xmc\",\"xmd\",\"xme\",\"xmf\",\"xmg\",\"xmh\",\"xmj\",\"xmk\",\"xml\",\"xmm\",\"xmn\",\"xmo\",\"xmp\",\"xmq\",\"xmr\",\"xms\",\"xmt\",\"xmu\",\"xmv\",\"xmw\",\"xmx\",\"xmy\",\"xmz\",\"xna\",\"xnb\",\"xnd\",\"xng\",\"xnh\",\"xni\",\"xnk\",\"xnn\",\"xno\",\"xnr\",\"xns\",\"xnt\",\"xnu\",\"xny\",\"xnz\",\"xoc\",\"xod\",\"xog\",\"xoi\",\"xok\",\"xom\",\"xon\",\"xoo\",\"xop\",\"xor\",\"xow\",\"xpa\",\"xpc\",\"xpe\",\"xpg\",\"xpi\",\"xpj\",\"xpk\",\"xpm\",\"xpn\",\"xpo\",\"xpp\",\"xpq\",\"xpr\",\"xps\",\"xpt\",\"xpu\",\"xpy\",\"xqa\",\"xqt\",\"xra\",\"xrb\",\"xrd\",\"xre\",\"xrg\",\"xri\",\"xrm\",\"xrn\",\"xrq\",\"xrr\",\"xrt\",\"xru\",\"xrw\",\"xsa\",\"xsb\",\"xsc\",\"xsd\",\"xse\",\"xsh\",\"xsi\",\"xsj\",\"xsl\",\"xsm\",\"xsn\",\"xso\",\"xsp\",\"xsq\",\"xsr\",\"xss\",\"xsu\",\"xsv\",\"xsy\",\"xta\",\"xtb\",\"xtc\",\"xtd\",\"xte\",\"xtg\",\"xth\",\"xti\",\"xtj\",\"xtl\",\"xtm\",\"xtn\",\"xto\",\"xtp\",\"xtq\",\"xtr\",\"xts\",\"xtt\",\"xtu\",\"xtv\",\"xtw\",\"xty\",\"xtz\",\"xua\",\"xub\",\"xud\",\"xug\",\"xuj\",\"xul\",\"xum\",\"xun\",\"xuo\",\"xup\",\"xur\",\"xut\",\"xuu\",\"xve\",\"xvi\",\"xvn\",\"xvo\",\"xvs\",\"xwa\",\"xwc\",\"xwd\",\"xwe\",\"xwg\",\"xwj\",\"xwk\",\"xwl\",\"xwo\",\"xwr\",\"xwt\",\"xww\",\"xxb\",\"xxk\",\"xxm\",\"xxr\",\"xxt\",\"xya\",\"xyb\",\"xyj\",\"xyk\",\"xyl\",\"xyt\",\"xyy\",\"xzh\",\"xzm\",\"xzp\",\"yaa\",\"yab\",\"yac\",\"yad\",\"yae\",\"yaf\",\"yag\",\"yah\",\"yai\",\"yaj\",\"yak\",\"yal\",\"yam\",\"yan\",\"yao\",\"yap\",\"yaq\",\"yar\",\"yas\",\"yat\",\"yau\",\"yav\",\"yaw\",\"yax\",\"yay\",\"yaz\",\"yba\",\"ybb\",\"ybd\",\"ybe\",\"ybh\",\"ybi\",\"ybj\",\"ybk\",\"ybl\",\"ybm\",\"ybn\",\"ybo\",\"ybx\",\"yby\",\"ych\",\"ycl\",\"ycn\",\"ycp\",\"yda\",\"ydd\",\"yde\",\"ydg\",\"ydk\",\"yds\",\"yea\",\"yec\",\"yee\",\"yei\",\"yej\",\"yel\",\"yen\",\"yer\",\"yes\",\"yet\",\"yeu\",\"yev\",\"yey\",\"yga\",\"ygi\",\"ygl\",\"ygm\",\"ygp\",\"ygr\",\"ygs\",\"ygu\",\"ygw\",\"yha\",\"yhd\",\"yhl\",\"yhs\",\"yia\",\"yif\",\"yig\",\"yih\",\"yii\",\"yij\",\"yik\",\"yil\",\"yim\",\"yin\",\"yip\",\"yiq\",\"yir\",\"yis\",\"yit\",\"yiu\",\"yiv\",\"yix\",\"yiy\",\"yiz\",\"yka\",\"ykg\",\"yki\",\"ykk\",\"ykl\",\"ykm\",\"ykn\",\"yko\",\"ykr\",\"ykt\",\"yku\",\"yky\",\"yla\",\"ylb\",\"yle\",\"ylg\",\"yli\",\"yll\",\"ylm\",\"yln\",\"ylo\",\"ylr\",\"ylu\",\"yly\",\"yma\",\"ymb\",\"ymc\",\"ymd\",\"yme\",\"ymg\",\"ymh\",\"ymi\",\"ymk\",\"yml\",\"ymm\",\"ymn\",\"ymo\",\"ymp\",\"ymq\",\"ymr\",\"yms\",\"ymt\",\"ymx\",\"ymz\",\"yna\",\"ynd\",\"yne\",\"yng\",\"ynh\",\"ynk\",\"ynl\",\"ynn\",\"yno\",\"ynq\",\"yns\",\"ynu\",\"yob\",\"yog\",\"yoi\",\"yok\",\"yol\",\"yom\",\"yon\",\"yos\",\"yot\",\"yox\",\"yoy\",\"ypa\",\"ypb\",\"ypg\",\"yph\",\"ypk\",\"ypm\",\"ypn\",\"ypo\",\"ypp\",\"ypz\",\"yra\",\"yrb\",\"yre\",\"yri\",\"yrk\",\"yrl\",\"yrm\",\"yrn\",\"yro\",\"yrs\",\"yrw\",\"yry\",\"ysc\",\"ysd\",\"ysg\",\"ysl\",\"ysn\",\"yso\",\"ysp\",\"ysr\",\"yss\",\"ysy\",\"yta\",\"ytl\",\"ytp\",\"ytw\",\"yty\",\"yua\",\"yub\",\"yuc\",\"yud\",\"yue\",\"yuf\",\"yug\",\"yui\",\"yuj\",\"yuk\",\"yul\",\"yum\",\"yun\",\"yup\",\"yuq\",\"yur\",\"yut\",\"yuu\",\"yuw\",\"yux\",\"yuy\",\"yuz\",\"yva\",\"yvt\",\"ywa\",\"ywg\",\"ywl\",\"ywn\",\"ywq\",\"ywr\",\"ywt\",\"ywu\",\"yww\",\"yxa\",\"yxg\",\"yxl\",\"yxm\",\"yxu\",\"yxy\",\"yyr\",\"yyu\",\"yyz\",\"yzg\",\"yzk\",\"zaa\",\"zab\",\"zac\",\"zad\",\"zae\",\"zaf\",\"zag\",\"zah\",\"zai\",\"zaj\",\"zak\",\"zal\",\"zam\",\"zao\",\"zap\",\"zaq\",\"zar\",\"zas\",\"zat\",\"zau\",\"zav\",\"zaw\",\"zax\",\"zay\",\"zaz\",\"zbc\",\"zbe\",\"zbl\",\"zbt\",\"zbw\",\"zca\",\"zch\",\"zdj\",\"zea\",\"zeg\",\"zeh\",\"zen\",\"zga\",\"zgb\",\"zgh\",\"zgm\",\"zgn\",\"zgr\",\"zhb\",\"zhd\",\"zhi\",\"zhn\",\"zhw\",\"zhx\",\"zia\",\"zib\",\"zik\",\"zil\",\"zim\",\"zin\",\"zir\",\"ziw\",\"ziz\",\"zka\",\"zkb\",\"zkd\",\"zkg\",\"zkh\",\"zkk\",\"zkn\",\"zko\",\"zkp\",\"zkr\",\"zkt\",\"zku\",\"zkv\",\"zkz\",\"zle\",\"zlj\",\"zlm\",\"zln\",\"zlq\",\"zls\",\"zlw\",\"zma\",\"zmb\",\"zmc\",\"zmd\",\"zme\",\"zmf\",\"zmg\",\"zmh\",\"zmi\",\"zmj\",\"zmk\",\"zml\",\"zmm\",\"zmn\",\"zmo\",\"zmp\",\"zmq\",\"zmr\",\"zms\",\"zmt\",\"zmu\",\"zmv\",\"zmw\",\"zmx\",\"zmy\",\"zmz\",\"zna\",\"znd\",\"zne\",\"zng\",\"znk\",\"zns\",\"zoc\",\"zoh\",\"zom\",\"zoo\",\"zoq\",\"zor\",\"zos\",\"zpa\",\"zpb\",\"zpc\",\"zpd\",\"zpe\",\"zpf\",\"zpg\",\"zph\",\"zpi\",\"zpj\",\"zpk\",\"zpl\",\"zpm\",\"zpn\",\"zpo\",\"zpp\",\"zpq\",\"zpr\",\"zps\",\"zpt\",\"zpu\",\"zpv\",\"zpw\",\"zpx\",\"zpy\",\"zpz\",\"zqe\",\"zra\",\"zrg\",\"zrn\",\"zro\",\"zrp\",\"zrs\",\"zsa\",\"zsk\",\"zsl\",\"zsm\",\"zsr\",\"zsu\",\"zte\",\"ztg\",\"ztl\",\"ztm\",\"ztn\",\"ztp\",\"ztq\",\"zts\",\"ztt\",\"ztu\",\"ztx\",\"zty\",\"zua\",\"zuh\",\"zum\",\"zun\",\"zuy\",\"zwa\",\"zxx\",\"zyb\",\"zyg\",\"zyj\",\"zyn\",\"zyp\",\"zza\",\"zzj\"]\n;return axe.utils.validLangs=function(){\"use strict\";return I},commons}()})}(\"object\"==typeof window?window:this);";
+const axeLibSource="/*! aXe v3.0.0-beta.2\n * Copyright (c) 2018 Deque Systems, Inc.\n *\n * Your use of this Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n *\n * This entire copyright notice must appear in every copy of this file you\n * distribute or in any file that contains substantial portions of this source\n * code.\n */\n!function a(window){function b(a){this.name=\"SupportError\",this.cause=a.cause,this.message=\"`\"+a.cause+\"` - feature unsupported in your environment.\",a.ruleId&&(this.ruleId=a.ruleId,this.message+=\" Skipping \"+this.ruleId+\" rule.\"),this.stack=(new Error).stack}function c(a){\"use strict\";var b;return a?(b=axe.utils.clone(a),b.commons=a.commons):b={},b.reporter=b.reporter||null,b.rules=b.rules||[],b.checks=b.checks||[],b.data=Object.assign({checks:{},rules:{}},b.data),b}function d(a,b,c){\"use strict\";var d,e;for(d=0,e=a.length;d<e;d++)b[c](a[d])}function e(a){this.brand=\"axe\",this.application=\"axeAPI\",this.tagExclude=[\"experimental\"],this.defaultConfig=a,this._init()}function f(a,b,c){var d=a.brand,e=a.application;return axe.constants.helpUrlBase+d+\"/\"+(c||axe.version.substring(0,axe.version.lastIndexOf(\".\")))+\"/\"+b+\"?application=\"+e}function g(a){\"use strict\";this.id=a.id,this.data=null,this.relatedNodes=[],this.result=null}function h(a){\"use strict\";return\"string\"==typeof a?new Function(\"return \"+a+\";\")():a}function i(a){a&&(this.id=a.id,this.configure(a))}function j(a,b){\"use strict\";if(!axe.utils.isHidden(b)){axe.utils.findBy(a,\"node\",b)||a.push({node:b,include:[],exclude:[]})}}function k(a,b,c){\"use strict\";a.frames=a.frames||[];var d,e,f=document.querySelectorAll(c.shift());a:for(var g=0,h=f.length;g<h;g++){e=f[g];for(var i=0,j=a.frames.length;i<j;i++)if(a.frames[i].node===e){a.frames[i][b].push(c);break a}d={node:e,include:[],exclude:[]},c&&d[b].push(c),a.frames.push(d)}}function l(a){\"use strict\";if(a&&\"object\"===(void 0===a?\"undefined\":za(a))||a instanceof NodeList){if(a instanceof Node)return{include:[a],exclude:[]};if(a.hasOwnProperty(\"include\")||a.hasOwnProperty(\"exclude\"))return{include:a.include&&+a.include.length?a.include:[document],exclude:a.exclude||[]};if(a.length===+a.length)return{include:a,exclude:[]}}return\"string\"==typeof a?{include:[a],exclude:[]}:{include:[document],exclude:[]}}function m(a,b){\"use strict\";for(var c,d,e=[],f=0,g=a[b].length;f<g;f++){if(\"string\"==typeof(c=a[b][f])){d=Array.from(document.querySelectorAll(c)),e=e.concat(d.map(function(a){return axe.utils.getFlattenedTree(a)[0]}));break}!c||!c.length||c instanceof Node?c instanceof Node&&e.push(axe.utils.getFlattenedTree(c)[0]):c.length>1?k(a,b,c):(d=Array.from(document.querySelectorAll(c[0])),e=e.concat(d.map(function(a){return axe.utils.getFlattenedTree(a)[0]})))}return e.filter(function(a){return a})}function n(a){\"use strict\";if(0===a.include.length){if(0===a.frames.length){var b=axe.utils.respondable.isInFrame()?\"frame\":\"page\";return new Error(\"No elements found for include in \"+b+\" Context\")}a.frames.forEach(function(a,b){if(0===a.include.length)return new Error(\"No elements found for include in Context of frame \"+b)})}}function o(a){\"use strict\";var b=this;this.frames=[],this.initiator=!a||\"boolean\"!=typeof a.initiator||a.initiator,this.page=!1,a=l(a),this.exclude=a.exclude,this.include=a.include,this.include=m(this,\"include\"),this.exclude=m(this,\"exclude\"),axe.utils.select(\"frame, iframe\",this).forEach(function(a){wa(a,b)&&j(b.frames,a.actualNode)}),1===this.include.length&&this.include[0].actualNode===document.documentElement&&(this.page=!0);var c=n(this);if(c instanceof Error)throw c;Array.isArray(this.include)||(this.include=Array.from(this.include)),this.include.sort(axe.utils.nodeSorter)}function p(a){\"use strict\";this.id=a.id,this.result=axe.constants.NA,this.pageLevel=a.pageLevel,this.impact=null,this.nodes=[]}function q(a,b){\"use strict\";this._audit=b,this.id=a.id,this.selector=a.selector||\"*\",this.excludeHidden=\"boolean\"!=typeof a.excludeHidden||a.excludeHidden,this.enabled=\"boolean\"!=typeof a.enabled||a.enabled,this.pageLevel=\"boolean\"==typeof a.pageLevel&&a.pageLevel,this.any=a.any||[],this.all=a.all||[],this.none=a.none||[],this.tags=a.tags||[],a.matches&&(this.matches=h(a.matches))}function r(a){\"use strict\";return axe.utils.getAllChecks(a).map(function(b){var c=a._audit.checks[b.id||b];return c&&\"function\"==typeof c.after?c:null}).filter(Boolean)}function s(a,b){\"use strict\";var c=[];return a.forEach(function(a){axe.utils.getAllChecks(a).forEach(function(a){a.id===b&&c.push(a)})}),c}function t(a){\"use strict\";return a.filter(function(a){return!0!==a.filtered})}function u(a){\"use strict\";var b=[\"any\",\"all\",\"none\"],c=a.nodes.filter(function(a){var c=0;return b.forEach(function(b){a[b]=t(a[b]),c+=a[b].length}),c>0});return a.pageLevel&&c.length&&(c=[c.reduce(function(a,c){if(a)return b.forEach(function(b){a[b].push.apply(a[b],c[b])}),a})]),c}function v(a,b){\"use strict\";if(a=a||function(){},b=b||axe.log,!axe._audit)throw new Error(\"No audit configured\");var c=axe.utils.queue(),d=[];Object.keys(axe.plugins).forEach(function(a){c.defer(function(b){var c=function(a){d.push(a),b()};try{axe.plugins[a].cleanup(b,c)}catch(a){c(a)}})});var e=axe.utils.getFlattenedTree(document.body);axe.utils.querySelectorAll(e,\"iframe, frame\").forEach(function(a){c.defer(function(b,c){return axe.utils.sendCommandToFrame(a.actualNode,{command:\"cleanup-plugin\"},b,c)})}),c.then(function(c){0===d.length?a(c):b(d)}).catch(b)}function w(a){\"use strict\";var b;if(!(b=axe._audit))throw new Error(\"No audit configured\");a.reporter&&(\"function\"==typeof a.reporter||Ca[a.reporter])&&(b.reporter=a.reporter),a.checks&&a.checks.forEach(function(a){b.addCheck(a)});var c=[];a.rules&&a.rules.forEach(function(a){c.push(a.id),b.addRule(a)}),a.disableOtherRules&&b.rules.forEach(function(a){!1===c.includes(a.id)&&(a.enabled=!1)}),void 0!==a.branding?b.setBranding(a.branding):b._constructHelpUrls(),a.tagExclude&&(b.tagExclude=a.tagExclude)}function x(a,b,c){\"use strict\";var d=c,e=function(a){a instanceof Error==!1&&(a=new Error(a)),c(a)},f=a&&a.context||{};f.hasOwnProperty(\"include\")&&!f.include.length&&(f.include=[document]);var g=a&&a.options||{};switch(a.command){case\"rules\":return A(f,g,d,e);case\"cleanup-plugin\":return v(d,e);default:if(axe._audit&&axe._audit.commands&&axe._audit.commands[a.command])return axe._audit.commands[a.command](a,c)}}function y(a){\"use strict\";this._run=a.run,this._collect=a.collect,this._registry={},a.commands.forEach(function(a){axe._audit.registerCommand(a)})}function z(){\"use strict\";var a=axe._audit;if(!a)throw new Error(\"No audit configured\");a.resetRulesAndChecks()}function A(a,b,c,d){\"use strict\";try{a=new o(a)}catch(a){return d(a)}var e=axe.utils.queue(),f=axe._audit;b.performanceTimer&&axe.utils.performanceTimer.auditStart(),a.frames.length&&!1!==b.iframes&&e.defer(function(c,d){axe.utils.collectResultsFromFrames(a,b,\"rules\",null,c,d)});var g=void 0;e.defer(function(c,d){b.restoreScroll&&(g=axe.utils.getScrollState()),f.run(a,b,c,d)}),e.then(function(e){try{g&&axe.utils.setScrollState(g),b.performanceTimer&&axe.utils.performanceTimer.auditEnd();var h=axe.utils.mergeResults(e.map(function(a){return{results:a}}));a.initiator&&(h=f.after(h,b),h.forEach(axe.utils.publishMetaData),h=h.map(axe.utils.finalizeRuleResult));try{c(h)}catch(a){axe.log(a)}}catch(a){d(a)}}).catch(d)}function B(a){\"use strict\";switch(!0){case\"string\"==typeof a:case Array.isArray(a):case Node&&a instanceof Node:case NodeList&&a instanceof NodeList:return!0;case\"object\"!==(void 0===a?\"undefined\":za(a)):return!1;case void 0!==a.include:case void 0!==a.exclude:case\"number\"==typeof a.length:return!0;default:return!1}}function C(a,b,c){\"use strict\";var d=new TypeError(\"axe.run arguments are invalid\");if(!B(a)){if(void 0!==c)throw d;c=b,b=a,a=document}if(\"object\"!==(void 0===b?\"undefined\":za(b))){if(void 0!==c)throw d;c=b,b={}}if(\"function\"!=typeof c&&void 0!==c)throw d;return{context:a,options:b,callback:c||Da}}function D(a,b){\"use strict\";[\"any\",\"all\",\"none\"].forEach(function(c){Array.isArray(a[c])&&a[c].filter(function(a){return Array.isArray(a.relatedNodes)}).forEach(function(a){a.relatedNodes=a.relatedNodes.map(function(a){var c={html:a.source};return b.elementRef&&!a.fromFrame&&(c.element=a.element),(!1!==b.selectors||a.fromFrame)&&(c.target=a.selector),b.xpath&&(c.xpath=a.xpath),c})})})}function E(a,b){return Ja.reduce(function(c,d){return c[d]=(a[d]||[]).map(function(a){return b(a,d)}),c},{})}function F(a,b,c){var d=Object.assign({},b);d.nodes=(d[c]||[]).concat(),axe.constants.resultGroups.forEach(function(a){delete d[a]}),a[c].push(d)}function G(a,b,c){\"use strict\";var d=window.getComputedStyle(a,null),e=!1;return!!d&&(b.forEach(function(a){d.getPropertyValue(a.property)===a.value&&(e=!0)}),!!e||!(a.nodeName.toUpperCase()===c.toUpperCase()||!a.parentNode)&&G(a.parentNode,b,c))}function H(a,b){\"use strict\";var c;return axe._tree&&(c=axe.utils.getSelector(b)),new Error(a+\": \"+(c||b))}function I(a,b,c,d,e,f){\"use strict\";var g=axe.utils.queue();a.frames.forEach(function(e){var f={options:b,command:c,parameter:d,context:{initiator:!1,page:a.page,include:e.include||[],exclude:e.exclude||[]}};g.defer(function(a,b){var c=e.node;axe.utils.sendCommandToFrame(c,f,function(b){if(b)return a({results:b,frameElement:c,frame:axe.utils.getSelector(c)});a(null)},b)})}),g.then(function(a){e(axe.utils.mergeResults(a,b))}).catch(f)}function J(a,b){if(b=b||300,a.length>b){var c=a.indexOf(\">\");a=a.substring(0,c+1)}return a}function K(a){var b=a.outerHTML;return b||\"function\"!=typeof XMLSerializer||(b=(new XMLSerializer).serializeToString(a)),J(b||\"\")}function L(a,b,c){this._fromFrame=!!c,this.spec=c||{},b&&b.absolutePaths&&(this._options={toRoot:!0}),this.source=void 0!==this.spec.source?this.spec.source:K(a),this._element=a}function M(a,b){return{shadowId:b,children:[],actualNode:a}}function N(a){var b=[];for(a=a.firstChild;a;)b.push(a),a=a.nextSibling;return b}function O(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"\";return 0!==a.length&&(a.match(/[0-9]/g)||\"\").length>=a.length/2}function P(a,b){return[a.substring(0,b),a.substring(b)]}function Q(a){var b=a,c=\"\",d=\"\",e=\"\",f=\"\",g=\"\",h=\"\";if(a.includes(\"#\")){var i=P(a,a.indexOf(\"#\")),j=Ka(i,2);a=j[0],h=j[1]}if(a.includes(\"?\")){var k=P(a,a.indexOf(\"?\")),l=Ka(k,2);a=l[0],g=l[1]}if(a.includes(\"://\")){var m=a.split(\"://\"),n=Ka(m,2);c=n[0],a=n[1];var o=P(a,a.indexOf(\"/\")),p=Ka(o,2);d=p[0],a=p[1]}else if(\"//\"===a.substr(0,2)){a=a.substr(2);var q=P(a,a.indexOf(\"/\")),r=Ka(q,2);d=r[0],a=r[1]}if(\"www.\"===d.substr(0,4)&&(d=d.substr(4)),d&&d.includes(\":\")){var s=P(d,d.indexOf(\":\")),t=Ka(s,2);d=t[0],e=t[1]}return f=a,{original:b,protocol:c,domain:d,port:e,path:f,query:g,hash:h}}function R(a,b){var c=b.name,d=void 0;if(-1!==c.indexOf(\"href\")||-1!==c.indexOf(\"src\")){var e=encodeURI(axe.utils.getFriendlyUriEnd(a.getAttribute(c)));if(!e)return;d=La(b.name)+'$=\"'+e+'\"'}else d=La(c)+'=\"'+La(b.value)+'\"';return d}function S(a,b){return a.count<b.count?-1:a.count===b.count?0:1}function T(a){return!Na.includes(a.name)&&-1===a.name.indexOf(\":\")&&(!a.value||a.value.length<Oa)}function U(a,b){var c=[],d=b.classes,e=b.tags;return a.classList&&Array.from(a.classList).forEach(function(b){var f=La(b);d[f]<e[a.nodeName]&&c.push({name:f,count:d[f],species:\"class\"})}),c.sort(S)}function V(a,b){var c=a.parentNode&&Array.from(a.parentNode.children||\"\")||[];if(c.find(function(c){return c!==a&&axe.utils.matchesSelector(c,b)}))return\":nth-child(\"+(1+c.indexOf(a))+\")\";return\"\"}function W(a){if(a.getAttribute(\"id\")){var b=a.getRootNode&&a.getRootNode()||document,c=\"#\"+La(a.getAttribute(\"id\")||\"\");return c.match(/player_uid_/)||1!==b.querySelectorAll(c).length?void 0:c}}function X(a){return void 0===Ma&&(Ma=axe.utils.isXHTML(document)),La(Ma?a.localName:a.nodeName.toLowerCase())}function Y(a,b){var c=[],d=b.attributes,e=b.tags;return a.attributes&&Array.from(a.attributes).filter(T).forEach(function(b){var f=R(a,b);f&&d[f]<e[a.nodeName]&&c.push({name:f,count:d[f],species:\"attribute\"})}),c.sort(S)}function Z(a,b){var c=\"\",d=void 0,e=U(a,b),f=Y(a,b);return e.length&&1===e[0].count?d=[e[0]]:f.length&&1===f[0].count?(d=[f[0]],c=X(a)):(d=e.concat(f),d.sort(S),d=d.slice(0,3),d.some(function(a){return\"class\"===a.species})?d.sort(function(a,b){return a.species!==b.species&&\"class\"===a.species?-1:a.species===b.species?0:1}):c=X(a)),c+=d.reduce(function(a,b){switch(b.species){case\"class\":return a+\".\"+b.name;case\"attribute\":return a+\"[\"+b.name+\"]\"}return a},\"\")}function $(a,b,c){axe._selectorData||(axe._selectorData=axe.utils.getSelectorData(axe._tree));var d=b.toRoot,e=void 0!==d&&d,f=void 0,g=void 0;do{var h=W(a);h||(h=Z(a,axe._selectorData),h+=V(a,h)),f=f?h+\" > \"+f:h,g=g?g.filter(function(a){return axe.utils.matchesSelector(a,f)}):Array.from(c.querySelectorAll(f)),a=a.parentElement}while((g.length>1||e)&&a&&11!==a.nodeType);return 1===g.length?f:-1!==f.indexOf(\" > \")?\":root\"+f.substring(f.indexOf(\" > \")):\":root\"}function _(a,b){var c,d;if(!a)return[];if(!b&&9===a.nodeType)return b=[{str:\"html\"}];if(b=b||[],a.parentNode&&a.parentNode!==a&&(b=_(a.parentNode,b)),a.previousSibling){d=1,c=a.previousSibling;do{1===c.nodeType&&c.nodeName===a.nodeName&&d++,c=c.previousSibling}while(c);1===d&&(d=null)}else if(a.nextSibling){c=a.nextSibling;do{1===c.nodeType&&c.nodeName===a.nodeName?(d=1,c=null):(d=null,c=c.previousSibling)}while(c)}if(1===a.nodeType){var e={};e.str=a.nodeName.toLowerCase();var f=a.getAttribute&&axe.utils.escapeSelector(a.getAttribute(\"id\"));f&&1===a.ownerDocument.querySelectorAll(\"#\"+f).length&&(e.id=a.getAttribute(\"id\")),d>1&&(e.count=d),b.push(e)}return b}function aa(a){return a.reduce(function(a,b){return b.id?\"/\"+b.str+\"[@id='\"+b.id+\"']\":a+\"/\"+b.str+(b.count>0?\"[\"+b.count+\"]\":\"\")},\"\")}function ba(a){\"use strict\";if(Pa&&Pa.parentNode)return void 0===Pa.styleSheet?Pa.appendChild(document.createTextNode(a)):Pa.styleSheet.cssText+=a,Pa;if(a){var b=document.head||document.getElementsByTagName(\"head\")[0];return Pa=document.createElement(\"style\"),Pa.type=\"text/css\",void 0===Pa.styleSheet?Pa.appendChild(document.createTextNode(a)):Pa.styleSheet.cssText=a,b.appendChild(Pa),Pa}}function ca(a,b,c,d){\"use strict\";var e=axe.utils.getXpath(c),f={element:c,selector:d,xpath:e};a.forEach(function(a){a.node=axe.utils.DqElement.fromFrame(a.node,b,f);var c=axe.utils.getAllChecks(a);c.length&&c.forEach(function(a){a.relatedNodes=a.relatedNodes.map(function(a){return axe.utils.DqElement.fromFrame(a,b,f)})})})}function da(a,b){\"use strict\";for(var c,d,e=b[0].node,f=0,g=a.length;f<g;f++)if(d=a[f].node,(c=axe.utils.nodeSorter({actualNode:d.element},{actualNode:e.element}))>0||0===c&&e.selector.length<d.selector.length)return void a.splice.apply(a,[f,0].concat(b));a.push.apply(a,b)}function ea(a){\"use strict\";return a&&a.results?Array.isArray(a.results)?a.results.length?a.results:null:[a.results]:null}function fa(a,b){function c(a){return a.incomplete&&a.incomplete.default?a.incomplete.default:Aa.incompleteFallbackMessage()}if(!a||!a.missingData)return c(b);try{var d=b.incomplete[a.missingData[0].reason];if(!d)throw new Error;return d}catch(d){return\"string\"==typeof a.missingData?b.incomplete[a.missingData]:c(b)}}function ga(a,b){\"use strict\";return function(c){var d=a[c.id]||{},e=d.messages||{},f=Object.assign({},d);delete f.messages,void 0===c.result?\"object\"===za(e.incomplete)?f.message=function(){return fa(c.data,e)}:f.message=e.incomplete:f.message=c.result===b?e.pass:e.fail,axe.utils.extendMetaData(c,f)}}function ha(a,b){return 1===a.nodeType&&(\"*\"===b.tag||a.nodeName.toLowerCase()===b.tag)}function ia(a,b){return!b.classes||b.classes.reduce(function(b,c){return b&&a.className&&a.className.match(c.regexp)},!0)}function ja(a,b){return!b.attributes||b.attributes.reduce(function(b,c){var d=a.getAttribute(c.key);return b&&null!==d&&(!c.value||c.test(d))},!0)}function ka(a,b){return!b.id||a.id===b.id}function la(a,b){return!(b.pseudos&&!b.pseudos.reduce(function(b,c){if(\"not\"===c.name)return b&&!Sa([a],c.expressions,!1).length;throw new Error(\"the pseudo selector \"+c.name+\" has not yet been implemented\")},!0))}function ma(a){/*! Credit Mootools Copyright Mootools, MIT License */\nif(a)return a.map(function(a){var b,c,d=a.name.replace(Ua,\"\"),e=(a.value||\"\").replace(Ua,\"\");switch(a.operator){case\"^=\":c=new RegExp(\"^\"+Ta(e));break;case\"$=\":c=new RegExp(Ta(e)+\"$\");break;case\"~=\":c=new RegExp(\"(^|\\\\s)\"+Ta(e)+\"(\\\\s|$)\");break;case\"|=\":c=new RegExp(\"^\"+Ta(e)+\"(-|$)\");break;case\"=\":b=function(a){return e===a};break;case\"*=\":b=function(a){return a&&a.includes(e)};break;case\"!=\":b=function(a){return e!==a};break;default:b=function(a){return!!a}}return\"\"===e&&/^[*$^]=$/.test(a.operator)&&(b=function(){return!1}),b||(b=function(a){return a&&c.test(a)}),{key:d,value:e,test:b}})}function na(a){if(a)return a.map(function(a){return a=a.replace(Ua,\"\"),{value:a,regexp:new RegExp(\"(^|\\\\s)\"+Ta(a)+\"(\\\\s|$)\")}})}function oa(a){if(a)return a.map(function(a){var b;return\"not\"===a.name&&(b=axe.utils.cssParser.parse(a.value),b=b.selectors?b.selectors:[b],b=Ra(b)),{name:a.name,expressions:b,value:a.value}})}function pa(a,b,c,d){var e={nodes:a.slice(),anyLevel:b,thisLevel:c,parentShadowId:d};return e.nodes.reverse(),e}function qa(a,b){return ha(a.actualNode,b[0])&&ia(a.actualNode,b[0])&&ja(a.actualNode,b[0])&&ka(a.actualNode,b[0])&&la(a,b[0])}function ra(a,b){\"use strict\";var c,d,e=axe._audit&&axe._audit.tagExclude?axe._audit.tagExclude:[];return b.hasOwnProperty(\"include\")||b.hasOwnProperty(\"exclude\")?(c=b.include||[],c=Array.isArray(c)?c:[c],d=b.exclude||[],d=Array.isArray(d)?d:[d],d=d.concat(e.filter(function(a){return-1===c.indexOf(a)}))):(c=Array.isArray(b)?b:[b],d=e.filter(function(a){return-1===c.indexOf(a)})),!!(c.some(function(b){return-1!==a.tags.indexOf(b)})||0===c.length&&!1!==a.enabled)&&d.every(function(b){return-1===a.tags.indexOf(b)})}function sa(a){var b=window.getComputedStyle(a),c=\"visible\"===b.getPropertyValue(\"overflow-y\"),d=\"visible\"===b.getPropertyValue(\"overflow-x\");if(!c&&a.scrollHeight>a.clientHeight||!d&&a.scrollWidth>a.clientWidth)return{elm:a,top:a.scrollTop,left:a.scrollLeft}}function ta(a,b,c){if(a===window)return a.scroll(b,c);a.scrollTop=b,a.scrollLeft=c}function ua(a){return Array.from(a.children).reduce(function(a,b){var c=sa(b);return c&&a.push(c),a.concat(ua(b))},[])}function va(a){\"use strict\";return a.sort(function(a,b){return axe.utils.contains(a,b)?1:-1})[0]}function wa(a,b){\"use strict\";var c=b.include&&va(b.include.filter(function(b){return axe.utils.contains(b,a)})),d=b.exclude&&va(b.exclude.filter(function(b){return axe.utils.contains(b,a)}));return!!(!d&&c||d&&axe.utils.contains(d,c))}function xa(a,b){\"use strict\";var c;if(0===a.length)return b;a.length<b.length&&(c=a,a=b,b=c);for(var d=0,e=b.length;d<e;d++)a.includes(b[d])||a.push(b[d]);return a}function ya(a){return a.reduce(function(a,b){return a.length&&a[a.length-1].actualNode.contains(b.actualNode)||a.push(b),a},[])}var document=window.document,za=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a},axe=axe||{};axe.version=\"3.0.0-beta.2\",\"function\"==typeof define&&define.amd&&define([],function(){\"use strict\";return axe}),\"object\"===(\"undefined\"==typeof module?\"undefined\":za(module))&&module.exports&&\"function\"==typeof a.toString&&(axe.source=\"(\"+a.toString()+')(typeof window === \"object\" ? window : this);',module.exports=axe),\"function\"==typeof window.getComputedStyle&&(window.axe=axe);var commons;b.prototype=Object.create(Error.prototype),b.prototype.constructor=b;var utils=axe.utils={},Aa={},za=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};e.prototype._init=function(){var a=c(this.defaultConfig);axe.commons=commons=a.commons,this.reporter=a.reporter,this.commands={},this.rules=[],this.checks={},d(a.rules,this,\"addRule\"),d(a.checks,this,\"addCheck\"),this.data={},this.data.checks=a.data&&a.data.checks||{},this.data.rules=a.data&&a.data.rules||{},this.data.failureSummaries=a.data&&a.data.failureSummaries||{},this.data.incompleteFallbackMessage=a.data&&a.data.incompleteFallbackMessage||\"\",this._constructHelpUrls()},e.prototype.registerCommand=function(a){\"use strict\";this.commands[a.id]=a.callback},e.prototype.addRule=function(a){\"use strict\";a.metadata&&(this.data.rules[a.id]=a.metadata);var b=this.getRule(a.id);b?b.configure(a):this.rules.push(new q(a,this))},e.prototype.addCheck=function(a){\"use strict\";var b=a.metadata;\"object\"===(void 0===b?\"undefined\":za(b))&&(this.data.checks[a.id]=b,\"object\"===za(b.messages)&&Object.keys(b.messages).filter(function(a){return b.messages.hasOwnProperty(a)&&\"string\"==typeof b.messages[a]}).forEach(function(a){0===b.messages[a].indexOf(\"function\")&&(b.messages[a]=new Function(\"return \"+b.messages[a]+\";\")())})),this.checks[a.id]?this.checks[a.id].configure(a):this.checks[a.id]=new i(a)},e.prototype.run=function(a,b,c,d){\"use strict\";this.validateOptions(b),axe._tree=axe.utils.getFlattenedTree(document.documentElement),axe._selectCache=[];var e=axe.utils.queue();this.rules.forEach(function(c){if(axe.utils.ruleShouldRun(c,a,b)){if(b.performanceTimer){var d=\"mark_rule_end_\"+c.id,f=\"mark_rule_start_\"+c.id;axe.utils.performanceTimer.mark(f)}e.defer(function(e,g){c.run(a,b,function(a){b.performanceTimer&&(axe.utils.performanceTimer.mark(d),axe.utils.performanceTimer.measure(\"rule_\"+c.id,f,d)),e(a)},function(a){if(b.debug)g(a);else{var d=Object.assign(new p(c),{result:axe.constants.CANTTELL,description:\"An error occured while running this rule\",message:a.message,stack:a.stack,error:a});e(d)}})})}}),e.then(function(a){axe._selectCache=void 0,c(a.filter(function(a){return!!a}))}).catch(d)},e.prototype.after=function(a,b){\"use strict\";var c=this.rules;return a.map(function(a){return axe.utils.findBy(c,\"id\",a.id).after(a,b)})},e.prototype.getRule=function(a){return this.rules.find(function(b){return b.id===a})},e.prototype.validateOptions=function(a){\"use strict\";var b=this;if(\"object\"===za(a.runOnly)){var c=a.runOnly;if(\"rule\"===c.type&&Array.isArray(c.value))c.value.forEach(function(a){if(!b.getRule(a))throw new Error(\"unknown rule `\"+a+\"` in options.runOnly\")});else if(Array.isArray(c.value)&&c.value.length>0){var d=[].concat(c.value);if(b.rules.forEach(function(a){var b,c,e;if(d)for(c=0,e=a.tags.length;c<e;c++)-1!==(b=d.indexOf(a.tags[c]))&&d.splice(b,1)}),0!==d.length)throw new Error(\"could not find tags `\"+d.join(\"`, `\")+\"`\")}}return\"object\"===za(a.rules)&&Object.keys(a.rules).forEach(function(a){if(!b.getRule(a))throw new Error(\"unknown rule `\"+a+\"` in options.rules\")}),a},e.prototype.setBranding=function(a){\"use strict\";var b={brand:this.brand,application:this.application};a&&a.hasOwnProperty(\"brand\")&&a.brand&&\"string\"==typeof a.brand&&(this.brand=a.brand),a&&a.hasOwnProperty(\"application\")&&a.application&&\"string\"==typeof a.application&&(this.application=a.application),this._constructHelpUrls(b)},e.prototype._constructHelpUrls=function(){var a=this,b=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,c=(axe.version.match(/^[1-9][0-9]*\\.[0-9]+/)||[\"x.y\"])[0];this.rules.forEach(function(d){a.data.rules[d.id]||(a.data.rules[d.id]={});var e=a.data.rules[d.id];(\"string\"!=typeof e.helpUrl||b&&e.helpUrl===f(b,d.id,c))&&(e.helpUrl=f(a,d.id,c))})},e.prototype.resetRulesAndChecks=function(){\"use strict\";this._init()},i.prototype.enabled=!0,i.prototype.run=function(a,b,c,d){\"use strict\";b=b||{};var e=b.hasOwnProperty(\"enabled\")?b.enabled:this.enabled,f=b.options||this.options;if(e){var h,i=new g(this),j=axe.utils.checkHelper(i,b,c,d);try{h=this.evaluate.call(j,a.actualNode,f,a)}catch(a){return void d(a)}j.isAsync||(i.result=h,setTimeout(function(){c(i)},0))}else c(null)},i.prototype.configure=function(a){var b=this;[\"options\",\"enabled\"].filter(function(b){return a.hasOwnProperty(b)}).forEach(function(c){return b[c]=a[c]}),[\"evaluate\",\"after\"].filter(function(b){return a.hasOwnProperty(b)}).forEach(function(c){return b[c]=h(a[c])})};var za=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};q.prototype.matches=function(){\"use strict\";return!0},q.prototype.gather=function(a){\"use strict\";var b=axe.utils.select(this.selector,a);return this.excludeHidden?b.filter(function(a){return!axe.utils.isHidden(a.actualNode)}):b},q.prototype.runChecks=function(a,b,c,d,e){\"use strict\";var f=this,g=axe.utils.queue();this[a].forEach(function(a){var d=f._audit.checks[a.id||a],e=axe.utils.getCheckOption(d,f.id,c);g.defer(function(a,c){d.run(b,e,a,c)})}),g.then(function(b){b=b.filter(function(a){return a}),d({type:a,results:b})}).catch(e)},q.prototype.run=function(a,c,d,e){var f=this,g=axe.utils.queue(),h=new p(this),i=\"mark_runchecks_start_\"+this.id,j=\"mark_runchecks_end_\"+this.id,k=void 0;try{k=this.gather(a).filter(function(a){return f.matches(a.actualNode,a)})}catch(a){return void e(new b({cause:a,ruleId:this.id}))}c.performanceTimer&&(axe.log(\"gather (\",k.length,\"):\",axe.utils.performanceTimer.timeElapsed()+\"ms\"),axe.utils.performanceTimer.mark(i)),k.forEach(function(a){g.defer(function(b,d){var e=axe.utils.queue();e.defer(function(b,d){f.runChecks(\"any\",a,c,b,d)}),e.defer(function(b,d){f.runChecks(\"all\",a,c,b,d)}),e.defer(function(b,d){f.runChecks(\"none\",a,c,b,d)}),e.then(function(d){if(d.length){var e=!1,f={};d.forEach(function(a){var b=a.results.filter(function(a){return a});f[a.type]=b,b.length&&(e=!0)}),e&&(f.node=new axe.utils.DqElement(a.actualNode,c),h.nodes.push(f))}b()}).catch(function(a){return d(a)})})}),c.performanceTimer&&(axe.utils.performanceTimer.mark(j),axe.utils.performanceTimer.measure(\"runchecks_\"+this.id,i,j)),g.then(function(){return d(h)}).catch(function(a){return e(a)})},q.prototype.after=function(a,b){\"use strict\";var c=r(this),d=this.id;return c.forEach(function(c){var e=s(a.nodes,c.id),f=axe.utils.getCheckOption(c,d,b),g=c.after(e,f);e.forEach(function(a){-1===g.indexOf(a)&&(a.filtered=!0)})}),a.nodes=u(a),a},q.prototype.configure=function(a){\"use strict\";a.hasOwnProperty(\"selector\")&&(this.selector=a.selector),a.hasOwnProperty(\"excludeHidden\")&&(this.excludeHidden=\"boolean\"!=typeof a.excludeHidden||a.excludeHidden),a.hasOwnProperty(\"enabled\")&&(this.enabled=\"boolean\"!=typeof a.enabled||a.enabled),a.hasOwnProperty(\"pageLevel\")&&(this.pageLevel=\"boolean\"==typeof a.pageLevel&&a.pageLevel),a.hasOwnProperty(\"any\")&&(this.any=a.any),a.hasOwnProperty(\"all\")&&(this.all=a.all),a.hasOwnProperty(\"none\")&&(this.none=a.none),a.hasOwnProperty(\"tags\")&&(this.tags=a.tags),a.hasOwnProperty(\"matches\")&&(\"string\"==typeof a.matches?this.matches=new Function(\"return \"+a.matches+\";\")():this.matches=a.matches)},function(axe){var a=[{name:\"NA\",value:\"inapplicable\",priority:0,group:\"inapplicable\"},{name:\"PASS\",value:\"passed\",priority:1,group:\"passes\"},{name:\"CANTTELL\",value:\"cantTell\",priority:2,group:\"incomplete\"},{name:\"FAIL\",value:\"failed\",priority:3,group:\"violations\"}],b={helpUrlBase:\"https://dequeuniversity.com/rules/\",results:[],resultGroups:[],resultGroupMap:{},impact:Object.freeze([\"minor\",\"moderate\",\"serious\",\"critical\"])};a.forEach(function(a){var c=a.name,d=a.value,e=a.priority,f=a.group;b[c]=d,b[c+\"_PRIO\"]=e,b[c+\"_GROUP\"]=f,b.results[e]=d,b.resultGroups[e]=f,b.resultGroupMap[d]=f}),Object.freeze(b.results),Object.freeze(b.resultGroups),Object.freeze(b.resultGroupMap),Object.freeze(b),Object.defineProperty(axe,\"constants\",{value:b,enumerable:!0,configurable:!1,writable:!1})}(axe);var za=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};axe.log=function(){\"use strict\";\"object\"===(\"undefined\"==typeof console?\"undefined\":za(console))&&console.log&&Function.prototype.apply.call(console.log,console,arguments)},axe.cleanup=v,axe.configure=w,axe.getRules=function(a){\"use strict\";a=a||[];var b=a.length?axe._audit.rules.filter(function(b){return!!a.filter(function(a){return-1!==b.tags.indexOf(a)}).length}):axe._audit.rules,c=axe._audit.data.rules||{};return b.map(function(a){var b=c[a.id]||{};return{ruleId:a.id,description:b.description,help:b.help,helpUrl:b.helpUrl,tags:a.tags}})},axe._load=function(a){\"use strict\";axe.utils.respondable.subscribe(\"axe.ping\",function(a,b,c){c({axe:!0})}),axe.utils.respondable.subscribe(\"axe.start\",x),axe._audit=new e(a)};var axe=axe||{};axe.plugins={},y.prototype.run=function(){\"use strict\";return this._run.apply(this,arguments)},y.prototype.collect=function(){\"use strict\";return this._collect.apply(this,arguments)},y.prototype.cleanup=function(a){\"use strict\";var b=axe.utils.queue(),c=this;Object.keys(this._registry).forEach(function(a){b.defer(function(b){c._registry[a].cleanup(b)})}),b.then(function(){a()})},y.prototype.add=function(a){\"use strict\";this._registry[a.id]=a},axe.registerPlugin=function(a){\"use strict\";axe.plugins[a.id]=new y(a)};var Ba,Ca={};axe.getReporter=function(a){\"use strict\";return\"string\"==typeof a&&Ca[a]?Ca[a]:\"function\"==typeof a?a:Ba},axe.addReporter=function(a,b,c){\"use strict\";Ca[a]=b,c&&(Ba=b)},axe.reset=z,axe._runRules=A;var za=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a},Da=function(){};axe.run=function(a,b,c){\"use strict\";if(!axe._audit)throw new Error(\"No audit configured\");var d=C(a,b,c);a=d.context,b=d.options,c=d.callback,b.reporter=b.reporter||axe._audit.reporter||\"v1\",b.performanceTimer&&axe.utils.performanceTimer.start();var e=void 0,f=Da,g=Da;return window.Promise&&c===Da&&(e=new Promise(function(a,b){f=b,g=a})),axe._runRules(a,b,function(a){var d=function(a){try{c(null,a)}catch(a){axe.log(a)}g(a)};b.performanceTimer&&axe.utils.performanceTimer.end();try{var e=axe.getReporter(b.reporter),h=e(a,b,d);axe._selectorData=void 0,axe._tree=void 0,void 0!==h&&d(h)}catch(a){c(a),f(a)}},function(a){c(a),f(a)}),e},Aa.failureSummary=function(a){\"use strict\";var b={};return b.none=a.none.concat(a.all),b.any=a.any,Object.keys(b).map(function(a){if(b[a].length){var c=axe._audit.data.failureSummaries[a];return c&&\"function\"==typeof c.failureMessage?c.failureMessage(b[a].map(function(a){return a.message||\"\"})):void 0}}).filter(function(a){return void 0!==a}).join(\"\\n\\n\")},Aa.incompleteFallbackMessage=function(){\"use strict\";return axe._audit.data.incompleteFallbackMessage()};var za=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a},Ea=axe.constants.resultGroups;Aa.processAggregate=function(a,b){var c=axe.utils.aggregateResult(a);return c.timestamp=(new Date).toISOString(),c.url=window.location.href,Ea.forEach(function(a){b.resultTypes&&!b.resultTypes.includes(a)&&(c[a]||[]).forEach(function(a){Array.isArray(a.nodes)&&a.nodes.length>0&&(a.nodes=[a.nodes[0]])}),c[a]=(c[a]||[]).map(function(a){return a=Object.assign({},a),Array.isArray(a.nodes)&&a.nodes.length>0&&(a.nodes=a.nodes.map(function(a){return\"object\"===za(a.node)&&(a.html=a.node.source,b.elementRef&&!a.node.fromFrame&&(a.element=a.node.element),(!1!==b.selectors||a.node.fromFrame)&&(a.target=a.node.selector),b.xpath&&(a.xpath=a.node.xpath)),delete a.result,delete a.node,D(a,b),a})),Ea.forEach(function(b){return delete a[b]}),delete a.pageLevel,delete a.result,a})}),c},axe.addReporter(\"na\",function(a,b,c){\"use strict\";\"function\"==typeof b&&(c=b,b={});var d=Aa.processAggregate(a,b);c({violations:d.violations,passes:d.passes,incomplete:d.incomplete,inapplicable:d.inapplicable,timestamp:d.timestamp,url:d.url})}),axe.addReporter(\"no-passes\",function(a,b,c){\"use strict\";\"function\"==typeof b&&(c=b,b={}),b.resultTypes=[\"violations\"];var d=Aa.processAggregate(a,b);c({violations:d.violations,timestamp:d.timestamp,url:d.url})}),axe.addReporter(\"raw\",function(a,b,c){\"use strict\";\"function\"==typeof b&&(c=b,b={}),c(a)}),axe.addReporter(\"v1\",function(a,b,c){\"use strict\";\"function\"==typeof b&&(c=b,b={});var d=Aa.processAggregate(a,b);d.violations.forEach(function(a){return a.nodes.forEach(function(a){a.failureSummary=Aa.failureSummary(a)})}),c({violations:d.violations,passes:d.passes,incomplete:d.incomplete,inapplicable:d.inapplicable,timestamp:d.timestamp,url:d.url})}),axe.addReporter(\"v2\",function(a,b,c){\"use strict\";\"function\"==typeof b&&(c=b,b={});var d=Aa.processAggregate(a,b);c({violations:d.violations,passes:d.passes,incomplete:d.incomplete,inapplicable:d.inapplicable,timestamp:d.timestamp,url:d.url})},!0),axe.utils.aggregate=function(a,b,c){b=b.slice(),c&&b.push(c);var d=b.map(function(b){return a.indexOf(b)}).sort();return a[d.pop()]};var Fa=axe.constants,Ga=Fa.CANTTELL_PRIO,Ha=Fa.FAIL_PRIO,Ia=[];Ia[axe.constants.PASS_PRIO]=!0,Ia[axe.constants.CANTTELL_PRIO]=null,Ia[axe.constants.FAIL_PRIO]=!1;var Ja=[\"any\",\"all\",\"none\"];axe.utils.aggregateChecks=function(a){var b=Object.assign({},a);E(b,function(a,b){var c=Ia.indexOf(a.result);a.priority=-1!==c?c:axe.constants.CANTTELL_PRIO,\"none\"===b&&(a.priority=4-a.priority)});var c={all:b.all.reduce(function(a,b){return Math.max(a,b.priority)},0),none:b.none.reduce(function(a,b){return Math.max(a,b.priority)},0),any:b.any.reduce(function(a,b){return Math.min(a,b.priority)},4)%4};b.priority=Math.max(c.all,c.none,c.any);var d=[];return Ja.forEach(function(a){b[a]=b[a].filter(function(d){return d.priority===b.priority&&d.priority===c[a]}),b[a].forEach(function(a){return d.push(a.impact)})}),[Ga,Ha].includes(b.priority)?b.impact=axe.utils.aggregate(axe.constants.impact,d):b.impact=null,E(b,function(a){delete a.result,delete a.priority}),b.result=axe.constants.results[b.priority],delete b.priority,b},function(){axe.utils.aggregateNodeResults=function(a){var b={};if((a=a.map(function(a){if(a.any&&a.all&&a.none)return axe.utils.aggregateChecks(a);if(Array.isArray(a.node))return axe.utils.finalizeRuleResult(a);throw new TypeError(\"Invalid Result type\")}))&&a.length){var c=a.map(function(a){return a.result});b.result=axe.utils.aggregate(axe.constants.results,c,b.result)}else b.result=\"inapplicable\";axe.constants.resultGroups.forEach(function(a){return b[a]=[]}),a.forEach(function(a){var c=axe.constants.resultGroupMap[a.result];b[c].push(a)});var d=axe.constants.FAIL_GROUP;if(0===b[d].length&&(d=axe.constants.CANTTELL_GROUP),b[d].length>0){var e=b[d].map(function(a){return a.impact});b.impact=axe.utils.aggregate(axe.constants.impact,e)||null}else b.impact=null;return b}}(),axe.utils.aggregateResult=function(a){var b={};return axe.constants.resultGroups.forEach(function(a){return b[a]=[]}),a.forEach(function(a){a.error?F(b,a,axe.constants.CANTTELL_GROUP):a.result===axe.constants.NA?F(b,a,axe.constants.NA_GROUP):axe.constants.resultGroups.forEach(function(c){Array.isArray(a[c])&&a[c].length>0&&F(b,a,c)})}),b},axe.utils.areStylesSet=G,axe.utils.checkHelper=function(a,b,c,d){\"use strict\";return{isAsync:!1,async:function(){return this.isAsync=!0,function(b){b instanceof Error==!1?(a.result=b,c(a)):d(b)}},data:function(b){a.data=b},relatedNodes:function(c){c=c instanceof Node?[c]:axe.utils.toArray(c),a.relatedNodes=c.map(function(a){return new axe.utils.DqElement(a,b)})}}};var za=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};axe.utils.clone=function(a){\"use strict\";var b,c,d=a;if(null!==a&&\"object\"===(void 0===a?\"undefined\":za(a)))if(Array.isArray(a))for(d=[],b=0,c=a.length;b<c;b++)d[b]=axe.utils.clone(a[b]);else{d={};for(b in a)d[b]=axe.utils.clone(a[b])}return d},axe.utils.sendCommandToFrame=function(a,b,c,d){\"use strict\";var e=a.contentWindow;if(!e)return axe.log(\"Frame does not have a content window\",a),void c(null);var f=setTimeout(function(){f=setTimeout(function(){var e=H(\"No response from frame\",a);b.debug?d(e):(axe.log(e),c(null))},0)},500);axe.utils.respondable(e,\"axe.ping\",null,void 0,function(){clearTimeout(f);var g=b.options&&b.options.frameWaitTime||6e4;f=setTimeout(function(){d(H(\"Axe in frame timed out\",a))},g),axe.utils.respondable(e,\"axe.start\",b,void 0,function(a){clearTimeout(f),a instanceof Error==!1?c(a):d(a)})})},axe.utils.collectResultsFromFrames=I,axe.utils.contains=function(a,b){\"use strict\";function c(a,b){return a.shadowId===b.shadowId||!!a.children.find(function(a){return c(a,b)})}return a.shadowId||b.shadowId?c(a,b):\"function\"==typeof a.actualNode.contains?a.actualNode.contains(b.actualNode):!!(16&a.actualNode.compareDocumentPosition(b.actualNode))},function(axe){/*!\n  * The copyright below covers the code within this function block only\n  *\n  * Copyright (c) 2013 Dulin Marat\n  * \n  * Permission is hereby granted, free of charge, to any person obtaining a copy\n  * of this software and associated documentation files (the \"Software\"), to deal\n  * in the Software without restriction, including without limitation the rights\n  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  * copies of the Software, and to permit persons to whom the Software is\n  * furnished to do so, subject to the following conditions:\n  * \n  * The above copyright notice and this permission notice shall be included in\n  * all copies or substantial portions of the Software.\n  * \n  * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n  * THE SOFTWARE.\n  */\nfunction a(){this.pseudos={},this.attrEqualityMods={},this.ruleNestingOperators={},this.substitutesEnabled=!1}function b(a){return a>=\"a\"&&a<=\"z\"||a>=\"A\"&&a<=\"Z\"||\"-\"===a||\"_\"===a}function c(a){return a>=\"a\"&&a<=\"z\"||a>=\"A\"&&a<=\"Z\"||a>=\"0\"&&a<=\"9\"||\"-\"===a||\"_\"===a}function d(a){return a>=\"a\"&&a<=\"f\"||a>=\"A\"&&a<=\"F\"||a>=\"0\"&&a<=\"9\"}function e(a,e,g,j,k,l){var m,n,o,p,q;return p=a.length,m=null,o=function(b,c){var f,g,h;for(h=\"\",e++,m=a.charAt(e);e<p;){if(m===b)return e++,h;if(\"\\\\\"===m)if(e++,(m=a.charAt(e))===b)h+=b;else if(f=c[m])h+=f;else{if(d(m)){for(g=m,e++,m=a.charAt(e);d(m);)g+=m,e++,m=a.charAt(e);\" \"===m&&(e++,m=a.charAt(e)),h+=String.fromCharCode(parseInt(g,16));continue}h+=m}else h+=m;e++,m=a.charAt(e)}return h},n=function(){var b=\"\";for(m=a.charAt(e);e<p;){if(c(m))b+=m;else{if(\"\\\\\"!==m)return b;if(++e>=p)throw Error(\"Expected symbol but end of file reached.\");if(m=a.charAt(e),f[m])b+=m;else{if(d(m)){var g=m;for(e++,m=a.charAt(e);d(m);)g+=m,e++,m=a.charAt(e);\" \"===m&&(e++,m=a.charAt(e)),b+=String.fromCharCode(parseInt(g,16));continue}b+=m}}e++,m=a.charAt(e)}return b},q=function(){m=a.charAt(e);for(var b=!1;\" \"===m||\"\\t\"===m||\"\\n\"===m||\"\\r\"===m||\"\\f\"===m;)b=!0,e++,m=a.charAt(e);return b},this.parse=function(){var b=this.parseSelector();if(e<p)throw Error('Rule expected but \"'+a.charAt(e)+'\" found.');return b},this.parseSelector=function(){var b,c=b=this.parseSingleSelector();for(m=a.charAt(e);\",\"===m;){if(e++,q(),\"selectors\"!==b.type&&(b={type:\"selectors\",selectors:[c]}),!(c=this.parseSingleSelector()))throw Error('Rule expected after \",\".');b.selectors.push(c)}return b},this.parseSingleSelector=function(){q();var b={type:\"ruleSet\"},c=this.parseRule();if(!c)return null;for(var d=b;c&&(c.type=\"rule\",d.rule=c,d=c,q(),m=a.charAt(e),!(e>=p||\",\"===m||\")\"===m));)if(k[m]){var f=m;if(e++,q(),!(c=this.parseRule()))throw Error('Rule expected after \"'+f+'\".');c.nestingOperator=f}else(c=this.parseRule())&&(c.nestingOperator=null);return b},this.parseRule=function(){for(var c=null;e<p;)if(\"*\"===(m=a.charAt(e)))e++,(c=c||{}).tagName=\"*\";else if(b(m)||\"\\\\\"===m)(c=c||{}).tagName=n();else if(\".\"===m)e++,c=c||{},(c.classNames=c.classNames||[]).push(n());else if(\"#\"===m)e++,(c=c||{}).id=n();else if(\"[\"===m){e++,q();var d={name:n()};if(q(),\"]\"===m)e++;else{var f=\"\";if(j[m]&&(f=m,e++,m=a.charAt(e)),e>=p)throw Error('Expected \"=\" but end of file reached.');if(\"=\"!==m)throw Error('Expected \"=\" but \"'+m+'\" found.');d.operator=f+\"=\",e++,q();var k=\"\";if(d.valueType=\"string\",'\"'===m)k=o('\"',i);else if(\"'\"===m)k=o(\"'\",h);else if(l&&\"$\"===m)e++,k=n(),d.valueType=\"substitute\";else{for(;e<p&&\"]\"!==m;)k+=m,e++,m=a.charAt(e);k=k.trim()}if(q(),e>=p)throw Error('Expected \"]\" but end of file reached.');if(\"]\"!==m)throw Error('Expected \"]\" but \"'+m+'\" found.');e++,d.value=k}c=c||{},(c.attrs=c.attrs||[]).push(d)}else{if(\":\"!==m)break;e++;var r=n(),s={name:r};if(\"(\"===m){e++;var t=\"\";if(q(),\"selector\"===g[r])s.valueType=\"selector\",t=this.parseSelector();else{if(s.valueType=g[r]||\"string\",'\"'===m)t=o('\"',i);else if(\"'\"===m)t=o(\"'\",h);else if(l&&\"$\"===m)e++,t=n(),s.valueType=\"substitute\";else{for(;e<p&&\")\"!==m;)t+=m,e++,m=a.charAt(e);t=t.trim()}q()}if(e>=p)throw Error('Expected \")\" but end of file reached.');if(\")\"!==m)throw Error('Expected \")\" but \"'+m+'\" found.');e++,s.value=t}c=c||{},(c.pseudos=c.pseudos||[]).push(s)}return c},this}a.prototype.registerSelectorPseudos=function(a){for(var b=0,c=arguments.length;b<c;b++)a=arguments[b],this.pseudos[a]=\"selector\";return this},a.prototype.unregisterSelectorPseudos=function(a){for(var b=0,c=arguments.length;b<c;b++)a=arguments[b],delete this.pseudos[a];return this},a.prototype.registerNumericPseudos=function(a){for(var b=0,c=arguments.length;b<c;b++)a=arguments[b],this.pseudos[a]=\"numeric\";return this},a.prototype.unregisterNumericPseudos=function(a){for(var b=0,c=arguments.length;b<c;b++)a=arguments[b],delete this.pseudos[a];return this},a.prototype.registerNestingOperators=function(a){for(var b=0,c=arguments.length;b<c;b++)a=arguments[b],this.ruleNestingOperators[a]=!0;return this},a.prototype.unregisterNestingOperators=function(a){for(var b=0,c=arguments.length;b<c;b++)a=arguments[b],delete this.ruleNestingOperators[a];return this},a.prototype.registerAttrEqualityMods=function(a){for(var b=0,c=arguments.length;b<c;b++)a=arguments[b],this.attrEqualityMods[a]=!0;return this},a.prototype.unregisterAttrEqualityMods=function(a){for(var b=0,c=arguments.length;b<c;b++)a=arguments[b],delete this.attrEqualityMods[a];return this},a.prototype.enableSubstitutes=function(){return this.substitutesEnabled=!0,this},a.prototype.disableSubstitutes=function(){return this.substitutesEnabled=!1,this};var f={\"!\":!0,'\"':!0,\"#\":!0,$:!0,\"%\":!0,\"&\":!0,\"'\":!0,\"(\":!0,\")\":!0,\"*\":!0,\"+\":!0,\",\":!0,\".\":!0,\"/\":!0,\";\":!0,\"<\":!0,\"=\":!0,\">\":!0,\"?\":!0,\"@\":!0,\"[\":!0,\"\\\\\":!0,\"]\":!0,\"^\":!0,\"`\":!0,\"{\":!0,\"|\":!0,\"}\":!0,\"~\":!0},g={\"\\n\":\"\\\\n\",\"\\r\":\"\\\\r\",\"\\t\":\"\\\\t\",\"\\f\":\"\\\\f\",\"\\v\":\"\\\\v\"},h={n:\"\\n\",r:\"\\r\",t:\"\\t\",f:\"\\f\",\"\\\\\":\"\\\\\",\"'\":\"'\"},i={n:\"\\n\",r:\"\\r\",t:\"\\t\",f:\"\\f\",\"\\\\\":\"\\\\\",'\"':'\"'};a.prototype.parse=function(a){return new e(a,0,this.pseudos,this.attrEqualityMods,this.ruleNestingOperators,this.substitutesEnabled).parse()},a.prototype.escapeIdentifier=function(a){for(var b=\"\",c=0,d=a.length;c<d;){var e=a.charAt(c);if(f[e])b+=\"\\\\\"+e;else if(\"_\"===e||\"-\"===e||e>=\"A\"&&e<=\"Z\"||e>=\"a\"&&e<=\"z\"||0!==c&&e>=\"0\"&&e<=\"9\")b+=e;else{var g=e.charCodeAt(0);if(55296==(63488&g)){var h=a.charCodeAt(c++);if(55296!=(64512&g)||56320!=(64512&h))throw Error(\"UCS-2(decode): illegal sequence\");g=((1023&g)<<10)+(1023&h)+65536}b+=\"\\\\\"+g.toString(16)+\" \"}c++}return b},a.prototype.escapeStr=function(a){for(var b,c,d=\"\",e=0,f=a.length;e<f;)b=a.charAt(e),'\"'===b?b='\\\\\"':\"\\\\\"===b?b=\"\\\\\\\\\":(c=g[b])&&(b=c),d+=b,e++;return'\"'+d+'\"'},a.prototype.render=function(a){return this._renderEntity(a).trim()},a.prototype._renderEntity=function(a){var b,c,d;switch(d=\"\",a.type){case\"ruleSet\":for(b=a.rule,c=[];b;)b.nestingOperator&&c.push(b.nestingOperator),c.push(this._renderEntity(b)),b=b.rule;d=c.join(\" \");break;case\"selectors\":d=a.selectors.map(this._renderEntity,this).join(\", \");break;case\"rule\":a.tagName&&(d=\"*\"===a.tagName?\"*\":this.escapeIdentifier(a.tagName)),a.id&&(d+=\"#\"+this.escapeIdentifier(a.id)),a.classNames&&(d+=a.classNames.map(function(a){return\".\"+this.escapeIdentifier(a)},this).join(\"\")),a.attrs&&(d+=a.attrs.map(function(a){return a.operator?\"substitute\"===a.valueType?\"[\"+this.escapeIdentifier(a.name)+a.operator+\"$\"+a.value+\"]\":\"[\"+this.escapeIdentifier(a.name)+a.operator+this.escapeStr(a.value)+\"]\":\"[\"+this.escapeIdentifier(a.name)+\"]\"},this).join(\"\")),a.pseudos&&(d+=a.pseudos.map(function(a){return a.valueType?\"selector\"===a.valueType?\":\"+this.escapeIdentifier(a.name)+\"(\"+this._renderEntity(a.value)+\")\":\"substitute\"===a.valueType?\":\"+this.escapeIdentifier(a.name)+\"($\"+a.value+\")\":\"numeric\"===a.valueType?\":\"+this.escapeIdentifier(a.name)+\"(\"+a.value+\")\":\":\"+this.escapeIdentifier(a.name)+\"(\"+this.escapeIdentifier(a.value)+\")\":\":\"+this.escapeIdentifier(a.name)},this).join(\"\"));break;default:throw Error('Unknown entity type: \"'+a.type(NaN))}return d};var j=new a;j.registerNestingOperators(\">\"),axe.utils.cssParser=j}(axe),L.prototype={get selector(){return this.spec.selector||[axe.utils.getSelector(this.element,this._options)]},get xpath(){return this.spec.xpath||[axe.utils.getXpath(this.element)]},get element(){return this._element},get fromFrame(){return this._fromFrame},toJSON:function(){\"use strict\";return{selector:this.selector,source:this.source,xpath:this.xpath}}},L.fromFrame=function(a,b,c){return a.selector.unshift(c.selector),a.xpath.unshift(c.xpath),new axe.utils.DqElement(c.element,b,a)},axe.utils.DqElement=L,axe.utils.matchesSelector=function(){\"use strict\";function a(a){var b,c,d=a.Element.prototype,e=[\"matches\",\"matchesSelector\",\"mozMatchesSelector\",\"webkitMatchesSelector\",\"msMatchesSelector\"],f=e.length;for(b=0;b<f;b++)if(c=e[b],d[c])return c}var b;return function(c,d){return b&&c[b]||(b=a(c.ownerDocument.defaultView)),c[b](d)}}(),axe.utils.escapeSelector=function(a){\"use strict\";for(var b,c=String(a),d=c.length,e=-1,f=\"\",g=c.charCodeAt(0);++e<d;){if(0==(b=c.charCodeAt(e)))throw new Error(\"INVALID_CHARACTER_ERR\");b>=1&&b<=31||b>=127&&b<=159||0==e&&b>=48&&b<=57||1==e&&b>=48&&b<=57&&45==g?f+=\"\\\\\"+b.toString(16)+\" \":f+=(1!=e||45!=b||45!=g)&&(b>=128||45==b||95==b||b>=48&&b<=57||b>=65&&b<=90||b>=97&&b<=122)?c.charAt(e):\"\\\\\"+c.charAt(e)}return f},axe.utils.extendMetaData=function(a,b){Object.assign(a,b),Object.keys(b).filter(function(a){return\"function\"==typeof b[a]}).forEach(function(c){a[c]=null;try{a[c]=b[c](a)}catch(a){}})},axe.utils.finalizeRuleResult=function(a){return Object.assign(a,axe.utils.aggregateNodeResults(a.nodes)),delete a.nodes,a};var za=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};axe.utils.findBy=function(a,b,c){if(Array.isArray(a))return a.find(function(a){return\"object\"===(void 0===a?\"undefined\":za(a))&&a[b]===c})};var axe=axe||{utils:{}};axe.utils.getFlattenedTree=function(a,b){function c(a,c){var d=axe.utils.getFlattenedTree(c,b);return d&&(a=a.concat(d)),a}var d,e,f;if(a.documentElement&&(a=a.documentElement),f=a.nodeName.toLowerCase(),axe.utils.isShadowRoot(a))return d=M(a,b),b=\"a\"+Math.random().toString().substring(2),e=Array.from(a.shadowRoot.childNodes),d.children=e.reduce(c,[]),[d];if(\"content\"===f)return e=Array.from(a.getDistributedNodes()),e.reduce(c,[]);if(\"slot\"===f){e=Array.from(a.assignedNodes()),e.length||(e=N(a));window.getComputedStyle(a);return e.reduce(c,[])}return 1===a.nodeType?(d=M(a,b),e=Array.from(a.childNodes),d.children=e.reduce(c,[]),[d]):3===a.nodeType?[M(a)]:void 0},axe.utils.getNodeFromTree=function(a,b){var c;return a.actualNode===b?a:(a.children.forEach(function(a){var d;a.actualNode===b?c=a:(d=axe.utils.getNodeFromTree(a,b))&&(c=d)}),c)},axe.utils.getAllChecks=function(a){\"use strict\";return[].concat(a.any||[]).concat(a.all||[]).concat(a.none||[])},axe.utils.getCheckOption=function(a,b,c){var d=((c.rules&&c.rules[b]||{}).checks||{})[a.id],e=(c.checks||{})[a.id],f=a.enabled,g=a.options;return e&&(e.hasOwnProperty(\"enabled\")&&(f=e.enabled),e.hasOwnProperty(\"options\")&&(g=e.options)),d&&(d.hasOwnProperty(\"enabled\")&&(f=d.enabled),d.hasOwnProperty(\"options\")&&(g=d.options)),{enabled:f,options:g,absolutePaths:c.absolutePaths}};var Ka=function(){function a(a,b){var c=[],d=!0,e=!1,f=void 0;try{for(var g,h=a[Symbol.iterator]();!(d=(g=h.next()).done)&&(c.push(g.value),!b||c.length!==b);d=!0);}catch(a){e=!0,f=a}finally{try{!d&&h.return&&h.return()}finally{if(e)throw f}}return c}return function(b,c){if(Array.isArray(b))return b;if(Symbol.iterator in Object(b))return a(b,c);throw new TypeError(\"Invalid attempt to destructure non-iterable instance\")}}();axe.utils.getFriendlyUriEnd=function(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"\",b=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!(a.length<=1||\"data:\"===a.substr(0,5)||\"javascript:\"===a.substr(0,11)||a.includes(\"?\"))){var c=b.currentDomain,d=b.maxLength,e=void 0===d?25:d,f=Q(a),g=f.path,h=f.domain,i=f.hash,j=g.substr(g.substr(0,g.length-2).lastIndexOf(\"/\")+1);if(i)return j&&(j+i).length<=e?j+i:j.length<2&&i.length>2&&i.length<=e?i:void 0;if(h&&h.length<e&&g.length<=1)return h+g;if(g===\"/\"+j&&h&&c&&h!==c&&(h+g).length<=e)return h+g;var k=j.lastIndexOf(\".\");return(-1===k||k>1)&&(-1!==k||j.length>2)&&j.length<=e&&!j.match(/index(\\.[a-zA-Z]{2-4})?/)&&!O(j)?j:void 0}};var La=axe.utils.escapeSelector,Ma=void 0,Na=[\"class\",\"style\",\"id\",\"selected\",\"checked\",\"disabled\",\"tabindex\",\"aria-checked\",\"aria-selected\",\"aria-invalid\",\"aria-activedescendant\",\"aria-busy\",\"aria-disabled\",\"aria-expanded\",\"aria-grabbed\",\"aria-pressed\",\"aria-valuenow\"],Oa=31;axe.utils.getSelectorData=function(a){var b={classes:{},tags:{},attributes:{}};a=Array.isArray(a)?a:[a];for(var c=a.slice(),d=[];c.length;)!function(){var a=c.pop(),e=a.actualNode;if(e.querySelectorAll){var f=e.nodeName;b.tags[f]?b.tags[f]++:b.tags[f]=1,e.classList&&Array.from(e.classList).forEach(function(a){var c=La(a);b.classes[c]?b.classes[c]++:b.classes[c]=1}),e.attributes&&Array.from(e.attributes).filter(T).forEach(function(a){var c=R(e,a);c&&(b.attributes[c]?b.attributes[c]++:b.attributes[c]=1)})}for(a.children.length&&(d.push(c),c=a.children.slice());!c.length&&d.length;)c=d.pop()}();return b},axe.utils.getSelector=function(a){var b=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!a)return\"\";var c=a.getRootNode&&a.getRootNode()||document;if(11===c.nodeType){for(var d=[];11===c.nodeType;)d.push({elm:a,doc:c}),a=c.host,c=a.getRootNode();return d.push({elm:a,doc:c}),d.reverse().map(function(a){return $(a.elm,b,a.doc)})}return $(a,b,c)},axe.utils.getXpath=function(a){return aa(_(a))};var Pa;axe.utils.injectStyle=ba,axe.utils.isHidden=function(a,b){\"use strict\";var c;if(9===a.nodeType)return!1;11===a.nodeType&&(a=a.host);var d=window.getComputedStyle(a,null);return!d||!a.parentNode||\"none\"===d.getPropertyValue(\"display\")||!b&&\"hidden\"===d.getPropertyValue(\"visibility\")||\"true\"===a.getAttribute(\"aria-hidden\")||(c=a.assignedSlot?a.assignedSlot:a.parentNode,axe.utils.isHidden(c,!0))};var Qa=[\"article\",\"aside\",\"blockquote\",\"body\",\"div\",\"footer\",\"h1\",\"h2\",\"h3\",\"h4\",\"h5\",\"h6\",\"header\",\"main\",\"nav\",\"p\",\"section\",\"span\"];axe.utils.isShadowRoot=function(a){var b=a.nodeName.toLowerCase();return!(!a.shadowRoot||!/^[a-z][a-z0-9_.-]*-[a-z0-9_.-]*$/.test(b)&&!Qa.includes(b))},axe.utils.isXHTML=function(a){\"use strict\";return!!a.createElement&&\"A\"===a.createElement(\"A\").localName},axe.utils.mergeResults=function(a,b){\"use strict\";var c=[];return a.forEach(function(a){var d=ea(a);d&&d.length&&d.forEach(function(d){d.nodes&&a.frame&&ca(d.nodes,b,a.frameElement,a.frame);var e=axe.utils.findBy(c,\"id\",d.id);e?d.nodes.length&&da(e.nodes,d.nodes):c.push(d)})}),c},axe.utils.nodeSorter=function(a,b){\"use strict\";return a.actualNode===b.actualNode?0:4&a.actualNode.compareDocumentPosition(b.actualNode)?-1:1},utils.performanceTimer=function(){\"use strict\";function a(){if(window.performance&&window.performance)return window.performance.now()}var b=null,c=a();return{start:function(){this.mark(\"mark_axe_start\")},end:function(){this.mark(\"mark_axe_end\"),this.measure(\"axe\",\"mark_axe_start\",\"mark_axe_end\"),this.logMeasures(\"axe\")},auditStart:function(){this.mark(\"mark_audit_start\")},auditEnd:function(){this.mark(\"mark_audit_end\"),this.measure(\"audit_start_to_end\",\"mark_audit_start\",\"mark_audit_end\"),this.logMeasures()},mark:function(a){window.performance&&void 0!==window.performance.mark&&window.performance.mark(a)},measure:function(a,b,c){window.performance&&void 0!==window.performance.measure&&window.performance.measure(a,b,c)},logMeasures:function(a){function b(a){axe.log(\"Measure \"+a.name+\" took \"+a.duration+\"ms\")}if(window.performance&&void 0!==window.performance.getEntriesByType)for(var c=window.performance.getEntriesByType(\"measure\"),d=0;d<c.length;++d){var e=c[d];if(e.name===a)return void b(e);b(e)}},timeElapsed:function(){return a()-c},reset:function(){b||(b=a()),c=a()}}}(),\"function\"!=typeof Object.assign&&function(){Object.assign=function(a){\"use strict\";if(void 0===a||null===a)throw new TypeError(\"Cannot convert undefined or null to object\");for(var b=Object(a),c=1;c<arguments.length;c++){var d=arguments[c];if(void 0!==d&&null!==d)for(var e in d)d.hasOwnProperty(e)&&(b[e]=d[e])}return b}}(),Array.prototype.find||Object.defineProperty(Array.prototype,\"find\",{value:function(a){if(null===this)throw new TypeError(\"Array.prototype.find called on null or undefined\");if(\"function\"!=typeof a)throw new TypeError(\"predicate must be a function\");for(var b,c=Object(this),d=c.length>>>0,e=arguments[1],f=0;f<d;f++)if(b=c[f],a.call(e,b,f,c))return b}}),axe.utils.pollyfillElementsFromPoint=function(){if(document.elementsFromPoint)return document.elementsFromPoint;if(document.msElementsFromPoint)return document.msElementsFromPoint;var a=function(){var a=document.createElement(\"x\");return a.style.cssText=\"pointer-events:auto\",\"auto\"===a.style.pointerEvents}(),b=a?\"pointer-events\":\"visibility\",c=a?\"none\":\"hidden\",d=document.createElement(\"style\");return d.innerHTML=a?\"* { pointer-events: all }\":\"* { visibility: visible }\",function(a,e){var f,g,h,i=[],j=[];for(document.head.appendChild(d);(f=document.elementFromPoint(a,e))&&-1===i.indexOf(f);)i.push(f),j.push({value:f.style.getPropertyValue(b),priority:f.style.getPropertyPriority(b)}),f.style.setProperty(b,c,\"important\");for(i.indexOf(document.documentElement)<i.length-1&&(i.splice(i.indexOf(document.documentElement),1),i.push(document.documentElement)),g=j.length;h=j[--g];)i[g].style.setProperty(b,h.value?h.value:\"\",h.priority);return document.head.removeChild(d),i}},\"function\"==typeof window.addEventListener&&(document.elementsFromPoint=axe.utils.pollyfillElementsFromPoint()),Array.prototype.includes||Object.defineProperty(Array.prototype,\"includes\",{value:function(a){\"use strict\";var b=Object(this),c=parseInt(b.length,10)||0;if(0===c)return!1;var d,e=parseInt(arguments[1],10)||0;e>=0?d=e:(d=c+e)<0&&(d=0);for(var f;d<c;){if(f=b[d],a===f||a!==a&&f!==f)return!0;d++}return!1}}),Array.prototype.some||Object.defineProperty(Array.prototype,\"some\",{value:function(a){\"use strict\";if(null==this)throw new TypeError(\"Array.prototype.some called on null or undefined\");if(\"function\"!=typeof a)throw new TypeError;for(var b=Object(this),c=b.length>>>0,d=arguments.length>=2?arguments[1]:void 0,e=0;e<c;e++)if(e in b&&a.call(d,b[e],e,b))return!0;return!1}}),Array.from||Object.defineProperty(Array,\"from\",{value:function(){var a=Object.prototype.toString,b=function(b){return\"function\"==typeof b||\"[object Function]\"===a.call(b)},c=function(a){var b=Number(a);return isNaN(b)?0:0!==b&&isFinite(b)?(b>0?1:-1)*Math.floor(Math.abs(b)):b},d=Math.pow(2,53)-1,e=function(a){var b=c(a);return Math.min(Math.max(b,0),d)};return function(a){var c=this,d=Object(a);if(null==a)throw new TypeError(\"Array.from requires an array-like object - not null or undefined\");var f,g=arguments.length>1?arguments[1]:void 0;if(void 0!==g){if(!b(g))throw new TypeError(\"Array.from: when provided, the second argument must be a function\");arguments.length>2&&(f=arguments[2])}for(var h,i=e(d.length),j=b(c)?Object(new c(i)):new Array(i),k=0;k<i;)h=d[k],j[k]=g?void 0===f?g(h,k):g.call(f,h,k):h,k+=1;return j.length=i,j}}()}),String.prototype.includes||(String.prototype.includes=function(a,b){return\"number\"!=typeof b&&(b=0),!(b+a.length>this.length)&&-1!==this.indexOf(a,b)});var za=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};axe.utils.publishMetaData=function(a){\"use strict\";var b=axe._audit.data.checks||{},c=axe._audit.data.rules||{},d=axe.utils.findBy(axe._audit.rules,\"id\",a.id)||{};a.tags=axe.utils.clone(d.tags||[]);var e=ga(b,!0),f=ga(b,!1);a.nodes.forEach(function(a){a.any.forEach(e),a.all.forEach(e),a.none.forEach(f)}),axe.utils.extendMetaData(a,axe.utils.clone(c[a.id]||{}))};var Ra=function(){},Sa=function(){},Ta=function(){/*! Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License */\nvar a=/(?=[\\-\\[\\]{}()*+?.\\\\\\^$|,#\\s])/g;return function(b){return b.replace(a,\"\\\\\")}}(),Ua=/\\\\/g;Ra=function(a){return a.map(function(a){for(var b=[],c=a.rule;c;)b.push({tag:c.tagName?c.tagName.toLowerCase():\"*\",combinator:c.nestingOperator?c.nestingOperator:\" \",id:c.id,attributes:ma(c.attrs),classes:na(c.classNames),pseudos:oa(c.pseudos)}),c=c.rule;return b})},Sa=function(a,b,c,d){for(var e=[],f=Array.isArray(a)?a:[a],g=pa(f,b,[],a[0].shadowId),h=[];g.nodes.length;){for(var i=g.nodes.pop(),j=[],k=[],l=g.anyLevel.slice().concat(g.thisLevel),m=!1,n=0;n<l.length;n++){var o=l[n];if(qa(i,o)&&(!o[0].id||i.shadowId===g.parentShadowId))if(1===o.length)m||d&&!d(i)||(h.push(i),m=!0);else{var p=o.slice(1);if(!1===[\" \",\">\"].includes(p[0].combinator))throw new Error(\"axe.utils.querySelectorAll does not support the combinator: \"+o[1].combinator);\">\"===p[0].combinator?j.push(p):k.push(p)}!g.anyLevel.includes(o)||o[0].id&&i.shadowId!==g.parentShadowId||k.push(o)}for(i.children&&i.children.length&&c&&(e.push(g),g=pa(i.children,k,j,i.shadowId));!g.nodes.length&&e.length;)g=e.pop()}return h},axe.utils.querySelectorAll=function(a,b){return axe.utils.querySelectorAllFilter(a,b)},axe.utils.querySelectorAllFilter=function(a,b,c){a=Array.isArray(a)?a:[a];var d=axe.utils.cssParser.parse(b);return d=d.selectors?d.selectors:[d],d=Ra(d),Sa(a,d,!0,c)};var za=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};!function(){\"use strict\";function a(){}function b(a){if(\"function\"!=typeof a)throw new TypeError(\"Queue methods require functions as arguments\")}function c(){function c(b){return function(c){g[b]=c,(i-=1)||j===a||(k=!0,j(g))}}function d(b){return j=a,m(b),g}function e(){for(var a=g.length;h<a;h++){var b=g[h];try{b.call(null,c(h),d)}catch(a){d(a)}}}var f,g=[],h=0,i=0,j=a,k=!1,l=function(a){f=a,setTimeout(function(){void 0!==f&&null!==f&&axe.log(\"Uncaught error (of queue)\",f)},1)},m=l,n={defer:function(a){if(\"object\"===(void 0===a?\"undefined\":za(a))&&a.then&&a.catch){var c=a;a=function(a,b){c.then(a).catch(b)}}if(b(a),void 0===f){if(k)throw new Error(\"Queue already completed\");return g.push(a),++i,e(),n}},then:function(c){if(b(c),j!==a)throw new Error(\"queue `then` already set\");return f||(j=c,i||(k=!0,j(g))),n},catch:function(a){if(b(a),m!==l)throw new Error(\"queue `catch` already set\");return f?(a(f),f=null):m=a,n},abort:d};return n}axe.utils.queue=c}();var za=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&\"function\"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?\"symbol\":typeof a};!function(a){\"use strict\";function b(){var a=\"axe\",b=\"\";return void 0!==axe&&axe._audit&&!axe._audit.application&&(a=axe._audit.application),void 0!==axe&&(b=axe.version),a+\".\"+b}function c(a){if(\"object\"===(void 0===a?\"undefined\":za(a))&&\"string\"==typeof a.uuid&&!0===a._respondable){var c=b();return a._source===c||\"axe.x.y.z\"===a._source||\"axe.x.y.z\"===c}return!1}function d(a,c,d,e,f,g){var h;d instanceof Error&&(h={name:d.name,message:d.message,stack:d.stack},d=void 0);var i={uuid:e,topic:c,message:d,error:h,_respondable:!0,_source:b(),_keepalive:f};\"function\"==typeof g&&(j[e]=g),a.postMessage(JSON.stringify(i),\"*\")}function e(a,b,c,e,f){d(a,b,c,Va.v1(),e,f)}function f(a,b,c){return function(e,f,g){d(a,b,e,c,f,g)}}function g(a,b,c){var d=b.topic,e=k[d];if(e){var g=f(a,null,b.uuid);e(b.message,c,g)}}function h(a){var b=a.message||\"Unknown error occurred\",c=l.includes(a.name)?a.name:\"Error\",d=window[c]||Error;return a.stack&&(b+=\"\\n\"+a.stack.replace(a.message,\"\")),new d(b)}function i(a){var b;if(\"string\"==typeof a){try{b=JSON.parse(a)}catch(a){}if(c(b))return\"object\"===za(b.error)?b.error=h(b.error):b.error=void 0,b}}var j={},k={},l=Object.freeze([\"EvalError\",\"RangeError\",\"ReferenceError\",\"SyntaxError\",\"TypeError\",\"URIError\"]);e.subscribe=function(a,b){k[a]=b},e.isInFrame=function(a){return a=a||window,!!a.frameElement},\"function\"==typeof window.addEventListener&&window.addEventListener(\"message\",function(a){var b=i(a.data);if(b){var c=b.uuid,e=b._keepalive,h=j[c];if(h){h(b.error||b.message,e,f(a.source,b.topic,c)),e||delete j[c]}if(!b.error)try{g(a.source,b,e)}catch(e){d(a.source,b.topic,e,c,!1)}}},!1),a.respondable=e}(utils),axe.utils.ruleShouldRun=function(a,b,c){\"use strict\";var d=c.runOnly||{},e=(c.rules||{})[a.id];return!(a.pageLevel&&!b.page)&&(\"rule\"===d.type?-1!==d.values.indexOf(a.id):e&&\"boolean\"==typeof e.enabled?e.enabled:\"tag\"===d.type&&d.values?ra(a,d.values):ra(a,[]))},axe.utils.getScrollState=function(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:window,b=a.document.documentElement;return[void 0!==a.pageXOffset?{elm:a,top:a.pageYOffset,left:a.pageXOffset}:{elm:b,top:b.scrollTop,left:b.scrollLeft}].concat(ua(document.body))},axe.utils.setScrollState=function(a){a.forEach(function(a){return ta(a.elm,a.top,a.left)})},axe.utils.select=function(a,b){\"use strict\";var c,d=[];if(axe._selectCache)for(var e=0,f=axe._selectCache.length;e<f;e++){var g=axe._selectCache[e];if(g.selector===a)return g.result}for(var h=function(a){return function(b){return wa(b,a)}}(b),i=ya(b.include),j=0;j<i.length;j++)c=i[j],c.actualNode.nodeType===c.actualNode.ELEMENT_NODE&&axe.utils.matchesSelector(c.actualNode,a)&&h(c)&&(d=xa(d,[c])),d=xa(d,axe.utils.querySelectorAllFilter(c,a,h));return axe._selectCache&&axe._selectCache.push({selector:a,result:d}),d},axe.utils.toArray=function(a){\"use strict\";return Array.prototype.slice.call(a)},axe.utils.uniqueArray=function(a,b){return a.concat(b).filter(function(a,b,c){return c.indexOf(a)===b})};var Va;!function(a){function b(a,b,c){var d=b&&c||0,e=0;for(b=b||[],a.toLowerCase().replace(/[0-9a-f]{2}/g,function(a){e<16&&(b[d+e++]=l[a])});e<16;)b[d+e++]=0;return b}function c(a,b){var c=b||0,d=k;return d[a[c++]]+d[a[c++]]+d[a[c++]]+d[a[c++]]+\"-\"+d[a[c++]]+d[a[c++]]+\"-\"+d[a[c++]]+d[a[c++]]+\"-\"+d[a[c++]]+d[a[c++]]+\"-\"+d[a[c++]]+d[a[c++]]+d[a[c++]]+d[a[c++]]+d[a[c++]]+d[a[c++]]}function d(a,b,d){var e=b&&d||0,f=b||[];a=a||{};var g=null!=a.clockseq?a.clockseq:p,h=null!=a.msecs?a.msecs:(new Date).getTime(),i=null!=a.nsecs?a.nsecs:r+1,j=h-q+(i-r)/1e4;if(j<0&&null==a.clockseq&&(g=g+1&16383),(j<0||h>q)&&null==a.nsecs&&(i=0),i>=1e4)throw new Error(\"uuid.v1(): Can't create more than 10M uuids/sec\");q=h,r=i,p=g,h+=122192928e5;var k=(1e4*(268435455&h)+i)%4294967296;f[e++]=k>>>24&255,f[e++]=k>>>16&255,f[e++]=k>>>8&255,f[e++]=255&k;var l=h/4294967296*1e4&268435455;f[e++]=l>>>8&255,f[e++]=255&l,f[e++]=l>>>24&15|16,f[e++]=l>>>16&255,f[e++]=g>>>8|128,f[e++]=255&g;for(var m=a.node||o,n=0;n<6;n++)f[e+n]=m[n];return b||c(f)}function e(a,b,d){var e=b&&d||0;\"string\"==typeof a&&(b=\"binary\"==a?new j(16):null,a=null),a=a||{};var g=a.random||(a.rng||f)();if(g[6]=15&g[6]|64,g[8]=63&g[8]|128,b)for(var h=0;h<16;h++)b[e+h]=g[h];return b||c(g)}var f,g=a.crypto||a.msCrypto;if(!f&&g&&g.getRandomValues){var h=new Uint8Array(16);f=function(){return g.getRandomValues(h),h}}if(!f){var i=new Array(16);f=function(){for(var a,b=0;b<16;b++)0==(3&b)&&(a=4294967296*Math.random()),i[b]=a>>>((3&b)<<3)&255;return i}}for(var j=\"function\"==typeof a.Buffer?a.Buffer:Array,k=[],l={},m=0;m<256;m++)k[m]=(m+256).toString(16).substr(1),l[k[m]]=m;var n=f(),o=[1|n[0],n[1],n[2],n[3],n[4],n[5]],p=16383&(n[6]<<8|n[7]),q=0,r=0;Va=e,Va.v1=d,Va.v4=e,Va.parse=b,Va.unparse=c,Va.BufferClass=j}(window),axe._load({data:{rules:{accesskeys:{description:\"Ensures every accesskey attribute value is unique\",help:\"accesskey attribute value must be unique\"},\"area-alt\":{description:\"Ensures <area> elements of image maps have alternate text\",help:\"Active <area> elements must have alternate text\"},\"aria-allowed-attr\":{description:\"Ensures ARIA attributes are allowed for an element's role\",help:\"Elements must only use allowed ARIA attributes\"},\"aria-hidden-body\":{description:\"Ensures aria-hidden='true' is not present on the document body.\",help:\"aria-hidden='true' must not be present on the document body\"},\"aria-required-attr\":{description:\"Ensures elements with ARIA roles have all required ARIA attributes\",help:\"Required ARIA attributes must be provided\"},\"aria-required-children\":{description:\"Ensures elements with an ARIA role that require child roles contain them\",help:\"Certain ARIA roles must contain particular children\"},\"aria-required-parent\":{description:\"Ensures elements with an ARIA role that require parent roles are contained by them\",help:\"Certain ARIA roles must be contained by particular parents\"},\"aria-roles\":{description:\"Ensures all elements with a role attribute use a valid value\",help:\"ARIA roles used must conform to valid values\"},\"aria-valid-attr-value\":{description:\"Ensures all ARIA attributes have valid values\",help:\"ARIA attributes must conform to valid values\"},\"aria-valid-attr\":{description:\"Ensures attributes that begin with aria- are valid ARIA attributes\",help:\"ARIA attributes must conform to valid names\"},\"audio-caption\":{description:\"Ensures <audio> elements have captions\",help:\"<audio> elements must have a captions track\"},blink:{description:\"Ensures <blink> elements are not used\",help:\"<blink> elements are deprecated and must not be used\"},\"button-name\":{description:\"Ensures buttons have discernible text\",help:\"Buttons must have discernible text\"},bypass:{description:\"Ensures each page has at least one mechanism for a user to bypass navigation and jump straight to the content\",help:\"Page must have means to bypass repeated blocks\"},checkboxgroup:{description:'Ensures related <input type=\"checkbox\"> elements have a group and that that group designation is consistent',help:\"Checkbox inputs with the same name attribute value must be part of a group\"},\"color-contrast\":{description:\"Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds\",help:\"Elements must have sufficient color contrast\"},\"definition-list\":{description:\"Ensures <dl> elements are structured correctly\",help:\"<dl> elements must only directly contain properly-ordered <dt> and <dd> groups, <script> or <template> elements\"},dlitem:{description:\"Ensures <dt> and <dd> elements are contained by a <dl>\",help:\"<dt> and <dd> elements must be contained by a <dl>\"},\"document-title\":{description:\"Ensures each HTML document contains a non-empty <title> element\",help:\"Documents must have <title> element to aid in navigation\"},\"duplicate-id\":{description:\"Ensures every id attribute value is unique\",help:\"id attribute value must be unique\"},\"empty-heading\":{description:\"Ensures headings have discernible text\",help:\"Headings must not be empty\"},\"focus-order-semantics\":{description:\"Ensures elements in the focus order have an appropriate role\",help:\"Elements in the focus order need a role appropriate for interactive content\"},\"frame-title-unique\":{description:\"Ensures <iframe> and <frame> elements contain a unique title attribute\",help:\"Frames must have a unique title attribute\"},\"frame-title\":{description:\"Ensures <iframe> and <frame> elements contain a non-empty title attribute\",help:\"Frames must have title attribute\"},\"heading-order\":{description:\"Ensures the order of headings is semantically correct\",help:\"Heading levels should only increase by one\"},\"hidden-content\":{description:\"Informs users about hidden content.\",help:\"Hidden content on the page cannot be analyzed\"},\"html-has-lang\":{description:\"Ensures every HTML document has a lang attribute\",help:\"<html> element must have a lang attribute\"},\"html-lang-valid\":{description:\"Ensures the lang attribute of the <html> element has a valid value\",help:\"<html> element must have a valid value for the lang attribute\"},\"image-alt\":{description:\"Ensures <img> elements have alternate text or a role of none or presentation\",help:\"Images must have alternate text\"},\"image-redundant-alt\":{description:\"Ensure button and link text is not repeated as image alternative\",help:\"Text of buttons and links should not be repeated in the image alternative\"},\"input-image-alt\":{description:'Ensures <input type=\"image\"> elements have alternate text',help:\"Image buttons must have alternate text\"},\"label-title-only\":{description:\"Ensures that every form element is not solely labeled using the title or aria-describedby attributes\",help:\"Form elements should have a visible label\"},label:{description:\"Ensures every form element has a label\",help:\"Form elements must have labels\"},\"landmark-main-is-top-level\":{description:\"The main landmark should not be contained in another landmark\",help:\"Main landmark is not at top level\"},\"landmark-one-main\":{description:\"Ensures a navigation point to the primary content of the page. If the page contains iframes, each iframe should contain either no main landmarks or just one.\",help:\"Page must contain one main landmark.\"},\"layout-table\":{description:\"Ensures presentational <table> elements do not use <th>, <caption> elements or the summary attribute\",help:\"Layout tables must not use data table elements\"},\"link-in-text-block\":{description:\"Links can be distinguished without relying on color\",help:\"Links must be distinguished from surrounding text in a way that does not rely on color\"},\"link-name\":{description:\"Ensures links have discernible text\",help:\"Links must have discernible text\"},list:{description:\"Ensures that lists are structured correctly\",help:\"<ul> and <ol> must only directly contain <li>, <script> or <template> elements\"},listitem:{description:\"Ensures <li> elements are used semantically\",help:\"<li> elements must be contained in a <ul> or <ol>\"},marquee:{description:\"Ensures <marquee> elements are not used\",help:\"<marquee> elements are deprecated and must not be used\"},\"meta-refresh\":{description:'Ensures <meta http-equiv=\"refresh\"> is not used',help:\"Timed refresh must not exist\"},\"meta-viewport-large\":{description:'Ensures <meta name=\"viewport\"> can scale a significant amount',help:\"Users should be able to zoom and scale the text up to 500%\"},\"meta-viewport\":{description:'Ensures <meta name=\"viewport\"> does not disable text scaling and zooming',help:\"Zooming and scaling must not be disabled\"},\"object-alt\":{description:\"Ensures <object> elements have alternate text\",help:\"<object> elements must have alternate text\"},\"p-as-heading\":{description:\"Ensure p elements are not used to style headings\",help:\"Bold, italic text and font-size are not used to style p elements as a heading\"},radiogroup:{description:'Ensures related <input type=\"radio\"> elements have a group and that the group designation is consistent',help:\"Radio inputs with the same name attribute value must be part of a group\"},region:{description:\"Ensures all content is contained within a landmark region\",help:\"Content should be contained in a landmark region\"},\"scope-attr-valid\":{description:\"Ensures the scope attribute is used correctly on tables\",help:\"scope attribute should be used correctly\"},\"server-side-image-map\":{description:\"Ensures that server-side image maps are not used\",help:\"Server-side image maps must not be used\"},\"skip-link\":{description:\"Ensure all skip links have a focusable target\",help:\"The skip-link target should exist and be focusable\"},tabindex:{description:\"Ensures tabindex attribute values are not greater than 0\",help:\"Elements should not have tabindex greater than zero\"},\"table-duplicate-name\":{description:\"Ensure that tables do not have the same summary and caption\",help:\"The <caption> element should not contain the same text as the summary attribute\"},\"table-fake-caption\":{description:\"Ensure that tables with a caption use the <caption> element.\",help:\"Data or header cells should not be used to give caption to a data table.\"},\"td-has-header\":{description:\"Ensure that each non-empty data cell in a large table has one or more table headers\",help:\"All non-empty td element in table larger than 3 by 3 must have an associated table header\"},\"td-headers-attr\":{description:\"Ensure that each cell in a table using the headers refers to another cell in that table\",help:\"All cells in a table element that use the headers attribute must only refer to other cells of that same table\"},\"th-has-data-cells\":{description:\"Ensure that each table header in a data table refers to data cells\",help:\"All th elements and elements with role=columnheader/rowheader must have data cells they describe\"},\"valid-lang\":{description:\"Ensures lang attributes have valid values\",help:\"lang attribute must have a valid value\"},\"video-caption\":{description:\"Ensures <video> elements have captions\",help:\"<video> elements must have captions\"},\"video-description\":{description:\"Ensures <video> elements have audio descriptions\",help:\"<video> elements must have an audio description track\"}},checks:{accesskeys:{impact:\"serious\",messages:{pass:function(a){return\"Accesskey attribute value is unique\"},fail:function(a){return\"Document has multiple elements with the same accesskey\"}}},\"non-empty-alt\":{impact:\"critical\",messages:{pass:function(a){return\"Element has a non-empty alt attribute\"},fail:function(a){return\"Element has no alt attribute or the alt attribute is empty\"}}},\"non-empty-title\":{impact:\"serious\",messages:{pass:function(a){return\"Element has a title attribute\"},fail:function(a){return\"Element has no title attribute or the title attribute is empty\"}}},\"aria-label\":{impact:\"serious\",messages:{pass:function(a){return\"aria-label attribute exists and is not empty\"},fail:function(a){return\"aria-label attribute does not exist or is empty\"}}},\"aria-labelledby\":{impact:\"serious\",messages:{pass:function(a){return\"aria-labelledby attribute exists and references elements that are visible to screen readers\"},fail:function(a){return\"aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty or not visible\"}}},\"aria-allowed-attr\":{impact:\"critical\",messages:{pass:function(a){return\"ARIA attributes are used correctly for the defined role\"},fail:function(a){var b=\"ARIA attribute\"+(a.data&&a.data.length>1?\"s are\":\" is\")+\" not allowed:\",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" \"+d;return b}}},\"aria-hidden-body\":{impact:\"critical\",messages:{pass:function(a){return\"No aria-hidden attribute is present on document body\"},fail:function(a){return\"aria-hidden=true should not be present on the document body\"}}},\"aria-required-attr\":{impact:\"critical\",messages:{pass:function(a){return\"All required ARIA attributes are present\"},fail:function(a){var b=\"Required ARIA attribute\"+(a.data&&a.data.length>1?\"s\":\"\")+\" not present:\",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" \"+d;return b}}},\"aria-required-children\":{impact:\"critical\",messages:{pass:function(a){return\"Required ARIA children are present\"},fail:function(a){var b=\"Required ARIA \"+(a.data&&a.data.length>1?\"children\":\"child\")+\" role not present:\",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" \"+d;return b}}},\"aria-required-parent\":{impact:\"critical\",messages:{pass:function(a){return\"Required ARIA parent role present\"},fail:function(a){var b=\"Required ARIA parent\"+(a.data&&a.data.length>1?\"s\":\"\")+\" role not present:\",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" \"+d;return b}}},invalidrole:{impact:\"critical\",messages:{pass:function(a){return\"ARIA role is valid\"},fail:function(a){return\"Role must be one of the valid ARIA roles\"}}},abstractrole:{impact:\"serious\",messages:{pass:function(a){return\"Abstract roles are not used\"},fail:function(a){return\"Abstract roles cannot be directly used\"}}},\"aria-valid-attr-value\":{impact:\"critical\",messages:{pass:function(a){return\"ARIA attribute values are valid\"},fail:function(a){var b=\"Invalid ARIA attribute value\"+(a.data&&a.data.length>1?\"s\":\"\")+\":\",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" \"+d;return b}}},\"aria-errormessage\":{impact:\"critical\",messages:{pass:function(a){return\"Uses a supported aria-errormessage technique\"},fail:function(a){var b=\"aria-errormessage value\"+(a.data&&a.data.length>1?\"s\":\"\")+\" \",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" `\"+d;return b+=\"` must use a technique to announce the message (e.g., aria-live, aria-describedby, role=alert, etc.)\"}}},\"aria-valid-attr\":{impact:\"critical\",messages:{pass:function(a){return\"ARIA attribute name\"+(a.data&&a.data.length>1?\"s\":\"\")+\" are valid\"},fail:function(a){var b=\"Invalid ARIA attribute name\"+(a.data&&a.data.length>1?\"s\":\"\")+\":\",c=a.data;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\" \"+d;return b}}},caption:{impact:\"critical\",messages:{pass:function(a){return\"The multimedia element has a captions track\"},fail:function(a){return\"The multimedia element does not have a captions track\"},incomplete:function(a){return\"A captions track for this element could not be found\"}}},\"is-on-screen\":{impact:\"serious\",messages:{pass:function(a){return\"Element is not visible\"},fail:function(a){return\"Element is visible\"}}},\"non-empty-if-present\":{impact:\"critical\",messages:{pass:function(a){var b=\"Element \";return a.data?b+=\"has a non-empty value attribute\":b+=\"does not have a value attribute\",b},fail:function(a){return\"Element has a value attribute and the value attribute is empty\"}}},\"non-empty-value\":{impact:\"critical\",messages:{pass:function(a){return\"Element has a non-empty value attribute\"},fail:function(a){return\"Element has no value attribute or the value attribute is empty\"}}},\"button-has-visible-text\":{impact:\"critical\",messages:{pass:function(a){return\"Element has inner text that is visible to screen readers\"},fail:function(a){return\"Element does not have inner text that is visible to screen readers\"}}},\"role-presentation\":{impact:\"minor\",messages:{pass:function(a){return'Element\\'s default semantics were overriden with role=\"presentation\"'},fail:function(a){return'Element\\'s default semantics were not overridden with role=\"presentation\"'}}},\"role-none\":{impact:\"minor\",messages:{pass:function(a){return'Element\\'s default semantics were overriden with role=\"none\"'},fail:function(a){return'Element\\'s default semantics were not overridden with role=\"none\"'}}},\"focusable-no-name\":{impact:\"serious\",messages:{pass:function(a){return\"Element is not in tab order or has accessible text\"},fail:function(a){return\"Element is in tab order and does not have accessible text\"}}},\"internal-link-present\":{impact:\"serious\",messages:{pass:function(a){return\"Valid skip link found\"},fail:function(a){return\"No valid skip link found\"}}},\"header-present\":{impact:\"serious\",messages:{pass:function(a){return\"Page has a header\"},fail:function(a){return\"Page does not have a header\"}}},landmark:{impact:\"serious\",messages:{pass:function(a){return\"Page has a landmark region\"},fail:function(a){return\"Page does not have a landmark region\"}}},\"group-labelledby\":{impact:\"critical\",messages:{pass:function(a){return'All elements with the name \"'+a.data.name+'\" reference the same element with aria-labelledby'},fail:function(a){return'All elements with the name \"'+a.data.name+'\" do not reference the same element with aria-labelledby'}}},fieldset:{impact:\"critical\",messages:{pass:function(a){return\"Element is contained in a fieldset\"},fail:function(a){var b=\"\",c=a.data&&a.data.failureCode;return b+=\"no-legend\"===c?\"Fieldset does not have a legend as its first child\":\"empty-legend\"===c?\"Legend does not have text that is visible to screen readers\":\"mixed-inputs\"===c?\"Fieldset contains unrelated inputs\":\"no-group-label\"===c?\"ARIA group does not have aria-label or aria-labelledby\":\"group-mixed-inputs\"===c?\"ARIA group contains unrelated inputs\":\"Element does not have a containing fieldset or ARIA group\"}}},\"color-contrast\":{impact:\"serious\",messages:{pass:function(a){return\"Element has sufficient color contrast of \"+a.data.contrastRatio},fail:function(a){return\"Element has insufficient color contrast of \"+a.data.contrastRatio+\" (foreground color: \"+a.data.fgColor+\", background color: \"+a.data.bgColor+\", font size: \"+a.data.fontSize+\", font weight: \"+a.data.fontWeight+\"). Expected contrast ratio of \"+a.data.expectedContrastRatio},incomplete:{bgImage:\"Element's background color could not be determined due to a background image\",bgGradient:\"Element's background color could not be determined due to a background gradient\",imgNode:\"Element's background color could not be determined because element contains an image node\",bgOverlap:\"Element's background color could not be determined because it is overlapped by another element\",fgAlpha:\"Element's foreground color could not be determined because of alpha transparency\",elmPartiallyObscured:\"Element's background color could not be determined because it's partially obscured by another element\",elmPartiallyObscuring:\"Element's background color could not be determined because it partially overlaps other elements\",outsideViewport:\"Element's background color could not be determined because it's outside the viewport\",equalRatio:\"Element has a 1:1 contrast ratio with the background\",default:\"Unable to determine contrast ratio\"}}},\"structured-dlitems\":{impact:\"serious\",messages:{pass:function(a){return\"When not empty, element has both <dt> and <dd> elements\"},fail:function(a){return\"When not empty, element does not have at least one <dt> element followed by at least one <dd> element\"}}},\"only-dlitems\":{impact:\"serious\",messages:{pass:function(a){return\"List element only has direct children that are allowed inside <dt> or <dd> elements\"},fail:function(a){return\"List element has direct children that are not allowed inside <dt> or <dd> elements\"}}},dlitem:{impact:\"serious\",messages:{pass:function(a){return\"Description list item has a <dl> parent element\"},fail:function(a){return\"Description list item does not have a <dl> parent element\"}}},\"doc-has-title\":{impact:\"serious\",messages:{pass:function(a){return\"Document has a non-empty <title> element\"},fail:function(a){return\"Document does not have a non-empty <title> element\"}}},\"duplicate-id\":{impact:\"moderate\",messages:{pass:function(a){return\"Document has no elements that share the same id attribute\"},fail:function(a){return\"Document has multiple elements with the same id attribute: \"+a.data}}},\"has-visible-text\":{impact:\"minor\",messages:{pass:function(a){return\"Element has text that is visible to screen readers\"},fail:function(a){return\"Element does not have text that is visible to screen readers\"}}},\"has-widget-role\":{impact:\"minor\",messages:{pass:function(a){return\"Element has a widget role.\"},fail:function(a){return\"Element does not have a widget role.\"}}},\"valid-scrollable-semantics\":{impact:\"minor\",messages:{pass:function(a){return\"Element has valid semantics for an element in the focus order.\"},fail:function(a){return\"Element has invalid semantics for an element in the focus order.\"}}},\"unique-frame-title\":{impact:\"serious\",messages:{pass:function(a){return\"Element's title attribute is unique\"},fail:function(a){return\"Element's title attribute is not unique\"}}},\"heading-order\":{impact:\"moderate\",messages:{pass:function(a){return\"Heading order valid\"},fail:function(a){return\"Heading order invalid\"}}},\"hidden-content\":{impact:\"minor\",messages:{pass:function(a){return\"All content on the page has been analyzed.\"},fail:function(a){return\"There were problems analyzing the content on this page.\"},incomplete:function(a){return\"There is hidden content on the page that was not analyzed. You will need to trigger the display of this content in order to analyze it.\"}}},\"has-lang\":{impact:\"serious\",messages:{pass:function(a){return\"The <html> element has a lang attribute\"},fail:function(a){return\"The <html> element does not have a lang attribute\"}}},\"valid-lang\":{impact:\"serious\",messages:{pass:function(a){return\"Value of lang attribute is included in the list of valid languages\"},fail:function(a){return\"Value of lang attribute not included in the list of valid languages\"}}},\"has-alt\":{impact:\"critical\",messages:{pass:function(a){return\"Element has an alt attribute\"},fail:function(a){return\"Element does not have an alt attribute\"}}},\"duplicate-img-label\":{impact:\"minor\",messages:{pass:function(a){return\"Element does not duplicate existing text in <img> alt text\"},fail:function(a){return\"Element contains <img> element with alt text that duplicates existing text\"}}},\"title-only\":{impact:\"serious\",messages:{pass:function(a){return\"Form element does not solely use title attribute for its label\"},fail:function(a){return\"Only title used to generate label for form element\"}}},\"implicit-label\":{impact:\"critical\",messages:{pass:function(a){return\"Form element has an implicit (wrapped) <label>\"},fail:function(a){return\"Form element does not have an implicit (wrapped) <label>\"}}},\"explicit-label\":{impact:\"critical\",messages:{pass:function(a){return\"Form element has an explicit <label>\"},fail:function(a){return\"Form element does not have an explicit <label>\"}}},\"help-same-as-label\":{impact:\"minor\",messages:{pass:function(a){return\"Help text (title or aria-describedby) does not duplicate label text\"},fail:function(a){return\"Help text (title or aria-describedby) text is the same as the label text\"}}},\"multiple-label\":{impact:\"serious\",messages:{pass:function(a){return\"Form element does not have multiple <label> elements\"},fail:function(a){return\"Form element has multiple <label> elements\"}}},\"main-is-top-level\":{impact:\"moderate\",messages:{pass:function(a){return\"The main landmark is at the top level.\"},fail:function(a){return\"The main landmark is contained in another landmark.\"}}},\"has-at-least-one-main\":{impact:\"moderate\",messages:{pass:function(a){return\"Document has at least one main landmark\"},fail:function(a){return\"Document has no main landmarks\"}}},\"has-no-more-than-one-main\":{impact:\"moderate\",messages:{pass:function(a){return\"Document has no more than one main landmark\"},fail:function(a){return\"Document has more than one main landmark\"}}},\"has-th\":{impact:\"serious\",messages:{pass:function(a){return\"Layout table does not use <th> elements\"},fail:function(a){return\"Layout table uses <th> elements\"}}},\"has-caption\":{impact:\"serious\",messages:{pass:function(a){return\"Layout table does not use <caption> element\"},fail:function(a){return\"Layout table uses <caption> element\"}}},\"has-summary\":{impact:\"serious\",messages:{pass:function(a){return\"Layout table does not use summary attribute\"},fail:function(a){return\"Layout table uses summary attribute\"}}},\"link-in-text-block\":{impact:\"serious\",messages:{pass:function(a){return\"Links can be distinguished from surrounding text in some way other than by color\"},fail:function(a){return\"Links need to be distinguished from surrounding text in some way other than by color\"},incomplete:{bgContrast:\"Element's contrast ratio could not be determined. Check for a distinct hover/focus style\",bgImage:\"Element's contrast ratio could not be determined due to a background image\",bgGradient:\"Element's contrast ratio could not be determined due to a background gradient\",imgNode:\"Element's contrast ratio could not be determined because element contains an image node\",bgOverlap:\"Element's contrast ratio could not be determined because of element overlap\",default:\"Unable to determine contrast ratio\"}}},\"only-listitems\":{impact:\"serious\",messages:{pass:function(a){return\"List element only has direct children that are allowed inside <li> elements\"},fail:function(a){return\"List element has direct children that are not allowed inside <li> elements\"}}},listitem:{impact:\"serious\",messages:{pass:function(a){return'List item has a <ul>, <ol> or role=\"list\" parent element'},fail:function(a){return'List item does not have a <ul>, <ol> or role=\"list\" parent element'}}},\"meta-refresh\":{impact:\"critical\",messages:{pass:function(a){return\"<meta> tag does not immediately refresh the page\"},fail:function(a){return\"<meta> tag forces timed refresh of page\"}}},\"meta-viewport-large\":{impact:\"minor\",messages:{pass:function(a){\nreturn\"<meta> tag does not prevent significant zooming on mobile devices\"},fail:function(a){return\"<meta> tag limits zooming on mobile devices\"}}},\"meta-viewport\":{impact:\"critical\",messages:{pass:function(a){return\"<meta> tag does not disable zooming on mobile devices\"},fail:function(a){return\"<meta> tag disables zooming on mobile devices\"}}},\"p-as-heading\":{impact:\"serious\",messages:{pass:function(a){return\"<p> elements are not styled as headings\"},fail:function(a){return\"Heading elements should be used instead of styled p elements\"}}},region:{impact:\"moderate\",messages:{pass:function(a){return\"Content contained by ARIA landmark\"},fail:function(a){return\"Content not contained by an ARIA landmark\"}}},\"html5-scope\":{impact:\"moderate\",messages:{pass:function(a){return\"Scope attribute is only used on table header elements (<th>)\"},fail:function(a){return\"In HTML 5, scope attributes may only be used on table header elements (<th>)\"}}},\"scope-value\":{impact:\"critical\",messages:{pass:function(a){return\"Scope attribute is used correctly\"},fail:function(a){return\"The value of the scope attribute may only be 'row' or 'col'\"}}},exists:{impact:\"minor\",messages:{pass:function(a){return\"Element does not exist\"},fail:function(a){return\"Element exists\"}}},\"skip-link\":{impact:\"moderate\",messages:{pass:function(a){return\"Skip link target exists\"},incomplete:function(a){return\"Skip link target should become visible on activation\"},fail:function(a){return\"No skip link target\"}}},tabindex:{impact:\"serious\",messages:{pass:function(a){return\"Element does not have a tabindex greater than 0\"},fail:function(a){return\"Element has a tabindex greater than 0\"}}},\"same-caption-summary\":{impact:\"minor\",messages:{pass:function(a){return\"Content of summary attribute and <caption> are not duplicated\"},fail:function(a){return\"Content of summary attribute and <caption> element are identical\"}}},\"caption-faked\":{impact:\"serious\",messages:{pass:function(a){return\"The first row of a table is not used as a caption\"},fail:function(a){return\"The first row of the table should be a caption instead of a table cell\"}}},\"td-has-header\":{impact:\"critical\",messages:{pass:function(a){return\"All non-empty data cells have table headers\"},fail:function(a){return\"Some non-empty data cells do not have table headers\"}}},\"td-headers-attr\":{impact:\"serious\",messages:{pass:function(a){return\"The headers attribute is exclusively used to refer to other cells in the table\"},fail:function(a){return\"The headers attribute is not exclusively used to refer to other cells in the table\"}}},\"th-has-data-cells\":{impact:\"serious\",messages:{pass:function(a){return\"All table header cells refer to data cells\"},fail:function(a){return\"Not all table header cells refer to data cells\"},incomplete:function(a){return\"Table data cells are missing or empty\"}}},description:{impact:\"critical\",messages:{pass:function(a){return\"The multimedia element has an audio description track\"},fail:function(a){return\"The multimedia element does not have an audio description track\"},incomplete:function(a){return\"An audio description track for this element could not be found\"}}}},failureSummaries:{any:{failureMessage:function(a){var b=\"Fix any of the following:\",c=a;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\"\\n  \"+d.split(\"\\n\").join(\"\\n  \");return b}},none:{failureMessage:function(a){var b=\"Fix all of the following:\",c=a;if(c)for(var d,e=-1,f=c.length-1;e<f;)d=c[e+=1],b+=\"\\n  \"+d.split(\"\\n\").join(\"\\n  \");return b}}},incompleteFallbackMessage:function(a){return\"aXe couldn't tell the reason. Time to break out the element inspector!\"}},rules:[{id:\"accesskeys\",selector:\"[accesskey]\",excludeHidden:!1,tags:[\"wcag2a\",\"wcag211\",\"cat.keyboard\"],all:[],any:[],none:[\"accesskeys\"]},{id:\"area-alt\",selector:\"map area[href]\",excludeHidden:!1,tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag111\",\"section508\",\"section508.22.a\"],all:[],any:[\"non-empty-alt\",\"non-empty-title\",\"aria-label\",\"aria-labelledby\"],none:[]},{id:\"aria-allowed-attr\",matches:function(a,b){var c=a.getAttribute(\"role\");c||(c=axe.commons.aria.implicitRole(a));var d=axe.commons.aria.allowedAttr(c);if(c&&d){var e=/^aria-/;if(a.hasAttributes())for(var f=a.attributes,g=0,h=f.length;g<h;g++)if(e.test(f[g].name))return!0}return!1},tags:[\"cat.aria\",\"wcag2a\",\"wcag411\",\"wcag412\"],all:[],any:[\"aria-allowed-attr\"],none:[]},{id:\"aria-hidden-body\",selector:\"body\",excludeHidden:!1,tags:[\"cat.aria\",\"wcag2a\",\"wcag412\"],all:[],any:[\"aria-hidden-body\"],none:[]},{id:\"aria-required-attr\",selector:\"[role]\",tags:[\"cat.aria\",\"wcag2a\",\"wcag411\",\"wcag412\"],all:[],any:[\"aria-required-attr\"],none:[]},{id:\"aria-required-children\",selector:\"[role]\",tags:[\"cat.aria\",\"wcag2a\",\"wcag131\"],all:[],any:[\"aria-required-children\"],none:[]},{id:\"aria-required-parent\",selector:\"[role]\",tags:[\"cat.aria\",\"wcag2a\",\"wcag131\"],all:[],any:[\"aria-required-parent\"],none:[]},{id:\"aria-roles\",selector:\"[role]\",tags:[\"cat.aria\",\"wcag2a\",\"wcag131\",\"wcag411\",\"wcag412\"],all:[],any:[],none:[\"invalidrole\",\"abstractrole\"]},{id:\"aria-valid-attr-value\",matches:function(a,b){var c=/^aria-/;if(a.hasAttributes())for(var d=a.attributes,e=0,f=d.length;e<f;e++)if(c.test(d[e].name))return!0;return!1},tags:[\"cat.aria\",\"wcag2a\",\"wcag131\",\"wcag411\",\"wcag412\"],all:[{options:[],id:\"aria-valid-attr-value\"},\"aria-errormessage\"],any:[],none:[]},{id:\"aria-valid-attr\",matches:function(a,b){var c=/^aria-/;if(a.hasAttributes())for(var d=a.attributes,e=0,f=d.length;e<f;e++)if(c.test(d[e].name))return!0;return!1},tags:[\"cat.aria\",\"wcag2a\",\"wcag411\"],all:[],any:[{options:[],id:\"aria-valid-attr\"}],none:[]},{id:\"audio-caption\",selector:\"audio\",excludeHidden:!1,tags:[\"cat.time-and-media\",\"wcag2a\",\"wcag121\",\"section508\",\"section508.22.a\"],all:[],any:[],none:[\"caption\"]},{id:\"blink\",selector:\"blink\",excludeHidden:!1,tags:[\"cat.time-and-media\",\"wcag2a\",\"wcag222\",\"section508\",\"section508.22.j\"],all:[],any:[],none:[\"is-on-screen\"]},{id:\"button-name\",selector:'button, [role=\"button\"], input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"]',tags:[\"cat.name-role-value\",\"wcag2a\",\"wcag412\",\"section508\",\"section508.22.a\"],all:[],any:[\"non-empty-if-present\",\"non-empty-value\",\"button-has-visible-text\",\"aria-label\",\"aria-labelledby\",\"role-presentation\",\"role-none\"],none:[\"focusable-no-name\"]},{id:\"bypass\",selector:\"html\",pageLevel:!0,matches:function(a,b){return!!a.querySelector(\"a[href]\")},tags:[\"cat.keyboard\",\"wcag2a\",\"wcag241\",\"section508\",\"section508.22.o\"],all:[],any:[\"internal-link-present\",\"header-present\",\"landmark\"],none:[]},{id:\"checkboxgroup\",selector:\"input[type=checkbox][name]\",tags:[\"cat.forms\",\"best-practice\"],all:[],any:[\"group-labelledby\",\"fieldset\"],none:[]},{id:\"color-contrast\",matches:function(a,b){var c=a.nodeName.toUpperCase(),d=a.type;if(\"true\"===a.getAttribute(\"aria-disabled\")||axe.commons.dom.findUpVirtual(b,'[aria-disabled=\"true\"]'))return!1;if(\"INPUT\"===c)return-1===[\"hidden\",\"range\",\"color\",\"checkbox\",\"radio\",\"image\"].indexOf(d)&&!a.disabled;if(\"SELECT\"===c)return!!a.options.length&&!a.disabled;if(\"TEXTAREA\"===c)return!a.disabled;if(\"OPTION\"===c)return!1;if(\"BUTTON\"===c&&a.disabled||axe.commons.dom.findUpVirtual(b,\"button[disabled]\"))return!1;if(\"FIELDSET\"===c&&a.disabled||axe.commons.dom.findUpVirtual(b,\"fieldset[disabled]\"))return!1;var e=axe.commons.dom.findUpVirtual(b,\"label\");if(\"LABEL\"===c||e){var f=a,g=b;e&&(f=e,g=axe.utils.getNodeFromTree(axe._tree[0],e));var h=axe.commons.dom.getRootNode(f),i=f.htmlFor&&h.getElementById(f.htmlFor);if(i&&i.disabled)return!1;var i=axe.utils.querySelectorAll(g,'input:not([type=\"hidden\"]):not([type=\"image\"]):not([type=\"button\"]):not([type=\"submit\"]):not([type=\"reset\"]), select, textarea');if(i.length&&i[0].actualNode.disabled)return!1}if(a.getAttribute(\"id\")){var j=axe.commons.utils.escapeSelector(a.getAttribute(\"id\")),k=axe.commons.dom.getRootNode(a),i=k.querySelector(\"[aria-labelledby~=\"+j+\"]\");if(i&&i.disabled)return!1}if(\"\"===axe.commons.text.visibleVirtual(b,!1,!0))return!1;var l,m,n=document.createRange(),o=b.children,p=o.length;for(m=0;m<p;m++)l=o[m],3===l.actualNode.nodeType&&\"\"!==axe.commons.text.sanitize(l.actualNode.nodeValue)&&n.selectNodeContents(l.actualNode);var q=n.getClientRects();for(p=q.length,m=0;m<p;m++)if(axe.commons.dom.visuallyOverlaps(q[m],a))return!0;return!1},excludeHidden:!1,options:{noScroll:!1},tags:[\"cat.color\",\"wcag2aa\",\"wcag143\"],all:[],any:[\"color-contrast\"],none:[]},{id:\"definition-list\",selector:\"dl\",matches:function(a,b){return!a.getAttribute(\"role\")},tags:[\"cat.structure\",\"wcag2a\",\"wcag131\"],all:[],any:[],none:[\"structured-dlitems\",\"only-dlitems\"]},{id:\"dlitem\",selector:\"dd, dt\",matches:function(a,b){return!a.getAttribute(\"role\")},tags:[\"cat.structure\",\"wcag2a\",\"wcag131\"],all:[],any:[\"dlitem\"],none:[]},{id:\"document-title\",selector:\"html\",matches:function(a,b){return a.ownerDocument.defaultView.self===a.ownerDocument.defaultView.top},tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag242\"],all:[],any:[\"doc-has-title\"],none:[]},{id:\"duplicate-id\",selector:\"[id]\",excludeHidden:!1,tags:[\"cat.parsing\",\"wcag2a\",\"wcag411\"],all:[],any:[\"duplicate-id\"],none:[]},{id:\"empty-heading\",selector:'h1, h2, h3, h4, h5, h6, [role=\"heading\"]',matches:function(a,b){var c=void 0;return a.hasAttribute(\"role\")&&(c=a.getAttribute(\"role\").split(/\\s+/i).filter(axe.commons.aria.isValidRole)),c&&c.length>0?c.includes(\"heading\"):\"heading\"===axe.commons.aria.implicitRole(a)},tags:[\"cat.name-role-value\",\"best-practice\"],all:[],any:[\"has-visible-text\"],none:[]},{id:\"focus-order-semantics\",selector:\"div, h1, h2, h3, h4, h5, h6, [role=heading], p, span\",matches:function(a,b){return axe.commons.dom.insertedIntoFocusOrder(a)},tags:[\"cat.keyboard\",\"best-practice\",\"experimental\"],all:[],any:[{options:[],id:\"has-widget-role\"},{options:[],id:\"valid-scrollable-semantics\"}],none:[]},{id:\"frame-title-unique\",selector:\"frame[title], iframe[title]\",matches:function(a,b){var c=a.getAttribute(\"title\");return!!(c?axe.commons.text.sanitize(c).trim():\"\")},tags:[\"cat.text-alternatives\",\"best-practice\"],all:[],any:[],none:[\"unique-frame-title\"]},{id:\"frame-title\",selector:\"frame, iframe\",tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag241\",\"section508\",\"section508.22.i\"],all:[],any:[\"aria-label\",\"aria-labelledby\",\"non-empty-title\",\"role-presentation\",\"role-none\"],none:[]},{id:\"heading-order\",selector:\"h1, h2, h3, h4, h5, h6, [role=heading]\",matches:function(a,b){var c=void 0;return a.hasAttribute(\"role\")&&(c=a.getAttribute(\"role\").split(/\\s+/i).filter(axe.commons.aria.isValidRole)),c&&c.length>0?c.includes(\"heading\"):\"heading\"===axe.commons.aria.implicitRole(a)},tags:[\"cat.semantics\",\"best-practice\"],all:[],any:[\"heading-order\"],none:[]},{id:\"hidden-content\",selector:\"*\",excludeHidden:!1,tags:[\"experimental\",\"review-item\"],all:[],any:[\"hidden-content\"],none:[]},{id:\"html-has-lang\",selector:\"html\",tags:[\"cat.language\",\"wcag2a\",\"wcag311\"],all:[],any:[\"has-lang\"],none:[]},{id:\"html-lang-valid\",selector:\"html[lang]\",tags:[\"cat.language\",\"wcag2a\",\"wcag311\"],all:[],any:[],none:[\"valid-lang\"]},{id:\"image-alt\",selector:\"img, [role='img']:not(svg)\",tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag111\",\"section508\",\"section508.22.a\"],all:[],any:[\"has-alt\",\"aria-label\",\"aria-labelledby\",\"non-empty-title\",\"role-presentation\",\"role-none\"],none:[]},{id:\"image-redundant-alt\",selector:'button, [role=\"button\"], a[href], p, li, td, th',tags:[\"cat.text-alternatives\",\"best-practice\"],all:[],any:[],none:[\"duplicate-img-label\"]},{id:\"input-image-alt\",selector:'input[type=\"image\"]',tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag111\",\"section508\",\"section508.22.a\"],all:[],any:[\"non-empty-alt\",\"aria-label\",\"aria-labelledby\",\"non-empty-title\"],none:[]},{id:\"label-title-only\",selector:\"input, select, textarea\",matches:function(a,b){return\"input\"!==a.nodeName.toLowerCase()||!1===a.hasAttribute(\"type\")||!1===[\"hidden\",\"image\",\"button\",\"submit\",\"reset\"].includes(a.getAttribute(\"type\").toLowerCase())},tags:[\"cat.forms\",\"best-practice\"],all:[],any:[],none:[\"title-only\"]},{id:\"label\",selector:\"input, select, textarea\",matches:function(a,b){return\"input\"!==a.nodeName.toLowerCase()||!1===a.hasAttribute(\"type\")||!1===[\"hidden\",\"image\",\"button\",\"submit\",\"reset\"].includes(a.getAttribute(\"type\").toLowerCase())},tags:[\"cat.forms\",\"wcag2a\",\"wcag332\",\"wcag131\",\"section508\",\"section508.22.n\"],all:[],any:[\"aria-label\",\"aria-labelledby\",\"implicit-label\",\"explicit-label\",\"non-empty-title\"],none:[\"help-same-as-label\",\"multiple-label\"]},{id:\"landmark-main-is-top-level\",selector:\"main, [role=main]\",tags:[\"best-practice\"],all:[],any:[\"main-is-top-level\"],none:[]},{id:\"landmark-one-main\",selector:\"html\",tags:[\"best-practice\"],all:[\"has-at-least-one-main\",\"has-no-more-than-one-main\"],any:[],none:[]},{id:\"layout-table\",selector:\"table\",matches:function(a,b){return!axe.commons.table.isDataTable(a)},tags:[\"cat.semantics\",\"wcag2a\",\"wcag131\"],all:[],any:[],none:[\"has-th\",\"has-caption\",\"has-summary\"]},{id:\"link-in-text-block\",selector:\"a[href], [role=link]\",matches:function(a,b){var c=axe.commons.text.sanitize(a.textContent),d=a.getAttribute(\"role\");return(!d||\"link\"===d)&&(!!c&&(!!axe.commons.dom.isVisible(a,!1)&&axe.commons.dom.isInTextBlock(a)))},excludeHidden:!1,tags:[\"cat.color\",\"experimental\",\"wcag2a\",\"wcag141\"],all:[\"link-in-text-block\"],any:[],none:[]},{id:\"link-name\",selector:\"a[href], [role=link][href]\",matches:function(a,b){return\"button\"!==a.getAttribute(\"role\")},tags:[\"cat.name-role-value\",\"wcag2a\",\"wcag111\",\"wcag412\",\"wcag244\",\"section508\",\"section508.22.a\"],all:[],any:[\"has-visible-text\",\"aria-label\",\"aria-labelledby\",\"role-presentation\",\"role-none\"],none:[\"focusable-no-name\"]},{id:\"list\",selector:\"ul, ol\",matches:function(a,b){return!a.getAttribute(\"role\")},tags:[\"cat.structure\",\"wcag2a\",\"wcag131\"],all:[],any:[],none:[\"only-listitems\"]},{id:\"listitem\",selector:\"li\",matches:function(a,b){return!a.getAttribute(\"role\")},tags:[\"cat.structure\",\"wcag2a\",\"wcag131\"],all:[],any:[\"listitem\"],none:[]},{id:\"marquee\",selector:\"marquee\",excludeHidden:!1,tags:[\"cat.parsing\",\"wcag2a\",\"wcag222\"],all:[],any:[],none:[\"is-on-screen\"]},{id:\"meta-refresh\",selector:'meta[http-equiv=\"refresh\"]',excludeHidden:!1,tags:[\"cat.time\",\"wcag2a\",\"wcag2aaa\",\"wcag221\",\"wcag224\",\"wcag325\"],all:[],any:[\"meta-refresh\"],none:[]},{id:\"meta-viewport-large\",selector:'meta[name=\"viewport\"]',excludeHidden:!1,tags:[\"cat.sensory-and-visual-cues\",\"best-practice\"],all:[],any:[{options:{scaleMinimum:5,lowerBound:2},id:\"meta-viewport-large\"}],none:[]},{id:\"meta-viewport\",selector:'meta[name=\"viewport\"]',excludeHidden:!1,tags:[\"cat.sensory-and-visual-cues\",\"wcag2aa\",\"wcag144\"],all:[],any:[{options:{scaleMinimum:2},id:\"meta-viewport\"}],none:[]},{id:\"object-alt\",selector:\"object\",tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag111\",\"section508\",\"section508.22.a\"],all:[],any:[\"has-visible-text\",\"aria-label\",\"aria-labelledby\",\"non-empty-title\"],none:[]},{id:\"p-as-heading\",selector:\"p\",matches:function(a,b){var c=Array.from(a.parentNode.childNodes),d=a.textContent.trim(),e=/[.!?:;](?![.!?:;])/g;return!(0===d.length||(d.match(e)||[]).length>=2)&&0!==c.slice(c.indexOf(a)+1).filter(function(a){return\"P\"===a.nodeName.toUpperCase()&&\"\"!==a.textContent.trim()}).length},tags:[\"cat.semantics\",\"wcag2a\",\"wcag131\",\"experimental\"],all:[{options:{margins:[{weight:150,italic:!0},{weight:150,size:1.15},{italic:!0,size:1.15},{size:1.4}]},id:\"p-as-heading\"}],any:[],none:[]},{id:\"radiogroup\",selector:\"input[type=radio][name]\",tags:[\"cat.forms\",\"best-practice\"],all:[],any:[\"group-labelledby\",\"fieldset\"],none:[]},{id:\"region\",selector:\"html\",pageLevel:!0,tags:[\"cat.keyboard\",\"best-practice\"],all:[],any:[\"region\"],none:[]},{id:\"scope-attr-valid\",selector:\"td[scope], th[scope]\",tags:[\"cat.tables\",\"best-practice\"],all:[\"html5-scope\",\"scope-value\"],any:[],none:[]},{id:\"server-side-image-map\",selector:\"img[ismap]\",tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag211\",\"section508\",\"section508.22.f\"],all:[],any:[],none:[\"exists\"]},{id:\"skip-link\",selector:\"a[href]\",matches:function(a,b){var c=a.getAttribute(\"href\");return\"#\"===c[0]&&c.length>1},tags:[\"cat.keyboard\",\"best-practice\"],all:[],any:[\"skip-link\"],none:[]},{id:\"tabindex\",selector:\"[tabindex]\",tags:[\"cat.keyboard\",\"best-practice\"],all:[],any:[\"tabindex\"],none:[]},{id:\"table-duplicate-name\",selector:\"table\",tags:[\"cat.tables\",\"best-practice\"],all:[],any:[],none:[\"same-caption-summary\"]},{id:\"table-fake-caption\",selector:\"table\",matches:function(a,b){return axe.commons.table.isDataTable(a)},tags:[\"cat.tables\",\"experimental\",\"wcag2a\",\"wcag131\",\"section508\",\"section508.22.g\"],all:[\"caption-faked\"],any:[],none:[]},{id:\"td-has-header\",selector:\"table\",matches:function(a,b){if(axe.commons.table.isDataTable(a)){var c=axe.commons.table.toArray(a);return c.length>=3&&c[0].length>=3&&c[1].length>=3&&c[2].length>=3}return!1},tags:[\"cat.tables\",\"experimental\",\"wcag2a\",\"wcag131\",\"section508\",\"section508.22.g\"],all:[\"td-has-header\"],any:[],none:[]},{id:\"td-headers-attr\",selector:\"table\",tags:[\"cat.tables\",\"wcag2a\",\"wcag131\",\"section508\",\"section508.22.g\"],all:[\"td-headers-attr\"],any:[],none:[]},{id:\"th-has-data-cells\",selector:\"table\",matches:function(a,b){return axe.commons.table.isDataTable(a)},tags:[\"cat.tables\",\"wcag2a\",\"wcag131\",\"section508\",\"section508.22.g\"],all:[\"th-has-data-cells\"],any:[],none:[]},{id:\"valid-lang\",selector:\"[lang], [xml\\\\:lang]\",matches:function(a,b){return\"html\"!==a.nodeName.toLowerCase()},tags:[\"cat.language\",\"wcag2aa\",\"wcag312\"],all:[],any:[],none:[\"valid-lang\"]},{id:\"video-caption\",selector:\"video\",excludeHidden:!1,tags:[\"cat.text-alternatives\",\"wcag2a\",\"wcag122\",\"wcag123\",\"section508\",\"section508.22.a\"],all:[],any:[],none:[\"caption\"]},{id:\"video-description\",selector:\"video\",excludeHidden:!1,tags:[\"cat.text-alternatives\",\"wcag2aa\",\"wcag125\",\"section508\",\"section508.22.b\"],all:[],any:[],none:[\"description\"]}],checks:[{id:\"abstractrole\",evaluate:function(a,b,c){return\"abstract\"===axe.commons.aria.getRoleType(a.getAttribute(\"role\"))}},{id:\"aria-allowed-attr\",evaluate:function(a,b,c){b=b||{};var d,e,f,g=[],h=a.getAttribute(\"role\"),i=a.attributes;if(h||(h=axe.commons.aria.implicitRole(a)),f=axe.commons.aria.allowedAttr(h),Array.isArray(b[h])&&(f=axe.utils.uniqueArray(b[h].concat(f))),h&&f)for(var j=0,k=i.length;j<k;j++)d=i[j],e=d.name,axe.commons.aria.validateAttr(e)&&!f.includes(e)&&g.push(e+'=\"'+d.nodeValue+'\"');return!g.length||(this.data(g),!1)}},{id:\"aria-hidden-body\",evaluate:function(a,b,c){return\"true\"!==a.getAttribute(\"aria-hidden\")}},{id:\"aria-errormessage\",evaluate:function(a,b,c){b=Array.isArray(b)?b:[];var d=a.getAttribute(\"aria-errormessage\"),e=a.hasAttribute(\"aria-errormessage\"),f=axe.commons.dom.getRootNode(a);return!(-1===b.indexOf(d)&&e&&!function(){var b=d&&f.getElementById(d);if(b)return\"alert\"===b.getAttribute(\"role\")||\"assertive\"===b.getAttribute(\"aria-live\")||axe.utils.tokenList(a.getAttribute(\"aria-describedby\")||\"\").indexOf(d)>-1}())||(this.data(d),!1)}},{id:\"has-widget-role\",evaluate:function(a,b,c){var d=a.getAttribute(\"role\");if(null===d)return!1;var e=axe.commons.aria.getRoleType(d);return\"widget\"===e||\"composite\"===e},options:[]},{id:\"invalidrole\",evaluate:function(a,b,c){return!axe.commons.aria.isValidRole(a.getAttribute(\"role\"))}},{id:\"aria-required-attr\",evaluate:function(a,b,c){b=b||{};var d=[];if(a.hasAttributes()){var e,f=a.getAttribute(\"role\"),g=axe.commons.aria.requiredAttr(f);if(Array.isArray(b[f])&&(g=axe.utils.uniqueArray(b[f],g)),f&&g)for(var h=0,i=g.length;h<i;h++)e=g[h],a.getAttribute(e)||d.push(e)}return!d.length||(this.data(d),!1)}},{id:\"aria-required-children\",evaluate:function(a,b,c){function d(a,b,c,d){if(null===a)return!1;var e=g(c),f=['[role=\"'+c+'\"]'];return e&&(f=f.concat(e)),f=f.join(\",\"),d?h(a,f)||!!axe.utils.querySelectorAll(b,f)[0]:!!axe.utils.querySelectorAll(b,f)[0]}function e(a,b){var c,e;for(c=0,e=a.length;c<e;c++)if(null!==a[c]){var f=axe.utils.getNodeFromTree(axe._tree[0],a[c]);if(d(a[c],f,b,!0))return!0}return!1}var f=axe.commons.aria.requiredOwned,g=axe.commons.aria.implicitNodes,h=axe.commons.utils.matchesSelector,i=axe.commons.dom.idrefs,j=a.getAttribute(\"role\"),k=f(j);if(!k)return!0;var l=!1,m=k.one;if(!m){var l=!0;m=k.all}var n=function(a,b,f,g){var h,j=b.length,k=[],l=i(a,\"aria-owns\");for(h=0;h<j;h++){var m=b[h];if(d(a,c,m)||e(l,m)){if(!f)return null}else f&&k.push(m)}if(\"combobox\"===g){var n=k.indexOf(\"textbox\"),o=[\"text\",\"search\",\"email\",\"url\",\"tel\"];n>=0&&\"INPUT\"===a.tagName&&o.includes(a.type)&&k.splice(n,1);var p=k.indexOf(\"listbox\"),q=a.getAttribute(\"aria-expanded\");p>=0&&(!q||\"false\"===q)&&k.splice(p,1)}return k.length?k:!f&&b.length?b:null}(a,m,l,j);return!n||(this.data(n),!1)}},{id:\"aria-required-parent\",evaluate:function(a,b,c){function d(a){return(axe.commons.aria.implicitNodes(a)||[]).concat('[role=\"'+a+'\"]').join(\",\")}function e(a,b,c){var e,f,g=a.actualNode.getAttribute(\"role\"),h=[];if(b||(b=axe.commons.aria.requiredContext(g)),!b)return null;for(e=0,f=b.length;e<f;e++){if(c&&axe.utils.matchesSelector(a.actualNode,d(b[e])))return null;if(axe.commons.dom.findUpVirtual(a,d(b[e])))return null;h.push(b[e])}return h}var f=e(c);if(!f)return!0;var g=function(a){for(var b=[],c=null;a;){if(a.getAttribute(\"id\")){var d=axe.commons.utils.escapeSelector(a.getAttribute(\"id\"));c=axe.commons.dom.getRootNode(a).querySelector(\"[aria-owns~=\"+d+\"]\"),c&&b.push(c)}a=a.parentElement}return b.length?b:null}(a);if(g)for(var h=0,i=g.length;h<i;h++)if(!(f=e(axe.utils.getNodeFromTree(axe._tree[0],g[h]),f,!0)))return!0;return this.data(f),!1}},{id:\"aria-valid-attr-value\",evaluate:function(a,b,c){b=Array.isArray(b)?b:[];for(var d,e,f=[],g=/^aria-/,h=a.attributes,i=[\"aria-errormessage\"],j=0,k=h.length;j<k;j++)d=h[j],e=d.name,i.includes(e)||-1===b.indexOf(e)&&g.test(e)&&!axe.commons.aria.validateAttrValue(a,e)&&f.push(e+'=\"'+d.nodeValue+'\"');return!f.length||(this.data(f),!1)},options:[]},{id:\"aria-valid-attr\",evaluate:function(a,b,c){b=Array.isArray(b)?b:[];for(var d,e=[],f=/^aria-/,g=a.attributes,h=0,i=g.length;h<i;h++)d=g[h].name,-1===b.indexOf(d)&&f.test(d)&&!axe.commons.aria.validateAttr(d)&&e.push(d);return!e.length||(this.data(e),!1)},options:[]},{id:\"valid-scrollable-semantics\",evaluate:function(a,b,c){function d(a){var b=a.tagName.toUpperCase();return f[b]||!1}function e(a){var b=a.getAttribute(\"role\");return!!b&&(g[b.toLowerCase()]||!1)}var f={ARTICLE:!0,ASIDE:!0,NAV:!0,SECTION:!0},g={banner:!1,complementary:!0,contentinfo:!0,form:!0,main:!0,navigation:!0,region:!0,search:!1};return function(a){return e(a)||d(a)}(a)},options:[]},{id:\"color-contrast\",evaluate:function(a,b,c){if(!axe.commons.dom.isVisible(a,!1))return!0;var d,e=!!(b||{}).noScroll,f=[],g=axe.commons.color.getBackgroundColor(a,f,e),h=axe.commons.color.getForegroundColor(a,e),i=window.getComputedStyle(a),j=parseFloat(i.getPropertyValue(\"font-size\")),k=i.getPropertyValue(\"font-weight\"),l=-1!==[\"bold\",\"bolder\",\"600\",\"700\",\"800\",\"900\"].indexOf(k),m=axe.commons.color.hasValidContrastRatio(g,h,j,l),n=Math.floor(100*m.contrastRatio)/100;null===g&&(d=axe.commons.color.incompleteData.get(\"bgColor\"));var o=!1;1===n&&(o=!0,d=axe.commons.color.incompleteData.set(\"bgColor\",\"equalRatio\"));var p={fgColor:h?h.toHexString():void 0,bgColor:g?g.toHexString():void 0,contrastRatio:m?n:void 0,fontSize:(72*j/96).toFixed(1)+\"pt\",fontWeight:l?\"bold\":\"normal\",missingData:d,expectedContrastRatio:m.expectedContrastRatio+\":1\"};return this.data(p),null===h||null===g||o?(d=null,axe.commons.color.incompleteData.clear(),void this.relatedNodes(f)):(m.isValid||this.relatedNodes(f),m.isValid)}},{id:\"link-in-text-block\",evaluate:function(a,b,c){function d(a,b){var c=a.getRelativeLuminance(),d=b.getRelativeLuminance();return(Math.max(c,d)+.05)/(Math.min(c,d)+.05)}function e(a){var b=window.getComputedStyle(a).getPropertyValue(\"display\");return-1!==i.indexOf(b)||\"table-\"===b.substr(0,6)}var f=axe.commons,g=f.color,h=f.dom,i=[\"block\",\"list-item\",\"table\",\"flex\",\"grid\",\"inline-block\"];if(e(a))return!1;for(var j=h.getComposedParent(a);1===j.nodeType&&!e(j);)j=h.getComposedParent(j);if(this.relatedNodes([j]),g.elementIsDistinct(a,j))return!0;var k,l;if(k=g.getForegroundColor(a),l=g.getForegroundColor(j),k&&l){var m=d(k,l);if(1===m)return!0;if(m>=3)return axe.commons.color.incompleteData.set(\"fgColor\",\"bgContrast\"),this.data({missingData:axe.commons.color.incompleteData.get(\"fgColor\")}),void axe.commons.color.incompleteData.clear();if(k=g.getBackgroundColor(a),l=g.getBackgroundColor(j),!k||!l||d(k,l)>=3){var n=void 0;return n=k&&l?\"bgContrast\":axe.commons.color.incompleteData.get(\"bgColor\"),axe.commons.color.incompleteData.set(\"fgColor\",n),this.data({missingData:axe.commons.color.incompleteData.get(\"fgColor\")}),void axe.commons.color.incompleteData.clear()}return!1}}},{id:\"fieldset\",evaluate:function(a,b,c){function d(a,b){return axe.commons.utils.toArray(a.querySelectorAll('select,textarea,button,input:not([name=\"'+b+'\"]):not([type=\"hidden\"])'))}function e(a,b){var c=a.firstElementChild;if(!c||\"LEGEND\"!==c.nodeName.toUpperCase())return i.relatedNodes([a]),h=\"no-legend\",!1;if(!axe.commons.text.accessibleText(c))return i.relatedNodes([c]),h=\"empty-legend\",!1;var e=d(a,b);return!e.length||(i.relatedNodes(e),h=\"mixed-inputs\",!1)}function f(a,b){var c=axe.commons.dom.idrefs(a,\"aria-labelledby\").some(function(a){return a&&axe.commons.text.accessibleText(a)}),e=a.getAttribute(\"aria-label\");if(!(c||e&&axe.commons.text.sanitize(e)))return i.relatedNodes(a),h=\"no-group-label\",!1;var f=d(a,b);return!f.length||(i.relatedNodes(f),h=\"group-mixed-inputs\",!1)}function g(a,b){return axe.commons.utils.toArray(a).filter(function(a){return a!==b})}var h,i=this,j={name:a.getAttribute(\"name\"),type:a.getAttribute(\"type\")},k=function(a){var b=axe.commons.utils.escapeSelector(a.actualNode.name),c=axe.commons.dom.getRootNode(a.actualNode),d=c.querySelectorAll('input[type=\"'+axe.commons.utils.escapeSelector(a.actualNode.type)+'\"][name=\"'+b+'\"]');if(d.length<2)return!0;var j=axe.commons.dom.findUpVirtual(a,\"fieldset\"),k=axe.commons.dom.findUpVirtual(a,'[role=\"group\"]'+(\"radio\"===a.actualNode.type?',[role=\"radiogroup\"]':\"\"));return k||j?j?e(j,b):f(k,b):(h=\"no-group\",i.relatedNodes(g(d,a.actualNode)),!1)}(c);return k||(j.failureCode=h),this.data(j),k},after:function(a,b){var c={};return a.filter(function(a){if(a.result)return!0;var b=a.data;if(b){if(c[b.type]=c[b.type]||{},!c[b.type][b.name])return c[b.type][b.name]=[b],!0;var d=c[b.type][b.name].some(function(a){return a.failureCode===b.failureCode});return d||c[b.type][b.name].push(b),!d}return!1})}},{id:\"group-labelledby\",evaluate:function(a,b,c){this.data({name:a.getAttribute(\"name\"),type:a.getAttribute(\"type\")});var d=axe.commons.dom.getRootNode(a),e=d.querySelectorAll('input[type=\"'+axe.commons.utils.escapeSelector(a.type)+'\"][name=\"'+axe.commons.utils.escapeSelector(a.name)+'\"]');return e.length<=1||0!==[].map.call(e,function(a){var b=a.getAttribute(\"aria-labelledby\");return b?b.split(/\\s+/):[]}).reduce(function(a,b){return a.filter(function(a){return b.includes(a)})}).filter(function(a){var b=d.getElementById(a);return b&&axe.commons.text.accessibleText(b,!0)}).length},after:function(a,b){var c={};return a.filter(function(a){var b=a.data;return!(!b||(c[b.type]=c[b.type]||{},c[b.type][b.name]))&&(c[b.type][b.name]=!0,!0)})}},{id:\"accesskeys\",evaluate:function(a,b,c){return axe.commons.dom.isVisible(a,!1)&&(this.data(a.getAttribute(\"accesskey\")),this.relatedNodes([a])),!0},after:function(a,b){var c={};return a.filter(function(a){if(!a.data)return!1;var b=a.data.toUpperCase();return c[b]?(c[b].relatedNodes.push(a.relatedNodes[0]),!1):(c[b]=a,a.relatedNodes=[],!0)}).map(function(a){return a.result=!!a.relatedNodes.length,a})}},{id:\"focusable-no-name\",evaluate:function(a,b,c){var d=a.getAttribute(\"tabindex\");return!!(axe.commons.dom.isFocusable(a)&&d>-1)&&!axe.commons.text.accessibleTextVirtual(c)}},{id:\"has-at-least-one-main\",evaluate:function(a,b,c){var d=axe.utils.querySelectorAll(c,\"main,[role=main]\");return this.data(!!d[0]),!!d[0]},after:function(a,b){for(var c=!1,d=0;d<a.length&&!c;d++)c=a[d].data;for(var d=0;d<a.length;d++)a[d].result=c;return a}},{id:\"has-no-more-than-one-main\",evaluate:function(a,b,c){return axe.utils.querySelectorAll(c,\"main,[role=main]\").length<=1}},{id:\"main-is-top-level\",evaluate:function(a,b,c){for(var d=axe.commons.aria.getRolesByType(\"landmark\"),e=axe.commons.dom.getComposedParent(a);e;){var f=e.getAttribute(\"role\");if(f||\"form\"===e.tagName.toLowerCase()||(f=axe.commons.aria.implicitRole(e)),f&&d.includes(f))return!1;e=axe.commons.dom.getComposedParent(e)}return!0}},{id:\"tabindex\",evaluate:function(a,b,c){return a.tabIndex<=0}},{id:\"duplicate-img-label\",evaluate:function(a,b,c){var d=axe.commons.text.visibleVirtual(c,!0).toLowerCase();return\"\"!==d&&axe.utils.querySelectorAll(c,\"img\").filter(function(a){var b=a.actualNode;return axe.commons.dom.isVisible(b)&&![\"none\",\"presentation\"].includes(b.getAttribute(\"role\"))}).some(function(a){return d===axe.commons.text.accessibleTextVirtual(a).toLowerCase()})}},{id:\"explicit-label\",evaluate:function(a,b,c){if(a.getAttribute(\"id\")){var d=axe.commons.dom.getRootNode(a),e=axe.commons.utils.escapeSelector(a.getAttribute(\"id\")),f=d.querySelector('label[for=\"'+e+'\"]');if(f)return!!axe.commons.text.accessibleText(f)}return!1}},{id:\"help-same-as-label\",evaluate:function(a,b,c){var d=axe.commons.text.labelVirtual(c),e=a.getAttribute(\"title\");if(!d)return!1;if(!e&&(e=\"\",a.getAttribute(\"aria-describedby\"))){e=axe.commons.dom.idrefs(a,\"aria-describedby\").map(function(a){return a?axe.commons.text.accessibleText(a):\"\"}).join(\"\")}return axe.commons.text.sanitize(e)===axe.commons.text.sanitize(d)},enabled:!1},{id:\"implicit-label\",evaluate:function(a,b,c){var d=axe.commons.dom.findUpVirtual(c,\"label\");return!!d&&!!axe.commons.text.accessibleTextVirtual(d)}},{id:\"multiple-label\",evaluate:function(a,b,c){var d=axe.commons.utils.escapeSelector(a.getAttribute(\"id\")),e=Array.from(document.querySelectorAll('label[for=\"'+d+'\"]')),f=a.parentNode;for(e.length&&(e=e.filter(function(a,b){if(0===b&&!axe.commons.dom.isVisible(a,!0)||axe.commons.dom.isVisible(a,!0))return a}));f;)\"LABEL\"===f.tagName&&-1===e.indexOf(f)&&e.push(f),f=f.parentNode;return this.relatedNodes(e),e.length>1}},{id:\"title-only\",evaluate:function(a,b,c){return!(axe.commons.text.labelVirtual(c)||!a.getAttribute(\"title\")&&!a.getAttribute(\"aria-describedby\"))}},{id:\"has-lang\",evaluate:function(a,b,c){return!!(a.getAttribute(\"lang\")||a.getAttribute(\"xml:lang\")||\"\").trim()}},{id:\"valid-lang\",evaluate:function(a,b,c){function d(a){return a.trim().split(\"-\")[0].toLowerCase()}var e,f;return e=(b||axe.commons.utils.validLangs()).map(d),f=[\"lang\",\"xml:lang\"].reduce(function(b,c){var f=a.getAttribute(c);if(\"string\"!=typeof f)return b;var g=d(f);return\"\"!==g&&-1===e.indexOf(g)&&b.push(c+'=\"'+a.getAttribute(c)+'\"'),b},[]),!!f.length&&(this.data(f),!0)}},{id:\"dlitem\",evaluate:function(a,b,c){return\"DL\"===axe.commons.dom.getComposedParent(a).nodeName.toUpperCase()}},{id:\"has-listitem\",evaluate:function(a,b,c){return c.children.every(function(a){return\"LI\"!==a.actualNode.nodeName.toUpperCase()})}},{id:\"listitem\",evaluate:function(a,b,c){var d=axe.commons.dom.getComposedParent(a);return[\"UL\",\"OL\"].includes(d.nodeName.toUpperCase())||\"list\"===(d.getAttribute(\"role\")||\"\").toLowerCase()}},{id:\"only-dlitems\",evaluate:function(a,b,c){var d=[],e=[\"STYLE\",\"META\",\"LINK\",\"MAP\",\"AREA\",\"SCRIPT\",\"DATALIST\",\"TEMPLATE\"],f=!1;return c.children.forEach(function(a){var b=a.actualNode,c=b.nodeName.toUpperCase();1===b.nodeType&&\"DT\"!==c&&\"DD\"!==c&&-1===e.indexOf(c)?d.push(b):3===b.nodeType&&\"\"!==b.nodeValue.trim()&&(f=!0)}),d.length&&this.relatedNodes(d),!!d.length||f}},{id:\"only-listitems\",evaluate:function(a,b,c){var d=[],e=[\"STYLE\",\"META\",\"LINK\",\"MAP\",\"AREA\",\"SCRIPT\",\"DATALIST\",\"TEMPLATE\"],f=!1;return c.children.forEach(function(a){var b=a.actualNode,c=b.nodeName.toUpperCase();1===b.nodeType&&\"LI\"!==c&&-1===e.indexOf(c)?d.push(b):3===b.nodeType&&\"\"!==b.nodeValue.trim()&&(f=!0)}),d.length&&this.relatedNodes(d),!!d.length||f}},{id:\"structured-dlitems\",\nevaluate:function(a,b,c){var d=c.children;if(!d||!d.length)return!1;for(var e,f=!1,g=!1,h=0;h<d.length;h++){if(e=d[h].actualNode.nodeName.toUpperCase(),\"DT\"===e&&(f=!0),f&&\"DD\"===e)return!1;\"DD\"===e&&(g=!0)}return f||g}},{id:\"caption\",evaluate:function(a,b,c){var d=axe.utils.querySelectorAll(c,\"track\");if(d.length)return!d.some(function(a){return\"captions\"===(a.actualNode.getAttribute(\"kind\")||\"\").toLowerCase()})}},{id:\"description\",evaluate:function(a,b,c){var d=axe.utils.querySelectorAll(c,\"track\");if(d.length){return!d.some(function(a){return\"descriptions\"===(a.actualNode.getAttribute(\"kind\")||\"\").toLowerCase()})}}},{id:\"meta-viewport-large\",evaluate:function(a,b,c){b=b||{};for(var d,e=a.getAttribute(\"content\")||\"\",f=e.split(/[;,]/),g={},h=b.scaleMinimum||2,i=b.lowerBound||!1,j=0,k=f.length;j<k;j++){d=f[j].split(\"=\");var l=d.shift().toLowerCase();l&&d.length&&(g[l.trim()]=d.shift().trim().toLowerCase())}return!!(i&&g[\"maximum-scale\"]&&parseFloat(g[\"maximum-scale\"])<i)||!(!i&&\"no\"===g[\"user-scalable\"])&&!(g[\"maximum-scale\"]&&parseFloat(g[\"maximum-scale\"])<h)},options:{scaleMinimum:5,lowerBound:2}},{id:\"meta-viewport\",evaluate:function(a,b,c){b=b||{};for(var d,e=a.getAttribute(\"content\")||\"\",f=e.split(/[;,]/),g={},h=b.scaleMinimum||2,i=b.lowerBound||!1,j=0,k=f.length;j<k;j++){d=f[j].split(\"=\");var l=d.shift().toLowerCase();l&&d.length&&(g[l.trim()]=d.shift().trim().toLowerCase())}return!!(i&&g[\"maximum-scale\"]&&parseFloat(g[\"maximum-scale\"])<i)||!(!i&&\"no\"===g[\"user-scalable\"])&&!(g[\"maximum-scale\"]&&parseFloat(g[\"maximum-scale\"])<h)},options:{scaleMinimum:2}},{id:\"header-present\",evaluate:function(a,b,c){return!!axe.utils.querySelectorAll(c,'h1, h2, h3, h4, h5, h6, [role=\"heading\"]')[0]}},{id:\"heading-order\",evaluate:function(a,b,c){var d=a.getAttribute(\"aria-level\");if(null!==d)return this.data(parseInt(d,10)),!0;var e=a.tagName.match(/H(\\d)/);return!e||(this.data(parseInt(e[1],10)),!0)},after:function(a,b){if(a.length<2)return a;for(var c=a[0].data,d=1;d<a.length;d++)a[d].result&&a[d].data>c+1&&(a[d].result=!1),c=a[d].data;return a}},{id:\"internal-link-present\",evaluate:function(a,b,c){return axe.utils.querySelectorAll(c,\"a[href]\").some(function(a){return\"#\"===a.actualNode.getAttribute(\"href\")[0]})}},{id:\"landmark\",evaluate:function(a,b,c){return axe.utils.querySelectorAll(c,'main, [role=\"main\"]').length>0}},{id:\"meta-refresh\",evaluate:function(a,b,c){var d=a.getAttribute(\"content\")||\"\",e=d.split(/[;,]/);return\"\"===d||\"0\"===e[0]}},{id:\"p-as-heading\",evaluate:function(a,b,c){function d(a){for(var b=a,c=a.textContent.trim(),d=c;d===c&&void 0!==b;){var e=-1;if(a=b,0===a.children.length)return a;do{e++,d=a.children[e].textContent.trim()}while(\"\"===d&&e+1<a.children.length);b=a.children[e]}return a}function e(a){switch(a){case\"lighter\":return 100;case\"normal\":return 400;case\"bold\":return 700;case\"bolder\":return 900}return a=parseInt(a),isNaN(a)?400:a}function f(a){var b=window.getComputedStyle(d(a));return{fontWeight:e(b.getPropertyValue(\"font-weight\")),fontSize:parseInt(b.getPropertyValue(\"font-size\")),isItalic:\"italic\"===b.getPropertyValue(\"font-style\")}}function g(a,b,c){return c.reduce(function(c,d){return c||(!d.size||a.fontSize/d.size>b.fontSize)&&(!d.weight||a.fontWeight-d.weight>b.fontWeight)&&(!d.italic||a.isItalic&&!b.isItalic)},!1)}var h=Array.from(a.parentNode.children),i=h.indexOf(a);b=b||{};var j=b.margins||[],k=h.slice(i+1).find(function(a){return\"P\"===a.nodeName.toUpperCase()}),l=h.slice(0,i).reverse().find(function(a){return\"P\"===a.nodeName.toUpperCase()}),m=f(a),n=k?f(k):null,o=l?f(l):null;if(!n||!g(m,n,j))return!0;var p=axe.commons.dom.findUpVirtual(c,\"blockquote\");return!!(p&&\"BLOCKQUOTE\"===p.nodeName.toUpperCase()||o&&!g(m,o,j))&&void 0},options:{margins:[{weight:150,italic:!0},{weight:150,size:1.15},{italic:!0,size:1.15},{size:1.4}]}},{id:\"region\",evaluate:function(a,b,c){function d(a){return j&&j===a}function e(a){return a.hasAttribute(\"role\")?k.includes(a.getAttribute(\"role\").toLowerCase()):l.some(function(b){return axe.utils.matchesSelector(a,b)})}function f(a){var b=a.actualNode;return e(b)||d(b)||!h.isVisible(b,!0)?[]:h.hasContent(b,!0)?[b]:a.children.filter(function(a){return 1===a.actualNode.nodeType}).map(f).reduce(function(a,b){return a.concat(b)},[])}var g=axe.commons,h=g.dom,i=g.aria,j=function(a){var b=axe.utils.querySelectorAll(a,\"a[href]\")[0];if(b&&axe.commons.dom.getElementByReference(b.actualNode,\"href\"))return b.actualNode}(c),k=i.getRolesByType(\"landmark\"),l=k.reduce(function(a,b){return a.concat(i.implicitNodes(b))},[]).filter(function(a){return null!==a}),m=f(c);return this.relatedNodes(m),0===m.length},after:function(a,b){return[a[0]]}},{id:\"skip-link\",evaluate:function(a,b,c){var d=axe.commons.dom.getElementByReference(a,\"href\");return!!d&&(axe.commons.dom.isVisible(d,!0)||void 0)}},{id:\"unique-frame-title\",evaluate:function(a,b,c){var d=axe.commons.text.sanitize(a.title).trim().toLowerCase();return this.data(d),!0},after:function(a,b){var c={};return a.forEach(function(a){c[a.data]=void 0!==c[a.data]?++c[a.data]:0}),a.forEach(function(a){a.result=!!c[a.data]}),a}},{id:\"aria-label\",evaluate:function(a,b,c){var d=a.getAttribute(\"aria-label\");return!!(d?axe.commons.text.sanitize(d).trim():\"\")}},{id:\"aria-labelledby\",evaluate:function(a,b,c){return(0,axe.commons.dom.idrefs)(a,\"aria-labelledby\").some(function(a){return a&&axe.commons.text.accessibleText(a,!0)})}},{id:\"button-has-visible-text\",evaluate:function(a,b,c){var d=a.nodeName.toUpperCase(),e=a.getAttribute(\"role\"),f=void 0;return(\"BUTTON\"===d||\"button\"===e&&\"INPUT\"!==d)&&(f=axe.commons.text.accessibleTextVirtual(c),this.data(f),!!f)}},{id:\"doc-has-title\",evaluate:function(a,b,c){var d=document.title;return!!(d?axe.commons.text.sanitize(d).trim():\"\")}},{id:\"duplicate-id\",evaluate:function(a,b,c){var d=a.getAttribute(\"id\").trim();if(!d)return!0;var e=axe.commons.dom.getRootNode(a),f=Array.from(e.querySelectorAll('[id=\"'+axe.commons.utils.escapeSelector(d)+'\"]')).filter(function(b){return b!==a});return f.length&&this.relatedNodes(f),this.data(d),0===f.length},after:function(a,b){var c=[];return a.filter(function(a){return-1===c.indexOf(a.data)&&(c.push(a.data),!0)})}},{id:\"exists\",evaluate:function(a,b,c){return!0}},{id:\"has-alt\",evaluate:function(a,b,c){var d=a.nodeName.toLowerCase();return a.hasAttribute(\"alt\")&&(\"img\"===d||\"input\"===d||\"area\"===d)}},{id:\"has-visible-text\",evaluate:function(a,b,c){return axe.commons.text.accessibleTextVirtual(c).length>0}},{id:\"is-on-screen\",evaluate:function(a,b,c){return axe.commons.dom.isVisible(a,!1)&&!axe.commons.dom.isOffscreen(a)}},{id:\"non-empty-alt\",evaluate:function(a,b,c){var d=a.getAttribute(\"alt\");return!!(d?axe.commons.text.sanitize(d).trim():\"\")}},{id:\"non-empty-if-present\",evaluate:function(a,b,c){var d=a.nodeName.toUpperCase(),e=(a.getAttribute(\"type\")||\"\").toLowerCase(),f=a.getAttribute(\"value\");return this.data(f),!(\"INPUT\"!==d||![\"submit\",\"reset\"].includes(e))&&null===f}},{id:\"non-empty-title\",evaluate:function(a,b,c){var d=a.getAttribute(\"title\");return!!(d?axe.commons.text.sanitize(d).trim():\"\")}},{id:\"non-empty-value\",evaluate:function(a,b,c){var d=a.getAttribute(\"value\");return!!(d?axe.commons.text.sanitize(d).trim():\"\")}},{id:\"role-none\",evaluate:function(a,b,c){return\"none\"===a.getAttribute(\"role\")}},{id:\"role-presentation\",evaluate:function(a,b,c){return\"presentation\"===a.getAttribute(\"role\")}},{id:\"caption-faked\",evaluate:function(a,b,c){var d=axe.commons.table.toGrid(a),e=d[0];return d.length<=1||e.length<=1||a.rows.length<=1||e.reduce(function(a,b,c){return a||b!==e[c+1]&&void 0!==e[c+1]},!1)}},{id:\"has-caption\",evaluate:function(a,b,c){return!!a.caption}},{id:\"has-summary\",evaluate:function(a,b,c){return!!a.summary}},{id:\"has-th\",evaluate:function(a,b,c){for(var d,e,f=[],g=0,h=a.rows.length;g<h;g++){d=a.rows[g];for(var i=0,j=d.cells.length;i<j;i++)e=d.cells[i],\"TH\"!==e.nodeName.toUpperCase()&&-1===[\"rowheader\",\"columnheader\"].indexOf(e.getAttribute(\"role\"))||f.push(e)}return!!f.length&&(this.relatedNodes(f),!0)}},{id:\"html5-scope\",evaluate:function(a,b,c){return!axe.commons.dom.isHTML5(document)||\"TH\"===a.nodeName.toUpperCase()}},{id:\"same-caption-summary\",evaluate:function(a,b,c){return!(!a.summary||!a.caption)&&a.summary===axe.commons.text.accessibleText(a.caption)}},{id:\"scope-value\",evaluate:function(a,b,c){b=b||{};var d=a.getAttribute(\"scope\").toLowerCase();return-1!==([\"row\",\"col\",\"rowgroup\",\"colgroup\"]||b.values).indexOf(d)}},{id:\"td-has-header\",evaluate:function(a,b,c){var d=axe.commons.table,e=[];return d.getAllCells(a).forEach(function(a){if(axe.commons.dom.hasContent(a)&&d.isDataCell(a)&&!axe.commons.aria.label(a)){var b=d.getHeaders(a);(b=b.reduce(function(a,b){return a||null!==b&&!!axe.commons.dom.hasContent(b)},!1))||e.push(a)}}),!e.length||(this.relatedNodes(e),!1)}},{id:\"td-headers-attr\",evaluate:function(a,b,c){for(var d=[],e=0,f=a.rows.length;e<f;e++)for(var g=a.rows[e],h=0,i=g.cells.length;h<i;h++)d.push(g.cells[h]);var j=d.reduce(function(a,b){return b.getAttribute(\"id\")&&a.push(b.getAttribute(\"id\")),a},[]),k=d.reduce(function(a,b){var c,d,e=(b.getAttribute(\"headers\")||\"\").split(/\\s/).reduce(function(a,b){return b=b.trim(),b&&a.push(b),a},[]);return 0!==e.length&&(b.getAttribute(\"id\")&&(c=-1!==e.indexOf(b.getAttribute(\"id\").trim())),d=e.reduce(function(a,b){return a||-1===j.indexOf(b)},!1),(c||d)&&a.push(b)),a},[]);return!(k.length>0)||(this.relatedNodes(k),!1)}},{id:\"th-has-data-cells\",evaluate:function(a,b,c){var d=axe.commons.table,e=d.getAllCells(a),f=this,g=[];e.forEach(function(a){var b=a.getAttribute(\"headers\");b&&(g=g.concat(b.split(/\\s+/)));var c=a.getAttribute(\"aria-labelledby\");c&&(g=g.concat(c.split(/\\s+/)))});var h=e.filter(function(a){return\"\"!==axe.commons.text.sanitize(a.textContent)&&(\"TH\"===a.nodeName.toUpperCase()||-1!==[\"rowheader\",\"columnheader\"].indexOf(a.getAttribute(\"role\")))}),i=d.toGrid(a);return!!h.reduce(function(a,b){if(b.getAttribute(\"id\")&&g.includes(b.getAttribute(\"id\")))return!!a||a;var c=!1,e=d.getCellPosition(b,i);return d.isColumnHeader(b)&&(c=d.traverse(\"down\",e,i).reduce(function(a,b){return a||axe.commons.dom.hasContent(b)&&!d.isColumnHeader(b)},!1)),!c&&d.isRowHeader(b)&&(c=d.traverse(\"right\",e,i).reduce(function(a,b){return a||axe.commons.dom.hasContent(b)&&!d.isRowHeader(b)},!1)),c||f.relatedNodes(b),a&&c},!0)||void 0}},{id:\"hidden-content\",evaluate:function(a,b,c){if(![\"SCRIPT\",\"HEAD\",\"TITLE\",\"NOSCRIPT\",\"STYLE\",\"TEMPLATE\"].includes(a.tagName.toUpperCase())&&axe.commons.dom.hasContentVirtual(c)){var d=window.getComputedStyle(a);if(\"none\"===d.getPropertyValue(\"display\"))return;if(\"hidden\"===d.getPropertyValue(\"visibility\")){var e=axe.commons.dom.getComposedParent(a),f=e&&window.getComputedStyle(e);if(!f||\"hidden\"!==f.getPropertyValue(\"visibility\"))return}}return!0}}],commons:function(){function a(a){return a.getPropertyValue(\"font-family\").split(/[,;]/g).map(function(a){return a.trim().toLowerCase()})}function b(b,c){var d=window.getComputedStyle(b);if(\"none\"!==d.getPropertyValue(\"background-image\"))return!0;if([\"border-bottom\",\"border-top\",\"outline\"].reduce(function(a,b){var c=new C.Color;return c.parseRgbString(d.getPropertyValue(b+\"-color\")),a||\"none\"!==d.getPropertyValue(b+\"-style\")&&parseFloat(d.getPropertyValue(b+\"-width\"))>0&&0!==c.alpha},!1))return!0;var e=window.getComputedStyle(c);if(a(d)[0]!==a(e)[0])return!0;var f=[\"text-decoration-line\",\"text-decoration-style\",\"font-weight\",\"font-style\",\"font-size\"].reduce(function(a,b){return a||d.getPropertyValue(b)!==e.getPropertyValue(b)},!1),g=d.getPropertyValue(\"text-decoration\");return g.split(\" \").length<3&&(f=f||g!==e.getPropertyValue(\"text-decoration\")),f}function c(a,b){var c=a.nodeName.toUpperCase();if(G.includes(c))return axe.commons.color.incompleteData.set(\"bgColor\",\"imgNode\"),!0;b=b||window.getComputedStyle(a);var d=b.getPropertyValue(\"background-image\"),e=\"none\"!==d;if(e){var f=/gradient/.test(d);axe.commons.color.incompleteData.set(\"bgColor\",f?\"bgGradient\":\"bgImage\")}return e}function d(a,b){b=b||window.getComputedStyle(a);var c=new C.Color;if(c.parseRgbString(b.getPropertyValue(\"background-color\")),0!==c.alpha){var d=b.getPropertyValue(\"opacity\");c.alpha=c.alpha*d}return c}function e(a,b){var c=a.getClientRects()[0],d=D.shadowElementsFromPoint(c.left,c.top);if(d)for(var e=0;e<d.length;e++)if(d[e]!==a&&d[e]===b)return!0;return!1}function f(a,b,c){var f=0;if(a>0)for(var g=a-1;g>=0;g--){var h=b[g],i=window.getComputedStyle(h),j=d(h,i);j.alpha&&e(c,h)?f+=j.alpha:b.splice(g,1)}return f}function g(a,b,c){var d=a!==b&&!D.visuallyContains(a,b)&&0!==c.alpha;return d&&axe.commons.color.incompleteData.set(\"bgColor\",\"elmPartiallyObscured\"),d}function h(a,b){var c={TD:[\"TR\",\"TBODY\"],TH:[\"TR\",\"THEAD\"],INPUT:[\"LABEL\"]},d=a.map(function(a){return a.tagName}),e=a;for(var f in c)if(d.includes(f))for(var g in c[f])if(f.hasOwnProperty(g)){var h=axe.commons.dom.findUp(b,c[f][g]);if(h&&-1===a.indexOf(h)){var i=axe.commons.dom.visuallyOverlaps(b.getBoundingClientRect(),h);i&&e.splice(d.indexOf(f)+1,0,h)}b.tagName===c[f][g]&&-1===d.indexOf(b.tagName)&&e.splice(d.indexOf(f)+1,0,b)}return e}function i(a){var b=a.indexOf(document.body),e=a;return b>1&&!c(document.documentElement)&&0===d(document.documentElement).alpha&&(e.splice(b,1),e.splice(a.indexOf(document.documentElement),1),e.push(document.body)),e}function j(a){if(!H.includes(a.actualNode.nodeName.toUpperCase()))return a.children.some(function(a){var b=a.actualNode;return 3===b.nodeType&&b.nodeValue.trim()})}function k(a){return a.disabled||!D.isVisible(a,!0)&&\"AREA\"!==a.nodeName.toUpperCase()}function l(a,b){!1!==b(a.actualNode)&&a.children.forEach(function(a){return l(a,b)})}function m(a){var b=window.getComputedStyle(a).getPropertyValue(\"display\");return I.includes(b)||\"table-\"===b.substr(0,6)}function n(a){for(var b=D.getComposedParent(a);b&&!m(b);)b=D.getComposedParent(b);return axe.utils.getNodeFromTree(axe._tree[0],b)}function o(a,b){for(a=D.getComposedParent(a);a&&\"html\"!==a.nodeName.toLowerCase();){if(a.scrollTop&&(b+=a.scrollTop)>=0)return!1;a=D.getComposedParent(a)}return!0}function p(a){\"use strict\";var b=a.match(/rect\\s*\\(([0-9]+)px,?\\s*([0-9]+)px,?\\s*([0-9]+)px,?\\s*([0-9]+)px\\s*\\)/);return!(!b||5!==b.length)&&(b[3]-b[1]<=0&&b[2]-b[4]<=0)}function q(a){var b=void 0;return b=a.actualNode.id?D.findElmsInContext({elm:\"label\",attr:\"for\",value:a.actualNode.id,context:a.actualNode})[0]:D.findUpVirtual(a,\"label\"),axe.utils.getNodeFromTree(axe._tree[0],b)}function r(a){return[\"button\",\"reset\",\"submit\"].includes(a.actualNode.type.toLowerCase())}function s(a){var b=a.actualNode,c=b.nodeName.toUpperCase();return\"TEXTAREA\"===c||\"SELECT\"===c||\"INPUT\"===c&&\"hidden\"!==b.type.toLowerCase()}function t(a){return[\"BUTTON\",\"SUMMARY\",\"A\"].includes(a.actualNode.nodeName.toUpperCase())}function u(a){return[\"TABLE\",\"FIGURE\"].includes(a.actualNode.nodeName.toUpperCase())}function v(a){var b=a.actualNode,c=b.nodeName.toUpperCase();if(\"INPUT\"===c)return!b.hasAttribute(\"type\")||L.includes(b.type.toLowerCase())?b.value:\"\";if(\"SELECT\"===c){var d=b.options;if(d&&d.length){for(var e=\"\",f=0;f<d.length;f++)d[f].selected&&(e+=\" \"+d[f].text);return F.sanitize(e)}return\"\"}return\"TEXTAREA\"===c&&b.value?b.value:\"\"}function w(a,b){var c=a.actualNode,d=c.querySelector(b.toLowerCase());return d?F.accessibleText(d):\"\"}function x(a){if(!a)return!1;var b=a.actualNode;switch(b.nodeName.toUpperCase()){case\"SELECT\":case\"TEXTAREA\":return!0;case\"INPUT\":return!b.hasAttribute(\"type\")||L.includes(b.getAttribute(\"type\").toLowerCase());default:return!1}}function y(a){var b=a.actualNode,c=b.nodeName.toUpperCase();return[\"IMG\",\"APPLET\",\"AREA\"].includes(c)||\"INPUT\"===c&&\"image\"===b.type.toLowerCase()}function z(a){return!!F.sanitize(a)}var commons={},A=commons.aria={},B=A.lookupTable={};B.attributes={\"aria-activedescendant\":{type:\"idref\"},\"aria-atomic\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-autocomplete\":{type:\"nmtoken\",values:[\"inline\",\"list\",\"both\",\"none\"]},\"aria-busy\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-checked\":{type:\"nmtoken\",values:[\"true\",\"false\",\"mixed\",\"undefined\"]},\"aria-colcount\":{type:\"int\"},\"aria-colindex\":{type:\"int\"},\"aria-colspan\":{type:\"int\"},\"aria-controls\":{type:\"idrefs\"},\"aria-current\":{type:\"nmtoken\",values:[\"page\",\"step\",\"location\",\"date\",\"time\",\"true\",\"false\"]},\"aria-describedby\":{type:\"idrefs\"},\"aria-disabled\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-dropeffect\":{type:\"nmtokens\",values:[\"copy\",\"move\",\"reference\",\"execute\",\"popup\",\"none\"]},\"aria-errormessage\":{type:\"idref\"},\"aria-expanded\":{type:\"nmtoken\",values:[\"true\",\"false\",\"undefined\"]},\"aria-flowto\":{type:\"idrefs\"},\"aria-grabbed\":{type:\"nmtoken\",values:[\"true\",\"false\",\"undefined\"]},\"aria-haspopup\":{type:\"nmtoken\",values:[\"true\",\"false\",\"menu\",\"listbox\",\"tree\",\"grid\",\"dialog\"]},\"aria-hidden\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-invalid\":{type:\"nmtoken\",values:[\"true\",\"false\",\"spelling\",\"grammar\"]},\"aria-keyshortcuts\":{type:\"string\"},\"aria-label\":{type:\"string\"},\"aria-labelledby\":{type:\"idrefs\"},\"aria-level\":{type:\"int\"},\"aria-live\":{type:\"nmtoken\",values:[\"off\",\"polite\",\"assertive\"]},\"aria-modal\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-multiline\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-multiselectable\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-orientation\":{type:\"nmtoken\",values:[\"horizontal\",\"vertical\"]},\"aria-owns\":{type:\"idrefs\"},\"aria-placeholder\":{type:\"string\"},\"aria-posinset\":{type:\"int\"},\"aria-pressed\":{type:\"nmtoken\",values:[\"true\",\"false\",\"mixed\",\"undefined\"]},\"aria-readonly\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-relevant\":{type:\"nmtokens\",values:[\"additions\",\"removals\",\"text\",\"all\"]},\"aria-required\":{type:\"boolean\",values:[\"true\",\"false\"]},\"aria-rowcount\":{type:\"int\"},\"aria-rowindex\":{type:\"int\"},\"aria-rowspan\":{type:\"int\"},\"aria-selected\":{type:\"nmtoken\",values:[\"true\",\"false\",\"undefined\"]},\"aria-setsize\":{type:\"int\"},\"aria-sort\":{type:\"nmtoken\",values:[\"ascending\",\"descending\",\"other\",\"none\"]},\"aria-valuemax\":{type:\"decimal\"},\"aria-valuemin\":{type:\"decimal\"},\"aria-valuenow\":{type:\"decimal\"},\"aria-valuetext\":{type:\"string\"}},B.globalAttributes=[\"aria-atomic\",\"aria-busy\",\"aria-controls\",\"aria-current\",\"aria-describedby\",\"aria-disabled\",\"aria-dropeffect\",\"aria-flowto\",\"aria-grabbed\",\"aria-haspopup\",\"aria-hidden\",\"aria-invalid\",\"aria-keyshortcuts\",\"aria-label\",\"aria-labelledby\",\"aria-live\",\"aria-owns\",\"aria-relevant\"],B.role={alert:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},alertdialog:{type:\"widget\",attributes:{allowed:[\"aria-expanded\",\"aria-modal\"]},owned:null,nameFrom:[\"author\"],context:null},application:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},article:{type:\"structure\",attributes:{allowed:[\"aria-expanded\",\"aria-posinset\",\"aria-setsize\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"article\"]},banner:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"header\"]},button:{type:\"widget\",attributes:{allowed:[\"aria-expanded\",\"aria-pressed\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null,implicit:[\"button\",'input[type=\"button\"]','input[type=\"image\"]','input[type=\"reset\"]','input[type=\"submit\"]',\"summary\"]},cell:{type:\"structure\",attributes:{allowed:[\"aria-colindex\",\"aria-colspan\",\"aria-rowindex\",\"aria-rowspan\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"row\"],implicit:[\"td\",\"th\"]},checkbox:{type:\"widget\",attributes:{allowed:[\"aria-checked\",\"aria-required\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null,implicit:['input[type=\"checkbox\"]']},columnheader:{type:\"structure\",attributes:{allowed:[\"aria-colindex\",\"aria-colspan\",\"aria-expanded\",\"aria-rowindex\",\"aria-rowspan\",\"aria-required\",\"aria-readonly\",\"aria-selected\",\"aria-sort\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"row\"],implicit:[\"th\"]},combobox:{type:\"composite\",attributes:{allowed:[\"aria-expanded\",\"aria-autocomplete\",\"aria-required\",\"aria-activedescendant\",\"aria-orientation\"]},owned:{all:[\"listbox\",\"textbox\"]},nameFrom:[\"author\"],context:null},command:{nameFrom:[\"author\"],type:\"abstract\"},complementary:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"aside\"]},composite:{nameFrom:[\"author\"],type:\"abstract\"},contentinfo:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"footer\"]},definition:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"dd\",\"dfn\"]},dialog:{type:\"widget\",attributes:{allowed:[\"aria-expanded\",\"aria-modal\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"dialog\"]},directory:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null},document:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"body\"]},feed:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:{one:[\"article\"]},nameFrom:[\"author\"],context:null},form:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"form\"]},grid:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\",\"aria-colcount\",\"aria-level\",\"aria-multiselectable\",\"aria-readonly\",\"aria-rowcount\"]},owned:{one:[\"rowgroup\",\"row\"]},nameFrom:[\"author\"],context:null,implicit:[\"table\"]},gridcell:{type:\"widget\",attributes:{allowed:[\"aria-colindex\",\"aria-colspan\",\"aria-expanded\",\"aria-rowindex\",\"aria-rowspan\",\"aria-selected\",\"aria-readonly\",\"aria-required\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"row\"],implicit:[\"td\",\"th\"]},group:{type:\"structure\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"details\",\"optgroup\"]},heading:{type:\"structure\",attributes:{allowed:[\"aria-level\",\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null,implicit:[\"h1\",\"h2\",\"h3\",\"h4\",\"h5\",\"h6\"]},img:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"img\"]},input:{nameFrom:[\"author\"],type:\"abstract\"},landmark:{nameFrom:[\"author\"],type:\"abstract\"},link:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null,implicit:[\"a[href]\"]},list:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:{all:[\"listitem\"]},nameFrom:[\"author\"],context:null,implicit:[\"ol\",\"ul\",\"dl\"]},listbox:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-multiselectable\",\"aria-required\",\"aria-expanded\",\"aria-orientation\"]},owned:{all:[\"option\"]},nameFrom:[\"author\"],context:null,implicit:[\"select\"]},listitem:{type:\"structure\",attributes:{allowed:[\"aria-level\",\"aria-posinset\",\"aria-setsize\",\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"list\"],implicit:[\"li\",\"dt\"]},log:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},main:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"main\"]},marquee:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},math:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"math\"]},menu:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\",\"aria-orientation\"]},owned:{one:[\"menuitem\",\"menuitemradio\",\"menuitemcheckbox\"]},nameFrom:[\"author\"],context:null,implicit:['menu[type=\"context\"]']},menubar:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\",\"aria-orientation\"]},owned:null,nameFrom:[\"author\"],context:null},menuitem:{type:\"widget\",attributes:{allowed:[\"aria-posinset\",\"aria-setsize\",\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"menu\",\"menubar\"],implicit:['menuitem[type=\"command\"]']},menuitemcheckbox:{type:\"widget\",attributes:{allowed:[\"aria-checked\",\"aria-posinset\",\"aria-setsize\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"menu\",\"menubar\"],implicit:['menuitem[type=\"checkbox\"]']},menuitemradio:{type:\"widget\",attributes:{allowed:[\"aria-checked\",\"aria-selected\",\"aria-posinset\",\"aria-setsize\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"menu\",\"menubar\"],implicit:['menuitem[type=\"radio\"]']},navigation:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"nav\"]},none:{type:\"structure\",attributes:null,owned:null,nameFrom:[\"author\"],context:null},note:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},option:{type:\"widget\",attributes:{allowed:[\"aria-selected\",\"aria-posinset\",\"aria-setsize\",\"aria-checked\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"listbox\"],implicit:[\"option\"]},presentation:{type:\"structure\",attributes:null,owned:null,nameFrom:[\"author\"],context:null},progressbar:{type:\"widget\",attributes:{allowed:[\"aria-valuetext\",\"aria-valuenow\",\"aria-valuemax\",\"aria-valuemin\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"progress\"]},radio:{type:\"widget\",attributes:{allowed:[\"aria-checked\",\"aria-selected\",\"aria-posinset\",\"aria-setsize\",\"aria-required\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null,implicit:['input[type=\"radio\"]']},radiogroup:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-required\",\"aria-expanded\"]},owned:{all:[\"radio\"]},nameFrom:[\"author\"],context:null},range:{nameFrom:[\"author\"],type:\"abstract\"},region:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"section[aria-label]\",\"section[aria-labelledby]\",\"section[title]\"]},roletype:{type:\"abstract\"},row:{type:\"structure\",attributes:{allowed:[\"aria-activedescendant\",\"aria-colindex\",\"aria-expanded\",\"aria-level\",\"aria-selected\",\"aria-rowindex\"]},owned:{one:[\"cell\",\"columnheader\",\"rowheader\",\"gridcell\"]},nameFrom:[\"author\",\"contents\"],context:[\"rowgroup\",\"grid\",\"treegrid\",\"table\"],implicit:[\"tr\"]},rowgroup:{type:\"structure\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\"]},owned:{all:[\"row\"]},nameFrom:[\"author\",\"contents\"],context:[\"grid\",\"table\"],implicit:[\"tbody\",\"thead\",\"tfoot\"]},rowheader:{type:\"structure\",attributes:{allowed:[\"aria-colindex\",\"aria-colspan\",\"aria-expanded\",\"aria-rowindex\",\"aria-rowspan\",\"aria-required\",\"aria-readonly\",\"aria-selected\",\"aria-sort\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"row\"],implicit:[\"th\"]},scrollbar:{type:\"widget\",attributes:{required:[\"aria-controls\",\"aria-valuenow\",\"aria-valuemax\",\"aria-valuemin\"],allowed:[\"aria-valuetext\",\"aria-orientation\"]},owned:null,nameFrom:[\"author\"],context:null},search:{type:\"landmark\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},searchbox:{type:\"widget\",attributes:{allowed:[\"aria-activedescendant\",\"aria-autocomplete\",\"aria-multiline\",\"aria-readonly\",\"aria-required\",\"aria-placeholder\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:['input[type=\"search\"]']},section:{nameFrom:[\"author\",\"contents\"],type:\"abstract\"},sectionhead:{nameFrom:[\"author\",\"contents\"],type:\"abstract\"},select:{nameFrom:[\"author\"],type:\"abstract\"},separator:{type:\"structure\",attributes:{allowed:[\"aria-expanded\",\"aria-orientation\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"hr\"]},slider:{type:\"widget\",attributes:{allowed:[\"aria-valuetext\",\"aria-orientation\"],required:[\"aria-valuenow\",\"aria-valuemax\",\"aria-valuemin\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:['input[type=\"range\"]']},spinbutton:{type:\"widget\",attributes:{allowed:[\"aria-valuetext\",\"aria-required\"],required:[\"aria-valuenow\",\"aria-valuemax\",\"aria-valuemin\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:['input[type=\"number\"]']},status:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:[\"output\"]},structure:{type:\"abstract\"},switch:{type:\"widget\",attributes:{required:[\"aria-checked\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null},tab:{type:\"widget\",attributes:{allowed:[\"aria-selected\",\"aria-expanded\",\"aria-setsize\",\"aria-posinset\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"tablist\"]},table:{type:\"structure\",attributes:{allowed:[\"aria-colcount\",\"aria-rowcount\"]},owned:{one:[\"rowgroup\",\"row\"]},nameFrom:[\"author\"],context:null,implicit:[\"table\"]},tablist:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\",\"aria-level\",\"aria-multiselectable\",\"aria-orientation\"]},owned:{all:[\"tab\"]},nameFrom:[\"author\"],context:null},tabpanel:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},term:{type:\"structure\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null,implicit:[\"dt\"]},text:{type:\"structure\",owned:null,nameFrom:[\"author\",\"contents\"],context:null},textbox:{type:\"widget\",attributes:{allowed:[\"aria-activedescendant\",\"aria-autocomplete\",\"aria-multiline\",\"aria-readonly\",\"aria-required\",\"aria-placeholder\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:['input[type=\"text\"]','input[type=\"email\"]','input[type=\"password\"]','input[type=\"tel\"]','input[type=\"url\"]',\"input:not([type])\",\"textarea\"]},timer:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null},toolbar:{type:\"structure\",attributes:{allowed:[\"aria-activedescendant\",\"aria-expanded\"]},owned:null,nameFrom:[\"author\"],context:null,implicit:['menu[type=\"toolbar\"]']},tooltip:{type:\"widget\",attributes:{allowed:[\"aria-expanded\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:null},tree:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-multiselectable\",\"aria-required\",\"aria-expanded\",\"aria-orientation\"]},owned:{all:[\"treeitem\"]},nameFrom:[\"author\"],context:null},treegrid:{type:\"composite\",attributes:{allowed:[\"aria-activedescendant\",\"aria-colcount\",\"aria-expanded\",\"aria-level\",\"aria-multiselectable\",\"aria-readonly\",\"aria-required\",\"aria-rowcount\",\"aria-orientation\"]},owned:{one:[\"rowgroup\",\"row\"]},nameFrom:[\"author\"],context:null},treeitem:{type:\"widget\",attributes:{allowed:[\"aria-checked\",\"aria-selected\",\"aria-expanded\",\"aria-level\",\"aria-posinset\",\"aria-setsize\"]},owned:null,nameFrom:[\"author\",\"contents\"],context:[\"group\",\"tree\"]},widget:{type:\"abstract\"},window:{nameFrom:[\"author\"],type:\"abstract\"}};var C={};commons.color=C;var D=commons.dom={},E=commons.table={},F=commons.text={EdgeFormDefaults:{}};commons.utils=axe.utils;A.requiredAttr=function(a){\"use strict\";var b=A.lookupTable.role[a];return b&&b.attributes&&b.attributes.required||[]},A.allowedAttr=function(a){\"use strict\";var b=A.lookupTable.role[a],c=b&&b.attributes&&b.attributes.allowed||[],d=b&&b.attributes&&b.attributes.required||[];return c.concat(A.lookupTable.globalAttributes).concat(d)},A.validateAttr=function(a){\"use strict\";return!!A.lookupTable.attributes[a]},A.validateAttrValue=function(a,b){\"use strict\";var c,d,e=a.getAttribute(b),f=A.lookupTable.attributes[b],g=D.getRootNode(a);if(!f)return!0;switch(f.type){case\"boolean\":case\"nmtoken\":return\"string\"==typeof e&&-1!==f.values.indexOf(e.toLowerCase());case\"nmtokens\":return d=axe.utils.tokenList(e),d.reduce(function(a,b){return a&&-1!==f.values.indexOf(b)},0!==d.length);case\"idref\":return!(!e||!g.getElementById(e));case\"idrefs\":return d=axe.utils.tokenList(e),d.reduce(function(a,b){return!(!a||!g.getElementById(b))},0!==d.length);case\"string\":return!0;case\"decimal\":return!(!(c=e.match(/^[-+]?([0-9]*)\\.?([0-9]*)$/))||!c[1]&&!c[2]);case\"int\":return/^[-+]?[0-9]+$/.test(e)}},A.labelVirtual=function(a){var b=a.actualNode,c=void 0,d=void 0;return b.getAttribute(\"aria-labelledby\")&&(c=D.idrefs(b,\"aria-labelledby\"),d=c.map(function(a){\nvar b=axe.utils.getNodeFromTree(axe._tree[0],a);return b?F.visibleVirtual(b,!0):\"\"}).join(\" \").trim())?d:(d=b.getAttribute(\"aria-label\"),d&&(d=F.sanitize(d).trim())?d:null)},A.label=function(a){return a=axe.utils.getNodeFromTree(axe._tree[0],a),A.labelVirtual(a)},A.isValidRole=function(a){\"use strict\";return!!A.lookupTable.role[a]},A.getRolesWithNameFromContents=function(){return Object.keys(A.lookupTable.role).filter(function(a){return A.lookupTable.role[a].nameFrom&&-1!==A.lookupTable.role[a].nameFrom.indexOf(\"contents\")})},A.getRolesByType=function(a){return Object.keys(A.lookupTable.role).filter(function(b){return A.lookupTable.role[b].type===a})},A.getRoleType=function(a){var b=A.lookupTable.role[a];return b&&b.type||null},A.requiredOwned=function(a){\"use strict\";var b=null,c=A.lookupTable.role[a];return c&&(b=axe.utils.clone(c.owned)),b},A.requiredContext=function(a){\"use strict\";var b=null,c=A.lookupTable.role[a];return c&&(b=axe.utils.clone(c.context)),b},A.implicitNodes=function(a){\"use strict\";var b=null,c=A.lookupTable.role[a];return c&&c.implicit&&(b=axe.utils.clone(c.implicit)),b},A.implicitRole=function(a){\"use strict\";var b=function(b,c){var d=function(b){return axe.utils.matchesSelector(a,b)};return c.implicit&&c.implicit.some(d)&&b.push(c.name),b},c=Object.keys(A.lookupTable.role).map(function(a){var b=A.lookupTable.role[a];return{name:a,implicit:b&&b.implicit}}),d=c.reduce(b,[]);if(!d.length)return null;for(var e=a.attributes,f=[],g=0,h=e.length;g<h;g++){var i=e[g];i.name.match(/^aria-/)&&f.push(i.name)}return function(a,b){var c=function(a){return A.allowedAttr(a).reduce(function(a,c){return a+(b.indexOf(c)>-1?1:0)},0)};return a.map(function(a){return{score:c(a),name:a}}).sort(function(a,b){return b.score-a.score}).map(function(a){return a.name})}(d,f).shift()},C.Color=function(a,b,c,d){this.red=a,this.green=b,this.blue=c,this.alpha=d,this.toHexString=function(){var a=Math.round(this.red).toString(16),b=Math.round(this.green).toString(16),c=Math.round(this.blue).toString(16);return\"#\"+(this.red>15.5?a:\"0\"+a)+(this.green>15.5?b:\"0\"+b)+(this.blue>15.5?c:\"0\"+c)};var e=/^rgb\\((\\d+), (\\d+), (\\d+)\\)$/,f=/^rgba\\((\\d+), (\\d+), (\\d+), (\\d*(\\.\\d+)?)\\)/;this.parseRgbString=function(a){if(\"transparent\"===a)return this.red=0,this.green=0,this.blue=0,void(this.alpha=0);var b=a.match(e);return b?(this.red=parseInt(b[1],10),this.green=parseInt(b[2],10),this.blue=parseInt(b[3],10),void(this.alpha=1)):(b=a.match(f),b?(this.red=parseInt(b[1],10),this.green=parseInt(b[2],10),this.blue=parseInt(b[3],10),void(this.alpha=parseFloat(b[4]))):void 0)},this.getRelativeLuminance=function(){var a=this.red/255,b=this.green/255,c=this.blue/255;return.2126*(a<=.03928?a/12.92:Math.pow((a+.055)/1.055,2.4))+.7152*(b<=.03928?b/12.92:Math.pow((b+.055)/1.055,2.4))+.0722*(c<=.03928?c/12.92:Math.pow((c+.055)/1.055,2.4))}},C.flattenColors=function(a,b){var c=a.alpha,d=(1-c)*b.red+c*a.red,e=(1-c)*b.green+c*a.green,f=(1-c)*b.blue+c*a.blue,g=a.alpha+b.alpha*(1-a.alpha);return new C.Color(d,e,f,g)},C.getContrast=function(a,b){if(!b||!a)return null;b.alpha<1&&(b=C.flattenColors(b,a));var c=a.getRelativeLuminance(),d=b.getRelativeLuminance();return(Math.max(d,c)+.05)/(Math.min(d,c)+.05)},C.hasValidContrastRatio=function(a,b,c,d){var e=C.getContrast(a,b),f=d&&Math.ceil(72*c)/96<14||!d&&Math.ceil(72*c)/96<18,g=f?4.5:3;return{isValid:e>g,contrastRatio:e,expectedContrastRatio:g}},C.elementIsDistinct=b;var G=[\"IMG\",\"CANVAS\",\"OBJECT\",\"IFRAME\",\"VIDEO\",\"SVG\"];C.getCoords=function(a){var b=void 0,c=void 0;if(!(a.left>window.innerWidth||a.top>window.innerHeight))return b=Math.min(Math.ceil(a.left+a.width/2),window.innerWidth-1),c=Math.min(Math.ceil(a.top+a.height/2),window.innerHeight-1),{x:b,y:c}},C.getRectStack=function(a){var b=C.getCoords(a.getBoundingClientRect());if(b){var c=D.shadowElementsFromPoint(b.x,b.y),d=Array.from(a.getClientRects());if(d&&d.length>1){var e=d.filter(function(a){return a.width&&a.width>0}).map(function(a){var b=C.getCoords(a);if(b)return D.shadowElementsFromPoint(b.x,b.y)});return e.splice(0,0,c),e}return[c]}return null},C.filteredRectStack=function(a){var b=C.getRectStack(a);if(b&&1===b.length)return b[0];if(b&&b.length>1){var c=b.shift(),d=void 0;return b.forEach(function(e,f){if(0!==f){var g=b[f-1],h=b[f];d=g.every(function(a,b){return a===h[b]})||c.includes(a)}}),d?b[0]:(axe.commons.color.incompleteData.set(\"bgColor\",\"elmPartiallyObscuring\"),null)}return axe.commons.color.incompleteData.set(\"bgColor\",\"outsideViewport\"),null},C.getBackgroundStack=function(a){var b=C.filteredRectStack(a);if(null===b)return null;b=h(b,a),b=D.reduceToElementsBelowFloating(b,a),b=i(b);var c=b.indexOf(a);return f(c,b,a)>=.99?(axe.commons.color.incompleteData.set(\"bgColor\",\"bgOverlap\"),null):-1!==c?b:null},C.getBackgroundColor=function(a){var b=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];if(!0!==(arguments.length>2&&void 0!==arguments[2]&&arguments[2])){var e=a.clientHeight-2>=2*window.innerHeight;a.scrollIntoView(e)}var f=[],h=C.getBackgroundStack(a);if((h||[]).some(function(e){var h=window.getComputedStyle(e),i=d(e,h);return g(a,e,i)||c(e,h)?(f=null,b.push(e),!0):0!==i.alpha&&(b.push(e),f.push(i),1===i.alpha)}),null!==f&&null!==h){f.push(new C.Color(255,255,255,1));return f.reduce(C.flattenColors)}return null},D.isOpaque=function(a){var b=window.getComputedStyle(a);return c(a,b)||1===d(a,b).alpha},C.getForegroundColor=function(a,b){var c=window.getComputedStyle(a),d=new C.Color;d.parseRgbString(c.getPropertyValue(\"color\"));var e=c.getPropertyValue(\"opacity\");if(d.alpha=d.alpha*e,1===d.alpha)return d;var f=C.getBackgroundColor(a,[],b);if(null===f){var g=axe.commons.color.incompleteData.get(\"bgColor\");return axe.commons.color.incompleteData.set(\"fgColor\",g),null}return C.flattenColors(d,f)},C.incompleteData=function(){var a={};return{set:function(b,c){if(\"string\"!=typeof b)throw new Error(\"Incomplete data: key must be a string\");return c&&(a[b]=c),a[b]},get:function(b){return a[b]},clear:function(){a={}}}}(),D.reduceToElementsBelowFloating=function(a,b){var c,d,e,f=[\"fixed\",\"sticky\"],g=[],h=!1;for(c=0;c<a.length;++c)d=a[c],d===b&&(h=!0),e=window.getComputedStyle(d),h||-1===f.indexOf(e.position)?g.push(d):g=[];return g},D.findElmsInContext=function(a){var b=a.context,c=a.value,d=a.attr,e=a.elm,f=void 0===e?\"\":e,g=void 0,h=axe.utils.escapeSelector(c);return g=9===b.nodeType||11===b.nodeType?b:D.getRootNode(b),Array.from(g.querySelectorAll(f+\"[\"+d+\"=\"+h+\"]\"))},D.findUp=function(a,b){return D.findUpVirtual(axe.utils.getNodeFromTree(axe._tree[0],a),b)},D.findUpVirtual=function(a,b){var c=void 0;if(c=a.actualNode,!a.shadowId&&\"function\"==typeof a.actualNode.closest){var d=a.actualNode.closest(b);return d||null}do{(c=c.assignedSlot?c.assignedSlot:c.parentNode)&&11===c.nodeType&&(c=c.host)}while(c&&!axe.utils.matchesSelector(c,b)&&c!==document.documentElement);return axe.utils.matchesSelector(c,b)?c:null},D.getComposedParent=function a(b){if(b.assignedSlot)return a(b.assignedSlot);if(b.parentNode){var c=b.parentNode;if(1===c.nodeType)return c;if(c.host)return c.host}return null},D.getElementByReference=function(a,b){var c=a.getAttribute(b);if(c&&\"#\"===c.charAt(0)){c=c.substring(1);var d=document.getElementById(c);if(d)return d;if(d=document.getElementsByName(c),d.length)return d[0]}return null},D.getElementCoordinates=function(a){\"use strict\";var b=D.getScrollOffset(document),c=b.left,d=b.top,e=a.getBoundingClientRect();return{top:e.top+d,right:e.right+c,bottom:e.bottom+d,left:e.left+c,width:e.right-e.left,height:e.bottom-e.top}},D.getRootNode=function(a){var b=a.getRootNode&&a.getRootNode()||document;return b===a&&(b=document),b},D.getScrollOffset=function(a){\"use strict\";if(!a.nodeType&&a.document&&(a=a.document),9===a.nodeType){var b=a.documentElement,c=a.body;return{left:b&&b.scrollLeft||c&&c.scrollLeft||0,top:b&&b.scrollTop||c&&c.scrollTop||0}}return{left:a.scrollLeft,top:a.scrollTop}},D.getViewportSize=function(a){\"use strict\";var b,c=a.document,d=c.documentElement;return a.innerWidth?{width:a.innerWidth,height:a.innerHeight}:d?{width:d.clientWidth,height:d.clientHeight}:(b=c.body,{width:b.clientWidth,height:b.clientHeight})};var H=[\"HEAD\",\"TITLE\",\"TEMPLATE\",\"SCRIPT\",\"STYLE\",\"IFRAME\",\"OBJECT\",\"VIDEO\",\"AUDIO\",\"NOSCRIPT\"];D.hasContentVirtual=function(a,b){return j(a)||D.isVisualContent(a.actualNode)||!!A.labelVirtual(a)||!b&&a.children.some(function(a){return 1===a.actualNode.nodeType&&D.hasContentVirtual(a)})},D.hasContent=function(a,b){return a=axe.utils.getNodeFromTree(axe._tree[0],a),D.hasContentVirtual(a,b)},D.idrefs=function(a,b){\"use strict\";var c,d,e=D.getRootNode(a),f=[],g=a.getAttribute(b);if(g)for(g=axe.utils.tokenList(g),c=0,d=g.length;c<d;c++)f.push(e.getElementById(g[c]));return f},D.isFocusable=function(a){\"use strict\";if(k(a))return!1;if(D.isNativelyFocusable(a))return!0;var b=a.getAttribute(\"tabindex\");return!(!b||isNaN(parseInt(b,10)))},D.isNativelyFocusable=function(a){\"use strict\";if(!a||k(a))return!1;switch(a.nodeName.toUpperCase()){case\"A\":case\"AREA\":if(a.href)return!0;break;case\"INPUT\":return\"hidden\"!==a.type;case\"TEXTAREA\":case\"SELECT\":case\"DETAILS\":case\"BUTTON\":return!0}return!1},D.insertedIntoFocusOrder=function(a){return a.tabIndex>-1&&D.isFocusable(a)&&!D.isNativelyFocusable(a)},D.isHTML5=function(a){var b=a.doctype;return null!==b&&(\"html\"===b.name&&!b.publicId&&!b.systemId)};var I=[\"block\",\"list-item\",\"table\",\"flex\",\"grid\",\"inline-block\"];D.isInTextBlock=function(a){if(m(a))return!1;var b=n(a),c=\"\",d=\"\",e=0;return l(b,function(b){if(2===e)return!1;if(3===b.nodeType&&(c+=b.nodeValue),1===b.nodeType){var f=(b.nodeName||\"\").toUpperCase();if([\"BR\",\"HR\"].includes(f))0===e?(c=\"\",d=\"\"):e=2;else{if(\"none\"===b.style.display||\"hidden\"===b.style.overflow||![\"\",null,\"none\"].includes(b.style.float)||![\"\",null,\"relative\"].includes(b.style.position))return!1;if(\"A\"===f&&b.href||\"link\"===(b.getAttribute(\"role\")||\"\").toLowerCase())return b===a&&(e=1),d+=b.textContent,!1}}}),c=axe.commons.text.sanitize(c),d=axe.commons.text.sanitize(d),c.length>d.length},D.isNode=function(a){\"use strict\";return a instanceof Node},D.isOffscreen=function(a){var b=void 0,c=document.documentElement,d=window.getComputedStyle(a),e=window.getComputedStyle(document.body||c).getPropertyValue(\"direction\"),f=D.getElementCoordinates(a);if(f.bottom<0&&(o(a,f.bottom)||\"absolute\"===d.position))return!0;if(0===f.left&&0===f.right)return!1;if(\"ltr\"===e){if(f.right<=0)return!0}else if(b=Math.max(c.scrollWidth,D.getViewportSize(window).width),f.left>=b)return!0;return!1},D.isVisible=function(a,b,c){\"use strict\";var d,e,f;return 9===a.nodeType||(11===a.nodeType&&(a=a.host),null!==(d=window.getComputedStyle(a,null))&&(e=a.nodeName.toUpperCase(),!(\"none\"===d.getPropertyValue(\"display\")||\"STYLE\"===e.toUpperCase()||\"SCRIPT\"===e.toUpperCase()||!b&&p(d.getPropertyValue(\"clip\"))||!c&&(\"hidden\"===d.getPropertyValue(\"visibility\")||!b&&D.isOffscreen(a))||b&&\"true\"===a.getAttribute(\"aria-hidden\"))&&(!!(f=a.assignedSlot?a.assignedSlot:a.parentNode)&&D.isVisible(f,b,!0))))};var J=[\"checkbox\",\"img\",\"radio\",\"range\",\"slider\",\"spinbutton\",\"textbox\"];D.isVisualContent=function(a){var b=a.getAttribute(\"role\");if(b)return-1!==J.indexOf(b);switch(a.tagName.toUpperCase()){case\"IMG\":case\"IFRAME\":case\"OBJECT\":case\"VIDEO\":case\"AUDIO\":case\"CANVAS\":case\"SVG\":case\"MATH\":case\"BUTTON\":case\"SELECT\":case\"TEXTAREA\":case\"KEYGEN\":case\"PROGRESS\":case\"METER\":return!0;case\"INPUT\":return\"hidden\"!==a.type;default:return!1}},D.shadowElementsFromPoint=function(a,b){return(arguments.length>2&&void 0!==arguments[2]?arguments[2]:document).elementsFromPoint(a,b).reduce(function(c,d){if(axe.utils.isShadowRoot(d)){var e=D.shadowElementsFromPoint(a,b,d.shadowRoot);c=c.concat(e),c.length&&axe.commons.dom.visuallyContains(c[0],d)&&c.push(d)}else c.push(d);return c},[])},D.visuallyContains=function(a,b){var c=a.getBoundingClientRect(),d={top:c.top+.01,bottom:c.bottom-.01,left:c.left+.01,right:c.right-.01},e=b.getBoundingClientRect(),f=e.top,g=e.left,h={top:f-b.scrollTop,bottom:f-b.scrollTop+b.scrollHeight,left:g-b.scrollLeft,right:g-b.scrollLeft+b.scrollWidth},i=window.getComputedStyle(b);return\"inline\"===i.getPropertyValue(\"display\")||!(d.left<h.left&&d.left<e.left||d.top<h.top&&d.top<e.top||d.right>h.right&&d.right>e.right||d.bottom>h.bottom&&d.bottom>e.bottom)&&(!(d.right>e.right||d.bottom>e.bottom)||(\"scroll\"===i.overflow||\"auto\"===i.overflow||\"hidden\"===i.overflow||b instanceof HTMLBodyElement||b instanceof HTMLHtmlElement))},D.visuallyOverlaps=function(a,b){var c=b.getBoundingClientRect(),d=c.top,e=c.left,f={top:d-b.scrollTop,bottom:d-b.scrollTop+b.scrollHeight,left:e-b.scrollLeft,right:e-b.scrollLeft+b.scrollWidth};if(a.left>f.right&&a.left>c.right||a.top>f.bottom&&a.top>c.bottom||a.right<f.left&&a.right<c.left||a.bottom<f.top&&a.bottom<c.top)return!1;var g=window.getComputedStyle(b);return!(a.left>c.right||a.top>c.bottom)||(\"scroll\"===g.overflow||\"auto\"===g.overflow||b instanceof HTMLBodyElement||b instanceof HTMLHtmlElement)},E.getAllCells=function(a){var b,c,d,e,f=[];for(b=0,d=a.rows.length;b<d;b++)for(c=0,e=a.rows[b].cells.length;c<e;c++)f.push(a.rows[b].cells[c]);return f},E.getCellPosition=function(a,b){var c,d;for(b||(b=E.toGrid(D.findUp(a,\"table\"))),c=0;c<b.length;c++)if(b[c]&&-1!==(d=b[c].indexOf(a)))return{x:d,y:c}},E.getHeaders=function(a){if(a.hasAttribute(\"headers\"))return commons.dom.idrefs(a,\"headers\");var b=commons.table.toGrid(commons.dom.findUp(a,\"table\")),c=commons.table.getCellPosition(a,b);return[].concat(E.traverse(\"left\",c,b).filter(function(a){return E.isRowHeader(a)}),E.traverse(\"up\",c,b).filter(function(a){return E.isColumnHeader(a)})).reverse()},E.getScope=function(a){var b=a.getAttribute(\"scope\"),c=a.getAttribute(\"role\");if(a instanceof Element==!1||-1===[\"TD\",\"TH\"].indexOf(a.nodeName.toUpperCase()))throw new TypeError(\"Expected TD or TH element\");if(\"columnheader\"===c)return\"col\";if(\"rowheader\"===c)return\"row\";if(\"col\"===b||\"row\"===b)return b;if(\"TH\"!==a.nodeName.toUpperCase())return!1;var d=E.toGrid(D.findUp(a,\"table\")),e=E.getCellPosition(a);return d[e.y].reduce(function(a,b){return a&&\"TH\"===b.nodeName.toUpperCase()},!0)?\"col\":d.map(function(a){return a[e.x]}).reduce(function(a,b){return a&&\"TH\"===b.nodeName.toUpperCase()},!0)?\"row\":\"auto\"},E.isColumnHeader=function(a){return-1!==[\"col\",\"auto\"].indexOf(E.getScope(a))},E.isDataCell=function(a){return!(!a.children.length&&!a.textContent.trim())&&\"TD\"===a.nodeName.toUpperCase()},E.isDataTable=function(a){var b=(a.getAttribute(\"role\")||\"\").toLowerCase();if((\"presentation\"===b||\"none\"===b)&&!D.isFocusable(a))return!1;if(\"true\"===a.getAttribute(\"contenteditable\")||D.findUp(a,'[contenteditable=\"true\"]'))return!0;if(\"grid\"===b||\"treegrid\"===b||\"table\"===b)return!0;if(\"landmark\"===commons.aria.getRoleType(b))return!0;if(\"0\"===a.getAttribute(\"datatable\"))return!1;if(a.getAttribute(\"summary\"))return!0;if(a.tHead||a.tFoot||a.caption)return!0;for(var c=0,d=a.children.length;c<d;c++)if(\"COLGROUP\"===a.children[c].nodeName.toUpperCase())return!0;for(var e,f,g=0,h=a.rows.length,i=!1,j=0;j<h;j++){e=a.rows[j];for(var k=0,l=e.cells.length;k<l;k++){if(f=e.cells[k],\"TH\"===f.nodeName.toUpperCase())return!0;if(i||f.offsetWidth===f.clientWidth&&f.offsetHeight===f.clientHeight||(i=!0),f.getAttribute(\"scope\")||f.getAttribute(\"headers\")||f.getAttribute(\"abbr\"))return!0;if([\"columnheader\",\"rowheader\"].includes((f.getAttribute(\"role\")||\"\").toLowerCase()))return!0;if(1===f.children.length&&\"ABBR\"===f.children[0].nodeName.toUpperCase())return!0;g++}}if(a.getElementsByTagName(\"table\").length)return!1;if(h<2)return!1;var m=a.rows[Math.ceil(h/2)];if(1===m.cells.length&&1===m.cells[0].colSpan)return!1;if(m.cells.length>=5)return!0;if(i)return!0;var n,o;for(j=0;j<h;j++){if(e=a.rows[j],n&&n!==window.getComputedStyle(e).getPropertyValue(\"background-color\"))return!0;if(n=window.getComputedStyle(e).getPropertyValue(\"background-color\"),o&&o!==window.getComputedStyle(e).getPropertyValue(\"background-image\"))return!0;o=window.getComputedStyle(e).getPropertyValue(\"background-image\")}return h>=20||!(D.getElementCoordinates(a).width>.95*D.getViewportSize(window).width)&&(!(g<10)&&!a.querySelector(\"object, embed, iframe, applet\"))},E.isHeader=function(a){if(E.isColumnHeader(a)||E.isRowHeader(a))return!0;if(a.getAttribute(\"id\")){var b=axe.utils.escapeSelector(a.getAttribute(\"id\"));return!!document.querySelector('[headers~=\"'+b+'\"]')}return!1},E.isRowHeader=function(a){return[\"row\",\"auto\"].includes(E.getScope(a))},E.toGrid=function(a){for(var b=[],c=a.rows,d=0,e=c.length;d<e;d++){var f=c[d].cells;b[d]=b[d]||[];for(var g=0,h=0,i=f.length;h<i;h++)for(var j=0;j<f[h].colSpan;j++){for(var k=0;k<f[h].rowSpan;k++){for(b[d+k]=b[d+k]||[];b[d+k][g];)g++;b[d+k][g]=f[h]}g++}}return b},E.toArray=E.toGrid,function(a){var b=function a(b,c,d,e){var f,g=d[c.y]?d[c.y][c.x]:void 0;return g?\"function\"==typeof e&&!0===(f=e(g,c,d))?[g]:(f=a(b,{x:c.x+b.x,y:c.y+b.y},d,e),f.unshift(g),f):[]};a.traverse=function(a,c,d,e){if(Array.isArray(c)&&(e=d,d=c,c={x:0,y:0}),\"string\"==typeof a)switch(a){case\"left\":a={x:-1,y:0};break;case\"up\":a={x:0,y:-1};break;case\"right\":a={x:1,y:0};break;case\"down\":a={x:0,y:1}}return b(a,{x:c.x+a.x,y:c.y+a.y},d,e)}}(E);var K={submit:\"Submit\",reset:\"Reset\"},L=[\"text\",\"search\",\"tel\",\"url\",\"email\",\"date\",\"time\",\"number\",\"range\",\"color\"],M=[\"A\",\"EM\",\"STRONG\",\"SMALL\",\"MARK\",\"ABBR\",\"DFN\",\"I\",\"B\",\"S\",\"U\",\"CODE\",\"VAR\",\"SAMP\",\"KBD\",\"SUP\",\"SUB\",\"Q\",\"CITE\",\"SPAN\",\"BDO\",\"BDI\",\"BR\",\"WBR\",\"INS\",\"DEL\",\"IMG\",\"EMBED\",\"OBJECT\",\"IFRAME\",\"MAP\",\"AREA\",\"SCRIPT\",\"NOSCRIPT\",\"RUBY\",\"VIDEO\",\"AUDIO\",\"INPUT\",\"TEXTAREA\",\"SELECT\",\"BUTTON\",\"LABEL\",\"OUTPUT\",\"DATALIST\",\"KEYGEN\",\"PROGRESS\",\"COMMAND\",\"CANVAS\",\"TIME\",\"METER\"];F.accessibleText=function(a,b){var c=axe.utils.getNodeFromTree(axe._tree[0],a);return axe.commons.text.accessibleTextVirtual(c,b)},F.accessibleTextVirtual=function(a,b){function c(a,b,c){return a.children.reduce(function(a,d){var e=d.actualNode;return 3===e.nodeType?a+=e.nodeValue:1===e.nodeType&&(M.includes(e.nodeName.toUpperCase())||(a+=\" \"),a+=g(d,b,c)),a},\"\")}function d(a){return axe.commons.table.isDataTable(a.actualNode)||1!==axe.commons.table.getAllCells(a.actualNode).length?\"\":c(a,!1,!1).trim()}function e(a,b,e){var f=\"\",h=a.actualNode,i=h.nodeName.toUpperCase();if(t(a)&&(f=c(a,!1,!1)||\"\",z(f)))return f;if(\"FIGURE\"===i&&(f=w(a,\"figcaption\"),z(f)))return f;if(\"TABLE\"===i){if(f=w(a,\"caption\"),z(f))return f;if(f=h.getAttribute(\"title\")||h.getAttribute(\"summary\")||d(a)||\"\",z(f))return f}if(y(a))return h.getAttribute(\"alt\")||\"\";if(s(a)&&!e){if(r(a))return h.value||h.title||K[h.type]||\"\";var j=q(a);if(j)return g(j,b,!0)}return\"\"}function f(a,b,c){var d=\"\",e=a.actualNode;return!b&&e.hasAttribute(\"aria-labelledby\")&&(d=F.sanitize(D.idrefs(e,\"aria-labelledby\").map(function(a){if(null!==a){e===a&&h.pop();var b=axe.utils.getNodeFromTree(axe._tree[0],a);return g(b,!0,e!==a)}return\"\"}).join(\" \"))),d||c&&x(a)||!e.hasAttribute(\"aria-label\")?d:F.sanitize(e.getAttribute(\"aria-label\"))}var g=void 0,h=[];return a instanceof Node&&(a=axe.utils.getNodeFromTree(axe._tree[0],a)),g=function(a,b,d){var g=void 0;if(!a||h.includes(a))return\"\";if(null!==a&&a.actualNode instanceof Node!=!0)throw new Error(\"Invalid argument. Virtual Node must be provided\");if(!b&&!D.isVisible(a.actualNode,!0))return\"\";h.push(a);var i=a.actualNode.getAttribute(\"role\");return g=f(a,b,d),z(g)?g:(g=e(a,b,d),z(g)?g:d&&(g=v(a),z(g))?g:u(a)||i&&-1===A.getRolesWithNameFromContents().indexOf(i)||(g=c(a,b,d),!z(g))?a.actualNode.hasAttribute(\"title\")?a.actualNode.getAttribute(\"title\"):\"\":g)},F.sanitize(g(a,b))},F.labelVirtual=function(a){var b,c,d;if(c=A.labelVirtual(a))return c;if(a.actualNode.id){var e=axe.commons.utils.escapeSelector(a.actualNode.getAttribute(\"id\"));if(d=axe.commons.dom.getRootNode(a.actualNode),b=d.querySelector('label[for=\"'+e+'\"]'),c=b&&F.visible(b,!0))return c}return b=D.findUpVirtual(a,\"label\"),(c=b&&F.visible(b,!0))||null},F.label=function(a){return a=axe.utils.getNodeFromTree(axe._tree[0],a),F.labelVirtual(a)},F.sanitize=function(a){\"use strict\";return a.replace(/\\r\\n/g,\"\\n\").replace(/\\u00A0/g,\" \").replace(/[\\s]{2,}/g,\" \").trim()},F.visibleVirtual=function(a,b,c){var d=a.children.map(function(d){if(3===d.actualNode.nodeType){var e=d.actualNode.nodeValue;if(e&&D.isVisible(a.actualNode,b))return e}else if(!c)return F.visibleVirtual(d,b)}).join(\"\");return F.sanitize(d)},F.visible=function(a,b,c){return a=axe.utils.getNodeFromTree(axe._tree[0],a),F.visibleVirtual(a,b,c)},axe.utils.tokenList=function(a){\"use strict\";return a.trim().replace(/\\s{2,}/g,\" \").split(\" \")}\n;var N=[\"aa\",\"ab\",\"ae\",\"af\",\"ak\",\"am\",\"an\",\"ar\",\"as\",\"av\",\"ay\",\"az\",\"ba\",\"be\",\"bg\",\"bh\",\"bi\",\"bm\",\"bn\",\"bo\",\"br\",\"bs\",\"ca\",\"ce\",\"ch\",\"co\",\"cr\",\"cs\",\"cu\",\"cv\",\"cy\",\"da\",\"de\",\"dv\",\"dz\",\"ee\",\"el\",\"en\",\"eo\",\"es\",\"et\",\"eu\",\"fa\",\"ff\",\"fi\",\"fj\",\"fo\",\"fr\",\"fy\",\"ga\",\"gd\",\"gl\",\"gn\",\"gu\",\"gv\",\"ha\",\"he\",\"hi\",\"ho\",\"hr\",\"ht\",\"hu\",\"hy\",\"hz\",\"ia\",\"id\",\"ie\",\"ig\",\"ii\",\"ik\",\"in\",\"io\",\"is\",\"it\",\"iu\",\"iw\",\"ja\",\"ji\",\"jv\",\"jw\",\"ka\",\"kg\",\"ki\",\"kj\",\"kk\",\"kl\",\"km\",\"kn\",\"ko\",\"kr\",\"ks\",\"ku\",\"kv\",\"kw\",\"ky\",\"la\",\"lb\",\"lg\",\"li\",\"ln\",\"lo\",\"lt\",\"lu\",\"lv\",\"mg\",\"mh\",\"mi\",\"mk\",\"ml\",\"mn\",\"mo\",\"mr\",\"ms\",\"mt\",\"my\",\"na\",\"nb\",\"nd\",\"ne\",\"ng\",\"nl\",\"nn\",\"no\",\"nr\",\"nv\",\"ny\",\"oc\",\"oj\",\"om\",\"or\",\"os\",\"pa\",\"pi\",\"pl\",\"ps\",\"pt\",\"qu\",\"rm\",\"rn\",\"ro\",\"ru\",\"rw\",\"sa\",\"sc\",\"sd\",\"se\",\"sg\",\"sh\",\"si\",\"sk\",\"sl\",\"sm\",\"sn\",\"so\",\"sq\",\"sr\",\"ss\",\"st\",\"su\",\"sv\",\"sw\",\"ta\",\"te\",\"tg\",\"th\",\"ti\",\"tk\",\"tl\",\"tn\",\"to\",\"tr\",\"ts\",\"tt\",\"tw\",\"ty\",\"ug\",\"uk\",\"ur\",\"uz\",\"ve\",\"vi\",\"vo\",\"wa\",\"wo\",\"xh\",\"yi\",\"yo\",\"za\",\"zh\",\"zu\",\"aaa\",\"aab\",\"aac\",\"aad\",\"aae\",\"aaf\",\"aag\",\"aah\",\"aai\",\"aak\",\"aal\",\"aam\",\"aan\",\"aao\",\"aap\",\"aaq\",\"aas\",\"aat\",\"aau\",\"aav\",\"aaw\",\"aax\",\"aaz\",\"aba\",\"abb\",\"abc\",\"abd\",\"abe\",\"abf\",\"abg\",\"abh\",\"abi\",\"abj\",\"abl\",\"abm\",\"abn\",\"abo\",\"abp\",\"abq\",\"abr\",\"abs\",\"abt\",\"abu\",\"abv\",\"abw\",\"abx\",\"aby\",\"abz\",\"aca\",\"acb\",\"acd\",\"ace\",\"acf\",\"ach\",\"aci\",\"ack\",\"acl\",\"acm\",\"acn\",\"acp\",\"acq\",\"acr\",\"acs\",\"act\",\"acu\",\"acv\",\"acw\",\"acx\",\"acy\",\"acz\",\"ada\",\"adb\",\"add\",\"ade\",\"adf\",\"adg\",\"adh\",\"adi\",\"adj\",\"adl\",\"adn\",\"ado\",\"adp\",\"adq\",\"adr\",\"ads\",\"adt\",\"adu\",\"adw\",\"adx\",\"ady\",\"adz\",\"aea\",\"aeb\",\"aec\",\"aed\",\"aee\",\"aek\",\"ael\",\"aem\",\"aen\",\"aeq\",\"aer\",\"aes\",\"aeu\",\"aew\",\"aey\",\"aez\",\"afa\",\"afb\",\"afd\",\"afe\",\"afg\",\"afh\",\"afi\",\"afk\",\"afn\",\"afo\",\"afp\",\"afs\",\"aft\",\"afu\",\"afz\",\"aga\",\"agb\",\"agc\",\"agd\",\"age\",\"agf\",\"agg\",\"agh\",\"agi\",\"agj\",\"agk\",\"agl\",\"agm\",\"agn\",\"ago\",\"agp\",\"agq\",\"agr\",\"ags\",\"agt\",\"agu\",\"agv\",\"agw\",\"agx\",\"agy\",\"agz\",\"aha\",\"ahb\",\"ahg\",\"ahh\",\"ahi\",\"ahk\",\"ahl\",\"ahm\",\"ahn\",\"aho\",\"ahp\",\"ahr\",\"ahs\",\"aht\",\"aia\",\"aib\",\"aic\",\"aid\",\"aie\",\"aif\",\"aig\",\"aih\",\"aii\",\"aij\",\"aik\",\"ail\",\"aim\",\"ain\",\"aio\",\"aip\",\"aiq\",\"air\",\"ais\",\"ait\",\"aiw\",\"aix\",\"aiy\",\"aja\",\"ajg\",\"aji\",\"ajn\",\"ajp\",\"ajt\",\"aju\",\"ajw\",\"ajz\",\"akb\",\"akc\",\"akd\",\"ake\",\"akf\",\"akg\",\"akh\",\"aki\",\"akj\",\"akk\",\"akl\",\"akm\",\"ako\",\"akp\",\"akq\",\"akr\",\"aks\",\"akt\",\"aku\",\"akv\",\"akw\",\"akx\",\"aky\",\"akz\",\"ala\",\"alc\",\"ald\",\"ale\",\"alf\",\"alg\",\"alh\",\"ali\",\"alj\",\"alk\",\"all\",\"alm\",\"aln\",\"alo\",\"alp\",\"alq\",\"alr\",\"als\",\"alt\",\"alu\",\"alv\",\"alw\",\"alx\",\"aly\",\"alz\",\"ama\",\"amb\",\"amc\",\"ame\",\"amf\",\"amg\",\"ami\",\"amj\",\"amk\",\"aml\",\"amm\",\"amn\",\"amo\",\"amp\",\"amq\",\"amr\",\"ams\",\"amt\",\"amu\",\"amv\",\"amw\",\"amx\",\"amy\",\"amz\",\"ana\",\"anb\",\"anc\",\"and\",\"ane\",\"anf\",\"ang\",\"anh\",\"ani\",\"anj\",\"ank\",\"anl\",\"anm\",\"ann\",\"ano\",\"anp\",\"anq\",\"anr\",\"ans\",\"ant\",\"anu\",\"anv\",\"anw\",\"anx\",\"any\",\"anz\",\"aoa\",\"aob\",\"aoc\",\"aod\",\"aoe\",\"aof\",\"aog\",\"aoh\",\"aoi\",\"aoj\",\"aok\",\"aol\",\"aom\",\"aon\",\"aor\",\"aos\",\"aot\",\"aou\",\"aox\",\"aoz\",\"apa\",\"apb\",\"apc\",\"apd\",\"ape\",\"apf\",\"apg\",\"aph\",\"api\",\"apj\",\"apk\",\"apl\",\"apm\",\"apn\",\"apo\",\"app\",\"apq\",\"apr\",\"aps\",\"apt\",\"apu\",\"apv\",\"apw\",\"apx\",\"apy\",\"apz\",\"aqa\",\"aqc\",\"aqd\",\"aqg\",\"aql\",\"aqm\",\"aqn\",\"aqp\",\"aqr\",\"aqt\",\"aqz\",\"arb\",\"arc\",\"ard\",\"are\",\"arh\",\"ari\",\"arj\",\"ark\",\"arl\",\"arn\",\"aro\",\"arp\",\"arq\",\"arr\",\"ars\",\"art\",\"aru\",\"arv\",\"arw\",\"arx\",\"ary\",\"arz\",\"asa\",\"asb\",\"asc\",\"asd\",\"ase\",\"asf\",\"asg\",\"ash\",\"asi\",\"asj\",\"ask\",\"asl\",\"asn\",\"aso\",\"asp\",\"asq\",\"asr\",\"ass\",\"ast\",\"asu\",\"asv\",\"asw\",\"asx\",\"asy\",\"asz\",\"ata\",\"atb\",\"atc\",\"atd\",\"ate\",\"atg\",\"ath\",\"ati\",\"atj\",\"atk\",\"atl\",\"atm\",\"atn\",\"ato\",\"atp\",\"atq\",\"atr\",\"ats\",\"att\",\"atu\",\"atv\",\"atw\",\"atx\",\"aty\",\"atz\",\"aua\",\"aub\",\"auc\",\"aud\",\"aue\",\"auf\",\"aug\",\"auh\",\"aui\",\"auj\",\"auk\",\"aul\",\"aum\",\"aun\",\"auo\",\"aup\",\"auq\",\"aur\",\"aus\",\"aut\",\"auu\",\"auw\",\"aux\",\"auy\",\"auz\",\"avb\",\"avd\",\"avi\",\"avk\",\"avl\",\"avm\",\"avn\",\"avo\",\"avs\",\"avt\",\"avu\",\"avv\",\"awa\",\"awb\",\"awc\",\"awd\",\"awe\",\"awg\",\"awh\",\"awi\",\"awk\",\"awm\",\"awn\",\"awo\",\"awr\",\"aws\",\"awt\",\"awu\",\"awv\",\"aww\",\"awx\",\"awy\",\"axb\",\"axe\",\"axg\",\"axk\",\"axl\",\"axm\",\"axx\",\"aya\",\"ayb\",\"ayc\",\"ayd\",\"aye\",\"ayg\",\"ayh\",\"ayi\",\"ayk\",\"ayl\",\"ayn\",\"ayo\",\"ayp\",\"ayq\",\"ayr\",\"ays\",\"ayt\",\"ayu\",\"ayx\",\"ayy\",\"ayz\",\"aza\",\"azb\",\"azc\",\"azd\",\"azg\",\"azj\",\"azm\",\"azn\",\"azo\",\"azt\",\"azz\",\"baa\",\"bab\",\"bac\",\"bad\",\"bae\",\"baf\",\"bag\",\"bah\",\"bai\",\"baj\",\"bal\",\"ban\",\"bao\",\"bap\",\"bar\",\"bas\",\"bat\",\"bau\",\"bav\",\"baw\",\"bax\",\"bay\",\"baz\",\"bba\",\"bbb\",\"bbc\",\"bbd\",\"bbe\",\"bbf\",\"bbg\",\"bbh\",\"bbi\",\"bbj\",\"bbk\",\"bbl\",\"bbm\",\"bbn\",\"bbo\",\"bbp\",\"bbq\",\"bbr\",\"bbs\",\"bbt\",\"bbu\",\"bbv\",\"bbw\",\"bbx\",\"bby\",\"bbz\",\"bca\",\"bcb\",\"bcc\",\"bcd\",\"bce\",\"bcf\",\"bcg\",\"bch\",\"bci\",\"bcj\",\"bck\",\"bcl\",\"bcm\",\"bcn\",\"bco\",\"bcp\",\"bcq\",\"bcr\",\"bcs\",\"bct\",\"bcu\",\"bcv\",\"bcw\",\"bcy\",\"bcz\",\"bda\",\"bdb\",\"bdc\",\"bdd\",\"bde\",\"bdf\",\"bdg\",\"bdh\",\"bdi\",\"bdj\",\"bdk\",\"bdl\",\"bdm\",\"bdn\",\"bdo\",\"bdp\",\"bdq\",\"bdr\",\"bds\",\"bdt\",\"bdu\",\"bdv\",\"bdw\",\"bdx\",\"bdy\",\"bdz\",\"bea\",\"beb\",\"bec\",\"bed\",\"bee\",\"bef\",\"beg\",\"beh\",\"bei\",\"bej\",\"bek\",\"bem\",\"beo\",\"bep\",\"beq\",\"ber\",\"bes\",\"bet\",\"beu\",\"bev\",\"bew\",\"bex\",\"bey\",\"bez\",\"bfa\",\"bfb\",\"bfc\",\"bfd\",\"bfe\",\"bff\",\"bfg\",\"bfh\",\"bfi\",\"bfj\",\"bfk\",\"bfl\",\"bfm\",\"bfn\",\"bfo\",\"bfp\",\"bfq\",\"bfr\",\"bfs\",\"bft\",\"bfu\",\"bfw\",\"bfx\",\"bfy\",\"bfz\",\"bga\",\"bgb\",\"bgc\",\"bgd\",\"bge\",\"bgf\",\"bgg\",\"bgi\",\"bgj\",\"bgk\",\"bgl\",\"bgm\",\"bgn\",\"bgo\",\"bgp\",\"bgq\",\"bgr\",\"bgs\",\"bgt\",\"bgu\",\"bgv\",\"bgw\",\"bgx\",\"bgy\",\"bgz\",\"bha\",\"bhb\",\"bhc\",\"bhd\",\"bhe\",\"bhf\",\"bhg\",\"bhh\",\"bhi\",\"bhj\",\"bhk\",\"bhl\",\"bhm\",\"bhn\",\"bho\",\"bhp\",\"bhq\",\"bhr\",\"bhs\",\"bht\",\"bhu\",\"bhv\",\"bhw\",\"bhx\",\"bhy\",\"bhz\",\"bia\",\"bib\",\"bic\",\"bid\",\"bie\",\"bif\",\"big\",\"bij\",\"bik\",\"bil\",\"bim\",\"bin\",\"bio\",\"bip\",\"biq\",\"bir\",\"bit\",\"biu\",\"biv\",\"biw\",\"bix\",\"biy\",\"biz\",\"bja\",\"bjb\",\"bjc\",\"bjd\",\"bje\",\"bjf\",\"bjg\",\"bjh\",\"bji\",\"bjj\",\"bjk\",\"bjl\",\"bjm\",\"bjn\",\"bjo\",\"bjp\",\"bjq\",\"bjr\",\"bjs\",\"bjt\",\"bju\",\"bjv\",\"bjw\",\"bjx\",\"bjy\",\"bjz\",\"bka\",\"bkb\",\"bkc\",\"bkd\",\"bkf\",\"bkg\",\"bkh\",\"bki\",\"bkj\",\"bkk\",\"bkl\",\"bkm\",\"bkn\",\"bko\",\"bkp\",\"bkq\",\"bkr\",\"bks\",\"bkt\",\"bku\",\"bkv\",\"bkw\",\"bkx\",\"bky\",\"bkz\",\"bla\",\"blb\",\"blc\",\"bld\",\"ble\",\"blf\",\"blg\",\"blh\",\"bli\",\"blj\",\"blk\",\"bll\",\"blm\",\"bln\",\"blo\",\"blp\",\"blq\",\"blr\",\"bls\",\"blt\",\"blv\",\"blw\",\"blx\",\"bly\",\"blz\",\"bma\",\"bmb\",\"bmc\",\"bmd\",\"bme\",\"bmf\",\"bmg\",\"bmh\",\"bmi\",\"bmj\",\"bmk\",\"bml\",\"bmm\",\"bmn\",\"bmo\",\"bmp\",\"bmq\",\"bmr\",\"bms\",\"bmt\",\"bmu\",\"bmv\",\"bmw\",\"bmx\",\"bmy\",\"bmz\",\"bna\",\"bnb\",\"bnc\",\"bnd\",\"bne\",\"bnf\",\"bng\",\"bni\",\"bnj\",\"bnk\",\"bnl\",\"bnm\",\"bnn\",\"bno\",\"bnp\",\"bnq\",\"bnr\",\"bns\",\"bnt\",\"bnu\",\"bnv\",\"bnw\",\"bnx\",\"bny\",\"bnz\",\"boa\",\"bob\",\"boe\",\"bof\",\"bog\",\"boh\",\"boi\",\"boj\",\"bok\",\"bol\",\"bom\",\"bon\",\"boo\",\"bop\",\"boq\",\"bor\",\"bot\",\"bou\",\"bov\",\"bow\",\"box\",\"boy\",\"boz\",\"bpa\",\"bpb\",\"bpd\",\"bpg\",\"bph\",\"bpi\",\"bpj\",\"bpk\",\"bpl\",\"bpm\",\"bpn\",\"bpo\",\"bpp\",\"bpq\",\"bpr\",\"bps\",\"bpt\",\"bpu\",\"bpv\",\"bpw\",\"bpx\",\"bpy\",\"bpz\",\"bqa\",\"bqb\",\"bqc\",\"bqd\",\"bqf\",\"bqg\",\"bqh\",\"bqi\",\"bqj\",\"bqk\",\"bql\",\"bqm\",\"bqn\",\"bqo\",\"bqp\",\"bqq\",\"bqr\",\"bqs\",\"bqt\",\"bqu\",\"bqv\",\"bqw\",\"bqx\",\"bqy\",\"bqz\",\"bra\",\"brb\",\"brc\",\"brd\",\"brf\",\"brg\",\"brh\",\"bri\",\"brj\",\"brk\",\"brl\",\"brm\",\"brn\",\"bro\",\"brp\",\"brq\",\"brr\",\"brs\",\"brt\",\"bru\",\"brv\",\"brw\",\"brx\",\"bry\",\"brz\",\"bsa\",\"bsb\",\"bsc\",\"bse\",\"bsf\",\"bsg\",\"bsh\",\"bsi\",\"bsj\",\"bsk\",\"bsl\",\"bsm\",\"bsn\",\"bso\",\"bsp\",\"bsq\",\"bsr\",\"bss\",\"bst\",\"bsu\",\"bsv\",\"bsw\",\"bsx\",\"bsy\",\"bta\",\"btb\",\"btc\",\"btd\",\"bte\",\"btf\",\"btg\",\"bth\",\"bti\",\"btj\",\"btk\",\"btl\",\"btm\",\"btn\",\"bto\",\"btp\",\"btq\",\"btr\",\"bts\",\"btt\",\"btu\",\"btv\",\"btw\",\"btx\",\"bty\",\"btz\",\"bua\",\"bub\",\"buc\",\"bud\",\"bue\",\"buf\",\"bug\",\"buh\",\"bui\",\"buj\",\"buk\",\"bum\",\"bun\",\"buo\",\"bup\",\"buq\",\"bus\",\"but\",\"buu\",\"buv\",\"buw\",\"bux\",\"buy\",\"buz\",\"bva\",\"bvb\",\"bvc\",\"bvd\",\"bve\",\"bvf\",\"bvg\",\"bvh\",\"bvi\",\"bvj\",\"bvk\",\"bvl\",\"bvm\",\"bvn\",\"bvo\",\"bvp\",\"bvq\",\"bvr\",\"bvt\",\"bvu\",\"bvv\",\"bvw\",\"bvx\",\"bvy\",\"bvz\",\"bwa\",\"bwb\",\"bwc\",\"bwd\",\"bwe\",\"bwf\",\"bwg\",\"bwh\",\"bwi\",\"bwj\",\"bwk\",\"bwl\",\"bwm\",\"bwn\",\"bwo\",\"bwp\",\"bwq\",\"bwr\",\"bws\",\"bwt\",\"bwu\",\"bww\",\"bwx\",\"bwy\",\"bwz\",\"bxa\",\"bxb\",\"bxc\",\"bxd\",\"bxe\",\"bxf\",\"bxg\",\"bxh\",\"bxi\",\"bxj\",\"bxk\",\"bxl\",\"bxm\",\"bxn\",\"bxo\",\"bxp\",\"bxq\",\"bxr\",\"bxs\",\"bxu\",\"bxv\",\"bxw\",\"bxx\",\"bxz\",\"bya\",\"byb\",\"byc\",\"byd\",\"bye\",\"byf\",\"byg\",\"byh\",\"byi\",\"byj\",\"byk\",\"byl\",\"bym\",\"byn\",\"byo\",\"byp\",\"byq\",\"byr\",\"bys\",\"byt\",\"byv\",\"byw\",\"byx\",\"byy\",\"byz\",\"bza\",\"bzb\",\"bzc\",\"bzd\",\"bze\",\"bzf\",\"bzg\",\"bzh\",\"bzi\",\"bzj\",\"bzk\",\"bzl\",\"bzm\",\"bzn\",\"bzo\",\"bzp\",\"bzq\",\"bzr\",\"bzs\",\"bzt\",\"bzu\",\"bzv\",\"bzw\",\"bzx\",\"bzy\",\"bzz\",\"caa\",\"cab\",\"cac\",\"cad\",\"cae\",\"caf\",\"cag\",\"cah\",\"cai\",\"caj\",\"cak\",\"cal\",\"cam\",\"can\",\"cao\",\"cap\",\"caq\",\"car\",\"cas\",\"cau\",\"cav\",\"caw\",\"cax\",\"cay\",\"caz\",\"cba\",\"cbb\",\"cbc\",\"cbd\",\"cbe\",\"cbg\",\"cbh\",\"cbi\",\"cbj\",\"cbk\",\"cbl\",\"cbn\",\"cbo\",\"cbq\",\"cbr\",\"cbs\",\"cbt\",\"cbu\",\"cbv\",\"cbw\",\"cby\",\"cca\",\"ccc\",\"ccd\",\"cce\",\"ccg\",\"cch\",\"ccj\",\"ccl\",\"ccm\",\"ccn\",\"cco\",\"ccp\",\"ccq\",\"ccr\",\"ccs\",\"cda\",\"cdc\",\"cdd\",\"cde\",\"cdf\",\"cdg\",\"cdh\",\"cdi\",\"cdj\",\"cdm\",\"cdn\",\"cdo\",\"cdr\",\"cds\",\"cdy\",\"cdz\",\"cea\",\"ceb\",\"ceg\",\"cek\",\"cel\",\"cen\",\"cet\",\"cfa\",\"cfd\",\"cfg\",\"cfm\",\"cga\",\"cgc\",\"cgg\",\"cgk\",\"chb\",\"chc\",\"chd\",\"chf\",\"chg\",\"chh\",\"chj\",\"chk\",\"chl\",\"chm\",\"chn\",\"cho\",\"chp\",\"chq\",\"chr\",\"cht\",\"chw\",\"chx\",\"chy\",\"chz\",\"cia\",\"cib\",\"cic\",\"cid\",\"cie\",\"cih\",\"cik\",\"cim\",\"cin\",\"cip\",\"cir\",\"ciw\",\"ciy\",\"cja\",\"cje\",\"cjh\",\"cji\",\"cjk\",\"cjm\",\"cjn\",\"cjo\",\"cjp\",\"cjr\",\"cjs\",\"cjv\",\"cjy\",\"cka\",\"ckb\",\"ckh\",\"ckl\",\"ckn\",\"cko\",\"ckq\",\"ckr\",\"cks\",\"ckt\",\"cku\",\"ckv\",\"ckx\",\"cky\",\"ckz\",\"cla\",\"clc\",\"cld\",\"cle\",\"clh\",\"cli\",\"clj\",\"clk\",\"cll\",\"clm\",\"clo\",\"clt\",\"clu\",\"clw\",\"cly\",\"cma\",\"cmc\",\"cme\",\"cmg\",\"cmi\",\"cmk\",\"cml\",\"cmm\",\"cmn\",\"cmo\",\"cmr\",\"cms\",\"cmt\",\"cna\",\"cnb\",\"cnc\",\"cng\",\"cnh\",\"cni\",\"cnk\",\"cnl\",\"cno\",\"cns\",\"cnt\",\"cnu\",\"cnw\",\"cnx\",\"coa\",\"cob\",\"coc\",\"cod\",\"coe\",\"cof\",\"cog\",\"coh\",\"coj\",\"cok\",\"col\",\"com\",\"con\",\"coo\",\"cop\",\"coq\",\"cot\",\"cou\",\"cov\",\"cow\",\"cox\",\"coy\",\"coz\",\"cpa\",\"cpb\",\"cpc\",\"cpe\",\"cpf\",\"cpg\",\"cpi\",\"cpn\",\"cpo\",\"cpp\",\"cps\",\"cpu\",\"cpx\",\"cpy\",\"cqd\",\"cqu\",\"cra\",\"crb\",\"crc\",\"crd\",\"crf\",\"crg\",\"crh\",\"cri\",\"crj\",\"crk\",\"crl\",\"crm\",\"crn\",\"cro\",\"crp\",\"crq\",\"crr\",\"crs\",\"crt\",\"crv\",\"crw\",\"crx\",\"cry\",\"crz\",\"csa\",\"csb\",\"csc\",\"csd\",\"cse\",\"csf\",\"csg\",\"csh\",\"csi\",\"csj\",\"csk\",\"csl\",\"csm\",\"csn\",\"cso\",\"csq\",\"csr\",\"css\",\"cst\",\"csu\",\"csv\",\"csw\",\"csy\",\"csz\",\"cta\",\"ctc\",\"ctd\",\"cte\",\"ctg\",\"cth\",\"ctl\",\"ctm\",\"ctn\",\"cto\",\"ctp\",\"cts\",\"ctt\",\"ctu\",\"ctz\",\"cua\",\"cub\",\"cuc\",\"cug\",\"cuh\",\"cui\",\"cuj\",\"cuk\",\"cul\",\"cum\",\"cuo\",\"cup\",\"cuq\",\"cur\",\"cus\",\"cut\",\"cuu\",\"cuv\",\"cuw\",\"cux\",\"cvg\",\"cvn\",\"cwa\",\"cwb\",\"cwd\",\"cwe\",\"cwg\",\"cwt\",\"cya\",\"cyb\",\"cyo\",\"czh\",\"czk\",\"czn\",\"czo\",\"czt\",\"daa\",\"dac\",\"dad\",\"dae\",\"daf\",\"dag\",\"dah\",\"dai\",\"daj\",\"dak\",\"dal\",\"dam\",\"dao\",\"dap\",\"daq\",\"dar\",\"das\",\"dau\",\"dav\",\"daw\",\"dax\",\"day\",\"daz\",\"dba\",\"dbb\",\"dbd\",\"dbe\",\"dbf\",\"dbg\",\"dbi\",\"dbj\",\"dbl\",\"dbm\",\"dbn\",\"dbo\",\"dbp\",\"dbq\",\"dbr\",\"dbt\",\"dbu\",\"dbv\",\"dbw\",\"dby\",\"dcc\",\"dcr\",\"dda\",\"ddd\",\"dde\",\"ddg\",\"ddi\",\"ddj\",\"ddn\",\"ddo\",\"ddr\",\"dds\",\"ddw\",\"dec\",\"ded\",\"dee\",\"def\",\"deg\",\"deh\",\"dei\",\"dek\",\"del\",\"dem\",\"den\",\"dep\",\"deq\",\"der\",\"des\",\"dev\",\"dez\",\"dga\",\"dgb\",\"dgc\",\"dgd\",\"dge\",\"dgg\",\"dgh\",\"dgi\",\"dgk\",\"dgl\",\"dgn\",\"dgo\",\"dgr\",\"dgs\",\"dgt\",\"dgu\",\"dgw\",\"dgx\",\"dgz\",\"dha\",\"dhd\",\"dhg\",\"dhi\",\"dhl\",\"dhm\",\"dhn\",\"dho\",\"dhr\",\"dhs\",\"dhu\",\"dhv\",\"dhw\",\"dhx\",\"dia\",\"dib\",\"dic\",\"did\",\"dif\",\"dig\",\"dih\",\"dii\",\"dij\",\"dik\",\"dil\",\"dim\",\"din\",\"dio\",\"dip\",\"diq\",\"dir\",\"dis\",\"dit\",\"diu\",\"diw\",\"dix\",\"diy\",\"diz\",\"dja\",\"djb\",\"djc\",\"djd\",\"dje\",\"djf\",\"dji\",\"djj\",\"djk\",\"djl\",\"djm\",\"djn\",\"djo\",\"djr\",\"dju\",\"djw\",\"dka\",\"dkk\",\"dkl\",\"dkr\",\"dks\",\"dkx\",\"dlg\",\"dlk\",\"dlm\",\"dln\",\"dma\",\"dmb\",\"dmc\",\"dmd\",\"dme\",\"dmg\",\"dmk\",\"dml\",\"dmm\",\"dmn\",\"dmo\",\"dmr\",\"dms\",\"dmu\",\"dmv\",\"dmw\",\"dmx\",\"dmy\",\"dna\",\"dnd\",\"dne\",\"dng\",\"dni\",\"dnj\",\"dnk\",\"dnn\",\"dnr\",\"dnt\",\"dnu\",\"dnv\",\"dnw\",\"dny\",\"doa\",\"dob\",\"doc\",\"doe\",\"dof\",\"doh\",\"doi\",\"dok\",\"dol\",\"don\",\"doo\",\"dop\",\"doq\",\"dor\",\"dos\",\"dot\",\"dov\",\"dow\",\"dox\",\"doy\",\"doz\",\"dpp\",\"dra\",\"drb\",\"drc\",\"drd\",\"dre\",\"drg\",\"drh\",\"dri\",\"drl\",\"drn\",\"dro\",\"drq\",\"drr\",\"drs\",\"drt\",\"dru\",\"drw\",\"dry\",\"dsb\",\"dse\",\"dsh\",\"dsi\",\"dsl\",\"dsn\",\"dso\",\"dsq\",\"dta\",\"dtb\",\"dtd\",\"dth\",\"dti\",\"dtk\",\"dtm\",\"dtn\",\"dto\",\"dtp\",\"dtr\",\"dts\",\"dtt\",\"dtu\",\"dty\",\"dua\",\"dub\",\"duc\",\"dud\",\"due\",\"duf\",\"dug\",\"duh\",\"dui\",\"duj\",\"duk\",\"dul\",\"dum\",\"dun\",\"duo\",\"dup\",\"duq\",\"dur\",\"dus\",\"duu\",\"duv\",\"duw\",\"dux\",\"duy\",\"duz\",\"dva\",\"dwa\",\"dwl\",\"dwr\",\"dws\",\"dwu\",\"dww\",\"dwy\",\"dya\",\"dyb\",\"dyd\",\"dyg\",\"dyi\",\"dym\",\"dyn\",\"dyo\",\"dyu\",\"dyy\",\"dza\",\"dzd\",\"dze\",\"dzg\",\"dzl\",\"dzn\",\"eaa\",\"ebg\",\"ebk\",\"ebo\",\"ebr\",\"ebu\",\"ecr\",\"ecs\",\"ecy\",\"eee\",\"efa\",\"efe\",\"efi\",\"ega\",\"egl\",\"ego\",\"egx\",\"egy\",\"ehu\",\"eip\",\"eit\",\"eiv\",\"eja\",\"eka\",\"ekc\",\"eke\",\"ekg\",\"eki\",\"ekk\",\"ekl\",\"ekm\",\"eko\",\"ekp\",\"ekr\",\"eky\",\"ele\",\"elh\",\"eli\",\"elk\",\"elm\",\"elo\",\"elp\",\"elu\",\"elx\",\"ema\",\"emb\",\"eme\",\"emg\",\"emi\",\"emk\",\"emm\",\"emn\",\"emo\",\"emp\",\"ems\",\"emu\",\"emw\",\"emx\",\"emy\",\"ena\",\"enb\",\"enc\",\"end\",\"enf\",\"enh\",\"enl\",\"enm\",\"enn\",\"eno\",\"enq\",\"enr\",\"enu\",\"env\",\"enw\",\"enx\",\"eot\",\"epi\",\"era\",\"erg\",\"erh\",\"eri\",\"erk\",\"ero\",\"err\",\"ers\",\"ert\",\"erw\",\"ese\",\"esg\",\"esh\",\"esi\",\"esk\",\"esl\",\"esm\",\"esn\",\"eso\",\"esq\",\"ess\",\"esu\",\"esx\",\"esy\",\"etb\",\"etc\",\"eth\",\"etn\",\"eto\",\"etr\",\"ets\",\"ett\",\"etu\",\"etx\",\"etz\",\"euq\",\"eve\",\"evh\",\"evn\",\"ewo\",\"ext\",\"eya\",\"eyo\",\"eza\",\"eze\",\"faa\",\"fab\",\"fad\",\"faf\",\"fag\",\"fah\",\"fai\",\"faj\",\"fak\",\"fal\",\"fam\",\"fan\",\"fap\",\"far\",\"fat\",\"fau\",\"fax\",\"fay\",\"faz\",\"fbl\",\"fcs\",\"fer\",\"ffi\",\"ffm\",\"fgr\",\"fia\",\"fie\",\"fil\",\"fip\",\"fir\",\"fit\",\"fiu\",\"fiw\",\"fkk\",\"fkv\",\"fla\",\"flh\",\"fli\",\"fll\",\"fln\",\"flr\",\"fly\",\"fmp\",\"fmu\",\"fnb\",\"fng\",\"fni\",\"fod\",\"foi\",\"fom\",\"fon\",\"for\",\"fos\",\"fox\",\"fpe\",\"fqs\",\"frc\",\"frd\",\"frk\",\"frm\",\"fro\",\"frp\",\"frq\",\"frr\",\"frs\",\"frt\",\"fse\",\"fsl\",\"fss\",\"fub\",\"fuc\",\"fud\",\"fue\",\"fuf\",\"fuh\",\"fui\",\"fuj\",\"fum\",\"fun\",\"fuq\",\"fur\",\"fut\",\"fuu\",\"fuv\",\"fuy\",\"fvr\",\"fwa\",\"fwe\",\"gaa\",\"gab\",\"gac\",\"gad\",\"gae\",\"gaf\",\"gag\",\"gah\",\"gai\",\"gaj\",\"gak\",\"gal\",\"gam\",\"gan\",\"gao\",\"gap\",\"gaq\",\"gar\",\"gas\",\"gat\",\"gau\",\"gav\",\"gaw\",\"gax\",\"gay\",\"gaz\",\"gba\",\"gbb\",\"gbc\",\"gbd\",\"gbe\",\"gbf\",\"gbg\",\"gbh\",\"gbi\",\"gbj\",\"gbk\",\"gbl\",\"gbm\",\"gbn\",\"gbo\",\"gbp\",\"gbq\",\"gbr\",\"gbs\",\"gbu\",\"gbv\",\"gbw\",\"gbx\",\"gby\",\"gbz\",\"gcc\",\"gcd\",\"gce\",\"gcf\",\"gcl\",\"gcn\",\"gcr\",\"gct\",\"gda\",\"gdb\",\"gdc\",\"gdd\",\"gde\",\"gdf\",\"gdg\",\"gdh\",\"gdi\",\"gdj\",\"gdk\",\"gdl\",\"gdm\",\"gdn\",\"gdo\",\"gdq\",\"gdr\",\"gds\",\"gdt\",\"gdu\",\"gdx\",\"gea\",\"geb\",\"gec\",\"ged\",\"geg\",\"geh\",\"gei\",\"gej\",\"gek\",\"gel\",\"gem\",\"geq\",\"ges\",\"gev\",\"gew\",\"gex\",\"gey\",\"gez\",\"gfk\",\"gft\",\"gfx\",\"gga\",\"ggb\",\"ggd\",\"gge\",\"ggg\",\"ggk\",\"ggl\",\"ggn\",\"ggo\",\"ggr\",\"ggt\",\"ggu\",\"ggw\",\"gha\",\"ghc\",\"ghe\",\"ghh\",\"ghk\",\"ghl\",\"ghn\",\"gho\",\"ghr\",\"ghs\",\"ght\",\"gia\",\"gib\",\"gic\",\"gid\",\"gie\",\"gig\",\"gih\",\"gil\",\"gim\",\"gin\",\"gio\",\"gip\",\"giq\",\"gir\",\"gis\",\"git\",\"giu\",\"giw\",\"gix\",\"giy\",\"giz\",\"gji\",\"gjk\",\"gjm\",\"gjn\",\"gjr\",\"gju\",\"gka\",\"gke\",\"gkn\",\"gko\",\"gkp\",\"gku\",\"glc\",\"gld\",\"glh\",\"gli\",\"glj\",\"glk\",\"gll\",\"glo\",\"glr\",\"glu\",\"glw\",\"gly\",\"gma\",\"gmb\",\"gmd\",\"gme\",\"gmg\",\"gmh\",\"gml\",\"gmm\",\"gmn\",\"gmq\",\"gmu\",\"gmv\",\"gmw\",\"gmx\",\"gmy\",\"gmz\",\"gna\",\"gnb\",\"gnc\",\"gnd\",\"gne\",\"gng\",\"gnh\",\"gni\",\"gnk\",\"gnl\",\"gnm\",\"gnn\",\"gno\",\"gnq\",\"gnr\",\"gnt\",\"gnu\",\"gnw\",\"gnz\",\"goa\",\"gob\",\"goc\",\"god\",\"goe\",\"gof\",\"gog\",\"goh\",\"goi\",\"goj\",\"gok\",\"gol\",\"gom\",\"gon\",\"goo\",\"gop\",\"goq\",\"gor\",\"gos\",\"got\",\"gou\",\"gow\",\"gox\",\"goy\",\"goz\",\"gpa\",\"gpe\",\"gpn\",\"gqa\",\"gqi\",\"gqn\",\"gqr\",\"gqu\",\"gra\",\"grb\",\"grc\",\"grd\",\"grg\",\"grh\",\"gri\",\"grj\",\"grk\",\"grm\",\"gro\",\"grq\",\"grr\",\"grs\",\"grt\",\"gru\",\"grv\",\"grw\",\"grx\",\"gry\",\"grz\",\"gse\",\"gsg\",\"gsl\",\"gsm\",\"gsn\",\"gso\",\"gsp\",\"gss\",\"gsw\",\"gta\",\"gti\",\"gtu\",\"gua\",\"gub\",\"guc\",\"gud\",\"gue\",\"guf\",\"gug\",\"guh\",\"gui\",\"guk\",\"gul\",\"gum\",\"gun\",\"guo\",\"gup\",\"guq\",\"gur\",\"gus\",\"gut\",\"guu\",\"guv\",\"guw\",\"gux\",\"guz\",\"gva\",\"gvc\",\"gve\",\"gvf\",\"gvj\",\"gvl\",\"gvm\",\"gvn\",\"gvo\",\"gvp\",\"gvr\",\"gvs\",\"gvy\",\"gwa\",\"gwb\",\"gwc\",\"gwd\",\"gwe\",\"gwf\",\"gwg\",\"gwi\",\"gwj\",\"gwm\",\"gwn\",\"gwr\",\"gwt\",\"gwu\",\"gww\",\"gwx\",\"gxx\",\"gya\",\"gyb\",\"gyd\",\"gye\",\"gyf\",\"gyg\",\"gyi\",\"gyl\",\"gym\",\"gyn\",\"gyr\",\"gyy\",\"gza\",\"gzi\",\"gzn\",\"haa\",\"hab\",\"hac\",\"had\",\"hae\",\"haf\",\"hag\",\"hah\",\"hai\",\"haj\",\"hak\",\"hal\",\"ham\",\"han\",\"hao\",\"hap\",\"haq\",\"har\",\"has\",\"hav\",\"haw\",\"hax\",\"hay\",\"haz\",\"hba\",\"hbb\",\"hbn\",\"hbo\",\"hbu\",\"hca\",\"hch\",\"hdn\",\"hds\",\"hdy\",\"hea\",\"hed\",\"heg\",\"heh\",\"hei\",\"hem\",\"hgm\",\"hgw\",\"hhi\",\"hhr\",\"hhy\",\"hia\",\"hib\",\"hid\",\"hif\",\"hig\",\"hih\",\"hii\",\"hij\",\"hik\",\"hil\",\"him\",\"hio\",\"hir\",\"hit\",\"hiw\",\"hix\",\"hji\",\"hka\",\"hke\",\"hkk\",\"hks\",\"hla\",\"hlb\",\"hld\",\"hle\",\"hlt\",\"hlu\",\"hma\",\"hmb\",\"hmc\",\"hmd\",\"hme\",\"hmf\",\"hmg\",\"hmh\",\"hmi\",\"hmj\",\"hmk\",\"hml\",\"hmm\",\"hmn\",\"hmp\",\"hmq\",\"hmr\",\"hms\",\"hmt\",\"hmu\",\"hmv\",\"hmw\",\"hmx\",\"hmy\",\"hmz\",\"hna\",\"hnd\",\"hne\",\"hnh\",\"hni\",\"hnj\",\"hnn\",\"hno\",\"hns\",\"hnu\",\"hoa\",\"hob\",\"hoc\",\"hod\",\"hoe\",\"hoh\",\"hoi\",\"hoj\",\"hok\",\"hol\",\"hom\",\"hoo\",\"hop\",\"hor\",\"hos\",\"hot\",\"hov\",\"how\",\"hoy\",\"hoz\",\"hpo\",\"hps\",\"hra\",\"hrc\",\"hre\",\"hrk\",\"hrm\",\"hro\",\"hrp\",\"hrr\",\"hrt\",\"hru\",\"hrw\",\"hrx\",\"hrz\",\"hsb\",\"hsh\",\"hsl\",\"hsn\",\"hss\",\"hti\",\"hto\",\"hts\",\"htu\",\"htx\",\"hub\",\"huc\",\"hud\",\"hue\",\"huf\",\"hug\",\"huh\",\"hui\",\"huj\",\"huk\",\"hul\",\"hum\",\"huo\",\"hup\",\"huq\",\"hur\",\"hus\",\"hut\",\"huu\",\"huv\",\"huw\",\"hux\",\"huy\",\"huz\",\"hvc\",\"hve\",\"hvk\",\"hvn\",\"hvv\",\"hwa\",\"hwc\",\"hwo\",\"hya\",\"hyx\",\"iai\",\"ian\",\"iap\",\"iar\",\"iba\",\"ibb\",\"ibd\",\"ibe\",\"ibg\",\"ibh\",\"ibi\",\"ibl\",\"ibm\",\"ibn\",\"ibr\",\"ibu\",\"iby\",\"ica\",\"ich\",\"icl\",\"icr\",\"ida\",\"idb\",\"idc\",\"idd\",\"ide\",\"idi\",\"idr\",\"ids\",\"idt\",\"idu\",\"ifa\",\"ifb\",\"ife\",\"iff\",\"ifk\",\"ifm\",\"ifu\",\"ify\",\"igb\",\"ige\",\"igg\",\"igl\",\"igm\",\"ign\",\"igo\",\"igs\",\"igw\",\"ihb\",\"ihi\",\"ihp\",\"ihw\",\"iin\",\"iir\",\"ijc\",\"ije\",\"ijj\",\"ijn\",\"ijo\",\"ijs\",\"ike\",\"iki\",\"ikk\",\"ikl\",\"iko\",\"ikp\",\"ikr\",\"iks\",\"ikt\",\"ikv\",\"ikw\",\"ikx\",\"ikz\",\"ila\",\"ilb\",\"ilg\",\"ili\",\"ilk\",\"ill\",\"ilm\",\"ilo\",\"ilp\",\"ils\",\"ilu\",\"ilv\",\"ilw\",\"ima\",\"ime\",\"imi\",\"iml\",\"imn\",\"imo\",\"imr\",\"ims\",\"imy\",\"inb\",\"inc\",\"ine\",\"ing\",\"inh\",\"inj\",\"inl\",\"inm\",\"inn\",\"ino\",\"inp\",\"ins\",\"int\",\"inz\",\"ior\",\"iou\",\"iow\",\"ipi\",\"ipo\",\"iqu\",\"iqw\",\"ira\",\"ire\",\"irh\",\"iri\",\"irk\",\"irn\",\"iro\",\"irr\",\"iru\",\"irx\",\"iry\",\"isa\",\"isc\",\"isd\",\"ise\",\"isg\",\"ish\",\"isi\",\"isk\",\"ism\",\"isn\",\"iso\",\"isr\",\"ist\",\"isu\",\"itb\",\"itc\",\"itd\",\"ite\",\"iti\",\"itk\",\"itl\",\"itm\",\"ito\",\"itr\",\"its\",\"itt\",\"itv\",\"itw\",\"itx\",\"ity\",\"itz\",\"ium\",\"ivb\",\"ivv\",\"iwk\",\"iwm\",\"iwo\",\"iws\",\"ixc\",\"ixl\",\"iya\",\"iyo\",\"iyx\",\"izh\",\"izi\",\"izr\",\"izz\",\"jaa\",\"jab\",\"jac\",\"jad\",\"jae\",\"jaf\",\"jah\",\"jaj\",\"jak\",\"jal\",\"jam\",\"jan\",\"jao\",\"jaq\",\"jar\",\"jas\",\"jat\",\"jau\",\"jax\",\"jay\",\"jaz\",\"jbe\",\"jbi\",\"jbj\",\"jbk\",\"jbn\",\"jbo\",\"jbr\",\"jbt\",\"jbu\",\"jbw\",\"jcs\",\"jct\",\"jda\",\"jdg\",\"jdt\",\"jeb\",\"jee\",\"jeg\",\"jeh\",\"jei\",\"jek\",\"jel\",\"jen\",\"jer\",\"jet\",\"jeu\",\"jgb\",\"jge\",\"jgk\",\"jgo\",\"jhi\",\"jhs\",\"jia\",\"jib\",\"jic\",\"jid\",\"jie\",\"jig\",\"jih\",\"jii\",\"jil\",\"jim\",\"jio\",\"jiq\",\"jit\",\"jiu\",\"jiv\",\"jiy\",\"jje\",\"jjr\",\"jka\",\"jkm\",\"jko\",\"jkp\",\"jkr\",\"jku\",\"jle\",\"jls\",\"jma\",\"jmb\",\"jmc\",\"jmd\",\"jmi\",\"jml\",\"jmn\",\"jmr\",\"jms\",\"jmw\",\"jmx\",\"jna\",\"jnd\",\"jng\",\"jni\",\"jnj\",\"jnl\",\"jns\",\"job\",\"jod\",\"jog\",\"jor\",\"jos\",\"jow\",\"jpa\",\"jpr\",\"jpx\",\"jqr\",\"jra\",\"jrb\",\"jrr\",\"jrt\",\"jru\",\"jsl\",\"jua\",\"jub\",\"juc\",\"jud\",\"juh\",\"jui\",\"juk\",\"jul\",\"jum\",\"jun\",\"juo\",\"jup\",\"jur\",\"jus\",\"jut\",\"juu\",\"juw\",\"juy\",\"jvd\",\"jvn\",\"jwi\",\"jya\",\"jye\",\"jyy\",\"kaa\",\"kab\",\"kac\",\"kad\",\"kae\",\"kaf\",\"kag\",\"kah\",\"kai\",\"kaj\",\"kak\",\"kam\",\"kao\",\"kap\",\"kaq\",\"kar\",\"kav\",\"kaw\",\"kax\",\"kay\",\"kba\",\"kbb\",\"kbc\",\"kbd\",\"kbe\",\"kbf\",\"kbg\",\"kbh\",\"kbi\",\"kbj\",\"kbk\",\"kbl\",\"kbm\",\"kbn\",\"kbo\",\"kbp\",\"kbq\",\"kbr\",\"kbs\",\"kbt\",\"kbu\",\"kbv\",\"kbw\",\"kbx\",\"kby\",\"kbz\",\"kca\",\"kcb\",\"kcc\",\"kcd\",\"kce\",\"kcf\",\"kcg\",\"kch\",\"kci\",\"kcj\",\"kck\",\"kcl\",\"kcm\",\"kcn\",\"kco\",\"kcp\",\"kcq\",\"kcr\",\"kcs\",\"kct\",\"kcu\",\"kcv\",\"kcw\",\"kcx\",\"kcy\",\"kcz\",\"kda\",\"kdc\",\"kdd\",\"kde\",\"kdf\",\"kdg\",\"kdh\",\"kdi\",\"kdj\",\"kdk\",\"kdl\",\"kdm\",\"kdn\",\"kdo\",\"kdp\",\"kdq\",\"kdr\",\"kdt\",\"kdu\",\"kdv\",\"kdw\",\"kdx\",\"kdy\",\"kdz\",\"kea\",\"keb\",\"kec\",\"ked\",\"kee\",\"kef\",\"keg\",\"keh\",\"kei\",\"kej\",\"kek\",\"kel\",\"kem\",\"ken\",\"keo\",\"kep\",\"keq\",\"ker\",\"kes\",\"ket\",\"keu\",\"kev\",\"kew\",\"kex\",\"key\",\"kez\",\"kfa\",\"kfb\",\"kfc\",\"kfd\",\"kfe\",\"kff\",\"kfg\",\"kfh\",\"kfi\",\"kfj\",\"kfk\",\"kfl\",\"kfm\",\"kfn\",\"kfo\",\"kfp\",\"kfq\",\"kfr\",\"kfs\",\"kft\",\"kfu\",\"kfv\",\"kfw\",\"kfx\",\"kfy\",\"kfz\",\"kga\",\"kgb\",\"kgc\",\"kgd\",\"kge\",\"kgf\",\"kgg\",\"kgh\",\"kgi\",\"kgj\",\"kgk\",\"kgl\",\"kgm\",\"kgn\",\"kgo\",\"kgp\",\"kgq\",\"kgr\",\"kgs\",\"kgt\",\"kgu\",\"kgv\",\"kgw\",\"kgx\",\"kgy\",\"kha\",\"khb\",\"khc\",\"khd\",\"khe\",\"khf\",\"khg\",\"khh\",\"khi\",\"khj\",\"khk\",\"khl\",\"khn\",\"kho\",\"khp\",\"khq\",\"khr\",\"khs\",\"kht\",\"khu\",\"khv\",\"khw\",\"khx\",\"khy\",\"khz\",\"kia\",\"kib\",\"kic\",\"kid\",\"kie\",\"kif\",\"kig\",\"kih\",\"kii\",\"kij\",\"kil\",\"kim\",\"kio\",\"kip\",\"kiq\",\"kis\",\"kit\",\"kiu\",\"kiv\",\"kiw\",\"kix\",\"kiy\",\"kiz\",\"kja\",\"kjb\",\"kjc\",\"kjd\",\"kje\",\"kjf\",\"kjg\",\"kjh\",\"kji\",\"kjj\",\"kjk\",\"kjl\",\"kjm\",\"kjn\",\"kjo\",\"kjp\",\"kjq\",\"kjr\",\"kjs\",\"kjt\",\"kju\",\"kjv\",\"kjx\",\"kjy\",\"kjz\",\"kka\",\"kkb\",\"kkc\",\"kkd\",\"kke\",\"kkf\",\"kkg\",\"kkh\",\"kki\",\"kkj\",\"kkk\",\"kkl\",\"kkm\",\"kkn\",\"kko\",\"kkp\",\"kkq\",\"kkr\",\"kks\",\"kkt\",\"kku\",\"kkv\",\"kkw\",\"kkx\",\"kky\",\"kkz\",\"kla\",\"klb\",\"klc\",\"kld\",\"kle\",\"klf\",\"klg\",\"klh\",\"kli\",\"klj\",\"klk\",\"kll\",\"klm\",\"kln\",\"klo\",\"klp\",\"klq\",\"klr\",\"kls\",\"klt\",\"klu\",\"klv\",\"klw\",\"klx\",\"kly\",\"klz\",\"kma\",\"kmb\",\"kmc\",\"kmd\",\"kme\",\"kmf\",\"kmg\",\"kmh\",\"kmi\",\"kmj\",\"kmk\",\"kml\",\"kmm\",\"kmn\",\"kmo\",\"kmp\",\"kmq\",\"kmr\",\"kms\",\"kmt\",\"kmu\",\"kmv\",\"kmw\",\"kmx\",\"kmy\",\"kmz\",\"kna\",\"knb\",\"knc\",\"knd\",\"kne\",\"knf\",\"kng\",\"kni\",\"knj\",\"knk\",\"knl\",\"knm\",\"knn\",\"kno\",\"knp\",\"knq\",\"knr\",\"kns\",\"knt\",\"knu\",\"knv\",\"knw\",\"knx\",\"kny\",\"knz\",\"koa\",\"koc\",\"kod\",\"koe\",\"kof\",\"kog\",\"koh\",\"koi\",\"koj\",\"kok\",\"kol\",\"koo\",\"kop\",\"koq\",\"kos\",\"kot\",\"kou\",\"kov\",\"kow\",\"kox\",\"koy\",\"koz\",\"kpa\",\"kpb\",\"kpc\",\"kpd\",\"kpe\",\"kpf\",\"kpg\",\"kph\",\"kpi\",\"kpj\",\"kpk\",\"kpl\",\"kpm\",\"kpn\",\"kpo\",\"kpp\",\"kpq\",\"kpr\",\"kps\",\"kpt\",\"kpu\",\"kpv\",\"kpw\",\"kpx\",\"kpy\",\"kpz\",\"kqa\",\"kqb\",\"kqc\",\"kqd\",\"kqe\",\"kqf\",\"kqg\",\"kqh\",\"kqi\",\"kqj\",\"kqk\",\"kql\",\"kqm\",\"kqn\",\"kqo\",\"kqp\",\"kqq\",\"kqr\",\"kqs\",\"kqt\",\"kqu\",\"kqv\",\"kqw\",\"kqx\",\"kqy\",\"kqz\",\"kra\",\"krb\",\"krc\",\"krd\",\"kre\",\"krf\",\"krh\",\"kri\",\"krj\",\"krk\",\"krl\",\"krm\",\"krn\",\"kro\",\"krp\",\"krr\",\"krs\",\"krt\",\"kru\",\"krv\",\"krw\",\"krx\",\"kry\",\"krz\",\"ksa\",\"ksb\",\"ksc\",\"ksd\",\"kse\",\"ksf\",\"ksg\",\"ksh\",\"ksi\",\"ksj\",\"ksk\",\"ksl\",\"ksm\",\"ksn\",\"kso\",\"ksp\",\"ksq\",\"ksr\",\"kss\",\"kst\",\"ksu\",\"ksv\",\"ksw\",\"ksx\",\"ksy\",\"ksz\",\"kta\",\"ktb\",\"ktc\",\"ktd\",\"kte\",\"ktf\",\"ktg\",\"kth\",\"kti\",\"ktj\",\"ktk\",\"ktl\",\"ktm\",\"ktn\",\"kto\",\"ktp\",\"ktq\",\"ktr\",\"kts\",\"ktt\",\"ktu\",\"ktv\",\"ktw\",\"ktx\",\"kty\",\"ktz\",\"kub\",\"kuc\",\"kud\",\"kue\",\"kuf\",\"kug\",\"kuh\",\"kui\",\"kuj\",\"kuk\",\"kul\",\"kum\",\"kun\",\"kuo\",\"kup\",\"kuq\",\"kus\",\"kut\",\"kuu\",\"kuv\",\"kuw\",\"kux\",\"kuy\",\"kuz\",\"kva\",\"kvb\",\"kvc\",\"kvd\",\"kve\",\"kvf\",\"kvg\",\"kvh\",\"kvi\",\"kvj\",\"kvk\",\"kvl\",\"kvm\",\"kvn\",\"kvo\",\"kvp\",\"kvq\",\"kvr\",\"kvs\",\"kvt\",\"kvu\",\"kvv\",\"kvw\",\"kvx\",\"kvy\",\"kvz\",\"kwa\",\"kwb\",\"kwc\",\"kwd\",\"kwe\",\"kwf\",\"kwg\",\"kwh\",\"kwi\",\"kwj\",\"kwk\",\"kwl\",\"kwm\",\"kwn\",\"kwo\",\"kwp\",\"kwq\",\"kwr\",\"kws\",\"kwt\",\"kwu\",\"kwv\",\"kww\",\"kwx\",\"kwy\",\"kwz\",\"kxa\",\"kxb\",\"kxc\",\"kxd\",\"kxe\",\"kxf\",\"kxh\",\"kxi\",\"kxj\",\"kxk\",\"kxl\",\"kxm\",\"kxn\",\"kxo\",\"kxp\",\"kxq\",\"kxr\",\"kxs\",\"kxt\",\"kxu\",\"kxv\",\"kxw\",\"kxx\",\"kxy\",\"kxz\",\"kya\",\"kyb\",\"kyc\",\"kyd\",\"kye\",\"kyf\",\"kyg\",\"kyh\",\"kyi\",\"kyj\",\"kyk\",\"kyl\",\"kym\",\"kyn\",\"kyo\",\"kyp\",\"kyq\",\"kyr\",\"kys\",\"kyt\",\"kyu\",\"kyv\",\"kyw\",\"kyx\",\"kyy\",\"kyz\",\"kza\",\"kzb\",\"kzc\",\"kzd\",\"kze\",\"kzf\",\"kzg\",\"kzh\",\"kzi\",\"kzj\",\"kzk\",\"kzl\",\"kzm\",\"kzn\",\"kzo\",\"kzp\",\"kzq\",\"kzr\",\"kzs\",\"kzt\",\"kzu\",\"kzv\",\"kzw\",\"kzx\",\"kzy\",\"kzz\",\"laa\",\"lab\",\"lac\",\"lad\",\"lae\",\"laf\",\"lag\",\"lah\",\"lai\",\"laj\",\"lak\",\"lal\",\"lam\",\"lan\",\"lap\",\"laq\",\"lar\",\"las\",\"lau\",\"law\",\"lax\",\"lay\",\"laz\",\"lba\",\"lbb\",\"lbc\",\"lbe\",\"lbf\",\"lbg\",\"lbi\",\"lbj\",\"lbk\",\"lbl\",\"lbm\",\"lbn\",\"lbo\",\"lbq\",\"lbr\",\"lbs\",\"lbt\",\"lbu\",\"lbv\",\"lbw\",\"lbx\",\"lby\",\"lbz\",\"lcc\",\"lcd\",\"lce\",\"lcf\",\"lch\",\"lcl\",\"lcm\",\"lcp\",\"lcq\",\"lcs\",\"lda\",\"ldb\",\"ldd\",\"ldg\",\"ldh\",\"ldi\",\"ldj\",\"ldk\",\"ldl\",\"ldm\",\"ldn\",\"ldo\",\"ldp\",\"ldq\",\"lea\",\"leb\",\"lec\",\"led\",\"lee\",\"lef\",\"leg\",\"leh\",\"lei\",\"lej\",\"lek\",\"lel\",\"lem\",\"len\",\"leo\",\"lep\",\"leq\",\"ler\",\"les\",\"let\",\"leu\",\"lev\",\"lew\",\"lex\",\"ley\",\"lez\",\"lfa\",\"lfn\",\"lga\",\"lgb\",\"lgg\",\"lgh\",\"lgi\",\"lgk\",\"lgl\",\"lgm\",\"lgn\",\"lgq\",\"lgr\",\"lgt\",\"lgu\",\"lgz\",\"lha\",\"lhh\",\"lhi\",\"lhl\",\"lhm\",\"lhn\",\"lhp\",\"lhs\",\"lht\",\"lhu\",\"lia\",\"lib\",\"lic\",\"lid\",\"lie\",\"lif\",\"lig\",\"lih\",\"lii\",\"lij\",\"lik\",\"lil\",\"lio\",\"lip\",\"liq\",\"lir\",\"lis\",\"liu\",\"liv\",\"liw\",\"lix\",\"liy\",\"liz\",\"lja\",\"lje\",\"lji\",\"ljl\",\"ljp\",\"ljw\",\"ljx\",\"lka\",\"lkb\",\"lkc\",\"lkd\",\"lke\",\"lkh\",\"lki\",\"lkj\",\"lkl\",\"lkm\",\"lkn\",\"lko\",\"lkr\",\"lks\",\"lkt\",\"lku\",\"lky\",\"lla\",\"llb\",\"llc\",\"lld\",\"lle\",\"llf\",\"llg\",\"llh\",\"lli\",\"llj\",\"llk\",\"lll\",\"llm\",\"lln\",\"llo\",\"llp\",\"llq\",\"lls\",\"llu\",\"llx\",\"lma\",\"lmb\",\"lmc\",\"lmd\",\"lme\",\"lmf\",\"lmg\",\"lmh\",\"lmi\",\"lmj\",\"lmk\",\"lml\",\"lmm\",\"lmn\",\"lmo\",\"lmp\",\"lmq\",\"lmr\",\"lmu\",\"lmv\",\"lmw\",\"lmx\",\"lmy\",\"lmz\",\"lna\",\"lnb\",\"lnd\",\"lng\",\"lnh\",\"lni\",\"lnj\",\"lnl\",\"lnm\",\"lnn\",\"lno\",\"lns\",\"lnu\",\"lnw\",\"lnz\",\"loa\",\"lob\",\"loc\",\"loe\",\"lof\",\"log\",\"loh\",\"loi\",\"loj\",\"lok\",\"lol\",\"lom\",\"lon\",\"loo\",\"lop\",\"loq\",\"lor\",\"los\",\"lot\",\"lou\",\"lov\",\"low\",\"lox\",\"loy\",\"loz\",\"lpa\",\"lpe\",\"lpn\",\"lpo\",\"lpx\",\"lra\",\"lrc\",\"lre\",\"lrg\",\"lri\",\"lrk\",\"lrl\",\"lrm\",\"lrn\",\"lro\",\"lrr\",\"lrt\",\"lrv\",\"lrz\",\"lsa\",\"lsd\",\"lse\",\"lsg\",\"lsh\",\"lsi\",\"lsl\",\"lsm\",\"lso\",\"lsp\",\"lsr\",\"lss\",\"lst\",\"lsy\",\"ltc\",\"ltg\",\"lth\",\"lti\",\"ltn\",\"lto\",\"lts\",\"ltu\",\"lua\",\"luc\",\"lud\",\"lue\",\"luf\",\"lui\",\"luj\",\"luk\",\"lul\",\"lum\",\"lun\",\"luo\",\"lup\",\"luq\",\"lur\",\"lus\",\"lut\",\"luu\",\"luv\",\"luw\",\"luy\",\"luz\",\"lva\",\"lvk\",\"lvs\",\"lvu\",\"lwa\",\"lwe\",\"lwg\",\"lwh\",\"lwl\",\"lwm\",\"lwo\",\"lwt\",\"lwu\",\"lww\",\"lya\",\"lyg\",\"lyn\",\"lzh\",\"lzl\",\"lzn\",\"lzz\",\"maa\",\"mab\",\"mad\",\"mae\",\"maf\",\"mag\",\"mai\",\"maj\",\"mak\",\"mam\",\"man\",\"map\",\"maq\",\"mas\",\"mat\",\"mau\",\"mav\",\"maw\",\"max\",\"maz\",\"mba\",\"mbb\",\"mbc\",\"mbd\",\"mbe\",\"mbf\",\"mbh\",\"mbi\",\"mbj\",\"mbk\",\"mbl\",\"mbm\",\"mbn\",\"mbo\",\"mbp\",\"mbq\",\"mbr\",\"mbs\",\"mbt\",\"mbu\",\"mbv\",\"mbw\",\"mbx\",\"mby\",\"mbz\",\"mca\",\"mcb\",\"mcc\",\"mcd\",\"mce\",\"mcf\",\"mcg\",\"mch\",\"mci\",\"mcj\",\"mck\",\"mcl\",\"mcm\",\"mcn\",\"mco\",\"mcp\",\"mcq\",\"mcr\",\"mcs\",\"mct\",\"mcu\",\"mcv\",\"mcw\",\"mcx\",\"mcy\",\"mcz\",\"mda\",\"mdb\",\"mdc\",\"mdd\",\"mde\",\"mdf\",\"mdg\",\"mdh\",\"mdi\",\"mdj\",\"mdk\",\"mdl\",\"mdm\",\"mdn\",\"mdp\",\"mdq\",\"mdr\",\"mds\",\"mdt\",\"mdu\",\"mdv\",\"mdw\",\"mdx\",\"mdy\",\"mdz\",\"mea\",\"meb\",\"mec\",\"med\",\"mee\",\"mef\",\"meg\",\"meh\",\"mei\",\"mej\",\"mek\",\"mel\",\"mem\",\"men\",\"meo\",\"mep\",\"meq\",\"mer\",\"mes\",\"met\",\"meu\",\"mev\",\"mew\",\"mey\",\"mez\",\"mfa\",\"mfb\",\"mfc\",\"mfd\",\"mfe\",\"mff\",\"mfg\",\"mfh\",\"mfi\",\"mfj\",\"mfk\",\"mfl\",\"mfm\",\"mfn\",\"mfo\",\"mfp\",\"mfq\",\"mfr\",\"mfs\",\"mft\",\"mfu\",\"mfv\",\"mfw\",\"mfx\",\"mfy\",\"mfz\",\"mga\",\"mgb\",\"mgc\",\"mgd\",\"mge\",\"mgf\",\"mgg\",\"mgh\",\"mgi\",\"mgj\",\"mgk\",\"mgl\",\"mgm\",\"mgn\",\"mgo\",\"mgp\",\"mgq\",\"mgr\",\"mgs\",\"mgt\",\"mgu\",\"mgv\",\"mgw\",\"mgx\",\"mgy\",\"mgz\",\"mha\",\"mhb\",\"mhc\",\"mhd\",\"mhe\",\"mhf\",\"mhg\",\"mhh\",\"mhi\",\"mhj\",\"mhk\",\"mhl\",\"mhm\",\"mhn\",\"mho\",\"mhp\",\"mhq\",\"mhr\",\"mhs\",\"mht\",\"mhu\",\"mhw\",\"mhx\",\"mhy\",\"mhz\",\"mia\",\"mib\",\"mic\",\"mid\",\"mie\",\"mif\",\"mig\",\"mih\",\"mii\",\"mij\",\"mik\",\"mil\",\"mim\",\"min\",\"mio\",\"mip\",\"miq\",\"mir\",\"mis\",\"mit\",\"miu\",\"miw\",\"mix\",\"miy\",\"miz\",\"mja\",\"mjb\",\"mjc\",\"mjd\",\"mje\",\"mjg\",\"mjh\",\"mji\",\"mjj\",\"mjk\",\"mjl\",\"mjm\",\"mjn\",\"mjo\",\"mjp\",\"mjq\",\"mjr\",\"mjs\",\"mjt\",\"mju\",\"mjv\",\"mjw\",\"mjx\",\"mjy\",\"mjz\",\"mka\",\"mkb\",\"mkc\",\"mke\",\"mkf\",\"mkg\",\"mkh\",\"mki\",\"mkj\",\"mkk\",\"mkl\",\"mkm\",\"mkn\",\"mko\",\"mkp\",\"mkq\",\"mkr\",\"mks\",\"mkt\",\"mku\",\"mkv\",\"mkw\",\"mkx\",\"mky\",\"mkz\",\"mla\",\"mlb\",\"mlc\",\"mld\",\"mle\",\"mlf\",\"mlh\",\"mli\",\"mlj\",\"mlk\",\"mll\",\"mlm\",\"mln\",\"mlo\",\"mlp\",\"mlq\",\"mlr\",\"mls\",\"mlu\",\"mlv\",\"mlw\",\"mlx\",\"mlz\",\"mma\",\"mmb\",\"mmc\",\"mmd\",\"mme\",\"mmf\",\"mmg\",\"mmh\",\"mmi\",\"mmj\",\"mmk\",\"mml\",\"mmm\",\"mmn\",\"mmo\",\"mmp\",\"mmq\",\"mmr\",\"mmt\",\"mmu\",\"mmv\",\"mmw\",\"mmx\",\"mmy\",\"mmz\",\"mna\",\"mnb\",\"mnc\",\"mnd\",\"mne\",\"mnf\",\"mng\",\"mnh\",\"mni\",\"mnj\",\"mnk\",\"mnl\",\"mnm\",\"mnn\",\"mno\",\"mnp\",\"mnq\",\"mnr\",\"mns\",\"mnt\",\"mnu\",\"mnv\",\"mnw\",\"mnx\",\"mny\",\"mnz\",\"moa\",\"moc\",\"mod\",\"moe\",\"mof\",\"mog\",\"moh\",\"moi\",\"moj\",\"mok\",\"mom\",\"moo\",\"mop\",\"moq\",\"mor\",\"mos\",\"mot\",\"mou\",\"mov\",\"mow\",\"mox\",\"moy\",\"moz\",\"mpa\",\"mpb\",\"mpc\",\"mpd\",\"mpe\",\"mpg\",\"mph\",\"mpi\",\"mpj\",\"mpk\",\"mpl\",\"mpm\",\"mpn\",\"mpo\",\"mpp\",\"mpq\",\"mpr\",\"mps\",\"mpt\",\"mpu\",\"mpv\",\"mpw\",\"mpx\",\"mpy\",\"mpz\",\"mqa\",\"mqb\",\"mqc\",\"mqe\",\"mqf\",\"mqg\",\"mqh\",\"mqi\",\"mqj\",\"mqk\",\"mql\",\"mqm\",\"mqn\",\"mqo\",\"mqp\",\"mqq\",\"mqr\",\"mqs\",\"mqt\",\"mqu\",\"mqv\",\"mqw\",\"mqx\",\"mqy\",\"mqz\",\"mra\",\"mrb\",\"mrc\",\"mrd\",\"mre\",\"mrf\",\"mrg\",\"mrh\",\"mrj\",\"mrk\",\"mrl\",\"mrm\",\"mrn\",\"mro\",\"mrp\",\"mrq\",\"mrr\",\"mrs\",\"mrt\",\"mru\",\"mrv\",\"mrw\",\"mrx\",\"mry\",\"mrz\",\"msb\",\"msc\",\"msd\",\"mse\",\"msf\",\"msg\",\"msh\",\"msi\",\"msj\",\"msk\",\"msl\",\"msm\",\"msn\",\"mso\",\"msp\",\"msq\",\"msr\",\"mss\",\"mst\",\"msu\",\"msv\",\"msw\",\"msx\",\"msy\",\"msz\",\"mta\",\"mtb\",\"mtc\",\"mtd\",\"mte\",\"mtf\",\"mtg\",\"mth\",\"mti\",\"mtj\",\"mtk\",\"mtl\",\"mtm\",\"mtn\",\"mto\",\"mtp\",\"mtq\",\"mtr\",\"mts\",\"mtt\",\"mtu\",\"mtv\",\"mtw\",\"mtx\",\"mty\",\"mua\",\"mub\",\"muc\",\"mud\",\"mue\",\"mug\",\"muh\",\"mui\",\"muj\",\"muk\",\"mul\",\"mum\",\"mun\",\"muo\",\"mup\",\"muq\",\"mur\",\"mus\",\"mut\",\"muu\",\"muv\",\"mux\",\"muy\",\"muz\",\"mva\",\"mvb\",\"mvd\",\"mve\",\"mvf\",\"mvg\",\"mvh\",\"mvi\",\"mvk\",\"mvl\",\"mvm\",\"mvn\",\"mvo\",\"mvp\",\"mvq\",\"mvr\",\"mvs\",\"mvt\",\"mvu\",\"mvv\",\"mvw\",\"mvx\",\"mvy\",\"mvz\",\"mwa\",\"mwb\",\"mwc\",\"mwd\",\"mwe\",\"mwf\",\"mwg\",\"mwh\",\"mwi\",\"mwj\",\"mwk\",\"mwl\",\"mwm\",\"mwn\",\"mwo\",\"mwp\",\"mwq\",\"mwr\",\"mws\",\"mwt\",\"mwu\",\"mwv\",\"mww\",\"mwx\",\"mwy\",\"mwz\",\"mxa\",\"mxb\",\"mxc\",\"mxd\",\"mxe\",\"mxf\",\"mxg\",\"mxh\",\"mxi\",\"mxj\",\"mxk\",\"mxl\",\"mxm\",\"mxn\",\"mxo\",\"mxp\",\"mxq\",\"mxr\",\"mxs\",\"mxt\",\"mxu\",\"mxv\",\"mxw\",\"mxx\",\"mxy\",\"mxz\",\"myb\",\"myc\",\"myd\",\"mye\",\"myf\",\"myg\",\"myh\",\"myi\",\"myj\",\"myk\",\"myl\",\"mym\",\"myn\",\"myo\",\"myp\",\"myq\",\"myr\",\"mys\",\"myt\",\"myu\",\"myv\",\"myw\",\"myx\",\"myy\",\"myz\",\"mza\",\"mzb\",\"mzc\",\"mzd\",\"mze\",\"mzg\",\"mzh\",\"mzi\",\"mzj\",\"mzk\",\"mzl\",\"mzm\",\"mzn\",\"mzo\",\"mzp\",\"mzq\",\"mzr\",\"mzs\",\"mzt\",\"mzu\",\"mzv\",\"mzw\",\"mzx\",\"mzy\",\"mzz\",\"naa\",\"nab\",\"nac\",\"nad\",\"nae\",\"naf\",\"nag\",\"nah\",\"nai\",\"naj\",\"nak\",\"nal\",\"nam\",\"nan\",\"nao\",\"nap\",\"naq\",\"nar\",\"nas\",\"nat\",\"naw\",\"nax\",\"nay\",\"naz\",\"nba\",\"nbb\",\"nbc\",\"nbd\",\"nbe\",\"nbf\",\"nbg\",\"nbh\",\"nbi\",\"nbj\",\"nbk\",\"nbm\",\"nbn\",\"nbo\",\"nbp\",\"nbq\",\"nbr\",\"nbs\",\"nbt\",\"nbu\",\"nbv\",\"nbw\",\"nbx\",\"nby\",\"nca\",\"ncb\",\"ncc\",\"ncd\",\"nce\",\"ncf\",\"ncg\",\"nch\",\"nci\",\"ncj\",\"nck\",\"ncl\",\"ncm\",\"ncn\",\"nco\",\"ncp\",\"ncq\",\"ncr\",\"ncs\",\"nct\",\"ncu\",\"ncx\",\"ncz\",\"nda\",\"ndb\",\"ndc\",\"ndd\",\"ndf\",\"ndg\",\"ndh\",\"ndi\",\"ndj\",\"ndk\",\"ndl\",\"ndm\",\"ndn\",\"ndp\",\"ndq\",\"ndr\",\"nds\",\"ndt\",\"ndu\",\"ndv\",\"ndw\",\"ndx\",\"ndy\",\"ndz\",\"nea\",\"neb\",\"nec\",\"ned\",\"nee\",\"nef\",\"neg\",\"neh\",\"nei\",\"nej\",\"nek\",\"nem\",\"nen\",\"neo\",\"neq\",\"ner\",\"nes\",\"net\",\"neu\",\"nev\",\"new\",\"nex\",\"ney\",\"nez\",\"nfa\",\"nfd\",\"nfl\",\"nfr\",\"nfu\",\"nga\",\"ngb\",\"ngc\",\"ngd\",\"nge\",\"ngf\",\"ngg\",\"ngh\",\"ngi\",\"ngj\",\"ngk\",\"ngl\",\"ngm\",\"ngn\",\"ngo\",\"ngp\",\"ngq\",\"ngr\",\"ngs\",\"ngt\",\"ngu\",\"ngv\",\"ngw\",\"ngx\",\"ngy\",\"ngz\",\"nha\",\"nhb\",\"nhc\",\"nhd\",\"nhe\",\"nhf\",\"nhg\",\"nhh\",\"nhi\",\"nhk\",\"nhm\",\"nhn\",\"nho\",\"nhp\",\"nhq\",\"nhr\",\"nht\",\"nhu\",\"nhv\",\"nhw\",\"nhx\",\"nhy\",\"nhz\",\"nia\",\"nib\",\"nic\",\"nid\",\"nie\",\"nif\",\"nig\",\"nih\",\"nii\",\"nij\",\"nik\",\"nil\",\"nim\",\"nin\",\"nio\",\"niq\",\"nir\",\"nis\",\"nit\",\"niu\",\"niv\",\"niw\",\"nix\",\"niy\",\"niz\",\"nja\",\"njb\",\"njd\",\"njh\",\"nji\",\"njj\",\"njl\",\"njm\",\"njn\",\"njo\",\"njr\",\"njs\",\"njt\",\"nju\",\"njx\",\"njy\",\"njz\",\"nka\",\"nkb\",\"nkc\",\"nkd\",\"nke\",\"nkf\",\"nkg\",\"nkh\",\"nki\",\"nkj\",\"nkk\",\"nkm\",\"nkn\",\"nko\",\"nkp\",\"nkq\",\"nkr\",\"nks\",\"nkt\",\"nku\",\"nkv\",\"nkw\",\"nkx\",\"nkz\",\"nla\",\"nlc\",\"nle\",\"nlg\",\"nli\",\"nlj\",\"nlk\",\"nll\",\"nln\",\"nlo\",\"nlq\",\"nlr\",\"nlu\",\"nlv\",\"nlw\",\"nlx\",\"nly\",\"nlz\",\"nma\",\"nmb\",\"nmc\",\"nmd\",\"nme\",\"nmf\",\"nmg\",\"nmh\",\"nmi\",\"nmj\",\"nmk\",\"nml\",\"nmm\",\"nmn\",\"nmo\",\"nmp\",\"nmq\",\"nmr\",\"nms\",\"nmt\",\"nmu\",\"nmv\",\"nmw\",\"nmx\",\"nmy\",\"nmz\",\"nna\",\"nnb\",\"nnc\",\"nnd\",\"nne\",\"nnf\",\"nng\",\"nnh\",\"nni\",\"nnj\",\"nnk\",\"nnl\",\"nnm\",\"nnn\",\"nnp\",\"nnq\",\"nnr\",\"nns\",\"nnt\",\"nnu\",\"nnv\",\"nnw\",\"nnx\",\"nny\",\"nnz\",\"noa\",\"noc\",\"nod\",\"noe\",\"nof\",\"nog\",\"noh\",\"noi\",\"noj\",\"nok\",\"nol\",\"nom\",\"non\",\"noo\",\"nop\",\"noq\",\"nos\",\"not\",\"nou\",\"nov\",\"now\",\"noy\",\"noz\",\"npa\",\"npb\",\"npg\",\"nph\",\"npi\",\"npl\",\"npn\",\"npo\",\"nps\",\"npu\",\"npx\",\"npy\",\"nqg\",\"nqk\",\"nql\",\"nqm\",\"nqn\",\"nqo\",\"nqq\",\"nqy\",\"nra\",\"nrb\",\"nrc\",\"nre\",\"nrf\",\"nrg\",\"nri\",\"nrk\",\"nrl\",\"nrm\",\"nrn\",\"nrp\",\"nrr\",\"nrt\",\"nru\",\"nrx\",\"nrz\",\"nsa\",\"nsc\",\"nsd\",\"nse\",\"nsf\",\"nsg\",\"nsh\",\"nsi\",\"nsk\",\"nsl\",\"nsm\",\"nsn\",\"nso\",\"nsp\",\"nsq\",\"nsr\",\"nss\",\"nst\",\"nsu\",\"nsv\",\"nsw\",\"nsx\",\"nsy\",\"nsz\",\"ntd\",\"nte\",\"ntg\",\"nti\",\"ntj\",\"ntk\",\"ntm\",\"nto\",\"ntp\",\"ntr\",\"nts\",\"ntu\",\"ntw\",\"ntx\",\"nty\",\"ntz\",\"nua\",\"nub\",\"nuc\",\"nud\",\"nue\",\"nuf\",\"nug\",\"nuh\",\"nui\",\"nuj\",\"nuk\",\"nul\",\"num\",\"nun\",\"nuo\",\"nup\",\"nuq\",\"nur\",\"nus\",\"nut\",\"nuu\",\"nuv\",\"nuw\",\"nux\",\"nuy\",\"nuz\",\"nvh\",\"nvm\",\"nvo\",\"nwa\",\"nwb\",\"nwc\",\"nwe\",\"nwg\",\"nwi\",\"nwm\",\"nwo\",\"nwr\",\"nwx\",\"nwy\",\"nxa\",\"nxd\",\"nxe\",\"nxg\",\"nxi\",\"nxk\",\"nxl\",\"nxm\",\"nxn\",\"nxo\",\"nxq\",\"nxr\",\"nxu\",\"nxx\",\"nyb\",\"nyc\",\"nyd\",\"nye\",\"nyf\",\"nyg\",\"nyh\",\"nyi\",\"nyj\",\"nyk\",\"nyl\",\"nym\",\"nyn\",\"nyo\",\"nyp\",\"nyq\",\"nyr\",\"nys\",\"nyt\",\"nyu\",\"nyv\",\"nyw\",\"nyx\",\"nyy\",\"nza\",\"nzb\",\"nzi\",\"nzk\",\"nzm\",\"nzs\",\"nzu\",\"nzy\",\"nzz\",\"oaa\",\"oac\",\"oar\",\"oav\",\"obi\",\"obk\",\"obl\",\"obm\",\"obo\",\"obr\",\"obt\",\"obu\",\"oca\",\"och\",\"oco\",\"ocu\",\"oda\",\"odk\",\"odt\",\"odu\",\"ofo\",\"ofs\",\"ofu\",\"ogb\",\"ogc\",\"oge\",\"ogg\",\"ogo\",\"ogu\",\"oht\",\"ohu\",\"oia\",\"oin\",\"ojb\",\"ojc\",\"ojg\",\"ojp\",\"ojs\",\"ojv\",\"ojw\",\"oka\",\"okb\",\"okd\",\"oke\",\"okg\",\"okh\",\"oki\",\"okj\",\"okk\",\"okl\",\"okm\",\"okn\",\"oko\",\"okr\",\"oks\",\"oku\",\"okv\",\"okx\",\"ola\",\"old\",\"ole\",\"olk\",\"olm\",\"olo\",\"olr\",\"olt\",\"olu\",\"oma\",\"omb\",\"omc\",\"ome\",\"omg\",\"omi\",\"omk\",\"oml\",\"omn\",\"omo\",\"omp\",\"omq\",\"omr\",\"omt\",\"omu\",\"omv\",\"omw\",\"omx\",\"ona\",\"onb\",\"one\",\"ong\",\"oni\",\"onj\",\"onk\",\"onn\",\"ono\",\"onp\",\"onr\",\"ons\",\"ont\",\"onu\",\"onw\",\"onx\",\"ood\",\"oog\",\"oon\",\"oor\",\"oos\",\"opa\",\"opk\",\"opm\",\"opo\",\"opt\",\"opy\",\"ora\",\"orc\",\"ore\",\"org\",\"orh\",\"orn\",\"oro\",\"orr\",\"ors\",\"ort\",\"oru\",\"orv\",\"orw\",\"orx\",\"ory\",\"orz\",\"osa\",\"osc\",\"osi\",\"oso\",\"osp\",\"ost\",\"osu\",\"osx\",\"ota\",\"otb\",\"otd\",\"ote\",\"oti\",\"otk\",\"otl\",\"otm\",\"otn\",\"oto\",\"otq\",\"otr\",\"ots\",\"ott\",\"otu\",\"otw\",\"otx\",\"oty\",\"otz\",\"oua\",\"oub\",\"oue\",\"oui\",\"oum\",\"oun\",\"ovd\",\"owi\",\"owl\",\"oyb\",\"oyd\",\"oym\",\"oyy\",\"ozm\",\"paa\",\"pab\",\"pac\",\"pad\",\"pae\",\"paf\",\"pag\",\"pah\",\"pai\",\"pak\",\"pal\",\"pam\",\"pao\",\"pap\",\"paq\",\"par\",\"pas\",\"pat\",\"pau\",\"pav\",\"paw\",\"pax\",\"pay\",\"paz\",\"pbb\",\"pbc\",\"pbe\",\"pbf\",\"pbg\",\"pbh\",\"pbi\",\"pbl\",\"pbn\",\"pbo\",\"pbp\",\"pbr\",\"pbs\",\"pbt\",\"pbu\",\"pbv\",\"pby\",\"pbz\",\"pca\",\"pcb\",\"pcc\",\"pcd\",\"pce\",\"pcf\",\"pcg\",\"pch\",\"pci\",\"pcj\",\"pck\",\"pcl\",\"pcm\",\"pcn\",\"pcp\",\"pcr\",\"pcw\",\"pda\",\"pdc\",\"pdi\",\"pdn\",\"pdo\",\"pdt\",\"pdu\",\"pea\",\"peb\",\"ped\",\"pee\",\"pef\",\"peg\",\"peh\",\"pei\",\"pej\",\"pek\",\"pel\",\"pem\",\"peo\",\"pep\",\"peq\",\"pes\",\"pev\",\"pex\",\"pey\",\"pez\",\"pfa\",\"pfe\",\"pfl\",\"pga\",\"pgd\",\"pgg\",\"pgi\",\"pgk\",\"pgl\",\"pgn\",\"pgs\",\"pgu\",\"pgy\",\"pgz\",\"pha\",\"phd\",\"phg\",\"phh\",\"phi\",\"phk\",\"phl\",\"phm\",\"phn\",\"pho\",\"phq\",\"phr\",\"pht\",\"phu\",\"phv\",\"phw\",\"pia\",\"pib\",\"pic\",\"pid\",\"pie\",\"pif\",\"pig\",\"pih\",\"pii\",\"pij\",\"pil\",\"pim\",\"pin\",\"pio\",\"pip\",\"pir\",\"pis\",\"pit\",\"piu\",\"piv\",\"piw\",\"pix\",\"piy\",\"piz\",\"pjt\",\"pka\",\"pkb\",\"pkc\",\"pkg\",\"pkh\",\"pkn\",\"pko\",\"pkp\",\"pkr\",\"pks\",\"pkt\",\"pku\",\"pla\",\"plb\",\"plc\",\"pld\",\"ple\",\"plf\",\"plg\",\"plh\",\"plj\",\"plk\",\"pll\",\"pln\",\"plo\",\"plp\",\"plq\",\"plr\",\"pls\",\"plt\",\"plu\",\"plv\",\"plw\",\"ply\",\"plz\",\"pma\",\"pmb\",\"pmc\",\"pmd\",\"pme\",\"pmf\",\"pmh\",\"pmi\",\"pmj\",\"pmk\",\"pml\",\"pmm\",\"pmn\",\"pmo\",\"pmq\",\"pmr\",\"pms\",\"pmt\",\"pmu\",\"pmw\",\"pmx\",\"pmy\",\"pmz\",\"pna\",\"pnb\",\"pnc\",\"pne\",\"png\",\"pnh\",\"pni\",\"pnj\",\"pnk\",\"pnl\",\"pnm\",\"pnn\",\"pno\",\"pnp\",\"pnq\",\"pnr\",\"pns\",\"pnt\",\"pnu\",\"pnv\",\"pnw\",\"pnx\",\"pny\",\"pnz\",\"poc\",\"pod\",\"poe\",\"pof\",\"pog\",\"poh\",\"poi\",\"pok\",\"pom\",\"pon\",\"poo\",\"pop\",\"poq\",\"pos\",\"pot\",\"pov\",\"pow\",\"pox\",\"poy\",\"poz\",\"ppa\",\"ppe\",\"ppi\",\"ppk\",\"ppl\",\"ppm\",\"ppn\",\"ppo\",\"ppp\",\"ppq\",\"ppr\",\"pps\",\"ppt\",\"ppu\",\"pqa\",\"pqe\",\"pqm\",\"pqw\",\"pra\",\"prb\",\"prc\",\"prd\",\"pre\",\"prf\",\"prg\",\"prh\",\"pri\",\"prk\",\"prl\",\"prm\",\"prn\",\"pro\",\"prp\",\"prq\",\"prr\",\"prs\",\"prt\",\"pru\",\"prw\",\"prx\",\"pry\",\"prz\",\"psa\",\"psc\",\"psd\",\"pse\",\"psg\",\"psh\",\"psi\",\"psl\",\"psm\",\"psn\",\"pso\",\"psp\",\"psq\",\"psr\",\"pss\",\"pst\",\"psu\",\"psw\",\"psy\",\"pta\",\"pth\",\"pti\",\"ptn\",\"pto\",\"ptp\",\"ptq\",\"ptr\",\"ptt\",\"ptu\",\"ptv\",\"ptw\",\"pty\",\"pua\",\"pub\",\"puc\",\"pud\",\"pue\",\"puf\",\"pug\",\"pui\",\"puj\",\"puk\",\"pum\",\"puo\",\"pup\",\"puq\",\"pur\",\"put\",\"puu\",\"puw\",\"pux\",\"puy\",\"puz\",\"pwa\",\"pwb\",\"pwg\",\"pwi\",\"pwm\",\"pwn\",\"pwo\",\"pwr\",\"pww\",\"pxm\",\"pye\",\"pym\",\"pyn\",\"pys\",\"pyu\",\"pyx\",\"pyy\",\"pzn\",\"qaa..qtz\",\"qua\",\"qub\",\"quc\",\"qud\",\"quf\",\"qug\",\"quh\",\"qui\",\"quk\",\"qul\",\"qum\",\"qun\",\"qup\",\"quq\",\"qur\",\"qus\",\"quv\",\"quw\",\"qux\",\"quy\",\"quz\",\"qva\",\"qvc\",\"qve\",\"qvh\",\"qvi\",\"qvj\",\"qvl\",\"qvm\",\"qvn\",\"qvo\",\"qvp\",\"qvs\",\"qvw\",\"qvy\",\"qvz\",\"qwa\",\"qwc\",\"qwe\",\"qwh\",\"qwm\",\"qws\",\"qwt\",\"qxa\",\"qxc\",\"qxh\",\"qxl\",\"qxn\",\"qxo\",\"qxp\",\"qxq\",\"qxr\",\"qxs\",\"qxt\",\"qxu\",\"qxw\",\"qya\",\"qyp\",\"raa\",\"rab\",\"rac\",\"rad\",\"raf\",\"rag\",\"rah\",\"rai\",\"raj\",\"rak\",\"ral\",\"ram\",\"ran\",\"rao\",\"rap\",\"raq\",\"rar\",\"ras\",\"rat\",\"rau\",\"rav\",\"raw\",\"rax\",\"ray\",\"raz\",\"rbb\",\"rbk\",\"rbl\",\"rbp\",\"rcf\",\"rdb\",\"rea\",\"reb\",\"ree\",\"reg\",\"rei\",\"rej\",\"rel\",\"rem\",\"ren\",\"rer\",\"res\",\"ret\",\"rey\",\"rga\",\"rge\",\"rgk\",\"rgn\",\"rgr\",\"rgs\",\"rgu\",\"rhg\",\"rhp\",\"ria\",\"rie\",\"rif\",\"ril\",\"rim\",\"rin\",\"rir\",\"rit\",\"riu\",\"rjg\",\"rji\",\"rjs\",\"rka\",\"rkb\",\"rkh\",\"rki\",\"rkm\",\"rkt\",\"rkw\",\"rma\",\"rmb\",\"rmc\",\"rmd\",\"rme\",\"rmf\",\"rmg\",\"rmh\",\"rmi\",\"rmk\",\"rml\",\"rmm\",\"rmn\",\"rmo\",\"rmp\",\"rmq\",\"rmr\",\"rms\",\"rmt\",\"rmu\",\"rmv\",\"rmw\",\"rmx\",\"rmy\",\"rmz\",\"rna\",\"rnd\",\"rng\",\"rnl\",\"rnn\",\"rnp\",\"rnr\",\"rnw\",\"roa\",\"rob\",\"roc\",\"rod\",\"roe\",\"rof\",\"rog\",\"rol\",\"rom\",\"roo\",\"rop\",\"ror\",\"rou\",\"row\",\"rpn\",\"rpt\",\"rri\",\"rro\",\"rrt\",\"rsb\",\"rsi\",\"rsl\",\"rsm\",\"rtc\",\"rth\",\"rtm\",\"rts\",\"rtw\",\"rub\",\"ruc\",\"rue\",\"ruf\",\"rug\",\"ruh\",\"rui\",\"ruk\",\"ruo\",\"rup\",\"ruq\",\"rut\",\"ruu\",\"ruy\",\"ruz\",\"rwa\",\"rwk\",\"rwm\",\"rwo\",\"rwr\",\"rxd\",\"rxw\",\"ryn\",\"rys\",\"ryu\",\"rzh\",\"saa\",\"sab\",\"sac\",\"sad\",\"sae\",\"saf\",\"sah\",\"sai\",\"saj\",\"sak\",\"sal\",\"sam\",\"sao\",\"sap\",\"saq\",\"sar\",\"sas\",\"sat\",\"sau\",\"sav\",\"saw\",\"sax\",\"say\",\"saz\",\"sba\",\"sbb\",\"sbc\",\"sbd\",\"sbe\",\"sbf\",\"sbg\",\"sbh\",\"sbi\",\"sbj\",\"sbk\",\"sbl\",\"sbm\",\"sbn\",\"sbo\",\"sbp\",\"sbq\",\"sbr\",\"sbs\",\"sbt\",\"sbu\",\"sbv\",\"sbw\",\"sbx\",\"sby\",\"sbz\",\"sca\",\"scb\",\"sce\",\"scf\",\"scg\",\"sch\",\"sci\",\"sck\",\"scl\",\"scn\",\"sco\",\"scp\",\"scq\",\"scs\",\"sct\",\"scu\",\"scv\",\"scw\",\"scx\",\"sda\",\"sdb\",\"sdc\",\"sde\",\"sdf\",\"sdg\",\"sdh\",\"sdj\",\"sdk\",\"sdl\",\"sdm\",\"sdn\",\"sdo\",\"sdp\",\"sdr\",\"sds\",\"sdt\",\"sdu\",\"sdv\",\"sdx\",\"sdz\",\"sea\",\"seb\",\"sec\",\"sed\",\"see\",\"sef\",\"seg\",\"seh\",\"sei\",\"sej\",\"sek\",\"sel\",\"sem\",\"sen\",\"seo\",\"sep\",\"seq\",\"ser\",\"ses\",\"set\",\"seu\",\"sev\",\"sew\",\"sey\",\"sez\",\"sfb\",\"sfe\",\"sfm\",\"sfs\",\"sfw\",\"sga\",\"sgb\",\"sgc\",\"sgd\",\"sge\",\"sgg\",\"sgh\",\"sgi\",\"sgj\",\"sgk\",\"sgl\",\"sgm\",\"sgn\",\"sgo\",\"sgp\",\"sgr\",\"sgs\",\"sgt\",\"sgu\",\"sgw\",\"sgx\",\"sgy\",\"sgz\",\"sha\",\"shb\",\"shc\",\"shd\",\"she\",\"shg\",\"shh\",\"shi\",\"shj\",\"shk\",\"shl\",\"shm\",\"shn\",\"sho\",\"shp\",\"shq\",\"shr\",\"shs\",\"sht\",\"shu\",\"shv\",\"shw\",\"shx\",\"shy\",\"shz\",\"sia\",\"sib\",\"sid\",\"sie\",\"sif\",\"sig\",\"sih\",\"sii\",\"sij\",\"sik\",\"sil\",\"sim\",\"sio\",\"sip\",\"siq\",\"sir\",\"sis\",\"sit\",\"siu\",\"siv\",\"siw\",\"six\",\"siy\",\"siz\",\"sja\",\"sjb\",\"sjd\",\"sje\",\"sjg\",\"sjk\",\"sjl\",\"sjm\",\"sjn\",\"sjo\",\"sjp\",\"sjr\",\"sjs\",\"sjt\",\"sju\",\"sjw\",\"ska\",\"skb\",\"skc\",\"skd\",\"ske\",\"skf\",\"skg\",\"skh\",\"ski\",\"skj\",\"skk\",\"skm\",\"skn\",\"sko\",\"skp\",\"skq\",\"skr\",\"sks\",\"skt\",\"sku\",\"skv\",\"skw\",\"skx\",\"sky\",\"skz\",\"sla\",\"slc\",\"sld\",\"sle\",\"slf\",\"slg\",\"slh\",\"sli\",\"slj\",\"sll\",\"slm\",\"sln\",\"slp\",\"slq\",\"slr\",\"sls\",\"slt\",\"slu\",\"slw\",\"slx\",\"sly\",\"slz\",\"sma\",\"smb\",\"smc\",\"smd\",\"smf\",\"smg\",\"smh\",\"smi\",\"smj\",\"smk\",\"sml\",\"smm\",\"smn\",\"smp\",\"smq\",\"smr\",\"sms\",\"smt\",\"smu\",\"smv\",\"smw\",\"smx\",\"smy\",\"smz\",\"snb\",\"snc\",\"sne\",\"snf\",\"sng\",\"snh\",\"sni\",\"snj\",\"snk\",\"snl\",\"snm\",\"snn\",\"sno\",\"snp\",\"snq\",\"snr\",\"sns\",\"snu\",\"snv\",\"snw\",\"snx\",\"sny\",\"snz\",\"soa\",\"sob\",\"soc\",\"sod\",\"soe\",\"sog\",\"soh\",\"soi\",\"soj\",\"sok\",\"sol\",\"son\",\"soo\",\"sop\",\"soq\",\"sor\",\"sos\",\"sou\",\"sov\",\"sow\",\"sox\",\"soy\",\"soz\",\"spb\",\"spc\",\"spd\",\"spe\",\"spg\",\"spi\",\"spk\",\"spl\",\"spm\",\"spn\",\"spo\",\"spp\",\"spq\",\"spr\",\"sps\",\"spt\",\"spu\",\"spv\",\"spx\",\"spy\",\"sqa\",\"sqh\",\"sqj\",\"sqk\",\"sqm\",\"sqn\",\"sqo\",\"sqq\",\"sqr\",\"sqs\",\"sqt\",\"squ\",\"sra\",\"srb\",\"src\",\"sre\",\"srf\",\"srg\",\"srh\",\"sri\",\"srk\",\"srl\",\"srm\",\"srn\",\"sro\",\"srq\",\"srr\",\"srs\",\"srt\",\"sru\",\"srv\",\"srw\",\"srx\",\"sry\",\"srz\",\"ssa\",\"ssb\",\"ssc\",\"ssd\",\"sse\",\"ssf\",\"ssg\",\"ssh\",\"ssi\",\"ssj\",\"ssk\",\"ssl\",\"ssm\",\"ssn\",\"sso\",\"ssp\",\"ssq\",\"ssr\",\"sss\",\"sst\",\"ssu\",\"ssv\",\"ssx\",\"ssy\",\"ssz\",\"sta\",\"stb\",\"std\",\"ste\",\"stf\",\"stg\",\"sth\",\"sti\",\"stj\",\"stk\",\"stl\",\"stm\",\"stn\",\"sto\",\"stp\",\"stq\",\"str\",\"sts\",\"stt\",\"stu\",\"stv\",\"stw\",\"sty\",\"sua\",\"sub\",\"suc\",\"sue\",\"sug\",\"sui\",\"suj\",\"suk\",\"sul\",\"sum\",\"suq\",\"sur\",\"sus\",\"sut\",\"suv\",\"suw\",\"sux\",\"suy\",\"suz\",\"sva\",\"svb\",\"svc\",\"sve\",\"svk\",\"svm\",\"svr\",\"svs\",\"svx\",\"swb\",\"swc\",\"swf\",\"swg\",\"swh\",\"swi\",\"swj\",\"swk\",\"swl\",\"swm\",\"swn\",\"swo\",\"swp\",\"swq\",\"swr\",\"sws\",\"swt\",\"swu\",\"swv\",\"sww\",\"swx\",\"swy\",\"sxb\",\"sxc\",\"sxe\",\"sxg\",\"sxk\",\"sxl\",\"sxm\",\"sxn\",\"sxo\",\"sxr\",\"sxs\",\"sxu\",\"sxw\",\"sya\",\"syb\",\"syc\",\"syd\",\"syi\",\"syk\",\"syl\",\"sym\",\"syn\",\"syo\",\"syr\",\"sys\",\"syw\",\"syx\",\"syy\",\"sza\",\"szb\",\"szc\",\"szd\",\"sze\",\"szg\",\"szl\",\"szn\",\"szp\",\"szs\",\"szv\",\"szw\",\"taa\",\"tab\",\"tac\",\"tad\",\"tae\",\"taf\",\"tag\",\"tai\",\"taj\",\"tak\",\"tal\",\"tan\",\"tao\",\"tap\",\"taq\",\"tar\",\"tas\",\"tau\",\"tav\",\"taw\",\"tax\",\"tay\",\"taz\",\"tba\",\"tbb\",\"tbc\",\"tbd\",\"tbe\",\"tbf\",\"tbg\",\"tbh\",\"tbi\",\"tbj\",\"tbk\",\"tbl\",\"tbm\",\"tbn\",\"tbo\",\"tbp\",\"tbq\",\"tbr\",\"tbs\",\"tbt\",\"tbu\",\"tbv\",\"tbw\",\"tbx\",\"tby\",\"tbz\",\"tca\",\"tcb\",\"tcc\",\"tcd\",\"tce\",\"tcf\",\"tcg\",\"tch\",\"tci\",\"tck\",\"tcl\",\"tcm\",\"tcn\",\"tco\",\"tcp\",\"tcq\",\"tcs\",\"tct\",\"tcu\",\"tcw\",\"tcx\",\"tcy\",\"tcz\",\"tda\",\"tdb\",\"tdc\",\"tdd\",\"tde\",\"tdf\",\"tdg\",\"tdh\",\"tdi\",\"tdj\",\"tdk\",\"tdl\",\"tdm\",\"tdn\",\"tdo\",\"tdq\",\"tdr\",\"tds\",\"tdt\",\"tdu\",\"tdv\",\"tdx\",\"tdy\",\"tea\",\"teb\",\"tec\",\"ted\",\"tee\",\"tef\",\"teg\",\"teh\",\"tei\",\"tek\",\"tem\",\"ten\",\"teo\",\"tep\",\"teq\",\"ter\",\"tes\",\"tet\",\"teu\",\"tev\",\"tew\",\"tex\",\"tey\",\"tfi\",\"tfn\",\"tfo\",\"tfr\",\"tft\",\"tga\",\"tgb\",\"tgc\",\"tgd\",\"tge\",\"tgf\",\"tgg\",\"tgh\",\"tgi\",\"tgj\",\"tgn\",\"tgo\",\"tgp\",\"tgq\",\"tgr\",\"tgs\",\"tgt\",\"tgu\",\"tgv\",\"tgw\",\"tgx\",\"tgy\",\"tgz\",\"thc\",\"thd\",\"the\",\"thf\",\"thh\",\"thi\",\"thk\",\"thl\",\"thm\",\"thn\",\"thp\",\"thq\",\"thr\",\"ths\",\"tht\",\"thu\",\"thv\",\"thw\",\"thx\",\"thy\",\"thz\",\"tia\",\"tic\",\"tid\",\"tie\",\"tif\",\"tig\",\"tih\",\"tii\",\"tij\",\"tik\",\"til\",\"tim\",\"tin\",\"tio\",\"tip\",\"tiq\",\"tis\",\"tit\",\"tiu\",\"tiv\",\"tiw\",\"tix\",\"tiy\",\"tiz\",\"tja\",\"tjg\",\"tji\",\"tjl\",\"tjm\",\"tjn\",\"tjo\",\"tjs\",\"tju\",\"tjw\",\"tka\",\"tkb\",\"tkd\",\"tke\",\"tkf\",\"tkg\",\"tkk\",\"tkl\",\"tkm\",\"tkn\",\"tkp\",\"tkq\",\"tkr\",\"tks\",\"tkt\",\"tku\",\"tkv\",\"tkw\",\"tkx\",\"tkz\",\"tla\",\"tlb\",\"tlc\",\"tld\",\"tlf\",\"tlg\",\"tlh\",\"tli\",\"tlj\",\"tlk\",\"tll\",\"tlm\",\"tln\",\"tlo\",\"tlp\",\"tlq\",\"tlr\",\"tls\",\"tlt\",\"tlu\",\"tlv\",\"tlw\",\"tlx\",\"tly\",\"tma\",\"tmb\",\"tmc\",\"tmd\",\"tme\",\"tmf\",\"tmg\",\"tmh\",\"tmi\",\"tmj\",\"tmk\",\"tml\",\"tmm\",\"tmn\",\"tmo\",\"tmp\",\"tmq\",\"tmr\",\"tms\",\"tmt\",\"tmu\",\"tmv\",\"tmw\",\"tmy\",\"tmz\",\"tna\",\"tnb\",\"tnc\",\"tnd\",\"tne\",\"tnf\",\"tng\",\"tnh\",\"tni\",\"tnk\",\"tnl\",\"tnm\",\"tnn\",\"tno\",\"tnp\",\"tnq\",\"tnr\",\"tns\",\"tnt\",\"tnu\",\"tnv\",\"tnw\",\"tnx\",\"tny\",\"tnz\",\"tob\",\"toc\",\"tod\",\"toe\",\"tof\",\"tog\",\"toh\",\"toi\",\"toj\",\"tol\",\"tom\",\"too\",\"top\",\"toq\",\"tor\",\"tos\",\"tou\",\"tov\",\"tow\",\"tox\",\"toy\",\"toz\",\"tpa\",\"tpc\",\"tpe\",\"tpf\",\"tpg\",\"tpi\",\"tpj\",\"tpk\",\"tpl\",\"tpm\",\"tpn\",\"tpo\",\"tpp\",\"tpq\",\"tpr\",\"tpt\",\"tpu\",\"tpv\",\"tpw\",\"tpx\",\"tpy\",\"tpz\",\"tqb\",\"tql\",\"tqm\",\"tqn\",\"tqo\",\"tqp\",\"tqq\",\"tqr\",\"tqt\",\"tqu\",\"tqw\",\"tra\",\"trb\",\"trc\",\"trd\",\"tre\",\"trf\",\"trg\",\"trh\",\"tri\",\"trj\",\"trk\",\"trl\",\"trm\",\"trn\",\"tro\",\"trp\",\"trq\",\"trr\",\"trs\",\"trt\",\"tru\",\"trv\",\"trw\",\"trx\",\"try\",\"trz\",\"tsa\",\"tsb\",\"tsc\",\"tsd\",\"tse\",\"tsf\",\"tsg\",\"tsh\",\"tsi\",\"tsj\",\"tsk\",\"tsl\",\"tsm\",\"tsp\",\"tsq\",\"tsr\",\"tss\",\"tst\",\"tsu\",\"tsv\",\"tsw\",\"tsx\",\"tsy\",\"tsz\",\"tta\",\"ttb\",\"ttc\",\"ttd\",\"tte\",\"ttf\",\"ttg\",\"tth\",\"tti\",\"ttj\",\"ttk\",\"ttl\",\"ttm\",\"ttn\",\"tto\",\"ttp\",\"ttq\",\"ttr\",\"tts\",\"ttt\",\"ttu\",\"ttv\",\"ttw\",\"tty\",\"ttz\",\"tua\",\"tub\",\"tuc\",\"tud\",\"tue\",\"tuf\",\"tug\",\"tuh\",\"tui\",\"tuj\",\"tul\",\"tum\",\"tun\",\"tuo\",\"tup\",\"tuq\",\"tus\",\"tut\",\"tuu\",\"tuv\",\"tuw\",\"tux\",\"tuy\",\"tuz\",\"tva\",\"tvd\",\"tve\",\"tvk\",\"tvl\",\"tvm\",\"tvn\",\"tvo\",\"tvs\",\"tvt\",\"tvu\",\"tvw\",\"tvy\",\"twa\",\"twb\",\"twc\",\"twd\",\"twe\",\"twf\",\"twg\",\"twh\",\"twl\",\"twm\",\"twn\",\"two\",\"twp\",\"twq\",\"twr\",\"twt\",\"twu\",\"tww\",\"twx\",\"twy\",\"txa\",\"txb\",\"txc\",\"txe\",\"txg\",\"txh\",\"txi\",\"txj\",\"txm\",\"txn\",\"txo\",\"txq\",\"txr\",\"txs\",\"txt\",\"txu\",\"txx\",\"txy\",\"tya\",\"tye\",\"tyh\",\"tyi\",\"tyj\",\"tyl\",\"tyn\",\"typ\",\"tyr\",\"tys\",\"tyt\",\"tyu\",\"tyv\",\"tyx\",\"tyz\",\"tza\",\"tzh\",\"tzj\",\"tzl\",\"tzm\",\"tzn\",\"tzo\",\"tzx\",\"uam\",\"uan\",\"uar\",\"uba\",\"ubi\",\"ubl\",\"ubr\",\"ubu\",\"uby\",\"uda\",\"ude\",\"udg\",\"udi\",\"udj\",\"udl\",\"udm\",\"udu\",\"ues\",\"ufi\",\"uga\",\"ugb\",\"uge\",\"ugn\",\"ugo\",\"ugy\",\"uha\",\"uhn\",\"uis\",\"uiv\",\"uji\",\"uka\",\"ukg\",\"ukh\",\"ukk\",\"ukl\",\"ukp\",\"ukq\",\"uks\",\"uku\",\"ukw\",\"uky\",\"ula\",\"ulb\",\"ulc\",\"ule\",\"ulf\",\"uli\",\"ulk\",\"ull\",\"ulm\",\"uln\",\"ulu\",\"ulw\",\"uma\",\"umb\",\"umc\",\"umd\",\"umg\",\"umi\",\"umm\",\"umn\",\"umo\",\"ump\",\"umr\",\"ums\",\"umu\",\"una\",\"und\",\"une\",\"ung\",\"unk\",\"unm\",\"unn\",\"unp\",\"unr\",\"unu\",\"unx\",\"unz\",\"uok\",\"upi\",\"upv\",\"ura\",\"urb\",\"urc\",\"ure\",\"urf\",\"urg\",\"urh\",\"uri\",\"urj\",\"urk\",\"url\",\"urm\",\"urn\",\"uro\",\"urp\",\"urr\",\"urt\",\"uru\",\"urv\",\"urw\",\"urx\",\"ury\",\"urz\",\"usa\",\"ush\",\"usi\",\"usk\",\"usp\",\"usu\",\"uta\",\"ute\",\"utp\",\"utr\",\"utu\",\"uum\",\"uun\",\"uur\",\"uuu\",\"uve\",\"uvh\",\"uvl\",\"uwa\",\"uya\",\"uzn\",\"uzs\",\"vaa\",\"vae\",\"vaf\",\"vag\",\"vah\",\"vai\",\"vaj\",\"val\",\"vam\",\"van\",\"vao\",\"vap\",\"var\",\"vas\",\"vau\",\"vav\",\"vay\",\"vbb\",\"vbk\",\"vec\",\"ved\",\"vel\",\"vem\",\"veo\",\"vep\",\"ver\",\"vgr\",\"vgt\",\"vic\",\"vid\",\"vif\",\"vig\",\"vil\",\"vin\",\"vis\",\"vit\",\"viv\",\"vka\",\"vki\",\"vkj\",\"vkk\",\"vkl\",\"vkm\",\"vko\",\"vkp\",\"vkt\",\"vku\",\"vlp\",\"vls\",\"vma\",\"vmb\",\"vmc\",\"vmd\",\"vme\",\"vmf\",\"vmg\",\"vmh\",\"vmi\",\"vmj\",\"vmk\",\"vml\",\"vmm\",\"vmp\",\"vmq\",\"vmr\",\"vms\",\"vmu\",\"vmv\",\"vmw\",\"vmx\",\"vmy\",\"vmz\",\"vnk\",\"vnm\",\"vnp\",\"vor\",\"vot\",\"vra\",\"vro\",\"vrs\",\"vrt\",\"vsi\",\"vsl\",\"vsv\",\"vto\",\"vum\",\"vun\",\"vut\",\"vwa\",\"waa\",\"wab\",\"wac\",\"wad\",\"wae\",\"waf\",\"wag\",\"wah\",\"wai\",\"waj\",\"wak\",\"wal\",\"wam\",\"wan\",\"wao\",\"wap\",\"waq\",\"war\",\"was\",\"wat\",\"wau\",\"wav\",\"waw\",\"wax\",\"way\",\"waz\",\"wba\",\"wbb\",\"wbe\",\"wbf\",\"wbh\",\"wbi\",\"wbj\",\"wbk\",\"wbl\",\"wbm\",\"wbp\",\"wbq\",\"wbr\",\"wbs\",\"wbt\",\"wbv\",\"wbw\",\"wca\",\"wci\",\"wdd\",\"wdg\",\"wdj\",\"wdk\",\"wdu\",\"wdy\",\"wea\",\"wec\",\"wed\",\"weg\",\"weh\",\"wei\",\"wem\",\"wen\",\"weo\",\"wep\",\"wer\",\"wes\",\"wet\",\"weu\",\"wew\",\"wfg\",\"wga\",\"wgb\",\"wgg\",\"wgi\",\"wgo\",\"wgu\",\"wgw\",\"wgy\",\"wha\",\"whg\",\"whk\",\"whu\",\"wib\",\"wic\",\"wie\",\"wif\",\"wig\",\"wih\",\"wii\",\"wij\",\"wik\",\"wil\",\"wim\",\"win\",\"wir\",\"wit\",\"wiu\",\"wiv\",\"wiw\",\"wiy\",\"wja\",\"wji\",\"wka\",\"wkb\",\"wkd\",\"wkl\",\"wku\",\"wkw\",\"wky\",\"wla\",\"wlc\",\"wle\",\"wlg\",\"wli\",\"wlk\",\"wll\",\"wlm\",\"wlo\",\"wlr\",\"wls\",\"wlu\",\"wlv\",\"wlw\",\"wlx\",\"wly\",\"wma\",\"wmb\",\"wmc\",\"wmd\",\"wme\",\"wmh\",\"wmi\",\"wmm\",\"wmn\",\"wmo\",\"wms\",\"wmt\",\"wmw\",\"wmx\",\"wnb\",\"wnc\",\"wnd\",\"wne\",\"wng\",\"wni\",\"wnk\",\"wnm\",\"wnn\",\"wno\",\"wnp\",\"wnu\",\"wnw\",\"wny\",\"woa\",\"wob\",\"woc\",\"wod\",\"woe\",\"wof\",\"wog\",\"woi\",\"wok\",\"wom\",\"won\",\"woo\",\"wor\",\"wos\",\"wow\",\"woy\",\"wpc\",\"wra\",\"wrb\",\"wrd\",\"wrg\",\"wrh\",\"wri\",\"wrk\",\"wrl\",\"wrm\",\"wrn\",\"wro\",\"wrp\",\"wrr\",\"wrs\",\"wru\",\"wrv\",\"wrw\",\"wrx\",\"wry\",\"wrz\",\"wsa\",\"wsg\",\"wsi\",\"wsk\",\"wsr\",\"wss\",\"wsu\",\"wsv\",\"wtf\",\"wth\",\"wti\",\"wtk\",\"wtm\",\"wtw\",\"wua\",\"wub\",\"wud\",\"wuh\",\"wul\",\"wum\",\"wun\",\"wur\",\"wut\",\"wuu\",\"wuv\",\"wux\",\"wuy\",\"wwa\",\"wwb\",\"wwo\",\"wwr\",\"www\",\"wxa\",\"wxw\",\"wya\",\"wyb\",\"wyi\",\"wym\",\"wyr\",\"wyy\",\"xaa\",\"xab\",\"xac\",\"xad\",\"xae\",\"xag\",\"xai\",\"xaj\",\"xak\",\"xal\",\"xam\",\"xan\",\"xao\",\"xap\",\"xaq\",\"xar\",\"xas\",\"xat\",\"xau\",\"xav\",\"xaw\",\"xay\",\"xba\",\"xbb\",\"xbc\",\"xbd\",\"xbe\",\"xbg\",\"xbi\",\"xbj\",\"xbm\",\"xbn\",\"xbo\",\"xbp\",\"xbr\",\"xbw\",\"xbx\",\"xby\",\"xcb\",\"xcc\",\"xce\",\"xcg\",\"xch\",\"xcl\",\"xcm\",\"xcn\",\"xco\",\"xcr\",\"xct\",\"xcu\",\"xcv\",\"xcw\",\"xcy\",\"xda\",\"xdc\",\"xdk\",\"xdm\",\"xdo\",\"xdy\",\"xeb\",\"xed\",\"xeg\",\"xel\",\"xem\",\"xep\",\"xer\",\"xes\",\"xet\",\"xeu\",\"xfa\",\"xga\",\"xgb\",\"xgd\",\"xgf\",\"xgg\",\"xgi\",\"xgl\",\"xgm\",\"xgn\",\"xgr\",\"xgu\",\"xgw\",\"xha\",\"xhc\",\"xhd\",\"xhe\",\"xhr\",\"xht\",\"xhu\",\"xhv\",\"xia\",\"xib\",\"xii\",\"xil\",\"xin\",\"xip\",\"xir\",\"xis\",\"xiv\",\"xiy\",\"xjb\",\"xjt\",\"xka\",\"xkb\",\"xkc\",\"xkd\",\"xke\",\"xkf\",\"xkg\",\"xkh\",\"xki\",\"xkj\",\"xkk\",\"xkl\",\"xkn\",\"xko\",\"xkp\",\"xkq\",\"xkr\",\"xks\",\"xkt\",\"xku\",\"xkv\",\"xkw\",\"xkx\",\"xky\",\"xkz\",\"xla\",\"xlb\",\"xlc\",\"xld\",\"xle\",\"xlg\",\"xli\",\"xln\",\"xlo\",\"xlp\",\"xls\",\"xlu\",\"xly\",\"xma\",\"xmb\",\"xmc\",\"xmd\",\"xme\",\"xmf\",\"xmg\",\"xmh\",\"xmj\",\"xmk\",\"xml\",\"xmm\",\"xmn\",\"xmo\",\"xmp\",\"xmq\",\"xmr\",\"xms\",\"xmt\",\"xmu\",\"xmv\",\"xmw\",\"xmx\",\"xmy\",\"xmz\",\"xna\",\"xnb\",\"xnd\",\"xng\",\"xnh\",\"xni\",\"xnk\",\"xnn\",\"xno\",\"xnr\",\"xns\",\"xnt\",\"xnu\",\"xny\",\"xnz\",\"xoc\",\"xod\",\"xog\",\"xoi\",\"xok\",\"xom\",\"xon\",\"xoo\",\"xop\",\"xor\",\"xow\",\"xpa\",\"xpc\",\"xpe\",\"xpg\",\"xpi\",\"xpj\",\"xpk\",\"xpm\",\"xpn\",\"xpo\",\"xpp\",\"xpq\",\"xpr\",\"xps\",\"xpt\",\"xpu\",\"xpy\",\"xqa\",\"xqt\",\"xra\",\"xrb\",\"xrd\",\"xre\",\"xrg\",\"xri\",\"xrm\",\"xrn\",\"xrq\",\"xrr\",\"xrt\",\"xru\",\"xrw\",\"xsa\",\"xsb\",\"xsc\",\"xsd\",\"xse\",\"xsh\",\"xsi\",\"xsj\",\"xsl\",\"xsm\",\"xsn\",\"xso\",\"xsp\",\"xsq\",\"xsr\",\"xss\",\"xsu\",\"xsv\",\"xsy\",\"xta\",\"xtb\",\"xtc\",\"xtd\",\"xte\",\"xtg\",\"xth\",\"xti\",\"xtj\",\"xtl\",\"xtm\",\"xtn\",\"xto\",\"xtp\",\"xtq\",\"xtr\",\"xts\",\"xtt\",\"xtu\",\"xtv\",\"xtw\",\"xty\",\"xtz\",\"xua\",\"xub\",\"xud\",\"xug\",\"xuj\",\"xul\",\"xum\",\"xun\",\"xuo\",\"xup\",\"xur\",\"xut\",\"xuu\",\"xve\",\"xvi\",\"xvn\",\"xvo\",\"xvs\",\"xwa\",\"xwc\",\"xwd\",\"xwe\",\"xwg\",\"xwj\",\"xwk\",\"xwl\",\"xwo\",\"xwr\",\"xwt\",\"xww\",\"xxb\",\"xxk\",\"xxm\",\"xxr\",\"xxt\",\"xya\",\"xyb\",\"xyj\",\"xyk\",\"xyl\",\"xyt\",\"xyy\",\"xzh\",\"xzm\",\"xzp\",\"yaa\",\"yab\",\"yac\",\"yad\",\"yae\",\"yaf\",\"yag\",\"yah\",\"yai\",\"yaj\",\"yak\",\"yal\",\"yam\",\"yan\",\"yao\",\"yap\",\"yaq\",\"yar\",\"yas\",\"yat\",\"yau\",\"yav\",\"yaw\",\"yax\",\"yay\",\"yaz\",\"yba\",\"ybb\",\"ybd\",\"ybe\",\"ybh\",\"ybi\",\"ybj\",\"ybk\",\"ybl\",\"ybm\",\"ybn\",\"ybo\",\"ybx\",\"yby\",\"ych\",\"ycl\",\"ycn\",\"ycp\",\"yda\",\"ydd\",\"yde\",\"ydg\",\"ydk\",\"yds\",\"yea\",\"yec\",\"yee\",\"yei\",\"yej\",\"yel\",\"yen\",\"yer\",\"yes\",\"yet\",\"yeu\",\"yev\",\"yey\",\"yga\",\"ygi\",\"ygl\",\"ygm\",\"ygp\",\"ygr\",\"ygs\",\"ygu\",\"ygw\",\"yha\",\"yhd\",\"yhl\",\"yhs\",\"yia\",\"yif\",\"yig\",\"yih\",\"yii\",\"yij\",\"yik\",\"yil\",\"yim\",\"yin\",\"yip\",\"yiq\",\"yir\",\"yis\",\"yit\",\"yiu\",\"yiv\",\"yix\",\"yiy\",\"yiz\",\"yka\",\"ykg\",\"yki\",\"ykk\",\"ykl\",\"ykm\",\"ykn\",\"yko\",\"ykr\",\"ykt\",\"yku\",\"yky\",\"yla\",\"ylb\",\"yle\",\"ylg\",\"yli\",\"yll\",\"ylm\",\"yln\",\"ylo\",\"ylr\",\"ylu\",\"yly\",\"yma\",\"ymb\",\"ymc\",\"ymd\",\"yme\",\"ymg\",\"ymh\",\"ymi\",\"ymk\",\"yml\",\"ymm\",\"ymn\",\"ymo\",\"ymp\",\"ymq\",\"ymr\",\"yms\",\"ymt\",\"ymx\",\"ymz\",\"yna\",\"ynd\",\"yne\",\"yng\",\"ynh\",\"ynk\",\"ynl\",\"ynn\",\"yno\",\"ynq\",\"yns\",\"ynu\",\"yob\",\"yog\",\"yoi\",\"yok\",\"yol\",\"yom\",\"yon\",\"yos\",\"yot\",\"yox\",\"yoy\",\"ypa\",\"ypb\",\"ypg\",\"yph\",\"ypk\",\"ypm\",\"ypn\",\"ypo\",\"ypp\",\"ypz\",\"yra\",\"yrb\",\"yre\",\"yri\",\"yrk\",\"yrl\",\"yrm\",\"yrn\",\"yro\",\"yrs\",\"yrw\",\"yry\",\"ysc\",\"ysd\",\"ysg\",\"ysl\",\"ysn\",\"yso\",\"ysp\",\"ysr\",\"yss\",\"ysy\",\"yta\",\"ytl\",\"ytp\",\"ytw\",\"yty\",\"yua\",\"yub\",\"yuc\",\"yud\",\"yue\",\"yuf\",\"yug\",\"yui\",\"yuj\",\"yuk\",\"yul\",\"yum\",\"yun\",\"yup\",\"yuq\",\"yur\",\"yut\",\"yuu\",\"yuw\",\"yux\",\"yuy\",\"yuz\",\"yva\",\"yvt\",\"ywa\",\"ywg\",\"ywl\",\"ywn\",\"ywq\",\"ywr\",\"ywt\",\"ywu\",\"yww\",\"yxa\",\"yxg\",\"yxl\",\"yxm\",\"yxu\",\"yxy\",\"yyr\",\"yyu\",\"yyz\",\"yzg\",\"yzk\",\"zaa\",\"zab\",\"zac\",\"zad\",\"zae\",\"zaf\",\"zag\",\"zah\",\"zai\",\"zaj\",\"zak\",\"zal\",\"zam\",\"zao\",\"zap\",\"zaq\",\"zar\",\"zas\",\"zat\",\"zau\",\"zav\",\"zaw\",\"zax\",\"zay\",\"zaz\",\"zbc\",\"zbe\",\"zbl\",\"zbt\",\"zbw\",\"zca\",\"zch\",\"zdj\",\"zea\",\"zeg\",\"zeh\",\"zen\",\"zga\",\"zgb\",\"zgh\",\"zgm\",\"zgn\",\"zgr\",\"zhb\",\"zhd\",\"zhi\",\"zhn\",\"zhw\",\"zhx\",\"zia\",\"zib\",\"zik\",\"zil\",\"zim\",\"zin\",\"zir\",\"ziw\",\"ziz\",\"zka\",\"zkb\",\"zkd\",\"zkg\",\"zkh\",\"zkk\",\"zkn\",\"zko\",\"zkp\",\"zkr\",\"zkt\",\"zku\",\"zkv\",\"zkz\",\"zle\",\"zlj\",\"zlm\",\"zln\",\"zlq\",\"zls\",\"zlw\",\"zma\",\"zmb\",\"zmc\",\"zmd\",\"zme\",\"zmf\",\"zmg\",\"zmh\",\"zmi\",\"zmj\",\"zmk\",\"zml\",\"zmm\",\"zmn\",\"zmo\",\"zmp\",\"zmq\",\"zmr\",\"zms\",\"zmt\",\"zmu\",\"zmv\",\"zmw\",\"zmx\",\"zmy\",\"zmz\",\"zna\",\"znd\",\"zne\",\"zng\",\"znk\",\"zns\",\"zoc\",\"zoh\",\"zom\",\"zoo\",\"zoq\",\"zor\",\"zos\",\"zpa\",\"zpb\",\"zpc\",\"zpd\",\"zpe\",\"zpf\",\"zpg\",\"zph\",\"zpi\",\"zpj\",\"zpk\",\"zpl\",\"zpm\",\"zpn\",\"zpo\",\"zpp\",\"zpq\",\"zpr\",\"zps\",\"zpt\",\"zpu\",\"zpv\",\"zpw\",\"zpx\",\"zpy\",\"zpz\",\"zqe\",\"zra\",\"zrg\",\"zrn\",\"zro\",\"zrp\",\"zrs\",\"zsa\",\"zsk\",\"zsl\",\"zsm\",\"zsr\",\"zsu\",\"zte\",\"ztg\",\"ztl\",\"ztm\",\"ztn\",\"ztp\",\"ztq\",\"zts\",\"ztt\",\"ztu\",\"ztx\",\"zty\",\"zua\",\"zuh\",\"zum\",\"zun\",\"zuy\",\"zwa\",\"zxx\",\"zyb\",\"zyg\",\"zyj\",\"zyn\",\"zyp\",\"zza\",\"zzj\"]\n;return axe.utils.validLangs=function(){\"use strict\";return N},commons}()})}(\"object\"==typeof window?window:this);";
+
+
+
 
 
 
 
 
 function runA11yChecks(){
+
 return window.axe.run(document,{
 elementRef:true,
 runOnly:{
@@ -9835,8 +9267,10 @@
 'blink':{enabled:false},
 'server-side-image-map':{enabled:false}}}).
 
+
 then(axeResult=>{
 
+
 axeResult.violations.forEach(v=>v.nodes.forEach(node=>{
 node.path=getNodePath(node.element);
 node.snippet=getOuterHTMLSnippet(node.element);
@@ -9852,12 +9286,18 @@
 
 
 
+
+
+
 function getNodePath(node){
+
 function getNodeIndex(node){
 let index=0;
-while(node=node.previousSibling){
+let prevNode;
+while(prevNode=node.previousSibling){
+node=prevNode;
 
-if(node.nodeType===Node.TEXT_NODE&&
+if(node.nodeType===Node.TEXT_NODE&&node.textContent&&
 node.textContent.trim().length===0)continue;
 index++;
 }
@@ -9879,10 +9319,11 @@
 
 
 
-function getOuterHTMLSnippet(node){
+function getOuterHTMLSnippet(el){
 const reOpeningTag=/^.*?>/;
-const match=node.outerHTML.match(reOpeningTag);
-return match&&match[0];
+const match=el.outerHTML.match(reOpeningTag);
+
+return match&&match[0]||'';
 }
 }
 
@@ -9891,8 +9332,8 @@
 
 
 
-afterPass(options){
-const driver=options.driver;
+afterPass(passContext){
+const driver=passContext.driver;
 const expression=`(function () {
       ${axeLibSource};
       return (${runA11yChecks.toString()}());
@@ -9912,7 +9353,7 @@
 
 module.exports=Accessibility;
 
-},{"./gatherer":16}],"./gatherers/cache-contents":[function(require,module,exports){
+},{"./gatherer":19}],"../gather/gatherers/cache-contents":[function(require,module,exports){
 
 
 
@@ -9926,6 +9367,9 @@
 
 
 
+
+
+
 function getCacheContents(){
 
 return caches.keys().
@@ -9934,6 +9378,7 @@
 then(cacheNames=>Promise.all(cacheNames.map(cacheName=>caches.open(cacheName)))).
 
 then(caches=>{
+
 const requests=[];
 
 
@@ -9954,23 +9399,22 @@
 
 
 
-afterPass(options){
-const driver=options.driver;
+async afterPass(passContext){
+const driver=passContext.driver;
 
-return driver.
-evaluateAsync(`(${getCacheContents.toString()}())`).
-then(returnedValue=>{
-if(!returnedValue||!Array.isArray(returnedValue)){
+
+const cacheUrls=await driver.evaluateAsync(`(${getCacheContents.toString()}())`);
+if(!cacheUrls||!Array.isArray(cacheUrls)){
 throw new Error('Unable to retrieve cache contents');
 }
-return returnedValue;
-});
+
+return cacheUrls;
 }}
 
 
 module.exports=CacheContents;
 
-},{"./gatherer":16}],"./gatherers/chrome-console-messages":[function(require,module,exports){
+},{"./gatherer":19}],"../gather/gatherers/chrome-console-messages":[function(require,module,exports){
 
 
 
@@ -9988,35 +9432,45 @@
 class ChromeConsoleMessages extends Gatherer{
 constructor(){
 super();
+
 this._logEntries=[];
 this._onConsoleEntryAdded=this.onConsoleEntry.bind(this);
 }
 
+
+
+
 onConsoleEntry(entry){
 this._logEntries.push(entry);
 }
 
-beforePass(options){
-const driver=options.driver;
+
+
+
+async beforePass(passContext){
+const driver=passContext.driver;
 driver.on('Log.entryAdded',this._onConsoleEntryAdded);
-return driver.sendCommand('Log.enable').
-then(()=>driver.sendCommand('Log.startViolationsReport',{
-config:[{name:'discouragedAPIUse',threshold:-1}]}));
+await driver.sendCommand('Log.enable');
+await driver.sendCommand('Log.startViolationsReport',{
+config:[{name:'discouragedAPIUse',threshold:-1}]});
 
 }
 
-afterPass(options){
-return Promise.resolve().
-then(_=>options.driver.sendCommand('Log.stopViolationsReport')).
-then(_=>options.driver.off('Log.entryAdded',this._onConsoleEntryAdded)).
-then(_=>options.driver.sendCommand('Log.disable')).
-then(_=>this._logEntries);
+
+
+
+
+async afterPass(passContext){
+await passContext.driver.sendCommand('Log.stopViolationsReport');
+await passContext.driver.off('Log.entryAdded',this._onConsoleEntryAdded);
+await passContext.driver.sendCommand('Log.disable');
+return this._logEntries;
 }}
 
 
 module.exports=ChromeConsoleMessages;
 
-},{"./gatherer":16}],"./gatherers/css-usage":[function(require,module,exports){
+},{"./gatherer":19}],"../gather/gatherers/css-usage":[function(require,module,exports){
 
 
 
@@ -10030,45 +9484,54 @@
 
 
 class CSSUsage extends Gatherer{
-afterPass(options){
-const driver=options.driver;
+
+
+
+
+async afterPass(passContext){
+const driver=passContext.driver;
+
 
 const stylesheets=[];
+
 const onStylesheetAdded=sheet=>stylesheets.push(sheet);
 driver.on('CSS.styleSheetAdded',onStylesheetAdded);
 
-return driver.
-sendCommand('DOM.enable').
-then(_=>driver.sendCommand('CSS.enable')).
-then(_=>driver.sendCommand('CSS.startRuleUsageTracking')).
-then(_=>driver.evaluateAsync('getComputedStyle(document.body)')).
-then(_=>{
+await driver.sendCommand('DOM.enable');
+await driver.sendCommand('CSS.enable');
+await driver.sendCommand('CSS.startRuleUsageTracking');
+await driver.evaluateAsync('getComputedStyle(document.body)');
 driver.off('CSS.styleSheetAdded',onStylesheetAdded);
+
+
 const promises=stylesheets.map(sheet=>{
 const styleSheetId=sheet.header.styleSheetId;
 return driver.sendCommand('CSS.getStyleSheetText',{styleSheetId}).then(content=>{
-sheet.content=content.text;
-});
-});
+return{
+header:sheet.header,
+content:content.text};
 
-return Promise.all(promises);
-}).
-then(_=>driver.sendCommand('CSS.stopRuleUsageTracking')).
-then(results=>{
-return driver.
-sendCommand('CSS.disable').
-then(_=>driver.sendCommand('DOM.disable')).
-then(_=>{
-const dedupedStylesheets=new Map(stylesheets.map(sheet=>[sheet.content,sheet]));
-return{rules:results.ruleUsage,stylesheets:Array.from(dedupedStylesheets.values())};
 });
 });
+const styleSheetInfo=await Promise.all(promises);
+
+const ruleUsageResponse=await driver.sendCommand('CSS.stopRuleUsageTracking');
+await driver.sendCommand('CSS.disable');
+await driver.sendCommand('DOM.disable');
+
+const dedupedStylesheets=new Map(styleSheetInfo.map(sheet=>{
+return[sheet.content,sheet];
+}));
+return{
+rules:ruleUsageResponse.ruleUsage,
+stylesheets:Array.from(dedupedStylesheets.values())};
+
 }}
 
 
 module.exports=CSSUsage;
 
-},{"./gatherer":16}],"./gatherers/dobetterweb/all-event-listeners":[function(require,module,exports){
+},{"./gatherer":19}],"../gather/gatherers/dobetterweb/all-event-listeners":[function(require,module,exports){
 
 
 
@@ -10082,19 +9545,28 @@
 'use strict';
 
 const Gatherer=require('../gatherer');
+const Driver=require('../../driver.js');
+const Element=require('../../../lib/element.js');
 
 class EventListeners extends Gatherer{
-listenForScriptParsedEvents(){
-this._listener=script=>{
-this._parsedScripts.set(script.scriptId,script);
+
+
+
+async listenForScriptParsedEvents(driver){
+
+const parsedScripts=new Map();
+
+const scriptListener=script=>{
+parsedScripts.set(script.scriptId,script);
 };
-this.driver.on('Debugger.scriptParsed',this._listener);
-return this.driver.sendCommand('Debugger.enable');
-}
 
-unlistenForScriptParsedEvents(){
-this.driver.off('Debugger.scriptParsed',this._listener);
-return this.driver.sendCommand('Debugger.disable');
+
+driver.on('Debugger.scriptParsed',scriptListener);
+await driver.sendCommand('Debugger.enable');
+await driver.sendCommand('Debugger.disable');
+driver.off('Debugger.scriptParsed',scriptListener);
+
+return parsedScripts;
 }
 
 
@@ -10103,27 +9575,33 @@
 
 
 
-_listEventListeners(nodeIdOrObject){
+
+_listEventListeners(driver,nodeIdOrObject){
 let promise;
 
 if(typeof nodeIdOrObject==='string'){
-promise=this.driver.sendCommand('Runtime.evaluate',{
+promise=driver.sendCommand('Runtime.evaluate',{
 expression:nodeIdOrObject,
-objectGroup:'event-listeners-gatherer'});
-
+objectGroup:'event-listeners-gatherer'}).
+then(result=>result.result);
 }else{
-promise=this.driver.sendCommand('DOM.resolveNode',{
+promise=driver.sendCommand('DOM.resolveNode',{
 nodeId:nodeIdOrObject,
-objectGroup:'event-listeners-gatherer'});
-
+objectGroup:'event-listeners-gatherer'}).
+then(result=>result.object);
 }
 
-return promise.then(result=>{
-const obj=result.object||result.result;
-return this.driver.sendCommand('DOMDebugger.getEventListeners',{
-objectId:obj.objectId}).
+return promise.then(obj=>{
+const objectId=obj.objectId;
+const description=obj.description;
+if(!objectId||!description){
+return{listeners:[],tagName:''};
+}
+
+return driver.sendCommand('DOMDebugger.getEventListeners',{
+objectId}).
 then(results=>{
-return{listeners:results.listeners,tagName:obj.description};
+return{listeners:results.listeners,tagName:description};
 });
 });
 }
@@ -10136,27 +9614,31 @@
 
 
 
-getEventListeners(nodeId){
+
+
+getEventListeners(driver,parsedScripts,nodeId){
+
 const matchedListeners=[];
 
-return this._listEventListeners(nodeId).then(results=>{
+return this._listEventListeners(driver,nodeId).then(results=>{
 results.listeners.forEach(listener=>{
 
 
-const script=this._parsedScripts.get(listener.scriptId);
+const script=parsedScripts.get(listener.scriptId);
 if(script){
 
 
 
-const combo=Object.assign(listener,script);
-combo.objectName=results.tagName;
+matchedListeners.push({
+url:script.url,
+type:listener.type,
+handler:listener.handler,
+objectName:results.tagName,
 
 
+line:listener.lineNumber+1,
+col:listener.columnNumber+1});
 
-combo.line=combo.lineNumber+1;
-combo.col=combo.columnNumber+1;
-
-matchedListeners.push(combo);
 }
 });
 
@@ -10170,37 +9652,35 @@
 
 
 
-collectListeners(nodes){
 
-return Promise.all(nodes.map(node=>{
-return this.getEventListeners(node.element?node.element.nodeId:node);
-})).then(nestedListeners=>[].concat(...nestedListeners));
+
+collectListeners(driver,parsedScripts,nodeIds){
+
+return Promise.all(nodeIds.map(node=>this.getEventListeners(driver,parsedScripts,node))).
+then(nestedListeners=>nestedListeners.reduce((prev,curr)=>prev.concat(curr)));
 }
 
 
 
 
 
-afterPass(options){
-this.driver=options.driver;
-this._parsedScripts=new Map();
-return options.driver.sendCommand('DOM.enable').
-then(()=>this.listenForScriptParsedEvents()).
-then(()=>this.unlistenForScriptParsedEvents()).
-then(()=>options.driver.getElementsInDocument()).
-then(nodes=>{
-nodes.push('document','window');
-return this.collectListeners(nodes);
-}).then(listeners=>{
-return options.driver.sendCommand('DOM.disable').
-then(()=>listeners);
-});
+async afterPass(passContext){
+const driver=passContext.driver;
+await passContext.driver.sendCommand('DOM.enable');
+const parsedScripts=await this.listenForScriptParsedEvents(driver);
+
+const elements=await passContext.driver.getElementsInDocument();
+const elementIds=[...elements.map(el=>el.getNodeId()),'document','window'];
+
+const listeners=await this.collectListeners(driver,parsedScripts,elementIds);
+await passContext.driver.sendCommand('DOM.disable');
+return listeners;
 }}
 
 
 module.exports=EventListeners;
 
-},{"../gatherer":16}],"./gatherers/dobetterweb/anchors-with-no-rel-noopener":[function(require,module,exports){
+},{"../../../lib/element.js":31,"../../driver.js":17,"../gatherer":19}],"../gather/gatherers/dobetterweb/anchors-with-no-rel-noopener":[function(require,module,exports){
 
 
 
@@ -10216,10 +9696,10 @@
 
 
 
-afterPass(options){
+afterPass(passContext){
 const expression=`(function() {
       ${DOMHelpers.getElementsInDocumentFnString}; // define function on page
-      const selector = 'a[target="_blank"]:not([rel~="noopener"])';
+      const selector = 'a[target="_blank"]:not([rel~="noopener"]):not([rel~="noreferrer"])';
       const elements = getElementsInDocument(selector);
       return elements.map(node => ({
         href: node.href,
@@ -10228,13 +9708,13 @@
       }));
     })()`;
 
-return options.driver.evaluateAsync(expression);
+return passContext.driver.evaluateAsync(expression);
 }}
 
 
 module.exports=AnchorsWithNoRelNoopener;
 
-},{"../../../lib/dom-helpers.js":25,"../gatherer":16}],"./gatherers/dobetterweb/appcache":[function(require,module,exports){
+},{"../../../lib/dom-helpers.js":30,"../gatherer":19}],"../gather/gatherers/dobetterweb/appcache":[function(require,module,exports){
 
 
 
@@ -10251,8 +9731,8 @@
 
 
 
-afterPass(options){
-const driver=options.driver;
+afterPass(passContext){
+const driver=passContext.driver;
 
 return driver.querySelector('html').
 then(node=>node&&node.getAttribute('manifest'));
@@ -10261,7 +9741,7 @@
 
 module.exports=AppCacheManifest;
 
-},{"../gatherer":16}],"./gatherers/dobetterweb/domstats":[function(require,module,exports){
+},{"../gatherer":19}],"../gather/gatherers/dobetterweb/domstats":[function(require,module,exports){
 
 
 
@@ -10285,6 +9765,18 @@
 
 
 
+function getOuterHTMLSnippet(element){
+const reOpeningTag=/^.*?>/;
+const match=element.outerHTML.match(reOpeningTag);
+return match&&match[0];
+}
+
+
+
+
+
+
+
 
 function createSelectorsLabel(element){
 let name=element.localName||'';
@@ -10353,6 +9845,10 @@
 let maxWidth=0;
 let parentWithMostChildren=null;
 
+
+
+
+
 const _calcDOMWidthAndHeight=function(element,depth=1){
 if(depth>maxDepth){
 deepestNode=element;
@@ -10381,11 +9877,13 @@
 return{
 depth:{
 max:result.maxDepth,
-pathToElement:elementPathInDOM(deepestNode)},
+pathToElement:elementPathInDOM(deepestNode),
+snippet:getOuterHTMLSnippet(deepestNode)},
 
 width:{
 max:result.maxWidth,
-pathToElement:elementPathInDOM(parentWithMostChildren)}};
+pathToElement:elementPathInDOM(parentWithMostChildren),
+snippet:getOuterHTMLSnippet(parentWithMostChildren)}};
 
 
 }
@@ -10395,24 +9893,25 @@
 
 
 
-afterPass(options){
+afterPass(passContext){
 const expression=`(function() {
+      ${getOuterHTMLSnippet.toString()};
       ${createSelectorsLabel.toString()};
       ${elementPathInDOM.toString()};
       return (${getDOMStats.toString()}(document.documentElement));
     })()`;
-return options.driver.sendCommand('DOM.enable').
-then(()=>options.driver.evaluateAsync(expression)).
-then(results=>options.driver.getElementsInDocument().then(allNodes=>{
+return passContext.driver.sendCommand('DOM.enable').
+then(()=>passContext.driver.evaluateAsync(expression,{useIsolation:true})).
+then(results=>passContext.driver.getElementsInDocument().then(allNodes=>{
 results.totalDOMNodes=allNodes.length;
-return options.driver.sendCommand('DOM.disable').then(()=>results);
+return passContext.driver.sendCommand('DOM.disable').then(()=>results);
 }));
 }}
 
 
 module.exports=DOMStats;
 
-},{"../gatherer":16}],"./gatherers/dobetterweb/js-libraries":[function(require,module,exports){
+},{"../gatherer":19}],"../gather/gatherers/dobetterweb/js-libraries":[function(require,module,exports){
 
 
 
@@ -10436,13 +9935,13 @@
 
 
 
-
-
 function detectLibraries(){
+
 const libraries=[];
 
 
 
+
 Object.entries(d41d8cd98f00b204e9800998ecf8427e_LibraryDetectorTests).forEach(([name,lib])=>{
 try{
 const result=lib.test(window);
@@ -10459,25 +9958,24 @@
 return libraries;
 }
 
-
 class JSLibraries extends Gatherer{
 
 
 
 
-afterPass(options){
+afterPass(passContext){
 const expression=`(function () {
       ${libDetectorSource};
       return (${detectLibraries.toString()}());
     })()`;
 
-return options.driver.evaluateAsync(expression);
+return passContext.driver.evaluateAsync(expression);
 }}
 
 
 module.exports=JSLibraries;
 
-},{"../gatherer":16}],"./gatherers/dobetterweb/optimized-images":[function(require,module,exports){
+},{"../gatherer":19}],"../gather/gatherers/dobetterweb/optimized-images":[function(require,module,exports){
 
 
 
@@ -10493,6 +9991,7 @@
 const Gatherer=require('../gatherer');
 const URL=require('../../../lib/url-shim');
 const Sentry=require('../../../lib/sentry');
+const Driver=require('../../driver.js');
 
 const JPEG_QUALITY=0.92;
 const WEBP_QUALITY=0.85;
@@ -10507,11 +10006,21 @@
 
 
 
+
+
 function getOptimizedNumBytes(url){
 return new Promise(function(resolve,reject){
 const img=new Image();
 const canvas=document.createElement('canvas');
 const context=canvas.getContext('2d');
+if(!context){
+return reject(new Error('unable to create canvas context'));
+}
+
+
+
+
+
 
 function getTypeStats(type,quality){
 const dataURI=canvas.toDataURL(type,quality);
@@ -10546,6 +10055,7 @@
 
 
 static filterImageRequests(pageUrl,networkRecords){
+
 const seenUrls=new Set();
 return networkRecords.reduce((prev,record)=>{
 if(seenUrls.has(record._url)||!record.finished){
@@ -10559,14 +10069,15 @@
 const isSameOrigin=URL.originsMatch(pageUrl,record._url);
 const isBase64DataUri=/^data:.{2,40}base64\s*,/.test(record._url);
 
-if(isOptimizableImage&&record._resourceSize>MINIMUM_IMAGE_SIZE){
+const actualResourceSize=Math.min(record._resourceSize||0,record._transferSize||0);
+if(isOptimizableImage&&actualResourceSize>MINIMUM_IMAGE_SIZE){
 prev.push({
 isSameOrigin,
 isBase64DataUri,
 requestId:record._requestId,
 url:record._url,
 mimeType:record._mimeType,
-resourceSize:record._resourceSize});
+resourceSize:actualResourceSize});
 
 }
 
@@ -10640,11 +10151,23 @@
 
 
 
-computeOptimizedImages(driver,imageRecords){
-return imageRecords.reduce((promise,record)=>{
-return promise.then(results=>{
-return this.calculateImageStats(driver,record).
-catch(err=>{
+async computeOptimizedImages(driver,imageRecords){
+
+const results=[];
+
+for(const record of imageRecords){
+try{
+const stats=await this.calculateImageStats(driver,record);
+if(stats===null){
+continue;
+}
+
+
+
+const image=Object.assign({failed:false},stats,record);
+results.push(image);
+}catch(err){
+
 
 
 Sentry.captureException(err,{
@@ -10652,30 +10175,28 @@
 extra:{imageUrl:URL.elideDataURI(record.url)},
 level:'warning'});
 
-return{failed:true,err};
-}).
-then(stats=>{
-if(!stats){
+
+
+
+const imageError=Object.assign({failed:true,errMsg:err.message},record);
+results.push(imageError);
+}
+}
+
 return results;
 }
 
-return results.concat(Object.assign(stats,record));
-});
-});
-},Promise.resolve([]));
-}
 
 
 
 
 
-
-afterPass(options,traceData){
-const networkRecords=traceData.networkRecords;
-const imageRecords=OptimizedImages.filterImageRequests(options.url,networkRecords);
+afterPass(passContext,loadData){
+const networkRecords=loadData.networkRecords;
+const imageRecords=OptimizedImages.filterImageRequests(passContext.url,networkRecords);
 
 return Promise.resolve().
-then(_=>this.computeOptimizedImages(options.driver,imageRecords)).
+then(_=>this.computeOptimizedImages(passContext.driver,imageRecords)).
 then(results=>{
 const successfulResults=results.filter(result=>!result.failed);
 if(results.length&&!successfulResults.length){
@@ -10689,7 +10210,7 @@
 
 module.exports=OptimizedImages;
 
-},{"../../../lib/sentry":33,"../../../lib/url-shim":41,"../gatherer":16}],"./gatherers/dobetterweb/password-inputs-with-prevented-paste":[function(require,module,exports){
+},{"../../../lib/sentry":39,"../../../lib/url-shim":"url","../../driver.js":17,"../gatherer":19}],"../gather/gatherers/dobetterweb/password-inputs-with-prevented-paste":[function(require,module,exports){
 
 
 
@@ -10703,15 +10224,19 @@
 
 
 
+
+
+
 function findPasswordInputsWithPreventedPaste(){
 
 
 
 
 
-function getOuterHTMLSnippet(node){
+function getOuterHTMLSnippet(element){
 const reOpeningTag=/^.*?>/;
-const match=node.outerHTML.match(reOpeningTag);
+const match=element.outerHTML.match(reOpeningTag);
+
 return match&&match[0];
 }
 
@@ -10731,9 +10256,8 @@
 
 
 
-afterPass(options){
-const driver=options.driver;
-return driver.evaluateAsync(
+afterPass(passContext){
+return passContext.driver.evaluateAsync(
 `(${findPasswordInputsWithPreventedPaste.toString()}())`);
 
 }}
@@ -10742,7 +10266,7 @@
 
 module.exports=PasswordInputsWithPreventedPaste;
 
-},{"../gatherer":16}],"./gatherers/dobetterweb/response-compression":[function(require,module,exports){
+},{"../gatherer":19}],"../gather/gatherers/dobetterweb/response-compression":[function(require,module,exports){
 (function(Buffer){
 
 
@@ -10759,6 +10283,7 @@
 const Gatherer=require('../gatherer');
 const gzip=require('zlib').gzip;
 
+const compressionHeaders=['content-encoding','x-original-content-encoding'];
 const compressionTypes=['gzip','br','deflate'];
 const binaryMimeTypes=['image','audio','video'];
 const CHROME_EXTENSION_PROTOCOL='chrome-extension:';
@@ -10769,23 +10294,25 @@
 
 
 static filterUnoptimizedResponses(networkRecords){
+
 const unoptimizedResponses=[];
 
 networkRecords.forEach(record=>{
-const mimeType=record.mimeType;
-const resourceType=record.resourceType();
+const mimeType=record._mimeType;
+const resourceType=record._resourceType;
+const resourceSize=record._resourceSize;
 
 const isBinaryResource=mimeType&&binaryMimeTypes.some(type=>mimeType.startsWith(type));
 const isTextBasedResource=!isBinaryResource&&resourceType&&resourceType.isTextType();
 const isChromeExtensionResource=record.url.startsWith(CHROME_EXTENSION_PROTOCOL);
 
-if(!isTextBasedResource||!record.resourceSize||!record.finished||
+if(!isTextBasedResource||!resourceSize||!record.finished||
 isChromeExtensionResource||!record.transferSize||record.statusCode===304){
 return;
 }
 
-const isContentEncoded=record.responseHeaders.find(header=>
-header.name.toLowerCase()==='content-encoding'&&
+const isContentEncoded=(record._responseHeaders||[]).find(header=>
+compressionHeaders.includes(header.name.toLowerCase())&&
 compressionTypes.includes(header.value));
 
 
@@ -10793,9 +10320,10 @@
 unoptimizedResponses.push({
 requestId:record.requestId,
 url:record.url,
-mimeType:record.mimeType,
+mimeType:mimeType,
 transferSize:record.transferSize,
-resourceSize:record.resourceSize});
+resourceSize:resourceSize,
+gzipSize:0});
 
 }
 });
@@ -10803,19 +10331,20 @@
 return unoptimizedResponses;
 }
 
-afterPass(options,traceData){
-const networkRecords=traceData.networkRecords;
+
+
+
+
+
+afterPass(passContext,loadData){
+const networkRecords=loadData.networkRecords;
 const textRecords=ResponseCompression.filterUnoptimizedResponses(networkRecords);
 
-const driver=options.driver;
+const driver=passContext.driver;
 return Promise.all(textRecords.map(record=>{
-const contentPromise=driver.getRequestContent(record.requestId);
-const timeoutPromise=new Promise(resolve=>setTimeout(resolve,3000));
-return Promise.race([contentPromise,timeoutPromise]).then(content=>{
+return driver.getRequestContent(record.requestId).then(content=>{
 
 if(!content){
-record.gzipSize=0;
-
 return record;
 }
 
@@ -10839,7 +10368,7 @@
 module.exports=ResponseCompression;
 
 }).call(this,require("buffer").Buffer);
-},{"../gatherer":16,"buffer":54,"zlib":51}],"./gatherers/dobetterweb/tags-blocking-first-paint":[function(require,module,exports){
+},{"../gatherer":19,"buffer":60,"zlib":57}],"../gather/gatherers/dobetterweb/tags-blocking-first-paint":[function(require,module,exports){
 
 
 
@@ -10862,6 +10391,27 @@
 'use strict';
 
 const Gatherer=require('../gatherer');
+const Driver=require('../../driver.js');
+
+
+
+
+function installMediaListener(){
+window.___linkMediaChanges=[];
+Object.defineProperty(HTMLLinkElement.prototype,'media',{
+set:function(val){
+window.___linkMediaChanges.push({
+href:this.href,
+media:val,
+msSinceHTMLEnd:Date.now()-window.performance.timing.responseEnd,
+matches:window.matchMedia(val).matches});
+
+
+return this.setAttribute('media',val);
+}});
+
+}
+
 
 
 
@@ -10872,19 +10422,25 @@
 const tagList=[...document.querySelectorAll('link, head script[src]')].
 filter(tag=>{
 if(tag.tagName==='SCRIPT'){
-return!tag.hasAttribute('async')&&
-!tag.hasAttribute('defer')&&
-!/^data:/.test(tag.src)&&
-tag.getAttribute('type')!=='module';
+const scriptTag=tag;
+return(
+!scriptTag.hasAttribute('async')&&
+!scriptTag.hasAttribute('defer')&&
+!/^data:/.test(scriptTag.src)&&
+scriptTag.getAttribute('type')!=='module');
+
+}else if(tag.tagName==='LINK'){
+
+
+
+const linkTag=tag;
+const blockingStylesheet=linkTag.rel==='stylesheet'&&
+window.matchMedia(linkTag.media).matches&&!linkTag.disabled;
+const blockingImport=linkTag.rel==='import'&&!linkTag.hasAttribute('async');
+return blockingStylesheet||blockingImport;
 }
 
-
-
-
-const blockingStylesheet=tag.rel==='stylesheet'&&
-window.matchMedia(tag.media).matches&&!tag.disabled;
-const blockingImport=tag.rel==='import'&&!tag.hasAttribute('async');
-return blockingStylesheet||blockingImport;
+return false;
 }).
 map(tag=>{
 return{
@@ -10894,7 +10450,8 @@
 href:tag.href,
 rel:tag.rel,
 media:tag.media,
-disabled:tag.disabled};
+disabled:tag.disabled,
+mediaChanges:window.___linkMediaChanges.filter(item=>item.href===tag.href)};
 
 });
 resolve(tagList);
@@ -10905,7 +10462,14 @@
 });
 }
 
-function filteredAndIndexedByUrl(networkRecords){
+class TagsBlockingFirstPaint extends Gatherer{
+
+
+
+static _filteredAndIndexedByUrl(networkRecords){
+
+const result={};
+
 return networkRecords.reduce((prev,record)=>{
 if(!record.finished){
 return prev;
@@ -10930,28 +10494,42 @@
 }
 
 return prev;
-},{});
+},result);
 }
 
-class TagsBlockingFirstPaint extends Gatherer{
-constructor(){
-super();
-this._filteredAndIndexedByUrl=filteredAndIndexedByUrl;
-}
+
+
+
 
 static findBlockingTags(driver,networkRecords){
 const scriptSrc=`(${collectTagsThatBlockFirstPaint.toString()}())`;
+const firstRequestEndTime=networkRecords.reduce(
+(min,record)=>Math.min(min,record._endTime),
+Infinity);
+
 return driver.evaluateAsync(scriptSrc).then(tags=>{
-const requests=filteredAndIndexedByUrl(networkRecords);
+const requests=TagsBlockingFirstPaint._filteredAndIndexedByUrl(networkRecords);
 
 return tags.reduce((prev,tag)=>{
 const request=requests[tag.url];
 if(request&&!request.isLinkPreload){
+
+
+
+const timesResourceBecameNonBlocking=(tag.mediaChanges||[]).
+filter(change=>!change.matches).
+map(change=>change.msSinceHTMLEnd);
+const earliestNonBlockingTime=Math.min(...timesResourceBecameNonBlocking);
+const lastTimeResourceWasBlocking=Math.max(
+request.startTime,
+firstRequestEndTime+earliestNonBlockingTime/1000);
+
+
 prev.push({
 tag,
 transferSize:request.transferSize||0,
 startTime:request.startTime,
-endTime:request.endTime});
+endTime:Math.min(request.endTime,lastTimeResourceWasBlocking)});
 
 
 
@@ -10966,16 +10544,23 @@
 
 
 
+beforePass(passContext){
+return passContext.driver.evaluteScriptOnNewDocument(`(${installMediaListener.toString()})()`);
+}
 
 
-afterPass(options,tracingData){
-return TagsBlockingFirstPaint.findBlockingTags(options.driver,tracingData.networkRecords);
+
+
+
+
+afterPass(passContext,loadData){
+return TagsBlockingFirstPaint.findBlockingTags(passContext.driver,loadData.networkRecords);
 }}
 
 
 module.exports=TagsBlockingFirstPaint;
 
-},{"../gatherer":16}],"./gatherers/dobetterweb/websql":[function(require,module,exports){
+},{"../../driver.js":17,"../gatherer":19}],"../gather/gatherers/dobetterweb/websql":[function(require,module,exports){
 
 
 
@@ -10984,11 +10569,17 @@
 'use strict';
 
 const Gatherer=require('../gatherer');
+const Driver=require('../../driver.js');
 
 const MAX_WAIT_TIMEOUT=500;
 
 class WebSQL extends Gatherer{
+
+
+
+
 listenForDatabaseEvents(driver){
+
 let timeout;
 
 return new Promise((resolve,reject)=>{
@@ -11013,8 +10604,8 @@
 
 
 
-afterPass(options){
-return this.listenForDatabaseEvents(options.driver).
+afterPass(passContext){
+return this.listenForDatabaseEvents(passContext.driver).
 then(result=>{
 return result&&result.database;
 });
@@ -11023,7 +10614,9 @@
 
 module.exports=WebSQL;
 
-},{"../gatherer":16}],"./gatherers/fonts":[function(require,module,exports){
+},{"../../driver.js":17,"../gatherer":19}],"../gather/gatherers/fonts":[function(require,module,exports){
+
+
 
 
 
@@ -11033,6 +10626,13 @@
 
 const Gatherer=require('./gatherer');
 const Sentry=require('../../lib/sentry');
+
+
+
+
+
+
+
 const fontFaceDescriptors=[
 'display',
 'family',
@@ -11052,8 +10652,12 @@
 
 
 function getAllLoadedFonts(descriptors){
+
 const getFont=fontFace=>{
-const fontRule={};
+
+const fontRule={
+src:[]};
+
 descriptors.forEach(descriptor=>{
 fontRule[descriptor]=fontFace[descriptor];
 });
@@ -11081,27 +10685,38 @@
 function getSheetsFontFaces(stylesheet){
 const fontUrlRegex='url\\((?:")([^"]+)(?:"|\')\\)';
 const fontFaceRules=[];
+
 if(stylesheet.cssRules){
-for(const rule of stylesheet.cssRules){
+for(const rule of Array.from(stylesheet.cssRules)){
 if(rule instanceof CSSFontFaceRule){
 const fontsObject={
+
+
 display:rule.style.fontDisplay||'auto',
-family:rule.style.fontFamily.replace(/"|'/g,''),
+family:rule.style.fontFamily?rule.style.fontFamily.replace(/"|'/g,''):'',
 stretch:rule.style.fontStretch||'normal',
 style:rule.style.fontStyle||'normal',
 weight:rule.style.fontWeight||'normal',
 variant:rule.style.fontVariant||'normal',
+
 unicodeRange:rule.style.unicodeRange||'U+0-10FFFF',
+
 featureSettings:rule.style.featureSettings||'normal',
+
 src:[]};
 
 
-if(rule.style.src){
-const matches=rule.style.src.match(new RegExp(fontUrlRegex,'g'));
+
+
+const src=rule.style.src;
+if(src){
+const matches=src.match(new RegExp(fontUrlRegex,'g'));
 if(matches){
-fontsObject.src=matches.map(match=>{
+matches.forEach(match=>{
 const res=new RegExp(fontUrlRegex).exec(match);
-return new URL(res[1],location.href).href;
+if(res){
+fontsObject.src.push(new URL(res[1],location.href).href);
+}
 });
 }
 }
@@ -11129,32 +10744,43 @@
 resolve(getFontFaceFromStylesheets());
 });
 newNode.crossOrigin='anonymous';
-oldNode.parentNode.insertBefore(newNode,oldNode);
+oldNode.parentNode&&oldNode.parentNode.insertBefore(newNode,oldNode);
 oldNode.remove();
 });
 }
 
-const promises=[];
 
-for(const stylesheet of document.styleSheets){
+const data=[];
+
+const corsDataPromises=[];
+
+for(const stylesheet of Array.from(document.styleSheets)){
 try{
+const cssStylesheet=stylesheet;
 
-if(stylesheet.cssRules===null&&stylesheet.href&&stylesheet.ownerNode&&
-!stylesheet.ownerNode.crossOrigin){
-promises.push(loadStylesheetWithCORS(stylesheet.ownerNode));
+if(cssStylesheet.cssRules===null&&cssStylesheet.href&&cssStylesheet.ownerNode&&
+
+!cssStylesheet.ownerNode.crossOrigin){
+const ownerLinkEl=cssStylesheet.ownerNode;
+corsDataPromises.push(loadStylesheetWithCORS(ownerLinkEl));
 }else{
-promises.push(Promise.resolve(getSheetsFontFaces(stylesheet)));
+data.push(...getSheetsFontFaces(cssStylesheet));
 }
 }catch(err){
-promises.push({err:{message:err.message,stack:err.stack}});
+data.push({err:{message:err.message,stack:err.stack}});
 }
 }
 
-return Promise.all(promises).then(fontFaces=>[].concat(...fontFaces));
+return Promise.all(corsDataPromises).then(corsFontFaces=>data.concat(...corsFontFaces));
 }
 
 
 class Fonts extends Gatherer{
+
+
+
+
+
 _findSameFontFamily(fontFace,fontFacesList){
 return fontFacesList.find(fontItem=>{
 return!fontFaceDescriptors.find(descriptor=>{
@@ -11163,34 +10789,49 @@
 });
 }
 
-afterPass({driver}){
+
+
+
+
+afterPass(passContext){
+const driver=passContext.driver;
 const args=JSON.stringify(fontFaceDescriptors);
-return Promise.all(
+
+const fontData=Promise.all(
 [
 driver.evaluateAsync(`(${getAllLoadedFonts.toString()})(${args})`),
-driver.evaluateAsync(`(${getFontFaceFromStylesheets.toString()})()`)]).
+driver.evaluateAsync(`(${getFontFaceFromStylesheets.toString()})()`)]);
 
-then(([loadedFonts,fontFaces])=>{
-return loadedFonts.map(fontFace=>{
-if(fontFace.err){
-const err=new Error(fontFace.err.message);
-err.stack=fontFace.err.stack;
+
+return fontData.then(([loadedFonts,fontsAndErrors])=>{
+
+const fontFaces=fontsAndErrors.filter(
+fontOrError=>{
+
+const dataError=fontOrError;
+if(dataError.err){
+const err=new Error(dataError.err.message);
+err.stack=dataError.err.stack;
+
 Sentry.captureException(err,{tags:{gatherer:'Fonts'},level:'warning'});
-return null;
+return false;
 }
+return true;
+});
 
-const fontFaceItem=this._findSameFontFamily(fontFace,fontFaces);
-fontFace.src=fontFaceItem&&fontFaceItem.src||[];
+return loadedFonts.map(loadedFont=>{
+const fontFaceItem=this._findSameFontFamily(loadedFont,fontFaces);
+loadedFont.src=fontFaceItem&&fontFaceItem.src||[];
 
-return fontFace;
-}).filter(Boolean);
+return loadedFont;
+});
 });
 }}
 
 
 module.exports=Fonts;
 
-},{"../../lib/sentry":33,"./gatherer":16}],"./gatherers/html-without-javascript":[function(require,module,exports){
+},{"../../lib/sentry":39,"./gatherer":19}],"../gather/gatherers/html-without-javascript":[function(require,module,exports){
 
 
 
@@ -11215,30 +10856,35 @@
 }
 
 class HTMLWithoutJavaScript extends Gatherer{
-beforePass(options){
-options.disableJavaScript=true;
+
+
+
+beforePass(passContext){
+passContext.disableJavaScript=true;
 }
 
-afterPass(options){
 
-options.disableJavaScript=false;
 
-return options.driver.evaluateAsync(`(${getBodyText.toString()}())`).
-then(result=>{
-if(typeof result!=='string'){
+
+
+async afterPass(passContext){
+
+passContext.disableJavaScript=false;
+
+const bodyText=await passContext.driver.evaluateAsync(`(${getBodyText.toString()}())`);
+if(typeof bodyText!=='string'){
 throw new Error('document body innerText returned by protocol was not a string');
 }
 
 return{
-value:result};
+value:bodyText};
 
-});
 }}
 
 
 module.exports=HTMLWithoutJavaScript;
 
-},{"./gatherer":16}],"./gatherers/http-redirect":[function(require,module,exports){
+},{"./gatherer":19}],"../gather/gatherers/http-redirect":[function(require,module,exports){
 
 
 
@@ -11256,55 +10902,36 @@
 class HTTPRedirect extends Gatherer{
 constructor(){
 super();
-this._preRedirectURL=undefined;
+this._preRedirectURL='';
 }
 
-beforePass(options){
-this._preRedirectURL=options.url;
-options.url=this._preRedirectURL.replace(/^https/,'http');
+
+
+
+beforePass(passContext){
+this._preRedirectURL=passContext.url;
+passContext.url=this._preRedirectURL.replace(/^https/,'http');
 }
 
-afterPass(options){
-
-options.url=this._preRedirectURL;
 
 
-const timeout=options._testTimeout||10000;
 
-const securityPromise=options.driver.getSecurityState().
-then(state=>{
+
+async afterPass(passContext){
+
+passContext.url=this._preRedirectURL;
+
+const expression=`new URL(window.location).protocol === 'https:'`;
+const isHttps=await passContext.driver.evaluateAsync(expression,{useIsolation:true});
 return{
-value:state.schemeIsCryptographic};
+value:isHttps};
 
-});
-
-let noSecurityChangesTimeout;
-const timeoutPromise=new Promise((resolve,reject)=>{
-
-
-noSecurityChangesTimeout=setTimeout(_=>{
-reject(new Error('Timed out waiting for HTTP redirection.'));
-},timeout);
-});
-
-return Promise.race([
-securityPromise,
-timeoutPromise]).
-then(result=>{
-
-clearTimeout(noSecurityChangesTimeout);
-return result;
-}).catch(err=>{
-clearTimeout(noSecurityChangesTimeout);
-throw err;
-});
 }}
 
 
 module.exports=HTTPRedirect;
 
-},{"./gatherer":16}],"./gatherers/image-usage":[function(require,module,exports){
-
+},{"./gatherer":19}],"../gather/gatherers/image-usage":[function(require,module,exports){
 
 
 
@@ -11318,11 +10945,14 @@
 
 const Gatherer=require('./gatherer');
 const DOMHelpers=require('../../lib/dom-helpers.js');
+const Driver=require('../driver.js');
+
 
 
 
 
 function collectImageElementInfo(){
+
 function getClientRect(element){
 const clientRect=element.getBoundingClientRect();
 return{
@@ -11334,8 +10964,13 @@
 
 }
 
+
+
 const allElements=getElementsInDocument();
-const allImageElements=allElements.filter(element=>element.localName==='img');
+const allImageElements=allElements.filter(element=>{
+return element.localName==='img';
+});
+
 
 const htmlImages=allImageElements.map(element=>{
 const computedStyle=window.getComputedStyle(element);
@@ -11365,12 +11000,13 @@
 
 const cssImages=allElements.reduce((images,element)=>{
 const style=window.getComputedStyle(element);
-if(!CSS_URL_REGEX.test(style.backgroundImage)||
-!CSS_SIZE_REGEX.test(style.backgroundSize)){
+if(!style.backgroundImage||!CSS_URL_REGEX.test(style.backgroundImage)||
+!style.backgroundSize||!CSS_SIZE_REGEX.test(style.backgroundSize)){
 return images;
 }
 
 const imageMatch=style.backgroundImage.match(CSS_URL_REGEX);
+
 const url=imageMatch[1];
 
 
@@ -11399,10 +11035,14 @@
 }
 
 
+
+
+
+
 function determineNaturalSize(url){
 return new Promise((resolve,reject)=>{
 const img=new Image();
-img.addEventListener('error',reject);
+img.addEventListener('error',_=>reject(new Error('determineNaturalSize failed img load')));
 img.addEventListener('load',()=>{
 resolve({
 naturalWidth:img.naturalWidth,
@@ -11419,24 +11059,34 @@
 
 
 
-fetchElementWithSizeInformation(element){
+
+async fetchElementWithSizeInformation(driver,element){
 const url=JSON.stringify(element.src);
-return this.driver.evaluateAsync(`(${determineNaturalSize.toString()})(${url})`).
-then(size=>{
+try{
+
+const size=await driver.evaluateAsync(`(${determineNaturalSize.toString()})(${url})`);
 return Object.assign(element,size);
-});
+}catch(_){
+
+return Object.assign(element,{naturalWidth:0,naturalHeight:0});
+}
 }
 
-afterPass(options,traceData){
-const driver=this.driver=options.driver;
-const indexedNetworkRecords=traceData.networkRecords.reduce((map,record)=>{
+
+
+
+
+
+async afterPass(passContext,loadData){
+const driver=passContext.driver;
+const indexedNetworkRecords=loadData.networkRecords.reduce((map,record)=>{
 if(/^image/.test(record._mimeType)&&record.finished){
 map[record._url]={
 url:record.url,
-resourceSize:record.resourceSize,
+resourceSize:Math.min(record._resourceSize||0,record.transferSize),
 startTime:record.startTime,
 endTime:record.endTime,
-responseReceivedTime:record.responseReceivedTime,
+responseReceivedTime:record._responseReceivedTime,
 mimeType:record._mimeType};
 
 }
@@ -11449,33 +11099,31 @@
       return (${collectImageElementInfo.toString()})();
     })()`;
 
-return driver.evaluateAsync(expression).
-then(elements=>{
-return elements.reduce((promise,element)=>{
-return promise.then(collector=>{
+
+const elements=await driver.evaluateAsync(expression);
+
+const imageUsage=[];
+for(let element of elements){
 
 element.networkRecord=indexedNetworkRecords[element.src];
 
 
 
 
-const elementPromise=(element.isPicture||element.isCss)&&element.networkRecord?
-this.fetchElementWithSizeInformation(element):
-Promise.resolve(element);
+if((element.isPicture||element.isCss)&&element.networkRecord){
+element=await this.fetchElementWithSizeInformation(driver,element);
+}
 
-return elementPromise.then(element=>{
-collector.push(element);
-return collector;
-});
-});
-},Promise.resolve([]));
-});
+imageUsage.push(element);
+}
+
+return imageUsage;
 }}
 
 
 module.exports=ImageUsage;
 
-},{"../../lib/dom-helpers.js":25,"./gatherer":16}],"./gatherers/js-usage":[function(require,module,exports){
+},{"../../lib/dom-helpers.js":30,"../driver.js":17,"./gatherer":19}],"../gather/gatherers/js-usage":[function(require,module,exports){
 
 
 
@@ -11489,32 +11137,31 @@
 
 
 class JsUsage extends Gatherer{
-beforePass(options){
-return options.driver.sendCommand('Profiler.enable').
-then(_=>options.driver.sendCommand('Profiler.startPreciseCoverage'));
+
+
+
+async beforePass(passContext){
+await passContext.driver.sendCommand('Profiler.enable');
+await passContext.driver.sendCommand('Profiler.startPreciseCoverage');
 }
 
 
 
 
 
-afterPass(options){
-const driver=options.driver;
+async afterPass(passContext){
+const driver=passContext.driver;
 
-return driver.sendCommand('Profiler.takePreciseCoverage').then(results=>{
-return driver.sendCommand('Profiler.stopPreciseCoverage').
-then(_=>driver.sendCommand('Profiler.disable')).
-then(_=>results.result);
-});
+const coverageResponse=await driver.sendCommand('Profiler.takePreciseCoverage');
+await driver.sendCommand('Profiler.stopPreciseCoverage');
+await driver.sendCommand('Profiler.disable');
+return coverageResponse.result;
 }}
 
 
 module.exports=JsUsage;
 
-
-JsUsage.JsUsageArtifact;
-
-},{"./gatherer":16}],"./gatherers/manifest":[function(require,module,exports){
+},{"./gatherer":19}],"../gather/gatherers/manifest":[function(require,module,exports){
 (function(Buffer){
 
 
@@ -11542,11 +11189,12 @@
 
 
 
-afterPass(options){
-const manifestPromise=options.driver.getAppManifest();
+async afterPass(passContext){
+const manifestPromise=passContext.driver.getAppManifest();
+
 const timeoutPromise=new Promise(resolve=>setTimeout(resolve,3000));
-return Promise.race([manifestPromise,timeoutPromise]).
-then(response=>{
+
+const response=await Promise.race([manifestPromise,timeoutPromise]);
 if(!response){
 return null;
 }
@@ -11556,15 +11204,14 @@
 response.data=Buffer.from(response.data).slice(BOM_LENGTH).toString();
 }
 
-return manifestParser(response.data,response.url,options.url);
-});
+return manifestParser(response.data,response.url,passContext.url);
 }}
 
 
 module.exports=Manifest;
 
 }).call(this,require("buffer").Buffer);
-},{"../../lib/manifest-parser":31,"./gatherer":16,"buffer":54}],"./gatherers/mixed-content":[function(require,module,exports){
+},{"../../lib/manifest-parser":36,"./gatherer":19,"buffer":60}],"../gather/gatherers/mixed-content":[function(require,module,exports){
 (function(Buffer){
 
 
@@ -11576,6 +11223,8 @@
 const Gatherer=require('./gatherer');
 const URL=require('../../lib/url-shim');
 
+const Driver=require('../driver.js');
+
 
 
 
@@ -11593,6 +11242,7 @@
 super();
 this.ids=new Set();
 this.url=undefined;
+this._onRequestIntercepted=undefined;
 }
 
 
@@ -11615,12 +11265,18 @@
 return parsedURL.href;
 }
 
-_onRequestIntercepted(driver,event){
+
+
+
+
+_getRequestInterceptor(pageUrl,driver){
+
+const onRequestIntercepted=event=>{
 
 
 
 if(new URL(event.request.url).protocol==='http:'&&
-!URL.equalWithExcludedFragments(event.request.url,this.url)&&
+!URL.equalWithExcludedFragments(event.request.url,pageUrl)&&
 !this.ids.has(event.interceptionId)){
 this.ids.add(event.interceptionId);
 event.request.url=this.upgradeURL(event.request.url);
@@ -11635,45 +11291,51 @@
 interceptionId:event.interceptionId});
 
 }
+};
+
+return onRequestIntercepted;
 }
 
-beforePass(options){
-const driver=options.driver;
+
+
+
+async beforePass(passContext){
+const driver=passContext.driver;
 
 
 
 
 
-options.url=this.downgradeURL(options.url);
-this.url=options.url;
+passContext.url=this.downgradeURL(passContext.url);
+this.url=passContext.url;
+this._onRequestIntercepted=this._getRequestInterceptor(this.url,driver);
 
-driver.sendCommand('Network.enable',{});
-driver.on('Network.requestIntercepted',
-this._onRequestIntercepted.bind(this,driver));
-driver.sendCommand('Network.setCacheDisabled',{cacheDisabled:true});
-driver.sendCommand('Network.setRequestInterception',
-{patterns:[{urlPattern:'*'}]});
+await driver.sendCommand('Network.enable');
+driver.on('Network.requestIntercepted',this._onRequestIntercepted);
+await driver.sendCommand('Network.setCacheDisabled',{cacheDisabled:true});
+await driver.sendCommand('Network.setRequestInterception',{patterns:[{urlPattern:'*'}]});
 }
 
-afterPass(options,_){
-const driver=options.driver;
-return Promise.resolve().then(_=>driver.sendCommand(
-'Network.setRequestInterception',{patterns:[]})).
-then(_=>driver.off(
-'Network.requestIntercepted',
-this._onRequestIntercepted.bind(this,driver))).
-then(driver.sendCommand(
-'Network.setCacheDisabled',{cacheDisabled:false})).
-then(_=>{
-return{url:options.url};
-});
+
+
+
+
+async afterPass(passContext){
+const driver=passContext.driver;
+await driver.sendCommand('Network.setRequestInterception',{patterns:[]});
+if(this._onRequestIntercepted){
+driver.off('Network.requestIntercepted',this._onRequestIntercepted);
+}
+await driver.sendCommand('Network.setCacheDisabled',{cacheDisabled:false});
+
+return{url:passContext.url};
 }}
 
 
 module.exports=MixedContent;
 
 }).call(this,require("buffer").Buffer);
-},{"../../lib/url-shim":41,"./gatherer":16,"buffer":54}],"./gatherers/offline":[function(require,module,exports){
+},{"../../lib/url-shim":"url","../driver.js":17,"./gatherer":19,"buffer":60}],"../gather/gatherers/offline":[function(require,module,exports){
 
 
 
@@ -11685,24 +11347,32 @@
 const URL=require('../../lib/url-shim');
 
 class Offline extends Gatherer{
-beforePass(options){
-return options.driver.goOffline();
+
+
+
+beforePass(passContext){
+return passContext.driver.goOffline();
 }
 
-afterPass(options,tracingData){
-const navigationRecord=tracingData.networkRecords.filter(record=>{
-return URL.equalWithExcludedFragments(record._url,options.url)&&
+
+
+
+
+
+afterPass(passContext,loadData){
+const navigationRecord=loadData.networkRecords.filter(record=>{
+return URL.equalWithExcludedFragments(record._url,passContext.url)&&
 record._fetchedViaServiceWorker;
 }).pop();
 
-return options.driver.goOnline(options).
+return passContext.driver.goOnline(passContext).
 then(_=>navigationRecord?navigationRecord.statusCode:-1);
 }}
 
 
 module.exports=Offline;
 
-},{"../../lib/url-shim":41,"./gatherer":16}],"./gatherers/runtime-exceptions":[function(require,module,exports){
+},{"../../lib/url-shim":"url","./gatherer":19}],"../gather/gatherers/runtime-exceptions":[function(require,module,exports){
 
 
 
@@ -11720,29 +11390,39 @@
 class RuntimeExceptions extends Gatherer{
 constructor(){
 super();
+
 this._exceptions=[];
 this._onRuntimeExceptionThrown=this.onRuntimeExceptionThrown.bind(this);
 }
 
+
+
+
 onRuntimeExceptionThrown(entry){
 this._exceptions.push(entry);
 }
 
-beforePass(options){
-const driver=options.driver;
+
+
+
+beforePass(passContext){
+const driver=passContext.driver;
 driver.on('Runtime.exceptionThrown',this._onRuntimeExceptionThrown);
 }
 
-afterPass(options){
-return Promise.resolve().
-then(_=>options.driver.off('Runtime.exceptionThrown',this._onRuntimeExceptionThrown)).
-then(_=>this._exceptions);
+
+
+
+
+async afterPass(passContext){
+await passContext.driver.off('Runtime.exceptionThrown',this._onRuntimeExceptionThrown);
+return this._exceptions;
 }}
 
 
 module.exports=RuntimeExceptions;
 
-},{"./gatherer":16}],"./gatherers/scripts":[function(require,module,exports){
+},{"./gatherer":19}],"../gather/gatherers/scripts":[function(require,module,exports){
 
 
 
@@ -11762,31 +11442,30 @@
 
 
 
-afterPass(options,traceData){
-const driver=options.driver;
+async afterPass(passContext,loadData){
+const driver=passContext.driver;
+
 
 const scriptContentMap={};
-const scriptRecords=traceData.networkRecords.
-filter(record=>record.resourceType()===WebInspector.resourceTypes.Script);
+const scriptRecords=loadData.networkRecords.
+filter(record=>record._resourceType===WebInspector.resourceTypes.Script);
 
-return scriptRecords.reduce((promise,record)=>{
-return promise.
-then(()=>{
-return driver.getRequestContent(record.requestId).
-catch(_=>null).
-then(content=>{
-if(!content)return;
+for(const record of scriptRecords){
+try{
+const content=await driver.getRequestContent(record.requestId);
+if(content){
 scriptContentMap[record.requestId]=content;
-});
-}).
-then(()=>scriptContentMap);
-},Promise.resolve(scriptContentMap));
+}
+}catch(e){}
+}
+
+return scriptContentMap;
 }}
 
 
 module.exports=Scripts;
 
-},{"../../lib/web-inspector":42,"./gatherer":16}],"./gatherers/seo/canonical":[function(require,module,exports){
+},{"../../lib/web-inspector":47,"./gatherer":19}],"../gather/gatherers/seo/canonical":[function(require,module,exports){
 
 
 
@@ -11801,8 +11480,8 @@
 
 
 
-afterPass(options){
-const driver=options.driver;
+afterPass(passContext){
+const driver=passContext.driver;
 
 return driver.querySelectorAll('head link[rel="canonical" i]').
 then(nodes=>Promise.all(nodes.map(node=>node.getAttribute('href'))));
@@ -11812,7 +11491,7 @@
 module.exports=Canonical;
 
 
-},{"../gatherer":16}],"./gatherers/seo/crawlable-links":[function(require,module,exports){
+},{"../gatherer":19}],"../gather/gatherers/seo/crawlable-links":[function(require,module,exports){
 
 
 
@@ -11828,7 +11507,7 @@
 
 
 
-afterPass(options){
+afterPass(passContext){
 const expression=`(function() {
       ${DOMHelpers.getElementsInDocumentFnString}; // define function on page
       const selector = 'a[href]:not([rel~="nofollow"])';
@@ -11840,14 +11519,14 @@
         }));
     })()`;
 
-return options.driver.evaluateAsync(expression);
+return passContext.driver.evaluateAsync(expression);
 }}
 
 
 module.exports=CrawlableLinks;
 
 
-},{"../../../lib/dom-helpers.js":25,"../gatherer":16}],"./gatherers/seo/embedded-content":[function(require,module,exports){
+},{"../../../lib/dom-helpers.js":30,"../gatherer":19}],"../gather/gatherers/seo/embedded-content":[function(require,module,exports){
 
 
 
@@ -11863,7 +11542,7 @@
 
 
 
-afterPass(options){
+afterPass(passContext){
 const expression=`(function() {
       ${DOMHelpers.getElementsInDocumentFnString}; // define function on page
       const selector = 'object, embed, applet';
@@ -11884,19 +11563,20 @@
         }));
     })()`;
 
-return options.driver.evaluateAsync(expression);
+return passContext.driver.evaluateAsync(expression);
 }}
 
 
 module.exports=EmbeddedContent;
 
-},{"../../../lib/dom-helpers.js":25,"../gatherer":16}],"./gatherers/seo/font-size":[function(require,module,exports){
+},{"../../../lib/dom-helpers.js":30,"../gatherer":19}],"../gather/gatherers/seo/font-size":[function(require,module,exports){
 (function(global){
 
 
 
 
 
+
 'use strict';
 
 
@@ -11912,12 +11592,13 @@
 const Gatherer=require('../gatherer');
 const FONT_SIZE_PROPERTY_NAME='font-size';
 const TEXT_NODE_BLOCK_LIST=new Set(['SCRIPT','STYLE','NOSCRIPT']);
-
-const MINIMAL_LEGIBLE_FONT_SIZE_PX=16;
+const MINIMAL_LEGIBLE_FONT_SIZE_PX=12;
 
 const MAX_NODES_VISITED=500;
 const MAX_NODES_ANALYZED=50;
 
+const Driver=require('../../driver.js');
+
 
 
 
@@ -12046,20 +11727,22 @@
 
 
 
-afterPass(options){
-const stylesheets=new Map();
-const onStylesheetAdd=sheet=>stylesheets.set(sheet.header.styleSheetId,sheet.header);
-options.driver.on('CSS.styleSheetAdded',onStylesheetAdd);
+afterPass(passContext){
 
-const enableDOM=options.driver.sendCommand('DOM.enable');
-const enableCSS=options.driver.sendCommand('CSS.enable');
+const stylesheets=new Map();
+
+const onStylesheetAdd=sheet=>stylesheets.set(sheet.header.styleSheetId,sheet.header);
+passContext.driver.on('CSS.styleSheetAdded',onStylesheetAdd);
+
+const enableDOM=passContext.driver.sendCommand('DOM.enable');
+const enableCSS=passContext.driver.sendCommand('CSS.enable');
 
 let failingTextLength=0;
 let visitedTextLength=0;
 let totalTextLength=0;
 
 return Promise.all([enableDOM,enableCSS]).
-then(()=>getAllNodesFromBody(options.driver)).
+then(()=>getAllNodesFromBody(passContext.driver)).
 then(nodes=>{
 const textNodes=nodes.filter(isNonEmptyTextNode);
 totalTextLength=textNodes.reduce((sum,node)=>sum+=getNodeTextLength(node),0);
@@ -12070,7 +11753,7 @@
 return nodesToVisit;
 }).
 then(textNodes=>
-Promise.all(textNodes.map(node=>getFontSizeInformation(options.driver,node)))).
+Promise.all(textNodes.map(node=>getFontSizeInformation(passContext.driver,node)))).
 then(fontSizeInfo=>{
 const visitedNodes=fontSizeInfo.filter(Boolean);
 visitedTextLength=visitedNodes.reduce((sum,{textLength})=>sum+=textLength,0);
@@ -12082,7 +11765,7 @@
 sort((a,b)=>b.textLength-a.textLength).
 slice(0,MAX_NODES_ANALYZED).
 map(info=>
-getFontSizeSourceRule(options.driver,info.node).
+getFontSizeSourceRule(passContext.driver,info.node).
 then(sourceRule=>{
 if(sourceRule){
 info.cssRule={
@@ -12104,7 +11787,7 @@
 
 }).
 then(analyzedFailingNodesData=>{
-options.driver.off('CSS.styleSheetAdded',onStylesheetAdd);
+passContext.driver.off('CSS.styleSheetAdded',onStylesheetAdd);
 
 const analyzedFailingTextLength=analyzedFailingNodesData.
 reduce((sum,{textLength})=>sum+=textLength,0);
@@ -12114,8 +11797,8 @@
 forEach(data=>data.cssRule.stylesheet=stylesheets.get(data.cssRule.styleSheetId));
 
 return Promise.all([
-options.driver.sendCommand('DOM.disable'),
-options.driver.sendCommand('CSS.disable')]).
+passContext.driver.sendCommand('DOM.disable'),
+passContext.driver.sendCommand('CSS.disable')]).
 then(_=>({
 analyzedFailingNodesData,
 analyzedFailingTextLength,
@@ -12140,7 +11823,7 @@
 
 
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{"../../../lib/sentry.js":33,"../../../lib/web-inspector":42,"../gatherer":16}],"./gatherers/seo/hreflang":[function(require,module,exports){
+},{"../../../lib/sentry.js":39,"../../../lib/web-inspector":47,"../../driver.js":17,"../gatherer":19}],"../gather/gatherers/seo/hreflang":[function(require,module,exports){
 
 
 
@@ -12155,8 +11838,8 @@
 
 
 
-afterPass(options){
-const driver=options.driver;
+afterPass(passContext){
+const driver=passContext.driver;
 
 return driver.querySelectorAll('head link[rel="alternate" i][hreflang]').
 then(nodes=>Promise.all(nodes.map(node=>
@@ -12165,7 +11848,10 @@
 then(attributeValues=>attributeValues&&
 attributeValues.map(values=>{
 const[href,hreflang]=values;
-return{href,hreflang};
+return{
+href:href||'',
+hreflang:hreflang||''};
+
 }));
 
 }}
@@ -12174,7 +11860,7 @@
 module.exports=Hreflang;
 
 
-},{"../gatherer":16}],"./gatherers/seo/meta-description":[function(require,module,exports){
+},{"../gatherer":19}],"../gather/gatherers/seo/meta-description":[function(require,module,exports){
 
 
 
@@ -12189,8 +11875,8 @@
 
 
 
-afterPass(options){
-const driver=options.driver;
+afterPass(passContext){
+const driver=passContext.driver;
 
 return driver.querySelector('head meta[name="description" i]').
 then(node=>node&&node.getAttribute('content'));
@@ -12200,7 +11886,7 @@
 module.exports=MetaDescription;
 
 
-},{"../gatherer":16}],"./gatherers/seo/meta-robots":[function(require,module,exports){
+},{"../gatherer":19}],"../gather/gatherers/seo/meta-robots":[function(require,module,exports){
 
 
 
@@ -12215,8 +11901,8 @@
 
 
 
-afterPass(options){
-const driver=options.driver;
+afterPass(passContext){
+const driver=passContext.driver;
 
 return driver.querySelector('head meta[name="robots" i]').
 then(node=>node&&node.getAttribute('content'));
@@ -12225,7 +11911,48 @@
 
 module.exports=MetaRobots;
 
-},{"../gatherer":16}],"./gatherers/service-worker":[function(require,module,exports){
+},{"../gatherer":19}],"../gather/gatherers/seo/robots-txt":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Gatherer=require('../gatherer');
+
+
+
+
+
+async function getRobotsTxtContent(){
+try{
+const response=await fetch(new URL('/robots.txt',location.href).href);
+if(!response.ok){
+return{status:response.status,content:null};
+}
+
+const content=await response.text();
+return{status:response.status,content};
+}catch(_){
+return{status:null,content:null};
+}
+}
+
+
+class RobotsTxt extends Gatherer{
+
+
+
+
+afterPass(passContext){
+return passContext.driver.evaluateAsync(`(${getRobotsTxtContent.toString()}())`);
+}}
+
+
+module.exports=RobotsTxt;
+
+},{"../gatherer":19}],"../gather/gatherers/service-worker":[function(require,module,exports){
 
 
 
@@ -12236,21 +11963,21 @@
 const Gatherer=require('./gatherer');
 
 class ServiceWorker extends Gatherer{
-beforePass(options){
-const driver=options.driver;
-return driver.
-getServiceWorkerVersions().
-then(data=>{
-return{
-versions:data.versions};
 
-});
+
+
+
+async beforePass(passContext){
+const{versions}=await passContext.driver.getServiceWorkerVersions();
+return{
+versions};
+
 }}
 
 
 module.exports=ServiceWorker;
 
-},{"./gatherer":16}],"./gatherers/start-url":[function(require,module,exports){
+},{"./gatherer":19}],"../gather/gatherers/start-url":[function(require,module,exports){
 
 
 
@@ -12259,83 +11986,105 @@
 'use strict';
 
 const Gatherer=require('./gatherer');
-const URL=require('../../lib/url-shim');
 const manifestParser=require('../../lib/manifest-parser');
+const Driver=require('../driver.js');
 
 class StartUrl extends Gatherer{
-constructor(){
-super();
 
-this.startUrl=null;
-this.err=null;
-}
 
-executeFetchRequest(driver,url){
-return driver.evaluateAsync(
-`fetch('${url}')
-        .then(response => response.status)
-        .catch(err => ({fetchFailed: true, message: err.message}))`);
 
-}
 
-pass(options){
-return options.driver.getAppManifest().
-then(response=>{
-return response&&manifestParser(response.data,response.url,options.url);
-}).
+
+afterPass(passContext){
+const driver=passContext.driver;
+return driver.goOnline(passContext).
+then(()=>driver.getAppManifest()).
+then(response=>driver.goOffline().then(()=>response)).
+then(response=>response&&manifestParser(response.data,response.url,passContext.url)).
 then(manifest=>{
+const startUrlInfo=this._readManifestStartUrl(manifest);
+if(startUrlInfo.isReadFailure){
+return{statusCode:-1,debugString:startUrlInfo.reason};
+}
+
+return this._attemptManifestFetch(passContext.driver,startUrlInfo.startUrl);
+}).catch(()=>{
+return{statusCode:-1,debugString:'Unable to fetch start URL via service worker'};
+});
+}
+
+
+
+
+
+
+_readManifestStartUrl(manifest){
 if(!manifest||!manifest.value){
 const detailedMsg=manifest&&manifest.debugString;
-this.debugString=detailedMsg?
-`Error fetching web app manifest: ${detailedMsg}`:
-`No usable web app manifest found on page ${options.url}`;
-return;
+
+if(detailedMsg){
+return{isReadFailure:true,reason:`Error fetching web app manifest: ${detailedMsg}`};
+}else{
+return{isReadFailure:true,reason:`No usable web app manifest found on page`};
 }
+}
+
+
 
 if(manifest.value.start_url.debugString){
-
-
-this.debugString=manifest.value.start_url.debugString;
+return{isReadFailure:true,reason:manifest.value.start_url.debugString};
 }
 
-this.startUrl=manifest.value.start_url.value;
-return this.executeFetchRequest(options.driver,this.startUrl);
-});
+
+return{isReadFailure:false,startUrl:manifest.value.start_url.value};
 }
 
-afterPass(options,tracingData){
-const networkRecords=tracingData.networkRecords;
-const navigationRecord=networkRecords.filter(record=>{
-return URL.equalWithExcludedFragments(record._url,this.startUrl)&&
-record._fetchedViaServiceWorker;
-}).pop();
 
-const msgWithExtraDebugString=msg=>this.debugString?`${msg}: ${this.debugString}`:msg;
-return options.driver.goOnline(options).
-then(_=>{
-if(!this.startUrl){
-return{
+
+
+
+
+
+
+_attemptManifestFetch(driver,startUrl){
+
+const timeoutPromise=new Promise(resolve=>
+setTimeout(
+()=>resolve({statusCode:-1,debugString:'Timed out waiting for fetched start_url'}),
+3000));
+
+
+
+const fetchPromise=new Promise(resolve=>{
+driver.on('Network.responseReceived',onResponseReceived);
+
+
+function onResponseReceived(responseEvent){
+const{response}=responseEvent;
+
+if(response.url!==startUrl)return;
+driver.off('Network.responseReceived',onResponseReceived);
+
+if(!response.fromServiceWorker){
+return resolve({
 statusCode:-1,
-debugString:msgWithExtraDebugString('No start URL to fetch')};
+debugString:'Unable to fetch start URL via service worker'});
 
-}else if(!navigationRecord){
-return{
-statusCode:-1,
-debugString:msgWithExtraDebugString('Unable to fetch start URL via service worker')};
+}
 
-}else{
-return{
-statusCode:navigationRecord.statusCode,
-debugString:this.debugString};
-
+return resolve({statusCode:response.status});
 }
 });
+
+return driver.
+evaluateAsync(`window.location = '${startUrl}'`).
+then(()=>Promise.race([fetchPromise,timeoutPromise]));
 }}
 
 
 module.exports=StartUrl;
 
-},{"../../lib/manifest-parser":31,"../../lib/url-shim":41,"./gatherer":16}],"./gatherers/theme-color":[function(require,module,exports){
+},{"../../lib/manifest-parser":36,"../driver.js":17,"./gatherer":19}],"../gather/gatherers/theme-color":[function(require,module,exports){
 
 
 
@@ -12350,17 +12099,17 @@
 
 
 
-afterPass(options){
-const driver=options.driver;
+async afterPass(passContext){
+const driver=passContext.driver;
 
-return driver.querySelector('head meta[name="theme-color" i]').
-then(node=>node&&node.getAttribute('content'));
+const metaEl=await driver.querySelector('head meta[name="theme-color" i]');
+return metaEl&&metaEl.getAttribute('content');
 }}
 
 
 module.exports=ThemeColor;
 
-},{"./gatherer":16}],"./gatherers/url":[function(require,module,exports){
+},{"./gatherer":19}],"../gather/gatherers/viewport-dimensions":[function(require,module,exports){
 
 
 
@@ -12370,32 +12119,10 @@
 
 const Gatherer=require('./gatherer');
 
-class URL extends Gatherer{
-afterPass(options){
 
 
 
 
-return{
-initialUrl:options.initialUrl,
-finalUrl:options.url};
-
-}}
-
-
-module.exports=URL;
-
-},{"./gatherer":16}],"./gatherers/viewport-dimensions":[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const Gatherer=require('./gatherer');
-
-
 
 
 function getViewportDimensions(){
@@ -12416,26 +12143,26 @@
 
 
 
-afterPass(options){
-const driver=options.driver;
+async afterPass(passContext){
+const driver=passContext.driver;
 
-return driver.evaluateAsync(`(${getViewportDimensions.toString()}())`,{useIsolation:true}).
 
-then(dimensions=>{
-const allNumeric=Object.keys(dimensions).every(key=>Number.isFinite(dimensions[key]));
+const dimensions=await driver.evaluateAsync(`(${getViewportDimensions.toString()}())`,
+{useIsolation:true});
+
+const allNumeric=Object.values(dimensions).every(Number.isFinite);
 if(!allNumeric){
 const results=JSON.stringify(dimensions);
 throw new Error(`ViewportDimensions results were not numeric: ${results}`);
 }
 
 return dimensions;
-});
 }}
 
 
 module.exports=ViewportDimensions;
 
-},{"./gatherer":16}],"./gatherers/viewport":[function(require,module,exports){
+},{"./gatherer":19}],"../gather/gatherers/viewport":[function(require,module,exports){
 
 
 
@@ -12450,17 +12177,2396 @@
 
 
 
-afterPass(options){
-const driver=options.driver;
+async afterPass(passContext){
+const driver=passContext.driver;
 
-return driver.querySelector('head meta[name="viewport" i]').
-then(node=>node&&node.getAttribute('content'));
+const metaEl=await driver.querySelector('head meta[name="viewport" i]');
+return metaEl&&metaEl.getAttribute('content');
 }}
 
 
 module.exports=Viewport;
 
-},{"./gatherer":16}],1:[function(require,module,exports){
+},{"./gatherer":19}],"./gather/computed/critical-request-chains":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('./computed-artifact');
+const WebInspector=require('../../lib/web-inspector');
+const assert=require('assert');
+
+class CriticalRequestChains extends ComputedArtifact{
+get name(){
+return'CriticalRequestChains';
+}
+
+
+
+
+
+
+
+
+
+static isCritical(request,mainResource){
+assert.ok(mainResource,'mainResource not provided');
+
+
+if(request._isLinkPreload){
+return false;
+}
+
+const resourceTypeCategory=request._resourceType&&request._resourceType._category;
+
+
+const isIframe=request._resourceType===WebInspector.resourceTypes.Document&&
+request._frameId!==mainResource._frameId;
+
+
+
+const nonCriticalResourceTypes=[
+WebInspector.resourceTypes.Image._category,
+WebInspector.resourceTypes.XHR._category];
+
+if(nonCriticalResourceTypes.includes(resourceTypeCategory)||
+isIframe||
+request._mimeType&&request._mimeType.startsWith('image/')){
+return false;
+}
+
+return['VeryHigh','High','Medium'].includes(request.priority());
+}
+
+
+
+
+
+
+static extractChain(networkRecords,mainResource){
+networkRecords=networkRecords.filter(req=>req.finished);
+
+
+
+const requestIdToRequests=new Map();
+for(const request of networkRecords){
+requestIdToRequests.set(request.requestId,request);
+}
+
+
+
+const criticalRequests=networkRecords.filter(request=>
+CriticalRequestChains.isCritical(request,mainResource));
+
+
+
+const criticalRequestChains={};
+for(const request of criticalRequests){
+
+
+
+
+const ancestors=[];
+let ancestorRequest=request.initiatorRequest();
+
+let node=criticalRequestChains;
+while(ancestorRequest){
+const ancestorIsCritical=CriticalRequestChains.isCritical(ancestorRequest,mainResource);
+
+
+
+
+
+if(!ancestorIsCritical||ancestors.includes(ancestorRequest.requestId)){
+
+
+ancestors.length=0;
+node=undefined;
+break;
+}
+ancestors.push(ancestorRequest.requestId);
+ancestorRequest=ancestorRequest.initiatorRequest();
+}
+
+
+
+let ancestor=ancestors.pop();
+while(ancestor&&node){
+const parentRequest=requestIdToRequests.get(ancestor);
+if(!parentRequest){
+throw new Error(`request with id ${ancestor} not found.`);
+}
+
+const parentRequestId=parentRequest.requestId;
+if(!node[parentRequestId]){
+node[parentRequestId]={
+request:parentRequest,
+children:{}};
+
+}
+
+
+ancestor=ancestors.pop();
+node=node[parentRequestId].children;
+}
+
+if(!node){
+continue;
+}
+
+
+if(node[request.requestId]){
+continue;
+}
+
+
+node[request.requestId]={
+request,
+children:{}};
+
+}
+
+return criticalRequestChains;
+}
+
+
+
+
+
+
+async compute_(data,artifacts){
+const[networkRecords,mainResource]=await Promise.all([
+artifacts.requestNetworkRecords(data.devtoolsLog),
+artifacts.requestMainResource(data)]);
+
+
+return CriticalRequestChains.extractChain(networkRecords,mainResource);
+}}
+
+
+module.exports=CriticalRequestChains;
+
+},{"../../lib/web-inspector":47,"./computed-artifact":11,"assert":53}],"./gather/computed/dtm-model":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('./computed-artifact');
+const DTM=require('../../lib/traces/devtools-timeline-model');
+
+class DevtoolsTimelineModel extends ComputedArtifact{
+get name(){
+return'DevtoolsTimelineModel';
+}
+
+
+
+
+
+async compute_(trace){
+return new DTM(trace);
+}}
+
+
+module.exports=DevtoolsTimelineModel;
+
+},{"../../lib/traces/devtools-timeline-model":43,"./computed-artifact":11}],"./gather/computed/load-simulator":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('./computed-artifact');
+const constants=require('../../config/constants');
+const Simulator=require('../../lib/dependency-graph/simulator/simulator');
+
+class LoadSimulatorArtifact extends ComputedArtifact{
+get name(){
+return'LoadSimulator';
+}
+
+
+
+
+
+
+async compute_(data,artifacts){
+const{throttlingMethod,throttling}=data.settings;
+const networkAnalysis=await artifacts.requestNetworkAnalysis(data.devtoolsLog);
+
+const options={
+additionalRttByOrigin:networkAnalysis.additionalRttByOrigin,
+serverResponseTimeByOrigin:networkAnalysis.serverResponseTimeByOrigin};
+
+
+switch(throttlingMethod){
+case'provided':
+options.rtt=networkAnalysis.rtt;
+options.throughput=networkAnalysis.throughput;
+options.cpuSlowdownMultiplier=1;
+options.layoutTaskMultiplier=1;
+break;
+case'devtools':
+if(throttling){
+options.rtt=
+throttling.requestLatencyMs/constants.throttling.DEVTOOLS_RTT_ADJUSTMENT_FACTOR;
+options.throughput=
+throttling.downloadThroughputKbps*1024/
+constants.throttling.DEVTOOLS_THROUGHPUT_ADJUSTMENT_FACTOR;
+}
+
+options.cpuSlowdownMultiplier=1;
+options.layoutTaskMultiplier=1;
+break;
+case'simulate':
+if(throttling){
+options.rtt=throttling.rttMs;
+options.throughput=throttling.throughputKbps*1024;
+options.cpuSlowdownMultiplier=throttling.cpuSlowdownMultiplier;
+}
+break;
+default:
+
+break;}
+
+
+return new Simulator(options);
+}}
+
+
+module.exports=LoadSimulatorArtifact;
+
+},{"../../config/constants":8,"../../lib/dependency-graph/simulator/simulator":28,"./computed-artifact":11}],"./gather/computed/main-resource":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('./computed-artifact');
+
+
+
+
+
+class MainResource extends ComputedArtifact{
+get name(){
+return'MainResource';
+}
+
+
+
+
+
+
+compute_(data,artifacts){
+const{URL,devtoolsLog}=data;
+return artifacts.requestNetworkRecords(devtoolsLog).
+then(requests=>{
+const mainResource=requests.find(request=>request.url===URL.finalUrl);
+
+if(!mainResource){
+throw new Error('Unable to identify the main resource');
+}
+
+return mainResource;
+});
+}}
+
+
+module.exports=MainResource;
+
+},{"./computed-artifact":11}],"./gather/computed/manifest-values":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('./computed-artifact');
+const icons=require('../../lib/icons');
+
+const PWA_DISPLAY_VALUES=['minimal-ui','fullscreen','standalone'];
+
+
+
+const SUGGESTED_SHORTNAME_LENGTH=12;
+
+class ManifestValues extends ComputedArtifact{
+get name(){
+return'ManifestValues';
+}
+
+static get validityIds(){
+return['hasManifest','hasParseableManifest'];
+}
+
+
+
+
+
+
+static get manifestChecks(){
+return[
+{
+id:'hasStartUrl',
+failureText:'Manifest does not contain a `start_url`',
+validate:manifestValue=>!!manifestValue.start_url.value},
+
+{
+id:'hasIconsAtLeast192px',
+failureText:'Manifest does not have icons at least 192px',
+validate:manifestValue=>icons.doExist(manifestValue)&&
+icons.sizeAtLeast(192,manifestValue).length>0},
+
+{
+id:'hasIconsAtLeast512px',
+failureText:'Manifest does not have icons at least 512px',
+validate:manifestValue=>icons.doExist(manifestValue)&&
+icons.sizeAtLeast(512,manifestValue).length>0},
+
+{
+id:'hasPWADisplayValue',
+failureText:'Manifest\'s `display` value is not one of: '+PWA_DISPLAY_VALUES.join(' | '),
+validate:manifestValue=>PWA_DISPLAY_VALUES.includes(manifestValue.display.value)},
+
+{
+id:'hasBackgroundColor',
+failureText:'Manifest does not have `background_color`',
+validate:manifestValue=>!!manifestValue.background_color.value},
+
+{
+id:'hasThemeColor',
+failureText:'Manifest does not have `theme_color`',
+validate:manifestValue=>!!manifestValue.theme_color.value},
+
+{
+id:'hasShortName',
+failureText:'Manifest does not have `short_name`',
+validate:manifestValue=>!!manifestValue.short_name.value},
+
+{
+id:'shortNameLength',
+failureText:'Manifest `short_name` will be truncated when displayed on the homescreen',
+validate:manifestValue=>!!manifestValue.short_name.value&&
+manifestValue.short_name.value.length<=SUGGESTED_SHORTNAME_LENGTH},
+
+{
+id:'hasName',
+failureText:'Manifest does not have `name`',
+validate:manifestValue=>!!manifestValue.name.value}];
+
+
+}
+
+
+
+
+
+
+async compute_(manifest){
+
+let parseFailureReason;
+
+if(manifest===null){
+return{
+isParseFailure:true,
+parseFailureReason:'No manifest was fetched',
+allChecks:[]};
+
+}
+const manifestValue=manifest.value;
+if(manifestValue===undefined){
+return{
+isParseFailure:true,
+parseFailureReason:'Manifest failed to parse as valid JSON',
+allChecks:[]};
+
+}
+
+
+const remainingChecks=ManifestValues.manifestChecks.map(item=>{
+return{
+id:item.id,
+failureText:item.failureText,
+passing:item.validate(manifestValue)};
+
+});
+
+return{
+isParseFailure:false,
+parseFailureReason,
+allChecks:remainingChecks};
+
+}}
+
+
+module.exports=ManifestValues;
+
+},{"../../lib/icons":35,"./computed-artifact":11}],"./gather/computed/metrics/estimated-input-latency":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const MetricArtifact=require('./metric');
+const LHError=require('../../../lib/errors');
+const TracingProcessor=require('../../../lib/traces/tracing-processor');
+
+const ROLLING_WINDOW_SIZE=5000;
+
+
+
+
+
+
+class EstimatedInputLatency extends MetricArtifact{
+get name(){
+return'EstimatedInputLatency';
+}
+
+
+
+
+
+static calculateRollingWindowEIL(events){
+const candidateStartEvts=events.filter(evt=>evt.duration>=10);
+
+let worst90thPercentileLatency=16;
+for(const startEvt of candidateStartEvts){
+const latencyPercentiles=TracingProcessor.getRiskToResponsiveness(
+events,
+startEvt.start,
+startEvt.start+ROLLING_WINDOW_SIZE,
+[0.9]);
+
+
+worst90thPercentileLatency=Math.max(latencyPercentiles[0].time,worst90thPercentileLatency);
+}
+
+return worst90thPercentileLatency;
+}
+
+
+
+
+
+computeObservedMetric(data){
+const{firstMeaningfulPaint}=data.traceOfTab.timings;
+if(!firstMeaningfulPaint){
+throw new LHError(LHError.errors.NO_FMP);
+}
+
+const events=TracingProcessor.getMainThreadTopLevelEvents(
+data.traceOfTab,
+firstMeaningfulPaint).
+filter(evt=>evt.duration>=1);
+
+return Promise.resolve({
+timing:EstimatedInputLatency.calculateRollingWindowEIL(events)});
+
+}}
+
+
+module.exports=EstimatedInputLatency;
+
+},{"../../../lib/errors":33,"../../../lib/traces/tracing-processor":46,"./metric":13}],"./gather/computed/metrics/first-contentful-paint":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const MetricArtifact=require('./metric');
+const LHError=require('../../../lib/errors');
+
+class FirstContentfulPaint extends MetricArtifact{
+get name(){
+return'FirstContentfulPaint';
+}
+
+
+
+
+
+computeObservedMetric(data){
+const{traceOfTab}=data;
+if(!traceOfTab.timestamps.firstContentfulPaint){
+throw new LHError(LHError.errors.NO_FCP);
+}
+
+return Promise.resolve({
+timing:traceOfTab.timings.firstContentfulPaint,
+timestamp:traceOfTab.timestamps.firstContentfulPaint});
+
+}}
+
+
+module.exports=FirstContentfulPaint;
+
+},{"../../../lib/errors":33,"./metric":13}],"./gather/computed/metrics/first-cpu-idle":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+const MetricArtifact=require('./metric');
+const TracingProcessor=require('../../../lib/traces/tracing-processor');
+const LHError=require('../../../lib/errors');
+
+const LONG_TASK_THRESHOLD=50;
+
+const MAX_TASK_CLUSTER_DURATION=250;
+const MIN_TASK_CLUSTER_PADDING=1000;
+const MIN_TASK_CLUSTER_FMP_DISTANCE=5000;
+
+const MAX_QUIET_WINDOW_SIZE=5000;
+
+
+const EXPONENTIATION_COEFFICIENT=-Math.log(3-1)/15;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class FirstCPUIdle extends MetricArtifact{
+get name(){
+return'FirstCPUIdle';
+}
+
+
+
+
+
+static getRequiredWindowSizeInMs(t){
+const tInSeconds=t/1000;
+const exponentiationComponent=Math.exp(EXPONENTIATION_COEFFICIENT*tInSeconds);
+return(4*exponentiationComponent+1)*1000;
+}
+
+
+
+
+
+
+
+
+
+
+static getTaskClustersInWindow(tasks,startIndex,windowEnd){
+const clusters=[];
+
+let previousTaskEndTime=-Infinity;
+
+let currentCluster=[];
+
+
+
+
+
+const clusteringWindowEnd=windowEnd+MIN_TASK_CLUSTER_PADDING;
+
+const isInClusteringWindow=task=>task.start<clusteringWindowEnd;
+for(let i=startIndex;i<tasks.length;i++){
+if(!isInClusteringWindow(tasks[i])){
+break;
+}
+
+const task=tasks[i];
+
+
+if(task.start-previousTaskEndTime>MIN_TASK_CLUSTER_PADDING){
+currentCluster=[];
+clusters.push(currentCluster);
+}
+
+currentCluster.push(task);
+previousTaskEndTime=task.end;
+}
+
+return clusters.
+
+map(tasks=>{
+const start=tasks[0].start;
+const end=tasks[tasks.length-1].end;
+const duration=end-start;
+return{start,end,duration};
+}).
+
+filter(cluster=>cluster.start<windowEnd);
+}
+
+
+
+
+
+
+
+
+
+
+static findQuietWindow(FMP,traceEnd,longTasks){
+
+if(longTasks.length===0||
+longTasks[0].start>FMP+FirstCPUIdle.getRequiredWindowSizeInMs(0)){
+return FMP;
+}
+
+
+const isTooCloseToFMP=cluster=>cluster.start<FMP+MIN_TASK_CLUSTER_FMP_DISTANCE;
+
+const isTooLong=cluster=>cluster.duration>MAX_TASK_CLUSTER_DURATION;
+
+const isBadCluster=cluster=>isTooCloseToFMP(cluster)||isTooLong(cluster);
+
+
+
+for(let i=0;i<longTasks.length;i++){
+const windowStart=longTasks[i].end;
+const windowSize=FirstCPUIdle.getRequiredWindowSizeInMs(windowStart-FMP);
+const windowEnd=windowStart+windowSize;
+
+
+if(windowEnd>traceEnd){
+throw new LHError(LHError.errors.NO_FCPUI_IDLE_PERIOD);
+}
+
+
+if(i+1<longTasks.length&&
+longTasks[i+1].start-windowStart<=MIN_TASK_CLUSTER_PADDING){
+continue;
+}
+
+const taskClusters=FirstCPUIdle.getTaskClustersInWindow(longTasks,i+1,windowEnd);
+const hasBadTaskClusters=taskClusters.some(isBadCluster);
+
+if(!hasBadTaskClusters){
+return windowStart;
+}
+}
+
+throw new LHError(LHError.errors.NO_FCPUI_IDLE_PERIOD);
+}
+
+
+
+
+
+computeObservedMetric(data){
+const{traceOfTab}=data;
+const navStart=traceOfTab.timestamps.navigationStart;
+const FMP=traceOfTab.timings.firstMeaningfulPaint;
+const DCL=traceOfTab.timings.domContentLoaded;
+const traceEnd=traceOfTab.timings.traceEnd;
+
+if(traceEnd-FMP<MAX_QUIET_WINDOW_SIZE){
+throw new LHError(LHError.errors.FMP_TOO_LATE_FOR_FCPUI);
+}
+
+if(!FMP||!DCL){
+throw new LHError(FMP?LHError.errors.NO_DCL:LHError.errors.NO_FMP);
+}
+
+const longTasksAfterFMP=TracingProcessor.getMainThreadTopLevelEvents(traceOfTab,FMP).
+filter(evt=>evt.duration>=LONG_TASK_THRESHOLD);
+const firstInteractive=FirstCPUIdle.findQuietWindow(FMP,traceEnd,longTasksAfterFMP);
+
+const valueInMs=Math.max(firstInteractive,DCL);
+
+return Promise.resolve({
+timing:valueInMs,
+timestamp:valueInMs*1000+navStart});
+
+}}
+
+
+
+
+
+
+
+module.exports=FirstCPUIdle;
+
+},{"../../../lib/errors":33,"../../../lib/traces/tracing-processor":46,"./metric":13}],"./gather/computed/metrics/first-meaningful-paint":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const MetricArtifact=require('./metric');
+const LHError=require('../../../lib/errors');
+
+class FirstMeaningfulPaint extends MetricArtifact{
+get name(){
+return'FirstMeaningfulPaint';
+}
+
+
+
+
+
+computeObservedMetric(data){
+const{traceOfTab}=data;
+if(!traceOfTab.timestamps.firstMeaningfulPaint){
+throw new LHError(LHError.errors.NO_FMP);
+}
+
+return Promise.resolve({
+timing:traceOfTab.timings.firstMeaningfulPaint,
+timestamp:traceOfTab.timestamps.firstMeaningfulPaint});
+
+}}
+
+
+module.exports=FirstMeaningfulPaint;
+
+},{"../../../lib/errors":33,"./metric":13}],"./gather/computed/metrics/interactive":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const MetricArtifact=require('./metric');
+
+const NetworkRecorder=require('../../../lib/network-recorder');
+const TracingProcessor=require('../../../lib/traces/tracing-processor');
+const LHError=require('../../../lib/errors');
+
+const REQUIRED_QUIET_WINDOW=5000;
+const ALLOWED_CONCURRENT_REQUESTS=2;
+
+
+
+
+
+
+class Interactive extends MetricArtifact{
+get name(){
+return'Interactive';
+}
+
+
+
+
+
+
+
+
+static _findNetworkQuietPeriods(networkRecords,traceOfTab){
+const traceEndTsInMs=traceOfTab.timestamps.traceEnd/1000;
+
+const filteredNetworkRecords=networkRecords.filter(record=>{
+return record.finished&&record.requestMethod==='GET'&&!record.failed&&
+
+record.statusCode<400;
+});
+return NetworkRecorder.findNetworkQuietPeriods(filteredNetworkRecords,
+ALLOWED_CONCURRENT_REQUESTS,traceEndTsInMs);
+}
+
+
+
+
+
+
+
+static _findCPUQuietPeriods(longTasks,traceOfTab){
+const navStartTsInMs=traceOfTab.timestamps.navigationStart/1000;
+const traceEndTsInMs=traceOfTab.timestamps.traceEnd/1000;
+if(longTasks.length===0){
+return[{start:0,end:traceEndTsInMs}];
+}
+
+
+const quietPeriods=[];
+longTasks.forEach((task,index)=>{
+if(index===0){
+quietPeriods.push({
+start:0,
+end:task.start+navStartTsInMs});
+
+}
+
+if(index===longTasks.length-1){
+quietPeriods.push({
+start:task.end+navStartTsInMs,
+end:traceEndTsInMs});
+
+}else{
+quietPeriods.push({
+start:task.end+navStartTsInMs,
+end:longTasks[index+1].start+navStartTsInMs});
+
+}
+});
+
+return quietPeriods;
+}
+
+
+
+
+
+
+
+
+static findOverlappingQuietPeriods(longTasks,networkRecords,traceOfTab){
+const FcpTsInMs=traceOfTab.timestamps.firstContentfulPaint/1000;
+
+
+const isLongEnoughQuietPeriod=period=>
+period.end>FcpTsInMs+REQUIRED_QUIET_WINDOW&&
+period.end-period.start>=REQUIRED_QUIET_WINDOW;
+const networkQuietPeriods=this._findNetworkQuietPeriods(networkRecords,traceOfTab).
+filter(isLongEnoughQuietPeriod);
+const cpuQuietPeriods=this._findCPUQuietPeriods(longTasks,traceOfTab).
+filter(isLongEnoughQuietPeriod);
+
+const cpuQueue=cpuQuietPeriods.slice();
+const networkQueue=networkQuietPeriods.slice();
+
+
+let cpuCandidate=cpuQueue.shift();
+let networkCandidate=networkQueue.shift();
+while(cpuCandidate&&networkCandidate){
+if(cpuCandidate.start>=networkCandidate.start){
+
+if(networkCandidate.end>=cpuCandidate.start+REQUIRED_QUIET_WINDOW){
+return{
+cpuQuietPeriod:cpuCandidate,
+networkQuietPeriod:networkCandidate,
+cpuQuietPeriods,
+networkQuietPeriods};
+
+}else{
+networkCandidate=networkQueue.shift();
+}
+}else{
+
+if(cpuCandidate.end>=networkCandidate.start+REQUIRED_QUIET_WINDOW){
+return{
+cpuQuietPeriod:cpuCandidate,
+networkQuietPeriod:networkCandidate,
+cpuQuietPeriods,
+networkQuietPeriods};
+
+}else{
+cpuCandidate=cpuQueue.shift();
+}
+}
+}
+
+throw new LHError(
+cpuCandidate?
+LHError.errors.NO_TTI_NETWORK_IDLE_PERIOD:
+LHError.errors.NO_TTI_CPU_IDLE_PERIOD);
+
+}
+
+
+
+
+
+computeObservedMetric(data){
+const{traceOfTab,networkRecords}=data;
+if(!traceOfTab.timestamps.firstContentfulPaint){
+throw new LHError(LHError.errors.NO_FCP);
+}
+
+if(!traceOfTab.timestamps.domContentLoaded){
+throw new LHError(LHError.errors.NO_DCL);
+}
+
+const longTasks=TracingProcessor.getMainThreadTopLevelEvents(traceOfTab).
+filter(event=>event.duration>=50);
+const quietPeriodInfo=Interactive.findOverlappingQuietPeriods(
+longTasks,
+networkRecords,
+traceOfTab);
+
+
+const cpuQuietPeriod=quietPeriodInfo.cpuQuietPeriod;
+
+const timestamp=Math.max(
+cpuQuietPeriod.start,
+traceOfTab.timestamps.firstContentfulPaint/1000,
+traceOfTab.timestamps.domContentLoaded/1000)*
+1000;
+const timing=(timestamp-traceOfTab.timestamps.navigationStart)/1000;
+return Promise.resolve({timing,timestamp});
+}}
+
+
+module.exports=Interactive;
+
+
+
+
+
+
+
+},{"../../../lib/errors":33,"../../../lib/network-recorder":37,"../../../lib/traces/tracing-processor":46,"./metric":13}],"./gather/computed/metrics/lantern-estimated-input-latency":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const LanternMetricArtifact=require('./lantern-metric');
+const Node=require('../../../lib/dependency-graph/node');
+const EstimatedInputLatency=require('./estimated-input-latency');
+
+class LanternEstimatedInputLatency extends LanternMetricArtifact{
+get name(){
+return'LanternEstimatedInputLatency';
+}
+
+
+
+
+get COEFFICIENTS(){
+return{
+intercept:0,
+optimistic:0.4,
+pessimistic:0.4};
+
+}
+
+
+
+
+
+getOptimisticGraph(dependencyGraph){
+return dependencyGraph;
+}
+
+
+
+
+
+getPessimisticGraph(dependencyGraph){
+return dependencyGraph;
+}
+
+
+
+
+
+
+getEstimateFromSimulation(simulation,extras){
+
+
+const fmpTimeInMs=extras.optimistic?
+extras.fmpResult.pessimisticEstimate.timeInMs:
+extras.fmpResult.optimisticEstimate.timeInMs;
+
+const events=LanternEstimatedInputLatency.getEventsAfterFMP(
+simulation.nodeTimings,
+fmpTimeInMs);
+
+
+return{
+timeInMs:EstimatedInputLatency.calculateRollingWindowEIL(events),
+nodeTimings:simulation.nodeTimings};
+
+}
+
+
+
+
+
+
+async compute_(data,artifacts){
+const fmpResult=await artifacts.requestLanternFirstMeaningfulPaint(data);
+return this.computeMetricWithGraphs(data,artifacts,{fmpResult});
+}
+
+
+
+
+
+static getEventsAfterFMP(nodeTimings,fmpTimeInMs){
+
+const events=[];
+for(const[node,timing]of nodeTimings.entries()){
+if(node.type!==Node.TYPES.CPU)continue;
+if(!timing.endTime||!timing.startTime)continue;
+if(timing.endTime<fmpTimeInMs)continue;
+
+events.push({
+start:timing.startTime,
+end:timing.endTime,
+duration:timing.endTime-timing.startTime});
+
+}
+
+return events;
+}}
+
+
+module.exports=LanternEstimatedInputLatency;
+
+},{"../../../lib/dependency-graph/node":25,"./estimated-input-latency":"./gather/computed/metrics/estimated-input-latency","./lantern-metric":12}],"./gather/computed/metrics/lantern-first-contentful-paint":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const MetricArtifact=require('./lantern-metric');
+const Node=require('../../../lib/dependency-graph/node');
+const CPUNode=require('../../../lib/dependency-graph/cpu-node');
+const NetworkNode=require('../../../lib/dependency-graph/network-node');
+
+class FirstContentfulPaint extends MetricArtifact{
+get name(){
+return'LanternFirstContentfulPaint';
+}
+
+
+
+
+get COEFFICIENTS(){
+return{
+intercept:600,
+optimistic:0.6,
+pessimistic:0.5};
+
+}
+
+
+
+
+
+
+getOptimisticGraph(dependencyGraph,traceOfTab){
+const fcp=traceOfTab.timestamps.firstContentfulPaint;
+const blockingScriptUrls=MetricArtifact.getScriptUrls(dependencyGraph,node=>{
+return(
+node.endTime<=fcp&&node.hasRenderBlockingPriority()&&node.initiatorType!=='script');
+
+});
+
+return dependencyGraph.cloneWithRelationships(node=>{
+if(node.endTime>fcp&&!node.isMainDocument())return false;
+
+if(node.type===Node.TYPES.CPU){
+return node.isEvaluateScriptFor(blockingScriptUrls);
+}
+
+const asNetworkNode=node;
+
+return asNetworkNode.hasRenderBlockingPriority()&&asNetworkNode.initiatorType!=='script';
+});
+}
+
+
+
+
+
+
+getPessimisticGraph(dependencyGraph,traceOfTab){
+const fcp=traceOfTab.timestamps.firstContentfulPaint;
+const blockingScriptUrls=MetricArtifact.getScriptUrls(dependencyGraph,node=>{
+return node.endTime<=fcp&&node.hasRenderBlockingPriority();
+});
+
+return dependencyGraph.cloneWithRelationships(node=>{
+if(node.endTime>fcp&&!node.isMainDocument())return false;
+
+if(node.type===Node.TYPES.CPU){
+return node.isEvaluateScriptFor(blockingScriptUrls);
+}
+
+
+return node.hasRenderBlockingPriority();
+});
+}}
+
+
+module.exports=FirstContentfulPaint;
+
+},{"../../../lib/dependency-graph/cpu-node":23,"../../../lib/dependency-graph/network-node":24,"../../../lib/dependency-graph/node":25,"./lantern-metric":12}],"./gather/computed/metrics/lantern-first-cpu-idle":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Node=require('../../../lib/dependency-graph/node');
+const CPUNode=require('../../../lib/dependency-graph/cpu-node');
+const NetworkNode=require('../../../lib/dependency-graph/network-node');
+
+const FirstCPUIdle=require('./first-cpu-idle');
+const LanternInteractive=require('./lantern-interactive');
+
+class LanternFirstCPUIdle extends LanternInteractive{
+get name(){
+return'LanternFirstCPUIdle';
+}
+
+
+
+
+
+
+getEstimateFromSimulation(simulation,extras){
+const fmpTimeInMs=extras.optimistic?
+extras.fmpResult.optimisticEstimate.timeInMs:
+extras.fmpResult.pessimisticEstimate.timeInMs;
+
+return{
+timeInMs:LanternFirstCPUIdle.getFirstCPUIdleWindowStart(simulation.nodeTimings,fmpTimeInMs),
+nodeTimings:simulation.nodeTimings};
+
+}
+
+
+
+
+
+
+static getFirstCPUIdleWindowStart(nodeTimings,fmpTimeInMs,longTaskLength=50){
+
+const longTasks=[];
+for(const[node,timing]of nodeTimings.entries()){
+if(node.type!==Node.TYPES.CPU)continue;
+if(!timing.endTime||!timing.startTime)continue;
+if(timing.endTime-timing.startTime<longTaskLength)continue;
+longTasks.push({start:timing.startTime,end:timing.endTime});
+}
+
+return FirstCPUIdle.findQuietWindow(fmpTimeInMs,Infinity,longTasks);
+}}
+
+
+module.exports=LanternFirstCPUIdle;
+
+},{"../../../lib/dependency-graph/cpu-node":23,"../../../lib/dependency-graph/network-node":24,"../../../lib/dependency-graph/node":25,"./first-cpu-idle":"./gather/computed/metrics/first-cpu-idle","./lantern-interactive":"./gather/computed/metrics/lantern-interactive"}],"./gather/computed/metrics/lantern-first-meaningful-paint":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const MetricArtifact=require('./lantern-metric');
+const Node=require('../../../lib/dependency-graph/node');
+const CPUNode=require('../../../lib/dependency-graph/cpu-node');
+const NetworkNode=require('../../../lib/dependency-graph/network-node');
+
+class FirstMeaningfulPaint extends MetricArtifact{
+get name(){
+return'LanternFirstMeaningfulPaint';
+}
+
+
+
+
+get COEFFICIENTS(){
+return{
+intercept:900,
+optimistic:0.45,
+pessimistic:0.6};
+
+}
+
+
+
+
+
+
+getOptimisticGraph(dependencyGraph,traceOfTab){
+const fmp=traceOfTab.timestamps.firstMeaningfulPaint;
+const blockingScriptUrls=MetricArtifact.getScriptUrls(dependencyGraph,node=>{
+return(
+node.endTime<=fmp&&node.hasRenderBlockingPriority()&&node.initiatorType!=='script');
+
+});
+
+return dependencyGraph.cloneWithRelationships(node=>{
+if(node.endTime>fmp&&!node.isMainDocument())return false;
+
+if(node.type===Node.TYPES.CPU){
+return node.isEvaluateScriptFor(blockingScriptUrls);
+}
+
+const asNetworkNode=node;
+
+return asNetworkNode.hasRenderBlockingPriority()&&asNetworkNode.initiatorType!=='script';
+});
+}
+
+
+
+
+
+
+getPessimisticGraph(dependencyGraph,traceOfTab){
+const fmp=traceOfTab.timestamps.firstMeaningfulPaint;
+const requiredScriptUrls=MetricArtifact.getScriptUrls(dependencyGraph,node=>{
+return node.endTime<=fmp&&node.hasRenderBlockingPriority();
+});
+
+return dependencyGraph.cloneWithRelationships(node=>{
+if(node.endTime>fmp&&!node.isMainDocument())return false;
+
+
+if(node.type===Node.TYPES.CPU){
+const asCpuNode=node;
+return asCpuNode.didPerformLayout()||asCpuNode.isEvaluateScriptFor(requiredScriptUrls);
+}
+
+
+return node.hasRenderBlockingPriority();
+});
+}
+
+
+
+
+
+
+async compute_(data,artifacts){
+const fcpResult=await artifacts.requestLanternFirstContentfulPaint(data);
+const metricResult=await this.computeMetricWithGraphs(data,artifacts);
+metricResult.timing=Math.max(metricResult.timing,fcpResult.timing);
+return metricResult;
+}}
+
+
+module.exports=FirstMeaningfulPaint;
+
+},{"../../../lib/dependency-graph/cpu-node":23,"../../../lib/dependency-graph/network-node":24,"../../../lib/dependency-graph/node":25,"./lantern-metric":12}],"./gather/computed/metrics/lantern-interactive":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const MetricArtifact=require('./lantern-metric');
+const Node=require('../../../lib/dependency-graph/node');
+const CPUNode=require('../../../lib/dependency-graph/cpu-node');
+const NetworkNode=require('../../../lib/dependency-graph/network-node');
+const WebInspector=require('../../../lib/web-inspector');
+
+
+const CRITICAL_LONG_TASK_THRESHOLD=20;
+
+class Interactive extends MetricArtifact{
+get name(){
+return'LanternInteractive';
+}
+
+
+
+
+get COEFFICIENTS(){
+return{
+intercept:1600,
+optimistic:0.6,
+pessimistic:0.45};
+
+}
+
+
+
+
+
+getOptimisticGraph(dependencyGraph){
+
+const minimumCpuTaskDuration=CRITICAL_LONG_TASK_THRESHOLD*1000;
+
+return dependencyGraph.cloneWithRelationships(node=>{
+
+if(node.type===Node.TYPES.CPU){
+return node.event.dur>minimumCpuTaskDuration;
+}
+
+const asNetworkNode=node;
+
+const isImage=asNetworkNode.record._resourceType===WebInspector.resourceTypes.Image;
+const isScript=asNetworkNode.record._resourceType===WebInspector.resourceTypes.Script;
+return(
+!isImage&&(
+isScript||
+asNetworkNode.record.priority()==='High'||
+asNetworkNode.record.priority()==='VeryHigh'));
+
+});
+}
+
+
+
+
+
+getPessimisticGraph(dependencyGraph){
+return dependencyGraph;
+}
+
+
+
+
+
+
+getEstimateFromSimulation(simulationResult,extras){
+const lastTaskAt=Interactive.getLastLongTaskEndTime(simulationResult.nodeTimings);
+const minimumTime=extras.optimistic?
+extras.fmpResult.optimisticEstimate.timeInMs:
+extras.fmpResult.pessimisticEstimate.timeInMs;
+return{
+timeInMs:Math.max(minimumTime,lastTaskAt),
+nodeTimings:simulationResult.nodeTimings};
+
+}
+
+
+
+
+
+
+async compute_(data,artifacts){
+const fmpResult=await artifacts.requestLanternFirstMeaningfulPaint(data);
+const metricResult=await this.computeMetricWithGraphs(data,artifacts,{fmpResult});
+metricResult.timing=Math.max(metricResult.timing,fmpResult.timing);
+return metricResult;
+}
+
+
+
+
+
+static getLastLongTaskEndTime(nodeTimings,duration=50){
+
+return Array.from(nodeTimings.entries()).
+filter(([node,timing])=>{
+if(node.type!==Node.TYPES.CPU)return false;
+if(!timing.endTime||!timing.startTime)return false;
+return timing.endTime-timing.startTime>duration;
+}).
+map(([_,timing])=>timing.endTime).
+reduce((max,x)=>Math.max(max||0,x||0),0);
+}}
+
+
+module.exports=Interactive;
+
+},{"../../../lib/dependency-graph/cpu-node":23,"../../../lib/dependency-graph/network-node":24,"../../../lib/dependency-graph/node":25,"../../../lib/web-inspector":47,"./lantern-metric":12}],"./gather/computed/metrics/lantern-speed-index":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const MetricArtifact=require('./lantern-metric');
+const Node=require('../../../lib/dependency-graph/node');
+const CPUNode=require('../../../lib/dependency-graph/cpu-node');
+
+class SpeedIndex extends MetricArtifact{
+get name(){
+return'LanternSpeedIndex';
+}
+
+
+
+
+get COEFFICIENTS(){
+return{
+
+
+
+intercept:-250,
+optimistic:1.4,
+pessimistic:0.65};
+
+}
+
+
+
+
+
+getOptimisticGraph(dependencyGraph){
+return dependencyGraph;
+}
+
+
+
+
+
+getPessimisticGraph(dependencyGraph){
+return dependencyGraph;
+}
+
+
+
+
+
+
+getEstimateFromSimulation(simulationResult,extras){
+const fcpTimeInMs=extras.fcpResult.pessimisticEstimate.timeInMs;
+const estimate=extras.optimistic?
+extras.speedline.speedIndex:
+SpeedIndex.computeLayoutBasedSpeedIndex(simulationResult.nodeTimings,fcpTimeInMs);
+return{
+timeInMs:estimate,
+nodeTimings:simulationResult.nodeTimings};
+
+}
+
+
+
+
+
+
+async compute_(data,artifacts){
+const speedline=await artifacts.requestSpeedline(data.trace);
+const fcpResult=await artifacts.requestLanternFirstContentfulPaint(data);
+const metricResult=await this.computeMetricWithGraphs(data,artifacts,{
+speedline,
+fcpResult});
+
+metricResult.timing=Math.max(metricResult.timing,fcpResult.timing);
+return metricResult;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static computeLayoutBasedSpeedIndex(nodeTimings,fcpTimeInMs){
+
+const layoutWeights=[];
+for(const[node,timing]of nodeTimings.entries()){
+if(node.type!==Node.TYPES.CPU)continue;
+if(!timing.startTime||!timing.endTime)continue;
+
+const cpuNode=node;
+if(cpuNode.childEvents.some(x=>x.name==='Layout')){
+const timingWeight=Math.max(Math.log2(timing.endTime-timing.startTime),0);
+layoutWeights.push({time:timing.endTime,weight:timingWeight});
+}
+}
+
+if(!layoutWeights.length){
+return fcpTimeInMs;
+}
+
+const totalWeightedTime=layoutWeights.
+map(evt=>evt.weight*Math.max(evt.time,fcpTimeInMs)).
+reduce((a,b)=>a+b,0);
+const totalWeight=layoutWeights.map(evt=>evt.weight).reduce((a,b)=>a+b,0);
+return totalWeightedTime/totalWeight;
+}}
+
+
+module.exports=SpeedIndex;
+
+},{"../../../lib/dependency-graph/cpu-node":23,"../../../lib/dependency-graph/node":25,"./lantern-metric":12}],"./gather/computed/metrics/speed-index":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const MetricArtifact=require('./metric');
+
+class SpeedIndex extends MetricArtifact{
+get name(){
+return'SpeedIndex';
+}
+
+
+
+
+
+
+async computeObservedMetric(data,artifacts){
+const speedline=await artifacts.requestSpeedline(data.trace);
+const timing=Math.round(speedline.speedIndex);
+const timestamp=(timing+speedline.beginning)*1000;
+return Promise.resolve({timing,timestamp});
+}}
+
+
+module.exports=SpeedIndex;
+
+},{"./metric":13}],"./gather/computed/network-analysis":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('./computed-artifact');
+const NetworkAnalyzer=require('../../lib/dependency-graph/simulator/network-analyzer');
+
+class NetworkAnalysis extends ComputedArtifact{
+get name(){
+return'NetworkAnalysis';
+}
+
+
+
+
+
+static computeRTTAndServerResponseTime(records){
+
+
+const rttByOrigin=new Map();
+for(const[origin,summary]of NetworkAnalyzer.estimateRTTByOrigin(records).entries()){
+rttByOrigin.set(origin,summary.min);
+}
+
+
+
+const minimumRtt=Math.min(...Array.from(rttByOrigin.values()));
+
+const responseTimeSummaries=NetworkAnalyzer.estimateServerResponseTimeByOrigin(records,{
+rttByOrigin});
+
+
+
+const additionalRttByOrigin=new Map();
+
+const serverResponseTimeByOrigin=new Map();
+for(const[origin,summary]of responseTimeSummaries.entries()){
+
+
+const rttForOrigin=rttByOrigin.get(origin);
+additionalRttByOrigin.set(origin,rttForOrigin-minimumRtt);
+serverResponseTimeByOrigin.set(origin,summary.median);
+}
+
+return{rtt:minimumRtt,additionalRttByOrigin,serverResponseTimeByOrigin,throughput:0};
+}
+
+
+
+
+
+
+async compute_(devtoolsLog,computedArtifacts){
+const records=await computedArtifacts.requestNetworkRecords(devtoolsLog);
+const throughput=await computedArtifacts.requestNetworkThroughput(devtoolsLog);
+const rttAndServerResponseTime=NetworkAnalysis.computeRTTAndServerResponseTime(records);
+rttAndServerResponseTime.throughput=throughput*8;
+return rttAndServerResponseTime;
+}}
+
+
+module.exports=NetworkAnalysis;
+
+},{"../../lib/dependency-graph/simulator/network-analyzer":27,"./computed-artifact":11}],"./gather/computed/network-records":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('./computed-artifact');
+const NetworkRecorder=require('../../lib/network-recorder');
+
+class NetworkRecords extends ComputedArtifact{
+get name(){
+return'NetworkRecords';
+}
+
+
+
+
+
+async compute_(devtoolsLog){
+return NetworkRecorder.recordsFromLogs(devtoolsLog);
+}}
+
+
+module.exports=NetworkRecords;
+
+},{"../../lib/network-recorder":37,"./computed-artifact":11}],"./gather/computed/network-throughput":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('./computed-artifact');
+
+class NetworkThroughput extends ComputedArtifact{
+get name(){
+return'NetworkThroughput';
+}
+
+
+
+
+
+
+
+
+
+static getThroughput(networkRecords){
+let totalBytes=0;
+const timeBoundaries=networkRecords.reduce((boundaries,record)=>{
+const scheme=record.parsedURL&&record.parsedURL.scheme;
+if(scheme==='data'||record.failed||!record.finished||
+record.statusCode>300||!record.transferSize){
+return boundaries;
+}
+
+totalBytes+=record.transferSize;
+boundaries.push({time:record._responseReceivedTime,isStart:true});
+boundaries.push({time:record.endTime,isStart:false});
+return boundaries;
+},[]).sort((a,b)=>a.time-b.time);
+
+if(!timeBoundaries.length){
+return Infinity;
+}
+
+let inflight=0;
+let currentStart=0;
+let totalDuration=0;
+timeBoundaries.forEach(boundary=>{
+if(boundary.isStart){
+if(inflight===0){
+currentStart=boundary.time;
+}
+inflight++;
+}else{
+inflight--;
+if(inflight===0){
+totalDuration+=boundary.time-currentStart;
+}
+}
+});
+
+return totalBytes/totalDuration;
+}
+
+
+
+
+
+
+compute_(devtoolsLog,computedArtifacts){
+
+return computedArtifacts.requestNetworkRecords(devtoolsLog).
+then(NetworkThroughput.getThroughput);
+}}
+
+
+module.exports=NetworkThroughput;
+
+},{"./computed-artifact":11}],"./gather/computed/page-dependency-graph":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('./computed-artifact');
+const NetworkNode=require('../../lib/dependency-graph/network-node');
+const CPUNode=require('../../lib/dependency-graph/cpu-node');
+const NetworkAnalyzer=require('../../lib/dependency-graph/simulator/network-analyzer');
+const TracingProcessor=require('../../lib/traces/tracing-processor');
+const WebInspector=require('../../lib/web-inspector');
+
+const Node=require('../../lib/dependency-graph/node.js');
+
+
+const MINIMUM_TASK_DURATION_OF_INTEREST=10;
+
+
+const IGNORED_MIME_TYPES_REGEX=/^video/;
+
+class PageDependencyGraphArtifact extends ComputedArtifact{
+get name(){
+return'PageDependencyGraph';
+}
+
+
+
+
+
+static getNetworkInitiators(record){
+if(!record._initiator)return[];
+if(record._initiator.url)return[record._initiator.url];
+if(record._initiator.type==='script'&&record._initiator.stack){
+const frames=record._initiator.stack.callFrames;
+return Array.from(new Set(frames.map(frame=>frame.url))).filter(Boolean);
+}
+
+return[];
+}
+
+
+
+
+
+static getNetworkNodeOutput(networkRecords){
+
+const nodes=[];
+const idToNodeMap=new Map();
+const urlToNodeMap=new Map();
+
+networkRecords.forEach(record=>{
+if(IGNORED_MIME_TYPES_REGEX.test(record._mimeType))return;
+
+
+
+while(idToNodeMap.has(record.requestId)){
+record._requestId+=':duplicate';
+}
+
+const node=new NetworkNode(record);
+nodes.push(node);
+
+const list=urlToNodeMap.get(record.url)||[];
+list.push(node);
+
+idToNodeMap.set(record.requestId,node);
+urlToNodeMap.set(record.url,list);
+});
+
+return{nodes,idToNodeMap,urlToNodeMap};
+}
+
+
+
+
+
+static getCPUNodes(traceOfTab){
+
+const nodes=[];
+let i=0;
+
+const minimumEvtDur=MINIMUM_TASK_DURATION_OF_INTEREST*1000;
+while(i<traceOfTab.mainThreadEvents.length){
+const evt=traceOfTab.mainThreadEvents[i];
+
+
+if(
+!TracingProcessor.isScheduleableTask(evt)||
+!evt.dur||
+evt.dur<minimumEvtDur)
+{
+i++;
+continue;
+}
+
+
+
+const children=[];
+i++;
+for(
+const endTime=evt.ts+evt.dur;
+i<traceOfTab.mainThreadEvents.length&&traceOfTab.mainThreadEvents[i].ts<endTime;
+i++)
+{
+children.push(traceOfTab.mainThreadEvents[i]);
+}
+
+nodes.push(new CPUNode(evt,children));
+}
+
+return nodes;
+}
+
+
+
+
+
+static linkNetworkNodes(rootNode,networkNodeOutput){
+networkNodeOutput.nodes.forEach(node=>{
+const initiators=PageDependencyGraphArtifact.getNetworkInitiators(node.record);
+if(initiators.length){
+initiators.forEach(initiator=>{
+const parentCandidates=networkNodeOutput.urlToNodeMap.get(initiator)||[rootNode];
+
+const parent=parentCandidates.length===1?parentCandidates[0]:rootNode;
+node.addDependency(parent);
+});
+}else if(node!==rootNode){
+rootNode.addDependent(node);
+}
+
+const redirects=Array.from(node.record.redirects||[]);
+redirects.push(node.record);
+
+for(let i=1;i<redirects.length;i++){
+const redirectNode=networkNodeOutput.idToNodeMap.get(redirects[i-1].requestId);
+const actualNode=networkNodeOutput.idToNodeMap.get(redirects[i].requestId);
+if(actualNode&&redirectNode){
+actualNode.addDependency(redirectNode);
+}
+}
+});
+}
+
+
+
+
+
+
+static linkCPUNodes(rootNode,networkNodeOutput,cpuNodes){
+
+function addDependentNetworkRequest(cpuNode,reqId){
+const networkNode=networkNodeOutput.idToNodeMap.get(reqId);
+if(!networkNode||
+
+networkNode.record._resourceType!==WebInspector.resourceTypes.XHR||
+
+
+networkNode.startTime<=cpuNode.startTime)return;
+cpuNode.addDependent(networkNode);
+}
+
+
+function addDependencyOnUrl(cpuNode,url){
+if(!url)return;
+
+
+const minimumAllowableTimeSinceNetworkNodeEnd=-100*1000;
+const candidates=networkNodeOutput.urlToNodeMap.get(url)||[];
+
+let minCandidate=null;
+let minDistance=Infinity;
+
+for(const candidate of candidates){
+
+
+if(cpuNode.startTime<=candidate.startTime)return;
+
+const distance=cpuNode.startTime-candidate.endTime;
+if(distance>=minimumAllowableTimeSinceNetworkNodeEnd&&distance<minDistance){
+minCandidate=candidate;
+minDistance=distance;
+}
+}
+
+if(!minCandidate)return;
+cpuNode.addDependency(minCandidate);
+}
+
+
+const timers=new Map();
+for(const node of cpuNodes){
+for(const evt of node.childEvents){
+if(!evt.args.data)continue;
+
+const argsUrl=evt.args.data.url;
+const stackTraceUrls=(evt.args.data.stackTrace||[]).map(l=>l.url).filter(Boolean);
+
+switch(evt.name){
+case'TimerInstall':
+
+timers.set(evt.args.data.timerId,node);
+stackTraceUrls.forEach(url=>addDependencyOnUrl(node,url));
+break;
+case'TimerFire':{
+
+const installer=timers.get(evt.args.data.timerId);
+if(!installer)break;
+installer.addDependent(node);
+break;
+}
+
+case'InvalidateLayout':
+case'ScheduleStyleRecalculation':
+stackTraceUrls.forEach(url=>addDependencyOnUrl(node,url));
+break;
+
+case'EvaluateScript':
+
+addDependencyOnUrl(node,argsUrl);
+stackTraceUrls.forEach(url=>addDependencyOnUrl(node,url));
+break;
+
+case'XHRReadyStateChange':
+
+
+if(evt.args.data.readyState!==4)break;
+
+
+addDependencyOnUrl(node,argsUrl);
+stackTraceUrls.forEach(url=>addDependencyOnUrl(node,url));
+break;
+
+case'FunctionCall':
+case'v8.compile':
+
+addDependencyOnUrl(node,argsUrl);
+break;
+
+case'ParseAuthorStyleSheet':
+
+addDependencyOnUrl(node,evt.args.data.styleSheetUrl);
+break;
+
+case'ResourceSendRequest':
+
+addDependentNetworkRequest(node,evt.args.data.requestId);
+stackTraceUrls.forEach(url=>addDependencyOnUrl(node,url));
+break;}
+
+}
+
+if(node.getNumberOfDependencies()===0){
+node.addDependency(rootNode);
+}
+}
+}
+
+
+
+
+
+
+static createGraph(traceOfTab,networkRecords){
+const networkNodeOutput=PageDependencyGraphArtifact.getNetworkNodeOutput(networkRecords);
+const cpuNodes=PageDependencyGraphArtifact.getCPUNodes(traceOfTab);
+
+const rootRequest=networkRecords.reduce((min,r)=>min.startTime<r.startTime?min:r);
+const rootNode=networkNodeOutput.idToNodeMap.get(rootRequest.requestId);
+const mainDocumentRequest=NetworkAnalyzer.findMainDocument(networkRecords);
+const mainDocumentNode=networkNodeOutput.idToNodeMap.get(mainDocumentRequest.requestId);
+
+if(!rootNode||!mainDocumentNode){
+
+throw new Error(`${rootNode?'mainDocument':'root'}Node not found.`);
+}
+
+PageDependencyGraphArtifact.linkNetworkNodes(rootNode,networkNodeOutput);
+PageDependencyGraphArtifact.linkCPUNodes(rootNode,networkNodeOutput,cpuNodes);
+mainDocumentNode.setIsMainDocument(true);
+
+if(NetworkNode.hasCycle(rootNode)){
+throw new Error('Invalid dependency graph created, cycle detected');
+}
+
+return rootNode;
+}
+
+
+
+
+
+static printGraph(rootNode,widthInCharacters=100){
+
+function padRight(str,target,padChar=' '){
+return str+padChar.repeat(Math.max(target-str.length,0));
+}
+
+
+const nodes=[];
+rootNode.traverse(node=>nodes.push(node));
+nodes.sort((a,b)=>a.startTime-b.startTime);
+
+const min=nodes[0].startTime;
+const max=nodes.reduce((max,node)=>Math.max(max,node.endTime),0);
+
+const totalTime=max-min;
+const timePerCharacter=totalTime/widthInCharacters;
+nodes.forEach(node=>{
+const offset=Math.round((node.startTime-min)/timePerCharacter);
+const length=Math.ceil((node.endTime-node.startTime)/timePerCharacter);
+const bar=padRight('',offset)+padRight('',length,'=');
+
+
+const displayName=node.record?node.record._url:node.type;
+
+console.log(padRight(bar,widthInCharacters),`| ${displayName.slice(0,30)}`);
+});
+}
+
+
+
+
+
+
+async compute_(data,artifacts){
+const trace=data.trace;
+const devtoolsLog=data.devtoolsLog;
+const[traceOfTab,networkRecords]=await Promise.all([
+artifacts.requestTraceOfTab(trace),
+artifacts.requestNetworkRecords(devtoolsLog)]);
+
+
+return PageDependencyGraphArtifact.createGraph(traceOfTab,networkRecords);
+}}
+
+
+module.exports=PageDependencyGraphArtifact;
+
+
+
+
+
+
+
+
+},{"../../lib/dependency-graph/cpu-node":23,"../../lib/dependency-graph/network-node":24,"../../lib/dependency-graph/node.js":25,"../../lib/dependency-graph/simulator/network-analyzer":27,"../../lib/traces/tracing-processor":46,"../../lib/web-inspector":47,"./computed-artifact":11}],"./gather/computed/pushed-requests":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('./computed-artifact');
+
+class PushedRequests extends ComputedArtifact{
+get name(){
+return'PushedRequests';
+}
+
+
+
+
+
+
+
+compute_(devtoolsLog,artifacts){
+return artifacts.requestNetworkRecords(devtoolsLog).then(records=>{
+const pushedRecords=records.filter(r=>r._timing&&!!r._timing.pushStart);
+return pushedRecords;
+});
+}}
+
+
+module.exports=PushedRequests;
+
+},{"./computed-artifact":11}],"./gather/computed/screenshots":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('./computed-artifact');
+
+class ScreenshotFilmstrip extends ComputedArtifact{
+get name(){
+return'Screenshots';
+}
+
+
+
+
+
+fetchScreenshot(frame){
+return frame.
+imageDataPromise().
+then(data=>'data:image/jpg;base64,'+data);
+}
+
+
+
+
+
+
+compute_(trace,computedArtifacts){
+return computedArtifacts.requestDevtoolsTimelineModel(trace).then(model=>{
+const filmStripFrames=model.filmStripModel().frames();
+const frameFetches=filmStripFrames.map(frame=>this.fetchScreenshot(frame));
+
+return Promise.all(frameFetches).then(images=>{
+const result=filmStripFrames.map((frame,i)=>({
+timestamp:frame.timestamp,
+datauri:images[i]}));
+
+return result;
+});
+});
+}}
+
+
+module.exports=ScreenshotFilmstrip;
+
+},{"./computed-artifact":11}],"./gather/computed/speedline":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('./computed-artifact');
+const speedline=require('speedline');
+const LHError=require('../../lib/errors');
+
+class Speedline extends ComputedArtifact{
+get name(){
+return'Speedline';
+}
+
+
+
+
+
+
+compute_(trace,computedArtifacts){
+
+
+return computedArtifacts.requestTraceOfTab(trace).then(traceOfTab=>{
+
+
+const traceEvents=trace.traceEvents.slice();
+
+
+const navStart=traceOfTab.timestamps.navigationStart;
+return speedline(traceEvents,{
+timeOrigin:navStart,
+fastMode:true,
+include:'speedIndex'});
+
+}).catch(err=>{
+if(/No screenshots found in trace/.test(err.message)){
+throw new LHError(LHError.errors.NO_SCREENSHOTS);
+}
+
+throw err;
+}).then(speedline=>{
+if(speedline.frames.length===0){
+throw new LHError(LHError.errors.NO_SPEEDLINE_FRAMES);
+}
+
+if(speedline.speedIndex===0){
+throw new LHError(LHError.errors.SPEEDINDEX_OF_ZERO);
+}
+
+return speedline;
+});
+}}
+
+
+module.exports=Speedline;
+
+},{"../../lib/errors":33,"./computed-artifact":11,"speedline":152}],"./gather/computed/trace-of-tab":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+
+
+
+
+
+
+
+
+
+
+
+const ComputedArtifact=require('./computed-artifact');
+const log=require('lighthouse-logger');
+const LHError=require('../../lib/errors');
+const Sentry=require('../../lib/sentry');
+
+
+
+
+const WebInspector=require('../../lib/web-inspector');
+
+class TraceOfTab extends ComputedArtifact{
+get name(){
+return'TraceOfTab';
+}
+
+
+
+
+
+
+
+async compute_(trace){
+
+
+
+const keyEvents=trace.traceEvents.
+filter(e=>{
+return e.cat.includes('blink.user_timing')||
+e.cat.includes('loading')||
+e.cat.includes('devtools.timeline')||
+e.name==='TracingStartedInPage';
+}).
+
+stableSort((event0,event1)=>event0.ts-event1.ts);
+
+
+
+const startedInPageEvt=keyEvents.find(e=>e.name==='TracingStartedInPage');
+if(!startedInPageEvt)throw new LHError(LHError.errors.NO_TRACING_STARTED);
+
+const frameId=startedInPageEvt.args.data.page;
+
+const frameEvents=keyEvents.filter(e=>e.args.frame===frameId);
+
+
+const navigationStart=frameEvents.filter(e=>e.name==='navigationStart').pop();
+if(!navigationStart)throw new LHError(LHError.errors.NO_NAVSTART);
+
+
+const firstPaint=frameEvents.find(e=>e.name==='firstPaint'&&e.ts>navigationStart.ts);
+
+
+const firstContentfulPaint=frameEvents.find(
+e=>e.name==='firstContentfulPaint'&&e.ts>navigationStart.ts);
+
+
+
+let firstMeaningfulPaint=frameEvents.find(
+e=>e.name==='firstMeaningfulPaint'&&e.ts>navigationStart.ts);
+
+let fmpFellBack=false;
+
+
+
+
+
+if(!firstMeaningfulPaint){
+
+
+Sentry.captureMessage('No firstMeaningfulPaint found, using fallback',{level:'warning'});
+
+const fmpCand='firstMeaningfulPaintCandidate';
+fmpFellBack=true;
+log.verbose('trace-of-tab',`No firstMeaningfulPaint found, falling back to last ${fmpCand}`);
+const lastCandidate=frameEvents.filter(e=>e.name===fmpCand).pop();
+if(!lastCandidate){
+log.verbose('trace-of-tab','No `firstMeaningfulPaintCandidate` events found in trace');
+}
+firstMeaningfulPaint=lastCandidate;
+}
+
+const load=frameEvents.find(e=>e.name==='loadEventEnd'&&e.ts>navigationStart.ts);
+const domContentLoaded=frameEvents.find(
+e=>e.name==='domContentLoadedEventEnd'&&e.ts>navigationStart.ts);
+
+
+
+
+
+const processEvents=trace.traceEvents.
+filter(e=>e.pid===startedInPageEvt.pid).
+
+stableSort((event0,event1)=>event0.ts-event1.ts);
+
+const mainThreadEvents=processEvents.
+filter(e=>e.tid===startedInPageEvt.tid);
+
+const traceEnd=trace.traceEvents.reduce((max,evt)=>{
+return max.ts>evt.ts?max:evt;
+});
+
+const metrics={
+navigationStart,
+firstPaint,
+firstContentfulPaint,
+firstMeaningfulPaint,
+traceEnd:{ts:traceEnd.ts+(traceEnd.dur||0)},
+load,
+domContentLoaded};
+
+
+const timings={};
+const timestamps={};
+
+Object.keys(metrics).forEach(metric=>{
+timestamps[metric]=metrics[metric]&&metrics[metric].ts;
+timings[metric]=(timestamps[metric]-navigationStart.ts)/1000;
+});
+
+
+
+
+return{
+timings:timings,
+timestamps:timestamps,
+processEvents,
+mainThreadEvents,
+startedInPageEvt,
+navigationStartEvt:navigationStart,
+firstPaintEvt:firstPaint,
+firstContentfulPaintEvt:firstContentfulPaint,
+firstMeaningfulPaintEvt:firstMeaningfulPaint,
+loadEvt:load,
+domContentLoadedEvt:domContentLoaded,
+fmpFellBack};
+
+}}
+
+
+module.exports=TraceOfTab;
+
+},{"../../lib/errors":33,"../../lib/sentry":39,"../../lib/web-inspector":47,"./computed-artifact":11,"lighthouse-logger":143}],1:[function(require,module,exports){
 
 
 
@@ -12489,7 +14595,7 @@
 const isNotApplicable=notApplicables.find(result=>result.id===this.meta.name);
 if(isNotApplicable){
 return{
-rawValue:false,
+rawValue:true,
 notApplicable:true};
 
 }
@@ -12497,29 +14603,29 @@
 const violations=artifacts.Accessibility.violations||[];
 const rule=violations.find(result=>result.id===this.meta.name);
 
-let nodeDetails=[];
+
+let items=[];
 if(rule&&rule.nodes){
-nodeDetails=rule.nodes.map(node=>({
+items=rule.nodes.map(node=>({
+node:{
 type:'node',
 selector:Array.isArray(node.target)?node.target.join(' '):'',
 path:node.path,
-snippet:node.snippet}));
+snippet:node.snippet}}));
+
 
 }
 
+const headings=[
+{key:'node',itemType:'node',text:'Failing Elements'}];
+
+
 return{
 rawValue:typeof rule==='undefined',
 extendedInfo:{
 value:rule},
 
-details:{
-type:'list',
-header:{
-type:'text',
-text:'View failing elements'},
-
-items:nodeDetails}};
-
+details:Audit.makeTableDetails(headings,items)};
 
 }}
 
@@ -12532,13 +14638,20 @@
 
 
 
-
 'use strict';
 
 const statistics=require('../lib/statistics');
+const Util=require('../report/html/renderer/util');
 
 const DEFAULT_PASS='defaultPass';
 
+
+
+
+
+
+const clampTo2Decimals=val=>Math.round(val*100)/100;
+
 class Audit{
 
 
@@ -12553,7 +14666,11 @@
 static get SCORING_MODES(){
 return{
 NUMERIC:'numeric',
-BINARY:'binary'};
+BINARY:'binary',
+MANUAL:'manual',
+INFORMATIVE:'informative',
+NOT_APPLICABLE:'not-applicable',
+ERROR:'error'};
 
 }
 
@@ -12567,6 +14684,27 @@
 
 
 
+static get defaultOptions(){
+return{};
+}
+
+
+
+
+
+
+
+
+
+static audit(artifacts,context){
+throw new Error('audit() method must be overriden');
+}
+
+
+
+
+
+
 
 
 
@@ -12580,10 +14718,10 @@
 diminishingReturnsValue);
 
 
-let score=100*distribution.computeComplementaryPercentile(measuredValue);
-score=Math.min(100,score);
+let score=distribution.computeComplementaryPercentile(measuredValue);
+score=Math.min(1,score);
 score=Math.max(0,score);
-return Math.round(score);
+return clampTo2Decimals(score);
 }
 
 
@@ -12591,11 +14729,10 @@
 
 
 
-static generateErrorAuditResult(audit,debugString){
+static generateErrorAuditResult(audit,errorMessage){
 return Audit.generateAuditResult(audit,{
 rawValue:null,
-error:true,
-debugString});
+errorMessage});
 
 }
 
@@ -12605,47 +14742,44 @@
 
 
 
-
-static makeTableRows(headings,results){
-const tableRows=results.map(item=>{
-return headings.map(heading=>{
-const value=item[heading.key];
-if(typeof value==='object'&&value&&value.type)return value;
-
-return{
-type:heading.itemType,
-text:value};
-
-});
-});
-return tableRows;
-}
-
-
-
-
-
-static makeTableHeaders(headings){
-return headings.map(heading=>({
-type:'text',
-itemType:heading.itemType,
-text:heading.text}));
-
-}
-
-
-
-
-
-
-static makeTableDetails(headings,results){
-const tableHeaders=Audit.makeTableHeaders(headings);
-const tableRows=Audit.makeTableRows(headings,results);
+static makeTableDetails(headings,results,summary){
+if(results.length===0){
 return{
 type:'table',
-header:'View Details',
-itemHeaders:tableHeaders,
-items:tableRows};
+headings:[],
+items:[],
+summary};
+
+}
+
+return{
+type:'table',
+headings:headings,
+items:results,
+summary};
+
+}
+
+
+
+
+
+
+static _normalizeAuditScore(audit,result){
+
+let score=result.score===undefined?Number(result.rawValue):result.score;
+
+if(!Number.isFinite(score))throw new Error(`Invalid score: ${score}`);
+if(score>1)throw new Error(`Audit score for ${audit.meta.name} is > 1`);
+if(score<0)throw new Error(`Audit score for ${audit.meta.name} is < 0`);
+
+score=clampTo2Decimals(score);
+
+const scoreDisplayMode=audit.meta.scoreDisplayMode||Audit.SCORING_MODES.BINARY;
+
+return{
+score,
+scoreDisplayMode};
 
 }
 
@@ -12659,36 +14793,45 @@
 throw new Error('generateAuditResult requires a rawValue');
 }
 
-const score=typeof result.score==='undefined'?result.rawValue:result.score;
-let displayValue=result.displayValue;
-if(typeof displayValue==='undefined'){
-displayValue=result.rawValue?result.rawValue:'';
-}
+
+let{score,scoreDisplayMode}=Audit._normalizeAuditScore(audit,result);
 
 
-if(displayValue===score){
-displayValue='';
+if(result.notApplicable){
+scoreDisplayMode=Audit.SCORING_MODES.NOT_APPLICABLE;
+result.rawValue=true;
 }
+
+if(result.errorMessage){
+scoreDisplayMode=Audit.SCORING_MODES.ERROR;
+}
+
 let auditDescription=audit.meta.description;
 if(audit.meta.failureDescription){
-if(!score||typeof score==='number'&&score<100){
+if(Number(score)<Util.PASS_THRESHOLD){
 auditDescription=audit.meta.failureDescription;
 }
 }
+
+if(scoreDisplayMode!==Audit.SCORING_MODES.BINARY&&
+scoreDisplayMode!==Audit.SCORING_MODES.NUMERIC){
+score=null;
+}
+
 return{
+id:audit.meta.name,
+title:auditDescription,
+description:audit.meta.helpText,
+
 score,
-displayValue:`${displayValue}`,
+scoreDisplayMode,
 rawValue:result.rawValue,
-error:result.error,
-debugString:result.debugString,
-extendedInfo:result.extendedInfo,
-scoringMode:audit.meta.scoringMode||Audit.SCORING_MODES.BINARY,
-informative:audit.meta.informative,
-manual:audit.meta.manual,
-notApplicable:result.notApplicable,
-name:audit.meta.name,
-description:auditDescription,
-helpText:audit.meta.helpText,
+
+displayValue:result.displayValue,
+explanation:result.explanation,
+errorMessage:result.errorMessage,
+warnings:result.warnings,
+
 details:result.details};
 
 }}
@@ -12696,26 +14839,7 @@
 
 module.exports=Audit;
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-},{"../lib/statistics":34}],3:[function(require,module,exports){
+},{"../lib/statistics":40,"../report/html/renderer/util":48}],3:[function(require,module,exports){
 
 
 
@@ -12724,7 +14848,11 @@
 'use strict';
 
 const Audit=require('../audit');
-const Util=require('../../report/v2/renderer/util');
+const Interactive=require('../../gather/computed/metrics/lantern-interactive');
+const Simulator=require('../../lib/dependency-graph/simulator/simulator');
+const Node=require('../../lib/dependency-graph/node.js');
+
+const NetworkNode=require('../../lib/dependency-graph/network-node.js');
 
 const KB_IN_BYTES=1024;
 
@@ -12741,9 +14869,9 @@
 
 
 static scoreForWastedMs(wastedMs){
-if(wastedMs===0)return 100;else
-if(wastedMs<WASTED_MS_FOR_AVERAGE)return 90;else
-if(wastedMs<WASTED_MS_FOR_POOR)return 65;else
+if(wastedMs===0)return 1;else
+if(wastedMs<WASTED_MS_FOR_AVERAGE)return 0.9;else
+if(wastedMs<WASTED_MS_FOR_POOR)return 0.65;else
 return 0;
 }
 
@@ -12751,28 +14879,10 @@
 
 
 
-static bytesToKbString(bytes){
-return Util.formatBytesToKB(bytes,0);
-}
 
-
-
-
-
-
-static toSavingsString(bytes=0,percent=0){
-const kbDisplay=this.bytesToKbString(bytes);
-const percentDisplay=Util.formatNumber(Math.round(percent))+'%';
-return`${kbDisplay} (${percentDisplay})`;
-}
-
-
-
-
-
-
-static bytesToMsString(bytes,networkThroughput){
-return Util.formatMilliseconds(bytes/networkThroughput*1000,10);
+static bytesToMs(bytes,networkThroughput){
+const milliseconds=bytes/networkThroughput*1000;
+return milliseconds;
 }
 
 
@@ -12793,11 +14903,13 @@
 return Math.round(totalBytes*compressionRatio);
 }else if(networkRecord._resourceType&&networkRecord._resourceType._name===resourceType){
 
-return networkRecord._transferSize;
+return networkRecord._transferSize||0;
 }else{
 
 
-const compressionRatio=networkRecord._transferSize/networkRecord._resourceSize||1;
+const transferSize=networkRecord._transferSize||0;
+const resourceSize=networkRecord._resourceSize;
+const compressionRatio=resourceSize!==undefined?transferSize/resourceSize:1;
 return Math.round(totalBytes*compressionRatio);
 }
 }
@@ -12806,14 +14918,86 @@
 
 
 
-static audit(artifacts){
+
+static audit(artifacts,context){
+const trace=artifacts.traces[Audit.DEFAULT_PASS];
 const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
-return artifacts.requestNetworkRecords(devtoolsLog).
-then(networkRecords=>this.audit_(artifacts,networkRecords)).
-then(result=>{
-return artifacts.requestNetworkThroughput(devtoolsLog).
-then(networkThroughput=>this.createAuditResult(result,networkThroughput));
+const settings=context&&context.settings||{};
+const simulatorOptions={
+devtoolsLog,
+settings};
+
+
+return artifacts.
+requestNetworkRecords(devtoolsLog).
+then(networkRecords=>
+Promise.all([
+this.audit_(artifacts,networkRecords,context),
+artifacts.requestPageDependencyGraph({trace,devtoolsLog}),
+artifacts.requestLoadSimulator(simulatorOptions)])).
+
+
+then(([result,graph,simulator])=>this.createAuditProduct(result,graph,simulator));
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+static computeWasteWithTTIGraph(results,graph,simulator,options){
+options=Object.assign({includeLoad:true},options);
+
+const simulationBeforeChanges=simulator.simulate(graph);
+
+const resultsByUrl=new Map();
+for(const result of results){
+resultsByUrl.set(result.url,result);
+}
+
+
+
+const originalTransferSizes=new Map();
+graph.traverse(node=>{
+if(node.type!=='network')return;
+const networkNode=node;
+const result=resultsByUrl.get(networkNode.record.url);
+if(!result)return;
+const original=networkNode.record.transferSize;
+
+originalTransferSizes.set(networkNode.record.requestId,original);
+
+const wastedBytes=result.wastedBytes;
+networkNode.record._transferSize=Math.max(original-wastedBytes,0);
 });
+
+const simulationAfterChanges=simulator.simulate(graph);
+
+
+graph.traverse(node=>{
+if(node.type!=='network')return;
+const networkNode=node;
+const originalTransferSize=originalTransferSizes.get(networkNode.record.requestId);
+if(originalTransferSize===undefined)return;
+networkNode.record._transferSize=originalTransferSize;
+});
+
+const savingsOnOverallLoad=simulationBeforeChanges.timeInMs-simulationAfterChanges.timeInMs;
+const savingsOnTTI=Interactive.getLastLongTaskEndTime(simulationBeforeChanges.nodeTimings)-
+Interactive.getLastLongTaskEndTime(simulationAfterChanges.nodeTimings);
+
+let savings=savingsOnTTI;
+if(options.includeLoad)savings=Math.max(savings,savingsOnOverallLoad);
+
+
+return Math.round(Math.max(savings,0)/10)*10;
 }
 
 
@@ -12821,39 +15005,31 @@
 
 
 
-static createAuditResult(result,networkThroughput){
-if(!Number.isFinite(networkThroughput)&&result.results.length){
-throw new Error('Invalid network timing information');
-}
 
-const debugString=result.debugString;
-const results=result.results.
-map(item=>{
-const wastedPercent=100*item.wastedBytes/item.totalBytes;
-item.wastedKb=this.bytesToKbString(item.wastedBytes);
-item.wastedMs=this.bytesToMsString(item.wastedBytes,networkThroughput);
-item.totalKb=this.bytesToKbString(item.totalBytes);
-item.totalMs=this.bytesToMsString(item.totalBytes,networkThroughput);
-item.potentialSavings=this.toSavingsString(item.wastedBytes,wastedPercent);
-return item;
-}).
-sort((itemA,itemB)=>itemB.wastedBytes-itemA.wastedBytes);
+static createAuditProduct(result,graph,simulator){
+const results=result.results.sort((itemA,itemB)=>itemB.wastedBytes-itemA.wastedBytes);
 
 const wastedBytes=results.reduce((sum,item)=>sum+item.wastedBytes,0);
 const wastedKb=Math.round(wastedBytes/KB_IN_BYTES);
-const wastedMs=Math.round(wastedBytes/networkThroughput*100)*10;
+const wastedMs=this.computeWasteWithTTIGraph(results,graph,simulator);
+
 
 let displayValue=result.displayValue||'';
-if(typeof result.displayValue==='undefined'&&wastedBytes){
-const wastedKbDisplay=this.bytesToKbString(wastedBytes);
-const wastedMsDisplay=this.bytesToMsString(wastedBytes,networkThroughput);
-displayValue=`Potential savings of ${wastedKbDisplay} (~${wastedMsDisplay})`;
+if(typeof result.displayValue==='undefined'&&wastedKb){
+displayValue=['Potential savings of %d\xa0KB',wastedKb];
 }
 
-const tableDetails=Audit.makeTableDetails(result.headings,results);
+const summary={
+wastedMs,
+wastedBytes};
+
+
+
+const details=Audit.makeTableDetails(result.headings,results,summary);
 
 return{
-debugString,
+explanation:result.explanation,
+warnings:result.warnings,
 displayValue,
 rawValue:wastedMs,
 score:UnusedBytes.scoreForWastedMs(wastedMs),
@@ -12864,7 +15040,7 @@
 results}},
 
 
-details:tableDetails};
+details};
 
 }
 
@@ -12872,14 +15048,20 @@
 
 
 
-static audit_(){
+
+
+
+
+static audit_(artifacts,networkRecords,context){
 throw new Error('audit_ unimplemented');
 }}
 
 
+
+
 module.exports=UnusedBytes;
 
-},{"../../report/v2/renderer/util":43,"../audit":2}],4:[function(require,module,exports){
+},{"../../gather/computed/metrics/lantern-interactive":"./gather/computed/metrics/lantern-interactive","../../lib/dependency-graph/network-node.js":24,"../../lib/dependency-graph/node.js":25,"../../lib/dependency-graph/simulator/simulator":28,"../audit":2}],4:[function(require,module,exports){
 
 
 
@@ -12898,10 +15080,9 @@
 
 
 
-static get meta(){
+static get partialMeta(){
 return{
-informative:true,
-manual:true,
+scoreDisplayMode:Audit.SCORING_MODES.MANUAL,
 requiredArtifacts:[]};
 
 }
@@ -12939,14 +15120,14 @@
 
 
 static audit(artifacts){
-return Promise.resolve(this.audit_(artifacts)).then(result=>this.createAuditResult(result));
+return Promise.resolve(this.audit_(artifacts)).then(result=>this.createAuditProduct(result));
 }
 
 
 
 
 
-static createAuditResult(result){
+static createAuditProduct(result){
 const extendedInfo={
 value:result};
 
@@ -12955,32 +15136,32 @@
 if(result.failures.length>0){
 return{
 rawValue:false,
-debugString:`Failures: ${result.failures.join(', ')}.`,
+explanation:`Failures: ${result.failures.join(',\n')}.`,
 extendedInfo};
 
 }
 
-let debugString;
-if(result.warnings&&result.warnings.length>0){
-debugString=`Warnings: ${result.warnings.join(', ')}`;
-}
-
 
 return{
 rawValue:true,
 extendedInfo,
-debugString};
+warnings:result.warnings};
 
 }
 
 
 
 
-static audit_(){
+
+
+
+static audit_(artifacts){
 throw new Error('audit_ unimplemented');
 }}
 
 
+
+
 module.exports=MultiCheckAudit;
 
 },{"./audit":2}],6:[function(require,module,exports){
@@ -13000,10 +15181,19 @@
 
 
 static getViolationResults(artifacts,pattern){
+const seen=new Set();
 return artifacts.ChromeConsoleMessages.
 map(message=>message.entry).
 filter(entry=>entry.url&&entry.source==='violation'&&pattern.test(entry.text)).
-map(entry=>Object.assign({label:`line: ${entry.lineNumber}`},entry));
+map(entry=>({label:`line: ${entry.lineNumber}`,url:entry.url})).
+filter(entry=>{
+
+
+const key=`${entry.url}!${entry.label}`;
+if(seen.has(key))return false;
+seen.add(key);
+return true;
+});
 }}
 
 
@@ -13019,102 +15209,18 @@
 
 'use strict';
 
-const defaultConfigPath='./default.js';
-const defaultConfig=require('./default.js');
+const defaultConfigPath='./default-config.js';
+const defaultConfig=require('./default-config.js');
 const fullConfig=require('./full-config.js');
+const constants=require('./constants');
 
-const GatherRunner=require('../gather/gather-runner');
+const isDeepEqual=require('lodash.isequal');
 const log=require('lighthouse-logger');
 const path=require('path');
 const Audit=require('../audits/audit');
 const Runner=require('../runner');
 
-const _flatten=arr=>[].concat(...arr);
-
-
-
-
-
-function cleanTrace(trace){
-const traceEvents=trace.traceEvents;
-
-const threads=[];
-const countsByThread={};
-const traceStartEvents=[];
-const makeMockEvent=(evt,ts)=>{
-return{
-pid:evt.pid,
-tid:evt.tid,
-ts:ts||0,
-ph:'I',
-cat:'disabled-by-default-devtools.timeline',
-name:'TracingStartedInPage',
-args:{
-data:{
-page:evt.frame}},
-
-
-s:'t'};
-
-};
-
-let frame;
-let data;
-let name;
-let counter;
-
-traceEvents.forEach((evt,idx)=>{
-if(evt.name.startsWith('TracingStartedIn')){
-traceStartEvents.push(idx);
-}
-
-
-data=evt.args&&(evt.args.data||evt.args.beginData||evt.args.counters);
-frame=evt.args&&evt.args.frame||data&&(data.frame||data.page);
-
-if(!frame){
-return;
-}
-
-
-name=`pid${evt.pid}-tid${evt.tid}-frame${frame}`;
-counter=countsByThread[name];
-if(!counter){
-counter={
-pid:evt.pid,
-tid:evt.tid,
-frame:frame,
-count:0};
-
-countsByThread[name]=counter;
-threads.push(counter);
-}
-counter.count++;
-});
-
-
-threads.sort((a,b)=>b.count-a.count);
-const mostActiveFrame=threads[0];
-
-
-
-const ts=traceEvents[traceStartEvents[0]]&&traceEvents[traceStartEvents[0]].ts;
-
-
-let i=0;
-for(const dup of traceStartEvents){
-traceEvents.splice(dup-i,1);
-i++;
-}
-
-
-
-traceEvents.unshift(makeMockEvent(mostActiveFrame,ts));
-
-return trace;
-}
-
-function validatePasses(passes,audits,rootPath){
+function validatePasses(passes,audits){
 if(!Array.isArray(passes)){
 return;
 }
@@ -13122,11 +15228,11 @@
 
 
 passes.forEach(pass=>{
-pass.gatherers.forEach(gatherer=>{
-const GathererClass=GatherRunner.getGathererClass(gatherer,rootPath);
-const isGatherRequiredByAudits=requiredGatherers.has(GathererClass.name);
+pass.gatherers.forEach(gathererDefn=>{
+const gatherer=gathererDefn.instance||gathererDefn.implementation;
+const isGatherRequiredByAudits=requiredGatherers.has(gatherer.name);
 if(isGatherRequiredByAudits===false){
-const msg=`${GathererClass.name} gatherer requested, however no audit requires it.`;
+const msg=`${gatherer.name} gatherer requested, however no audit requires it.`;
 log.warn('config',msg);
 }
 });
@@ -13134,18 +15240,8 @@
 
 
 const usedNames=new Set();
-let defaultUsed=false;
-passes.forEach((pass,index)=>{
-let passName=pass.passName;
-if(!passName){
-if(defaultUsed){
-throw new Error(`passes[${index}] requires a passName`);
-}
-
-passName=Audit.DEFAULT_PASS;
-defaultUsed=true;
-}
-
+passes.forEach(pass=>{
+const passName=pass.passName;
 if(usedNames.has(passName)){
 throw new Error(`Passes must have unique names (repeated passName: ${passName}.`);
 }
@@ -13158,32 +15254,39 @@
 return;
 }
 
-const auditIds=audits.map(audit=>audit.meta.name);
 Object.keys(categories).forEach(categoryId=>{
-categories[categoryId].audits.forEach((audit,index)=>{
-if(!audit.id){
+categories[categoryId].auditRefs.forEach((auditRef,index)=>{
+if(!auditRef.id){
 throw new Error(`missing an audit id at ${categoryId}[${index}]`);
 }
 
-if(!auditIds.includes(audit.id)){
-throw new Error(`could not find ${audit.id} audit for category ${categoryId}`);
+const audit=audits.find(a=>a.implementation.meta.name===auditRef.id);
+if(!audit){
+throw new Error(`could not find ${auditRef.id} audit for category ${categoryId}`);
 }
 
-if(categoryId==='accessibility'&&!audit.group){
-throw new Error(`${audit.id} accessibility audit does not have a group`);
+const auditImpl=audit.implementation;
+const isManual=auditImpl.meta.scoreDisplayMode==='manual';
+if(categoryId==='accessibility'&&!auditRef.group&&!isManual){
+throw new Error(`${auditRef.id} accessibility audit does not have a group`);
 }
 
-if(audit.group&&!groups[audit.group]){
-throw new Error(`${audit.id} references unknown group ${audit.group}`);
+if(auditRef.weight>0&&isManual){
+throw new Error(`${auditRef.id} is manual but has a positive weight`);
+}
+
+if(auditRef.group&&!groups[auditRef.group]){
+throw new Error(`${auditRef.id} references unknown group ${auditRef.group}`);
 }
 });
 });
 }
 
 function assertValidAudit(auditDefinition,auditPath){
-const auditName=auditPath||auditDefinition.meta.name;
+const auditName=auditPath||
+auditDefinition&&auditDefinition.meta&&auditDefinition.meta.name;
 
-if(typeof auditDefinition.audit!=='function'){
+if(typeof auditDefinition.audit!=='function'||auditDefinition.audit===Audit.audit){
 throw new Error(`${auditName} has no audit() method.`);
 }
 
@@ -13199,8 +15302,7 @@
 
 
 if(typeof auditDefinition.meta.failureDescription!=='string'&&
-auditDefinition.meta.informative!==true&&
-auditDefinition.meta.scoringMode!==Audit.SCORING_MODES.NUMERIC){
+auditDefinition.meta.scoreDisplayMode===Audit.SCORING_MODES.BINARY){
 throw new Error(`${auditName} has no failureDescription and should.`);
 }
 
@@ -13221,48 +15323,62 @@
 }
 }
 
-function expandArtifacts(artifacts){
-if(!artifacts){
-return null;
+function assertValidGatherer(gathererInstance,gathererName){
+gathererName=gathererName||gathererInstance.name||'gatherer';
+
+if(typeof gathererInstance.beforePass!=='function'){
+throw new Error(`${gathererName} has no beforePass() method.`);
 }
 
-if(artifacts.traces){
-Object.keys(artifacts.traces).forEach(key=>{
-log.log('info','Normalizng trace contents into expected state...');
-let trace=require(artifacts.traces[key]);
-
-
-
-if(Array.isArray(trace)){
-trace={
-traceEvents:trace};
-
-}
-trace=cleanTrace(trace);
-
-artifacts.traces[key]=trace;
-});
+if(typeof gathererInstance.pass!=='function'){
+throw new Error(`${gathererName} has no pass() method.`);
 }
 
-if(artifacts.devtoolsLogs){
-Object.keys(artifacts.devtoolsLogs).forEach(key=>{
-artifacts.devtoolsLogs[key]=require(artifacts.devtoolsLogs[key]);
-});
+if(typeof gathererInstance.afterPass!=='function'){
+throw new Error(`${gathererName} has no afterPass() method.`);
+}
 }
 
-return artifacts;
+
+
+
+
+
+
+
+function cleanFlagsForSettings(flags={}){
+const settings={};
+for(const key of Object.keys(flags)){
+if(typeof constants.defaultSettings[key]!=='undefined'){
+settings[key]=flags[key];
+}
 }
 
-function merge(base,extension){
-if(typeof base==='undefined'){
+return settings;
+}
+
+
+function merge(base,extension,overwriteArrays=false){
+
+if(typeof base==='undefined'||base===null){
 return extension;
+}else if(typeof extension==='undefined'){
+return base;
 }else if(Array.isArray(extension)){
+if(overwriteArrays)return extension;
 if(!Array.isArray(base))throw new TypeError(`Expected array but got ${typeof base}`);
-return base.concat(extension);
+const merged=base.slice();
+extension.forEach(item=>{
+if(!merged.some(candidate=>isDeepEqual(candidate,item)))merged.push(item);
+});
+
+return merged;
 }else if(typeof extension==='object'){
 if(typeof base!=='object')throw new TypeError(`Expected object but got ${typeof base}`);
 Object.keys(extension).forEach(key=>{
-base[key]=merge(base[key],extension[key]);
+const localOverwriteArrays=overwriteArrays||
+key==='settings'&&typeof base[key]==='object';
+base[key]=merge(base[key],extension[key],localOverwriteArrays);
 });
 return base;
 }
@@ -13270,8 +15386,28 @@
 return extension;
 }
 
+function cloneArrayWithPluginSafety(array){
+return array.map(item=>{
+return typeof item==='object'?Object.assign({},item):item;
+});
+}
+
 function deepClone(json){
-return JSON.parse(JSON.stringify(json));
+const cloned=JSON.parse(JSON.stringify(json));
+
+
+
+if(Array.isArray(json.passes)){
+cloned.passes.forEach((pass,i)=>{
+pass.gatherers=cloneArrayWithPluginSafety(json.passes[i].gatherers||[]);
+});
+}
+
+if(Array.isArray(json.audits)){
+cloned.audits=cloneArrayWithPluginSafety(json.audits);
+}
+
+return cloned;
 }
 
 class Config{
@@ -13280,7 +15416,9 @@
 
 
 
-constructor(configJSON,configPath){
+constructor(configJSON,flags){
+let configPath=flags&&flags.configPath;
+
 if(!configJSON){
 configJSON=defaultConfig;
 configPath=path.resolve(__dirname,defaultConfigPath);
@@ -13291,21 +15429,9 @@
 }
 
 
-const inputConfig=configJSON;
 configJSON=deepClone(configJSON);
 
 
-
-if(Array.isArray(inputConfig.passes)){
-configJSON.passes.forEach((pass,i)=>{
-pass.gatherers=Array.from(inputConfig.passes[i].gatherers);
-});
-}
-if(Array.isArray(inputConfig.audits)){
-configJSON.audits=Array.from(inputConfig.audits);
-}
-
-
 if(configJSON.extends==='lighthouse:full'){
 const explodedFullConfig=Config.extendConfigJSON(deepClone(defaultConfig),
 deepClone(fullConfig));
@@ -13315,10 +15441,19 @@
 }
 
 
-if(configJSON.settings&&(
-Array.isArray(configJSON.settings.onlyCategories)||
+configJSON=Config.augmentWithDefaults(configJSON);
+
+
+configJSON.audits=Config.expandAuditShorthandAndMergeOptions(configJSON.audits);
+configJSON.passes=Config.expandGathererShorthandAndMergeOptions(configJSON.passes);
+
+
+configJSON.settings=merge(configJSON.settings||{},cleanFlagsForSettings(flags),true);
+
+
+if(Array.isArray(configJSON.settings.onlyCategories)||
 Array.isArray(configJSON.settings.onlyAudits)||
-Array.isArray(configJSON.settings.skipAudits))){
+Array.isArray(configJSON.settings.skipAudits)){
 const categoryIds=configJSON.settings.onlyCategories;
 const auditIds=configJSON.settings.onlyAudits;
 const skipAuditIds=configJSON.settings.skipAudits;
@@ -13326,18 +15461,19 @@
 skipAuditIds);
 }
 
+Config.adjustDefaultPassForThrottling(configJSON);
+
 
 this._configDir=configPath?path.dirname(configPath):undefined;
 
-this._passes=configJSON.passes||null;
-
+this._passes=Config.requireGatherers(configJSON.passes,this._configDir);
 this._audits=Config.requireAudits(configJSON.audits,this._configDir);
-this._artifacts=expandArtifacts(configJSON.artifacts);
 this._categories=configJSON.categories;
 this._groups=configJSON.groups;
+this._settings=configJSON.settings||{};
 
 
-validatePasses(configJSON.passes,this._audits,this._configDir);
+validatePasses(configJSON.passes,this._audits);
 validateCategories(configJSON.categories,this._audits,this._groups);
 }
 
@@ -13349,8 +15485,11 @@
 static extendConfigJSON(baseJSON,extendJSON){
 if(extendJSON.passes){
 extendJSON.passes.forEach(pass=>{
-const basePass=baseJSON.passes.find(candidate=>candidate.passName===pass.passName);
-if(!basePass||!pass.passName){
+
+const passName=pass.passName||constants.defaultPassConfig.passName;
+const basePass=baseJSON.passes.find(candidate=>candidate.passName===passName);
+
+if(!basePass){
 baseJSON.passes.push(pass);
 }else{
 merge(basePass,pass);
@@ -13367,6 +15506,123 @@
 
 
 
+static augmentWithDefaults(config){
+const{defaultSettings,defaultPassConfig}=constants;
+config.settings=merge(deepClone(defaultSettings),config.settings,true);
+if(config.passes){
+config.passes=config.passes.map(pass=>merge(deepClone(defaultPassConfig),pass));
+}
+
+return config;
+}
+
+
+
+
+
+
+
+static expandAuditShorthandAndMergeOptions(audits){
+if(!audits){
+return audits;
+}
+
+const newAudits=audits.map(audit=>{
+if(typeof audit==='string'){
+return{path:audit,options:{}};
+}else if(audit&&typeof audit.audit==='function'){
+return{implementation:audit,options:{}};
+}else{
+return audit;
+}
+});
+
+return Config._mergeOptionsOfItems(newAudits);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+static expandGathererShorthandAndMergeOptions(passes){
+if(!passes){
+return passes;
+}
+
+passes.forEach(pass=>{
+pass.gatherers=pass.gatherers.map(gatherer=>{
+if(typeof gatherer==='string'){
+return{path:gatherer,options:{}};
+}else if(typeof gatherer==='function'){
+return{implementation:gatherer,options:{}};
+}else if(gatherer&&typeof gatherer.beforePass==='function'){
+return{instance:gatherer,options:{}};
+}else{
+return gatherer;
+}
+});
+
+pass.gatherers=Config._mergeOptionsOfItems(pass.gatherers);
+});
+
+return passes;
+}
+
+
+
+
+
+static _mergeOptionsOfItems(items){
+const mergedItems=[];
+
+for(const item of items){
+const existingItem=item.path&&mergedItems.find(candidate=>candidate.path===item.path);
+if(!existingItem){
+mergedItems.push(item);
+continue;
+}
+
+existingItem.options=Object.assign({},existingItem.options,item.options);
+}
+
+return mergedItems;
+}
+
+
+
+
+
+
+
+static adjustDefaultPassForThrottling(config){
+if(config.settings.throttlingMethod!=='devtools'&&
+config.settings.throttlingMethod!=='provided'){
+return;
+}
+
+const defaultPass=config.passes.find(pass=>pass.passName==='defaultPass');
+if(!defaultPass)return;
+
+const overrides=constants.nonSimulatedPassConfigOverrides;
+defaultPass.pauseAfterLoadMs=
+Math.max(overrides.pauseAfterLoadMs,defaultPass.pauseAfterLoadMs);
+defaultPass.cpuQuietThresholdMs=
+Math.max(overrides.cpuQuietThresholdMs,defaultPass.cpuQuietThresholdMs);
+defaultPass.networkQuietThresholdMs=
+Math.max(overrides.networkQuietThresholdMs,defaultPass.networkQuietThresholdMs);
+}
+
+
+
+
+
 
 
 
@@ -13374,15 +15630,27 @@
 static generateNewFilteredConfig(oldConfig,categoryIds,auditIds,skipAuditIds){
 
 const config=deepClone(oldConfig);
+config.audits=Config.expandAuditShorthandAndMergeOptions(config.audits);
+config.passes=Config.expandGathererShorthandAndMergeOptions(config.passes);
+config.passes=Config.requireGatherers(config.passes);
 
-config.categories=Config.filterCategoriesAndAudits(config.categories,categoryIds,auditIds,
+
+const{categories,audits:requestedAuditNames}=Config.filterCategoriesAndAudits(
+config.categories,
+categoryIds,
+auditIds,
 skipAuditIds);
 
 
-const requestedAuditNames=Config.getAuditIdsInCategories(config.categories);
+config.categories=categories;
+
+
 const auditPathToNameMap=Config.getMapOfAuditPathToName(config);
-config.audits=config.audits.filter(auditPath=>
-requestedAuditNames.has(auditPathToNameMap.get(auditPath)));
+const getAuditName=auditDefn=>auditDefn.implementation?
+auditDefn.implementation.meta.name:
+auditPathToNameMap.get(auditDefn.path);
+config.audits=config.audits.filter(auditDefn=>
+requestedAuditNames.has(getAuditName(auditDefn)));
 
 
 const auditObjectsSelected=Config.requireAudits(config.audits);
@@ -13424,8 +15692,8 @@
 const auditsToValidate=new Set(auditIds.concat(skipAuditIds));
 for(const auditId of auditsToValidate){
 const foundCategory=Object.keys(oldCategories).find(categoryId=>{
-const audits=oldCategories[categoryId].audits;
-return audits.find(candidate=>candidate.id===auditId);
+const auditRefs=oldCategories[categoryId].auditRefs;
+return auditRefs.find(candidate=>candidate.id===auditId);
 });
 
 if(!foundCategory){
@@ -13439,13 +15707,16 @@
 }
 }
 
+const includedAudits=new Set(auditIds);
+skipAuditIds.forEach(id=>includedAudits.delete(id));
+
 Object.keys(oldCategories).forEach(categoryId=>{
 const category=deepClone(oldCategories[categoryId]);
 
 if(filterByIncludedCategory&&filterByIncludedAudit){
 
 if(!categoryIds.includes(categoryId)){
-category.audits=category.audits.filter(audit=>auditIds.includes(audit.id));
+category.auditRefs=category.auditRefs.filter(audit=>auditIds.includes(audit.id));
 }
 }else if(filterByIncludedCategory){
 
@@ -13453,28 +15724,19 @@
 return;
 }
 }else if(filterByIncludedAudit){
-category.audits=category.audits.filter(audit=>auditIds.includes(audit.id));
+category.auditRefs=category.auditRefs.filter(audit=>auditIds.includes(audit.id));
 }
 
 
-category.audits=category.audits.filter(audit=>!skipAuditIds.includes(audit.id));
+category.auditRefs=category.auditRefs.filter(audit=>!skipAuditIds.includes(audit.id));
 
-if(category.audits.length){
+if(category.auditRefs.length){
 categories[categoryId]=category;
+category.auditRefs.forEach(audit=>includedAudits.add(audit.id));
 }
 });
 
-return categories;
-}
-
-
-
-
-
-
-static getAuditIdsInCategories(categories){
-const audits=_flatten(Object.keys(categories).map(id=>categories[id].audits));
-return new Set(audits.map(audit=>audit.id));
+return{categories,audits:includedAudits};
 }
 
 
@@ -13483,8 +15745,8 @@
 
 static getCategories(config){
 return Object.keys(config.categories).map(id=>{
-const name=config.categories[id].name;
-return{id,name};
+const title=config.categories[id].title;
+return{id,title};
 });
 }
 
@@ -13495,7 +15757,8 @@
 
 static getMapOfAuditPathToName(config){
 const auditObjectsAll=Config.requireAudits(config.audits);
-const auditPathToName=new Map(auditObjectsAll.map((AuditClass,index)=>{
+const auditPathToName=new Map(auditObjectsAll.map((auditDefn,index)=>{
+const AuditClass=auditDefn.implementation;
 const auditPath=config.audits[index];
 const auditName=AuditClass.meta.name;
 return[auditPath,auditName];
@@ -13515,8 +15778,8 @@
 return new Set();
 }
 
-return audits.reduce((list,audit)=>{
-audit.meta.requiredArtifacts.forEach(artifact=>list.add(artifact));
+return audits.reduce((list,auditDefn)=>{
+auditDefn.implementation.meta.requiredArtifacts.forEach(artifact=>list.add(artifact));
 return list;
 },new Set());
 }
@@ -13527,14 +15790,13 @@
 
 
 
-static generatePassesNeededByGatherers(oldPasses,requiredGatherers){
+static generatePassesNeededByGatherers(passes,requiredGatherers){
 const auditsNeedTrace=requiredGatherers.has('traces');
-const passes=JSON.parse(JSON.stringify(oldPasses));
 const filteredPasses=passes.map(pass=>{
 
-pass.gatherers=pass.gatherers.filter(gathererName=>{
-gathererName=GatherRunner.getGathererClass(gathererName).name;
-return requiredGatherers.has(gathererName);
+pass.gatherers=pass.gatherers.filter(gathererDefn=>{
+const gatherer=gathererDefn.instance||gathererDefn.implementation;
+return requiredGatherers.has(gatherer.name);
 });
 
 
@@ -13569,10 +15831,9 @@
 }
 
 const coreList=Runner.getAuditList();
-return audits.map(pathOrAuditClass=>{
-let AuditClass;
-if(typeof pathOrAuditClass==='string'){
-const path=pathOrAuditClass;
+return audits.map(auditDefn=>{
+if(!auditDefn.implementation){
+const path=auditDefn.path;
 
 const coreAudit=coreList.find(a=>a===`${path}.js`);
 let requirePath=`../audits/${path}`;
@@ -13580,18 +15841,57 @@
 
 requirePath=Runner.resolvePlugin(path,configPath,'audit');
 }
-AuditClass=require(requirePath);
-assertValidAudit(AuditClass,path);
-}else{
-AuditClass=pathOrAuditClass;
-assertValidAudit(AuditClass);
+
+auditDefn.implementation=require(requirePath);
 }
 
-return AuditClass;
+assertValidAudit(auditDefn.implementation,auditDefn.path);
+return auditDefn;
 });
 }
 
 
+
+
+
+
+
+static requireGatherers(passes,configPath){
+if(!passes){
+return null;
+}
+
+const coreList=Runner.getGathererList();
+passes.forEach(pass=>{
+pass.gatherers.forEach(gathererDefn=>{
+if(!gathererDefn.instance){
+let GathererClass=gathererDefn.implementation;
+if(!GathererClass){
+
+const name=gathererDefn.path;
+const coreGatherer=coreList.find(a=>a===`${name}.js`);
+
+let requirePath=`../gather/gatherers/${name}`;
+if(!coreGatherer){
+
+requirePath=Runner.resolvePlugin(name,configPath,'gatherer');
+}
+
+GathererClass=require(requirePath);
+}
+
+gathererDefn.implementation=GathererClass;
+gathererDefn.instance=new GathererClass();
+}
+
+assertValidGatherer(gathererDefn.instance,gathererDefn.path);
+});
+});
+
+return passes;
+}
+
+
 get configDir(){
 return this._configDir;
 }
@@ -13607,11 +15907,6 @@
 }
 
 
-get artifacts(){
-return this._artifacts;
-}
-
-
 get categories(){
 return this._categories;
 }
@@ -13619,13 +15914,33 @@
 
 get groups(){
 return this._groups;
+}
+
+
+get settings(){
+return this._settings;
 }}
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 module.exports=Config;
 
 }).call(this,"/../lighthouse-core/config");
-},{"../audits/audit":2,"../gather/gather-runner":15,"../runner":44,"./default.js":8,"./full-config.js":10,"lighthouse-logger":137,"path":69}],8:[function(require,module,exports){
+},{"../audits/audit":2,"../runner":50,"./constants":8,"./default-config.js":9,"./full-config.js":10,"lighthouse-logger":143,"lodash.isequal":144,"path":75}],8:[function(require,module,exports){
 
 
 
@@ -13635,17 +15950,94 @@
 
 
 
+
+
+
+const DEVTOOLS_RTT_ADJUSTMENT_FACTOR=3.75;
+const DEVTOOLS_THROUGHPUT_ADJUSTMENT_FACTOR=0.9;
+
+const throttling={
+DEVTOOLS_RTT_ADJUSTMENT_FACTOR,
+DEVTOOLS_THROUGHPUT_ADJUSTMENT_FACTOR,
+mobile3G:{
+rttMs:150,
+throughputKbps:1.6*1024,
+requestLatencyMs:150*DEVTOOLS_RTT_ADJUSTMENT_FACTOR,
+downloadThroughputKbps:1.6*1024*DEVTOOLS_THROUGHPUT_ADJUSTMENT_FACTOR,
+uploadThroughputKbps:750*DEVTOOLS_THROUGHPUT_ADJUSTMENT_FACTOR,
+cpuSlowdownMultiplier:4}};
+
+
+
+
+const defaultSettings={
+output:'json',
+maxWaitForLoad:45*1000,
+throttlingMethod:'simulate',
+throttling:throttling.mobile3G,
+auditMode:false,
+gatherMode:false,
+disableStorageReset:false,
+disableDeviceEmulation:false,
+
+
+
+blockedUrlPatterns:null,
+additionalTraceCategories:null,
+extraHeaders:null,
+onlyAudits:null,
+onlyCategories:null,
+skipAudits:null};
+
+
+const defaultPassConfig={
+passName:'defaultPass',
+recordTrace:false,
+useThrottling:false,
+pauseAfterLoadMs:0,
+networkQuietThresholdMs:0,
+cpuQuietThresholdMs:0,
+blockedUrlPatterns:[],
+blankPage:'about:blank',
+blankDuration:300,
+gatherers:[]};
+
+
+const nonSimulatedPassConfigOverrides={
+pauseAfterLoadMs:5250,
+networkQuietThresholdMs:5250,
+cpuQuietThresholdMs:5250};
+
+
 module.exports={
-settings:{},
+throttling,
+defaultSettings,
+defaultPassConfig,
+nonSimulatedPassConfigOverrides};
+
+
+},{}],9:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const constants=require('./constants');
+
+
+
+module.exports={
+settings:constants.defaultSettings,
 passes:[{
 passName:'defaultPass',
 recordTrace:true,
-pauseAfterLoadMs:5250,
-networkQuietThresholdMs:5250,
-cpuQuietThresholdMs:5250,
 useThrottling:true,
+pauseAfterLoadMs:1000,
+networkQuietThresholdMs:1000,
+cpuQuietThresholdMs:1000,
 gatherers:[
-'url',
 'scripts',
 'css-usage',
 'viewport',
@@ -13673,14 +16065,12 @@
 'seo/hreflang',
 'seo/embedded-content',
 'seo/canonical',
+'seo/robots-txt',
 'fonts']},
 
 
 {
 passName:'offlinePass',
-useThrottling:false,
-
-networkQuietThresholdMs:0,
 gatherers:[
 'service-worker',
 'offline',
@@ -13689,9 +16079,6 @@
 
 {
 passName:'redirectPass',
-useThrottling:false,
-
-networkQuietThresholdMs:0,
 
 blockedUrlPatterns:['*.css','*.jpg','*.jpeg','*.png','*.gif','*.svg','*.ttf','*.woff','*.woff2'],
 gatherers:[
@@ -13706,15 +16093,16 @@
 'works-offline',
 'viewport',
 'without-javascript',
+'first-contentful-paint',
 'first-meaningful-paint',
 'load-fast-enough-for-pwa',
-'speed-index-metric',
+'speed-index',
 'screenshot-thumbnails',
 'estimated-input-latency',
 'errors-in-console',
 'time-to-first-byte',
-'first-interactive',
-'consistently-interactive',
+'first-cpu-idle',
+'interactive',
 'user-timings',
 'critical-request-chains',
 'redirects',
@@ -13728,7 +16116,10 @@
 'mainthread-work-breakdown',
 'bootup-time',
 'uses-rel-preload',
+'uses-rel-preconnect',
 'font-display',
+'network-requests',
+'metrics',
 'manual/pwa-cross-browser',
 'manual/pwa-page-transitions',
 'manual/pwa-each-page-has-url',
@@ -13780,25 +16171,25 @@
 'byte-efficiency/uses-long-cache-ttl',
 'byte-efficiency/total-byte-weight',
 'byte-efficiency/offscreen-images',
+'byte-efficiency/render-blocking-resources',
 'byte-efficiency/unminified-css',
 'byte-efficiency/unminified-javascript',
 'byte-efficiency/unused-css-rules',
 'byte-efficiency/uses-webp-images',
 'byte-efficiency/uses-optimized-images',
-'byte-efficiency/uses-request-compression',
+'byte-efficiency/uses-text-compression',
 'byte-efficiency/uses-responsive-images',
+'byte-efficiency/efficient-animated-content',
 'dobetterweb/appcache-manifest',
 'dobetterweb/dom-size',
 'dobetterweb/external-anchors-use-rel-noopener',
 'dobetterweb/geolocation-on-start',
-'dobetterweb/link-blocking-first-paint',
 'dobetterweb/no-document-write',
 'dobetterweb/no-mutation-events',
 'dobetterweb/no-vulnerable-libraries',
 'dobetterweb/no-websql',
 'dobetterweb/notification-on-start',
 'dobetterweb/password-inputs-can-be-pasted-into',
-'dobetterweb/script-blocking-first-paint',
 'dobetterweb/uses-http2',
 'dobetterweb/uses-passive-event-listeners',
 'seo/meta-description',
@@ -13806,6 +16197,7 @@
 'seo/font-size',
 'seo/link-text',
 'seo/is-crawlable',
+'seo/robots-txt',
 'seo/hreflang',
 'seo/plugins',
 'seo/canonical',
@@ -13814,15 +16206,14 @@
 
 
 groups:{
-'perf-metric':{
-title:'Metrics',
-description:'These metrics encapsulate your web app\'s performance across a number of dimensions.'},
+'metrics':{
+title:'Metrics'},
 
-'perf-hint':{
+'load-opportunities':{
 title:'Opportunities',
 description:'These are opportunities to speed up your application by optimizing the following resources.'},
 
-'perf-info':{
+'diagnostics':{
 title:'Diagnostics',
 description:'More information about the performance of your application.'},
 
@@ -13858,16 +16249,6 @@
 title:'Meta Tags Used Properly',
 description:'These are opportunities to improve the user experience of your site.'},
 
-'manual-a11y-checks':{
-title:'Additional items to manually check',
-description:'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).'},
-
-'manual-pwa-checks':{
-title:'Additional items to manually check',
-description:'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.'},
-
 'seo-mobile':{
 title:'Mobile Friendly',
 description:'Make sure your pages are mobile friendly so users don’t have to pinch or zoom '+
@@ -13879,52 +16260,54 @@
 
 'seo-crawl':{
 title:'Crawling and Indexing',
-description:'To appear in search results, crawlers need access to your app.'},
-
-'manual-seo-checks':{
-title:'Additional items to manually check',
-description:'Run these additional validators on your site to check additional SEO best practices.'}},
+description:'To appear in search results, crawlers need access to your app.'}},
 
 
 categories:{
 'performance':{
-name:'Performance',
-description:'These encapsulate your web app\'s current performance and opportunities to improve it.',
-audits:[
-{id:'first-meaningful-paint',weight:5,group:'perf-metric'},
-{id:'first-interactive',weight:5,group:'perf-metric'},
-{id:'consistently-interactive',weight:5,group:'perf-metric'},
-{id:'speed-index-metric',weight:1,group:'perf-metric'},
-{id:'estimated-input-latency',weight:1,group:'perf-metric'},
-{id:'link-blocking-first-paint',weight:0,group:'perf-hint'},
-{id:'script-blocking-first-paint',weight:0,group:'perf-hint'},
-{id:'uses-responsive-images',weight:0,group:'perf-hint'},
-{id:'offscreen-images',weight:0,group:'perf-hint'},
-{id:'unminified-css',weight:0,group:'perf-hint'},
-{id:'unminified-javascript',weight:0,group:'perf-hint'},
-{id:'unused-css-rules',weight:0,group:'perf-hint'},
-{id:'uses-optimized-images',weight:0,group:'perf-hint'},
-{id:'uses-webp-images',weight:0,group:'perf-hint'},
-{id:'uses-request-compression',weight:0,group:'perf-hint'},
-{id:'time-to-first-byte',weight:0,group:'perf-hint'},
-{id:'redirects',weight:0,group:'perf-hint'},
-{id:'uses-rel-preload',weight:0,group:'perf-hint'},
-{id:'total-byte-weight',weight:0,group:'perf-info'},
-{id:'uses-long-cache-ttl',weight:0,group:'perf-info'},
-{id:'dom-size',weight:0,group:'perf-info'},
-{id:'critical-request-chains',weight:0,group:'perf-info'},
-{id:'user-timings',weight:0,group:'perf-info'},
-{id:'bootup-time',weight:0,group:'perf-info'},
+title:'Performance',
+auditRefs:[
+{id:'first-contentful-paint',weight:3,group:'metrics'},
+{id:'first-meaningful-paint',weight:1,group:'metrics'},
+{id:'speed-index',weight:4,group:'metrics'},
+{id:'interactive',weight:5,group:'metrics'},
+{id:'first-cpu-idle',weight:2,group:'metrics'},
+{id:'estimated-input-latency',weight:0,group:'metrics'},
+
+{id:'render-blocking-resources',weight:0,group:'load-opportunities'},
+{id:'uses-responsive-images',weight:0,group:'load-opportunities'},
+{id:'offscreen-images',weight:0,group:'load-opportunities'},
+{id:'unminified-css',weight:0,group:'load-opportunities'},
+{id:'unminified-javascript',weight:0,group:'load-opportunities'},
+{id:'unused-css-rules',weight:0,group:'load-opportunities'},
+{id:'uses-optimized-images',weight:0,group:'load-opportunities'},
+{id:'uses-webp-images',weight:0,group:'load-opportunities'},
+{id:'uses-text-compression',weight:0,group:'load-opportunities'},
+{id:'uses-rel-preconnect',weight:0,group:'load-opportunities'},
+{id:'time-to-first-byte',weight:0,group:'load-opportunities'},
+{id:'redirects',weight:0,group:'load-opportunities'},
+{id:'uses-rel-preload',weight:0,group:'load-opportunities'},
+{id:'efficient-animated-content',weight:0,group:'load-opportunities'},
+{id:'total-byte-weight',weight:0,group:'diagnostics'},
+{id:'uses-long-cache-ttl',weight:0,group:'diagnostics'},
+{id:'dom-size',weight:0,group:'diagnostics'},
+{id:'critical-request-chains',weight:0,group:'diagnostics'},
+{id:'network-requests',weight:0},
+{id:'metrics',weight:0},
+{id:'user-timings',weight:0,group:'diagnostics'},
+{id:'bootup-time',weight:0,group:'diagnostics'},
 {id:'screenshot-thumbnails',weight:0},
-{id:'mainthread-work-breakdown',weight:0,group:'perf-info'},
-{id:'font-display',weight:0,group:'perf-info'}]},
+{id:'mainthread-work-breakdown',weight:0,group:'diagnostics'},
+{id:'font-display',weight:0,group:'diagnostics'}]},
 
 
 'pwa':{
-name:'Progressive Web App',
-weight:1,
+title:'Progressive Web App',
 description:'These checks validate the aspects of a Progressive Web App, as specified by the baseline [PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist).',
-audits:[
+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.',
+auditRefs:[
 {id:'service-worker',weight:1},
 {id:'works-offline',weight:1},
 {id:'without-javascript',weight:1},
@@ -13936,15 +16319,17 @@
 {id:'themed-omnibox',weight:1},
 {id:'viewport',weight:1},
 {id:'content-width',weight:1},
-{id:'pwa-cross-browser',weight:0,group:'manual-pwa-checks'},
-{id:'pwa-page-transitions',weight:0,group:'manual-pwa-checks'},
-{id:'pwa-each-page-has-url',weight:0,group:'manual-pwa-checks'}]},
+
+{id:'pwa-cross-browser',weight:0},
+{id:'pwa-page-transitions',weight:0},
+{id:'pwa-each-page-has-url',weight:0}]},
 
 
 'accessibility':{
-name:'Accessibility',
+title:'Accessibility',
 description:'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.',
-audits:[
+manualDescription:'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).',
+auditRefs:[
 {id:'accesskeys',weight:1,group:'a11y-correct-attributes'},
 {id:'aria-allowed-attr',weight:3,group:'a11y-aria'},
 {id:'aria-required-attr',weight:2,group:'a11y-aria'},
@@ -13980,22 +16365,22 @@
 {id:'valid-lang',weight:1,group:'a11y-language'},
 {id:'video-caption',weight:4,group:'a11y-describe-contents'},
 {id:'video-description',weight:3,group:'a11y-describe-contents'},
-{id:'logical-tab-order',weight:0,group:'manual-a11y-checks'},
-{id:'focusable-controls',weight:0,group:'manual-a11y-checks'},
-{id:'managed-focus',weight:0,group:'manual-a11y-checks'},
-{id:'focus-traps',weight:0,group:'manual-a11y-checks'},
-{id:'custom-controls-labels',weight:0,group:'manual-a11y-checks'},
-{id:'custom-controls-roles',weight:0,group:'manual-a11y-checks'},
-{id:'visual-order-follows-dom',weight:0,group:'manual-a11y-checks'},
-{id:'offscreen-content-hidden',weight:0,group:'manual-a11y-checks'},
-{id:'heading-levels',weight:0,group:'manual-a11y-checks'},
-{id:'use-landmarks',weight:0,group:'manual-a11y-checks'}]},
+
+{id:'logical-tab-order',weight:0},
+{id:'focusable-controls',weight:0},
+{id:'managed-focus',weight:0},
+{id:'focus-traps',weight:0},
+{id:'custom-controls-labels',weight:0},
+{id:'custom-controls-roles',weight:0},
+{id:'visual-order-follows-dom',weight:0},
+{id:'offscreen-content-hidden',weight:0},
+{id:'heading-levels',weight:0},
+{id:'use-landmarks',weight:0}]},
 
 
 'best-practices':{
-name:'Best Practices',
-description:'We\'ve compiled some recommendations for modernizing your web app and avoiding performance pitfalls.',
-audits:[
+title:'Best Practices',
+auditRefs:[
 {id:'appcache-manifest',weight:1},
 {id:'no-websql',weight:1},
 {id:'is-on-https',weight:1},
@@ -14015,29 +16400,32 @@
 
 
 'seo':{
-name:'SEO',
+title:'SEO',
 description:'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).',
-audits:[
+manualDescription:'Run these additional validators on your site to check additional SEO best practices.',
+auditRefs:[
 {id:'viewport',weight:1,group:'seo-mobile'},
 {id:'document-title',weight:1,group:'seo-content'},
 {id:'meta-description',weight:1,group:'seo-content'},
 {id:'http-status-code',weight:1,group:'seo-crawl'},
 {id:'link-text',weight:1,group:'seo-content'},
 {id:'is-crawlable',weight:1,group:'seo-crawl'},
+{id:'robots-txt',weight:1,group:'seo-crawl'},
 {id:'hreflang',weight:1,group:'seo-content'},
 {id:'canonical',weight:1,group:'seo-content'},
 {id:'font-size',weight:1,group:'seo-mobile'},
 {id:'plugins',weight:1,group:'seo-content'},
-{id:'mobile-friendly',weight:0,group:'manual-seo-checks'},
-{id:'structured-data',weight:0,group:'manual-seo-checks'}]}}};
+
+{id:'mobile-friendly',weight:0},
+{id:'structured-data',weight:0}]}}};
 
 
 
 
 
-},{}],9:[function(require,module,exports){
+},{"./constants":8}],10:[function(require,module,exports){
 
 
 
@@ -14047,58 +16435,7 @@
 
 module.exports={
 extends:'lighthouse:default',
-settings:{
-skipAudits:[
-
-'no-mutation-events',
-'screenshot-thumbnails',
-
-
-'first-meaningful-paint',
-'first-interactive',
-'consistently-interactive',
-'estimated-input-latency',
-'speed-index-metric',
-'offscreen-images',
-'load-fast-enough-for-pwa'],
-
-
-onlyCategories:['performance','pwa','best-practices']},
-
-passes:[
-{
-passName:'defaultPass',
-
-useThrottling:false,
-pauseAfterLoadMs:0,
-networkQuietThresholdMs:500,
-cpuQuietThresholdMs:500,
-
-gatherers:[]}],
-
-
-audits:[
-'predictive-perf'],
-
-categories:{
-performance:{
-audits:[
-{id:'predictive-perf',weight:5,group:'perf-metric'}]}}};
-
-
-
-
-
-},{}],10:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-module.exports={
-extends:'lighthouse:default',
+settings:{},
 passes:[
 {
 passName:'extraPass',
@@ -14112,8 +16449,8 @@
 
 categories:{
 'performance':{
-audits:[
-{id:'unused-javascript',weight:0,group:'perf-hint'}]}}};
+auditRefs:[
+{id:'unused-javascript',weight:0,group:'load-opportunities'}]}}};
 
 
 
@@ -14125,6 +16462,283 @@
 
 
 
+'use strict';
+
+const ArbitraryEqualityMap=require('../../lib/arbitrary-equality-map');
+
+class ComputedArtifact{
+
+
+
+constructor(allComputedArtifacts){
+const cache=new ArbitraryEqualityMap();
+cache.setEqualityFn(ArbitraryEqualityMap.deepEquals);
+
+
+
+this._cache=cache;
+
+
+this._allComputedArtifacts=allComputedArtifacts;
+}
+
+
+
+
+get name(){
+throw new Error('name getter not implemented for computed artifact '+this.constructor.name);
+}
+
+
+
+
+
+
+
+
+
+
+
+async compute_(artifact,allComputedArtifacts){
+throw new Error('compute_() not implemented for computed artifact '+this.name);
+}
+
+
+
+
+
+
+
+
+
+
+async request(requiredArtifacts){
+const computed=this._cache.get(requiredArtifacts);
+if(computed){
+return computed;
+}
+
+
+
+const artifactPromise=
+this.compute_(requiredArtifacts,this._allComputedArtifacts);
+this._cache.set(requiredArtifacts,artifactPromise);
+
+return artifactPromise;
+}}
+
+
+module.exports=ComputedArtifact;
+
+},{"../../lib/arbitrary-equality-map":20}],12:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('../computed-artifact');
+const Node=require('../../../lib/dependency-graph/node');
+const NetworkNode=require('../../../lib/dependency-graph/network-node');
+const Simulator=require('../../../lib/dependency-graph/simulator/simulator');
+const WebInspector=require('../../../lib/web-inspector');
+
+class LanternMetricArtifact extends ComputedArtifact{
+
+
+
+
+
+static getScriptUrls(dependencyGraph,condition){
+const scriptUrls=new Set();
+
+dependencyGraph.traverse(node=>{
+if(node.type===Node.TYPES.CPU)return;
+const asNetworkNode=node;
+if(asNetworkNode.record._resourceType!==WebInspector.resourceTypes.Script)return;
+if(condition&&!condition(asNetworkNode))return;
+scriptUrls.add(asNetworkNode.record.url);
+});
+
+return scriptUrls;
+}
+
+
+
+
+get COEFFICIENTS(){
+throw new Error('COEFFICIENTS unimplemented!');
+}
+
+
+
+
+
+
+getOptimisticGraph(dependencyGraph,traceOfTab){
+throw new Error('Optimistic graph unimplemented!');
+}
+
+
+
+
+
+
+getPessimisticGraph(dependencyGraph,traceOfTab){
+throw new Error('Pessmistic graph unimplemented!');
+}
+
+
+
+
+
+
+getEstimateFromSimulation(simulationResult,extras){
+return simulationResult;
+}
+
+
+
+
+
+
+
+async computeMetricWithGraphs(data,artifacts,extras){
+const{trace,devtoolsLog,settings}=data;
+const graph=await artifacts.requestPageDependencyGraph({trace,devtoolsLog});
+const traceOfTab=await artifacts.requestTraceOfTab(trace);
+
+const simulator=data.simulator||(
+await artifacts.requestLoadSimulator({devtoolsLog,settings}));
+
+const optimisticGraph=this.getOptimisticGraph(graph,traceOfTab);
+const pessimisticGraph=this.getPessimisticGraph(graph,traceOfTab);
+
+const optimisticSimulation=simulator.simulate(optimisticGraph);
+const optimisticFlexSimulation=simulator.simulate(optimisticGraph,{flexibleOrdering:true});
+const pessimisticSimulation=simulator.simulate(pessimisticGraph);
+
+const optimisticEstimate=this.getEstimateFromSimulation(
+optimisticSimulation.timeInMs<optimisticFlexSimulation.timeInMs?
+optimisticSimulation:optimisticFlexSimulation,
+Object.assign({},extras,{optimistic:true}));
+
+
+const pessimisticEstimate=this.getEstimateFromSimulation(
+pessimisticSimulation,
+Object.assign({},extras,{optimistic:false}));
+
+
+
+const interceptMultiplier=this.COEFFICIENTS.intercept>0?
+Math.min(1,optimisticEstimate.timeInMs/1000):1;
+const timing=
+this.COEFFICIENTS.intercept*interceptMultiplier+
+this.COEFFICIENTS.optimistic*optimisticEstimate.timeInMs+
+this.COEFFICIENTS.pessimistic*pessimisticEstimate.timeInMs;
+
+return{
+timing,
+optimisticEstimate,
+pessimisticEstimate,
+optimisticGraph,
+pessimisticGraph};
+
+}
+
+
+
+
+
+
+compute_(data,computedArtifacts){
+return this.computeMetricWithGraphs(data,computedArtifacts);
+}}
+
+
+module.exports=LanternMetricArtifact;
+
+},{"../../../lib/dependency-graph/network-node":24,"../../../lib/dependency-graph/node":25,"../../../lib/dependency-graph/simulator/simulator":28,"../../../lib/web-inspector":47,"../computed-artifact":11}],13:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const ComputedArtifact=require('../computed-artifact');
+
+
+
+
+
+
+
+
+
+
+
+class ComputedMetric extends ComputedArtifact{
+
+get name(){
+throw new Error('Unimplemented');
+}
+
+
+
+
+
+
+computeSimulatedMetric(data,artifacts){
+
+return artifacts[`requestLantern${this.name}`](data);
+}
+
+
+
+
+
+
+computeObservedMetric(data,artifacts){
+throw new Error('Unimplemented');
+}
+
+
+
+
+
+
+async compute_(data,computedArtifacts){
+const{trace,devtoolsLog,settings}=data;
+if(!trace||!devtoolsLog||!settings){
+throw new Error('Did not provide necessary metric computation data');
+}
+
+const augmentedData=Object.assign({
+networkRecords:await computedArtifacts.requestNetworkRecords(devtoolsLog),
+traceOfTab:await computedArtifacts.requestTraceOfTab(trace)},
+data);
+
+switch(settings.throttlingMethod){
+case'simulate':
+return this.computeSimulatedMetric(augmentedData,computedArtifacts);
+case'provided':
+case'devtools':
+return this.computeObservedMetric(augmentedData,computedArtifacts);
+default:
+throw new TypeError(`Unrecognized throttling method: ${settings.throttlingMethod}`);}
+
+}}
+
+
+module.exports=ComputedMetric;
+
+},{"../computed-artifact":11}],14:[function(require,module,exports){
+
+
+
+
 
 'use strict';
 
@@ -14132,11 +16746,19 @@
 const log=require('lighthouse-logger');
 const LHError=require('../../lib/errors');
 
+
+
+
+
+
+
 class Connection{
 constructor(){
 this._lastCommandId=0;
 
 this._callbacks=new Map();
+
+
 this._eventEmitter=new EventEmitter();
 }
 
@@ -14157,7 +16779,6 @@
 
 
 
-
 wsEndpoint(){
 return Promise.reject(new Error('Not implemented'));
 }
@@ -14168,39 +16789,8 @@
 
 
 
-
-
-sendCommand(method,params={},cmdOpts={}){
-log.formatProtocol('method => browser',{method,params},'verbose');
-const id=++this._lastCommandId;
-const message=JSON.stringify({id,method,params});
-this.sendRawMessage(message);
-return new Promise((resolve,reject)=>{
-this._callbacks.set(id,{resolve,reject,method,options:cmdOpts});
-});
-}
-
-
-
-
-
-
-on(eventName,cb){
-if(eventName!=='notification'){
-throw new Error('Only supports "notification" events');
-}
-this._eventEmitter.on(eventName,cb);
-}
-
-
-
-
-
-
-
-
 sendRawMessage(message){
-return Promise.reject(new Error('Not implemented'));
+throw new Error('Not implemented');
 }
 
 
@@ -14214,13 +16804,24 @@
 const object=JSON.parse(message);
 
 
-if(this._callbacks.has(object.id)){
+if(!('id'in object)){
+
+const eventMessage=object;
+log.formatProtocol('<= event',
+{method:eventMessage.method,params:eventMessage.params},'verbose');
+this.emitProtocolEvent(eventMessage);
+return;
+}
+
 const callback=this._callbacks.get(object.id);
+if(callback){
 this._callbacks.delete(object.id);
 
+
+
 return callback.resolve(Promise.resolve().then(_=>{
 if(object.error){
-const logLevel=callback.options&&callback.options.silent?'verbose':'error';
+const logLevel=callback.options.silent?'verbose':'error';
 log.formatProtocol('method <= browser ERR',{method:callback.method},logLevel);
 throw LHError.fromProtocolMessage(callback.method,object.error);
 }
@@ -14229,40 +16830,80 @@
 {method:callback.method,params:object.result},'verbose');
 return object.result;
 }));
-}else if(object.id){
+}else{
 
 
 const error=object.error&&object.error.message;
 log.formatProtocol(`disowned method <= browser ${error?'ERR':'OK'}`,
-{method:object.method,params:error||object.result},'verbose');
-}else{
-log.formatProtocol('<= event',
-{method:object.method,params:object.params},'verbose');
-this.emitNotification(object.method,object.params);
+{method:'UNKNOWN',params:error||object.result},'verbose');
 }
 }
 
 
 
 
+emitProtocolEvent(eventMessage){
+if(!this._eventEmitter){
+throw new Error('Attempted to emit event after connection disposed.');
+}
 
-
-emitNotification(method,params){
-this._eventEmitter.emit('notification',{method,params});
+this._eventEmitter.emit('protocolevent',eventMessage);
 }
 
 
 
 
 dispose(){
+if(this._eventEmitter){
 this._eventEmitter.removeAllListeners();
 this._eventEmitter=null;
+}
 }}
 
 
+
+
+
+
+
+Connection.prototype.on=function on(eventName,cb){
+if(eventName!=='protocolevent'){
+throw new Error('Only supports "protocolevent" events');
+}
+
+if(!this._eventEmitter){
+throw new Error('Attempted to add event listener after connection disposed.');
+}
+this._eventEmitter.on(eventName,cb);
+};
+
+
+
+
+
+
+
+function _sendCommand(method,params={},cmdOpts={}){
+
+log.formatProtocol('method => browser',{method,params},'verbose');
+const id=++this._lastCommandId;
+const message=JSON.stringify({id,method,params});
+this.sendRawMessage(message);
+return new Promise(resolve=>{
+this._callbacks.set(id,{resolve,method,options:cmdOpts});
+});
+
+}
+
+
+
+
+
+Connection.prototype.sendCommand=_sendCommand;
+
 module.exports=Connection;
 
-},{"../../lib/errors":28,"events":56,"lighthouse-logger":137}],12:[function(require,module,exports){
+},{"../../lib/errors":33,"events":62,"lighthouse-logger":143}],15:[function(require,module,exports){
 
 
 
@@ -14295,6 +16936,9 @@
 
 
 class RawConnection extends Connection{
+
+
+
 constructor(port){
 super();
 this._port=port;
@@ -14322,6 +16966,7 @@
 
 
 
+
 sendRawMessage(message){
 this._port.send(message);
 }}
@@ -14329,7 +16974,7 @@
 
 module.exports=RawConnection;
 
-},{"./connection.js":11}],13:[function(require,module,exports){
+},{"./connection.js":14}],16:[function(require,module,exports){
 
 
 
@@ -14386,8 +17031,7 @@
 
 module.exports=DevtoolsLog;
 
-},{}],14:[function(require,module,exports){
-
+},{}],17:[function(require,module,exports){
 
 
 
@@ -14398,13 +17042,21 @@
 const NetworkRecorder=require('../lib/network-recorder');
 const emulation=require('../lib/emulation');
 const Element=require('../lib/element');
+const LHError=require('../lib/errors');
 const EventEmitter=require('events').EventEmitter;
 const URL=require('../lib/url-shim');
 const TraceParser=require('../lib/traces/trace-parser');
+const constants=require('../config/constants');
 
 const log=require('lighthouse-logger');
 const DevtoolsLog=require('./devtools-log');
 
+const pageFunctions=require('../lib/page-functions.js');
+
+
+
+const Connection=require('./connections/connection.js');
+
 
 const DEFAULT_PAUSE_AFTER_LOAD=0;
 
@@ -14412,43 +17064,48 @@
 
 const DEFAULT_CPU_QUIET_THRESHOLD=0;
 
-const _uniq=arr=>Array.from(new Set(arr));
+
+
+
 
 class Driver{
-static get MAX_WAIT_FOR_FULLY_LOADED(){
-return 45*1000;
-}
-
 
 
 
 constructor(connection){
-this._traceEvents=[];
 this._traceCategories=Driver.traceCategories;
+
+
+
+
 this._eventEmitter=new EventEmitter();
 this._connection=connection;
 
 this._devtoolsLog=new DevtoolsLog(/^(Page|Network)\./);
 this.online=true;
+
 this._domainEnabledCounts=new Map();
+
 this._isolatedExecutionContextId=undefined;
 
 
 
 
 
+
 this._networkStatusMonitor=null;
 
 
 
 
 
+
 this._monitoredUrl=null;
 
-connection.on('notification',event=>{
+connection.on('protocolevent',event=>{
 this._devtoolsLog.record(event);
 if(this._networkStatusMonitor){
-this._networkStatusMonitor.dispatch(event.method,event.params);
+this._networkStatusMonitor.dispatch(event);
 }
 this._eventEmitter.emit(event.method,event.params);
 });
@@ -14490,6 +17147,9 @@
 return this._connection.connect();
 }
 
+
+
+
 disconnect(){
 return this._connection.disconnect();
 }
@@ -14508,49 +17168,6 @@
 
 
 
-on(eventName,cb){
-if(this._eventEmitter===null){
-throw new Error('connect() must be called before attempting to listen to events.');
-}
-
-
-log.formatProtocol('listen for event =>',{method:eventName},'verbose');
-this._eventEmitter.on(eventName,cb);
-}
-
-
-
-
-
-
-
-once(eventName,cb){
-if(this._eventEmitter===null){
-throw new Error('connect() must be called before attempting to listen to events.');
-}
-
-log.formatProtocol('listen once for event =>',{method:eventName},'verbose');
-this._eventEmitter.once(eventName,cb);
-}
-
-
-
-
-
-
-off(eventName,cb){
-if(this._eventEmitter===null){
-throw new Error('connect() must be called before attempting to remove an event listener.');
-}
-
-this._eventEmitter.removeListener(eventName,cb);
-}
-
-
-
-
-
-
 
 
 
@@ -14580,25 +17197,6 @@
 
 
 
-
-
-sendCommand(method,params,cmdOpts){
-const domainCommand=/^(\w+)\.(enable|disable)$/.exec(method);
-if(domainCommand){
-const enable=domainCommand[2]==='enable';
-if(!this._shouldToggleDomain(domainCommand[1],enable)){
-return Promise.resolve();
-}
-}
-
-return this._connection.sendCommand(method,params,cmdOpts);
-}
-
-
-
-
-
-
 isDomainEnabled(domain){
 
 return!!this._domainEnabledCounts.get(domain);
@@ -14625,6 +17223,9 @@
 
 
 evaluateAsync(expression,options={}){
+
+
+
 const contextIdPromise=options.useIsolation?
 this._getOrCreateIsolatedContextId():
 Promise.resolve(undefined);
@@ -14657,7 +17258,7 @@
           return new __nativePromise(function (resolve) {
             return __nativePromise.resolve()
               .then(_ => ${expression})
-              .catch(${wrapRuntimeEvalErrorInBrowser.toString()})
+              .catch(${pageFunctions.wrapRuntimeEvalErrorInBrowser.toString()})
               .then(resolve);
           });
         }())`,
@@ -14686,6 +17287,9 @@
 });
 }
 
+
+
+
 getAppManifest(){
 return this.sendCommand('Page.getAppManifest').
 then(response=>{
@@ -14701,19 +17305,14 @@
 });
 }
 
-getSecurityState(){
-return new Promise((resolve,reject)=>{
-this.once('Security.securityStateChanged',data=>{
-this.sendCommand('Security.disable').
-then(_=>resolve(data),reject);
-});
 
-this.sendCommand('Security.enable').catch(reject);
-});
-}
+
 
 getServiceWorkerVersions(){
 return new Promise((resolve,reject)=>{
+
+
+
 const versionUpdatedListener=data=>{
 
 
@@ -14737,6 +17336,9 @@
 });
 }
 
+
+
+
 getServiceWorkerRegistrations(){
 return new Promise((resolve,reject)=>{
 this.once('ServiceWorker.workerRegistrationUpdated',data=>{
@@ -14756,8 +17358,11 @@
 
 
 assertNoSameOriginServiceWorkerClients(pageUrl){
+
 let registrations;
+
 let versions;
+
 return this.getServiceWorkerRegistrations().then(data=>{
 registrations=data.registrations;
 }).then(_=>this.getServiceWorkerVersions()).then(data=>{
@@ -14795,13 +17400,24 @@
 
 
 _waitForNetworkIdle(networkQuietThresholdMs){
+
 let idleTimeout;
-let cancel;
+
+let cancel=()=>{
+throw new Error('_waitForNetworkIdle.cancel() called before it was defined');
+};
+
+
+
+if(!this._networkStatusMonitor){
+throw new Error('Driver._waitForNetworkIdle called with no networkStatusMonitor');
+}
+const networkStatusMonitor=this._networkStatusMonitor;
 
 const promise=new Promise((resolve,reject)=>{
 const onIdle=()=>{
 
-this._networkStatusMonitor.once('network-2-busy',onBusy);
+networkStatusMonitor.once('network-2-busy',onBusy);
 idleTimeout=setTimeout(_=>{
 cancel();
 resolve();
@@ -14809,12 +17425,12 @@
 };
 
 const onBusy=()=>{
-this._networkStatusMonitor.once('network-2-idle',onIdle);
-clearTimeout(idleTimeout);
+networkStatusMonitor.once('network-2-idle',onIdle);
+idleTimeout&&clearTimeout(idleTimeout);
 };
 
 const domContentLoadedListener=()=>{
-if(this._networkStatusMonitor.is2Idle()){
+if(networkStatusMonitor.is2Idle()){
 onIdle();
 }else{
 onBusy();
@@ -14823,10 +17439,10 @@
 
 this.once('Page.domContentEventFired',domContentLoadedListener);
 cancel=()=>{
-clearTimeout(idleTimeout);
+idleTimeout&&clearTimeout(idleTimeout);
 this.off('Page.domContentEventFired',domContentLoadedListener);
-this._networkStatusMonitor.removeListener('network-2-busy',onBusy);
-this._networkStatusMonitor.removeListener('network-2-idle',onIdle);
+networkStatusMonitor.removeListener('network-2-busy',onBusy);
+networkStatusMonitor.removeListener('network-2-idle',onIdle);
 };
 });
 
@@ -14849,10 +17465,15 @@
 
 }
 
+
 let lastTimeout;
 let cancelled=false;
 
-const checkForQuietExpression=`(${checkTimeSinceLastLongTask.toString()})()`;
+const checkForQuietExpression=`(${pageFunctions.checkTimeSinceLastLongTask.toString()})()`;
+
+
+
+
 function checkForQuiet(driver,resolve){
 if(cancelled)return;
 
@@ -14860,7 +17481,8 @@
 then(timeSinceLongTask=>{
 if(cancelled)return;
 
-if(typeof timeSinceLongTask==='number'&&timeSinceLongTask>=waitForCPUQuiet){
+if(typeof timeSinceLongTask==='number'){
+if(timeSinceLongTask>=waitForCPUQuiet){
 log.verbose('Driver',`CPU has been idle for ${timeSinceLongTask} ms`);
 resolve();
 }else{
@@ -14868,10 +17490,14 @@
 const timeToWait=waitForCPUQuiet-timeSinceLongTask;
 lastTimeout=setTimeout(()=>checkForQuiet(driver,resolve),timeToWait);
 }
+}
 });
 }
 
-let cancel;
+
+let cancel=()=>{
+throw new Error('_waitForCPUIdle.cancel() called before it was defined');
+};
 const promise=new Promise((resolve,reject)=>{
 checkForQuiet(this,resolve);
 cancel=()=>{
@@ -14895,19 +17521,24 @@
 
 
 _waitForLoadEvent(pauseAfterLoadMs){
-let loadListener;
-let loadTimeout;
+
+let cancel=()=>{
+throw new Error('_waitForLoadEvent.cancel() called before it was defined');
+};
 
 const promise=new Promise((resolve,reject)=>{
-loadListener=function(){
+
+let loadTimeout;
+const loadListener=function(){
 loadTimeout=setTimeout(resolve,pauseAfterLoadMs);
 };
 this.once('Page.loadEventFired',loadListener);
-});
-const cancel=()=>{
+
+cancel=()=>{
 this.off('Page.loadEventFired',loadListener);
-clearTimeout(loadTimeout);
+loadTimeout&&clearTimeout(loadTimeout);
 };
+});
 
 return{
 promise,
@@ -14931,8 +17562,9 @@
 
 
 
-_waitForFullyLoaded(pauseAfterLoadMs,networkQuietThresholdMs,cpuQuietThresholdMs,
+async _waitForFullyLoaded(pauseAfterLoadMs,networkQuietThresholdMs,cpuQuietThresholdMs,
 maxWaitForLoadedMs){
+
 let maxTimeoutHandle;
 
 
@@ -14940,6 +17572,7 @@
 
 const waitForNetworkIdle=this._waitForNetworkIdle(networkQuietThresholdMs);
 
+
 let waitForCPUIdle=null;
 
 
@@ -14953,7 +17586,7 @@
 }).then(()=>{
 return function(){
 log.verbose('Driver','loadEventFired and network considered idle');
-clearTimeout(maxTimeoutHandle);
+maxTimeoutHandle&&clearTimeout(maxTimeoutHandle);
 };
 });
 
@@ -14971,10 +17604,11 @@
 });
 
 
-return Promise.race([
+const cleanupFn=await Promise.race([
 loadPromise,
-maxTimeoutPromise]).
-then(cleanup=>cleanup());
+maxTimeoutPromise]);
+
+cleanupFn();
 }
 
 
@@ -14990,7 +17624,8 @@
 
 
 this._monitoredUrl=startingUrl;
-this._networkStatusMonitor.on('requestloaded',redirectRequest=>{
+
+const requestLoadedListener=redirectRequest=>{
 
 if(!redirectRequest.redirectSource){
 return;
@@ -14999,7 +17634,8 @@
 if(earlierRequest.url===this._monitoredUrl){
 this._monitoredUrl=redirectRequest.url;
 }
-});
+};
+this._networkStatusMonitor.on('requestloaded',requestLoadedListener);
 
 return this.sendCommand('Network.enable');
 }
@@ -15014,6 +17650,11 @@
 this._networkStatusMonitor=null;
 const finalUrl=this._monitoredUrl;
 this._monitoredUrl=null;
+
+if(!finalUrl){
+throw new Error('Network Status Monitoring ended with an undefined finalUrl');
+}
+
 return finalUrl;
 }
 
@@ -15023,20 +17664,21 @@
 
 
 
-_getOrCreateIsolatedContextId(){
+async _getOrCreateIsolatedContextId(){
 if(typeof this._isolatedExecutionContextId==='number'){
-return Promise.resolve(this._isolatedExecutionContextId);
+return this._isolatedExecutionContextId;
 }
 
-return this.sendCommand('Page.getResourceTree').
-then(data=>{
-const mainFrameId=data.frameTree.frame.id;
-return this.sendCommand('Page.createIsolatedWorld',{
+const resourceTreeResponse=await this.sendCommand('Page.getResourceTree');
+const mainFrameId=resourceTreeResponse.frameTree.frame.id;
+
+const isolatedWorldResponse=await this.sendCommand('Page.createIsolatedWorld',{
 frameId:mainFrameId,
 worldName:'lighthouse_isolated_context'});
 
-}).
-then(data=>this._isolatedExecutionContextId=data.executionContextId);
+
+this._isolatedExecutionContextId=isolatedWorldResponse.executionContextId;
+return isolatedWorldResponse.executionContextId;
 }
 
 _clearIsolatedContextId(){
@@ -15054,35 +17696,38 @@
 
 
 
-gotoURL(url,options={}){
+async gotoURL(url,options={}){
 const waitForLoad=options.waitForLoad||false;
-const disableJS=options.disableJavaScript||false;
+const passContext=options.passContext||{};
+const disableJS=passContext.disableJavaScript||false;
 
-let pauseAfterLoadMs=options.config&&options.config.pauseAfterLoadMs;
-let networkQuietThresholdMs=options.config&&options.config.networkQuietThresholdMs;
-let cpuQuietThresholdMs=options.config&&options.config.cpuQuietThresholdMs;
-let maxWaitMs=options.flags&&options.flags.maxWaitForLoad;
+await this._beginNetworkStatusMonitoring(url);
+await this._clearIsolatedContextId();
 
 
-if(typeof pauseAfterLoadMs!=='number')pauseAfterLoadMs=DEFAULT_PAUSE_AFTER_LOAD;
-if(typeof networkQuietThresholdMs!=='number')networkQuietThresholdMs=DEFAULT_NETWORK_QUIET_THRESHOLD;
-if(typeof cpuQuietThresholdMs!=='number')cpuQuietThresholdMs=DEFAULT_CPU_QUIET_THRESHOLD;
-if(typeof maxWaitMs!=='number')maxWaitMs=Driver.MAX_WAIT_FOR_FULLY_LOADED;
-
-
-return this._beginNetworkStatusMonitoring(url).
-then(_=>this._clearIsolatedContextId()).
-then(_=>{
-
 
 
 this.sendCommand('Page.enable');
 this.sendCommand('Emulation.setScriptExecutionDisabled',{value:disableJS});
 this.sendCommand('Page.navigate',{url});
-}).
-then(_=>waitForLoad&&this._waitForFullyLoaded(pauseAfterLoadMs,
-networkQuietThresholdMs,cpuQuietThresholdMs,maxWaitMs)).
-then(_=>this._endNetworkStatusMonitoring());
+
+if(waitForLoad){
+const passConfig=passContext.passConfig||{};
+let{pauseAfterLoadMs,networkQuietThresholdMs,cpuQuietThresholdMs}=passConfig;
+let maxWaitMs=passContext.settings&&passContext.settings.maxWaitForLoad;
+
+
+if(typeof pauseAfterLoadMs!=='number')pauseAfterLoadMs=DEFAULT_PAUSE_AFTER_LOAD;
+if(typeof networkQuietThresholdMs!=='number')networkQuietThresholdMs=DEFAULT_NETWORK_QUIET_THRESHOLD;
+if(typeof cpuQuietThresholdMs!=='number')cpuQuietThresholdMs=DEFAULT_CPU_QUIET_THRESHOLD;
+if(typeof maxWaitMs!=='number')maxWaitMs=constants.defaultSettings.maxWaitForLoad;
+
+
+await this._waitForFullyLoaded(pauseAfterLoadMs,networkQuietThresholdMs,cpuQuietThresholdMs,
+maxWaitMs);
+}
+
+return this._endNetworkStatusMonitoring();
 }
 
 
@@ -15090,37 +17735,47 @@
 
 
 
-getObjectProperty(objectId,propName){
-return new Promise((resolve,reject)=>{
-this.sendCommand('Runtime.getProperties',{
+async getObjectProperty(objectId,propName){
+const propertiesResponse=await this.sendCommand('Runtime.getProperties',{
 objectId,
 accessorPropertiesOnly:true,
 generatePreview:false,
-ownProperties:false}).
+ownProperties:false});
 
-then(properties=>{
-const propertyForName=properties.result.
+
+const propertyForName=propertiesResponse.result.
 find(property=>property.name===propName);
 
 if(propertyForName&&propertyForName.value){
-resolve(propertyForName.value.value);
+return propertyForName.value.value;
 }else{
-resolve(null);
+return null;
 }
-}).catch(reject);
+}
+
+
+
+
+
+
+
+
+getRequestContent(requestId,timeout=1000){
+return new Promise((resolve,reject)=>{
+
+
+const err=new LHError(LHError.errors.REQUEST_CONTENT_TIMEOUT);
+const asyncTimeout=setTimeout(_=>reject(err),timeout);
+
+this.sendCommand('Network.getResponseBody',{requestId}).then(result=>{
+clearTimeout(asyncTimeout);
+
+resolve(result.body);
+}).catch(e=>{
+clearTimeout(asyncTimeout);
+reject(e);
 });
-}
-
-
-
-
-
-
-getRequestContent(requestId){
-return this.sendCommand('Network.getResponseBody',{
-requestId}).
-
-then(result=>result.body);
+});
 }
 
 
@@ -15142,41 +17797,42 @@
 
 
 
-querySelector(selector){
-return this.sendCommand('DOM.getDocument').
-then(result=>result.root.nodeId).
-then(nodeId=>this.sendCommand('DOM.querySelector',{
-nodeId,
-selector})).
+async querySelector(selector){
+const documentResponse=await this.sendCommand('DOM.getDocument');
+const rootNodeId=documentResponse.root.nodeId;
 
-then(element=>{
-if(element.nodeId===0){
+const targetNode=await this.sendCommand('DOM.querySelector',{
+nodeId:rootNodeId,
+selector});
+
+
+if(targetNode.nodeId===0){
 return null;
 }
-return new Element(element,this);
-});
+return new Element(targetNode,this);
 }
 
 
 
 
 
-querySelectorAll(selector){
-return this.sendCommand('DOM.getDocument').
-then(result=>result.root.nodeId).
-then(nodeId=>this.sendCommand('DOM.querySelectorAll',{
-nodeId,
-selector})).
+async querySelectorAll(selector){
+const documentResponse=await this.sendCommand('DOM.getDocument');
+const rootNodeId=documentResponse.root.nodeId;
 
-then(nodeList=>{
+const targetNodeList=await this.sendCommand('DOM.querySelectorAll',{
+nodeId:rootNodeId,
+selector});
+
+
+
 const elementList=[];
-nodeList.nodeIds.forEach(nodeId=>{
+targetNodeList.nodeIds.forEach(nodeId=>{
 if(nodeId!==0){
 elementList.push(new Element({nodeId},this));
 }
 });
 return elementList;
-});
 }
 
 
@@ -15199,23 +17855,22 @@
 
 
 
-getNodesInDocument(pierce=true){
-return this.sendCommand('DOM.getFlattenedDocument',{depth:-1,pierce}).
-then(result=>result.nodes?result.nodes:[]);
+async getNodesInDocument(pierce=true){
+const flattenedDocument=await this.sendCommand('DOM.getFlattenedDocument',
+{depth:-1,pierce});
+
+return flattenedDocument.nodes?flattenedDocument.nodes:[];
 }
 
 
 
 
-beginTrace(flags){
-const additionalCategories=flags&&flags.additionalTraceCategories&&
-flags.additionalTraceCategories.split(',')||[];
-const traceCategories=this._traceCategories.concat(additionalCategories);
-const tracingOpts={
-categories:_uniq(traceCategories).join(','),
-transferMode:'ReturnAsStream',
-options:'sampling-frequency=10000'};
 
+beginTrace(settings){
+const additionalCategories=settings&&settings.additionalTraceCategories&&
+settings.additionalTraceCategories.split(',')||[];
+const traceCategories=this._traceCategories.concat(additionalCategories);
+const uniqueCategories=Array.from(new Set(traceCategories));
 
 
 if(this.isDomainEnabled('Debugger')){
@@ -15233,9 +17888,16 @@
 
 
 then(_=>this.endTraceIfStarted()).
-then(_=>this.sendCommand('Tracing.start',tracingOpts));
+then(_=>this.sendCommand('Tracing.start',{
+categories:uniqueCategories.join(','),
+transferMode:'ReturnAsStream',
+options:'sampling-frequency=10000'}));
+
 }
 
+
+
+
 endTraceIfStarted(){
 return new Promise(resolve=>{
 const traceCallback=()=>resolve();
@@ -15247,11 +17909,14 @@
 });
 }
 
+
+
+
 endTrace(){
 return new Promise((resolve,reject)=>{
 
-this.once('Tracing.tracingComplete',streamHandle=>{
-this._readTraceFromStream(streamHandle).
+this.once('Tracing.tracingComplete',completeEvent=>{
+this._readTraceFromStream(completeEvent).
 then(traceContents=>resolve(traceContents),reject);
 });
 
@@ -15260,13 +17925,25 @@
 });
 }
 
-_readTraceFromStream(streamHandle){
+
+
+
+
+_readTraceFromStream(traceCompleteEvent){
 return new Promise((resolve,reject)=>{
 let isEOF=false;
 const parser=new TraceParser();
 
+if(!traceCompleteEvent.stream){
+return reject('No streamHandle returned by traceCompleteEvent');
+}
+
 const readArguments={
-handle:streamHandle.stream};
+handle:traceCompleteEvent.stream};
+
+
+
+
 
 
 const onChunkRead=response=>{
@@ -15305,37 +17982,53 @@
 return this._devtoolsLog.messages;
 }
 
+
+
+
 enableRuntimeEvents(){
 return this.sendCommand('Runtime.enable');
 }
 
-beginEmulation(flags){
-return Promise.resolve().then(_=>{
-if(!flags.disableDeviceEmulation)return emulation.enableNexus5X(this);
-}).then(_=>this.setThrottling(flags,{useThrottling:true}));
+
+
+
+
+async beginEmulation(settings){
+if(!settings.disableDeviceEmulation){
+await emulation.enableNexus5X(this);
 }
 
-setThrottling(flags,passConfig){
-const throttleCpu=passConfig.useThrottling&&!flags.disableCpuThrottling;
-const throttleNetwork=passConfig.useThrottling&&!flags.disableNetworkThrottling;
-const cpuPromise=throttleCpu?
-emulation.enableCPUThrottling(this):
+await this.setThrottling(settings,{useThrottling:true});
+}
+
+
+
+
+
+
+async setThrottling(settings,passConfig){
+if(settings.throttlingMethod!=='devtools'){
+return emulation.clearAllNetworkEmulation(this);
+}
+
+const cpuPromise=passConfig.useThrottling?
+emulation.enableCPUThrottling(this,settings.throttling):
 emulation.disableCPUThrottling(this);
-const networkPromise=throttleNetwork?
-emulation.enableNetworkThrottling(this):
-emulation.disableNetworkThrottling(this);
+const networkPromise=passConfig.useThrottling?
+emulation.enableNetworkThrottling(this,settings.throttling):
+emulation.clearAllNetworkEmulation(this);
 
-return Promise.all([cpuPromise,networkPromise]);
+await Promise.all([cpuPromise,networkPromise]);
 }
 
 
 
 
 
-goOffline(){
-return this.sendCommand('Network.enable').
-then(_=>emulation.goOffline(this)).
-then(_=>this.online=false);
+async goOffline(){
+await this.sendCommand('Network.enable');
+await emulation.goOffline(this);
+this.online=false;
 }
 
 
@@ -15343,12 +18036,15 @@
 
 
 
-
-goOnline(options){
-return this.setThrottling(options.flags,options.config).
-then(_=>this.online=true);
+async goOnline(options){
+await this.setThrottling(options.settings,options.passConfig);
+this.online=true;
 }
 
+
+
+
+
 cleanBrowserCaches(){
 
 return this.sendCommand('Network.clearBrowserCache').
@@ -15361,16 +18057,18 @@
 
 
 
-setExtraHTTPHeaders(headers){
-if(headers){
-return this.sendCommand('Network.setExtraHTTPHeaders',{
-headers});
-
+async setExtraHTTPHeaders(headers){
+if(!headers){
+return;
 }
 
-return Promise.resolve({});
+return this.sendCommand('Network.setExtraHTTPHeaders',{headers});
 }
 
+
+
+
+
 clearDataForOrigin(url){
 const origin=new URL(url).origin;
 
@@ -15399,8 +18097,8 @@
 
 
 
-cacheNatives(){
-return this.evaluteScriptOnNewDocument(`window.__nativePromise = Promise;
+async cacheNatives(){
+await this.evaluteScriptOnNewDocument(`window.__nativePromise = Promise;
         window.__nativeError = Error;`);
 }
 
@@ -15408,8 +18106,9 @@
 
 
 
-registerPerformanceObserver(){
-return this.evaluteScriptOnNewDocument(`(${registerPerformanceObserverInPage.toString()})()`);
+async registerPerformanceObserver(){
+const scriptStr=`(${pageFunctions.registerPerformanceObserverInPage.toString()})()`;
+await this.evaluteScriptOnNewDocument(scriptStr);
 }
 
 
@@ -15435,7 +18134,7 @@
 });
 };
 
-const funcBody=captureJSCallUsage.toString();
+const funcBody=pageFunctions.captureJSCallUsage.toString();
 
 this.evaluteScriptOnNewDocument(`
         ${globalVarToPopulate} = new Set();
@@ -15463,8 +18162,9 @@
 
 
 
-dismissJavaScriptDialogs(){
-return this.sendCommand('Page.enable').then(_=>{
+async dismissJavaScriptDialogs(){
+await this.sendCommand('Page.enable');
+
 this.on('Page.javascriptDialogOpening',data=>{
 log.warn('Driver',`${data.type} dialog opened by the page automatically suppressed.`);
 
@@ -15474,10 +18174,3922 @@
 promptText:'Lighthouse prompt response'});
 
 });
+}}
+
+
+
+
+
+
+
+Driver.prototype.on=function on(eventName,cb){
+if(this._eventEmitter===null){
+throw new Error('connect() must be called before attempting to listen to events.');
+}
+
+
+log.formatProtocol('listen for event =>',{method:eventName},'verbose');
+this._eventEmitter.on(eventName,cb);
+};
+
+
+
+
+
+
+Driver.prototype.once=function once(eventName,cb){
+if(this._eventEmitter===null){
+throw new Error('connect() must be called before attempting to listen to events.');
+}
+
+log.formatProtocol('listen once for event =>',{method:eventName},'verbose');
+this._eventEmitter.once(eventName,cb);
+};
+
+
+
+
+
+Driver.prototype.off=function off(eventName,cb){
+if(this._eventEmitter===null){
+throw new Error('connect() must be called before attempting to remove an event listener.');
+}
+
+this._eventEmitter.removeListener(eventName,cb);
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+function _sendCommand(method,params={},cmdOpts={}){
+const domainCommand=/^(\w+)\.(enable|disable)$/.exec(method);
+if(domainCommand){
+const enable=domainCommand[2]==='enable';
+
+if(!this._shouldToggleDomain(domainCommand[1],enable)){
+return Promise.resolve();
+}
+}
+
+
+return this._connection.sendCommand(method,params,cmdOpts);
+}
+
+
+
+
+
+Driver.prototype.sendCommand=_sendCommand;
+
+module.exports=Driver;
+
+},{"../config/constants":8,"../lib/element":31,"../lib/emulation":32,"../lib/errors":33,"../lib/network-recorder":37,"../lib/page-functions.js":38,"../lib/traces/trace-parser":45,"../lib/url-shim":"url","./connections/connection.js":14,"./devtools-log":16,"events":62,"lighthouse-logger":143}],18:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const log=require('lighthouse-logger');
+const LHError=require('../lib/errors');
+const URL=require('../lib/url-shim');
+const NetworkRecorder=require('../lib/network-recorder.js');
+const constants=require('../config/constants');
+
+const Driver=require('../gather/driver.js');
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class GatherRunner{
+
+
+
+
+
+
+
+
+
+
+static loadBlank(
+driver,
+url=constants.defaultPassConfig.blankPage,
+duration=constants.defaultPassConfig.blankDuration)
+{
+return driver.gotoURL(url).then(_=>new Promise(resolve=>setTimeout(resolve,duration)));
+}
+
+
+
+
+
+
+
+
+
+
+static loadPage(driver,passContext){
+return driver.gotoURL(passContext.url,{
+waitForLoad:true,
+passContext}).
+then(finalUrl=>{
+passContext.url=finalUrl;
+});
+}
+
+
+
+
+
+
+
+static setupDriver(driver,gathererResults,options){
+log.log('status','Initializing…');
+const resetStorage=!options.settings.disableStorageReset;
+
+return driver.assertNoSameOriginServiceWorkerClients(options.requestedUrl).
+then(_=>driver.getUserAgent()).
+then(userAgent=>{
+gathererResults.UserAgent=[userAgent];
+GatherRunner.warnOnHeadless(userAgent,gathererResults);
+}).
+then(_=>driver.beginEmulation(options.settings)).
+then(_=>driver.enableRuntimeEvents()).
+then(_=>driver.cacheNatives()).
+then(_=>driver.registerPerformanceObserver()).
+then(_=>driver.dismissJavaScriptDialogs()).
+then(_=>{
+if(resetStorage)return driver.clearDataForOrigin(options.requestedUrl);
+});
+}
+
+
+
+
+
+static disposeDriver(driver){
+log.log('status','Disconnecting from browser...');
+return driver.disconnect().catch(err=>{
+
+
+if(!/close\/.*status: 500$/.test(err.message)){
+log.error('GatherRunner disconnect',err.message);
+}
+});
+}
+
+
+
+
+
+
+
+static recoverOrThrow(promise){
+return promise.catch(err=>{
+if(err.fatal){
+throw err;
+}
+});
+}
+
+
+
+
+
+
+
+static getPageLoadError(url,networkRecords){
+const mainRecord=networkRecords.find(record=>{
+
+return URL.equalWithExcludedFragments(record.url,url);
+});
+
+let errorCode;
+let errorReason;
+if(!mainRecord){
+errorCode=LHError.errors.NO_DOCUMENT_REQUEST;
+}else if(mainRecord.failed){
+errorCode=LHError.errors.FAILED_DOCUMENT_REQUEST;
+errorReason=mainRecord.localizedFailDescription;
+}
+
+if(errorCode){
+const error=new LHError(errorCode,{reason:errorReason});
+log.error('GatherRunner',error.message,url);
+return error;
+}
+}
+
+
+
+
+
+
+static warnOnHeadless(userAgent,gathererResults){
+const chromeVersion=userAgent.split(/HeadlessChrome\/(.*) /)[1];
+
+
+const minVersion='63.0.3239.0';
+if(chromeVersion&&chromeVersion<minVersion){
+gathererResults.LighthouseRunWarnings.push('Your site\'s mobile performance may be '+
+'worse than the numbers presented in this report. Lighthouse could not test on a '+
+'mobile connection because Headless Chrome does not support network throttling '+
+'prior to version '+minVersion+'. The version used was '+chromeVersion);
+}
+}
+
+
+
+
+
+
+
+
+static beforePass(passContext,gathererResults){
+const blockedUrls=(passContext.passConfig.blockedUrlPatterns||[]).
+concat(passContext.settings.blockedUrlPatterns||[]);
+const blankPage=passContext.passConfig.blankPage;
+const blankDuration=passContext.passConfig.blankDuration;
+const pass=GatherRunner.loadBlank(passContext.driver,blankPage,blankDuration).
+
+
+
+then(()=>passContext.driver.blockUrlPatterns(blockedUrls)).
+then(()=>passContext.driver.setExtraHTTPHeaders(passContext.settings.extraHeaders));
+
+return passContext.passConfig.gatherers.reduce((chain,gathererDefn)=>{
+return chain.then(_=>{
+const gatherer=gathererDefn.instance;
+
+passContext.options=gathererDefn.options||{};
+const artifactPromise=Promise.resolve().then(_=>gatherer.beforePass(passContext));
+gathererResults[gatherer.name]=[artifactPromise];
+return GatherRunner.recoverOrThrow(artifactPromise);
+});
+},pass);
+}
+
+
+
+
+
+
+
+
+static pass(passContext,gathererResults){
+const driver=passContext.driver;
+const config=passContext.passConfig;
+const settings=passContext.settings;
+const gatherers=config.gatherers;
+
+const recordTrace=config.recordTrace;
+const isPerfRun=!settings.disableStorageReset&&recordTrace&&config.useThrottling;
+
+const gatherernames=gatherers.map(g=>g.instance.name).join(', ');
+const status='Loading page & waiting for onload';
+log.log('status',status,gatherernames);
+
+const pass=Promise.resolve().
+
+then(_=>{
+if(isPerfRun)driver.cleanBrowserCaches();
+}).
+
+then(_=>driver.beginDevtoolsLog()).
+
+then(_=>{
+if(recordTrace)driver.beginTrace(settings);
+}).
+
+then(_=>GatherRunner.loadPage(driver,passContext)).
+then(_=>log.log('statusEnd',status));
+
+return gatherers.reduce((chain,gathererDefn)=>{
+return chain.then(_=>{
+const gatherer=gathererDefn.instance;
+
+passContext.options=gathererDefn.options||{};
+const artifactPromise=Promise.resolve().then(_=>gatherer.pass(passContext));
+gathererResults[gatherer.name].push(artifactPromise);
+return GatherRunner.recoverOrThrow(artifactPromise);
+});
+},pass);
+}
+
+
+
+
+
+
+
+
+
+static async afterPass(passContext,gathererResults){
+const driver=passContext.driver;
+const config=passContext.passConfig;
+const gatherers=config.gatherers;
+
+let trace;
+if(config.recordTrace){
+log.log('status','Retrieving trace');
+trace=await driver.endTrace();
+log.verbose('statusEnd','Retrieving trace');
+}
+
+const status='Retrieving devtoolsLog and network records';
+log.log('status',status);
+const devtoolsLog=driver.endDevtoolsLog();
+const networkRecords=NetworkRecorder.recordsFromLogs(devtoolsLog);
+log.verbose('statusEnd',status);
+
+let pageLoadError=GatherRunner.getPageLoadError(passContext.url,networkRecords);
+
+if(!driver.online)pageLoadError=undefined;
+
+if(pageLoadError){
+gathererResults.LighthouseRunWarnings.push('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.');
+}
+
+
+
+const passData={
+networkRecords,
+devtoolsLog,
+trace};
+
+
+
+await driver.setThrottling(passContext.settings,{useThrottling:false});
+
+for(const gathererDefn of gatherers){
+const gatherer=gathererDefn.instance;
+const status=`Retrieving: ${gatherer.name}`;
+log.log('status',status);
+
+
+passContext.options=gathererDefn.options||{};
+
+
+const artifactPromise=pageLoadError?
+Promise.reject(pageLoadError):
+
+Promise.resolve().then(_=>gatherer.afterPass(passContext,passData));
+gathererResults[gatherer.name].push(artifactPromise);
+await GatherRunner.recoverOrThrow(artifactPromise);
+log.verbose('statusEnd',status);
+}
+
+
+return passData;
+}
+
+
+
+
+
+
+
+
+
+
+
+static async collectArtifacts(gathererResults,tracingData,settings){
+
+
+
+const artifacts={
+traces:tracingData.traces,
+devtoolsLogs:tracingData.devtoolsLogs,
+settings,
+
+LighthouseRunWarnings:Array.from(new Set(gathererResults.LighthouseRunWarnings))};
+
+
+const pageLoadFailures=[];
+for(const[gathererName,phaseResultsPromises]of Object.entries(gathererResults)){
+if(artifacts[gathererName]!==undefined)continue;
+
+try{
+const phaseResults=await Promise.all(phaseResultsPromises);
+
+const definedResults=phaseResults.filter(element=>element!==undefined);
+const artifact=definedResults[definedResults.length-1];
+artifacts[gathererName]=artifact;
+}catch(err){
+
+
+artifacts[gathererName]=err;
+
+if(LHError.isPageLoadError(err))pageLoadFailures.push(err);
+}
+
+if(artifacts[gathererName]===undefined){
+throw new Error(`${gathererName} failed to provide an artifact.`);
+}
+
+
+if(pageLoadFailures.length>Object.keys(artifacts).length*0.5){
+throw pageLoadFailures[0];
+}
+}
+
+return artifacts;
+}
+
+
+
+
+
+
+static run(passes,options){
+const driver=options.driver;
+
+const tracingData={
+traces:{},
+devtoolsLogs:{}};
+
+
+
+const gathererResults={
+LighthouseRunWarnings:[],
+fetchTime:[new Date().toJSON()],
+URL:[{requestedUrl:options.requestedUrl,finalUrl:''}]};
+
+
+return driver.connect().
+then(_=>GatherRunner.loadBlank(driver)).
+then(_=>GatherRunner.setupDriver(driver,gathererResults,options)).
+
+
+then(_=>{
+return passes.reduce((chain,passConfig,passIndex)=>{
+const passContext={
+driver:options.driver,
+
+url:options.requestedUrl,
+settings:options.settings,
+passConfig};
+
+
+return chain.
+then(_=>driver.setThrottling(options.settings,passConfig)).
+then(_=>GatherRunner.beforePass(passContext,gathererResults)).
+then(_=>GatherRunner.pass(passContext,gathererResults)).
+then(_=>GatherRunner.afterPass(passContext,gathererResults)).
+then(passData=>{
+
+tracingData.devtoolsLogs[passConfig.passName]=passData.devtoolsLog;
+
+
+if(passData.trace){
+tracingData.traces[passConfig.passName]=passData.trace;
+}
+
+if(passIndex===0){
+
+gathererResults.URL[0].finalUrl=passContext.url;
+}
+});
+},Promise.resolve());
+}).
+then(_=>GatherRunner.disposeDriver(driver)).
+then(_=>GatherRunner.collectArtifacts(gathererResults,tracingData,options.settings)).
+
+catch(err=>{
+GatherRunner.disposeDriver(driver);
+
+throw err;
 });
 }}
 
 
+module.exports=GatherRunner;
+
+},{"../config/constants":8,"../gather/driver.js":17,"../lib/errors":33,"../lib/network-recorder.js":37,"../lib/url-shim":"url","lighthouse-logger":143}],19:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+
+
+
+
+
+
+
+
+
+
+
+class Gatherer{
+
+
+
+get name(){
+return this.constructor.name;
+}
+
+
+
+
+
+
+
+
+beforePass(passContext){}
+
+
+
+
+
+
+
+pass(passContext){}
+
+
+
+
+
+
+
+
+
+afterPass(passContext,loadData){}}
+
+
+
+
+module.exports=Gatherer;
+
+},{}],20:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+
+const isEqual=require('lodash.isequal');
+
+
+
+
+
+
+module.exports=class ArbitraryEqualityMap{
+constructor(){
+this._equalsFn=(a,b)=>a===b;
+
+this._entries=[];
+}
+
+
+
+
+setEqualityFn(equalsFn){
+this._equalsFn=equalsFn;
+}
+
+
+
+
+
+has(key){
+return this._findIndexOf(key)!==-1;
+}
+
+
+
+
+
+get(key){
+const entry=this._entries[this._findIndexOf(key)];
+return entry&&entry.value;
+}
+
+
+
+
+
+set(key,value){
+let index=this._findIndexOf(key);
+if(index===-1)index=this._entries.length;
+this._entries[index]={key,value};
+}
+
+
+
+
+
+_findIndexOf(key){
+for(let i=0;i<this._entries.length;i++){
+if(this._equalsFn(key,this._entries[i].key))return i;
+}
+
+return-1;
+}
+
+
+
+
+
+
+
+
+
+static deepEquals(objA,objB){
+return isEqual(objA,objB);
+}};
+
+
+},{"lodash.isequal":144}],21:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+
+const path=require('path');
+const log=require('lighthouse-logger');
+const stream=require('stream');
+const Metrics=require('./traces/pwmetrics-events');
+const TraceParser=require('./traces/trace-parser');
+const rimraf=require('rimraf');
+const mkdirp=require('mkdirp');
+
+const artifactsFilename='artifacts.json';
+const traceSuffix='.trace.json';
+const devtoolsLogSuffix='.devtoolslog.json';
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function screenshotDump(screenshots){
+return`
+  <!doctype html>
+  <meta charset="utf-8">
+  <title>screenshots</title>
+  <style>
+html {
+    overflow-x: scroll;
+    overflow-y: hidden;
+    height: 100%;
+    background-image: linear-gradient(to left, #4ca1af , #c4e0e5);
+    background-attachment: fixed;
+    padding: 10px;
+}
+body {
+    white-space: nowrap;
+    background-image: linear-gradient(to left, #4ca1af , #c4e0e5);
+    width: 100%;
+    margin: 0;
+}
+img {
+    margin: 4px;
+}
+</style>
+  <body>
+    <script>
+      var shots = ${JSON.stringify(screenshots)};
+
+  shots.forEach(s => {
+    var i = document.createElement('img');
+    i.src = s.datauri;
+    i.title = s.timestamp;
+    document.body.appendChild(i);
+  });
+  </script>
+  `;
+}
+
+
+
+
+
+
+
+async function loadArtifacts(basePath){
+log.log('Reading artifacts from disk:',basePath);
+
+if(!fs.existsSync(basePath)){
+throw new Error('No saved artifacts found at '+basePath);
+}
+
+
+
+const artifacts=JSON.parse(fs.readFileSync(path.join(basePath,artifactsFilename),'utf8'));
+
+const filenames=fs.readdirSync(basePath);
+
+
+artifacts.devtoolsLogs={};
+filenames.filter(f=>f.endsWith(devtoolsLogSuffix)).map(filename=>{
+const passName=filename.replace(devtoolsLogSuffix,'');
+const devtoolsLog=JSON.parse(fs.readFileSync(path.join(basePath,filename),'utf8'));
+artifacts.devtoolsLogs[passName]=devtoolsLog;
+});
+
+
+artifacts.traces={};
+const promises=filenames.filter(f=>f.endsWith(traceSuffix)).map(filename=>{
+return new Promise(resolve=>{
+const passName=filename.replace(traceSuffix,'');
+const readStream=fs.createReadStream(path.join(basePath,filename),{
+encoding:'utf-8',
+highWaterMark:4*1024*1024});
+
+const parser=new TraceParser();
+readStream.on('data',chunk=>parser.parseChunk(chunk));
+readStream.on('end',_=>{
+artifacts.traces[passName]=parser.getTrace();
+resolve();
+});
+});
+});
+await Promise.all(promises);
+
+return artifacts;
+}
+
+
+
+
+
+
+
+
+async function saveArtifacts(artifacts,basePath){
+mkdirp.sync(basePath);
+rimraf.sync(`${basePath}/*${traceSuffix}`);
+rimraf.sync(`${basePath}/${artifactsFilename}`);
+
+const{traces,devtoolsLogs,...restArtifacts}=artifacts;
+
+
+for(const[passName,trace]of Object.entries(traces)){
+await saveTrace(trace,`${basePath}/${passName}${traceSuffix}`);
+}
+
+
+for(const[passName,devtoolsLog]of Object.entries(devtoolsLogs)){
+const log=JSON.stringify(devtoolsLog);
+fs.writeFileSync(`${basePath}/${passName}${devtoolsLogSuffix}`,log,'utf8');
+}
+
+
+const restArtifactsString=JSON.stringify(restArtifacts,null,2);
+fs.writeFileSync(`${basePath}/${artifactsFilename}`,restArtifactsString,'utf8');
+log.log('Artifacts saved to disk in folder:',basePath);
+}
+
+
+
+
+
+
+
+async function prepareAssets(artifacts,audits){
+const passNames=Object.keys(artifacts.traces);
+
+const assets=[];
+
+for(const passName of passNames){
+const trace=artifacts.traces[passName];
+const devtoolsLog=artifacts.devtoolsLogs[passName];
+
+
+const Runner=require('../runner.js');
+const computedArtifacts=Runner.instantiateComputedArtifacts();
+
+
+const screenshots=await computedArtifacts.requestScreenshots(trace);
+
+const traceData=Object.assign({},trace);
+const screenshotsHTML=screenshotDump(screenshots);
+
+if(audits){
+const evts=new Metrics(traceData.traceEvents,audits).generateFakeEvents();
+traceData.traceEvents=traceData.traceEvents.concat(evts);
+}
+
+assets.push({
+passName,
+traceData,
+devtoolsLog,
+screenshotsHTML,
+screenshots});
+
+}
+
+return assets;
+}
+
+
+
+
+
+
+
+function*traceJsonGenerator(traceData){
+const keys=Object.keys(traceData);
+
+yield'{\n';
+
+
+yield'"traceEvents": [\n';
+if(traceData.traceEvents.length>0){
+const eventsIterator=traceData.traceEvents[Symbol.iterator]();
+
+const firstEvent=eventsIterator.next().value;
+yield`  ${JSON.stringify(firstEvent)}`;
+for(const event of eventsIterator){
+yield`,\n  ${JSON.stringify(event)}`;
+}
+}
+yield'\n]';
+
+
+if(keys.length>1){
+for(const key of keys){
+if(key==='traceEvents')continue;
+
+yield`,\n"${key}": ${JSON.stringify(traceData[key],null,2)}`;
+}
+}
+
+yield'}\n';
+}
+
+
+
+
+
+
+
+function saveTrace(traceData,traceFilename){
+return new Promise((resolve,reject)=>{
+const traceIter=traceJsonGenerator(traceData);
+
+
+const traceStream=new stream.Readable({
+read(){
+const next=traceIter.next();
+this.push(next.done?null:next.value);
+}});
+
+
+const writeStream=fs.createWriteStream(traceFilename);
+writeStream.on('finish',resolve);
+writeStream.on('error',reject);
+
+traceStream.pipe(writeStream);
+});
+}
+
+
+
+
+
+
+
+
+async function saveAssets(artifacts,audits,pathWithBasename){
+const allAssets=await prepareAssets(artifacts,audits);
+const saveAll=allAssets.map(async(passAssets,index)=>{
+const devtoolsLogFilename=`${pathWithBasename}-${index}${devtoolsLogSuffix}`;
+fs.writeFileSync(devtoolsLogFilename,JSON.stringify(passAssets.devtoolsLog,null,2));
+log.log('saveAssets','devtools log saved to disk: '+devtoolsLogFilename);
+
+const screenshotsHTMLFilename=`${pathWithBasename}-${index}.screenshots.html`;
+fs.writeFileSync(screenshotsHTMLFilename,passAssets.screenshotsHTML);
+log.log('saveAssets','screenshots saved to disk: '+screenshotsHTMLFilename);
+
+const screenshotsJSONFilename=`${pathWithBasename}-${index}.screenshots.json`;
+fs.writeFileSync(screenshotsJSONFilename,JSON.stringify(passAssets.screenshots,null,2));
+log.log('saveAssets','screenshots saved to disk: '+screenshotsJSONFilename);
+
+const streamTraceFilename=`${pathWithBasename}-${index}${traceSuffix}`;
+log.log('saveAssets','streaming trace file to disk: '+streamTraceFilename);
+await saveTrace(passAssets.traceData,streamTraceFilename);
+log.log('saveAssets','trace file streamed to disk: '+streamTraceFilename);
+});
+
+await Promise.all(saveAll);
+}
+
+
+
+
+
+
+
+async function logAssets(artifacts,audits){
+const allAssets=await prepareAssets(artifacts,audits);
+allAssets.map(passAssets=>{
+log.log(`devtoolslog-${passAssets.passName}.json`,JSON.stringify(passAssets.devtoolsLog));
+const traceIter=traceJsonGenerator(passAssets.traceData);
+let traceJson='';
+for(const trace of traceIter){
+traceJson+=trace;
+}
+log.log(`trace-${passAssets.passName}.json`,traceJson);
+});
+}
+
+module.exports={
+saveArtifacts,
+loadArtifacts,
+saveAssets,
+prepareAssets,
+saveTrace,
+logAssets};
+
+
+},{"../runner.js":50,"./traces/pwmetrics-events":44,"./traces/trace-parser":45,"lighthouse-logger":143,"mkdirp":58,"path":75,"rimraf":58,"stream":92}],22:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+
+
+const log=require('lighthouse-logger');
+
+
+let _logs=[];
+
+class ConsoleQuieter{
+
+static mute(opts){
+_logs=_logs||[];
+
+
+console.log=function(...args){
+_logs.push({type:'log',args,prefix:opts.prefix});
+};
+
+console.warn=function(...args){
+_logs.push({type:'warn',args,prefix:opts.prefix});
+};
+
+console.error=function(...args){
+_logs.push({type:'error',args,prefix:opts.prefix});
+};
+}
+
+static unmuteAndFlush(){
+console.log=ConsoleQuieter._consolelog;
+console.warn=ConsoleQuieter._consolewarn;
+console.error=ConsoleQuieter._consoleerror;
+
+_logs.forEach(entry=>{
+log.verbose(`${entry.prefix}-${entry.type}`,...entry.args);
+});
+_logs=[];
+}}
+
+
+ConsoleQuieter._consolelog=console.log.bind(console);
+ConsoleQuieter._consolewarn=console.warn.bind(console);
+ConsoleQuieter._consoleerror=console.error.bind(console);
+
+module.exports=ConsoleQuieter;
+
+},{"lighthouse-logger":143}],23:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Node=require('./node');
+
+class CPUNode extends Node{
+
+
+
+
+constructor(parentEvent,childEvents=[]){
+const nodeId=`${parentEvent.tid}.${parentEvent.ts}`;
+super(nodeId);
+
+this._event=parentEvent;
+this._childEvents=childEvents;
+}
+
+
+
+
+get type(){
+return Node.TYPES.CPU;
+}
+
+
+
+
+get startTime(){
+return this._event.ts;
+}
+
+
+
+
+get endTime(){
+return this._event.ts+this._event.dur;
+}
+
+
+
+
+get event(){
+return this._event;
+}
+
+
+
+
+get childEvents(){
+return this._childEvents;
+}
+
+
+
+
+
+didPerformLayout(){
+return this._childEvents.some(evt=>evt.name==='Layout');
+}
+
+
+
+
+
+
+isEvaluateScriptFor(urls){
+return this._childEvents.some(evt=>{
+return evt.name==='EvaluateScript'&&
+!!evt.args.data&&!!evt.args.data.url&&
+urls.has(evt.args.data.url);
+});
+}
+
+
+
+
+cloneWithoutRelationships(){
+return new CPUNode(this._event,this._childEvents);
+}}
+
+
+module.exports=CPUNode;
+
+},{"./node":25}],24:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Node=require('./node');
+const WebInspector=require('../web-inspector');
+
+class NetworkNode extends Node{
+
+
+
+constructor(networkRecord){
+super(networkRecord.requestId);
+this._record=networkRecord;
+}
+
+
+
+
+get type(){
+return Node.TYPES.NETWORK;
+}
+
+
+
+
+get startTime(){
+return this._record.startTime*1000*1000;
+}
+
+
+
+
+get endTime(){
+return this._record.endTime*1000*1000;
+}
+
+
+
+
+get record(){
+return this._record;
+}
+
+
+
+
+get initiatorType(){
+return this._record._initiator&&this._record._initiator.type;
+}
+
+
+
+
+get fromDiskCache(){
+return!!this._record._fromDiskCache;
+}
+
+
+
+
+hasRenderBlockingPriority(){
+const priority=this._record.priority();
+const isScript=this._record._resourceType===WebInspector.resourceTypes.Script;
+const isDocument=this._record._resourceType===WebInspector.resourceTypes.Document;
+const isBlockingScript=priority==='High'&&isScript;
+const isBlockingHtmlImport=priority==='High'&&isDocument;
+return priority==='VeryHigh'||isBlockingScript||isBlockingHtmlImport;
+}
+
+
+
+
+cloneWithoutRelationships(){
+const node=new NetworkNode(this._record);
+node.setIsMainDocument(this._isMainDocument);
+return node;
+}}
+
+
+module.exports=NetworkNode;
+
+},{"../web-inspector":47,"./node":25}],25:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+
+
+
+
+
+
+
+
+
+
+
+
+class Node{
+
+
+
+constructor(id){
+this._id=id;
+this._isMainDocument=false;
+
+this._dependents=[];
+
+this._dependencies=[];
+}
+
+
+
+
+get id(){
+return this._id;
+}
+
+
+
+
+get type(){
+throw new Error('Unimplemented');
+}
+
+
+
+
+get startTime(){
+throw new Error('Unimplemented');
+}
+
+
+
+
+get endTime(){
+throw new Error('Unimplemented');
+}
+
+
+
+
+setIsMainDocument(value){
+this._isMainDocument=value;
+}
+
+
+
+
+isMainDocument(){
+return this._isMainDocument;
+}
+
+
+
+
+getDependents(){
+return this._dependents.slice();
+}
+
+
+
+
+getDependencies(){
+return this._dependencies.slice();
+}
+
+
+
+
+getNumberOfDependencies(){
+return this._dependencies.length;
+}
+
+
+
+
+getRootNode(){
+
+let rootNode=this;
+while(rootNode._dependencies.length){
+rootNode=rootNode._dependencies[0];
+}
+
+return rootNode;
+}
+
+
+
+
+addDependent(node){
+node.addDependency(this);
+}
+
+
+
+
+addDependency(node){
+if(this._dependencies.includes(node)){
+return;
+}
+
+node._dependents.push(this);
+this._dependencies.push(node);
+}
+
+
+
+
+removeDependent(node){
+node.removeDependency(this);
+}
+
+
+
+
+removeDependency(node){
+if(!this._dependencies.includes(node)){
+return;
+}
+
+node._dependents.splice(node._dependents.indexOf(this),1);
+this._dependencies.splice(this._dependencies.indexOf(node),1);
+}
+
+removeAllDependencies(){
+for(const node of this._dependencies.slice()){
+this.removeDependency(node);
+}
+}
+
+
+
+
+
+cloneWithoutRelationships(){
+const node=new Node(this.id);
+node.setIsMainDocument(this._isMainDocument);
+return node;
+}
+
+
+
+
+
+
+
+
+cloneWithRelationships(predicate){
+const rootNode=this.getRootNode();
+
+
+let shouldIncludeNode=()=>true;
+if(predicate){
+const idsToInclude=new Set();
+rootNode.traverse(node=>{
+if(predicate(node)){
+node.traverse(
+node=>idsToInclude.add(node.id),
+node=>node._dependencies.filter(parent=>!idsToInclude.has(parent)));
+
+}
+});
+
+shouldIncludeNode=node=>idsToInclude.has(node.id);
+}
+
+const idToNodeMap=new Map();
+rootNode.traverse(originalNode=>{
+if(!shouldIncludeNode(originalNode))return;
+const clonedNode=originalNode.cloneWithoutRelationships();
+idToNodeMap.set(clonedNode.id,clonedNode);
+});
+
+rootNode.traverse(originalNode=>{
+if(!shouldIncludeNode(originalNode))return;
+const clonedNode=idToNodeMap.get(originalNode.id);
+
+for(const dependency of originalNode._dependencies){
+const clonedDependency=idToNodeMap.get(dependency.id);
+clonedNode.addDependency(clonedDependency);
+}
+});
+
+if(!idToNodeMap.has(this.id))throw new Error(`Cloned graph missing node ${this.id}`);
+return idToNodeMap.get(this.id);
+}
+
+
+
+
+
+
+
+_traversePaths(iterator,getNext){
+
+const stack=[[this]];
+while(stack.length){
+
+
+const path=stack.shift();
+const node=path[0];
+iterator(node,path);
+
+const nodesToAdd=getNext(node);
+for(const nextNode of nodesToAdd){
+stack.push([nextNode].concat(path));
+}
+}
+}
+
+
+
+
+
+
+
+traverse(iterator,getNext){
+if(!getNext){
+getNext=node=>node.getDependents();
+}
+
+const visited=new Set();
+const originalGetNext=getNext;
+
+getNext=node=>{
+visited.add(node.id);
+const allNodesToVisit=originalGetNext(node);
+const nodesToVisit=allNodesToVisit.filter(nextNode=>!visited.has(nextNode.id));
+nodesToVisit.forEach(nextNode=>visited.add(nextNode.id));
+return nodesToVisit;
+};
+
+this._traversePaths(iterator,getNext);
+}
+
+
+
+
+
+
+
+static hasCycle(node,direction='both'){
+
+if(direction==='both'){
+return Node.hasCycle(node,'dependents')||Node.hasCycle(node,'dependencies');
+}
+
+const visited=new Set();
+
+const currentPath=[];
+const toVisit=[node];
+const depthAdded=new Map([[node,0]]);
+
+
+while(toVisit.length){
+
+
+
+const currentNode=toVisit.pop();
+
+
+if(currentPath.includes(currentNode))return true;
+
+if(visited.has(currentNode))continue;
+
+
+
+while(currentPath.length>depthAdded.get(currentNode))currentPath.pop();
+
+
+visited.add(currentNode);
+currentPath.push(currentNode);
+
+
+const nodesToExplore=direction==='dependents'?
+currentNode._dependents:
+currentNode._dependencies;
+for(const nextNode of nodesToExplore){
+if(toVisit.includes(nextNode))continue;
+toVisit.push(nextNode);
+depthAdded.set(nextNode,currentPath.length);
+}
+}
+
+return false;
+}}
+
+
+Node.TYPES={
+NETWORK:'network',
+CPU:'cpu'};
+
+
+module.exports=Node;
+
+},{}],26:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const NetworkAnalyzer=require('./network-analyzer');
+const TcpConnection=require('./tcp-connection');
+
+const DEFAULT_SERVER_RESPONSE_TIME=30;
+const TLS_SCHEMES=['https','wss'];
+
+
+
+const CONNECTIONS_PER_ORIGIN=6;
+
+module.exports=class ConnectionPool{
+
+
+
+
+constructor(records,options){
+this._options=Object.assign(
+{
+rtt:undefined,
+throughput:undefined,
+additionalRttByOrigin:new Map(),
+serverResponseTimeByOrigin:new Map()},
+
+options);
+
+
+if(!this._options.rtt||!this._options.throughput){
+throw new Error('Cannot create pool with no rtt or throughput');
+}
+
+this._records=records;
+
+this._connectionsByOrigin=new Map();
+
+this._connectionsByRecord=new Map();
+this._connectionsInUse=new Set();
+this._connectionReusedByRequestId=NetworkAnalyzer.estimateIfConnectionWasReused(records,{
+forceCoarseEstimates:true});
+
+
+this._initializeConnections();
+}
+
+
+
+
+connectionsInUse(){
+return Array.from(this._connectionsInUse);
+}
+
+_initializeConnections(){
+const connectionReused=this._connectionReusedByRequestId;
+const additionalRttByOrigin=this._options.additionalRttByOrigin;
+const serverResponseTimeByOrigin=this._options.serverResponseTimeByOrigin;
+
+const recordsByOrigin=NetworkAnalyzer.groupByOrigin(this._records);
+for(const[origin,records]of recordsByOrigin.entries()){
+const connections=[];
+const additionalRtt=additionalRttByOrigin.get(origin)||0;
+const responseTime=serverResponseTimeByOrigin.get(origin)||DEFAULT_SERVER_RESPONSE_TIME;
+
+for(const record of records){
+if(connectionReused.get(record.requestId))continue;
+
+const isTLS=TLS_SCHEMES.includes(record.parsedURL.scheme);
+const isH2=record.protocol==='h2';
+const connection=new TcpConnection(
+this._options.rtt+additionalRtt,
+this._options.throughput,
+responseTime,
+isTLS,
+isH2);
+
+
+connections.push(connection);
+}
+
+if(!connections.length){
+throw new Error(`Could not find a connection for origin: ${origin}`);
+}
+
+
+while(connections.length<CONNECTIONS_PER_ORIGIN)connections.push(connections[0].clone());
+
+this._connectionsByOrigin.set(origin,connections);
+}
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+acquire(record,options={}){
+if(this._connectionsByRecord.has(record)){
+
+return this._connectionsByRecord.get(record);
+}
+
+const origin=String(record.parsedURL.securityOrigin());
+
+const connections=this._connectionsByOrigin.get(origin)||[];
+
+const availableConnections=connections.
+filter(connection=>!this._connectionsInUse.has(connection)).
+sort((a,b)=>b.congestionWindow-a.congestionWindow);
+
+const observedConnectionWasReused=!!this._connectionReusedByRequestId.get(record.requestId);
+
+
+let connectionToUse=availableConnections[0];
+if(!options.ignoreConnectionReused){
+connectionToUse=availableConnections.find(
+connection=>connection.isWarm()===observedConnectionWasReused);
+
+}
+
+if(!connectionToUse)return null;
+
+this._connectionsInUse.add(connectionToUse);
+this._connectionsByRecord.set(record,connectionToUse);
+return connectionToUse;
+}
+
+
+
+
+release(record){
+const connection=this._connectionsByRecord.get(record);
+this._connectionsByRecord.delete(record);
+this._connectionsInUse.delete(connection);
+}};
+
+
+},{"./network-analyzer":27,"./tcp-connection":29}],27:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const INITIAL_CWD=14*1024;
+const WebInspector=require('../../web-inspector');
+
+class NetworkAnalyzer{
+
+
+
+static get SUMMARY(){
+return'__SUMMARY__';
+}
+
+
+
+
+
+static groupByOrigin(records){
+const grouped=new Map();
+records.forEach(item=>{
+const key=item.parsedURL.securityOrigin();
+const group=grouped.get(key)||[];
+group.push(item);
+grouped.set(key,group);
+});
+return grouped;
+}
+
+
+
+
+
+static getSummary(values){
+values.sort((a,b)=>a-b);
+
+return{
+min:values[0],
+max:values[values.length-1],
+avg:values.reduce((a,b)=>a+b,0)/values.length,
+median:values[Math.floor((values.length-1)/2)]};
+
+}
+
+
+
+
+
+static summarize(values){
+const summaryByKey=new Map();
+const allEstimates=[];
+for(const[key,estimates]of values){
+summaryByKey.set(key,NetworkAnalyzer.getSummary(estimates));
+allEstimates.push(...estimates);
+}
+
+summaryByKey.set(NetworkAnalyzer.SUMMARY,NetworkAnalyzer.getSummary(allEstimates));
+return summaryByKey;
+}
+
+
+
+
+
+
+static _estimateValueByOrigin(records,iteratee){
+const connectionWasReused=NetworkAnalyzer.estimateIfConnectionWasReused(records);
+const groupedByOrigin=NetworkAnalyzer.groupByOrigin(records);
+
+const estimates=new Map();
+for(const[origin,originRecords]of groupedByOrigin.entries()){
+
+let originEstimates=[];
+
+for(const record of originRecords){
+const timing=record._timing;
+if(!timing)continue;
+
+const value=iteratee({
+record,
+timing,
+connectionReused:connectionWasReused.get(record.requestId)});
+
+if(typeof value!=='undefined'){
+originEstimates=originEstimates.concat(value);
+}
+}
+
+if(!originEstimates.length)continue;
+estimates.set(origin,originEstimates);
+}
+
+return estimates;
+}
+
+
+
+
+
+
+
+
+static _estimateRTTByOriginViaTCPTiming(records){
+return NetworkAnalyzer._estimateValueByOrigin(records,({timing,connectionReused})=>{
+if(connectionReused)return;
+
+
+
+if(timing.sslStart>0&&timing.sslEnd>0){
+return[timing.connectEnd-timing.sslStart,timing.sslStart-timing.connectStart];
+}else if(timing.connectStart>0&&timing.connectEnd>0){
+return timing.connectEnd-timing.connectStart;
+}
+});
+}
+
+
+
+
+
+
+
+
+
+static _estimateRTTByOriginViaDownloadTiming(records){
+return NetworkAnalyzer._estimateValueByOrigin(records,({record,timing,connectionReused})=>{
+if(connectionReused)return;
+
+if(record.transferSize<=INITIAL_CWD)return;
+if(!Number.isFinite(timing.receiveHeadersEnd)||timing.receiveHeadersEnd<0)return;
+
+
+const totalTime=(record.endTime-record.startTime)*1000;
+const downloadTimeAfterFirstByte=totalTime-timing.receiveHeadersEnd;
+const numberOfRoundTrips=Math.log2(record.transferSize/INITIAL_CWD);
+
+
+
+if(numberOfRoundTrips>5)return;
+return downloadTimeAfterFirstByte/numberOfRoundTrips;
+});
+}
+
+
+
+
+
+
+
+
+
+
+static _estimateRTTByOriginViaSendStartTiming(records){
+return NetworkAnalyzer._estimateValueByOrigin(records,({record,timing,connectionReused})=>{
+if(connectionReused)return;
+if(!Number.isFinite(timing.sendStart)||timing.sendStart<0)return;
+
+
+
+let roundTrips=1;
+if(record.parsedURL.scheme==='https')roundTrips+=1;
+return timing.sendStart/roundTrips;
+});
+}
+
+
+
+
+
+
+
+
+static _estimateResponseTimeByOrigin(records,rttByOrigin){
+return NetworkAnalyzer._estimateValueByOrigin(records,({record,timing})=>{
+if(!Number.isFinite(timing.receiveHeadersEnd)||timing.receiveHeadersEnd<0)return;
+if(!Number.isFinite(timing.sendEnd)||timing.sendEnd<0)return;
+
+const ttfb=timing.receiveHeadersEnd-timing.sendEnd;
+const origin=record.parsedURL.securityOrigin();
+const rtt=rttByOrigin.get(origin)||rttByOrigin.get(NetworkAnalyzer.SUMMARY)||0;
+return Math.max(ttfb-rtt,0);
+});
+}
+
+
+
+
+
+static canTrustConnectionInformation(records){
+const connectionIdWasStarted=new Map();
+for(const record of records){
+const started=connectionIdWasStarted.get(record.connectionId)||!record.connectionReused;
+connectionIdWasStarted.set(record.connectionId,started);
+}
+
+
+if(connectionIdWasStarted.size<=1)return false;
+
+return Array.from(connectionIdWasStarted.values()).every(started=>started);
+}
+
+
+
+
+
+
+
+
+
+static estimateIfConnectionWasReused(records,options){
+options=Object.assign({forceCoarseEstimates:false},options);
+
+
+if(!options.forceCoarseEstimates&&NetworkAnalyzer.canTrustConnectionInformation(records)){
+
+return new Map(records.map(record=>[record.requestId,!!record.connectionReused]));
+}
+
+
+
+
+
+const connectionWasReused=new Map();
+const groupedByOrigin=NetworkAnalyzer.groupByOrigin(records);
+for(const[_,originRecords]of groupedByOrigin.entries()){
+const earliestReusePossible=originRecords.
+map(record=>record.endTime).
+reduce((a,b)=>Math.min(a,b),Infinity);
+
+for(const record of originRecords){
+connectionWasReused.set(
+record.requestId,
+record.startTime>=earliestReusePossible||record.protocol==='h2');
+
+}
+
+
+
+const firstRecord=originRecords.reduce((a,b)=>a.startTime>b.startTime?b:a);
+connectionWasReused.set(firstRecord.requestId,false);
+}
+
+return connectionWasReused;
+}
+
+
+
+
+
+
+
+
+
+
+static estimateRTTByOrigin(records,options){
+options=Object.assign(
+{
+
+
+forceCoarseEstimates:false,
+
+
+coarseEstimateMultiplier:0.5},
+
+options);
+
+
+let estimatesByOrigin=NetworkAnalyzer._estimateRTTByOriginViaTCPTiming(records);
+if(!estimatesByOrigin.size||options.forceCoarseEstimates){
+estimatesByOrigin=new Map();
+const estimatesViaDownload=NetworkAnalyzer._estimateRTTByOriginViaDownloadTiming(records);
+const estimatesViaSendStart=NetworkAnalyzer._estimateRTTByOriginViaSendStartTiming(records);
+
+for(const[origin,estimates]of estimatesViaDownload.entries()){
+estimatesByOrigin.set(origin,estimates);
+}
+
+for(const[origin,estimates]of estimatesViaSendStart.entries()){
+const existing=estimatesByOrigin.get(origin)||[];
+estimatesByOrigin.set(origin,existing.concat(estimates));
+}
+
+for(const estimates of estimatesByOrigin.values()){
+estimates.forEach((x,i)=>estimates[i]=x*options.coarseEstimateMultiplier);
+}
+}
+
+if(!estimatesByOrigin.size)throw new Error('No timing information available');
+return NetworkAnalyzer.summarize(estimatesByOrigin);
+}
+
+
+
+
+
+
+
+
+
+static estimateServerResponseTimeByOrigin(records,options){
+options=Object.assign(
+{
+rttByOrigin:null},
+
+options);
+
+
+let rttByOrigin=options.rttByOrigin;
+if(!rttByOrigin){
+rttByOrigin=NetworkAnalyzer.estimateRTTByOrigin(records,options);
+for(const[origin,summary]of rttByOrigin.entries()){
+rttByOrigin.set(origin,summary.min);
+}
+}
+
+const estimatesByOrigin=NetworkAnalyzer._estimateResponseTimeByOrigin(records,rttByOrigin);
+return NetworkAnalyzer.summarize(estimatesByOrigin);
+}
+
+
+
+
+
+static findMainDocument(records){
+
+const documentRequests=records.filter(record=>record._resourceType===
+WebInspector.resourceTypes.Document);
+return documentRequests.sort((a,b)=>a.startTime-b.startTime)[0];
+}}
+
+
+module.exports=NetworkAnalyzer;
+
+
+
+
+
+
+
+
+
+},{"../../web-inspector":47}],28:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Node=require('../node');
+const NetworkNode=require('../network-node');
+const CpuNode=require('../cpu-node');
+const TcpConnection=require('./tcp-connection');
+const ConnectionPool=require('./connection-pool');
+const mobile3G=require('../../../config/constants').throttling.mobile3G;
+
+
+const DEFAULT_MAXIMUM_CONCURRENT_REQUESTS=10;
+
+const DEFAULT_LAYOUT_TASK_MULTIPLIER=0.5;
+
+const DEFAULT_MAXIMUM_CPU_TASK_DURATION=10000;
+
+const NodeState={
+NotReadyToStart:0,
+ReadyToStart:1,
+InProgress:2,
+Complete:3};
+
+
+class Simulator{
+
+
+
+constructor(options){
+
+this._options=Object.assign(
+{
+rtt:mobile3G.rttMs,
+throughput:mobile3G.throughputKbps*1024,
+maximumConcurrentRequests:DEFAULT_MAXIMUM_CONCURRENT_REQUESTS,
+cpuSlowdownMultiplier:mobile3G.cpuSlowdownMultiplier,
+layoutTaskMultiplier:DEFAULT_LAYOUT_TASK_MULTIPLIER,
+additionalRttByOrigin:new Map(),
+serverResponseTimeByOrigin:new Map()},
+
+options);
+
+
+this._rtt=this._options.rtt;
+this._throughput=this._options.throughput;
+this._maximumConcurrentRequests=Math.max(Math.min(
+TcpConnection.maximumSaturatedConnections(this._rtt,this._throughput),
+this._options.maximumConcurrentRequests),
+1);
+this._cpuSlowdownMultiplier=this._options.cpuSlowdownMultiplier;
+this._layoutTaskMultiplier=this._cpuSlowdownMultiplier*this._options.layoutTaskMultiplier;
+
+
+this._flexibleOrdering=false;
+this._nodeTimings=new Map();
+this._numberInProgressByType=new Map();
+this._nodes={};
+
+this._connectionPool=null;
+}
+
+
+
+
+_initializeConnectionPool(graph){
+
+const records=[];
+graph.getRootNode().traverse(node=>{
+if(node.type===Node.TYPES.NETWORK){
+records.push(node.record);
+}
+});
+
+this._connectionPool=new ConnectionPool(records,this._options);
+}
+
+
+
+
+_initializeAuxiliaryData(){
+this._nodeTimings=new Map();
+this._numberInProgressByType=new Map();
+
+this._nodes={};
+for(const key of Object.keys(NodeState)){
+this._nodes[NodeState[key]]=new Set();
+}
+}
+
+
+
+
+
+_numberInProgress(type){
+return this._numberInProgressByType.get(type)||0;
+}
+
+
+
+
+
+_setTimingData(node,values){
+const timingData=this._nodeTimings.get(node)||{};
+Object.assign(timingData,values);
+this._nodeTimings.set(node,timingData);
+}
+
+
+
+
+
+_markNodeAsReadyToStart(node,queuedTime){
+this._nodes[NodeState.ReadyToStart].add(node);
+this._nodes[NodeState.NotReadyToStart].delete(node);
+this._setTimingData(node,{queuedTime});
+}
+
+
+
+
+
+_markNodeAsInProgress(node,startTime){
+this._nodes[NodeState.InProgress].add(node);
+this._nodes[NodeState.ReadyToStart].delete(node);
+this._numberInProgressByType.set(node.type,this._numberInProgress(node.type)+1);
+this._setTimingData(node,{startTime});
+}
+
+
+
+
+
+_markNodeAsComplete(node,endTime){
+this._nodes[NodeState.Complete].add(node);
+this._nodes[NodeState.InProgress].delete(node);
+this._numberInProgressByType.set(node.type,this._numberInProgress(node.type)-1);
+this._setTimingData(node,{endTime});
+
+
+for(const dependent of node.getDependents()){
+
+const dependencies=dependent.getDependencies();
+if(dependencies.some(dep=>!this._nodes[NodeState.Complete].has(dep)))continue;
+
+
+this._markNodeAsReadyToStart(dependent,endTime);
+}
+}
+
+
+
+
+
+_acquireConnection(record){
+return this._connectionPool.acquire(record,{
+ignoreConnectionReused:this._flexibleOrdering});
+
+}
+
+
+
+
+
+_startNodeIfPossible(node,totalElapsedTime){
+if(node.type===Node.TYPES.CPU){
+
+if(this._numberInProgress(node.type)===0){
+this._markNodeAsInProgress(node,totalElapsedTime);
+this._setTimingData(node,{timeElapsed:0});
+}
+
+return;
+}
+
+if(node.type!==Node.TYPES.NETWORK)throw new Error('Unsupported');
+
+const networkNode=node;
+
+if(!networkNode.fromDiskCache){
+
+const numberOfActiveRequests=this._numberInProgress(node.type);
+if(numberOfActiveRequests>=this._maximumConcurrentRequests)return;
+const connection=this._acquireConnection(networkNode.record);
+if(!connection)return;
+}
+
+this._markNodeAsInProgress(node,totalElapsedTime);
+this._setTimingData(node,{
+timeElapsed:0,
+timeElapsedOvershoot:0,
+bytesDownloaded:0});
+
+}
+
+
+
+
+
+_updateNetworkCapacity(){
+for(const connection of this._connectionPool.connectionsInUse()){
+connection.setThroughput(this._throughput/this._nodes[NodeState.InProgress].size);
+}
+}
+
+
+
+
+
+
+_estimateTimeRemaining(node){
+if(node.type===Node.TYPES.CPU){
+return this._estimateCPUTimeRemaining(node);
+}else if(node.type===Node.TYPES.NETWORK){
+return this._estimateNetworkTimeRemaining(node);
+}else{
+throw new Error('Unsupported');
+}
+}
+
+
+
+
+
+_estimateCPUTimeRemaining(cpuNode){
+const timingData=this._nodeTimings.get(cpuNode);
+const multiplier=cpuNode.didPerformLayout()?
+this._layoutTaskMultiplier:
+this._cpuSlowdownMultiplier;
+const totalDuration=Math.min(
+Math.round(cpuNode.event.dur/1000*multiplier),
+DEFAULT_MAXIMUM_CPU_TASK_DURATION);
+
+const estimatedTimeElapsed=totalDuration-timingData.timeElapsed;
+this._setTimingData(cpuNode,{estimatedTimeElapsed});
+return estimatedTimeElapsed;
+}
+
+
+
+
+
+_estimateNetworkTimeRemaining(networkNode){
+const timingData=this._nodeTimings.get(networkNode);
+
+let timeElapsed=0;
+if(networkNode.fromDiskCache){
+
+
+const sizeInMb=(networkNode.record._resourceSize||0)/1024/1024;
+timeElapsed=8+20*sizeInMb-timingData.timeElapsed;
+}else{
+
+const connection=this._acquireConnection(networkNode.record);
+const calculation=connection.simulateDownloadUntil(
+networkNode.record.transferSize-timingData.bytesDownloaded,
+{timeAlreadyElapsed:timingData.timeElapsed,maximumTimeToElapse:Infinity});
+
+
+timeElapsed=calculation.timeElapsed;
+}
+
+const estimatedTimeElapsed=timeElapsed+timingData.timeElapsedOvershoot;
+this._setTimingData(networkNode,{estimatedTimeElapsed});
+return estimatedTimeElapsed;
+}
+
+
+
+
+
+_findNextNodeCompletionTime(){
+let minimumTime=Infinity;
+for(const node of this._nodes[NodeState.InProgress]){
+minimumTime=Math.min(minimumTime,this._estimateTimeRemaining(node));
+}
+
+return minimumTime;
+}
+
+
+
+
+
+
+
+_updateProgressMadeInTimePeriod(node,timePeriodLength,totalElapsedTime){
+const timingData=this._nodeTimings.get(node);
+const isFinished=timingData.estimatedTimeElapsed===timePeriodLength;
+
+const networkNode=node;
+
+if(node.type===Node.TYPES.CPU||networkNode.fromDiskCache){
+return isFinished?
+this._markNodeAsComplete(node,totalElapsedTime):
+timingData.timeElapsed+=timePeriodLength;
+}
+
+if(node.type!==Node.TYPES.NETWORK)throw new Error('Unsupported');
+
+const record=networkNode.record;
+
+const connection=this._acquireConnection(record);
+const calculation=connection.simulateDownloadUntil(
+record.transferSize-timingData.bytesDownloaded,
+{
+timeAlreadyElapsed:timingData.timeElapsed,
+maximumTimeToElapse:timePeriodLength-timingData.timeElapsedOvershoot});
+
+
+
+connection.setCongestionWindow(calculation.congestionWindow);
+connection.setH2OverflowBytesDownloaded(calculation.extraBytesDownloaded);
+
+if(isFinished){
+connection.setWarmed(true);
+this._connectionPool.release(record);
+this._markNodeAsComplete(node,totalElapsedTime);
+}else{
+timingData.timeElapsed+=calculation.timeElapsed;
+timingData.timeElapsedOvershoot+=calculation.timeElapsed-timePeriodLength;
+timingData.bytesDownloaded+=calculation.bytesDownloaded;
+}
+}
+
+
+
+
+getOptions(){
+return this._options;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+simulate(graph,options){
+if(Node.hasCycle(graph)){
+throw new Error('Cannot simulate graph with cycle');
+}
+
+options=Object.assign({flexibleOrdering:false},options);
+
+this._flexibleOrdering=!!options.flexibleOrdering;
+this._initializeConnectionPool(graph);
+this._initializeAuxiliaryData();
+
+const nodesNotReadyToStart=this._nodes[NodeState.NotReadyToStart];
+const nodesReadyToStart=this._nodes[NodeState.ReadyToStart];
+const nodesInProgress=this._nodes[NodeState.InProgress];
+
+const rootNode=graph.getRootNode();
+rootNode.traverse(node=>nodesNotReadyToStart.add(node));
+let totalElapsedTime=0;
+let iteration=0;
+
+
+this._markNodeAsReadyToStart(rootNode,totalElapsedTime);
+
+
+while(nodesReadyToStart.size||nodesInProgress.size){
+
+for(const node of nodesReadyToStart){
+this._startNodeIfPossible(node,totalElapsedTime);
+}
+
+if(!nodesInProgress.size){
+
+
+if(this._flexibleOrdering)throw new Error('Failed to start a node');
+this._flexibleOrdering=true;
+continue;
+}
+
+
+this._updateNetworkCapacity();
+
+
+const minimumTime=this._findNextNodeCompletionTime();
+totalElapsedTime+=minimumTime;
+
+
+if(!Number.isFinite(minimumTime)||iteration>100000){
+throw new Error('Graph creation failed, depth exceeded');
+}
+
+iteration++;
+
+for(const node of nodesInProgress){
+this._updateProgressMadeInTimePeriod(node,minimumTime,totalElapsedTime);
+}
+}
+
+return{
+timeInMs:totalElapsedTime,
+nodeTimings:this._nodeTimings};
+
+}}
+
+
+module.exports=Simulator;
+
+},{"../../../config/constants":8,"../cpu-node":23,"../network-node":24,"../node":25,"./connection-pool":26,"./tcp-connection":29}],29:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const INITIAL_CONGESTION_WINDOW=10;
+const TCP_SEGMENT_SIZE=1460;
+
+class TcpConnection{
+
+
+
+
+
+
+
+constructor(rtt,throughput,serverLatency=0,ssl=true,h2=false){
+this._warmed=false;
+this._ssl=ssl;
+this._h2=h2;
+this._rtt=rtt;
+this._throughput=throughput;
+this._serverLatency=serverLatency;
+this._congestionWindow=INITIAL_CONGESTION_WINDOW;
+this._h2OverflowBytesDownloaded=0;
+}
+
+
+
+
+
+
+static maximumSaturatedConnections(rtt,availableThroughput){
+const roundTripsPerSecond=1000/rtt;
+const bytesPerRoundTrip=TCP_SEGMENT_SIZE;
+const bytesPerSecond=roundTripsPerSecond*bytesPerRoundTrip;
+const minimumThroughputRequiredPerRequest=bytesPerSecond*8;
+return Math.floor(availableThroughput/minimumThroughputRequiredPerRequest);
+}
+
+
+
+
+_computeMaximumCongestionWindowInSegments(){
+const bytesPerSecond=this._throughput/8;
+const secondsPerRoundTrip=this._rtt/1000;
+const bytesPerRoundTrip=bytesPerSecond*secondsPerRoundTrip;
+return Math.floor(bytesPerRoundTrip/TCP_SEGMENT_SIZE);
+}
+
+
+
+
+setThroughput(throughput){
+this._throughput=throughput;
+}
+
+
+
+
+setCongestionWindow(congestion){
+this._congestionWindow=congestion;
+}
+
+
+
+
+setWarmed(warmed){
+this._warmed=warmed;
+}
+
+
+
+
+isWarm(){
+return this._warmed;
+}
+
+
+
+
+isH2(){
+return this._h2;
+}
+
+
+
+
+get congestionWindow(){
+return this._congestionWindow;
+}
+
+
+
+
+
+
+setH2OverflowBytesDownloaded(bytes){
+if(!this._h2)return;
+this._h2OverflowBytesDownloaded=bytes;
+}
+
+
+
+
+clone(){
+return Object.assign(new TcpConnection(this._rtt,this._throughput),this);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+simulateDownloadUntil(bytesToDownload,options){
+const{timeAlreadyElapsed=0,maximumTimeToElapse=Infinity}=options||{};
+
+if(this._warmed&&this._h2){
+bytesToDownload-=this._h2OverflowBytesDownloaded;
+}
+const twoWayLatency=this._rtt;
+const oneWayLatency=twoWayLatency/2;
+const maximumCongestionWindow=this._computeMaximumCongestionWindowInSegments();
+
+let handshakeAndRequest=oneWayLatency;
+if(!this._warmed){
+handshakeAndRequest=
+
+oneWayLatency+
+
+oneWayLatency+
+
+oneWayLatency+(
+
+this._ssl?twoWayLatency:0);
+}
+
+let roundTrips=Math.ceil(handshakeAndRequest/twoWayLatency);
+let timeToFirstByte=handshakeAndRequest+this._serverLatency+oneWayLatency;
+if(this._warmed&&this._h2)timeToFirstByte=0;
+
+const timeElapsedForTTFB=Math.max(timeToFirstByte-timeAlreadyElapsed,0);
+const maximumDownloadTimeToElapse=maximumTimeToElapse-timeElapsedForTTFB;
+
+let congestionWindow=Math.min(this._congestionWindow,maximumCongestionWindow);
+let totalBytesDownloaded=0;
+if(timeElapsedForTTFB>0){
+totalBytesDownloaded=congestionWindow*TCP_SEGMENT_SIZE;
+}else{
+roundTrips=0;
+}
+
+let downloadTimeElapsed=0;
+let bytesRemaining=bytesToDownload-totalBytesDownloaded;
+while(bytesRemaining>0&&downloadTimeElapsed<=maximumDownloadTimeToElapse){
+roundTrips++;
+downloadTimeElapsed+=twoWayLatency;
+congestionWindow=Math.max(Math.min(maximumCongestionWindow,congestionWindow*2),1);
+
+const bytesDownloadedInWindow=congestionWindow*TCP_SEGMENT_SIZE;
+totalBytesDownloaded+=bytesDownloadedInWindow;
+bytesRemaining-=bytesDownloadedInWindow;
+}
+
+const timeElapsed=timeElapsedForTTFB+downloadTimeElapsed;
+const extraBytesDownloaded=this._h2?Math.max(totalBytesDownloaded-bytesToDownload,0):0;
+const bytesDownloaded=Math.max(Math.min(totalBytesDownloaded,bytesToDownload),0);
+
+return{
+roundTrips,
+timeElapsed,
+bytesDownloaded,
+extraBytesDownloaded,
+congestionWindow};
+
+}}
+
+
+module.exports=TcpConnection;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+},{}],30:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function getElementsInDocument(selector){
+
+const results=[];
+
+
+const _findAllElements=nodes=>{
+for(let i=0,el;el=nodes[i];++i){
+if(!selector||el.matches(selector)){
+results.push(el);
+}
+
+if(el.shadowRoot){
+_findAllElements(el.shadowRoot.querySelectorAll('*'));
+}
+}
+};
+_findAllElements(document.querySelectorAll('*'));
+
+return results;
+}
+
+module.exports={
+getElementsInDocumentFnString:getElementsInDocument.toString()};
+
+
+},{}],31:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Driver=require('../gather/driver.js');
+
+class Element{
+
+
+
+
+constructor(element,driver){
+if(!element||!driver){
+throw Error('Driver and element required to create Element');
+}
+this.driver=driver;
+this.element=element;
+}
+
+
+
+
+
+getAttribute(name){
+return this.driver.
+sendCommand('DOM.getAttributes',{
+nodeId:this.element.nodeId}).
+
+
+
+
+then(resp=>{
+const attrIndex=resp.attributes.indexOf(name);
+if(attrIndex===-1){
+return null;
+}
+
+return resp.attributes[attrIndex+1];
+});
+}
+
+
+
+
+getNodeId(){
+return this.element.nodeId;
+}
+
+
+
+
+
+getProperty(propName){
+return this.driver.
+sendCommand('DOM.resolveNode',{
+nodeId:this.element.nodeId}).
+
+then(resp=>{
+if(!resp.object.objectId){
+return null;
+}
+return this.driver.getObjectProperty(resp.object.objectId,propName);
+});
+}}
+
+
+module.exports=Element;
+
+},{"../gather/driver.js":17}],32:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const Driver=require('../gather/driver');
+const mobile3G=require('../config/constants').throttling.mobile3G;
+
+
+
+
+
+const NEXUS5X_EMULATION_METRICS={
+mobile:true,
+screenWidth:412,
+screenHeight:732,
+width:412,
+height:732,
+positionX:0,
+positionY:0,
+scale:1,
+deviceScaleFactor:2.625,
+screenOrientation:{
+angle:0,
+type:'portraitPrimary'}};
+
+
+
+const NEXUS5X_USERAGENT={
+userAgent:'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MRA58N) AppleWebKit/537.36'+
+'(KHTML, like Gecko) Chrome/66.0.3359.30 Mobile Safari/537.36'};
+
+
+const OFFLINE_METRICS={
+offline:true,
+
+latency:0,
+downloadThroughput:0,
+uploadThroughput:0};
+
+
+const NO_THROTTLING_METRICS={
+latency:0,
+downloadThroughput:0,
+uploadThroughput:0,
+offline:false};
+
+
+const NO_CPU_THROTTLE_METRICS={
+rate:1};
+
+const CPU_THROTTLE_METRICS={
+rate:4};
+
+
+
+
+
+function enableNexus5X(driver){
+return Promise.all([
+driver.sendCommand('Emulation.setDeviceMetricsOverride',NEXUS5X_EMULATION_METRICS),
+
+driver.sendCommand('Network.enable'),
+driver.sendCommand('Network.setUserAgentOverride',NEXUS5X_USERAGENT),
+driver.sendCommand('Emulation.setEmitTouchEventsForMouse',{
+enabled:true,
+configuration:'mobile'})]);
+
+
+}
+
+
+
+
+
+
+function enableNetworkThrottling(driver,throttlingSettings=mobile3G){
+
+const conditions={
+offline:false,
+latency:throttlingSettings.requestLatencyMs||0,
+downloadThroughput:throttlingSettings.downloadThroughputKbps||0,
+uploadThroughput:throttlingSettings.uploadThroughputKbps||0};
+
+
+
+conditions.downloadThroughput=Math.floor(conditions.downloadThroughput*1024/8);
+conditions.uploadThroughput=Math.floor(conditions.uploadThroughput*1024/8);
+return driver.sendCommand('Network.emulateNetworkConditions',conditions);
+}
+
+
+
+
+
+function clearAllNetworkEmulation(driver){
+return driver.sendCommand('Network.emulateNetworkConditions',NO_THROTTLING_METRICS);
+}
+
+
+
+
+
+function goOffline(driver){
+return driver.sendCommand('Network.emulateNetworkConditions',OFFLINE_METRICS);
+}
+
+
+
+
+
+
+function enableCPUThrottling(driver,throttlingSettings){
+
+const rate=throttlingSettings&&throttlingSettings.cpuSlowdownMultiplier!==undefined?
+throttlingSettings.cpuSlowdownMultiplier:
+CPU_THROTTLE_METRICS.rate;
+return driver.sendCommand('Emulation.setCPUThrottlingRate',{rate});
+}
+
+
+
+
+
+function disableCPUThrottling(driver){
+return driver.sendCommand('Emulation.setCPUThrottlingRate',NO_CPU_THROTTLE_METRICS);
+}
+
+module.exports={
+enableNexus5X,
+enableNetworkThrottling,
+clearAllNetworkEmulation,
+enableCPUThrottling,
+disableCPUThrottling,
+goOffline};
+
+
+},{"../config/constants":8,"../gather/driver":17}],33:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const strings=require('./strings');
+
+
+
+
+
+
+
+
+class LighthouseError extends Error{
+
+
+
+
+constructor(errorDefinition,properties){
+super(errorDefinition.code);
+this.name='LHError';
+this.code=errorDefinition.code;
+this.friendlyMessage=errorDefinition.message;
+if(properties)Object.assign(this,properties);
+
+Error.captureStackTrace(this,LighthouseError);
+}
+
+
+
+
+static isPageLoadError(err){
+return err.code===ERRORS.NO_DOCUMENT_REQUEST.code||
+err.code===ERRORS.FAILED_DOCUMENT_REQUEST.code;
+}
+
+
+
+
+
+
+static fromProtocolMessage(method,protocolError){
+
+const protocolErrors=Object.keys(ERRORS).filter(k=>ERRORS[k].pattern).map(k=>ERRORS[k]);
+
+const matchedErrorDefinition=protocolErrors.find(e=>e.pattern.test(protocolError.message));
+if(matchedErrorDefinition){
+return new LighthouseError(matchedErrorDefinition,{
+protocolMethod:method,
+protocolError:protocolError.message});
+
+}
+
+
+let errMsg=`(${method}): ${protocolError.message}`;
+if(protocolError.data)errMsg+=` (${protocolError.data})`;
+const error=new Error(`Protocol error ${errMsg}`);
+return Object.assign(error,{protocolMethod:method,protocolError:protocolError.message});
+}}
+
+
+const ERRORS={
+
+NO_SPEEDLINE_FRAMES:{message:strings.didntCollectScreenshots},
+SPEEDINDEX_OF_ZERO:{message:strings.didntCollectScreenshots},
+NO_SCREENSHOTS:{message:strings.didntCollectScreenshots},
+INVALID_SPEEDLINE:{message:strings.didntCollectScreenshots},
+
+
+NO_TRACING_STARTED:{message:strings.badTraceRecording},
+NO_NAVSTART:{message:strings.badTraceRecording},
+NO_FCP:{message:strings.badTraceRecording},
+NO_FMP:{message:strings.badTraceRecording},
+NO_DCL:{message:strings.badTraceRecording},
+
+
+FMP_TOO_LATE_FOR_FCPUI:{message:strings.pageLoadTookTooLong},
+NO_FCPUI_IDLE_PERIOD:{message:strings.pageLoadTookTooLong},
+NO_TTI_CPU_IDLE_PERIOD:{message:strings.pageLoadTookTooLong},
+NO_TTI_NETWORK_IDLE_PERIOD:{message:strings.pageLoadTookTooLong},
+
+
+NO_DOCUMENT_REQUEST:{message:strings.pageLoadFailed},
+FAILED_DOCUMENT_REQUEST:{message:strings.pageLoadFailed},
+
+
+TRACING_ALREADY_STARTED:{message:strings.internalChromeError,pattern:/Tracing.*started/},
+PARSING_PROBLEM:{message:strings.internalChromeError,pattern:/Parsing problem/},
+READ_FAILED:{message:strings.internalChromeError,pattern:/Read failed/},
+
+
+REQUEST_CONTENT_TIMEOUT:{message:strings.requestContentTimeout}};
+
+
+Object.keys(ERRORS).forEach(code=>ERRORS[code].code=code);
+
+
+LighthouseError.errors=ERRORS;
+module.exports=LighthouseError;
+
+
+},{"./strings":41}],34:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function addFormattedCodeSnippet(listener){
+const handler=listener.handler?listener.handler.description:'...';
+const objectName=listener.objectName.toLowerCase().replace('#document','document');
+return Object.assign({
+pre:`${objectName}.addEventListener('${listener.type}', ${handler})`},
+listener);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function groupCodeSnippetsByLocation(listeners){
+
+const locToListenerMap=new Map();
+
+
+listeners.forEach(loc=>{
+const accPre=loc.pre.trim()+'\n\n';
+const simplifiedLoc={line:loc.line,col:loc.col,url:loc.url,type:loc.type,pre:''};
+
+const key=JSON.stringify(simplifiedLoc);
+const accListener=locToListenerMap.get(key)||simplifiedLoc;
+accListener.pre+=accPre;
+locToListenerMap.set(key,accListener);
+});
+
+return[...locToListenerMap.values()];
+}
+
+module.exports={
+addFormattedCodeSnippet,
+groupCodeSnippetsByLocation};
+
+
+},{}],35:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+
+
+
+
+function doExist(manifest){
+if(!manifest||!manifest.icons){
+return false;
+}
+if(manifest.icons.value.length===0){
+return false;
+}
+return true;
+}
+
+
+
+
+
+
+function sizeAtLeast(sizeRequirement,manifest){
+
+
+const iconValues=manifest.icons.value;
+
+const flattenedSizes=[];
+iconValues.forEach(icon=>{
+if(icon.value.sizes.value){
+flattenedSizes.push(...icon.value.sizes.value);
+}
+});
+
+return flattenedSizes.
+
+filter(size=>/\d+x\d+/.test(size)).
+filter(size=>{
+
+const sizeStrs=size.split(/x/i);
+
+const sizeNums=[parseFloat(sizeStrs[0]),parseFloat(sizeStrs[1])];
+
+const areIconsBigEnough=sizeNums[0]>=sizeRequirement&&sizeNums[1]>=sizeRequirement;
+
+const areIconsSquare=sizeNums[0]===sizeNums[1];
+return areIconsBigEnough&&areIconsSquare;
+});
+}
+
+module.exports={
+doExist,
+sizeAtLeast};
+
+
+},{}],36:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const URL=require('./url-shim');
+const validateColor=require('./web-inspector').Color.parse;
+
+const ALLOWED_DISPLAY_VALUES=[
+'fullscreen',
+'standalone',
+'minimal-ui',
+'browser'];
+
+
+
+
+
+const DEFAULT_DISPLAY_MODE='browser';
+
+const ALLOWED_ORIENTATION_VALUES=[
+'any',
+'natural',
+'landscape',
+'portrait',
+'portrait-primary',
+'portrait-secondary',
+'landscape-primary',
+'landscape-secondary'];
+
+
+
+
+
+
+function parseString(raw,trim){
+let value;
+let debugString;
+
+if(typeof raw==='string'){
+value=trim?raw.trim():raw;
+}else{
+if(raw!==undefined){
+debugString='ERROR: expected a string.';
+}
+value=undefined;
+}
+
+return{
+raw,
+value,
+debugString};
+
+}
+
+
+
+
+function parseColor(raw){
+const color=parseString(raw);
+
+
+if(color.value===undefined){
+return color;
+}
+
+
+const validatedColor=validateColor(color.raw);
+if(!validatedColor){
+color.value=undefined;
+color.debugString='ERROR: color parsing failed.';
+}
+
+return color;
+}
+
+
+
+
+function parseName(jsonInput){
+return parseString(jsonInput.name,true);
+}
+
+
+
+
+function parseShortName(jsonInput){
+return parseString(jsonInput.short_name,true);
+}
+
+
+
+
+
+
+
+function checkSameOrigin(url1,url2){
+const parsed1=new URL(url1);
+const parsed2=new URL(url2);
+
+return parsed1.origin===parsed2.origin;
+}
+
+
+
+
+
+
+
+function parseStartUrl(jsonInput,manifestUrl,documentUrl){
+const raw=jsonInput.start_url;
+
+
+if(raw===''){
+return{
+raw,
+value:documentUrl,
+debugString:'ERROR: start_url string empty'};
+
+}
+const parsedAsString=parseString(raw);
+if(!parsedAsString.value){
+parsedAsString.value=documentUrl;
+return parsedAsString;
+}
+
+
+let startUrl;
+try{
+startUrl=new URL(raw,manifestUrl).href;
+}catch(e){
+
+return{
+raw,
+value:documentUrl,
+debugString:'ERROR: invalid start_url relative to ${manifestUrl}'};
+
+}
+
+
+if(!checkSameOrigin(startUrl,documentUrl)){
+return{
+raw,
+value:documentUrl,
+debugString:'ERROR: start_url must be same-origin as document'};
+
+}
+
+return{
+raw,
+value:startUrl};
+
+}
+
+
+
+
+function parseDisplay(jsonInput){
+const parsedString=parseString(jsonInput.display,true);
+const stringValue=parsedString.value;
+
+if(!stringValue){
+return{
+raw:jsonInput,
+value:DEFAULT_DISPLAY_MODE,
+debugString:parsedString.debugString};
+
+}
+
+const displayValue=stringValue.toLowerCase();
+if(!ALLOWED_DISPLAY_VALUES.includes(displayValue)){
+return{
+raw:jsonInput,
+value:DEFAULT_DISPLAY_MODE,
+debugString:'ERROR: \'display\' has invalid value '+displayValue+
+`. will fall back to ${DEFAULT_DISPLAY_MODE}.`};
+
+}
+
+return{
+raw:jsonInput,
+value:displayValue,
+debugString:undefined};
+
+}
+
+
+
+
+function parseOrientation(jsonInput){
+const orientation=parseString(jsonInput.orientation,true);
+
+if(orientation.value&&
+!ALLOWED_ORIENTATION_VALUES.includes(orientation.value.toLowerCase())){
+orientation.value=undefined;
+orientation.debugString='ERROR: \'orientation\' has an invalid value, will be ignored.';
+}
+
+return orientation;
+}
+
+
+
+
+
+function parseIcon(raw,manifestUrl){
+
+const src=parseString(raw.src,true);
+
+if(src.value===''){
+src.value=undefined;
+}
+if(src.value){
+
+src.value=new URL(src.value,manifestUrl).href;
+}
+
+const type=parseString(raw.type,true);
+
+const density={
+raw:raw.density,
+value:1,
+
+debugString:undefined};
+
+if(density.raw!==undefined){
+density.value=parseFloat(density.raw);
+if(isNaN(density.value)||!isFinite(density.value)||density.value<=0){
+density.value=1;
+density.debugString='ERROR: icon density cannot be NaN, +∞, or less than or equal to +0.';
+}
+}
+
+let sizes;
+const parsedSizes=parseString(raw.sizes);
+if(parsedSizes.value!==undefined){
+
+const set=new Set();
+parsedSizes.value.trim().split(/\s+/).forEach(size=>set.add(size.toLowerCase()));
+sizes={
+raw:raw.sizes,
+value:set.size>0?Array.from(set):undefined,
+debugString:undefined};
+
+}else{
+sizes={...parsedSizes,value:undefined};
+}
+
+return{
+raw,
+value:{
+src,
+type,
+density,
+sizes},
+
+debugString:undefined};
+
+}
+
+
+
+
+
+function parseIcons(jsonInput,manifestUrl){
+const raw=jsonInput.icons;
+
+if(raw===undefined){
+return{
+raw,
+
+value:[],
+debugString:undefined};
+
+}
+
+if(!Array.isArray(raw)){
+return{
+raw,
+
+value:[],
+debugString:'ERROR: \'icons\' expected to be an array but is not.'};
+
+}
+
+
+
+const value=raw.
+
+filter(icon=>icon.src!==undefined).
+
+map(icon=>parseIcon(icon,manifestUrl)).
+
+filter(parsedIcon=>parsedIcon.value.src.value!==undefined);
+
+return{
+raw,
+value,
+debugString:undefined};
+
+}
+
+
+
+
+function parseApplication(raw){
+const platform=parseString(raw.platform,true);
+const id=parseString(raw.id,true);
+
+
+const appUrl=parseString(raw.url,true);
+if(appUrl.value){
+try{
+
+appUrl.value=new URL(appUrl.value).href;
+}catch(e){
+appUrl.value=undefined;
+appUrl.debugString='ERROR: invalid application URL ${raw.url}';
+}
+}
+
+return{
+raw,
+value:{
+platform,
+id,
+url:appUrl},
+
+debugString:undefined};
+
+}
+
+
+
+
+function parseRelatedApplications(jsonInput){
+const raw=jsonInput.related_applications;
+
+if(raw===undefined){
+return{
+raw,
+value:undefined,
+debugString:undefined};
+
+}
+
+if(!Array.isArray(raw)){
+return{
+raw,
+value:undefined,
+debugString:'ERROR: \'related_applications\' expected to be an array but is not.'};
+
+}
+
+
+
+const value=raw.
+filter(application=>!!application.platform).
+map(parseApplication).
+filter(parsedApp=>!!parsedApp.value.id.value||!!parsedApp.value.url.value);
+
+return{
+raw,
+value,
+debugString:undefined};
+
+}
+
+
+
+
+function parsePreferRelatedApplications(jsonInput){
+const raw=jsonInput.prefer_related_applications;
+let value;
+let debugString;
+
+if(typeof raw==='boolean'){
+value=raw;
+}else{
+if(raw!==undefined){
+debugString='ERROR: \'prefer_related_applications\' expected to be a boolean.';
+}
+value=undefined;
+}
+
+return{
+raw,
+value,
+debugString};
+
+}
+
+
+
+
+function parseThemeColor(jsonInput){
+return parseColor(jsonInput.theme_color);
+}
+
+
+
+
+function parseBackgroundColor(jsonInput){
+return parseColor(jsonInput.background_color);
+}
+
+
+
+
+
+
+
+function parse(string,manifestUrl,documentUrl){
+if(manifestUrl===undefined||documentUrl===undefined){
+throw new Error('Manifest and document URLs required for manifest parsing.');
+}
+
+let jsonInput;
+
+try{
+jsonInput=JSON.parse(string);
+}catch(e){
+return{
+raw:string,
+value:undefined,
+debugString:'ERROR: file isn\'t valid JSON: '+e};
+
+}
+
+
+const manifest={
+name:parseName(jsonInput),
+short_name:parseShortName(jsonInput),
+start_url:parseStartUrl(jsonInput,manifestUrl,documentUrl),
+display:parseDisplay(jsonInput),
+orientation:parseOrientation(jsonInput),
+icons:parseIcons(jsonInput,manifestUrl),
+related_applications:parseRelatedApplications(jsonInput),
+prefer_related_applications:parsePreferRelatedApplications(jsonInput),
+theme_color:parseThemeColor(jsonInput),
+background_color:parseBackgroundColor(jsonInput)};
+
+
+
+return{
+raw:string,
+value:manifest,
+debugString:undefined};
+
+}
+
+module.exports=parse;
+
+},{"./url-shim":"url","./web-inspector":47}],37:[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+const NetworkManager=require('./web-inspector').NetworkManager;
+const EventEmitter=require('events').EventEmitter;
+const log=require('lighthouse-logger');
+
+const IGNORED_NETWORK_SCHEMES=['data','ws'];
+
+
+
+class NetworkRecorder extends EventEmitter{
+
+
+
+
+constructor(recordArray){
+super();
+
+this._records=recordArray;
+this.networkManager=NetworkManager.createWithFakeTarget();
+
+this.networkManager.addEventListener(
+this.EventTypes.RequestStarted,
+this.onRequestStarted.bind(this));
+
+this.networkManager.addEventListener(
+this.EventTypes.RequestFinished,
+this.onRequestFinished.bind(this));
+
+}
+
+
+
+
+
+on(event,listener){
+return super.on(event,listener);
+}
+
+
+
+
+
+once(event,listener){
+return super.once(event,listener);
+}
+
+get EventTypes(){
+return NetworkManager.Events;
+}
+
+isIdle(){
+return!!this._getActiveIdlePeriod(0);
+}
+
+is2Idle(){
+return!!this._getActiveIdlePeriod(2);
+}
+
+
+
+
+_getActiveIdlePeriod(allowedRequests){
+const quietPeriods=NetworkRecorder.findNetworkQuietPeriods(this._records,allowedRequests);
+return quietPeriods.find(period=>!Number.isFinite(period.end));
+}
+
+_emitNetworkStatus(){
+const zeroQuiet=this._getActiveIdlePeriod(0);
+const twoQuiet=this._getActiveIdlePeriod(2);
+
+if(twoQuiet&&zeroQuiet){
+log.verbose('NetworkRecorder','network fully-quiet');
+this.emit('network-2-idle');
+this.emit('networkidle');
+}else if(twoQuiet&&!zeroQuiet){
+log.verbose('NetworkRecorder','network semi-quiet');
+this.emit('network-2-idle');
+this.emit('networkbusy');
+}else{
+log.verbose('NetworkRecorder','network busy');
+this.emit('network-2-busy');
+this.emit('networkbusy');
+}
+}
+
+
+
+
+
+
+
+static _isQUICAndFinished(record){
+const isQUIC=record._responseHeaders&&record._responseHeaders.
+some(header=>header.name.toLowerCase()==='alt-svc'&&/quic/.test(header.value));
+const receivedHeaders=record._timing&&record._timing.receiveHeadersEnd>0;
+return!!(isQUIC&&receivedHeaders&&record.endTime);
+}
+
+
+
+
+
+
+
+
+
+static findNetworkQuietPeriods(networkRecords,allowedConcurrentRequests,endTime=Infinity){
+
+
+let timeBoundaries=[];
+networkRecords.forEach(record=>{
+const scheme=record.parsedURL&&record.parsedURL.scheme;
+if(IGNORED_NETWORK_SCHEMES.includes(scheme)){
+return;
+}
+
+
+timeBoundaries.push({time:record.startTime*1000,isStart:true});
+if(record.finished||NetworkRecorder._isQUICAndFinished(record)){
+timeBoundaries.push({time:record.endTime*1000,isStart:false});
+}
+});
+
+timeBoundaries=timeBoundaries.
+filter(boundary=>boundary.time<=endTime).
+sort((a,b)=>a.time-b.time);
+
+let numInflightRequests=0;
+let quietPeriodStart=0;
+
+const quietPeriods=[];
+timeBoundaries.forEach(boundary=>{
+if(boundary.isStart){
+
+if(numInflightRequests===allowedConcurrentRequests){
+quietPeriods.push({start:quietPeriodStart,end:boundary.time});
+}
+numInflightRequests++;
+}else{
+numInflightRequests--;
+
+if(numInflightRequests===allowedConcurrentRequests){
+quietPeriodStart=boundary.time;
+}
+}
+});
+
+
+if(numInflightRequests<=allowedConcurrentRequests){
+quietPeriods.push({start:quietPeriodStart,end:endTime});
+}
+
+return quietPeriods.filter(period=>period.start!==period.end);
+}
+
+
+
+
+
+
+
+onRequestStarted(request){
+this._records.push(request.data);
+this._emitNetworkStatus();
+}
+
+
+
+
+
+
+
+onRequestFinished(request){
+this.emit('requestloaded',request.data);
+this._emitNetworkStatus();
+}
+
+
+
+
+
+
+
+
+onRequestWillBeSent(data){
+
+this.networkManager._dispatcher.requestWillBeSent(data.requestId,
+data.frameId,data.loaderId,data.documentURL,data.request,
+data.timestamp,data.wallTime,data.initiator,data.redirectResponse,
+data.type);
+}
+
+
+
+
+onRequestServedFromCache(data){
+this.networkManager._dispatcher.requestServedFromCache(data.requestId);
+}
+
+
+
+
+onResponseReceived(data){
+
+this.networkManager._dispatcher.responseReceived(data.requestId,
+data.frameId,data.loaderId,data.timestamp,data.type,data.response);
+}
+
+
+
+
+onDataReceived(data){
+
+this.networkManager._dispatcher.dataReceived(data.requestId,data.timestamp,
+data.dataLength,data.encodedDataLength);
+}
+
+
+
+
+onLoadingFinished(data){
+
+this.networkManager._dispatcher.loadingFinished(data.requestId,
+data.timestamp,data.encodedDataLength);
+}
+
+
+
+
+onLoadingFailed(data){
+
+
+this.networkManager._dispatcher.loadingFailed(data.requestId,
+data.timestamp,data.type,data.errorText,data.canceled,
+data.blockedReason);
+}
+
+
+
+
+onResourceChangedPriority(data){
+this.networkManager._dispatcher.resourceChangedPriority(data.requestId,
+data.newPriority,data.timestamp);
+}
+
+
+
+
+
+dispatch(event){
+if(!event.method.startsWith('Network.')){
+return;
+}
+
+switch(event.method){
+case'Network.requestWillBeSent':return this.onRequestWillBeSent(event.params);
+case'Network.requestServedFromCache':return this.onRequestServedFromCache(event.params);
+case'Network.responseReceived':return this.onResponseReceived(event.params);
+case'Network.dataReceived':return this.onDataReceived(event.params);
+case'Network.loadingFinished':return this.onLoadingFinished(event.params);
+case'Network.loadingFailed':return this.onLoadingFailed(event.params);
+case'Network.resourceChangedPriority':return this.onResourceChangedPriority(event.params);
+default:return;}
+
+}
+
+
+
+
+
+
+static recordsFromLogs(devtoolsLog){
+
+const records=[];
+const nr=new NetworkRecorder(records);
+devtoolsLog.forEach(message=>{
+nr.dispatch(message);
+});
+return records;
+}}
+
+
+module.exports=NetworkRecorder;
+
+},{"./web-inspector":47,"events":62,"lighthouse-logger":143}],38:[function(require,module,exports){
+
+
+
+
+
+
+'use strict';
+
+
+
+
+
 
 
 
@@ -15609,3114 +22221,14 @@
 });
 }
 
-module.exports=Driver;
-
-},{"../lib/element":26,"../lib/emulation":27,"../lib/network-recorder":32,"../lib/traces/trace-parser":39,"../lib/url-shim":41,"./devtools-log":13,"events":56,"lighthouse-logger":137}],15:[function(require,module,exports){
-
-
-
-
-
-
-'use strict';
-
-const log=require('lighthouse-logger');
-const Audit=require('../audits/audit');
-const LHError=require('../lib/errors');
-const URL=require('../lib/url-shim');
-const NetworkRecorder=require('../lib/network-recorder.js');
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-class GatherRunner{
-
-
-
-
-
-
-
-
-
-
-static loadBlank(driver,url='about:blank',duration=300){
-return driver.gotoURL(url).then(_=>new Promise(resolve=>setTimeout(resolve,duration)));
-}
-
-
-
-
-
-
-
-
-
-
-static loadPage(driver,options){
-return driver.gotoURL(options.url,{
-waitForLoad:true,
-disableJavaScript:!!options.disableJavaScript,
-flags:options.flags,
-config:options.config}).
-then(finalUrl=>{
-options.url=finalUrl;
-});
-}
-
-
-
-
-
-
-
-static setupDriver(driver,gathererResults,options){
-log.log('status','Initializing…');
-const resetStorage=!options.flags.disableStorageReset;
-
-return driver.assertNoSameOriginServiceWorkerClients(options.url).
-then(_=>driver.getUserAgent()).
-then(userAgent=>{
-gathererResults.UserAgent=[userAgent];
-GatherRunner.warnOnHeadless(userAgent,gathererResults);
-}).
-then(_=>driver.beginEmulation(options.flags)).
-then(_=>driver.enableRuntimeEvents()).
-then(_=>driver.cacheNatives()).
-then(_=>driver.registerPerformanceObserver()).
-then(_=>driver.dismissJavaScriptDialogs()).
-then(_=>resetStorage&&driver.clearDataForOrigin(options.url));
-}
-
-static disposeDriver(driver){
-log.log('status','Disconnecting from browser...');
-return driver.disconnect().catch(err=>{
-
-
-if(!/close\/.*status: 500$/.test(err.message)){
-log.error('GatherRunner disconnect',err.message);
-}
-});
-}
-
-
-
-
-
-
-
-static recoverOrThrow(promise){
-return promise.catch(err=>{
-if(err.fatal){
-throw err;
-}
-});
-}
-
-
-
-
-
-
-
-static getPageLoadError(url,networkRecords){
-const mainRecord=networkRecords.find(record=>{
-
-return URL.equalWithExcludedFragments(record.url,url);
-});
-
-let errorCode;
-let errorReason;
-if(!mainRecord){
-errorCode=LHError.errors.NO_DOCUMENT_REQUEST;
-}else if(mainRecord.failed){
-errorCode=LHError.errors.FAILED_DOCUMENT_REQUEST;
-errorReason=mainRecord.localizedFailDescription;
-}
-
-if(errorCode){
-const error=new LHError(errorCode,{reason:errorReason});
-log.error('GatherRunner',error.message,url);
-return error;
-}
-}
-
-
-
-
-
-
-static warnOnHeadless(userAgent,gathererResults){
-const chromeVersion=userAgent.split(/HeadlessChrome\/(.*) /)[1];
-
-
-const minVersion='63.0.3239.0';
-if(chromeVersion&&chromeVersion<minVersion){
-gathererResults.LighthouseRunWarnings.push('Your site\'s mobile performance may be '+
-'worse than the numbers presented in this report. Lighthouse could not test on a '+
-'mobile connection because Headless Chrome does not support network throttling '+
-'prior to version '+minVersion+'. The version used was '+chromeVersion);
-}
-}
-
-
-
-
-
-
-
-
-static beforePass(options,gathererResults){
-const blockedUrls=(options.config.blockedUrlPatterns||[]).
-concat(options.flags.blockedUrlPatterns||[]);
-const blankPage=options.config.blankPage;
-const blankDuration=options.config.blankDuration;
-const pass=GatherRunner.loadBlank(options.driver,blankPage,blankDuration).
-
-
-
-then(()=>options.driver.blockUrlPatterns(blockedUrls)).
-then(()=>options.driver.setExtraHTTPHeaders(options.flags.extraHeaders));
-
-return options.config.gatherers.reduce((chain,gatherer)=>{
-return chain.then(_=>{
-const artifactPromise=Promise.resolve().then(_=>gatherer.beforePass(options));
-gathererResults[gatherer.name]=[artifactPromise];
-return GatherRunner.recoverOrThrow(artifactPromise);
-});
-},pass);
-}
-
-
-
-
-
-
-
-
-static pass(options,gathererResults){
-const driver=options.driver;
-const config=options.config;
-const gatherers=config.gatherers;
-
-const recordTrace=config.recordTrace;
-const isPerfRun=!options.flags.disableStorageReset&&recordTrace&&config.useThrottling;
-
-const gatherernames=gatherers.map(g=>g.name).join(', ');
-const status='Loading page & waiting for onload';
-log.log('status',status,gatherernames);
-
-const pass=Promise.resolve().
-
-then(_=>isPerfRun&&driver.cleanBrowserCaches()).
-
-then(_=>driver.beginDevtoolsLog()).
-
-then(_=>recordTrace&&driver.beginTrace(options.flags)).
-
-then(_=>GatherRunner.loadPage(driver,options)).
-then(_=>log.log('statusEnd',status));
-
-return gatherers.reduce((chain,gatherer)=>{
-return chain.then(_=>{
-const artifactPromise=Promise.resolve().then(_=>gatherer.pass(options));
-gathererResults[gatherer.name].push(artifactPromise);
-return GatherRunner.recoverOrThrow(artifactPromise);
-});
-},pass);
-}
-
-
-
-
-
-
-
-
-
-static afterPass(options,gathererResults){
-const driver=options.driver;
-const config=options.config;
-const gatherers=config.gatherers;
-const passData={};
-
-let pass=Promise.resolve();
-let pageLoadError;
-
-if(config.recordTrace){
-pass=pass.then(_=>{
-log.log('status','Retrieving trace');
-return driver.endTrace();
-}).then(traceContents=>{
-
-
-
-passData.trace=Array.isArray(traceContents)?
-{traceEvents:traceContents}:traceContents;
-log.verbose('statusEnd','Retrieving trace');
-});
-}
-
-pass=pass.then(_=>{
-const status='Retrieving devtoolsLog and network records';
-log.log('status',status);
-const devtoolsLog=driver.endDevtoolsLog();
-const networkRecords=NetworkRecorder.recordsFromLogs(devtoolsLog);
-log.verbose('statusEnd',status);
-
-pageLoadError=GatherRunner.getPageLoadError(options.url,networkRecords);
-
-if(!driver.online)pageLoadError=null;
-
-if(pageLoadError){
-gathererResults.LighthouseRunWarnings.push('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.');
-}
-
-
-passData.devtoolsLog=devtoolsLog;
-passData.networkRecords=networkRecords;
-});
-
-
-pass=pass.then(_=>driver.setThrottling(options.flags,{useThrottling:false}));
-
-pass=gatherers.reduce((chain,gatherer)=>{
-const status=`Retrieving: ${gatherer.name}`;
-return chain.then(_=>{
-log.log('status',status);
-const artifactPromise=pageLoadError?
-Promise.reject(pageLoadError):
-Promise.resolve().then(_=>gatherer.afterPass(options,passData));
-gathererResults[gatherer.name].push(artifactPromise);
-return GatherRunner.recoverOrThrow(artifactPromise);
-}).then(_=>{
-log.verbose('statusEnd',status);
-});
-},pass);
-
-
-return pass.then(_=>passData);
-}
-
-
-
-
-
-
-
-
-
-static collectArtifacts(gathererResults){
-const artifacts={};
-
-
-const uniqueWarnings=Array.from(new Set(gathererResults.LighthouseRunWarnings));
-gathererResults.LighthouseRunWarnings=[uniqueWarnings];
-
-const pageLoadFailures=[];
-return Object.keys(gathererResults).reduce((chain,gathererName)=>{
-return chain.then(_=>{
-const phaseResultsPromises=gathererResults[gathererName];
-return Promise.all(phaseResultsPromises).then(phaseResults=>{
-
-const definedResults=phaseResults.filter(element=>element!==undefined);
-const artifact=definedResults[definedResults.length-1];
-if(artifact===undefined){
-throw new Error(`${gathererName} failed to provide an artifact.`);
-}
-artifacts[gathererName]=artifact;
-},err=>{
-
-
-artifacts[gathererName]=err;
-
-if(LHError.isPageLoadError(err))pageLoadFailures.push(err);
-});
-});
-},Promise.resolve()).then(_=>{
-
-if(pageLoadFailures.length>Object.keys(artifacts).length*.5){
-throw pageLoadFailures[0];
-}
-
-return artifacts;
-});
-}
-
-static run(passes,options){
-const driver=options.driver;
-const tracingData={
-traces:{},
-devtoolsLogs:{}};
-
-
-if(typeof options.url!=='string'||options.url.length===0){
-return Promise.reject(new Error('You must provide a url to the gather-runner'));
-}
-
-if(typeof options.flags==='undefined'){
-options.flags={};
-}
-
-if(typeof options.config==='undefined'){
-return Promise.reject(new Error('You must provide a config'));
-}
-
-if(typeof options.flags.disableCpuThrottling==='undefined'){
-options.flags.disableCpuThrottling=false;
-}
-
-passes=this.instantiateGatherers(passes,options.config.configDir);
-
-const gathererResults={
-LighthouseRunWarnings:[]};
-
-
-return driver.connect().
-then(_=>GatherRunner.loadBlank(driver)).
-then(_=>GatherRunner.setupDriver(driver,gathererResults,options)).
-
-
-then(_=>{
-
-let urlAfterRedirects;
-return passes.reduce((chain,config,passIndex)=>{
-const runOptions=Object.assign({},options,{config});
-return chain.
-then(_=>driver.setThrottling(options.flags,config)).
-then(_=>GatherRunner.beforePass(runOptions,gathererResults)).
-then(_=>GatherRunner.pass(runOptions,gathererResults)).
-then(_=>GatherRunner.afterPass(runOptions,gathererResults)).
-then(passData=>{
-const passName=config.passName||Audit.DEFAULT_PASS;
-
-
-tracingData.devtoolsLogs[passName]=passData.devtoolsLog;
-
-
-if(config.recordTrace){
-tracingData.traces[passName]=passData.trace;
-}
-
-if(passIndex===0){
-urlAfterRedirects=runOptions.url;
-}
-});
-},Promise.resolve()).then(_=>{
-options.url=urlAfterRedirects;
-});
-}).
-then(_=>GatherRunner.disposeDriver(driver)).
-then(_=>GatherRunner.collectArtifacts(gathererResults)).
-then(artifacts=>{
-
-return Object.assign(artifacts,tracingData);
-}).
-
-catch(err=>{
-GatherRunner.disposeDriver(driver);
-
-throw err;
-});
-}
-
-static getGathererClass(nameOrGathererClass,configPath){
-const Runner=require('../runner');
-const coreList=Runner.getGathererList();
-
-let GathererClass;
-if(typeof nameOrGathererClass==='string'){
-const name=nameOrGathererClass;
-
-
-const coreGatherer=coreList.find(a=>a===`${name}.js`);
-let requirePath=`./gatherers/${name}`;
-if(!coreGatherer){
-
-requirePath=Runner.resolvePlugin(name,configPath,'gatherer');
-}
-
-GathererClass=require(requirePath);
-
-this.assertValidGatherer(GathererClass,name);
-}else{
-GathererClass=nameOrGathererClass;
-this.assertValidGatherer(GathererClass);
-}
-
-return GathererClass;
-}
-
-static assertValidGatherer(GathererDefinition,gathererName){
-const gathererInstance=new GathererDefinition();
-gathererName=gathererName||gathererInstance.name||'gatherer';
-
-if(typeof gathererInstance.beforePass!=='function'){
-throw new Error(`${gathererName} has no beforePass() method.`);
-}
-
-if(typeof gathererInstance.pass!=='function'){
-throw new Error(`${gathererName} has no pass() method.`);
-}
-
-if(typeof gathererInstance.afterPass!=='function'){
-throw new Error(`${gathererName} has no afterPass() method.`);
-}
-}
-
-static instantiateGatherers(passes,rootPath){
-return passes.map(pass=>{
-pass.gatherers=pass.gatherers.map(gatherer=>{
-
-if(typeof gatherer!=='string'){
-return gatherer;
-}
-
-const GathererClass=GatherRunner.getGathererClass(gatherer,rootPath);
-return new GathererClass();
-});
-
-return pass;
-});
-}}
-
-
-module.exports=GatherRunner;
-
-},{"../audits/audit":2,"../lib/errors":28,"../lib/network-recorder.js":32,"../lib/url-shim":41,"../runner":44,"lighthouse-logger":137}],16:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-
-
-
-
-
-
-
-
-
-
-
-class Gatherer{
-
-
-
-get name(){
-return this.constructor.name;
-}
-
-
-
-
-
-
-
-beforePass(options){}
-
-
-
-
-
-
-pass(options){}
-
-
-
-
-
-
-
-
-
-afterPass(options,loadData){}}
-
-
-
-
-module.exports=Gatherer;
-
-},{}],17:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const isEqual=require('lodash.isequal');
-
-
-
-
-
-
-module.exports=class ArbitraryEqualityMap{
-constructor(){
-this._equalsFn=(a,b)=>a===b;
-this._entries=[];
-}
-
-
-
-
-setEqualityFn(equalsFn){
-this._equalsFn=equalsFn;
-}
-
-has(key){
-return this._findIndexOf(key)!==-1;
-}
-
-get(key){
-const entry=this._entries[this._findIndexOf(key)];
-return entry&&entry.value;
-}
-
-set(key,value){
-let index=this._findIndexOf(key);
-if(index===-1)index=this._entries.length;
-this._entries[index]={key,value};
-}
-
-_findIndexOf(key){
-for(let i=0;i<this._entries.length;i++){
-if(this._equalsFn(key,this._entries[i].key))return i;
-}
-
-return-1;
-}
-
-
-
-
-
-
-
-
-
-static deepEquals(objA,objB){
-return isEqual(objA,objB);
-}};
-
-
-},{"lodash.isequal":138}],18:[function(require,module,exports){
-
-
-
-
-
-
-'use strict';
-
-
-const path=require('path');
-const log=require('lighthouse-logger');
-const stream=require('stream');
-const Metrics=require('./traces/pwmetrics-events');
-const TraceParser=require('./traces/trace-parser');
-const rimraf=require('rimraf');
-const mkdirp=require('mkdirp');
-
-
-
-
-
-
-function screenshotDump(screenshots){
-return`
-  <!doctype html>
-  <meta charset="utf-8">
-  <title>screenshots</title>
-  <style>
-html {
-    overflow-x: scroll;
-    overflow-y: hidden;
-    height: 100%;
-    background-image: linear-gradient(to left, #4ca1af , #c4e0e5);
-    background-attachment: fixed;
-    padding: 10px;
-}
-body {
-    white-space: nowrap;
-    background-image: linear-gradient(to left, #4ca1af , #c4e0e5);
-    width: 100%;
-    margin: 0;
-}
-img {
-    margin: 4px;
-}
-</style>
-  <body>
-    <script>
-      var shots = ${JSON.stringify(screenshots)};
-
-  shots.forEach(s => {
-    var i = document.createElement('img');
-    i.src = s.datauri;
-    i.title = s.timestamp;
-    document.body.appendChild(i);
-  });
-  </script>
-  `;
-}
-
-const artifactsFilename='artifacts.json';
-const traceSuffix='.trace.json';
-const devtoolsLogSuffix='.devtoolslog.json';
-
-
-
-
-
-
-
-
-
-function loadArtifacts(basePath){
-log.log('Reading artifacts from disk:',basePath);
-
-if(!fs.existsSync(basePath))return Promise.reject(new Error('No saved artifacts found'));
-
-
-const filenames=fs.readdirSync(basePath);
-const artifacts=JSON.parse(fs.readFileSync(path.join(basePath,artifactsFilename),'utf8'));
-
-
-artifacts.devtoolsLogs={};
-filenames.filter(f=>f.endsWith(devtoolsLogSuffix)).map(filename=>{
-const passName=filename.replace(devtoolsLogSuffix,'');
-const devtoolsLog=JSON.parse(fs.readFileSync(path.join(basePath,filename),'utf8'));
-artifacts.devtoolsLogs[passName]=devtoolsLog;
-});
-
-
-artifacts.traces={};
-const promises=filenames.filter(f=>f.endsWith(traceSuffix)).map(filename=>{
-return new Promise(resolve=>{
-const passName=filename.replace(traceSuffix,'');
-const readStream=fs.createReadStream(path.join(basePath,filename),{
-encoding:'utf-8',
-highWaterMark:4*1024*1024});
-
-const parser=new TraceParser();
-readStream.on('data',chunk=>parser.parseChunk(chunk));
-readStream.on('end',_=>{
-artifacts.traces[passName]=parser.getTrace();
-resolve();
-});
-});
-});
-return Promise.all(promises).then(_=>artifacts);
-}
-
-
-
-
-
-
-
-
-
-function saveArtifacts(artifacts,basePath){
-mkdirp.sync(basePath);
-rimraf.sync(`${basePath}/*${traceSuffix}`);
-rimraf.sync(`${basePath}/${artifactsFilename}`);
-
-
-artifacts=Object.assign({},artifacts);
-
-
-const traces=artifacts.traces;
-let promise=Promise.all(Object.keys(traces).map(passName=>{
-return saveTrace(traces[passName],`${basePath}/${passName}${traceSuffix}`);
-}));
-
-
-const devtoolsLogs=artifacts.devtoolsLogs;
-promise=promise.then(_=>{
-Object.keys(devtoolsLogs).map(passName=>{
-const log=JSON.stringify(devtoolsLogs[passName]);
-fs.writeFileSync(`${basePath}/${passName}${devtoolsLogSuffix}`,log,'utf8');
-});
-delete artifacts.traces;
-delete artifacts.devtoolsLogs;
-});
-
-
-promise=promise.then(_=>{
-fs.writeFileSync(`${basePath}/${artifactsFilename}`,JSON.stringify(artifacts,0,2),'utf8');
-log.log('Artifacts saved to disk in folder:',basePath);
-});
-return promise;
-}
-
-
-
-
-
-
-
-function prepareAssets(artifacts,audits){
-const passNames=Object.keys(artifacts.traces);
-const assets=[];
-
-return passNames.reduce((chain,passName)=>{
-const trace=artifacts.traces[passName];
-const devtoolsLog=artifacts.devtoolsLogs[passName];
-
-return chain.then(_=>artifacts.requestScreenshots(trace)).
-then(screenshots=>{
-const traceData=Object.assign({},trace);
-const screenshotsHTML=screenshotDump(screenshots);
-
-if(audits){
-const evts=new Metrics(traceData.traceEvents,audits).generateFakeEvents();
-traceData.traceEvents=traceData.traceEvents.concat(evts);
-}
-
-assets.push({
-passName,
-traceData,
-devtoolsLog,
-screenshotsHTML,
-screenshots});
-
-});
-},Promise.resolve()).
-then(_=>assets);
-}
-
-
-
-
-
-
-
-function*traceJsonGenerator(traceData){
-const keys=Object.keys(traceData);
-
-yield'{\n';
-
-
-yield'"traceEvents": [\n';
-if(traceData.traceEvents.length>0){
-const eventsIterator=traceData.traceEvents[Symbol.iterator]();
-
-const firstEvent=eventsIterator.next().value;
-yield`  ${JSON.stringify(firstEvent)}`;
-for(const event of eventsIterator){
-yield`,\n  ${JSON.stringify(event)}`;
-}
-}
-yield'\n]';
-
-
-if(keys.length>1){
-for(const key of keys){
-if(key==='traceEvents')continue;
-
-yield`,\n"${key}": ${JSON.stringify(traceData[key],null,2)}`;
-}
-}
-
-yield'}\n';
-}
-
-
-
-
-
-
-
-function saveTrace(traceData,traceFilename){
-return new Promise((resolve,reject)=>{
-const traceIter=traceJsonGenerator(traceData);
-
-
-const traceStream=new stream.Readable({
-read(){
-const next=traceIter.next();
-this.push(next.done?null:next.value);
-}});
-
-
-const writeStream=fs.createWriteStream(traceFilename);
-writeStream.on('finish',resolve);
-writeStream.on('error',reject);
-
-traceStream.pipe(writeStream);
-});
-}
-
-
-
-
-
-
-
-
-function saveAssets(artifacts,audits,pathWithBasename){
-return prepareAssets(artifacts,audits).then(assets=>{
-return Promise.all(assets.map((data,index)=>{
-const devtoolsLogFilename=`${pathWithBasename}-${index}${devtoolsLogSuffix}`;
-fs.writeFileSync(devtoolsLogFilename,JSON.stringify(data.devtoolsLog,null,2));
-log.log('saveAssets','devtools log saved to disk: '+devtoolsLogFilename);
-
-const screenshotsHTMLFilename=`${pathWithBasename}-${index}.screenshots.html`;
-fs.writeFileSync(screenshotsHTMLFilename,data.screenshotsHTML);
-log.log('saveAssets','screenshots saved to disk: '+screenshotsHTMLFilename);
-
-const screenshotsJSONFilename=`${pathWithBasename}-${index}.screenshots.json`;
-fs.writeFileSync(screenshotsJSONFilename,JSON.stringify(data.screenshots,null,2));
-log.log('saveAssets','screenshots saved to disk: '+screenshotsJSONFilename);
-
-const streamTraceFilename=`${pathWithBasename}-${index}${traceSuffix}`;
-log.log('saveAssets','streaming trace file to disk: '+streamTraceFilename);
-return saveTrace(data.traceData,streamTraceFilename).then(_=>{
-log.log('saveAssets','trace file streamed to disk: '+streamTraceFilename);
-});
-}));
-});
-}
-
-
-
-
-
-
-
-function logAssets(artifacts,audits){
-return prepareAssets(artifacts,audits).then(assets=>{
-assets.map(data=>{
-log.log(`devtoolslog-${data.passName}.json`,JSON.stringify(data.devtoolsLog));
-const traceIter=traceJsonGenerator(data.traceData);
-let traceJson='';
-for(const trace of traceIter){
-traceJson+=trace;
-}
-log.log(`trace-${data.passName}.json`,traceJson);
-});
-});
-}
-
 module.exports={
-saveArtifacts,
-loadArtifacts,
-saveAssets,
-prepareAssets,
-saveTrace,
-logAssets};
+captureJSCallUsage,
+wrapRuntimeEvalErrorInBrowser,
+registerPerformanceObserverInPage,
+checkTimeSinceLastLongTask};
 
 
-},{"./traces/pwmetrics-events":38,"./traces/trace-parser":39,"lighthouse-logger":137,"mkdirp":52,"path":69,"rimraf":52,"stream":86}],19:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-
-
-const log=require('lighthouse-logger');
-
-class ConsoleQuieter{
-static mute(opts){
-ConsoleQuieter._logs=ConsoleQuieter._logs||[];
-
-console.log=function(...args){
-ConsoleQuieter._logs.push({type:'log',args,prefix:opts.prefix});
-};
-console.warn=function(...args){
-ConsoleQuieter._logs.push({type:'warn',args,prefix:opts.prefix});
-};
-console.error=function(...args){
-ConsoleQuieter._logs.push({type:'error',args,prefix:opts.prefix});
-};
-}
-
-static unmuteAndFlush(){
-console.log=ConsoleQuieter._consolelog;
-console.warn=ConsoleQuieter._consolewarn;
-console.error=ConsoleQuieter._consoleerror;
-
-ConsoleQuieter._logs.forEach(entry=>{
-log.verbose(`${entry.prefix}-${entry.type}`,...entry.args);
-});
-ConsoleQuieter._logs=[];
-}}
-
-
-ConsoleQuieter._consolelog=console.log.bind(console);
-ConsoleQuieter._consolewarn=console.warn.bind(console);
-ConsoleQuieter._consoleerror=console.error.bind(console);
-
-module.exports=ConsoleQuieter;
-
-},{"lighthouse-logger":137}],20:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const Node=require('./node');
-
-class CPUNode extends Node{
-
-
-
-
-constructor(parentEvent,childEvents=[]){
-const nodeId=`${parentEvent.tid}.${parentEvent.ts}`;
-super(nodeId);
-
-this._event=parentEvent;
-this._childEvents=childEvents;
-}
-
-
-
-
-get type(){
-return Node.TYPES.CPU;
-}
-
-
-
-
-get startTime(){
-return this._event.ts;
-}
-
-
-
-
-get endTime(){
-return this._event.ts+this._event.dur;
-}
-
-
-
-
-get event(){
-return this._event;
-}
-
-
-
-
-get childEvents(){
-return this._childEvents;
-}
-
-
-
-
-
-didPerformLayout(){
-return this._childEvents.some(evt=>evt.name==='Layout');
-}
-
-
-
-
-
-
-isEvaluateScriptFor(urls){
-return this._childEvents.some(evt=>{
-return evt.name==='EvaluateScript'&&
-evt.args.data&&
-urls.has(evt.args.data.url);
-});
-}
-
-
-
-
-cloneWithoutRelationships(){
-return new CPUNode(this._event,this._childEvents);
-}}
-
-
-module.exports=CPUNode;
-
-},{"./node":22}],21:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const Node=require('./node');
-const WebInspector=require('../web-inspector');
-
-class NetworkNode extends Node{
-
-
-
-constructor(networkRecord){
-super(networkRecord.requestId);
-this._record=networkRecord;
-}
-
-
-
-
-get type(){
-return Node.TYPES.NETWORK;
-}
-
-
-
-
-get startTime(){
-return this._record.startTime*1000*1000;
-}
-
-
-
-
-get endTime(){
-return this._record.endTime*1000*1000;
-}
-
-
-
-
-get record(){
-return this._record;
-}
-
-
-
-
-get initiatorType(){
-return this._record._initiator&&this._record._initiator.type;
-}
-
-
-
-
-hasRenderBlockingPriority(){
-const priority=this._record.priority();
-const isScript=this._record._resourceType===WebInspector.resourceTypes.Script;
-return priority==='VeryHigh'||priority==='High'&&isScript;
-}
-
-
-
-
-cloneWithoutRelationships(){
-return new NetworkNode(this._record);
-}}
-
-
-module.exports=NetworkNode;
-
-},{"../web-inspector":42,"./node":22}],22:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-
-
-
-
-
-
-
-
-
-
-
-
-class Node{
-
-
-
-constructor(id){
-this._id=id;
-this._dependents=[];
-this._dependencies=[];
-}
-
-
-
-
-get id(){
-return this._id;
-}
-
-
-
-
-get type(){
-throw new Error('Unimplemented');
-}
-
-
-
-
-get startTime(){
-throw new Error('Unimplemented');
-}
-
-
-
-
-get endTime(){
-throw new Error('Unimplemented');
-}
-
-
-
-
-getDependents(){
-return this._dependents.slice();
-}
-
-
-
-
-getDependencies(){
-return this._dependencies.slice();
-}
-
-
-
-
-getNumberOfDependencies(){
-return this._dependencies.length;
-}
-
-
-
-
-getRootNode(){
-let rootNode=this;
-while(rootNode._dependencies.length){
-rootNode=rootNode._dependencies[0];
-}
-
-return rootNode;
-}
-
-
-
-
-addDependent(node){
-node.addDependency(this);
-}
-
-
-
-
-addDependency(node){
-if(this._dependencies.includes(node)){
-return;
-}
-
-node._dependents.push(this);
-this._dependencies.push(node);
-}
-
-
-
-
-
-cloneWithoutRelationships(){
-return new Node(this.id);
-}
-
-
-
-
-
-
-
-
-
-cloneWithRelationships(predicate){
-const rootNode=this.getRootNode();
-
-let shouldIncludeNode=()=>true;
-if(predicate){
-const idsToInclude=new Set();
-rootNode.traverse(node=>{
-if(predicate(node)){
-node.traverse(
-node=>idsToInclude.add(node.id),
-node=>node._dependencies.filter(parent=>!idsToInclude.has(parent)));
-
-}
-});
-
-shouldIncludeNode=node=>idsToInclude.has(node.id);
-}
-
-const idToNodeMap=new Map();
-rootNode.traverse(originalNode=>{
-if(!shouldIncludeNode(originalNode))return;
-const clonedNode=originalNode.cloneWithoutRelationships();
-idToNodeMap.set(clonedNode.id,clonedNode);
-});
-
-rootNode.traverse(originalNode=>{
-if(!shouldIncludeNode(originalNode))return;
-const clonedNode=idToNodeMap.get(originalNode.id);
-
-for(const dependency of originalNode._dependencies){
-const clonedDependency=idToNodeMap.get(dependency.id);
-clonedNode.addDependency(clonedDependency);
-}
-});
-
-return idToNodeMap.get(this.id);
-}
-
-
-
-
-
-
-
-_traversePaths(iterator,getNext){
-const stack=[[this]];
-while(stack.length){
-const path=stack.shift();
-const node=path[0];
-iterator(node,path);
-
-const nodesToAdd=getNext(node);
-for(const nextNode of nodesToAdd){
-stack.push([nextNode].concat(path));
-}
-}
-}
-
-
-
-
-
-
-
-traverse(iterator,getNext){
-if(!getNext){
-getNext=node=>node.getDependents();
-}
-
-const visited=new Set();
-const originalGetNext=getNext;
-
-getNext=node=>{
-visited.add(node.id);
-const allNodesToVisit=originalGetNext(node);
-const nodesToVisit=allNodesToVisit.filter(nextNode=>!visited.has(nextNode.id));
-nodesToVisit.forEach(nextNode=>visited.add(nextNode.id));
-return nodesToVisit;
-};
-
-this._traversePaths(iterator,getNext);
-}
-
-
-
-
-
-
-static hasCycle(node){
-const visited=new Set();
-const currentPath=[];
-const toVisit=[node];
-const depthAdded=new Map([[node,0]]);
-
-
-while(toVisit.length){
-
-const currentNode=toVisit.pop();
-
-
-if(currentPath.includes(currentNode))return true;
-
-if(visited.has(currentNode))continue;
-
-
-while(currentPath.length>depthAdded.get(currentNode))currentPath.pop();
-
-
-visited.add(currentNode);
-currentPath.push(currentNode);
-
-
-for(const dependent of currentNode._dependents){
-if(toVisit.includes(dependent))continue;
-toVisit.push(dependent);
-depthAdded.set(dependent,currentPath.length);
-}
-}
-
-return false;
-}}
-
-
-Node.TYPES={
-NETWORK:'network',
-CPU:'cpu'};
-
-
-module.exports=Node;
-
-},{}],23:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const Node=require('../node');
-const TcpConnection=require('./tcp-connection');
-const emulation=require('../../emulation').settings;
-
-
-const DEFAULT_MAXIMUM_CONCURRENT_REQUESTS=10;
-
-
-const DEFAULT_FALLBACK_TTFB=30;
-const DEFAULT_RTT=emulation.TYPICAL_MOBILE_THROTTLING_METRICS.targetLatency;
-const DEFAULT_THROUGHPUT=emulation.TYPICAL_MOBILE_THROTTLING_METRICS.targetDownloadThroughput*8;
-
-
-const DEFAULT_CPU_TASK_MULTIPLIER=emulation.CPU_THROTTLE_METRICS.rate;
-
-const DEFAULT_LAYOUT_TASK_MULTIPLIER=DEFAULT_CPU_TASK_MULTIPLIER/2;
-
-const DEFAULT_MAXIMUM_CPU_TASK_DURATION=10000;
-
-const TLS_SCHEMES=['https','wss'];
-
-function groupBy(items,keyFunc){
-const grouped=new Map();
-items.forEach(item=>{
-const key=keyFunc(item);
-const group=grouped.get(key)||[];
-group.push(item);
-grouped.set(key,group);
-});
-
-return grouped;
-}
-
-const NodeState={
-NotReadyToStart:0,
-ReadyToStart:1,
-InProgress:2,
-Complete:3};
-
-
-class Simulator{
-
-
-
-
-
-constructor(graph,options){
-this._graph=graph;
-this._options=Object.assign(
-{
-rtt:DEFAULT_RTT,
-throughput:DEFAULT_THROUGHPUT,
-fallbackTTFB:DEFAULT_FALLBACK_TTFB,
-maximumConcurrentRequests:DEFAULT_MAXIMUM_CONCURRENT_REQUESTS,
-cpuTaskMultiplier:DEFAULT_CPU_TASK_MULTIPLIER,
-layoutTaskMultiplier:DEFAULT_LAYOUT_TASK_MULTIPLIER},
-
-options);
-
-
-this._rtt=this._options.rtt;
-this._throughput=this._options.throughput;
-this._fallbackTTFB=this._options.fallbackTTFB;
-this._maximumConcurrentRequests=Math.min(
-TcpConnection.maximumSaturatedConnections(this._rtt,this._throughput),
-this._options.maximumConcurrentRequests);
-
-this._cpuTaskMultiplier=this._options.cpuTaskMultiplier;
-this._layoutTaskMultiplier=this._options.layoutTaskMultiplier;
-}
-
-
-
-
-
-
-static getTTFB(record){
-const timing=record._timing;
-return timing&&timing.receiveHeadersEnd-timing.sendEnd||Infinity;
-}
-
-
-
-
-_initializeNetworkRecords(){
-this._networkRecords=[];
-
-this._graph.getRootNode().traverse(node=>{
-if(node.type===Node.TYPES.NETWORK){
-this._networkRecords.push(node.record);
-}
-});
-}
-
-
-
-
-_initializeNetworkConnections(){
-const connections=new Map();
-const recordsByConnection=groupBy(this._networkRecords,record=>record.connectionId);
-
-for(const[connectionId,records]of recordsByConnection.entries()){
-const isTLS=TLS_SCHEMES.includes(records[0].parsedURL.scheme);
-const isH2=records[0].protocol==='h2';
-
-
-
-
-
-
-
-let estimatedResponseTime=Math.min(...records.map(Simulator.getTTFB));
-
-
-if(!Number.isFinite(estimatedResponseTime)){
-estimatedResponseTime=this._fallbackTTFB;
-}
-
-const connection=new TcpConnection(
-this._rtt,
-this._throughput,
-estimatedResponseTime,
-isTLS,
-isH2);
-
-
-connections.set(connectionId,connection);
-}
-
-this._connections=connections;
-return connections;
-}
-
-
-
-
-_initializeAuxiliaryData(){
-this._nodeTiming=new Map();
-this._connectionsInUse=new Set();
-this._numberInProgressByType=new Map();
-
-this._nodes=[];
-for(const key of Object.keys(NodeState)){
-this._nodes[NodeState[key]]=new Set();
-}
-}
-
-
-
-
-
-_numberInProgress(type){
-return this._numberInProgressByType.get(type)||0;
-}
-
-
-
-
-
-_setTimingData(node,values){
-const timingData=this._nodeTiming.get(node)||{};
-Object.assign(timingData,values);
-this._nodeTiming.set(node,timingData);
-}
-
-
-
-
-
-_markNodeAsReadyToStart(node,queuedTime){
-this._nodes[NodeState.ReadyToStart].add(node);
-this._nodes[NodeState.NotReadyToStart].delete(node);
-this._setTimingData(node,{queuedTime});
-}
-
-
-
-
-
-_markNodeAsInProgress(node,startTime){
-this._nodes[NodeState.InProgress].add(node);
-this._nodes[NodeState.ReadyToStart].delete(node);
-this._numberInProgressByType.set(node.type,this._numberInProgress(node.type)+1);
-this._setTimingData(node,{startTime});
-}
-
-
-
-
-
-_markNodeAsComplete(node,endTime){
-this._nodes[NodeState.Complete].add(node);
-this._nodes[NodeState.InProgress].delete(node);
-this._numberInProgressByType.set(node.type,this._numberInProgress(node.type)-1);
-this._setTimingData(node,{endTime});
-
-
-for(const dependent of node.getDependents()){
-
-const dependencies=dependent.getDependencies();
-if(dependencies.some(dep=>!this._nodes[NodeState.Complete].has(dep)))continue;
-
-
-this._markNodeAsReadyToStart(dependent,endTime);
-}
-}
-
-
-
-
-
-_startNodeIfPossible(node,totalElapsedTime){
-if(node.type===Node.TYPES.CPU){
-
-if(this._numberInProgress(node.type)===0){
-this._markNodeAsInProgress(node,totalElapsedTime);
-this._setTimingData(node,{timeElapsed:0});
-}
-
-return;
-}
-
-if(node.type!==Node.TYPES.NETWORK)throw new Error('Unsupported');
-
-const connection=this._connections.get(node.record.connectionId);
-const numberOfActiveRequests=this._numberInProgress(node.type);
-
-
-if(
-numberOfActiveRequests>=this._maximumConcurrentRequests||
-this._connectionsInUse.has(connection))
-{
-return;
-}
-
-this._markNodeAsInProgress(node,totalElapsedTime);
-this._setTimingData(node,{
-timeElapsed:0,
-timeElapsedOvershoot:0,
-bytesDownloaded:0});
-
-
-this._connectionsInUse.add(connection);
-}
-
-
-
-
-
-_updateNetworkCapacity(){
-for(const connection of this._connectionsInUse){
-connection.setThroughput(this._throughput/this._nodes[NodeState.InProgress].size);
-}
-}
-
-
-
-
-
-
-_estimateTimeRemaining(node){
-if(node.type===Node.TYPES.CPU){
-const timingData=this._nodeTiming.get(node);
-const multiplier=node.didPerformLayout()?
-this._layoutTaskMultiplier:
-this._cpuTaskMultiplier;
-const totalDuration=Math.min(
-Math.round(node.event.dur/1000*multiplier),
-DEFAULT_MAXIMUM_CPU_TASK_DURATION);
-
-const estimatedTimeElapsed=totalDuration-timingData.timeElapsed;
-this._setTimingData(node,{estimatedTimeElapsed});
-return estimatedTimeElapsed;
-}
-
-if(node.type!==Node.TYPES.NETWORK)throw new Error('Unsupported');
-
-const timingData=this._nodeTiming.get(node);
-const connection=this._connections.get(node.record.connectionId);
-const calculation=connection.simulateDownloadUntil(
-node.record.transferSize-timingData.bytesDownloaded,
-{timeAlreadyElapsed:timingData.timeElapsed});
-
-
-const estimatedTimeElapsed=calculation.timeElapsed+timingData.timeElapsedOvershoot;
-this._setTimingData(node,{estimatedTimeElapsed});
-return estimatedTimeElapsed;
-}
-
-
-
-
-
-_findNextNodeCompletionTime(){
-let minimumTime=Infinity;
-for(const node of this._nodes[NodeState.InProgress]){
-minimumTime=Math.min(minimumTime,this._estimateTimeRemaining(node));
-}
-
-return minimumTime;
-}
-
-
-
-
-
-
-
-_updateProgressMadeInTimePeriod(node,timePeriodLength,totalElapsedTime){
-const timingData=this._nodeTiming.get(node);
-const isFinished=timingData.estimatedTimeElapsed===timePeriodLength;
-
-if(node.type===Node.TYPES.CPU){
-return isFinished?
-this._markNodeAsComplete(node,totalElapsedTime):
-timingData.timeElapsed+=timePeriodLength;
-}
-
-if(node.type!==Node.TYPES.NETWORK)throw new Error('Unsupported');
-
-const connection=this._connections.get(node.record.connectionId);
-const calculation=connection.simulateDownloadUntil(
-node.record.transferSize-timingData.bytesDownloaded,
-{
-timeAlreadyElapsed:timingData.timeElapsed,
-maximumTimeToElapse:timePeriodLength-timingData.timeElapsedOvershoot});
-
-
-
-connection.setCongestionWindow(calculation.congestionWindow);
-connection.setH2OverflowBytesDownloaded(calculation.extraBytesDownloaded);
-
-if(isFinished){
-connection.setWarmed(true);
-this._connectionsInUse.delete(connection);
-this._markNodeAsComplete(node,totalElapsedTime);
-}else{
-timingData.timeElapsed+=calculation.timeElapsed;
-timingData.timeElapsedOvershoot+=calculation.timeElapsed-timePeriodLength;
-timingData.bytesDownloaded+=calculation.bytesDownloaded;
-}
-}
-
-
-
-
-
-simulate(){
-
-this._initializeNetworkRecords();
-this._initializeNetworkConnections();
-this._initializeAuxiliaryData();
-
-const nodesNotReadyToStart=this._nodes[NodeState.NotReadyToStart];
-const nodesReadyToStart=this._nodes[NodeState.ReadyToStart];
-const nodesInProgress=this._nodes[NodeState.InProgress];
-
-const rootNode=this._graph.getRootNode();
-rootNode.traverse(node=>nodesNotReadyToStart.add(node));
-
-let totalElapsedTime=0;
-
-
-this._markNodeAsReadyToStart(rootNode,totalElapsedTime);
-
-
-while(nodesReadyToStart.size||nodesInProgress.size){
-
-for(const node of nodesReadyToStart){
-this._startNodeIfPossible(node,totalElapsedTime);
-}
-
-
-this._updateNetworkCapacity();
-
-
-const minimumTime=this._findNextNodeCompletionTime();
-totalElapsedTime+=minimumTime;
-
-
-for(const node of nodesInProgress){
-this._updateProgressMadeInTimePeriod(node,minimumTime,totalElapsedTime);
-}
-}
-
-return{
-timeInMs:totalElapsedTime,
-nodeTiming:this._nodeTiming};
-
-}}
-
-
-module.exports=Simulator;
-
-
-
-
-
-
-
-
-
-Simulator.NodeTimingData;
-
-},{"../../emulation":27,"../node":22,"./tcp-connection":24}],24:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const INITIAL_CONGESTION_WINDOW=10;
-const TCP_SEGMENT_SIZE=1460;
-
-class TcpConnection{
-
-
-
-
-
-
-
-constructor(rtt,throughput,serverLatency=0,ssl=true,h2=false){
-this._warmed=false;
-this._ssl=ssl;
-this._h2=h2;
-this._rtt=rtt;
-this._throughput=throughput;
-this._serverLatency=serverLatency;
-this._congestionWindow=INITIAL_CONGESTION_WINDOW;
-this._h2OverflowBytesDownloaded=0;
-}
-
-
-
-
-
-
-static maximumSaturatedConnections(rtt,availableThroughput){
-const roundTripsPerSecond=1000/rtt;
-const bytesPerRoundTrip=TCP_SEGMENT_SIZE;
-const bytesPerSecond=roundTripsPerSecond*bytesPerRoundTrip;
-const minimumThroughputRequiredPerRequest=bytesPerSecond*8;
-return Math.floor(availableThroughput/minimumThroughputRequiredPerRequest);
-}
-
-
-
-
-_computeMaximumCongestionWindowInSegments(){
-const bytesPerSecond=this._throughput/8;
-const secondsPerRoundTrip=this._rtt/1000;
-const bytesPerRoundTrip=bytesPerSecond*secondsPerRoundTrip;
-return Math.floor(bytesPerRoundTrip/TCP_SEGMENT_SIZE);
-}
-
-
-
-
-setThroughput(throughput){
-this._throughput=throughput;
-}
-
-
-
-
-setCongestionWindow(congestion){
-this._congestionWindow=congestion;
-}
-
-
-
-
-setWarmed(warmed){
-this._warmed=warmed;
-}
-
-
-
-
-
-
-setH2OverflowBytesDownloaded(bytes){
-if(!this._h2)return;
-this._h2OverflowBytesDownloaded=bytes;
-}
-
-
-
-
-
-
-
-
-
-
-
-
-simulateDownloadUntil(bytesToDownload,options){
-const{timeAlreadyElapsed=0,maximumTimeToElapse=Infinity}=options||{};
-
-if(this._warmed&&this._h2){
-bytesToDownload-=this._h2OverflowBytesDownloaded;
-}
-const twoWayLatency=this._rtt;
-const oneWayLatency=twoWayLatency/2;
-const maximumCongestionWindow=this._computeMaximumCongestionWindowInSegments();
-
-let handshakeAndRequest=oneWayLatency;
-if(!this._warmed){
-handshakeAndRequest=
-
-oneWayLatency+
-
-oneWayLatency+
-
-oneWayLatency+(
-
-this._ssl?twoWayLatency:0);
-}
-
-let roundTrips=Math.ceil(handshakeAndRequest/twoWayLatency);
-let timeToFirstByte=handshakeAndRequest+this._serverLatency+oneWayLatency;
-if(this._warmed&&this._h2)timeToFirstByte=0;
-
-const timeElapsedForTTFB=Math.max(timeToFirstByte-timeAlreadyElapsed,0);
-const maximumDownloadTimeToElapse=maximumTimeToElapse-timeElapsedForTTFB;
-
-let congestionWindow=Math.min(this._congestionWindow,maximumCongestionWindow);
-let totalBytesDownloaded=0;
-if(timeElapsedForTTFB>0){
-totalBytesDownloaded=congestionWindow*TCP_SEGMENT_SIZE;
-}else{
-roundTrips=0;
-}
-
-let downloadTimeElapsed=0;
-let bytesRemaining=bytesToDownload-totalBytesDownloaded;
-while(bytesRemaining>0&&downloadTimeElapsed<=maximumDownloadTimeToElapse){
-roundTrips++;
-downloadTimeElapsed+=twoWayLatency;
-congestionWindow=Math.max(Math.min(maximumCongestionWindow,congestionWindow*2),1);
-
-const bytesDownloadedInWindow=congestionWindow*TCP_SEGMENT_SIZE;
-totalBytesDownloaded+=bytesDownloadedInWindow;
-bytesRemaining-=bytesDownloadedInWindow;
-}
-
-const timeElapsed=timeElapsedForTTFB+downloadTimeElapsed;
-const extraBytesDownloaded=this._h2?Math.max(totalBytesDownloaded-bytesToDownload,0):0;
-const bytesDownloaded=Math.max(Math.min(totalBytesDownloaded,bytesToDownload),0);
-
-return{
-roundTrips,
-timeElapsed,
-bytesDownloaded,
-extraBytesDownloaded,
-congestionWindow};
-
-}}
-
-
-module.exports=TcpConnection;
-
-},{}],25:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-
-
-
-
-
-
-
-
-
-
-
-
-function getElementsInDocument(selector){
-const results=[];
-
-const _findAllElements=nodes=>{
-for(let i=0,el;el=nodes[i];++i){
-if(!selector||el.matches(selector)){
-results.push(el);
-}
-
-if(el.shadowRoot){
-_findAllElements(el.shadowRoot.querySelectorAll('*'));
-}
-}
-};
-_findAllElements(document.querySelectorAll('*'));
-
-return results;
-}
-
-module.exports={
-getElementsInDocumentFnString:getElementsInDocument.toString()};
-
-
-},{}],26:[function(require,module,exports){
-
-
-
-
-
-
-'use strict';
-
-class Element{
-constructor(element,driver){
-if(!element||!driver){
-throw Error('Driver and element required to create Element');
-}
-this.driver=driver;
-this.element=element;
-}
-
-
-
-
-
-getAttribute(name){
-return this.driver.
-sendCommand('DOM.getAttributes',{
-nodeId:this.element.nodeId}).
-
-
-
-
-then(resp=>{
-const attrIndex=resp.attributes.indexOf(name);
-if(attrIndex===-1){
-return null;
-}
-
-return resp.attributes[attrIndex+1];
-});
-}
-
-
-
-
-
-getProperty(propName){
-return this.driver.
-sendCommand('DOM.resolveNode',{
-nodeId:this.element.nodeId}).
-
-then(resp=>{
-return this.driver.getObjectProperty(resp.object.objectId,propName);
-});
-}}
-
-
-module.exports=Element;
-
-},{}],27:[function(require,module,exports){
-
-
-
-
-
-
-'use strict';
-
-
-
-
-const NEXUS5X_EMULATION_METRICS={
-mobile:true,
-screenWidth:412,
-screenHeight:732,
-width:412,
-height:732,
-positionX:0,
-positionY:0,
-scale:1,
-deviceScaleFactor:2.625,
-fitWindow:false,
-screenOrientation:{
-angle:0,
-type:'portraitPrimary'}};
-
-
-
-const NEXUS5X_USERAGENT={
-userAgent:'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MRA58N) AppleWebKit/537.36'+
-'(KHTML, like Gecko) Chrome/61.0.3116.0 Mobile Safari/537.36'};
-
-
-
-
-
-
-
-const LATENCY_FACTOR=3.75;
-const THROUGHPUT_FACTOR=0.9;
-
-const TARGET_LATENCY=150;
-const TARGET_DOWNLOAD_THROUGHPUT=Math.floor(1.6*1024*1024/8);
-const TARGET_UPLOAD_THROUGHPUT=Math.floor(750*1024/8);
-
-const TYPICAL_MOBILE_THROTTLING_METRICS={
-targetLatency:TARGET_LATENCY,
-latency:TARGET_LATENCY*LATENCY_FACTOR,
-targetDownloadThroughput:TARGET_DOWNLOAD_THROUGHPUT,
-downloadThroughput:TARGET_DOWNLOAD_THROUGHPUT*THROUGHPUT_FACTOR,
-targetUploadThroughput:TARGET_UPLOAD_THROUGHPUT,
-uploadThroughput:TARGET_UPLOAD_THROUGHPUT*THROUGHPUT_FACTOR,
-offline:false};
-
-
-const OFFLINE_METRICS={
-offline:true,
-
-latency:0,
-downloadThroughput:0,
-uploadThroughput:0};
-
-
-const NO_THROTTLING_METRICS={
-latency:0,
-downloadThroughput:0,
-uploadThroughput:0,
-offline:false};
-
-
-const NO_CPU_THROTTLE_METRICS={
-rate:1};
-
-const CPU_THROTTLE_METRICS={
-rate:4};
-
-
-function enableNexus5X(driver){
-
-
-
-
-
-
-
-
-
-
-
-
-const injectedTouchEventsFunction=function(){
-const touchEvents=['ontouchstart','ontouchend','ontouchmove','ontouchcancel'];
-const recepients=[window.__proto__,document.__proto__];
-for(let i=0;i<touchEvents.length;++i){
-for(let j=0;j<recepients.length;++j){
-if(!(touchEvents[i]in recepients[j])){
-Object.defineProperty(recepients[j],touchEvents[i],{
-value:null,writable:true,configurable:true,enumerable:true});
-
-}
-}
-}
-};
-
-
-return Promise.all([
-driver.sendCommand('Emulation.setDeviceMetricsOverride',NEXUS5X_EMULATION_METRICS),
-
-driver.sendCommand('Network.enable'),
-driver.sendCommand('Network.setUserAgentOverride',NEXUS5X_USERAGENT),
-driver.sendCommand('Emulation.setTouchEmulationEnabled',{
-enabled:true,
-configuration:'mobile'}),
-
-driver.sendCommand('Page.addScriptToEvaluateOnLoad',{
-scriptSource:'('+injectedTouchEventsFunction.toString()+')()'})]);
-
-
-}
-
-function enableNetworkThrottling(driver){
-return driver.sendCommand('Network.emulateNetworkConditions',TYPICAL_MOBILE_THROTTLING_METRICS);
-}
-
-function disableNetworkThrottling(driver){
-return driver.sendCommand('Network.emulateNetworkConditions',NO_THROTTLING_METRICS);
-}
-
-function goOffline(driver){
-return driver.sendCommand('Network.emulateNetworkConditions',OFFLINE_METRICS);
-}
-
-function enableCPUThrottling(driver){
-return driver.sendCommand('Emulation.setCPUThrottlingRate',CPU_THROTTLE_METRICS);
-}
-
-function disableCPUThrottling(driver){
-return driver.sendCommand('Emulation.setCPUThrottlingRate',NO_CPU_THROTTLE_METRICS);
-}
-
-function getEmulationDesc(){
-const{latency,downloadThroughput,uploadThroughput}=TYPICAL_MOBILE_THROTTLING_METRICS;
-const byteToMbit=bytes=>(bytes/1024/1024*8).toFixed(1);
-return{
-'deviceEmulation':'Nexus 5X',
-'cpuThrottling':`${CPU_THROTTLE_METRICS.rate}x slowdown`,
-'networkThrottling':`${latency}ms RTT, ${byteToMbit(downloadThroughput)}Mbps down, `+
-`${byteToMbit(uploadThroughput)}Mbps up`};
-
-}
-
-module.exports={
-enableNexus5X,
-enableNetworkThrottling,
-disableNetworkThrottling,
-enableCPUThrottling,
-disableCPUThrottling,
-goOffline,
-getEmulationDesc,
-settings:{
-NEXUS5X_EMULATION_METRICS,
-NEXUS5X_USERAGENT,
-TYPICAL_MOBILE_THROTTLING_METRICS,
-OFFLINE_METRICS,
-NO_THROTTLING_METRICS,
-NO_CPU_THROTTLE_METRICS,
-CPU_THROTTLE_METRICS}};
-
-
-
-},{}],28:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const strings=require('./strings');
-
-
-
-
-
-
-
-
-class LighthouseError extends Error{
-
-
-
-
-constructor(errorDefinition,properties){
-super(errorDefinition.code);
-this.name='LHError';
-this.code=errorDefinition.code;
-this.friendlyMessage=errorDefinition.message;
-if(properties)Object.assign(this,properties);
-
-Error.captureStackTrace(this,LighthouseError);
-}
-
-
-
-
-static isPageLoadError(err){
-return err.code===ERRORS.NO_DOCUMENT_REQUEST.code||
-err.code===ERRORS.FAILED_DOCUMENT_REQUEST.code;
-}
-
-
-
-
-
-
-static fromProtocolMessage(method,protocolError){
-
-const protocolErrors=Object.keys(ERRORS).filter(k=>ERRORS[k].pattern).map(k=>ERRORS[k]);
-
-const matchedErrorDefinition=protocolErrors.find(e=>e.pattern.test(protocolError.message));
-if(matchedErrorDefinition){
-return new LighthouseError(matchedErrorDefinition,{
-protocolMethod:method,
-protocolError:protocolError.message});
-
-}
-
-
-let errMsg=`(${method}): ${protocolError.message}`;
-if(protocolError.data)errMsg+=` (${protocolError.data})`;
-const error=new Error(`Protocol error ${errMsg}`);
-return Object.assign(error,{protocolMethod:method,protocolError:protocolError.message});
-}}
-
-
-const ERRORS={
-
-NO_SPEEDLINE_FRAMES:{message:strings.didntCollectScreenshots},
-SPEEDINDEX_OF_ZERO:{message:strings.didntCollectScreenshots},
-NO_SCREENSHOTS:{message:strings.didntCollectScreenshots},
-
-
-NO_TRACING_STARTED:{message:strings.badTraceRecording},
-NO_NAVSTART:{message:strings.badTraceRecording},
-NO_FMP:{message:strings.badTraceRecording},
-NO_DCL:{message:strings.badTraceRecording},
-
-
-FMP_TOO_LATE_FOR_FCPUI:{message:strings.pageLoadTookTooLong},
-NO_FCPUI_IDLE_PERIOD:{message:strings.pageLoadTookTooLong},
-NO_TTI_CPU_IDLE_PERIOD:{message:strings.pageLoadTookTooLong},
-NO_TTI_NETWORK_IDLE_PERIOD:{message:strings.pageLoadTookTooLong},
-
-
-NO_DOCUMENT_REQUEST:{message:strings.pageLoadFailed},
-FAILED_DOCUMENT_REQUEST:{message:strings.pageLoadFailed},
-
-
-TRACING_ALREADY_STARTED:{message:strings.internalChromeError,pattern:/Tracing.*started/},
-PARSING_PROBLEM:{message:strings.internalChromeError,pattern:/Parsing problem/},
-READ_FAILED:{message:strings.internalChromeError,pattern:/Read failed/}};
-
-
-Object.keys(ERRORS).forEach(code=>ERRORS[code].code=code);
-LighthouseError.errors=ERRORS;
-module.exports=LighthouseError;
-
-
-},{"./strings":35}],29:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-
-
-
-
-
-
-
-
-
-function addFormattedCodeSnippet(listener){
-const handler=listener.handler?listener.handler.description:'...';
-const objectName=listener.objectName.toLowerCase().replace('#document','document');
-return Object.assign({
-label:`line: ${listener.line}, col: ${listener.col}`,
-pre:`${objectName}.addEventListener('${listener.type}', ${handler})`},
-listener);
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-function groupCodeSnippetsByLocation(listeners){
-const locToListenersMap=new Map();
-listeners.forEach(loc=>{
-const key=JSON.stringify({line:loc.line,col:loc.col,url:loc.url,type:loc.type});
-if(locToListenersMap.has(key)){
-locToListenersMap.get(key).push(loc);
-}else{
-locToListenersMap.set(key,[loc]);
-}
-});
-
-const results=[];
-locToListenersMap.forEach((listenersForLocation,key)=>{
-const lineColUrlObj=JSON.parse(key);
-
-const codeSnippets=listenersForLocation.reduce((prev,loc)=>{
-return prev+loc.pre.trim()+'\n\n';
-},'');
-lineColUrlObj.pre=codeSnippets;
-
-
-lineColUrlObj.label=listenersForLocation[0].label;
-results.push(lineColUrlObj);
-});
-
-return results;
-}
-
-module.exports={
-addFormattedCodeSnippet,
-groupCodeSnippetsByLocation};
-
-
-},{}],30:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-
-
-
-
-function doExist(manifest){
-if(!manifest||!manifest.icons){
-return false;
-}
-if(manifest.icons.value.length===0){
-return false;
-}
-return true;
-}
-
-
-
-
-
-
-function sizeAtLeast(sizeRequirement,manifest){
-
-
-const iconValues=manifest.icons.value;
-const nestedSizes=iconValues.map(icon=>icon.value.sizes.value);
-const flattenedSizes=[].concat(...nestedSizes);
-
-return flattenedSizes.
-
-filter(size=>typeof size==='string').
-
-filter(size=>/\d+x\d+/.test(size)).
-filter(size=>{
-
-const sizeStrs=size.split(/x/i);
-
-const sizeNums=[parseFloat(sizeStrs[0]),parseFloat(sizeStrs[1])];
-
-const areIconsBigEnough=sizeNums[0]>=sizeRequirement&&sizeNums[1]>=sizeRequirement;
-
-const areIconsSquare=sizeNums[0]===sizeNums[1];
-return areIconsBigEnough&&areIconsSquare;
-});
-}
-
-module.exports={
-doExist,
-sizeAtLeast};
-
-
-},{}],31:[function(require,module,exports){
-
-
-
-
-
-'use strict';
-
-const URL=require('./url-shim');
-const validateColor=require('./web-inspector').Color.parse;
-
-const ALLOWED_DISPLAY_VALUES=[
-'fullscreen',
-'standalone',
-'minimal-ui',
-'browser'];
-
-
-
-
-
-const DEFAULT_DISPLAY_MODE='browser';
-
-const ALLOWED_ORIENTATION_VALUES=[
-'any',
-'natural',
-'landscape',
-'portrait',
-'portrait-primary',
-'portrait-secondary',
-'landscape-primary',
-'landscape-secondary'];
-
-
-function parseString(raw,trim){
-let value;
-let debugString;
-
-if(typeof raw==='string'){
-value=trim?raw.trim():raw;
-}else{
-if(raw!==undefined){
-debugString='ERROR: expected a string.';
-}
-value=undefined;
-}
-
-return{
-raw,
-value,
-debugString};
-
-}
-
-function parseColor(raw){
-const color=parseString(raw);
-
-
-if(color.value===undefined){
-return color;
-}
-
-
-const validatedColor=validateColor(color.raw);
-if(!validatedColor){
-color.value=undefined;
-color.debugString='ERROR: color parsing failed.';
-}
-
-return color;
-}
-
-function parseName(jsonInput){
-return parseString(jsonInput.name,true);
-}
-
-function parseShortName(jsonInput){
-return parseString(jsonInput.short_name,true);
-}
-
-
-
-
-
-
-
-function checkSameOrigin(url1,url2){
-const parsed1=new URL(url1);
-const parsed2=new URL(url2);
-
-return parsed1.origin===parsed2.origin;
-}
-
-
-
-
-function parseStartUrl(jsonInput,manifestUrl,documentUrl){
-const raw=jsonInput.start_url;
-
-
-if(raw===''){
-return{
-raw,
-value:documentUrl,
-debugString:'ERROR: start_url string empty'};
-
-}
-const parsedAsString=parseString(raw);
-if(!parsedAsString.value){
-parsedAsString.value=documentUrl;
-return parsedAsString;
-}
-
-
-let startUrl;
-try{
-startUrl=new URL(raw,manifestUrl).href;
-}catch(e){
-
-return{
-raw,
-value:documentUrl,
-debugString:'ERROR: invalid start_url relative to ${manifestUrl}'};
-
-}
-
-
-if(!checkSameOrigin(startUrl,documentUrl)){
-return{
-raw,
-value:documentUrl,
-debugString:'ERROR: start_url must be same-origin as document'};
-
-}
-
-return{
-raw,
-value:startUrl};
-
-}
-
-function parseDisplay(jsonInput){
-const display=parseString(jsonInput.display,true);
-
-if(!display.value){
-display.value=DEFAULT_DISPLAY_MODE;
-return display;
-}
-
-display.value=display.value.toLowerCase();
-if(!ALLOWED_DISPLAY_VALUES.includes(display.value)){
-display.debugString='ERROR: \'display\' has invalid value '+display.value+
-` will fall back to ${DEFAULT_DISPLAY_MODE}.`;
-display.value=DEFAULT_DISPLAY_MODE;
-}
-
-return display;
-}
-
-function parseOrientation(jsonInput){
-const orientation=parseString(jsonInput.orientation,true);
-
-if(orientation.value&&
-!ALLOWED_ORIENTATION_VALUES.includes(orientation.value.toLowerCase())){
-orientation.value=undefined;
-orientation.debugString='ERROR: \'orientation\' has an invalid value, will be ignored.';
-}
-
-return orientation;
-}
-
-function parseIcon(raw,manifestUrl){
-
-const src=parseString(raw.src,true);
-
-if(src.value===''){
-src.value=undefined;
-}
-if(src.value){
-
-src.value=new URL(src.value,manifestUrl).href;
-}
-
-const type=parseString(raw.type,true);
-
-const density={
-raw:raw.density,
-value:1,
-debugString:undefined};
-
-if(density.raw!==undefined){
-density.value=parseFloat(density.raw);
-if(isNaN(density.value)||!isFinite(density.value)||density.value<=0){
-density.value=1;
-density.debugString='ERROR: icon density cannot be NaN, +∞, or less than or equal to +0.';
-}
-}
-
-const sizes=parseString(raw.sizes);
-if(sizes.value!==undefined){
-const set=new Set();
-sizes.value.trim().split(/\s+/).forEach(size=>set.add(size.toLowerCase()));
-sizes.value=set.size>0?Array.from(set):undefined;
-}
-
-return{
-raw,
-value:{
-src,
-type,
-density,
-sizes},
-
-debugString:undefined};
-
-}
-
-function parseIcons(jsonInput,manifestUrl){
-const raw=jsonInput.icons;
-
-if(raw===undefined){
-return{
-raw,
-value:[],
-debugString:undefined};
-
-}
-
-if(!Array.isArray(raw)){
-return{
-raw,
-value:[],
-debugString:'ERROR: \'icons\' expected to be an array but is not.'};
-
-}
-
-
-
-const value=raw.
-
-filter(icon=>icon.src!==undefined).
-
-map(icon=>parseIcon(icon,manifestUrl)).
-
-filter(parsedIcon=>parsedIcon.value.src.value!==undefined);
-
-return{
-raw,
-value,
-debugString:undefined};
-
-}
-
-function parseApplication(raw){
-const platform=parseString(raw.platform,true);
-const id=parseString(raw.id,true);
-
-
-const appUrl=parseString(raw.url,true);
-if(appUrl.value){
-try{
-
-appUrl.value=new URL(appUrl.value).href;
-}catch(e){
-appUrl.value=undefined;
-appUrl.debugString='ERROR: invalid application URL ${raw.url}';
-}
-}
-
-return{
-raw,
-value:{
-platform,
-id,
-url:appUrl},
-
-debugString:undefined};
-
-}
-
-function parseRelatedApplications(jsonInput){
-const raw=jsonInput.related_applications;
-
-if(raw===undefined){
-return{
-raw,
-value:undefined,
-debugString:undefined};
-
-}
-
-if(!Array.isArray(raw)){
-return{
-raw,
-value:undefined,
-debugString:'ERROR: \'related_applications\' expected to be an array but is not.'};
-
-}
-
-
-
-const value=raw.
-filter(application=>!!application.platform).
-map(parseApplication).
-filter(parsedApp=>!!parsedApp.value.id.value||!!parsedApp.value.url.value);
-
-return{
-raw,
-value,
-debugString:undefined};
-
-}
-
-function parsePreferRelatedApplications(jsonInput){
-const raw=jsonInput.prefer_related_applications;
-let value;
-let debugString;
-
-if(typeof raw==='boolean'){
-value=raw;
-}else{
-if(raw!==undefined){
-debugString='ERROR: \'prefer_related_applications\' expected to be a boolean.';
-}
-value=undefined;
-}
-
-return{
-raw,
-value,
-debugString};
-
-}
-
-function parseThemeColor(jsonInput){
-return parseColor(jsonInput.theme_color);
-}
-
-function parseBackgroundColor(jsonInput){
-return parseColor(jsonInput.background_color);
-}
-
-
-
-
-
-
-
-
-function parse(string,manifestUrl,documentUrl){
-if(manifestUrl===undefined||documentUrl===undefined){
-throw new Error('Manifest and document URLs required for manifest parsing.');
-}
-
-let jsonInput;
-
-try{
-jsonInput=JSON.parse(string);
-}catch(e){
-return{
-raw:string,
-value:undefined,
-debugString:'ERROR: file isn\'t valid JSON: '+e};
-
-}
-
-
-const manifest={
-name:parseName(jsonInput),
-short_name:parseShortName(jsonInput),
-start_url:parseStartUrl(jsonInput,manifestUrl,documentUrl),
-display:parseDisplay(jsonInput),
-orientation:parseOrientation(jsonInput),
-icons:parseIcons(jsonInput,manifestUrl),
-related_applications:parseRelatedApplications(jsonInput),
-prefer_related_applications:parsePreferRelatedApplications(jsonInput),
-theme_color:parseThemeColor(jsonInput),
-background_color:parseBackgroundColor(jsonInput)};
-
-
-
-return{
-raw:string,
-value:manifest,
-debugString:undefined};
-
-}
-
-module.exports=parse;
-
-},{"./url-shim":41,"./web-inspector":42}],32:[function(require,module,exports){
-
-
-
-
-
-
-'use strict';
-
-const NetworkManager=require('./web-inspector').NetworkManager;
-const EventEmitter=require('events').EventEmitter;
-const log=require('lighthouse-logger');
-
-const IGNORED_NETWORK_SCHEMES=['data','ws'];
-
-class NetworkRecorder extends EventEmitter{
-
-
-
-
-constructor(recordArray){
-super();
-
-this._records=recordArray;
-this.networkManager=NetworkManager.createWithFakeTarget();
-
-this.networkManager.addEventListener(
-this.EventTypes.RequestStarted,
-this.onRequestStarted.bind(this));
-
-this.networkManager.addEventListener(
-this.EventTypes.RequestFinished,
-this.onRequestFinished.bind(this));
-
-}
-
-get EventTypes(){
-return NetworkManager.Events;
-}
-
-isIdle(){
-return!!this._getActiveIdlePeriod(0);
-}
-
-is2Idle(){
-return!!this._getActiveIdlePeriod(2);
-}
-
-_getActiveIdlePeriod(allowedRequests){
-const quietPeriods=NetworkRecorder.findNetworkQuietPeriods(this._records,allowedRequests);
-return quietPeriods.find(period=>!Number.isFinite(period.end));
-}
-
-_emitNetworkStatus(){
-const zeroQuiet=this._getActiveIdlePeriod(0);
-const twoQuiet=this._getActiveIdlePeriod(2);
-
-if(twoQuiet&&zeroQuiet){
-log.verbose('NetworkRecorder','network fully-quiet');
-this.emit('network-2-idle');
-this.emit('networkidle');
-}else if(twoQuiet&&!zeroQuiet){
-log.verbose('NetworkRecorder','network semi-quiet');
-this.emit('network-2-idle');
-this.emit('networkbusy');
-}else{
-log.verbose('NetworkRecorder','network busy');
-this.emit('network-2-busy');
-this.emit('networkbusy');
-}
-}
-
-
-
-
-
-
-
-
-
-static findNetworkQuietPeriods(networkRecords,allowedConcurrentRequests,endTime=Infinity){
-
-let timeBoundaries=[];
-networkRecords.forEach(record=>{
-const scheme=record.parsedURL&&record.parsedURL.scheme;
-if(IGNORED_NETWORK_SCHEMES.includes(scheme)){
-return;
-}
-
-
-timeBoundaries.push({time:record.startTime*1000,isStart:true});
-if(record.finished){
-timeBoundaries.push({time:record.endTime*1000,isStart:false});
-}
-});
-
-timeBoundaries=timeBoundaries.
-filter(boundary=>boundary.time<=endTime).
-sort((a,b)=>a.time-b.time);
-
-let numInflightRequests=0;
-let quietPeriodStart=0;
-const quietPeriods=[];
-timeBoundaries.forEach(boundary=>{
-if(boundary.isStart){
-
-if(numInflightRequests===allowedConcurrentRequests){
-quietPeriods.push({start:quietPeriodStart,end:boundary.time});
-}
-numInflightRequests++;
-}else{
-numInflightRequests--;
-
-if(numInflightRequests===allowedConcurrentRequests){
-quietPeriodStart=boundary.time;
-}
-}
-});
-
-
-if(numInflightRequests<=allowedConcurrentRequests){
-quietPeriods.push({start:quietPeriodStart,end:endTime});
-}
-
-return quietPeriods;
-}
-
-
-
-
-
-
-onRequestStarted(request){
-this._records.push(request.data);
-this._emitNetworkStatus();
-}
-
-
-
-
-
-
-
-onRequestFinished(request){
-this.emit('requestloaded',request.data);
-this._emitNetworkStatus();
-}
-
-
-
-
-
-onRequestWillBeSent(data){
-
-this.networkManager._dispatcher.requestWillBeSent(data.requestId,
-data.frameId,data.loaderId,data.documentURL,data.request,
-data.timestamp,data.wallTime,data.initiator,data.redirectResponse,
-data.type);
-}
-
-onRequestServedFromCache(data){
-this.networkManager._dispatcher.requestServedFromCache(data.requestId);
-}
-
-onResponseReceived(data){
-
-this.networkManager._dispatcher.responseReceived(data.requestId,
-data.frameId,data.loaderId,data.timestamp,data.type,data.response);
-}
-
-onDataReceived(data){
-
-this.networkManager._dispatcher.dataReceived(data.requestId,data.timestamp,
-data.dataLength,data.encodedDataLength);
-}
-
-onLoadingFinished(data){
-
-this.networkManager._dispatcher.loadingFinished(data.requestId,
-data.timestamp,data.encodedDataLength);
-}
-
-onLoadingFailed(data){
-
-
-this.networkManager._dispatcher.loadingFailed(data.requestId,
-data.timestamp,data.type,data.errorText,data.canceled,
-data.blockedReason);
-}
-
-onResourceChangedPriority(data){
-this.networkManager._dispatcher.resourceChangedPriority(data.requestId,
-data.newPriority,data.timestamp);
-}
-
-
-
-
-
-
-dispatch(method,params){
-if(!method.startsWith('Network.')){
-return;
-}
-
-switch(method){
-case'Network.requestWillBeSent':return this.onRequestWillBeSent(params);
-case'Network.requestServedFromCache':return this.onRequestServedFromCache(params);
-case'Network.responseReceived':return this.onResponseReceived(params);
-case'Network.dataReceived':return this.onDataReceived(params);
-case'Network.loadingFinished':return this.onLoadingFinished(params);
-case'Network.loadingFailed':return this.onLoadingFailed(params);
-case'Network.resourceChangedPriority':return this.onResourceChangedPriority(params);
-default:return;}
-
-}
-
-
-
-
-
-
-static recordsFromLogs(devtoolsLog){
-const records=[];
-const nr=new NetworkRecorder(records);
-devtoolsLog.forEach(message=>{
-nr.dispatch(message.method,message.params);
-});
-return records;
-}}
-
-
-module.exports=NetworkRecorder;
-
-},{"./web-inspector":42,"events":56,"lighthouse-logger":137}],33:[function(require,module,exports){
+},{}],39:[function(require,module,exports){
 
 
 
@@ -18801,12 +22313,11 @@
 
 }
 
-const context={
+const context=Object.assign({
 url:opts.url,
 deviceEmulation:!opts.flags.disableDeviceEmulation,
-networkThrottling:!opts.flags.disableNetworkThrottling,
-cpuThrottling:!opts.flags.disableCpuThrottling};
-
+throttlingMethod:opts.flags.throttlingMethod},
+opts.flags.throttling);
 
 sentryDelegate.mergeContext({extra:Object.assign({},environmentData.extra,context)});
 return context;
@@ -18814,7 +22325,7 @@
 
 module.exports=sentryDelegate;
 
-},{"lighthouse-logger":137,"raven":52}],34:[function(require,module,exports){
+},{"lighthouse-logger":143,"raven":58}],40:[function(require,module,exports){
 
 
 
@@ -18880,7 +22391,7 @@
 getLogNormalDistribution};
 
 
-},{}],35:[function(require,module,exports){
+},{}],41:[function(require,module,exports){
 
 
 
@@ -18894,10 +22405,11 @@
 badTraceRecording:`Something went wrong with recording the trace over your page load. Please run Lighthouse again.`,
 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.`,
 pageLoadFailed:`Your page failed to load. Verify that the URL is valid and re-run Lighthouse.`,
-internalChromeError:`An internal Chrome error occurred. Please restart Chrome and try re-running Lighthouse.`};
+internalChromeError:`An internal Chrome error occurred. Please restart Chrome and try re-running Lighthouse.`,
+requestContentTimeout:'Fetching resource content has exceeded the allotted time'};
 
 
-},{}],36:[function(require,module,exports){
+},{}],42:[function(require,module,exports){
 
 
 
@@ -18993,7 +22505,7 @@
 taskToGroup};
 
 
-},{}],37:[function(require,module,exports){
+},{}],43:[function(require,module,exports){
 
 
 
@@ -19006,14 +22518,17 @@
 
 
 const TimelineModelTreeView=
+
 require('devtools-timeline-model/lib/timeline-model-treeview.js')(WebInspector);
 
 class TimelineModel{
+
 constructor(events){
 this.init(events);
 }
 
-init(events){
+
+init(trace){
 
 this._tracingModel=
 new WebInspector.TracingModel(new WebInspector.TempFileBackingStorage('tracing'));
@@ -19021,11 +22536,15 @@
 this._timelineModel=
 new WebInspector.TimelineModel(WebInspector.TimelineUIUtils.visibleEventsFilter());
 
-if(typeof events==='string'){
-events=JSON.parse(events);
+let events;
+if(Array.isArray(trace)){
+events=trace;
 }
-if(events.hasOwnProperty('traceEvents')){
-events=events.traceEvents;
+if(typeof trace==='string'){
+events=JSON.parse(trace);
+}
+if(trace.hasOwnProperty('traceEvents')){
+events=trace.traceEvents;
 }
 
 
@@ -19083,7 +22602,6 @@
 
 
 
-
 bottomUpGroupBy(grouping){
 const topDown=this.topDown();
 
@@ -19098,14 +22616,15 @@
 }
 
 frameModel(){
-const frameModel=new WebInspector.TimelineFrameModel(event=>
-WebInspector.TimelineUIUtils.eventStyle(event).category.name);
 
+const mapper=event=>WebInspector.TimelineUIUtils.eventStyle(event).category.name;
+const frameModel=new WebInspector.TimelineFrameModel(mapper);
 frameModel.addTraceEvents({},
 this._timelineModel.inspectedTargetEvents(),this._timelineModel.sessionId()||'');
 return frameModel;
 }
 
+
 filmStripModel(){
 return new WebInspector.FilmStripModel(this._tracingModel);
 }
@@ -19119,7 +22638,7 @@
 
 module.exports=TimelineModel;
 
-},{"../console-quieter":19,"../web-inspector":42,"devtools-timeline-model/lib/timeline-model-treeview.js":129}],38:[function(require,module,exports){
+},{"../console-quieter":22,"../web-inspector":47,"devtools-timeline-model/lib/timeline-model-treeview.js":135}],44:[function(require,module,exports){
 
 
 
@@ -19132,18 +22651,14 @@
 
 
 
+function findValueInMetricsAuditFn(metricName){
+return auditResults=>{
+const metricsAudit=auditResults.metrics;
+if(!metricsAudit||!metricsAudit.details||!metricsAudit.details.items)return;
 
-
-
-function safeGet(object,path){
-const components=path.split('.');
-for(const component of components){
-if(!object){
-return null;
-}
-object=object[component];
-}
-return object;
+const values=metricsAudit.details.items[0];
+return values&&values[metricName];
+};
 }
 
 class Metrics{
@@ -19161,134 +22676,68 @@
 {
 name:'Navigation Start',
 id:'navstart',
-getTs:auditResults=>{
-const fmpExt=auditResults['first-meaningful-paint'].extendedInfo;
-return safeGet(fmpExt,'value.timestamps.navStart');
-},
-getTiming:auditResults=>{
-const fmpExt=auditResults['first-meaningful-paint'].extendedInfo;
-return safeGet(fmpExt,'value.timings.navStart');
-}},
+getTs:findValueInMetricsAuditFn('observedNavigationStartTs'),
+getTiming:findValueInMetricsAuditFn('observedNavigationStart')},
 
 {
 name:'First Contentful Paint',
 id:'ttfcp',
-getTs:auditResults=>{
-const fmpExt=auditResults['first-meaningful-paint'].extendedInfo;
-return safeGet(fmpExt,'value.timestamps.fCP');
-},
-getTiming:auditResults=>{
-const fmpExt=auditResults['first-meaningful-paint'].extendedInfo;
-return safeGet(fmpExt,'value.timings.fCP');
-}},
+getTs:findValueInMetricsAuditFn('observedFirstContentfulPaintTs'),
+getTiming:findValueInMetricsAuditFn('observedFirstContentfulPaint')},
 
 {
 name:'First Meaningful Paint',
 id:'ttfmp',
-getTs:auditResults=>{
-const fmpExt=auditResults['first-meaningful-paint'].extendedInfo;
-return safeGet(fmpExt,'value.timestamps.fMP');
-},
-getTiming:auditResults=>{
-const fmpExt=auditResults['first-meaningful-paint'].extendedInfo;
-return safeGet(fmpExt,'value.timings.fMP');
-}},
+getTs:findValueInMetricsAuditFn('observedFirstMeaningfulPaintTs'),
+getTiming:findValueInMetricsAuditFn('observedFirstMeaningfulPaint')},
 
 {
-name:'Perceptual Speed Index',
-id:'psi',
-getTs:auditResults=>{
-const siExt=auditResults['speed-index-metric'].extendedInfo;
-return safeGet(siExt,'value.timestamps.perceptualSpeedIndex');
-},
-getTiming:auditResults=>{
-const siExt=auditResults['speed-index-metric'].extendedInfo;
-return safeGet(siExt,'value.timings.perceptualSpeedIndex');
-}},
+name:'Speed Index',
+id:'si',
+getTs:findValueInMetricsAuditFn('observedSpeedIndexTs'),
+getTiming:findValueInMetricsAuditFn('observedSpeedIndex')},
 
 {
 name:'First Visual Change',
 id:'fv',
-getTs:auditResults=>{
-const siExt=auditResults['speed-index-metric'].extendedInfo;
-return safeGet(siExt,'value.timestamps.firstVisualChange');
-},
-getTiming:auditResults=>{
-const siExt=auditResults['speed-index-metric'].extendedInfo;
-return safeGet(siExt,'value.timings.firstVisualChange');
-}},
-
-{
-name:'Visually Complete 85%',
-id:'vc85',
-getTs:auditResults=>{
-const siExt=auditResults['speed-index-metric'].extendedInfo;
-return safeGet(siExt,'value.timestamps.visuallyReady');
-},
-getTiming:auditResults=>{
-const siExt=auditResults['speed-index-metric'].extendedInfo;
-return safeGet(siExt,'value.timings.visuallyReady');
-}},
+getTs:findValueInMetricsAuditFn('observedFirstVisualChangeTs'),
+getTiming:findValueInMetricsAuditFn('observedFirstVisualChange')},
 
 {
 name:'Visually Complete 100%',
 id:'vc100',
-getTs:auditResults=>{
-const siExt=auditResults['speed-index-metric'].extendedInfo;
-return safeGet(siExt,'value.timestamps.visuallyComplete');
-},
-getTiming:auditResults=>{
-const siExt=auditResults['speed-index-metric'].extendedInfo;
-return safeGet(siExt,'value.timings.visuallyComplete');
-}},
+getTs:findValueInMetricsAuditFn('observedLastVisualChangeTs'),
+getTiming:findValueInMetricsAuditFn('observedLastVisualChange')},
 
 {
-name:'First Interactive (vBeta)',
+name:'First CPU Idle',
 id:'ttfi',
-getTs:auditResults=>{
-const ttfiExt=auditResults['first-interactive'].extendedInfo;
-return safeGet(ttfiExt,'value.timestamp');
-},
-getTiming:auditResults=>{
-const ttfiExt=auditResults['first-interactive'].extendedInfo;
-return safeGet(ttfiExt,'value.timeInMs');
-}},
+getTs:findValueInMetricsAuditFn('firstCPUIdleTs'),
+getTiming:findValueInMetricsAuditFn('firstCPUIdle')},
 
 {
-name:'Time to Consistently Interactive (vBeta)',
-id:'ttci',
-getTs:auditResults=>{
-const ttiExt=auditResults['consistently-interactive'].extendedInfo;
-return safeGet(ttiExt,'value.timestamp');
-},
-getTiming:auditResults=>{
-const ttiExt=auditResults['consistently-interactive'].extendedInfo;
-return safeGet(ttiExt,'value.timeInMs');
-}},
+name:'Interactive',
+id:'tti',
+getTs:findValueInMetricsAuditFn('interactiveTs'),
+getTiming:findValueInMetricsAuditFn('interactive')},
 
 {
 name:'End of Trace',
 id:'eot',
-getTs:auditResults=>{
-const ttiExt=auditResults['first-meaningful-paint'].extendedInfo;
-return safeGet(ttiExt,'value.timestamps.endOfTrace');
-},
-getTiming:auditResults=>{
-const ttiExt=auditResults['first-meaningful-paint'].extendedInfo;
-return safeGet(ttiExt,'value.timings.endOfTrace');
-}},
+getTs:findValueInMetricsAuditFn('observedTraceEndTs'),
+getTiming:findValueInMetricsAuditFn('observedTraceEnd')},
 
 {
 name:'On Load',
 id:'onload',
-getTs:auditResults=>{
-const ttiExt=auditResults['first-meaningful-paint'].extendedInfo;
-return safeGet(ttiExt,'value.timestamps.onLoad');
-},
-getTiming:auditResults=>{
-const ttiExt=auditResults['first-meaningful-paint'].extendedInfo;
-return safeGet(ttiExt,'value.timings.onLoad');
-}}];
+getTs:findValueInMetricsAuditFn('observedLoadTs'),
+getTiming:findValueInMetricsAuditFn('observedLoad')},
+
+{
+name:'DOM Content Loaded',
+id:'dcl',
+getTs:findValueInMetricsAuditFn('observedDomContentLoadedTs'),
+getTiming:findValueInMetricsAuditFn('observedDomContentLoaded')}];
 
 
 }
@@ -19390,8 +22839,7 @@
 
 module.exports=Metrics;
 
-},{"lighthouse-logger":137}],39:[function(require,module,exports){
-
+},{"lighthouse-logger":143}],45:[function(require,module,exports){
 
 
 
@@ -19411,16 +22859,19 @@
 
 class TraceParser{
 constructor(){
+
 this.traceEvents=[];
 
 this.tracingModel={
-reset:_=>this._reset(),
+reset:()=>this._reset(),
+
 addEvents:evts=>this._addEvents(evts)};
 
 
 const delegateMock={
-loadingProgress:_=>{},
-loadingStarted:_=>{},
+loadingProgress:()=>{},
+loadingStarted:()=>{},
+
 loadingComplete:success=>{
 if(!success)throw new Error('Parsing problem');
 }};
@@ -19464,7 +22915,7 @@
 
 module.exports=TraceParser;
 
-},{"../web-inspector":42}],40:[function(require,module,exports){
+},{"../web-inspector":47}],46:[function(require,module,exports){
 
 
 
@@ -19558,15 +23009,15 @@
 
 
 static getRiskToResponsiveness(
-tabTrace,
-startTime=0,
-endTime=tabTrace.timings.traceEnd,
+events,
+startTime,
+endTime,
 percentiles=[0.5,0.75,0.9,0.99,1])
 {
 const totalTime=endTime-startTime;
 percentiles.sort((a,b)=>a-b);
 
-const ret=TraceProcessor.getMainThreadTopLevelEventDurations(tabTrace,startTime,endTime);
+const ret=TraceProcessor.getMainThreadTopLevelEventDurations(events,startTime,endTime);
 return TraceProcessor._riskPercentiles(ret.durations,totalTime,percentiles,
 ret.clippedLength);
 }
@@ -19578,14 +23029,17 @@
 
 
 
-static getMainThreadTopLevelEventDurations(tabTrace,startTime=0,endTime=Infinity){
-const topLevelEvents=TraceProcessor.getMainThreadTopLevelEvents(tabTrace,startTime,endTime);
+static getMainThreadTopLevelEventDurations(topLevelEvents,startTime=0,endTime=Infinity){
 
 
 const durations=[];
 let clippedLength=0;
 
-topLevelEvents.forEach(event=>{
+for(const event of topLevelEvents){
+if(event.end<startTime||event.start>endTime){
+continue;
+}
+
 let duration=event.duration;
 let eventStart=event.start;
 if(eventStart<startTime){
@@ -19600,7 +23054,7 @@
 }
 
 durations.push(duration);
-});
+}
 durations.sort((a,b)=>a-b);
 
 return{
@@ -19643,153 +23097,25 @@
 return topLevelEvents;
 }
 
+
+
+
+
 static isScheduleableTask(evt){
 return evt.name===SCHEDULABLE_TASK_TITLE||evt.name===SCHEDULABLE_TASK_TITLE_ALT;
 }}
 
 
+
+
+
+
+
+
+
 module.exports=TraceProcessor;
 
-},{}],41:[function(require,module,exports){
-
-
-
-
-
-
-'use strict';
-
-
-
-
-
-
-
-const Util=require('../report/v2/renderer/util.js');
-
-
-
-const URL=typeof self!=='undefined'&&self.URL||require('whatwg-url').URL;
-
-URL.URLSearchParams=typeof self!=='undefined'&&self.URLSearchParams||
-require('whatwg-url').URLSearchParams;
-
-URL.INVALID_URL_DEBUG_STRING=
-'Lighthouse was unable to determine the URL of some script executions. '+
-'It\'s possible a Chrome extension or other eval\'d code is the source.';
-
-
-
-
-
-URL.isValid=function isValid(url){
-try{
-new URL(url);
-return true;
-}catch(e){
-return false;
-}
-};
-
-
-
-
-
-
-URL.hostsMatch=function hostsMatch(urlA,urlB){
-try{
-return new URL(urlA).host===new URL(urlB).host;
-}catch(e){
-return false;
-}
-};
-
-
-
-
-
-
-URL.originsMatch=function originsMatch(urlA,urlB){
-try{
-return new URL(urlA).origin===new URL(urlB).origin;
-}catch(e){
-return false;
-}
-};
-
-
-
-
-
-URL.getOrigin=function getOrigin(url){
-try{
-const urlInfo=new URL(url);
-
-
-return urlInfo.host&&urlInfo.origin||null;
-}catch(e){
-return null;
-}
-};
-
-
-
-
-
-
-URL.getURLDisplayName=function getURLDisplayName(url,options){
-return Util.getURLDisplayName(new URL(url),options);
-};
-
-
-
-
-
-
-URL.elideDataURI=function elideDataURI(url){
-try{
-const parsed=new URL(url);
-return parsed.protocol==='data:'?url.slice(0,100):url;
-}catch(e){
-return url;
-}
-};
-
-
-
-
-function rewriteChromeInternalUrl(url){
-if(!url||!url.startsWith('chrome://'))return url;
-
-
-if(url.endsWith('/'))url=url.replace(/\/$/,'');
-return url.replace(/^chrome:\/\/chrome\//,'chrome://');
-}
-
-
-
-
-
-
-
-URL.equalWithExcludedFragments=function(url1,url2){
-[url1,url2]=[url1,url2].map(rewriteChromeInternalUrl);
-try{
-url1=new URL(url1);
-url1.hash='';
-
-url2=new URL(url2);
-url2.hash='';
-
-return url1.href===url2.href;
-}catch(e){
-return false;
-}
-};
-
-module.exports=URL;
-
-},{"../report/v2/renderer/util.js":43,"whatwg-url":52}],42:[function(require,module,exports){
+},{}],47:[function(require,module,exports){
 (function(global){
 
 
@@ -20114,7 +23440,7 @@
 }();
 
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{"chrome-devtools-frontend/front_end/bindings/TempFile.js":93,"chrome-devtools-frontend/front_end/common/Color.js":94,"chrome-devtools-frontend/front_end/common/Object.js":95,"chrome-devtools-frontend/front_end/common/ParsedURL.js":96,"chrome-devtools-frontend/front_end/common/ResourceType.js":97,"chrome-devtools-frontend/front_end/common/SegmentedRange.js":98,"chrome-devtools-frontend/front_end/common/TextRange.js":99,"chrome-devtools-frontend/front_end/common/TextUtils.js":100,"chrome-devtools-frontend/front_end/common/UIString.js":101,"chrome-devtools-frontend/front_end/components_lazy/FilmStripModel.js":102,"chrome-devtools-frontend/front_end/platform/utilities.js":103,"chrome-devtools-frontend/front_end/sdk/CPUProfileDataModel.js":104,"chrome-devtools-frontend/front_end/sdk/CSSMatchedStyles.js":105,"chrome-devtools-frontend/front_end/sdk/CSSMedia.js":106,"chrome-devtools-frontend/front_end/sdk/CSSMetadata.js":107,"chrome-devtools-frontend/front_end/sdk/CSSProperty.js":108,"chrome-devtools-frontend/front_end/sdk/CSSRule.js":109,"chrome-devtools-frontend/front_end/sdk/CSSStyleDeclaration.js":110,"chrome-devtools-frontend/front_end/sdk/NetworkManager.js":111,"chrome-devtools-frontend/front_end/sdk/NetworkRequest.js":112,"chrome-devtools-frontend/front_end/sdk/ProfileTreeModel.js":113,"chrome-devtools-frontend/front_end/sdk/Target.js":114,"chrome-devtools-frontend/front_end/sdk/TargetManager.js":115,"chrome-devtools-frontend/front_end/sdk/TracingModel.js":116,"chrome-devtools-frontend/front_end/timeline/TimelineLoader.js":117,"chrome-devtools-frontend/front_end/timeline/TimelineTreeView.js":118,"chrome-devtools-frontend/front_end/timeline/TimelineUIUtils.js":119,"chrome-devtools-frontend/front_end/timeline_model/LayerTreeModel.js":120,"chrome-devtools-frontend/front_end/timeline_model/TimelineFrameModel.js":121,"chrome-devtools-frontend/front_end/timeline_model/TimelineIRModel.js":122,"chrome-devtools-frontend/front_end/timeline_model/TimelineJSProfile.js":123,"chrome-devtools-frontend/front_end/timeline_model/TimelineModel.js":124,"chrome-devtools-frontend/front_end/timeline_model/TimelineProfileTree.js":125,"chrome-devtools-frontend/front_end/ui_lazy/SortableDataGrid.js":126}],43:[function(require,module,exports){
+},{"chrome-devtools-frontend/front_end/bindings/TempFile.js":99,"chrome-devtools-frontend/front_end/common/Color.js":100,"chrome-devtools-frontend/front_end/common/Object.js":101,"chrome-devtools-frontend/front_end/common/ParsedURL.js":102,"chrome-devtools-frontend/front_end/common/ResourceType.js":103,"chrome-devtools-frontend/front_end/common/SegmentedRange.js":104,"chrome-devtools-frontend/front_end/common/TextRange.js":105,"chrome-devtools-frontend/front_end/common/TextUtils.js":106,"chrome-devtools-frontend/front_end/common/UIString.js":107,"chrome-devtools-frontend/front_end/components_lazy/FilmStripModel.js":108,"chrome-devtools-frontend/front_end/platform/utilities.js":109,"chrome-devtools-frontend/front_end/sdk/CPUProfileDataModel.js":110,"chrome-devtools-frontend/front_end/sdk/CSSMatchedStyles.js":111,"chrome-devtools-frontend/front_end/sdk/CSSMedia.js":112,"chrome-devtools-frontend/front_end/sdk/CSSMetadata.js":113,"chrome-devtools-frontend/front_end/sdk/CSSProperty.js":114,"chrome-devtools-frontend/front_end/sdk/CSSRule.js":115,"chrome-devtools-frontend/front_end/sdk/CSSStyleDeclaration.js":116,"chrome-devtools-frontend/front_end/sdk/NetworkManager.js":117,"chrome-devtools-frontend/front_end/sdk/NetworkRequest.js":118,"chrome-devtools-frontend/front_end/sdk/ProfileTreeModel.js":119,"chrome-devtools-frontend/front_end/sdk/Target.js":120,"chrome-devtools-frontend/front_end/sdk/TargetManager.js":121,"chrome-devtools-frontend/front_end/sdk/TracingModel.js":122,"chrome-devtools-frontend/front_end/timeline/TimelineLoader.js":123,"chrome-devtools-frontend/front_end/timeline/TimelineTreeView.js":124,"chrome-devtools-frontend/front_end/timeline/TimelineUIUtils.js":125,"chrome-devtools-frontend/front_end/timeline_model/LayerTreeModel.js":126,"chrome-devtools-frontend/front_end/timeline_model/TimelineFrameModel.js":127,"chrome-devtools-frontend/front_end/timeline_model/TimelineIRModel.js":128,"chrome-devtools-frontend/front_end/timeline_model/TimelineJSProfile.js":129,"chrome-devtools-frontend/front_end/timeline_model/TimelineModel.js":130,"chrome-devtools-frontend/front_end/timeline_model/TimelineProfileTree.js":131,"chrome-devtools-frontend/front_end/ui_lazy/SortableDataGrid.js":132}],48:[function(require,module,exports){
 
 
 
@@ -20126,20 +23452,106 @@
 
 const ELLIPSIS='\u2026';
 const NBSP='\xa0';
+const PASS_THRESHOLD=0.75;
 
 const RATINGS={
-PASS:{label:'pass',minScore:75},
-AVERAGE:{label:'average',minScore:45},
-FAIL:{label:'fail'}};
+PASS:{label:'pass',minScore:PASS_THRESHOLD},
+AVERAGE:{label:'average',minScore:0.45},
+FAIL:{label:'fail'},
+ERROR:{label:'error'}};
 
 
 class Util{
+static get PASS_THRESHOLD(){
+return PASS_THRESHOLD;
+}
+
+static get MS_DISPLAY_VALUE(){
+return`%10d${NBSP}ms`;
+}
 
 
 
 
 
-static calculateRating(score){
+static formatDisplayValue(displayValue){
+if(typeof displayValue==='string')return displayValue;
+if(!displayValue)return'';
+
+const replacementRegex=/%([0-9]*(\.[0-9]+)?d|s)/;
+const template=displayValue[0];
+if(typeof template!=='string'){
+
+
+return'UNKNOWN';
+}
+
+let output=template;
+for(const replacement of displayValue.slice(1)){
+if(!replacementRegex.test(output)){
+
+console.warn('Too many replacements given');
+break;
+}
+
+output=output.replace(replacementRegex,match=>{
+const granularity=Number(match.match(/[0-9.]+/))||1;
+return match==='%s'?
+replacement.toLocaleString():
+(Math.round(Number(replacement)/granularity)*granularity).toLocaleString();
+});
+}
+
+if(replacementRegex.test(output)){
+
+console.warn('Not enough replacements given');
+}
+
+return output;
+}
+
+
+
+
+
+
+
+
+static showAsPassed(audit){
+switch(audit.scoreDisplayMode){
+case'manual':
+case'not-applicable':
+return true;
+case'error':
+case'informative':
+return false;
+case'numeric':
+case'binary':
+default:
+
+
+
+return Number(audit.score)===1;}
+
+}
+
+
+
+
+
+
+
+static calculateRating(score,scoreDisplayMode){
+
+if(scoreDisplayMode==='manual'||scoreDisplayMode==='not-applicable'){
+return RATINGS.PASS.label;
+}else if(scoreDisplayMode==='error'){
+return RATINGS.ERROR.label;
+}else if(score===null){
+return RATINGS.FAIL.label;
+}
+
+
 let rating=RATINGS.FAIL.label;
 if(score>=RATINGS.PASS.minScore){
 rating=RATINGS.PASS.label;
@@ -20155,8 +23567,9 @@
 
 
 
-static formatNumber(number,decimalPlaces=1){
-return number.toLocaleString(undefined,{maximumFractionDigits:decimalPlaces});
+static formatNumber(number,granularity=0.1){
+const coarseValue=Math.round(number/granularity)*granularity;
+return coarseValue.toLocaleString();
 }
 
 
@@ -20164,8 +23577,8 @@
 
 
 
-static formatBytesToKB(size,decimalPlaces=2){
-const kbs=(size/1024).toLocaleString(undefined,{maximumFractionDigits:decimalPlaces});
+static formatBytesToKB(size,granularity=0.1){
+const kbs=(Math.round(size/1024/granularity)*granularity).toLocaleString();
 return`${kbs}${NBSP}KB`;
 }
 
@@ -20184,6 +23597,16 @@
 
 
 
+static formatSeconds(ms,granularity=0.1){
+const coarseTime=Math.round(ms/1000/granularity)*granularity;
+return`${coarseTime.toLocaleString()}${NBSP}s`;
+}
+
+
+
+
+
+
 static formatDateTime(date){
 const options={
 month:'short',day:'numeric',year:'numeric',
@@ -20205,10 +23628,10 @@
 
 
 
-
-static formatDuration(timeInSeconds,zeroLabel='None'){
-if(timeInSeconds===0){
-return zeroLabel;
+static formatDuration(timeInMilliseconds){
+let timeInSeconds=timeInMilliseconds/1000;
+if(Math.round(timeInSeconds)===0){
+return'None';
 }
 
 
@@ -20238,7 +23661,9 @@
 
 
 static getURLDisplayName(parsedUrl,options){
-options=options||{};
+
+options=options||{numPathParts:undefined,preserveQuery:undefined,
+preserveHost:undefined};
 const numPathParts=options.numPathParts!==undefined?options.numPathParts:2;
 const preserveQuery=options.preserveQuery!==undefined?options.preserveQuery:true;
 const preserveHost=options.preserveHost||false;
@@ -20307,7 +23732,11 @@
 
 static parseURL(url){
 const parsedUrl=new URL(url);
-return{file:Util.getURLDisplayName(parsedUrl),hostname:parsedUrl.hostname};
+return{
+file:Util.getURLDisplayName(parsedUrl),
+hostname:parsedUrl.hostname,
+origin:parsedUrl.origin};
+
 }
 
 
@@ -20317,19 +23746,88 @@
 
 static chainDuration(startTime,endTime){
 return Util.formatNumber((endTime-startTime)*1000);
+}
+
+
+
+
+
+static getEnvironmentDisplayValues(settings){
+const emulationDesc=Util.getEmulationDescriptions(settings);
+
+return[
+{
+name:'Device',
+description:emulationDesc.deviceEmulation},
+
+{
+name:'Network throttling',
+description:emulationDesc.networkThrottling},
+
+{
+name:'CPU throttling',
+description:emulationDesc.cpuThrottling}];
+
+
+}
+
+
+
+
+
+static getEmulationDescriptions(settings){
+let cpuThrottling;
+let networkThrottling;
+let summary;
+
+const throttling=settings.throttling;
+
+switch(settings.throttlingMethod){
+case'provided':
+cpuThrottling='Provided by environment';
+networkThrottling='Provided by environment';
+summary='No throttling applied';
+break;
+case'devtools':{
+const{cpuSlowdownMultiplier,requestLatencyMs}=throttling;
+cpuThrottling=`${Util.formatNumber(cpuSlowdownMultiplier)}x slowdown (DevTools)`;
+networkThrottling=`${Util.formatNumber(requestLatencyMs)}${NBSP}ms HTTP RTT, `+
+`${Util.formatNumber(throttling.downloadThroughputKbps)}${NBSP}Kbps down, `+
+`${Util.formatNumber(throttling.uploadThroughputKbps)}${NBSP}Kbps up (DevTools)`;
+summary='Throttled Fast 3G network';
+break;
+}
+case'simulate':{
+const{cpuSlowdownMultiplier,rttMs,throughputKbps}=throttling;
+cpuThrottling=`${Util.formatNumber(cpuSlowdownMultiplier)}x slowdown (Simulated)`;
+networkThrottling=`${Util.formatNumber(rttMs)}${NBSP}ms TCP RTT, `+
+`${Util.formatNumber(throughputKbps)}${NBSP}Kbps throughput (Simulated)`;
+summary='Simulated Fast 3G network';
+break;
+}
+default:
+cpuThrottling='Unknown';
+networkThrottling='Unknown';
+summary='Unknown';}
+
+
+const deviceEmulation=settings.disableDeviceEmulation?'No emulation':'Emulated Nexus 5X';
+return{
+deviceEmulation,
+cpuThrottling,
+networkThrottling,
+summary:`${deviceEmulation}, ${summary}`};
+
 }}
 
 
 if(typeof module!=='undefined'&&module.exports){
 module.exports=Util;
 }else{
-
 self.Util=Util;
 }
 
-},{}],44:[function(require,module,exports){
-(function(process,__dirname){
-
+},{}],49:[function(require,module,exports){
 
 
 
@@ -20337,46 +23835,179 @@
 
 'use strict';
 
+const htmlReportAssets=require('./html/html-report-assets');
+
+class ReportGenerator{
+
+
+
+
+
+
+static replaceStrings(source,replacements){
+if(replacements.length===0){
+return source;
+}
+
+const firstReplacement=replacements[0];
+const nextReplacements=replacements.slice(1);
+return source.
+split(firstReplacement.search).
+map(part=>ReportGenerator.replaceStrings(part,nextReplacements)).
+join(firstReplacement.replacement);
+}
+
+
+
+
+
+
+static generateReportHtml(lhr){
+const sanitizedJson=JSON.stringify(lhr).
+replace(/</g,'\\u003c').
+replace(/\u2028/g,'\\u2028').
+replace(/\u2029/g,'\\u2029');
+const sanitizedJavascript=htmlReportAssets.REPORT_JAVASCRIPT.replace(/<\//g,'\\u003c/');
+
+return ReportGenerator.replaceStrings(htmlReportAssets.REPORT_TEMPLATE,[
+{search:'%%LIGHTHOUSE_JSON%%',replacement:sanitizedJson},
+{search:'%%LIGHTHOUSE_JAVASCRIPT%%',replacement:sanitizedJavascript},
+{search:'/*%%LIGHTHOUSE_CSS%%*/',replacement:htmlReportAssets.REPORT_CSS},
+{search:'%%LIGHTHOUSE_TEMPLATES%%',replacement:htmlReportAssets.REPORT_TEMPLATES}]);
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+static generateReportCSV(lhr){
+
+
+const CRLF='\r\n';
+const separator=',';
+
+const escape=value=>`"${value.replace(/"/g,'""')}"`;
+
+
+const header=['category','name','title','type','score'];
+const table=Object.values(lhr.categories).map(category=>{
+return category.auditRefs.map(auditRef=>{
+const audit=lhr.audits[auditRef.id];
+
+const numericScore=audit.score===null?-1:audit.score;
+return[category.title,audit.id,audit.title,audit.scoreDisplayMode,numericScore].
+map(value=>value.toString()).
+map(escape);
+});
+});
+
+
+const flattedTable=[].concat(...table);
+return[header,...flattedTable].map(row=>row.join(separator)).join(CRLF);
+}
+
+
+
+
+
+
+
+static generateReport(lhr,outputModes){
+const outputAsArray=Array.isArray(outputModes);
+if(typeof outputModes==='string')outputModes=[outputModes];
+
+const output=outputModes.map(outputMode=>{
+
+if(outputMode==='html'){
+return ReportGenerator.generateReportHtml(lhr);
+}
+
+if(outputMode==='csv'){
+return ReportGenerator.generateReportCSV(lhr);
+}
+
+if(outputMode==='json'){
+return JSON.stringify(lhr,null,2);
+}
+
+throw new Error('Invalid output mode: '+outputMode);
+});
+
+return outputAsArray?output:output[0];
+}}
+
+
+module.exports=ReportGenerator;
+
+},{"./html/html-report-assets":58}],50:[function(require,module,exports){
+(function(process,__dirname){
+
+
+
+
+
+'use strict';
+
+const isDeepEqual=require('lodash.isequal');
 const Driver=require('./gather/driver.js');
 const GatherRunner=require('./gather/gather-runner');
 const ReportScoring=require('./scoring');
 const Audit=require('./audits/audit');
-const emulation=require('./lib/emulation');
 const log=require('lighthouse-logger');
 const assetSaver=require('./lib/asset-saver');
 
 const path=require('path');
 const URL=require('./lib/url-shim');
 const Sentry=require('./lib/sentry');
+const generateReport=require('./report/report-generator').generateReport;
 
-const basePath=path.join(process.cwd(),'latest-run');
+const Connection=require('./gather/connections/connection.js');
 
 class Runner{
-static run(connection,opts){
 
-opts.flags=opts.flags||{};
+
+
+
+
+static async run(connection,opts){
+try{
+const startTime=Date.now();
+const settings=opts.config.settings;
+
+
+
 
 
 const lighthouseRunWarnings=[];
 
 
-opts.initialUrl=opts.url;
-if(typeof opts.initialUrl!=='string'||opts.initialUrl.length===0){
-return Promise.reject(new Error('You must provide a url to the runner'));
+const rawRequestedUrl=opts.url;
+if(typeof rawRequestedUrl!=='string'||rawRequestedUrl.length===0){
+throw new Error(`You must provide a url to the runner. '${rawRequestedUrl}' provided.`);
 }
 
 let parsedURL;
 try{
 parsedURL=new URL(opts.url);
 }catch(e){
-const err=new Error('The url provided should have a proper protocol and hostname.');
-return Promise.reject(err);
+throw new Error('The url provided should have a proper protocol and hostname.');
 }
 
 const sentryContext=Sentry.getContext();
+
 Sentry.captureBreadcrumb({
 message:'Run started',
 category:'lifecycle',
+
 data:sentryContext&&sentryContext.extra});
 
 
@@ -20387,10 +24018,7 @@
 }
 
 
-opts.url=parsedURL.href;
-
-
-let run=Promise.resolve();
+const requestedUrl=parsedURL.href;
 
 
 
@@ -20398,66 +24026,74 @@
 
 
 
-if(opts.flags.auditMode&&!opts.flags.gatherMode){
-run=run.then(_=>Runner._loadArtifactsFromDisk());
-}else if(opts.config.artifacts){
-run=run.then(_=>opts.config.artifacts);
+let artifacts;
+if(settings.auditMode&&!settings.gatherMode){
+
+const path=Runner._getArtifactsPath(settings);
+artifacts=await assetSaver.loadArtifacts(path);
 }else{
-run=run.then(_=>Runner._gatherArtifactsFromBrowser(opts,connection));
+artifacts=await Runner._gatherArtifactsFromBrowser(requestedUrl,opts,connection);
+
+if(settings.gatherMode){
+const path=Runner._getArtifactsPath(settings);
+await assetSaver.saveArtifacts(artifacts,path);
+}
 }
 
 
-if(opts.flags.gatherMode&&!opts.flags.auditMode)return run;
+if(settings.gatherMode&&!settings.auditMode)return;
 
 
-run=run.then(artifacts=>Runner._runAudits(opts,artifacts));
+if(!opts.config.audits){
+throw new Error('No audits to evaluate.');
+}
+const auditResults=await Runner._runAudits(settings,opts.config.audits,artifacts);
 
 
-run=run.then(runResults=>{
 log.log('status','Generating results...');
 
-if(runResults.artifacts.LighthouseRunWarnings){
-lighthouseRunWarnings.push(...runResults.artifacts.LighthouseRunWarnings);
+if(artifacts.LighthouseRunWarnings){
+lighthouseRunWarnings.push(...artifacts.LighthouseRunWarnings);
 }
 
 
+
+const lighthouseVersion=require('../package.json').version;
+
+
 const resultsById={};
-for(const audit of runResults.auditResults)resultsById[audit.name]=audit;
-
-let report;
-if(opts.config.categories){
-report=Runner._scoreAndCategorize(opts,resultsById);
+for(const audit of auditResults){
+resultsById[audit.id]=audit;
 }
 
-return{
-userAgent:runResults.artifacts.UserAgent,
-lighthouseVersion:require('../package').version,
-generatedTime:new Date().toJSON(),
-initialUrl:opts.initialUrl,
-url:opts.url,
+
+let categories={};
+if(opts.config.categories){
+categories=ReportScoring.scoreAllCategories(opts.config.categories,resultsById);
+}
+
+
+const lhr={
+userAgent:artifacts.UserAgent,
+lighthouseVersion,
+fetchTime:artifacts.fetchTime,
+requestedUrl:requestedUrl,
+finalUrl:artifacts.URL.finalUrl,
 runWarnings:lighthouseRunWarnings,
 audits:resultsById,
-artifacts:runResults.artifacts,
-runtimeConfig:Runner.getRuntimeConfig(opts.flags),
-score:report?report.score:0,
-reportCategories:report?report.categories:[],
-reportGroups:opts.config.groups};
+configSettings:settings,
+categories,
+categoryGroups:opts.config.groups,
+timing:{total:Date.now()-startTime}};
 
-}).catch(err=>{
-return Sentry.captureException(err,{level:'fatal'}).then(()=>{
+
+const report=generateReport(lhr,settings.output);
+return{lhr,artifacts,report};
+}catch(err){
+
+await Sentry.captureException(err,{level:'fatal'});
 throw err;
-});
-});
-
-return run;
 }
-
-
-
-
-
-static _loadArtifactsFromDisk(){
-return assetSaver.loadArtifacts(basePath);
 }
 
 
@@ -20466,27 +24102,20 @@
 
 
 
-static _gatherArtifactsFromBrowser(opts,connection){
-if(!opts.config.passes){
-return Promise.reject(new Error('No browser artifacts are either provided or requested.'));
+
+static async _gatherArtifactsFromBrowser(requestedUrl,runnerOpts,connection){
+if(!runnerOpts.config.passes){
+throw new Error('No browser artifacts are either provided or requested.');
 }
 
-opts.driver=opts.driverMock||new Driver(connection);
-return GatherRunner.run(opts.config.passes,opts).then(artifacts=>{
-const flags=opts.flags;
-const shouldSave=flags.gatherMode;
-const p=shouldSave?Runner._saveArtifacts(artifacts):Promise.resolve();
-return p.then(_=>artifacts);
-});
-}
+const driver=runnerOpts.driverMock||new Driver(connection);
+const gatherOpts={
+driver,
+requestedUrl,
+settings:runnerOpts.config.settings};
 
-
-
-
-
-
-static _saveArtifacts(artifacts){
-return assetSaver.saveArtifacts(artifacts,basePath);
+const artifacts=await GatherRunner.run(runnerOpts.config.passes,gatherOpts);
+return artifacts;
 }
 
 
@@ -20495,35 +24124,30 @@
 
 
 
-static _runAudits(opts,artifacts){
-if(!opts.config.audits){
-return Promise.reject(new Error('No audits to evaluate.'));
-}
 
+static async _runAudits(settings,audits,artifacts){
 log.log('status','Analyzing and running audits...');
-artifacts=Object.assign(Runner.instantiateComputedArtifacts(),
-artifacts||opts.config.artifacts);
+artifacts=Object.assign({},Runner.instantiateComputedArtifacts(),artifacts);
+
+if(artifacts.settings){
+const overrides={gatherMode:undefined,auditMode:undefined,output:undefined};
+const normalizedGatherSettings=Object.assign({},artifacts.settings,overrides);
+const normalizedAuditSettings=Object.assign({},settings,overrides);
+
+
+if(!isDeepEqual(normalizedGatherSettings,normalizedAuditSettings)){
+throw new Error('Cannot change settings between gathering and auditing');
+}
+}
 
 
 const auditResults=[];
-let promise=Promise.resolve();
-for(const audit of opts.config.audits){
-promise=promise.then(_=>{
-return Runner._runAudit(audit,artifacts).then(ret=>auditResults.push(ret));
-});
-}
-return promise.then(_=>{
-const runResults={artifacts,auditResults};
-return runResults;
-});
+for(const auditDefn of audits){
+const auditResult=await Runner._runAudit(auditDefn,artifacts,settings);
+auditResults.push(auditResult);
 }
 
-
-
-
-
-static _scoreAndCategorize(opts,resultsById){
-return ReportScoring.scoreAllCategories(opts.config,resultsById);
+return auditResults;
 }
 
 
@@ -20534,15 +24158,17 @@
 
 
 
-static _runAudit(audit,artifacts){
+
+static async _runAudit(auditDefn,artifacts,settings){
+const audit=auditDefn.implementation;
 const status=`Evaluating: ${audit.meta.description}`;
 
-return Promise.resolve().then(_=>{
 log.log('status',status);
-
+let auditResult;
+try{
 
 for(const artifactName of audit.meta.requiredArtifacts){
-const noArtifact=typeof artifacts[artifactName]==='undefined';
+const noArtifact=artifacts[artifactName]===undefined;
 
 
 
@@ -20557,7 +24183,11 @@
 
 
 if(artifacts[artifactName]instanceof Error){
+
+
 const artifactError=artifacts[artifactName];
+
+
 Sentry.captureException(artifactError,{
 tags:{gatherer:artifactName},
 level:'error'});
@@ -20569,30 +24199,33 @@
 
 const error=new Error(
 `Required ${artifactName} gatherer encountered an error: ${artifactError.message}`);
+
 error.expected=true;
 throw error;
 }
 }
 
-return audit.audit(artifacts);
 
-}).then(auditResult=>Audit.generateAuditResult(audit,auditResult)).
-catch(err=>{
+const auditOptions=Object.assign({},audit.defaultOptions,auditDefn.options);
+const product=await audit.audit(artifacts,{options:auditOptions,settings:settings});
+auditResult=Audit.generateAuditResult(audit,product);
+}catch(err){
 log.warn(audit.meta.name,`Caught exception: ${err.message}`);
 if(err.fatal){
 throw err;
 }
 
+
 Sentry.captureException(err,{tags:{audit:audit.meta.name},level:'error'});
 
-const debugString=err.friendlyMessage?
+const errorMessage=err.friendlyMessage?
 `${err.friendlyMessage} (${err.message})`:
 `Audit error: ${err.message}`;
-return Audit.generateErrorAuditResult(audit,debugString);
-}).then(result=>{
+auditResult=Audit.generateErrorAuditResult(audit,errorMessage);
+}
+
 log.verbose('statusEnd',status);
-return result;
-});
+return auditResult;
 }
 
 
@@ -20610,15 +24243,15 @@
 
 
 const fileList=[
-...[".DS_Store","accessibility","audit.js","bootup-time.js","byte-efficiency","cache-start-url.js","consistently-interactive.js","content-width.js","critical-request-chains.js","deprecations.js","dobetterweb","errors-in-console.js","estimated-input-latency.js","first-interactive.js","first-meaningful-paint.js","font-display.js","image-aspect-ratio.js","is-on-https.js","load-fast-enough-for-pwa.js","mainthread-work-breakdown.js","manifest-short-name-length.js","manual","mixed-content.js","multi-check-audit.js","predictive-perf.js","redirects-http.js","redirects.js","screenshot-thumbnails.js","seo","service-worker.js","speed-index-metric.js","splash-screen.js","themed-omnibox.js","time-to-first-byte.js","user-timings.js","uses-rel-preload.js","viewport.js","violation-audit.js","webapp-install-banner.js","without-javascript.js","works-offline.js"],
-...["appcache-manifest.js","dom-size.js","external-anchors-use-rel-noopener.js","geolocation-on-start.js","link-blocking-first-paint.js","no-document-write.js","no-mutation-events.js","no-vulnerable-libraries.js","no-websql.js","notification-on-start.js","password-inputs-can-be-pasted-into.js","script-blocking-first-paint.js","uses-http2.js","uses-passive-event-listeners.js"].map(f=>`dobetterweb/${f}`),
-...["canonical.js","font-size.js","hreflang.js","http-status-code.js","is-crawlable.js","link-text.js","manual","meta-description.js","plugins.js"].map(f=>`seo/${f}`),
+...["accessibility","audit.js","bootup-time.js","byte-efficiency","content-width.js","critical-request-chains.js","deprecations.js","dobetterweb","errors-in-console.js","estimated-input-latency.js","first-contentful-paint.js","first-cpu-idle.js","first-meaningful-paint.js","font-display.js","image-aspect-ratio.js","interactive.js","is-on-https.js","load-fast-enough-for-pwa.js","mainthread-work-breakdown.js","manifest-short-name-length.js","manual","metrics.js","mixed-content.js","multi-check-audit.js","network-requests.js","predictive-perf.js","redirects-http.js","redirects.js","screenshot-thumbnails.js","seo","service-worker.js","speed-index.js","splash-screen.js","themed-omnibox.js","time-to-first-byte.js","user-timings.js","uses-rel-preconnect.js","uses-rel-preload.js","viewport.js","violation-audit.js","webapp-install-banner.js","without-javascript.js","works-offline.js"],
+...["appcache-manifest.js","dom-size.js","external-anchors-use-rel-noopener.js","geolocation-on-start.js","no-document-write.js","no-mutation-events.js","no-vulnerable-libraries.js","no-websql.js","notification-on-start.js","password-inputs-can-be-pasted-into.js","uses-http2.js","uses-passive-event-listeners.js"].map(f=>`dobetterweb/${f}`),
+...["canonical.js","font-size.js","hreflang.js","http-status-code.js","is-crawlable.js","link-text.js","manual","meta-description.js","plugins.js","robots-txt.js"].map(f=>`seo/${f}`),
 ...["mobile-friendly.js","structured-data.js"].map(f=>`seo/manual/${f}`),
 ...["accesskeys.js","aria-allowed-attr.js","aria-required-attr.js","aria-required-children.js","aria-required-parent.js","aria-roles.js","aria-valid-attr-value.js","aria-valid-attr.js","audio-caption.js","axe-audit.js","button-name.js","bypass.js","color-contrast.js","definition-list.js","dlitem.js","document-title.js","duplicate-id.js","frame-title.js","html-has-lang.js","html-lang-valid.js","image-alt.js","input-image-alt.js","label.js","layout-table.js","link-name.js","list.js","listitem.js","manual","meta-refresh.js","meta-viewport.js","object-alt.js","tabindex.js","td-headers-attr.js","th-has-data-cells.js","valid-lang.js","video-caption.js","video-description.js"].
 map(f=>`accessibility/${f}`),
 ...["custom-controls-labels.js","custom-controls-roles.js","focus-traps.js","focusable-controls.js","heading-levels.js","logical-tab-order.js","managed-focus.js","offscreen-content-hidden.js","use-landmarks.js","visual-order-follows-dom.js"].
 map(f=>`accessibility/manual/${f}`),
-...["byte-efficiency-audit.js","offscreen-images.js","total-byte-weight.js","unminified-css.js","unminified-javascript.js","unused-css-rules.js","unused-javascript.js","uses-long-cache-ttl.js","uses-optimized-images.js","uses-request-compression.js","uses-responsive-images.js","uses-webp-images.js"].
+...["byte-efficiency-audit.js","efficient-animated-content.js","offscreen-images.js","render-blocking-resources.js","total-byte-weight.js","unminified-css.js","unminified-javascript.js","unused-css-rules.js","unused-javascript.js","uses-long-cache-ttl.js","uses-optimized-images.js","uses-responsive-images.js","uses-text-compression.js","uses-webp-images.js"].
 map(f=>`byte-efficiency/${f}`),
 ...["manual-audit.js","pwa-cross-browser.js","pwa-each-page-has-url.js","pwa-page-transitions.js"].map(f=>`manual/${f}`)];
 
@@ -20633,8 +24266,8 @@
 
 static getGathererList(){
 const fileList=[
-...["accessibility.js","cache-contents.js","chrome-console-messages.js","css-usage.js","dobetterweb","fonts.js","gatherer.js","html-without-javascript.js","http-redirect.js","image-usage.js","js-usage.js","manifest.js","mixed-content.js","offline.js","runtime-exceptions.js","scripts.js","seo","service-worker.js","start-url.js","theme-color.js","url.js","viewport-dimensions.js","viewport.js"],
-...["canonical.js","crawlable-links.js","embedded-content.js","font-size.js","hreflang.js","meta-description.js","meta-robots.js"].map(f=>`seo/${f}`),
+...["accessibility.js","cache-contents.js","chrome-console-messages.js","css-usage.js","dobetterweb","fonts.js","gatherer.js","html-without-javascript.js","http-redirect.js","image-usage.js","js-usage.js","manifest.js","mixed-content.js","offline.js","runtime-exceptions.js","scripts.js","seo","service-worker.js","start-url.js","theme-color.js","viewport-dimensions.js","viewport.js"],
+...["canonical.js","crawlable-links.js","embedded-content.js","font-size.js","hreflang.js","meta-description.js","meta-robots.js","robots-txt.js"].map(f=>`seo/${f}`),
 ...["all-event-listeners.js","anchors-with-no-rel-noopener.js","appcache.js","domstats.js","js-libraries.js","optimized-images.js","password-inputs-with-prevented-paste.js","response-compression.js","tags-blocking-first-paint.js","websql.js"].
 map(f=>`dobetterweb/${f}`)];
 
@@ -20644,15 +24277,30 @@
 
 
 
+
+static getComputedGathererList(){
+const filenamesToSkip=[
+'computed-artifact.js',
+'metrics',
+'metrics/lantern-metric.js',
+'metrics/metric.js'];
+
+
+const fileList=[
+...["computed-artifact.js","critical-request-chains.js","dtm-model.js","load-simulator.js","main-resource.js","manifest-values.js","metrics","network-analysis.js","network-records.js","network-throughput.js","page-dependency-graph.js","pushed-requests.js","screenshots.js","speedline.js","trace-of-tab.js"],
+...["estimated-input-latency.js","first-contentful-paint.js","first-cpu-idle.js","first-meaningful-paint.js","interactive.js","lantern-estimated-input-latency.js","lantern-first-contentful-paint.js","lantern-first-cpu-idle.js","lantern-first-meaningful-paint.js","lantern-interactive.js","lantern-metric.js","lantern-speed-index.js","metric.js","speed-index.js"].map(f=>`metrics/${f}`)];
+
+
+return fileList.filter(f=>/\.js$/.test(f)&&!filenamesToSkip.includes(f)).sort();
+}
+
+
+
+
+
 static instantiateComputedArtifacts(){
 const computedArtifacts={};
-const filenamesToSkip=[
-'computed-artifact.js'];
-
-
-["computed-artifact.js","critical-request-chains.js","dtm-model.js","first-interactive.js","main-resource.js","manifest-values.js","network-records.js","network-throughput.js","page-dependency-graph.js","pushed-requests.js","screenshots.js","speedline.js","trace-of-tab.js"].forEach(function(filename){
-if(filenamesToSkip.includes(filename))return;
-
+Runner.getComputedGathererList().forEach(function(filename){
 
 filename=filename.replace(/\.js$/,'');
 const ArtifactClass=require('./gather/computed/'+filename);
@@ -20660,6 +24308,7 @@
 
 computedArtifacts['request'+artifact.name]=artifact.request.bind(artifact);
 });
+
 return computedArtifacts;
 }
 
@@ -20714,38 +24363,21 @@
 
 
 
-static getRuntimeConfig(flags){
-const emulationDesc=emulation.getEmulationDesc();
-const environment=[
-{
-name:'Device Emulation',
-enabled:!flags.disableDeviceEmulation,
-description:emulationDesc['deviceEmulation']},
-
-{
-name:'Network Throttling',
-enabled:!flags.disableNetworkThrottling,
-description:emulationDesc['networkThrottling']},
-
-{
-name:'CPU Throttling',
-enabled:!flags.disableCpuThrottling,
-description:emulationDesc['cpuThrottling']}];
+static _getArtifactsPath(settings){
+const{auditMode,gatherMode}=settings;
 
 
+if(typeof auditMode==='string')return path.resolve(process.cwd(),auditMode);
+if(typeof gatherMode==='string')return path.resolve(process.cwd(),gatherMode);
 
-return{
-environment,
-blockedUrlPatterns:flags.blockedUrlPatterns||[],
-extraHeaders:flags.extraHeaders||{}};
-
+return path.join(process.cwd(),'latest-run');
 }}
 
 
 module.exports=Runner;
 
 }).call(this,require('_process'),"/../lighthouse-core");
-},{"../package":146,"./audits/audit":2,"./gather/driver.js":14,"./gather/gather-runner":15,"./lib/asset-saver":18,"./lib/emulation":27,"./lib/sentry":33,"./lib/url-shim":41,"./scoring":45,"_process":71,"lighthouse-logger":137,"path":69}],45:[function(require,module,exports){
+},{"../package.json":154,"./audits/audit":2,"./gather/connections/connection.js":14,"./gather/driver.js":17,"./gather/gather-runner":18,"./lib/asset-saver":21,"./lib/sentry":39,"./lib/url-shim":"url","./report/report-generator":49,"./scoring":51,"_process":77,"lighthouse-logger":143,"lodash.isequal":144,"path":75}],51:[function(require,module,exports){
 
 
 
@@ -20754,6 +24386,15 @@
 
 'use strict';
 
+const Audit=require('./audits/audit');
+
+
+
+
+
+
+const clampTo2Decimals=val=>Math.round(val*100)/100;
+
 class ReportScoring{
 
 
@@ -20761,16 +24402,25 @@
 
 
 static arithmeticMean(items){
-const results=items.reduce((result,item)=>{
-const score=Number(item.score)||0;
-const weight=Number(item.weight)||0;
+
+items=items.filter(item=>item.weight>0);
+
+if(items.some(item=>item.score===null))return null;
+
+const results=items.reduce(
+(result,item)=>{
+const score=item.score;
+const weight=item.weight;
+
 return{
 weight:result.weight+weight,
 sum:result.sum+score*weight};
 
-},{weight:0,sum:0});
+},
+{weight:0,sum:0});
 
-return results.sum/results.weight||0;
+
+return clampTo2Decimals(results.sum/results.weight||0);
 }
 
 
@@ -20779,43 +24429,49 @@
 
 
 
-static scoreAllCategories(config,resultsByAuditId){
-const categories=Object.keys(config.categories).map(categoryId=>{
-const category=config.categories[categoryId];
-category.id=categoryId;
+static scoreAllCategories(configCategories,resultsByAuditId){
+const scoredCategories={};
 
-const audits=category.audits.map(audit=>{
-const result=resultsByAuditId[audit.id];
+for(const[categoryId,configCategory]of Object.entries(configCategories)){
 
-let auditScore=Number(result.score)||0;
-if(typeof result.score==='boolean'){
-auditScore=result.score?100:0;
+const auditRefs=configCategory.auditRefs.map(configMember=>{
+const member={...configMember};
+
+
+
+
+
+const result=resultsByAuditId[member.id];
+if(result.scoreDisplayMode===Audit.SCORING_MODES.NOT_APPLICABLE||
+result.scoreDisplayMode===Audit.SCORING_MODES.INFORMATIVE||
+result.scoreDisplayMode===Audit.SCORING_MODES.MANUAL){
+member.weight=0;
 }
 
-
-
-
-if(result.notApplicable){
-auditScore=100;
-audit.weight=0;
-result.informative=true;
-}
-
-return Object.assign({},audit,{result,score:auditScore});
+return member;
 });
 
-const categoryScore=ReportScoring.arithmeticMean(audits);
-return Object.assign({},category,{audits,score:categoryScore});
-});
+const scores=auditRefs.map(auditRef=>({
+score:resultsByAuditId[auditRef.id].score,
+weight:auditRef.weight}));
 
-const overallScore=ReportScoring.arithmeticMean(categories);
-return{score:overallScore,categories};
+const score=ReportScoring.arithmeticMean(scores);
+
+scoredCategories[categoryId]={
+...configCategory,
+auditRefs,
+id:categoryId,
+score};
+
+}
+
+return scoredCategories;
 }}
 
 
 module.exports=ReportScoring;
 
-},{}],46:[function(require,module,exports){
+},{"./audits/audit":2}],52:[function(require,module,exports){
 
 
 
@@ -20826,8 +24482,7 @@
 const RawProtocol=require('../../../lighthouse-core/gather/connections/raw');
 const Runner=require('../../../lighthouse-core/runner');
 const Config=require('../../../lighthouse-core/config/config');
-const defaultConfig=require('../../../lighthouse-core/config/default.js');
-const fastConfig=require('../../../lighthouse-core/config/fast-config.js');
+const defaultConfig=require('../../../lighthouse-core/config/default-config.js');
 const log=require('lighthouse-logger');
 
 
@@ -20840,10 +24495,10 @@
 window.runLighthouseForConnection=function(
 connection,url,options,categoryIDs,
 updateBadgeFn=function(){}){
-const config=options&&options.fastMode?new Config(fastConfig):new Config({
+const config=new Config({
 extends:'lighthouse:default',
-settings:{onlyCategories:categoryIDs}});
-
+settings:{onlyCategories:categoryIDs}},
+options.flags);
 
 
 const runOptions=Object.assign({},options,{url,config});
@@ -20886,7 +24541,7 @@
 log.events.addListener('status',listenCallback);
 };
 
-},{"../../../lighthouse-core/config/config":7,"../../../lighthouse-core/config/default.js":8,"../../../lighthouse-core/config/fast-config.js":9,"../../../lighthouse-core/gather/connections/raw":12,"../../../lighthouse-core/runner":44,"lighthouse-logger":137}],47:[function(require,module,exports){
+},{"../../../lighthouse-core/config/config":7,"../../../lighthouse-core/config/default-config.js":9,"../../../lighthouse-core/gather/connections/raw":15,"../../../lighthouse-core/runner":50,"lighthouse-logger":143}],53:[function(require,module,exports){
 (function(global){
 'use strict';
 
@@ -21380,7 +25035,7 @@
 };
 
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{"util/":91}],48:[function(require,module,exports){
+},{"util/":97}],54:[function(require,module,exports){
 'use strict';
 
 exports.byteLength=byteLength;
@@ -21397,68 +25052,102 @@
 revLookup[code.charCodeAt(i)]=i;
 }
 
+
+
 revLookup['-'.charCodeAt(0)]=62;
 revLookup['_'.charCodeAt(0)]=63;
 
-function placeHoldersCount(b64){
+function getLens(b64){
 var len=b64.length;
+
 if(len%4>0){
 throw new Error('Invalid string. Length must be a multiple of 4');
 }
 
 
 
+var validLen=b64.indexOf('=');
+if(validLen===-1)validLen=len;
 
+var placeHoldersLen=validLen===len?
+0:
+4-validLen%4;
 
-
-return b64[len-2]==='='?2:b64[len-1]==='='?1:0;
+return[validLen,placeHoldersLen];
 }
 
-function byteLength(b64){
 
-return b64.length*3/4-placeHoldersCount(b64);
+function byteLength(b64){
+var lens=getLens(b64);
+var validLen=lens[0];
+var placeHoldersLen=lens[1];
+return(validLen+placeHoldersLen)*3/4-placeHoldersLen;
+}
+
+function _byteLength(b64,validLen,placeHoldersLen){
+return(validLen+placeHoldersLen)*3/4-placeHoldersLen;
 }
 
 function toByteArray(b64){
-var i,j,l,tmp,placeHolders,arr;
-var len=b64.length;
-placeHolders=placeHoldersCount(b64);
+var tmp;
+var lens=getLens(b64);
+var validLen=lens[0];
+var placeHoldersLen=lens[1];
 
-arr=new Arr(len*3/4-placeHolders);
+var arr=new Arr(_byteLength(b64,validLen,placeHoldersLen));
+
+var curByte=0;
 
 
-l=placeHolders>0?len-4:len;
+var len=placeHoldersLen>0?
+validLen-4:
+validLen;
 
-var L=0;
-
-for(i=0,j=0;i<l;i+=4,j+=3){
-tmp=revLookup[b64.charCodeAt(i)]<<18|revLookup[b64.charCodeAt(i+1)]<<12|revLookup[b64.charCodeAt(i+2)]<<6|revLookup[b64.charCodeAt(i+3)];
-arr[L++]=tmp>>16&0xFF;
-arr[L++]=tmp>>8&0xFF;
-arr[L++]=tmp&0xFF;
+for(var i=0;i<len;i+=4){
+tmp=
+revLookup[b64.charCodeAt(i)]<<18|
+revLookup[b64.charCodeAt(i+1)]<<12|
+revLookup[b64.charCodeAt(i+2)]<<6|
+revLookup[b64.charCodeAt(i+3)];
+arr[curByte++]=tmp>>16&0xFF;
+arr[curByte++]=tmp>>8&0xFF;
+arr[curByte++]=tmp&0xFF;
 }
 
-if(placeHolders===2){
-tmp=revLookup[b64.charCodeAt(i)]<<2|revLookup[b64.charCodeAt(i+1)]>>4;
-arr[L++]=tmp&0xFF;
-}else if(placeHolders===1){
-tmp=revLookup[b64.charCodeAt(i)]<<10|revLookup[b64.charCodeAt(i+1)]<<4|revLookup[b64.charCodeAt(i+2)]>>2;
-arr[L++]=tmp>>8&0xFF;
-arr[L++]=tmp&0xFF;
+if(placeHoldersLen===2){
+tmp=
+revLookup[b64.charCodeAt(i)]<<2|
+revLookup[b64.charCodeAt(i+1)]>>4;
+arr[curByte++]=tmp&0xFF;
+}
+
+if(placeHoldersLen===1){
+tmp=
+revLookup[b64.charCodeAt(i)]<<10|
+revLookup[b64.charCodeAt(i+1)]<<4|
+revLookup[b64.charCodeAt(i+2)]>>2;
+arr[curByte++]=tmp>>8&0xFF;
+arr[curByte++]=tmp&0xFF;
 }
 
 return arr;
 }
 
 function tripletToBase64(num){
-return lookup[num>>18&0x3F]+lookup[num>>12&0x3F]+lookup[num>>6&0x3F]+lookup[num&0x3F];
+return lookup[num>>18&0x3F]+
+lookup[num>>12&0x3F]+
+lookup[num>>6&0x3F]+
+lookup[num&0x3F];
 }
 
 function encodeChunk(uint8,start,end){
 var tmp;
 var output=[];
 for(var i=start;i<end;i+=3){
-tmp=(uint8[i]<<16)+(uint8[i+1]<<8)+uint8[i+2];
+tmp=
+(uint8[i]<<16&0xFF0000)+(
+uint8[i+1]<<8&0xFF00)+(
+uint8[i+2]&0xFF);
 output.push(tripletToBase64(tmp));
 }
 return output.join('');
@@ -21468,40 +25157,47 @@
 var tmp;
 var len=uint8.length;
 var extraBytes=len%3;
-var output='';
 var parts=[];
 var maxChunkLength=16383;
 
 
 for(var i=0,len2=len-extraBytes;i<len2;i+=maxChunkLength){
-parts.push(encodeChunk(uint8,i,i+maxChunkLength>len2?len2:i+maxChunkLength));
+parts.push(encodeChunk(
+uint8,i,i+maxChunkLength>len2?len2:i+maxChunkLength));
+
 }
 
 
 if(extraBytes===1){
 tmp=uint8[len-1];
-output+=lookup[tmp>>2];
-output+=lookup[tmp<<4&0x3F];
-output+='==';
+parts.push(
+lookup[tmp>>2]+
+lookup[tmp<<4&0x3F]+
+'==');
+
 }else if(extraBytes===2){
 tmp=(uint8[len-2]<<8)+uint8[len-1];
-output+=lookup[tmp>>10];
-output+=lookup[tmp>>4&0x3F];
-output+=lookup[tmp<<2&0x3F];
-output+='=';
-}
+parts.push(
+lookup[tmp>>10]+
+lookup[tmp>>4&0x3F]+
+lookup[tmp<<2&0x3F]+
+'=');
 
-parts.push(output);
+}
 
 return parts.join('');
 }
 
-},{}],49:[function(require,module,exports){
+},{}],55:[function(require,module,exports){
 
-},{}],50:[function(require,module,exports){
+},{}],56:[function(require,module,exports){
 (function(process,Buffer){
-var msg=require('pako/lib/zlib/messages');
-var zstream=require('pako/lib/zlib/zstream');
+'use strict';
+
+
+var assert=require('assert');
+
+var Zstream=require('pako/lib/zlib/zstream');
 var zlib_deflate=require('pako/lib/zlib/deflate.js');
 var zlib_inflate=require('pako/lib/zlib/inflate.js');
 var constants=require('pako/lib/zlib/constants');
@@ -21520,178 +25216,30 @@
 exports.INFLATERAW=6;
 exports.UNZIP=7;
 
+var GZIP_HEADER_ID1=0x1f;
+var GZIP_HEADER_ID2=0x8b;
+
 
 
 
 function Zlib(mode){
-if(mode<exports.DEFLATE||mode>exports.UNZIP)
-throw new TypeError("Bad argument");
+if(typeof mode!=='number'||mode<exports.DEFLATE||mode>exports.UNZIP){
+throw new TypeError('Bad argument');
+}
 
-this.mode=mode;
+this.dictionary=null;
+this.err=0;
+this.flush=0;
 this.init_done=false;
-this.write_in_progress=false;
-this.pending_close=false;
-this.windowBits=0;
 this.level=0;
 this.memLevel=0;
+this.mode=mode;
 this.strategy=0;
-this.dictionary=null;
-}
-
-Zlib.prototype.init=function(windowBits,level,memLevel,strategy,dictionary){
-this.windowBits=windowBits;
-this.level=level;
-this.memLevel=memLevel;
-this.strategy=strategy;
-
-
-if(this.mode===exports.GZIP||this.mode===exports.GUNZIP)
-this.windowBits+=16;
-
-if(this.mode===exports.UNZIP)
-this.windowBits+=32;
-
-if(this.mode===exports.DEFLATERAW||this.mode===exports.INFLATERAW)
-this.windowBits=-this.windowBits;
-
-this.strm=new zstream();
-
-switch(this.mode){
-case exports.DEFLATE:
-case exports.GZIP:
-case exports.DEFLATERAW:
-var status=zlib_deflate.deflateInit2(
-this.strm,
-this.level,
-exports.Z_DEFLATED,
-this.windowBits,
-this.memLevel,
-this.strategy);
-
-break;
-case exports.INFLATE:
-case exports.GUNZIP:
-case exports.INFLATERAW:
-case exports.UNZIP:
-var status=zlib_inflate.inflateInit2(
-this.strm,
-this.windowBits);
-
-break;
-default:
-throw new Error("Unknown mode "+this.mode);}
-
-
-if(status!==exports.Z_OK){
-this._error(status);
-return;
-}
-
+this.windowBits=0;
 this.write_in_progress=false;
-this.init_done=true;
-};
-
-Zlib.prototype.params=function(){
-throw new Error("deflateParams Not supported");
-};
-
-Zlib.prototype._writeCheck=function(){
-if(!this.init_done)
-throw new Error("write before init");
-
-if(this.mode===exports.NONE)
-throw new Error("already finalized");
-
-if(this.write_in_progress)
-throw new Error("write already in progress");
-
-if(this.pending_close)
-throw new Error("close is pending");
-};
-
-Zlib.prototype.write=function(flush,input,in_off,in_len,out,out_off,out_len){
-this._writeCheck();
-this.write_in_progress=true;
-
-var self=this;
-process.nextTick(function(){
-self.write_in_progress=false;
-var res=self._write(flush,input,in_off,in_len,out,out_off,out_len);
-self.callback(res[0],res[1]);
-
-if(self.pending_close)
-self.close();
-});
-
-return this;
-};
-
-
-function bufferSet(data,offset){
-for(var i=0;i<data.length;i++){
-this[offset+i]=data[i];
+this.pending_close=false;
+this.gzip_id_bytes_read=0;
 }
-}
-
-Zlib.prototype.writeSync=function(flush,input,in_off,in_len,out,out_off,out_len){
-this._writeCheck();
-return this._write(flush,input,in_off,in_len,out,out_off,out_len);
-};
-
-Zlib.prototype._write=function(flush,input,in_off,in_len,out,out_off,out_len){
-this.write_in_progress=true;
-
-if(flush!==exports.Z_NO_FLUSH&&
-flush!==exports.Z_PARTIAL_FLUSH&&
-flush!==exports.Z_SYNC_FLUSH&&
-flush!==exports.Z_FULL_FLUSH&&
-flush!==exports.Z_FINISH&&
-flush!==exports.Z_BLOCK){
-throw new Error("Invalid flush value");
-}
-
-if(input==null){
-input=new Buffer(0);
-in_len=0;
-in_off=0;
-}
-
-if(out._set)
-out.set=out._set;else
-
-out.set=bufferSet;
-
-var strm=this.strm;
-strm.avail_in=in_len;
-strm.input=input;
-strm.next_in=in_off;
-strm.avail_out=out_len;
-strm.output=out;
-strm.next_out=out_off;
-
-switch(this.mode){
-case exports.DEFLATE:
-case exports.GZIP:
-case exports.DEFLATERAW:
-var status=zlib_deflate.deflate(strm,flush);
-break;
-case exports.UNZIP:
-case exports.INFLATE:
-case exports.GUNZIP:
-case exports.INFLATERAW:
-var status=zlib_inflate.inflate(strm,flush);
-break;
-default:
-throw new Error("Unknown mode "+this.mode);}
-
-
-if(status!==exports.Z_STREAM_END&&status!==exports.Z_OK){
-this._error(status);
-}
-
-this.write_in_progress=false;
-return[strm.avail_in,strm.avail_out];
-};
 
 Zlib.prototype.close=function(){
 if(this.write_in_progress){
@@ -21701,71 +25249,370 @@
 
 this.pending_close=false;
 
+assert(this.init_done,'close before init');
+assert(this.mode<=exports.UNZIP);
+
 if(this.mode===exports.DEFLATE||this.mode===exports.GZIP||this.mode===exports.DEFLATERAW){
 zlib_deflate.deflateEnd(this.strm);
-}else{
+}else if(this.mode===exports.INFLATE||this.mode===exports.GUNZIP||this.mode===exports.INFLATERAW||this.mode===exports.UNZIP){
 zlib_inflate.inflateEnd(this.strm);
 }
 
 this.mode=exports.NONE;
+
+this.dictionary=null;
 };
 
-Zlib.prototype.reset=function(){
+Zlib.prototype.write=function(flush,input,in_off,in_len,out,out_off,out_len){
+return this._write(true,flush,input,in_off,in_len,out,out_off,out_len);
+};
+
+Zlib.prototype.writeSync=function(flush,input,in_off,in_len,out,out_off,out_len){
+return this._write(false,flush,input,in_off,in_len,out,out_off,out_len);
+};
+
+Zlib.prototype._write=function(async,flush,input,in_off,in_len,out,out_off,out_len){
+assert.equal(arguments.length,8);
+
+assert(this.init_done,'write before init');
+assert(this.mode!==exports.NONE,'already finalized');
+assert.equal(false,this.write_in_progress,'write already in progress');
+assert.equal(false,this.pending_close,'close is pending');
+
+this.write_in_progress=true;
+
+assert.equal(false,flush===undefined,'must provide flush value');
+
+this.write_in_progress=true;
+
+if(flush!==exports.Z_NO_FLUSH&&flush!==exports.Z_PARTIAL_FLUSH&&flush!==exports.Z_SYNC_FLUSH&&flush!==exports.Z_FULL_FLUSH&&flush!==exports.Z_FINISH&&flush!==exports.Z_BLOCK){
+throw new Error('Invalid flush value');
+}
+
+if(input==null){
+input=Buffer.alloc(0);
+in_len=0;
+in_off=0;
+}
+
+this.strm.avail_in=in_len;
+this.strm.input=input;
+this.strm.next_in=in_off;
+this.strm.avail_out=out_len;
+this.strm.output=out;
+this.strm.next_out=out_off;
+this.flush=flush;
+
+if(!async){
+
+this._process();
+
+if(this._checkError()){
+return this._afterSync();
+}
+return;
+}
+
+
+var self=this;
+process.nextTick(function(){
+self._process();
+self._after();
+});
+
+return this;
+};
+
+Zlib.prototype._afterSync=function(){
+var avail_out=this.strm.avail_out;
+var avail_in=this.strm.avail_in;
+
+this.write_in_progress=false;
+
+return[avail_in,avail_out];
+};
+
+Zlib.prototype._process=function(){
+var next_expected_header_byte=null;
+
+
+
+
 switch(this.mode){
 case exports.DEFLATE:
+case exports.GZIP:
 case exports.DEFLATERAW:
-var status=zlib_deflate.deflateReset(this.strm);
+this.err=zlib_deflate.deflate(this.strm,this.flush);
 break;
+case exports.UNZIP:
+if(this.strm.avail_in>0){
+next_expected_header_byte=this.strm.next_in;
+}
+
+switch(this.gzip_id_bytes_read){
+case 0:
+if(next_expected_header_byte===null){
+break;
+}
+
+if(this.strm.input[next_expected_header_byte]===GZIP_HEADER_ID1){
+this.gzip_id_bytes_read=1;
+next_expected_header_byte++;
+
+if(this.strm.avail_in===1){
+
+break;
+}
+}else{
+this.mode=exports.INFLATE;
+break;
+}
+
+
+case 1:
+if(next_expected_header_byte===null){
+break;
+}
+
+if(this.strm.input[next_expected_header_byte]===GZIP_HEADER_ID2){
+this.gzip_id_bytes_read=2;
+this.mode=exports.GUNZIP;
+}else{
+
+
+this.mode=exports.INFLATE;
+}
+
+break;
+default:
+throw new Error('invalid number of gzip magic number bytes read');}
+
+
+
 case exports.INFLATE:
+case exports.GUNZIP:
 case exports.INFLATERAW:
-var status=zlib_inflate.inflateReset(this.strm);
-break;}
+this.err=zlib_inflate.inflate(this.strm,this.flush);
 
 
-if(status!==exports.Z_OK){
-this._error(status);
+if(this.err===exports.Z_NEED_DICT&&this.dictionary){
+
+this.err=zlib_inflate.inflateSetDictionary(this.strm,this.dictionary);
+if(this.err===exports.Z_OK){
+
+this.err=zlib_inflate.inflate(this.strm,this.flush);
+}else if(this.err===exports.Z_DATA_ERROR){
+
+
+
+this.err=exports.Z_NEED_DICT;
+}
+}
+while(this.strm.avail_in>0&&this.mode===exports.GUNZIP&&this.err===exports.Z_STREAM_END&&this.strm.next_in[0]!==0x00){
+
+
+
+
+
+this.reset();
+this.err=zlib_inflate.inflate(this.strm,this.flush);
+}
+break;
+default:
+throw new Error('Unknown mode '+this.mode);}
+
+};
+
+Zlib.prototype._checkError=function(){
+
+switch(this.err){
+case exports.Z_OK:
+case exports.Z_BUF_ERROR:
+if(this.strm.avail_out!==0&&this.flush===exports.Z_FINISH){
+this._error('unexpected end of file');
+return false;
+}
+break;
+case exports.Z_STREAM_END:
+
+break;
+case exports.Z_NEED_DICT:
+if(this.dictionary==null){
+this._error('Missing dictionary');
+}else{
+this._error('Bad dictionary');
+}
+return false;
+default:
+
+this._error('Zlib error');
+return false;}
+
+
+return true;
+};
+
+Zlib.prototype._after=function(){
+if(!this._checkError()){
+return;
+}
+
+var avail_out=this.strm.avail_out;
+var avail_in=this.strm.avail_in;
+
+this.write_in_progress=false;
+
+
+this.callback(avail_in,avail_out);
+
+if(this.pending_close){
+this.close();
 }
 };
 
-Zlib.prototype._error=function(status){
-this.onerror(msg[status]+': '+this.strm.msg,status);
+Zlib.prototype._error=function(message){
+if(this.strm.msg){
+message=this.strm.msg;
+}
+this.onerror(message,this.err);
+
 
 this.write_in_progress=false;
-if(this.pending_close)
+if(this.pending_close){
 this.close();
+}
+};
+
+Zlib.prototype.init=function(windowBits,level,memLevel,strategy,dictionary){
+assert(arguments.length===4||arguments.length===5,'init(windowBits, level, memLevel, strategy, [dictionary])');
+
+assert(windowBits>=8&&windowBits<=15,'invalid windowBits');
+assert(level>=-1&&level<=9,'invalid compression level');
+
+assert(memLevel>=1&&memLevel<=9,'invalid memlevel');
+
+assert(strategy===exports.Z_FILTERED||strategy===exports.Z_HUFFMAN_ONLY||strategy===exports.Z_RLE||strategy===exports.Z_FIXED||strategy===exports.Z_DEFAULT_STRATEGY,'invalid strategy');
+
+this._init(level,windowBits,memLevel,strategy,dictionary);
+this._setDictionary();
+};
+
+Zlib.prototype.params=function(){
+throw new Error('deflateParams Not supported');
+};
+
+Zlib.prototype.reset=function(){
+this._reset();
+this._setDictionary();
+};
+
+Zlib.prototype._init=function(level,windowBits,memLevel,strategy,dictionary){
+this.level=level;
+this.windowBits=windowBits;
+this.memLevel=memLevel;
+this.strategy=strategy;
+
+this.flush=exports.Z_NO_FLUSH;
+
+this.err=exports.Z_OK;
+
+if(this.mode===exports.GZIP||this.mode===exports.GUNZIP){
+this.windowBits+=16;
+}
+
+if(this.mode===exports.UNZIP){
+this.windowBits+=32;
+}
+
+if(this.mode===exports.DEFLATERAW||this.mode===exports.INFLATERAW){
+this.windowBits=-1*this.windowBits;
+}
+
+this.strm=new Zstream();
+
+switch(this.mode){
+case exports.DEFLATE:
+case exports.GZIP:
+case exports.DEFLATERAW:
+this.err=zlib_deflate.deflateInit2(this.strm,this.level,exports.Z_DEFLATED,this.windowBits,this.memLevel,this.strategy);
+break;
+case exports.INFLATE:
+case exports.GUNZIP:
+case exports.INFLATERAW:
+case exports.UNZIP:
+this.err=zlib_inflate.inflateInit2(this.strm,this.windowBits);
+break;
+default:
+throw new Error('Unknown mode '+this.mode);}
+
+
+if(this.err!==exports.Z_OK){
+this._error('Init error');
+}
+
+this.dictionary=dictionary;
+
+this.write_in_progress=false;
+this.init_done=true;
+};
+
+Zlib.prototype._setDictionary=function(){
+if(this.dictionary==null){
+return;
+}
+
+this.err=exports.Z_OK;
+
+switch(this.mode){
+case exports.DEFLATE:
+case exports.DEFLATERAW:
+this.err=zlib_deflate.deflateSetDictionary(this.strm,this.dictionary);
+break;
+default:
+break;}
+
+
+if(this.err!==exports.Z_OK){
+this._error('Failed to set dictionary');
+}
+};
+
+Zlib.prototype._reset=function(){
+this.err=exports.Z_OK;
+
+switch(this.mode){
+case exports.DEFLATE:
+case exports.DEFLATERAW:
+case exports.GZIP:
+this.err=zlib_deflate.deflateReset(this.strm);
+break;
+case exports.INFLATE:
+case exports.INFLATERAW:
+case exports.GUNZIP:
+this.err=zlib_inflate.inflateReset(this.strm);
+break;
+default:
+break;}
+
+
+if(this.err!==exports.Z_OK){
+this._error('Failed to reset stream');
+}
 };
 
 exports.Zlib=Zlib;
-
 }).call(this,require('_process'),require("buffer").Buffer);
-},{"_process":71,"buffer":54,"pako/lib/zlib/constants":63,"pako/lib/zlib/deflate.js":65,"pako/lib/zlib/inflate.js":52,"pako/lib/zlib/messages":66,"pako/lib/zlib/zstream":68}],51:[function(require,module,exports){
-(function(process,Buffer){
+},{"_process":77,"assert":53,"buffer":60,"pako/lib/zlib/constants":69,"pako/lib/zlib/deflate.js":71,"pako/lib/zlib/inflate.js":58,"pako/lib/zlib/zstream":74}],57:[function(require,module,exports){
+(function(process){
+'use strict';
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-var Transform=require('_stream_transform');
-
+var Buffer=require('buffer').Buffer;
+var Transform=require('stream').Transform;
 var binding=require('./binding');
 var util=require('util');
 var assert=require('assert').ok;
+var kMaxLength=require('buffer').kMaxLength;
+var kRangeErrorMessage='Cannot create final Buffer. It would be larger '+'than 0x'+kMaxLength.toString(16)+' bytes';
 
 
 
@@ -21789,12 +25636,18 @@
 binding.Z_DEFAULT_LEVEL=binding.Z_DEFAULT_COMPRESSION;
 
 
-Object.keys(binding).forEach(function(k){
-if(k.match(/^Z/))exports[k]=binding[k];
-});
+var bkeys=Object.keys(binding);
+for(var bk=0;bk<bkeys.length;bk++){
+var bkey=bkeys[bk];
+if(bkey.match(/^Z/)){
+Object.defineProperty(exports,bkey,{
+enumerable:true,value:binding[bkey],writable:false});
+
+}
+}
 
 
-exports.codes={
+var codes={
 Z_OK:binding.Z_OK,
 Z_STREAM_END:binding.Z_STREAM_END,
 Z_NEED_DICT:binding.Z_NEED_DICT,
@@ -21806,9 +25659,15 @@
 Z_VERSION_ERROR:binding.Z_VERSION_ERROR};
 
 
-Object.keys(exports.codes).forEach(function(k){
-exports.codes[exports.codes[k]]=k;
-});
+var ckeys=Object.keys(codes);
+for(var ck=0;ck<ckeys.length;ck++){
+var ckey=ckeys[ck];
+codes[codes[ckey]]=ckey;
+}
+
+Object.defineProperty(exports,'codes',{
+enumerable:true,value:Object.freeze(codes),writable:false});
+
 
 exports.Deflate=Deflate;
 exports.Inflate=Inflate;
@@ -21848,7 +25707,6 @@
 
 
 
-
 exports.deflate=function(buffer,opts,callback){
 if(typeof opts==='function'){
 callback=opts;
@@ -21959,20 +25817,27 @@
 }
 
 function onEnd(){
-var buf=Buffer.concat(buffers,nread);
+var buf;
+var err=null;
+
+if(nread>=kMaxLength){
+err=new RangeError(kRangeErrorMessage);
+}else{
+buf=Buffer.concat(buffers,nread);
+}
+
 buffers=[];
-callback(null,buf);
 engine.close();
+callback(err,buf);
 }
 }
 
 function zlibBufferSync(engine,buffer){
-if(typeof buffer==='string')
-buffer=new Buffer(buffer);
-if(!Buffer.isBuffer(buffer))
-throw new TypeError('Not a string or buffer');
+if(typeof buffer==='string')buffer=Buffer.from(buffer);
 
-var flushFlag=binding.Z_FINISH;
+if(!Buffer.isBuffer(buffer))throw new TypeError('Not a string or buffer');
+
+var flushFlag=engine._finishFlushFlag;
 
 return engine._processChunk(buffer,flushFlag);
 }
@@ -21990,8 +25855,6 @@
 }
 
 
-
-
 function Gzip(opts){
 if(!(this instanceof Gzip))return new Gzip(opts);
 Zlib.call(this,opts,binding.GZIP);
@@ -22003,8 +25866,6 @@
 }
 
 
-
-
 function DeflateRaw(opts){
 if(!(this instanceof DeflateRaw))return new DeflateRaw(opts);
 Zlib.call(this,opts,binding.DEFLATERAW);
@@ -22016,12 +25877,14 @@
 }
 
 
-
 function Unzip(opts){
 if(!(this instanceof Unzip))return new Unzip(opts);
 Zlib.call(this,opts,binding.UNZIP);
 }
 
+function isValidFlushFlag(flag){
+return flag===binding.Z_NO_FLUSH||flag===binding.Z_PARTIAL_FLUSH||flag===binding.Z_SYNC_FLUSH||flag===binding.Z_FULL_FLUSH||flag===binding.Z_FINISH||flag===binding.Z_BLOCK;
+}
 
 
 
@@ -22029,57 +25892,49 @@
 
 
 function Zlib(opts,mode){
+var _this=this;
+
 this._opts=opts=opts||{};
 this._chunkSize=opts.chunkSize||exports.Z_DEFAULT_CHUNK;
 
 Transform.call(this,opts);
 
-if(opts.flush){
-if(opts.flush!==binding.Z_NO_FLUSH&&
-opts.flush!==binding.Z_PARTIAL_FLUSH&&
-opts.flush!==binding.Z_SYNC_FLUSH&&
-opts.flush!==binding.Z_FULL_FLUSH&&
-opts.flush!==binding.Z_FINISH&&
-opts.flush!==binding.Z_BLOCK){
+if(opts.flush&&!isValidFlushFlag(opts.flush)){
 throw new Error('Invalid flush flag: '+opts.flush);
 }
+if(opts.finishFlush&&!isValidFlushFlag(opts.finishFlush)){
+throw new Error('Invalid flush flag: '+opts.finishFlush);
 }
+
 this._flushFlag=opts.flush||binding.Z_NO_FLUSH;
+this._finishFlushFlag=typeof opts.finishFlush!=='undefined'?opts.finishFlush:binding.Z_FINISH;
 
 if(opts.chunkSize){
-if(opts.chunkSize<exports.Z_MIN_CHUNK||
-opts.chunkSize>exports.Z_MAX_CHUNK){
+if(opts.chunkSize<exports.Z_MIN_CHUNK||opts.chunkSize>exports.Z_MAX_CHUNK){
 throw new Error('Invalid chunk size: '+opts.chunkSize);
 }
 }
 
 if(opts.windowBits){
-if(opts.windowBits<exports.Z_MIN_WINDOWBITS||
-opts.windowBits>exports.Z_MAX_WINDOWBITS){
+if(opts.windowBits<exports.Z_MIN_WINDOWBITS||opts.windowBits>exports.Z_MAX_WINDOWBITS){
 throw new Error('Invalid windowBits: '+opts.windowBits);
 }
 }
 
 if(opts.level){
-if(opts.level<exports.Z_MIN_LEVEL||
-opts.level>exports.Z_MAX_LEVEL){
+if(opts.level<exports.Z_MIN_LEVEL||opts.level>exports.Z_MAX_LEVEL){
 throw new Error('Invalid compression level: '+opts.level);
 }
 }
 
 if(opts.memLevel){
-if(opts.memLevel<exports.Z_MIN_MEMLEVEL||
-opts.memLevel>exports.Z_MAX_MEMLEVEL){
+if(opts.memLevel<exports.Z_MIN_MEMLEVEL||opts.memLevel>exports.Z_MAX_MEMLEVEL){
 throw new Error('Invalid memLevel: '+opts.memLevel);
 }
 }
 
 if(opts.strategy){
-if(opts.strategy!=exports.Z_FILTERED&&
-opts.strategy!=exports.Z_HUFFMAN_ONLY&&
-opts.strategy!=exports.Z_RLE&&
-opts.strategy!=exports.Z_FIXED&&
-opts.strategy!=exports.Z_DEFAULT_STRATEGY){
+if(opts.strategy!=exports.Z_FILTERED&&opts.strategy!=exports.Z_HUFFMAN_ONLY&&opts.strategy!=exports.Z_RLE&&opts.strategy!=exports.Z_FIXED&&opts.strategy!=exports.Z_DEFAULT_STRATEGY){
 throw new Error('Invalid strategy: '+opts.strategy);
 }
 }
@@ -22090,14 +25945,14 @@
 }
 }
 
-this._binding=new binding.Zlib(mode);
+this._handle=new binding.Zlib(mode);
 
 var self=this;
 this._hadError=false;
-this._binding.onerror=function(message,errno){
+this._handle.onerror=function(message,errno){
 
 
-self._binding=null;
+_close(self);
 self._hadError=true;
 
 var error=new Error(message);
@@ -22112,40 +25967,39 @@
 var strategy=exports.Z_DEFAULT_STRATEGY;
 if(typeof opts.strategy==='number')strategy=opts.strategy;
 
-this._binding.init(opts.windowBits||exports.Z_DEFAULT_WINDOWBITS,
-level,
-opts.memLevel||exports.Z_DEFAULT_MEMLEVEL,
-strategy,
-opts.dictionary);
+this._handle.init(opts.windowBits||exports.Z_DEFAULT_WINDOWBITS,level,opts.memLevel||exports.Z_DEFAULT_MEMLEVEL,strategy,opts.dictionary);
 
-this._buffer=new Buffer(this._chunkSize);
+this._buffer=Buffer.allocUnsafe(this._chunkSize);
 this._offset=0;
-this._closed=false;
 this._level=level;
 this._strategy=strategy;
 
 this.once('end',this.close);
+
+Object.defineProperty(this,'_closed',{
+get:function(){
+return!_this._handle;
+},
+configurable:true,
+enumerable:true});
+
 }
 
 util.inherits(Zlib,Transform);
 
 Zlib.prototype.params=function(level,strategy,callback){
-if(level<exports.Z_MIN_LEVEL||
-level>exports.Z_MAX_LEVEL){
+if(level<exports.Z_MIN_LEVEL||level>exports.Z_MAX_LEVEL){
 throw new RangeError('Invalid compression level: '+level);
 }
-if(strategy!=exports.Z_FILTERED&&
-strategy!=exports.Z_HUFFMAN_ONLY&&
-strategy!=exports.Z_RLE&&
-strategy!=exports.Z_FIXED&&
-strategy!=exports.Z_DEFAULT_STRATEGY){
+if(strategy!=exports.Z_FILTERED&&strategy!=exports.Z_HUFFMAN_ONLY&&strategy!=exports.Z_RLE&&strategy!=exports.Z_FIXED&&strategy!=exports.Z_DEFAULT_STRATEGY){
 throw new TypeError('Invalid strategy: '+strategy);
 }
 
 if(this._level!==level||this._strategy!==strategy){
 var self=this;
 this.flush(binding.Z_SYNC_FLUSH,function(){
-self._binding.params(level,strategy);
+assert(self._handle,'zlib binding closed');
+self._handle.params(level,strategy);
 if(!self._hadError){
 self._level=level;
 self._strategy=strategy;
@@ -22158,73 +26012,77 @@
 };
 
 Zlib.prototype.reset=function(){
-return this._binding.reset();
+assert(this._handle,'zlib binding closed');
+return this._handle.reset();
 };
 
 
 
 Zlib.prototype._flush=function(callback){
-this._transform(new Buffer(0),'',callback);
+this._transform(Buffer.alloc(0),'',callback);
 };
 
 Zlib.prototype.flush=function(kind,callback){
+var _this2=this;
+
 var ws=this._writableState;
 
-if(typeof kind==='function'||kind===void 0&&!callback){
+if(typeof kind==='function'||kind===undefined&&!callback){
 callback=kind;
 kind=binding.Z_FULL_FLUSH;
 }
 
 if(ws.ended){
-if(callback)
-process.nextTick(callback);
+if(callback)process.nextTick(callback);
 }else if(ws.ending){
-if(callback)
-this.once('end',callback);
+if(callback)this.once('end',callback);
 }else if(ws.needDrain){
-var self=this;
+if(callback){
 this.once('drain',function(){
-self.flush(callback);
+return _this2.flush(kind,callback);
 });
+}
 }else{
 this._flushFlag=kind;
-this.write(new Buffer(0),'',callback);
+this.write(Buffer.alloc(0),'',callback);
 }
 };
 
 Zlib.prototype.close=function(callback){
-if(callback)
-process.nextTick(callback);
-
-if(this._closed)
-return;
-
-this._closed=true;
-
-this._binding.close();
-
-var self=this;
-process.nextTick(function(){
-self.emit('close');
-});
+_close(this,callback);
+process.nextTick(emitCloseNT,this);
 };
 
+function _close(engine,callback){
+if(callback)process.nextTick(callback);
+
+
+if(!engine._handle)return;
+
+engine._handle.close();
+engine._handle=null;
+}
+
+function emitCloseNT(self){
+self.emit('close');
+}
+
 Zlib.prototype._transform=function(chunk,encoding,cb){
 var flushFlag;
 var ws=this._writableState;
 var ending=ws.ending||ws.ended;
 var last=ending&&(!chunk||ws.length===chunk.length);
 
-if(!chunk===null&&!Buffer.isBuffer(chunk))
-return cb(new Error('invalid input'));
+if(chunk!==null&&!Buffer.isBuffer(chunk))return cb(new Error('invalid input'));
+
+if(!this._handle)return cb(new Error('zlib binding closed'));
 
 
 
 
 
-if(last)
-flushFlag=binding.Z_FINISH;else
-{
+
+if(last)flushFlag=this._finishFlushFlag;else{
 flushFlag=this._flushFlag;
 
 
@@ -22233,7 +26091,6 @@
 }
 }
 
-var self=this;
 this._processChunk(chunk,flushFlag,cb);
 };
 
@@ -22255,9 +26112,9 @@
 error=er;
 });
 
+assert(this._handle,'zlib binding closed');
 do{
-var res=this._binding.writeSync(flushFlag,
-chunk,
+var res=this._handle.writeSync(flushFlag,chunk,
 inOff,
 availInBefore,
 this._buffer,
@@ -22269,14 +26126,19 @@
 throw error;
 }
 
+if(nread>=kMaxLength){
+_close(this);
+throw new RangeError(kRangeErrorMessage);
+}
+
 var buf=Buffer.concat(buffers,nread);
-this.close();
+_close(this);
 
 return buf;
 }
 
-var req=this._binding.write(flushFlag,
-chunk,
+assert(this._handle,'zlib binding closed');
+var req=this._handle.write(flushFlag,chunk,
 inOff,
 availInBefore,
 this._buffer,
@@ -22287,8 +26149,17 @@
 req.callback=callback;
 
 function callback(availInAfter,availOutAfter){
-if(self._hadError)
-return;
+
+
+
+
+
+if(this){
+this.buffer=null;
+this.callback=null;
+}
+
+if(self._hadError)return;
 
 var have=availOutBefore-availOutAfter;
 assert(have>=0,'have should not go down');
@@ -22309,7 +26180,7 @@
 if(availOutAfter===0||self._offset>=self._chunkSize){
 availOutBefore=self._chunkSize;
 self._offset=0;
-self._buffer=new Buffer(self._chunkSize);
+self._buffer=Buffer.allocUnsafe(self._chunkSize);
 }
 
 if(availOutAfter===0){
@@ -22320,23 +26191,15 @@
 inOff+=availInBefore-availInAfter;
 availInBefore=availInAfter;
 
-if(!async)
-return true;
+if(!async)return true;
 
-var newReq=self._binding.write(flushFlag,
-chunk,
-inOff,
-availInBefore,
-self._buffer,
-self._offset,
-self._chunkSize);
+var newReq=self._handle.write(flushFlag,chunk,inOff,availInBefore,self._buffer,self._offset,self._chunkSize);
 newReq.callback=callback;
 newReq.buffer=chunk;
 return;
 }
 
-if(!async)
-return false;
+if(!async)return false;
 
 
 cb();
@@ -22350,11 +26213,10 @@
 util.inherits(DeflateRaw,Zlib);
 util.inherits(InflateRaw,Zlib);
 util.inherits(Unzip,Zlib);
-
-}).call(this,require('_process'),require("buffer").Buffer);
-},{"./binding":50,"_process":71,"_stream_transform":84,"assert":47,"buffer":54,"util":91}],52:[function(require,module,exports){
-arguments[4][49][0].apply(exports,arguments);
-},{"dup":49}],53:[function(require,module,exports){
+}).call(this,require('_process'));
+},{"./binding":56,"_process":77,"assert":53,"buffer":60,"stream":92,"util":97}],58:[function(require,module,exports){
+arguments[4][55][0].apply(exports,arguments);
+},{"dup":55}],59:[function(require,module,exports){
 (function(global){
 'use strict';
 
@@ -22466,8 +26328,7 @@
 };
 
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{"buffer":54}],54:[function(require,module,exports){
-(function(global){
+},{"buffer":60}],60:[function(require,module,exports){
 
 
 
@@ -22480,12 +26341,13 @@
 
 var base64=require('base64-js');
 var ieee754=require('ieee754');
-var isArray=require('isarray');
 
 exports.Buffer=Buffer;
 exports.SlowBuffer=SlowBuffer;
 exports.INSPECT_MAX_BYTES=50;
 
+var K_MAX_LENGTH=0x7fffffff;
+exports.kMaxLength=K_MAX_LENGTH;
 
 
 
@@ -22501,59 +26363,53 @@
 
 
 
+Buffer.TYPED_ARRAY_SUPPORT=typedArraySupport();
 
+if(!Buffer.TYPED_ARRAY_SUPPORT&&typeof console!=='undefined'&&
+typeof console.error==='function'){
+console.error(
+'This browser lacks typed array (Uint8Array) support which is required by '+
+'`buffer` v5.x. Use `buffer` v4.x if you require old browser support.');
 
-
-
-
-
-
-
-
-Buffer.TYPED_ARRAY_SUPPORT=global.TYPED_ARRAY_SUPPORT!==undefined?
-global.TYPED_ARRAY_SUPPORT:
-typedArraySupport();
-
-
-
-
-exports.kMaxLength=kMaxLength();
+}
 
 function typedArraySupport(){
+
 try{
 var arr=new Uint8Array(1);
 arr.__proto__={__proto__:Uint8Array.prototype,foo:function(){return 42;}};
-return arr.foo()===42&&
-typeof arr.subarray==='function'&&
-arr.subarray(1,1).byteLength===0;
+return arr.foo()===42;
 }catch(e){
 return false;
 }
 }
 
-function kMaxLength(){
-return Buffer.TYPED_ARRAY_SUPPORT?
-0x7fffffff:
-0x3fffffff;
+Object.defineProperty(Buffer.prototype,'parent',{
+get:function(){
+if(!(this instanceof Buffer)){
+return undefined;
 }
+return this.buffer;
+}});
 
-function createBuffer(that,length){
-if(kMaxLength()<length){
+
+Object.defineProperty(Buffer.prototype,'offset',{
+get:function(){
+if(!(this instanceof Buffer)){
+return undefined;
+}
+return this.byteOffset;
+}});
+
+
+function createBuffer(length){
+if(length>K_MAX_LENGTH){
 throw new RangeError('Invalid typed array length');
 }
-if(Buffer.TYPED_ARRAY_SUPPORT){
 
-that=new Uint8Array(length);
-that.__proto__=Buffer.prototype;
-}else{
-
-if(that===null){
-that=new Buffer(length);
-}
-that.length=length;
-}
-
-return that;
+var buf=new Uint8Array(length);
+buf.__proto__=Buffer.prototype;
+return buf;
 }
 
 
@@ -22567,10 +26423,6 @@
 
 
 function Buffer(arg,encodingOrOffset,length){
-if(!Buffer.TYPED_ARRAY_SUPPORT&&!(this instanceof Buffer)){
-return new Buffer(arg,encodingOrOffset,length);
-}
-
 
 if(typeof arg==='number'){
 if(typeof encodingOrOffset==='string'){
@@ -22578,33 +26430,38 @@
 'If encoding is specified then the first argument must be a string');
 
 }
-return allocUnsafe(this,arg);
+return allocUnsafe(arg);
 }
-return from(this,arg,encodingOrOffset,length);
+return from(arg,encodingOrOffset,length);
+}
+
+
+if(typeof Symbol!=='undefined'&&Symbol.species&&
+Buffer[Symbol.species]===Buffer){
+Object.defineProperty(Buffer,Symbol.species,{
+value:null,
+configurable:true,
+enumerable:false,
+writable:false});
+
 }
 
 Buffer.poolSize=8192;
 
-
-Buffer._augment=function(arr){
-arr.__proto__=Buffer.prototype;
-return arr;
-};
-
-function from(that,value,encodingOrOffset,length){
+function from(value,encodingOrOffset,length){
 if(typeof value==='number'){
 throw new TypeError('"value" argument must not be a number');
 }
 
-if(typeof ArrayBuffer!=='undefined'&&value instanceof ArrayBuffer){
-return fromArrayBuffer(that,value,encodingOrOffset,length);
+if(isArrayBuffer(value)||value&&isArrayBuffer(value.buffer)){
+return fromArrayBuffer(value,encodingOrOffset,length);
 }
 
 if(typeof value==='string'){
-return fromString(that,value,encodingOrOffset);
+return fromString(value,encodingOrOffset);
 }
 
-return fromObject(that,value);
+return fromObject(value);
 }
 
 
@@ -22616,44 +26473,36 @@
 
 
 Buffer.from=function(value,encodingOrOffset,length){
-return from(null,value,encodingOrOffset,length);
+return from(value,encodingOrOffset,length);
 };
 
-if(Buffer.TYPED_ARRAY_SUPPORT){
+
+
 Buffer.prototype.__proto__=Uint8Array.prototype;
 Buffer.__proto__=Uint8Array;
-if(typeof Symbol!=='undefined'&&Symbol.species&&
-Buffer[Symbol.species]===Buffer){
-
-Object.defineProperty(Buffer,Symbol.species,{
-value:null,
-configurable:true});
-
-}
-}
 
 function assertSize(size){
 if(typeof size!=='number'){
-throw new TypeError('"size" argument must be a number');
+throw new TypeError('"size" argument must be of type number');
 }else if(size<0){
 throw new RangeError('"size" argument must not be negative');
 }
 }
 
-function alloc(that,size,fill,encoding){
+function alloc(size,fill,encoding){
 assertSize(size);
 if(size<=0){
-return createBuffer(that,size);
+return createBuffer(size);
 }
 if(fill!==undefined){
 
 
 
 return typeof encoding==='string'?
-createBuffer(that,size).fill(fill,encoding):
-createBuffer(that,size).fill(fill);
+createBuffer(size).fill(fill,encoding):
+createBuffer(size).fill(fill);
 }
-return createBuffer(that,size);
+return createBuffer(size);
 }
 
 
@@ -22661,132 +26510,118 @@
 
 
 Buffer.alloc=function(size,fill,encoding){
-return alloc(null,size,fill,encoding);
+return alloc(size,fill,encoding);
 };
 
-function allocUnsafe(that,size){
+function allocUnsafe(size){
 assertSize(size);
-that=createBuffer(that,size<0?0:checked(size)|0);
-if(!Buffer.TYPED_ARRAY_SUPPORT){
-for(var i=0;i<size;++i){
-that[i]=0;
-}
-}
-return that;
+return createBuffer(size<0?0:checked(size)|0);
 }
 
 
 
 
 Buffer.allocUnsafe=function(size){
-return allocUnsafe(null,size);
+return allocUnsafe(size);
 };
 
 
 
 Buffer.allocUnsafeSlow=function(size){
-return allocUnsafe(null,size);
+return allocUnsafe(size);
 };
 
-function fromString(that,string,encoding){
+function fromString(string,encoding){
 if(typeof encoding!=='string'||encoding===''){
 encoding='utf8';
 }
 
 if(!Buffer.isEncoding(encoding)){
-throw new TypeError('"encoding" must be a valid string encoding');
+throw new TypeError('Unknown encoding: '+encoding);
 }
 
 var length=byteLength(string,encoding)|0;
-that=createBuffer(that,length);
+var buf=createBuffer(length);
 
-var actual=that.write(string,encoding);
+var actual=buf.write(string,encoding);
 
 if(actual!==length){
 
 
 
-that=that.slice(0,actual);
+buf=buf.slice(0,actual);
 }
 
-return that;
+return buf;
 }
 
-function fromArrayLike(that,array){
+function fromArrayLike(array){
 var length=array.length<0?0:checked(array.length)|0;
-that=createBuffer(that,length);
+var buf=createBuffer(length);
 for(var i=0;i<length;i+=1){
-that[i]=array[i]&255;
+buf[i]=array[i]&255;
 }
-return that;
+return buf;
 }
 
-function fromArrayBuffer(that,array,byteOffset,length){
-array.byteLength;
-
+function fromArrayBuffer(array,byteOffset,length){
 if(byteOffset<0||array.byteLength<byteOffset){
-throw new RangeError('\'offset\' is out of bounds');
+throw new RangeError('"offset" is outside of buffer bounds');
 }
 
 if(array.byteLength<byteOffset+(length||0)){
-throw new RangeError('\'length\' is out of bounds');
+throw new RangeError('"length" is outside of buffer bounds');
 }
 
+var buf;
 if(byteOffset===undefined&&length===undefined){
-array=new Uint8Array(array);
+buf=new Uint8Array(array);
 }else if(length===undefined){
-array=new Uint8Array(array,byteOffset);
+buf=new Uint8Array(array,byteOffset);
 }else{
-array=new Uint8Array(array,byteOffset,length);
+buf=new Uint8Array(array,byteOffset,length);
 }
 
-if(Buffer.TYPED_ARRAY_SUPPORT){
 
-that=array;
-that.__proto__=Buffer.prototype;
-}else{
-
-that=fromArrayLike(that,array);
-}
-return that;
+buf.__proto__=Buffer.prototype;
+return buf;
 }
 
-function fromObject(that,obj){
+function fromObject(obj){
 if(Buffer.isBuffer(obj)){
 var len=checked(obj.length)|0;
-that=createBuffer(that,len);
+var buf=createBuffer(len);
 
-if(that.length===0){
-return that;
+if(buf.length===0){
+return buf;
 }
 
-obj.copy(that,0,0,len);
-return that;
+obj.copy(buf,0,0,len);
+return buf;
 }
 
 if(obj){
-if(typeof ArrayBuffer!=='undefined'&&
-obj.buffer instanceof ArrayBuffer||'length'in obj){
-if(typeof obj.length!=='number'||isnan(obj.length)){
-return createBuffer(that,0);
+if(ArrayBuffer.isView(obj)||'length'in obj){
+if(typeof obj.length!=='number'||numberIsNaN(obj.length)){
+return createBuffer(0);
 }
-return fromArrayLike(that,obj);
+return fromArrayLike(obj);
 }
 
-if(obj.type==='Buffer'&&isArray(obj.data)){
-return fromArrayLike(that,obj.data);
+if(obj.type==='Buffer'&&Array.isArray(obj.data)){
+return fromArrayLike(obj.data);
 }
 }
 
-throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.');
+throw new TypeError('The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object.');
 }
 
 function checked(length){
 
 
-if(length>=kMaxLength()){
+if(length>=K_MAX_LENGTH){
 throw new RangeError('Attempt to allocate Buffer larger than maximum '+
-'size: 0x'+kMaxLength().toString(16)+' bytes');
+'size: 0x'+K_MAX_LENGTH.toString(16)+' bytes');
 }
 return length|0;
 }
@@ -22799,7 +26634,7 @@
 }
 
 Buffer.isBuffer=function isBuffer(b){
-return!!(b!=null&&b._isBuffer);
+return b!=null&&b._isBuffer===true;
 };
 
 Buffer.compare=function compare(a,b){
@@ -22845,7 +26680,7 @@
 };
 
 Buffer.concat=function concat(list,length){
-if(!isArray(list)){
+if(!Array.isArray(list)){
 throw new TypeError('"list" argument must be an Array of Buffers');
 }
 
@@ -22865,6 +26700,9 @@
 var pos=0;
 for(i=0;i<list.length;++i){
 var buf=list[i];
+if(ArrayBuffer.isView(buf)){
+buf=Buffer.from(buf);
+}
 if(!Buffer.isBuffer(buf)){
 throw new TypeError('"list" argument must be an Array of Buffers');
 }
@@ -22878,8 +26716,7 @@
 if(Buffer.isBuffer(string)){
 return string.length;
 }
-if(typeof ArrayBuffer!=='undefined'&&typeof ArrayBuffer.isView==='function'&&(
-ArrayBuffer.isView(string)||string instanceof ArrayBuffer)){
+if(ArrayBuffer.isView(string)||isArrayBuffer(string)){
 return string.byteLength;
 }
 if(typeof string!=='string'){
@@ -22991,6 +26828,10 @@
 
 
 
+
+
+
+
 Buffer.prototype._isBuffer=true;
 
 function swap(b,n,m){
@@ -23037,12 +26878,14 @@
 };
 
 Buffer.prototype.toString=function toString(){
-var length=this.length|0;
+var length=this.length;
 if(length===0)return'';
 if(arguments.length===0)return utf8Slice(this,0,length);
 return slowToString.apply(this,arguments);
 };
 
+Buffer.prototype.toLocaleString=Buffer.prototype.toString;
+
 Buffer.prototype.equals=function equals(b){
 if(!Buffer.isBuffer(b))throw new TypeError('Argument must be a Buffer');
 if(this===b)return true;
@@ -23141,7 +26984,7 @@
 byteOffset=-0x80000000;
 }
 byteOffset=+byteOffset;
-if(isNaN(byteOffset)){
+if(numberIsNaN(byteOffset)){
 
 byteOffset=dir?0:buffer.length-1;
 }
@@ -23170,8 +27013,7 @@
 return arrayIndexOf(buffer,val,byteOffset,encoding,dir);
 }else if(typeof val==='number'){
 val=val&0xFF;
-if(Buffer.TYPED_ARRAY_SUPPORT&&
-typeof Uint8Array.prototype.indexOf==='function'){
+if(typeof Uint8Array.prototype.indexOf==='function'){
 if(dir){
 return Uint8Array.prototype.indexOf.call(buffer,val,byteOffset);
 }else{
@@ -23264,16 +27106,14 @@
 }
 }
 
-
 var strLen=string.length;
-if(strLen%2!==0)throw new TypeError('Invalid hex string');
 
 if(length>strLen/2){
 length=strLen/2;
 }
 for(var i=0;i<length;++i){
 var parsed=parseInt(string.substr(i*2,2),16);
-if(isNaN(parsed))return i;
+if(numberIsNaN(parsed))return i;
 buf[offset+i]=parsed;
 }
 return i;
@@ -23312,15 +27152,14 @@
 offset=0;
 
 }else if(isFinite(offset)){
-offset=offset|0;
+offset=offset>>>0;
 if(isFinite(length)){
-length=length|0;
+length=length>>>0;
 if(encoding===undefined)encoding='utf8';
 }else{
 encoding=length;
 length=undefined;
 }
-
 }else{
 throw new Error(
 'Buffer.write(string, encoding, offset[, length]) is no longer supported');
@@ -23545,18 +27384,9 @@
 
 if(end<start)end=start;
 
-var newBuf;
-if(Buffer.TYPED_ARRAY_SUPPORT){
-newBuf=this.subarray(start,end);
-newBuf.__proto__=Buffer.prototype;
-}else{
-var sliceLen=end-start;
-newBuf=new Buffer(sliceLen,undefined);
-for(var i=0;i<sliceLen;++i){
-newBuf[i]=this[i+start];
-}
-}
+var newBuf=this.subarray(start,end);
 
+newBuf.__proto__=Buffer.prototype;
 return newBuf;
 };
 
@@ -23569,8 +27399,8 @@
 }
 
 Buffer.prototype.readUIntLE=function readUIntLE(offset,byteLength,noAssert){
-offset=offset|0;
-byteLength=byteLength|0;
+offset=offset>>>0;
+byteLength=byteLength>>>0;
 if(!noAssert)checkOffset(offset,byteLength,this.length);
 
 var val=this[offset];
@@ -23584,8 +27414,8 @@
 };
 
 Buffer.prototype.readUIntBE=function readUIntBE(offset,byteLength,noAssert){
-offset=offset|0;
-byteLength=byteLength|0;
+offset=offset>>>0;
+byteLength=byteLength>>>0;
 if(!noAssert){
 checkOffset(offset,byteLength,this.length);
 }
@@ -23600,21 +27430,25 @@
 };
 
 Buffer.prototype.readUInt8=function readUInt8(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,1,this.length);
 return this[offset];
 };
 
 Buffer.prototype.readUInt16LE=function readUInt16LE(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,2,this.length);
 return this[offset]|this[offset+1]<<8;
 };
 
 Buffer.prototype.readUInt16BE=function readUInt16BE(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,2,this.length);
 return this[offset]<<8|this[offset+1];
 };
 
 Buffer.prototype.readUInt32LE=function readUInt32LE(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,4,this.length);
 
 return(this[offset]|
@@ -23624,6 +27458,7 @@
 };
 
 Buffer.prototype.readUInt32BE=function readUInt32BE(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,4,this.length);
 
 return this[offset]*0x1000000+(
@@ -23633,8 +27468,8 @@
 };
 
 Buffer.prototype.readIntLE=function readIntLE(offset,byteLength,noAssert){
-offset=offset|0;
-byteLength=byteLength|0;
+offset=offset>>>0;
+byteLength=byteLength>>>0;
 if(!noAssert)checkOffset(offset,byteLength,this.length);
 
 var val=this[offset];
@@ -23651,8 +27486,8 @@
 };
 
 Buffer.prototype.readIntBE=function readIntBE(offset,byteLength,noAssert){
-offset=offset|0;
-byteLength=byteLength|0;
+offset=offset>>>0;
+byteLength=byteLength>>>0;
 if(!noAssert)checkOffset(offset,byteLength,this.length);
 
 var i=byteLength;
@@ -23669,24 +27504,28 @@
 };
 
 Buffer.prototype.readInt8=function readInt8(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,1,this.length);
 if(!(this[offset]&0x80))return this[offset];
 return(0xff-this[offset]+1)*-1;
 };
 
 Buffer.prototype.readInt16LE=function readInt16LE(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,2,this.length);
 var val=this[offset]|this[offset+1]<<8;
 return val&0x8000?val|0xFFFF0000:val;
 };
 
 Buffer.prototype.readInt16BE=function readInt16BE(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,2,this.length);
 var val=this[offset+1]|this[offset]<<8;
 return val&0x8000?val|0xFFFF0000:val;
 };
 
 Buffer.prototype.readInt32LE=function readInt32LE(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,4,this.length);
 
 return this[offset]|
@@ -23696,6 +27535,7 @@
 };
 
 Buffer.prototype.readInt32BE=function readInt32BE(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,4,this.length);
 
 return this[offset]<<24|
@@ -23705,21 +27545,25 @@
 };
 
 Buffer.prototype.readFloatLE=function readFloatLE(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,4,this.length);
 return ieee754.read(this,offset,true,23,4);
 };
 
 Buffer.prototype.readFloatBE=function readFloatBE(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,4,this.length);
 return ieee754.read(this,offset,false,23,4);
 };
 
 Buffer.prototype.readDoubleLE=function readDoubleLE(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,8,this.length);
 return ieee754.read(this,offset,true,52,8);
 };
 
 Buffer.prototype.readDoubleBE=function readDoubleBE(offset,noAssert){
+offset=offset>>>0;
 if(!noAssert)checkOffset(offset,8,this.length);
 return ieee754.read(this,offset,false,52,8);
 };
@@ -23732,8 +27576,8 @@
 
 Buffer.prototype.writeUIntLE=function writeUIntLE(value,offset,byteLength,noAssert){
 value=+value;
-offset=offset|0;
-byteLength=byteLength|0;
+offset=offset>>>0;
+byteLength=byteLength>>>0;
 if(!noAssert){
 var maxBytes=Math.pow(2,8*byteLength)-1;
 checkInt(this,value,offset,byteLength,maxBytes,0);
@@ -23751,8 +27595,8 @@
 
 Buffer.prototype.writeUIntBE=function writeUIntBE(value,offset,byteLength,noAssert){
 value=+value;
-offset=offset|0;
-byteLength=byteLength|0;
+offset=offset>>>0;
+byteLength=byteLength>>>0;
 if(!noAssert){
 var maxBytes=Math.pow(2,8*byteLength)-1;
 checkInt(this,value,offset,byteLength,maxBytes,0);
@@ -23770,87 +27614,55 @@
 
 Buffer.prototype.writeUInt8=function writeUInt8(value,offset,noAssert){
 value=+value;
-offset=offset|0;
+offset=offset>>>0;
 if(!noAssert)checkInt(this,value,offset,1,0xff,0);
-if(!Buffer.TYPED_ARRAY_SUPPORT)value=Math.floor(value);
 this[offset]=value&0xff;
 return offset+1;
 };
 
-function objectWriteUInt16(buf,value,offset,littleEndian){
-if(value<0)value=0xffff+value+1;
-for(var i=0,j=Math.min(buf.length-offset,2);i<j;++i){
-buf[offset+i]=(value&0xff<<8*(littleEndian?i:1-i))>>>
-(littleEndian?i:1-i)*8;
-}
-}
-
 Buffer.prototype.writeUInt16LE=function writeUInt16LE(value,offset,noAssert){
 value=+value;
-offset=offset|0;
+offset=offset>>>0;
 if(!noAssert)checkInt(this,value,offset,2,0xffff,0);
-if(Buffer.TYPED_ARRAY_SUPPORT){
 this[offset]=value&0xff;
 this[offset+1]=value>>>8;
-}else{
-objectWriteUInt16(this,value,offset,true);
-}
 return offset+2;
 };
 
 Buffer.prototype.writeUInt16BE=function writeUInt16BE(value,offset,noAssert){
 value=+value;
-offset=offset|0;
+offset=offset>>>0;
 if(!noAssert)checkInt(this,value,offset,2,0xffff,0);
-if(Buffer.TYPED_ARRAY_SUPPORT){
 this[offset]=value>>>8;
 this[offset+1]=value&0xff;
-}else{
-objectWriteUInt16(this,value,offset,false);
-}
 return offset+2;
 };
 
-function objectWriteUInt32(buf,value,offset,littleEndian){
-if(value<0)value=0xffffffff+value+1;
-for(var i=0,j=Math.min(buf.length-offset,4);i<j;++i){
-buf[offset+i]=value>>>(littleEndian?i:3-i)*8&0xff;
-}
-}
-
 Buffer.prototype.writeUInt32LE=function writeUInt32LE(value,offset,noAssert){
 value=+value;
-offset=offset|0;
+offset=offset>>>0;
 if(!noAssert)checkInt(this,value,offset,4,0xffffffff,0);
-if(Buffer.TYPED_ARRAY_SUPPORT){
 this[offset+3]=value>>>24;
 this[offset+2]=value>>>16;
 this[offset+1]=value>>>8;
 this[offset]=value&0xff;
-}else{
-objectWriteUInt32(this,value,offset,true);
-}
 return offset+4;
 };
 
 Buffer.prototype.writeUInt32BE=function writeUInt32BE(value,offset,noAssert){
 value=+value;
-offset=offset|0;
+offset=offset>>>0;
 if(!noAssert)checkInt(this,value,offset,4,0xffffffff,0);
-if(Buffer.TYPED_ARRAY_SUPPORT){
 this[offset]=value>>>24;
 this[offset+1]=value>>>16;
 this[offset+2]=value>>>8;
 this[offset+3]=value&0xff;
-}else{
-objectWriteUInt32(this,value,offset,false);
-}
 return offset+4;
 };
 
 Buffer.prototype.writeIntLE=function writeIntLE(value,offset,byteLength,noAssert){
 value=+value;
-offset=offset|0;
+offset=offset>>>0;
 if(!noAssert){
 var limit=Math.pow(2,8*byteLength-1);
 
@@ -23873,7 +27685,7 @@
 
 Buffer.prototype.writeIntBE=function writeIntBE(value,offset,byteLength,noAssert){
 value=+value;
-offset=offset|0;
+offset=offset>>>0;
 if(!noAssert){
 var limit=Math.pow(2,8*byteLength-1);
 
@@ -23896,9 +27708,8 @@
 
 Buffer.prototype.writeInt8=function writeInt8(value,offset,noAssert){
 value=+value;
-offset=offset|0;
+offset=offset>>>0;
 if(!noAssert)checkInt(this,value,offset,1,0x7f,-0x80);
-if(!Buffer.TYPED_ARRAY_SUPPORT)value=Math.floor(value);
 if(value<0)value=0xff+value+1;
 this[offset]=value&0xff;
 return offset+1;
@@ -23906,58 +27717,42 @@
 
 Buffer.prototype.writeInt16LE=function writeInt16LE(value,offset,noAssert){
 value=+value;
-offset=offset|0;
+offset=offset>>>0;
 if(!noAssert)checkInt(this,value,offset,2,0x7fff,-0x8000);
-if(Buffer.TYPED_ARRAY_SUPPORT){
 this[offset]=value&0xff;
 this[offset+1]=value>>>8;
-}else{
-objectWriteUInt16(this,value,offset,true);
-}
 return offset+2;
 };
 
 Buffer.prototype.writeInt16BE=function writeInt16BE(value,offset,noAssert){
 value=+value;
-offset=offset|0;
+offset=offset>>>0;
 if(!noAssert)checkInt(this,value,offset,2,0x7fff,-0x8000);
-if(Buffer.TYPED_ARRAY_SUPPORT){
 this[offset]=value>>>8;
 this[offset+1]=value&0xff;
-}else{
-objectWriteUInt16(this,value,offset,false);
-}
 return offset+2;
 };
 
 Buffer.prototype.writeInt32LE=function writeInt32LE(value,offset,noAssert){
 value=+value;
-offset=offset|0;
+offset=offset>>>0;
 if(!noAssert)checkInt(this,value,offset,4,0x7fffffff,-0x80000000);
-if(Buffer.TYPED_ARRAY_SUPPORT){
 this[offset]=value&0xff;
 this[offset+1]=value>>>8;
 this[offset+2]=value>>>16;
 this[offset+3]=value>>>24;
-}else{
-objectWriteUInt32(this,value,offset,true);
-}
 return offset+4;
 };
 
 Buffer.prototype.writeInt32BE=function writeInt32BE(value,offset,noAssert){
 value=+value;
-offset=offset|0;
+offset=offset>>>0;
 if(!noAssert)checkInt(this,value,offset,4,0x7fffffff,-0x80000000);
 if(value<0)value=0xffffffff+value+1;
-if(Buffer.TYPED_ARRAY_SUPPORT){
 this[offset]=value>>>24;
 this[offset+1]=value>>>16;
 this[offset+2]=value>>>8;
 this[offset+3]=value&0xff;
-}else{
-objectWriteUInt32(this,value,offset,false);
-}
 return offset+4;
 };
 
@@ -23967,6 +27762,8 @@
 }
 
 function writeFloat(buf,value,offset,littleEndian,noAssert){
+value=+value;
+offset=offset>>>0;
 if(!noAssert){
 checkIEEE754(buf,value,offset,4,3.4028234663852886e+38,-3.4028234663852886e+38);
 }
@@ -23983,6 +27780,8 @@
 };
 
 function writeDouble(buf,value,offset,littleEndian,noAssert){
+value=+value;
+offset=offset>>>0;
 if(!noAssert){
 checkIEEE754(buf,value,offset,8,1.7976931348623157E+308,-1.7976931348623157E+308);
 }
@@ -24000,6 +27799,7 @@
 
 
 Buffer.prototype.copy=function copy(target,targetStart,start,end){
+if(!Buffer.isBuffer(target))throw new TypeError('argument should be a Buffer');
 if(!start)start=0;
 if(!end&&end!==0)end=this.length;
 if(targetStart>=target.length)targetStart=target.length;
@@ -24014,7 +27814,7 @@
 if(targetStart<0){
 throw new RangeError('targetStart out of bounds');
 }
-if(start<0||start>=this.length)throw new RangeError('sourceStart out of bounds');
+if(start<0||start>=this.length)throw new RangeError('Index out of range');
 if(end<0)throw new RangeError('sourceEnd out of bounds');
 
 
@@ -24024,22 +27824,19 @@
 }
 
 var len=end-start;
-var i;
 
-if(this===target&&start<targetStart&&targetStart<end){
+if(this===target&&typeof Uint8Array.prototype.copyWithin==='function'){
 
-for(i=len-1;i>=0;--i){
-target[i+targetStart]=this[i+start];
-}
-}else if(len<1000||!Buffer.TYPED_ARRAY_SUPPORT){
+this.copyWithin(targetStart,start,end);
+}else if(this===target&&start<targetStart&&targetStart<end){
 
-for(i=0;i<len;++i){
+for(var i=len-1;i>=0;--i){
 target[i+targetStart]=this[i+start];
 }
 }else{
 Uint8Array.prototype.set.call(
 target,
-this.subarray(start,start+len),
+this.subarray(start,end),
 targetStart);
 
 }
@@ -24062,18 +27859,20 @@
 encoding=end;
 end=this.length;
 }
-if(val.length===1){
-var code=val.charCodeAt(0);
-if(code<256){
-val=code;
-}
-}
 if(encoding!==undefined&&typeof encoding!=='string'){
 throw new TypeError('encoding must be a string');
 }
 if(typeof encoding==='string'&&!Buffer.isEncoding(encoding)){
 throw new TypeError('Unknown encoding: '+encoding);
 }
+if(val.length===1){
+var code=val.charCodeAt(0);
+if(encoding==='utf8'&&code<128||
+encoding==='latin1'){
+
+val=code;
+}
+}
 }else if(typeof val==='number'){
 val=val&255;
 }
@@ -24100,8 +27899,12 @@
 }else{
 var bytes=Buffer.isBuffer(val)?
 val:
-utf8ToBytes(new Buffer(val,encoding).toString());
+new Buffer(val,encoding);
 var len=bytes.length;
+if(len===0){
+throw new TypeError('The value "'+val+
+'" is invalid for argument "value"');
+}
 for(i=0;i<end-start;++i){
 this[i+start]=bytes[i%len];
 }
@@ -24113,11 +27916,13 @@
 
 
 
-var INVALID_BASE64_RE=/[^+\/0-9A-Za-z-_]/g;
+var INVALID_BASE64_RE=/[^+/0-9A-Za-z-_]/g;
 
 function base64clean(str){
 
-str=stringtrim(str).replace(INVALID_BASE64_RE,'');
+str=str.split('=')[0];
+
+str=str.trim().replace(INVALID_BASE64_RE,'');
 
 if(str.length<2)return'';
 
@@ -24127,11 +27932,6 @@
 return str;
 }
 
-function stringtrim(str){
-if(str.trim)return str.trim();
-return str.replace(/^\s+|\s+$/g,'');
-}
-
 function toHex(n){
 if(n<16)return'0'+n.toString(16);
 return n.toString(16);
@@ -24254,12 +28054,19 @@
 return i;
 }
 
-function isnan(val){
-return val!==val;
+
+
+function isArrayBuffer(obj){
+return obj instanceof ArrayBuffer||
+obj!=null&&obj.constructor!=null&&obj.constructor.name==='ArrayBuffer'&&
+typeof obj.byteLength==='number';
 }
 
-}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{"base64-js":48,"ieee754":57,"isarray":60}],55:[function(require,module,exports){
+function numberIsNaN(obj){
+return obj!==obj;
+}
+
+},{"base64-js":54,"ieee754":63}],61:[function(require,module,exports){
 (function(Buffer){
 
 
@@ -24370,7 +28177,7 @@
 }
 
 }).call(this,{"isBuffer":require("../../is-buffer/index.js")});
-},{"../../is-buffer/index.js":59}],56:[function(require,module,exports){
+},{"../../is-buffer/index.js":65}],62:[function(require,module,exports){
 
 
 
@@ -24392,8 +28199,16 @@
 
 
 
+var objectCreate=Object.create||objectCreatePolyfill;
+var objectKeys=Object.keys||objectKeysPolyfill;
+var bind=Function.prototype.bind||functionBindPolyfill;
+
 function EventEmitter(){
-this._events=this._events||{};
+if(!this._events||!Object.prototype.hasOwnProperty.call(this,'_events')){
+this._events=objectCreate(null);
+this._eventsCount=0;
+}
+
 this._maxListeners=this._maxListeners||undefined;
 }
 module.exports=EventEmitter;
@@ -24406,169 +28221,319 @@
 
 
 
-EventEmitter.defaultMaxListeners=10;
+var defaultMaxListeners=10;
+
+var hasDefineProperty;
+try{
+var o={};
+if(Object.defineProperty)Object.defineProperty(o,'x',{value:0});
+hasDefineProperty=o.x===0;
+}catch(err){hasDefineProperty=false;}
+if(hasDefineProperty){
+Object.defineProperty(EventEmitter,'defaultMaxListeners',{
+enumerable:true,
+get:function(){
+return defaultMaxListeners;
+},
+set:function(arg){
+
+
+if(typeof arg!=='number'||arg<0||arg!==arg)
+throw new TypeError('"defaultMaxListeners" must be a positive number');
+defaultMaxListeners=arg;
+}});
+
+}else{
+EventEmitter.defaultMaxListeners=defaultMaxListeners;
+}
 
 
 
-EventEmitter.prototype.setMaxListeners=function(n){
-if(!isNumber(n)||n<0||isNaN(n))
-throw TypeError('n must be a positive number');
+EventEmitter.prototype.setMaxListeners=function setMaxListeners(n){
+if(typeof n!=='number'||n<0||isNaN(n))
+throw new TypeError('"n" argument must be a positive number');
 this._maxListeners=n;
 return this;
 };
 
-EventEmitter.prototype.emit=function(type){
-var er,handler,len,args,i,listeners;
+function $getMaxListeners(that){
+if(that._maxListeners===undefined)
+return EventEmitter.defaultMaxListeners;
+return that._maxListeners;
+}
 
-if(!this._events)
-this._events={};
+EventEmitter.prototype.getMaxListeners=function getMaxListeners(){
+return $getMaxListeners(this);
+};
 
 
-if(type==='error'){
-if(!this._events.error||
-isObject(this._events.error)&&!this._events.error.length){
+
+
+
+
+function emitNone(handler,isFn,self){
+if(isFn)
+handler.call(self);else
+{
+var len=handler.length;
+var listeners=arrayClone(handler,len);
+for(var i=0;i<len;++i)
+listeners[i].call(self);
+}
+}
+function emitOne(handler,isFn,self,arg1){
+if(isFn)
+handler.call(self,arg1);else
+{
+var len=handler.length;
+var listeners=arrayClone(handler,len);
+for(var i=0;i<len;++i)
+listeners[i].call(self,arg1);
+}
+}
+function emitTwo(handler,isFn,self,arg1,arg2){
+if(isFn)
+handler.call(self,arg1,arg2);else
+{
+var len=handler.length;
+var listeners=arrayClone(handler,len);
+for(var i=0;i<len;++i)
+listeners[i].call(self,arg1,arg2);
+}
+}
+function emitThree(handler,isFn,self,arg1,arg2,arg3){
+if(isFn)
+handler.call(self,arg1,arg2,arg3);else
+{
+var len=handler.length;
+var listeners=arrayClone(handler,len);
+for(var i=0;i<len;++i)
+listeners[i].call(self,arg1,arg2,arg3);
+}
+}
+
+function emitMany(handler,isFn,self,args){
+if(isFn)
+handler.apply(self,args);else
+{
+var len=handler.length;
+var listeners=arrayClone(handler,len);
+for(var i=0;i<len;++i)
+listeners[i].apply(self,args);
+}
+}
+
+EventEmitter.prototype.emit=function emit(type){
+var er,handler,len,args,i,events;
+var doError=type==='error';
+
+events=this._events;
+if(events)
+doError=doError&&events.error==null;else
+if(!doError)
+return false;
+
+
+if(doError){
+if(arguments.length>1)
 er=arguments[1];
 if(er instanceof Error){
 throw er;
 }else{
 
-var err=new Error('Uncaught, unspecified "error" event. ('+er+')');
+var err=new Error('Unhandled "error" event. ('+er+')');
 err.context=er;
 throw err;
 }
-}
+return false;
 }
 
-handler=this._events[type];
+handler=events[type];
 
-if(isUndefined(handler))
+if(!handler)
 return false;
 
-if(isFunction(handler)){
-switch(arguments.length){
+var isFn=typeof handler==='function';
+len=arguments.length;
+switch(len){
 
 case 1:
-handler.call(this);
+emitNone(handler,isFn,this);
 break;
 case 2:
-handler.call(this,arguments[1]);
+emitOne(handler,isFn,this,arguments[1]);
 break;
 case 3:
-handler.call(this,arguments[1],arguments[2]);
+emitTwo(handler,isFn,this,arguments[1],arguments[2]);
+break;
+case 4:
+emitThree(handler,isFn,this,arguments[1],arguments[2],arguments[3]);
 break;
 
 default:
-args=Array.prototype.slice.call(arguments,1);
-handler.apply(this,args);}
+args=new Array(len-1);
+for(i=1;i<len;i++)
+args[i-1]=arguments[i];
+emitMany(handler,isFn,this,args);}
 
-}else if(isObject(handler)){
-args=Array.prototype.slice.call(arguments,1);
-listeners=handler.slice();
-len=listeners.length;
-for(i=0;i<len;i++)
-listeners[i].apply(this,args);
-}
 
 return true;
 };
 
-EventEmitter.prototype.addListener=function(type,listener){
+function _addListener(target,type,listener,prepend){
 var m;
+var events;
+var existing;
 
-if(!isFunction(listener))
-throw TypeError('listener must be a function');
+if(typeof listener!=='function')
+throw new TypeError('"listener" argument must be a function');
 
-if(!this._events)
-this._events={};
-
-
-
-if(this._events.newListener)
-this.emit('newListener',type,
-isFunction(listener.listener)?
-listener.listener:listener);
-
-if(!this._events[type])
-
-this._events[type]=listener;else
-if(isObject(this._events[type]))
-
-this._events[type].push(listener);else
-
-
-this._events[type]=[this._events[type],listener];
-
-
-if(isObject(this._events[type])&&!this._events[type].warned){
-if(!isUndefined(this._maxListeners)){
-m=this._maxListeners;
+events=target._events;
+if(!events){
+events=target._events=objectCreate(null);
+target._eventsCount=0;
 }else{
-m=EventEmitter.defaultMaxListeners;
+
+
+if(events.newListener){
+target.emit('newListener',type,
+listener.listener?listener.listener:listener);
+
+
+
+events=target._events;
+}
+existing=events[type];
 }
 
-if(m&&m>0&&this._events[type].length>m){
-this._events[type].warned=true;
-console.error('(node) warning: possible EventEmitter memory '+
-'leak detected. %d listeners added. '+
-'Use emitter.setMaxListeners() to increase limit.',
-this._events[type].length);
-if(typeof console.trace==='function'){
+if(!existing){
 
-console.trace();
+existing=events[type]=listener;
+++target._eventsCount;
+}else{
+if(typeof existing==='function'){
+
+existing=events[type]=
+prepend?[listener,existing]:[existing,listener];
+}else{
+
+if(prepend){
+existing.unshift(listener);
+}else{
+existing.push(listener);
+}
+}
+
+
+if(!existing.warned){
+m=$getMaxListeners(target);
+if(m&&m>0&&existing.length>m){
+existing.warned=true;
+var w=new Error('Possible EventEmitter memory leak detected. '+
+existing.length+' "'+String(type)+'" listeners '+
+'added. Use emitter.setMaxListeners() to '+
+'increase limit.');
+w.name='MaxListenersExceededWarning';
+w.emitter=target;
+w.type=type;
+w.count=existing.length;
+if(typeof console==='object'&&console.warn){
+console.warn('%s: %s',w.name,w.message);
+}
 }
 }
 }
 
-return this;
+return target;
+}
+
+EventEmitter.prototype.addListener=function addListener(type,listener){
+return _addListener(this,type,listener,false);
 };
 
 EventEmitter.prototype.on=EventEmitter.prototype.addListener;
 
-EventEmitter.prototype.once=function(type,listener){
-if(!isFunction(listener))
-throw TypeError('listener must be a function');
+EventEmitter.prototype.prependListener=
+function prependListener(type,listener){
+return _addListener(this,type,listener,true);
+};
 
-var fired=false;
+function onceWrapper(){
+if(!this.fired){
+this.target.removeListener(this.type,this.wrapFn);
+this.fired=true;
+switch(arguments.length){
+case 0:
+return this.listener.call(this.target);
+case 1:
+return this.listener.call(this.target,arguments[0]);
+case 2:
+return this.listener.call(this.target,arguments[0],arguments[1]);
+case 3:
+return this.listener.call(this.target,arguments[0],arguments[1],
+arguments[2]);
+default:
+var args=new Array(arguments.length);
+for(var i=0;i<args.length;++i)
+args[i]=arguments[i];
+this.listener.apply(this.target,args);}
 
-function g(){
-this.removeListener(type,g);
-
-if(!fired){
-fired=true;
-listener.apply(this,arguments);
 }
 }
 
-g.listener=listener;
-this.on(type,g);
+function _onceWrap(target,type,listener){
+var state={fired:false,wrapFn:undefined,target:target,type:type,listener:listener};
+var wrapped=bind.call(onceWrapper,state);
+wrapped.listener=listener;
+state.wrapFn=wrapped;
+return wrapped;
+}
 
+EventEmitter.prototype.once=function once(type,listener){
+if(typeof listener!=='function')
+throw new TypeError('"listener" argument must be a function');
+this.on(type,_onceWrap(this,type,listener));
+return this;
+};
+
+EventEmitter.prototype.prependOnceListener=
+function prependOnceListener(type,listener){
+if(typeof listener!=='function')
+throw new TypeError('"listener" argument must be a function');
+this.prependListener(type,_onceWrap(this,type,listener));
 return this;
 };
 
 
-EventEmitter.prototype.removeListener=function(type,listener){
-var list,position,length,i;
+EventEmitter.prototype.removeListener=
+function removeListener(type,listener){
+var list,events,position,i,originalListener;
 
-if(!isFunction(listener))
-throw TypeError('listener must be a function');
+if(typeof listener!=='function')
+throw new TypeError('"listener" argument must be a function');
 
-if(!this._events||!this._events[type])
+events=this._events;
+if(!events)
 return this;
 
-list=this._events[type];
-length=list.length;
+list=events[type];
+if(!list)
+return this;
+
+if(list===listener||list.listener===listener){
+if(--this._eventsCount===0)
+this._events=objectCreate(null);else
+{
+delete events[type];
+if(events.removeListener)
+this.emit('removeListener',type,list.listener||listener);
+}
+}else if(typeof list!=='function'){
 position=-1;
 
-if(list===listener||
-isFunction(list.listener)&&list.listener===listener){
-delete this._events[type];
-if(this._events.removeListener)
-this.emit('removeListener',type,listener);
-
-}else if(isObject(list)){
-for(i=length;i-->0;){
-if(list[i]===listener||
-list[i].listener&&list[i].listener===listener){
+for(i=list.length-1;i>=0;i--){
+if(list[i]===listener||list[i].listener===listener){
+originalListener=list[i].listener;
 position=i;
 break;
 }
@@ -24577,104 +28542,163 @@
 if(position<0)
 return this;
 
-if(list.length===1){
-list.length=0;
-delete this._events[type];
-}else{
-list.splice(position,1);
-}
+if(position===0)
+list.shift();else
 
-if(this._events.removeListener)
-this.emit('removeListener',type,listener);
+spliceOne(list,position);
+
+if(list.length===1)
+events[type]=list[0];
+
+if(events.removeListener)
+this.emit('removeListener',type,originalListener||listener);
 }
 
 return this;
 };
 
-EventEmitter.prototype.removeAllListeners=function(type){
-var key,listeners;
+EventEmitter.prototype.removeAllListeners=
+function removeAllListeners(type){
+var listeners,events,i;
 
-if(!this._events)
+events=this._events;
+if(!events)
 return this;
 
 
-if(!this._events.removeListener){
-if(arguments.length===0)
-this._events={};else
-if(this._events[type])
-delete this._events[type];
+if(!events.removeListener){
+if(arguments.length===0){
+this._events=objectCreate(null);
+this._eventsCount=0;
+}else if(events[type]){
+if(--this._eventsCount===0)
+this._events=objectCreate(null);else
+
+delete events[type];
+}
 return this;
 }
 
 
 if(arguments.length===0){
-for(key in this._events){
+var keys=objectKeys(events);
+var key;
+for(i=0;i<keys.length;++i){
+key=keys[i];
 if(key==='removeListener')continue;
 this.removeAllListeners(key);
 }
 this.removeAllListeners('removeListener');
-this._events={};
+this._events=objectCreate(null);
+this._eventsCount=0;
 return this;
 }
 
-listeners=this._events[type];
+listeners=events[type];
 
-if(isFunction(listeners)){
+if(typeof listeners==='function'){
 this.removeListener(type,listeners);
 }else if(listeners){
 
-while(listeners.length)
-this.removeListener(type,listeners[listeners.length-1]);
+for(i=listeners.length-1;i>=0;i--){
+this.removeListener(type,listeners[i]);
 }
-delete this._events[type];
+}
 
 return this;
 };
 
-EventEmitter.prototype.listeners=function(type){
+EventEmitter.prototype.listeners=function listeners(type){
+var evlistener;
 var ret;
-if(!this._events||!this._events[type])
-ret=[];else
-if(isFunction(this._events[type]))
-ret=[this._events[type]];else
+var events=this._events;
 
-ret=this._events[type].slice();
+if(!events)
+ret=[];else
+{
+evlistener=events[type];
+if(!evlistener)
+ret=[];else
+if(typeof evlistener==='function')
+ret=[evlistener.listener||evlistener];else
+
+ret=unwrapListeners(evlistener);
+}
+
 return ret;
 };
 
-EventEmitter.prototype.listenerCount=function(type){
-if(this._events){
-var evlistener=this._events[type];
+EventEmitter.listenerCount=function(emitter,type){
+if(typeof emitter.listenerCount==='function'){
+return emitter.listenerCount(type);
+}else{
+return listenerCount.call(emitter,type);
+}
+};
 
-if(isFunction(evlistener))
-return 1;else
-if(evlistener)
+EventEmitter.prototype.listenerCount=listenerCount;
+function listenerCount(type){
+var events=this._events;
+
+if(events){
+var evlistener=events[type];
+
+if(typeof evlistener==='function'){
+return 1;
+}else if(evlistener){
 return evlistener.length;
 }
+}
+
 return 0;
+}
+
+EventEmitter.prototype.eventNames=function eventNames(){
+return this._eventsCount>0?Reflect.ownKeys(this._events):[];
 };
 
-EventEmitter.listenerCount=function(emitter,type){
-return emitter.listenerCount(type);
+
+function spliceOne(list,index){
+for(var i=index,k=i+1,n=list.length;k<n;i+=1,k+=1)
+list[i]=list[k];
+list.pop();
+}
+
+function arrayClone(arr,n){
+var copy=new Array(n);
+for(var i=0;i<n;++i)
+copy[i]=arr[i];
+return copy;
+}
+
+function unwrapListeners(arr){
+var ret=new Array(arr.length);
+for(var i=0;i<ret.length;++i){
+ret[i]=arr[i].listener||arr[i];
+}
+return ret;
+}
+
+function objectCreatePolyfill(proto){
+var F=function(){};
+F.prototype=proto;
+return new F();
+}
+function objectKeysPolyfill(obj){
+var keys=[];
+for(var k in obj)if(Object.prototype.hasOwnProperty.call(obj,k)){
+keys.push(k);
+}
+return k;
+}
+function functionBindPolyfill(context){
+var fn=this;
+return function(){
+return fn.apply(context,arguments);
 };
-
-function isFunction(arg){
-return typeof arg==='function';
 }
 
-function isNumber(arg){
-return typeof arg==='number';
-}
-
-function isObject(arg){
-return typeof arg==='object'&&arg!==null;
-}
-
-function isUndefined(arg){
-return arg===void 0;
-}
-
-},{}],57:[function(require,module,exports){
+},{}],63:[function(require,module,exports){
 exports.read=function(buffer,offset,isLE,mLen,nBytes){
 var e,m;
 var eLen=nBytes*8-mLen-1;
@@ -24760,7 +28784,7 @@
 buffer[offset+i-d]|=s*128;
 };
 
-},{}],58:[function(require,module,exports){
+},{}],64:[function(require,module,exports){
 if(typeof Object.create==='function'){
 
 module.exports=function inherits(ctor,superCtor){
@@ -24785,7 +28809,7 @@
 };
 }
 
-},{}],59:[function(require,module,exports){
+},{}],65:[function(require,module,exports){
 
 
 
@@ -24808,14 +28832,14 @@
 return typeof obj.readFloatLE==='function'&&typeof obj.slice==='function'&&isBuffer(obj.slice(0,0));
 }
 
-},{}],60:[function(require,module,exports){
+},{}],66:[function(require,module,exports){
 var toString={}.toString;
 
 module.exports=Array.isArray||function(arr){
 return toString.call(arr)=='[object Array]';
 };
 
-},{}],61:[function(require,module,exports){
+},{}],67:[function(require,module,exports){
 'use strict';
 
 
@@ -24823,6 +28847,9 @@
 typeof Uint16Array!=='undefined'&&
 typeof Int32Array!=='undefined';
 
+function _has(obj,key){
+return Object.prototype.hasOwnProperty.call(obj,key);
+}
 
 exports.assign=function(obj){
 var sources=Array.prototype.slice.call(arguments,1);
@@ -24835,7 +28862,7 @@
 }
 
 for(var p in source){
-if(source.hasOwnProperty(p)){
+if(_has(source,p)){
 obj[p]=source[p];
 }
 }
@@ -24919,13 +28946,32 @@
 
 exports.setTyped(TYPED_OK);
 
-},{}],62:[function(require,module,exports){
+},{}],68:[function(require,module,exports){
 'use strict';
 
 
 
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 function adler32(adler,buf,len,pos){
 var s1=adler&0xffff|0,
 s2=adler>>>16&0xffff|0,
@@ -24953,10 +28999,28 @@
 
 module.exports=adler32;
 
-},{}],63:[function(require,module,exports){
+},{}],69:[function(require,module,exports){
 'use strict';
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 module.exports={
 
 
@@ -25005,7 +29069,7 @@
 
 
 
-},{}],64:[function(require,module,exports){
+},{}],70:[function(require,module,exports){
 'use strict';
 
 
@@ -25014,6 +29078,24 @@
 
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 function makeTable(){
 var c,table=[];
 
@@ -25048,9 +29130,28 @@
 
 module.exports=crc32;
 
-},{}],65:[function(require,module,exports){
+},{}],71:[function(require,module,exports){
 'use strict';
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 var utils=require('../utils/common');
 var trees=require('./trees');
 var adler32=require('./adler32');
@@ -26905,9 +31006,28 @@
 
 
 
-},{"../utils/common":61,"./adler32":62,"./crc32":64,"./messages":66,"./trees":67}],66:[function(require,module,exports){
+},{"../utils/common":67,"./adler32":68,"./crc32":70,"./messages":72,"./trees":73}],72:[function(require,module,exports){
 'use strict';
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 module.exports={
 2:'need dictionary',
 1:'stream end',
@@ -26920,10 +31040,28 @@
 '-6':'incompatible version'};
 
 
-},{}],67:[function(require,module,exports){
+},{}],73:[function(require,module,exports){
 'use strict';
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 var utils=require('../utils/common');
 
 
@@ -28124,10 +32262,28 @@
 exports._tr_tally=_tr_tally;
 exports._tr_align=_tr_align;
 
-},{"../utils/common":61}],68:[function(require,module,exports){
+},{"../utils/common":67}],74:[function(require,module,exports){
 'use strict';
 
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 function ZStream(){
 
 this.input=null;
@@ -28155,7 +32311,7 @@
 
 module.exports=ZStream;
 
-},{}],69:[function(require,module,exports){
+},{}],75:[function(require,module,exports){
 (function(process){
 
 
@@ -28383,7 +32539,7 @@
 
 
 }).call(this,require('_process'));
-},{"_process":71}],70:[function(require,module,exports){
+},{"_process":77}],76:[function(require,module,exports){
 (function(process){
 'use strict';
 
@@ -28430,7 +32586,7 @@
 }
 
 }).call(this,require('_process'));
-},{"_process":71}],71:[function(require,module,exports){
+},{"_process":77}],77:[function(require,module,exports){
 
 var process=module.exports={};
 
@@ -28601,6 +32757,10 @@
 process.removeListener=noop;
 process.removeAllListeners=noop;
 process.emit=noop;
+process.prependListener=noop;
+process.prependOnceListener=noop;
+
+process.listeners=function(name){return[];};
 
 process.binding=function(name){
 throw new Error('process.binding is not supported');
@@ -28612,7 +32772,7 @@
 };
 process.umask=function(){return 0;};
 
-},{}],72:[function(require,module,exports){
+},{}],78:[function(require,module,exports){
 
 
 
@@ -28698,7 +32858,7 @@
 return Object.prototype.toString.call(xs)==='[object Array]';
 };
 
-},{}],73:[function(require,module,exports){
+},{}],79:[function(require,module,exports){
 
 
 
@@ -28785,16 +32945,16 @@
 return res;
 };
 
-},{}],74:[function(require,module,exports){
+},{}],80:[function(require,module,exports){
 'use strict';
 
 exports.decode=exports.parse=require('./decode');
 exports.encode=exports.stringify=require('./encode');
 
-},{"./decode":72,"./encode":73}],75:[function(require,module,exports){
+},{"./decode":78,"./encode":79}],81:[function(require,module,exports){
 module.exports=require("./lib/_stream_duplex.js");
 
-},{"./lib/_stream_duplex.js":76}],76:[function(require,module,exports){
+},{"./lib/_stream_duplex.js":82}],82:[function(require,module,exports){
 
 
 
@@ -28870,7 +33030,7 @@
 f(xs[i],i);
 }
 }
-},{"./_stream_readable":78,"./_stream_writable":80,"core-util-is":55,"inherits":58,"process-nextick-args":70}],77:[function(require,module,exports){
+},{"./_stream_readable":84,"./_stream_writable":86,"core-util-is":61,"inherits":64,"process-nextick-args":76}],83:[function(require,module,exports){
 
 
 
@@ -28897,7 +33057,7 @@
 PassThrough.prototype._transform=function(chunk,encoding,cb){
 cb(null,chunk);
 };
-},{"./_stream_transform":79,"core-util-is":55,"inherits":58}],78:[function(require,module,exports){
+},{"./_stream_transform":85,"core-util-is":61,"inherits":64}],84:[function(require,module,exports){
 (function(process){
 'use strict';
 
@@ -29841,7 +34001,7 @@
 return-1;
 }
 }).call(this,require('_process'));
-},{"./_stream_duplex":76,"./internal/streams/BufferList":81,"_process":71,"buffer":54,"buffer-shims":53,"core-util-is":55,"events":56,"inherits":58,"isarray":60,"process-nextick-args":70,"string_decoder/":87,"util":49}],79:[function(require,module,exports){
+},{"./_stream_duplex":82,"./internal/streams/BufferList":87,"_process":77,"buffer":60,"buffer-shims":59,"core-util-is":61,"events":62,"inherits":64,"isarray":66,"process-nextick-args":76,"string_decoder/":93,"util":55}],85:[function(require,module,exports){
 
 
 
@@ -30024,7 +34184,7 @@
 
 return stream.push(null);
 }
-},{"./_stream_duplex":76,"core-util-is":55,"inherits":58}],80:[function(require,module,exports){
+},{"./_stream_duplex":82,"core-util-is":61,"inherits":64}],86:[function(require,module,exports){
 (function(process){
 
 
@@ -30581,7 +34741,7 @@
 };
 }
 }).call(this,require('_process'));
-},{"./_stream_duplex":76,"_process":71,"buffer":54,"buffer-shims":53,"core-util-is":55,"events":56,"inherits":58,"process-nextick-args":70,"util-deprecate":88}],81:[function(require,module,exports){
+},{"./_stream_duplex":82,"_process":77,"buffer":60,"buffer-shims":59,"core-util-is":61,"events":62,"inherits":64,"process-nextick-args":76,"util-deprecate":94}],87:[function(require,module,exports){
 'use strict';
 
 var Buffer=require('buffer').Buffer;
@@ -30646,10 +34806,10 @@
 }
 return ret;
 };
-},{"buffer":54,"buffer-shims":53}],82:[function(require,module,exports){
+},{"buffer":60,"buffer-shims":59}],88:[function(require,module,exports){
 module.exports=require("./lib/_stream_passthrough.js");
 
-},{"./lib/_stream_passthrough.js":77}],83:[function(require,module,exports){
+},{"./lib/_stream_passthrough.js":83}],89:[function(require,module,exports){
 (function(process){
 var Stream=function(){
 try{
@@ -30669,13 +34829,13 @@
 }
 
 }).call(this,require('_process'));
-},{"./lib/_stream_duplex.js":76,"./lib/_stream_passthrough.js":77,"./lib/_stream_readable.js":78,"./lib/_stream_transform.js":79,"./lib/_stream_writable.js":80,"_process":71}],84:[function(require,module,exports){
+},{"./lib/_stream_duplex.js":82,"./lib/_stream_passthrough.js":83,"./lib/_stream_readable.js":84,"./lib/_stream_transform.js":85,"./lib/_stream_writable.js":86,"_process":77}],90:[function(require,module,exports){
 module.exports=require("./lib/_stream_transform.js");
 
-},{"./lib/_stream_transform.js":79}],85:[function(require,module,exports){
+},{"./lib/_stream_transform.js":85}],91:[function(require,module,exports){
 module.exports=require("./lib/_stream_writable.js");
 
-},{"./lib/_stream_writable.js":80}],86:[function(require,module,exports){
+},{"./lib/_stream_writable.js":86}],92:[function(require,module,exports){
 
 
 
@@ -30804,7 +34964,7 @@
 return dest;
 };
 
-},{"events":56,"inherits":58,"readable-stream/duplex.js":75,"readable-stream/passthrough.js":82,"readable-stream/readable.js":83,"readable-stream/transform.js":84,"readable-stream/writable.js":85}],87:[function(require,module,exports){
+},{"events":62,"inherits":64,"readable-stream/duplex.js":81,"readable-stream/passthrough.js":88,"readable-stream/readable.js":89,"readable-stream/transform.js":90,"readable-stream/writable.js":91}],93:[function(require,module,exports){
 
 
 
@@ -31027,7 +35187,7 @@
 this.charLength=this.charReceived?3:0;
 }
 
-},{"buffer":54}],88:[function(require,module,exports){
+},{"buffer":60}],94:[function(require,module,exports){
 (function(global){
 
 
@@ -31098,16 +35258,16 @@
 }
 
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{}],89:[function(require,module,exports){
-arguments[4][58][0].apply(exports,arguments);
-},{"dup":58}],90:[function(require,module,exports){
+},{}],95:[function(require,module,exports){
+arguments[4][64][0].apply(exports,arguments);
+},{"dup":64}],96:[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';
 };
-},{}],91:[function(require,module,exports){
+},{}],97:[function(require,module,exports){
 (function(process,global){
 
 
@@ -31697,7 +35857,7 @@
 }
 
 }).call(this,require('_process'),typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{"./support/isBuffer":90,"_process":71,"inherits":89}],92:[function(require,module,exports){
+},{"./support/isBuffer":96,"_process":77,"inherits":95}],98:[function(require,module,exports){
 
 
 var langs=[
@@ -39841,7 +44001,7 @@
 return langs;
 };
 
-},{}],93:[function(require,module,exports){
+},{}],99:[function(require,module,exports){
 
 
 
@@ -40435,7 +44595,7 @@
 }};
 
 
-},{}],94:[function(require,module,exports){
+},{}],100:[function(require,module,exports){
 
 
 
@@ -41262,7 +45422,7 @@
 return format;
 };
 
-},{}],95:[function(require,module,exports){
+},{}],101:[function(require,module,exports){
 
 
 
@@ -41483,7 +45643,7 @@
 this.method=method;
 };
 
-},{}],96:[function(require,module,exports){
+},{}],102:[function(require,module,exports){
 
 
 
@@ -41846,7 +46006,7 @@
 return null;
 };
 
-},{}],97:[function(require,module,exports){
+},{}],103:[function(require,module,exports){
 
 
 
@@ -42156,7 +46316,7 @@
 ["woff","application/font-woff"]]);
 
 
-},{}],98:[function(require,module,exports){
+},{}],104:[function(require,module,exports){
 
 
 
@@ -42270,7 +46430,7 @@
 }};
 
 
-},{}],99:[function(require,module,exports){
+},{}],105:[function(require,module,exports){
 
 
 
@@ -42617,7 +46777,7 @@
 return WebInspector.TextRange.comparator(edit1.oldRange,edit2.oldRange);
 };
 
-},{}],100:[function(require,module,exports){
+},{}],106:[function(require,module,exports){
 
 
 
@@ -42927,7 +47087,7 @@
 createTokenizer:function(mimeType){}};
 
 
-},{}],101:[function(require,module,exports){
+},{}],107:[function(require,module,exports){
 
 
 
@@ -43039,7 +47199,7 @@
 }};
 
 
-},{}],102:[function(require,module,exports){
+},{}],108:[function(require,module,exports){
 
 
 
@@ -43195,7 +47355,7 @@
 }};
 
 
-},{}],103:[function(require,module,exports){
+},{}],109:[function(require,module,exports){
 
 
 
@@ -44737,7 +48897,7 @@
 
 };
 
-},{}],104:[function(require,module,exports){
+},{}],110:[function(require,module,exports){
 
 
 
@@ -45116,7 +49276,7 @@
 __proto__:WebInspector.ProfileTreeModel.prototype};
 
 
-},{}],105:[function(require,module,exports){
+},{}],111:[function(require,module,exports){
 
 
 
@@ -45562,7 +49722,7 @@
 Overloaded:"Overloaded"};
 
 
-},{}],106:[function(require,module,exports){
+},{}],112:[function(require,module,exports){
 
 
 
@@ -45816,7 +49976,7 @@
 }};
 
 
-},{}],107:[function(require,module,exports){
+},{}],113:[function(require,module,exports){
 
 
 
@@ -46787,7 +50947,7 @@
 "zoom":200};
 
 
-},{}],108:[function(require,module,exports){
+},{}],114:[function(require,module,exports){
 
 
 
@@ -47089,7 +51249,7 @@
 }};
 
 
-},{}],109:[function(require,module,exports){
+},{}],115:[function(require,module,exports){
 
 
 
@@ -47415,7 +51575,7 @@
 __proto__:WebInspector.CSSRule.prototype};
 
 
-},{}],110:[function(require,module,exports){
+},{}],116:[function(require,module,exports){
 
 
 
@@ -47730,7 +51890,7 @@
 }};
 
 
-},{}],111:[function(require,module,exports){
+},{}],117:[function(require,module,exports){
 
 
 
@@ -48668,7 +52828,7 @@
 
 WebInspector.multitargetNetworkManager;
 
-},{}],112:[function(require,module,exports){
+},{}],118:[function(require,module,exports){
 
 
 
@@ -49933,7 +54093,7 @@
 __proto__:WebInspector.SDKObject.prototype};
 
 
-},{}],113:[function(require,module,exports){
+},{}],119:[function(require,module,exports){
 
 
 
@@ -50060,7 +54220,7 @@
 }};
 
 
-},{}],114:[function(require,module,exports){
+},{}],120:[function(require,module,exports){
 
 
 
@@ -50353,7 +54513,7 @@
 __proto__:WebInspector.SDKObject.prototype};
 
 
-},{}],115:[function(require,module,exports){
+},{}],121:[function(require,module,exports){
 
 
 
@@ -50744,7 +54904,7 @@
 
 WebInspector.targetManager=new WebInspector.TargetManager();
 
-},{}],116:[function(require,module,exports){
+},{}],122:[function(require,module,exports){
 
 
 
@@ -51731,7 +55891,7 @@
 __proto__:WebInspector.TracingModel.NamedObject.prototype};
 
 
-},{}],117:[function(require,module,exports){
+},{}],123:[function(require,module,exports){
 
 
 
@@ -52012,7 +56172,7 @@
 }};
 
 
-},{}],118:[function(require,module,exports){
+},{}],124:[function(require,module,exports){
 
 
 
@@ -52902,7 +57062,7 @@
 __proto__:WebInspector.VBox.prototype};
 
 
-},{}],119:[function(require,module,exports){
+},{}],125:[function(require,module,exports){
 
 
 
@@ -55058,7 +59218,7 @@
 return span;
 };
 
-},{}],120:[function(require,module,exports){
+},{}],126:[function(require,module,exports){
 
 
 
@@ -56370,7 +60530,7 @@
 return model;
 };
 
-},{}],121:[function(require,module,exports){
+},{}],127:[function(require,module,exports){
 
 
 
@@ -57031,7 +61191,7 @@
 this.triggerTime=triggerTime;
 };
 
-},{}],122:[function(require,module,exports){
+},{}],128:[function(require,module,exports){
 
 
 
@@ -57366,7 +61526,7 @@
 
 
 
-},{}],123:[function(require,module,exports){
+},{}],129:[function(require,module,exports){
 
 
 
@@ -57866,7 +62026,7 @@
 return samples;
 };
 
-},{}],124:[function(require,module,exports){
+},{}],130:[function(require,module,exports){
 
 
 
@@ -59665,7 +63825,7 @@
 }};
 
 
-},{}],125:[function(require,module,exports){
+},{}],131:[function(require,module,exports){
 
 
 
@@ -60046,7 +64206,7 @@
 }};
 
 
-},{}],126:[function(require,module,exports){
+},{}],132:[function(require,module,exports){
 
 
 
@@ -60235,7 +64395,7 @@
 __proto__:WebInspector.ViewportDataGridNode.prototype};
 
 
-},{}],127:[function(require,module,exports){
+},{}],133:[function(require,module,exports){
 (function(process){
 
 
@@ -60424,7 +64584,7 @@
 }
 
 }).call(this,require('_process'));
-},{"./debug":128,"_process":71}],128:[function(require,module,exports){
+},{"./debug":134,"_process":77}],134:[function(require,module,exports){
 
 
 
@@ -60628,7 +64788,7 @@
 return val;
 }
 
-},{"ms":140}],129:[function(require,module,exports){
+},{"ms":146}],135:[function(require,module,exports){
 
 
 
@@ -60725,7 +64885,7 @@
 
 };
 
-},{}],130:[function(require,module,exports){
+},{}],136:[function(require,module,exports){
 (function webpackUniversalModuleDefinition(root,factory){
 
 if(typeof exports==='object'&&typeof module==='object')
@@ -67426,7 +71586,7 @@
 
 });
 ;
-},{}],131:[function(require,module,exports){
+},{}],137:[function(require,module,exports){
 (function(Buffer){
 var querystring=require('querystring');
 var trim=require('./trim');
@@ -67733,12 +71893,12 @@
 module.exports=Link;
 
 }).call(this,{"isBuffer":require("../../../lighthouse-extension/node_modules/is-buffer/index.js")});
-},{"../../../lighthouse-extension/node_modules/is-buffer/index.js":59,"./trim":132,"querystring":74}],132:[function(require,module,exports){
+},{"../../../lighthouse-extension/node_modules/is-buffer/index.js":65,"./trim":138,"querystring":80}],138:[function(require,module,exports){
 module.exports=function trim(value){
 return value.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,'');
 };
 
-},{}],133:[function(require,module,exports){
+},{}],139:[function(require,module,exports){
 
 
 
@@ -67883,7 +72043,7 @@
 })(ImageSSIM||(ImageSSIM={}));
 module.exports=ImageSSIM;
 
-},{}],134:[function(require,module,exports){
+},{}],140:[function(require,module,exports){
 var encode=require('./lib/encoder'),
 decode=require('./lib/decoder');
 
@@ -67892,7 +72052,7 @@
 decode:decode};
 
 
-},{"./lib/decoder":135,"./lib/encoder":136}],135:[function(require,module,exports){
+},{"./lib/decoder":141,"./lib/encoder":142}],141:[function(require,module,exports){
 (function(Buffer){
 
 
@@ -68882,7 +73042,7 @@
 }
 
 }).call(this,require("buffer").Buffer);
-},{"buffer":54}],136:[function(require,module,exports){
+},{"buffer":60}],142:[function(require,module,exports){
 (function(Buffer){
 
 
@@ -69652,7 +73812,7 @@
 }
 
 }).call(this,require("buffer").Buffer);
-},{"buffer":54}],137:[function(require,module,exports){
+},{"buffer":60}],143:[function(require,module,exports){
 (function(process){
 
 
@@ -69868,7 +74028,7 @@
 module.exports=Log;
 
 }).call(this,require('_process'));
-},{"_process":71,"debug":127,"events":56}],138:[function(require,module,exports){
+},{"_process":77,"debug":133,"events":62}],144:[function(require,module,exports){
 (function(global){
 
 
@@ -71720,7 +75880,7 @@
 module.exports=isEqual;
 
 }).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{});
-},{}],139:[function(require,module,exports){
+},{}],145:[function(require,module,exports){
 exports.getRenderingDataFromViewport=function(viewportProperties,uaDeviceWidth,uaDeviceHeight,uaMaxZoom,uaMinZoom){
 
 var vw=uaDeviceWidth/100;
@@ -72063,7 +76223,7 @@
 "viewport-fit":["auto","cover"]};
 
 
-},{}],140:[function(require,module,exports){
+},{}],146:[function(require,module,exports){
 
 
 
@@ -72217,7 +76377,7 @@
 return Math.ceil(ms/n)+' '+name+'s';
 }
 
-},{}],141:[function(require,module,exports){
+},{}],147:[function(require,module,exports){
 module.exports=function parseCacheControl(field){
 
 if(typeof field!=='string'){
@@ -72256,7 +76416,436 @@
 return err?null:header;
 };
 
-},{}],142:[function(require,module,exports){
+},{}],148:[function(require,module,exports){
+var URL=require('url').URL;
+
+
+
+
+
+
+
+
+
+
+
+function trimLine(line){
+if(!line){
+return null;
+}
+
+if(Array.isArray(line)){
+return line.map(trimLine);
+}
+
+return String(line).trim();
+}
+
+
+
+
+
+
+
+
+function removeComments(line){
+var commentStartIndex=line.indexOf('#');
+if(commentStartIndex>-1){
+return line.substr(0,commentStartIndex);
+}
+
+return line;
+}
+
+
+
+
+
+
+
+
+function splitLine(line){
+var idx=String(line).indexOf(':');
+
+if(!line||idx<0){
+return null;
+}
+
+return[line.slice(0,idx),line.slice(idx+1)];
+}
+
+
+
+
+
+
+
+
+
+function formatUserAgent(userAgent){
+var formattedUserAgent=userAgent.toLowerCase();
+
+
+var idx=formattedUserAgent.indexOf('/');
+if(idx>-1){
+formattedUserAgent=formattedUserAgent.substr(0,idx);
+}
+
+return formattedUserAgent.trim();
+}
+
+
+
+
+
+
+
+
+
+function normaliseEncoding(path){
+try{
+return urlEncodeToUpper(encodeURI(path).replace(/%25/g,'%'));
+}catch(e){
+return path;
+}
+}
+
+
+
+
+
+
+
+
+
+
+function urlEncodeToUpper(path){
+return path.replace(/%[0-9a-fA-F]{2}/g,function(match){
+return match.toUpperCase();
+});
+}
+
+
+
+
+
+
+
+
+
+
+
+function parsePattern(pattern){
+var regexSpecialChars=/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g;
+var wildCardPattern=/\*/g;
+var endOfLinePattern=/\\\$$/;
+
+pattern=normaliseEncoding(pattern);
+
+if(pattern.indexOf('*')<0&&pattern.indexOf('$')<0){
+return pattern;
+}
+
+pattern=pattern.
+replace(regexSpecialChars,'\\$&').
+replace(wildCardPattern,'(?:.*)').
+replace(endOfLinePattern,'$');
+
+return new RegExp(pattern);
+}
+
+function parseRobots(contents,robots){
+var newlineRegex=/\r\n|\r|\n/;
+var lines=contents.
+split(newlineRegex).
+map(removeComments).
+map(splitLine).
+map(trimLine);
+
+var currentUserAgents=[];
+var isNoneUserAgentState=true;
+for(var i=0;i<lines.length;i++){
+var line=lines[i];
+
+if(!line||!line[0]){
+continue;
+}
+
+switch(line[0].toLowerCase()){
+case'user-agent':
+if(isNoneUserAgentState){
+currentUserAgents.length=0;
+}
+
+if(line[1]){
+currentUserAgents.push(formatUserAgent(line[1]));
+}
+break;
+case'disallow':
+robots.addRule(currentUserAgents,line[1],false,i+1);
+break;
+case'allow':
+robots.addRule(currentUserAgents,line[1],true,i+1);
+break;
+case'crawl-delay':
+robots.setCrawlDelay(currentUserAgents,line[1]);
+break;
+case'sitemap':
+if(line[1]){
+robots.addSitemap(line[1]);
+}
+break;
+case'host':
+if(line[1]){
+robots.setPreferredHost(line[1].toLowerCase());
+}
+break;}
+
+
+isNoneUserAgentState=line[0].toLowerCase()!=='user-agent';
+}
+}
+
+
+
+
+
+
+
+
+
+function findRule(path,rules){
+var matchingRule=null;
+
+for(var i=0;i<rules.length;i++){
+var rule=rules[i];
+
+if(typeof rule.pattern==='string'){
+if(path.indexOf(rule.pattern)!==0){
+continue;
+}
+
+
+if(!matchingRule||rule.pattern.length>matchingRule.pattern.length){
+matchingRule=rule;
+}
+
+
+}else if(rule.pattern.test(path)){
+return rule;
+}
+}
+
+return matchingRule;
+}
+
+
+
+
+
+
+
+
+
+
+function parseUrl(url){
+try{
+return new URL(url);
+}catch(e){
+return null;
+}
+}
+
+
+function Robots(url,contents){
+this._url=parseUrl(url)||{};
+this._url.port=this._url.port||80;
+
+this._rules={};
+this._sitemaps=[];
+this._preferedHost=null;
+
+parseRobots(contents||'',this);
+}
+
+
+
+
+
+
+
+
+
+
+Robots.prototype.addRule=function(userAgents,pattern,allow,lineNumber){
+var rules=this._rules;
+
+userAgents.forEach(function(userAgent){
+rules[userAgent]=rules[userAgent]||[];
+
+if(!pattern){
+return;
+}
+
+rules[userAgent].push({
+pattern:parsePattern(pattern),
+allow:allow,
+lineNumber:lineNumber});
+
+});
+};
+
+
+
+
+
+
+
+Robots.prototype.setCrawlDelay=function(userAgents,delayStr){
+var rules=this._rules;
+var delay=Number(delayStr);
+
+userAgents.forEach(function(userAgent){
+rules[userAgent]=rules[userAgent]||[];
+
+if(isNaN(delay)){
+return;
+}
+
+rules[userAgent].crawlDelay=delay;
+});
+};
+
+
+
+
+
+
+Robots.prototype.addSitemap=function(url){
+this._sitemaps.push(url);
+};
+
+
+
+
+
+
+Robots.prototype.setPreferredHost=function(url){
+this._preferedHost=url;
+};
+
+Robots.prototype._getRule=function(url,ua){
+var parsedUrl=parseUrl(url)||{};
+var userAgent=formatUserAgent(ua||'*');
+
+parsedUrl.port=parsedUrl.port||'80';
+
+
+if(parsedUrl.protocol!==this._url.protocol||
+parsedUrl.hostname!==this._url.hostname||
+parsedUrl.port!==this._url.port){
+return;
+}
+
+var rules=this._rules[userAgent]||this._rules['*']||[];
+var path=urlEncodeToUpper(parsedUrl.pathname+parsedUrl.search);
+var rule=findRule(path,rules);
+
+return rule;
+};
+
+
+
+
+
+
+
+
+
+
+
+Robots.prototype.isAllowed=function(url,ua){
+var rule=this._getRule(url,ua);
+
+if(typeof rule==='undefined'){
+return;
+}
+
+return!rule||rule.allow;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Robots.prototype.getMatchingLineNumber=function(url,ua){
+var rule=this._getRule(url,ua);
+
+return rule?rule.lineNumber:-1;
+};
+
+
+
+
+
+
+
+
+Robots.prototype.isDisallowed=function(url,ua){
+return!this.isAllowed(url,ua);
+};
+
+
+
+
+
+
+
+
+
+Robots.prototype.getCrawlDelay=function(ua){
+var userAgent=formatUserAgent(ua||'*');
+
+return(this._rules[userAgent]||this._rules['*']||{}).crawlDelay;
+};
+
+
+
+
+
+
+Robots.prototype.getPreferredHost=function(){
+return this._preferedHost;
+};
+
+
+
+
+
+
+Robots.prototype.getSitemaps=function(){
+return this._sitemaps.slice(0);
+};
+
+module.exports=Robots;
+
+},{"url":"url"}],149:[function(require,module,exports){
+var Robots=require('./Robots');
+
+module.exports=function(url,contents){
+return new Robots(url,contents);
+};
+},{"./Robots":148}],150:[function(require,module,exports){
 (function(process){
 exports=module.exports=SemVer;
 
@@ -73463,7 +78052,7 @@
 }
 
 }).call(this,require('_process'));
-},{"_process":71}],143:[function(require,module,exports){
+},{"_process":77}],151:[function(require,module,exports){
 (function(Buffer){
 'use strict';
 
@@ -73651,7 +78240,7 @@
 
 
 }).call(this,require("buffer").Buffer);
-},{"buffer":54,"jpeg-js":134}],144:[function(require,module,exports){
+},{"buffer":60,"jpeg-js":140}],152:[function(require,module,exports){
 'use strict';
 
 const frame=require('./frame');
@@ -73709,7 +78298,7 @@
 });
 };
 
-},{"./frame":143,"./speed-index":145}],145:[function(require,module,exports){
+},{"./frame":151,"./speed-index":153}],153:[function(require,module,exports){
 'use strict';
 
 const imageSSIM=require('image-ssim');
@@ -73932,11 +78521,260 @@
 calculateSpeedIndexes};
 
 
-},{"image-ssim":133}],146:[function(require,module,exports){
+},{"image-ssim":139}],154:[function(require,module,exports){
 module.exports={
-"version":"2.9.1"};
+"version":"3.0.0-beta.0"};
 
-},{}],147:[function(require,module,exports){
-module.exports={"npm":{"angular":[{"title":"Cross-site Scripting (XSS)","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-78"],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10170"]},"severity":"medium","semver":{"unaffected":[">=1.2.0"],"vulnerable":["<=1.1.5"]},"credit":["Chirayu Krishnappa"],"CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N","disclosureTime":"2013-06-20T21:00:00.000Z","patches":[],"publicationTime":"2017-01-23T10:00:00.000Z","modificationTime":"2016-11-01T14:08:59.890Z","creationTime":"2016-11-01T14:08:59.890Z","id":"npm:angular:20130621","packageName":"angular","cvssScore":6.8,"alternativeIds":["SNYK-JS-ANGULAR-10170"]},{"title":"Cross-site Scripting (XSS)","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10179"]},"severity":"medium","semver":{"unaffected":[">=1.2.0"],"vulnerable":["<1.2.0 >=1.0.0"]},"credit":["Chirayu Krishnappa"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2013-06-21T21:00:00.000Z","patches":[],"publicationTime":"2017-01-23T10:10:00.000Z","modificationTime":"2016-11-01T15:35:22.355Z","creationTime":"2016-11-01T15:35:22.355Z","id":"npm:angular:20130622","packageName":"angular","cvssScore":5.4,"alternativeIds":["SNYK-JS-ANGULAR-10179"]},{"title":"Arbitrary Script Injection","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-78"],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10140"]},"severity":"high","semver":{"unaffected":[">=1.1.5"],"vulnerable":["<1.1.5"]},"credit":["Chirayu Krishnappa","Igor Minar"],"CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H","disclosureTime":"2013-06-24T21:00:00.000Z","patches":[],"publicationTime":"2017-01-23T10:20:00.000Z","modificationTime":"2016-11-01T12:48:50.251Z","creationTime":"2016-11-01T12:48:50.251Z","id":"npm:angular:20130625","packageName":"angular","cvssScore":8.1,"alternativeIds":["SNYK-JS-ANGULAR-10140"]},{"title":"Protection Bypass","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":[],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10200"]},"severity":"high","semver":{"unaffected":[">=1.2.2"],"vulnerable":["<1.2.2"]},"credit":["Chirayu Krishnappa"],"CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N","disclosureTime":"2013-11-12T22:00:00.000Z","patches":[],"publicationTime":"2017-01-23T10:30:00.000Z","modificationTime":"2016-11-09T12:07:09.956Z","creationTime":"2016-11-09T12:07:09.956Z","id":"npm:angular:20131113","packageName":"angular","cvssScore":7.4,"alternativeIds":["SNYK-JS-ANGULAR-10200"]},{"title":"Arbitrary Code Execution","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":[],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10201"]},"severity":"low","semver":{"unaffected":[">=1.3.0"],"vulnerable":["<1.3.0"]},"credit":["Jann Horn"],"CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N","disclosureTime":"2014-06-07T21:00:00.000Z","patches":[],"publicationTime":"2017-01-23T10:40:00.000Z","modificationTime":"2016-11-09T12:23:07.035Z","creationTime":"2016-11-09T12:23:07.035Z","id":"npm:angular:20140608","packageName":"angular","cvssScore":3.7,"alternativeIds":["SNYK-JS-ANGULAR-10201"]},{"title":"Cross-site Scripting (XSS)","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10191"]},"severity":"medium","semver":{"unaffected":[">=1.3.0-rc.4"],"vulnerable":["<1.3.0-rc.4"]},"credit":["Laurent Trillaud"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","disclosureTime":"2014-09-07T21:00:00.000Z","patches":[],"publicationTime":"2017-01-23T10:50:00.000Z","modificationTime":"2016-11-07T09:46:43.092Z","creationTime":"2016-11-07T09:46:43.092Z","id":"npm:angular:20140908","packageName":"angular","cvssScore":5.3,"alternativeIds":["SNYK-JS-ANGULAR-10191"]},{"title":"Unsafe Object Deserialization","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":[],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10141"]},"severity":"high","semver":{"unaffected":[">=1.2.24"],"vulnerable":["<1.2.24 >=1.2.19"]},"credit":["Chirayu Krishnappa"],"CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N","disclosureTime":"2014-09-08T21:00:00.000Z","patches":[],"publicationTime":"2017-01-23T11:00:00.000Z","modificationTime":"2016-11-01T13:57:31.962Z","creationTime":"2016-11-01T13:57:31.962Z","id":"npm:angular:20140909","packageName":"angular","cvssScore":7.4,"alternativeIds":["SNYK-JS-ANGULAR-10141"]},{"title":"Arbitrary Command Execution","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-78"],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10173"]},"severity":"medium","semver":{"unaffected":[">=1.3.2"],"vulnerable":["<1.3.2"]},"credit":["Sebastian Lekies","Jann Horn","Gábor Molnár"],"CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:L/A:L","disclosureTime":"2014-11-03T22:00:00.000Z","patches":[],"publicationTime":"2017-01-23T11:10:00.000Z","modificationTime":"2016-11-01T12:33:38.496Z","creationTime":"2016-11-01T12:33:38.496Z","id":"npm:angular:20141104","packageName":"angular","cvssScore":6.5,"alternativeIds":["SNYK-JS-ANGULAR-10173"]},{"title":"Arbitrary Code Execution","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-78"],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10174"]},"severity":"high","semver":{"unaffected":[">=1.5.0-beta.2"],"vulnerable":["<1.5.0-beta.2"]},"credit":["Rodric Haddad"],"CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N","disclosureTime":"2015-03-09T22:00:00.000Z","patches":[],"publicationTime":"2017-01-23T11:20:00.000Z","modificationTime":"2017-02-13T14:24:12.988Z","creationTime":"2016-11-01T14:24:12.988Z","id":"npm:angular:20150310","packageName":"angular","cvssScore":7.4,"alternativeIds":["SNYK-JS-ANGULAR-10174"]},{"title":"JSONP Callback Attack","credit":["Pete Bacon Darwin"],"moduleName":"angular","packageName":"angular","language":"js","packageManager":"npm","id":"npm:angular:20150315","identifiers":{"CWE":[],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10175"]},"semver":{"vulnerable":["<1.6.1"],"unaffected":[">=1.6.1"]},"patches":[],"cvssScore":6.5,"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","disclosureTime":"2015-03-14T22:00:00.000Z","publicationTime":"2017-02-13T18:30:00.000Z","modificationTime":"2017-02-13T14:36:18.735Z","creationTime":"2016-11-01T14:36:18.735Z","alternativeIds":["SNYK-JS-ANGULAR-10175"]},{"title":"Cross-site Scripting (XSS)","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-78"],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10176"]},"severity":"high","semver":{"unaffected":[">=1.5.0-beta.0"],"vulnerable":["<1.5.0-beta.0 >=1.0.0"]},"credit":["Igor Minar"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N","disclosureTime":"2015-08-06T21:00:00.000Z","patches":[],"publicationTime":"2017-01-23T11:40:00.000Z","modificationTime":"2016-11-01T13:30:14.967Z","creationTime":"2016-11-01T13:30:14.967Z","id":"npm:angular:20150807","packageName":"angular","cvssScore":7.1,"alternativeIds":["SNYK-JS-ANGULAR-10176"]},{"title":"Clickjacking","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-693"],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10177"]},"severity":"medium","semver":{"unaffected":[">=1.5.0-beta.0"],"vulnerable":["<1.5.0-beta.0 >=1.3.1"]},"credit":["Igor Minar"],"CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N","disclosureTime":"2015-08-06T21:00:00.000Z","patches":[],"publicationTime":"2017-01-23T11:50:00.000Z","modificationTime":"2016-11-01T13:30:14.967Z","creationTime":"2016-11-01T13:30:14.967Z","id":"npm:angular:20150807-1","packageName":"angular","cvssScore":6.8,"alternativeIds":["SNYK-JS-ANGULAR-10177"]},{"title":"Cross-site Scripting (XSS)","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":[],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10182"]},"severity":"high","semver":{"unaffected":[">=1.5.0-beta.2"],"vulnerable":["<1.5.0-beta.2"]},"credit":["Igor Minar"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N","disclosureTime":"2015-09-08T21:00:00.000Z","patches":[],"publicationTime":"2017-01-23T12:00:00.000Z","modificationTime":"2016-11-02T08:40:11.750Z","creationTime":"2016-11-02T08:40:11.750Z","id":"npm:angular:20150909","packageName":"angular","cvssScore":7.1,"alternativeIds":["SNYK-JS-ANGULAR-10182"]},{"title":"Cross-site Scripting (XSS)","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10180"]},"severity":"medium","semver":{"unaffected":[">=1.4.10"],"vulnerable":["<1.4.10"]},"credit":["Lucas Mirelmann"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2015-11-29T22:00:00.000Z","patches":[],"publicationTime":"2017-01-23T12:10:00.000Z","modificationTime":"2016-11-02T08:16:55.157Z","creationTime":"2016-11-02T08:16:55.157Z","id":"npm:angular:20151130","packageName":"angular","cvssScore":5.4,"alternativeIds":["SNYK-JS-ANGULAR-10180"]},{"title":"Cross-site Scripting (XSS)","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10181"]},"severity":"medium","semver":{"unaffected":[">=1.5.0-rc.0"],"vulnerable":["<1.5.0-rc.0"]},"credit":["Pete Bacon Darwin"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N","disclosureTime":"2015-12-04T22:00:00.000Z","patches":[],"publicationTime":"2017-01-23T12:20:00.000Z","modificationTime":"2016-11-02T08:26:38.753Z","creationTime":"2016-11-02T08:26:38.753Z","id":"npm:angular:20151205","packageName":"angular","cvssScore":4.3,"alternativeIds":["SNYK-JS-ANGULAR-10181"]},{"title":"Cross-site Scripting (XSS)","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10202"]},"severity":"medium","semver":{"unaffected":[">=1.5.0-rc.2"],"vulnerable":["<1.5.0-rc.2 >=1.3.0"]},"credit":["Lucas Mirelmann"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N","disclosureTime":"2016-01-21T22:00:00.000Z","patches":[],"publicationTime":"2017-01-23T12:30:00.000Z","modificationTime":"2016-11-09T12:45:57.682Z","creationTime":"2016-11-09T12:45:57.682Z","id":"npm:angular:20160122","packageName":"angular","cvssScore":4.3,"alternativeIds":["SNYK-JS-ANGULAR-10202"]},{"title":"Arbitrary Script Injection","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":[],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10203"]},"severity":"medium","semver":{"unaffected":[">=1.2.30"],"vulnerable":["<1.2.30 >=1.0.0"]},"credit":["Raphaël Jamet"],"CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N","disclosureTime":"2016-05-26T21:00:00.000Z","patches":[],"publicationTime":"2017-01-23T12:40:00.000Z","modificationTime":"2016-11-09T13:00:18.135Z","creationTime":"2016-11-09T13:00:18.135Z","id":"npm:angular:20160527","packageName":"angular","cvssScore":4.8,"alternativeIds":["SNYK-JS-ANGULAR-10203"]},{"title":"Content Security Policy (CSP) Bypass","moduleName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":[],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-10190"]},"severity":"medium","semver":{"unaffected":[">=1.5.9"],"vulnerable":["<1.5.9 >=1.5.0"]},"credit":["Martin Probst"],"CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:L/A:N","disclosureTime":"2016-10-31T22:00:00.000Z","patches":[],"publicationTime":"2017-01-23T12:50:00.000Z","modificationTime":"2017-01-24T09:16:32.893Z","creationTime":"2016-11-07T09:16:32.893Z","id":"npm:angular:20161101","packageName":"angular","cvssScore":6.5,"alternativeIds":["SNYK-JS-ANGULAR-10190"]},{"title":"Cross-site Scripting (XSS)","credit":["Unknown"],"moduleName":"angular","packageName":"angular","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-ANGULAR-12026"]},"semver":{"unaffected":[">=1.6.7"],"vulnerable":["<1.6.7"]},"patches":[],"cvssScore":6.5,"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","disclosureTime":"2017-10-17T21:00:00.000Z","publicationTime":"2017-12-25T14:45:01.473Z","modificationTime":"2017-12-19T11:18:55.007Z","creationTime":"2017-12-19T11:18:55.007Z","id":"npm:angular:20171018","alternativeIds":["SNYK-JS-ANGULAR-12026"]}],"backbone":[{"title":"Cross-site Scripting (XSS)","credit":[],"language":"js","packageManager":"npm","packageName":"backbone","moduleName":"backbone","semver":{"vulnerable":["<0.5.0"],"unaffected":[">=0.5.0"]},"identifiers":{"CWE":[],"CVE":[],"ALTERNATIVE":["SNYK-JS-BACKBONE-10054"]},"patches":[{"urls":["https://s3.amazonaws.com/snyk-rules-pre-repository/snapshots/master/patches/npm/backbone/20110701/backbone_20110701_0_0_0cdc525961d3fa98e810ffae6bcc8e3838e36d93.patch"],"version":"<0.5.0 >=0.3.3","modificationTime":"2015-11-06T02:09:36.180Z","comments":["https://github.com/jashkenas/backbone/commit/0cdc525961d3fa98e810ffae6bcc8e3838e36d93.patch"],"id":"patch:npm:backbone:20110701:0"}],"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","creationTime":"2015-11-06T02:09:36.180Z","publicationTime":"2015-11-06T02:09:36.180Z","modificationTime":"2015-11-06T02:09:36.180Z","disclosureTime":"2015-11-06T02:09:36.180Z","id":"npm:backbone:20110701","cvssScore":6.5,"alternativeIds":["SNYK-JS-BACKBONE-10054"]},{"title":"Cross-site Scripting (XSS)","credit":["Unknown"],"creationTime":"2016-05-24T06:45:20.086Z","modificationTime":"2016-05-24T06:45:20.086Z","publicationTime":"2016-06-22T17:50:20.000Z","disclosureTime":"2016-05-23T17:50:20.000Z","semver":{"vulnerable":["<= 0.3.3"],"unaffected":[">= 0.5.0"]},"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","severity":"medium","identifiers":{"CWE":["CWE-79"],"CVE":[],"NSP":108,"ALTERNATIVE":["SNYK-JS-BACKBONE-10110"]},"patches":[],"moduleName":"backbone","language":"js","packageManager":"npm","id":"npm:backbone:20160523","packageName":"backbone","cvssScore":6.5,"alternativeIds":["SNYK-JS-BACKBONE-10110"]}],"bootstrap":[{"title":"Cross-site Scripting (XSS)","credit":["Peter Corsaro"],"packageName":"bootstrap","moduleName":"bootstrap","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-BOOTSTRAP-10433"]},"semver":{"unaffected":[">=2.1.0"],"vulnerable":["<2.1.0"]},"patches":[],"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","disclosureTime":"2012-05-09T21:00:00.000Z","publicationTime":"2017-04-10T09:39:59.975Z","modificationTime":"2017-02-27T10:05:00.075Z","creationTime":"2017-02-27T10:05:00.075Z","id":"npm:bootstrap:20120510","cvssScore":6.5,"alternativeIds":["SNYK-JS-BOOTSTRAP-10433"]},{"title":"Cross-Site Scripting (XSS)","credit":["Unknown"],"moduleName":"bootstrap","packageName":"bootstrap","language":"js","packageManager":"npm","identifiers":{"CVE":[],"CWE":["CWE-79"],"ALTERNATIVE":["SNYK-JS-BOOTSTRAP-10860"]},"semver":{"unaffected":[">=3.4.0 <4.0.0-alpha || >4.0.0-beta.2"],"vulnerable":["<3.4.0 || >=4.0.0-alpha <4.0.0-beta.2"]},"severity":"medium","cvssScore":6.5,"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","patches":[],"creationTime":"2017-11-25T17:23:26.518Z","modificationTime":"2017-11-25T17:23:26.518Z","publicationTime":"2018-01-19T09:37:48.056Z","disclosureTime":"2016-06-27T17:23:26.518Z","id":"npm:bootstrap:20160627","alternativeIds":["SNYK-JS-BOOTSTRAP-10860"]}],"dojo":[{"title":"Cross-site Scripting (XSS)","credit":[],"semver":{"vulnerable":["<1.1"],"unaffected":[">=1.1"]},"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","severity":"medium","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2008-6681"],"ALTERNATIVE":["SNYK-JS-DOJO-10051"]},"patches":[],"moduleName":"dojo","creationTime":"2015-11-06T02:09:36.180Z","publicationTime":"2015-11-06T02:09:36.180Z","modificationTime":"2015-11-06T02:09:36.180Z","disclosureTime":"2015-11-06T02:09:36.180Z","language":"js","packageManager":"npm","id":"npm:dojo:20090409","packageName":"dojo","cvssScore":6.5,"alternativeIds":["SNYK-JS-DOJO-10051"]},{"title":"Cross-site Scripting (XSS)","credit":[],"semver":{"vulnerable":[">=0.4 <0.4.4 || >=1.0 <1.0.3 || >=1.1 <1.1.2 || >=1.2 <1.2.4 || >=1.3 <1.3.3 || >=1.4 <1.4.2"],"unaffected":["<0.4 >=0.4.4 || <1.0 >=1.0.3 || <1.1 >=1.1.2 || <1.2 >=1.2.4 || <1.3 >=1.3.3 || <1.4 >=1.4.2"]},"CVSSv2":"CVSS:2.0/AV:N/AC:L/Au:N/C:C/I:C/A:C","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H","severity":"high","identifiers":{"CWE":["CWE-16"],"CVE":["CVE-2010-2276","CVE-2010-2272"],"ALTERNATIVE":["npm:dojo:20100614-1","npm:dojo:20100614-2","npm:dojo:20100614-3","npm:dojo:20100614-4","npm:dojo:20100614-5","SNYK-JS-DOJO-10052"]},"patches":[],"moduleName":"dojo","creationTime":"2015-11-06T02:09:36.180Z","publicationTime":"2015-11-06T02:09:36.180Z","modificationTime":"2015-11-06T02:09:36.180Z","disclosureTime":"2015-11-06T02:09:36.180Z","language":"js","packageManager":"npm","id":"npm:dojo:20100614","packageName":"dojo","cvssScore":10,"alternativeIds":["npm:dojo:20100614-1","npm:dojo:20100614-2","npm:dojo:20100614-3","npm:dojo:20100614-4","npm:dojo:20100614-5","SNYK-JS-DOJO-10052"]},{"title":"Cross-site Scripting (XSS)","credit":[],"semver":{"vulnerable":["<1.4.2"],"unaffected":[">=1.4.2"]},"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","severity":"medium","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2010-2275"],"ALTERNATIVE":["SNYK-JS-DOJO-10053"]},"patches":[],"moduleName":"dojo","creationTime":"2015-11-06T02:09:36.180Z","publicationTime":"2015-11-06T02:09:36.180Z","modificationTime":"2015-11-06T02:09:36.180Z","disclosureTime":"2015-11-06T02:09:36.180Z","language":"js","packageManager":"npm","id":"npm:dojo:20100614-6","packageName":"dojo","cvssScore":6.5,"alternativeIds":["SNYK-JS-DOJO-10053"]},{"title":"Cross Site Scripting","credit":["Unknown"],"creationTime":"2016-05-24T06:45:20.086Z","modificationTime":"2016-05-24T06:45:20.086Z","publicationTime":"2016-06-22T00:00:00.000Z","disclosureTime":"2016-05-23T16:48:27.000Z","semver":{"vulnerable":["<= 1.0.0"],"unaffected":[">= 1.1.0"]},"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N","severity":"medium","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2008-6681"],"NSP":107,"ALTERNATIVE":["SNYK-JS-DOJO-10108"]},"patches":[],"moduleName":"dojo","language":"js","packageManager":"npm","id":"npm:dojo:20160523","packageName":"dojo","cvssScore":4.3,"alternativeIds":["SNYK-JS-DOJO-10108"]}],"foundation-sites":[{"title":"Cross-site Scripting (XSS)","credit":["Mathieu Amiot"],"moduleName":"foundation-sites","packageName":"foundation-sites","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-FOUNDATIONSITES-10413"]},"semver":{"unaffected":[">=3.0.6"],"vulnerable":["<3.0.6 >=3.0.0"]},"patches":[],"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","cvssScore":6.5,"disclosureTime":"2012-07-16T21:00:00.000Z","publicationTime":"2017-03-13T08:00:22.155Z","modificationTime":"2017-03-06T12:29:55.952Z","creationTime":"2017-03-06T12:29:55.952Z","id":"npm:foundation-sites:20120717","alternativeIds":["SNYK-JS-FOUNDATIONSITES-10413"]},{"title":"Cross-site Scripting (XSS)","credit":["Maya Kokits"],"moduleName":"foundation-sites","packageName":"foundation-sites","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-FOUNDATIONSITES-10414"]},"semver":{"unaffected":[">=5.5.3"],"vulnerable":["<5.5.3"]},"patches":[],"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","disclosureTime":"2015-06-18T21:00:00.000Z","publicationTime":"2017-03-13T08:00:22.227Z","modificationTime":"2017-03-06T12:57:37.670Z","creationTime":"2017-03-06T12:57:37.670Z","id":"npm:foundation-sites:20150619","cvssScore":6.5,"alternativeIds":["SNYK-JS-FOUNDATIONSITES-10414"]},{"title":"Cross-site Scripting (XSS)","credit":["Nathaniel Paulus"],"moduleName":"foundation-sites","packageName":"foundation-sites","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-FOUNDATIONSITES-10743"]},"semver":{"vulnerable":["<6.0.0"],"unaffected":[">=6.0.0"]},"patches":[],"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","cvssScore":6.5,"disclosureTime":"2017-08-01T21:00:00.000Z","publicationTime":"2017-08-02T13:09:44.451Z","modificationTime":"2017-08-02T10:42:11.945Z","creationTime":"2017-08-02T10:42:11.945Z","id":"npm:foundation-sites:20170802","alternativeIds":["SNYK-JS-FOUNDATIONSITES-10743"]}],"handlebars":[{"title":"Cross-site Scripting (XSS)","credit":[],"semver":{"vulnerable":["<=1.0.0-beta.3"],"unaffected":[">1.0.0-beta.3"]},"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N","severity":"medium","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-HANDLEBARS-10047"]},"patches":[{"urls":["https://s3.amazonaws.com/snyk-rules-pre-repository/snapshots/master/patches/npm/handlebars/20110425/handlebars_20110425_0_0_b291a1ad8c9a33f834d126450635f0b6ca546a0c.patch"],"version":"<=1.0.0-beta.3","modificationTime":"2015-11-06T02:09:36.180Z","comments":["https://github.com/rgrove/handlebars.js/commit/b291a1ad8c9a33f834d126450635f0b6ca546a0c.patch"],"id":"patch:npm:handlebars:20110425:0"}],"moduleName":"handlebars","creationTime":"2015-11-06T02:09:36.180Z","publicationTime":"2015-11-06T02:09:36.180Z","modificationTime":"2015-11-06T02:09:36.180Z","disclosureTime":"2015-11-06T02:09:36.180Z","language":"js","packageManager":"npm","id":"npm:handlebars:20110425","packageName":"handlebars","cvssScore":5.3,"alternativeIds":["SNYK-JS-HANDLEBARS-10047"]},{"title":"Content Injection (XSS)","credit":["Matias P. Brutti"],"semver":{"vulnerable":["<4.0.0"],"unaffected":[">=4.0.0"]},"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N","severity":"medium","identifiers":{"CWE":["CWE-79"],"CVE":[],"NSP":61,"ALTERNATIVE":["SNYK-JS-HANDLEBARS-10068"]},"patches":[{"urls":["https://s3.amazonaws.com/snyk-rules-pre-repository/snapshots/master/patches/npm/handlebars/20151207/handlebars_0.patch"],"version":"<4.0.0 >=3.0.2","modificationTime":"2015-12-14T23:52:16.811Z","comments":["https://github.com/wycats/handlebars.js/commit/83b8e846a3569bd366cf0b6bdc1e4604d1a2077e"],"id":"patch:npm:handlebars:20151207:0"}],"moduleName":"handlebars","creationTime":"2015-12-14T23:52:16.811Z","modificationTime":"2015-12-14T23:52:16.811Z","publicationTime":"2015-12-14T23:52:16.811Z","disclosureTime":"2015-12-07T16:52:07.962Z","language":"js","packageManager":"npm","id":"npm:handlebars:20151207","packageName":"handlebars","cvssScore":5.3,"alternativeIds":["SNYK-JS-HANDLEBARS-10068"]}],"jquery":[{"title":"Cross-site Scripting (XSS)","moduleName":"jquery","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2011-4969"],"ALTERNATIVE":["SNYK-JS-JQUERY-10183"]},"severity":"medium","semver":{"unaffected":[">=1.6.3"],"vulnerable":["<1.6.3"]},"credit":["Dave Methvin"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2011-06-05T21:00:00.000Z","patches":[],"publicationTime":"2016-10-20T14:16:53.138Z","modificationTime":"2016-11-06T15:25:26.117Z","creationTime":"2016-11-06T15:25:26.117Z","id":"npm:jquery:20110606","packageName":"jquery","cvssScore":5.4,"alternativeIds":["SNYK-JS-JQUERY-10183"]},{"title":"Cross-site Scripting (XSS)","moduleName":"jquery","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2012-6708"],"NSP":329,"ALTERNATIVE":["SNYK-JS-JQUERY-10184"]},"severity":"medium","semver":{"unaffected":[">=1.9.0"],"vulnerable":["<1.9.0 >=1.7.1"]},"credit":["Richard Gibson"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2012-06-19T21:00:00.000Z","patches":[],"publicationTime":"2016-10-20T14:16:53.138Z","modificationTime":"2017-03-12T14:17:57.686Z","creationTime":"2016-11-06T13:53:57.686Z","id":"npm:jquery:20120206","packageName":"jquery","cvssScore":5.4,"alternativeIds":["SNYK-JS-JQUERY-10184"]},{"title":"DOM Based Cross-site Scripting (XSS)","moduleName":"jquery","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2014-6071"],"ALTERNATIVE":["SNYK-JS-JQUERY-10185"]},"severity":"medium","semver":{"unaffected":[">=1.6.2"],"vulnerable":["<=1.5.1 >=1.4.2"]},"credit":["Mauro Risonho de Paula Assumpção"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2014-09-01T21:00:00.000Z","patches":[],"publicationTime":"2016-10-20T14:16:53.138Z","modificationTime":"2016-10-06T14:16:53.138Z","creationTime":"2016-11-06T14:16:53.138Z","id":"npm:jquery:20140902","packageName":"jquery","cvssScore":5.4,"alternativeIds":["SNYK-JS-JQUERY-10185"]},{"title":"Cross-site Scripting (XSS)","moduleName":"jquery","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2015-9251"],"NSP":328,"ALTERNATIVE":["SNYK-JS-JQUERY-10186"]},"severity":"medium","semver":{"unaffected":[">=3.0.0-beta1 || >=1.12.0 <1.12.3"],"vulnerable":["<3.0.0-beta1 >1.12.3 || <1.12.0 >=1.4.0"]},"credit":["Egor Homakov"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2015-06-26T21:00:00.000Z","patches":[],"publicationTime":"2016-11-27T00:00:00.000Z","modificationTime":"2017-03-27T15:12:44.538Z","creationTime":"2016-11-06T15:12:44.538Z","id":"npm:jquery:20150627","packageName":"jquery","cvssScore":5.4,"alternativeIds":["SNYK-JS-JQUERY-10186"]},{"title":"Denial of Service (DoS)","moduleName":"jquery","language":"js","packageManager":"npm","identifiers":{"CWE":[],"CVE":["CVE-2016-10707"],"NSP":330,"ALTERNATIVE":["SNYK-JS-JQUERY-10187"]},"severity":"low","semver":{"unaffected":[">=3.0.0"],"vulnerable":["<3.0.0 >=2.1.0-beta1"]},"credit":["MichaÅ‚ Gołębiowski"],"CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L","disclosureTime":"2016-05-28T21:00:00.000Z","patches":[],"publicationTime":"2016-12-26T15:37:35.224Z","modificationTime":"2016-12-26T15:37:35.224Z","creationTime":"2016-11-06T15:37:35.224Z","id":"npm:jquery:20160529","packageName":"jquery","cvssScore":3.7,"alternativeIds":["SNYK-JS-JQUERY-10187"]}],"jquery-mobile":[{"title":"Cross-site Scripting (XSS)","moduleName":"jquery-mobile","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-JQUERYMOBILE-10199"]},"severity":"medium","semver":{"unaffected":[">=1.2.0"],"vulnerable":["<1.2.0"]},"credit":["Masato Kinugawa"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N","disclosureTime":"2012-08-01T21:00:00.000Z","patches":[],"publicationTime":"2016-12-26T11:28:34.624Z","modificationTime":"2016-12-26T11:28:34.624Z","creationTime":"2016-11-09T11:28:34.624Z","id":"npm:jquery-mobile:20120802","packageName":"jquery-mobile","cvssScore":6.5,"alternativeIds":["SNYK-JS-JQUERYMOBILE-10199"]}],"jquery-ui":[{"title":"Cross-site Scripting (XSS)","moduleName":"jquery-ui","packageName":"jquery-ui","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2010-5312"],"ALTERNATIVE":["SNYK-JS-JQUERYUI-10188"]},"severity":"medium","semver":{"unaffected":[">=1.10.0"],"vulnerable":["<1.10.0"]},"credit":["shadowman131"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N","disclosureTime":"2010-09-02T21:00:00.000Z","patches":[],"publicationTime":"2017-02-13T14:37:13.516Z","modificationTime":"2017-02-13T14:37:13.516Z","creationTime":"2016-12-26T14:37:13.516Z","id":"npm:jquery-ui:20100903","cvssScore":4.3,"alternativeIds":["SNYK-JS-JQUERYUI-10188"]},{"title":"Cross-site Scripting (XSS) via Tooltip","moduleName":"jquery-ui","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2012-6662"],"ALTERNATIVE":["SNYK-JS-JQUERYUI-10189"]},"severity":"medium","semver":{"unaffected":[">=1.10.0"],"vulnerable":["<1.10.0"]},"credit":["Scott González"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N","disclosureTime":"2012-11-26T22:00:00.000Z","patches":[],"publicationTime":"2016-12-26T15:04:27.065Z","modificationTime":"2016-12-26T15:04:27.065Z","creationTime":"2016-11-06T15:04:27.065Z","id":"npm:jquery-ui:20121127","packageName":"jquery-ui","cvssScore":4.3,"alternativeIds":["SNYK-JS-JQUERYUI-10189"]},{"title":"XSS in dialog closeText","credit":["Phat Ly"],"creationTime":"2016-07-22T00:00:02.715Z","modificationTime":"2016-07-22T00:00:02.715Z","publicationTime":"2016-07-21T22:21:41.000Z","disclosureTime":"2016-07-21T22:21:41.000Z","semver":{"vulnerable":["<=1.11.4"],"unaffected":[">=1.12.0"]},"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N","severity":"high","identifiers":{"CWE":["CWE-79"],"CVE":[],"NSP":127,"ALTERNATIVE":["SNYK-JS-JQUERYUI-10118"]},"patches":[],"moduleName":"jquery-ui","language":"js","packageManager":"npm","id":"npm:jquery-ui:20160721","packageName":"jquery-ui","cvssScore":7.3,"alternativeIds":["SNYK-JS-JQUERYUI-10118"]}],"knockout":[{"title":"Cross-site Scripting (XSS)","credit":["Steven Sanderson"],"moduleName":"knockout","packageName":"knockout","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-KNOCKOUT-10415"]},"semver":{"unaffected":[">=3.0.0"],"vulnerable":["<3.0.0 >=2.1.0-pre"]},"patches":[],"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2013-06-30T21:00:00.000Z","publicationTime":"2017-03-13T08:00:22.295Z","modificationTime":"2017-03-01T12:39:34.669Z","creationTime":"2017-03-01T12:39:34.669Z","id":"npm:knockout:20130701","cvssScore":5.4,"alternativeIds":["SNYK-JS-KNOCKOUT-10415"]}],"moment":[{"title":"Regular Expression Denial of Service (ReDoS)","credit":["Adam Baldwin"],"language":"js","packageManager":"npm","moduleName":"moment","packageName":"moment","id":"npm:moment:20160126","semver":{"vulnerable":["<=2.11.1"],"unaffected":[">2.11.1"]},"identifiers":{"CWE":["CWE-400"],"CVE":[],"NSP":55,"ALTERNATIVE":["SNYK-JS-MOMENT-10084"]},"patches":[{"urls":["https://s3.amazonaws.com/snyk-rules-pre-repository/snapshots/master/patches/npm/moment/20160126/moment_20160126_0_0_34af63b8b21208a949dfaf42d228502c73d20ec0.patch"],"version":"<=2.11.1 >2.10.6","modificationTime":"2016-01-26T20:04:21.225Z","comments":[],"id":"patch:npm:moment:20160126:0"},{"urls":["https://s3.amazonaws.com/snyk-rules-pre-repository/snapshots/master/patches/npm/moment/20160126/moment_20160126_0_1_34af63b8b21208a949dfaf42d228502c73d20ec0.patch"],"version":"<=2.10.6 >2.9.0","modificationTime":"2016-01-26T20:04:21.225Z","comments":[],"id":"patch:npm:moment:20160126:1"},{"urls":["https://s3.amazonaws.com/snyk-rules-pre-repository/snapshots/master/patches/npm/moment/20160126/moment_20160126_0_2_34af63b8b21208a949dfaf42d228502c73d20ec0.patch"],"version":"<=2.9.0 >2.2.1","modificationTime":"2016-01-26T20:04:21.225Z","comments":[],"id":"patch:npm:moment:20160126:2"},{"urls":["https://s3.amazonaws.com/snyk-rules-pre-repository/snapshots/master/patches/npm/moment/20160126/moment_20160126_0_3_34af63b8b21208a949dfaf42d228502c73d20ec0.patch"],"version":"=2.2.1","modificationTime":"2016-01-26T20:04:21.225Z","comments":[],"id":"patch:npm:moment:20160126:3"},{"urls":["https://s3.amazonaws.com/snyk-rules-pre-repository/snapshots/master/patches/npm/moment/20160126/moment_20160126_0_4_34af63b8b21208a949dfaf42d228502c73d20ec0.patch"],"version":"<2.2.1 >2.0.0","modificationTime":"2016-01-26T20:04:21.225Z","comments":[],"id":"patch:npm:moment:20160126:4"}],"cvssScore":5.3,"severity":"low","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","disclosureTime":"2016-01-26T20:04:21.225Z","creationTime":"2016-02-01T19:00:03.862Z","modificationTime":"2016-09-28T19:00:03.862Z","publicationTime":"2016-02-01T19:00:03.862Z","alternativeIds":["SNYK-JS-MOMENT-10084"]},{"title":"Regular Expression Denial of Service (ReDoS)","credit":["Snyk Security Research Team"],"language":"js","packageManager":"npm","moduleName":"moment","packageName":"moment","id":"npm:moment:20161019","identifiers":{"CWE":["CWE-400"],"CVE":[],"ALTERNATIVE":["SNYK-JS-MOMENT-10164"]},"semver":{"vulnerable":["<2.15.2"],"unaffected":[">=2.15.2"]},"patches":[{"urls":["https://s3.amazonaws.com/snyk-rules-pre-repository/snapshots/master/patches/npm/moment/20161019/moment_20161019_0_1.patch"],"version":"<2.15.2 >=2.14.0","modificationTime":"2016-10-24T00:00:00.000Z","comments":[],"id":"patch:npm:moment:20161019:0"},{"urls":["https://s3.amazonaws.com/snyk-rules-pre-repository/snapshots/master/patches/npm/moment/20161019/moment_20161019_0_0.patch"],"version":"<2.14.0 >=2.12.0","modificationTime":"2016-10-24T00:00:00.000Z","comments":[],"id":"patch:npm:moment:20161019:1"}],"cvssScore":5.9,"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H","disclosureTime":"2016-10-18T21:00:00.000Z","publicationTime":"2016-10-24T06:57:59.675Z","modificationTime":"2016-10-23T06:57:59.675Z","creationTime":"2016-10-23T06:57:59.675Z","alternativeIds":["SNYK-JS-MOMENT-10164"]},{"title":"Regular Expression Denial of Service (ReDoS)","credit":["Cristian-Alexandru Staicu"],"moduleName":"moment","packageName":"moment","language":"js","packageManager":"npm","identifiers":{"NSP":532,"CWE":["CWE-400"],"CVE":[],"ALTERNATIVE":["SNYK-JS-MOMENT-10841"]},"semver":{"unaffected":[">=2.19.3"],"vulnerable":["<2.19.3"]},"patches":[{"urls":["https://s3.amazonaws.com/snyk-rules-pre-repository/snapshots/master/patches/npm/moment/20170905/moment_0_0_69ed9d44957fa6ab12b73d2ae29d286a857b80eb.patch"],"version":"<2.19.3 >=2.16.0","modificationTime":"2017-11-30T14:47:22.471Z","comments":[],"id":"patch:npm:moment:20170905:0"}],"cvssScore":3.7,"severity":"low","CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L","disclosureTime":"2017-09-05T21:00:00.000Z","publicationTime":"2017-11-28T14:47:22.471Z","modificationTime":"2017-11-28T06:55:05.106Z","creationTime":"2017-09-13T07:55:05.106Z","id":"npm:moment:20170905","alternativeIds":["SNYK-JS-MOMENT-10841"]}],"mustache":[{"title":"Cross-site Scripting (XSS)","credit":[],"semver":{"vulnerable":["< 0.3.1"],"unaffected":[">= 0.3.1"]},"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","severity":"medium","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-MUSTACHE-10046"]},"patches":[],"moduleName":"mustache","creationTime":"2015-11-06T02:09:36.180Z","publicationTime":"2015-11-06T02:09:36.180Z","modificationTime":"2015-11-06T02:09:36.180Z","disclosureTime":"2015-11-06T02:09:36.180Z","language":"js","packageManager":"npm","id":"npm:mustache:20110814","packageName":"mustache","cvssScore":5.4,"alternativeIds":["SNYK-JS-MUSTACHE-10046"]},{"title":"Content Injection due to quoteless attributes","credit":["Matias P. Brutti"],"semver":{"vulnerable":["<2.2.1"],"unaffected":[">=2.2.1"]},"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N","severity":"medium","identifiers":{"CWE":["CWE-79"],"CVE":[],"NSP":62,"ALTERNATIVE":["SNYK-JS-MUSTACHE-10067"]},"patches":[{"urls":["https://s3.amazonaws.com/snyk-rules-pre-repository/snapshots/master/patches/npm/mustache/20151207/mustache_0.patch"],"version":"<2.2.1 >=2.1.0","modificationTime":"2015-12-14T23:52:16.806Z","comments":["https://github.com/janl/mustache.js/commit/378bcca8a5cfe4058f294a3dbb78e8755e8e0da5"],"id":"patch:npm:mustache:20151207:0"}],"moduleName":"mustache","creationTime":"2015-12-14T23:52:16.806Z","modificationTime":"2015-12-14T23:52:16.806Z","publicationTime":"2015-12-14T23:52:16.806Z","disclosureTime":"2015-12-07T17:13:57.565Z","language":"js","packageManager":"npm","id":"npm:mustache:20151207","packageName":"mustache","cvssScore":5.3,"alternativeIds":["SNYK-JS-MUSTACHE-10067"]}],"react":[{"title":"Cross-site Scripting (XSS)","moduleName":"react","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2013-7035"],"ALTERNATIVE":["SNYK-JS-REACT-10192"]},"severity":"medium","semver":{"unaffected":[">=0.5.2 || <=0.3.x || =0.4.2"],"vulnerable":[">=0.5.0 <0.5.2 || >=0.4.0 <0.4.2"]},"credit":["Paul O’Shannessy","Thomas Aylott"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N","disclosureTime":"2013-12-16T22:00:00.000Z","patches":[],"publicationTime":"2017-01-18T14:00:21.094Z","modificationTime":"2016-11-08T08:23:21.094Z","creationTime":"2016-11-08T08:23:21.094Z","id":"npm:react:20131217","packageName":"react","cvssScore":6.5,"alternativeIds":["SNYK-JS-REACT-10192"]},{"title":"Cross-site Scripting (XSS)","moduleName":"react","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-REACT-10193"]},"severity":"high","semver":{"unaffected":[">=0.14.0"],"vulnerable":["<0.14.0"]},"credit":["Daniel LeCheminant"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N","disclosureTime":"2015-03-17T22:00:00.000Z","patches":[],"publicationTime":"2017-01-18T14:00:38.403Z","modificationTime":"2016-11-08T09:59:38.403Z","creationTime":"2016-11-08T09:59:38.403Z","id":"npm:react:20150318","packageName":"react","cvssScore":7.1,"alternativeIds":["SNYK-JS-REACT-10193"]}],"riot":[{"title":"Cross-site Scripting (XSS)","credit":["crazy2be"],"moduleName":"riot","packageName":"riot","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-RIOT-10447"]},"semver":{"unaffected":[">=0.9.6"],"vulnerable":["<0.9.6"]},"patches":[],"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","disclosureTime":"2013-11-13T22:00:00.000Z","publicationTime":"2017-05-08T12:34:46.386Z","modificationTime":"2017-03-20T14:44:23.092Z","creationTime":"2017-03-20T14:44:23.092Z","id":"npm:riot:20131114","cvssScore":6.5,"alternativeIds":["SNYK-JS-RIOT-10447"]}],"socket.io":[{"title":"Insecure Randomness","credit":["Martin Thomson"],"moduleName":"socket.io","packageName":"socket.io","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-330"],"CVE":[],"NSP":321,"ALTERNATIVE":["SNYK-JS-SOCKETIO-10397"]},"semver":{"unaffected":[">=0.9.7"],"vulnerable":["<0.9.7"]},"patches":[],"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","disclosureTime":"2012-03-22T22:00:00.000Z","publicationTime":"2017-02-13T13:46:59.513Z","modificationTime":"2017-02-13T13:46:59.513Z","creationTime":"2017-02-01T13:46:59.513Z","id":"npm:socket.io:20120323","cvssScore":5.3,"alternativeIds":["SNYK-JS-SOCKETIO-10397"]},{"title":"Cross-site Scripting (XSS)","credit":["Almog Melamed"],"moduleName":"socket.io","packageName":"socket.io","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-SOCKETIO-10398"]},"semver":{"unaffected":[">=0.9.6"],"vulnerable":["<0.9.6"]},"patches":[],"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2012-04-16T21:00:00.000Z","publicationTime":"2017-02-13T13:28:52.754Z","modificationTime":"2017-02-13T13:28:52.754Z","creationTime":"2017-02-01T13:28:52.754Z","id":"npm:socket.io:20120417","cvssScore":5.4,"alternativeIds":["SNYK-JS-SOCKETIO-10398"]}],"vue":[{"title":"Cross-site Scripting (XSS)","credit":["Unknown"],"moduleName":"vue","packageName":"vue","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-VUE-12035"]},"semver":{"unaffected":[">=2.3.0-beta.1"],"vulnerable":["<2.3.0-beta.1"]},"patches":[],"cvssScore":6.5,"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","disclosureTime":"2017-03-31T21:00:00.000Z","publicationTime":"2017-12-25T14:45:02.463Z","modificationTime":"2017-12-19T11:55:30.354Z","creationTime":"2017-12-19T11:55:30.354Z","id":"npm:vue:20170401","alternativeIds":["SNYK-JS-VUE-12035"]},{"title":"Cross-site Scripting (XSS)","credit":["Unknown"],"moduleName":"vue","packageName":"vue","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-VUE-12036"]},"semver":{"unaffected":[">=2.4.3"],"vulnerable":["<2.4.3"]},"patches":[],"cvssScore":6.5,"severity":"medium","CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N","disclosureTime":"2017-08-28T21:00:00.000Z","publicationTime":"2017-12-25T14:45:02.568Z","modificationTime":"2017-12-19T11:56:17.017Z","creationTime":"2017-12-19T11:56:17.017Z","id":"npm:vue:20170829","alternativeIds":["SNYK-JS-VUE-12036"]}],"yui":[{"title":"Cross-site Scripting (XSS)","moduleName":"yui","packageName":"yui","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2010-4207"],"ALTERNATIVE":["SNYK-JS-YUI-10383"]},"severity":"medium","semver":{"unaffected":[">=2.8.2 || <2.4.0"],"vulnerable":["<2.8.2 >=2.4.0"]},"credit":["Unknown"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2010-10-24T22:00:00.000Z","patches":[],"publicationTime":"2017-02-13T09:24:55.944Z","modificationTime":"2017-01-22T09:24:55.944Z","creationTime":"2017-01-22T09:24:55.944Z","id":"npm:yui:20101025","cvssScore":5.4,"alternativeIds":["SNYK-JS-YUI-10383"]},{"title":"Cross-site Scripting (XSS)","moduleName":"yui","packageName":"yui","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":[],"ALTERNATIVE":["SNYK-JS-YUI-10384"]},"severity":"medium","semver":{"unaffected":[">=3.5.1 || <3.5.0-PR1"],"vulnerable":["<3.5.1 >=3.5.0-PR1"]},"credit":["Ryan Grove"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2012-04-27T21:00:00.000Z","patches":[],"publicationTime":"2017-02-13T09:12:40.841Z","modificationTime":"2017-02-13T09:12:40.841Z","creationTime":"2017-01-22T09:12:40.841Z","id":"npm:yui:20120428","cvssScore":5.4,"alternativeIds":["SNYK-JS-YUI-10384"]},{"title":"Cross-site Scripting (XSS)","moduleName":"yui","packageName":"yui","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2012-5881","CVE-2012-5882","CVE-2012-5883"],"ALTERNATIVE":["SNYK-JS-YUI-10385"]},"severity":"medium","semver":{"unaffected":[">=3.0.0 || <2.4.0"],"vulnerable":["<3.0.0 >=2.4.0"]},"credit":["Unknwon"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2012-10-29T22:00:00.000Z","patches":[],"publicationTime":"2017-02-13T09:20:03.679Z","modificationTime":"2017-02-13T09:20:03.679Z","creationTime":"2017-01-22T09:20:03.679Z","id":"npm:yui:20121030","cvssScore":5.4,"alternativeIds":["SNYK-JS-YUI-10385"]},{"title":"Cross-site Scripting (XSS)","moduleName":"yui","packageName":"yui","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2013-4941"],"NSP":332,"ALTERNATIVE":["SNYK-JS-YUI-10386"]},"severity":"medium","semver":{"unaffected":[">=3.10.0 || <3.0.0"],"vulnerable":["<3.10.0 >=3.0.0"]},"credit":["Aleksandr Dobkin"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2013-05-14T21:00:00.000Z","patches":[],"publicationTime":"2017-02-13T08:54:05.822Z","modificationTime":"2017-02-13T08:54:05.822Z","creationTime":"2017-01-22T08:54:05.822Z","id":"npm:yui:20130515","cvssScore":5.4,"alternativeIds":["SNYK-JS-YUI-10386"]},{"title":"Cross-site Scripting (XSS)","moduleName":"yui","packageName":"yui","language":"js","packageManager":"npm","identifiers":{"CWE":["CWE-79"],"CVE":["CVE-2013-4940"],"ALTERNATIVE":["SNYK-JS-YUI-10387"]},"severity":"medium","semver":{"unaffected":[">=3.10.3 <3.10.2"],"vulnerable":["=3.10.2"]},"credit":["Unknown"],"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N","disclosureTime":"2013-06-03T21:00:00.000Z","patches":[],"publicationTime":"2017-02-13T09:01:24.863Z","modificationTime":"2017-02-13T09:01:24.863Z","creationTime":"2017-01-22T09:01:24.863Z","id":"npm:yui:20130604","cvssScore":5.4,"alternativeIds":["SNYK-JS-YUI-10387"]}]}};
+},{}],155:[function(require,module,exports){
+module.exports={
+"npm":{
+"angular":[
+{"id":"npm:angular:20130621","severity":"medium","semver":{"vulnerable":["<=1.1.5"]}},
+{"id":"npm:angular:20130622","severity":"medium","semver":{"vulnerable":["<1.2.0 >=1.0.0"]}},
+{"id":"npm:angular:20130625","severity":"high","semver":{"vulnerable":["<1.1.5"]}},
+{"id":"npm:angular:20131113","severity":"high","semver":{"vulnerable":["<1.2.2"]}},
+{"id":"npm:angular:20140608","severity":"low","semver":{"vulnerable":["<1.3.0"]}},
+{"id":"npm:angular:20140908","severity":"medium","semver":{"vulnerable":["<1.3.0-rc.4"]}},
+{"id":"npm:angular:20140909","severity":"high","semver":{"vulnerable":["<1.2.24 >=1.2.19"]}},
+{"id":"npm:angular:20141104","severity":"medium","semver":{"vulnerable":["<1.3.2"]}},
+{"id":"npm:angular:20150310","severity":"high","semver":{"vulnerable":["<1.5.0-beta.2"]}},
+{"id":"npm:angular:20150315","severity":"medium","semver":{"vulnerable":["<1.6.1"]}},
+{"id":"npm:angular:20150807","severity":"high","semver":{"vulnerable":["<1.5.0-beta.0 >=1.0.0"]}},
+{"id":"npm:angular:20150807-1","severity":"medium","semver":{"vulnerable":["<1.5.0-beta.0 >=1.3.1"]}},
+{"id":"npm:angular:20150909","severity":"high","semver":{"vulnerable":["<1.5.0-beta.2"]}},
+{"id":"npm:angular:20151130","severity":"medium","semver":{"vulnerable":["<1.4.10"]}},
+{"id":"npm:angular:20151205","severity":"medium","semver":{"vulnerable":["<1.5.0-rc.0"]}},
+{"id":"npm:angular:20160122","severity":"medium","semver":{"vulnerable":["<1.5.0-rc.2 >=1.3.0"]}},
+{"id":"npm:angular:20160527","severity":"medium","semver":{"vulnerable":["<1.2.30 >=1.0.0"]}},
+{"id":"npm:angular:20161101","severity":"medium","semver":{"vulnerable":["<1.5.9 >=1.5.0"]}},
+{"id":"npm:angular:20171018","severity":"medium","semver":{"vulnerable":["<1.6.7"]}},
+{"id":"npm:angular:20180202","severity":"medium","semver":{"vulnerable":["<1.6.9"]}}],
 
-},{}]},{},[46]);
\ No newline at end of file
+"backbone":[
+{"id":"npm:backbone:20110701","severity":"medium","semver":{"vulnerable":["<0.5.0"]}},
+{"id":"npm:backbone:20160523","severity":"medium","semver":{"vulnerable":["<= 0.3.3"]}}],
+
+"bootstrap":[
+{"id":"npm:bootstrap:20120510","severity":"medium","semver":{"vulnerable":["<2.1.0"]}},
+{"id":"npm:bootstrap:20160627","severity":"medium","semver":{"vulnerable":["<3.4.0 || >=4.0.0-alpha <4.0.0-beta.2"]}}],
+
+"dojo":[
+{"id":"npm:dojo:20090409","severity":"medium","semver":{"vulnerable":["<1.1"]}},
+{"id":"npm:dojo:20100614","severity":"high","semver":{"vulnerable":[">=0.4 <0.4.4 || >=1.0 <1.0.3 || >=1.1 <1.1.2 || >=1.2 <1.2.4 || >=1.3 <1.3.3 || >=1.4 <1.4.2"]}},
+{"id":"npm:dojo:20100614-6","severity":"medium","semver":{"vulnerable":["<1.4.2"]}},
+{"id":"npm:dojo:20160523","severity":"medium","semver":{"vulnerable":["<= 1.0.0"]}}],
+
+"foundation-sites":[
+{"id":"npm:foundation-sites:20120717","severity":"medium","semver":{"vulnerable":["<3.0.6 >=3.0.0"]}},
+{"id":"npm:foundation-sites:20150619","severity":"medium","semver":{"vulnerable":["<5.5.3"]}},
+{"id":"npm:foundation-sites:20170802","severity":"medium","semver":{"vulnerable":["<6.0.0"]}}],
+
+"handlebars":[
+{"id":"npm:handlebars:20110425","severity":"medium","semver":{"vulnerable":["<=1.0.0-beta.3"]}},
+{"id":"npm:handlebars:20151207","severity":"medium","semver":{"vulnerable":["<4.0.0"]}}],
+
+"highcharts":[
+{"id":"npm:highcharts:20180225","severity":"low","semver":{"vulnerable":["<=6.0.7"]}}],
+
+"jquery":[
+{"id":"npm:jquery:20110606","severity":"medium","semver":{"vulnerable":["<1.6.3"]}},
+{"id":"npm:jquery:20120206","severity":"medium","semver":{"vulnerable":["<1.9.0 >=1.7.1"]}},
+{"id":"npm:jquery:20140902","severity":"medium","semver":{"vulnerable":["<=1.5.1 >=1.4.2"]}},
+{"id":"npm:jquery:20150627","severity":"medium","semver":{"vulnerable":["<3.0.0-beta1 >1.12.3 || <1.12.0 >=1.4.0"]}},
+{"id":"npm:jquery:20160529","severity":"low","semver":{"vulnerable":["=3.0.0-rc.1"]}}],
+
+"jquery-mobile":[
+{"id":"npm:jquery-mobile:20120802","severity":"medium","semver":{"vulnerable":["<1.2.0"]}}],
+
+"jquery-ui":[
+{"id":"npm:jquery-ui:20100903","severity":"medium","semver":{"vulnerable":["<1.10.0"]}},
+{"id":"npm:jquery-ui:20121127","severity":"medium","semver":{"vulnerable":["<1.10.0"]}},
+{"id":"npm:jquery-ui:20160721","severity":"high","semver":{"vulnerable":["<=1.11.4"]}}],
+
+"knockout":[
+{"id":"npm:knockout:20130701","severity":"medium","semver":{"vulnerable":["<3.0.0 >=2.1.0-pre"]}},
+{"id":"npm:knockout:20180213","severity":"medium","semver":{"vulnerable":["<3.5.0-beta"]}}],
+
+"lodash":[
+{"id":"npm:lodash:20180130","severity":"low","semver":{"vulnerable":["<4.17.5"]}}],
+
+"moment":[
+{"id":"npm:moment:20160126","severity":"low","semver":{"vulnerable":["<=2.11.1"]}},
+{"id":"npm:moment:20161019","severity":"medium","semver":{"vulnerable":["<2.15.2"]}},
+{"id":"npm:moment:20170905","severity":"low","semver":{"vulnerable":["<2.19.3"]}}],
+
+"mustache":[
+{"id":"npm:mustache:20110814","severity":"medium","semver":{"vulnerable":["< 0.3.1"]}},
+{"id":"npm:mustache:20151207","severity":"medium","semver":{"vulnerable":["<2.2.1"]}}],
+
+"react":[
+{"id":"npm:react:20131217","severity":"medium","semver":{"vulnerable":[">=0.5.0 <0.5.2 || >=0.4.0 <0.4.2"]}},
+{"id":"npm:react:20150318","severity":"high","semver":{"vulnerable":["<0.14.0"]}}],
+
+"riot":[
+{"id":"npm:riot:20131114","severity":"medium","semver":{"vulnerable":["<0.9.6"]}}],
+
+"socket.io":[
+{"id":"npm:socket.io:20120323","severity":"medium","semver":{"vulnerable":["<0.9.7"]}},
+{"id":"npm:socket.io:20120417","severity":"medium","semver":{"vulnerable":["<0.9.6"]}}],
+
+"vue":[
+{"id":"npm:vue:20170401","severity":"medium","semver":{"vulnerable":["<2.3.0-beta.1"]}},
+{"id":"npm:vue:20170829","severity":"medium","semver":{"vulnerable":["<2.4.3"]}},
+{"id":"npm:vue:20180222","severity":"low","semver":{"vulnerable":["<=2.5.14"]}}],
+
+"yui":[
+{"id":"npm:yui:20101025","severity":"medium","semver":{"vulnerable":["<2.8.2 >=2.4.0"]}},
+{"id":"npm:yui:20120428","severity":"medium","semver":{"vulnerable":["<3.5.1 >=3.5.0-PR1"]}},
+{"id":"npm:yui:20121030","severity":"medium","semver":{"vulnerable":["<3.0.0 >=2.4.0"]}},
+{"id":"npm:yui:20130515","severity":"medium","semver":{"vulnerable":["<3.10.0 >=3.0.0"]}},
+{"id":"npm:yui:20130604","severity":"medium","semver":{"vulnerable":[">=3.0.0 <3.10.1 || =3.10.2"]}}]}};
+
+
+
+},{}],"url":[function(require,module,exports){
+
+
+
+
+
+'use strict';
+
+
+
+
+
+
+
+const Util=require('../report/html/renderer/util.js');
+
+
+const URL=typeof self!=='undefined'&&self.URL||
+require('url').URL;
+
+
+
+
+
+
+
+
+function rewriteChromeInternalUrl(url){
+if(!url||!url.startsWith('chrome://'))return url;
+
+
+if(url.endsWith('/'))url=url.replace(/\/$/,'');
+return url.replace(/^chrome:\/\/chrome\//,'chrome://');
+}
+
+class URLShim extends URL{
+
+
+
+
+static isValid(url){
+try{
+new URL(url);
+return true;
+}catch(e){
+return false;
+}
+}
+
+
+
+
+
+
+static hostsMatch(urlA,urlB){
+try{
+return new URL(urlA).host===new URL(urlB).host;
+}catch(e){
+return false;
+}
+}
+
+
+
+
+
+
+static originsMatch(urlA,urlB){
+try{
+return new URL(urlA).origin===new URL(urlB).origin;
+}catch(e){
+return false;
+}
+}
+
+
+
+
+
+static getOrigin(url){
+try{
+const urlInfo=new URL(url);
+
+
+return urlInfo.host&&urlInfo.origin||null;
+}catch(e){
+return null;
+}
+}
+
+
+
+
+
+
+static getURLDisplayName(url,options){
+return Util.getURLDisplayName(new URL(url),options);
+}
+
+
+
+
+
+
+static elideDataURI(url){
+try{
+const parsed=new URL(url);
+return parsed.protocol==='data:'?url.slice(0,100):url;
+}catch(e){
+return url;
+}
+}
+
+
+
+
+
+
+
+static equalWithExcludedFragments(url1,url2){
+[url1,url2]=[url1,url2].map(rewriteChromeInternalUrl);
+try{
+const urla=new URL(url1);
+urla.hash='';
+
+const urlb=new URL(url2);
+urlb.hash='';
+
+return urla.href===urlb.href;
+}catch(e){
+return false;
+}
+}}
+
+
+URLShim.URL=URL;
+URLShim.URLSearchParams=typeof self!=='undefined'&&self.URLSearchParams||
+require('url').URLSearchParams;
+
+URLShim.INVALID_URL_DEBUG_STRING=
+'Lighthouse was unable to determine the URL of some script executions. '+
+'It\'s possible a Chrome extension or other eval\'d code is the source.';
+
+module.exports=URLShim;
+
+},{"../report/html/renderer/util.js":48,"url":"url"}]},{},[52]);
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/bindings/FileUtils.js b/third_party/blink/renderer/devtools/front_end/bindings/FileUtils.js
index 0afebe5..2128eca 100644
--- a/third_party/blink/renderer/devtools/front_end/bindings/FileUtils.js
+++ b/third_party/blink/renderer/devtools/front_end/bindings/FileUtils.js
@@ -143,7 +143,7 @@
     if (event.target.readyState !== FileReader.DONE)
       return;
 
-    const buffer = event.target.result;
+    const buffer = this._reader.result;
     this._loadedSize += buffer.byteLength;
     const endOfFile = this._loadedSize === this._fileSize;
     const decodedString = this._decoder.decode(buffer, {stream: !endOfFile});
diff --git a/third_party/blink/renderer/devtools/front_end/elements/ElementsPanel.js b/third_party/blink/renderer/devtools/front_end/elements/ElementsPanel.js
index 570f3b12..8979f943 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/ElementsPanel.js
+++ b/third_party/blink/renderer/devtools/front_end/elements/ElementsPanel.js
@@ -615,44 +615,6 @@
   }
 
   /**
-   * @override
-   * @param {!KeyboardEvent} event
-   */
-  handleShortcut(event) {
-    if (this._treeOutlines.find(to => to.editing()))
-      return;
-
-    const treeOutline = this._treeOutlines.find(to => !!to.selectedDOMNode());
-    if (!treeOutline)
-      return;
-
-    if (UI.KeyboardShortcut.eventHasCtrlOrMeta(event) && !event.shiftKey &&
-        (event.key === 'Z' || event.key === 'z')) {  // Z key
-      SDK.domModelUndoStack.undo();
-      event.handled = true;
-    }
-
-    const isRedoKey = Host.isMac() ?
-        event.metaKey && event.shiftKey && (event.key === 'Z' || event.key === 'z') :  // Z key
-        event.ctrlKey && (event.key === 'Y' || event.key === 'y');                     // Y key
-    if (isRedoKey) {
-      SDK.domModelUndoStack.redo();
-      event.handled = true;
-    }
-
-    if (event.handled) {
-      this._stylesWidget.forceUpdate();
-      return;
-    }
-
-    treeOutline.handleShortcut(event);
-    if (event.handled)
-      return;
-
-    super.handleShortcut(event);
-  }
-
-  /**
    * @param {?SDK.DOMNode} node
    * @return {?Elements.ElementsTreeOutline}
    */
@@ -969,6 +931,18 @@
       case 'elements.edit-as-html':
         treeOutline.toggleEditAsHTML(node);
         return true;
+      case 'elements.undo':
+        if (UI.isEditing())
+          return false;
+        SDK.domModelUndoStack.undo();
+        Elements.ElementsPanel.instance()._stylesWidget.forceUpdate();
+        return true;
+      case 'elements.redo':
+        if (UI.isEditing())
+          return false;
+        SDK.domModelUndoStack.redo();
+        Elements.ElementsPanel.instance()._stylesWidget.forceUpdate();
+        return true;
     }
     return false;
   }
diff --git a/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeOutline.js b/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeOutline.js
index cef69395..e5c1c4f8 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeOutline.js
+++ b/third_party/blink/renderer/devtools/front_end/elements/ElementsTreeOutline.js
@@ -60,6 +60,7 @@
     this._element.addEventListener('clipboard-copy', this._onCopyOrCut.bind(this, false), false);
     this._element.addEventListener('clipboard-cut', this._onCopyOrCut.bind(this, true), false);
     this._element.addEventListener('clipboard-paste', this._onPaste.bind(this), false);
+    this._element.addEventListener('keydown', this._onKeyDown.bind(this), false);
 
     outlineDisclosureElement.appendChild(this._element);
     this.element = shadowContainer;
@@ -774,7 +775,13 @@
     this._updateModifiedNodes();
   }
 
-  handleShortcut(event) {
+  /**
+   * @param {!Event} event
+   */
+  _onKeyDown(event) {
+    const keyboardEvent = /** @type {!KeyboardEvent} */ (event);
+    if (UI.isEditing())
+      return;
     const node = this.selectedDOMNode();
     if (!node)
       return;
@@ -782,16 +789,16 @@
     if (!treeElement)
       return;
 
-    if (UI.KeyboardShortcut.eventHasCtrlOrMeta(event) && node.parentNode) {
-      if (event.key === 'ArrowUp' && node.previousSibling) {
+    if (UI.KeyboardShortcut.eventHasCtrlOrMeta(keyboardEvent) && node.parentNode) {
+      if (keyboardEvent.key === 'ArrowUp' && node.previousSibling) {
         node.moveTo(node.parentNode, node.previousSibling, this.selectNodeAfterEdit.bind(this, treeElement.expanded));
-        event.handled = true;
+        keyboardEvent.consume(true);
         return;
       }
-      if (event.key === 'ArrowDown' && node.nextSibling) {
+      if (keyboardEvent.key === 'ArrowDown' && node.nextSibling) {
         node.moveTo(
             node.parentNode, node.nextSibling.nextSibling, this.selectNodeAfterEdit.bind(this, treeElement.expanded));
-        event.handled = true;
+        keyboardEvent.consume(true);
         return;
       }
     }
diff --git a/third_party/blink/renderer/devtools/front_end/elements/module.json b/third_party/blink/renderer/devtools/front_end/elements/module.json
index f789cec..b655b00 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/module.json
+++ b/third_party/blink/renderer/devtools/front_end/elements/module.json
@@ -192,6 +192,42 @@
             ]
         },
         {
+            "type": "action",
+            "actionId": "elements.undo",
+            "contextTypes": [
+                "Elements.ElementsPanel"
+            ],
+            "className": "Elements.ElementsActionDelegate",
+            "bindings": [
+                {
+                    "platform": "windows,linux",
+                    "shortcut": "Ctrl+Z"
+                },
+                {
+                    "platform": "mac",
+                    "shortcut": "Meta+Z"
+                }
+            ]
+        },
+        {
+            "type": "action",
+            "actionId": "elements.redo",
+            "contextTypes": [
+                "Elements.ElementsPanel"
+            ],
+            "className": "Elements.ElementsActionDelegate",
+            "bindings": [
+                {
+                    "platform": "windows,linux",
+                    "shortcut": "Ctrl+Y"
+                },
+                {
+                    "platform": "mac",
+                    "shortcut": "Meta+Shift+Z"
+                }
+            ]
+        },
+        {
             "type": "@Elements.MarkerDecorator",
             "className": "Elements.ElementsPanel.PseudoStateMarkerDecorator",
             "marker": "pseudo-state-marker"
diff --git a/third_party/blink/renderer/devtools/front_end/externs.js b/third_party/blink/renderer/devtools/front_end/externs.js
index 2993f2c..7f815d3 100644
--- a/third_party/blink/renderer/devtools/front_end/externs.js
+++ b/third_party/blink/renderer/devtools/front_end/externs.js
@@ -836,3 +836,164 @@
  * @param {function(!Array<*>)} callback
  */
 const ResizeObserver = function(callback) {};
+
+
+// Lighthouse Report Renderer
+
+/**
+ * @constructor
+ * @param {!Document} document
+ */
+const DOM = function(document) {};
+
+/**
+ * @constructor
+ * @param {!DOM} dom
+ */
+const ReportRenderer = function(dom) {};
+
+ReportRenderer.prototype = {
+  /**
+   * @param {!ReportRenderer.ReportJSON} report
+   * @param {!Element} container Parent element to render the report into.
+   */
+  renderReport: function(report, container) {},
+
+  /**
+   * @param {!Document|!Element} context
+   */
+  setTemplateContext: function(context) {},
+
+};
+
+/**
+ * @typedef {{
+ *     rawValue: (number|boolean|undefined),
+ *     id: string,
+ *     title: string,
+ *     description: string,
+ *     explanation: (string|undefined),
+ *     errorMessage: (string|undefined),
+ *     displayValue: (string|Array<string|number>|undefined),
+ *     scoreDisplayMode: string,
+ *     error: boolean,
+ *     score: (number|null),
+ *     details: (!DetailsRenderer.DetailsJSON|undefined),
+ * }}
+ */
+ReportRenderer.AuditResultJSON;
+
+/**
+ * @typedef {{
+ *     id: string,
+ *     score: (number|null),
+ *     weight: number,
+ *     group: (string|undefined),
+ *     result: ReportRenderer.AuditResultJSON
+ * }}
+ */
+ReportRenderer.AuditJSON;
+
+/**
+ * @typedef {{
+ *     title: string,
+ *     id: string,
+ *     score: (number|null),
+ *     description: (string|undefined),
+ *     manualDescription: string,
+ *     auditRefs: !Array<!ReportRenderer.AuditJSON>
+ * }}
+ */
+ReportRenderer.CategoryJSON;
+
+/**
+ * @typedef {{
+ *     title: string,
+ *     description: (string|undefined),
+ * }}
+ */
+ReportRenderer.GroupJSON;
+
+/**
+ * @typedef {{
+ *     lighthouseVersion: string,
+ *     userAgent: string,
+ *     fetchTime: string,
+ *     timing: {total: number},
+ *     requestedUrl: string,
+ *     finalUrl: string,
+ *     runWarnings: (!Array<string>|undefined),
+ *     artifacts: {traces: {defaultPass: {traceEvents: !Array}}},
+ *     audits: !Object<string, !ReportRenderer.AuditResultJSON>,
+ *     categories: !Object<string, !ReportRenderer.CategoryJSON>,
+ *     categoryGroups: !Object<string, !ReportRenderer.GroupJSON>,
+ * }}
+ */
+ReportRenderer.ReportJSON;
+
+/**
+ * @typedef {{
+ *     traces: {defaultPass: {traceEvents: !Array}},
+ * }}
+ */
+ReportRenderer.RunnerResultArtifacts;
+
+/**
+ * @typedef {{
+ *     lhr: !ReportRenderer.ReportJSON,
+ *     artifacts: ReportRenderer.RunnerResultArtifacts,
+ *     report: string
+ * }}
+ */
+ReportRenderer.RunnerResult;
+
+
+/**
+ * @constructor
+ * @param {!DOM} dom
+ * @param {!DetailsRenderer} detailsRenderer
+ */
+const CategoryRenderer = function(dom, detailsRenderer) {};
+
+
+/**
+ * @constructor
+ * @param {!DOM} dom
+ */
+const DetailsRenderer = function(dom) {};
+
+DetailsRenderer.prototype = {
+  /**
+   * @param {!DetailsRenderer.NodeDetailsJSON} item
+   * @return {!Element}
+   */
+  renderNode: function(item) {},
+};
+
+/**
+ * @typedef {{
+ *     type: string,
+ *     value: (string|number|undefined),
+ *     summary: (DetailsRenderer.OpportunitySummary|undefined),
+ *     granularity: (number|undefined),
+ *     displayUnit: (string|undefined)
+ * }}
+ */
+DetailsRenderer.DetailsJSON;
+
+/**
+ * @typedef {{
+ *     type: string,
+ *     path: (string|undefined),
+ *     selector: (string|undefined),
+ *     snippet:(string|undefined)
+ * }}
+ */
+DetailsRenderer.NodeDetailsJSON;
+
+/** @typedef {{
+ *     wastedMs: (number|undefined),
+ *     wastedBytes: (number|undefined),
+ * }}
+ */
+DetailsRenderer.OpportunitySummary;
diff --git a/third_party/blink/renderer/devtools/front_end/main/Main.js b/third_party/blink/renderer/devtools/front_end/main/Main.js
index 6428493..f8094c1 100644
--- a/third_party/blink/renderer/devtools/front_end/main/Main.js
+++ b/third_party/blink/renderer/devtools/front_end/main/Main.js
@@ -383,19 +383,8 @@
   }
 
   _postDocumentKeyDown(event) {
-    if (event.handled)
-      return;
-
-    if (!UI.Dialog.hasInstance() && UI.inspectorView.currentPanelDeprecated() &&
-        UI.inspectorView.currentPanelDeprecated().hasFocus()) {
-      UI.inspectorView.currentPanelDeprecated().handleShortcut(event);
-      if (event.handled) {
-        event.consume(true);
-        return;
-      }
-    }
-
-    UI.shortcutRegistry.handleShortcut(event);
+    if (!event.handled)
+      UI.shortcutRegistry.handleShortcut(event);
   }
 
   /**
diff --git a/third_party/blink/renderer/devtools/front_end/network/SignedExchangeInfoView.js b/third_party/blink/renderer/devtools/front_end/network/SignedExchangeInfoView.js
index 858d0c2..9de4834 100644
--- a/third_party/blink/renderer/devtools/front_end/network/SignedExchangeInfoView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/SignedExchangeInfoView.js
@@ -22,24 +22,35 @@
     root.expandTreeElementsWhenArrowing = true;
     this.element.appendChild(root.element);
 
+    /** @type {!Map<number|undefined, !Set<string>>} */
+    const errorFieldSetMap = new Map();
+
     if (signedExchangeInfo.errors && signedExchangeInfo.errors.length) {
       const errorMessagesCategory = new Network.SignedExchangeInfoView.Category(root, Common.UIString('Errors'));
-      for (const errorMessage of signedExchangeInfo.errors) {
+      for (const error of signedExchangeInfo.errors) {
         const fragment = createDocumentFragment();
         fragment.appendChild(UI.Icon.create('smallicon-error', 'prompt-icon'));
-        fragment.createChild('div', 'error-log').textContent = errorMessage;
+        fragment.createChild('div', 'error-log').textContent = error.message;
         errorMessagesCategory.createLeaf(fragment);
+        if (error.errorField) {
+          let errorFieldSet = errorFieldSetMap.get(error.signatureIndex);
+          if (!errorFieldSet) {
+            errorFieldSet = new Set();
+            errorFieldSetMap.set(error.signatureIndex, errorFieldSet);
+          }
+          errorFieldSet.add(error.errorField);
+        }
       }
     }
+
+    const titleElement = createDocumentFragment();
+    titleElement.createChild('div', 'header-name').textContent = Common.UIString('Signed HTTP exchange');
+    const learnMoreNode =
+        UI.XLink.create('https://github.com/WICG/webpackage', Common.UIString('Learn\xa0more'), 'header-toggle');
+    titleElement.appendChild(learnMoreNode);
+    const headerCategory = new Network.SignedExchangeInfoView.Category(root, titleElement);
     if (signedExchangeInfo.header) {
       const header = signedExchangeInfo.header;
-      const titleElement = createDocumentFragment();
-      titleElement.createChild('div', 'header-name').textContent = Common.UIString('Signed HTTP exchange');
-      const learnMoreNode =
-          UI.XLink.create('https://github.com/WICG/webpackage', Common.UIString('Learn\xa0more'), 'header-toggle');
-      titleElement.appendChild(learnMoreNode);
-
-      const headerCategory = new Network.SignedExchangeInfoView.Category(root, titleElement);
       const redirectDestination = request.redirectDestination();
       const requestURLElement = this._formatHeader(Common.UIString('Request URL'), header.requestUrl);
       if (redirectDestination) {
@@ -61,14 +72,19 @@
       }
       this._responseHeadersItem.expand();
 
-      for (const signature of header.signatures) {
+      for (let i = 0; i < header.signatures.length; ++i) {
+        const errorFieldSet = errorFieldSetMap.get(i) || new Set();
+        const signature = header.signatures[i];
         const signatureCategory = new Network.SignedExchangeInfoView.Category(root, Common.UIString('Signature'));
         signatureCategory.createLeaf(this._formatHeader(Common.UIString('Label'), signature.label));
-        signatureCategory.createLeaf(this._formatHeaderForHexData(Common.UIString('Signature'), signature.signature));
-        signatureCategory.createLeaf(this._formatHeader(Common.UIString('Integrity'), signature.integrity));
+        signatureCategory.createLeaf(this._formatHeaderForHexData(
+            Common.UIString('Signature'), signature.signature,
+            errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureSig)));
 
         if (signature.certUrl) {
-          const certURLElement = this._formatHeader(Common.UIString('Certificate URL'), signature.certUrl);
+          const certURLElement = this._formatHeader(
+              Common.UIString('Certificate URL'), signature.certUrl,
+              errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureCertUrl));
           if (signature.certificates) {
             const viewCertLink = certURLElement.createChild('span', 'devtools-link header-toggle');
             viewCertLink.textContent = Common.UIString('View certificate');
@@ -77,15 +93,23 @@
           }
           signatureCategory.createLeaf(certURLElement);
         }
+        signatureCategory.createLeaf(this._formatHeader(
+            Common.UIString('Integrity'), signature.integrity,
+            errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureIntegrity)));
         if (signature.certSha256) {
-          signatureCategory.createLeaf(
-              this._formatHeaderForHexData(Common.UIString('Certificate SHA256'), signature.certSha256));
+          signatureCategory.createLeaf(this._formatHeaderForHexData(
+              Common.UIString('Certificate SHA256'), signature.certSha256,
+              errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureCertSha256)));
         }
-        signatureCategory.createLeaf(this._formatHeader(Common.UIString('Validity URL'), signature.validityUrl));
-        signatureCategory.createLeaf().title =
-            this._formatHeader(Common.UIString('Date'), new Date(1000 * signature.date).toUTCString());
-        signatureCategory.createLeaf().title =
-            this._formatHeader(Common.UIString('Expires'), new Date(1000 * signature.expires).toUTCString());
+        signatureCategory.createLeaf(this._formatHeader(
+            Common.UIString('Validity URL'), signature.validityUrl,
+            errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureValidityUrl)));
+        signatureCategory.createLeaf().title = this._formatHeader(
+            Common.UIString('Date'), new Date(1000 * signature.date).toUTCString(),
+            errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureTimestamps));
+        signatureCategory.createLeaf().title = this._formatHeader(
+            Common.UIString('Expires'), new Date(1000 * signature.expires).toUTCString(),
+            errorFieldSet.has(Protocol.Network.SignedExchangeErrorField.SignatureTimestamps));
       }
     }
     if (signedExchangeInfo.securityDetails) {
@@ -103,26 +127,40 @@
   /**
    * @param {string} name
    * @param {string} value
+   * @param {boolean=} highlighted
    * @return {!DocumentFragment}
    */
-  _formatHeader(name, value) {
+  _formatHeader(name, value, highlighted) {
     const fragment = createDocumentFragment();
-    fragment.createChild('div', 'header-name').textContent = name + ': ';
+    const nameElement = fragment.createChild('div', 'header-name');
+    nameElement.textContent = name + ': ';
     fragment.createChild('span', 'header-separator');
-    fragment.createChild('div', 'header-value source-code').textContent = value;
+    const valueElement = fragment.createChild('div', 'header-value source-code');
+    valueElement.textContent = value;
+    if (highlighted) {
+      nameElement.classList.add('error-field');
+      valueElement.classList.add('error-field');
+    }
     return fragment;
   }
 
   /**
    * @param {string} name
    * @param {string} value
+   * @param {boolean=} highlighted
    * @return {!DocumentFragment}
    */
-  _formatHeaderForHexData(name, value) {
+  _formatHeaderForHexData(name, value, highlighted) {
     const fragment = createDocumentFragment();
-    fragment.createChild('div', 'header-name').textContent = name + ': ';
+    const nameElement = fragment.createChild('div', 'header-name');
+    nameElement.textContent = name + ': ';
     fragment.createChild('span', 'header-separator');
-    fragment.createChild('div', 'header-value source-code hex-data').textContent = value.replace(/(.{2})/g, '$1 ');
+    const valueElement = fragment.createChild('div', 'header-value source-code hex-data');
+    valueElement.textContent = value.replace(/(.{2})/g, '$1 ');
+    if (highlighted) {
+      nameElement.classList.add('error-field');
+      valueElement.classList.add('error-field');
+    }
     return fragment;
   }
 };
diff --git a/third_party/blink/renderer/devtools/front_end/network/signedExchangeInfoTree.css b/third_party/blink/renderer/devtools/front_end/network/signedExchangeInfoTree.css
index cb63682..9d4ea3a 100644
--- a/third_party/blink/renderer/devtools/front_end/network/signedExchangeInfoTree.css
+++ b/third_party/blink/renderer/devtools/front_end/network/signedExchangeInfoTree.css
@@ -86,3 +86,7 @@
     word-break: break-word;
     margin-left: 20px;
 }
+
+.tree-outline .error-field {
+    color: red;
+}
diff --git a/third_party/blink/renderer/devtools/front_end/network_test_runner/NetworkTestRunner.js b/third_party/blink/renderer/devtools/front_end/network_test_runner/NetworkTestRunner.js
index e3d8000..869853f 100644
--- a/third_party/blink/renderer/devtools/front_end/network_test_runner/NetworkTestRunner.js
+++ b/third_party/blink/renderer/devtools/front_end/network_test_runner/NetworkTestRunner.js
@@ -91,7 +91,7 @@
       }
       if (request.signedExchangeInfo().errors) {
         for (const errorMessage of request.signedExchangeInfo().errors)
-          TestRunner.addResult(`    Error: ${errorMessage}`);
+          TestRunner.addResult(`    Error: ${JSON.stringify(errorMessage)}`);
       }
     }
   }
diff --git a/third_party/blink/renderer/devtools/front_end/ui/Panel.js b/third_party/blink/renderer/devtools/front_end/ui/Panel.js
index 3c0387a..30f0ad3 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/Panel.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/Panel.js
@@ -45,8 +45,6 @@
 
     // For testing.
     UI.panels[name] = this;
-
-    this._shortcuts = /** !Object.<number, function(Event=):boolean> */ ({});
   }
 
   get name() {
@@ -69,16 +67,6 @@
   }
 
   /**
-   * @param {!KeyboardEvent} event
-   */
-  handleShortcut(event) {
-    const shortcutKey = UI.KeyboardShortcut.makeKeyFromEvent(event);
-    const handler = this._shortcuts[shortcutKey];
-    if (handler && handler(event))
-      event.handled = true;
-  }
-
-  /**
    * @param {!UI.Infobar} infobar
    */
   showInfobar(infobar) {
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ShortcutRegistry.js b/third_party/blink/renderer/devtools/front_end/ui/ShortcutRegistry.js
index ae30dc1..6b72862 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/ShortcutRegistry.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/ShortcutRegistry.js
@@ -147,6 +147,21 @@
         return true;
 
       const modifiers = UI.KeyboardShortcut.Modifiers;
+      // Undo/Redo will also cause input, so textual undo should take precedence over DevTools undo when editing.
+      if (Host.isMac()) {
+        if (UI.KeyboardShortcut.makeKey('z', modifiers.Meta) === key)
+          return true;
+        if (UI.KeyboardShortcut.makeKey('z', modifiers.Meta | modifiers.Shift) === key)
+          return true;
+      } else {
+        if (UI.KeyboardShortcut.makeKey('z', modifiers.Ctrl) === key)
+          return true;
+        if (UI.KeyboardShortcut.makeKey('y', modifiers.Ctrl) === key)
+          return true;
+        if (!Host.isWin() && UI.KeyboardShortcut.makeKey('z', modifiers.Ctrl | modifiers.Shift) === key)
+          return true;
+      }
+
       if ((keyModifiers & (modifiers.Ctrl | modifiers.Alt)) === (modifiers.Ctrl | modifiers.Alt))
         return Host.isWin();
 
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ShortcutsScreen.js b/third_party/blink/renderer/devtools/front_end/ui/ShortcutsScreen.js
index 8daf09a..9bcd4f0 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/ShortcutsScreen.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/ShortcutsScreen.js
@@ -52,9 +52,10 @@
     elementsSection.addAlternateKeys(
         UI.ShortcutsScreen.ElementsPanelShortcuts.EditAttribute, Common.UIString('Edit attribute'));
     elementsSection.addAlternateKeys(
-        UI.ShortcutsScreen.ElementsPanelShortcuts.HideElement, Common.UIString('Hide element'));
+        UI.shortcutRegistry.shortcutDescriptorsForAction('elements.hide-element'), Common.UIString('Hide element'));
     elementsSection.addAlternateKeys(
-        UI.ShortcutsScreen.ElementsPanelShortcuts.ToggleEditAsHTML, Common.UIString('Toggle edit as HTML'));
+        UI.shortcutRegistry.shortcutDescriptorsForAction('elements.edit-as-html'),
+        Common.UIString('Toggle edit as HTML'));
 
     const stylesPaneSection = UI.shortcutsScreen.section(Common.UIString('Styles Pane'));
 
@@ -371,10 +372,6 @@
 
   EditAttribute: [UI.KeyboardShortcut.makeDescriptor(UI.KeyboardShortcut.Keys.Enter)],
 
-  HideElement: [UI.KeyboardShortcut.makeDescriptor(UI.KeyboardShortcut.Keys.H)],
-
-  ToggleEditAsHTML: [UI.KeyboardShortcut.makeDescriptor(UI.KeyboardShortcut.Keys.F2)],
-
   NextProperty: [UI.KeyboardShortcut.makeDescriptor(UI.KeyboardShortcut.Keys.Tab)],
 
   PreviousProperty:
diff --git a/third_party/blink/renderer/devtools/front_end/ui/SuggestBox.js b/third_party/blink/renderer/devtools/front_end/ui/SuggestBox.js
index 3294d29..1ede0c2f 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/SuggestBox.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/SuggestBox.js
@@ -128,7 +128,9 @@
       }
     }
     const element = this.createElementForItem(/** @type {!UI.SuggestBox.Suggestion} */ (maxItem));
-    return Math.min(kMaxWidth, UI.measurePreferredSize(element, this._element).width);
+    const preferredWidth =
+        UI.measurePreferredSize(element, this._element).width + UI.measuredScrollbarWidth(this._element.ownerDocument);
+    return Math.min(kMaxWidth, preferredWidth);
   }
 
   /**
@@ -197,7 +199,7 @@
       element.classList.add('secondary');
     element.tabIndex = -1;
     const maxTextLength = 50 + query.length;
-    const displayText = (item.title || item.text).trimEnd(maxTextLength);
+    const displayText = (item.title || item.text).trim().trimEnd(maxTextLength).replace(/\n/g, '\u21B5');
 
     const titleElement = element.createChild('span', 'suggestion-title');
     const index = displayText.toLowerCase().indexOf(query.toLowerCase());
diff --git a/third_party/blink/renderer/devtools/front_end/ui/suggestBox.css b/third_party/blink/renderer/devtools/front_end/ui/suggestBox.css
index 0e1130b..ea8c85e5 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/suggestBox.css
+++ b/third_party/blink/renderer/devtools/front_end/ui/suggestBox.css
@@ -59,6 +59,10 @@
     text-overflow: ellipsis;
 }
 
+.suggestion-title span {
+    white-space: pre;
+}
+
 .suggestion-subtitle {
     flex: auto;
     text-align: right;
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.cc b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.cc
index 52b7dbd..7693e1e 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.cc
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.cc
@@ -76,6 +76,13 @@
   return ContextLifecycleObserver::GetExecutionContext();
 }
 
+bool BluetoothRemoteGATTCharacteristic::HasPendingActivity() const {
+  // This object should be considered active as long as there are registered
+  // event listeners. Even if script drops all references this can still be
+  // found again through the BluetoothRemoteGATTServer object.
+  return GetExecutionContext() && HasEventListeners();
+}
+
 void BluetoothRemoteGATTCharacteristic::AddedEventListener(
     const AtomicString& event_type,
     RegisteredEventListener& registered_listener) {
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.h b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.h
index 0890312a..a6ab54b7 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.h
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.h
@@ -7,6 +7,7 @@
 
 #include "mojo/public/cpp/bindings/associated_binding_set.h"
 #include "third_party/blink/public/platform/modules/bluetooth/web_bluetooth.mojom-blink.h"
+#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_data_view.h"
@@ -34,6 +35,7 @@
 // CallbackPromiseAdapter class comments.
 class BluetoothRemoteGATTCharacteristic final
     : public EventTargetWithInlineData,
+      public ActiveScriptWrappable<BluetoothRemoteGATTCharacteristic>,
       public ContextLifecycleObserver,
       public mojom::blink::WebBluetoothCharacteristicClient {
   USING_PRE_FINALIZER(BluetoothRemoteGATTCharacteristic, Dispose);
@@ -71,6 +73,9 @@
   const AtomicString& InterfaceName() const override;
   ExecutionContext* GetExecutionContext() const override;
 
+  // ActiveScriptWrappable methods:
+  bool HasPendingActivity() const override;
+
   // Interface required by garbage collection.
   void Trace(blink::Visitor*) override;
 
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.idl b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.idl
index bf38de0..28c3672 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.idl
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.idl
@@ -7,6 +7,7 @@
 // Implement BluetoothRemoteGATTCharacteristic interface: https://crbug.com/483344
 
 [
+    ActiveScriptWrappable,
     RuntimeEnabled=WebBluetooth
 ] interface BluetoothRemoteGATTCharacteristic : EventTarget {//: CharacteristicEventHandlers {
     [SameObject] readonly attribute BluetoothRemoteGATTService service;
diff --git a/third_party/blink/renderer/modules/keyboard/keyboard.idl b/third_party/blink/renderer/modules/keyboard/keyboard.idl
index b7456431..c6e9af5 100644
--- a/third_party/blink/renderer/modules/keyboard/keyboard.idl
+++ b/third_party/blink/renderer/modules/keyboard/keyboard.idl
@@ -2,10 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// TODO(joedow): Remove RuntimeEnabled flag after the next branch point.
 [
     Exposed=Window,
-    RuntimeEnabled=KeyboardLock,
     SecureContext
 ] interface Keyboard {
     // Keyboard Lock specification: https://w3c.github.io/keyboard-lock/
diff --git a/third_party/blink/renderer/modules/keyboard/navigator_keyboard.idl b/third_party/blink/renderer/modules/keyboard/navigator_keyboard.idl
index efda7e7..903a1921 100644
--- a/third_party/blink/renderer/modules/keyboard/navigator_keyboard.idl
+++ b/third_party/blink/renderer/modules/keyboard/navigator_keyboard.idl
@@ -3,8 +3,7 @@
 // found in the LICENSE file.
 
 [
-    ImplementedAs=NavigatorKeyboard,
-    RuntimeEnabled=KeyboardLock
+    ImplementedAs=NavigatorKeyboard
 ] partial interface Navigator {
     [SameObject, SecureContext] readonly attribute Keyboard keyboard;
 };
diff --git a/third_party/blink/renderer/platform/exported/web_url_request.cc b/third_party/blink/renderer/platform/exported/web_url_request.cc
index 632bf7f..5711067f 100644
--- a/third_party/blink/renderer/platform/exported/web_url_request.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_request.cc
@@ -397,10 +397,6 @@
   resource_request_->SetNavigationStartTime(navigation_start_seconds);
 }
 
-void WebURLRequest::SetIsSameDocumentNavigation(bool is_same_document) {
-  resource_request_->SetIsSameDocumentNavigation(is_same_document);
-}
-
 WebURLRequest::InputToLoadPerfMetricReportPolicy
 WebURLRequest::InputPerfMetricReportPolicy() const {
   return static_cast<WebURLRequest::InputToLoadPerfMetricReportPolicy>(
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
index 875d723..edbd0f0 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
@@ -79,7 +79,6 @@
       is_external_request_(false),
       cors_preflight_policy_(
           network::mojom::CORSPreflightPolicy::kConsiderPreflight),
-      is_same_document_navigation_(false),
       input_perf_metric_report_policy_(
           InputToLoadPerfMetricReportPolicy::kNoReport),
       redirect_status_(RedirectStatus::kNoRedirect) {}
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.h b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
index d71f780..1670118 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
@@ -366,11 +366,6 @@
   void SetNavigationStartTime(TimeTicks);
   TimeTicks NavigationStartTime() const { return navigation_start_; }
 
-  void SetIsSameDocumentNavigation(bool is_same_document) {
-    is_same_document_navigation_ = is_same_document;
-  }
-  bool IsSameDocumentNavigation() const { return is_same_document_navigation_; }
-
   void SetIsAdResource() { is_ad_resource_ = true; }
   bool IsAdResource() const { return is_ad_resource_; }
 
@@ -436,7 +431,6 @@
   double ui_start_time_;
   bool is_external_request_;
   network::mojom::CORSPreflightPolicy cors_preflight_policy_;
-  bool is_same_document_navigation_;
   InputToLoadPerfMetricReportPolicy input_perf_metric_report_policy_;
   RedirectStatus redirect_status_;
   base::Optional<String> suggested_filename_;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 61144cd..1fd93ae 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -626,10 +626,6 @@
       status: "stable",
     },
     {
-      name: "KeyboardLock",
-      status: "stable",
-    },
-    {
       name: "KeyboardMap",
       status: "experimental",
     },
diff --git a/third_party/blink/renderer/platform/scheduler/base/enqueue_order.cc b/third_party/blink/renderer/platform/scheduler/base/enqueue_order.cc
index 0baabcb..10c33ad 100644
--- a/third_party/blink/renderer/platform/scheduler/base/enqueue_order.cc
+++ b/third_party/blink/renderer/platform/scheduler/base/enqueue_order.cc
@@ -16,8 +16,8 @@
 EnqueueOrderGenerator::~EnqueueOrderGenerator() = default;
 
 EnqueueOrder EnqueueOrderGenerator::GenerateNext() {
-  AutoLock lock(lock_);
-  return enqueue_order_++;
+  return std::atomic_fetch_add_explicit(&enqueue_order_, uint64_t(1),
+                                        std::memory_order_seq_cst);
 }
 
 }  // namespace internal
diff --git a/third_party/blink/renderer/platform/scheduler/base/enqueue_order.h b/third_party/blink/renderer/platform/scheduler/base/enqueue_order.h
index ae5f480..933455a 100644
--- a/third_party/blink/renderer/platform/scheduler/base/enqueue_order.h
+++ b/third_party/blink/renderer/platform/scheduler/base/enqueue_order.h
@@ -7,7 +7,7 @@
 
 #include <stdint.h>
 
-#include "base/synchronization/lock.h"
+#include <atomic>
 
 namespace base {
 namespace sequence_manager {
@@ -41,8 +41,7 @@
   }
 
  private:
-  Lock lock_;
-  EnqueueOrder enqueue_order_;
+  std::atomic_uint64_t enqueue_order_;
 };
 
 }  // namespace internal
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index e7c9e38..0204b0c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -8,8 +8,8 @@
 
 #include "base/callback.h"
 #include "base/location.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "components/viz/test/ordered_simple_task_runner.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -29,16 +29,21 @@
 
 class FrameSchedulerImplTest : public testing::Test {
  public:
-  FrameSchedulerImplTest() = default;
+  FrameSchedulerImplTest()
+      : task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
+            base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED) {
+    // Null clock might trigger some assertions.
+    task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(5));
+  }
+
   ~FrameSchedulerImplTest() override = default;
 
   void SetUp() override {
-    clock_.Advance(base::TimeDelta::FromMicroseconds(5000));
-    mock_task_runner_ =
-        base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(&clock_, true);
     scheduler_.reset(new MainThreadSchedulerImpl(
         base::sequence_manager::TaskQueueManagerForTest::Create(
-            nullptr, mock_task_runner_, &clock_),
+            nullptr, task_environment_.GetMainThreadTaskRunner(),
+            task_environment_.GetMockTickClock()),
         base::nullopt));
     page_scheduler_.reset(
         new PageSchedulerImpl(nullptr, scheduler_.get(), false));
@@ -95,8 +100,7 @@
     return frame_scheduler_->CalculateThrottlingState(type);
   }
 
-  base::SimpleTestTickClock clock_;
-  scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+  base::test::ScopedTaskEnvironment task_environment_;
   std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
   std::unique_ptr<PageSchedulerImpl> page_scheduler_;
   std::unique_ptr<FrameSchedulerImpl> frame_scheduler_;
@@ -294,13 +298,13 @@
   frame_scheduler_->SetPaused(true);
 
   EXPECT_EQ(0, counter);
-  mock_task_runner_->RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, counter);
 
   frame_scheduler_->SetPaused(false);
 
   EXPECT_EQ(1, counter);
-  mock_task_runner_->RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
   EXPECT_EQ(5, counter);
 }
 
@@ -323,14 +327,15 @@
   page_scheduler_->SetPageFrozen(true);
 
   EXPECT_EQ(0, counter);
-  mock_task_runner_->RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
   // unpausable tasks continue to run.
   EXPECT_EQ(1, counter);
 
   page_scheduler_->SetPageFrozen(false);
 
   EXPECT_EQ(1, counter);
-  mock_task_runner_->RunUntilIdle();
+  // Same as RunUntilIdle but also advances the cock if necessary.
+  task_environment_.FastForwardUntilNoTasksRemain();
   EXPECT_EQ(5, counter);
 }
 
@@ -353,14 +358,15 @@
   page_scheduler_->SetPageFrozen(true);
 
   EXPECT_EQ(0, counter);
-  mock_task_runner_->RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
   // throttleable tasks are frozen, other tasks continue to run.
   EXPECT_EQ(4, counter);
 
   page_scheduler_->SetPageFrozen(false);
 
   EXPECT_EQ(4, counter);
-  mock_task_runner_->RunUntilIdle();
+  // Same as RunUntilIdle but also advances the clock if necessary.
+  task_environment_.FastForwardUntilNoTasksRemain();
   EXPECT_EQ(5, counter);
 }
 
@@ -387,7 +393,7 @@
   page_scheduler_->SetPageFrozen(true);
 
   EXPECT_THAT(tasks, UnorderedElementsAre());
-  mock_task_runner_->RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
   // Everything runs except throttleable tasks (timers)
   EXPECT_THAT(tasks, UnorderedElementsAre(
                          std::string(LoadingTaskQueue()->GetName()),
@@ -400,7 +406,7 @@
       FROM_HERE, base::BindOnce(&RecordQueueName, LoadingTaskQueue(), &tasks));
 
   EXPECT_THAT(tasks, UnorderedElementsAre());
-  mock_task_runner_->RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
   // loading task runs
   EXPECT_THAT(tasks,
               UnorderedElementsAre(std::string(LoadingTaskQueue()->GetName())));
@@ -411,13 +417,13 @@
   // KeepActive is false when Service Worker stops.
   page_scheduler_->SetKeepActive(false);
   EXPECT_THAT(tasks, UnorderedElementsAre());
-  mock_task_runner_->RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
   EXPECT_THAT(tasks, UnorderedElementsAre());  // loading task does not run
 
   tasks.clear();
   page_scheduler_->SetKeepActive(true);
   EXPECT_THAT(tasks, UnorderedElementsAre());
-  mock_task_runner_->RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
   // loading task runs
   EXPECT_THAT(tasks,
               UnorderedElementsAre(std::string(LoadingTaskQueue()->GetName())));
@@ -442,14 +448,14 @@
   page_scheduler_->SetPageFrozen(true);
 
   EXPECT_EQ(0, counter);
-  mock_task_runner_->RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, counter);
 
   // Making the page visible should cause frozen queues to resume.
   page_scheduler_->SetPageVisible(true);
 
   EXPECT_EQ(1, counter);
-  mock_task_runner_->RunUntilIdle();
+  base::RunLoop().RunUntilIdle();
   EXPECT_EQ(5, counter);
 }
 
@@ -487,7 +493,7 @@
   observer->CheckObserverState(FROM_HERE, not_throttled_count, hidden_count,
                                throttled_count, stopped_count);
 
-  mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(30));
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(30));
 
   // The frame gets throttled after some time in background.
   observer->CheckObserverState(FROM_HERE, not_throttled_count, hidden_count,
@@ -521,8 +527,8 @@
   page_scheduler_->SetPageVisible(false);
 
   // Wait 100 secs virtually and run pending tasks just in case.
-  clock_.Advance(base::TimeDelta::FromSeconds(100));
-  mock_task_runner_->RunUntilIdle();
+  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(100));
+  base::RunLoop().RunUntilIdle();
 
   observer->CheckObserverState(FROM_HERE, not_throttled_count, hidden_count,
                                throttled_count, stopped_count);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc
index 7c884075..22b8887 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_metrics_helper_unittest.cc
@@ -7,8 +7,7 @@
 #include <memory>
 #include "base/macros.h"
 #include "base/test/histogram_tester.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "components/viz/test/ordered_simple_task_runner.h"
+#include "base/test/scoped_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/page/launching_process_state.h"
@@ -44,17 +43,22 @@
 
 class MainThreadMetricsHelperTest : public testing::Test {
  public:
-  MainThreadMetricsHelperTest() = default;
+  MainThreadMetricsHelperTest()
+      : task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
+            base::test::ScopedTaskEnvironment::ExecutionMode::QUEUED) {
+    // Null clock might trigger some assertions.
+    task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(1));
+  }
+
   ~MainThreadMetricsHelperTest() override = default;
 
   void SetUp() override {
     histogram_tester_.reset(new base::HistogramTester());
-    clock_.Advance(base::TimeDelta::FromMilliseconds(1));
-    mock_task_runner_ =
-        base::MakeRefCounted<cc::OrderedSimpleTaskRunner>(&clock_, true);
     scheduler_ = std::make_unique<MainThreadSchedulerImplForTest>(
         base::sequence_manager::TaskQueueManagerForTest::Create(
-            nullptr, mock_task_runner_, &clock_),
+            nullptr, task_environment_.GetMainThreadTaskRunner(),
+            task_environment_.GetMockTickClock()),
         base::nullopt);
     metrics_helper_ = &scheduler_->main_thread_only().metrics_helper;
   }
@@ -64,11 +68,20 @@
     scheduler_.reset();
   }
 
+  base::TimeTicks Now() {
+    return task_environment_.GetMockTickClock()->NowTicks();
+  }
+
+  void FastForwardTo(base::TimeTicks time) {
+    CHECK_LE(Now(), time);
+    task_environment_.FastForwardBy(time - Now());
+  }
+
   void RunTask(MainThreadTaskQueue::QueueType queue_type,
                base::TimeTicks start,
                base::TimeDelta duration) {
-    DCHECK_LE(clock_.NowTicks(), start);
-    clock_.SetNowTicks(start + duration);
+    DCHECK_LE(Now(), start);
+    FastForwardTo(start + duration);
     scoped_refptr<MainThreadTaskQueueForTest> queue;
     if (queue_type != MainThreadTaskQueue::QueueType::kDetached) {
       queue = scoped_refptr<MainThreadTaskQueueForTest>(
@@ -85,8 +98,8 @@
   void RunTask(FrameSchedulerImpl* scheduler,
                base::TimeTicks start,
                base::TimeDelta duration) {
-    DCHECK_LE(clock_.NowTicks(), start);
-    clock_.SetNowTicks(start + duration);
+    DCHECK_LE(Now(), start);
+    FastForwardTo(start + duration);
     scoped_refptr<MainThreadTaskQueueForTest> queue(
         new MainThreadTaskQueueForTest(QueueType::kDefault));
     queue->SetFrameSchedulerForTest(scheduler);
@@ -100,8 +113,8 @@
   void RunTask(UseCase use_case,
                base::TimeTicks start,
                base::TimeDelta duration) {
-    DCHECK_LE(clock_.NowTicks(), start);
-    clock_.SetNowTicks(start + duration);
+    DCHECK_LE(Now(), start);
+    FastForwardTo(start + duration);
     scoped_refptr<MainThreadTaskQueueForTest> queue(
         new MainThreadTaskQueueForTest(QueueType::kDefault));
     scheduler_->SetCurrentUseCaseForTest(use_case);
@@ -226,8 +239,7 @@
     return builder.Build();
   }
 
-  base::SimpleTestTickClock clock_;
-  scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
+  base::test::ScopedTaskEnvironment task_environment_;
   std::unique_ptr<MainThreadSchedulerImplForTest> scheduler_;
   MainThreadMetricsHelper* metrics_helper_;  // NOT OWNED
   std::unique_ptr<base::HistogramTester> histogram_tester_;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index b181305..4dc1e2a 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -63,6 +63,7 @@
 constexpr base::TimeDelta kQueueingTimeWindowDuration =
     base::TimeDelta::FromSeconds(1);
 const double kSamplingRateForTaskUkm = 0.0001;
+const int64_t kSecondsPerMinute = 60;
 
 // Field trial name.
 const char kWakeUpThrottlingTrial[] = "RendererSchedulerWakeUpThrottling";
@@ -2603,6 +2604,23 @@
       GetFrameStatus(queue ? queue->GetFrameScheduler() : nullptr)));
   builder.SetTaskDuration((end - start).InMicroseconds());
 
+  if (main_thread_only().renderer_backgrounded) {
+    base::TimeDelta time_since_backgrounded =
+        (end - main_thread_only().background_status_changed_at);
+
+    // Trade off for privacy: Round to seconds for times below 10 minutes and
+    // minutes afterwards.
+    int seconds_since_backgrounded = 0;
+    if (time_since_backgrounded < base::TimeDelta::FromMinutes(10)) {
+      seconds_since_backgrounded = time_since_backgrounded.InSeconds();
+    } else {
+      seconds_since_backgrounded =
+          time_since_backgrounded.InMinutes() * kSecondsPerMinute;
+    }
+
+    builder.SetSecondsSinceBackgrounded(seconds_since_backgrounded);
+  }
+
   if (thread_time) {
     builder.SetTaskCPUDuration(thread_time->InMicroseconds());
   }
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc b/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc
index 824a51f0d..a2b3d722 100644
--- a/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc
+++ b/third_party/blink/renderer/platform/text/text_break_iterator_icu.cc
@@ -34,6 +34,7 @@
 #include "third_party/blink/renderer/platform/text/text_break_iterator_internal_icu.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/thread_specific.h"
 #include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
diff --git a/third_party/blink/renderer/platform/wtf/math_extras.h b/third_party/blink/renderer/platform/wtf/math_extras.h
index 2e3d074..d4667619 100644
--- a/third_party/blink/renderer/platform/wtf/math_extras.h
+++ b/third_party/blink/renderer/platform/wtf/math_extras.h
@@ -60,51 +60,6 @@
 const double twoPiDouble = piDouble * 2.0;
 const float twoPiFloat = piFloat * 2.0f;
 
-#if defined(COMPILER_MSVC)
-
-// VS2013 has most of the math functions now, but we still need to work
-// around various differences in behavior of Inf.
-
-// Work around a bug in Win, where atan2(+-infinity, +-infinity) yields NaN
-// instead of specific values.
-inline double wtf_atan2(double x, double y) {
-  double posInf = std::numeric_limits<double>::infinity();
-  double negInf = -std::numeric_limits<double>::infinity();
-  double nan = std::numeric_limits<double>::quiet_NaN();
-
-  double result = nan;
-
-  if (x == posInf && y == posInf)
-    result = piOverFourDouble;
-  else if (x == posInf && y == negInf)
-    result = 3 * piOverFourDouble;
-  else if (x == negInf && y == posInf)
-    result = -piOverFourDouble;
-  else if (x == negInf && y == negInf)
-    result = -3 * piOverFourDouble;
-  else
-    result = ::atan2(x, y);
-
-  return result;
-}
-
-// Work around a bug in the Microsoft CRT, where fmod(x, +-infinity) yields NaN
-// instead of x.
-inline double wtf_fmod(double x, double y) {
-  return (!std::isinf(x) && std::isinf(y)) ? x : fmod(x, y);
-}
-
-// Work around a bug in the Microsoft CRT, where pow(NaN, 0) yields NaN instead
-// of 1.
-inline double wtf_pow(double x, double y) {
-  return y == 0 ? 1 : pow(x, y);
-}
-
-#define atan2(x, y) wtf_atan2(x, y)
-#define fmod(x, y) wtf_fmod(x, y)
-#define pow(x, y) wtf_pow(x, y)
-
-#endif  // defined(COMPILER_MSVC)
 
 inline double deg2rad(double d) {
   return d * piDouble / 180.0;
@@ -418,25 +373,4 @@
   }
 }
 
-namespace WTF {
-
-inline unsigned FastLog2(unsigned i) {
-  unsigned log2 = 0;
-  if (i & (i - 1))
-    log2 += 1;
-  if (i >> 16)
-    log2 += 16, i >>= 16;
-  if (i >> 8)
-    log2 += 8, i >>= 8;
-  if (i >> 4)
-    log2 += 4, i >>= 4;
-  if (i >> 2)
-    log2 += 2, i >>= 2;
-  if (i >> 1)
-    log2 += 1;
-  return log2;
-}
-
-}  // namespace WTF
-
 #endif  // #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_MATH_EXTRAS_H_
diff --git a/third_party/blink/renderer/platform/wtf/math_extras_test.cc b/third_party/blink/renderer/platform/wtf/math_extras_test.cc
index aedfb303..e90ac43 100644
--- a/third_party/blink/renderer/platform/wtf/math_extras_test.cc
+++ b/third_party/blink/renderer/platform/wtf/math_extras_test.cc
@@ -235,8 +235,8 @@
             clampTo<unsigned long long>(0xFFFFFFFFFFFFFFF5ULL));
 }
 
-// Make sure that various +-inf cases are handled properly (they aren't
-// by default on VS).
+// Make sure that various +-inf cases are handled properly (they weren't
+// by default on older VS).
 TEST(MathExtrasTest, infinityMath) {
   double pos_inf = std::numeric_limits<double>::infinity();
   double neg_inf = -std::numeric_limits<double>::infinity();
diff --git a/third_party/fuchsia-sdk/BUILD.gn b/third_party/fuchsia-sdk/BUILD.gn
index f92a3b9a..4589767 100644
--- a/third_party/fuchsia-sdk/BUILD.gn
+++ b/third_party/fuchsia-sdk/BUILD.gn
@@ -24,28 +24,32 @@
 }
 
 # async-default keep a per-thread dispatcher for async.
-fuchsia_sdk_lib_pkg("async_default") {
+fuchsia_sdk_pkg("async_default") {
   package_name = "async-default"
   sources = [
     "include/lib/async/default.h",
   ]
+
+  libs = [ "async-default" ]
 }
 
-fuchsia_sdk_pkg("component") {
-  fidl_files = [
+fuchsia_sdk_fidl_pkg("component") {
+  sources = [
     "application_controller.fidl",
-    "application_environment.fidl",
-    "application_environment_controller.fidl",
     "application_launcher.fidl",
-    "application_runner.fidl",
+    "environment.fidl",
+    "environment_controller.fidl",
     "flat_namespace.fidl",
     "loader.fidl",
+    "runner.fidl",
     "service_provider.fidl",
   ]
-  fidl_deps = [ ":mem" ]
+  deps = [
+    ":mem",
+  ]
 }
 
-fuchsia_sdk_lib_pkg("fdio") {
+fuchsia_sdk_pkg("fdio") {
   sources = [
     "include/fdio/debug.h",
     "include/fdio/io.fidl.h",
@@ -60,6 +64,8 @@
     "include/fdio/vfs.h",
     "include/fdio/watcher.h",
   ]
+
+  libs = [ "fdio" ]
 }
 
 fuchsia_sdk_pkg("fidl") {
@@ -147,12 +153,10 @@
   ]
 }
 
-fuchsia_sdk_pkg("geometry") {
-  fidl_files = [ "geometry.fidl" ]
-}
-
-fuchsia_sdk_pkg("gfx") {
-  fidl_files = [
+fuchsia_sdk_fidl_pkg("gfx") {
+  namespace = "fuchsia.ui"
+  namespace_path = "fuchsia/ui"
+  sources = [
     "commands.fidl",
     "display_info.fidl",
     "events.fidl",
@@ -163,11 +167,15 @@
     "shapes.fidl",
     "types.fidl",
   ]
-  fidl_deps = [ ":images" ]
+  deps = [
+    ":images",
+  ]
 }
 
-fuchsia_sdk_pkg("images") {
-  fidl_files = [
+fuchsia_sdk_fidl_pkg("images") {
+  namespace = "fuchsia"
+  namespace_path = "fuchsia"
+  sources = [
     "encoded_image.fidl",
     "image_info.fidl",
     "image_pipe.fidl",
@@ -176,8 +184,10 @@
   ]
 }
 
-fuchsia_sdk_pkg("input") {
-  fidl_files = [
+fuchsia_sdk_fidl_pkg("input") {
+  namespace = "fuchsia.ui"
+  namespace_path = "fuchsia/ui"
+  sources = [
     "ime_service.fidl",
     "input_connection.fidl",
     "input_device_registry.fidl",
@@ -191,15 +201,25 @@
   ]
 }
 
-fuchsia_sdk_lib_pkg("launchpad") {
+fuchsia_sdk_pkg("launchpad") {
   sources = [
     "include/launchpad/launchpad.h",
     "include/launchpad/vmo.h",
   ]
+
+  libs = [ "launchpad" ]
 }
 
-fuchsia_sdk_pkg("media") {
-  fidl_files = [
+fuchsia_sdk_fidl_pkg("math") {
+  namespace = "fuchsia"
+  namespace_path = "fuchsia"
+  sources = [
+    "geometry.fidl",
+  ]
+}
+
+fuchsia_sdk_fidl_pkg("media") {
+  sources = [
     "audio_capturer.fidl",
     "audio_renderer.fidl",
     "audio_server.fidl",
@@ -213,43 +233,43 @@
   ]
 }
 
-fuchsia_sdk_pkg("mem") {
-  fidl_namespace = "fuchsia"
-  fidl_namespace_path = "fuchsia"
+fuchsia_sdk_fidl_pkg("mem") {
+  namespace = "fuchsia"
+  namespace_path = "fuchsia"
 
-  fidl_files = [ "buffer.fidl" ]
+  sources = [
+    "buffer.fidl",
+  ]
 }
 
-fuchsia_sdk_pkg("netstack") {
-  fidl_files = [
+fuchsia_sdk_fidl_pkg("netstack") {
+  sources = [
     "net_address.fidl",
     "netstack.fidl",
   ]
 }
 
-fuchsia_sdk_pkg("presentation") {
-  fidl_files = [
+fuchsia_sdk_fidl_pkg("presentation") {
+  sources = [
     "display_usage.fidl",
     "presentation.fidl",
     "presenter.fidl",
   ]
-  fidl_deps = [ ":views_v1" ]
-}
-
-fuchsia_sdk_lib_pkg("svc") {
-  sources = [
-    "include/lib/svc/dir.h",
+  deps = [
+    ":views_v1",
   ]
 }
 
-fuchsia_sdk_pkg("ui") {
-  fidl_files = [
+fuchsia_sdk_fidl_pkg("scenic") {
+  namespace = "fuchsia.ui"
+  namespace_path = "fuchsia/ui"
+  sources = [
     "commands.fidl",
     "events.fidl",
     "scenic.fidl",
     "session.fidl",
   ]
-  fidl_deps = [
+  deps = [
     ":component",
     ":gfx",
     ":input",
@@ -257,38 +277,48 @@
   ]
 }
 
-fuchsia_sdk_pkg("views") {
-  fidl_files = [
+fuchsia_sdk_pkg("svc") {
+  sources = [
+    "include/lib/svc/dir.h",
+  ]
+
+  libs = [ "svc" ]
+}
+
+fuchsia_sdk_fidl_pkg("views") {
+  sources = [
     "commands.fidl",
     "events.fidl",
   ]
-  fidl_deps = [
+  deps = [
     ":gfx",
     ":images",
   ]
 }
 
-fuchsia_sdk_pkg("views_v1") {
-  fidl_files = [
+fuchsia_sdk_fidl_pkg("views_v1") {
+  sources = [
     "view_containers.fidl",
-    "view_properties.fidl",
-    "views.fidl",
-    "view_tree_token.fidl",
     "view_manager.fidl",
+    "view_properties.fidl",
     "view_provider.fidl",
+    "view_tree_token.fidl",
     "view_trees.fidl",
+    "views.fidl",
   ]
-  fidl_deps = [
-    ":geometry",
+  deps = [
     ":gfx",
-    ":ui",
     ":images",
+    ":math",
+    ":scenic",
     ":views_v1_token",
   ]
 }
 
-fuchsia_sdk_pkg("views_v1_token") {
-  fidl_files = [ "view_token.fidl" ]
+fuchsia_sdk_fidl_pkg("views_v1_token") {
+  sources = [
+    "view_token.fidl",
+  ]
 }
 
 fuchsia_sdk_pkg("zx") {
diff --git a/third_party/fuchsia-sdk/fidl_library.gni b/third_party/fuchsia-sdk/fidl_library.gni
index dacf8ed4..559d939 100644
--- a/third_party/fuchsia-sdk/fidl_library.gni
+++ b/third_party/fuchsia-sdk/fidl_library.gni
@@ -52,8 +52,6 @@
   _tables_file = "$_output_gen_dir/${_library_name}.fidl-tables.cc"
 
   action("${target_name}_response_file") {
-    visibility = [ ":*" ]
-
     script = "//third_party/fuchsia-sdk/gen_fidl_response_file.py"
 
     forward_variables_from(invoker,
diff --git a/third_party/fuchsia-sdk/fuchsia_sdk_pkg.gni b/third_party/fuchsia-sdk/fuchsia_sdk_pkg.gni
index 6c8c1c5..a133035 100644
--- a/third_party/fuchsia-sdk/fuchsia_sdk_pkg.gni
+++ b/third_party/fuchsia-sdk/fuchsia_sdk_pkg.gni
@@ -6,65 +6,64 @@
 
 import("fidl_library.gni")
 
-# Template for Fuchsia SDK packages. Each package may contain a mix of C++ files
-# and FIDL interfaces. The following parameters can be specified when
-# instantiating this template:
+# Templates for Fuchsia SDK packages.
+
+# Declares a package specifying FIDL files and its dependencies.
+#
+# Parameters:
 #   package_name - Name of the library. target_name is used if name
 #                  is not specified explicitly.
-#   fidl_namespace, fidl_namespace_path - FIDL namespace. See fidl_library.gni
-#                  for details.
-#   sources      - List of sources relative to sdk/pkg/${name}.
-#   fidl_sources - List of .fidl files relative to
-#                  sdk/fidl/${fidl_namespace}.${name}.
-#   public_deps  - List of public dependencies which are propagated to
-#                  dependents.
+#   namespace, namespace_path - FIDL namespace. See fidl_library.gni
+#                               for details.
+#   sources      - List of sources relative to sdk/fidl/${name}.
 #   deps         - List of dependencies.
-#   fidl_deps    - List of FIDL dependencies for this package (must be other
-#                  fuchsia_sdk_pkg).
-
-template("fuchsia_sdk_pkg") {
+template("fuchsia_sdk_fidl_pkg") {
   _package_name = target_name
   if (defined(invoker.package_name)) {
     _package_name = invoker.package_name
   }
 
-  _has_fidl_files = defined(invoker.fidl_files)
+  fidl_library(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "deps",
+                             "public_deps",
+                             "testonly",
+                             "visibility",
+                           ])
 
-  if (_has_fidl_files) {
-    fidl_library("${target_name}_fidl") {
-      forward_variables_from(invoker,
-                             [
-                               "public_deps",
-                               "testonly",
-                               "visibility",
-                             ])
+    library_name = _package_name
 
-      library_name = _package_name
+    if (defined(invoker.namespace)) {
+      assert(defined(invoker.namespace_path),
+             "SDK packages with namespace must specify namespace_path")
+      namespace = invoker.namespace
+      namespace_path = invoker.namespace_path
 
-      if (defined(invoker.fidl_namespace)) {
-        assert(
-            defined(invoker.fidl_namespace_path),
-            "SDK packages with fidl_namespace must specify fidl_namespace_path")
-        namespace = invoker.fidl_namespace
-        namespace_path = invoker.fidl_namespace_path
-
-        _library_name = "${namespace}.${_package_name}"
-      } else {
-        _library_name = _package_name
-      }
-
-      sources = []
-      foreach(file, invoker.fidl_files) {
-        sources += [ "sdk/fidl/${_library_name}/${file}" ]
-      }
-
-      if (defined(invoker.fidl_deps)) {
-        deps = []
-        foreach(fidl_dep, invoker.fidl_deps) {
-          deps += [ "${fidl_dep}_fidl" ]
-        }
-      }
+      _library_name = "${namespace}.${_package_name}"
+    } else {
+      _library_name = _package_name
     }
+
+    sources = []
+    foreach(file, invoker.sources) {
+      sources += [ "sdk/fidl/${_library_name}/${file}" ]
+    }
+  }
+}
+
+# Declares a package containing uncompiled code and/or precompiled libraries.
+#
+# Parameters:
+#   package_name - Name of the library. target_name is used if name
+#                  is not specified explicitly.
+#   sources      - List of sources relative to sdk/pkg/${name}.
+#   deps         - List of dependencies.
+#   libs         - List of precompiled libraries.
+template("fuchsia_sdk_pkg") {
+  _package_name = target_name
+  if (defined(invoker.package_name)) {
+    _package_name = invoker.package_name
   }
 
   config("${target_name}_config") {
@@ -72,7 +71,7 @@
     include_dirs = [ "sdk/pkg/${_package_name}/include" ]
   }
 
-  static_library("${target_name}") {
+  static_library(target_name) {
     forward_variables_from(invoker,
                            [
                              "data",
@@ -89,12 +88,6 @@
       }
     }
 
-    if (_has_fidl_files) {
-      public_deps = [
-        ":${invoker.target_name}_fidl",
-      ]
-    }
-
     public_configs = [ ":${invoker.target_name}_config" ]
 
     if (defined(invoker.libs)) {
@@ -103,14 +96,3 @@
     }
   }
 }
-
-template("fuchsia_sdk_lib_pkg") {
-  fuchsia_sdk_pkg(target_name) {
-    forward_variables_from(invoker, "*")
-    if (defined(invoker.package_name)) {
-      libs = [ invoker.package_name ]
-    } else {
-      libs = [ target_name ]
-    }
-  }
-}
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 5f1ef5e..4d35e74 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -127,7 +127,7 @@
       'ToTAndroid64': 'android_clang_tot_release_arm64',
       'ToTAndroid x64': 'android_clang_tot_x64',
       'ToTLinux': 'clang_tot_linux_full_symbols_shared_release',
-      'ToTLinuxCoverage': 'clang_tot_coverage_minimal_symbols_shared_release',
+      'ToTLinuxCoverage': 'clang_tot_coverage_minimal_symbols_shared_release_with_libfuzzer',
       'ToTLinux (dbg)': 'clang_tot_shared_debug',
       'ToTLinuxASan': 'clang_tot_asan_lsan_static_release',
       'ToTLinuxASanLibfuzzer': 'release_libfuzzer_asan_clang_tot',
@@ -1150,6 +1150,10 @@
       'clang_tot', 'minimal_symbols', 'shared', 'release',
     ],
 
+    'clang_tot_coverage_minimal_symbols_shared_release_with_libfuzzer': [
+      'clang_tot', 'use_clang_coverage', 'minimal_symbols', 'shared', 'release', 'libfuzzer',
+    ],
+
     'clang_tot_coverage_minimal_symbols_shared_release': [
       'clang_tot', 'use_clang_coverage', 'minimal_symbols', 'shared', 'release',
     ],
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ffadad8..1a5952a0 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -43774,6 +43774,7 @@
   <int value="3" label="Unsupported scheme (deprecated)"/>
   <int value="4" label="URL whitelisted"/>
   <int value="5" label="Activation conditions not met"/>
+  <int value="6" label="Forced activation (e.g. devtools toggle)"/>
 </enum>
 
 <enum name="SubresourceFilterActivationState">
@@ -47113,6 +47114,10 @@
   <int value="66" label=".gslides"/>
 </enum>
 
+<enum name="ViewsTextServicesContextMenu">
+  <int value="0" label="EMOJI"/>
+</enum>
+
 <enum name="VirtualKeyboardContainerType">
   <int value="0" label="FULL_WIDTH"/>
   <int value="1" label="FLOATING"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 7be99c7d..54e1997 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -94340,6 +94340,10 @@
 
 <histogram name="SubresourceFilter.PageLoad.ForcedActivation.DisallowedLoad"
     enum="Boolean">
+  <obsolete>
+    Deprecated May 2018 in favor of
+    SubresourceFilter.PageLoad.ActivationDecision
+  </obsolete>
   <owner>csharrison@chromium.org</owner>
   <summary>
     Logged when the first resource is disallowed from a page with forced
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 10508cf..be86def 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -2693,20 +2693,26 @@
   </metric>
   <metric name="RendererAudible">
     <summary>
-      Whether renderer was playing audio when this task was run. Boolean,
+      Whether renderer was playing audio when this task was completed. Boolean,
       encoded as an integer (0/1).
     </summary>
   </metric>
   <metric name="RendererBackgrounded">
     <summary>
-      Whether renderer was backgrounded when this task was run. Boolean, encoded
-      as an integer (0/1).
+      Whether renderer was backgrounded when this task was completed. Boolean,
+      encoded as an integer (0/1).
     </summary>
   </metric>
   <metric name="RendererHidden">
     <summary>
-      Whether renderer was hidden when this task was run. Boolean, encoded as an
-      integer (0/1).
+      Whether renderer was hidden when this task was completed. Boolean, encoded
+      as an integer (0/1).
+    </summary>
+  </metric>
+  <metric name="SecondsSinceBackgrounded">
+    <summary>
+      Seconds since the renderer was backgrounded, recorded when the task was
+      completed. Only set if the renderer is backgrounded.
     </summary>
   </metric>
   <metric name="TaskCPUDuration">
@@ -2726,7 +2732,7 @@
   </metric>
   <metric name="UseCase">
     <summary>
-      MainThreadSchedulerImpl's UseCase when this task was run. See
+      MainThreadSchedulerImpl's UseCase when this task was completed. See
       blink::scheduler::UseCase for the values of this enum.
     </summary>
   </metric>
diff --git a/tools/perf/core/benchmark_sharding_map.json b/tools/perf/core/benchmark_sharding_map.json
index c8743d1e..617619b 100644
--- a/tools/perf/core/benchmark_sharding_map.json
+++ b/tools/perf/core/benchmark_sharding_map.json
@@ -184,20 +184,20 @@
     }
   },
   "Android Nexus5X Perf": {
-    "build73-b1--device1": {
+    "build211-b7--device1": {
       "benchmarks": [
         "system_health.memory_desktop",
         "system_health.memory_mobile"
       ]
     },
-    "build73-b1--device2": {
+    "build211-b7--device2": {
       "benchmarks": [
         "loading.desktop",
         "smoothness.key_silk_cases",
         "smoothness.simple_mobile_sites"
       ]
     },
-    "build73-b1--device3": {
+    "build211-b7--device3": {
       "benchmarks": [
         "blink_perf.image_decoder",
         "blink_perf.owp_storage",
@@ -205,7 +205,7 @@
 	"rendering.desktop"
       ]
     },
-    "build73-b1--device4": {
+    "build211-b7--device4": {
       "benchmarks": [
         "speedometer-future",
         "speedometer2-future",
@@ -213,13 +213,13 @@
         "v8.browsing_mobile-future"
       ]
     },
-    "build73-b1--device5": {
+    "build211-b7--device5": {
       "benchmarks": [
         "system_health.common_desktop",
         "v8.browsing_mobile"
       ]
     },
-    "build73-b1--device6": {
+    "build211-b7--device6": {
       "benchmarks": [
         "memory.desktop",
         "power.typical_10_mobile",
@@ -228,14 +228,14 @@
         "webrtc"
       ]
     },
-    "build73-b1--device7": {
+    "build211-b7--device7": {
       "benchmarks": [
         "blink_perf.shadow_dom",
         "power.idle_platform",
         "smoothness.gpu_rasterization_and_decoding.image_decoding_cases"
       ]
     },
-    "build74-b1--device1": {
+    "build212-b7--device1": {
       "benchmarks": [
         "battor.trivial_pages",
         "media.desktop",
@@ -246,7 +246,7 @@
         "thread_times.key_idle_power_cases"
       ]
     },
-    "build74-b1--device2": {
+    "build212-b7--device2": {
       "benchmarks": [
         "dummy_benchmark.histogram_benchmark_1",
         "dummy_benchmark.noisy_benchmark_1",
@@ -256,7 +256,7 @@
         "speedometer2"
       ]
     },
-    "build74-b1--device3": {
+    "build212-b7--device3": {
       "benchmarks": [
         "memory.top_10_mobile",
         "smoothness.gpu_rasterization.tough_filters_cases",
@@ -265,7 +265,7 @@
         "smoothness.tough_pinch_zoom_cases"
       ]
     },
-    "build74-b1--device4": {
+    "build212-b7--device4": {
       "benchmarks": [
         "blink_perf.bindings",
         "blink_perf.canvas",
@@ -275,7 +275,7 @@
         "system_health.webview_startup"
       ]
     },
-    "build74-b1--device5": {
+    "build212-b7--device5": {
       "benchmarks": [
         "blink_perf.layout",
         "dromaeo",
@@ -285,7 +285,7 @@
         "thread_times.simple_mobile_sites"
       ]
     },
-    "build74-b1--device6": {
+    "build212-b7--device6": {
       "benchmarks": [
         "battor.steady_state",
         "blink_perf.svg",
@@ -295,13 +295,13 @@
         "thread_times.key_noop_cases"
       ]
     },
-    "build74-b1--device7": {
+    "build212-b7--device7": {
       "benchmarks": [
         "memory.long_running_idle_gmail_tbmv2",
         "system_health.common_mobile"
       ]
     },
-    "build75-b1--device1": {
+    "build213-b7--device1": {
       "benchmarks": [
         "loading.mobile",
         "media.mobile",
@@ -310,7 +310,7 @@
         "v8.browsing_desktop"
       ]
     },
-    "build75-b1--device2": {
+    "build213-b7--device2": {
       "benchmarks": [
         "blink_perf.events",
         "dummy_benchmark.stable_benchmark_1",
@@ -318,7 +318,7 @@
         "smoothness.tough_ad_cases"
       ]
     },
-    "build75-b1--device3": {
+    "build213-b7--device3": {
       "benchmarks": [
         "smoothness.key_desktop_move_cases",
         "smoothness.sync_scroll.key_mobile_sites_smooth",
@@ -326,7 +326,7 @@
         "thread_times.tough_scrolling_cases"
       ]
     },
-    "build75-b1--device4": {
+    "build213-b7--device4": {
       "benchmarks": [
         "blink_perf.dom",
         "rasterize_and_record_micro.top_25",
@@ -334,7 +334,7 @@
         "thread_times.key_silk_cases"
       ]
     },
-    "build75-b1--device5": {
+    "build213-b7--device5": {
       "benchmarks": [
         "blink_perf.css",
         "blink_perf.parser",
@@ -344,7 +344,7 @@
         "start_with_url.warm.startup_pages"
       ]
     },
-    "build75-b1--device6": {
+    "build213-b7--device6": {
       "benchmarks": [
         "blink_perf.paint",
         "kraken",
@@ -353,7 +353,7 @@
         "tracing.tracing_with_background_memory_infra"
       ]
     },
-    "build75-b1--device7": {
+    "build213-b7--device7": {
       "benchmarks": [
         "smoothness.desktop_tough_pinch_zoom_cases",
         "smoothness.tough_path_rendering_cases",
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 3647bae..36b2ec36 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -144,29 +144,32 @@
     swarming=[
       {
        'os': 'Android',
-       'pool': 'Chrome-perf',
+       'pool': 'chrome.tests.perf',
        'device_os': 'MMB29Q',
        'device_type': 'bullhead',
        'device_os_flavor': 'google',
        'device_ids': [
-           'build73-b1--device1', 'build73-b1--device2', 'build73-b1--device3',
-           'build73-b1--device4', 'build73-b1--device5', 'build73-b1--device6',
-           'build73-b1--device7',
-           'build74-b1--device1', 'build74-b1--device2', 'build74-b1--device3',
-           'build74-b1--device4', 'build74-b1--device5', 'build74-b1--device6',
-           'build74-b1--device7',
-           'build75-b1--device1', 'build75-b1--device2', 'build75-b1--device3',
-           'build75-b1--device4', 'build75-b1--device5', 'build75-b1--device6',
-           'build75-b1--device7',
+           'build211-b7--device1', 'build211-b7--device2',
+           'build211-b7--device3', 'build211-b7--device4',
+           'build211-b7--device5', 'build211-b7--device6',
+           'build211-b7--device7',
+           'build212-b7--device1', 'build212-b7--device2',
+           'build212-b7--device3', 'build212-b7--device4',
+           'build212-b7--device5', 'build212-b7--device6',
+           'build212-b7--device7',
+           'build213-b7--device1', 'build213-b7--device2',
+           'build213-b7--device3', 'build213-b7--device4',
+           'build213-b7--device5', 'build213-b7--device6',
+           'build213-b7--device7',
           ],
        'perf_tests': [
-         ('tracing_perftests', 'build73-b1--device2'),
-         ('gpu_perftests', 'build73-b1--device2'),
-         ('media_perftests', 'build74-b1--device7'),
-         ('components_perftests', 'build74-b1--device1'),
+         ('tracing_perftests', 'build211-b7--device2'),
+         ('gpu_perftests', 'build211-b7--device2'),
+         ('media_perftests', 'build212-b7--device7'),
+         ('components_perftests', 'build212-b7--device1'),
        ],
        'perf_tests_with_args': [
-         ('angle_perftests', 'build73-b1--device4', ['--shard-timeout=300'],
+         ('angle_perftests', 'build211-b7--device4', ['--shard-timeout=300'],
            'angle_perftests'),
        ]
       }
diff --git a/ui/display/manager/display_configurator_unittest.cc b/ui/display/manager/display_configurator_unittest.cc
index cc39ab3..cde72cc 100644
--- a/ui/display/manager/display_configurator_unittest.cc
+++ b/ui/display/manager/display_configurator_unittest.cc
@@ -11,7 +11,7 @@
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
-#include "chromeos/chromeos_switches.cc"
+#include "chromeos/chromeos_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/manager/fake_display_snapshot.h"
 #include "ui/display/manager/test/action_logger_util.h"
diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn
index a60ee1c..d3945c3 100644
--- a/ui/gl/BUILD.gn
+++ b/ui/gl/BUILD.gn
@@ -158,9 +158,6 @@
     ":gl_features",
     "//base/third_party/dynamic_annotations",
 
-    # Remove after fixing crbug.com/724999.
-    "//components/crash/core/common:crash_key",
-
     # ANGLE includes are used cross-platform.
     "//third_party/angle:includes",
   ]
diff --git a/ui/gl/DEPS b/ui/gl/DEPS
index eb3f11f..581cf2e3 100644
--- a/ui/gl/DEPS
+++ b/ui/gl/DEPS
@@ -1,5 +1,4 @@
 include_rules = [
-  "+components/crash/core/common/crash_key.h",  # Remove after fixing crbug.com/724999.
   "+third_party/khronos",
   "+third_party/libsync",
   "+third_party/skia",
diff --git a/ui/gl/gl_context.cc b/ui/gl/gl_context.cc
index 75cd030..fa2ce57 100644
--- a/ui/gl/gl_context.cc
+++ b/ui/gl/gl_context.cc
@@ -9,13 +9,11 @@
 #include "base/bind.h"
 #include "base/cancelable_callback.h"
 #include "base/command_line.h"
-#include "base/debug/stack_trace.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_local.h"
-#include "components/crash/core/common/crash_key.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_gl_api_implementation.h"
 #include "ui/gl/gl_implementation.h"
@@ -236,10 +234,6 @@
   return current_real_context_.Pointer()->Get();
 }
 
-GLContext* GLContext::GetRealCurrentForDebugging() {
-  return GetRealCurrent();
-}
-
 std::unique_ptr<gl::GLVersionInfo> GLContext::GenerateGLVersionInfo() {
   return std::make_unique<GLVersionInfo>(
       GetGLVersion().c_str(), GetGLRenderer().c_str(), GetExtensions());
@@ -252,14 +246,8 @@
   // TODO(sievers): Remove this, but needs all gpu_unittest classes
   // to create and make current a context.
   if (!surface && GetGLImplementation() != kGLImplementationMockGL &&
-      GetGLImplementation() != kGLImplementationStubGL) {
-    // TODO(sunnyps): Remove after fixing crbug.com/724999.
-    static crash_reporter::CrashKeyString<1024> crash_key(
-        "gl-context-set-current-stack-trace");
-    crash_reporter::SetCrashKeyStringToStackTrace(&crash_key,
-                                                  base::debug::StackTrace());
+      GetGLImplementation() != kGLImplementationStubGL)
     SetCurrentGL(nullptr);
-  }
 }
 
 void GLContext::SetGLWorkarounds(const GLWorkarounds& workarounds) {
diff --git a/ui/gl/gl_context.h b/ui/gl/gl_context.h
index 12bdd6e4..6ab2006 100644
--- a/ui/gl/gl_context.h
+++ b/ui/gl/gl_context.h
@@ -158,9 +158,6 @@
   // Returns the last GLContext made current, virtual or real.
   static GLContext* GetCurrent();
 
-  // TODO(sunnyps): Remove after crbug.com/724999 is fixed.
-  static GLContext* GetRealCurrentForDebugging();
-
   virtual bool WasAllocatedUsingRobustnessExtension();
 
   // Make this context current when used for context virtualization.
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 881c006c..af61f55b 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -1251,45 +1251,29 @@
                                     bool has_alpha) {
   if (size == GetSize())
     return true;
-
   size_ = size;
-
-  ui::ScopedReleaseCurrent release_current;
-
-  Destroy();
-
-  if (!Initialize(format_)) {
-    LOG(ERROR) << "Failed to resize window.";
-    return false;
+  {
+    ui::ScopedReleaseCurrent release_current;
+    Destroy();
+    if (!Initialize(format_)) {
+      LOG(ERROR) << "Failed to resize window.";
+      return false;
+    }
   }
-
-  if (!release_current.Restore()) {
-    LOG(ERROR) << "Could not MakeCurrent after Resize";
-    return false;
-  }
-
   SetVSyncEnabled(vsync_enabled_);
-
   return true;
 }
 
 bool NativeViewGLSurfaceEGL::Recreate() {
-  ui::ScopedReleaseCurrent release_current;
-
-  Destroy();
-
-  if (!Initialize(format_)) {
-    LOG(ERROR) << "Failed to create surface.";
-    return false;
+  {
+    ui::ScopedReleaseCurrent release_current;
+    Destroy();
+    if (!Initialize(format_)) {
+      LOG(ERROR) << "Failed to create surface.";
+      return false;
+    }
   }
-
-  if (!release_current.Restore()) {
-    LOG(ERROR) << "Failed to MakeCurrent after Recreate";
-    return false;
-  }
-
   SetVSyncEnabled(vsync_enabled_);
-
   return true;
 }
 
@@ -1543,11 +1527,6 @@
     return false;
   }
 
-  if (!release_current.Restore()) {
-    LOG(ERROR) << "Failed to MakeCurrent after Resize";
-    return false;
-  }
-
   return true;
 }
 
diff --git a/ui/gl/scoped_make_current.cc b/ui/gl/scoped_make_current.cc
index cb06ba55..72eaec367 100644
--- a/ui/gl/scoped_make_current.cc
+++ b/ui/gl/scoped_make_current.cc
@@ -5,7 +5,6 @@
 #include "ui/gl/scoped_make_current.h"
 
 #include "base/logging.h"
-#include "components/crash/core/common/crash_key.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_surface.h"
 
@@ -19,58 +18,30 @@
       surface_(surface) {
   DCHECK(context);
   DCHECK(surface);
-  succeeded_ = context->MakeCurrent(surface);
+  context->MakeCurrent(surface);
 }
 
 ScopedMakeCurrent::~ScopedMakeCurrent() {
-  if (!restored_)
-    CHECK(Restore()) << "ScopedMakeCurrent: Restore failed";
-}
-
-bool ScopedMakeCurrent::Succeeded() const {
-  return succeeded_;
-}
-
-bool ScopedMakeCurrent::Restore() {
-  DCHECK(!restored_);
-  restored_ = true;
-
-  if (!succeeded_)
-    return true;
-
-  if (previous_context_)
-    return previous_context_->MakeCurrent(previous_surface_.get());
-
-  context_->ReleaseCurrent(surface_.get());
-  return true;
+  if (previous_context_) {
+    DCHECK(previous_surface_);
+    previous_context_->MakeCurrent(previous_surface_.get());
+  } else {
+    context_->ReleaseCurrent(surface_.get());
+  }
 }
 
 ScopedReleaseCurrent::ScopedReleaseCurrent()
     : previous_context_(gl::GLContext::GetCurrent()),
       previous_surface_(gl::GLSurface::GetCurrent()) {
-  if (previous_context_)
+  if (previous_context_) {
+    DCHECK(previous_surface_);
     previous_context_->ReleaseCurrent(previous_surface_.get());
+  }
 }
 
 ScopedReleaseCurrent::~ScopedReleaseCurrent() {
-  if (!restored_)
-    CHECK(Restore()) << "ScopedReleaseCurrent: Restore failed";
-}
-
-bool ScopedReleaseCurrent::Restore() {
-  DCHECK(!restored_);
-  restored_ = true;
-
-  if (previous_context_) {
-    // TODO(sunnyps): Remove after fixing https://crbug.com/724999.
-    static crash_reporter::CrashKeyString<4> crash_key(
-        "scoped-release-current-is-current");
-    crash_key.Set(previous_context_->IsCurrent(previous_surface_.get()) ? "1"
-                                                                        : "0");
-    return previous_context_->MakeCurrent(previous_surface_.get());
-  }
-
-  return true;
+  if (previous_context_)
+    previous_context_->MakeCurrent(previous_surface_.get());
 }
 
 }  // namespace ui
diff --git a/ui/gl/scoped_make_current.h b/ui/gl/scoped_make_current.h
index 3aaff0d..d850f86 100644
--- a/ui/gl/scoped_make_current.h
+++ b/ui/gl/scoped_make_current.h
@@ -28,17 +28,11 @@
   ScopedMakeCurrent(gl::GLContext* context, gl::GLSurface* surface);
   ~ScopedMakeCurrent();
 
-  bool Succeeded() const;
-
-  bool Restore() WARN_UNUSED_RESULT;
-
  private:
   scoped_refptr<gl::GLContext> previous_context_;
   scoped_refptr<gl::GLSurface> previous_surface_;
   scoped_refptr<gl::GLContext> context_;
   scoped_refptr<gl::GLSurface> surface_;
-  bool succeeded_ = false;
-  bool restored_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(ScopedMakeCurrent);
 };
@@ -50,12 +44,9 @@
   ScopedReleaseCurrent();
   ~ScopedReleaseCurrent();
 
-  bool Restore() WARN_UNUSED_RESULT;
-
  private:
   scoped_refptr<gl::GLContext> previous_context_;
   scoped_refptr<gl::GLSurface> previous_surface_;
-  bool restored_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(ScopedReleaseCurrent);
 };
diff --git a/ui/keyboard/keyboard_controller.cc b/ui/keyboard/keyboard_controller.cc
index bec1950..85be463 100644
--- a/ui/keyboard/keyboard_controller.cc
+++ b/ui/keyboard/keyboard_controller.cc
@@ -643,21 +643,8 @@
 
   ui::LayerAnimator* container_animator = container_->layer()->GetAnimator();
 
-  // If |container_| has hide animation, its visibility is set to false when the
-  // hide animation finished. So even if the container is visible at this
-  // point, it may be in the process of hiding. We still need to show keyboard
-  // container in this case.
-  if (container_->IsVisible() && !container_animator->is_animating()) {
-    // TODO(oka): This clause is excercised in
-    // VirtualKeyboardRootWindowControllerTest
-    //     .EnsureCaretInWorkAreaWithMultipleDisplays
-    // when keyboard container window has been shown from outside.
-    // This should not happen in the real code. Fix the test and change this to
-    // NOTREACHED.
-    DCHECK_EQ(state_, KeyboardControllerState::HIDDEN);
-    ChangeState(KeyboardControllerState::SHOWN);
-    return;
-  }
+  // Ensure that the keyboard is either hidden or is in the process of hiding.
+  DCHECK(!container_->IsVisible() || container_animator->is_animating());
 
   SetTouchEventLogging(!show_keyboard /* enable */);
 
diff --git a/ui/views/controls/views_text_services_context_menu.cc b/ui/views/controls/views_text_services_context_menu.cc
index 03a3a7b..5e1559e 100644
--- a/ui/views/controls/views_text_services_context_menu.cc
+++ b/ui/views/controls/views_text_services_context_menu.cc
@@ -25,4 +25,4 @@
   return false;
 }
 
-}  // namespace views
\ No newline at end of file
+}  // namespace views
diff --git a/ui/views/controls/views_text_services_context_menu_base.cc b/ui/views/controls/views_text_services_context_menu_base.cc
index e5fdea9..eae69906 100644
--- a/ui/views/controls/views_text_services_context_menu_base.cc
+++ b/ui/views/controls/views_text_services_context_menu_base.cc
@@ -4,6 +4,7 @@
 
 #include "ui/views/controls/views_text_services_context_menu_base.h"
 
+#include "base/metrics/histogram_macros.h"
 #include "ui/base/emoji/emoji_panel_helper.h"
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/resources/grit/ui_resources.h"
@@ -12,6 +13,9 @@
 
 namespace views {
 
+const char kViewsTextServicesContextMenuHistogram[] =
+    "ViewsTextServicesContextMenu.Used";
+
 ViewsTextServicesContextMenuBase::ViewsTextServicesContextMenuBase(
     ui::SimpleMenuModel* menu,
     Textfield* client)
@@ -21,7 +25,7 @@
   // Not inserted on read-only fields or if the OS/version doesn't support it.
   if (!client_->read_only() && ui::IsEmojiPanelSupported()) {
     menu->InsertSeparatorAt(0, ui::NORMAL_SEPARATOR);
-    menu->InsertItemWithStringIdAt(0, IDS_CONTENT_CONTEXT_EMOJI,
+    menu->InsertItemWithStringIdAt(0, static_cast<int>(Command::kEmoji),
                                    IDS_CONTENT_CONTEXT_EMOJI);
   }
 }
@@ -29,7 +33,7 @@
 ViewsTextServicesContextMenuBase::~ViewsTextServicesContextMenuBase() {}
 
 bool ViewsTextServicesContextMenuBase::SupportsCommand(int command_id) const {
-  return command_id == IDS_CONTENT_CONTEXT_EMOJI;
+  return command_id == static_cast<int>(Command::kEmoji);
 }
 
 bool ViewsTextServicesContextMenuBase::IsCommandIdChecked(
@@ -39,15 +43,18 @@
 
 bool ViewsTextServicesContextMenuBase::IsCommandIdEnabled(
     int command_id) const {
-  if (command_id == IDS_CONTENT_CONTEXT_EMOJI)
+  if (command_id == static_cast<int>(Command::kEmoji))
     return true;
 
   return false;
 }
 
 void ViewsTextServicesContextMenuBase::ExecuteCommand(int command_id) {
-  if (command_id == IDS_CONTENT_CONTEXT_EMOJI)
+  if (command_id == static_cast<int>(Command::kEmoji)) {
     ui::ShowEmojiPanel();
+    UMA_HISTOGRAM_ENUMERATION(kViewsTextServicesContextMenuHistogram,
+                              Command::kEmoji);
+  }
 }
 
-}  // namespace views
\ No newline at end of file
+}  // namespace views
diff --git a/ui/views/controls/views_text_services_context_menu_base.h b/ui/views/controls/views_text_services_context_menu_base.h
index 7c39439b..08e5a7ad 100644
--- a/ui/views/controls/views_text_services_context_menu_base.h
+++ b/ui/views/controls/views_text_services_context_menu_base.h
@@ -30,6 +30,9 @@
   Textfield* client() const { return client_; }
 
  private:
+  // Do not change the values in this enum as they are used by UMA.
+  enum class Command { kEmoji = 0, kMaxValue = kEmoji };
+
   // The view associated with the menu. Weak. Owns |this|.
   Textfield* client_ = nullptr;