diff --git a/AUTHORS b/AUTHORS
index 374b1a1..2e79a84 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -749,6 +749,7 @@
 Rosen Dash <nqk836@motorola.com>
 Rosen Dash <rosen.dash@gmail.com>
 ruben <chromium@hybridsource.org>
+Ruben Bridgewater <ruben@bridgewater.de>
 Ruben Terrazas <rubentopo@gmail.com>
 Rufus Hamade <rufus.hamade@imgtec.com>
 Ruiyi Luo <luoruiyi2008@gmail.com>
diff --git a/DEPS b/DEPS
index 068a4339..900517d 100644
--- a/DEPS
+++ b/DEPS
@@ -121,11 +121,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': 'd8a90f9be1f068dd8231cc6c9e9d490d17e7ece6',
+  'skia_revision': 'be6549a59d50b4f7368babd1092af4f49d9a51ec',
   # 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': '6e89f8f552fe46cbf81a5bbb40066f881b2e7e65',
+  'v8_revision': '22de82ca15b5c91bb975e86e0097dbf7a8af9ad4',
   # 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.
@@ -133,7 +133,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': '0f073667daf00e894e17cbe7d40187c881552648',
+  'angle_revision': '4b2e00f4d1801c6aa00cb58f21c29735eb355bb2',
   # 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.
@@ -181,7 +181,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'eae881c2a8ba3cea65d9e610ce347373486f91b4',
+  'catapult_revision': 'a08f0fce7903644529bd0437a011d0d0af414917',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -245,7 +245,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '79ff8bead9091f8041f623c1109c290154211f21',
+  'dawn_revision': '74e95fff4a11db7b1da2c72c6553400fbec5eff4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -704,7 +704,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '3f812d07b2aa257fcdb7860e6cdd53e24b283bca',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b69515579db8fb53d13c0d7acf4b5fc6ac4e2b5e',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1036,7 +1036,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '038fb113a1f0c941c6b04ae63fc93acc8b3d0ec1',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'acd0b962902304bc70cc265855ce7cec29cabcc5',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1199,7 +1199,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'db52df17f0d012983dc281e4864c71485a86bd0e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '190713c7cd0fc02b95ccd94baae5f184d6529596',
+    Var('webrtc_git') + '/src.git' + '@' + '3d02384487f85c0915f0ef379781ba3a2f765b3f',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1240,7 +1240,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@56f3eeaa1714342e2fed47845f821dfa923a5d28',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@86579d2a3a9fff1ed85e36e814d592d9471bd070',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/support_library/PRESUBMIT.py b/android_webview/support_library/PRESUBMIT.py
index 885e7dd4..ceb0d14 100644
--- a/android_webview/support_library/PRESUBMIT.py
+++ b/android_webview/support_library/PRESUBMIT.py
@@ -63,11 +63,14 @@
   right place to use it is SupportLibWebViewChromiumFactory.
   """
 
+  pattern = input_api.re.compile(r'\bDEV_SUFFIX\b')
+
   problems = []
   filt = lambda f: 'boundary_interfaces' in f.LocalPath()
   for f in input_api.AffectedFiles(file_filter=filt):
     for line_num, line in f.ChangedContents():
-      if 'DEV_SUFFIX' in line:
+      m = pattern.search(line)
+      if m:
         problems.append('  %s:%d\n    %s\n' % (f.LocalPath(), line_num, line))
 
   if not problems:
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc
index 8d6aff8..65cf63f 100644
--- a/ash/wm/overview/overview_controller.cc
+++ b/ash/wm/overview/overview_controller.cc
@@ -521,6 +521,10 @@
       base::UserMetricsAction("Tablet_LongPressOverviewButtonEnterSplitView"));
 }
 
+bool OverviewController::IsInStartAnimation() {
+  return !start_animations_.empty();
+}
+
 std::vector<aura::Window*>
 OverviewController::GetWindowsListInOverviewGridsForTesting() {
   std::vector<aura::Window*> windows;
@@ -551,9 +555,10 @@
     OnStartingAnimationComplete(/*canceled=*/true);
   start_animations_.clear();
 
-  overview_session_->UpdateMaskAndShadow(/*show=*/false);
-
   auto* overview_session = overview_session_.release();
+  // Do not show mask and show during overview shutdown.
+  overview_session->UpdateMaskAndShadow();
+
   Shell::Get()->NotifyOverviewModeEnding(overview_session);
   overview_session->Shutdown();
   // Don't delete |overview_session_| yet since the stack is still using it.
diff --git a/ash/wm/overview/overview_controller.h b/ash/wm/overview/overview_controller.h
index 6a767cf..659cffb 100644
--- a/ash/wm/overview/overview_controller.h
+++ b/ash/wm/overview/overview_controller.h
@@ -61,6 +61,9 @@
   // if device is not currently in overview mode.
   void OnOverviewButtonTrayLongPressed(const gfx::Point& event_location);
 
+  // Returns true if we're in start-overview animation.
+  bool IsInStartAnimation();
+
   // Gets the windows list that are shown in the overview windows grids if the
   // overview mode is active for testing.
   std::vector<aura::Window*> GetWindowsListInOverviewGridsForTesting();
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index 425a0f79..f3b4108 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -270,12 +270,8 @@
   // animations, manually set the shadow. Shadow relies on both the window
   // transform and |item_widget_|'s new bounds so set it after SetItemBounds
   // and UpdateHeaderLayout. Do not apply the shadow for drop target.
-  if (new_animation_type == OVERVIEW_ANIMATION_NONE) {
-    SetShadowBounds(
-        overview_grid_->IsDropTargetWindow(GetWindow())
-            ? base::nullopt
-            : base::make_optional(transform_window_.GetTransformedBounds()));
-  }
+  if (new_animation_type == OVERVIEW_ANIMATION_NONE)
+    UpdateMaskAndShadow();
 
   UpdateBackdropBounds();
 }
@@ -348,15 +344,19 @@
 }
 
 void OverviewItem::OnSelectorItemDragStarted(OverviewItem* item) {
+  is_being_dragged_ = (item == this);
   caption_container_view_->SetHeaderVisibility(
-      item == this
+      is_being_dragged_
           ? CaptionContainerView::HeaderVisibility::kInvisible
           : CaptionContainerView::HeaderVisibility::kCloseButtonInvisibleOnly);
+  UpdateMaskAndShadow();
 }
 
 void OverviewItem::OnSelectorItemDragEnded() {
+  is_being_dragged_ = false;
   caption_container_view_->SetHeaderVisibility(
       CaptionContainerView::HeaderVisibility::kVisible);
+  UpdateMaskAndShadow();
 }
 
 ScopedOverviewTransformWindow::GridWindowFillMode
@@ -557,16 +557,38 @@
   shadow_->SetContentBounds(bounds_in_item);
 }
 
-void OverviewItem::UpdateMaskAndShadow(bool show) {
-  transform_window_.UpdateMask(show);
+void OverviewItem::UpdateMaskAndShadow() {
+  // Do not show mask and shadow if:
+  // 1) overview is shutting down or
+  // 2) this overview item is in an overview grid that contains more than 10
+  //    windows. In this case don't apply rounded corner mask because it can
+  //    push the compositor memory usage to the limit. TODO(oshima): Remove
+  //    this once new rounded corner impl is available. (crbug.com/903486)
+  // 3) we're currently in entering overview animation or
+  // 4) this overview item is being dragged or
+  // 5) this overview item is the drop target window or
+  // 6) this overview item is in animation.
+  bool should_show = true;
+  OverviewController* overview_controller = Shell::Get()->overview_controller();
+  if (!overview_controller->IsSelecting() ||
+      overview_grid_->window_list().size() > 10 ||
+      overview_controller->IsInStartAnimation() || is_being_dragged_ ||
+      overview_grid_->IsDropTargetWindow(GetWindow()) ||
+      transform_window_.GetOverviewWindow()
+          ->layer()
+          ->GetAnimator()
+          ->is_animating()) {
+    should_show = false;
+  }
 
-  // Do not apply the shadow for the drop target in overview.
-  if (!show || overview_grid_->IsDropTargetWindow(GetWindow())) {
+  if (!should_show) {
+    transform_window_.UpdateMask(false);
     SetShadowBounds(base::nullopt);
     DisableBackdrop();
     return;
   }
 
+  transform_window_.UpdateMask(true);
   SetShadowBounds(transform_window_.GetTransformedBounds());
   EnableBackdropIfNeeded();
 }
diff --git a/ash/wm/overview/overview_item.h b/ash/wm/overview/overview_item.h
index 6ff486bc..fc476a7 100644
--- a/ash/wm/overview/overview_item.h
+++ b/ash/wm/overview/overview_item.h
@@ -191,8 +191,8 @@
   // the shadow is hidden.
   void SetShadowBounds(base::Optional<gfx::Rect> bounds_in_screen);
 
-  // Show or hide the mask and shadow on this window item.
-  void UpdateMaskAndShadow(bool show);
+  // Updates the mask and shadow on this overview window item.
+  void UpdateMaskAndShadow();
 
   // Called when the starting animation is completed, or called immediately
   // if there was no starting animation.
@@ -336,6 +336,9 @@
   // OverviewGrid::PositionWindows.
   bool animating_to_close_ = false;
 
+  // True if this overview item is currently being dragged around.
+  bool is_being_dragged_ = false;
+
   // The shadow around the overview window. Shadows the original window, not
   // |item_widget_|. Done here instead of on the original window because of the
   // rounded edges mask applied on entering overview window.
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 8159a59..a162ca8d 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -566,22 +566,15 @@
     grid->UpdateYPositionAndOpacity(new_y, opacity, work_area, callback);
 }
 
-void OverviewSession::UpdateMaskAndShadow(bool show) {
-  for (auto& grid : grid_list_) {
-    // Don't apply rounded corner mask if the grid has move than 10 windows
-    // because it can push the compositor memory usage to the limit.
-    // TODO(osima): Remove this once new rounded corner impl is available.
-    // (crbug.com/903486)
-    if (show && grid->window_list().size() > 10)
-      continue;
+void OverviewSession::UpdateMaskAndShadow() {
+  for (auto& grid : grid_list_)
     for (auto& window : grid->window_list())
-      window->UpdateMaskAndShadow(show);
-  }
+      window->UpdateMaskAndShadow();
 }
 
 void OverviewSession::OnStartingAnimationComplete(bool canceled) {
   if (!canceled) {
-    UpdateMaskAndShadow(!canceled);
+    UpdateMaskAndShadow();
     if (overview_focus_widget_)
       overview_focus_widget_->Show();
     for (auto& grid : grid_list_)
diff --git a/ash/wm/overview/overview_session.h b/ash/wm/overview/overview_session.h
index 7f21b8e7..5349e8e 100644
--- a/ash/wm/overview/overview_session.h
+++ b/ash/wm/overview/overview_session.h
@@ -201,8 +201,8 @@
       const gfx::Rect& work_area,
       UpdateAnimationSettingsCallback callback);
 
-  // Shows or hides all the window selector items' mask and shadow.
-  void UpdateMaskAndShadow(bool show);
+  // Updates all the window selector items' mask and shadow.
+  void UpdateMaskAndShadow();
 
   // Called when the overview mode starting animation completes.
   void OnStartingAnimationComplete(bool canceled);
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index 711d078..645f97a 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -2234,8 +2234,9 @@
   ui::ScopedAnimationDurationScaleMode test_duration_mode(
       ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
 
-  // Drag the first window. Verify that the mask still exists for both items as
-  // we do not apply any animation to the window items at this point.
+  // Drag the first window. Verify that the mask was removed for the first
+  // window but still exists for the second window as we do not apply mask
+  // for a dragged window.
   const gfx::Point start_drag = item1->target_bounds().CenterPoint();
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->MoveMouseTo(start_drag);
@@ -2243,7 +2244,7 @@
   EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating());
   EXPECT_FALSE(window2->layer()->GetAnimator()->is_animating());
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(HasMaskForItem(item1));
+  EXPECT_FALSE(HasMaskForItem(item1));
   EXPECT_TRUE(HasMaskForItem(item2));
 
   // Drag to horizontally and then back to the start to avoid activating the
diff --git a/ash/wm/overview/scoped_overview_transform_window.cc b/ash/wm/overview/scoped_overview_transform_window.cc
index ec17b8f..1e46e5b 100644
--- a/ash/wm/overview/scoped_overview_transform_window.cc
+++ b/ash/wm/overview/scoped_overview_transform_window.cc
@@ -188,6 +188,7 @@
   if (null_targeter_ == window_->targeter())
     window_->SetEventTargeter(std::move(original_targeter_));
 
+  UpdateMask(/*show=*/false);
   StopObservingImplicitAnimations();
 }
 
@@ -258,11 +259,6 @@
   if (animation_type == OVERVIEW_ANIMATION_NONE)
     return;
 
-  // Remove the mask before animating because masks affect animation
-  // performance. Observe the animation and add the mask after animating if the
-  // animation type is layouting selector items during overview.
-  selector_item_->UpdateMaskAndShadow(/*show=*/false);
-
   for (auto* window : wm::GetTransientTreeIterator(GetOverviewWindow())) {
     auto settings = std::make_unique<ScopedOverviewAnimationSettings>(
         animation_type, window);
@@ -512,8 +508,15 @@
   minimized_widget_->SetContentsView(preview_view);
 }
 
+void ScopedOverviewTransformWindow::OnLayerAnimationStarted(
+    ui::LayerAnimationSequence* sequence) {
+  // Remove the mask before animating because masks affect animation
+  // performance. The mask will be added back once the animation is completed.
+  selector_item_->UpdateMaskAndShadow();
+}
+
 void ScopedOverviewTransformWindow::OnImplicitAnimationsCompleted() {
-  selector_item_->UpdateMaskAndShadow(/*show=*/true);
+  selector_item_->UpdateMaskAndShadow();
   selector_item_->OnDragAnimationCompleted();
 }
 
@@ -551,6 +554,8 @@
     bounds.Inset(0, 0, 0, inset);
   }
   minimized_widget_->SetBounds(bounds);
+  minimized_widget_->SetVisibilityAnimationTransition(
+      views::Widget::ANIMATE_NONE);
   minimized_widget_->Show();
 
   // Stack the minimized window at the bottom since it is never transformed in
diff --git a/ash/wm/overview/scoped_overview_transform_window.h b/ash/wm/overview/scoped_overview_transform_window.h
index 5ff2c9f..f670f9d 100644
--- a/ash/wm/overview/scoped_overview_transform_window.h
+++ b/ash/wm/overview/scoped_overview_transform_window.h
@@ -178,6 +178,7 @@
   views::Widget* minimized_widget() { return minimized_widget_.get(); }
 
   // ui::ImplicitAnimationObserver:
+  void OnLayerAnimationStarted(ui::LayerAnimationSequence* sequence) override;
   void OnImplicitAnimationsCompleted() override;
 
   gfx::Rect GetMaskBoundsForTesting() const;
diff --git a/ash/ws/ash_gpu_interface_provider.cc b/ash/ws/ash_gpu_interface_provider.cc
index a7fdb4b4..1de5ee7 100644
--- a/ash/ws/ash_gpu_interface_provider.cc
+++ b/ash/ws/ash_gpu_interface_provider.cc
@@ -27,8 +27,8 @@
 
 void AshGpuInterfaceProvider::RegisterGpuInterfaces(
     service_manager::BinderRegistry* registry) {
-  registry->AddInterface<ws::mojom::Arc>(base::BindRepeating(
-      &AshGpuInterfaceProvider::BindArcRequest, base::Unretained(this)));
+  registry->AddInterface<ws::mojom::ArcGpu>(base::BindRepeating(
+      &AshGpuInterfaceProvider::BindArcGpuRequest, base::Unretained(this)));
   registry->AddInterface(base::BindRepeating(
       &AshGpuInterfaceProvider::BindDiscardableSharedMemoryManagerRequest,
       base::Unretained(this)));
@@ -42,8 +42,9 @@
   gpu_host_->BindOzoneGpuInterface(interface_name, std::move(handle));
 }
 
-void AshGpuInterfaceProvider::BindArcRequest(ws::mojom::ArcRequest request) {
-  gpu_host_->AddArc(std::move(request));
+void AshGpuInterfaceProvider::BindArcGpuRequest(
+    ws::mojom::ArcGpuRequest request) {
+  gpu_host_->AddArcGpu(std::move(request));
 }
 
 void AshGpuInterfaceProvider::BindDiscardableSharedMemoryManagerRequest(
diff --git a/ash/ws/ash_gpu_interface_provider.h b/ash/ws/ash_gpu_interface_provider.h
index 004189a..2bb665a 100644
--- a/ash/ws/ash_gpu_interface_provider.h
+++ b/ash/ws/ash_gpu_interface_provider.h
@@ -7,7 +7,7 @@
 
 #include "components/discardable_memory/public/interfaces/discardable_shared_memory_manager.mojom.h"
 #include "services/ws/public/cpp/host/gpu_interface_provider.h"
-#include "services/ws/public/mojom/arc.mojom.h"
+#include "services/ws/public/mojom/arc_gpu.mojom.h"
 #include "services/ws/public/mojom/gpu.mojom.h"
 
 namespace discardable_memory {
@@ -37,7 +37,7 @@
                              mojo::ScopedMessagePipeHandle handle) override;
 
  private:
-  void BindArcRequest(ws::mojom::ArcRequest request);
+  void BindArcGpuRequest(ws::mojom::ArcGpuRequest request);
   void BindDiscardableSharedMemoryManagerRequest(
       discardable_memory::mojom::DiscardableSharedMemoryManagerRequest request);
   void BindGpuRequest(ws::mojom::GpuRequest request);
diff --git a/base/file_version_info.h b/base/file_version_info.h
index 3b9457c..a8343f7 100644
--- a/base/file_version_info.h
+++ b/base/file_version_info.h
@@ -5,6 +5,7 @@
 #ifndef BASE_FILE_VERSION_INFO_H_
 #define BASE_FILE_VERSION_INFO_H_
 
+#include <memory>
 #include <string>
 
 #include "build/build_config.h"
@@ -33,21 +34,23 @@
  public:
   virtual ~FileVersionInfo() {}
 #if defined(OS_WIN) || defined(OS_MACOSX)
-  // Creates a FileVersionInfo for the specified path. Returns NULL if something
-  // goes wrong (typically the file does not exit or cannot be opened). The
-  // returned object should be deleted when you are done with it.
-  static FileVersionInfo* CreateFileVersionInfo(
+  // Creates a FileVersionInfo for the specified path. Returns nullptr if
+  // something goes wrong (typically the file does not exit or cannot be
+  // opened).
+  static std::unique_ptr<FileVersionInfo> CreateFileVersionInfo(
       const base::FilePath& file_path);
 #endif  // OS_WIN || OS_MACOSX
 
 #if defined(OS_WIN)
-  // Creates a FileVersionInfo for the specified module. Returns NULL in case
-  // of error. The returned object should be deleted when you are done with it.
-  static FileVersionInfo* CreateFileVersionInfoForModule(HMODULE module);
+  // Creates a FileVersionInfo for the specified module. Returns nullptr in
+  // case of error.
+  static std::unique_ptr<FileVersionInfo> CreateFileVersionInfoForModule(
+      HMODULE module);
 #else
-  // Creates a FileVersionInfo for the current module. Returns NULL in case
-  // of error. The returned object should be deleted when you are done with it.
-  static FileVersionInfo* CreateFileVersionInfoForCurrentModule();
+  // Creates a FileVersionInfo for the current module. Returns nullptr in case
+  // of error.
+  static std::unique_ptr<FileVersionInfo>
+  CreateFileVersionInfoForCurrentModule();
 #endif  // OS_WIN
 
   // Accessors to the different version properties.
diff --git a/base/file_version_info_mac.mm b/base/file_version_info_mac.mm
index ce42924..4e8718f9 100644
--- a/base/file_version_info_mac.mm
+++ b/base/file_version_info_mac.mm
@@ -20,16 +20,17 @@
 FileVersionInfoMac::~FileVersionInfoMac() {}
 
 // static
-FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForCurrentModule() {
+std::unique_ptr<FileVersionInfo>
+FileVersionInfo::CreateFileVersionInfoForCurrentModule() {
   return CreateFileVersionInfo(base::mac::FrameworkBundlePath());
 }
 
 // static
-FileVersionInfo* FileVersionInfo::CreateFileVersionInfo(
+std::unique_ptr<FileVersionInfo> FileVersionInfo::CreateFileVersionInfo(
     const base::FilePath& file_path) {
   NSString* path = base::SysUTF8ToNSString(file_path.value());
   NSBundle* bundle = [NSBundle bundleWithPath:path];
-  return new FileVersionInfoMac(bundle);
+  return std::make_unique<FileVersionInfoMac>(bundle);
 }
 
 base::string16 FileVersionInfoMac::company_name() {
diff --git a/base/file_version_info_win.cc b/base/file_version_info_win.cc
index 2c1a7436..7abcd2a7 100644
--- a/base/file_version_info_win.cc
+++ b/base/file_version_info_win.cc
@@ -9,6 +9,7 @@
 
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/stl_util.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/win/resource_util.h"
@@ -49,8 +50,8 @@
 FileVersionInfoWin::~FileVersionInfoWin() = default;
 
 // static
-FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForModule(
-    HMODULE module) {
+std::unique_ptr<FileVersionInfo>
+FileVersionInfo::CreateFileVersionInfoForModule(HMODULE module) {
   void* data;
   size_t version_info_length;
   const bool has_version_resource = base::win::GetResourceFromModule(
@@ -62,13 +63,19 @@
   if (!translate)
     return nullptr;
 
-  return new FileVersionInfoWin(data, translate->language,
-                                translate->code_page);
+  return base::WrapUnique(
+      new FileVersionInfoWin(data, translate->language, translate->code_page));
 }
 
 // static
-FileVersionInfo* FileVersionInfo::CreateFileVersionInfo(
+std::unique_ptr<FileVersionInfo> FileVersionInfo::CreateFileVersionInfo(
     const FilePath& file_path) {
+  return FileVersionInfoWin::CreateFileVersionInfoWin(file_path);
+}
+
+// static
+std::unique_ptr<FileVersionInfoWin>
+FileVersionInfoWin::CreateFileVersionInfoWin(const FilePath& file_path) {
   base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
 
   DWORD dummy;
@@ -86,8 +93,8 @@
   if (!translate)
     return nullptr;
 
-  return new FileVersionInfoWin(std::move(data), translate->language,
-                                translate->code_page);
+  return base::WrapUnique(new FileVersionInfoWin(
+      std::move(data), translate->language, translate->code_page));
 }
 
 base::string16 FileVersionInfoWin::company_name() {
diff --git a/base/file_version_info_win.h b/base/file_version_info_win.h
index d91b67f..4751db1e 100644
--- a/base/file_version_info_win.h
+++ b/base/file_version_info_win.h
@@ -53,6 +53,10 @@
   // Get the fixed file info if it exists. Otherwise NULL
   const VS_FIXEDFILEINFO* fixed_file_info() const { return fixed_file_info_; }
 
+  // Behaves like CreateFileVersionInfo, but returns a FileVersionInfoWin.
+  static std::unique_ptr<FileVersionInfoWin> CreateFileVersionInfoWin(
+      const base::FilePath& file_path);
+
  private:
   friend FileVersionInfo;
 
diff --git a/base/file_version_info_win_unittest.cc b/base/file_version_info_win_unittest.cc
index a4acc4ca3..43f7f7ad 100644
--- a/base/file_version_info_win_unittest.cc
+++ b/base/file_version_info_win_unittest.cc
@@ -13,7 +13,6 @@
 #include "base/file_version_info.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
-#include "base/memory/ptr_util.h"
 #include "base/path_service.h"
 #include "base/scoped_native_library.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -37,7 +36,7 @@
   explicit FileVersionInfoFactory(const FilePath& path) : path_(path) {}
 
   std::unique_ptr<FileVersionInfo> Create() const {
-    return base::WrapUnique(FileVersionInfo::CreateFileVersionInfo(path_));
+    return FileVersionInfo::CreateFileVersionInfo(path_);
   }
 
  private:
@@ -58,8 +57,7 @@
   }
 
   std::unique_ptr<FileVersionInfo> Create() const {
-    return base::WrapUnique(
-        FileVersionInfo::CreateFileVersionInfoForModule(library_.get()));
+    return FileVersionInfo::CreateFileVersionInfoForModule(library_.get());
   }
 
  private:
diff --git a/base/files/file_enumerator_win.cc b/base/files/file_enumerator_win.cc
index b20ef47..3d1f5e794 100644
--- a/base/files/file_enumerator_win.cc
+++ b/base/files/file_enumerator_win.cc
@@ -8,6 +8,7 @@
 #include <string.h>
 
 #include "base/logging.h"
+#include "base/strings/string_util.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/win/shlwapi.h"
 
@@ -24,7 +25,7 @@
     case FileEnumerator::FolderSearchPolicy::MATCH_ONLY:
       return root_path.Append(pattern);
     case FileEnumerator::FolderSearchPolicy::ALL:
-      return root_path.Append(L"*");
+      return root_path.Append(FILE_PATH_LITERAL("*"));
   }
   NOTREACHED();
   return {};
@@ -43,7 +44,7 @@
 }
 
 FilePath FileEnumerator::FileInfo::GetName() const {
-  return FilePath(find_data_.cFileName);
+  return FilePath(CastToStringPiece16(find_data_.cFileName));
 }
 
 int64_t FileEnumerator::FileInfo::GetSize() const {
@@ -87,7 +88,7 @@
                                FolderSearchPolicy folder_search_policy)
     : recursive_(recursive),
       file_type_(file_type),
-      pattern_(!pattern.empty() ? pattern : L"*"),
+      pattern_(!pattern.empty() ? pattern : FILE_PATH_LITERAL("*")),
       folder_search_policy_(folder_search_policy) {
   // INCLUDE_DOT_DOT must not be specified if recursive.
   DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
@@ -122,7 +123,7 @@
       // Start a new find operation.
       const FilePath src =
           BuildSearchFilter(folder_search_policy_, root_path_, pattern_);
-      find_handle_ = FindFirstFileEx(src.value().c_str(),
+      find_handle_ = FindFirstFileEx(base::wdata(src.value()),
                                      FindExInfoBasic,  // Omit short name.
                                      &find_data_, FindExSearchNameMatch,
                                      nullptr, FIND_FIRST_EX_LARGE_FETCH);
@@ -146,13 +147,13 @@
         // files in the root search directory, but for those directories which
         // were matched, we want to enumerate all files inside them. This will
         // happen when the handle is empty.
-        pattern_ = L"*";
+        pattern_ = FILE_PATH_LITERAL("*");
       }
 
       continue;
     }
 
-    const FilePath filename(find_data_.cFileName);
+    const FilePath filename(CastToStringPiece16(find_data_.cFileName));
     if (ShouldSkip(filename))
       continue;
 
@@ -166,7 +167,7 @@
       // add it to pending_paths_ so we scan it after we finish scanning this
       // directory. However, don't do recursion through reparse points or we
       // may end up with an infinite cycle.
-      DWORD attributes = GetFileAttributes(abs_path.value().c_str());
+      DWORD attributes = GetFileAttributes(base::wdata(abs_path.value()));
       if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT))
         pending_paths_.push(abs_path);
     }
@@ -186,7 +187,8 @@
     case FolderSearchPolicy::ALL:
       // ALL policy enumerates all files, we need to check pattern match
       // manually.
-      return PathMatchSpec(src.value().c_str(), pattern_.c_str()) == TRUE;
+      return PathMatchSpec(base::wdata(src.value()), base::wdata(pattern_)) ==
+             TRUE;
   }
   NOTREACHED();
   return false;
diff --git a/base/files/file_path.cc b/base/files/file_path.cc
index ab6c2f9..bfc1122 100644
--- a/base/files/file_path.cc
+++ b/base/files/file_path.cc
@@ -607,7 +607,7 @@
 }
 
 std::string FilePath::AsUTF8Unsafe() const {
-  return WideToUTF8(value());
+  return UTF16ToUTF8(value());
 }
 
 string16 FilePath::AsUTF16Unsafe() const {
@@ -616,7 +616,7 @@
 
 // static
 FilePath FilePath::FromUTF8Unsafe(StringPiece utf8) {
-  return FilePath(UTF8ToWide(utf8));
+  return FilePath(UTF8ToUTF16(utf8));
 }
 
 // static
diff --git a/base/files/file_path_unittest.cc b/base/files/file_path_unittest.cc
index 34aa91c..afe70916 100644
--- a/base/files/file_path_unittest.cc
+++ b/base/files/file_path_unittest.cc
@@ -26,33 +26,33 @@
 namespace base {
 
 struct UnaryTestData {
-  const FilePath::CharType* input;
-  const FilePath::CharType* expected;
+  FilePath::StringPieceType input;
+  FilePath::StringPieceType expected;
 };
 
 struct UnaryBooleanTestData {
-  const FilePath::CharType* input;
+  FilePath::StringPieceType input;
   bool expected;
 };
 
 struct BinaryTestData {
-  const FilePath::CharType* inputs[2];
-  const FilePath::CharType* expected;
+  FilePath::StringPieceType inputs[2];
+  FilePath::StringPieceType expected;
 };
 
 struct BinaryBooleanTestData {
-  const FilePath::CharType* inputs[2];
+  FilePath::StringPieceType inputs[2];
   bool expected;
 };
 
 struct BinaryIntTestData {
-  const FilePath::CharType* inputs[2];
+  FilePath::StringPieceType inputs[2];
   int expected;
 };
 
 struct UTF8TestData {
-  const FilePath::CharType* native;
-  const char* utf8;
+  FilePath::StringPieceType native;
+  StringPiece utf8;
 };
 
 // file_util winds up using autoreleased objects on the Mac, so this needs
@@ -320,7 +320,7 @@
     // TODO(erikkay): It would be nice to have a unicode test append value to
     // handle the case when AppendASCII is passed UTF8
 #if defined(OS_WIN)
-    std::string ascii = WideToUTF8(leaf);
+    std::string ascii = UTF16ToUTF8(leaf);
 #elif defined(OS_POSIX) || defined(OS_FUCHSIA)
     std::string ascii = leaf;
 #endif
@@ -629,19 +629,19 @@
     {
       FilePath result;
       bool success = parent.AppendRelativePath(child, &result);
-      EXPECT_EQ(cases[i].expected[0] != '\0', success) <<
-        "i: " << i << ", parent: " << parent.value() << ", child: " <<
-        child.value();
-      EXPECT_STREQ(cases[i].expected, result.value().c_str()) <<
-        "i: " << i << ", parent: " << parent.value() << ", child: " <<
-        child.value();
+      EXPECT_EQ(!cases[i].expected.empty(), success)
+          << "i: " << i << ", parent: " << parent.value()
+          << ", child: " << child.value();
+      EXPECT_EQ(cases[i].expected, result.value())
+          << "i: " << i << ", parent: " << parent.value()
+          << ", child: " << child.value();
     }
     {
       FilePath result(base);
       bool success = parent.AppendRelativePath(child, &result);
-      EXPECT_EQ(cases[i].expected[0] != '\0', success) <<
-        "i: " << i << ", parent: " << parent.value() << ", child: " <<
-        child.value();
+      EXPECT_EQ(!cases[i].expected.empty(), success)
+          << "i: " << i << ", parent: " << parent.value()
+          << ", child: " << child.value();
       EXPECT_EQ(base.Append(cases[i].expected).value(), result.value()) <<
         "i: " << i << ", parent: " << parent.value() << ", child: " <<
         child.value();
@@ -777,15 +777,15 @@
     FilePath path(cases[i].input);
     FilePath::StringType extension = path.Extension();
     FilePath::StringType final_extension = path.FinalExtension();
-    EXPECT_STREQ(cases[i].expected, extension.c_str())
+    EXPECT_EQ(cases[i].expected, extension)
         << "i: " << i << ", path: " << path.value();
-    EXPECT_STREQ(cases[i].expected, final_extension.c_str())
+    EXPECT_EQ(cases[i].expected, final_extension)
         << "i: " << i << ", path: " << path.value();
   }
   for (unsigned int i = 0; i < base::size(double_extension_cases); ++i) {
     FilePath path(double_extension_cases[i].input);
     FilePath::StringType extension = path.Extension();
-    EXPECT_STREQ(double_extension_cases[i].expected, extension.c_str())
+    EXPECT_EQ(double_extension_cases[i].expected, extension)
         << "i: " << i << ", path: " << path.value();
   }
 }
@@ -853,8 +853,9 @@
   for (unsigned int i = 0; i < base::size(cases); ++i) {
     FilePath path(cases[i].inputs[0]);
     FilePath result = path.InsertBeforeExtension(cases[i].inputs[1]);
-    EXPECT_EQ(cases[i].expected, result.value()) << "i: " << i <<
-        ", path: " << path.value() << ", insert: " << cases[i].inputs[1];
+    EXPECT_EQ(cases[i].expected, result.value())
+        << "i: " << i << ", path: " << path.value()
+        << ", insert: " << cases[i].inputs[1];
   }
 }
 
diff --git a/base/files/file_path_watcher_win.cc b/base/files/file_path_watcher_win.cc
index d468602e..8bb8e94 100644
--- a/base/files/file_path_watcher_win.cc
+++ b/base/files/file_path_watcher_win.cc
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/string_util.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
@@ -202,11 +203,10 @@
                                            HANDLE* handle) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
   *handle = FindFirstChangeNotification(
-      dir.value().c_str(),
-      recursive,
+      base::wdata(dir.value()), recursive,
       FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE |
-      FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME |
-      FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY);
+          FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME |
+          FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY);
   if (*handle != INVALID_HANDLE_VALUE) {
     // Make sure the handle we got points to an existing directory. It seems
     // that windows sometimes hands out watches to directories that are
diff --git a/base/files/file_unittest.cc b/base/files/file_unittest.cc
index 8a57322..e336741 100644
--- a/base/files/file_unittest.cc
+++ b/base/files/file_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/files/file_util.h"
 #include "base/files/memory_mapped_file.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/strings/string_util.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -576,10 +577,8 @@
   ASSERT_TRUE(CreateDirectory(empty_dir));
 
   base::File dir(
-      ::CreateFile(empty_dir.value().c_str(),
-                   GENERIC_READ | GENERIC_WRITE,
-                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-                   NULL,
+      ::CreateFile(base::wdata(empty_dir.value()), GENERIC_READ | GENERIC_WRITE,
+                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
                    OPEN_EXISTING,
                    FILE_FLAG_BACKUP_SEMANTICS,  // Needed to open a directory.
                    NULL));
diff --git a/base/files/file_util.cc b/base/files/file_util.cc
index 73cee5b..4c25acf1 100644
--- a/base/files/file_util.cc
+++ b/base/files/file_util.cc
@@ -54,10 +54,15 @@
   // We open the file in binary format even if they are text files because
   // we are just comparing that bytes are exactly same in both files and not
   // doing anything smart with text formatting.
-  std::ifstream file1(filename1.value().c_str(),
+#if defined(OS_WIN)
+  std::ifstream file1(base::wdata(filename1.value()),
                       std::ios::in | std::ios::binary);
-  std::ifstream file2(filename2.value().c_str(),
+  std::ifstream file2(base::wdata(filename2.value()),
                       std::ios::in | std::ios::binary);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  std::ifstream file1(filename1.value(), std::ios::in | std::ios::binary);
+  std::ifstream file2(filename2.value(), std::ios::in | std::ios::binary);
+#endif  // OS_WIN
 
   // Even if both files aren't openable (and thus, in some sense, "equal"),
   // any unusable file yields a result of "false".
@@ -85,8 +90,13 @@
 }
 
 bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
-  std::ifstream file1(filename1.value().c_str(), std::ios::in);
-  std::ifstream file2(filename2.value().c_str(), std::ios::in);
+#if defined(OS_WIN)
+  std::ifstream file1(base::wdata(filename1.value()), std::ios::in);
+  std::ifstream file2(base::wdata(filename2.value()), std::ios::in);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  std::ifstream file1(filename1.value(), std::ios::in);
+  std::ifstream file2(filename2.value(), std::ios::in);
+#endif  // OS_WIN
 
   // Even if both files aren't openable (and thus, in some sense, "equal"),
   // any unusable file yields a result of "false".
diff --git a/base/files/file_util_unittest.cc b/base/files/file_util_unittest.cc
index 7e80583..bc00a1b 100644
--- a/base/files/file_util_unittest.cc
+++ b/base/files/file_util_unittest.cc
@@ -111,13 +111,13 @@
 // Sets a reparse point. |source| will now point to |target|. Returns true if
 // the call succeeds, false otherwise.
 bool SetReparsePoint(HANDLE source, const FilePath& target_path) {
-  std::wstring kPathPrefix = L"\\??\\";
-  std::wstring target_str;
+  base::string16 kPathPrefix = FILE_PATH_LITERAL("\\??\\");
+  base::string16 target_str;
   // The juction will not work if the target path does not start with \??\ .
   if (kPathPrefix != target_path.value().substr(0, kPathPrefix.size()))
     target_str += kPathPrefix;
   target_str += target_path.value();
-  const wchar_t* target = target_str.c_str();
+  const wchar_t* target = base::wdata(target_str);
   USHORT size_target = static_cast<USHORT>(wcslen(target)) * sizeof(target[0]);
   char buffer[2000] = {0};
   DWORD returned;
@@ -159,13 +159,11 @@
   // Creates a reparse point from |source| (an empty directory) to |target|.
   ReparsePoint(const FilePath& source, const FilePath& target) {
     dir_.Set(
-      ::CreateFile(source.value().c_str(),
-                   GENERIC_READ | GENERIC_WRITE,
-                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-                   NULL,
-                   OPEN_EXISTING,
-                   FILE_FLAG_BACKUP_SEMANTICS,  // Needed to open a directory.
-                   NULL));
+        ::CreateFile(base::wdata(source.value()), GENERIC_READ | GENERIC_WRITE,
+                     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                     NULL, OPEN_EXISTING,
+                     FILE_FLAG_BACKUP_SEMANTICS,  // Needed to open a directory.
+                     NULL));
     created_ = dir_.IsValid() && SetReparsePoint(dir_.Get(), target);
   }
 
@@ -208,11 +206,12 @@
 void SetReadOnly(const FilePath& path, bool read_only) {
 #if defined(OS_WIN)
   // On Windows, it involves setting/removing the 'readonly' bit.
-  DWORD attrs = GetFileAttributes(path.value().c_str());
+  DWORD attrs = GetFileAttributes(base::wdata(path.value()));
   ASSERT_NE(INVALID_FILE_ATTRIBUTES, attrs);
-  ASSERT_TRUE(SetFileAttributes(
-      path.value().c_str(), read_only ? (attrs | FILE_ATTRIBUTE_READONLY)
-                                      : (attrs & ~FILE_ATTRIBUTE_READONLY)));
+  ASSERT_TRUE(SetFileAttributes(base::wdata(path.value()),
+                                read_only
+                                    ? (attrs | FILE_ATTRIBUTE_READONLY)
+                                    : (attrs & ~FILE_ATTRIBUTE_READONLY)));
 
   DWORD expected =
       read_only
@@ -221,7 +220,7 @@
           : (attrs & (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY));
 
   // Ignore FILE_ATTRIBUTE_NOT_CONTENT_INDEXED if present.
-  attrs = GetFileAttributes(path.value().c_str()) &
+  attrs = GetFileAttributes(base::wdata(path.value())) &
           ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
   ASSERT_EQ(expected, attrs);
 #else
@@ -234,7 +233,7 @@
 
 bool IsReadOnly(const FilePath& path) {
 #if defined(OS_WIN)
-  DWORD attrs = GetFileAttributes(path.value().c_str());
+  DWORD attrs = GetFileAttributes(base::wdata(path.value()));
   EXPECT_NE(INVALID_FILE_ATTRIBUTES, attrs);
   return attrs & FILE_ATTRIBUTE_READONLY;
 #else
@@ -297,7 +296,11 @@
 void CreateTextFile(const FilePath& filename,
                     const std::wstring& contents) {
   std::wofstream file;
-  file.open(filename.value().c_str());
+#if defined(OS_WIN)
+  file.open(base::wdata(filename.value()));
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  file.open(filename.value());
+#endif  // OS_WIN
   ASSERT_TRUE(file.is_open());
   file << contents;
   file.close();
@@ -307,7 +310,11 @@
 std::wstring ReadTextFile(const FilePath& filename) {
   wchar_t contents[64];
   std::wifstream file;
-  file.open(filename.value().c_str());
+#if defined(OS_WIN)
+  file.open(base::wdata(filename.value()));
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  file.open(filename.value());
+#endif  // OS_WIN
   EXPECT_TRUE(file.is_open());
   file.getline(contents, base::size(contents));
   file.close();
@@ -476,13 +483,13 @@
 
     // Normalize a junction free path: base_a\sub_a\file.txt .
     ASSERT_TRUE(NormalizeFilePath(file_txt, &normalized_path));
-    ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
+    ASSERT_EQ(file_txt.value(), normalized_path.value());
 
     // Check that the path base_b\to_sub_a\file.txt can be normalized to exclude
     // the junction to_sub_a.
     ASSERT_TRUE(NormalizeFilePath(to_sub_a.Append(FPL("file.txt")),
                                              &normalized_path));
-    ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
+    ASSERT_EQ(file_txt.value(), normalized_path.value());
 
     // Check that the path base_b\to_base_b\to_base_b\to_sub_a\file.txt can be
     // normalized to exclude junctions to_base_b and to_sub_a .
@@ -491,7 +498,7 @@
                                                    .Append(FPL("to_sub_a"))
                                                    .Append(FPL("file.txt")),
                                              &normalized_path));
-    ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
+    ASSERT_EQ(file_txt.value(), normalized_path.value());
 
     // A long enough path will cause NormalizeFilePath() to fail.  Make a long
     // path using to_base_b many times, and check that paths long enough to fail
@@ -529,9 +536,9 @@
   }
 
   // Get the NT style path to that drive.
-  wchar_t device_path[MAX_PATH] = {'\0'};
-  ASSERT_TRUE(
-      ::QueryDosDevice(real_drive_letter.c_str(), device_path, MAX_PATH));
+  base::char16 device_path[MAX_PATH] = {'\0'};
+  ASSERT_TRUE(::QueryDosDevice(base::wdata(real_drive_letter),
+                               base::wdata(device_path), MAX_PATH));
   FilePath actual_device_path(device_path);
   FilePath win32_path;
 
@@ -546,7 +553,9 @@
   ASSERT_TRUE(DevicePathToDriveLetterPath(
       actual_device_path.Append(kRelativePath),
       &win32_path));
-  EXPECT_EQ(FilePath(real_drive_letter + L"\\").Append(kRelativePath).value(),
+  EXPECT_EQ(FilePath(real_drive_letter + FILE_PATH_LITERAL("\\"))
+                .Append(kRelativePath)
+                .value(),
             win32_path.value());
 
   // Deform the real path so that it is invalid by removing the last four
@@ -594,24 +603,24 @@
   // - the filesystem at |temp_dir_| supports long filenames.
   // - the account has FILE_LIST_DIRECTORY permission for all ancestor
   //   directories of |temp_dir_|.
-  const FilePath::CharType kLongDirName[] = FPL("A long path");
-  const FilePath::CharType kTestSubDirName[] = FPL("test");
+  constexpr FilePath::CharType kLongDirName[] = FPL("A long path");
+  constexpr FilePath::CharType kTestSubDirName[] = FPL("test");
   FilePath long_test_dir = temp_dir_.GetPath().Append(kLongDirName);
   ASSERT_TRUE(CreateDirectory(long_test_dir));
 
   // kLongDirName is not a 8.3 component. So GetShortName() should give us a
   // different short name.
   WCHAR path_buffer[MAX_PATH];
-  DWORD path_buffer_length = GetShortPathName(long_test_dir.value().c_str(),
-                                              path_buffer, MAX_PATH);
+  DWORD path_buffer_length = GetShortPathName(
+      base::wdata(long_test_dir.value()), path_buffer, MAX_PATH);
   ASSERT_LT(path_buffer_length, DWORD(MAX_PATH));
   ASSERT_NE(DWORD(0), path_buffer_length);
-  FilePath short_test_dir(path_buffer);
-  ASSERT_STRNE(kLongDirName, short_test_dir.BaseName().value().c_str());
+  FilePath short_test_dir(CastToStringPiece16(path_buffer));
+  ASSERT_NE(kLongDirName, short_test_dir.BaseName().value());
 
   FilePath temp_file;
   ASSERT_TRUE(CreateTemporaryFileInDir(short_test_dir, &temp_file));
-  EXPECT_STREQ(kLongDirName, temp_file.DirName().BaseName().value().c_str());
+  EXPECT_EQ(kLongDirName, temp_file.DirName().BaseName().value());
   EXPECT_TRUE(PathExists(temp_file));
 
   // Create a subdirectory of |long_test_dir| and make |long_test_dir|
@@ -633,8 +642,8 @@
   EXPECT_TRUE(short_test_dir.IsParent(temp_file.DirName()));
 
   // Check that the long path can't be determined for |temp_file|.
-  path_buffer_length = GetLongPathName(temp_file.value().c_str(),
-                                       path_buffer, MAX_PATH);
+  path_buffer_length =
+      GetLongPathName(base::wdata(temp_file.value()), path_buffer, MAX_PATH);
   EXPECT_EQ(DWORD(0), path_buffer_length);
 }
 
@@ -3032,8 +3041,9 @@
   EXPECT_TRUE(StringToUint(switch_string, &switch_uint));
   win::ScopedHandle sync_event(win::Uint32ToHandle(switch_uint));
 
-  HANDLE ph = CreateNamedPipe(pipe_path.value().c_str(), PIPE_ACCESS_OUTBOUND,
-                              PIPE_WAIT, 1, 0, 0, 0, NULL);
+  HANDLE ph =
+      CreateNamedPipe(base::wdata(pipe_path.value()), PIPE_ACCESS_OUTBOUND,
+                      PIPE_WAIT, 1, 0, 0, 0, NULL);
   EXPECT_NE(ph, INVALID_HANDLE_VALUE);
   EXPECT_TRUE(SetEvent(sync_event.Get()));
   EXPECT_TRUE(ConnectNamedPipe(ph, NULL));
@@ -3059,8 +3069,9 @@
   EXPECT_TRUE(StringToUint(switch_string, &switch_uint));
   win::ScopedHandle sync_event(win::Uint32ToHandle(switch_uint));
 
-  HANDLE ph = CreateNamedPipe(pipe_path.value().c_str(), PIPE_ACCESS_OUTBOUND,
-                              PIPE_WAIT, 1, data.size(), data.size(), 0, NULL);
+  HANDLE ph =
+      CreateNamedPipe(base::wdata(pipe_path.value()), PIPE_ACCESS_OUTBOUND,
+                      PIPE_WAIT, 1, data.size(), data.size(), 0, NULL);
   EXPECT_NE(ph, INVALID_HANDLE_VALUE);
   EXPECT_TRUE(SetEvent(sync_event.Get()));
   EXPECT_TRUE(ConnectNamedPipe(ph, NULL));
diff --git a/base/files/file_util_win.cc b/base/files/file_util_win.cc
index e4544698..18db61b4 100644
--- a/base/files/file_util_win.cc
+++ b/base/files/file_util_win.cc
@@ -66,7 +66,7 @@
   } metric = PostOperationState::kOperationSucceeded;
 
   if (!operation_succeeded) {
-    const DWORD attributes = ::GetFileAttributes(path.value().c_str());
+    const DWORD attributes = ::GetFileAttributes(base::wdata(path.value()));
     if (attributes == INVALID_FILE_ATTRIBUTES) {
       // On failure to delete, one might expect the file/directory to still be
       // in place. Slice a failure to get its attributes into a few common error
@@ -133,7 +133,7 @@
     if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) &&
         (recursive || !info.IsDirectory())) {
       ::SetFileAttributes(
-          current.value().c_str(),
+          base::wdata(current.value()),
           info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
     }
 
@@ -144,11 +144,11 @@
         DCHECK_NE(static_cast<LONG>(this_result), ERROR_FILE_NOT_FOUND);
         DCHECK_NE(static_cast<LONG>(this_result), ERROR_PATH_NOT_FOUND);
         if (this_result == ERROR_SUCCESS &&
-            !::RemoveDirectory(current.value().c_str())) {
+            !::RemoveDirectory(base::wdata(current.value()))) {
           this_result = ReturnLastErrorOrSuccessOnNotFound();
         }
       }
-    } else if (!::DeleteFile(current.value().c_str())) {
+    } else if (!::DeleteFile(base::wdata(current.value()))) {
       this_result = ReturnLastErrorOrSuccessOnNotFound();
     }
     if (result == ERROR_SUCCESS)
@@ -183,8 +183,8 @@
   // the ACL bits, CopyFile() copies the complete SECURITY_DESCRIPTOR and access
   // bits, which is usually not what we want. We can't do much about the
   // SECURITY_DESCRIPTOR but at least remove the read only bit.
-  const wchar_t* dest = to_path.value().c_str();
-  if (!::CopyFile(from_path.value().c_str(), dest, fail_if_exists)) {
+  const wchar_t* dest = base::wdata(to_path.value());
+  if (!::CopyFile(base::wdata(from_path.value()), dest, fail_if_exists)) {
     // Copy failed.
     return false;
   }
@@ -269,7 +269,7 @@
 
     if (from_is_dir) {
       if (!DirectoryExists(target_path) &&
-          !::CreateDirectory(target_path.value().c_str(), NULL)) {
+          !::CreateDirectory(base::wdata(target_path.value()), NULL)) {
         DLOG(ERROR) << "CopyDirectory() couldn't create directory: "
                     << target_path.value().c_str();
         success = false;
@@ -299,7 +299,7 @@
     return ERROR_BAD_PATHNAME;
 
   // Handle any path with wildcards.
-  if (path.BaseName().value().find_first_of(L"*?") !=
+  if (path.BaseName().value().find_first_of(FILE_PATH_LITERAL("*?")) !=
       FilePath::StringType::npos) {
     const DWORD error_code =
         DeleteFileRecursive(path.DirName(), path.BaseName().value(), recursive);
@@ -309,13 +309,13 @@
   }
 
   // Report success if the file or path does not exist.
-  const DWORD attr = ::GetFileAttributes(path.value().c_str());
+  const DWORD attr = ::GetFileAttributes(base::wdata(path.value()));
   if (attr == INVALID_FILE_ATTRIBUTES)
     return ReturnLastErrorOrSuccessOnNotFound();
 
   // Clear the read-only bit if it is set.
   if ((attr & FILE_ATTRIBUTE_READONLY) &&
-      !::SetFileAttributes(path.value().c_str(),
+      !::SetFileAttributes(base::wdata(path.value()),
                            attr & ~FILE_ATTRIBUTE_READONLY)) {
     // It's possible for |path| to be gone now under a race with other deleters.
     return ReturnLastErrorOrSuccessOnNotFound();
@@ -323,19 +323,20 @@
 
   // Perform a simple delete on anything that isn't a directory.
   if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
-    return ::DeleteFile(path.value().c_str())
+    return ::DeleteFile(base::wdata(path.value()))
                ? ERROR_SUCCESS
                : ReturnLastErrorOrSuccessOnNotFound();
   }
 
   if (recursive) {
-    const DWORD error_code = DeleteFileRecursive(path, L"*", true);
+    const DWORD error_code =
+        DeleteFileRecursive(path, FILE_PATH_LITERAL("*"), true);
     DCHECK_NE(static_cast<LONG>(error_code), ERROR_FILE_NOT_FOUND);
     DCHECK_NE(static_cast<LONG>(error_code), ERROR_PATH_NOT_FOUND);
     if (error_code != ERROR_SUCCESS)
       return error_code;
   }
-  return ::RemoveDirectory(path.value().c_str())
+  return ::RemoveDirectory(base::wdata(path.value()))
              ? ERROR_SUCCESS
              : ReturnLastErrorOrSuccessOnNotFound();
 }
@@ -344,8 +345,8 @@
 
 FilePath MakeAbsoluteFilePath(const FilePath& input) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
-  wchar_t file_path[MAX_PATH];
-  if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH))
+  base::char16 file_path[MAX_PATH];
+  if (!_wfullpath(base::wdata(file_path), base::wdata(input.value()), MAX_PATH))
     return FilePath();
   return FilePath(file_path);
 }
@@ -376,7 +377,7 @@
   if (path.value().length() >= MAX_PATH)
     return false;
 
-  return ::MoveFileEx(path.value().c_str(), nullptr,
+  return ::MoveFileEx(base::wdata(path.value()), nullptr,
                       MOVEFILE_DELAY_UNTIL_REBOOT);
 }
 
@@ -386,7 +387,7 @@
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
   // Try a simple move first.  It will only succeed when |to_path| doesn't
   // already exist.
-  if (::MoveFile(from_path.value().c_str(), to_path.value().c_str()))
+  if (::MoveFile(base::wdata(from_path.value()), base::wdata(to_path.value())))
     return true;
   File::Error move_error = File::OSErrorToFileError(GetLastError());
 
@@ -394,7 +395,8 @@
   // succeed when |to_path| does exist. When writing to a network share, we may
   // not be able to change the ACLs. Ignore ACL errors then
   // (REPLACEFILE_IGNORE_MERGE_ERRORS).
-  if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL,
+  if (::ReplaceFile(base::wdata(to_path.value()),
+                    base::wdata(from_path.value()), NULL,
                     REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) {
     return true;
   }
@@ -423,14 +425,15 @@
 
 bool PathExists(const FilePath& path) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
-  return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
+  return (GetFileAttributes(base::wdata(path.value())) !=
+          INVALID_FILE_ATTRIBUTES);
 }
 
 bool PathIsWritable(const FilePath& path) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
   HANDLE dir =
-      CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll,
-                 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+      CreateFile(base::wdata(path.value()), FILE_ADD_FILE, kFileShareAll, NULL,
+                 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
 
   if (dir == INVALID_HANDLE_VALUE)
     return false;
@@ -441,15 +444,15 @@
 
 bool DirectoryExists(const FilePath& path) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
-  DWORD fileattr = GetFileAttributes(path.value().c_str());
+  DWORD fileattr = GetFileAttributes(base::wdata(path.value()));
   if (fileattr != INVALID_FILE_ATTRIBUTES)
     return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
   return false;
 }
 
 bool GetTempDir(FilePath* path) {
-  wchar_t temp_path[MAX_PATH + 1];
-  DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
+  base::char16 temp_path[MAX_PATH + 1];
+  DWORD path_len = ::GetTempPath(MAX_PATH, base::wdata(temp_path));
   if (path_len >= MAX_PATH || path_len <= 0)
     return false;
   // TODO(evanm): the old behavior of this function was to always strip the
@@ -462,7 +465,7 @@
 FilePath GetHomeDir() {
   char16 result[MAX_PATH];
   if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT,
-                                result)) &&
+                                base::wdata(result))) &&
       result[0]) {
     return FilePath(result);
   }
@@ -473,7 +476,7 @@
     return temp;
 
   // Last resort.
-  return FilePath(L"C:\\");
+  return FilePath(FILE_PATH_LITERAL("C:\\"));
 }
 
 bool CreateTemporaryFile(FilePath* path) {
@@ -523,7 +526,8 @@
   // Although it is nearly impossible to get a duplicate name with GUID, we
   // still use a loop here in case it happens.
   for (int i = 0; i < 100; ++i) {
-    temp_name = dir.Append(ASCIIToUTF16(base::GenerateGUID()) + L".tmp");
+    temp_name = dir.Append(ASCIIToUTF16(base::GenerateGUID()) +
+                           FILE_PATH_LITERAL(".tmp"));
     File file(temp_name,
               File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE);
     if (file.IsValid()) {
@@ -539,9 +543,9 @@
     return false;
   }
 
-  wchar_t long_temp_name[MAX_PATH + 1];
-  DWORD long_name_len =
-      GetLongPathName(temp_name.value().c_str(), long_temp_name, MAX_PATH);
+  base::char16 long_temp_name[MAX_PATH + 1];
+  DWORD long_name_len = GetLongPathName(base::wdata(temp_name.value()),
+                                        base::wdata(long_temp_name), MAX_PATH);
   if (long_name_len > MAX_PATH || long_name_len == 0) {
     // GetLongPathName() failed, but we still have a temporary file.
     *temp_file = std::move(temp_name);
@@ -572,7 +576,7 @@
         IntToString16(RandInt(0, std::numeric_limits<int16_t>::max())));
 
     path_to_create = base_dir.Append(new_dir_name);
-    if (::CreateDirectory(path_to_create.value().c_str(), NULL)) {
+    if (::CreateDirectory(base::wdata(path_to_create.value()), NULL)) {
       *new_dir = path_to_create;
       return true;
     }
@@ -597,7 +601,7 @@
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
 
   // If the path exists, we've succeeded if it's a directory, failed otherwise.
-  const wchar_t* const full_path_str = full_path.value().c_str();
+  const wchar_t* const full_path_str = base::wdata(full_path.value());
   const DWORD fileattr = ::GetFileAttributes(full_path_str);
   if (fileattr != INVALID_FILE_ATTRIBUTES) {
     if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
@@ -676,8 +680,8 @@
   // The drive mapping is a sequence of null terminated strings.
   // The last string is empty.
   wchar_t* drive_map_ptr = drive_mapping;
-  wchar_t device_path_as_string[MAX_PATH];
-  wchar_t drive[] = L" :";
+  base::char16 device_path_as_string[MAX_PATH];
+  base::char16 drive[] = FILE_PATH_LITERAL(" :");
 
   // For each string in the drive mapping, get the junction that links
   // to it.  If that junction is a prefix of |device_path|, then we
@@ -685,12 +689,14 @@
   while (*drive_map_ptr) {
     drive[0] = drive_map_ptr[0];  // Copy the drive letter.
 
-    if (QueryDosDevice(drive, device_path_as_string, MAX_PATH)) {
+    if (QueryDosDevice(base::wdata(drive), base::wdata(device_path_as_string),
+                       MAX_PATH)) {
       FilePath device_path(device_path_as_string);
       if (device_path == nt_device_path ||
           device_path.IsParent(nt_device_path)) {
-        *out_drive_letter_path = FilePath(drive +
-            nt_device_path.value().substr(wcslen(device_path_as_string)));
+        *out_drive_letter_path =
+            FilePath(drive + nt_device_path.value().substr(
+                                 wcslen(base::wdata(device_path_as_string))));
         return true;
       }
     }
@@ -713,13 +719,8 @@
   // function uses is explained in the following msdn article:
   // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
   win::ScopedHandle file_handle(
-      ::CreateFile(path.value().c_str(),
-                   GENERIC_READ,
-                   kFileShareAll,
-                   NULL,
-                   OPEN_EXISTING,
-                   FILE_ATTRIBUTE_NORMAL,
-                   NULL));
+      ::CreateFile(base::wdata(path.value()), GENERIC_READ, kFileShareAll, NULL,
+                   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
   if (!file_handle.IsValid())
     return false;
 
@@ -749,10 +750,11 @@
   // not return kMaxPathLength.  This would mean that only part of the
   // path fit in |mapped_file_path|.
   const int kMaxPathLength = MAX_PATH + 10;
-  wchar_t mapped_file_path[kMaxPathLength];
+  base::char16 mapped_file_path[kMaxPathLength];
   bool success = false;
   HANDLE cp = GetCurrentProcess();
-  if (::GetMappedFileNameW(cp, file_view, mapped_file_path, kMaxPathLength)) {
+  if (::GetMappedFileNameW(cp, file_view, base::wdata(mapped_file_path),
+                           kMaxPathLength)) {
     *nt_path = FilePath(mapped_file_path);
     success = true;
   }
@@ -770,7 +772,7 @@
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
 
   WIN32_FILE_ATTRIBUTE_DATA attr;
-  if (!GetFileAttributesEx(file_path.value().c_str(),
+  if (!GetFileAttributesEx(base::wdata(file_path.value()),
                            GetFileExInfoStandard, &attr)) {
     return false;
   }
@@ -798,7 +800,8 @@
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
   string16 w_mode = ASCIIToUTF16(mode);
   AppendModeCharacter(L'N', &w_mode);
-  return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO);
+  return _wfsopen(base::wdata(filename.value()), base::wdata(w_mode),
+                  _SH_DENYNO);
 }
 
 FILE* FileToFILE(File file, const char* mode) {
@@ -817,12 +820,9 @@
 
 int ReadFile(const FilePath& filename, char* data, int max_size) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
-  win::ScopedHandle file(CreateFile(filename.value().c_str(),
-                                    GENERIC_READ,
-                                    FILE_SHARE_READ | FILE_SHARE_WRITE,
-                                    NULL,
-                                    OPEN_EXISTING,
-                                    FILE_FLAG_SEQUENTIAL_SCAN,
+  win::ScopedHandle file(CreateFile(base::wdata(filename.value()), GENERIC_READ,
+                                    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                                    OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,
                                     NULL));
   if (!file.IsValid())
     return -1;
@@ -836,9 +836,9 @@
 
 int WriteFile(const FilePath& filename, const char* data, int size) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
-  win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_WRITE, 0,
-                                    NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
-                                    NULL));
+  win::ScopedHandle file(CreateFile(base::wdata(filename.value()),
+                                    GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+                                    FILE_ATTRIBUTE_NORMAL, NULL));
   if (!file.IsValid()) {
     DPLOG(WARNING) << "CreateFile failed for path "
                    << UTF16ToUTF8(filename.value());
@@ -864,12 +864,8 @@
 
 bool AppendToFile(const FilePath& filename, const char* data, int size) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
-  win::ScopedHandle file(CreateFile(filename.value().c_str(),
-                                    FILE_APPEND_DATA,
-                                    0,
-                                    NULL,
-                                    OPEN_EXISTING,
-                                    0,
+  win::ScopedHandle file(CreateFile(base::wdata(filename.value()),
+                                    FILE_APPEND_DATA, 0, NULL, OPEN_EXISTING, 0,
                                     NULL));
   if (!file.IsValid()) {
     VPLOG(1) << "CreateFile failed for path " << UTF16ToUTF8(filename.value());
@@ -895,29 +891,29 @@
 bool GetCurrentDirectory(FilePath* dir) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
 
-  wchar_t system_buffer[MAX_PATH];
+  base::char16 system_buffer[MAX_PATH];
   system_buffer[0] = 0;
-  DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
+  DWORD len = ::GetCurrentDirectory(MAX_PATH, base::wdata(system_buffer));
   if (len == 0 || len > MAX_PATH)
     return false;
   // TODO(evanm): the old behavior of this function was to always strip the
   // trailing slash.  We duplicate this here, but it shouldn't be necessary
   // when everyone is using the appropriate FilePath APIs.
-  std::wstring dir_str(system_buffer);
+  base::string16 dir_str(system_buffer);
   *dir = FilePath(dir_str).StripTrailingSeparators();
   return true;
 }
 
 bool SetCurrentDirectory(const FilePath& directory) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
-  return ::SetCurrentDirectory(directory.value().c_str()) != 0;
+  return ::SetCurrentDirectory(base::wdata(directory.value())) != 0;
 }
 
 int GetMaximumPathComponentLength(const FilePath& path) {
   ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
 
   wchar_t volume_path[MAX_PATH];
-  if (!GetVolumePathNameW(path.NormalizePathSeparators().value().c_str(),
+  if (!GetVolumePathNameW(base::wdata(path.NormalizePathSeparators().value()),
                           volume_path, base::size(volume_path))) {
     return -1;
   }
@@ -960,7 +956,7 @@
       to_path.value().length() >= MAX_PATH) {
     return false;
   }
-  if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(),
+  if (MoveFileEx(base::wdata(from_path.value()), base::wdata(to_path.value()),
                  MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0)
     return true;
 
diff --git a/base/files/file_win.cc b/base/files/file_win.cc
index 2662713..1acd20f 100644
--- a/base/files/file_win.cc
+++ b/base/files/file_win.cc
@@ -9,6 +9,7 @@
 
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/strings/string_util.h"
 #include "base/threading/scoped_blocking_call.h"
 
 #include <windows.h>
@@ -414,7 +415,7 @@
   if (flags & FLAG_SEQUENTIAL_SCAN)
     create_flags |= FILE_FLAG_SEQUENTIAL_SCAN;
 
-  file_.Set(CreateFile(path.value().c_str(), access, sharing, NULL,
+  file_.Set(CreateFile(base::wdata(path.value()), access, sharing, NULL,
                        disposition, create_flags, NULL));
 
   if (file_.IsValid()) {
diff --git a/base/strings/string16.cc b/base/strings/string16.cc
index 2abb0e5d..84962e6 100644
--- a/base/strings/string16.cc
+++ b/base/strings/string16.cc
@@ -15,7 +15,7 @@
 
 #include <ostream>
 
-#include "base/strings/utf_string_conversions.h"
+#include "base/strings/string_piece.h"
 
 namespace base {
 
@@ -70,7 +70,7 @@
 namespace string16_internals {
 
 std::ostream& operator<<(std::ostream& out, const string16& str) {
-  return out << UTF16ToUTF8(str);
+  return out << base::StringPiece16(str);
 }
 
 void PrintTo(const string16& str, std::ostream* out) {
diff --git a/base/strings/string_piece.cc b/base/strings/string_piece.cc
index f9bba9c..278849fa 100644
--- a/base/strings/string_piece.cc
+++ b/base/strings/string_piece.cc
@@ -11,6 +11,7 @@
 #include <ostream>
 
 #include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
 
 namespace base {
 namespace {
@@ -45,6 +46,10 @@
   return o;
 }
 
+std::ostream& operator<<(std::ostream& o, const StringPiece16& piece) {
+  return o << UTF16ToUTF8(piece);
+}
+
 namespace internal {
 
 template<typename STR>
diff --git a/base/strings/string_piece.h b/base/strings/string_piece.h
index ee6a071..60a415c8 100644
--- a/base/strings/string_piece.h
+++ b/base/strings/string_piece.h
@@ -517,6 +517,9 @@
 BASE_EXPORT std::ostream& operator<<(std::ostream& o,
                                      const StringPiece& piece);
 
+BASE_EXPORT std::ostream& operator<<(std::ostream& o,
+                                     const StringPiece16& piece);
+
 // Hashing ---------------------------------------------------------------------
 
 // We provide appropriate hash functions so StringPiece and StringPiece16 can
diff --git a/base/win/embedded_i18n/language_selector.cc b/base/win/embedded_i18n/language_selector.cc
index b1047446..1a3a8a79 100644
--- a/base/win/embedded_i18n/language_selector.cc
+++ b/base/win/embedded_i18n/language_selector.cc
@@ -14,8 +14,8 @@
 
 #include "base/logging.h"
 #include "base/stl_util.h"
-#include "base/strings/string16.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/win/i18n.h"
 
 namespace base {
@@ -43,81 +43,49 @@
 };
 
 #if DCHECK_IS_ON()
-// Returns true if the items in the given range are sorted.  If
-// |byNameAndOffset| is true, the items must be sorted by both name and offset.
-bool IsArraySortedAndLowerCased(const LangToOffset* languages_to_offset_begin,
-                                const LangToOffset* languages_to_offset_end,
-                                bool byNameAndOffset) {
-  DCHECK(languages_to_offset_begin);
-  DCHECK(languages_to_offset_end);
-
-  int count_languages_to_offset =
-      languages_to_offset_end - languages_to_offset_begin;
-  if (count_languages_to_offset > 1) {
-    const LangToOffset* first = languages_to_offset_begin;
-    const LangToOffset* last = languages_to_offset_end;
-    for (--last; first != last; ++first) {
-      if (!(base::string16(first->language) < (first + 1)->language) ||
-          (byNameAndOffset && !(first->offset < (first + 1)->offset)) ||
-          base::string16(first->language) !=
-              base::ToLowerASCII(first->language)) {
-        return false;
-      }
-    }
-  } else if (count_languages_to_offset == 1) {
-    return base::ToLowerASCII(languages_to_offset_begin->language) ==
-           base::string16(languages_to_offset_begin->language);
-  }
-  return true;
-}
-
-// Validates that the language to offset mappings are properly sorted and
-// lower cased.
-void ValidateMappings(const LangToOffset* languages_to_offset_begin,
-                      const LangToOffset* languages_to_offset_end) {
-  // Ensure that languages_to_offset is sorted.
-  DCHECK(IsArraySortedAndLowerCased(languages_to_offset_begin,
-                                    languages_to_offset_end, true))
-      << "languages_to_offset is not sorted";
+// Returns true if the items in the given range are sorted and lower cased.
+bool IsArraySortedAndLowerCased(
+    base::span<const LangToOffset> languages_to_offset) {
+  return std::is_sorted(languages_to_offset.begin(),
+                        languages_to_offset.end()) &&
+         std::all_of(languages_to_offset.begin(), languages_to_offset.end(),
+                     [](const auto& lang) {
+                       return base::ToLowerASCII(lang.first) == lang.first;
+                     });
 }
 #endif  // DCHECK_IS_ON()
 
 // Determines the availability of all languages that may be used as aliases in
 // GetAliasedLanguageOffset or GetCompatibleNeutralLanguageOffset
 AvailableLanguageAliases DetermineAvailableAliases(
-    const LangToOffset* languages_to_offset_begin,
-    const LangToOffset* languages_to_offset_end) {
+    base::span<const LangToOffset> languages_to_offset) {
   AvailableLanguageAliases available_aliases = {};
 
-  for (const LangToOffset* lang_to_offset = languages_to_offset_begin;
-       lang_to_offset != languages_to_offset_end; ++lang_to_offset) {
-    base::string16 available_language = lang_to_offset->language;
-
-    if (available_language == L"en-gb")
-      available_aliases.en_gb_language_offset = lang_to_offset;
-    else if (available_language == L"en-us")
-      available_aliases.en_us_language_offset = lang_to_offset;
-    else if (available_language == L"es")
-      available_aliases.es_language_offset = lang_to_offset;
-    else if (available_language == L"es-419")
-      available_aliases.es_419_language_offset = lang_to_offset;
-    else if (available_language == L"fil")
-      available_aliases.fil_language_offset = lang_to_offset;
-    else if (available_language == L"iw")
-      available_aliases.iw_language_offset = lang_to_offset;
-    else if (available_language == L"no")
-      available_aliases.no_language_offset = lang_to_offset;
-    else if (available_language == L"pt-br")
-      available_aliases.pt_br_language_offset = lang_to_offset;
-    else if (available_language == L"zh-cn")
-      available_aliases.zh_cn_language_offset = lang_to_offset;
-    else if (available_language == L"zh-tw")
-      available_aliases.zh_tw_language_offset = lang_to_offset;
+  for (const LangToOffset& lang_to_offset : languages_to_offset) {
+    if (lang_to_offset.first == STRING16_LITERAL("en-gb"))
+      available_aliases.en_gb_language_offset = &lang_to_offset;
+    else if (lang_to_offset.first == STRING16_LITERAL("en-us"))
+      available_aliases.en_us_language_offset = &lang_to_offset;
+    else if (lang_to_offset.first == STRING16_LITERAL("es"))
+      available_aliases.es_language_offset = &lang_to_offset;
+    else if (lang_to_offset.first == STRING16_LITERAL("es-419"))
+      available_aliases.es_419_language_offset = &lang_to_offset;
+    else if (lang_to_offset.first == STRING16_LITERAL("fil"))
+      available_aliases.fil_language_offset = &lang_to_offset;
+    else if (lang_to_offset.first == STRING16_LITERAL("iw"))
+      available_aliases.iw_language_offset = &lang_to_offset;
+    else if (lang_to_offset.first == STRING16_LITERAL("no"))
+      available_aliases.no_language_offset = &lang_to_offset;
+    else if (lang_to_offset.first == STRING16_LITERAL("pt-br"))
+      available_aliases.pt_br_language_offset = &lang_to_offset;
+    else if (lang_to_offset.first == STRING16_LITERAL("zh-cn"))
+      available_aliases.zh_cn_language_offset = &lang_to_offset;
+    else if (lang_to_offset.first == STRING16_LITERAL("zh-tw"))
+      available_aliases.zh_tw_language_offset = &lang_to_offset;
   }
 
   // Fallback language must exist.
   DCHECK(available_aliases.en_us_language_offset);
-
   return available_aliases;
 }
 
@@ -125,24 +93,21 @@
 // that matches the |language| exactly. |offset| will store the offset of the
 // language that matches if any. |languages_to_offset| must be sorted by
 // language and all languages must lower case.
-bool GetExactLanguageOffset(const LangToOffset* languages_to_offset_begin,
-                            const LangToOffset* languages_to_offset_end,
+bool GetExactLanguageOffset(base::span<const LangToOffset> languages_to_offset,
                             const base::string16& language,
                             const LangToOffset** matched_language_to_offset) {
-  DCHECK(languages_to_offset_begin);
-  DCHECK(languages_to_offset_end);
   DCHECK(matched_language_to_offset);
 
   // Binary search in the sorted arrays to find the offset corresponding
   // to a given language |name|.
-  const LangToOffset* search_result = std::lower_bound(
-      languages_to_offset_begin, languages_to_offset_end, language,
+  auto search_result = std::lower_bound(
+      languages_to_offset.begin(), languages_to_offset.end(), language,
       [](const LangToOffset& left, const base::string16& to_find) {
-        return left.language < to_find;
+        return left.first < to_find;
       });
-  if (languages_to_offset_end != search_result &&
-      search_result->language == language) {
-    *matched_language_to_offset = search_result;
+  if (languages_to_offset.end() != search_result &&
+      search_result->first == language) {
+    *matched_language_to_offset = &*search_result;
     return true;
   }
   return false;
@@ -157,51 +122,58 @@
   // Alias some English variants to British English (all others wildcard to
   // US).
   if (available_aliases.en_gb_language_offset &&
-      (language == L"en-au" || language == L"en-ca" || language == L"en-nz" ||
-       language == L"en-za")) {
+      (language == STRING16_LITERAL("en-au") ||
+       language == STRING16_LITERAL("en-ca") ||
+       language == STRING16_LITERAL("en-nz") ||
+       language == STRING16_LITERAL("en-za"))) {
     *matched_language_to_offset = available_aliases.en_gb_language_offset;
     return true;
   }
   // Alias es-es to es (all others wildcard to es-419).
-  if (available_aliases.es_language_offset && language == L"es-es") {
+  if (available_aliases.es_language_offset &&
+      language == STRING16_LITERAL("es-es")) {
     *matched_language_to_offset = available_aliases.es_language_offset;
     return true;
   }
   // Google web properties use iw for he. Handle both just to be safe.
-  if (available_aliases.iw_language_offset && language == L"he") {
+  if (available_aliases.iw_language_offset &&
+      language == STRING16_LITERAL("he")) {
     *matched_language_to_offset = available_aliases.iw_language_offset;
     return true;
   }
   // Google web properties use no for nb. Handle both just to be safe.
-  if (available_aliases.no_language_offset && language == L"nb") {
+  if (available_aliases.no_language_offset &&
+      language == STRING16_LITERAL("nb")) {
     *matched_language_to_offset = available_aliases.no_language_offset;
     return true;
   }
   // Some Google web properties use tl for fil. Handle both just to be safe.
   // They're not completely identical, but alias it here.
-  if (available_aliases.fil_language_offset && language == L"tl") {
+  if (available_aliases.fil_language_offset &&
+      language == STRING16_LITERAL("tl")) {
     *matched_language_to_offset = available_aliases.fil_language_offset;
     return true;
   }
   if (available_aliases.zh_cn_language_offset &&
       // Pre-Vista alias for Chinese w/ script subtag.
-      (language == L"zh-chs" ||
+      (language == STRING16_LITERAL("zh-chs") ||
        // Vista+ alias for Chinese w/ script subtag.
-       language == L"zh-hans" ||
+       language == STRING16_LITERAL("zh-hans") ||
        // Although the wildcard entry for zh would result in this, alias zh-sg
        // so that it will win if it precedes another valid tag in a list of
        // candidates.
-       language == L"zh-sg")) {
+       language == STRING16_LITERAL("zh-sg"))) {
     *matched_language_to_offset = available_aliases.zh_cn_language_offset;
     return true;
   }
   if (available_aliases.zh_tw_language_offset &&
       // Pre-Vista alias for Chinese w/ script subtag.
-      (language == L"zh-cht" ||
+      (language == STRING16_LITERAL("zh-cht") ||
        // Vista+ alias for Chinese w/ script subtag.
-       language == L"zh-hant" ||
+       language == STRING16_LITERAL("zh-hant") ||
        // Alias Hong Kong and Macau to Taiwan.
-       language == L"zh-hk" || language == L"zh-mo")) {
+       language == STRING16_LITERAL("zh-hk") ||
+       language == STRING16_LITERAL("zh-mo"))) {
     *matched_language_to_offset = available_aliases.zh_tw_language_offset;
     return true;
   }
@@ -217,22 +189,26 @@
     const LangToOffset** matched_language_to_offset) {
   DCHECK(matched_language_to_offset);
 
-  if (available_aliases.en_us_language_offset && neutral_language == L"en") {
+  if (available_aliases.en_us_language_offset &&
+      neutral_language == STRING16_LITERAL("en")) {
     // Use the U.S. region for anything English.
     *matched_language_to_offset = available_aliases.en_us_language_offset;
     return true;
   }
-  if (available_aliases.es_419_language_offset && neutral_language == L"es") {
+  if (available_aliases.es_419_language_offset &&
+      neutral_language == STRING16_LITERAL("es")) {
     // Use the Latin American region for anything Spanish.
     *matched_language_to_offset = available_aliases.es_419_language_offset;
     return true;
   }
-  if (available_aliases.pt_br_language_offset && neutral_language == L"pt") {
+  if (available_aliases.pt_br_language_offset &&
+      neutral_language == STRING16_LITERAL("pt")) {
     // Use the Brazil region for anything Portugese.
     *matched_language_to_offset = available_aliases.pt_br_language_offset;
     return true;
   }
-  if (available_aliases.zh_cn_language_offset && neutral_language == L"zh") {
+  if (available_aliases.zh_cn_language_offset &&
+      neutral_language == STRING16_LITERAL("zh")) {
     // Use the P.R.C. region for anything Chinese.
     *matched_language_to_offset = available_aliases.zh_cn_language_offset;
     return true;
@@ -248,8 +224,7 @@
 // selected translation.
 // static
 bool SelectIf(const std::vector<base::string16>& candidates,
-              const LangToOffset* languages_to_offset_begin,
-              const LangToOffset* languages_to_offset_end,
+              base::span<const LangToOffset> languages_to_offset,
               const AvailableLanguageAliases& available_aliases,
               const LangToOffset** matched_language_to_offset,
               base::string16* matched_name) {
@@ -263,8 +238,7 @@
   // precedence over a later candidate entry matching on an exact match.
   for (const base::string16& scan : candidates) {
     base::string16 lower_case_candidate = base::ToLowerASCII(scan);
-    if (GetExactLanguageOffset(languages_to_offset_begin,
-                               languages_to_offset_end, lower_case_candidate,
+    if (GetExactLanguageOffset(languages_to_offset, lower_case_candidate,
                                matched_language_to_offset) ||
         GetAliasedLanguageOffset(available_aliases, lower_case_candidate,
                                  matched_language_to_offset)) {
@@ -295,28 +269,29 @@
 
 void SelectLanguageMatchingCandidate(
     const std::vector<base::string16>& candidates,
-    const LangToOffset* languages_to_offset_begin,
-    const LangToOffset* languages_to_offset_end,
+    base::span<const LangToOffset> languages_to_offset,
     int* selected_offset,
     base::string16* matched_candidate,
     base::string16* selected_language) {
   DCHECK(selected_offset);
   DCHECK(matched_candidate);
   DCHECK(selected_language);
-  DCHECK_GT(languages_to_offset_end, languages_to_offset_begin);
-  DCHECK_EQ(*selected_offset,
-            languages_to_offset_end - languages_to_offset_begin);
+  DCHECK(!languages_to_offset.empty());
+  DCHECK_EQ(size_t{*selected_offset}, languages_to_offset.size());
   DCHECK(matched_candidate->empty());
   DCHECK(selected_language->empty());
-
+  // Note: While DCHECK_IS_ON() seems redundant here, this is required to avoid
+  // compilation errors, since IsArraySortedAndLowerCased is not defined
+  // otherwise.
 #if DCHECK_IS_ON()
-  ValidateMappings(languages_to_offset_begin, languages_to_offset_end);
+  DCHECK(IsArraySortedAndLowerCased(languages_to_offset))
+      << "languages_to_offset is not sorted and lower cased";
 #endif  // DCHECK_IS_ON()
 
   // Get which languages that are commonly used as aliases and wildcards are
   // available for use to match candidates.
-  AvailableLanguageAliases available_aliases = DetermineAvailableAliases(
-      languages_to_offset_begin, languages_to_offset_end);
+  AvailableLanguageAliases available_aliases =
+      DetermineAvailableAliases(languages_to_offset);
 
   // The fallback must exist.
   DCHECK(available_aliases.en_us_language_offset);
@@ -324,27 +299,27 @@
   // Try to find the first matching candidate from all the language mappings
   // that are given. Failing that, used en-us as the fallback language.
   const LangToOffset* matched_language_to_offset = nullptr;
-  if (!SelectIf(candidates, languages_to_offset_begin, languages_to_offset_end,
-                available_aliases, &matched_language_to_offset,
-                matched_candidate)) {
+  if (!SelectIf(candidates, languages_to_offset, available_aliases,
+                &matched_language_to_offset, matched_candidate)) {
     matched_language_to_offset = available_aliases.en_us_language_offset;
-    *matched_candidate = available_aliases.en_us_language_offset->language;
+    *matched_candidate =
+        base::string16(available_aliases.en_us_language_offset->first);
   }
 
   DCHECK(matched_language_to_offset);
   // Get the real language being used for the matched candidate.
-  *selected_language = matched_language_to_offset->language;
-  *selected_offset = matched_language_to_offset->offset;
+  *selected_language = base::string16(matched_language_to_offset->first);
+  *selected_offset = matched_language_to_offset->second;
 }
 
 std::vector<base::string16> GetCandidatesFromSystem(
-    const base::string16& preferred_language) {
+    base::StringPiece16 preferred_language) {
   std::vector<base::string16> candidates;
 
   // Get the intitial candidate list for this particular implementation (if
   // applicable).
   if (!preferred_language.empty())
-    candidates.push_back(preferred_language);
+    candidates.emplace_back(preferred_language);
 
   // Now try the UI languages.  Use the thread preferred ones since that will
   // kindly return us a list of all kinds of fallbacks.
@@ -355,21 +330,18 @@
 }  // namespace
 
 LanguageSelector::LanguageSelector(
-    const base::string16& preferred_language,
-    const LangToOffset* languages_to_offset_begin,
-    const LangToOffset* languages_to_offset_end)
+    base::StringPiece16 preferred_language,
+    base::span<const LangToOffset> languages_to_offset)
     : LanguageSelector(GetCandidatesFromSystem(preferred_language),
-                       languages_to_offset_begin,
-                       languages_to_offset_end) {}
+                       languages_to_offset) {}
 
 LanguageSelector::LanguageSelector(
     const std::vector<base::string16>& candidates,
-    const LangToOffset* languages_to_offset_begin,
-    const LangToOffset* languages_to_offset_end)
-    : selected_offset_(languages_to_offset_end - languages_to_offset_begin) {
-  SelectLanguageMatchingCandidate(candidates, languages_to_offset_begin,
-                                  languages_to_offset_end, &selected_offset_,
-                                  &matched_candidate_, &selected_language_);
+    base::span<const LangToOffset> languages_to_offset)
+    : selected_offset_(languages_to_offset.size()) {
+  SelectLanguageMatchingCandidate(candidates, languages_to_offset,
+                                  &selected_offset_, &matched_candidate_,
+                                  &selected_language_);
 }
 
 LanguageSelector::~LanguageSelector() = default;
diff --git a/base/win/embedded_i18n/language_selector.h b/base/win/embedded_i18n/language_selector.h
index cc78f10..2772aee 100644
--- a/base/win/embedded_i18n/language_selector.h
+++ b/base/win/embedded_i18n/language_selector.h
@@ -8,11 +8,14 @@
 #ifndef BASE_WIN_EMBEDDED_I18N_LANGUAGE_SELECTOR_H_
 #define BASE_WIN_EMBEDDED_I18N_LANGUAGE_SELECTOR_H_
 
+#include <utility>
 #include <vector>
 
 #include "base/base_export.h"
+#include "base/containers/span.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
 
 namespace base {
 namespace win {
@@ -23,10 +26,7 @@
 // override selection should a corresponding translation be available.
 class BASE_EXPORT LanguageSelector {
  public:
-  struct LangToOffset {
-    const wchar_t* language;
-    int offset;
-  };
+  using LangToOffset = std::pair<base::StringPiece16, int>;
 
   // Constructor to be used for users of this class that will provide the actual
   // language offsets that will be used.
@@ -36,9 +36,8 @@
   // |languages_to_offset_begin| and |languages_to_offset_end| point to a sorted
   // array of language identifiers (and their offsets) for which translations
   // are available.
-  LanguageSelector(const base::string16& preferred_language,
-                   const LangToOffset* languages_to_offset_begin,
-                   const LangToOffset* languages_to_offset_end);
+  LanguageSelector(base::StringPiece16 preferred_language,
+                   base::span<const LangToOffset> languages_to_offset);
 
   // Constructor for testing purposes.
   // |candidates| is a list of all candiate languages that can be used to
@@ -47,8 +46,7 @@
   // array of language identifiers (and their offsets) for which translations
   // are available.
   LanguageSelector(const std::vector<base::string16>& candidates,
-                   const LangToOffset* languages_to_offset_begin,
-                   const LangToOffset* languages_to_offset_end);
+                   base::span<const LangToOffset> languages_to_offset);
 
   ~LanguageSelector();
 
diff --git a/base/win/embedded_i18n/language_selector_unittest.cc b/base/win/embedded_i18n/language_selector_unittest.cc
index 2341f73..64efc1d 100644
--- a/base/win/embedded_i18n/language_selector_unittest.cc
+++ b/base/win/embedded_i18n/language_selector_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <string>
+#include <tuple>
 #include <vector>
 
 #include "base/macros.h"
@@ -16,26 +17,49 @@
 namespace i18n {
 namespace {
 
-constexpr const wchar_t* kExactMatchCandidates[] = {
-    L"am",  L"ar",    L"bg",    L"bn",    L"ca",     L"cs", L"da", L"de",
-    L"el",  L"en-gb", L"en-us", L"es",    L"es-419", L"et", L"fa", L"fi",
-    L"fil", L"fr",    L"gu",    L"hi",    L"hr",     L"hu", L"id", L"it",
-    L"iw",  L"ja",    L"kn",    L"ko",    L"lt",     L"lv", L"ml", L"mr",
-    L"nl",  L"no",    L"pl",    L"pt-br", L"pt-pt",  L"ro", L"ru", L"sk",
-    L"sl",  L"sr",    L"sv",    L"sw",    L"ta",     L"te", L"th", L"tr",
-    L"uk",  L"vi",    L"zh-cn", L"zh-tw"};
+constexpr const base::char16* kExactMatchCandidates[] = {
+    STRING16_LITERAL("am"),     STRING16_LITERAL("ar"),
+    STRING16_LITERAL("bg"),     STRING16_LITERAL("bn"),
+    STRING16_LITERAL("ca"),     STRING16_LITERAL("cs"),
+    STRING16_LITERAL("da"),     STRING16_LITERAL("de"),
+    STRING16_LITERAL("el"),     STRING16_LITERAL("en-gb"),
+    STRING16_LITERAL("en-us"),  STRING16_LITERAL("es"),
+    STRING16_LITERAL("es-419"), STRING16_LITERAL("et"),
+    STRING16_LITERAL("fa"),     STRING16_LITERAL("fi"),
+    STRING16_LITERAL("fil"),    STRING16_LITERAL("fr"),
+    STRING16_LITERAL("gu"),     STRING16_LITERAL("hi"),
+    STRING16_LITERAL("hr"),     STRING16_LITERAL("hu"),
+    STRING16_LITERAL("id"),     STRING16_LITERAL("it"),
+    STRING16_LITERAL("iw"),     STRING16_LITERAL("ja"),
+    STRING16_LITERAL("kn"),     STRING16_LITERAL("ko"),
+    STRING16_LITERAL("lt"),     STRING16_LITERAL("lv"),
+    STRING16_LITERAL("ml"),     STRING16_LITERAL("mr"),
+    STRING16_LITERAL("nl"),     STRING16_LITERAL("no"),
+    STRING16_LITERAL("pl"),     STRING16_LITERAL("pt-br"),
+    STRING16_LITERAL("pt-pt"),  STRING16_LITERAL("ro"),
+    STRING16_LITERAL("ru"),     STRING16_LITERAL("sk"),
+    STRING16_LITERAL("sl"),     STRING16_LITERAL("sr"),
+    STRING16_LITERAL("sv"),     STRING16_LITERAL("sw"),
+    STRING16_LITERAL("ta"),     STRING16_LITERAL("te"),
+    STRING16_LITERAL("th"),     STRING16_LITERAL("tr"),
+    STRING16_LITERAL("uk"),     STRING16_LITERAL("vi"),
+    STRING16_LITERAL("zh-cn"),  STRING16_LITERAL("zh-tw")};
 
-constexpr const wchar_t* kAliasMatchCandidates[] = {
-    L"he",      L"nb",      L"tl",    L"zh-chs", L"zh-cht",
-    L"zh-hans", L"zh-hant", L"zh-hk", L"zh-mo"};
+constexpr const base::char16* kAliasMatchCandidates[] = {
+    STRING16_LITERAL("he"),      STRING16_LITERAL("nb"),
+    STRING16_LITERAL("tl"),      STRING16_LITERAL("zh-chs"),
+    STRING16_LITERAL("zh-cht"),  STRING16_LITERAL("zh-hans"),
+    STRING16_LITERAL("zh-hant"), STRING16_LITERAL("zh-hk"),
+    STRING16_LITERAL("zh-mo")};
 
-constexpr const wchar_t* kWildcardMatchCandidates[] = {L"en-AU", L"es-CO",
-                                                       L"pt-AB", L"zh-SG"};
+constexpr const base::char16* kWildcardMatchCandidates[] = {
+    STRING16_LITERAL("en-AU"), STRING16_LITERAL("es-CO"),
+    STRING16_LITERAL("pt-AB"), STRING16_LITERAL("zh-SG")};
 
 std::vector<LanguageSelector::LangToOffset> MakeLanguageOffsetPairs() {
   std::vector<LanguageSelector::LangToOffset> language_offset_pairs;
   int i = 0;
-  for (const wchar_t* lang : kExactMatchCandidates) {
+  for (const base::char16* lang : kExactMatchCandidates) {
     language_offset_pairs.push_back({lang, i++});
   }
 
@@ -50,11 +74,8 @@
       : TestLanguageSelector(candidates, MakeLanguageOffsetPairs()) {}
   TestLanguageSelector(
       const std::vector<base::string16>& candidates,
-      const std::vector<LanguageSelector::LangToOffset>& languages_to_offset)
-      : LanguageSelector(
-            candidates,
-            languages_to_offset.data(),
-            languages_to_offset.data() + languages_to_offset.size()) {}
+      base::span<const LanguageSelector::LangToOffset> languages_to_offset)
+      : LanguageSelector(candidates, languages_to_offset) {}
 };
 
 }  // namespace
@@ -68,112 +89,109 @@
 // Test some hypothetical candidate sets.
 TEST(LanguageSelectorTest, AssortedSelections) {
   {
-    base::string16 candidates[] = {L"fr-BE", L"fr", L"en"};
-    TestLanguageSelector instance(std::vector<base::string16>(
-        &candidates[0], &candidates[base::size(candidates)]));
+    std::vector<base::string16> candidates = {STRING16_LITERAL("fr-BE"),
+                                              STRING16_LITERAL("fr"),
+                                              STRING16_LITERAL("en")};
+    TestLanguageSelector instance(candidates);
     // Expect the exact match to win.
-    EXPECT_EQ(L"fr", instance.matched_candidate());
+    EXPECT_EQ(STRING16_LITERAL("fr"), instance.matched_candidate());
   }
   {
-    base::string16 candidates[] = {L"xx-YY", L"cc-Ssss-RR"};
-    TestLanguageSelector instance(std::vector<base::string16>(
-        &candidates[0], &candidates[base::size(candidates)]));
+    std::vector<base::string16> candidates = {STRING16_LITERAL("xx-YY"),
+                                              STRING16_LITERAL("cc-Ssss-RR")};
+    TestLanguageSelector instance(candidates);
     // Expect the fallback to win.
-    EXPECT_EQ(L"en-us", instance.matched_candidate());
+    EXPECT_EQ(STRING16_LITERAL("en-us"), instance.matched_candidate());
   }
   {
-    base::string16 candidates[] = {L"zh-SG", L"en-GB"};
-    TestLanguageSelector instance(std::vector<base::string16>(
-        &candidates[0], &candidates[base::size(candidates)]));
+    std::vector<base::string16> candidates = {STRING16_LITERAL("zh-SG"),
+                                              STRING16_LITERAL("en-GB")};
+    TestLanguageSelector instance(candidates);
     // Expect the alias match to win.
-    EXPECT_EQ(L"zh-SG", instance.matched_candidate());
+    EXPECT_EQ(STRING16_LITERAL("zh-SG"), instance.matched_candidate());
   }
 }
 
 // A fixture for testing sets of single-candidate selections.
 class LanguageSelectorMatchCandidateTest
-    : public ::testing::TestWithParam<const wchar_t*> {};
+    : public ::testing::TestWithParam<const base::char16*> {};
 
 TEST_P(LanguageSelectorMatchCandidateTest, TestMatchCandidate) {
-  TestLanguageSelector instance(
-      std::vector<base::string16>(1, base::string16(GetParam())));
+  TestLanguageSelector instance({GetParam()});
   EXPECT_EQ(GetParam(), instance.matched_candidate());
 }
 
 // Test that all existing translations can be found by exact match.
-INSTANTIATE_TEST_SUITE_P(
-    TestExactMatches,
-    LanguageSelectorMatchCandidateTest,
-    ::testing::ValuesIn(
-        &kExactMatchCandidates[0],
-        &kExactMatchCandidates[base::size(kExactMatchCandidates)]));
+INSTANTIATE_TEST_SUITE_P(TestExactMatches,
+                         LanguageSelectorMatchCandidateTest,
+                         ::testing::ValuesIn(kExactMatchCandidates));
 
 // Test the alias matches.
-INSTANTIATE_TEST_SUITE_P(
-    TestAliasMatches,
-    LanguageSelectorMatchCandidateTest,
-    ::testing::ValuesIn(
-        &kAliasMatchCandidates[0],
-        &kAliasMatchCandidates[base::size(kAliasMatchCandidates)]));
+INSTANTIATE_TEST_SUITE_P(TestAliasMatches,
+                         LanguageSelectorMatchCandidateTest,
+                         ::testing::ValuesIn(kAliasMatchCandidates));
 
 // Test a few wildcard matches.
-INSTANTIATE_TEST_SUITE_P(
-    TestWildcardMatches,
-    LanguageSelectorMatchCandidateTest,
-    ::testing::ValuesIn(
-        &kWildcardMatchCandidates[0],
-        &kWildcardMatchCandidates[base::size(kWildcardMatchCandidates)]));
+INSTANTIATE_TEST_SUITE_P(TestWildcardMatches,
+                         LanguageSelectorMatchCandidateTest,
+                         ::testing::ValuesIn(kWildcardMatchCandidates));
 
 // A fixture for testing aliases that match to an expected translation.  The
 // first member of the tuple is the expected translation, the second is a
 // candidate that should be aliased to the expectation.
 class LanguageSelectorAliasTest
     : public ::testing::TestWithParam<
-          std::tuple<const wchar_t*, const wchar_t*>> {};
+          std::tuple<const base::char16*, const base::char16*>> {};
 
 // Test that the candidate language maps to the aliased translation.
 TEST_P(LanguageSelectorAliasTest, AliasesMatch) {
-  TestLanguageSelector instance(
-      std::vector<base::string16>(1, std::get<1>(GetParam())));
+  TestLanguageSelector instance({std::get<1>(GetParam())});
   EXPECT_EQ(std::get<0>(GetParam()), instance.selected_translation());
 }
 
-INSTANTIATE_TEST_SUITE_P(EnGbAliases,
-                         LanguageSelectorAliasTest,
-                         ::testing::Combine(::testing::Values(L"en-gb"),
-                                            ::testing::Values(L"en-au",
-                                                              L"en-ca",
-                                                              L"en-nz",
-                                                              L"en-za")));
+INSTANTIATE_TEST_SUITE_P(
+    EnGbAliases,
+    LanguageSelectorAliasTest,
+    ::testing::Combine(::testing::Values(STRING16_LITERAL("en-gb")),
+                       ::testing::Values(STRING16_LITERAL("en-au"),
+                                         STRING16_LITERAL("en-ca"),
+                                         STRING16_LITERAL("en-nz"),
+                                         STRING16_LITERAL("en-za"))));
 
-INSTANTIATE_TEST_SUITE_P(IwAliases,
-                         LanguageSelectorAliasTest,
-                         ::testing::Combine(::testing::Values(L"iw"),
-                                            ::testing::Values(L"he")));
+INSTANTIATE_TEST_SUITE_P(
+    IwAliases,
+    LanguageSelectorAliasTest,
+    ::testing::Combine(::testing::Values(STRING16_LITERAL("iw")),
+                       ::testing::Values(STRING16_LITERAL("he"))));
 
-INSTANTIATE_TEST_SUITE_P(NoAliases,
-                         LanguageSelectorAliasTest,
-                         ::testing::Combine(::testing::Values(L"no"),
-                                            ::testing::Values(L"nb")));
+INSTANTIATE_TEST_SUITE_P(
+    NoAliases,
+    LanguageSelectorAliasTest,
+    ::testing::Combine(::testing::Values(STRING16_LITERAL("no")),
+                       ::testing::Values(STRING16_LITERAL("nb"))));
 
-INSTANTIATE_TEST_SUITE_P(FilAliases,
-                         LanguageSelectorAliasTest,
-                         ::testing::Combine(::testing::Values(L"fil"),
-                                            ::testing::Values(L"tl")));
+INSTANTIATE_TEST_SUITE_P(
+    FilAliases,
+    LanguageSelectorAliasTest,
+    ::testing::Combine(::testing::Values(STRING16_LITERAL("fil")),
+                       ::testing::Values(STRING16_LITERAL("tl"))));
 
 INSTANTIATE_TEST_SUITE_P(
     ZhCnAliases,
     LanguageSelectorAliasTest,
-    ::testing::Combine(::testing::Values(L"zh-cn"),
-                       ::testing::Values(L"zh-chs", L"zh-hans", L"zh-sg")));
+    ::testing::Combine(::testing::Values(STRING16_LITERAL("zh-cn")),
+                       ::testing::Values(STRING16_LITERAL("zh-chs"),
+                                         STRING16_LITERAL("zh-hans"),
+                                         STRING16_LITERAL("zh-sg"))));
 
-INSTANTIATE_TEST_SUITE_P(ZhTwAliases,
-                         LanguageSelectorAliasTest,
-                         ::testing::Combine(::testing::Values(L"zh-tw"),
-                                            ::testing::Values(L"zh-cht",
-                                                              L"zh-hant",
-                                                              L"zh-hk",
-                                                              L"zh-mo")));
+INSTANTIATE_TEST_SUITE_P(
+    ZhTwAliases,
+    LanguageSelectorAliasTest,
+    ::testing::Combine(::testing::Values(STRING16_LITERAL("zh-tw")),
+                       ::testing::Values(STRING16_LITERAL("zh-cht"),
+                                         STRING16_LITERAL("zh-hant"),
+                                         STRING16_LITERAL("zh-hk"),
+                                         STRING16_LITERAL("zh-mo"))));
 
 // Test that we can get a match of the default language.
 TEST(LanguageSelectorTest, DefaultLanguageName) {
@@ -184,53 +202,34 @@
 // All languages given to the selector must be lower cased (since generally
 // the language names are generated by a python script).
 TEST(LanguageSelectorTest, InvalidLanguageCasing) {
-  constexpr LanguageSelector::LangToOffset kLangToOffset[] = {{L"en-US", 0}};
-  EXPECT_DCHECK_DEATH(
-      LanguageSelector instance({base::string16(L"en-us")}, &kLangToOffset[0],
-                                &kLangToOffset[base::size(kLangToOffset)]));
+  constexpr LanguageSelector::LangToOffset kLangToOffset[] = {
+      {STRING16_LITERAL("en-US"), 0}};
+  EXPECT_DCHECK_DEATH(LanguageSelector instance(
+      std::vector<base::string16>({STRING16_LITERAL("en-us")}), kLangToOffset));
 }
 
-// Language name and offset must both be ordered when generated by the
+// Language name and offset pairs must be ordered when generated by the
 // python script.
 TEST(LanguageSelectorTest, InvalidLanguageNameOrder) {
-  constexpr LanguageSelector::LangToOffset kLangToOffset[] = {{L"en-us", 0},
-                                                              {L"en-gb", 1}};
-  EXPECT_DCHECK_DEATH(
-      LanguageSelector instance({base::string16(L"en-us")}, &kLangToOffset[0],
-                                &kLangToOffset[base::size(kLangToOffset)]));
-}
-
-// Language name and offset must both be ordered when generated by the
-// python script.
-TEST(LanguageSelectorTest, InvalidLanguageOffsetOrder) {
-  constexpr LanguageSelector::LangToOffset kLangToOffset[] = {{L"en-gb", 1},
-                                                              {L"en-us", 0}};
-  EXPECT_DCHECK_DEATH(
-      LanguageSelector instance({base::string16(L"en-us")}, &kLangToOffset[0],
-                                &kLangToOffset[base::size(kLangToOffset)]));
+  constexpr LanguageSelector::LangToOffset kLangToOffset[] = {
+      {STRING16_LITERAL("en-us"), 0}, {STRING16_LITERAL("en-gb"), 1}};
+  EXPECT_DCHECK_DEATH(LanguageSelector instance(
+      std::vector<base::string16>({STRING16_LITERAL("en-us")}), kLangToOffset));
 }
 
 // There needs to be a fallback language available in the generated
 // languages if ever the selector is given a language that does not exist.
 TEST(LanguageSelectorTest, NoFallbackLanguageAvailable) {
-  constexpr LanguageSelector::LangToOffset kLangToOffset[] = {{L"en-gb", 0}};
-  EXPECT_DCHECK_DEATH(
-      LanguageSelector instance({base::string16(L"aa-bb")}, &kLangToOffset[0],
-                                &kLangToOffset[base::size(kLangToOffset)]));
+  constexpr LanguageSelector::LangToOffset kLangToOffset[] = {
+      {STRING16_LITERAL("en-gb"), 0}};
+  EXPECT_DCHECK_DEATH(LanguageSelector instance(
+      std::vector<base::string16>({STRING16_LITERAL("aa-bb")}), kLangToOffset));
 }
 
 // No languages available.
 TEST(LanguageSelectorTest, NoLanguagesAvailable) {
-  EXPECT_DCHECK_DEATH(
-      LanguageSelector instance({base::string16(L"en-us")}, nullptr, nullptr));
-}
-
-// End given is < begin Given.
-TEST(LanguageSelectorTest, InvalidBeginAndEnd) {
-  constexpr LanguageSelector::LangToOffset kLangToOffset[] = {{L"en-gb", 0}};
   EXPECT_DCHECK_DEATH(LanguageSelector instance(
-      {base::string16(L"en-us")}, &kLangToOffset[base::size(kLangToOffset)],
-      &kLangToOffset[0]));
+      std::vector<base::string16>({STRING16_LITERAL("en-us")}), {}));
 }
 
 }  // namespace i18n
diff --git a/base/win/i18n.cc b/base/win/i18n.cc
index 9c25e62..440cb01 100644
--- a/base/win/i18n.cc
+++ b/base/win/i18n.cc
@@ -8,6 +8,8 @@
 
 #include "base/logging.h"
 #include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 
 namespace {
 
@@ -86,25 +88,26 @@
   return false;
 }
 
-bool GetUserDefaultUILanguage(std::wstring* language, std::wstring* region) {
+bool GetUserDefaultUILanguage(base::string16* language,
+                              base::string16* region) {
   DCHECK(language);
 
   LANGID lang_id = ::GetUserDefaultUILanguage();
   if (LOCALE_CUSTOM_UI_DEFAULT != lang_id) {
     const LCID locale_id = MAKELCID(lang_id, SORT_DEFAULT);
     // max size for LOCALE_SISO639LANGNAME and LOCALE_SISO3166CTRYNAME is 9
-    wchar_t result_buffer[9];
+    base::char16 result_buffer[9];
     int result_length =
-        GetLocaleInfo(locale_id, LOCALE_SISO639LANGNAME, &result_buffer[0],
-                      base::size(result_buffer));
+        GetLocaleInfo(locale_id, LOCALE_SISO639LANGNAME,
+                      base::wdata(result_buffer), base::size(result_buffer));
     DPCHECK(0 != result_length) << "Failed getting language id";
     if (1 < result_length) {
       language->assign(&result_buffer[0], result_length - 1);
       region->clear();
       if (SUBLANG_NEUTRAL != SUBLANGID(lang_id)) {
-        result_length =
-            GetLocaleInfo(locale_id, LOCALE_SISO3166CTRYNAME, &result_buffer[0],
-                          base::size(result_buffer));
+        result_length = GetLocaleInfo(locale_id, LOCALE_SISO3166CTRYNAME,
+                                      base::wdata(result_buffer),
+                                      base::size(result_buffer));
         DPCHECK(0 != result_length) << "Failed getting region id";
         if (1 < result_length)
           region->assign(&result_buffer[0], result_length - 1);
@@ -119,27 +122,26 @@
   return false;
 }
 
-bool GetPreferredUILanguageList(LanguageFunction function, ULONG flags,
-                                std::vector<std::wstring>* languages) {
+bool GetPreferredUILanguageList(LanguageFunction function,
+                                ULONG flags,
+                                std::vector<base::string16>* languages) {
   std::vector<wchar_t> buffer;
-  std::wstring language;
-  std::wstring region;
+  base::string16 language;
+  base::string16 region;
 
   if (GetMUIPreferredUILanguageList(function, flags, &buffer)) {
     std::vector<wchar_t>::const_iterator scan = buffer.begin();
-    language.assign(&*scan);
+    language = base::WideToUTF16(&*scan);
     while (!language.empty()) {
       languages->push_back(language);
       scan += language.size() + 1;
-      language.assign(&*scan);
+      language = base::WideToUTF16(&*scan);
     }
   } else if (GetUserDefaultUILanguage(&language, &region)) {
     // Mimic the MUI behavior of putting the neutral version of the lang after
     // the regional one (e.g., "fr-CA, fr").
     if (!region.empty())
-      languages->push_back(std::wstring(language)
-                               .append(1, L'-')
-                               .append(region));
+      languages->push_back(language + base::string16(1, '-') + region);
     languages->push_back(language);
   } else {
     return false;
@@ -154,12 +156,12 @@
 namespace win {
 namespace i18n {
 
-bool GetUserPreferredUILanguageList(std::vector<std::wstring>* languages) {
+bool GetUserPreferredUILanguageList(std::vector<base::string16>* languages) {
   DCHECK(languages);
   return GetPreferredUILanguageList(USER_LANGUAGES, 0, languages);
 }
 
-bool GetThreadPreferredUILanguageList(std::vector<std::wstring>* languages) {
+bool GetThreadPreferredUILanguageList(std::vector<base::string16>* languages) {
   DCHECK(languages);
   return GetPreferredUILanguageList(
       THREAD_LANGUAGES, MUI_MERGE_SYSTEM_FALLBACK | MUI_MERGE_USER_FALLBACK,
diff --git a/base/win/i18n.h b/base/win/i18n.h
index 9e74d3f..d586646 100644
--- a/base/win/i18n.h
+++ b/base/win/i18n.h
@@ -5,10 +5,10 @@
 #ifndef BASE_WIN_I18N_H_
 #define BASE_WIN_I18N_H_
 
-#include <string>
 #include <vector>
 
 #include "base/base_export.h"
+#include "base/strings/string16.h"
 
 namespace base {
 namespace win {
@@ -18,13 +18,13 @@
 // available, falling-back on the user default UI language otherwise.  Returns
 // true if at least one language is added.
 BASE_EXPORT bool GetUserPreferredUILanguageList(
-    std::vector<std::wstring>* languages);
+    std::vector<base::string16>* languages);
 
 // Adds to |languages| the list of thread, process, user, and system preferred
 // UI languages from MUI, if available, falling-back on the user default UI
 // language otherwise.  Returns true if at least one language is added.
 BASE_EXPORT bool GetThreadPreferredUILanguageList(
-    std::vector<std::wstring>* languages);
+    std::vector<base::string16>* languages);
 
 }  // namespace i18n
 }  // namespace win
diff --git a/base/win/i18n_unittest.cc b/base/win/i18n_unittest.cc
index 9af6dbf..0188838 100644
--- a/base/win/i18n_unittest.cc
+++ b/base/win/i18n_unittest.cc
@@ -17,25 +17,21 @@
 
 // Tests that at least one user preferred UI language can be obtained.
 TEST(I18NTest, GetUserPreferredUILanguageList) {
-  std::vector<std::wstring> languages;
+  std::vector<base::string16> languages;
   EXPECT_TRUE(GetUserPreferredUILanguageList(&languages));
-  EXPECT_NE(static_cast<std::vector<std::wstring>::size_type>(0),
-            languages.size());
-  for (std::vector<std::wstring>::const_iterator scan = languages.begin(),
-          end = languages.end(); scan != end; ++scan) {
-    EXPECT_FALSE((*scan).empty());
+  EXPECT_FALSE(languages.empty());
+  for (const auto& language : languages) {
+    EXPECT_FALSE(language.empty());
   }
 }
 
 // Tests that at least one thread preferred UI language can be obtained.
 TEST(I18NTest, GetThreadPreferredUILanguageList) {
-  std::vector<std::wstring> languages;
+  std::vector<base::string16> languages;
   EXPECT_TRUE(GetThreadPreferredUILanguageList(&languages));
-  EXPECT_NE(static_cast<std::vector<std::wstring>::size_type>(0),
-            languages.size());
-  for (std::vector<std::wstring>::const_iterator scan = languages.begin(),
-          end = languages.end(); scan != end; ++scan) {
-    EXPECT_FALSE((*scan).empty());
+  EXPECT_FALSE(languages.empty());
+  for (const auto& language : languages) {
+    EXPECT_FALSE(language.empty());
   }
 }
 
diff --git a/base/win/windows_version.cc b/base/win/windows_version.cc
index 05a2f73..07c040a 100644
--- a/base/win/windows_version.cc
+++ b/base/win/windows_version.cc
@@ -194,17 +194,15 @@
 // kernel32 will still be the "real" version.
 base::Version OSInfo::Kernel32BaseVersion() const {
   static const base::NoDestructor<base::Version> version([] {
-    std::unique_ptr<FileVersionInfoWin> file_version_info(
-        static_cast<FileVersionInfoWin*>(
-            FileVersionInfoWin::CreateFileVersionInfo(
-                base::FilePath(FILE_PATH_LITERAL("kernel32.dll")))));
+    std::unique_ptr<FileVersionInfoWin> file_version_info =
+        FileVersionInfoWin::CreateFileVersionInfoWin(
+            base::FilePath(FILE_PATH_LITERAL("kernel32.dll")));
     if (!file_version_info) {
       // crbug.com/912061: on some systems it seems kernel32.dll might be
       // corrupted or not in a state to get version info. In this case try
       // kernelbase.dll as a fallback.
-      file_version_info.reset(static_cast<FileVersionInfoWin*>(
-          FileVersionInfoWin::CreateFileVersionInfo(
-              base::FilePath(FILE_PATH_LITERAL("kernelbase.dll")))));
+      file_version_info = FileVersionInfoWin::CreateFileVersionInfoWin(
+          base::FilePath(FILE_PATH_LITERAL("kernelbase.dll")));
     }
     CHECK(file_version_info);
     const int major =
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index 5b60633c..817efb4 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -171,11 +171,9 @@
 declare_args() {
   # Set to true to use lld, the LLVM linker.
   # https://crbug.com/911658 for using lld on 32-bit linux.
-  # https://crbug.com/917504 for arm chromeos
   use_lld = is_clang &&
             (is_win || is_fuchsia || is_android ||
-             (is_linux && target_os != "chromeos" && current_cpu != "x86") ||
-             (target_os == "chromeos" && current_cpu != "arm"))
+             (is_linux && target_os != "chromeos" && current_cpu != "x86"))
 }
 
 declare_args() {
diff --git a/chrome/VERSION b/chrome/VERSION
index f04ae99..26ba5e124 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=74
 MINOR=0
-BUILD=3687
+BUILD=3688
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 5f477dea..1ab4aa1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -372,10 +372,12 @@
     /**
      * Return whether the passed in class name matches any of the supported tabbed mode activities.
      */
-    public static boolean isTabbedModeClassName(String className) {
-        return TextUtils.equals(className, ChromeTabbedActivity.class.getName())
-                || TextUtils.equals(className, MultiInstanceChromeTabbedActivity.class.getName())
-                || TextUtils.equals(className, ChromeTabbedActivity2.class.getName());
+    public static boolean isTabbedModeComponentName(String componentName) {
+        return TextUtils.equals(componentName, ChromeTabbedActivity.class.getName())
+                || TextUtils.equals(
+                        componentName, MultiInstanceChromeTabbedActivity.class.getName())
+                || TextUtils.equals(componentName, ChromeTabbedActivity2.class.getName())
+                || TextUtils.equals(componentName, BuildInfo.getInstance().packageName + ".Main");
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
index e9666238..85c2a66 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -69,6 +69,14 @@
         mOverlayCoordinator =
                 new AssistantOverlayCoordinator(activity, mAssistantView, mModel.getOverlayModel());
 
+        // Notify AssistantKeyboardCoordinator when we should (dis)allow the soft keyboard.
+        mModel.addObserver((source, propertyKey) -> {
+            if (AssistantModel.ALLOW_SOFT_KEYBOARD == propertyKey) {
+                mKeyboardCoordinator.allowShowingSoftKeyboard(
+                        mModel.get(AssistantModel.ALLOW_SOFT_KEYBOARD));
+            }
+        });
+
         showAssistantView();
     }
 
@@ -164,14 +172,6 @@
         return mBottomBarCoordinator;
     }
 
-    public AssistantKeyboardCoordinator getKeyboardCoordinator() {
-        return mKeyboardCoordinator;
-    }
-
-    public AssistantOverlayCoordinator getOverlayCoordinator() {
-        return mOverlayCoordinator;
-    }
-
     /**
      * Dismiss the assistant view and show a cancellable snackbar alerting the user that the
      * Autofill assistant is shutting down.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
index df5b97b..412a9794 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java
@@ -10,17 +10,24 @@
 import org.chromium.chrome.browser.autofill_assistant.details.AssistantDetailsModel;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel;
+import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * State for the Autofill Assistant UI.
  */
 @JNINamespace("autofill_assistant")
-class AssistantModel {
+class AssistantModel extends PropertyModel {
+    static final WritableBooleanPropertyKey ALLOW_SOFT_KEYBOARD = new WritableBooleanPropertyKey();
+
     private final AssistantOverlayModel mOverlayModel = new AssistantOverlayModel();
     private final AssistantHeaderModel mHeaderModel = new AssistantHeaderModel();
     private final AssistantDetailsModel mDetailsModel = new AssistantDetailsModel();
     private final AssistantCarouselModel mCarouselModel = new AssistantCarouselModel();
 
+    AssistantModel() {
+        super(ALLOW_SOFT_KEYBOARD);
+    }
+
     @CalledByNative
     public AssistantOverlayModel getOverlayModel() {
         return mOverlayModel;
@@ -40,4 +47,9 @@
     public AssistantCarouselModel getCarouselModel() {
         return mCarouselModel;
     }
+
+    @CalledByNative
+    private void setAllowSoftKeyboard(boolean allowed) {
+        set(ALLOW_SOFT_KEYBOARD, allowed);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index 153c6ebc..6a695a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -112,11 +112,6 @@
     }
 
     @CalledByNative
-    private void onAllowShowingSoftKeyboard(boolean allowed) {
-        mCoordinator.getKeyboardCoordinator().allowShowingSoftKeyboard(allowed);
-    }
-
-    @CalledByNative
     private void onShutdown(@DropOutReason int reason) {
         mCoordinator.shutdownImmediately(reason);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ContactsFetcherWorkerTask.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ContactsFetcherWorkerTask.java
index 5d693a44..000f1c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ContactsFetcherWorkerTask.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ContactsFetcherWorkerTask.java
@@ -43,6 +43,9 @@
     // The callback to use to communicate the results.
     private ContactsRetrievedCallback mCallback;
 
+    // Whether names were requested by the website.
+    private final boolean mIncludeNames;
+
     // Whether to include emails in the data fetched.
     private final boolean mIncludeEmails;
 
@@ -51,12 +54,18 @@
 
     /**
      * A ContactsFetcherWorkerTask constructor.
+     * @param contentResolver The ContentResolver to use to fetch the contacts data.
      * @param callback The callback to use to communicate back the results.
+     * @param includeNames Whether names were requested by the website.
+     * @param includeEmails Whether to include emails in the data fetched.
+     * @param includeTel Whether to include telephones in the data fetched.
      */
     public ContactsFetcherWorkerTask(ContentResolver contentResolver,
-            ContactsRetrievedCallback callback, boolean includeEmails, boolean includeTel) {
+            ContactsRetrievedCallback callback, boolean includeNames, boolean includeEmails,
+            boolean includeTel) {
         mContentResolver = contentResolver;
         mCallback = callback;
+        mIncludeNames = includeNames;
         mIncludeEmails = includeEmails;
         mIncludeTel = includeTel;
     }
@@ -147,7 +156,7 @@
                     cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY));
             List<String> email = mIncludeEmails ? emailMap.get(id) : null;
             List<String> tel = mIncludeTel ? phoneMap.get(id) : null;
-            if (email != null || tel != null)
+            if (mIncludeNames || email != null || tel != null)
                 contacts.add(new ContactDetails(id, name, email, tel));
         } while (cursor.moveToNext());
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
index 85b84c27..29462be 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
@@ -91,8 +91,9 @@
         mContentResolver = contentResolver;
 
         if (getAllContacts() == null && sTestContacts == null) {
-            mWorkerTask = new ContactsFetcherWorkerTask(
-                    mContentResolver, this, mCategoryView.includeEmails, mCategoryView.includeTel);
+            mWorkerTask = new ContactsFetcherWorkerTask(mContentResolver, this,
+                    mCategoryView.includeNames, mCategoryView.includeEmails,
+                    mCategoryView.includeTel);
             mWorkerTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
         } else {
             mContactDetails = sTestContacts;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentUtils.java
index 6bbd9b0..d4bf99c5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/document/DocumentUtils.java
@@ -100,13 +100,13 @@
     }
 
     /**
-     * Given an AppTask retrieves the task class name.
+     * Given an AppTask retrieves the task component name.
      * @param task The app task to use.
      * @param pm The package manager to use for resolving intent.
-     * @return Fully qualified class name or null if we were not able to
+     * @return Fully qualified component name name or null if we were not able to
      * determine it.
      */
-    public static String getTaskClassName(AppTask task, PackageManager pm) {
+    public static String getTaskComponentName(AppTask task, PackageManager pm) {
         RecentTaskInfo info = getTaskInfoFromTask(task);
         if (info == null) return null;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java
index e7fccf62..9c3c9563 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java
@@ -123,13 +123,13 @@
         for (AppTask task : manager.getAppTasks()) {
             RecentTaskInfo info = DocumentUtils.getTaskInfoFromTask(task);
             if (info == null) continue;
-            String className = DocumentUtils.getTaskClassName(task, pm);
+            String componentName = DocumentUtils.getTaskComponentName(task, pm);
 
             // It is not easily possible to distinguish between tasks sitting on top of
             // ChromeLauncherActivity, so we treat them all as likely ChromeTabbedActivities and
             // close them to be on the cautious side of things.
-            if ((ChromeTabbedActivity.isTabbedModeClassName(className)
-                    || TextUtils.equals(className, ChromeLauncherActivity.class.getName()))
+            if ((ChromeTabbedActivity.isTabbedModeComponentName(componentName)
+                        || TextUtils.equals(componentName, ChromeLauncherActivity.class.getName()))
                     && !visibleTaskIds.contains(info.id)) {
                 task.finishAndRemoveTask();
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java
index eb91a24..2e5b48a3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoUtils.java
@@ -53,9 +53,9 @@
         for (ActivityManager.AppTask task : manager.getAppTasks()) {
             ActivityManager.RecentTaskInfo info = DocumentUtils.getTaskInfoFromTask(task);
             if (info == null) continue;
-            String className = DocumentUtils.getTaskClassName(task, pm);
+            String componentName = DocumentUtils.getTaskComponentName(task, pm);
 
-            if (ChromeTabbedActivity.isTabbedModeClassName(className)) {
+            if (ChromeTabbedActivity.isTabbedModeComponentName(componentName)) {
                 tabbedModeTaskIds.add(info.id);
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index fbb0568..ec4140ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -1490,8 +1490,7 @@
                 || !TextUtils.isEmpty(errors.languageCode)
                 || !TextUtils.isEmpty(errors.organization) || !TextUtils.isEmpty(errors.phone)
                 || !TextUtils.isEmpty(errors.postalCode) || !TextUtils.isEmpty(errors.recipient)
-                || !TextUtils.isEmpty(errors.region) || !TextUtils.isEmpty(errors.regionCode)
-                || !TextUtils.isEmpty(errors.sortingCode);
+                || !TextUtils.isEmpty(errors.region) || !TextUtils.isEmpty(errors.sortingCode);
     }
 
     private boolean hasPayerError(PayerErrors errors) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java
index 4c86f05..29b31b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java
@@ -7,7 +7,10 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.share.ShareActivity;
+import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.util.UrlUtilities;
+import org.chromium.components.sync.ModelType;
 
 /**
  * A simple activity that allows Chrome to expose send tab to self as an
@@ -20,10 +23,25 @@
     }
 
     public static boolean featureIsAvailable(Tab currentTab) {
-      // TODO(tgupta): Add additional checks here to make sure the user
-      // has additional devices that they are signed into and syncing on.
-      return ChromeFeatureList.isEnabled(ChromeFeatureList.SEND_TAB_TO_SELF) &&
-              !currentTab.isIncognito();
+        // Check that sync requirements are met:
+        //   User is syncing on at least 2 devices (including this one)
+        //   SendTabToSelf sync datatype is enabled
+        ProfileSyncService syncService = ProfileSyncService.get();
+        boolean userEnabledSyncType =
+                syncService.getPreferredDataTypes().contains(ModelType.SEND_TAB_TO_SELF);
+        boolean syncRequirementsMet =
+                userEnabledSyncType && syncService.getNumberOfSyncedDevices() > 1;
+
+        // Check that the tab and web content requirements are met:
+        //   The active tab is not in inCognito mode or on a native page
+        //   User is viewing an HTTP or HTTPS page
+        boolean isHttpOrHttps = UrlUtilities.isHttpOrHttps(currentTab.getUrl());
+        boolean contentRequirementsMet =
+                isHttpOrHttps && !currentTab.isNativePage() && !currentTab.isIncognito();
+
+        // Return whether the feature is enabled and the criteria is met as defined above.
+        boolean featureEnabled = ChromeFeatureList.isEnabled(ChromeFeatureList.SEND_TAB_TO_SELF);
+        return featureEnabled && contentRequirementsMet && syncRequirementsMet;
     }
 }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
index 9c54f701..8995813 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
@@ -211,6 +211,10 @@
         return nativeGetSyncEnterCustomPassphraseBodyText(mNativeProfileSyncServiceAndroid);
     }
 
+    public int getNumberOfSyncedDevices() {
+        return nativeGetNumberOfSyncedDevices(mNativeProfileSyncServiceAndroid);
+    }
+
     /**
      * Checks if sync is currently set to use a custom passphrase. The sync engine must be running
      * (isEngineInitialized() returns true) before calling this function.
@@ -642,6 +646,7 @@
     private native String nativeGetCurrentSignedInAccountText(long nativeProfileSyncServiceAndroid);
     private native String nativeGetSyncEnterCustomPassphraseBodyText(
             long nativeProfileSyncServiceAndroid);
+    private native int nativeGetNumberOfSyncedDevices(long nativeProfileSyncServiceAndroid);
     private native int[] nativeGetActiveDataTypes(long nativeProfileSyncServiceAndroid);
     private native int[] nativeGetChosenDataTypes(long nativeProfileSyncServiceAndroid);
     private native int[] nativeGetPreferredDataTypes(long nativeProfileSyncServiceAndroid);
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 58df017..0d27277e 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -2272,6 +2272,7 @@
   "javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTestUtils.java",
   "javatests/src/org/chromium/chrome/browser/searchwidget/SearchActivityTest.java",
   "javatests/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProviderTest.java",
+  "javatests/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java",
   "javatests/src/org/chromium/chrome/browser/services/GoogleServicesManagerIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/shape_detection/ShapeDetectionTest.java",
   "javatests/src/org/chromium/chrome/browser/share/ShareMenuActionHandlerIntegrationTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java
new file mode 100644
index 0000000..cc2f179
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java
@@ -0,0 +1,162 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.send_tab_to_self;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.sync.FakeProfileSyncService;
+import org.chromium.chrome.browser.sync.ProfileSyncService;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.browser.TabLoadObserver;
+import org.chromium.components.sync.ModelType;
+import org.chromium.net.test.EmbeddedTestServer;
+
+import java.util.HashSet;
+
+/**
+ * Test suite for the SendTabToSelfShare Activity
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
+        "enable-features=" + ChromeFeatureList.SEND_TAB_TO_SELF})
+public class SendTabToSelfShareActivityTest {
+    @Rule
+    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+
+    private FakeProfileSyncService mProfileSyncService;
+    private EmbeddedTestServer mTestServer;
+    private Tab mTab;
+
+    private static final String TEST_PAGE = "/chrome/test/data/android/simple.html";
+    private static final String ABOUT_BLANK = "about:blank";
+    private static final String NATIVE_PAGE = "chrome://newtab";
+
+    @Before
+    public void setUp() throws Exception {
+        mActivityTestRule.startMainActivityOnBlankPage();
+        mTab = mActivityTestRule.getActivity().getActivityTab();
+        mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                ProfileSyncService.overrideForTests(new FakeProfileSyncService());
+                mProfileSyncService = (FakeProfileSyncService) ProfileSyncService.get();
+            }
+        });
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mTestServer != null) {
+            mTestServer.stopAndDestroyServer();
+        }
+    }
+
+    // Test enabling of share activity
+    @Test
+    @LargeTest
+    @Feature({"SendTabToSelf"})
+    public void testEnabled() throws Exception {
+        setChosenTypes(ModelType.SEND_TAB_TO_SELF);
+        mProfileSyncService.setNumberOfSyncedDevices(2);
+        loadUrlInTab(TEST_PAGE);
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                boolean result = SendTabToSelfShareActivity.featureIsAvailable(mTab);
+                Assert.assertTrue(
+                        "SendTabToSelfShareActivity disabled because of unsupported type", result);
+            }
+        });
+    }
+
+    // Test disabling of share activity because sync model type is not supported.
+    @Test
+    @LargeTest
+    @Feature({"SendTabToSelf"})
+    public void testDisabledUnsupportedSyncModelType() throws Exception {
+        setChosenTypes(null);
+        mProfileSyncService.setNumberOfSyncedDevices(2);
+        loadUrlInTab(TEST_PAGE);
+        assertFeatureIsDisabled("SendTabToSelfShareActivity disabled because of unsupported type");
+    }
+
+    // Test disabling of share activity because <2 devices are being synced.
+    @Test
+    @LargeTest
+    @Feature({"Sync"})
+    public void testDisabledInsufficientSyncedDevices() throws Exception {
+        mProfileSyncService.setNumberOfSyncedDevices(0);
+        setChosenTypes(ModelType.SEND_TAB_TO_SELF);
+        loadUrlInTab(TEST_PAGE);
+        assertFeatureIsDisabled("SendTabToSelfShareActivity disabled because of insufficient "
+                + "synced devices");
+
+        mProfileSyncService.setNumberOfSyncedDevices(1);
+        loadUrlInTab(TEST_PAGE);
+        assertFeatureIsDisabled("SendTabToSelfShareActivity disabled because of insufficient "
+                + "synced devices");
+    }
+
+    // Test disabling of share feature because of URL
+    @Test
+    @LargeTest
+    @Feature({"SendTabToSelf"})
+    public void testDisableWrongUrl() throws Exception {
+        setChosenTypes(ModelType.SEND_TAB_TO_SELF);
+        mProfileSyncService.setNumberOfSyncedDevices(2);
+        assertFeatureIsDisabled("SendTabToSelfShareActivity disabled because of unsupported url");
+    }
+
+    // Test enabling of share activity
+    @Test
+    @LargeTest
+    @Feature({"SendTabToSelf"})
+    @CommandLineFlags.Add("disable-features=" + ChromeFeatureList.SEND_TAB_TO_SELF)
+    public void testFeatureDisabled() throws Exception {
+        setChosenTypes(ModelType.SEND_TAB_TO_SELF);
+        mProfileSyncService.setNumberOfSyncedDevices(2);
+        loadUrlInTab(TEST_PAGE);
+        assertFeatureIsDisabled("SendTabToSelfShareActivity disabled because of unsupported type");
+    }
+
+    private void assertFeatureIsDisabled(final String errorMessage) {
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                boolean result = SendTabToSelfShareActivity.featureIsAvailable(mTab);
+                Assert.assertFalse(errorMessage, result);
+            }
+        });
+    }
+
+    private void setChosenTypes(Integer type) {
+        HashSet<Integer> chosenTypes = new HashSet<Integer>();
+        if (type != null) {
+            chosenTypes.add(type);
+        }
+        mProfileSyncService.setChosenDataTypes(false, chosenTypes);
+    }
+
+    private void loadUrlInTab(String url) throws Exception {
+        String testServerUrl = mTestServer.getURL(url);
+        new TabLoadObserver(mTab).fullyLoadUrl(testServerUrl);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
index f6177b6..1ce5e78 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.sync;
 
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * Fake some ProfileSyncService methods for testing.
  *
@@ -11,6 +14,8 @@
  */
 public class FakeProfileSyncService extends ProfileSyncService {
     private boolean mEngineInitialized;
+    private int mNumberOfSyncedDevices;
+    private Set<Integer> mChosenTypes = new HashSet<>();
 
     public FakeProfileSyncService() {
         super();
@@ -29,4 +34,23 @@
     public boolean isUsingSecondaryPassphrase() {
         return true;
     }
+
+    @Override
+    public int getNumberOfSyncedDevices() {
+        return mNumberOfSyncedDevices;
+    }
+
+    public void setNumberOfSyncedDevices(int numDevices) {
+        mNumberOfSyncedDevices = numDevices;
+    }
+
+    @Override
+    public void setChosenDataTypes(boolean syncEverything, Set<Integer> enabledTypes) {
+        mChosenTypes = enabledTypes;
+    }
+
+    @Override
+    public Set<Integer> getPreferredDataTypes() {
+        return mChosenTypes;
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java
index ccf0428c..beec7c9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/UkmTest.java
@@ -16,7 +16,6 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.FlakyTest;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.metrics.UmaSessionStats;
 import org.chromium.chrome.browser.tab.Tab;
@@ -188,7 +187,6 @@
 
     @Test
     @SmallTest
-    @FlakyTest(message = "crbug.com/879248")
     public void singleDisableHistorySyncCheck() throws Exception {
         // Keep in sync with UkmBrowserTest.SingleDisableHistorySyncCheck in
         // chrome/browser/metrics/ukm_browsertest.cc.
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index f2c21cd..721069a 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-74.0.3686.0_rc-r1.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-74.0.3687.0_rc-r1.afdo.bz2
\ No newline at end of file
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 9448ca8..07db9b1e 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -165,8 +165,8 @@
 }
 
 void UiControllerAndroid::AllowShowingSoftKeyboard(bool enabled) {
-  Java_AutofillAssistantUiController_onAllowShowingSoftKeyboard(
-      AttachCurrentThread(), java_autofill_assistant_ui_controller_, enabled);
+  Java_AssistantModel_setAllowSoftKeyboard(AttachCurrentThread(), GetModel(),
+                                           enabled);
 }
 
 void UiControllerAndroid::ExpandBottomSheet() {
diff --git a/chrome/browser/apps/app_service/app_icon_factory.cc b/chrome/browser/apps/app_service/app_icon_factory.cc
index 4105def..7c8a5d3 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.cc
+++ b/chrome/browser/apps/app_service/app_icon_factory.cc
@@ -41,6 +41,11 @@
   return std::vector<uint8_t>(data.begin(), data.end());
 }
 
+std::vector<uint8_t> CompressedDataFromResource(
+    const extensions::ExtensionResource resource) {
+  return ReadFileAsCompressedData(resource.GetFilePath());
+}
+
 // Runs |callback| passing an IconValuePtr with a compressed image: a
 // std::vector<uint8_t>.
 void RunCallbackWithCompressedData(
@@ -188,8 +193,7 @@
         // decoding from and re-encoding to PNG before and after the filter.
         base::PostTaskWithTraitsAndReplyWithResult(
             FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
-            base::BindOnce(&ReadFileAsCompressedData,
-                           ext_resource.GetFilePath()),
+            base::BindOnce(&CompressedDataFromResource, ext_resource),
             base::BindOnce(&RunCallbackWithCompressedData,
                            std::move(callback)));
         return;
diff --git a/chrome/browser/apps/app_service/app_icon_source.cc b/chrome/browser/apps/app_service/app_icon_source.cc
index 4f43009..6eeab21 100644
--- a/chrome/browser/apps/app_service/app_icon_source.cc
+++ b/chrome/browser/apps/app_service/app_icon_source.cc
@@ -36,7 +36,7 @@
 
 void RunCallback(const content::URLDataSource::GotDataCallback& callback,
                  apps::mojom::IconValuePtr iv) {
-  if (!iv->compressed.has_value()) {
+  if (!iv->compressed.has_value() || iv->compressed.value().empty()) {
     LoadDefaultImage(callback);
     return;
   }
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index f1d65937..5a8d74d9 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -404,7 +404,6 @@
   manifest_ = blink::Manifest();
   manifest_url_ = GURL();
   validated_url_ = GURL();
-  referrer_.erase();
   installable_ = Installable::UNKNOWN;
 }
 
@@ -639,8 +638,7 @@
 
 void AppBannerManager::OnBannerPromptReply(
     blink::mojom::AppBannerControllerPtr controller,
-    blink::mojom::AppBannerPromptReply reply,
-    const std::string& referrer) {
+    blink::mojom::AppBannerPromptReply reply) {
   // The renderer might have requested the prompt to be canceled. They may
   // request that it is redisplayed later, so don't Terminate() here. However,
   // log that the cancelation was requested, so Terminate() can be called if a
@@ -651,7 +649,6 @@
   // already been received before cancel was sent (e.g. if redisplay was
   // requested in the beforeinstallprompt event handler), we keep going and show
   // the banner immediately.
-  referrer_ = referrer;
   if (reply == blink::mojom::AppBannerPromptReply::CANCEL) {
     TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_PREVENT_DEFAULT_CALLED);
     if (IsDebugMode()) {
diff --git a/chrome/browser/banners/app_banner_manager.h b/chrome/browser/banners/app_banner_manager.h
index ace33940..aa4ed56 100644
--- a/chrome/browser/banners/app_banner_manager.h
+++ b/chrome/browser/banners/app_banner_manager.h
@@ -290,10 +290,6 @@
   // The primary icon object.
   SkBitmap primary_icon_;
 
-  // The referrer string (if any) specified in the app URL. Used only for native
-  // app banners.
-  std::string referrer_;
-
   // The current banner pipeline state for this page load.
   State state_;
 
@@ -317,8 +313,7 @@
   // opportunity to cancel.
   virtual void OnBannerPromptReply(
       blink::mojom::AppBannerControllerPtr controller,
-      blink::mojom::AppBannerPromptReply reply,
-      const std::string& referrer);
+      blink::mojom::AppBannerPromptReply reply);
 
   // Does the non-platform specific parts of showing the app banner.
   void ShowBanner();
diff --git a/chrome/browser/banners/app_banner_manager_browsertest.cc b/chrome/browser/banners/app_banner_manager_browsertest.cc
index 2c0d60b..dc534bc 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest.cc
@@ -109,10 +109,8 @@
   }
 
   void OnBannerPromptReply(blink::mojom::AppBannerControllerPtr controller,
-                           blink::mojom::AppBannerPromptReply reply,
-                           const std::string& referrer) override {
-    AppBannerManager::OnBannerPromptReply(std::move(controller), reply,
-                                          referrer);
+                           blink::mojom::AppBannerPromptReply reply) override {
+    AppBannerManager::OnBannerPromptReply(std::move(controller), reply);
     if (on_banner_prompt_reply_) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
           FROM_HERE, std::move(on_banner_prompt_reply_));
diff --git a/chrome/browser/chromeos/account_manager/account_manager_migrator.cc b/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
index 51b8d17a..f37dae1 100644
--- a/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
+++ b/chrome/browser/chromeos/account_manager/account_manager_migrator.cc
@@ -28,6 +28,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/account_reconcilor_factory.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
+#include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/web_data_service_factory.h"
 #include "chrome/common/pref_names.h"
@@ -39,9 +40,9 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_reconcilor.h"
 #include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/webdata/token_web_data.h"
 #include "components/webdata/common/web_data_service_consumer.h"
-#include "services/identity/public/cpp/accounts_in_cookie_jar_info.h"
 #include "services/identity/public/cpp/identity_manager.h"
 
 namespace chromeos {
@@ -272,34 +273,39 @@
 // to |AccountManager|. The objective is to migrate the account names only. We
 // cannot migrate any credentials (cookies).
 class ContentAreaAccountsMigration : public AccountMigrationBaseStep,
-                                     identity::IdentityManager::Observer {
+                                     GaiaCookieManagerService::Observer {
  public:
-  ContentAreaAccountsMigration(AccountManager* account_manager,
-                               identity::IdentityManager* identity_manager)
+  ContentAreaAccountsMigration(
+      AccountManager* account_manager,
+      identity::IdentityManager* identity_manager,
+      GaiaCookieManagerService* gaia_cookie_manager_service)
       : AccountMigrationBaseStep(kContentAreaAccountsMigration,
                                  account_manager,
                                  identity_manager),
-        identity_manager_(identity_manager) {}
+        gaia_cookie_manager_service_(gaia_cookie_manager_service) {}
   ~ContentAreaAccountsMigration() override {
-    identity_manager_->RemoveObserver(this);
+    gaia_cookie_manager_service_->RemoveObserver(this);
   }
 
  private:
   void StartMigration() override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-    identity_manager_->AddObserver(this);
-    identity::AccountsInCookieJarInfo accounts_in_cookie_jar_info =
-        identity_manager_->GetAccountsInCookieJar();
-    if (accounts_in_cookie_jar_info.accounts_are_fresh) {
-      OnAccountsInCookieUpdated(
-          accounts_in_cookie_jar_info,
+    std::vector<gaia::ListedAccount> signed_in_content_area_accounts;
+    std::vector<gaia::ListedAccount> signed_out_content_area_accounts;
+    gaia_cookie_manager_service_->AddObserver(this);
+    if (gaia_cookie_manager_service_->ListAccounts(
+            &signed_in_content_area_accounts,
+            &signed_out_content_area_accounts)) {
+      OnGaiaAccountsInCookieUpdated(
+          signed_in_content_area_accounts, signed_out_content_area_accounts,
           GoogleServiceAuthError(GoogleServiceAuthError::NONE));
     }
   }
 
-  void OnAccountsInCookieUpdated(
-      const identity::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
+  void OnGaiaAccountsInCookieUpdated(
+      const std::vector<gaia::ListedAccount>& signed_in_content_area_accounts,
+      const std::vector<gaia::ListedAccount>& signed_out_content_area_accounts,
       const GoogleServiceAuthError& error) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     // We should not have reached here without |OnGetAccounts| having been
@@ -307,10 +313,10 @@
     // Furthermore, Account Manager must have been populated with the Device
     // Account before this |Step| is run.
     DCHECK(!IsAccountManagerEmpty());
-    identity_manager_->RemoveObserver(this);
+    gaia_cookie_manager_service_->RemoveObserver(this);
 
-    MigrateAccounts(accounts_in_cookie_jar_info.signed_in_accounts,
-                    accounts_in_cookie_jar_info.signed_out_accounts);
+    MigrateAccounts(signed_in_content_area_accounts,
+                    signed_out_content_area_accounts);
 
     FinishWithSuccess();
   }
@@ -328,8 +334,8 @@
     }
   }
 
-  // A non-owning pointer to |IdentityManager|.
-  identity::IdentityManager* const identity_manager_;
+  // A non-owning pointer to |GaiaCookieManagerService|.
+  GaiaCookieManagerService* const gaia_cookie_manager_service_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
@@ -518,7 +524,9 @@
       WebDataServiceFactory::GetTokenWebDataForProfile(
           profile_, ServiceAccessType::EXPLICIT_ACCESS) /* token_web_data */));
   migration_runner_.AddStep(std::make_unique<ContentAreaAccountsMigration>(
-      account_manager, identity_manager));
+      account_manager, identity_manager,
+      GaiaCookieManagerServiceFactory::GetForProfile(
+          profile_) /* gaia_cookie_manager_service */));
 
   if (arc::IsArcProvisioned(profile_)) {
     // Add a migration step for ARC only if ARC has been provisioned. If ARC has
@@ -602,7 +610,7 @@
   // be re-enabled once migration is done.
   DependsOn(AccountReconcilorFactory::GetInstance());
   // For getting Chrome content area accounts.
-  DependsOn(IdentityManagerFactory::GetInstance());
+  DependsOn(GaiaCookieManagerServiceFactory::GetInstance());
 }
 
 AccountManagerMigratorFactory::~AccountManagerMigratorFactory() = default;
diff --git a/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc b/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
index 645bae2..0bb3afd 100644
--- a/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
+++ b/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
@@ -26,7 +26,7 @@
 #include "mojo/public/cpp/system/invitation.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "services/service_manager/public/cpp/connector.h"
-#include "services/ws/public/mojom/arc.mojom.h"
+#include "services/ws/public/mojom/arc_gpu.mojom.h"
 #include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/base/ui_base_features.h"
 
@@ -98,7 +98,7 @@
     DETACH_FROM_THREAD(thread_checker_);
     auto* connector =
         content::ServiceManagerConnection::GetForProcess()->GetConnector();
-    connector->BindInterface(ws::mojom::kServiceName, &arc_);
+    connector->BindInterface(ws::mojom::kServiceName, &arc_gpu_);
   }
 
   ~VideoAcceleratorFactoryServiceViz() override {
@@ -108,25 +108,25 @@
   void CreateDecodeAccelerator(
       mojom::VideoDecodeAcceleratorRequest request) override {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-    arc_->CreateVideoDecodeAccelerator(std::move(request));
+    arc_gpu_->CreateVideoDecodeAccelerator(std::move(request));
   }
 
   void CreateEncodeAccelerator(
       mojom::VideoEncodeAcceleratorRequest request) override {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-    arc_->CreateVideoEncodeAccelerator(std::move(request));
+    arc_gpu_->CreateVideoEncodeAccelerator(std::move(request));
   }
 
   void CreateProtectedBufferAllocator(
       mojom::VideoProtectedBufferAllocatorRequest request) override {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-    arc_->CreateVideoProtectedBufferAllocator(std::move(request));
+    arc_gpu_->CreateVideoProtectedBufferAllocator(std::move(request));
   }
 
  private:
   THREAD_CHECKER(thread_checker_);
 
-  ws::mojom::ArcPtr arc_;
+  ws::mojom::ArcGpuPtr arc_gpu_;
 
   DISALLOW_COPY_AND_ASSIGN(VideoAcceleratorFactoryServiceViz);
 };
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index 0cd53397..b7c4e0c 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -2144,11 +2144,10 @@
 }
 
 void CrostiniManager::RemoveCrostini(std::string vm_name,
-                                     std::string container_name,
                                      RemoveCrostiniCallback callback) {
   AddRemoveCrostiniCallback(std::move(callback));
   auto crostini_remover = base::MakeRefCounted<CrostiniRemover>(
-      profile_, std::move(vm_name), std::move(container_name),
+      profile_, std::move(vm_name),
       base::BindOnce(&CrostiniManager::OnRemoveCrostini,
                      weak_ptr_factory_.GetWeakPtr()));
   crostini_remover->RemoveCrostini();
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.h b/chrome/browser/chromeos/crostini/crostini_manager.h
index fe445f3..8b83a30 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.h
+++ b/chrome/browser/chromeos/crostini/crostini_manager.h
@@ -480,7 +480,6 @@
       const vm_tools::cicerone::LxdContainerStartingSignal& signal) override;
 
   void RemoveCrostini(std::string vm_name,
-                      std::string container_name,
                       RemoveCrostiniCallback callback);
 
   void SetVmState(std::string vm_name, VmState vm_state);
diff --git a/chrome/browser/chromeos/crostini/crostini_mime_types_service.cc b/chrome/browser/chromeos/crostini/crostini_mime_types_service.cc
index 2c5d0e22..a3e7c34 100644
--- a/chrome/browser/chromeos/crostini/crostini_mime_types_service.cc
+++ b/chrome/browser/chromeos/crostini/crostini_mime_types_service.cc
@@ -67,16 +67,12 @@
       ->GetString();
 }
 
-void CrostiniMimeTypesService::ClearMimeTypes(
-    const std::string& vm_name,
-    const std::string& container_name) {
+void CrostiniMimeTypesService::ClearMimeTypes(const std::string& vm_name) {
   DictionaryPrefUpdate update(prefs_, prefs::kCrostiniMimeTypes);
   base::DictionaryValue* mime_type_mappings = update.Get();
   std::vector<std::string> removed_ids;
   for (const auto& item : mime_type_mappings->DictItems()) {
-    if (item.second.FindKey(kAppVmNameKey)->GetString() == vm_name &&
-        item.second.FindKey(kAppContainerNameKey)->GetString() ==
-            container_name) {
+    if (item.second.FindKey(kAppVmNameKey)->GetString() == vm_name) {
       removed_ids.push_back(item.first);
     }
   }
diff --git a/chrome/browser/chromeos/crostini/crostini_mime_types_service.h b/chrome/browser/chromeos/crostini/crostini_mime_types_service.h
index 6314d48..b1a9f7a 100644
--- a/chrome/browser/chromeos/crostini/crostini_mime_types_service.h
+++ b/chrome/browser/chromeos/crostini/crostini_mime_types_service.h
@@ -37,10 +37,9 @@
                           const std::string& vm_name,
                           const std::string& container_name) const;
 
-  // Remove all MIME type associations for the named container. Used in the
+  // Remove all MIME type associations for the named VM. Used in the
   // uninstall process.
-  void ClearMimeTypes(const std::string& vm_name,
-                      const std::string& container_name);
+  void ClearMimeTypes(const std::string& vm_name);
 
   // The existing list of MIME type mappings is replaced by
   // |mime_type_mappings|.
diff --git a/chrome/browser/chromeos/crostini/crostini_mime_types_service_unittest.cc b/chrome/browser/chromeos/crostini/crostini_mime_types_service_unittest.cc
index 3a1e409..e9f39a7 100644
--- a/chrome/browser/chromeos/crostini/crostini_mime_types_service_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_mime_types_service_unittest.cc
@@ -107,7 +107,7 @@
 }
 
 // Test that ClearMimeTypes works, and only removes apps from the
-// specified container.
+// specified VM.
 TEST_F(CrostiniMimeTypesServiceTest, ClearMimeTypes) {
   service()->UpdateMimeTypes(
       CreateMimeTypesProto({"foo"}, {"foo/mime"}, "vm 1", "container 1"));
@@ -123,7 +123,7 @@
   EXPECT_EQ("foobar/mime", service()->GetMimeType(base::FilePath("test.foobar"),
                                                   "vm 2", "container 1"));
 
-  service()->ClearMimeTypes("vm 2", "container 1");
+  service()->ClearMimeTypes("vm 2");
 
   EXPECT_EQ("foo/mime", service()->GetMimeType(base::FilePath("test.foo"),
                                                "vm 1", "container 1"));
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.cc b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
index 3d44016..32e1b75 100644
--- a/chrome/browser/chromeos/crostini/crostini_registry_service.cc
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
@@ -659,9 +659,7 @@
   RequestIcon(app_id, scale_factor);
 }
 
-void CrostiniRegistryService::ClearApplicationList(
-    const std::string& vm_name,
-    const std::string& container_name) {
+void CrostiniRegistryService::ClearApplicationList(const std::string& vm_name) {
   std::vector<std::string> removed_apps;
   // The DictionaryPrefUpdate should be destructed before calling the observer.
   {
@@ -671,11 +669,8 @@
     for (const auto& item : apps->DictItems()) {
       if (item.first == kCrostiniTerminalId)
         continue;
-      if (item.second.FindKey(kAppVmNameKey)->GetString() == vm_name &&
-          item.second.FindKey(kAppContainerNameKey)->GetString() ==
-              container_name) {
+      if (item.second.FindKey(kAppVmNameKey)->GetString() == vm_name)
         removed_apps.push_back(item.first);
-      }
     }
     for (const std::string& removed_app : removed_apps) {
       RemoveAppData(removed_app);
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.h b/chrome/browser/chromeos/crostini/crostini_registry_service.h
index af0503b..c1c1ab5 100644
--- a/chrome/browser/chromeos/crostini/crostini_registry_service.h
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service.h
@@ -166,9 +166,8 @@
   void MaybeRequestIcon(const std::string& app_id,
                         ui::ScaleFactor scale_factor);
 
-  // Remove all apps from the named container. Used in the uninstall process.
-  void ClearApplicationList(const std::string& vm_name,
-                            const std::string& container_name);
+  // Remove all apps from the named VM. Used in the uninstall process.
+  void ClearApplicationList(const std::string& vm_name);
 
   // The existing list of apps is replaced by |application_list|.
   void UpdateApplicationList(const vm_tools::apps::ApplicationList& app_list);
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc b/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc
index 3de6990d..be93fe8 100644
--- a/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service_unittest.cc
@@ -286,7 +286,7 @@
 }
 
 // Test that ClearApplicationList works, and only removes apps from the
-// specified container.
+// specified VM.
 TEST_F(CrostiniRegistryServiceTest, ClearApplicationList) {
   service()->UpdateApplicationList(
       CrostiniTestHelper::BasicAppList("app", "vm 1", "container 1"));
@@ -309,7 +309,7 @@
               testing::UnorderedElementsAre(app_id_1, app_id_2, app_id_3,
                                             app_id_4, kCrostiniTerminalId));
 
-  service()->ClearApplicationList("vm 2", "container 1");
+  service()->ClearApplicationList("vm 2");
 
   EXPECT_THAT(
       service()->GetRegisteredAppIds(),
diff --git a/chrome/browser/chromeos/crostini/crostini_remover.cc b/chrome/browser/chromeos/crostini/crostini_remover.cc
index ab2fab7..d4e96ad1 100644
--- a/chrome/browser/chromeos/crostini/crostini_remover.cc
+++ b/chrome/browser/chromeos/crostini/crostini_remover.cc
@@ -25,11 +25,9 @@
 CrostiniRemover::CrostiniRemover(
     Profile* profile,
     std::string vm_name,
-    std::string container_name,
     CrostiniManager::RemoveCrostiniCallback callback)
     : profile_(profile),
       vm_name_(std::move(vm_name)),
-      container_name_(std::move(container_name)),
       callback_(std::move(callback)) {}
 
 CrostiniRemover::~CrostiniRemover() = default;
@@ -70,10 +68,11 @@
     std::move(callback_).Run(result);
     return;
   }
+
   CrostiniRegistryServiceFactory::GetForProfile(profile_)->ClearApplicationList(
-      vm_name_, container_name_);
+      vm_name_);
   CrostiniMimeTypesServiceFactory::GetForProfile(profile_)->ClearMimeTypes(
-      vm_name_, container_name_);
+      vm_name_);
   CrostiniManager::GetForProfile(profile_)->DestroyDiskImage(
       base::FilePath(vm_name_),
       vm_tools::concierge::StorageLocation::STORAGE_CRYPTOHOME_ROOT,
diff --git a/chrome/browser/chromeos/crostini/crostini_remover.h b/chrome/browser/chromeos/crostini/crostini_remover.h
index d3642ea1..9febb63 100644
--- a/chrome/browser/chromeos/crostini/crostini_remover.h
+++ b/chrome/browser/chromeos/crostini/crostini_remover.h
@@ -13,7 +13,6 @@
  public:
   CrostiniRemover(Profile* profile,
                   std::string vm_name,
-                  std::string container_name,
                   CrostiniManager::RemoveCrostiniCallback callback);
 
   void RemoveCrostini();
@@ -31,7 +30,6 @@
 
   Profile* profile_;
   std::string vm_name_;
-  std::string container_name_;
   CrostiniManager::RemoveCrostiniCallback callback_;
 
   DISALLOW_COPY_AND_ASSIGN(CrostiniRemover);
diff --git a/chrome/browser/extensions/OWNERS b/chrome/browser/extensions/OWNERS
index 57b1cbc..0d40771 100644
--- a/chrome/browser/extensions/OWNERS
+++ b/chrome/browser/extensions/OWNERS
@@ -4,6 +4,7 @@
 per-file *networking*=tbarzic@chromium.org
 
 per-file bookmark_app_*=calamity@chromium.org
+per-file bookmark_app_*=dominickn@chromium.org
 per-file bookmark_app_*=mgiuca@chromium.org
 per-file bookmark_app_*=ortuno@chromium.org
 
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index d9ced93..96c0e4d 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -740,8 +740,8 @@
         base::MakeRefCounted<chromeos::DemoExtensionsExternalLoader>(cache_dir);
     std::unique_ptr<ExternalProviderImpl> demo_apps_provider =
         std::make_unique<ExternalProviderImpl>(
-            service, loader, profile, Manifest::EXTERNAL_PREF,
-            Manifest::EXTERNAL_PREF_DOWNLOAD, Extension::NO_FLAGS);
+            service, loader, profile, Manifest::EXTERNAL_POLICY,
+            Manifest::EXTERNAL_POLICY_DOWNLOAD, Extension::NO_FLAGS);
     demo_apps_provider->set_auto_acknowledge(true);
     demo_apps_provider->set_install_immediately(true);
     chromeos::DemoSession::Get()->SetExtensionsExternalLoader(loader);
diff --git a/chrome/browser/resource_coordinator/render_process_user_data.cc b/chrome/browser/resource_coordinator/render_process_user_data.cc
index 19a966a..4c954df 100644
--- a/chrome/browser/resource_coordinator/render_process_user_data.cc
+++ b/chrome/browser/resource_coordinator/render_process_user_data.cc
@@ -7,10 +7,12 @@
 #include <memory>
 #include <utility>
 
+#include "base/command_line.h"
 #include "base/memory/ptr_util.h"
 #include "content/public/browser/child_process_termination_info.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_process_host_observer.h"
+#include "content/public/common/content_switches.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
 
@@ -59,7 +61,9 @@
     content::RenderProcessHost* render_process_host)
     : process_resource_coordinator_(MaybeGetConnectionForProcess()) {
   // The process itself shouldn't have been created at this point.
-  DCHECK(!render_process_host->GetProcess().IsValid());
+  DCHECK(!render_process_host->GetProcess().IsValid() ||
+         base::CommandLine::ForCurrentProcess()->HasSwitch(
+             switches::kSingleProcess));
   render_process_host->AddObserver(new RenderProcessLifetimeWatcher);
 }
 
diff --git a/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc b/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc
index afab7bb..e3fb0d3 100644
--- a/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_activity_watcher_unittest.cc
@@ -584,7 +584,13 @@
 };
 
 // Tests TabManager.Backgrounded.ForegroundedOrClosed UKM logging.
-TEST_F(ForegroundedOrClosedTest, SingleTab) {
+// Flaky on ChromeOS. http://crbug.com/924864
+#if defined(OS_CHROMEOS)
+#define MAYBE_SingleTab DISABLED_SingleTab
+#else
+#define MAYBE_SingleTab SingleTab
+#endif
+TEST_F(ForegroundedOrClosedTest, MAYBE_SingleTab) {
   Browser::CreateParams params(profile(), true);
   std::unique_ptr<Browser> browser =
       CreateBrowserWithTestWindowForParams(&params);
diff --git a/chrome/browser/resources/app_management/util.js b/chrome/browser/resources/app_management/util.js
index 6e381888..c7a43c97 100644
--- a/chrome/browser/resources/app_management/util.js
+++ b/chrome/browser/resources/app_management/util.js
@@ -53,7 +53,7 @@
    * @return {string}
    */
   function getAppIcon(app) {
-    return `chrome://extension-icon/${app.id}/128/1`;
+    return `chrome://app-icon/${app.id}/128`;
   }
 
   /**
diff --git a/chrome/browser/resources/chromeos/assistant_optin/setting_zippy.css b/chrome/browser/resources/chromeos/assistant_optin/setting_zippy.css
index 80766b01..26bb724b 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/setting_zippy.css
+++ b/chrome/browser/resources/chromeos/assistant_optin/setting_zippy.css
@@ -2,11 +2,11 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-#container[popupStyle] {
+#container:not([expandStyle]) {
   padding-top: 8px;
 }
 
-#container:not([popupStyle]) {
+#container[expandStyle] {
   align-items: center;
   height: 48px;
 }
@@ -34,11 +34,11 @@
 }
 
 cr-expand-button {
-  padding-right: 8px;
+  padding-inline-end: 8px;
 }
 
 .toggle {
-  padding-right: 20px;
+  padding-inline-end: 20px;
 }
 
 .indent {
diff --git a/chrome/browser/resources/chromeos/camera/src/css/main.css b/chrome/browser/resources/chromeos/camera/src/css/main.css
index d36f177..c4c85c6f 100644
--- a/chrome/browser/resources/chromeos/camera/src/css/main.css
+++ b/chrome/browser/resources/chromeos/camera/src/css/main.css
@@ -66,13 +66,13 @@
 button,
 input {
   background-color: transparent;
-  position: relative;
-  pointer-events: auto;
-  margin: 0;
-  padding: 0;
   border-radius: 4px;
   border-style: none;
+  margin: 0;
   outline: none;
+  padding: 0;
+  pointer-events: auto;
+  position: relative;
 }
 
 input {
@@ -81,14 +81,14 @@
 
 button:focus:after,
 input:focus:after {
-  position: absolute;
   border: 2px solid rgba(37, 129, 223, 0.7);
   border-radius: 4px;
-  top: -3px;
-  left: -3px;
   bottom: -3px;
-  right: -3px;
   content: '';
+  left: -3px;
+  position: absolute;
+  right: -3px;
+  top: -3px;
 }
 
 #browser .media-wrapper:focus {
@@ -118,11 +118,11 @@
 .buttons input,
 .menu-header .icon,
 .menu-item .icon {
-  flex-shrink: 0;
-  width: 40px;
-  height: 40px;
   background-position: center;
   background-repeat: no-repeat;
+  flex-shrink: 0;
+  height: 40px;
+  width: 40px;
 }
 
 #browser-print {
@@ -142,8 +142,8 @@
 }
 
 ::-webkit-scrollbar {
-  width: 0;
   height: 0;
+  width: 0;
 }
 
 ::-webkit-scrollbar-track {
@@ -152,8 +152,8 @@
 
 ::-webkit-scrollbar-thumb {
   background: transparent;
-  width: 0;
   height: 0;
+  width: 0;
 }
 
 .top-stripe {
@@ -168,8 +168,8 @@
 
 .bottom-stripe,
 body:not(.tablet-landscape) .actions-group {
-  position: absolute;
   bottom: 56px;
+  position: absolute;
   transform: translateY(50%);
 }
 
@@ -183,8 +183,8 @@
 }
 
 .left-stripe {
-  position: absolute;
   left: 48px;
+  position: absolute;
   transform: translateX(-50%);
 }
 
@@ -238,8 +238,8 @@
 
 #shutter {
   background-image: url(../images/camera_shutter_photo_start.svg);
-  width: 72px;
   height: 72px;
+  width: 72px;
   z-index: 1;  /* On top of transforming switch-mode buttons. */
 }
 
@@ -277,8 +277,8 @@
 
 #switch-recordvideo,
 #switch-takephoto {
-  width: 56px;
   height: 56px;
+  width: 56px;
 }
 
 #switch-recordvideo {
@@ -316,9 +316,9 @@
 
 #camera-mode {
   background-image: url(../images/camera_mode_photo.svg);
-  width: 40px;
   height: 40px;
   visibility: hidden;
+  width: 40px;
 }
 
 body.record-mode #camera-mode {
@@ -331,22 +331,22 @@
 
 #timer-tick-msg {
   color: white;
-  font-size: 72px;
   font-family: 'Roboto', sans-serif;
+  font-size: 72px;
   visibility: hidden;
 }
 
 #timer-tick-msg.animate {
-  transform: scale(1.8, 1.8);
   opacity: 0.2;
+  transform: scale(1.8, 1.8);
   transition: transform 500ms ease-out, opacity 500ms ease-out;
   visibility: visible;
 }
 
 #switch-device {
   background-image: url(../images/camera_button_switch_device.svg);
-  width: 48px;
   height: 48px;
+  width: 48px;
 }
 
 #switch-device.animate {
@@ -363,16 +363,16 @@
 }
 
 #gallery-enter {
+  background-color: rgba(24, 24, 24, 1);
+  background-size: cover;
+  height: 48px;
   position: relative;
   width: 48px;
-  height: 48px;
-  background-size: cover;
-  background-color: rgba(24, 24, 24, 1);
 }
 
 .centered-overlay {
-  position: absolute;
   left: 50%;
+  position: absolute;
   top: 50%;
   transform: translate(-50%, -50%);
 }
@@ -450,8 +450,8 @@
 body.dialog #dialog,
 body.warning #warning {
   opacity: 1;
-  visibility: visible;
   transition: opacity 100ms;
+  visibility: visible;
 }
 
 body.gridsettings #gridsettings,
@@ -483,8 +483,8 @@
 }
 
 body.shift-preview-left #preview-wrapper {
-  position: absolute;
   left: 0;
+  position: absolute;
 }
 
 #preview-wrapper,
@@ -504,11 +504,11 @@
 }
 
 #preview-focus {
-  position: absolute;
-  left: 0;
-  top: 0;
-  right: 0;
   bottom: 0;
+  left: 0;
+  position: absolute;
+  right: 0;
+  top: 0;
 }
 
 #preview-focus-aim {
@@ -527,12 +527,12 @@
 }
 
 #preview-grid {
-  position: absolute;
   bottom: 0;
   left: 0;
+  opacity: 0.5;
+  position: absolute;
   right: 0;
   top: 0;
-  opacity: 0.5;
 }
 
 body:not(.streaming) #preview-grid,
@@ -546,22 +546,22 @@
 #preview-grid-horizontal {
   border-bottom: 1px solid white;
   border-top: 1px solid white;
-  position: absolute;
+  height: 100%;
   left: 0;
+  position: absolute;
   right: 0;
   top: 50%;
-  height: 100%;
   transform: translateY(-50%);
 }
 
 body._4x4 #preview-grid-horizontal:before {
   border-bottom: 1px solid white;
-  position: absolute;
+  content: '';
+  height: 0;
   left: 0;
+  position: absolute;
   right: 0;
   top: 0;
-  height: 0;
-  content: '';
 }
 
 body.grid._3x3 #preview-grid-horizontal,
@@ -584,22 +584,22 @@
 #preview-grid-vertical {
   border-left: 1px solid white;
   border-right: 1px solid white;
-  position: absolute;
   bottom: 0;
-  top: 0;
   left: 50%;
-  width: 100%;
+  position: absolute;
+  top: 0;
   transform: translateX(-50%);
+  width: 100%;
 }
 
 body._4x4 #preview-grid-vertical:before {
   border-right: 1px solid white;
-  position: absolute;
   bottom: 0;
-  top: 0;
-  left: 0;
-  width: 0;
   content: '';
+  left: 0;
+  position: absolute;
+  top: 0;
+  width: 0;
 }
 
 body.grid._3x3 #preview-grid-vertical,
@@ -638,13 +638,13 @@
 
 #record-time {
   align-items: center;
-  display: flex;
-  justify-content: flex-start;
-  pointer-events: none;
   background-color: rgba(0, 0, 0, 0.2);
   border-radius: 4px;
+  display: flex;
   height: 32px;
+  justify-content: flex-start;
   padding: 0 12px;
+  pointer-events: none;
 }
 
 #record-time[hidden],
@@ -653,25 +653,25 @@
 }
 
 #record-time .icon {
-  flex-shrink: 0;
   background-color: #ea4335;
-  width: 6px;
-  height: 6px;
   border-radius: 50%;
+  flex-shrink: 0;
+  height: 6px;
+  width: 6px;
 }
 
 #record-time #record-time-msg {
-  flex-shrink: 0;
   color: white;
-  font-size: 13px;
+  flex-shrink: 0;
   font-family: 'Roboto', sans-serif;
+  font-size: 13px;
   margin-left: 8px;
 }
 
 #browser .buttons {
+  left: 10px;
   position: fixed;
   top: 10px;
-  left: 10px;
 }
 
 #browser .buttons button {
@@ -695,9 +695,9 @@
 
 #browser div.media-wrapper {
   align-items: center;
-  justify-content: center;
   display: flex;
   flex-shrink: 0;
+  justify-content: center;
   margin: 4%;
   pointer-events: auto;
 }
@@ -749,9 +749,9 @@
 
   /* Set default styles and max-dimensions for printing the browser view. */
   #browser div.media-wrapper.selected {
-    display: block;  /* Allows to keep aspect ratio of children. */
     border: none;
     box-shadow: none;
+    display: block;  /* Allows to keep aspect ratio of children. */
     margin: auto;
     position: absolute;
   }
@@ -773,16 +773,16 @@
   background: rgba(241, 243, 244, 0.8);
   border-radius: 2px;
   color: #202124;
-  font-size: 12px;
   font-family: 'Roboto', sans-serif;
-  line-height: 22px;
-  white-space: nowrap;
-  padding: 0 8px;
+  font-size: 12px;
   left: 0;
+  line-height: 22px;
+  opacity: 0;
+  padding: 0 8px;
   pointer-events: none;
   position: absolute;
   top: 0;
-  opacity: 0;
+  white-space: nowrap;
   z-index: 100;
 }
 
@@ -795,12 +795,12 @@
   background: #1e1e23;
   border-radius: 16px;
   color: white;
-  font-size: 16px;
   font-family: 'Roboto', sans-serif;
+  font-size: 16px;
   line-height: 32px;
+  opacity: 0;
   padding: 0 16px;
   pointer-events: none;
-  opacity: 0;
   z-index: 100;
 }
 
@@ -834,32 +834,32 @@
   background: rgba(0, 0, 0, 0.75);
   display: flex;
   flex-direction: column;
-  position: absolute;
-  top: 0px;
-  left: 0px;
   height: 100%;
+  left: 0px;
   min-width: 360px;
   opacity: 0.9;
+  position: absolute;
+  top: 0px;
 }
 
 .menu-header,
 .menu-item {
   align-items: center;
+  color: #f1f3f4;
   display: flex;
   flex-shrink: 0;
-  justify-content: flex-start;
-  text-align: left;
-  padding: 0 20px 0 24px;
-  height: 64px;
-  color: #f1f3f4;
-  font-size: 13px;
   font-family: 'Roboto', sans-serif;
+  font-size: 13px;
+  height: 64px;
+  justify-content: flex-start;
+  padding: 0 20px 0 24px;
+  text-align: left;
 }
 
 .menu-header {
-  height: 88px;
   color: white;
   font-size: 20px;
+  height: 88px;
 }
 
 .menu-header .icon,
@@ -872,8 +872,8 @@
 }
 
 .menu-item .description {
-  margin-top: 5px;
   color: #bdc1c6;
+  margin-top: 5px;
 }
 
 .menu-item .description span {
@@ -889,25 +889,25 @@
 }
 
 .menu-item input:before {
-  position: absolute;
-  top: 13px;
-  left: 13px;
-  bottom: 13px;
-  right: 13px;
-  box-shadow: 0px 0px 0px 2px #f1f3f4;
   border-radius: 50%;
+  bottom: 13px;
+  box-shadow: 0px 0px 0px 2px #f1f3f4;
   content: '';
+  left: 13px;
+  position: absolute;
+  right: 13px;
+  top: 13px;
 }
 
 .menu-item input:checked:before {
-  top: 12px;
-  left: 12px;
-  bottom: 12px;
-  right: 12px;
-  box-shadow: 0px 0px 0px 1px #f1f3f4;
-  background-color: #f1f3f4;
   background-clip: padding-box;
+  background-color: #f1f3f4;
   border: 4px solid transparent;
+  bottom: 12px;
+  box-shadow: 0px 0px 0px 1px #f1f3f4;
+  left: 12px;
+  right: 12px;
+  top: 12px;
   transition: border-width 100ms ease-in;
 }
 
@@ -955,10 +955,10 @@
 }
 
 #warning #error-msg {
-  text-align: center;
-  font-size: 18px;
   font-family: 'Roboto', sans-serif;
+  font-size: 18px;
   line-height: 32px;
+  text-align: center;
   white-space: pre-wrap;
 }
 
@@ -967,12 +967,12 @@
 }
 
 #dialog-popup {
-  transform: translateY(20px);
   background: white;
+  border-radius: 4px;
   display: flex;
   flex-direction: column;
   padding: 20px;
-  border-radius: 4px;
+  transform: translateY(20px);
   transition: transform 200ms;
 }
 
@@ -981,21 +981,21 @@
 }
 
 #dialog #dialog-msg {
-  user-select: text;  /* Allow copying the message. */
-  cursor: text;
   color: #202124;
-  font-size: 14px;
+  cursor: text;
   font-family: 'Roboto', sans-serif;
+  font-size: 14px;
   max-height: 320px;
   max-width: 472px;
   overflow: auto;
   padding: 20px 0;
+  user-select: text;  /* Allow copying the message. */
   white-space: pre-wrap;
 }
 
 #dialog #dialog-msg::-webkit-scrollbar {
-  width: 6px;
   height: 6px;
+  width: 6px;
 }
 
 #dialog #dialog-msg::-webkit-scrollbar-track {
@@ -1004,8 +1004,8 @@
 
 #dialog #dialog-msg::-webkit-scrollbar-thumb {
   background: gray;
-  width: 6px;
   height: 6px;
+  width: 6px;
 }
 
 #dialog-buttons {
@@ -1017,12 +1017,12 @@
 
 #dialog-buttons button {
   background-color: white;
-  color: #2581df;
-  font-size: 12px;
-  font-family: 'Roboto', sans-serif;
   border-style: solid;
-  padding: 6px 18px;
+  color: #2581df;
+  font-family: 'Roboto', sans-serif;
+  font-size: 12px;
   margin: 4px;
+  padding: 6px 18px;
 }
 
 #dialog-buttons button:focus {
@@ -1037,10 +1037,10 @@
 
 #spinner {
   background-image: url(../images/spinner.svg);
-  width: 32px;
   height: 32px;
-  z-index: 1;
   visibility: hidden;
+  width: 32px;
+  z-index: 1;
 }
 
 body:not(.mode-switching):not(.streaming) #spinner {
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/BUILD.gn b/chrome/browser/resources/chromeos/camera/src/strings/BUILD.gn
new file mode 100644
index 0000000..bfd9210
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/BUILD.gn
@@ -0,0 +1,73 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//chrome/common/features.gni")
+import("//tools/grit/grit_rule.gni")
+
+chrome_camera_app_dir = "$root_out_dir/resources/chromeos/camera"
+
+grit("camera_strings") {
+  source = "camera_strings.grd"
+  defines = chrome_grit_defines
+  outputs = [
+    "_locales/am/messages.json",
+    "_locales/ar/messages.json",
+    "_locales/bg/messages.json",
+    "_locales/bn/messages.json",
+    "_locales/ca/messages.json",
+    "_locales/cs/messages.json",
+    "_locales/da/messages.json",
+    "_locales/de/messages.json",
+    "_locales/el/messages.json",
+    "_locales/en_GB/messages.json",
+    "_locales/en/messages.json",
+    "_locales/es/messages.json",
+    "_locales/es_419/messages.json",
+    "_locales/et/messages.json",
+    "_locales/fa/messages.json",
+    "_locales/fi/messages.json",
+    "_locales/fil/messages.json",
+    "_locales/fr/messages.json",
+    "_locales/gu/messages.json",
+    "_locales/he/messages.json",
+    "_locales/hi/messages.json",
+    "_locales/hr/messages.json",
+    "_locales/hu/messages.json",
+    "_locales/id/messages.json",
+    "_locales/it/messages.json",
+    "_locales/ja/messages.json",
+    "_locales/kn/messages.json",
+    "_locales/ko/messages.json",
+    "_locales/lt/messages.json",
+    "_locales/lv/messages.json",
+    "_locales/ml/messages.json",
+    "_locales/mr/messages.json",
+    "_locales/ms/messages.json",
+    "_locales/nl/messages.json",
+    "_locales/nb/messages.json",
+    "_locales/pl/messages.json",
+    "_locales/pt_BR/messages.json",
+    "_locales/pt_PT/messages.json",
+    "_locales/ro/messages.json",
+    "_locales/ru/messages.json",
+    "_locales/sk/messages.json",
+    "_locales/sl/messages.json",
+    "_locales/sr/messages.json",
+    "_locales/sv/messages.json",
+    "_locales/sw/messages.json",
+    "_locales/ta/messages.json",
+    "_locales/te/messages.json",
+    "_locales/th/messages.json",
+    "_locales/tr/messages.json",
+    "_locales/uk/messages.json",
+    "_locales/vi/messages.json",
+    "_locales/zh_CN/messages.json",
+    "_locales/zh_TW/messages.json",
+  ]
+  output_dir = chrome_camera_app_dir
+
+  # Don't pollute the extension directory with stamp and .d files.
+  depfile_dir = target_out_dir
+  resource_ids = ""
+}
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings.grd b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings.grd
new file mode 100644
index 0000000..ea64d782
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings.grd
@@ -0,0 +1,257 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<grit base_dir="." current_release="1" latest_public_release="0"
+      output_all_resource_defines="false" enc_check="möl" source_lang_id="en">
+  <outputs>
+    <output filename="_locales/am/messages.json" type="chrome_messages_json" lang="am"/>
+    <output filename="_locales/ar/messages.json" type="chrome_messages_json" lang="ar"/>
+    <output filename="_locales/bg/messages.json" type="chrome_messages_json" lang="bg"/>
+    <output filename="_locales/bn/messages.json" type="chrome_messages_json" lang="bn"/>
+    <output filename="_locales/ca/messages.json" type="chrome_messages_json" lang="ca"/>
+    <output filename="_locales/cs/messages.json" type="chrome_messages_json" lang="cs"/>
+    <output filename="_locales/da/messages.json" type="chrome_messages_json" lang="da"/>
+    <output filename="_locales/de/messages.json" type="chrome_messages_json" lang="de"/>
+    <output filename="_locales/el/messages.json" type="chrome_messages_json" lang="el"/>
+    <output filename="_locales/en_GB/messages.json" type="chrome_messages_json" lang="en-GB"/>
+    <output filename="_locales/en/messages.json" type="chrome_messages_json" lang="en"/>
+    <output filename="_locales/es/messages.json" type="chrome_messages_json" lang="es"/>
+    <output filename="_locales/es_419/messages.json" type="chrome_messages_json" lang="es-419"/>
+    <output filename="_locales/et/messages.json" type="chrome_messages_json" lang="et"/>
+    <output filename="_locales/fa/messages.json" type="chrome_messages_json" lang="fa"/>
+    <output filename="_locales/fi/messages.json" type="chrome_messages_json" lang="fi"/>
+    <output filename="_locales/fil/messages.json" type="chrome_messages_json" lang="fil"/>
+    <output filename="_locales/fr/messages.json" type="chrome_messages_json" lang="fr"/>
+    <output filename="_locales/gu/messages.json" type="chrome_messages_json" lang="gu"/>
+    <output filename="_locales/he/messages.json" type="chrome_messages_json" lang="he"/>
+    <output filename="_locales/hi/messages.json" type="chrome_messages_json" lang="hi"/>
+    <output filename="_locales/hr/messages.json" type="chrome_messages_json" lang="hr"/>
+    <output filename="_locales/hu/messages.json" type="chrome_messages_json" lang="hu"/>
+    <output filename="_locales/id/messages.json" type="chrome_messages_json" lang="id"/>
+    <output filename="_locales/it/messages.json" type="chrome_messages_json" lang="it"/>
+    <output filename="_locales/ja/messages.json" type="chrome_messages_json" lang="ja"/>
+    <output filename="_locales/kn/messages.json" type="chrome_messages_json" lang="kn"/>
+    <output filename="_locales/ko/messages.json" type="chrome_messages_json" lang="ko"/>
+    <output filename="_locales/lt/messages.json" type="chrome_messages_json" lang="lt"/>
+    <output filename="_locales/lv/messages.json" type="chrome_messages_json" lang="lv"/>
+    <output filename="_locales/ml/messages.json" type="chrome_messages_json" lang="ml"/>
+    <output filename="_locales/mr/messages.json" type="chrome_messages_json" lang="mr"/>
+    <output filename="_locales/ms/messages.json" type="chrome_messages_json" lang="ms"/>
+    <output filename="_locales/nl/messages.json" type="chrome_messages_json" lang="nl"/>
+    <output filename="_locales/nb/messages.json" type="chrome_messages_json" lang="no"/>
+    <output filename="_locales/pl/messages.json" type="chrome_messages_json" lang="pl"/>
+    <output filename="_locales/pt_BR/messages.json" type="chrome_messages_json" lang="pt-BR"/>
+    <output filename="_locales/pt_PT/messages.json" type="chrome_messages_json" lang="pt-PT"/>
+    <output filename="_locales/ro/messages.json" type="chrome_messages_json" lang="ro"/>
+    <output filename="_locales/ru/messages.json" type="chrome_messages_json" lang="ru"/>
+    <output filename="_locales/sk/messages.json" type="chrome_messages_json" lang="sk"/>
+    <output filename="_locales/sl/messages.json" type="chrome_messages_json" lang="sl"/>
+    <output filename="_locales/sr/messages.json" type="chrome_messages_json" lang="sr"/>
+    <output filename="_locales/sv/messages.json" type="chrome_messages_json" lang="sv"/>
+    <output filename="_locales/sw/messages.json" type="chrome_messages_json" lang="sw"/>
+    <output filename="_locales/ta/messages.json" type="chrome_messages_json" lang="ta"/>
+    <output filename="_locales/te/messages.json" type="chrome_messages_json" lang="te"/>
+    <output filename="_locales/th/messages.json" type="chrome_messages_json" lang="th"/>
+    <output filename="_locales/tr/messages.json" type="chrome_messages_json" lang="tr"/>
+    <output filename="_locales/uk/messages.json" type="chrome_messages_json" lang="uk"/>
+    <output filename="_locales/vi/messages.json" type="chrome_messages_json" lang="vi"/>
+    <output filename="_locales/zh_CN/messages.json" type="chrome_messages_json" lang="zh-CN"/>
+    <output filename="_locales/zh_TW/messages.json" type="chrome_messages_json" lang="zh-TW"/>
+  </outputs>
+  <translations>
+    <file path="camera_strings_am.xtb" lang="am" />
+    <file path="camera_strings_ar.xtb" lang="ar" />
+    <file path="camera_strings_bg.xtb" lang="bg" />
+    <file path="camera_strings_bn.xtb" lang="bn" />
+    <file path="camera_strings_ca.xtb" lang="ca" />
+    <file path="camera_strings_cs.xtb" lang="cs" />
+    <file path="camera_strings_da.xtb" lang="da" />
+    <file path="camera_strings_de.xtb" lang="de" />
+    <file path="camera_strings_el.xtb" lang="el" />
+    <file path="camera_strings_en-GB.xtb" lang="en-GB" />
+    <file path="camera_strings_es.xtb" lang="es" />
+    <file path="camera_strings_es-419.xtb" lang="es-419" />
+    <file path="camera_strings_et.xtb" lang="et" />
+    <file path="camera_strings_fa.xtb" lang="fa" />
+    <file path="camera_strings_fi.xtb" lang="fi" />
+    <file path="camera_strings_fil.xtb" lang="fil" />
+    <file path="camera_strings_fr.xtb" lang="fr" />
+    <file path="camera_strings_gu.xtb" lang="gu" />
+    <file path="camera_strings_hi.xtb" lang="hi" />
+    <file path="camera_strings_hr.xtb" lang="hr" />
+    <file path="camera_strings_hu.xtb" lang="hu" />
+    <file path="camera_strings_id.xtb" lang="id" />
+    <file path="camera_strings_it.xtb" lang="it" />
+    <!-- The translation console uses 'iw' for Hebrew, but we use 'he'. -->
+    <file path="camera_strings_iw.xtb" lang="he" />
+    <file path="camera_strings_ja.xtb" lang="ja" />
+    <file path="camera_strings_kn.xtb" lang="kn" />
+    <file path="camera_strings_ko.xtb" lang="ko" />
+    <file path="camera_strings_lt.xtb" lang="lt" />
+    <file path="camera_strings_lv.xtb" lang="lv" />
+    <file path="camera_strings_ml.xtb" lang="ml" />
+    <file path="camera_strings_mr.xtb" lang="mr" />
+    <file path="camera_strings_ms.xtb" lang="ms" />
+    <file path="camera_strings_nl.xtb" lang="nl" />
+    <file path="camera_strings_no.xtb" lang="no" />
+    <file path="camera_strings_pl.xtb" lang="pl" />
+    <file path="camera_strings_pt-BR.xtb" lang="pt-BR" />
+    <file path="camera_strings_pt-PT.xtb" lang="pt-PT" />
+    <file path="camera_strings_ro.xtb" lang="ro" />
+    <file path="camera_strings_ru.xtb" lang="ru" />
+    <file path="camera_strings_sk.xtb" lang="sk" />
+    <file path="camera_strings_sl.xtb" lang="sl" />
+    <file path="camera_strings_sr.xtb" lang="sr" />
+    <file path="camera_strings_sv.xtb" lang="sv" />
+    <file path="camera_strings_sw.xtb" lang="sw" />
+    <file path="camera_strings_ta.xtb" lang="ta" />
+    <file path="camera_strings_te.xtb" lang="te" />
+    <file path="camera_strings_th.xtb" lang="th" />
+    <file path="camera_strings_tr.xtb" lang="tr" />
+    <file path="camera_strings_uk.xtb" lang="uk" />
+    <file path="camera_strings_vi.xtb" lang="vi" />
+    <file path="camera_strings_zh-CN.xtb" lang="zh-CN" />
+    <file path="camera_strings_zh-TW.xtb" lang="zh-TW" />
+  </translations>
+  <release allow_pseudo="false" seq="1">
+    <messages fallback_to_english="true">
+      <message desc="Label for timer-duration: 10 seconds." name="IDS_LABEL_TIMER_10S">
+        10 seconds
+      </message>
+      <message desc="Label for the help button." name="IDS_HELP_BUTTON">
+        Help
+      </message>
+      <message desc="Label for the dismissing button in the dialog." name="IDS_DIALOG_CANCEL_BUTTON">
+        Cancel
+      </message>
+      <message desc="Label for the printing button." name="IDS_PRINT_BUTTON">
+        Print
+      </message>
+      <message desc="Label for the gallery button." name="IDS_GALLERY_BUTTON">
+        Go to gallery
+      </message>
+      <message desc="Error message shown when saving/adding a image or video to the file system failed." name="IDS_ERROR_MSG_SAVE_FILE_FAILED">
+        Unable to save the file
+      </message>
+      <message desc="Label for the exporting button." name="IDS_EXPORT_BUTTON">
+        Export to disk
+      </message>
+      <message desc="Label for the checkbox to toggle the countdown timer." name="IDS_TOGGLE_TIMER_BUTTON">
+        Timer
+      </message>
+      <message desc="Message shown before moving all photos and videos stored in the Camera App to the Downloads folder." name="IDS_MIGRATE_PICTURES_MSG">
+        Photos and videos taken with the camera will be moved to the Downloads folder. You can access them in Files.
+
+        Apps with storage permissions will have access to your photos and videos.
+      </message>
+      <message desc="Label for spoken feedback to read out grid-type: 3x3." name="IDS_ARIA_GRID_3X3">
+        3 by 3
+      </message>
+      <message desc="Label for the shutter button to start recording." name="IDS_RECORD_VIDEO_START_BUTTON">
+        Start recording
+      </message>
+      <message desc="Label for the shutter button to stop recording." name="IDS_RECORD_VIDEO_STOP_BUTTON">
+        Stop recording
+      </message>
+      <message desc="Label for the button to switch to next connected camera. Eg. switching from front facing to back facing, or a camera connected via USB. The button is shown only when more than one camera is connected." name="IDS_SWITCH_CAMERA_BUTTON">
+        Switch to next camera
+      </message>
+      <message desc="Label for the button of timer-duration options." name="IDS_TIMER_DURATION_BUTTON">
+        Timer duration
+      </message>
+      <message desc="Label for timer-duration: 3 seconds." name="IDS_LABEL_TIMER_3S">
+        3 seconds
+      </message>
+      <message desc="Status message for spoken feedback when video recording has been stopped." name="IDS_STATUS_MSG_RECORDING_STOPPED">
+        Recording stopped
+      </message>
+      <message desc="Label for spoken feedback to read out grid-type: 4x4." name="IDS_ARIA_GRID_4X4">
+        4 by 4
+      </message>
+      <message desc="Label for for grid-type: 4x4." name="IDS_LABEL_GRID_4X4">
+        4 x 4
+      </message>
+      <message desc="Confirmation message before removing a selected item from the gallery. Expects a file name to be passed in." name="IDS_DELETE_CONFIRMATION_MSG">
+        Do you really want to remove <ph name="file">$1<ex>IMG_20160520_000000.jpg</ex></ph>?
+      </message>
+      <message desc="Label for the button to switch to take photo mode." name="IDS_SWITCH_TAKE_PHOTO_BUTTON">
+        Switch to take photo
+      </message>
+      <message desc="Label for the checkbox to toggle the grid shown on preview." name="IDS_TOGGLE_GRID_BUTTON">
+        Grid
+      </message>
+      <message desc="Label for the shutter button to take photo." name="IDS_TAKE_PHOTO_BUTTON">
+        Take photo
+      </message>
+      <message desc="Error message shown when it was impossible to connect to the camera due to unavailability." name="IDS_ERROR_MSG_NO_CAMERA">
+        Your camera is currently unavailable.
+        Please check if the camera is properly connected.
+      </message>
+      <message desc="Error message shown when failing to start recording video." name="IDS_ERROR_MSG_RECORD_START_FAILED">
+        Unable to start recording
+      </message>
+      <message desc="Label for the checkbox to toggle the microphone for recording video." name="IDS_TOGGLE_MIC_BUTTON">
+        Microphone
+      </message>
+      <message desc="Label for the feedback button." name="IDS_FEEDBACK_BUTTON">
+        Send feedback
+      </message>
+      <message desc="Error message shown when failing to take photo." name="IDS_ERROR_MSG_TAKE_PHOTO_FAILED">
+        Unable to take photo
+      </message>
+      <message desc="Short description of the Camera App." name="IDS_DESCRIPTION">
+        Take photos and record videos with your camera.
+      </message>
+      <message desc="Label for for grid-type: 3x3." name="IDS_LABEL_GRID_3X3">
+        3 x 3
+      </message>
+      <message desc="Label for the shutter button to cancel countdown timer and stop taking photo." name="IDS_TAKE_PHOTO_CANCEL_BUTTON">
+        Stop taking photo
+      </message>
+      <message desc="Label for the delete button." name="IDS_DELETE_BUTTON">
+        Delete
+      </message>
+      <message desc="Error message shown when no data is recorded for a recording and it won't be added to the gallery." name="IDS_ERROR_MSG_EMPTY_RECORDING">
+        Nothing recorded
+      </message>
+      <message desc="Confirmation message before removing multiple selected items from the gallery. Expects a file count to be passed in." name="IDS_DELETE_MULTI_CONFIRMATION_MSG">
+        Do you really want to remove <ph name="count">$1<ex>20</ex></ph> items?
+      </message>
+      <message desc="Text to speech label for the gallery list." name="IDS_GALLERY_IMAGES">
+        Gallery images
+      </message>
+      <message desc="Error message shown when exporting to an external directory failed. Expects a file name to be passed in." name="IDS_ERROR_MSG_GALLERY_EXPORT_FAILED">
+        Unable to export <ph name="file">$1<ex>IMG_20160520_000000.jpg</ex></ph>
+      </message>
+      <message desc="Status message for spoken feedback when switching over to another camera." name="IDS_STATUS_MSG_CAMERA_SWITCHED">
+        <ph name="camera">$1<ex>USB WebCam (12ab:5678) User-facing</ex></ph> active
+      </message>
+      <message desc="Name of the Camera App." name="IDS_NAME">
+        Camera
+      </message>
+      <message desc="Error message shown when failing to read or write the file system." name="IDS_ERROR_MSG_FILE_SYSTEM_FAILED">
+        File system errors.
+      </message>
+      <message desc="Label for the settings button." name="IDS_SETTINGS_BUTTON">
+        Settings
+      </message>
+      <message desc="Label for the accepting button in the dialog." name="IDS_DIALOG_OK_BUTTON">
+        OK
+      </message>
+      <message desc="Label for grid-type: golden ratio." name="IDS_LABEL_GRID_GOLDEN">
+        Golden ratio
+      </message>
+      <message desc="Label for the button to switch to record video mode." name="IDS_SWITCH_RECORD_VIDEO_BUTTON">
+        Switch to record video
+      </message>
+      <message desc="Label for the checkbox to toggle the preview mirroring. Eg. if mirroring is toggled on, preview will be flipped horizontally." name="IDS_TOGGLE_MIRROR_BUTTON">
+        Mirroring
+      </message>
+      <message desc="Label for the button of grid-type options." name="IDS_GRID_TYPE_BUTTON">
+        Grid type
+      </message>
+      <message desc="Label for the back button." name="IDS_BACK_BUTTON">
+        Go back
+      </message>
+    </messages>
+  </release>
+</grit>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_am.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_am.xtb
new file mode 100644
index 0000000..1a8356cf
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_am.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="am"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ar.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ar.xtb
new file mode 100644
index 0000000..577c15f
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ar.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ar"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_bg.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_bg.xtb
new file mode 100644
index 0000000..25d06b3
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_bg.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="bg"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_bn.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_bn.xtb
new file mode 100644
index 0000000..b02b1e7f
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_bn.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="bn"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ca.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ca.xtb
new file mode 100644
index 0000000..1e9d243
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ca.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ca"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_cs.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_cs.xtb
new file mode 100644
index 0000000..b6103f7
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_cs.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="cs"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_da.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_da.xtb
new file mode 100644
index 0000000..6feffd32
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_da.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="da"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_de.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_de.xtb
new file mode 100644
index 0000000..d2908e8a
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_de.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="de"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_el.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_el.xtb
new file mode 100644
index 0000000..2d96e6c
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_el.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="el"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_en-GB.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_en-GB.xtb
new file mode 100644
index 0000000..769a524
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_en-GB.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="en-GB"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_es-419.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_es-419.xtb
new file mode 100644
index 0000000..37258dd
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_es-419.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="es-419"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_es.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_es.xtb
new file mode 100644
index 0000000..27d8ca3
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_es.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="es"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_et.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_et.xtb
new file mode 100644
index 0000000..a14139f
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_et.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="et"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_fa.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_fa.xtb
new file mode 100644
index 0000000..41bc8c38
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_fa.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fa"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_fi.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_fi.xtb
new file mode 100644
index 0000000..b2ed2bf
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_fi.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fi"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_fil.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_fil.xtb
new file mode 100644
index 0000000..6ca565417
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_fil.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fil"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_fr.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_fr.xtb
new file mode 100644
index 0000000..1ce4293c
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_fr.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="fr"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_gu.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_gu.xtb
new file mode 100644
index 0000000..1b8a058
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_gu.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="gu"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_hi.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_hi.xtb
new file mode 100644
index 0000000..e9f9cc54
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_hi.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="hi"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_hr.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_hr.xtb
new file mode 100644
index 0000000..abb82688
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_hr.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="hr"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_hu.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_hu.xtb
new file mode 100644
index 0000000..5a7e2c9
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_hu.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="hu"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_id.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_id.xtb
new file mode 100644
index 0000000..bced312
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_id.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="id"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_it.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_it.xtb
new file mode 100644
index 0000000..d56be5c
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_it.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="it"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_iw.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_iw.xtb
new file mode 100644
index 0000000..d17d24e
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_iw.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="iw"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ja.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ja.xtb
new file mode 100644
index 0000000..c5828bf2
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ja.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ja"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_kn.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_kn.xtb
new file mode 100644
index 0000000..6f2561a
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_kn.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="kn"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ko.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ko.xtb
new file mode 100644
index 0000000..aac09f4
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ko.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ko"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_lt.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_lt.xtb
new file mode 100644
index 0000000..e386c81b
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_lt.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="lt"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_lv.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_lv.xtb
new file mode 100644
index 0000000..c27c4065
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_lv.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="lv"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ml.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ml.xtb
new file mode 100644
index 0000000..970244e
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ml.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ml"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_mr.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_mr.xtb
new file mode 100644
index 0000000..7eb198d
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_mr.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="mr"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ms.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ms.xtb
new file mode 100644
index 0000000..b8f88eb5
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ms.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ms"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_nl.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_nl.xtb
new file mode 100644
index 0000000..08c20249
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_nl.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="nl"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_no.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_no.xtb
new file mode 100644
index 0000000..52b6011
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_no.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="no"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_pl.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_pl.xtb
new file mode 100644
index 0000000..57c76f6
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_pl.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="pl"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_pt-BR.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_pt-BR.xtb
new file mode 100644
index 0000000..1ccc1be
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_pt-BR.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="pt-BR"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_pt-PT.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_pt-PT.xtb
new file mode 100644
index 0000000..448ac9de
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_pt-PT.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="pt-PT"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ro.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ro.xtb
new file mode 100644
index 0000000..ee107e4
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ro.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ro"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ru.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ru.xtb
new file mode 100644
index 0000000..1161eea
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ru.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ru"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sk.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sk.xtb
new file mode 100644
index 0000000..285c7cb
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sk.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sk"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sl.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sl.xtb
new file mode 100644
index 0000000..5b943686
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sl.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sl"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sr.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sr.xtb
new file mode 100644
index 0000000..037a5c14
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sr.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sr"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sv.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sv.xtb
new file mode 100644
index 0000000..8f4581fd
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sv.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sv"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sw.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sw.xtb
new file mode 100644
index 0000000..0b25c33
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_sw.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="sw"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ta.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ta.xtb
new file mode 100644
index 0000000..ab9e8aab
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_ta.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="ta"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_te.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_te.xtb
new file mode 100644
index 0000000..c35f476
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_te.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="te"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_th.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_th.xtb
new file mode 100644
index 0000000..5d81291
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_th.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="th"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_tr.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_tr.xtb
new file mode 100644
index 0000000..ead1d39
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_tr.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="tr"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_uk.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_uk.xtb
new file mode 100644
index 0000000..29134e1
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_uk.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="uk"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_vi.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_vi.xtb
new file mode 100644
index 0000000..d858f99
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_vi.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="vi"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_zh-CN.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_zh-CN.xtb
new file mode 100644
index 0000000..effe01e
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_zh-CN.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="zh-CN"></translationbundle>
diff --git a/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_zh-TW.xtb b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_zh-TW.xtb
new file mode 100644
index 0000000..cb82bea
--- /dev/null
+++ b/chrome/browser/resources/chromeos/camera/src/strings/camera_strings_zh-TW.xtb
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE translationbundle><translationbundle lang="zh-TW"></translationbundle>
diff --git a/chrome/browser/sync/profile_sync_service_android.cc b/chrome/browser/sync/profile_sync_service_android.cc
index 00361ea..0f265da 100644
--- a/chrome/browser/sync/profile_sync_service_android.cc
+++ b/chrome/browser/sync/profile_sync_service_android.cc
@@ -20,6 +20,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/sync/device_info_sync_service_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/session_sync_service_factory.h"
 #include "chrome/browser/sync/sync_ui_util.h"
@@ -31,6 +32,9 @@
 #include "components/strings/grit/components_strings.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/pref_names.h"
+#include "components/sync/device_info/device_info.h"
+#include "components/sync/device_info/device_info_sync_service.h"
+#include "components/sync/device_info/device_info_tracker.h"
 #include "components/sync/driver/about_sync_util.h"
 #include "components/sync/driver/sync_service_utils.h"
 #include "components/sync/engine/net/network_resources.h"
@@ -50,6 +54,7 @@
 using base::android::ScopedJavaLocalRef;
 using browser_sync::ProfileSyncService;
 using content::BrowserThread;
+using syncer::DeviceInfo;
 using unified_consent::UrlKeyedDataCollectionConsentHelper;
 
 namespace {
@@ -461,6 +466,20 @@
                                       base::ASCIIToUTF16(sync_username)));
 }
 
+jint ProfileSyncServiceAndroid::GetNumberOfSyncedDevices(
+    JNIEnv* env,
+    const JavaParamRef<jobject>&) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  syncer::DeviceInfoSyncService* device_sync_service =
+      DeviceInfoSyncServiceFactory::GetForProfile(profile_);
+  if (!device_sync_service) {
+    return 0;
+  }
+  const std::vector<std::unique_ptr<DeviceInfo>>& all_devices =
+      device_sync_service->GetDeviceInfoTracker()->GetAllDeviceInfo();
+  return all_devices.size();
+}
+
 ScopedJavaLocalRef<jstring>
 ProfileSyncServiceAndroid::GetSyncEnterCustomPassphraseBodyText(
     JNIEnv* env,
diff --git a/chrome/browser/sync/profile_sync_service_android.h b/chrome/browser/sync/profile_sync_service_android.h
index e1264184..de3c09f 100644
--- a/chrome/browser/sync/profile_sync_service_android.h
+++ b/chrome/browser/sync/profile_sync_service_android.h
@@ -147,6 +147,10 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj);
 
+  jint GetNumberOfSyncedDevices(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
+
   // UI string getters.
 
   base::android::ScopedJavaLocalRef<jstring>
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
index ffe8502..9c805c44 100644
--- a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
+++ b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
@@ -238,9 +238,7 @@
 
   // The uninstall flow removes all apps before setting the CrostiniEnabled pref
   // to false, so we need to do that explicitly too.
-  RegistryService()->ClearApplicationList(
-      crostini::kCrostiniDefaultVmName,
-      crostini::kCrostiniDefaultContainerName);
+  RegistryService()->ClearApplicationList(crostini::kCrostiniDefaultVmName);
   CrostiniTestHelper::DisableCrostini(profile());
   // Root folder is left. We rely on default handling of empty folder.
   EXPECT_EQ(1u, model_updater_->ItemCount());
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller_unittest.cc b/chrome/browser/ui/extensions/extension_action_view_controller_unittest.cc
index 7fb307d..ad7fa32 100644
--- a/chrome/browser/ui/extensions/extension_action_view_controller_unittest.cc
+++ b/chrome/browser/ui/extensions/extension_action_view_controller_unittest.cc
@@ -407,9 +407,9 @@
   RunGrayscaleTest(PermissionType::kScriptableHost);
 }
 
-INSTANTIATE_TEST_CASE_P(,
-                        ExtensionActionViewControllerGrayscaleTest,
-                        testing::Values(false, true));
+INSTANTIATE_TEST_SUITE_P(,
+                         ExtensionActionViewControllerGrayscaleTest,
+                         testing::Values(false, true));
 
 TEST_P(ToolbarActionsBarUnitTest, RuntimeHostsTooltip) {
   base::test::ScopedFeatureList feature_list;
diff --git a/chrome/browser/ui/views/crostini/crostini_installer_view.cc b/chrome/browser/ui/views/crostini/crostini_installer_view.cc
index acfef6cd..d7c16a36e 100644
--- a/chrome/browser/ui/views/crostini/crostini_installer_view.cc
+++ b/chrome/browser/ui/views/crostini/crostini_installer_view.cc
@@ -203,7 +203,6 @@
               base::Unretained(
                   crostini::CrostiniManager::GetForProfile(profile_)),
               crostini::kCrostiniDefaultVmName,
-              crostini::kCrostiniDefaultContainerName,
               base::BindOnce(&CrostiniInstallerView::FinishCleanup,
                              weak_ptr_factory_.GetWeakPtr())));
       UpdateState(State::CLEANUP);
diff --git a/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc b/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc
index 625e107..d728ada 100644
--- a/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc
+++ b/chrome/browser/ui/views/crostini/crostini_uninstaller_view.cc
@@ -87,7 +87,7 @@
 
   // Kick off the Crostini Remove sequence.
   crostini::CrostiniManager::GetForProfile(profile_)->RemoveCrostini(
-      crostini::kCrostiniDefaultVmName, crostini::kCrostiniDefaultContainerName,
+      crostini::kCrostiniDefaultVmName,
       base::BindOnce(&CrostiniUninstallerView::UninstallCrostiniFinished,
                      weak_ptr_factory_.GetWeakPtr()));
 
diff --git a/chrome/browser/ui/webui/certificates_handler.cc b/chrome/browser/ui/webui/certificates_handler.cc
index d3120861..f0d1c1f 100644
--- a/chrome/browser/ui/webui/certificates_handler.cc
+++ b/chrome/browser/ui/webui/certificates_handler.cc
@@ -14,7 +14,6 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/containers/id_map.h"
 #include "base/files/file_util.h"  // for FileAccessProvider
 #include "base/i18n/string_compare.h"
 #include "base/macros.h"
@@ -173,62 +172,6 @@
 namespace certificate_manager {
 
 ///////////////////////////////////////////////////////////////////////////////
-//  CertIdMap
-
-class CertIdMap {
- public:
-  CertIdMap() {}
-  ~CertIdMap() {}
-
-  std::string CertToId(CERTCertificate* cert);
-  CERTCertificate* IdToCert(const std::string& id);
-  CERTCertificate* CallbackArgsToCert(const base::ListValue* args);
-
- private:
-  typedef std::map<CERTCertificate*, int32_t> CertMap;
-
-  // Creates an ID for cert and looks up the cert for an ID.
-  base::IDMap<net::ScopedCERTCertificate> id_map_;
-
-  // Finds the ID for a cert.
-  CertMap cert_map_;
-
-  DISALLOW_COPY_AND_ASSIGN(CertIdMap);
-};
-
-std::string CertIdMap::CertToId(CERTCertificate* cert) {
-  CertMap::const_iterator iter = cert_map_.find(cert);
-  if (iter != cert_map_.end())
-    return base::IntToString(iter->second);
-
-  int32_t new_id = id_map_.Add(net::x509_util::DupCERTCertificate(cert));
-  cert_map_[cert] = new_id;
-  return base::IntToString(new_id);
-}
-
-CERTCertificate* CertIdMap::IdToCert(const std::string& id) {
-  int32_t cert_id = 0;
-  if (!base::StringToInt(id, &cert_id))
-    return nullptr;
-
-  return id_map_.Lookup(cert_id);
-}
-
-CERTCertificate* CertIdMap::CallbackArgsToCert(const base::ListValue* args) {
-  std::string node_id;
-  if (!args->GetString(0, &node_id))
-    return nullptr;
-
-  CERTCertificate* cert = IdToCert(node_id);
-  if (!cert) {
-    NOTREACHED();
-    return nullptr;
-  }
-
-  return cert;
-}
-
-///////////////////////////////////////////////////////////////////////////////
 //  FileAccessProvider
 
 // TODO(mattm): Move to some shared location?
@@ -328,8 +271,7 @@
 CertificatesHandler::CertificatesHandler()
     : requested_certificate_manager_model_(false),
       use_hardware_backed_(false),
-      file_access_provider_(new FileAccessProvider()),
-      cert_id_map_(new CertIdMap),
+      file_access_provider_(base::MakeRefCounted<FileAccessProvider>()),
       weak_ptr_factory_(this) {}
 
 CertificatesHandler::~CertificatesHandler() {}
@@ -447,12 +389,12 @@
 }
 
 void CertificatesHandler::HandleViewCertificate(const base::ListValue* args) {
-  CERTCertificate* cert = cert_id_map_->CallbackArgsToCert(args);
-  if (!cert)
+  CertificateManagerModel::CertInfo* cert_info =
+      GetCertInfoFromCallbackArgs(*args, 0 /* arg_index */);
+  if (!cert_info)
     return;
-
   net::ScopedCERTCertificateList certs;
-  certs.push_back(net::x509_util::DupCERTCertificate(cert));
+  certs.push_back(net::x509_util::DupCERTCertificate(cert_info->cert()));
   CertificateViewerDialog::ShowConstrained(
       std::move(certs), web_ui()->GetWebContents(), GetParentWindow());
 }
@@ -468,14 +410,15 @@
 
   CHECK_EQ(2U, args->GetSize());
   AssignWebUICallbackId(args);
-  std::string node_id;
-  CHECK(args->GetString(1, &node_id));
 
-  CERTCertificate* cert = cert_id_map_->IdToCert(node_id);
-  CHECK(cert);
+  CertificateManagerModel::CertInfo* cert_info =
+      GetCertInfoFromCallbackArgs(*args, 1 /* arg_index */);
+  if (!cert_info)
+    return;
 
   net::NSSCertDatabase::TrustBits trust_bits =
-      certificate_manager_model_->cert_db()->GetCertTrust(cert, net::CA_CERT);
+      certificate_manager_model_->cert_db()->GetCertTrust(cert_info->cert(),
+                                                          net::CA_CERT);
   std::unique_ptr<base::DictionaryValue> ca_trust_info(
       new base::DictionaryValue);
   ca_trust_info->SetBoolean(
@@ -493,11 +436,11 @@
 void CertificatesHandler::HandleEditCATrust(const base::ListValue* args) {
   CHECK_EQ(5U, args->GetSize());
   AssignWebUICallbackId(args);
-  std::string node_id;
-  CHECK(args->GetString(1, &node_id));
 
-  CERTCertificate* cert = cert_id_map_->IdToCert(node_id);
-  CHECK(cert);
+  CertificateManagerModel::CertInfo* cert_info =
+      GetCertInfoFromCallbackArgs(*args, 1 /* arg_index */);
+  if (!cert_info)
+    return;
 
   bool trust_ssl = false;
   bool trust_email = false;
@@ -507,7 +450,7 @@
   CHECK(args->GetBoolean(4, &trust_obj_sign));
 
   bool result = certificate_manager_model_->SetCertTrust(
-      cert, net::CA_CERT,
+      cert_info->cert(), net::CA_CERT,
       trust_ssl * net::NSSCertDatabase::TRUSTED_SSL +
           trust_email * net::NSSCertDatabase::TRUSTED_EMAIL +
           trust_obj_sign * net::NSSCertDatabase::TRUSTED_OBJ_SIGN);
@@ -526,12 +469,14 @@
 void CertificatesHandler::HandleExportPersonal(const base::ListValue* args) {
   CHECK_EQ(2U, args->GetSize());
   AssignWebUICallbackId(args);
-  std::string node_id;
-  CHECK(args->GetString(1, &node_id));
 
-  CERTCertificate* cert = cert_id_map_->IdToCert(node_id);
-  CHECK(cert);
-  selected_cert_list_.push_back(net::x509_util::DupCERTCertificate(cert));
+  CertificateManagerModel::CertInfo* cert_info =
+      GetCertInfoFromCallbackArgs(*args, 1 /* arg_index */);
+  if (!cert_info)
+    return;
+
+  selected_cert_list_.push_back(
+      net::x509_util::DupCERTCertificate(cert_info->cert()));
 
   ui::SelectFileDialog::FileTypeInfo file_type_info;
   file_type_info.extensions.resize(1);
@@ -934,11 +879,13 @@
 }
 
 void CertificatesHandler::HandleExportCertificate(const base::ListValue* args) {
-  CERTCertificate* cert = cert_id_map_->CallbackArgsToCert(args);
-  if (!cert)
+  CertificateManagerModel::CertInfo* cert_info =
+      GetCertInfoFromCallbackArgs(*args, 0 /* arg_index */);
+  if (!cert_info)
     return;
+
   net::ScopedCERTCertificateList export_certs;
-  export_certs.push_back(net::x509_util::DupCERTCertificate(cert));
+  export_certs.push_back(net::x509_util::DupCERTCertificate(cert_info->cert()));
   ShowCertExportDialog(web_ui()->GetWebContents(), GetParentWindow(),
                        export_certs.begin(), export_certs.end());
 }
@@ -946,13 +893,13 @@
 void CertificatesHandler::HandleDeleteCertificate(const base::ListValue* args) {
   CHECK_EQ(2U, args->GetSize());
   AssignWebUICallbackId(args);
-  std::string node_id;
-  CHECK(args->GetString(1, &node_id));
 
-  CERTCertificate* cert = cert_id_map_->IdToCert(node_id);
-  CHECK(cert);
+  CertificateManagerModel::CertInfo* cert_info =
+      GetCertInfoFromCallbackArgs(*args, 1 /* arg_index */);
+  if (!cert_info)
+    return;
 
-  bool result = certificate_manager_model_->Delete(cert);
+  bool result = certificate_manager_model_->Delete(cert_info->cert());
   if (!result) {
     // TODO(mattm): better error messages?
     RejectCallbackWithError(
@@ -1023,7 +970,7 @@
                                                            &org_grouping_map);
 
   base::ListValue nodes;
-  for (const auto& org_grouping_map_entry : org_grouping_map) {
+  for (auto& org_grouping_map_entry : org_grouping_map) {
     // Populate first level (org name).
     base::DictionaryValue org_dict;
     org_dict.SetKey(kCertificatesHandlerKeyField,
@@ -1034,33 +981,36 @@
     // Populate second level (certs).
     base::ListValue subnodes;
     bool contains_policy_certs = false;
-    for (const auto& org_cert : org_grouping_map_entry.second) {
+    for (auto& org_cert : org_grouping_map_entry.second) {
+      // Move the CertInfo into |cert_info_id_map_|.
+      CertificateManagerModel::CertInfo* cert_info = org_cert.get();
+      std::string id =
+          base::IntToString(cert_info_id_map_.Add(std::move(org_cert)));
+
       base::DictionaryValue cert_dict;
-      CERTCertificate* cert = org_cert->cert();
-      cert_dict.SetKey(kCertificatesHandlerKeyField,
-                       base::Value(cert_id_map_->CertToId(cert)));
+      cert_dict.SetKey(kCertificatesHandlerKeyField, base::Value(id));
       cert_dict.SetKey(kCertificatesHandlerNameField,
-                       base::Value(org_cert->name()));
+                       base::Value(cert_info->name()));
       cert_dict.SetKey(kCertificatesHandlerReadonlyField,
-                       base::Value(org_cert->read_only()));
+                       base::Value(cert_info->read_only()));
       cert_dict.SetKey(kCertificatesHandlerUntrustedField,
-                       base::Value(org_cert->untrusted()));
+                       base::Value(cert_info->untrusted()));
       cert_dict.SetKey(
           kCertificatesHandlerPolicyInstalledField,
-          base::Value(org_cert->source() ==
+          base::Value(cert_info->source() ==
                       CertificateManagerModel::CertInfo::Source::kPolicy));
       cert_dict.SetKey(kCertificatesHandlerWebTrustAnchorField,
-                       base::Value(org_cert->web_trust_anchor()));
+                       base::Value(cert_info->web_trust_anchor()));
       // TODO(hshi): This should be determined by testing for PKCS #11
       // CKA_EXTRACTABLE attribute. We may need to use the NSS function
       // PK11_ReadRawAttribute to do that.
       cert_dict.SetKey(kCertificatesHandlerExtractableField,
-                       base::Value(!org_cert->hardware_backed()));
+                       base::Value(!cert_info->hardware_backed()));
       // TODO(mattm): Other columns.
       subnodes.GetList().push_back(std::move(cert_dict));
 
       contains_policy_certs |=
-          org_cert->source() ==
+          cert_info->source() ==
           CertificateManagerModel::CertInfo::Source::kPolicy;
     }
     std::sort(subnodes.GetList().begin(), subnodes.GetList().end(), comparator);
@@ -1137,4 +1087,22 @@
   return web_ui()->GetWebContents()->GetTopLevelNativeWindow();
 }
 
+CertificateManagerModel::CertInfo*
+CertificatesHandler::GetCertInfoFromCallbackArgs(const base::Value& args,
+                                                 size_t arg_index) {
+  if (!args.is_list())
+    return nullptr;
+  if (arg_index >= args.GetList().size())
+    return nullptr;
+  const auto& arg = args.GetList()[arg_index];
+  if (!arg.is_string())
+    return nullptr;
+
+  int32_t cert_info_id = 0;
+  if (!base::StringToInt(arg.GetString(), &cert_info_id))
+    return nullptr;
+
+  return cert_info_id_map_.Lookup(cert_info_id);
+}
+
 }  // namespace certificate_manager
diff --git a/chrome/browser/ui/webui/certificates_handler.h b/chrome/browser/ui/webui/certificates_handler.h
index 3e69636..d7d4a83 100644
--- a/chrome/browser/ui/webui/certificates_handler.h
+++ b/chrome/browser/ui/webui/certificates_handler.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/compiler_specific.h"
+#include "base/containers/id_map.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/task/cancelable_task_tracker.h"
@@ -20,7 +21,6 @@
 
 namespace certificate_manager {
 
-class CertIdMap;
 class FileAccessProvider;
 
 class CertificatesHandler : public content::WebUIMessageHandler,
@@ -156,6 +156,13 @@
 
   gfx::NativeWindow GetParentWindow() const;
 
+  // Assuming that |args| is a list, parses the list element at |arg_index| as
+  // an id for |cert_info_id_map_| and looks up the corresponding CertInfo. If
+  // anything goes wrong, returns nullptr.
+  CertificateManagerModel::CertInfo* GetCertInfoFromCallbackArgs(
+      const base::Value& args,
+      size_t arg_index);
+
   // The Certificates Manager model
   bool requested_certificate_manager_model_;
   std::unique_ptr<CertificateManagerModel> certificate_manager_model_;
@@ -178,7 +185,8 @@
   base::CancelableTaskTracker tracker_;
   scoped_refptr<FileAccessProvider> file_access_provider_;
 
-  std::unique_ptr<CertIdMap> cert_id_map_;
+  base::IDMap<std::unique_ptr<CertificateManagerModel::CertInfo>>
+      cert_info_id_map_;
 
   base::WeakPtrFactory<CertificatesHandler> weak_ptr_factory_;
 
diff --git a/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
index 674dd92..d76d588a 100644
--- a/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
@@ -120,6 +120,7 @@
   auto pending_app_manager = std::make_unique<TestPendingAppManager>();
   WebAppPolicyManager web_app_policy_manager(profile(),
                                              pending_app_manager.get());
+  web_app_policy_manager.Start();
   base::RunLoop().RunUntilIdle();
 
   const auto& apps_to_install = pending_app_manager->install_requests();
@@ -133,6 +134,7 @@
   auto pending_app_manager = std::make_unique<TestPendingAppManager>();
   WebAppPolicyManager web_app_policy_manager(profile(),
                                              pending_app_manager.get());
+  web_app_policy_manager.Start();
   base::RunLoop().RunUntilIdle();
 
   const auto& apps_to_install = pending_app_manager->install_requests();
@@ -149,6 +151,7 @@
   auto pending_app_manager = std::make_unique<TestPendingAppManager>();
   WebAppPolicyManager web_app_policy_manager(profile(),
                                              pending_app_manager.get());
+  web_app_policy_manager.Start();
   base::RunLoop().RunUntilIdle();
 
   const auto& apps_to_install = pending_app_manager->install_requests();
@@ -168,6 +171,7 @@
   auto pending_app_manager = std::make_unique<TestPendingAppManager>();
   WebAppPolicyManager web_app_policy_manager(profile(),
                                              pending_app_manager.get());
+  web_app_policy_manager.Start();
   base::RunLoop().RunUntilIdle();
 
   const auto& apps_to_install = pending_app_manager->install_requests();
@@ -187,6 +191,7 @@
   auto pending_app_manager = std::make_unique<TestPendingAppManager>();
   WebAppPolicyManager web_app_policy_manager(profile(),
                                              pending_app_manager.get());
+  web_app_policy_manager.Start();
   base::RunLoop().RunUntilIdle();
 
   const auto& apps_to_install = pending_app_manager->install_requests();
@@ -229,6 +234,7 @@
 
   WebAppPolicyManager web_app_policy_manager(profile(),
                                              pending_app_manager.get());
+  web_app_policy_manager.Start();
   base::RunLoop().RunUntilIdle();
 
   // We should only try to install the app in the policy.
@@ -247,6 +253,7 @@
   auto pending_app_manager = std::make_unique<TestPendingAppManager>();
   WebAppPolicyManager web_app_policy_manager(profile(),
                                              pending_app_manager.get());
+  web_app_policy_manager.Start();
   base::RunLoop().RunUntilIdle();
 
   // Add two sites, one that opens in a window and one that opens in a tab.
diff --git a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
index 570fb32..2ab4890 100644
--- a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
@@ -149,8 +149,8 @@
     return nullptr;
 
   auto provider = std::make_unique<TestWebAppProvider>(profile);
-  // Create all real subsystems:
-  provider->CreateSubsystems();
+  // Create all real subsystems but do not start them:
+  provider->Init();
 
   // But override SystemWebAppManager with TestSystemWebAppManager:
   DCHECK(!test_system_web_app_manager_);
@@ -163,8 +163,8 @@
   system_apps.emplace_back(GURL("chrome://test-system-app/pwa.html"));
   test_system_web_app_manager_->SetSystemApps(std::move(system_apps));
 
-  // Initialize all subsystems:
-  provider->Init();
+  // Start all subsystems:
+  provider->Start();
 
   return provider;
 }
diff --git a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_unittest.cc
index ef00b05..7794c61 100644
--- a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_unittest.cc
@@ -99,7 +99,7 @@
   TestSystemWebAppManager system_web_app_manager(profile(),
                                                  pending_app_manager.get());
   system_web_app_manager.SetSystemApps(std::move(system_apps));
-  system_web_app_manager.Init();
+  system_web_app_manager.Start();
 
   base::RunLoop().RunUntilIdle();
 
@@ -122,7 +122,7 @@
   TestSystemWebAppManager system_web_app_manager(profile(),
                                                  pending_app_manager.get());
   system_web_app_manager.SetSystemApps(std::move(system_apps));
-  system_web_app_manager.Init();
+  system_web_app_manager.Start();
 
   base::RunLoop().RunUntilIdle();
 
@@ -148,7 +148,7 @@
   TestSystemWebAppManager system_web_app_manager(profile(),
                                                  pending_app_manager.get());
   system_web_app_manager.SetSystemApps(std::move(system_apps));
-  system_web_app_manager.Init();
+  system_web_app_manager.Start();
 
   base::RunLoop().RunUntilIdle();
 
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager.cc b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
index bf7979971..09bfb9e8 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_manager.cc
+++ b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
@@ -25,7 +25,11 @@
                                          PendingAppManager* pending_app_manager)
     : profile_(profile),
       pref_service_(profile_->GetPrefs()),
-      pending_app_manager_(pending_app_manager) {
+      pending_app_manager_(pending_app_manager) {}
+
+WebAppPolicyManager::~WebAppPolicyManager() = default;
+
+void WebAppPolicyManager::Start() {
   content::BrowserThread::PostAfterStartupTask(
       FROM_HERE,
       base::CreateSingleThreadTaskRunnerWithTraits(
@@ -35,8 +39,6 @@
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
-WebAppPolicyManager::~WebAppPolicyManager() = default;
-
 // static
 void WebAppPolicyManager::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager.h b/chrome/browser/web_applications/policy/web_app_policy_manager.h
index cbde4cb6..0ecdac1d 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_manager.h
+++ b/chrome/browser/web_applications/policy/web_app_policy_manager.h
@@ -33,6 +33,8 @@
   WebAppPolicyManager(Profile* profile, PendingAppManager* pending_app_manager);
   ~WebAppPolicyManager();
 
+  void Start();
+
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
  private:
diff --git a/chrome/browser/web_applications/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_app_manager.cc
index 13b00c86..0fa6f80 100644
--- a/chrome/browser/web_applications/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_app_manager.cc
@@ -41,7 +41,7 @@
 
 SystemWebAppManager::~SystemWebAppManager() = default;
 
-void SystemWebAppManager::Init() {
+void SystemWebAppManager::Start() {
   content::BrowserThread::PostAfterStartupTask(
       FROM_HERE,
       base::CreateSingleThreadTaskRunnerWithTraits(
diff --git a/chrome/browser/web_applications/system_web_app_manager.h b/chrome/browser/web_applications/system_web_app_manager.h
index 65fcb4e7..615b30d 100644
--- a/chrome/browser/web_applications/system_web_app_manager.h
+++ b/chrome/browser/web_applications/system_web_app_manager.h
@@ -25,7 +25,7 @@
   SystemWebAppManager(Profile* profile, PendingAppManager* pending_app_manager);
   virtual ~SystemWebAppManager();
 
-  void Init();
+  void Start();
 
   static bool ShouldEnableForProfile(Profile* profile);
 
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index d163b8c..853d969 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -64,7 +64,7 @@
 
 WebAppProvider::~WebAppProvider() = default;
 
-void WebAppProvider::CreateSubsystems() {
+void WebAppProvider::Init() {
   audio_focus_id_map_ = std::make_unique<WebAppAudioFocusIdMap>();
 
   if (base::FeatureList::IsEnabled(features::kDesktopPWAsWithoutExtensions))
@@ -73,7 +73,7 @@
     CreateBookmarkAppsSubsystems(profile_);
 }
 
-void WebAppProvider::Init() {
+void WebAppProvider::Start() {
   notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
                               content::Source<Profile>(profile_));
 
@@ -81,9 +81,11 @@
     registrar_->Init(base::BindOnce(&WebAppProvider::OnRegistryReady,
                                     weak_ptr_factory_.GetWeakPtr()));
   } else {
-    system_web_app_manager_->Init();
+    web_app_policy_manager_->Start();
+    system_web_app_manager_->Start();
 
-    web_app::ScanForExternalWebApps(
+    // Start ExternalWebApps subsystem:
+    ScanForExternalWebApps(
         profile_, base::BindOnce(&WebAppProvider::OnScanForExternalWebApps,
                                  weak_ptr_factory_.GetWeakPtr()));
 
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h
index 2b91bad..ce13117 100644
--- a/chrome/browser/web_applications/web_app_provider.h
+++ b/chrome/browser/web_applications/web_app_provider.h
@@ -57,10 +57,10 @@
   explicit WebAppProvider(Profile* profile);
   ~WebAppProvider() override;
 
-  // 1st pass: Just create subsystems.
-  void CreateSubsystems();
-  // 2nd pass: Initialize subsystems.
+  // Create subsystems but do not start them (yet).
   void Init();
+  // Start all subsystems.
+  void Start();
 
   // Clients can use PendingAppManager to install, uninstall, and update
   // Web Apps.
diff --git a/chrome/browser/web_applications/web_app_provider_factory.cc b/chrome/browser/web_applications/web_app_provider_factory.cc
index 1361373..b185550 100644
--- a/chrome/browser/web_applications/web_app_provider_factory.cc
+++ b/chrome/browser/web_applications/web_app_provider_factory.cc
@@ -39,8 +39,8 @@
     content::BrowserContext* context) const {
   Profile* profile = Profile::FromBrowserContext(context);
   WebAppProvider* provider = new WebAppProvider(profile);
-  provider->CreateSubsystems();
   provider->Init();
+  provider->Start();
   return provider;
 }
 
diff --git a/chrome/credential_provider/gaiacp/gcp_utils.cc b/chrome/credential_provider/gaiacp/gcp_utils.cc
index 76a9ca4b..e138eb65 100644
--- a/chrome/credential_provider/gaiacp/gcp_utils.cc
+++ b/chrome/credential_provider/gaiacp/gcp_utils.cc
@@ -82,9 +82,7 @@
 
 const base::win::i18n::LanguageSelector& GetLanguageSelector() {
   static base::NoDestructor<base::win::i18n::LanguageSelector> instance(
-      base::string16(), &kLanguageOffsetPairs[0],
-      &kLanguageOffsetPairs[base::size(kLanguageOffsetPairs)]);
-
+      base::string16(), kLanguageOffsetPairs);
   return *instance;
 }
 
diff --git a/chrome/gpu/chrome_content_gpu_client.cc b/chrome/gpu/chrome_content_gpu_client.cc
index 8fec54b..4e5e2ea 100644
--- a/chrome/gpu/chrome_content_gpu_client.cc
+++ b/chrome/gpu/chrome_content_gpu_client.cc
@@ -13,6 +13,7 @@
 #include "base/token.h"
 #include "build/build_config.h"
 #include "content/public/child/child_thread.h"
+#include "content/public/common/content_switches.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
@@ -80,10 +81,14 @@
                      base::Unretained(protected_buffer_manager_.get())));
 #endif
 
-  main_thread_profiler_->SetMainThreadTaskRunner(
-      base::ThreadTaskRunnerHandle::Get());
-  ThreadProfiler::SetServiceManagerConnectorForChildProcess(
-      content::ChildThread::Get()->GetConnector());
+  // This doesn't work in single-process mode.
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSingleProcess)) {
+    main_thread_profiler_->SetMainThreadTaskRunner(
+        base::ThreadTaskRunnerHandle::Get());
+    ThreadProfiler::SetServiceManagerConnectorForChildProcess(
+        content::ChildThread::Get()->GetConnector());
+  }
 }
 
 void ChromeContentGpuClient::PostIOThreadCreated(
diff --git a/chrome/installer/util/l10n_string_util.cc b/chrome/installer/util/l10n_string_util.cc
index e768fd7a..0ab1352 100644
--- a/chrome/installer/util/l10n_string_util.cc
+++ b/chrome/installer/util/l10n_string_util.cc
@@ -45,9 +45,7 @@
 
 const base::win::i18n::LanguageSelector& GetLanguageSelector() {
   static base::NoDestructor<base::win::i18n::LanguageSelector> instance(
-      GetPreferredLanguageFromGoogleUpdate(), &kLanguageOffsetPairs[0],
-      &kLanguageOffsetPairs[base::size(kLanguageOffsetPairs)]);
-
+      GetPreferredLanguageFromGoogleUpdate(), kLanguageOffsetPairs);
   return *instance;
 }
 
diff --git a/chrome/renderer/autofill/password_generation_agent_browsertest.cc b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
index 3adc3d5..ac89241 100644
--- a/chrome/renderer/autofill/password_generation_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
@@ -35,6 +35,7 @@
 #include "third_party/blink/public/web/web_widget.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 
+using base::ASCIIToUTF16;
 using blink::WebDocument;
 using blink::WebElement;
 using blink::WebInputElement;
@@ -880,11 +881,36 @@
 
 TEST_F(PasswordGenerationAgentTest, ManualGenerationNoIds) {
   LoadHTMLWithUserGesture(kAccountCreationNoIds);
+  WebDocument document = GetMainFrame()->GetDocument();
+
   ExecuteJavaScriptForTests(
       "document.getElementsByClassName('first_password')[0].focus();");
-  // TODO(crbug/866444): generation doesn't work properly on the password field
-  // without name and id. Temporarily it's disabled.
-  SelectGenerationFallbackAndExpect(false);
+  WebInputElement first_password_element =
+      document.FocusedElement().To<WebInputElement>();
+  ASSERT_FALSE(first_password_element.IsNull());
+  SelectGenerationFallbackAndExpect(true);
+
+  // Simulate that the user accepts a generated password.
+  base::string16 password = ASCIIToUTF16("random_password");
+  EXPECT_CALL(fake_pw_client_,
+              PresaveGeneratedPassword(testing::Field(
+                  &autofill::PasswordForm::password_value, password)));
+  password_generation_->GeneratedPasswordAccepted(password);
+
+  // Check that the first password field is autofilled with the generated
+  // password.
+  EXPECT_EQ(password, first_password_element.Value().Utf16());
+  EXPECT_TRUE(first_password_element.IsAutofilled());
+
+  // Check that the second password field is autofilled with the generated
+  // password (since it is chosen as a confirmation password field).
+  ExecuteJavaScriptForTests(
+      "document.getElementsByClassName('second_password')[0].focus();");
+  WebInputElement second_password_element =
+      document.FocusedElement().To<WebInputElement>();
+  ASSERT_FALSE(second_password_element.IsNull());
+  EXPECT_EQ(password, second_password_element.Value().Utf16());
+  EXPECT_TRUE(second_password_element.IsAutofilled());
 }
 
 TEST_F(PasswordGenerationAgentTest, PresavingGeneratedPassword) {
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 9351275..e19c16c 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -471,10 +471,14 @@
         WebString::FromASCII(scheme));
   }
 
-  main_thread_profiler_->SetMainThreadTaskRunner(
-      base::ThreadTaskRunnerHandle::Get());
-  ThreadProfiler::SetServiceManagerConnectorForChildProcess(
-      thread->GetConnector());
+  // This doesn't work in single-process mode.
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSingleProcess)) {
+    main_thread_profiler_->SetMainThreadTaskRunner(
+        base::ThreadTaskRunnerHandle::Get());
+    ThreadProfiler::SetServiceManagerConnectorForChildProcess(
+        thread->GetConnector());
+  }
 }
 
 void ChromeContentRendererClient::RenderFrameCreated(
diff --git a/chrome/renderer/media/cast_receiver_session.cc b/chrome/renderer/media/cast_receiver_session.cc
index 11fa601..659bf04 100644
--- a/chrome/renderer/media/cast_receiver_session.cc
+++ b/chrome/renderer/media/cast_receiver_session.cc
@@ -99,7 +99,7 @@
 }
 
 void CastReceiverSession::StartVideo(
-    content::VideoCaptureDeliverFrameCB frame_callback) {
+    blink::VideoCaptureDeliverFrameCB frame_callback) {
   io_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&CastReceiverSessionDelegate::StartVideo,
diff --git a/chrome/renderer/media/cast_receiver_session.h b/chrome/renderer/media/cast_receiver_session.h
index a1c10632..c9bfa5a 100644
--- a/chrome/renderer/media/cast_receiver_session.h
+++ b/chrome/renderer/media/cast_receiver_session.h
@@ -60,7 +60,7 @@
   virtual ~CastReceiverSession();
   void StartAudio(scoped_refptr<CastReceiverAudioValve> audio_valve);
 
-  void StartVideo(content::VideoCaptureDeliverFrameCB frame_callback);
+  void StartVideo(blink::VideoCaptureDeliverFrameCB frame_callback);
   // Stop Video callbacks.
   // Note that this returns immediately, but callbacks do not stop immediately.
   void StopVideo();
diff --git a/chrome/renderer/media/cast_receiver_session_delegate.cc b/chrome/renderer/media/cast_receiver_session_delegate.cc
index 790ed174..4307e3b 100644
--- a/chrome/renderer/media/cast_receiver_session_delegate.cc
+++ b/chrome/renderer/media/cast_receiver_session_delegate.cc
@@ -72,14 +72,14 @@
 }
 
 void CastReceiverSessionDelegate::StartVideo(
-    content::VideoCaptureDeliverFrameCB video_callback) {
+    blink::VideoCaptureDeliverFrameCB video_callback) {
   DCHECK(io_task_runner_->BelongsToCurrentThread());
   frame_callback_ = video_callback;
   cast_receiver_->RequestDecodedVideoFrame(on_video_decoded_cb_);
 }
 
 void  CastReceiverSessionDelegate::StopVideo() {
-  frame_callback_ = content::VideoCaptureDeliverFrameCB();
+  frame_callback_ = blink::VideoCaptureDeliverFrameCB();
 }
 
 void CastReceiverSessionDelegate::OnDecodedVideoFrame(
diff --git a/chrome/renderer/media/cast_receiver_session_delegate.h b/chrome/renderer/media/cast_receiver_session_delegate.h
index 57915715..0e1c001 100644
--- a/chrome/renderer/media/cast_receiver_session_delegate.h
+++ b/chrome/renderer/media/cast_receiver_session_delegate.h
@@ -33,7 +33,7 @@
 
   void StartAudio(scoped_refptr<CastReceiverAudioValve> audio_valve);
 
-  void StartVideo(content::VideoCaptureDeliverFrameCB frame_callback);
+  void StartVideo(blink::VideoCaptureDeliverFrameCB frame_callback);
   // Stop Video callbacks (eventually).
   void StopVideo();
 
@@ -47,7 +47,7 @@
                            bool is_continous);
 
   scoped_refptr<CastReceiverAudioValve> audio_valve_;
-  content::VideoCaptureDeliverFrameCB frame_callback_;
+  blink::VideoCaptureDeliverFrameCB frame_callback_;
   media::cast::AudioFrameDecodedCallback on_audio_decoded_cb_;
   media::cast::VideoFrameDecodedCallback on_video_decoded_cb_;
   std::unique_ptr<media::cast::CastReceiver> cast_receiver_;
diff --git a/chrome/test/base/in_process_browser_test_browsertest.cc b/chrome/test/base/in_process_browser_test_browsertest.cc
index 97433a2..79e3260c 100644
--- a/chrome/test/base/in_process_browser_test_browsertest.cc
+++ b/chrome/test/base/in_process_browser_test_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/files/file_util.h"
 #include "base/path_service.h"
 #include "base/stl_util.h"
+#include "build/build_config.h"
 #include "chrome/browser/after_startup_task_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -17,6 +18,7 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/content_switches.h"
 #include "net/base/filename_util.h"
 #include "net/base/net_errors.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -93,4 +95,20 @@
   EXPECT_TRUE(AfterStartupTaskUtils::IsBrowserStartupComplete());
 }
 
+// On Mac this crashes inside cc::SingleThreadProxy::SetNeedsCommit. See
+// https://ci.chromium.org/b/8923336499994443392
+#if !defined(OS_MACOSX)
+class SingleProcessBrowserTest : public InProcessBrowserTest {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kSingleProcess);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(SingleProcessBrowserTest, Test) {
+  // Should not crash.
+}
+
+#endif
+
 }  // namespace
diff --git a/chrome/test/data/extensions/api_test/serial/api/background.js b/chrome/test/data/extensions/api_test/serial/api/background.js
index 560f03e..cb3be603 100644
--- a/chrome/test/data/extensions/api_test/serial/api/background.js
+++ b/chrome/test/data/extensions/api_test/serial/api/background.js
@@ -105,7 +105,7 @@
   var onReceive = function(receiveInfo) {
     var data = new Uint8Array(receiveInfo.data);
     bytesToReceive -= data.length;
-    var receiveBufferIndex = bufferLength - data.length;
+    var receiveBufferIndex = bufferLength - bytesToReceive - data.length;
     for (var i = 0; i < data.length; i++)
       receiveBufferUint8View[i + receiveBufferIndex] = data[i];
     if (bytesToReceive == 0) {
@@ -118,7 +118,28 @@
   };
 
   var onReceiveError = function(errorInfo) {
-    chrome.test.fail('Failed to receive serial data');
+    chrome.test.assertEq(connectionId, errorInfo.connectionId,
+                         "Unmatch connectionId for ReceiveError");
+    if (errorInfo.error == "parity_error") {
+      serial.getInfo(connectionId, onGetInfoToReconnect);
+    } else {
+      chrome.test.fail('Failed to receive serial data');
+    }
+  };
+
+  var onGetInfoToReconnect = function(connectionInfo) {
+    chrome.test.assertEq(true, connectionInfo.paused,
+                         'Failed to pause connection on read error.');
+    // Try to reconnect the read data pipe.
+    serial.setPaused(connectionId, false, () => {
+      serial.getInfo(connectionId, onGetInfoAfterReconnect);
+    });
+  };
+
+  var onGetInfoAfterReconnect = function(connectionInfo) {
+    if (connectionInfo.paused != false) {
+      chrome.test.fail('Failed to reconnect on read error.');
+    }
   };
 
   var onSend = function(sendInfo) {
diff --git a/chrome/test/data/extensions/api_test/webrequest/framework.js b/chrome/test/data/extensions/api_test/webrequest/framework.js
index 1050b671..17e8804 100644
--- a/chrome/test/data/extensions/api_test/webrequest/framework.js
+++ b/chrome/test/data/extensions/api_test/webrequest/framework.js
@@ -132,7 +132,7 @@
   chrome.tabs.update(tabId, {url: url});
 }
 
-// data: array of extected events, each one is a dictionary:
+// data: array of expected events, each one is a dictionary:
 //     { label: "<unique identifier>",
 //       event: "<webrequest event type>",
 //       details: { <expected details of the webrequest event> },
@@ -345,14 +345,25 @@
     delete details.responseHeaders;
   }
 
+  // Check if the equivalent event is already captured, and issue a unique
+  // |eventCount| to identify each.
+  var eventCount = 0;
+  capturedEventData.forEach(function (event) {
+    if (deepEq(event.event, name) && deepEq(event.details, details)) {
+      eventCount++;
+      // update |details| for the next match.
+      details.eventCount = eventCount;
+    }
+  });
+
   // find |details| in expectedEventData
   var found = false;
   var label = undefined;
   expectedEventData.forEach(function (exp) {
     if (deepEq(exp.event, name) && deepEq(exp.details, details)) {
       if (found) {
-        chrome.test.fail("Received event twice '" + name + "':" +
-            JSON.stringify(details));
+        chrome.test.fail("Duplicated expectation entry '" + exp.label +
+        "' should be identified by |eventCount|: " + JSON.stringify(details));
       } else {
         found = true;
         label = exp.label;
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_types.js b/chrome/test/data/extensions/api_test/webrequest/test_types.js
index 70d2a4cc..87f4ac5 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_types.js
+++ b/chrome/test/data/extensions/api_test/webrequest/test_types.js
@@ -255,7 +255,7 @@
           url: getFontURL(),
           tabId: 1,
           initiator: getDomain(initiators.WEB_INITIATED)
-        },
+        }
       },
       { label: 'onSendHeaders',
         event: 'onSendHeaders',
@@ -264,7 +264,49 @@
           url: getFontURL(),
           tabId: 1,
           initiator: getDomain(initiators.WEB_INITIATED)
-        },
+        }
+      },
+      { label: 'onErrorOccurred',
+        event: 'onErrorOccurred',
+        details: {
+          type: 'font',
+          url: getFontURL(),
+          tabId: 1,
+          initiator: getDomain(initiators.WEB_INITIATED),
+          error: 'net::ERR_CACHE_MISS',
+          fromCache: false,
+        }
+      },
+      { label: 'onBeforeRequest-1',
+        event: 'onBeforeRequest',
+        details: {
+          eventCount: 1,
+          type: 'font',
+          url: getFontURL(),
+          frameUrl: 'unknown frame URL',
+          tabId: 1,
+          initiator: getDomain(initiators.WEB_INITIATED)
+        }
+      },
+      { label: 'onBeforeSendHeaders-1',
+        event: 'onBeforeSendHeaders',
+        details: {
+          eventCount: 1,
+          type: 'font',
+          url: getFontURL(),
+          tabId: 1,
+          initiator: getDomain(initiators.WEB_INITIATED)
+        }
+      },
+      { label: 'onSendHeaders-1',
+        event: 'onSendHeaders',
+        details: {
+          eventCount: 1,
+          type: 'font',
+          url: getFontURL(),
+          tabId: 1,
+          initiator: getDomain(initiators.WEB_INITIATED)
+        }
       },
       { label: 'onHeadersReceived',
         event: 'onHeadersReceived',
@@ -275,7 +317,7 @@
           statusLine: 'HTTP/1.1 200 OK',
           statusCode: 200,
           initiator: getDomain(initiators.WEB_INITIATED)
-        },
+        }
       },
       { label: 'onResponseStarted',
         event: 'onResponseStarted',
@@ -288,7 +330,7 @@
           statusLine: 'HTTP/1.1 200 OK',
           statusCode: 200,
           initiator: getDomain(initiators.WEB_INITIATED)
-        },
+        }
       },
       { label: 'onCompleted',
         event: 'onCompleted',
@@ -301,10 +343,12 @@
           statusLine: 'HTTP/1.1 200 OK',
           statusCode: 200,
           initiator: getDomain(initiators.WEB_INITIATED)
-        },
+        }
       }],
       [['onBeforeRequest', 'onBeforeSendHeaders', 'onSendHeaders',
-        'onHeadersReceived', 'onResponseStarted', 'onCompleted']],
+        'onErrorOccurred', 'onBeforeRequest-1', 'onBeforeSendHeaders-1',
+        'onSendHeaders-1', 'onHeadersReceived', 'onResponseStarted',
+        'onCompleted']],
       {urls: [getFontURL()]});
 
     // Load a page to be sure webRequest listeners are set up.
diff --git a/chrome/test/data/webui/settings/bluetooth_page_tests.js b/chrome/test/data/webui/settings/bluetooth_page_tests.js
index 35144622..a6fcd05 100644
--- a/chrome/test/data/webui/settings/bluetooth_page_tests.js
+++ b/chrome/test/data/webui/settings/bluetooth_page_tests.js
@@ -109,19 +109,20 @@
       });
     }
 
-    setup(function() {
+    setup(async function() {
       bluetoothApi_.setEnabled(true);
       Polymer.dom.flush();
       const div = bluetoothPage.$$('div.settings-box');
       div.click();
-      return flushAsync().then(() => {
-        subpage = bluetoothPage.$$('settings-bluetooth-subpage');
-        subpage.listUpdateFrequencyMs = 0;
-        assertTrue(!!subpage);
-        assertTrue(subpage.bluetoothToggleState);
-        assertFalse(subpage.stateChangeInProgress);
-        assertEquals(0, subpage.listUpdateFrequencyMs);
-      });
+
+      await flushAsync();
+
+      subpage = bluetoothPage.$$('settings-bluetooth-subpage');
+      subpage.listUpdateFrequencyMs = 0;
+      assertTrue(!!subpage);
+      assertTrue(subpage.bluetoothToggleState);
+      assertFalse(subpage.stateChangeInProgress);
+      assertEquals(0, subpage.listUpdateFrequencyMs);
     });
 
     test('toggle', function() {
@@ -147,85 +148,86 @@
       });
     }
 
-    test('paired device list', function() {
+    test('paired device list', async function() {
       const pairedContainer = subpage.$.pairedContainer;
       assertTrue(!!pairedContainer);
       assertTrue(pairedContainer.hidden);
       assertFalse(subpage.$.noPairedDevices.hidden);
 
       bluetoothApi_.setDevicesForTest(fakeDevices_);
-      return waitForListUpdateTimeout().then(function() {
-        Polymer.dom.flush();
-        assertEquals(4, subpage.deviceList_.length);
-        assertEquals(2, subpage.pairedDeviceList_.length);
-        assertTrue(subpage.$.noPairedDevices.hidden);
 
-        const ironList = subpage.$.pairedDevices;
-        assertTrue(!!ironList);
-        ironList.notifyResize();
-        Polymer.dom.flush();
-        const devices = ironList.querySelectorAll('bluetooth-device-list-item');
-        assertEquals(2, devices.length);
-        assertTrue(devices[0].device.connected);
-        assertFalse(devices[1].device.connected);
-      });
+      await waitForListUpdateTimeout();
+
+      Polymer.dom.flush();
+      assertEquals(4, subpage.deviceList_.length);
+      assertEquals(2, subpage.pairedDeviceList_.length);
+      assertTrue(subpage.$.noPairedDevices.hidden);
+
+      const ironList = subpage.$.pairedDevices;
+      assertTrue(!!ironList);
+      ironList.notifyResize();
+      Polymer.dom.flush();
+      const devices = ironList.querySelectorAll('bluetooth-device-list-item');
+      assertEquals(2, devices.length);
+      assertTrue(devices[0].device.connected);
+      assertFalse(devices[1].device.connected);
     });
 
-    test('unpaired device list', function() {
+    test('unpaired device list', async function() {
       const unpairedContainer = subpage.$.unpairedContainer;
       assertTrue(!!unpairedContainer);
       assertTrue(unpairedContainer.hidden);
       assertFalse(subpage.$.noUnpairedDevices.hidden);
 
       bluetoothApi_.setDevicesForTest(fakeDevices_);
-      return waitForListUpdateTimeout().then(function() {
-        Polymer.dom.flush();
-        assertEquals(4, subpage.deviceList_.length);
-        assertEquals(2, subpage.unpairedDeviceList_.length);
-        assertTrue(subpage.$.noUnpairedDevices.hidden);
+      await waitForListUpdateTimeout();
 
-        const ironList = subpage.$.unpairedDevices;
-        assertTrue(!!ironList);
-        ironList.notifyResize();
-        Polymer.dom.flush();
-        const devices = ironList.querySelectorAll('bluetooth-device-list-item');
-        assertEquals(2, devices.length);
-        assertFalse(devices[0].device.paired);
-        assertFalse(devices[1].device.paired);
-      });
+      Polymer.dom.flush();
+      assertEquals(4, subpage.deviceList_.length);
+      assertEquals(2, subpage.unpairedDeviceList_.length);
+      assertTrue(subpage.$.noUnpairedDevices.hidden);
+
+      const ironList = subpage.$.unpairedDevices;
+      assertTrue(!!ironList);
+      ironList.notifyResize();
+      Polymer.dom.flush();
+      const devices = ironList.querySelectorAll('bluetooth-device-list-item');
+      assertEquals(2, devices.length);
+      assertFalse(devices[0].device.paired);
+      assertFalse(devices[1].device.paired);
     });
 
-    test('pair device', function(done) {
+    test('pair device', async function() {
       bluetoothApi_.setDevicesForTest(fakeDevices_);
-      return waitForListUpdateTimeout().then(function() {
-        Polymer.dom.flush();
-        assertEquals(4, subpage.deviceList_.length);
-        assertEquals(2, subpage.pairedDeviceList_.length);
-        assertEquals(2, subpage.unpairedDeviceList_.length);
+      await waitForListUpdateTimeout();
 
-        const address = subpage.unpairedDeviceList_[0].address;
-        bluetoothPrivateApi_.connect(address, function() {
-          Polymer.dom.flush();
-          assertEquals(3, subpage.pairedDeviceList_.length);
-          assertEquals(1, subpage.unpairedDeviceList_.length);
-          done();
-        });
-      });
+      Polymer.dom.flush();
+      assertEquals(4, subpage.deviceList_.length);
+      assertEquals(2, subpage.pairedDeviceList_.length);
+      assertEquals(2, subpage.unpairedDeviceList_.length);
+
+      const address = subpage.unpairedDeviceList_[0].address;
+      await new Promise(
+          resolve => bluetoothPrivateApi_.connect(address, resolve));
+
+      Polymer.dom.flush();
+      assertEquals(3, subpage.pairedDeviceList_.length);
+      assertEquals(1, subpage.unpairedDeviceList_.length);
     });
 
-    test('pair dialog', function() {
+    test('pair dialog', async function() {
       bluetoothApi_.setDevicesForTest(fakeDevices_);
-      return waitForListUpdateTimeout().then(function() {
-        Polymer.dom.flush();
-        const dialog = subpage.$.deviceDialog;
-        assertTrue(!!dialog);
-        assertFalse(dialog.$.dialog.open);
+      await waitForListUpdateTimeout();
 
-        // Simulate selecting an unpaired device; should show the pair dialog.
-        subpage.connectDevice_(subpage.unpairedDeviceList_[0]);
-        Polymer.dom.flush();
-        assertTrue(dialog.$.dialog.open);
-      });
+      Polymer.dom.flush();
+      const dialog = subpage.$.deviceDialog;
+      assertTrue(!!dialog);
+      assertFalse(dialog.$.dialog.open);
+
+      // Simulate selecting an unpaired device; should show the pair dialog.
+      subpage.connectDevice_(subpage.unpairedDeviceList_[0]);
+      Polymer.dom.flush();
+      assertTrue(dialog.$.dialog.open);
     });
 
   });
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 8c39a66..6de2a75 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-11657.0.0
\ No newline at end of file
+11668.0.0
\ No newline at end of file
diff --git a/cloud_print/common/win/install_utils.cc b/cloud_print/common/win/install_utils.cc
index 687fe1c..dae0282 100644
--- a/cloud_print/common/win/install_utils.cc
+++ b/cloud_print/common/win/install_utils.cc
@@ -7,7 +7,7 @@
 #include <windows.h>
 
 #include "base/command_line.h"
-#include "base/file_version_info_win.h"
+#include "base/file_version_info.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
@@ -60,12 +60,10 @@
 
   // Get the version from the resource file.
   base::string16 version_string;
-  std::unique_ptr<FileVersionInfo> version_info(
-      FileVersionInfo::CreateFileVersionInfoForModule(CURRENT_MODULE()));
-  if (version_info.get()) {
-    FileVersionInfoWin* version_info_win =
-        static_cast<FileVersionInfoWin*>(version_info.get());
-    version_string = version_info_win->product_version();
+  std::unique_ptr<FileVersionInfo> version_info =
+      FileVersionInfo::CreateFileVersionInfoForModule(CURRENT_MODULE());
+  if (version_info) {
+    version_string = version_info->product_version();
   } else {
     LOG(ERROR) << "Unable to get version string";
     // Use a random version string so that Google Update has something to go by.
@@ -149,14 +147,12 @@
   key.WriteValue(kInstallLocation, unstall_binary.DirName().value().c_str());
 
   // Get the version resource.
-  std::unique_ptr<FileVersionInfo> version_info(
-      FileVersionInfo::CreateFileVersionInfoForModule(CURRENT_MODULE()));
+  std::unique_ptr<FileVersionInfo> version_info =
+      FileVersionInfo::CreateFileVersionInfoForModule(CURRENT_MODULE());
 
-  if (version_info.get()) {
-    FileVersionInfoWin* version_info_win =
-        static_cast<FileVersionInfoWin*>(version_info.get());
-    key.WriteValue(kDisplayVersion, version_info_win->file_version().c_str());
-    key.WriteValue(kPublisher, version_info_win->company_name().c_str());
+  if (version_info) {
+    key.WriteValue(kDisplayVersion, version_info->file_version().c_str());
+    key.WriteValue(kPublisher, version_info->company_name().c_str());
   } else {
     LOG(ERROR) << "Unable to get version string";
   }
diff --git a/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc b/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
index 339aa6f..e17506e 100644
--- a/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
+++ b/components/arc/video_accelerator/gpu_arc_video_decode_accelerator.cc
@@ -538,8 +538,27 @@
           mojom::VideoDecodeAccelerator::Result::INVALID_ARGUMENT);
       return;
     }
-    gmb_handle.native_pixmap_handle.fds.push_back(
-        base::FileDescriptor(handle_fd.release(), true));
+
+    const size_t num_planes = media::VideoFrame::NumPlanes(pixel_format);
+
+    // TODO(crbug.com/911370): Remove this workaround once Android passes one fd
+    // per plane.
+    std::array<base::ScopedFD, media::VideoFrame::kMaxPlanes> scoped_fds;
+    scoped_fds[0].reset(handle_fd.release());
+    for (size_t i = 1; i < num_planes; ++i) {
+      scoped_fds[i].reset(HANDLE_EINTR(dup(scoped_fds[0].get())));
+      if (!scoped_fds[i].is_valid()) {
+        VLOGF(1) << "Failed to duplicate fd.";
+        client_->NotifyError(
+            mojom::VideoDecodeAccelerator::Result::PLATFORM_FAILURE);
+        return;
+      }
+    }
+    for (size_t i = 0; i < num_planes; ++i) {
+      gmb_handle.native_pixmap_handle.fds.push_back(
+          base::FileDescriptor(scoped_fds[i].release(), true));
+    }
+
     for (const auto& plane : planes) {
       gmb_handle.native_pixmap_handle.planes.emplace_back(plane.stride,
                                                           plane.offset, 0);
diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc
index 62d0258..11104f5 100644
--- a/components/autofill/content/renderer/password_generation_agent.cc
+++ b/components/autofill/content/renderer/password_generation_agent.cc
@@ -85,6 +85,28 @@
   return !passwords->empty();
 }
 
+// Returns the renderer id of the next password field in |control_elements|
+// after |new_password|. This field is likely to be the confirmation field.
+// Returns FormFieldData::kNotSetFormControlRendererId if there is no such
+// field.
+uint32_t FindConfirmationPasswordFieldId(
+    const std::vector<WebFormControlElement>& control_elements,
+    const WebFormControlElement& new_password) {
+  auto iter =
+      std::find(control_elements.begin(), control_elements.end(), new_password);
+
+  if (iter == control_elements.end())
+    return FormFieldData::kNotSetFormControlRendererId;
+
+  ++iter;
+  for (; iter != control_elements.end(); ++iter) {
+    const WebInputElement* input_element = ToWebInputElement(&(*iter));
+    if (input_element && input_element->IsPasswordFieldForAutofill())
+      return input_element->UniqueRendererFormControlId();
+  }
+  return FormFieldData::kNotSetFormControlRendererId;
+}
+
 bool ContainsURL(const std::vector<GURL>& urls, const GURL& url) {
   return base::ContainsValue(urls, url);
 }
@@ -607,46 +629,58 @@
   if (last_focused_password_element_.IsNull() || !render_frame())
     return false;
 
-  WebFormElement form = last_focused_password_element_.Form();
-  std::unique_ptr<PasswordForm> password_form;
-  std::vector<WebFormControlElement> control_elements;
-  if (!form.IsNull()) {
-    password_form = password_agent_->GetPasswordFormFromWebForm(form);
-    control_elements = form_util::ExtractAutofillableElementsInForm(form);
+  uint32_t last_focused_password_element_id =
+      last_focused_password_element_.UniqueRendererFormControlId();
+
+  bool is_automatic_generation_available = base::ContainsKey(
+      generation_enabled_fields_, last_focused_password_element_id);
+
+  if (!is_automatic_generation_available) {
+    WebFormElement form = last_focused_password_element_.Form();
+    std::vector<WebFormControlElement> control_elements;
+    if (!form.IsNull()) {
+      control_elements = form_util::ExtractAutofillableElementsInForm(form);
+    } else {
+      const WebLocalFrame& frame = *render_frame()->GetWebFrame();
+      blink::WebDocument doc = frame.GetDocument();
+      if (doc.IsNull())
+        return false;
+      control_elements =
+          form_util::GetUnownedFormFieldElements(doc.All(), nullptr);
+    }
+
+    MaybeCreateCurrentGenerationItem(
+        last_focused_password_element_,
+        FindConfirmationPasswordFieldId(control_elements,
+                                        last_focused_password_element_));
   } else {
-    const WebLocalFrame& frame = *render_frame()->GetWebFrame();
-    blink::WebDocument doc = frame.GetDocument();
-    if (doc.IsNull())
-      return false;
-    password_form = password_agent_->GetPasswordFormFromUnownedInputElements();
-    control_elements =
-        form_util::GetUnownedFormFieldElements(doc.All(), nullptr);
+    auto it = generation_enabled_fields_.find(last_focused_password_element_id);
+    MaybeCreateCurrentGenerationItem(
+        last_focused_password_element_,
+        it->second.confirmation_password_renderer_id);
   }
 
-  if (!password_form)
+  if (!current_generation_item_)
     return false;
 
-  std::vector<WebInputElement> password_elements;
-  GetAccountCreationPasswordFields(control_elements, &password_elements);
-  password_elements = FindPasswordElementsForGeneration(
-      password_elements,
-      PasswordFormGenerationData(
-          0, /* form_signature */
-          CalculateFieldSignatureByNameAndType(
-              last_focused_password_element_.NameForAutofill().Utf16(),
-              last_focused_password_element_.FormControlTypeForAutofill()
-                  .Utf8())));
-  // TODO(crbug/866444): remove this once it's impossible that currently focused
-  // password field isn't found by FindPasswordElementsForGeneration.
-  if (!std::count(password_elements.begin(), password_elements.end(),
-                  last_focused_password_element_))
+  if (current_generation_item_->generation_element_ !=
+      last_focused_password_element_) {
     return false;
-  current_generation_item_.reset(new GenerationItemInfo(
-      last_focused_password_element_, std::move(*password_form),
-      std::move(password_elements)));
-  // |automatic_generation_element_| should always generate the UI.
+  }
+
+  // Automatic generation depends on whether the new parser is on because the
+  // new parser sends now information to the renderer about fields for
+  // generation. In case when the new old parser is used the old path for
+  // automatic generation is used. So detecting which automatic generation
+  // depends on which parser is used.
+  // TODO(https://crbug.com/831123): Remove this variable when the old parser is
+  // gone.
+  bool automatic_generation_available_with_the_old_parser =
+      last_focused_password_element_ == automatic_generation_element_;
+
   current_generation_item_->is_manually_triggered_ =
-      (last_focused_password_element_ != automatic_generation_element_);
+      !is_automatic_generation_available &&
+      !automatic_generation_available_with_the_old_parser;
   return true;
 }
 
@@ -663,13 +697,18 @@
   }
 
   const WebInputElement* element = ToWebInputElement(&web_element);
-  if (element && element->IsPasswordFieldForAutofill())
-    last_focused_password_element_ = *element;
-
   if (!element)
     return false;
 
-  MaybeCreateCurrentGenerationItem(*element);
+  if (element->IsPasswordFieldForAutofill())
+    last_focused_password_element_ = *element;
+
+  auto it =
+      generation_enabled_fields_.find(element->UniqueRendererFormControlId());
+  if (it != generation_enabled_fields_.end()) {
+    MaybeCreateCurrentGenerationItem(
+        *element, it->second.confirmation_password_renderer_id);
+  }
 
   if (!current_generation_item_ ||
       *element != current_generation_item_->generation_element_) {
@@ -844,7 +883,8 @@
 }
 
 void PasswordGenerationAgent::MaybeCreateCurrentGenerationItem(
-    WebInputElement element) {
+    WebInputElement element,
+    uint32_t confirmation_password_renderer_id) {
   // Do not create |current_generation_item_| if it already is created for
   // |element| or the user accepted generated password. So if the user accepted
   // the generated password, generation is not offered on any other field.
@@ -853,11 +893,6 @@
        current_generation_item_->password_is_generated_))
     return;
 
-  auto it =
-      generation_enabled_fields_.find(element.UniqueRendererFormControlId());
-  if (it == generation_enabled_fields_.end())
-    return;
-
   std::unique_ptr<PasswordForm> password_form =
       element.Form().IsNull()
           ? password_agent_->GetPasswordFormFromUnownedInputElements()
@@ -867,7 +902,7 @@
 
   WebFormControlElement confirmation_password =
       form_util::FindFormControlElementsByUniqueRendererId(
-          element.GetDocument(), it->second.confirmation_password_renderer_id);
+          element.GetDocument(), confirmation_password_renderer_id);
 
   if (!confirmation_password.IsNull()) {
     WebInputElement* input = ToWebInputElement(&confirmation_password);
diff --git a/components/autofill/content/renderer/password_generation_agent.h b/components/autofill/content/renderer/password_generation_agent.h
index 8582c21..40915c5 100644
--- a/components/autofill/content/renderer/password_generation_agent.h
+++ b/components/autofill/content/renderer/password_generation_agent.h
@@ -163,7 +163,9 @@
   // Creates |current_generation_item_| for |element| if |element| is a
   // generation enabled element. If |current_generation_item_| is already
   // created for |element| it is not recreated.
-  void MaybeCreateCurrentGenerationItem(blink::WebInputElement element);
+  void MaybeCreateCurrentGenerationItem(
+      blink::WebInputElement element,
+      uint32_t confirmation_password_renderer_id);
 
   // Runs HTML parsing based classifier and saves its outcome to proto.
   // TODO(crbug.com/621442): Remove client-side form classifier when server-side
diff --git a/components/autofill_assistant/browser/actions/prompt_action.cc b/components/autofill_assistant/browser/actions/prompt_action.cc
index 7aa41af..f1415bf 100644
--- a/components/autofill_assistant/browser/actions/prompt_action.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action.cc
@@ -28,9 +28,52 @@
   delegate->SetStatusMessage(proto_.prompt().message());
 
   callback_ = std::move(callback);
-  auto chips = std::make_unique<std::vector<Chip>>();
   DCHECK_GT(proto_.prompt().choices_size(), 0);
 
+  delegate->Prompt(CreateChips());
+
+  batch_element_checker_ = delegate->CreateBatchElementChecker();
+
+  // Register elements whose existence enable new chips.
+  for (int i = 0; i < proto_.prompt().choices_size(); i++) {
+    auto& choice_proto = proto_.prompt().choices(i);
+    Selector selector(choice_proto.show_only_if_element_exists());
+    if (selector.empty())
+      continue;
+
+    batch_element_checker_->AddElementCheck(
+        kExistenceCheck, selector,
+        base::BindOnce(&PromptAction::OnRequiredElementExists,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       base::Unretained(delegate), i));
+  }
+
+  // Wait as long as necessary for one of the elements to show up. This is
+  // cancelled by CancelProto()
+  for (const auto& choice_proto : proto_.prompt().choices()) {
+    Selector selector(choice_proto.element_exists());
+    if (selector.empty())
+      continue;
+
+    std::string payload;
+    choice_proto.SerializeToString(&payload);
+    batch_element_checker_->AddElementCheck(
+        kExistenceCheck, selector,
+        base::BindOnce(&PromptAction::OnElementExist,
+                       weak_ptr_factory_.GetWeakPtr(), payload));
+  }
+
+  batch_element_checker_->Run(
+      base::TimeDelta::Max(),
+      /* try_done= */
+      base::BindRepeating(&PromptAction::OnElementChecksDone,
+                          weak_ptr_factory_.GetWeakPtr(),
+                          base::Unretained(delegate)),
+      /* all_done= */ base::DoNothing());
+}
+
+std::unique_ptr<std::vector<Chip>> PromptAction::CreateChips() {
+  auto chips = std::make_unique<std::vector<Chip>>();
   // TODO(crbug.com/806868): Surface type in proto instead of guessing it from
   // highlight flag.
   Chip::Type non_highlight_type = Chip::Type::CHIP_ASSISTIVE;
@@ -41,8 +84,10 @@
     }
   }
 
-  for (const auto& choice_proto : proto_.prompt().choices()) {
-    if (choice_proto.name().empty())
+  for (int i = 0; i < proto_.prompt().choices_size(); i++) {
+    auto& choice_proto = proto_.prompt().choices(i);
+    if (choice_proto.show_only_if_element_exists().selectors_size() > 0 &&
+        required_element_found_.count(i) == 0)
       continue;
 
     chips->emplace_back();
@@ -58,29 +103,7 @@
         base::BindOnce(&PromptAction::OnSuggestionChosen,
                        weak_ptr_factory_.GetWeakPtr(), server_payload);
   }
-  delegate->Prompt(std::move(chips));
-
-  batch_element_checker_ = delegate->CreateBatchElementChecker();
-  for (const auto& choice_proto : proto_.prompt().choices()) {
-    if (choice_proto.element_exists().selectors_size() == 0)
-      continue;
-
-    std::string payload;
-    choice_proto.SerializeToString(&payload);
-    batch_element_checker_->AddElementCheck(
-        kExistenceCheck, Selector(choice_proto.element_exists()),
-        base::BindOnce(&PromptAction::OnElementExist,
-                       weak_ptr_factory_.GetWeakPtr(), payload));
-  }
-  // Wait as long as necessary for one of the elements to show up. This is
-  // cancelled by OnSuggestionChosen()
-  batch_element_checker_->Run(
-      base::TimeDelta::Max(),
-      /* try_done= */
-      base::BindRepeating(&PromptAction::OnElementChecksDone,
-                          weak_ptr_factory_.GetWeakPtr(),
-                          base::Unretained(delegate)),
-      /* all_done= */ base::DoNothing());
+  return chips;
 }
 
 void PromptAction::OnElementExist(const std::string& payload, bool exists) {
@@ -92,6 +115,16 @@
   // callback.
 }
 
+void PromptAction::OnRequiredElementExists(ActionDelegate* delegate,
+                                           int choice_index,
+                                           bool exists) {
+  if (!exists)
+    return;
+
+  required_element_found_.insert(choice_index);
+  delegate->Prompt(CreateChips());
+}
+
 void PromptAction::OnElementChecksDone(ActionDelegate* delegate) {
   if (!forced_payload_.empty()) {
     delegate->CancelPrompt();
diff --git a/components/autofill_assistant/browser/actions/prompt_action.h b/components/autofill_assistant/browser/actions/prompt_action.h
index 66dd536..49f8192f 100644
--- a/components/autofill_assistant/browser/actions/prompt_action.h
+++ b/components/autofill_assistant/browser/actions/prompt_action.h
@@ -6,12 +6,15 @@
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_PROMPT_ACTION_H_
 
 #include <memory>
+#include <set>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill_assistant/browser/actions/action.h"
 #include "components/autofill_assistant/browser/batch_element_checker.h"
+#include "components/autofill_assistant/browser/chip.h"
 
 namespace autofill_assistant {
 
@@ -26,7 +29,11 @@
   void InternalProcessAction(ActionDelegate* delegate,
                              ProcessActionCallback callback) override;
 
+  std::unique_ptr<std::vector<Chip>> CreateChips();
   void OnElementExist(const std::string& payload, bool exists);
+  void OnRequiredElementExists(ActionDelegate* delegate,
+                               int choice_index,
+                               bool exists);
   void OnElementChecksDone(ActionDelegate* delegate);
   void OnSuggestionChosen(const std::string& payload);
 
@@ -34,6 +41,10 @@
 
   std::string forced_payload_;
   std::unique_ptr<BatchElementChecker> batch_element_checker_;
+
+  // Index of an element in PromptActionProto::choices that has a
+  // show_only_if_element_exists condition that is met.
+  std::set<int> required_element_found_;
   base::WeakPtrFactory<PromptAction> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PromptAction);
diff --git a/components/autofill_assistant/browser/actions/show_details_action.cc b/components/autofill_assistant/browser/actions/show_details_action.cc
index 176cd02..e1f2626 100644
--- a/components/autofill_assistant/browser/actions/show_details_action.cc
+++ b/components/autofill_assistant/browser/actions/show_details_action.cc
@@ -26,9 +26,11 @@
 
 void ShowDetailsAction::InternalProcessAction(ActionDelegate* delegate,
                                               ProcessActionCallback callback) {
+  callback_ = std::move(callback);
+
   if (!proto_.show_details().has_details()) {
     delegate->ClearDetails();
-    OnActionProcessed(std::move(callback), ACTION_APPLIED);
+    OnActionProcessed(ACTION_APPLIED);
     return;
   }
 
@@ -38,7 +40,7 @@
   delegate->SetDetails(details);
 
   if (!details.changes.user_approval_required()) {
-    OnActionProcessed(std::move(callback), ACTION_APPLIED);
+    OnActionProcessed(ACTION_APPLIED);
     return;
   }
 
@@ -56,7 +58,7 @@
   chips->back().type = Chip::Type::BUTTON_FILLED_BLUE;
   chips->back().callback = base::BindOnce(
       &ShowDetailsAction::OnUserResponse, weak_ptr_factory_.GetWeakPtr(),
-      std::move(callback), base::Unretained(delegate), previous_status_message,
+      base::Unretained(delegate), previous_status_message,
       /* success= */ true);
 
   // Go back button.
@@ -66,20 +68,19 @@
   chips->back().type = Chip::Type::BUTTON_TEXT;
   chips->back().callback = base::BindOnce(
       &ShowDetailsAction::OnUserResponse, weak_ptr_factory_.GetWeakPtr(),
-      std::move(callback), base::Unretained(delegate), previous_status_message,
+      base::Unretained(delegate), previous_status_message,
       /* success= */ false);
 
   delegate->Prompt(std::move(chips));
 }
 
 void ShowDetailsAction::OnUserResponse(
-    ProcessActionCallback callback,
     ActionDelegate* delegate,
     const std::string& previous_status_message,
     bool can_continue) {
   if (!can_continue) {
     delegate->Close();
-    OnActionProcessed(std::move(callback), MANUAL_FALLBACK);
+    OnActionProcessed(MANUAL_FALLBACK);
   } else {
     // Same details, without highlights.
     Details details;
@@ -87,13 +88,14 @@
     delegate->SetDetails(details);
     // Restore status message
     delegate->SetStatusMessage(previous_status_message);
-    OnActionProcessed(std::move(callback), ACTION_APPLIED);
+    OnActionProcessed(ACTION_APPLIED);
   }
 }
 
-void ShowDetailsAction::OnActionProcessed(ProcessActionCallback callback,
-                                          ProcessedActionStatusProto status) {
+void ShowDetailsAction::OnActionProcessed(ProcessedActionStatusProto status) {
+  DCHECK(callback_);
+
   UpdateProcessedAction(status);
-  std::move(callback).Run(std::move(processed_action_proto_));
+  std::move(callback_).Run(std::move(processed_action_proto_));
 }
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/show_details_action.h b/components/autofill_assistant/browser/actions/show_details_action.h
index f6324799..f29c3f1bd 100644
--- a/components/autofill_assistant/browser/actions/show_details_action.h
+++ b/components/autofill_assistant/browser/actions/show_details_action.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill_assistant/browser/actions/action.h"
@@ -22,13 +23,12 @@
   // Overrides Action:
   void InternalProcessAction(ActionDelegate* delegate,
                              ProcessActionCallback callback) override;
-  void OnUserResponse(ProcessActionCallback callback,
-                      ActionDelegate* delegate,
+  void OnUserResponse(ActionDelegate* delegate,
                       const std::string& old_status_message,
                       bool can_continue);
-  void OnActionProcessed(ProcessActionCallback callback,
-                         ProcessedActionStatusProto status);
+  void OnActionProcessed(ProcessedActionStatusProto status);
 
+  ProcessActionCallback callback_;
   base::WeakPtrFactory<ShowDetailsAction> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ShowDetailsAction);
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 8e3541ad..7e1dcf2 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -148,6 +148,8 @@
 void Controller::EnterState(AutofillAssistantState state) {
   if (state_ == state)
     return;
+  DCHECK_NE(state_, AutofillAssistantState::STOPPED)
+      << "Unexpected transition from STOPPED to " << static_cast<int>(state);
 
   state_ = state;
   GetUiController()->OnStateChanged(state);
@@ -205,10 +207,8 @@
   if (should_fail_after_checking_scripts_ &&
       ++total_script_check_count_ >= kAutostartCheckCountLimit) {
     should_fail_after_checking_scripts_ = false;
-    SetStatusMessage(
-        l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_DEFAULT_ERROR));
-    stop_reason_ = Metrics::AUTOSTART_TIMEOUT;
-    EnterState(AutofillAssistantState::STOPPED);
+    OnFatalError(l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_DEFAULT_ERROR),
+                 Metrics::AUTOSTART_TIMEOUT);
     return;
   }
 
@@ -224,6 +224,9 @@
 void Controller::OnGetScripts(const GURL& url,
                               bool result,
                               const std::string& response) {
+  if (state_ == AutofillAssistantState::STOPPED)
+    return;
+
   // If the domain of the current URL changed since the request was sent, the
   // response is not relevant anymore and can be safely discarded.
   if (url.host() != script_domain_)
@@ -266,10 +269,8 @@
                                   const ScriptExecutor::Result& result) {
   if (!result.success) {
     LOG(ERROR) << "Failed to execute script " << script_path;
-    SetStatusMessage(
-        l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_DEFAULT_ERROR));
-    stop_reason_ = Metrics::SCRIPT_FAILED;
-    EnterState(AutofillAssistantState::STOPPED);
+    OnFatalError(l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_DEFAULT_ERROR),
+                 Metrics::SCRIPT_FAILED);
     return;
   }
 
@@ -321,8 +322,13 @@
   GetOrCheckScripts(web_contents()->GetLastCommittedURL());
 }
 
-void Controller::GiveUp(Metrics::DropOutReason reason) {
-  SetStatusMessage(l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_GIVE_UP));
+void Controller::OnFatalError(const std::string& error_message,
+                              Metrics::DropOutReason reason) {
+  if (state_ == AutofillAssistantState::STOPPED)
+    return;
+
+  StopPeriodicScriptChecks();
+  SetStatusMessage(error_message);
   stop_reason_ = reason;
   EnterState(AutofillAssistantState::STOPPED);
 }
@@ -481,7 +487,8 @@
 
   // We're navigated to a page that has no scripts or the scripts have reached a
   // state from which they cannot recover through a DOM change.
-  GiveUp(Metrics::NO_SCRIPTS);
+  OnFatalError(l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_GIVE_UP),
+               Metrics::NO_SCRIPTS);
   return;
 }
 
@@ -489,7 +496,7 @@
     const std::vector<ScriptHandle>& runnable_scripts) {
   // Script selection is disabled when a script is already running. We will
   // check again and maybe update when the current script has finished.
-  if (script_tracker()->running())
+  if (script_tracker()->running() || state_ == AutofillAssistantState::STOPPED)
     return;
 
   if (!runnable_scripts.empty()) {
@@ -558,10 +565,8 @@
 
 void Controller::DidStartNavigation(
     content::NavigationHandle* navigation_handle) {
-  if (!started_)
-    return;
-
-  // The following types of navigations are allowed for the main frame:
+  // The following types of navigations are allowed for the main frame, when
+  // in PROMPT state:
   //  - first-time URL load
   //  - script-directed navigation, while a script is running unless
   //    there's a touchable area.
@@ -575,16 +580,14 @@
   //
   // Everything else, such as going back to a previous page, or refreshing the
   // page is considered an end condition.
-  if (navigation_handle->IsInMainFrame() &&
+  if (state_ == AutofillAssistantState::PROMPT &&
+      navigation_handle->IsInMainFrame() &&
       web_contents()->GetLastCommittedURL().is_valid() &&
       !navigation_handle->WasServerRedirect() &&
       !navigation_handle->IsSameDocument() &&
       !navigation_handle->IsRendererInitiated()) {
-    // The action can define a touchable element area that prevents navigation.
-    if (!script_tracker_ || !script_tracker()->running() ||
-        touchable_element_area()->HasElements()) {
-      GiveUp(Metrics::NAVIGATION);
-    }
+    OnFatalError(l10n_util::GetStringUTF8(IDS_AUTOFILL_ASSISTANT_GIVE_UP),
+                 Metrics::NAVIGATION);
   }
 }
 
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 0bf5641..cf9ceb7 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -103,7 +103,10 @@
   void StartPeriodicScriptChecks();
   void StopPeriodicScriptChecks();
   void OnPeriodicScriptCheck();
-  void GiveUp(Metrics::DropOutReason reason);
+
+  // Shows the given message and stops the controller with |reason|.
+  void OnFatalError(const std::string& error_message,
+                    Metrics::DropOutReason reason);
 
   // Runs autostart scripts from |runnable_scripts|, if the conditions are
   // right. Returns true if a script was auto-started.
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index e0aedf5..843a939 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -564,6 +564,9 @@
     // Server payload originally sent by the server. This should
     // be transmitted as-is by the client without interpreting.
     optional bytes server_payload = 5;
+
+    // The chip is only visible if the given element exists.
+    optional ElementReferenceProto show_only_if_element_exists = 6;
   }
   repeated Choice choices = 4;
 }
diff --git a/components/invalidation/impl/fcm_invalidation_service.cc b/components/invalidation/impl/fcm_invalidation_service.cc
index 32f49ea..162a009 100644
--- a/components/invalidation/impl/fcm_invalidation_service.cc
+++ b/components/invalidation/impl/fcm_invalidation_service.cc
@@ -4,7 +4,10 @@
 
 #include "components/invalidation/impl/fcm_invalidation_service.h"
 
+#include <memory>
+
 #include "base/bind.h"
+#include "base/i18n/time_formatting.h"
 #include "base/metrics/histogram_macros.h"
 #include "components/gcm_driver/instance_id/instance_id_driver.h"
 #include "components/invalidation/impl/fcm_invalidator.h"
@@ -124,22 +127,26 @@
 void FCMInvalidationService::RequestDetailedStatus(
     base::RepeatingCallback<void(const base::DictionaryValue&)> return_callback)
     const {
+  return_callback.Run(*diagnostic_info_.CollectDebugData());
   if (IsStarted()) {
     invalidator_->RequestDetailedStatus(return_callback);
   }
 }
 
 void FCMInvalidationService::OnActiveAccountLogin() {
+  diagnostic_info_.active_account_login = base::Time::Now();
   if (!IsStarted() && IsReadyToStart())
     StartInvalidator();
 }
 
 void FCMInvalidationService::OnActiveAccountRefreshTokenUpdated() {
+  diagnostic_info_.active_account_token_updated = base::Time::Now();
   if (!IsStarted() && IsReadyToStart())
     StartInvalidator();
 }
 
 void FCMInvalidationService::OnActiveAccountLogout() {
+  diagnostic_info_.active_account_logged_out = base::Time::Now();
   if (IsStarted()) {
     StopInvalidator();
     if (!client_id_.empty())
@@ -184,7 +191,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!invalidator_);
   DCHECK(IsReadyToStart());
-
+  diagnostic_info_.service_was_started = base::Time::Now();
   auto network = std::make_unique<syncer::FCMNetworkHandler>(
       gcm_driver_, instance_id_driver_, kInvalidationGCMSenderId,
       kApplicationName);
@@ -204,12 +211,14 @@
 
 void FCMInvalidationService::StopInvalidator() {
   DCHECK(invalidator_);
+  diagnostic_info_.service_was_stopped = base::Time::Now();
   // TODO(melandory): reset the network.
   invalidator_->UnregisterHandler(this);
   invalidator_.reset();
 }
 
 void FCMInvalidationService::PopulateClientID() {
+  diagnostic_info_.instance_id_requested = base::Time::Now();
   client_id_ = pref_service_->GetString(prefs::kFCMInvalidationClientIDCache);
   instance_id::InstanceID* instance_id =
       instance_id_driver_->GetInstanceID(kApplicationName);
@@ -226,6 +235,7 @@
 }
 
 void FCMInvalidationService::OnInstanceIdRecieved(const std::string& id) {
+  diagnostic_info_.instance_id_received = base::Time::Now();
   if (client_id_ != id) {
     client_id_ = id;
     pref_service_->SetString(prefs::kFCMInvalidationClientIDCache, id);
@@ -246,4 +256,30 @@
   update_was_requested_ = false;
 }
 
+FCMInvalidationService::Diagnostics::Diagnostics() {}
+
+std::unique_ptr<base::DictionaryValue>
+FCMInvalidationService::Diagnostics::CollectDebugData() const {
+  std::unique_ptr<base::DictionaryValue> status =
+      std::make_unique<base::DictionaryValue>();
+
+  status->SetString("Active account login",
+                    base::TimeFormatShortDateAndTime(active_account_login));
+  status->SetString(
+      "Active account token updated",
+      base::TimeFormatShortDateAndTime(active_account_token_updated));
+  status->SetString(
+      "Active account logged out",
+      base::TimeFormatShortDateAndTime(active_account_logged_out));
+  status->SetString("Instance id requested",
+                    base::TimeFormatShortDateAndTime(instance_id_requested));
+  status->SetString("Instance id received",
+                    base::TimeFormatShortDateAndTime(instance_id_received));
+  status->SetString("Service was stopped",
+                    base::TimeFormatShortDateAndTime(service_was_stopped));
+  status->SetString("Service was started",
+                    base::TimeFormatShortDateAndTime(service_was_started));
+  return status;
+}
+
 }  // namespace invalidation
diff --git a/components/invalidation/impl/fcm_invalidation_service.h b/components/invalidation/impl/fcm_invalidation_service.h
index 65b1314..465a265 100644
--- a/components/invalidation/impl/fcm_invalidation_service.h
+++ b/components/invalidation/impl/fcm_invalidation_service.h
@@ -6,7 +6,7 @@
 #define COMPONENTS_INVALIDATION_IMPL_FCM_INVALIDATION_SERVICE_H_
 
 #include "base/macros.h"
-#include "base/timer/timer.h"
+#include "base/time/time.h"
 #include "components/gcm_driver/instance_id/instance_id.h"
 #include "components/invalidation/impl/invalidation_logger.h"
 #include "components/invalidation/impl/invalidator_registrar_with_memory.h"
@@ -82,6 +82,21 @@
   void InitForTest(syncer::Invalidator* invalidator);
 
  private:
+  struct Diagnostics {
+    Diagnostics();
+
+    // Collect all the internal variables in a single readable dictionary.
+    std::unique_ptr<base::DictionaryValue> CollectDebugData() const;
+
+    base::Time active_account_login;
+    base::Time active_account_token_updated;
+    base::Time active_account_logged_out;
+    base::Time instance_id_requested;
+    base::Time instance_id_received;
+    base::Time service_was_stopped;
+    base::Time service_was_started;
+  };
+
   bool IsReadyToStart();
   bool IsStarted() const;
 
@@ -111,6 +126,7 @@
   syncer::ParseJSONCallback parse_json_;
   network::mojom::URLLoaderFactory* loader_factory_;
   bool update_was_requested_ = false;
+  Diagnostics diagnostic_info_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/components/invalidation/impl/fcm_invalidation_service_unittest.cc b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
index db1acdf..1945374 100644
--- a/components/invalidation/impl/fcm_invalidation_service_unittest.cc
+++ b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
@@ -187,7 +187,7 @@
   invalidator->RequestDetailedStatus(
       base::BindRepeating(&internal::FakeCallbackContainer::FakeCallback,
                           fake_container.weak_ptr_factory_.GetWeakPtr()));
-  EXPECT_FALSE(fake_container.called_);
+  EXPECT_TRUE(fake_container.called_);
 
   delegate->InitializeInvalidationService();
 
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
index ef15682..a0c6f79f 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
@@ -571,6 +571,7 @@
       }));
 
   ASSERT_THAT(batch, NotNull());
+  EXPECT_TRUE(batch->HasNext());
   while (batch->HasNext()) {
     const syncer::KeyAndData& data_pair = batch->Next();
     EXPECT_EQ("hidden", data_pair.second->specifics.password()
diff --git a/components/payments/content/payment_request_spec.cc b/components/payments/content/payment_request_spec.cc
index c3406df..3613256 100644
--- a/components/payments/content/payment_request_spec.cc
+++ b/components/payments/content/payment_request_spec.cc
@@ -193,7 +193,6 @@
            details_->shipping_address_errors->postal_code.empty() &&
            details_->shipping_address_errors->recipient.empty() &&
            details_->shipping_address_errors->region.empty() &&
-           details_->shipping_address_errors->region_code.empty() &&
            details_->shipping_address_errors->sorting_code.empty());
 }
 
diff --git a/components/payments/core/payments_validators.cc b/components/payments/core/payments_validators.cc
index b90b0db..4bdbd5e9 100644
--- a/components/payments/core/payments_validators.cc
+++ b/components/payments/core/payments_validators.cc
@@ -129,7 +129,6 @@
          IsValidErrorMsgFormat(errors->postal_code, optional_error_message) &&
          IsValidErrorMsgFormat(errors->recipient, optional_error_message) &&
          IsValidErrorMsgFormat(errors->region, optional_error_message) &&
-         IsValidErrorMsgFormat(errors->region_code, optional_error_message) &&
          IsValidErrorMsgFormat(errors->sorting_code, optional_error_message);
 }
 
diff --git a/components/payments/core/payments_validators_unittest.cc b/components/payments/core/payments_validators_unittest.cc
index 3e09be78..81a4eae1 100644
--- a/components/payments/core/payments_validators_unittest.cc
+++ b/components/payments/core/payments_validators_unittest.cc
@@ -268,7 +268,6 @@
   const char* m_shipping_address_postal_code = "";
   const char* m_shipping_address_recipient = "";
   const char* m_shipping_address_region = "";
-  const char* m_shipping_address_region_code = "";
   const char* m_shipping_address_sorting_code = "";
   bool expected_valid;
 };
@@ -302,7 +301,6 @@
   shipping_address->postal_code = test_case.m_shipping_address_postal_code;
   shipping_address->recipient = test_case.m_shipping_address_recipient;
   shipping_address->region = test_case.m_shipping_address_region;
-  shipping_address->region_code = test_case.m_shipping_address_region_code;
   shipping_address->sorting_code = test_case.m_shipping_address_sorting_code;
 
   errors->payer = std::move(payer);
@@ -352,7 +350,6 @@
         VALIDATION_ERRORS_TEST_CASE(shipping_address_postal_code, "test", true),
         VALIDATION_ERRORS_TEST_CASE(shipping_address_recipient, "test", true),
         VALIDATION_ERRORS_TEST_CASE(shipping_address_region, "test", true),
-        VALIDATION_ERRORS_TEST_CASE(shipping_address_region_code, "test", true),
         VALIDATION_ERRORS_TEST_CASE(shipping_address_sorting_code,
                                     "test",
                                     true),
@@ -392,9 +389,6 @@
         VALIDATION_ERRORS_TEST_CASE(shipping_address_region,
                                     LongString2049(),
                                     false),
-        VALIDATION_ERRORS_TEST_CASE(shipping_address_region_code,
-                                    LongString2049(),
-                                    false),
         VALIDATION_ERRORS_TEST_CASE(shipping_address_sorting_code,
                                     LongString2049(),
                                     false)));
diff --git a/components/payments/mojom/payment_request_data.mojom b/components/payments/mojom/payment_request_data.mojom
index e1e008c3..19f56b9 100644
--- a/components/payments/mojom/payment_request_data.mojom
+++ b/components/payments/mojom/payment_request_data.mojom
@@ -74,6 +74,5 @@
   string postal_code;
   string recipient;
   string region;
-  string region_code;
   string sorting_code;
 };
diff --git a/components/policy/proto/install_attributes.proto b/components/policy/proto/install_attributes.proto
index 396794b..a1f0368 100644
--- a/components/policy/proto/install_attributes.proto
+++ b/components/policy/proto/install_attributes.proto
@@ -8,13 +8,13 @@
 
 package cryptohome;
 
-// This must match install_attributes.proto in the Chromium OS cryptohome
-// repository. Only the fields that are relevant to the browser are present
-// here.
 message SerializedInstallAttributes {
   message Attribute {
     required string name = 1;
     required bytes value = 2;
   }
+  // Specifies the version of the install attributes used to write the file.
+  // Should be incremented when fallback behavior is needed.
+  optional uint32 version = 1 [default = 1];
   repeated Attribute attributes = 2;
 }
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 995571468..bb76880a 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -7714,7 +7714,6 @@
       capture only be available to URLs configured in VideoCaptureAllowedUrls.
 
       This policy affects all types of video inputs and not only the built-in camera.''',
-      'arc_support': 'For Android apps, this policy affects the built-in camera only. When this policy is set to true, the camera is disabled for all Android apps, with no exceptions.',
     },
     {
       'name': 'VideoCaptureAllowedUrls',
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index d85aaee..23518fd8 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -22,6 +22,8 @@
 # both by IdentityManager and by its clients.
 static_library("shared") {
   sources = [
+    "account_consistency_method.cc",
+    "account_consistency_method.h",
     "account_info.cc",
     "account_info.h",
     "account_info_util.cc",
@@ -64,8 +66,6 @@
 # not add code to this target without consulting with blundell@chromium.org.
 static_library("internals") {
   sources = [
-    "account_consistency_method.cc",
-    "account_consistency_method.h",
     "account_fetcher_service.cc",
     "account_fetcher_service.h",
     "account_info_fetcher.cc",
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc
index 3a4fb30a..ee6bce9 100644
--- a/content/browser/renderer_host/media/video_capture_controller.cc
+++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -266,7 +266,7 @@
       device_launcher_(std::move(device_launcher)),
       emit_log_message_cb_(std::move(emit_log_message_cb)),
       device_launch_observer_(nullptr),
-      state_(VIDEO_CAPTURE_STATE_STARTING),
+      state_(blink::VIDEO_CAPTURE_STATE_STARTING),
       has_received_frames_(false),
       weak_ptr_factory_(this) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -314,7 +314,7 @@
     video_capture_format_ = params.requested_format;
 
   // Signal error in case device is already in error state.
-  if (state_ == VIDEO_CAPTURE_STATE_ERROR) {
+  if (state_ == blink::VIDEO_CAPTURE_STATE_ERROR) {
     event_handler->OnError(
         id,
         media::VideoCaptureError::kVideoCaptureControllerIsAlreadyInErrorState);
@@ -326,14 +326,14 @@
     return;
 
   // If the device has reported OnStarted event, report it to this client here.
-  if (state_ == VIDEO_CAPTURE_STATE_STARTED)
+  if (state_ == blink::VIDEO_CAPTURE_STATE_STARTED)
     event_handler->OnStarted(id);
 
   std::unique_ptr<ControllerClient> client =
       std::make_unique<ControllerClient>(id, event_handler, session_id, params);
   // If we already have gotten frame_info from the device, repeat it to the new
   // client.
-  if (state_ != VIDEO_CAPTURE_STATE_ERROR) {
+  if (state_ != blink::VIDEO_CAPTURE_STATE_ERROR) {
     controller_clients_.push_back(std::move(client));
   }
 }
@@ -502,7 +502,7 @@
   buffer_context_iter->set_frame_feedback_id(frame_feedback_id);
   DCHECK(!buffer_context_iter->HasConsumers());
 
-  if (state_ != VIDEO_CAPTURE_STATE_ERROR) {
+  if (state_ != blink::VIDEO_CAPTURE_STATE_ERROR) {
     const int buffer_context_id = buffer_context_iter->buffer_context_id();
     for (const auto& client : controller_clients_) {
       if (client->session_closed || client->paused)
@@ -574,7 +574,7 @@
 
 void VideoCaptureController::OnError(media::VideoCaptureError error) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  state_ = VIDEO_CAPTURE_STATE_ERROR;
+  state_ = blink::VIDEO_CAPTURE_STATE_ERROR;
   PerformForClientsWithOpenSession(base::BindRepeating(&CallOnError, error));
 }
 
@@ -616,7 +616,7 @@
 
 void VideoCaptureController::OnStarted() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  state_ = VIDEO_CAPTURE_STATE_STARTED;
+  state_ = blink::VIDEO_CAPTURE_STATE_STARTED;
   PerformForClientsWithOpenSession(base::BindRepeating(&CallOnStarted));
 }
 
diff --git a/content/browser/renderer_host/media/video_capture_controller.h b/content/browser/renderer_host/media/video_capture_controller.h
index 9b358f7..54b2e4c 100644
--- a/content/browser/renderer_host/media/video_capture_controller.h
+++ b/content/browser/renderer_host/media/video_capture_controller.h
@@ -16,10 +16,10 @@
 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
 #include "content/browser/renderer_host/media/video_capture_provider.h"
 #include "content/common/content_export.h"
-#include "content/common/media/video_capture.h"
 #include "content/public/browser/video_capture_device_launcher.h"
 #include "media/capture/video/video_frame_receiver.h"
 #include "media/capture/video_capture_types.h"
+#include "third_party/blink/public/common/media/video_capture.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 
 namespace content {
@@ -257,7 +257,7 @@
 
   // Takes on only the states 'STARTING', 'STARTED' and 'ERROR'. 'ERROR' is an
   // absorbing state which stops the flow of data to clients.
-  VideoCaptureState state_;
+  blink::VideoCaptureState state_;
 
   FrameDropLogState frame_drop_log_state_;
 
diff --git a/content/browser/service_worker/service_worker_client_utils.cc b/content/browser/service_worker/service_worker_client_utils.cc
index 7c670500..84d13a1 100644
--- a/content/browser/service_worker/service_worker_client_utils.cc
+++ b/content/browser/service_worker/service_worker_client_utils.cc
@@ -475,6 +475,39 @@
                      std::move(clients)));
 }
 
+void DidGetExecutionReadyClient(
+    const base::WeakPtr<ServiceWorkerContextCore>& context,
+    const std::string& client_uuid,
+    const GURL& sane_origin,
+    NavigationCallback callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (!context) {
+    std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort,
+                            nullptr /* client_info */);
+    return;
+  }
+
+  ServiceWorkerProviderHost* provider_host =
+      context->GetProviderHostByClientID(client_uuid);
+  if (!provider_host || !provider_host->is_execution_ready()) {
+    // The page was destroyed before it became execution ready.  Tell the
+    // renderer the page opened but it doesn't have access to it.
+    std::move(callback).Run(blink::ServiceWorkerStatusCode::kOk,
+                            nullptr /* client_info */);
+    return;
+  }
+
+  CHECK_EQ(provider_host->url().GetOrigin(), sane_origin);
+
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {BrowserThread::UI},
+      base::BindOnce(&GetWindowClientInfoOnUI, provider_host->process_id(),
+                     provider_host->route_id(), provider_host->create_time(),
+                     provider_host->client_uuid()),
+      base::BindOnce(std::move(callback), blink::ServiceWorkerStatusCode::kOk));
+}
+
 }  // namespace
 
 void FocusWindowClient(ServiceWorkerProviderHost* provider_host,
@@ -597,20 +630,26 @@
 
   for (std::unique_ptr<ServiceWorkerContextCore::ProviderHostIterator> it =
            context->GetClientProviderHostIterator(
-               origin, false /* include_reserved_clients */);
+               origin, true /* include_reserved_clients */);
        !it->IsAtEnd(); it->Advance()) {
     ServiceWorkerProviderHost* provider_host = it->GetProviderHost();
     if (provider_host->process_id() != render_process_id ||
         provider_host->frame_id() != render_frame_id) {
       continue;
     }
-    base::PostTaskWithTraitsAndReplyWithResult(
-        FROM_HERE, {BrowserThread::UI},
-        base::BindOnce(&GetWindowClientInfoOnUI, provider_host->process_id(),
-                       provider_host->route_id(), provider_host->create_time(),
-                       provider_host->client_uuid()),
-        base::BindOnce(std::move(callback),
-                       blink::ServiceWorkerStatusCode::kOk));
+    // DidNavigate must be called with a preparation complete client (the
+    // navigation was committed), but the client might not be execution ready
+    // yet (Blink hasn't yet created the Document).
+    DCHECK(provider_host->is_response_committed());
+    if (!provider_host->is_execution_ready()) {
+      provider_host->AddExecutionReadyCallback(base::BindOnce(
+          &DidGetExecutionReadyClient, context, provider_host->client_uuid(),
+          origin, std::move(callback)));
+      return;
+    }
+
+    DidGetExecutionReadyClient(context, provider_host->client_uuid(), origin,
+                               std::move(callback));
     return;
   }
 
diff --git a/content/browser/service_worker/service_worker_navigation_handle_core.cc b/content/browser/service_worker/service_worker_navigation_handle_core.cc
index 357d82e..474af5de4 100644
--- a/content/browser/service_worker/service_worker_navigation_handle_core.cc
+++ b/content/browser/service_worker/service_worker_navigation_handle_core.cc
@@ -45,9 +45,10 @@
   // pointer tied to the lifetime of ServiceWorkerProviderHost, and send the
   // Mojo pointer to the renderer on navigation commit. If the handle core dies
   // before that, the provider host would be destroyed by Mojo connection error.
-  if (!host->is_execution_ready())
+  if (!host->is_response_committed()) {
     context->RemoveProviderHost(ChildProcessHost::kInvalidUniqueID,
                                 provider_id_);
+  }
 }
 
 void ServiceWorkerNavigationHandleCore::DidPreCreateProviderHost(
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 8b1d09d..350603de2 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -283,9 +283,14 @@
     int process_id,
     blink::mojom::ServiceWorkerProviderHostInfoPtr info,
     base::WeakPtr<ServiceWorkerContextCore> context) {
+  // This function seems to be for legacy purposes. It is a renderer-side
+  // created provider, that will probably never be used and never have a valid
+  // URL.
+  // TODO(falken): Try to remove this code path.
   auto host = base::WrapUnique(
       new ServiceWorkerProviderHost(process_id, std::move(info), context));
-  host->is_execution_ready_ = true;
+  host->TransitionToClientPhase(ClientPhase::kResponseCommitted);
+  host->TransitionToClientPhase(ClientPhase::kExecutionReady);
   return host;
 }
 
@@ -535,28 +540,8 @@
   // SetController message should be sent only for clients.
   DCHECK(IsProviderForClient());
 
-  // The final response hasn't been committed yet, so there's no reason to send
-  // the controller since it can be changed again before the final response.
-  if (!is_execution_ready_) {
-    if (client_type() == blink::mojom::ServiceWorkerClientType::kWindow) {
-      // |this| is hosting a reserved client undergoing navigation. The
-      // controller will be sent on navigation commit. See CommitNavigation in
-      // frame.mojom.
-      DCHECK(!container_.is_bound());
-      return;
-    }
-    DCHECK_EQ(blink::mojom::ServiceWorkerClientType::kSharedWorker,
-              client_type());
-
-    // NetworkService (PlzWorker):
-    if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-      // When PlzWorker is enabled, the controller will be sent when the
-      // response is committed to the renderer at SharedWorkerHost::Start().
-      return;
-    }
-    // When NetworkService is disabled and the client is for a shared worker,
-    // the controller won't be sent on response commit, so send it here.
-  }
+  if (!IsControllerDecided())
+    return;
 
   SendSetControllerServiceWorker(notify_controllerchange);
 }
@@ -761,32 +746,13 @@
 }
 
 void ServiceWorkerProviderHost::CountFeature(blink::mojom::WebFeature feature) {
-  // CountFeature message should be sent only for clients.
+  // CountFeature is a message about the client's controller. It should be sent
+  // only for clients.
   DCHECK(IsProviderForClient());
 
-  // The final response hasn't been committed yet, so there's no reason to send
-  // the use counter since it can be changed again before the final response.
-  if (!is_execution_ready_) {
-    if (client_type() == blink::mojom::ServiceWorkerClientType::kWindow) {
-      // |this| is hosting a reserved client undergoing navigation. The use
-      // counter will be sent correctly in CompleteNavigationInitialized()
-      // later.
-      DCHECK(!container_.is_bound());
-      return;
-    }
-    DCHECK_EQ(blink::mojom::ServiceWorkerClientType::kSharedWorker,
-              client_type());
-
-    // NetworkService (PlzWorker):
-    if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-      // When PlzWorker is enabled, the use counter will be sent when the
-      // response is committed to the renderer at SharedWorkerHost::Start().
-      // TODO(nhiroki): Send the use counter on starting the shared worker.
-      return;
-    }
-    // When NetworkService is disabled and the client is for a shared worker,
-    // the use counter won't be sent on response commit, so send it here.
-  }
+  // And only when loading finished so the controller is really settled.
+  if (!IsControllerDecided())
+    return;
 
   container_->CountFeature(feature);
 }
@@ -819,7 +785,7 @@
   DCHECK_EQ(info_->provider_id, info->provider_id);
   DCHECK_NE(MSG_ROUTING_NONE, info->route_id);
 
-  SetExecutionReady();
+  TransitionToClientPhase(ClientPhase::kResponseCommitted);
 
   // Connect with the blink::mojom::ServiceWorkerContainer on the renderer.
   DCHECK(!container_.is_bound());
@@ -879,6 +845,7 @@
 void ServiceWorkerProviderHost::CompleteSharedWorkerPreparation() {
   DCHECK_EQ(blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
             provider_type());
+  TransitionToClientPhase(ClientPhase::kResponseCommitted);
   SetExecutionReady();
 }
 
@@ -989,6 +956,39 @@
                             notify_controllerchange);
 }
 
+bool ServiceWorkerProviderHost::IsControllerDecided() const {
+  DCHECK(IsProviderForClient());
+
+  if (is_response_committed())
+    return true;
+
+  // TODO(falken): This function just becomes |is_response_committed()|
+  // when NetworkService is enabled, so remove/simplify it when
+  // non-NetworkService code is removed.
+
+  switch (client_type()) {
+    case blink::mojom::ServiceWorkerClientType::kWindow:
+      // |this| is hosting a reserved client undergoing navigation. Don't send
+      // the controller since it can be changed again before the final
+      // response. The controller will be sent on navigation commit. See
+      // CommitNavigation in frame.mojom.
+      return false;
+    case blink::mojom::ServiceWorkerClientType::kSharedWorker:
+      // NetworkService (PlzWorker):
+      if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+        // When PlzWorker is enabled, the controller will be sent when the
+        // response is committed to the renderer at SharedWorkerHost::Start().
+        return false;
+      }
+      return true;
+    case blink::mojom::ServiceWorkerClientType::kAll:
+      NOTREACHED();
+  }
+
+  NOTREACHED();
+  return true;
+}
+
 #if DCHECK_IS_ON()
 void ServiceWorkerProviderHost::CheckControllerConsistency() const {
   if (!controller_) {
@@ -1293,6 +1293,25 @@
   versions_to_update_.clear();
 }
 
+void ServiceWorkerProviderHost::OnExecutionReady() {
+  if (!IsProviderForClient()) {
+    mojo::ReportBadMessage("SWPH_OER_NOT_CLIENT");
+    return;
+  }
+
+  if (is_execution_ready()) {
+    // We can get here for providers that were created via Create() instead of
+    // being precreated, i.e., shared workers in the non-S13nServiceWorker path
+    // and for renderer-initiated navigations. Just ignore if this is already
+    // execution ready.
+    // TODO(falken): See if this can turn into a ReportBadMessage after the
+    // non-S13nServiceWorker path or Create() is removed.
+    return;
+  }
+
+  SetExecutionReady();
+}
+
 bool ServiceWorkerProviderHost::IsValidGetRegistrationMessage(
     const GURL& client_url,
     std::string* out_error) const {
@@ -1410,13 +1429,31 @@
 
 void ServiceWorkerProviderHost::AddExecutionReadyCallback(
     ExecutionReadyCallback callback) {
-  DCHECK(!is_execution_ready_);
+  DCHECK(!is_execution_ready());
   execution_ready_callbacks_.push_back(std::move(callback));
 }
 
+bool ServiceWorkerProviderHost::is_response_committed() const {
+  DCHECK(IsProviderForClient());
+  switch (client_phase_) {
+    case ClientPhase::kInitial:
+      return false;
+    case ClientPhase::kResponseCommitted:
+    case ClientPhase::kExecutionReady:
+      return true;
+  }
+  NOTREACHED();
+  return false;
+}
+
+bool ServiceWorkerProviderHost::is_execution_ready() const {
+  DCHECK(IsProviderForClient());
+  return client_phase_ == ClientPhase::kExecutionReady;
+}
+
 void ServiceWorkerProviderHost::SetExecutionReady() {
-  DCHECK(!is_execution_ready_);
-  is_execution_ready_ = true;
+  DCHECK(!is_execution_ready());
+  TransitionToClientPhase(ClientPhase::kExecutionReady);
   RunExecutionReadyCallbacks();
 }
 
@@ -1427,4 +1464,21 @@
       FROM_HERE, base::BindOnce(&RunCallbacks, std::move(callbacks)));
 }
 
+void ServiceWorkerProviderHost::TransitionToClientPhase(ClientPhase new_phase) {
+  if (client_phase_ == new_phase)
+    return;
+  switch (client_phase_) {
+    case ClientPhase::kInitial:
+      DCHECK_EQ(new_phase, ClientPhase::kResponseCommitted);
+      break;
+    case ClientPhase::kResponseCommitted:
+      DCHECK_EQ(new_phase, ClientPhase::kExecutionReady);
+      break;
+    case ClientPhase::kExecutionReady:
+      NOTREACHED();
+      break;
+  }
+  client_phase_ = new_phase;
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index ea67ded..4f15e953 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -286,7 +286,7 @@
   // OriginCanAccessServiceWorkers returned false).
   //
   // The URL may also change on redirects during loading. Once
-  // is_execution_ready() is true, the URL should no longer change.
+  // is_response_committed() is true, the URL should no longer change.
   const GURL& url() const;
 
   // The URL representing the first-party site for this context. See
@@ -318,7 +318,7 @@
   // TODO(crbug.com/866353): This should be unneccessary: registration code
   // already avoids claiming clients that are not execution ready. However
   // there may be edge cases with shared workers (pre-NetS13nServiceWorker) and
-  // about:blank iframes, since |is_execution_ready_| is initialized true for
+  // about:blank iframes, since |is_execution_ready()| is initialized true for
   // them. Try to remove this after S13nServiceWorker.
   void AllowSetControllerRegistration(bool allow) {
     allow_set_controller_registration_ = allow;
@@ -406,7 +406,8 @@
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info);
 
   // Called when the shared worker main script resource has finished loading.
-  // After this is called, is_execution_ready() returns true.
+  // After this is called, is_response_committed() and is_execution_ready()
+  // return true.
   void CompleteSharedWorkerPreparation();
 
   // For service worker clients. The host keeps track of all the prospective
@@ -468,9 +469,28 @@
   // execution ready or if it is destroyed first.
   void AddExecutionReadyCallback(ExecutionReadyCallback callback);
 
-  bool is_execution_ready() const { return is_execution_ready_; }
+  // For service worker clients. True if the response for the main resource load
+  // was committed to the renderer. When this is false, the client's URL may
+  // still change due to redirects.
+  bool is_response_committed() const;
+
+  // For service worker clients. True if the client is execution ready and
+  // therefore can be exposed to JavaScript. Execution ready implies connected
+  // to renderer.
+  // https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-execution-ready-flag
+  bool is_execution_ready() const;
 
  private:
+  // For service worker clients. The flow is kInitial -> kResponseCommitted ->
+  // kExecutionReady.
+  //
+  // - kInitial: The initial phase.
+  // - kResponseCommitted: The response for the main resource has been
+  //   committed to the renderer. This client's URL should no longer change.
+  // - kExecutionReady: This client can be exposed to JavaScript as a Client
+  //   object.
+  enum class ClientPhase { kInitial, kResponseCommitted, kExecutionReady };
+
   friend class LinkHeaderServiceWorkerTest;
   friend class ServiceWorkerProviderHostTest;
   friend class ServiceWorkerWriteToCacheJobTest;
@@ -539,6 +559,12 @@
   // instructs the renderer to dispatch a 'controllerchange' event.
   void SendSetControllerServiceWorker(bool notify_controllerchange);
 
+  // For service worker clients. Returns false if it's not yet time to send the
+  // renderer information about the controller. Basically returns false if this
+  // client is still loading so due to potential redirects the initial
+  // controller has not yet been decided.
+  bool IsControllerDecided() const;
+
 #if DCHECK_IS_ON()
   void CheckControllerConsistency() const;
 #endif  // DCHECK_IS_ON()
@@ -559,6 +585,7 @@
                               container_host_request) override;
   void Ping(PingCallback callback) override;
   void HintToUpdateServiceWorker() override;
+  void OnExecutionReady() override;
 
   // Callback for ServiceWorkerContextCore::RegisterServiceWorker().
   void RegistrationComplete(RegisterCallback callback,
@@ -613,6 +640,8 @@
 
   void RunExecutionReadyCallbacks();
 
+  void TransitionToClientPhase(ClientPhase new_phase);
+
   // A GUID that is web-exposed as FetchEvent.clientId.
   std::string client_uuid_;
 
@@ -715,10 +744,11 @@
   mojo::Binding<service_manager::mojom::InterfaceProvider>
       interface_provider_binding_;
 
-  // For service worker clients. |is_execution_ready_| is true if the main
-  // resource for this host has finished loading. When false, the document URL
-  // may still change due to redirects.
-  bool is_execution_ready_ = false;
+  // For service worker clients.
+  ClientPhase client_phase_ = ClientPhase::kInitial;
+
+  // For service worker clients. Callbacks to run upon transition to
+  // kExecutionReady.
   std::vector<ExecutionReadyCallback> execution_ready_callbacks_;
 
   // For service worker clients. The service workers in the chain of redirects
diff --git a/content/browser/service_worker/service_worker_provider_host_unittest.cc b/content/browser/service_worker/service_worker_provider_host_unittest.cc
index b1518df..33c69d2 100644
--- a/content/browser/service_worker/service_worker_provider_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -907,13 +907,63 @@
     remote_endpoint.BindWithProviderHostInfo(&info);
     GURL url = GURL("https://www.example.com/page");
     host->UpdateUrls(url, url);
+    FinishNavigation(host.get(), std::move(info));
     EXPECT_FALSE(CanFindClientProviderHost(host.get()));
 
-    FinishNavigation(host.get(), std::move(info));
+    base::RunLoop run_loop;
+    host->AddExecutionReadyCallback(run_loop.QuitClosure());
+    remote_endpoint.host_ptr()->get()->OnExecutionReady();
+    run_loop.Run();
     EXPECT_TRUE(CanFindClientProviderHost(host.get()));
   }
 }
 
+// Tests the client phase transitions for a navigation.
+TEST_P(ServiceWorkerProviderHostTest, ClientPhaseForWindow) {
+  base::WeakPtr<ServiceWorkerProviderHost> host =
+      ServiceWorkerProviderHost::PreCreateNavigationHost(
+          helper_->context()->AsWeakPtr(), true,
+          base::RepeatingCallback<WebContents*(void)>());
+  EXPECT_FALSE(host->is_response_committed());
+  EXPECT_FALSE(host->is_execution_ready());
+
+  blink::mojom::ServiceWorkerProviderHostInfoPtr info =
+      CreateProviderHostInfoForWindow(host->provider_id(), 1 /* route_id */);
+  ServiceWorkerRemoteProviderEndpoint remote_endpoint;
+  remote_endpoint.BindWithProviderHostInfo(&info);
+  GURL url = GURL("https://www.example.com/page");
+  host->UpdateUrls(url, url);
+  FinishNavigation(host.get(), std::move(info));
+  EXPECT_TRUE(host->is_response_committed());
+  EXPECT_FALSE(host->is_execution_ready());
+
+  base::RunLoop run_loop;
+  host->AddExecutionReadyCallback(run_loop.QuitClosure());
+  remote_endpoint.host_ptr()->get()->OnExecutionReady();
+  run_loop.Run();
+  EXPECT_TRUE(host->is_response_committed());
+  EXPECT_TRUE(host->is_execution_ready());
+}
+
+// Tests the client phase transitions for a shared worker.
+TEST_P(ServiceWorkerProviderHostTest, ClientPhaseForSharedWorker) {
+  auto provider_info =
+      blink::mojom::ServiceWorkerProviderInfoForSharedWorker::New();
+  base::WeakPtr<ServiceWorkerProviderHost> host =
+      ServiceWorkerProviderHost::PreCreateForSharedWorker(
+          context_->AsWeakPtr(), helper_->mock_render_process_id(),
+          &provider_info);
+  EXPECT_FALSE(host->is_response_committed());
+  EXPECT_FALSE(host->is_execution_ready());
+
+  const GURL url("https://www.example.com/shared_worker.js");
+  host->UpdateUrls(url, url);
+  host->CompleteSharedWorkerPreparation();
+
+  EXPECT_TRUE(host->is_response_committed());
+  EXPECT_TRUE(host->is_execution_ready());
+}
+
 // Tests that the service worker involved with a navigation (via
 // AddServiceWorkerToUpdate) is updated when the host for the navigation is
 // destroyed.
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 2a57158..3965af63 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -1196,6 +1196,28 @@
     return;
   }
   if (!provider_host->is_execution_ready()) {
+    // It's subtle why this ReportBadMessage is correct. Consider the
+    // sequence:
+    // 1. Page does ServiceWorker.postMessage().
+    // 2. Service worker does onmessage = (evt) => {evt.source.postMessage()};.
+    //
+    // The IPC sequence is:
+    // 1. Page sends NotifyExecutionReady() to its ServiceWorkerContainerHost
+    //    once created.
+    // 2. Page sends PostMessageToServiceWorker() to the object's
+    //    ServiceWorkerObjectHost.
+    // 3. Service worker sends PostMessageToClient() to its ServiceWorkerHost.
+    //
+    // It's guaranteed that 1. arrives before 2., since the
+    // ServiceWorkerObjectHost must have been sent over
+    // ServiceWorkerContainerHost (using Register, GetRegistrationForReady), so
+    // they are associated. After that 3. occurs and we get here and are
+    // guaranteed execution ready the above ordering.
+    //
+    // The above reasoning would break if there is a way for a page to get a
+    // ServiceWorkerObjectHost not associated with its
+    // ServiceWorkerContainerHost. If that world should occur, we should queue
+    // the message instead of crashing.
     mojo::ReportBadMessage(
         "Received Client#postMessage() request for a reserved client.");
     binding_.Close();
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index af1924a1..f5ccba79 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -184,7 +184,6 @@
     "media/media_player_delegate_messages.h",
     "media/media_player_messages_android.h",
     "media/peer_connection_tracker_messages.h",
-    "media/video_capture.h",
     "mime_sniffing_throttle.cc",
     "mime_sniffing_throttle.h",
     "mime_sniffing_url_loader.cc",
diff --git a/content/public/browser/shared_cors_origin_access_list.h b/content/public/browser/shared_cors_origin_access_list.h
index 936cba0..fce3a08 100644
--- a/content/public/browser/shared_cors_origin_access_list.h
+++ b/content/public/browser/shared_cors_origin_access_list.h
@@ -7,6 +7,7 @@
 
 #include <vector>
 
+#include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "content/common/content_export.h"
@@ -60,6 +61,8 @@
 
  private:
   friend class base::RefCountedThreadSafe<SharedCorsOriginAccessList>;
+
+  DISALLOW_COPY_AND_ASSIGN(SharedCorsOriginAccessList);
 };
 
 }  // namespace content
diff --git a/content/public/renderer/media_stream_video_sink.cc b/content/public/renderer/media_stream_video_sink.cc
index e439ab4..57351e45 100644
--- a/content/public/renderer/media_stream_video_sink.cc
+++ b/content/public/renderer/media_stream_video_sink.cc
@@ -17,7 +17,7 @@
 
 void MediaStreamVideoSink::ConnectToTrack(
     const blink::WebMediaStreamTrack& track,
-    const VideoCaptureDeliverFrameCB& callback,
+    const blink::VideoCaptureDeliverFrameCB& callback,
     bool is_sink_secure) {
   DCHECK(connected_track_.IsNull());
   connected_track_ = track;
diff --git a/content/public/renderer/media_stream_video_sink.h b/content/public/renderer/media_stream_video_sink.h
index cf3ea4d..b7a5f2c4 100644
--- a/content/public/renderer/media_stream_video_sink.h
+++ b/content/public/renderer/media_stream_video_sink.h
@@ -10,8 +10,8 @@
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "content/common/content_export.h"
-#include "content/common/media/video_capture.h"
 #include "media/capture/video_capturer_source.h"
+#include "third_party/blink/public/common/media/video_capture.h"
 #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_sink.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 
@@ -46,7 +46,7 @@
   // meets output protection requirement). Generally, this should be false
   // unless you know what you are doing.
   void ConnectToTrack(const blink::WebMediaStreamTrack& track,
-                      const VideoCaptureDeliverFrameCB& callback,
+                      const blink::VideoCaptureDeliverFrameCB& callback,
                       bool is_sink_secure);
   void DisconnectFromTrack();
 
diff --git a/content/renderer/media/stream/media_stream_video_capturer_source.cc b/content/renderer/media/stream/media_stream_video_capturer_source.cc
index eede041..3dc34f1 100644
--- a/content/renderer/media/stream/media_stream_video_capturer_source.cc
+++ b/content/renderer/media/stream/media_stream_video_capturer_source.cc
@@ -41,7 +41,7 @@
   // VideoCaptureDelegate Implementation.
   media::VideoCaptureFormats GetPreferredFormats() override;
   void StartCapture(const media::VideoCaptureParams& params,
-                    const VideoCaptureDeliverFrameCB& new_frame_callback,
+                    const blink::VideoCaptureDeliverFrameCB& new_frame_callback,
                     const RunningCallback& running_callback) override;
   void RequestRefreshFrame() override;
   void MaybeSuspend() override;
@@ -49,7 +49,7 @@
   void StopCapture() override;
 
  private:
-  void OnStateUpdate(VideoCaptureState state);
+  void OnStateUpdate(blink::VideoCaptureState state);
 
   // |session_id_| identifies the capture device used for this capture session.
   const media::VideoCaptureSessionId session_id_;
@@ -92,7 +92,7 @@
 
 void LocalVideoCapturerSource::StartCapture(
     const media::VideoCaptureParams& params,
-    const VideoCaptureDeliverFrameCB& new_frame_callback,
+    const blink::VideoCaptureDeliverFrameCB& new_frame_callback,
     const RunningCallback& running_callback) {
   DCHECK(params.requested_format.IsValid());
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -130,27 +130,27 @@
     base::ResetAndReturn(&stop_capture_cb_).Run();
 }
 
-void LocalVideoCapturerSource::OnStateUpdate(VideoCaptureState state) {
+void LocalVideoCapturerSource::OnStateUpdate(blink::VideoCaptureState state) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (running_callback_.is_null())
     return;
   switch (state) {
-    case VIDEO_CAPTURE_STATE_STARTED:
+    case blink::VIDEO_CAPTURE_STATE_STARTED:
       running_callback_.Run(true);
       break;
 
-    case VIDEO_CAPTURE_STATE_STOPPING:
-    case VIDEO_CAPTURE_STATE_STOPPED:
-    case VIDEO_CAPTURE_STATE_ERROR:
-    case VIDEO_CAPTURE_STATE_ENDED:
+    case blink::VIDEO_CAPTURE_STATE_STOPPING:
+    case blink::VIDEO_CAPTURE_STATE_STOPPED:
+    case blink::VIDEO_CAPTURE_STATE_ERROR:
+    case blink::VIDEO_CAPTURE_STATE_ENDED:
       release_device_cb_.Run();
       release_device_cb_ = manager_->UseDevice(session_id_);
       running_callback_.Run(false);
       break;
 
-    case VIDEO_CAPTURE_STATE_STARTING:
-    case VIDEO_CAPTURE_STATE_PAUSED:
-    case VIDEO_CAPTURE_STATE_RESUMED:
+    case blink::VIDEO_CAPTURE_STATE_STARTING:
+    case blink::VIDEO_CAPTURE_STATE_PAUSED:
+    case blink::VIDEO_CAPTURE_STATE_RESUMED:
       // Not applicable to reporting on device starts or errors.
       break;
   }
@@ -221,7 +221,7 @@
 }
 
 void MediaStreamVideoCapturerSource::StartSourceImpl(
-    const VideoCaptureDeliverFrameCB& frame_callback) {
+    const blink::VideoCaptureDeliverFrameCB& frame_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   state_ = STARTING;
   frame_callback_ = frame_callback;
diff --git a/content/renderer/media/stream/media_stream_video_capturer_source.h b/content/renderer/media/stream/media_stream_video_capturer_source.h
index 9ee1bd8..3350c19 100644
--- a/content/renderer/media/stream/media_stream_video_capturer_source.h
+++ b/content/renderer/media/stream/media_stream_video_capturer_source.h
@@ -11,8 +11,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
-#include "content/common/media/video_capture.h"
 #include "content/renderer/media/stream/media_stream_video_source.h"
+#include "third_party/blink/public/common/media/video_capture.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
 
 namespace media {
@@ -60,7 +60,7 @@
   void OnHasConsumers(bool has_consumers) override;
   void OnCapturingLinkSecured(bool is_secure) override;
   void StartSourceImpl(
-      const VideoCaptureDeliverFrameCB& frame_callback) override;
+      const blink::VideoCaptureDeliverFrameCB& frame_callback) override;
   void StopSourceImpl() override;
   void StopSourceForRestartImpl() override;
   void RestartSourceImpl(const media::VideoCaptureFormat& new_format) override;
@@ -97,7 +97,7 @@
   State state_ = STOPPED;
 
   media::VideoCaptureParams capture_params_;
-  VideoCaptureDeliverFrameCB frame_callback_;
+  blink::VideoCaptureDeliverFrameCB frame_callback_;
   DeviceVideoCapturerFactoryCallback device_video_capturer_factory_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaStreamVideoCapturerSource);
diff --git a/content/renderer/media/stream/media_stream_video_capturer_source_unittest.cc b/content/renderer/media/stream/media_stream_video_capturer_source_unittest.cc
index b1a3c3de..6e5993ba 100644
--- a/content/renderer/media/stream/media_stream_video_capturer_source_unittest.cc
+++ b/content/renderer/media/stream/media_stream_video_capturer_source_unittest.cc
@@ -36,11 +36,11 @@
   MOCK_METHOD0(GetPreferredFormats, media::VideoCaptureFormats());
   MOCK_METHOD3(MockStartCapture,
                void(const media::VideoCaptureParams& params,
-                    const VideoCaptureDeliverFrameCB& new_frame_callback,
+                    const blink::VideoCaptureDeliverFrameCB& new_frame_callback,
                     const RunningCallback& running_callback));
   MOCK_METHOD0(MockStopCapture, void());
   void StartCapture(const media::VideoCaptureParams& params,
-                    const VideoCaptureDeliverFrameCB& new_frame_callback,
+                    const blink::VideoCaptureDeliverFrameCB& new_frame_callback,
                     const RunningCallback& running_callback) override {
     running_cb_ = running_callback;
     capture_params_ = params;
@@ -224,7 +224,7 @@
 }
 
 TEST_F(MediaStreamVideoCapturerSourceTest, CaptureTimeAndMetadataPlumbing) {
-  VideoCaptureDeliverFrameCB deliver_frame_cb;
+  blink::VideoCaptureDeliverFrameCB deliver_frame_cb;
   media::VideoCapturerSource::RunningCallback running_cb;
 
   InSequence s;
diff --git a/content/renderer/media/stream/media_stream_video_renderer_sink.h b/content/renderer/media/stream/media_stream_video_renderer_sink.h
index f57286e..8e3df03d 100644
--- a/content/renderer/media/stream/media_stream_video_renderer_sink.h
+++ b/content/renderer/media/stream/media_stream_video_renderer_sink.h
@@ -9,9 +9,9 @@
 #include "base/macros.h"
 #include "base/threading/thread_checker.h"
 #include "content/common/content_export.h"
-#include "content/common/media/video_capture.h"
 #include "content/public/renderer/media_stream_video_renderer.h"
 #include "content/public/renderer/media_stream_video_sink.h"
+#include "third_party/blink/public/common/media/video_capture.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "ui/gfx/geometry/size.h"
 
diff --git a/content/renderer/media/stream/media_stream_video_source.cc b/content/renderer/media/stream/media_stream_video_source.cc
index 60512ac..95bb1758 100644
--- a/content/renderer/media/stream/media_stream_video_source.cc
+++ b/content/renderer/media/stream/media_stream_video_source.cc
@@ -48,7 +48,7 @@
 void MediaStreamVideoSource::AddTrack(
     MediaStreamVideoTrack* track,
     const VideoTrackAdapterSettings& track_adapter_settings,
-    const VideoCaptureDeliverFrameCB& frame_callback,
+    const blink::VideoCaptureDeliverFrameCB& frame_callback,
     const VideoTrackSettingsCallback& settings_callback,
     const VideoTrackFormatCallback& format_callback,
     const ConstraintsCallback& callback) {
@@ -434,7 +434,7 @@
 
 MediaStreamVideoSource::PendingTrackInfo::PendingTrackInfo(
     MediaStreamVideoTrack* track,
-    const VideoCaptureDeliverFrameCB& frame_callback,
+    const blink::VideoCaptureDeliverFrameCB& frame_callback,
     const VideoTrackSettingsCallback& settings_callback,
     const VideoTrackFormatCallback& format_callback,
     std::unique_ptr<VideoTrackAdapterSettings> adapter_settings,
diff --git a/content/renderer/media/stream/media_stream_video_source.h b/content/renderer/media/stream/media_stream_video_source.h
index 7892882..c05eec85 100644
--- a/content/renderer/media/stream/media_stream_video_source.h
+++ b/content/renderer/media/stream/media_stream_video_source.h
@@ -14,11 +14,11 @@
 #include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "content/common/content_export.h"
-#include "content/common/media/video_capture.h"
 #include "content/renderer/media/stream/media_stream_types.h"
 #include "content/renderer/media/stream/secure_display_link_tracker.h"
 #include "media/base/video_frame.h"
 #include "media/capture/video_capture_types.h"
+#include "third_party/blink/public/common/media/video_capture.h"
 #include "third_party/blink/public/platform/modules/mediastream/platform_media_stream_source.h"
 #include "third_party/blink/public/platform/web_media_constraints.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
@@ -69,7 +69,7 @@
   // Puts |track| in the registered tracks list.
   void AddTrack(MediaStreamVideoTrack* track,
                 const VideoTrackAdapterSettings& track_adapter_settings,
-                const VideoCaptureDeliverFrameCB& frame_callback,
+                const blink::VideoCaptureDeliverFrameCB& frame_callback,
                 const VideoTrackSettingsCallback& settings_callback,
                 const VideoTrackFormatCallback& format_callback,
                 const ConstraintsCallback& callback);
@@ -170,7 +170,7 @@
   // An implementation must call |frame_callback| on the IO thread with the
   // captured frames.
   virtual void StartSourceImpl(
-      const VideoCaptureDeliverFrameCB& frame_callback) = 0;
+      const blink::VideoCaptureDeliverFrameCB& frame_callback) = 0;
   void OnStartDone(blink::MediaStreamRequestResult result);
 
   // A subclass that supports restart must override this method such that it
@@ -260,7 +260,7 @@
 
   // Actually adds |track| to this source, provided the source has started.
   void FinalizeAddTrack(MediaStreamVideoTrack* track,
-                        const VideoCaptureDeliverFrameCB& frame_callback,
+                        const blink::VideoCaptureDeliverFrameCB& frame_callback,
                         const VideoTrackAdapterSettings& adapter_settings);
   void StartFrameMonitoring();
   void UpdateTrackSettings(MediaStreamVideoTrack* track,
@@ -272,7 +272,7 @@
   struct PendingTrackInfo {
     PendingTrackInfo(
         MediaStreamVideoTrack* track,
-        const VideoCaptureDeliverFrameCB& frame_callback,
+        const blink::VideoCaptureDeliverFrameCB& frame_callback,
         const VideoTrackSettingsCallback& settings_callback,
         const VideoTrackFormatCallback& format_callback,
         std::unique_ptr<VideoTrackAdapterSettings> adapter_settings,
@@ -282,7 +282,7 @@
     ~PendingTrackInfo();
 
     MediaStreamVideoTrack* track;
-    VideoCaptureDeliverFrameCB frame_callback;
+    blink::VideoCaptureDeliverFrameCB frame_callback;
     VideoTrackSettingsCallback settings_callback;
     VideoTrackFormatCallback format_callback;
     // TODO(guidou): Make |adapter_settings| a regular field instead of a
diff --git a/content/renderer/media/stream/media_stream_video_track.cc b/content/renderer/media/stream/media_stream_video_track.cc
index 26ab8e05..e65bbb6 100644
--- a/content/renderer/media/stream/media_stream_video_track.cc
+++ b/content/renderer/media/stream/media_stream_video_track.cc
@@ -21,7 +21,8 @@
 namespace content {
 
 namespace {
-void ResetCallback(std::unique_ptr<VideoCaptureDeliverFrameCB> callback) {
+void ResetCallback(
+    std::unique_ptr<blink::VideoCaptureDeliverFrameCB> callback) {
   // |callback| will be deleted when this exits.
 }
 
@@ -49,7 +50,8 @@
 
   // Add |callback| to receive video frames on the IO-thread.
   // Must be called on the main render thread.
-  void AddCallback(VideoSinkId id, const VideoCaptureDeliverFrameCB& callback);
+  void AddCallback(VideoSinkId id,
+                   const blink::VideoCaptureDeliverFrameCB& callback);
 
   // Removes |callback| associated with |id| from receiving video frames if |id|
   // has been added. It is ok to call RemoveCallback even if the |id| has not
@@ -66,7 +68,7 @@
   friend class base::RefCountedThreadSafe<FrameDeliverer>;
   virtual ~FrameDeliverer();
   void AddCallbackOnIO(VideoSinkId id,
-                       const VideoCaptureDeliverFrameCB& callback);
+                       const blink::VideoCaptureDeliverFrameCB& callback);
   void RemoveCallbackOnIO(
       VideoSinkId id,
       const scoped_refptr<base::SingleThreadTaskRunner>& task_runner);
@@ -86,7 +88,7 @@
   scoped_refptr<media::VideoFrame> black_frame_;
 
   using VideoIdCallbackPair =
-      std::pair<VideoSinkId, VideoCaptureDeliverFrameCB>;
+      std::pair<VideoSinkId, blink::VideoCaptureDeliverFrameCB>;
   std::vector<VideoIdCallbackPair> callbacks_;
 
   DISALLOW_COPY_AND_ASSIGN(FrameDeliverer);
@@ -105,7 +107,7 @@
 
 void MediaStreamVideoTrack::FrameDeliverer::AddCallback(
     VideoSinkId id,
-    const VideoCaptureDeliverFrameCB& callback) {
+    const blink::VideoCaptureDeliverFrameCB& callback) {
   DCHECK_CALLED_ON_VALID_THREAD(main_render_thread_checker_);
   io_task_runner_->PostTask(
       FROM_HERE,
@@ -114,7 +116,7 @@
 
 void MediaStreamVideoTrack::FrameDeliverer::AddCallbackOnIO(
     VideoSinkId id,
-    const VideoCaptureDeliverFrameCB& callback) {
+    const blink::VideoCaptureDeliverFrameCB& callback) {
   DCHECK(io_task_runner_->BelongsToCurrentThread());
   callbacks_.push_back(std::make_pair(id, callback));
 }
@@ -134,8 +136,8 @@
   for (; it != callbacks_.end(); ++it) {
     if (it->first == id) {
       // Callback is copied to heap and then deleted on the target thread.
-      std::unique_ptr<VideoCaptureDeliverFrameCB> callback;
-      callback.reset(new VideoCaptureDeliverFrameCB(it->second));
+      std::unique_ptr<blink::VideoCaptureDeliverFrameCB> callback;
+      callback.reset(new blink::VideoCaptureDeliverFrameCB(it->second));
       callbacks_.erase(it);
       task_runner->PostTask(
           FROM_HERE, base::BindOnce(&ResetCallback, std::move(callback)));
@@ -317,9 +319,10 @@
   DVLOG(3) << "~MediaStreamVideoTrack()";
 }
 
-void MediaStreamVideoTrack::AddSink(MediaStreamVideoSink* sink,
-                                    const VideoCaptureDeliverFrameCB& callback,
-                                    bool is_sink_secure) {
+void MediaStreamVideoTrack::AddSink(
+    MediaStreamVideoSink* sink,
+    const blink::VideoCaptureDeliverFrameCB& callback,
+    bool is_sink_secure) {
   DCHECK_CALLED_ON_VALID_THREAD(main_render_thread_checker_);
   DCHECK(!base::ContainsValue(sinks_, sink));
   sinks_.push_back(sink);
diff --git a/content/renderer/media/stream/media_stream_video_track.h b/content/renderer/media/stream/media_stream_video_track.h
index 333f5b16..667f787 100644
--- a/content/renderer/media/stream/media_stream_video_track.h
+++ b/content/renderer/media/stream/media_stream_video_track.h
@@ -148,7 +148,7 @@
   // store the callback. This is important to ensure that we can release
   // the callback on render thread without reference to it on the IO-thread.
   void AddSink(MediaStreamVideoSink* sink,
-               const VideoCaptureDeliverFrameCB& callback,
+               const blink::VideoCaptureDeliverFrameCB& callback,
                bool is_sink_secure);
   void RemoveSink(MediaStreamVideoSink* sink);
 
diff --git a/content/renderer/media/stream/mock_media_stream_video_sink.cc b/content/renderer/media/stream/mock_media_stream_video_sink.cc
index 0f071af9..0646cbf 100644
--- a/content/renderer/media/stream/mock_media_stream_video_sink.cc
+++ b/content/renderer/media/stream/mock_media_stream_video_sink.cc
@@ -19,7 +19,7 @@
 MockMediaStreamVideoSink::~MockMediaStreamVideoSink() {
 }
 
-VideoCaptureDeliverFrameCB
+blink::VideoCaptureDeliverFrameCB
 MockMediaStreamVideoSink::GetDeliverFrameCB() {
   return media::BindToCurrentLoop(
       base::Bind(
diff --git a/content/renderer/media/stream/mock_media_stream_video_sink.h b/content/renderer/media/stream/mock_media_stream_video_sink.h
index 552672d3..f49999e 100644
--- a/content/renderer/media/stream/mock_media_stream_video_sink.h
+++ b/content/renderer/media/stream/mock_media_stream_video_sink.h
@@ -8,9 +8,9 @@
 #include "content/public/renderer/media_stream_video_sink.h"
 
 #include "base/memory/weak_ptr.h"
-#include "content/common/media/video_capture.h"
 #include "media/base/video_frame.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/public/common/media/video_capture.h"
 
 namespace content {
 
@@ -23,8 +23,9 @@
     MediaStreamVideoSink::ConnectToTrack(track, GetDeliverFrameCB(), true);
   }
 
-  void ConnectToTrackWithCallback(const blink::WebMediaStreamTrack& track,
-                                  const VideoCaptureDeliverFrameCB& callback) {
+  void ConnectToTrackWithCallback(
+      const blink::WebMediaStreamTrack& track,
+      const blink::VideoCaptureDeliverFrameCB& callback) {
     MediaStreamVideoSink::ConnectToTrack(track, callback, true);
   }
 
@@ -40,7 +41,7 @@
   // is called.
   MOCK_METHOD0(OnVideoFrame, void());
 
-  VideoCaptureDeliverFrameCB GetDeliverFrameCB();
+  blink::VideoCaptureDeliverFrameCB GetDeliverFrameCB();
 
   int number_of_frames() const { return number_of_frames_; }
   media::VideoPixelFormat format() const { return format_; }
diff --git a/content/renderer/media/stream/mock_media_stream_video_source.cc b/content/renderer/media/stream/mock_media_stream_video_source.cc
index cf692af..9e56768 100644
--- a/content/renderer/media/stream/mock_media_stream_video_source.cc
+++ b/content/renderer/media/stream/mock_media_stream_video_source.cc
@@ -67,7 +67,7 @@
 }
 
 void MockMediaStreamVideoSource::StartSourceImpl(
-    const VideoCaptureDeliverFrameCB& frame_callback) {
+    const blink::VideoCaptureDeliverFrameCB& frame_callback) {
   DCHECK(frame_callback_.is_null());
   attempted_to_start_ = true;
   frame_callback_ = frame_callback;
diff --git a/content/renderer/media/stream/mock_media_stream_video_source.h b/content/renderer/media/stream/mock_media_stream_video_source.h
index c639257..38f682a 100644
--- a/content/renderer/media/stream/mock_media_stream_video_source.h
+++ b/content/renderer/media/stream/mock_media_stream_video_source.h
@@ -67,7 +67,7 @@
 
   // Implements MediaStreamVideoSource.
   void StartSourceImpl(
-      const VideoCaptureDeliverFrameCB& frame_callback) override;
+      const blink::VideoCaptureDeliverFrameCB& frame_callback) override;
   void StopSourceImpl() override;
   base::Optional<media::VideoCaptureFormat> GetCurrentFormat() const override;
   void StopSourceForRestartImpl() override;
@@ -84,7 +84,7 @@
   bool can_stop_for_restart_ = true;
   bool can_restart_ = true;
   bool is_suspended_ = false;
-  VideoCaptureDeliverFrameCB frame_callback_;
+  blink::VideoCaptureDeliverFrameCB frame_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(MockMediaStreamVideoSource);
 };
diff --git a/content/renderer/media/stream/video_track_adapter.cc b/content/renderer/media/stream/video_track_adapter.cc
index 196117f..27d930c 100644
--- a/content/renderer/media/stream/video_track_adapter.cc
+++ b/content/renderer/media/stream/video_track_adapter.cc
@@ -122,7 +122,7 @@
     : public base::RefCountedThreadSafe<VideoFrameResolutionAdapter> {
  public:
   struct VideoTrackCallbacks {
-    VideoCaptureDeliverFrameCB frame_callback;
+    blink::VideoCaptureDeliverFrameCB frame_callback;
     VideoTrackSettingsCallback settings_callback;
     VideoTrackFormatCallback format_callback;
   };
@@ -136,7 +136,7 @@
   // |settings_callback| to set track settings on the main thread.
   // |frame_callback| will however be released on the main render thread.
   void AddCallbacks(const MediaStreamVideoTrack* track,
-                    VideoCaptureDeliverFrameCB frame_callback,
+                    blink::VideoCaptureDeliverFrameCB frame_callback,
                     VideoTrackSettingsCallback settings_callback,
                     VideoTrackFormatCallback format_callback);
 
@@ -187,7 +187,7 @@
   // Bound to the IO-thread.
   THREAD_CHECKER(io_thread_checker_);
 
-  // The task runner where we will release VideoCaptureDeliverFrameCB
+  // The task runner where we will release blink::VideoCaptureDeliverFrameCB
   // registered in AddCallbacks.
   const scoped_refptr<base::SingleThreadTaskRunner> renderer_task_runner_;
 
@@ -239,7 +239,7 @@
 
 void VideoTrackAdapter::VideoFrameResolutionAdapter::AddCallbacks(
     const MediaStreamVideoTrack* track,
-    VideoCaptureDeliverFrameCB frame_callback,
+    blink::VideoCaptureDeliverFrameCB frame_callback,
     VideoTrackSettingsCallback settings_callback,
     VideoTrackFormatCallback format_callback) {
   DCHECK_CALLED_ON_VALID_THREAD(io_thread_checker_);
@@ -499,11 +499,12 @@
   DCHECK(adapters_.empty());
 }
 
-void VideoTrackAdapter::AddTrack(const MediaStreamVideoTrack* track,
-                                 VideoCaptureDeliverFrameCB frame_callback,
-                                 VideoTrackSettingsCallback settings_callback,
-                                 VideoTrackFormatCallback format_callback,
-                                 const VideoTrackAdapterSettings& settings) {
+void VideoTrackAdapter::AddTrack(
+    const MediaStreamVideoTrack* track,
+    blink::VideoCaptureDeliverFrameCB frame_callback,
+    VideoTrackSettingsCallback settings_callback,
+    VideoTrackFormatCallback format_callback,
+    const VideoTrackAdapterSettings& settings) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   io_task_runner_->PostTask(
@@ -515,7 +516,7 @@
 
 void VideoTrackAdapter::AddTrackOnIO(
     const MediaStreamVideoTrack* track,
-    VideoCaptureDeliverFrameCB frame_callback,
+    blink::VideoCaptureDeliverFrameCB frame_callback,
     VideoTrackSettingsCallback settings_callback,
     VideoTrackFormatCallback format_callback,
     const VideoTrackAdapterSettings& settings) {
diff --git a/content/renderer/media/stream/video_track_adapter.h b/content/renderer/media/stream/video_track_adapter.h
index e2922996..839abcc 100644
--- a/content/renderer/media/stream/video_track_adapter.h
+++ b/content/renderer/media/stream/video_track_adapter.h
@@ -91,7 +91,7 @@
   // |source_frame_rate| is used to calculate a prudent interval to check for
   // passing frames and inform of the result via |on_muted_state_callback|.
   void AddTrack(const MediaStreamVideoTrack* track,
-                VideoCaptureDeliverFrameCB frame_callback,
+                blink::VideoCaptureDeliverFrameCB frame_callback,
                 VideoTrackSettingsCallback settings_callback,
                 VideoTrackFormatCallback track_callback,
                 const VideoTrackAdapterSettings& settings);
@@ -133,7 +133,7 @@
   friend class base::RefCountedThreadSafe<VideoTrackAdapter>;
 
   void AddTrackOnIO(const MediaStreamVideoTrack* track,
-                    VideoCaptureDeliverFrameCB frame_callback,
+                    blink::VideoCaptureDeliverFrameCB frame_callback,
                     VideoTrackSettingsCallback settings_callback,
                     VideoTrackFormatCallback track_callback,
                     const VideoTrackAdapterSettings& settings);
diff --git a/content/renderer/media/video_capture_impl.cc b/content/renderer/media/video_capture_impl.cc
index aedecff2..afc9190 100644
--- a/content/renderer/media/video_capture_impl.cc
+++ b/content/renderer/media/video_capture_impl.cc
@@ -127,9 +127,9 @@
 
   media::VideoCaptureParams params;
 
-  VideoCaptureStateUpdateCB state_update_cb;
+  blink::VideoCaptureStateUpdateCB state_update_cb;
 
-  VideoCaptureDeliverFrameCB deliver_frame_cb;
+  blink::VideoCaptureDeliverFrameCB deliver_frame_cb;
 };
 
 VideoCaptureImpl::VideoCaptureImpl(media::VideoCaptureSessionId session_id)
@@ -137,7 +137,7 @@
       session_id_(session_id),
       video_capture_host_for_testing_(nullptr),
       observer_binding_(this),
-      state_(VIDEO_CAPTURE_STATE_STOPPED),
+      state_(blink::VIDEO_CAPTURE_STATE_STOPPED),
       weak_factory_(this) {
   io_thread_checker_.DetachFromThread();
 
@@ -152,8 +152,8 @@
 
 VideoCaptureImpl::~VideoCaptureImpl() {
   DCHECK(io_thread_checker_.CalledOnValidThread());
-  if ((state_ == VIDEO_CAPTURE_STATE_STARTING ||
-       state_ == VIDEO_CAPTURE_STATE_STARTED) &&
+  if ((state_ == blink::VIDEO_CAPTURE_STATE_STARTING ||
+       state_ == blink::VIDEO_CAPTURE_STATE_STARTED) &&
       GetVideoCaptureHost())
     GetVideoCaptureHost()->Stop(device_id_);
 }
@@ -169,8 +169,8 @@
 void VideoCaptureImpl::StartCapture(
     int client_id,
     const media::VideoCaptureParams& params,
-    const VideoCaptureStateUpdateCB& state_update_cb,
-    const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
+    const blink::VideoCaptureStateUpdateCB& state_update_cb,
+    const blink::VideoCaptureDeliverFrameCB& deliver_frame_cb) {
   DVLOG(1) << __func__ << " |device_id_| = " << device_id_;
   DCHECK(io_thread_checker_.CalledOnValidThread());
   ClientInfo client_info;
@@ -179,21 +179,21 @@
   client_info.deliver_frame_cb = deliver_frame_cb;
 
   switch (state_) {
-    case VIDEO_CAPTURE_STATE_STARTING:
-    case VIDEO_CAPTURE_STATE_STARTED:
+    case blink::VIDEO_CAPTURE_STATE_STARTING:
+    case blink::VIDEO_CAPTURE_STATE_STARTED:
       clients_[client_id] = client_info;
       // TODO(sheu): Allowing resolution change will require that all
       // outstanding clients of a capture session support resolution change.
       DCHECK_EQ(params_.resolution_change_policy,
                 params.resolution_change_policy);
       return;
-    case VIDEO_CAPTURE_STATE_STOPPING:
+    case blink::VIDEO_CAPTURE_STATE_STOPPING:
       clients_pending_on_restart_[client_id] = client_info;
       DVLOG(1) << __func__ << " Got new resolution while stopping: "
                << params.requested_format.frame_size.ToString();
       return;
-    case VIDEO_CAPTURE_STATE_STOPPED:
-    case VIDEO_CAPTURE_STATE_ENDED:
+    case blink::VIDEO_CAPTURE_STATE_STOPPED:
+    case blink::VIDEO_CAPTURE_STATE_ENDED:
       clients_[client_id] = client_info;
       params_ = params;
       params_.requested_format.frame_rate =
@@ -204,11 +204,11 @@
                << params_.requested_format.frame_size.ToString();
       StartCaptureInternal();
       return;
-    case VIDEO_CAPTURE_STATE_ERROR:
-      state_update_cb.Run(VIDEO_CAPTURE_STATE_ERROR);
+    case blink::VIDEO_CAPTURE_STATE_ERROR:
+      state_update_cb.Run(blink::VIDEO_CAPTURE_STATE_ERROR);
       return;
-    case VIDEO_CAPTURE_STATE_PAUSED:
-    case VIDEO_CAPTURE_STATE_RESUMED:
+    case blink::VIDEO_CAPTURE_STATE_PAUSED:
+    case blink::VIDEO_CAPTURE_STATE_RESUMED:
       // The internal |state_| is never set to PAUSED/RESUMED since
       // VideoCaptureImpl is not modified by those.
       NOTREACHED();
@@ -239,7 +239,7 @@
 }
 
 void VideoCaptureImpl::GetDeviceSupportedFormats(
-    const VideoCaptureDeviceFormatsCB& callback) {
+    const blink::VideoCaptureDeviceFormatsCB& callback) {
   DCHECK(io_thread_checker_.CalledOnValidThread());
   GetVideoCaptureHost()->GetDeviceSupportedFormats(
       device_id_, session_id_,
@@ -248,7 +248,7 @@
 }
 
 void VideoCaptureImpl::GetDeviceFormatsInUse(
-    const VideoCaptureDeviceFormatsCB& callback) {
+    const blink::VideoCaptureDeviceFormatsCB& callback) {
   DCHECK(io_thread_checker_.CalledOnValidThread());
   GetVideoCaptureHost()->GetDeviceFormatsInUse(
       device_id_, session_id_,
@@ -262,16 +262,16 @@
 
   switch (state) {
     case media::mojom::VideoCaptureState::STARTED:
-      state_ = VIDEO_CAPTURE_STATE_STARTED;
+      state_ = blink::VIDEO_CAPTURE_STATE_STARTED;
       for (const auto& client : clients_)
-        client.second.state_update_cb.Run(VIDEO_CAPTURE_STATE_STARTED);
+        client.second.state_update_cb.Run(blink::VIDEO_CAPTURE_STATE_STARTED);
       // In case there is any frame dropped before STARTED, always request for
       // a frame refresh to start the video call with.
       // Capture device will make a decision if it should refresh a frame.
       RequestRefreshFrame();
       break;
     case media::mojom::VideoCaptureState::STOPPED:
-      state_ = VIDEO_CAPTURE_STATE_STOPPED;
+      state_ = blink::VIDEO_CAPTURE_STATE_STOPPED;
       client_buffers_.clear();
       weak_factory_.InvalidateWeakPtrs();
       if (!clients_.empty() || !clients_pending_on_restart_.empty())
@@ -279,24 +279,24 @@
       break;
     case media::mojom::VideoCaptureState::PAUSED:
       for (const auto& client : clients_)
-        client.second.state_update_cb.Run(VIDEO_CAPTURE_STATE_PAUSED);
+        client.second.state_update_cb.Run(blink::VIDEO_CAPTURE_STATE_PAUSED);
       break;
     case media::mojom::VideoCaptureState::RESUMED:
       for (const auto& client : clients_)
-        client.second.state_update_cb.Run(VIDEO_CAPTURE_STATE_RESUMED);
+        client.second.state_update_cb.Run(blink::VIDEO_CAPTURE_STATE_RESUMED);
       break;
     case media::mojom::VideoCaptureState::FAILED:
       for (const auto& client : clients_)
-        client.second.state_update_cb.Run(VIDEO_CAPTURE_STATE_ERROR);
+        client.second.state_update_cb.Run(blink::VIDEO_CAPTURE_STATE_ERROR);
       clients_.clear();
-      state_ = VIDEO_CAPTURE_STATE_ERROR;
+      state_ = blink::VIDEO_CAPTURE_STATE_ERROR;
       break;
     case media::mojom::VideoCaptureState::ENDED:
       // We'll only notify the client that the stream has stopped.
       for (const auto& client : clients_)
-        client.second.state_update_cb.Run(VIDEO_CAPTURE_STATE_STOPPED);
+        client.second.state_update_cb.Run(blink::VIDEO_CAPTURE_STATE_STOPPED);
       clients_.clear();
-      state_ = VIDEO_CAPTURE_STATE_ENDED;
+      state_ = blink::VIDEO_CAPTURE_STATE_ENDED;
       break;
   }
 }
@@ -320,7 +320,7 @@
   DVLOG(1) << __func__ << " buffer_id: " << buffer_id;
   DCHECK(io_thread_checker_.CalledOnValidThread());
 
-  bool consume_buffer = state_ == VIDEO_CAPTURE_STATE_STARTED;
+  bool consume_buffer = state_ == blink::VIDEO_CAPTURE_STATE_STARTED;
   if (!consume_buffer) {
     GetVideoCaptureHost()->ReleaseBuffer(device_id_, buffer_id, -1.0);
     return;
@@ -475,17 +475,17 @@
 
 void VideoCaptureImpl::StopDevice() {
   DCHECK(io_thread_checker_.CalledOnValidThread());
-  if (state_ != VIDEO_CAPTURE_STATE_STARTING &&
-      state_ != VIDEO_CAPTURE_STATE_STARTED)
+  if (state_ != blink::VIDEO_CAPTURE_STATE_STARTING &&
+      state_ != blink::VIDEO_CAPTURE_STATE_STARTED)
     return;
-  state_ = VIDEO_CAPTURE_STATE_STOPPING;
+  state_ = blink::VIDEO_CAPTURE_STATE_STOPPING;
   GetVideoCaptureHost()->Stop(device_id_);
   params_.requested_format.frame_size.SetSize(0, 0);
 }
 
 void VideoCaptureImpl::RestartCapture() {
   DCHECK(io_thread_checker_.CalledOnValidThread());
-  DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPED);
+  DCHECK_EQ(state_, blink::VIDEO_CAPTURE_STATE_STOPPED);
 
   int width = 0;
   int height = 0;
@@ -505,7 +505,7 @@
 
 void VideoCaptureImpl::StartCaptureInternal() {
   DCHECK(io_thread_checker_.CalledOnValidThread());
-  state_ = VIDEO_CAPTURE_STATE_STARTING;
+  state_ = blink::VIDEO_CAPTURE_STATE_STARTING;
 
   media::mojom::VideoCaptureObserverPtr observer;
   observer_binding_.Bind(mojo::MakeRequest(&observer));
@@ -514,14 +514,14 @@
 }
 
 void VideoCaptureImpl::OnDeviceSupportedFormats(
-    const VideoCaptureDeviceFormatsCB& callback,
+    const blink::VideoCaptureDeviceFormatsCB& callback,
     const media::VideoCaptureFormats& supported_formats) {
   DCHECK(io_thread_checker_.CalledOnValidThread());
   callback.Run(supported_formats);
 }
 
 void VideoCaptureImpl::OnDeviceFormatsInUse(
-    const VideoCaptureDeviceFormatsCB& callback,
+    const blink::VideoCaptureDeviceFormatsCB& callback,
     const media::VideoCaptureFormats& formats_in_use) {
   DCHECK(io_thread_checker_.CalledOnValidThread());
   callback.Run(formats_in_use);
@@ -534,7 +534,7 @@
   if (it == clients->end())
     return false;
 
-  it->second.state_update_cb.Run(VIDEO_CAPTURE_STATE_STOPPED);
+  it->second.state_update_cb.Run(blink::VIDEO_CAPTURE_STATE_STOPPED);
   clients->erase(it);
   return true;
 }
diff --git a/content/renderer/media/video_capture_impl.h b/content/renderer/media/video_capture_impl.h
index ddd06b3..0fda3624 100644
--- a/content/renderer/media/video_capture_impl.h
+++ b/content/renderer/media/video_capture_impl.h
@@ -14,11 +14,11 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "content/common/content_export.h"
-#include "content/common/media/video_capture.h"
 #include "media/base/video_frame.h"
 #include "media/capture/mojom/video_capture.mojom.h"
 #include "media/capture/video_capture_types.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "third_party/blink/public/common/media/video_capture.h"
 
 namespace content {
 
@@ -43,8 +43,8 @@
   // |deliver_frame_cb| will be called when a frame is ready.
   void StartCapture(int client_id,
                     const media::VideoCaptureParams& params,
-                    const VideoCaptureStateUpdateCB& state_update_cb,
-                    const VideoCaptureDeliverFrameCB& deliver_frame_cb);
+                    const blink::VideoCaptureStateUpdateCB& state_update_cb,
+                    const blink::VideoCaptureDeliverFrameCB& deliver_frame_cb);
 
   // Stop capturing. |client_id| is the identifier used to call StartCapture.
   void StopCapture(int client_id);
@@ -55,11 +55,13 @@
 
   // Get capturing formats supported by this device.
   // |callback| will be invoked with the results.
-  void GetDeviceSupportedFormats(const VideoCaptureDeviceFormatsCB& callback);
+  void GetDeviceSupportedFormats(
+      const blink::VideoCaptureDeviceFormatsCB& callback);
 
   // Get capturing formats currently in use by this device.
   // |callback| will be invoked with the results.
-  void GetDeviceFormatsInUse(const VideoCaptureDeviceFormatsCB& callback);
+  void GetDeviceFormatsInUse(
+      const blink::VideoCaptureDeviceFormatsCB& callback);
 
   media::VideoCaptureSessionId session_id() const { return session_id_; }
 
@@ -99,11 +101,10 @@
   void StartCaptureInternal();
 
   void OnDeviceSupportedFormats(
-      const VideoCaptureDeviceFormatsCB& callback,
+      const blink::VideoCaptureDeviceFormatsCB& callback,
       const media::VideoCaptureFormats& supported_formats);
-  void OnDeviceFormatsInUse(
-      const VideoCaptureDeviceFormatsCB& callback,
-      const media::VideoCaptureFormats& formats_in_use);
+  void OnDeviceFormatsInUse(const blink::VideoCaptureDeviceFormatsCB& callback,
+                            const media::VideoCaptureFormats& formats_in_use);
 
   // Tries to remove |client_id| from |clients|, returning false if not found.
   bool RemoveClient(int client_id, ClientInfoMap* clients);
@@ -145,7 +146,7 @@
   // First captured frame reference time sent from browser process side.
   base::TimeTicks first_frame_ref_time_;
 
-  VideoCaptureState state_;
+  blink::VideoCaptureState state_;
 
   base::ThreadChecker io_thread_checker_;
 
diff --git a/content/renderer/media/video_capture_impl_manager.cc b/content/renderer/media/video_capture_impl_manager.cc
index 7f09023..4f426d5 100644
--- a/content/renderer/media/video_capture_impl_manager.cc
+++ b/content/renderer/media/video_capture_impl_manager.cc
@@ -107,8 +107,8 @@
 base::Closure VideoCaptureImplManager::StartCapture(
     media::VideoCaptureSessionId id,
     const media::VideoCaptureParams& params,
-    const VideoCaptureStateUpdateCB& state_update_cb,
-    const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
+    const blink::VideoCaptureStateUpdateCB& state_update_cb,
+    const blink::VideoCaptureDeliverFrameCB& deliver_frame_cb) {
   DCHECK(render_main_task_runner_->BelongsToCurrentThread());
   const auto it = std::find_if(
       devices_.begin(), devices_.end(),
@@ -174,7 +174,7 @@
 
 void VideoCaptureImplManager::GetDeviceSupportedFormats(
     media::VideoCaptureSessionId id,
-    const VideoCaptureDeviceFormatsCB& callback) {
+    const blink::VideoCaptureDeviceFormatsCB& callback) {
   DCHECK(render_main_task_runner_->BelongsToCurrentThread());
   const auto it = std::find_if(
       devices_.begin(), devices_.end(),
@@ -187,7 +187,7 @@
 
 void VideoCaptureImplManager::GetDeviceFormatsInUse(
     media::VideoCaptureSessionId id,
-    const VideoCaptureDeviceFormatsCB& callback) {
+    const blink::VideoCaptureDeviceFormatsCB& callback) {
   DCHECK(render_main_task_runner_->BelongsToCurrentThread());
   const auto it = std::find_if(
       devices_.begin(), devices_.end(),
diff --git a/content/renderer/media/video_capture_impl_manager.h b/content/renderer/media/video_capture_impl_manager.h
index ae7f5f5..27e3c83 100644
--- a/content/renderer/media/video_capture_impl_manager.h
+++ b/content/renderer/media/video_capture_impl_manager.h
@@ -16,9 +16,9 @@
 #include "base/synchronization/lock.h"
 #include "base/threading/thread_checker.h"
 #include "content/common/content_export.h"
-#include "content/common/media/video_capture.h"
 #include "content/public/renderer/media_stream_video_sink.h"
 #include "media/capture/video_capture_types.h"
+#include "third_party/blink/public/common/media/video_capture.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 
 namespace content {
@@ -70,8 +70,8 @@
   base::Closure StartCapture(
       media::VideoCaptureSessionId id,
       const media::VideoCaptureParams& params,
-      const VideoCaptureStateUpdateCB& state_update_cb,
-      const VideoCaptureDeliverFrameCB& deliver_frame_cb);
+      const blink::VideoCaptureStateUpdateCB& state_update_cb,
+      const blink::VideoCaptureDeliverFrameCB& deliver_frame_cb);
 
   // Requests that the video capturer send a frame "soon" (e.g., to resolve
   // picture loss or quality issues).
@@ -83,13 +83,15 @@
 
   // Get supported formats supported by the device for the given session
   // ID. |callback| will be called on the IO thread.
-  void GetDeviceSupportedFormats(media::VideoCaptureSessionId id,
-                                 const VideoCaptureDeviceFormatsCB& callback);
+  void GetDeviceSupportedFormats(
+      media::VideoCaptureSessionId id,
+      const blink::VideoCaptureDeviceFormatsCB& callback);
 
   // Get supported formats currently in use for the given session ID.
   // |callback| will be called on the IO thread.
-  void GetDeviceFormatsInUse(media::VideoCaptureSessionId id,
-                             const VideoCaptureDeviceFormatsCB& callback);
+  void GetDeviceFormatsInUse(
+      media::VideoCaptureSessionId id,
+      const blink::VideoCaptureDeviceFormatsCB& callback);
 
   // Make all VideoCaptureImpl instances in the input |video_devices|
   // stop/resume delivering video frames to their clients, depends on flag
diff --git a/content/renderer/media/video_capture_impl_manager_unittest.cc b/content/renderer/media/video_capture_impl_manager_unittest.cc
index 117d39f..f901ab7 100644
--- a/content/renderer/media/video_capture_impl_manager_unittest.cc
+++ b/content/renderer/media/video_capture_impl_manager_unittest.cc
@@ -188,10 +188,11 @@
   MOCK_METHOD1(OnPaused, void(media::VideoCaptureSessionId id));
   MOCK_METHOD1(OnResumed, void(media::VideoCaptureSessionId id));
 
-  void OnStateUpdate(media::VideoCaptureSessionId id, VideoCaptureState state) {
-    if (state == VIDEO_CAPTURE_STATE_STARTED)
+  void OnStateUpdate(media::VideoCaptureSessionId id,
+                     blink::VideoCaptureState state) {
+    if (state == blink::VIDEO_CAPTURE_STATE_STARTED)
       OnStarted(id);
-    else if (state == VIDEO_CAPTURE_STATE_STOPPED)
+    else if (state == blink::VIDEO_CAPTURE_STATE_STOPPED)
       OnStopped(id);
     else
       NOTREACHED();
diff --git a/content/renderer/media/video_capture_impl_unittest.cc b/content/renderer/media/video_capture_impl_unittest.cc
index 81e6104d..b47ce9c 100644
--- a/content/renderer/media/video_capture_impl_unittest.cc
+++ b/content/renderer/media/video_capture_impl_unittest.cc
@@ -119,7 +119,7 @@
   // These four mocks are used to create callbacks for the different oeprations.
   MOCK_METHOD2(OnFrameReady,
                void(const scoped_refptr<media::VideoFrame>&, base::TimeTicks));
-  MOCK_METHOD1(OnStateUpdate, void(VideoCaptureState));
+  MOCK_METHOD1(OnStateUpdate, void(blink::VideoCaptureState));
   MOCK_METHOD1(OnDeviceFormatsInUse, void(const media::VideoCaptureFormats&));
   MOCK_METHOD1(OnDeviceSupportedFormats,
                void(const media::VideoCaptureFormats&));
@@ -215,8 +215,8 @@
 };
 
 TEST_F(VideoCaptureImplTest, Simple) {
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED));
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_small_));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
 
@@ -225,8 +225,9 @@
 }
 
 TEST_F(VideoCaptureImplTest, TwoClientsInSequence) {
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED)).Times(2);
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED))
+      .Times(2);
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_small_));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
 
@@ -237,8 +238,9 @@
 }
 
 TEST_F(VideoCaptureImplTest, LargeAndSmall) {
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED)).Times(2);
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED))
+      .Times(2);
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_large_));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
 
@@ -249,8 +251,9 @@
 }
 
 TEST_F(VideoCaptureImplTest, SmallAndLarge) {
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED)).Times(2);
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED))
+      .Times(2);
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_small_));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
 
@@ -300,8 +303,8 @@
       media::PIXEL_FORMAT_I420, params_small_.requested_format.frame_size);
   ASSERT_TRUE(CreateAndMapSharedMemory(frame_size, &shm));
 
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED));
   EXPECT_CALL(*this, OnFrameReady(_, _));
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_small_));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
@@ -327,8 +330,8 @@
       base::ReadOnlySharedMemoryRegion::Create(frame_size);
   ASSERT_TRUE(shm.IsValid());
 
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED));
   EXPECT_CALL(*this, OnFrameReady(_, _));
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_small_));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
@@ -353,8 +356,8 @@
       media::PIXEL_FORMAT_I420, params_large_.requested_format.frame_size);
   ASSERT_TRUE(CreateAndMapSharedMemory(frame_size, &shm));
 
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED));
   EXPECT_CALL(*this, OnFrameReady(_, _)).Times(0);
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_large_));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
@@ -381,8 +384,8 @@
       base::ReadOnlySharedMemoryRegion::Create(frame_size);
   ASSERT_TRUE(shm.IsValid());
 
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED));
   EXPECT_CALL(*this, OnFrameReady(_, _)).Times(0);
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_large_));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
@@ -402,8 +405,9 @@
 
 TEST_F(VideoCaptureImplTest, AlreadyStarted) {
   media::VideoCaptureParams params = {};
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED)).Times(2);
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED))
+      .Times(2);
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_small_))
       .WillOnce(DoAll(InvokeWithoutArgs([this]() {
                         video_capture_impl_->OnStateChanged(
@@ -420,8 +424,8 @@
 }
 
 TEST_F(VideoCaptureImplTest, EndedBeforeStop) {
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED));
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_small_));
 
   StartCapture(0, params_small_);
@@ -432,8 +436,8 @@
 }
 
 TEST_F(VideoCaptureImplTest, ErrorBeforeStop) {
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_ERROR));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_ERROR));
   EXPECT_CALL(mock_video_capture_host_, DoStart(_, kSessionId, params_small_));
 
   StartCapture(0, params_small_);
@@ -461,16 +465,16 @@
   SimulateBufferReceived(kArbitraryBufferId,
                          params_small_.requested_format.frame_size);
 
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
   EXPECT_CALL(mock_video_capture_host_, RequestRefreshFrame(_));
   video_capture_impl_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
 
   // Additional STARTED will cause RequestRefreshFrame a second time.
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
   EXPECT_CALL(mock_video_capture_host_, RequestRefreshFrame(_));
   video_capture_impl_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
 
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
   StopCapture(0);
 }
@@ -495,16 +499,16 @@
   SimulateBufferReceived(kArbitraryBufferId,
                          params_small_.requested_format.frame_size);
 
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
   EXPECT_CALL(mock_video_capture_host_, RequestRefreshFrame(_));
   video_capture_impl_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
 
   // Additional STARTED will cause RequestRefreshFrame a second time.
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STARTED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STARTED));
   EXPECT_CALL(mock_video_capture_host_, RequestRefreshFrame(_));
   video_capture_impl_->OnStateChanged(media::mojom::VideoCaptureState::STARTED);
 
-  EXPECT_CALL(*this, OnStateUpdate(VIDEO_CAPTURE_STATE_STOPPED));
+  EXPECT_CALL(*this, OnStateUpdate(blink::VIDEO_CAPTURE_STATE_STOPPED));
   EXPECT_CALL(mock_video_capture_host_, Stop(_));
   StopCapture(0);
 }
diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source.cc b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
index 0bff40c..1d57d6ea 100644
--- a/content/renderer/media/webrtc/media_stream_remote_video_source.cc
+++ b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
@@ -32,7 +32,7 @@
  public:
   RemoteVideoSourceDelegate(
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-      const VideoCaptureDeliverFrameCB& new_frame_callback);
+      const blink::VideoCaptureDeliverFrameCB& new_frame_callback);
 
  protected:
   friend class base::RefCountedThreadSafe<RemoteVideoSourceDelegate>;
@@ -50,7 +50,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
   // |frame_callback_| is accessed on the IO thread.
-  VideoCaptureDeliverFrameCB frame_callback_;
+  blink::VideoCaptureDeliverFrameCB frame_callback_;
 
   // Timestamp of the first received frame.
   base::TimeDelta start_timestamp_;
@@ -62,7 +62,7 @@
 MediaStreamRemoteVideoSource::RemoteVideoSourceDelegate::
     RemoteVideoSourceDelegate(
         scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-        const VideoCaptureDeliverFrameCB& new_frame_callback)
+        const blink::VideoCaptureDeliverFrameCB& new_frame_callback)
     : io_task_runner_(io_task_runner),
       frame_callback_(new_frame_callback),
       start_timestamp_(media::kNoTimestamp),
@@ -226,7 +226,7 @@
 }
 
 void MediaStreamRemoteVideoSource::StartSourceImpl(
-    const VideoCaptureDeliverFrameCB& frame_callback) {
+    const blink::VideoCaptureDeliverFrameCB& frame_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!delegate_.get());
   delegate_ = new RemoteVideoSourceDelegate(io_task_runner(), frame_callback);
diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source.h b/content/renderer/media/webrtc/media_stream_remote_video_source.h
index 3657eb0..04401c8 100644
--- a/content/renderer/media/webrtc/media_stream_remote_video_source.h
+++ b/content/renderer/media/webrtc/media_stream_remote_video_source.h
@@ -38,7 +38,7 @@
  protected:
   // Implements MediaStreamVideoSource.
   void StartSourceImpl(
-      const VideoCaptureDeliverFrameCB& frame_callback) override;
+      const blink::VideoCaptureDeliverFrameCB& frame_callback) override;
 
   void StopSourceImpl() override;
 
diff --git a/content/renderer/media_capture_from_element/canvas_capture_handler.cc b/content/renderer/media_capture_from_element/canvas_capture_handler.cc
index ec3f0e1..547fd0a8 100644
--- a/content/renderer/media_capture_from_element/canvas_capture_handler.cc
+++ b/content/renderer/media_capture_from_element/canvas_capture_handler.cc
@@ -60,7 +60,7 @@
     return formats;
   }
   void StartCapture(const media::VideoCaptureParams& params,
-                    const VideoCaptureDeliverFrameCB& frame_callback,
+                    const blink::VideoCaptureDeliverFrameCB& frame_callback,
                     const RunningCallback& running_callback) override {
     DCHECK(main_render_thread_checker_.CalledOnValidThread());
     if (canvas_handler_.get()) {
diff --git a/content/renderer/pepper/pepper_media_stream_video_track_host.cc b/content/renderer/pepper/pepper_media_stream_video_track_host.cc
index 14242ab..30390fd 100644
--- a/content/renderer/pepper/pepper_media_stream_video_track_host.cc
+++ b/content/renderer/pepper/pepper_media_stream_video_track_host.cc
@@ -182,7 +182,7 @@
     : public base::RefCountedThreadSafe<FrameDeliverer> {
  public:
   FrameDeliverer(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-                 const VideoCaptureDeliverFrameCB& new_frame_callback);
+                 const blink::VideoCaptureDeliverFrameCB& new_frame_callback);
 
   void DeliverVideoFrame(const scoped_refptr<media::VideoFrame>& frame);
 
@@ -193,16 +193,16 @@
   void DeliverFrameOnIO(const scoped_refptr<media::VideoFrame>& frame);
 
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
-  VideoCaptureDeliverFrameCB new_frame_callback_;
+  blink::VideoCaptureDeliverFrameCB new_frame_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(FrameDeliverer);
 };
 
 PepperMediaStreamVideoTrackHost::FrameDeliverer::FrameDeliverer(
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-    const VideoCaptureDeliverFrameCB& new_frame_callback)
-    : io_task_runner_(io_task_runner), new_frame_callback_(new_frame_callback) {
-}
+    const blink::VideoCaptureDeliverFrameCB& new_frame_callback)
+    : io_task_runner_(io_task_runner),
+      new_frame_callback_(new_frame_callback) {}
 
 PepperMediaStreamVideoTrackHost::FrameDeliverer::~FrameDeliverer() {
 }
@@ -417,7 +417,7 @@
   ~VideoSource() final { StopSourceImpl(); }
 
   void StartSourceImpl(
-      const VideoCaptureDeliverFrameCB& frame_callback) final {
+      const blink::VideoCaptureDeliverFrameCB& frame_callback) final {
     if (host_) {
       host_->frame_deliverer_ =
           new FrameDeliverer(io_task_runner(), frame_callback);
diff --git a/content/renderer/pepper/pepper_platform_camera_device.h b/content/renderer/pepper/pepper_platform_camera_device.h
index 907034c..84975f34 100644
--- a/content/renderer/pepper/pepper_platform_camera_device.h
+++ b/content/renderer/pepper/pepper_platform_camera_device.h
@@ -13,8 +13,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
-#include "content/common/media/video_capture.h"
 #include "media/capture/video_capture_types.h"
+#include "third_party/blink/public/common/media/video_capture.h"
 
 namespace content {
 class PepperMediaDeviceManager;
diff --git a/content/renderer/pepper/pepper_platform_video_capture.cc b/content/renderer/pepper/pepper_platform_video_capture.cc
index 91210951..9c9fca4 100644
--- a/content/renderer/pepper/pepper_platform_video_capture.cc
+++ b/content/renderer/pepper/pepper_platform_video_capture.cc
@@ -116,20 +116,20 @@
     handler_->OnInitialized(succeeded);
 }
 
-void PepperPlatformVideoCapture::OnStateUpdate(VideoCaptureState state) {
+void PepperPlatformVideoCapture::OnStateUpdate(blink::VideoCaptureState state) {
   if (!handler_)
     return;
   switch (state) {
-    case VIDEO_CAPTURE_STATE_STARTED:
+    case blink::VIDEO_CAPTURE_STATE_STARTED:
       handler_->OnStarted();
       break;
-    case VIDEO_CAPTURE_STATE_STOPPED:
+    case blink::VIDEO_CAPTURE_STATE_STOPPED:
       handler_->OnStopped();
       break;
-    case VIDEO_CAPTURE_STATE_PAUSED:
+    case blink::VIDEO_CAPTURE_STATE_PAUSED:
       handler_->OnPaused();
       break;
-    case VIDEO_CAPTURE_STATE_ERROR:
+    case blink::VIDEO_CAPTURE_STATE_ERROR:
       handler_->OnError();
       break;
     default:
diff --git a/content/renderer/pepper/pepper_platform_video_capture.h b/content/renderer/pepper/pepper_platform_video_capture.h
index 31182aa..f21937dd 100644
--- a/content/renderer/pepper/pepper_platform_video_capture.h
+++ b/content/renderer/pepper/pepper_platform_video_capture.h
@@ -14,8 +14,8 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
-#include "content/common/media/video_capture.h"
 #include "media/capture/video_capture_types.h"
+#include "third_party/blink/public/common/media/video_capture.h"
 
 namespace media {
 class VideoFrame;
@@ -41,7 +41,7 @@
 
  private:
   void OnDeviceOpened(int request_id, bool succeeded, const std::string& label);
-  void OnStateUpdate(VideoCaptureState state);
+  void OnStateUpdate(blink::VideoCaptureState state);
   void OnFrameReady(const scoped_refptr<media::VideoFrame>& frame,
                     base::TimeTicks estimated_capture_time);
 
diff --git a/content/renderer/pepper/pepper_video_capture_host.h b/content/renderer/pepper/pepper_video_capture_host.h
index 300b21d..0efb69c 100644
--- a/content/renderer/pepper/pepper_video_capture_host.h
+++ b/content/renderer/pepper/pepper_video_capture_host.h
@@ -13,7 +13,6 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "content/common/media/video_capture.h"
 #include "content/public/renderer/renderer_ppapi_host.h"
 #include "content/renderer/pepper/pepper_device_enumeration_host_helper.h"
 #include "content/renderer/pepper/ppb_buffer_impl.h"
@@ -21,6 +20,7 @@
 #include "ppapi/c/dev/ppp_video_capture_dev.h"
 #include "ppapi/host/host_message_context.h"
 #include "ppapi/host/resource_host.h"
+#include "third_party/blink/public/common/media/video_capture.h"
 
 namespace media {
 class VideoFrame;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 139c441..3adc000 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -7254,8 +7254,8 @@
       network::SharedURLLoaderFactory::Create(
           GetLoaderFactoryBundle()->CloneWithoutAppCacheFactory());
   return ServiceWorkerNetworkProvider::CreateForNavigation(
-      routing_id_, commit_params, frame_,
-      std::move(controller_service_worker_info), std::move(fallback_factory));
+      this, commit_params, std::move(controller_service_worker_info),
+      std::move(fallback_factory));
 }
 
 }  // namespace content
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index bfa5fba..9cdecad 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -2156,13 +2156,16 @@
 }
 
 TEST_F(RenderViewImplTest, ServiceWorkerNetworkProviderSetup) {
+  // Service workers require https.
+  GURL example_url("https://example.com");
+
   blink::WebServiceWorkerNetworkProvider* webprovider = nullptr;
   ServiceWorkerNetworkProvider* provider = nullptr;
   RequestExtraData* extra_data = nullptr;
 
   // Make sure each new document has a new provider and
   // that the main request is tagged with the provider's id.
-  LoadHTML("<b>A Document</b>");
+  LoadHTMLWithUrlOverride("<b>A Document</b>", example_url.spec().c_str());
   ASSERT_TRUE(GetMainFrame()->GetDocumentLoader());
   webprovider =
       GetMainFrame()->GetDocumentLoader()->GetServiceWorkerNetworkProvider();
@@ -2172,7 +2175,8 @@
   ASSERT_TRUE(provider);
   int provider1_id = provider->provider_id();
 
-  LoadHTML("<b>New Document B Goes Here</b>");
+  LoadHTMLWithUrlOverride("<b>New Document B Goes Here</b>",
+                          example_url.spec().c_str());
   ASSERT_TRUE(GetMainFrame()->GetDocumentLoader());
   webprovider =
       GetMainFrame()->GetDocumentLoader()->GetServiceWorkerNetworkProvider();
diff --git a/content/renderer/service_worker/service_worker_network_provider.cc b/content/renderer/service_worker/service_worker_network_provider.cc
index 6dd350a..6727910 100644
--- a/content/renderer/service_worker/service_worker_network_provider.cc
+++ b/content/renderer/service_worker/service_worker_network_provider.cc
@@ -8,6 +8,7 @@
 #include "content/child/child_thread_impl.h"
 #include "content/common/navigation_params.h"
 #include "content/common/service_worker/service_worker_utils.h"
+#include "content/renderer/render_frame_impl.h"
 #include "content/renderer/renderer_blink_platform_impl.h"
 #include "content/renderer/service_worker/web_service_worker_network_provider_base_impl.h"
 #include "content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h"
@@ -48,14 +49,20 @@
 
 }  // namespace
 
+std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
+ServiceWorkerNetworkProvider::CreateInvalidInstanceForNavigation() {
+  return std::make_unique<WebServiceWorkerNetworkProviderImplForFrame>(
+      base::WrapUnique(new ServiceWorkerNetworkProvider()), nullptr);
+}
+
 // static
 std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
 ServiceWorkerNetworkProvider::CreateForNavigation(
-    int route_id,
+    RenderFrameImpl* frame,
     const CommitNavigationParams* commit_params,
-    blink::WebLocalFrame* frame,
     blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
     scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory) {
+  blink::WebLocalFrame* web_frame = frame->GetWebFrame();
   // Determine if a ServiceWorkerNetworkProvider should be created and properly
   // initialized for the navigation. A default ServiceWorkerNetworkProvider
   // will always be created since it is expected in a certain number of places,
@@ -66,16 +73,19 @@
     should_create_provider = commit_params->should_create_service_worker;
     provider_id = commit_params->service_worker_provider_id;
   } else {
+    // It'd be convenient to check web_frame->GetSecurityOrigin().IsOpaque()
+    // here instead of just looking at the sandbox flags, but
+    // GetSecurityOrigin() crashes because the frame does not yet have a
+    // security context.
     should_create_provider =
-        ((frame->EffectiveSandboxFlags() & blink::WebSandboxFlags::kOrigin) !=
-         blink::WebSandboxFlags::kOrigin);
+        ((web_frame->EffectiveSandboxFlags() &
+          blink::WebSandboxFlags::kOrigin) != blink::WebSandboxFlags::kOrigin);
   }
 
   // If we shouldn't create a real ServiceWorkerNetworkProvider, return one with
   // an invalid id.
   if (!should_create_provider) {
-    return std::make_unique<WebServiceWorkerNetworkProviderImplForFrame>(
-        base::WrapUnique(new ServiceWorkerNetworkProvider()));
+    return CreateInvalidInstanceForNavigation();
   }
 
   // Otherwise, create the ServiceWorkerNetworkProvider.
@@ -85,7 +95,7 @@
   // is_parent_frame_secure to the browser process, so it can determine the
   // context security when deciding whether to allow a service worker to control
   // the document.
-  const bool is_parent_frame_secure = IsFrameSecure(frame->Parent());
+  const bool is_parent_frame_secure = IsFrameSecure(web_frame->Parent());
 
   // If the browser process did not assign a provider id already, assign one
   // now (see class comments for content::ServiceWorkerProviderHost).
@@ -95,11 +105,12 @@
     provider_id = GetNextProviderId();
 
   auto provider = base::WrapUnique(new ServiceWorkerNetworkProvider(
-      route_id, blink::mojom::ServiceWorkerProviderType::kForWindow,
-      provider_id, is_parent_frame_secure, std::move(controller_info),
+      frame->GetRoutingID(),
+      blink::mojom::ServiceWorkerProviderType::kForWindow, provider_id,
+      is_parent_frame_secure, std::move(controller_info),
       std::move(fallback_loader_factory)));
   return std::make_unique<WebServiceWorkerNetworkProviderImplForFrame>(
-      std::move(provider));
+      std::move(provider), frame);
 }
 
 // static
@@ -195,11 +206,20 @@
       std::move(host_ptr_info), std::move(controller_info),
       std::move(fallback_loader_factory));
 
-  // current() may be null in tests.
   if (ChildThreadImpl::current()) {
     ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface(
         &dispatcher_host_);
     dispatcher_host_->OnProviderCreated(std::move(host_info));
+  } else {
+    // current() may be null in tests. Silently drop messages sent over
+    // ServiceWorkerContainerHost since we couldn't set it up correctly due to
+    // this test limitation. This way we don't crash when the associated
+    // interface ptr is used.
+    //
+    // TODO(falken): Just give SWPC a null interface ptr and make the callsites
+    // deal with it. They are supposed to anyway because
+    // OnNetworkProviderDestroyed() can reset the ptr to null at any time.
+    mojo::AssociateWithDisconnectedPipe(host_info->host_request.PassHandle());
   }
 }
 
diff --git a/content/renderer/service_worker/service_worker_network_provider.h b/content/renderer/service_worker/service_worker_network_provider.h
index df2fc4a6..9d7d57d2 100644
--- a/content/renderer/service_worker/service_worker_network_provider.h
+++ b/content/renderer/service_worker/service_worker_network_provider.h
@@ -21,7 +21,6 @@
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
 
 namespace blink {
-class WebLocalFrame;
 class WebServiceWorkerNetworkProvider;
 }  // namespace blink
 
@@ -36,6 +35,7 @@
 }
 
 struct CommitNavigationParams;
+class RenderFrameImpl;
 class ServiceWorkerProviderContext;
 
 // ServiceWorkerNetworkProvider enables the browser process to recognize
@@ -78,12 +78,16 @@
   // the loading context, e.g. a frame, provides it.
   static std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
   CreateForNavigation(
-      int route_id,
+      RenderFrameImpl* frame,
       const CommitNavigationParams* commit_params,
-      blink::WebLocalFrame* frame,
       blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
       scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory);
 
+  // Creates a "null" network provider for a frame (it has an invalid provider
+  // id). Useful when the frame should not use service worker.
+  static std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
+  CreateInvalidInstanceForNavigation();
+
   // Creates a ServiceWorkerNetworkProvider for a shared worker (as a
   // non-document service worker client).
   //
@@ -142,7 +146,10 @@
       blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
       scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory);
 
+  // |context_| is null if |this| is an invalid instance, in which case there is
+  // no connection to the browser process.
   scoped_refptr<ServiceWorkerProviderContext> context_;
+
   blink::mojom::ServiceWorkerDispatcherHostAssociatedPtr dispatcher_host_;
 
   // For shared worker contexts. The URL loader factory for loading the worker's
diff --git a/content/renderer/service_worker/service_worker_provider_context.cc b/content/renderer/service_worker/service_worker_provider_context.cc
index 9dd93e8..1fb66eb 100644
--- a/content/renderer/service_worker/service_worker_provider_context.cc
+++ b/content/renderer/service_worker/service_worker_provider_context.cc
@@ -228,6 +228,28 @@
   container_host_->HintToUpdateServiceWorker();
 }
 
+void ServiceWorkerProviderContext::NotifyExecutionReady() {
+  DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
+  DCHECK_EQ(provider_type(),
+            blink::mojom::ServiceWorkerProviderType::kForWindow)
+      << "only windows need to send this message; shared workers have "
+         "execution ready set on the browser-side when the response is "
+         "committed";
+  if (!container_host_)
+    return;
+  if (sent_execution_ready_) {
+    // Sometimes a new document can be created for a frame without a proper
+    // navigation, in cases like about:blank and javascript: URLs. In these
+    // cases the provider is not recreated and Blink can tell us that it's
+    // execution ready more than once. The browser-side host doesn't support
+    // changing the URL of the provider in these cases, so just ignore these
+    // notifications.
+    return;
+  }
+  sent_execution_ready_ = true;
+  container_host_->OnExecutionReady();
+}
+
 void ServiceWorkerProviderContext::UnregisterWorkerFetchContext(
     blink::mojom::ServiceWorkerWorkerClient* client) {
   DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
diff --git a/content/renderer/service_worker/service_worker_provider_context.h b/content/renderer/service_worker/service_worker_provider_context.h
index b4811299..57e6bc1 100644
--- a/content/renderer/service_worker/service_worker_provider_context.h
+++ b/content/renderer/service_worker/service_worker_provider_context.h
@@ -162,6 +162,10 @@
   // hint to start the service worker update check.
   void DispatchNetworkQuiet();
 
+  // Tells the container host that this context is execution ready:
+  // https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-execution-ready-flag
+  void NotifyExecutionReady();
+
  private:
   friend class base::DeleteHelper<ServiceWorkerProviderContext>;
   friend class base::RefCountedThreadSafe<ServiceWorkerProviderContext,
@@ -217,6 +221,8 @@
   // class is only for service worker clients now.
   std::unique_ptr<ServiceWorkerProviderStateForClient> state_for_client_;
 
+  bool sent_execution_ready_ = false;
+
   // NOTE: Add new members to |state_for_client_| if they are relevant only for
   // service worker clients. Not here!
 
diff --git a/content/renderer/service_worker/service_worker_provider_context_unittest.cc b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
index 40c6bc411b..7ec251f 100644
--- a/content/renderer/service_worker/service_worker_provider_context_unittest.cc
+++ b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
@@ -100,9 +100,9 @@
     was_set_controller_called_ = true;
   }
 
-  void DispatchMessageEvent(blink::WebServiceWorkerObjectInfo info,
-                            blink::TransferableMessage message) override {
-    was_dispatch_message_event_called_ = true;
+  void ReceiveMessage(blink::WebServiceWorkerObjectInfo info,
+                      blink::TransferableMessage message) override {
+    was_receive_message_called_ = true;
   }
 
   void CountFeature(blink::mojom::WebFeature feature) override {
@@ -111,8 +111,8 @@
 
   bool was_set_controller_called() const { return was_set_controller_called_; }
 
-  bool was_dispatch_message_event_called() const {
-    return was_dispatch_message_event_called_;
+  bool was_receive_message_called() const {
+    return was_receive_message_called_;
   }
 
   const std::set<blink::mojom::WebFeature>& used_features() const {
@@ -121,7 +121,7 @@
 
  private:
   bool was_set_controller_called_ = false;
-  bool was_dispatch_message_event_called_ = false;
+  bool was_receive_message_called_ = false;
   std::set<blink::mojom::WebFeature> used_features_;
 };
 
@@ -250,6 +250,7 @@
   }
   void Ping(PingCallback callback) override { NOTIMPLEMENTED(); }
   void HintToUpdateServiceWorker() override { NOTIMPLEMENTED(); }
+  void OnExecutionReady() override {}
 
  private:
   mojo::BindingSet<blink::mojom::ServiceWorkerContainerHost> bindings_;
@@ -312,6 +313,10 @@
         mock_service_worker_object_host->CreateObjectInfo();
     EXPECT_EQ(1, mock_service_worker_object_host->GetBindingCount());
 
+    blink::mojom::ServiceWorkerContainerHostAssociatedPtr host_ptr;
+    blink::mojom::ServiceWorkerContainerHostAssociatedRequest host_request =
+        mojo::MakeRequestAssociatedWithDedicatedPipe(&host_ptr);
+
     // (1) In the case there is no WebSWProviderClient but SWProviderContext for
     // the provider, the passed reference should be adopted and owned by the
     // provider context.
@@ -320,7 +325,7 @@
         mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr);
     auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
         kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow,
-        std::move(container_request), nullptr /* host_ptr_info */,
+        std::move(container_request), host_ptr.PassInterface(),
         nullptr /* controller_info */, nullptr /* loader_factory*/);
 
     auto info = blink::mojom::ControllerServiceWorkerInfo::New();
@@ -351,16 +356,16 @@
     // context and then be transfered ownership to the provider client, after
     // that due to limitation of the mock implementation, the reference
     // immediately gets released.
-    blink::mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info;
+    blink::mojom::ServiceWorkerContainerHostAssociatedPtr host_ptr;
     blink::mojom::ServiceWorkerContainerHostAssociatedRequest host_request =
-        mojo::MakeRequest(&host_ptr_info);
+        mojo::MakeRequestAssociatedWithDedicatedPipe(&host_ptr);
 
     blink::mojom::ServiceWorkerContainerAssociatedPtr container_ptr;
     blink::mojom::ServiceWorkerContainerAssociatedRequest container_request =
         mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr);
     auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
         kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow,
-        std::move(container_request), std::move(host_ptr_info),
+        std::move(container_request), host_ptr.PassInterface(),
         nullptr /* controller_info */, nullptr /* loader_factory*/);
     auto provider_impl =
         std::make_unique<WebServiceWorkerProviderImpl>(provider_context.get());
@@ -386,16 +391,16 @@
 TEST_F(ServiceWorkerProviderContextTest, SetController_Null) {
   const int kProviderId = 10;
 
-  blink::mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info;
+  blink::mojom::ServiceWorkerContainerHostAssociatedPtr host_ptr;
   blink::mojom::ServiceWorkerContainerHostAssociatedRequest host_request =
-      mojo::MakeRequest(&host_ptr_info);
+      mojo::MakeRequestAssociatedWithDedicatedPipe(&host_ptr);
 
   blink::mojom::ServiceWorkerContainerAssociatedPtr container_ptr;
   blink::mojom::ServiceWorkerContainerAssociatedRequest container_request =
       mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr);
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
       kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow,
-      std::move(container_request), std::move(host_ptr_info),
+      std::move(container_request), host_ptr.PassInterface(),
       nullptr /* controller_info */, nullptr /* loader_factory*/);
   auto provider_impl =
       std::make_unique<WebServiceWorkerProviderImpl>(provider_context.get());
@@ -643,22 +648,22 @@
       mock_service_worker_object_host->CreateObjectInfo();
   EXPECT_EQ(1, mock_service_worker_object_host->GetBindingCount());
 
-  blink::mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info;
+  blink::mojom::ServiceWorkerContainerHostAssociatedPtr host_ptr;
   blink::mojom::ServiceWorkerContainerHostAssociatedRequest host_request =
-      mojo::MakeRequest(&host_ptr_info);
+      mojo::MakeRequestAssociatedWithDedicatedPipe(&host_ptr);
 
   blink::mojom::ServiceWorkerContainerAssociatedPtr container_ptr;
   blink::mojom::ServiceWorkerContainerAssociatedRequest container_request =
       mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr);
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
       kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow,
-      std::move(container_request), std::move(host_ptr_info),
+      std::move(container_request), host_ptr.PassInterface(),
       nullptr /* controller_info */, nullptr /* loader_factory*/);
   auto provider_impl =
       std::make_unique<WebServiceWorkerProviderImpl>(provider_context.get());
   auto client = std::make_unique<MockWebServiceWorkerProviderClientImpl>();
   provider_impl->SetClient(client.get());
-  ASSERT_FALSE(client->was_dispatch_message_event_called());
+  ASSERT_FALSE(client->was_receive_message_called());
 
   container_ptr->PostMessageToClient(std::move(object_info),
                                      blink::TransferableMessage());
@@ -666,23 +671,23 @@
 
   // The passed reference should be owned by the provider client (but the
   // reference is immediately released by the mock provider client).
-  EXPECT_TRUE(client->was_dispatch_message_event_called());
+  EXPECT_TRUE(client->was_receive_message_called());
   EXPECT_EQ(0, mock_service_worker_object_host->GetBindingCount());
 }
 
 TEST_F(ServiceWorkerProviderContextTest, CountFeature) {
   const int kProviderId = 10;
 
-  blink::mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info;
+  blink::mojom::ServiceWorkerContainerHostAssociatedPtr host_ptr;
   blink::mojom::ServiceWorkerContainerHostAssociatedRequest host_request =
-      mojo::MakeRequest(&host_ptr_info);
+      mojo::MakeRequestAssociatedWithDedicatedPipe(&host_ptr);
 
   blink::mojom::ServiceWorkerContainerAssociatedPtr container_ptr;
   blink::mojom::ServiceWorkerContainerAssociatedRequest container_request =
       mojo::MakeRequestAssociatedWithDedicatedPipe(&container_ptr);
   auto provider_context = base::MakeRefCounted<ServiceWorkerProviderContext>(
       kProviderId, blink::mojom::ServiceWorkerProviderType::kForWindow,
-      std::move(container_request), std::move(host_ptr_info),
+      std::move(container_request), host_ptr.PassInterface(),
       nullptr /* controller_info */, nullptr /* loader_factory*/);
   auto provider_impl =
       std::make_unique<WebServiceWorkerProviderImpl>(provider_context.get());
diff --git a/content/renderer/service_worker/service_worker_provider_state_for_client.h b/content/renderer/service_worker/service_worker_provider_state_for_client.h
index 59ab7e2..79a278f 100644
--- a/content/renderer/service_worker/service_worker_provider_state_for_client.h
+++ b/content/renderer/service_worker/service_worker_provider_state_for_client.h
@@ -57,8 +57,9 @@
   // Tracks feature usage for UseCounter.
   std::set<blink::mojom::WebFeature> used_features;
 
-  // Corresponds to a ServiceWorkerContainer. We notify it when
-  // ServiceWorkerContainer#controller should be changed.
+  // Corresponds to this client's ServiceWorkerContainer. May be null when not
+  // yet created, when already destroyed, or when this client is not a Document
+  // and therefore doesn't support navigator.serviceWorker.
   base::WeakPtr<WebServiceWorkerProviderImpl> web_service_worker_provider;
 
   // Keeps ServiceWorkerWorkerClient pointers of dedicated or shared workers
diff --git a/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc b/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
index 5a14bef..05341526 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
+++ b/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
@@ -448,6 +448,7 @@
   }
   void Ping(PingCallback callback) override { NOTIMPLEMENTED(); }
   void HintToUpdateServiceWorker() override { NOTIMPLEMENTED(); }
+  void OnExecutionReady() override {}
 
  private:
   int get_controller_service_worker_count_ = 0;
diff --git a/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.cc b/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.cc
index 8a69020..f86037f 100644
--- a/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.cc
+++ b/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.cc
@@ -4,19 +4,66 @@
 
 #include "content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h"
 
+#include <utility>
+
 #include "content/public/common/origin_util.h"
+#include "content/public/renderer/render_frame_observer.h"
 #include "content/renderer/loader/request_extra_data.h"
+#include "content/renderer/render_frame_impl.h"
 #include "content/renderer/render_thread_impl.h"
 #include "content/renderer/service_worker/service_worker_network_provider.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "third_party/blink/public/common/service_worker/service_worker_utils.h"
+#include "third_party/blink/public/web/web_local_frame.h"
 
 namespace content {
 
+class WebServiceWorkerNetworkProviderImplForFrame::NewDocumentObserver
+    : public RenderFrameObserver {
+ public:
+  NewDocumentObserver(WebServiceWorkerNetworkProviderImplForFrame* owner,
+                      RenderFrameImpl* frame)
+      : RenderFrameObserver(frame), owner_(owner) {}
+
+  void DidCreateNewDocument() override {
+    blink::WebLocalFrame* web_frame = render_frame()->GetWebFrame();
+    blink::WebDocumentLoader* web_loader =
+        render_frame()->GetWebFrame()->GetDocumentLoader();
+    DCHECK_EQ(owner_, web_loader->GetServiceWorkerNetworkProvider());
+
+    if (web_frame->GetSecurityOrigin().IsOpaque()) {
+      // At navigation commit we thought the document was eligible to use
+      // service workers so created the network provider, but it turns out it is
+      // not eligible because it is CSP sandboxed.
+      web_loader->SetServiceWorkerNetworkProvider(
+          ServiceWorkerNetworkProvider::CreateInvalidInstanceForNavigation());
+      // |this| and its owner are destroyed.
+      return;
+    }
+
+    owner_->NotifyExecutionReady();
+  }
+
+  void OnDestruct() override {
+    // Deletes |this|.
+    owner_->observer_.reset();
+  }
+
+ private:
+  WebServiceWorkerNetworkProviderImplForFrame* owner_;
+};
+
 WebServiceWorkerNetworkProviderImplForFrame::
     WebServiceWorkerNetworkProviderImplForFrame(
-        std::unique_ptr<ServiceWorkerNetworkProvider> provider)
-    : WebServiceWorkerNetworkProviderBaseImpl(std::move(provider)) {}
+        std::unique_ptr<ServiceWorkerNetworkProvider> provider,
+        RenderFrameImpl* frame)
+    : WebServiceWorkerNetworkProviderBaseImpl(std::move(provider)) {
+  if (frame)
+    observer_ = std::make_unique<NewDocumentObserver>(this, frame);
+}
+
+WebServiceWorkerNetworkProviderImplForFrame::
+    ~WebServiceWorkerNetworkProviderImplForFrame() = default;
 
 void WebServiceWorkerNetworkProviderImplForFrame::WillSendRequest(
     blink::WebURLRequest& request) {
@@ -98,4 +145,9 @@
   provider()->DispatchNetworkQuiet();
 }
 
+void WebServiceWorkerNetworkProviderImplForFrame::NotifyExecutionReady() {
+  if (provider()->context())
+    provider()->context()->NotifyExecutionReady();
+}
+
 }  // namespace content
diff --git a/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h b/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h
index 8cc0809..9baad27 100644
--- a/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h
+++ b/content/renderer/service_worker/web_service_worker_network_provider_impl_for_frame.h
@@ -5,17 +5,24 @@
 #ifndef CONTENT_RENDERER_SERVICE_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_IMPL_FOR_FRAME_H_
 #define CONTENT_RENDERER_SERVICE_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_IMPL_FOR_FRAME_H_
 
+#include <memory>
+
+#include "content/renderer/service_worker/service_worker_network_provider.h"
 #include "content/renderer/service_worker/web_service_worker_network_provider_base_impl.h"
 
 namespace content {
 
+class RenderFrameImpl;
+
 // An WebServiceWorkerNetworkProvider for frame. This wraps
 // ServiceWorkerNetworkProvider implementation and is owned by blink.
 class WebServiceWorkerNetworkProviderImplForFrame final
     : public WebServiceWorkerNetworkProviderBaseImpl {
  public:
-  explicit WebServiceWorkerNetworkProviderImplForFrame(
-      std::unique_ptr<ServiceWorkerNetworkProvider> provider);
+  WebServiceWorkerNetworkProviderImplForFrame(
+      std::unique_ptr<ServiceWorkerNetworkProvider> provider,
+      RenderFrameImpl* frame);
+  ~WebServiceWorkerNetworkProviderImplForFrame() override;
 
   // Implements WebServiceWorkerNetworkProviderBaseImpl.
   void WillSendRequest(blink::WebURLRequest& request) override;
@@ -24,6 +31,13 @@
       std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>
           task_runner_handle) override;
   void DispatchNetworkQuiet() override;
+
+ private:
+  class NewDocumentObserver;
+
+  void NotifyExecutionReady();
+
+  std::unique_ptr<NewDocumentObserver> observer_;
 };
 
 }  // namespace content
diff --git a/content/renderer/service_worker/web_service_worker_provider_impl.cc b/content/renderer/service_worker/web_service_worker_provider_impl.cc
index 4ddfa11..ead5198d 100644
--- a/content/renderer/service_worker/web_service_worker_provider_impl.cc
+++ b/content/renderer/service_worker/web_service_worker_provider_impl.cc
@@ -201,7 +201,7 @@
   if (!provider_client_)
     return;
 
-  provider_client_->DispatchMessageEvent(
+  provider_client_->ReceiveMessage(
       source.To<blink::WebServiceWorkerObjectInfo>(), std::move(message));
 }
 
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.cc b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
index c0d220d9..53b9bee 100644
--- a/content/renderer/shared_worker/embedded_shared_worker_stub.cc
+++ b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
@@ -208,7 +208,6 @@
 void EmbeddedSharedWorkerStub::WorkerScriptLoadFailed() {
   host_->OnScriptLoadFailed();
   pending_channels_.clear();
-  delete this;
 }
 
 void EmbeddedSharedWorkerStub::WorkerScriptEvaluated(bool success) {
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.h b/content/renderer/shared_worker/embedded_shared_worker_stub.h
index 27d2e428..8d607611 100644
--- a/content/renderer/shared_worker/embedded_shared_worker_stub.h
+++ b/content/renderer/shared_worker/embedded_shared_worker_stub.h
@@ -49,10 +49,7 @@
 // A stub class to receive IPC from browser process and talk to
 // blink::WebSharedWorker. Implements blink::WebSharedWorkerClient.
 // This class is self-destructed (no one explicitly owns this). It deletes
-// itself when either one of following methods is called by
-// blink::WebSharedWorker:
-// - WorkerScriptLoadFailed() or
-// - WorkerContextDestroyed()
+// itself when WorkerContextDestroyed() is called by blink::WebSharedWorker.
 //
 // This class owns blink::WebSharedWorker.
 class EmbeddedSharedWorkerStub : public blink::WebSharedWorkerClient,
diff --git a/content/shell/test_runner/app_banner_service.cc b/content/shell/test_runner/app_banner_service.cc
index 0dbf26e..8fe9c46 100644
--- a/content/shell/test_runner/app_banner_service.cc
+++ b/content/shell/test_runner/app_banner_service.cc
@@ -42,8 +42,7 @@
 
 void AppBannerService::OnBannerPromptReply(
     base::OnceCallback<void(bool)> callback,
-    blink::mojom::AppBannerPromptReply reply,
-    const std::string& referrer) {
+    blink::mojom::AppBannerPromptReply reply) {
   std::move(callback).Run(reply == blink::mojom::AppBannerPromptReply::CANCEL);
 }
 
diff --git a/content/shell/test_runner/app_banner_service.h b/content/shell/test_runner/app_banner_service.h
index d52125b..fd005ad3 100644
--- a/content/shell/test_runner/app_banner_service.h
+++ b/content/shell/test_runner/app_banner_service.h
@@ -34,8 +34,7 @@
 
  private:
   void OnBannerPromptReply(base::OnceCallback<void(bool)> callback,
-                           blink::mojom::AppBannerPromptReply,
-                           const std::string& referrer);
+                           blink::mojom::AppBannerPromptReply);
 
   mojo::Binding<blink::mojom::AppBannerService> binding_;
   blink::mojom::AppBannerEventPtr event_;
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py
index c707c979..6ee446c 100644
--- a/content/test/gpu/gpu_tests/pixel_expectations.py
+++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -117,6 +117,10 @@
     self.Fail('Pixel_BackgroundImage',
         ['android', ('qualcomm', 'Adreno (TM) 430')], bug=883500)
 
+    # Flakes on Nexus 5X.
+    self.Flaky('Pixel_BackgroundImage',
+        ['android', ('qualcomm', 'Adreno (TM) 418')], bug=883500)
+
     # TODO(wangxianzhu): Re-enable after and rebaselining
     self.Fail('Pixel_CSSFilterEffects', bug=836884)
     self.Fail('Pixel_CSSFilterEffects_NoOverlays', bug=836884)
diff --git a/extensions/browser/api/serial/serial_api.cc b/extensions/browser/api/serial/serial_api.cc
index 23d4f88..98b5f7aa 100644
--- a/extensions/browser/api/serial/serial_api.cc
+++ b/extensions/browser/api/serial/serial_api.cc
@@ -179,9 +179,9 @@
           connections->Remove(extension_id, api_resource_id);
         },
         manager_->data_, extension_->id(), id));
-
     info->connection_id = id;
-    serial_port_manager_->PollConnection(extension_->id(), id);
+    // Start polling.
+    serial_port_manager_->StartConnectionPolling(extension_->id(), id);
     results_ = serial::Connect::Results::Create(*info);
   }
   AsyncWorkCompleted();
@@ -321,10 +321,6 @@
 
   if (params_->paused != connection->paused()) {
     connection->set_paused(params_->paused);
-    if (!params_->paused) {
-      serial_port_manager_->PollConnection(extension_->id(),
-                                           params_->connection_id);
-    }
   }
 
   results_ = serial::SetPaused::Results::Create();
diff --git a/extensions/browser/api/serial/serial_apitest.cc b/extensions/browser/api/serial/serial_apitest.cc
index b0c4e1c..1de63283 100644
--- a/extensions/browser/api/serial/serial_apitest.cc
+++ b/extensions/browser/api/serial/serial_apitest.cc
@@ -21,6 +21,8 @@
 #include "extensions/common/switches.h"
 #include "extensions/test/result_catcher.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 #include "services/device/public/mojom/constants.mojom.h"
 #include "services/device/public/mojom/serial.mojom.h"
 #include "services/service_manager/public/cpp/service_binding.h"
@@ -55,7 +57,9 @@
 
 class FakeSerialPort : public device::mojom::SerialPort {
  public:
-  explicit FakeSerialPort(const base::FilePath& path) {
+  explicit FakeSerialPort(const base::FilePath& path)
+      : out_stream_watcher_(FROM_HERE,
+                            mojo::SimpleWatcher::ArmingPolicy::MANUAL) {
     options_.bitrate = 9600;
     options_.data_bits = device::mojom::SerialDataBits::EIGHT;
     options_.parity_bit = device::mojom::SerialParityBit::NO_PARITY;
@@ -68,29 +72,26 @@
  private:
   // device::mojom::SerialPort methods:
   void Open(device::mojom::SerialConnectionOptionsPtr options,
+            mojo::ScopedDataPipeProducerHandle out_stream,
+            device::mojom::SerialPortClientAssociatedPtrInfo client,
             OpenCallback callback) override {
     DoConfigurePort(*options);
+    DCHECK(client);
+    client_.Bind(std::move(client));
+    SetUpReadDataPipe(std::move(out_stream));
     std::move(callback).Run(true);
   }
-  void Read(uint32_t bytes, ReadCallback callback) override {
-    DCHECK(!pending_read_callback_);
-    pending_read_callback_ = std::move(callback);
-    pending_read_bytes_ = bytes;
-    if (buffer_.empty())
-      return;
-
-    DoRead();
-  }
   void Write(const std::vector<uint8_t>& data,
              WriteCallback callback) override {
     buffer_.insert(buffer_.end(), data.cbegin(), data.cend());
     std::move(callback).Run(data.size(), device::mojom::SerialSendError::NONE);
-    DoRead();
+    out_stream_watcher_.ArmOrNotify();
   }
-  void CancelRead(device::mojom::SerialReceiveError reason) override {
-    if (pending_read_callback_) {
-      std::move(pending_read_callback_).Run(std::vector<uint8_t>(), reason);
+  void ClearReadError(mojo::ScopedDataPipeProducerHandle producer) override {
+    if (out_stream_) {
+      return;
     }
+    SetUpReadDataPipe(std::move(producer));
   }
   void CancelWrite(device::mojom::SerialSendError reason) override {}
   void Flush(FlushCallback callback) override { std::move(callback).Run(true); }
@@ -127,17 +128,46 @@
     std::move(callback).Run(true);
   }
 
-  void DoRead() {
-    if (!pending_read_callback_) {
+  void SetUpReadDataPipe(mojo::ScopedDataPipeProducerHandle producer) {
+    out_stream_.swap(producer);
+    out_stream_watcher_.Watch(
+        out_stream_.get(),
+        MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+        MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
+        base::BindRepeating(&FakeSerialPort::DoRead, base::Unretained(this)));
+    out_stream_watcher_.ArmOrNotify();
+  }
+
+  void DoRead(MojoResult result, const mojo::HandleSignalsState& state) {
+    if (result != MOJO_RESULT_OK) {
+      out_stream_.reset();
       return;
     }
-    size_t num_bytes =
-        std::min(buffer_.size(), static_cast<size_t>(pending_read_bytes_));
-    std::move(pending_read_callback_)
-        .Run(std::vector<uint8_t>(buffer_.data(), buffer_.data() + num_bytes),
-             device::mojom::SerialReceiveError::NONE);
+    if (buffer_.empty()) {
+      return;
+    }
+    read_step_++;
+    if (read_step_ == 1) {
+      // Write one byte first.
+      WriteOutReadData(1);
+    } else if (read_step_ == 2) {
+      // Write one byte in second step and trigger a break error.
+      WriteOutReadData(1);
+      DCHECK(client_);
+      client_->OnReadError(device::mojom::SerialReceiveError::PARITY_ERROR);
+      out_stream_.reset();
+      return;
+    } else {
+      // Write out the rest data after reconnecting.
+      WriteOutReadData(buffer_.size());
+    }
+    out_stream_watcher_.ArmOrNotify();
+  }
+
+  void WriteOutReadData(uint32_t num_bytes) {
+    out_stream_->WriteData(buffer_.data(), &num_bytes,
+                           MOJO_WRITE_DATA_FLAG_NONE);
     buffer_.erase(buffer_.begin(), buffer_.begin() + num_bytes);
-    pending_read_bytes_ = 0;
   }
 
   void DoConfigurePort(const device::mojom::SerialConnectionOptions& options) {
@@ -163,8 +193,10 @@
   // Currently applied connection options.
   device::mojom::SerialConnectionOptions options_;
   std::vector<uint8_t> buffer_;
-  FakeSerialPort::ReadCallback pending_read_callback_;
-  uint32_t pending_read_bytes_ = 0;
+  int read_step_ = 0;
+  device::mojom::SerialPortClientAssociatedPtr client_;
+  mojo::ScopedDataPipeProducerHandle out_stream_;
+  mojo::SimpleWatcher out_stream_watcher_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeSerialPort);
 };
diff --git a/extensions/browser/api/serial/serial_connection.cc b/extensions/browser/api/serial/serial_connection.cc
index c0333ea..0af4a299 100644
--- a/extensions/browser/api/serial/serial_connection.cc
+++ b/extensions/browser/api/serial/serial_connection.cc
@@ -171,7 +171,11 @@
       buffer_size_(kDefaultBufferSize),
       receive_timeout_(0),
       send_timeout_(0),
-      paused_(false),
+      paused_(true),
+      read_error_(base::nullopt),
+      receive_pipe_watcher_(FROM_HERE,
+                            mojo::SimpleWatcher::ArmingPolicy::MANUAL),
+      client_binding_(this),
       weak_factory_(this) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(serial_port_info.is_valid());
@@ -181,7 +185,6 @@
 }
 
 SerialConnection::~SerialConnection() {
-  serial_port_->CancelRead(device::mojom::SerialReceiveError::DISCONNECTED);
   serial_port_->CancelWrite(device::mojom::SerialSendError::DISCONNECTED);
 }
 
@@ -203,9 +206,21 @@
 
 void SerialConnection::set_paused(bool paused) {
   DCHECK(serial_port_);
+  if (paused_ == paused)
+    return;
+
   paused_ = paused;
-  if (paused) {
-    serial_port_->CancelRead(device::mojom::SerialReceiveError::NONE);
+  if (!paused_) {
+    // If |receive_pipe_| is closed and there is no pending ReceiveError event,
+    // try to reconnect the data pipe.
+    if (!receive_pipe_ && !read_error_) {
+      mojo::ScopedDataPipeProducerHandle receive_producer;
+      SetUpReceiveDataPipe(&receive_producer);
+      serial_port_->ClearReadError(std::move(receive_producer));
+    }
+    receive_pipe_watcher_.ArmOrNotify();
+    receive_timeout_task_.Cancel();
+    SetTimeoutCallback();
   }
 }
 
@@ -234,32 +249,119 @@
     set_receive_timeout(*options.receive_timeout);
   if (options.send_timeout.get())
     set_send_timeout(*options.send_timeout);
+
+  mojo::ScopedDataPipeProducerHandle receive_producer;
+  // Make sure receive_pipe_ only be initialized once.
+  if (!receive_pipe_) {
+    SetUpReceiveDataPipe(&receive_producer);
+  }
+  DCHECK(receive_pipe_);
+  // In case Open() being called more than once.
+  if (client_binding_) {
+    client_binding_.Close();
+  }
+  device::mojom::SerialPortClientAssociatedPtrInfo client;
+  client_binding_.Bind(mojo::MakeRequest(&client));
+  client_binding_.set_connection_error_handler(base::BindOnce(
+      &SerialConnection::OnClientBindingClosed, weak_factory_.GetWeakPtr()));
+
   serial_port_->Open(
       device::mojom::SerialConnectionOptions::From(options),
+      std::move(receive_producer), std::move(client),
       mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), false));
 }
 
-bool SerialConnection::Receive(ReceiveCompleteCallback callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  if (receive_complete_)
-    return false;
-  DCHECK(serial_port_);
-  receive_complete_ = std::move(callback);
-  serial_port_->Read(buffer_size_,
-                     mojo::WrapCallbackWithDefaultInvokeIfNotRun(
-                         base::BindOnce(&SerialConnection::OnAsyncReadComplete,
-                                        weak_factory_.GetWeakPtr()),
-                         std::vector<uint8_t>(),
-                         device::mojom::SerialReceiveError::DISCONNECTED));
-  receive_timeout_task_.Cancel();
-  if (receive_timeout_ > 0) {
-    receive_timeout_task_.Reset(base::Bind(&SerialConnection::OnReceiveTimeout,
-                                           weak_factory_.GetWeakPtr()));
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE, receive_timeout_task_.callback(),
-        base::TimeDelta::FromMilliseconds(receive_timeout_));
+void SerialConnection::SetUpReceiveDataPipe(
+    mojo::ScopedDataPipeProducerHandle* producer) {
+  MojoCreateDataPipeOptions options;
+  options.struct_size = sizeof(MojoCreateDataPipeOptions);
+  options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE;
+  options.element_num_bytes = 1;
+  options.capacity_num_bytes = buffer_size_;
+
+  if (receive_pipe_watcher_.IsWatching()) {
+    receive_pipe_watcher_.Cancel();
   }
-  return true;
+  CHECK_EQ(MOJO_RESULT_OK,
+           mojo::CreateDataPipe(&options, producer, &receive_pipe_));
+  receive_pipe_watcher_.Watch(
+      receive_pipe_.get(),
+      MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+      MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
+      base::BindRepeating(&SerialConnection::OnReadPipeReadableOrClosed,
+                          weak_factory_.GetWeakPtr()));
+}
+
+void SerialConnection::OnReadError(device::mojom::SerialReceiveError error) {
+  DCHECK_NE(device::mojom::SerialReceiveError::NONE, error);
+  if (receive_pipe_) {
+    // Wait for remaining data to be read from the pipe before signaling an
+    // error.
+    read_error_ = error;
+    return;
+  }
+  // Dispatch OnReceiveError event when the data pipe has been closed.
+  receive_event_cb_.Run(std::vector<uint8_t>(),
+                        ConvertReceiveErrorFromMojo(error));
+}
+
+void SerialConnection::OnReadPipeClosed() {
+  receive_pipe_.reset();
+  if (read_error_) {
+    // Dispatch OnReceiveError if there is a pending error.
+    receive_event_cb_.Run(std::vector<uint8_t>(),
+                          ConvertReceiveErrorFromMojo(read_error_.value()));
+    read_error_.reset();
+  }
+}
+
+void SerialConnection::OnReadPipeReadableOrClosed(
+    MojoResult result,
+    const mojo::HandleSignalsState& state) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  // Data pipe disconnected.
+  if (result != MOJO_RESULT_OK) {
+    OnReadPipeClosed();
+    return;
+  }
+  // Return when it is paused, the watcher will be disarmed.
+  if (paused_)
+    return;
+
+  DCHECK(receive_pipe_);
+  const void* buffer;
+  uint32_t read_bytes;
+  result = receive_pipe_->BeginReadData(&buffer, &read_bytes,
+                                        MOJO_READ_DATA_FLAG_NONE);
+  if (result == MOJO_RESULT_SHOULD_WAIT) {
+    receive_pipe_watcher_.ArmOrNotify();
+    return;
+  }
+  // For remote pipe producer handle has been closed.
+  if (result == MOJO_RESULT_FAILED_PRECONDITION) {
+    OnReadPipeClosed();
+    return;
+  }
+  const char* char_buffer = static_cast<const char*>(buffer);
+  std::vector<uint8_t> data(char_buffer, char_buffer + read_bytes);
+  result = receive_pipe_->EndReadData(read_bytes);
+  DCHECK_EQ(MOJO_RESULT_OK, result);
+
+  receive_event_cb_.Run(std::move(data), api::serial::RECEIVE_ERROR_NONE);
+  receive_timeout_task_.Cancel();
+  // If there is no error nor paused, go on with the polling process and set
+  // timeout callback.
+  receive_pipe_watcher_.ArmOrNotify();
+  SetTimeoutCallback();
+}
+
+void SerialConnection::StartPolling(const ReceiveEventCallback& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  receive_event_cb_ = callback;
+  DCHECK(receive_event_cb_);
+  DCHECK(receive_pipe_);
+  DCHECK(paused_);
+  set_paused(false);
 }
 
 bool SerialConnection::Send(const std::vector<uint8_t>& data,
@@ -303,7 +405,6 @@
   serial_port_->ConfigurePort(
       device::mojom::SerialConnectionOptions::From(options),
       mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), false));
-  serial_port_->CancelRead(device::mojom::SerialReceiveError::NONE);
 }
 
 void SerialConnection::GetInfo(GetInfoCompleteCallback callback) const {
@@ -390,10 +491,23 @@
       mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), false));
 }
 
+void SerialConnection::SetTimeoutCallback() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  if (receive_timeout_ > 0) {
+    receive_timeout_task_.Reset(base::Bind(&SerialConnection::OnReceiveTimeout,
+                                           weak_factory_.GetWeakPtr()));
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, receive_timeout_task_.callback(),
+        base::TimeDelta::FromMilliseconds(receive_timeout_));
+  }
+}
+
 void SerialConnection::OnReceiveTimeout() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(serial_port_);
-  serial_port_->CancelRead(device::mojom::SerialReceiveError::TIMEOUT);
+  receive_event_cb_.Run(std::vector<uint8_t>(),
+                        api::serial::RECEIVE_ERROR_TIMEOUT);
 }
 
 void SerialConnection::OnSendTimeout() {
@@ -402,16 +516,6 @@
   serial_port_->CancelWrite(device::mojom::SerialSendError::TIMEOUT);
 }
 
-void SerialConnection::OnAsyncReadComplete(
-    const std::vector<uint8_t>& data_read,
-    device::mojom::SerialReceiveError error) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  DCHECK(receive_complete_);
-  receive_timeout_task_.Cancel();
-  std::move(receive_complete_)
-      .Run(data_read, ConvertReceiveErrorFromMojo(error));
-}
-
 void SerialConnection::OnAsyncWriteComplete(
     uint32_t bytes_sent,
     device::mojom::SerialSendError error) {
@@ -428,6 +532,11 @@
   }
 }
 
+void SerialConnection::OnClientBindingClosed() {
+  client_binding_.Close();
+  OnReadError(device::mojom::SerialReceiveError::DISCONNECTED);
+}
+
 }  // namespace extensions
 
 namespace mojo {
diff --git a/extensions/browser/api/serial/serial_connection.h b/extensions/browser/api/serial/serial_connection.h
index a97cfed..ec29975 100644
--- a/extensions/browser/api/serial/serial_connection.h
+++ b/extensions/browser/api/serial/serial_connection.h
@@ -18,6 +18,9 @@
 #include "extensions/browser/api/api_resource.h"
 #include "extensions/browser/api/api_resource_manager.h"
 #include "extensions/common/api/serial.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 #include "net/base/io_buffer.h"
 #include "services/device/public/mojom/serial.mojom.h"
 
@@ -29,7 +32,8 @@
 // corresponds with an open serial port in remote side(Device Service). NOTE:
 // Instances of this object should only be constructed on the IO thread, and all
 // methods should only be called on the IO thread unless otherwise noted.
-class SerialConnection : public ApiResource {
+class SerialConnection : public ApiResource,
+                         public device::mojom::SerialPortClient {
  public:
   using OpenCompleteCallback = device::mojom::SerialPort::OpenCallback;
   using GetInfoCompleteCallback =
@@ -39,10 +43,9 @@
   // This is the callback type expected by Receive. Note that an error result
   // does not necessarily imply an empty |data| string, since a receive may
   // complete partially before being interrupted by an error condition.
-  using ReceiveCompleteCallback =
-      base::OnceCallback<void(std::vector<uint8_t> data,
-                              api::serial::ReceiveError error)>;
-
+  using ReceiveEventCallback =
+      base::RepeatingCallback<void(std::vector<uint8_t> data,
+                                   api::serial::ReceiveError error)>;
   // This is the callback type expected by Send. Note that an error result
   // does not necessarily imply 0 bytes sent, since a send may complete
   // partially before being interrupted by an error condition.
@@ -99,17 +102,15 @@
   virtual void Open(const api::serial::ConnectionOptions& options,
                     OpenCompleteCallback callback);
 
-  // Begins an asynchronous receive operation. Calling this while a Receive
-  // is already pending is a no-op and returns |false| without calling
-  // |callback|.
-  virtual bool Receive(ReceiveCompleteCallback callback);
-
   // Begins an asynchronous send operation. Calling this while a Send
   // is already pending is a no-op and returns |false| without calling
   // |callback|.
   virtual bool Send(const std::vector<uint8_t>& data,
                     SendCompleteCallback callback);
 
+  // Start to the polling process from |receive_pipe_|.
+  virtual void StartPolling(const ReceiveEventCallback& callback);
+
   // Flushes input and output buffers.
   void Flush(FlushCompleteCallback callback) const;
 
@@ -148,16 +149,25 @@
   friend class ApiResourceManager<SerialConnection>;
   static const char* service_name() { return "SerialConnectionManager"; }
 
+  // device::mojom::SerialPortClient override.
+  void OnReadError(device::mojom::SerialReceiveError error) override;
+
+  // Read data from |receive_pipe_| when the data is ready or dispatch error
+  // events in error cases.
+  void OnReadPipeReadableOrClosed(MojoResult result,
+                                  const mojo::HandleSignalsState& state);
+  void OnReadPipeClosed();
+
+  void SetUpReceiveDataPipe(mojo::ScopedDataPipeProducerHandle* producer);
+
+  void SetTimeoutCallback();
+
   // Handles a receive timeout.
   void OnReceiveTimeout();
 
   // Handles a send timeout.
   void OnSendTimeout();
 
-  // Receives read completion notification from the |serial_port_|.
-  void OnAsyncReadComplete(const std::vector<uint8_t>& data,
-                           device::mojom::SerialReceiveError error);
-
   // Receives write completion notification from the |serial_port_|.
   void OnAsyncWriteComplete(uint32_t bytes_sent,
                             device::mojom::SerialSendError error);
@@ -165,6 +175,9 @@
   // Handles |serial_port_| connection error.
   void OnConnectionError();
 
+  // Handles |client_binding_| connection error.
+  void OnClientBindingClosed();
+
   // Flag indicating whether or not the connection should persist when
   // its host app is suspended.
   bool persistent_;
@@ -188,7 +201,8 @@
   bool paused_;
 
   // Callback to handle the completion of a pending Receive() request.
-  ReceiveCompleteCallback receive_complete_;
+  ReceiveEventCallback receive_event_cb_;
+  base::Optional<device::mojom::SerialReceiveError> read_error_;
 
   // Callback to handle the completion of a pending Send() request.
   SendCompleteCallback send_complete_;
@@ -203,6 +217,12 @@
 
   // Mojo interface ptr corresponding with remote asynchronous I/O handler.
   device::mojom::SerialPortPtr serial_port_;
+
+  // Pipe for read.
+  mojo::ScopedDataPipeConsumerHandle receive_pipe_;
+  mojo::SimpleWatcher receive_pipe_watcher_;
+  mojo::AssociatedBinding<device::mojom::SerialPortClient> client_binding_;
+
   // Closure which is set by client and will be called when |serial_port_|
   // connection encountered an error.
   base::OnceClosure connection_error_handler_;
diff --git a/extensions/browser/api/serial/serial_port_manager.cc b/extensions/browser/api/serial/serial_port_manager.cc
index 32774d0..44ea9b1 100644
--- a/extensions/browser/api/serial/serial_port_manager.cc
+++ b/extensions/browser/api/serial/serial_port_manager.cc
@@ -84,9 +84,14 @@
                      weak_factory_.GetWeakPtr(), path, std::move(request)));
 }
 
-void SerialPortManager::PollConnection(const std::string& extension_id,
-                                       int connection_id) {
+void SerialPortManager::StartConnectionPolling(const std::string& extension_id,
+                                               int connection_id) {
   DCHECK_CURRENTLY_ON(thread_id_);
+  auto* connection = connections_->Get(extension_id, connection_id);
+  if (!connection)
+    return;
+
+  DCHECK_EQ(extension_id, connection->owner_extension_id());
 
   ReceiveParams params;
   params.thread_id = thread_id_;
@@ -95,29 +100,13 @@
   params.connections = connections_;
   params.connection_id = connection_id;
 
-  StartReceive(params);
+  connection->StartPolling(base::BindRepeating(&DispatchReceiveEvent, params));
 }
 
 // static
-void SerialPortManager::StartReceive(const ReceiveParams& params) {
-  DCHECK_CURRENTLY_ON(params.thread_id);
-
-  SerialConnection* connection =
-      params.connections->Get(params.extension_id, params.connection_id);
-  if (!connection)
-    return;
-  DCHECK(params.extension_id == connection->owner_extension_id());
-
-  if (connection->paused())
-    return;
-
-  connection->Receive(base::BindOnce(&ReceiveCallback, params));
-}
-
-// static
-void SerialPortManager::ReceiveCallback(const ReceiveParams& params,
-                                        std::vector<uint8_t> data,
-                                        serial::ReceiveError error) {
+void SerialPortManager::DispatchReceiveEvent(const ReceiveParams& params,
+                                             std::vector<uint8_t> data,
+                                             serial::ReceiveError error) {
   DCHECK_CURRENTLY_ON(params.thread_id);
 
   // Note that an error (e.g. timeout) does not necessarily mean that no data
@@ -151,10 +140,6 @@
         connection->set_paused(true);
     }
   }
-
-  // Queue up the next read operation.
-  base::PostTaskWithTraits(FROM_HERE, {params.thread_id},
-                           base::BindOnce(&StartReceive, params));
 }
 
 // static
diff --git a/extensions/browser/api/serial/serial_port_manager.h b/extensions/browser/api/serial/serial_port_manager.h
index 502c221f..14606cb 100644
--- a/extensions/browser/api/serial/serial_port_manager.h
+++ b/extensions/browser/api/serial/serial_port_manager.h
@@ -43,8 +43,9 @@
   void GetPort(const std::string& path,
                device::mojom::SerialPortRequest request);
 
-  // Start receiving data and firing events for a connection.
-  void PollConnection(const std::string& extension_id, int connection_id);
+  // Start the poilling process for the connection.
+  void StartConnectionPolling(const std::string& extension_id,
+                              int connection_id);
 
  private:
   typedef ApiResourceManager<SerialConnection>::ApiResourceData ConnectionData;
@@ -66,12 +67,9 @@
     scoped_refptr<ConnectionData> connections;
     int connection_id;
   };
-
-  static void StartReceive(const ReceiveParams& params);
-
-  static void ReceiveCallback(const ReceiveParams& params,
-                              std::vector<uint8_t> data,
-                              serial::ReceiveError error);
+  static void DispatchReceiveEvent(const ReceiveParams& params,
+                                   std::vector<uint8_t> data,
+                                   serial::ReceiveError error);
 
   static void PostEvent(const ReceiveParams& params,
                         std::unique_ptr<extensions::Event> event);
diff --git a/extensions/browser/api/webcam_private/visca_webcam.cc b/extensions/browser/api/webcam_private/visca_webcam.cc
index 307f89fc..3c768c0 100644
--- a/extensions/browser/api/webcam_private/visca_webcam.cc
+++ b/extensions/browser/api/webcam_private/visca_webcam.cc
@@ -267,22 +267,18 @@
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   // TODO(xdai): Check |bytes_sent|?
   if (error == api::serial::SEND_ERROR_NONE) {
-    ReceiveLoop(callback);
+    serial_connection_->StartPolling(
+        base::BindRepeating(&ViscaWebcam::OnReceiveEvent,
+                            weak_ptr_factory_.GetWeakPtr(), callback));
   } else {
     base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
                              base::Bind(callback, false, std::vector<char>()));
   }
 }
 
-void ViscaWebcam::ReceiveLoop(const CommandCompleteCallback& callback) {
-  serial_connection_->Receive(base::Bind(&ViscaWebcam::OnReceiveCompleted,
-                                         weak_ptr_factory_.GetWeakPtr(),
-                                         callback));
-}
-
-void ViscaWebcam::OnReceiveCompleted(const CommandCompleteCallback& callback,
-                                     std::vector<uint8_t> data,
-                                     api::serial::ReceiveError error) {
+void ViscaWebcam::OnReceiveEvent(const CommandCompleteCallback& callback,
+                                 std::vector<uint8_t> data,
+                                 api::serial::ReceiveError error) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
   data_buffer_.insert(data_buffer_.end(), data.begin(), data.end());
 
@@ -292,17 +288,14 @@
     response.swap(data_buffer_);
     base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
                              base::Bind(callback, false, response));
+    serial_connection_->set_paused(true);
     return;
   }
 
   // Success case. If waiting for more data, then loop until encounter the
   // terminator.
-  if (data_buffer_.back() != kViscaTerminator) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&ViscaWebcam::ReceiveLoop,
-                              weak_ptr_factory_.GetWeakPtr(), callback));
+  if (data_buffer_.back() != kViscaTerminator)
     return;
-  }
 
   // Success case, and a complete response has been received.
   // Clear |data_buffer_|.
@@ -313,15 +306,13 @@
       (static_cast<int>(response[1]) & 0xF0) == kViscaResponseError) {
     base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
                              base::Bind(callback, false, response));
+    serial_connection_->set_paused(true);
   } else if ((static_cast<int>(response[1]) & 0xF0) != kViscaResponseAck &&
              (static_cast<int>(response[1]) & 0xFF) !=
                  kViscaResponseNetworkChange) {
     base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
                              base::Bind(callback, true, response));
-  } else {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(&ViscaWebcam::ReceiveLoop,
-                              weak_ptr_factory_.GetWeakPtr(), callback));
+    serial_connection_->set_paused(true);
   }
 }
 
diff --git a/extensions/browser/api/webcam_private/visca_webcam.h b/extensions/browser/api/webcam_private/visca_webcam.h
index 77c7d223..db277d56 100644
--- a/extensions/browser/api/webcam_private/visca_webcam.h
+++ b/extensions/browser/api/webcam_private/visca_webcam.h
@@ -79,10 +79,9 @@
   void OnSendCompleted(const CommandCompleteCallback& callback,
                        uint32_t bytes_sent,
                        api::serial::SendError error);
-  void ReceiveLoop(const CommandCompleteCallback& callback);
-  void OnReceiveCompleted(const CommandCompleteCallback& callback,
-                          std::vector<uint8_t> data,
-                          api::serial::ReceiveError error);
+  void OnReceiveEvent(const CommandCompleteCallback& callback,
+                      std::vector<uint8_t> data,
+                      api::serial::ReceiveError error);
 
   // Callback function that will be called after the send and reply of a command
   // are both completed.
diff --git a/extensions/browser/api/webcam_private/visca_webcam_unittest.cc b/extensions/browser/api/webcam_private/visca_webcam_unittest.cc
index 9ccdfd7..cb2cb1e 100644
--- a/extensions/browser/api/webcam_private/visca_webcam_unittest.cc
+++ b/extensions/browser/api/webcam_private/visca_webcam_unittest.cc
@@ -19,7 +19,7 @@
 
 class TestSerialConnection : public SerialConnection {
  public:
-  TestSerialConnection(device::mojom::SerialPortPtrInfo port_ptr_info)
+  explicit TestSerialConnection(device::mojom::SerialPortPtrInfo port_ptr_info)
       : SerialConnection("dummy_id", std::move(port_ptr_info)) {}
   ~TestSerialConnection() override {}
 
@@ -39,11 +39,10 @@
     NOTREACHED();
   }
 
-  bool Receive(ReceiveCompleteCallback callback) override {
-    std::move(callback).Run(std::move(receive_buffer_),
-                            api::serial::RECEIVE_ERROR_NONE);
+  void StartPolling(const ReceiveEventCallback& callback) override {
+    set_paused(false);
+    callback.Run(std::move(receive_buffer_), api::serial::RECEIVE_ERROR_NONE);
     receive_buffer_.clear();
-    return true;
   }
 
   bool Send(const std::vector<uint8_t>& data,
diff --git a/gpu/command_buffer/service/gl_utils.cc b/gpu/command_buffer/service/gl_utils.cc
index 39428c16..bc1e858 100644
--- a/gpu/command_buffer/service/gl_utils.cc
+++ b/gpu/command_buffer/service/gl_utils.cc
@@ -903,6 +903,13 @@
       break;
   }
 
+  // Sometimes glCopyTexImage2D() fails if source is GL_RGB10_A2 and dest isn't.
+  if (feature_info->workarounds().disable_copy_tex_image_2d_rgb10_a2 &&
+      source_internal_format == GL_RGB10_A2 &&
+      dest_internal_format != GL_RGB10_A2) {
+    return CopyTextureMethod::DRAW_AND_COPY;
+  }
+
   // CopyTexImage* should not allow internalformat of GL_BGRA_EXT and
   // GL_BGRA8_EXT. https://crbug.com/663086.
   bool copy_tex_image_format_valid =
@@ -1018,7 +1025,8 @@
       source_internal_format == GL_BGRA8_EXT ||
       source_internal_format == GL_RGB_YCBCR_420V_CHROMIUM ||
       source_internal_format == GL_RGB_YCBCR_422_CHROMIUM ||
-      source_internal_format == GL_R16_EXT;
+      source_internal_format == GL_R16_EXT ||
+      source_internal_format == GL_RGB10_A2;
   if (!valid_source_format) {
     *output_error_msg = "invalid source internal format " +
                         GLES2Util::GetStringEnum(source_internal_format);
diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
index 939bf436..a58de95 100644
--- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
+++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc
@@ -43,6 +43,7 @@
   S_FORMAT_RGB_YCBCR_420V_CHROMIUM,
   S_FORMAT_RGB_YCBCR_422_CHROMIUM,
   S_FORMAT_COMPRESSED,
+  S_FORMAT_RGB10_A2,
   NUM_S_FORMAT
 };
 
@@ -185,8 +186,12 @@
     case GL_ETC1_RGB8_OES:
       sourceFormatIndex = S_FORMAT_COMPRESSED;
       break;
+    case GL_RGB10_A2:
+      sourceFormatIndex = S_FORMAT_RGB10_A2;
+      break;
     default:
-      NOTREACHED();
+      NOTREACHED() << "Invalid source format "
+                   << gl::GLEnums::GetStringEnum(source_format);
       break;
   }
 
diff --git a/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc b/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc
index 78255a9..5542e25 100644
--- a/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc
+++ b/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc
@@ -14,10 +14,12 @@
 #include <stdint.h>
 
 #include "base/stl_util.h"
+#include "build/build_config.h"
 #include "gpu/command_buffer/tests/gl_manager.h"
 #include "gpu/command_buffer/tests/gl_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_enums.h"
 #include "ui/gl/gl_version_info.h"
 
 namespace gpu {
@@ -153,11 +155,11 @@
   color[3] = a;
 }
 
-void getExpectedColor(GLenum src_internal_format,
-                      GLenum dest_internal_format,
-                      uint8_t* color,
-                      uint8_t* expected_color,
-                      uint8_t* mask) {
+void getExpectedColorAndMask(GLenum src_internal_format,
+                             GLenum dest_internal_format,
+                             const uint8_t* color,
+                             uint8_t* expected_color,
+                             uint8_t* expected_mask) {
   uint8_t adjusted_color[4];
   switch (src_internal_format) {
     case GL_ALPHA:
@@ -187,37 +189,34 @@
     case GL_BGRA8_EXT:
       setColor(color[2], color[1], color[0], color[3], adjusted_color);
       break;
+    case GL_RGB10_A2: {
+      // Map the source 2-bit Alpha to 8-bits.
+      const uint8_t alpha_value = (color[3] & 0x3) * 255 / 3;
+      setColor(color[0] >> 2, color[1] >> 2, color[2] >> 2, alpha_value,
+               adjusted_color);
+      break;
+    }
     default:
-      NOTREACHED();
+      NOTREACHED() << gl::GLEnums::GetStringEnum(src_internal_format);
       break;
   }
 
   switch (dest_internal_format) {
-    case GL_ALPHA:
-      setColor(0, 0, 0, adjusted_color[3], expected_color);
-      setColor(0, 0, 0, 1, mask);
-      break;
+    // TODO(crbug.com/577144): Enable GL_ALPHA, GL_LUMINANCE and
+    // GL_LUMINANCE_ALPHA.
     case GL_R8:
     case GL_R16F:
     case GL_R32F:
     case GL_R8UI:
       setColor(adjusted_color[0], 0, 0, 0, expected_color);
-      setColor(1, 0, 0, 0, mask);
-      break;
-    case GL_LUMINANCE:
-      setColor(adjusted_color[0], 0, 0, 0, expected_color);
-      setColor(1, 0, 0, 0, mask);
-      break;
-    case GL_LUMINANCE_ALPHA:
-      setColor(adjusted_color[0], 0, 0, adjusted_color[3], expected_color);
-      setColor(1, 0, 0, 1, mask);
+      setColor(1, 0, 0, 0, expected_mask);
       break;
     case GL_RG8:
     case GL_RG16F:
     case GL_RG32F:
     case GL_RG8UI:
       setColor(adjusted_color[0], adjusted_color[1], 0, 0, expected_color);
-      setColor(1, 1, 0, 0, mask);
+      setColor(1, 1, 0, 0, expected_mask);
       break;
     case GL_RGB:
     case GL_RGB8:
@@ -231,7 +230,7 @@
     case GL_RGB8UI:
       setColor(adjusted_color[0], adjusted_color[1], adjusted_color[2], 0,
                expected_color);
-      setColor(1, 1, 1, 0, mask);
+      setColor(1, 1, 1, 0, expected_mask);
       break;
     case GL_RGBA:
     case GL_RGBA8:
@@ -245,8 +244,25 @@
     case GL_RGBA8UI:
       setColor(adjusted_color[0], adjusted_color[1], adjusted_color[2],
                adjusted_color[3], expected_color);
-      setColor(1, 1, 1, 1, mask);
+      setColor(1, 1, 1, 1, expected_mask);
       break;
+    case GL_RGB10_A2: {
+      //  Map the 2-bit Alpha values back to full bytes.
+      constexpr uint8_t step = 0x55;
+      const uint8_t alpha_value = (adjusted_color[3] + step / 2) / step * step;
+
+      setColor(adjusted_color[0], adjusted_color[1], adjusted_color[2],
+               alpha_value, expected_color);
+#if defined(OS_MACOSX)
+      // The alpha channel values for LUMINANCE_ALPHA source don't work as
+      // expected on Mac, so skip comparison of those.
+      setColor(1, 1, 1, src_internal_format != GL_LUMINANCE_ALPHA,
+               expected_mask);
+#else
+      setColor(1, 1, 1, 1, expected_mask);
+#endif
+      break;
+    }
     case GL_RGB5_A1:
       setColor(adjusted_color[0], adjusted_color[1], adjusted_color[2],
                (adjusted_color[3] >> 7) ? 0xFF : 0x0, expected_color);
@@ -254,10 +270,10 @@
       // channel of expected color is the source alpha value other than 255.
       // This should be wrong. Skip the alpha channel check and revisit this in
       // future.
-      setColor(1, 1, 1, 0, mask);
+      setColor(1, 1, 1, 0, expected_mask);
       break;
     default:
-      NOTREACHED();
+      NOTREACHED() << gl::GLEnums::GetStringEnum(dest_internal_format);
       break;
   }
 }
@@ -271,38 +287,51 @@
     uint8_t* expected_mask) {
   const uint32_t src_channel_count = gles2::GLES2Util::ElementsPerGroup(
       src_format_type.format, src_format_type.type);
-  uint8_t color[4] = {1u, 63u, 127u, 255u};
-  getExpectedColor(src_format_type.internal_format,
-                   dest_format_type.internal_format, color, expected_color,
-                   expected_mask);
+  constexpr uint8_t color[4] = {1u, 63u, 127u, 255u};
+  getExpectedColorAndMask(src_format_type.internal_format,
+                          dest_format_type.internal_format, color,
+                          expected_color, expected_mask);
+  const size_t num_pixels = width * height;
+  // TODO(mcasas): use std::make_unique<uint8_t[]> in this function.
+
   if (src_format_type.type == GL_UNSIGNED_BYTE) {
     std::unique_ptr<uint8_t[]> pixels(
-        new uint8_t[width * height * src_channel_count]);
-    for (uint32_t i = 0; i < width * height * src_channel_count;
+        new uint8_t[num_pixels * src_channel_count]);
+    for (uint32_t i = 0; i < num_pixels * src_channel_count;
          i += src_channel_count) {
       for (uint32_t j = 0; j < src_channel_count; ++j)
         pixels[i + j] = color[j];
     }
     return pixels;
   } else if (src_format_type.type == GL_UNSIGNED_SHORT) {
-    uint16_t color_16bit[4] = {1u << 8, 63u << 8, 127u << 8, 255u << 8};
+    constexpr uint16_t color_16bit[4] = {color[0] << 8, color[1] << 8,
+                                         color[2] << 8, color[3] << 8};
     std::unique_ptr<uint8_t[]> data(
-        new uint8_t[width * height * src_channel_count * sizeof(uint16_t)]);
+        new uint8_t[num_pixels * src_channel_count * sizeof(uint16_t)]);
     uint16_t* pixels = reinterpret_cast<uint16_t*>(data.get());
     int16_t flip_sign = -1;
-    for (uint32_t i = 0; i < width * height * src_channel_count;
+    for (uint32_t i = 0; i < num_pixels * src_channel_count;
          i += src_channel_count) {
       for (uint32_t j = 0; j < src_channel_count; ++j) {
         // Introduce an offset to the value to check. Expected value should be
         // the same as without the offset.
         flip_sign *= -1;
         pixels[i + j] =
-            color_16bit[j] + flip_sign * (0x7F * (i + j)) / (width * height);
+            color_16bit[j] + flip_sign * (0x7F * (i + j)) / num_pixels;
       }
     }
     return data;
+  } else if (src_format_type.type == GL_UNSIGNED_INT_2_10_10_10_REV) {
+    DCHECK_EQ(src_channel_count, 1u);
+    constexpr uint32_t color_rgb10_a2 = ((color[3] & 0x3) << 30) +
+                                        (color[2] << 20) + (color[1] << 10) +
+                                        color[0];
+    std::unique_ptr<uint8_t[]> data(new uint8_t[num_pixels * sizeof(uint32_t)]);
+    uint32_t* pixels = reinterpret_cast<uint32_t*>(data.get());
+    std::fill(pixels, pixels + num_pixels, color_rgb10_a2);
+    return data;
   }
-  NOTREACHED();
+  NOTREACHED() << gl::GLEnums::GetStringEnum(src_format_type.type);
   return nullptr;
 }
 
@@ -477,7 +506,9 @@
                                textures_[1], dest_level, 0, 0, 0, 0, width_,
                                height_, false, false, false);
     }
-    EXPECT_TRUE(glGetError() == GL_NO_ERROR);
+    const GLenum last_error = glGetError();
+    EXPECT_TRUE(last_error == GL_NO_ERROR)
+        << gl::GLEnums::GetStringError(last_error);
 
     // Draw destination texture to a fbo with a TEXTURE_2D texture attachment
     // in RGBA format.
@@ -571,6 +602,19 @@
 #endif
     return !gl_.decoder()->GetFeatureInfo()->feature_flags().ext_texture_norm16;
   }
+
+  bool ShouldSkipRGB10A2() const {
+    DCHECK(!ShouldSkipTest());
+    const gl::GLVersionInfo& gl_version_info =
+        gl_.decoder()->GetFeatureInfo()->gl_version_info();
+    // XB30 support was introduced in GLES 3.0/ OpenGL 3.3, before that it was
+    // signalled via a specific extension.
+    const bool supports_rgb10_a2 =
+        gl_version_info.IsAtLeastGL(3, 3) ||
+        gl_version_info.IsAtLeastGLES(3, 0) ||
+        GLTestHelper::HasExtension("GL_EXT_texture_type_2_10_10_10_REV");
+    return !supports_rgb10_a2;
+  }
 };
 
 INSTANTIATE_TEST_CASE_P(CopyType,
@@ -627,7 +671,7 @@
         << "Passthrough command decoder expected failure. Skipping test...";
     return;
   }
-  CopyType copy_type = GetParam();
+  const CopyType copy_type = GetParam();
 
   FormatType src_format_types[] = {
       {GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE},
@@ -639,6 +683,7 @@
       {GL_BGRA_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE},
       {GL_BGRA8_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE},
       {GL_R16_EXT, GL_RED, GL_UNSIGNED_SHORT},
+      {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV},
   };
 
   FormatType dest_format_types[] = {
@@ -683,6 +728,7 @@
       {GL_RGBA16F, GL_RGBA, GL_FLOAT},
       {GL_RGBA32F, GL_RGBA, GL_FLOAT},
       {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE},
+      {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV},
   };
 
   for (auto src_format_type : src_format_types) {
@@ -695,14 +741,18 @@
         continue;
       }
       if (gles2::GLES2Util::IsFloatFormat(dest_format_type.internal_format) &&
-          ShouldSkipFloatFormat())
+          ShouldSkipFloatFormat()) {
         continue;
+      }
       if ((dest_format_type.internal_format == GL_SRGB_EXT ||
            dest_format_type.internal_format == GL_SRGB_ALPHA_EXT) &&
-          ShouldSkipSRGBEXT())
+          ShouldSkipSRGBEXT()) {
         continue;
+      }
       if (src_format_type.internal_format == GL_R16_EXT && ShouldSkipNorm16())
         continue;
+      if (src_format_type.internal_format == GL_RGB10_A2 && ShouldSkipRGB10A2())
+        continue;
 
       RunCopyTexture(GL_TEXTURE_2D, copy_type, src_format_type, 0,
                      dest_format_type, 0, true);
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 2ac3cf7..480a5e4 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -3067,6 +3067,23 @@
       "features": [
         "disable_direct_composition"
       ]
+    },
+    {
+      "id": 287,
+      "description": "glCopyTexImage2D on Adreno 4xx fails if source is GL_RGB10_A2 and destination is not.",
+      "cr_bugs": [925986],
+      "os": {
+        "type": "android",
+        "version": {
+          "op": ">=",
+          "value": "5.0.0"
+        }
+      },
+      "gl_vendor": "Qualcomm.*",
+      "gl_renderer": ".*4\\d\\d",
+      "features": [
+        "disable_copy_tex_image_2d_rgb10_a2"
+      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_info_collector_win.cc b/gpu/config/gpu_info_collector_win.cc
index bde5ff34..4b0453b 100644
--- a/gpu/config/gpu_info_collector_win.cc
+++ b/gpu/config/gpu_info_collector_win.cc
@@ -320,18 +320,17 @@
   // 32-bit dll will be used to detect the AMD Vulkan driver.
   const base::FilePath kAmdDriver64(FILE_PATH_LITERAL("amdvlk64.dll"));
   const base::FilePath kAmdDriver32(FILE_PATH_LITERAL("amdvlk32.dll"));
-  auto file_version_info =
-      base::WrapUnique(FileVersionInfoWin::CreateFileVersionInfo(kAmdDriver64));
+  std::unique_ptr<FileVersionInfoWin> file_version_info =
+      FileVersionInfoWin::CreateFileVersionInfoWin(kAmdDriver64);
   if (!file_version_info) {
-    file_version_info.reset(
-        FileVersionInfoWin::CreateFileVersionInfo(kAmdDriver32));
+    file_version_info =
+        FileVersionInfoWin::CreateFileVersionInfoWin(kAmdDriver32);
     if (!file_version_info)
       return false;
   }
 
   const VS_FIXEDFILEINFO* fixed_file_info =
-      static_cast<FileVersionInfoWin*>(file_version_info.get())
-          ->fixed_file_info();
+      file_version_info->fixed_file_info();
   const int major = HIWORD(fixed_file_info->dwFileVersionMS);
   const int minor = LOWORD(fixed_file_info->dwFileVersionMS);
   const int minor_1 = HIWORD(fixed_file_info->dwFileVersionLS);
@@ -348,17 +347,14 @@
 }
 
 bool BadVulkanDllVersion() {
-  std::unique_ptr<FileVersionInfoWin> file_version_info(
-      static_cast<FileVersionInfoWin*>(
-          FileVersionInfoWin::CreateFileVersionInfo(
-              base::FilePath(FILE_PATH_LITERAL("vulkan-1.dll")))));
-
+  std::unique_ptr<FileVersionInfoWin> file_version_info =
+      FileVersionInfoWin::CreateFileVersionInfoWin(
+          base::FilePath(FILE_PATH_LITERAL("vulkan-1.dll")));
   if (!file_version_info)
     return false;
 
   const VS_FIXEDFILEINFO* fixed_file_info =
-      static_cast<FileVersionInfoWin*>(file_version_info.get())
-          ->fixed_file_info();
+      file_version_info->fixed_file_info();
   const int major = HIWORD(fixed_file_info->dwFileVersionMS);
   const int minor = LOWORD(fixed_file_info->dwFileVersionMS);
   const int build_1 = HIWORD(fixed_file_info->dwFileVersionLS);
diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt
index 826c74a..dda9a14 100644
--- a/gpu/config/gpu_workaround_list.txt
+++ b/gpu/config/gpu_workaround_list.txt
@@ -106,3 +106,4 @@
 validate_multisample_buffer_allocation
 wake_up_gpu_before_drawing
 use_copyteximage2d_instead_of_readpixels_on_multisampled_textures
+disable_copy_tex_image_2d_rgb10_a2
diff --git a/gpu/config/nvml_info.cc b/gpu/config/nvml_info.cc
index 2233380..066d6c2 100644
--- a/gpu/config/nvml_info.cc
+++ b/gpu/config/nvml_info.cc
@@ -43,10 +43,8 @@
   }
   dll_path = dll_path.Append(L"NVIDIA Corporation\\NVSMI\\nvml.dll");
 
-  std::unique_ptr<FileVersionInfoWin> file_version_info(
-      static_cast<FileVersionInfoWin*>(
-          FileVersionInfoWin::CreateFileVersionInfo(dll_path)));
-
+  std::unique_ptr<FileVersionInfoWin> file_version_info =
+      FileVersionInfoWin::CreateFileVersionInfoWin(dll_path);
   if (!file_version_info) {
     return false;
   }
diff --git a/gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.cc b/gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.cc
index e8d9614..569e015 100644
--- a/gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.cc
+++ b/gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.cc
@@ -36,11 +36,11 @@
     DestructionCallback callback,
     std::unique_ptr<gfx::ClientNativePixmap> pixmap,
     const std::vector<gfx::NativePixmapPlane>& planes,
-    base::ScopedFD fd)
+    std::vector<base::ScopedFD> fds)
     : GpuMemoryBufferImpl(id, size, format, std::move(callback)),
       pixmap_(std::move(pixmap)),
       planes_(planes),
-      fd_(std::move(fd)) {}
+      fds_(std::move(fds)) {}
 
 GpuMemoryBufferImplNativePixmap::~GpuMemoryBufferImplNativePixmap() = default;
 
@@ -53,42 +53,33 @@
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
     DestructionCallback callback) {
-  // GpuMemoryBufferImpl needs the FD to implement GetHandle() but
-  // gfx::ClientNativePixmapFactory::ImportFromHandle is expected to take
-  // ownership of the FD passed in the handle so we have to dup it here in
-  // order to pass a valid FD to the GpuMemoryBufferImpl ctor.
-  base::ScopedFD scoped_native_pixmap_handle_fd;
-  base::ScopedFD scoped_fd;
-  if (!handle.native_pixmap_handle.fds.empty()) {
-    // Take ownership of FD at index 0.
-    scoped_native_pixmap_handle_fd.reset(handle.native_pixmap_handle.fds[0].fd);
-
-    // Close all remaining FDs.
-    for (size_t i = 1; i < handle.native_pixmap_handle.fds.size(); ++i)
-      base::ScopedFD scoped_fd(handle.native_pixmap_handle.fds[i].fd);
-
+  std::vector<base::ScopedFD> fds;
+  std::vector<base::ScopedFD> dup_fds;
+  for (auto& fd : handle.native_pixmap_handle.fds) {
+    DCHECK(fd.auto_close);
+    // Take ownership of FD
+    fds.emplace_back(fd.fd);
     // Duplicate FD for GpuMemoryBufferImplNativePixmap ctor.
-    scoped_fd.reset(HANDLE_EINTR(dup(scoped_native_pixmap_handle_fd.get())));
-    if (!scoped_fd.is_valid()) {
+    dup_fds.emplace_back(HANDLE_EINTR(dup(fd.fd)));
+    if (!dup_fds.back().is_valid()) {
       PLOG(ERROR) << "dup";
       return nullptr;
     }
   }
 
   gfx::NativePixmapHandle native_pixmap_handle;
-  if (scoped_native_pixmap_handle_fd.is_valid()) {
-    native_pixmap_handle.fds.emplace_back(
-        scoped_native_pixmap_handle_fd.release(), true /* auto_close */);
+  for (auto& fd : dup_fds) {
+    native_pixmap_handle.fds.emplace_back(fd.release(), true /* auto_close */);
   }
   native_pixmap_handle.planes = handle.native_pixmap_handle.planes;
   std::unique_ptr<gfx::ClientNativePixmap> native_pixmap =
-      client_native_pixmap_factory->ImportFromHandle(native_pixmap_handle, size,
-                                                     usage);
+      client_native_pixmap_factory->ImportFromHandle(
+          std::move(native_pixmap_handle), size, usage);
   DCHECK(native_pixmap);
 
   return base::WrapUnique(new GpuMemoryBufferImplNativePixmap(
       handle.id, size, format, std::move(callback), std::move(native_pixmap),
-      handle.native_pixmap_handle.planes, std::move(scoped_fd)));
+      handle.native_pixmap_handle.planes, std::move(fds)));
 }
 
 // static
@@ -145,8 +136,9 @@
   handle.type = gfx::NATIVE_PIXMAP;
   handle.id = id_;
   gfx::NativePixmapHandle native_pixmap_handle;
-  if (fd_.is_valid())
-    native_pixmap_handle.fds.emplace_back(fd_.get(), false /* auto_close */);
+  for (const auto& fd : fds_) {
+    native_pixmap_handle.fds.emplace_back(fd.get(), false /* auto_close */);
+  }
   native_pixmap_handle.planes = planes_;
   handle.native_pixmap_handle = gfx::CloneHandleForIPC(native_pixmap_handle);
   return handle;
diff --git a/gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.h b/gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.h
index ddc63f5e..44a0559 100644
--- a/gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.h
+++ b/gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 
 #include <memory>
+#include <vector>
 
 #include "base/macros.h"
 #include "gpu/gpu_export.h"
@@ -57,11 +58,11 @@
       DestructionCallback callback,
       std::unique_ptr<gfx::ClientNativePixmap> native_pixmap,
       const std::vector<gfx::NativePixmapPlane>& planes,
-      base::ScopedFD fd);
+      std::vector<base::ScopedFD> fds);
 
-  std::unique_ptr<gfx::ClientNativePixmap> pixmap_;
+  const std::unique_ptr<gfx::ClientNativePixmap> pixmap_;
   std::vector<gfx::NativePixmapPlane> planes_;
-  base::ScopedFD fd_;
+  std::vector<base::ScopedFD> fds_;
 
   DISALLOW_COPY_AND_ASSIGN(GpuMemoryBufferImplNativePixmap);
 };
diff --git a/media/audio/android/audio_android_unittest.cc b/media/audio/android/audio_android_unittest.cc
index 99a0852..f5b2046 100644
--- a/media/audio/android/audio_android_unittest.cc
+++ b/media/audio/android/audio_android_unittest.cc
@@ -974,8 +974,8 @@
   StopAndCloseAudioInputStreamOnAudioThread();
 }
 
-INSTANTIATE_TEST_CASE_P(AudioAndroidInputTest,
-                        AudioAndroidInputTest,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(AudioAndroidInputTest,
+                         AudioAndroidInputTest,
+                         testing::Bool());
 
 }  // namespace media
diff --git a/media/audio/audio_debug_file_writer_unittest.cc b/media/audio/audio_debug_file_writer_unittest.cc
index 5d2474a..c13be18 100644
--- a/media/audio/audio_debug_file_writer_unittest.cc
+++ b/media/audio/audio_debug_file_writer_unittest.cc
@@ -295,7 +295,7 @@
   debug_writer_.reset();
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     AudioDebugFileWriterTest,
     AudioDebugFileWriterTest,
     // Using 10ms frames per buffer everywhere.
@@ -331,23 +331,23 @@
                         48000 / 100,
                         1500)));
 
-INSTANTIATE_TEST_CASE_P(AudioDebugFileWriterBehavioralTest,
-                        AudioDebugFileWriterBehavioralTest,
-                        // Using 10ms frames per buffer everywhere.
-                        testing::Values(
-                            // No writes.
-                            std::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO,
-                                            44100,
-                                            44100 / 100,
-                                            100)));
+INSTANTIATE_TEST_SUITE_P(AudioDebugFileWriterBehavioralTest,
+                         AudioDebugFileWriterBehavioralTest,
+                         // Using 10ms frames per buffer everywhere.
+                         testing::Values(
+                             // No writes.
+                             std::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO,
+                                             44100,
+                                             44100 / 100,
+                                             100)));
 
-INSTANTIATE_TEST_CASE_P(AudioDebugFileWriterSingleThreadTest,
-                        AudioDebugFileWriterSingleThreadTest,
-                        // Using 10ms frames per buffer everywhere.
-                        testing::Values(
-                            // No writes.
-                            std::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO,
-                                            44100,
-                                            44100 / 100,
-                                            100)));
+INSTANTIATE_TEST_SUITE_P(AudioDebugFileWriterSingleThreadTest,
+                         AudioDebugFileWriterSingleThreadTest,
+                         // Using 10ms frames per buffer everywhere.
+                         testing::Values(
+                             // No writes.
+                             std::make_tuple(ChannelLayout::CHANNEL_LAYOUT_MONO,
+                                             44100,
+                                             44100 / 100,
+                                             100)));
 }  // namespace media
diff --git a/media/audio/audio_input_controller_unittest.cc b/media/audio/audio_input_controller_unittest.cc
index 3b3802c..1499f18 100644
--- a/media/audio/audio_input_controller_unittest.cc
+++ b/media/audio/audio_input_controller_unittest.cc
@@ -299,6 +299,6 @@
   CloseAudioController();
 }
 
-INSTANTIATE_TEST_CASE_P(SyncAsync, AudioInputControllerTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(SyncAsync, AudioInputControllerTest, testing::Bool());
 
 }  // namespace media
diff --git a/media/audio/audio_output_controller_unittest.cc b/media/audio/audio_output_controller_unittest.cc
index 5847d48..3cbbbfe3 100644
--- a/media/audio/audio_output_controller_unittest.cc
+++ b/media/audio/audio_output_controller_unittest.cc
@@ -520,6 +520,6 @@
   Close();
 }
 
-INSTANTIATE_TEST_CASE_P(AOC, AudioOutputControllerTest, Bool());
+INSTANTIATE_TEST_SUITE_P(AOC, AudioOutputControllerTest, Bool());
 
 }  // namespace media
diff --git a/media/audio/audio_power_monitor_unittest.cc b/media/audio/audio_power_monitor_unittest.cc
index 099cfa9..8d8eed0 100644
--- a/media/audio/audio_power_monitor_unittest.cc
+++ b/media/audio/audio_power_monitor_unittest.cc
@@ -273,33 +273,58 @@
   0.25f, -0.25f, 0.5f, -0.5f, 0.75f, -0.75f, 1.0f, -1.0f
 };
 
-INSTANTIATE_TEST_CASE_P(
-    Scenarios, AudioPowerMonitorTest,
+INSTANTIATE_TEST_SUITE_P(
+    Scenarios,
+    AudioPowerMonitorTest,
     ::testing::Values(
-         TestScenario(kMonoSilentNoise, 1, 2, -40, false),
-         TestScenario(kMonoMaxAmplitude, 1, 1,
-                      AudioPowerMonitor::max_power(), false),
-         TestScenario(kMonoMaxAmplitude2, 1, 2,
-                      AudioPowerMonitor::max_power(), false),
-         TestScenario(kMonoHalfMaxAmplitude, 1, 4, -6, false),
-         TestScenario(kMonoAmplitudeClipped, 1, 2,
-                      AudioPowerMonitor::max_power(), true),
-         TestScenario(kMonoMaxAmplitudeWithClip, 1, 4,
-                      AudioPowerMonitor::max_power(), true),
-         TestScenario(kMonoMaxAmplitudeWithClip2, 1, 4,
-                      AudioPowerMonitor::max_power(), true),
-         TestScenario(kMonoSilentNoise, 1, 2,
-                      AudioPowerMonitor::zero_power(), false).
-             WithABadSample(std::numeric_limits<float>::infinity()),
-         TestScenario(kMonoHalfMaxAmplitude, 1, 4,
-                      AudioPowerMonitor::zero_power(), false).
-             WithABadSample(std::numeric_limits<float>::quiet_NaN()),
-         TestScenario(kStereoSilentNoise, 2, 2, -46, false),
-         TestScenario(kStereoMaxAmplitude, 2, 2,
-                      AudioPowerMonitor::max_power(), false),
-         TestScenario(kRightChannelMaxAmplitude, 2, 4, -3, false),
-         TestScenario(kLeftChannelHalfMaxAmplitude, 2, 4, -9, false),
-         TestScenario(kStereoMixed, 2, 4, -2, false),
-         TestScenario(kStereoMixed2, 2, 8, -3, false)));
+        TestScenario(kMonoSilentNoise, 1, 2, -40, false),
+        TestScenario(kMonoMaxAmplitude,
+                     1,
+                     1,
+                     AudioPowerMonitor::max_power(),
+                     false),
+        TestScenario(kMonoMaxAmplitude2,
+                     1,
+                     2,
+                     AudioPowerMonitor::max_power(),
+                     false),
+        TestScenario(kMonoHalfMaxAmplitude, 1, 4, -6, false),
+        TestScenario(kMonoAmplitudeClipped,
+                     1,
+                     2,
+                     AudioPowerMonitor::max_power(),
+                     true),
+        TestScenario(kMonoMaxAmplitudeWithClip,
+                     1,
+                     4,
+                     AudioPowerMonitor::max_power(),
+                     true),
+        TestScenario(kMonoMaxAmplitudeWithClip2,
+                     1,
+                     4,
+                     AudioPowerMonitor::max_power(),
+                     true),
+        TestScenario(kMonoSilentNoise,
+                     1,
+                     2,
+                     AudioPowerMonitor::zero_power(),
+                     false)
+            .WithABadSample(std::numeric_limits<float>::infinity()),
+        TestScenario(kMonoHalfMaxAmplitude,
+                     1,
+                     4,
+                     AudioPowerMonitor::zero_power(),
+                     false)
+            .WithABadSample(std::numeric_limits<float>::quiet_NaN()),
+        TestScenario(kStereoSilentNoise, 2, 2, -46, false),
+        TestScenario(kStereoMaxAmplitude,
+                     2,
+                     2,
+                     AudioPowerMonitor::max_power(),
+                     false),
+        TestScenario(kRightChannelMaxAmplitude, 2, 4, -3, false),
+        TestScenario(kLeftChannelHalfMaxAmplitude, 2, 4, -9, false),
+        TestScenario(kStereoMixed, 2, 4, -2, false),
+        TestScenario(kStereoMixed2, 2, 8, -3, false)));
 
 }  // namespace media
diff --git a/media/audio/audio_sync_reader_unittest.cc b/media/audio/audio_sync_reader_unittest.cc
index 7d829a4..f92eaba5 100644
--- a/media/audio/audio_sync_reader_unittest.cc
+++ b/media/audio/audio_sync_reader_unittest.cc
@@ -103,8 +103,8 @@
   reader->Read(output_bus.get());
 }
 
-INSTANTIATE_TEST_CASE_P(AudioSyncReaderTest,
-                        AudioSyncReaderBitstreamTest,
-                        ::testing::ValuesIn(overflow_test_case_values));
+INSTANTIATE_TEST_SUITE_P(AudioSyncReaderTest,
+                         AudioSyncReaderBitstreamTest,
+                         ::testing::ValuesIn(overflow_test_case_values));
 
 }  // namespace media
diff --git a/media/audio/audio_system_impl_unittest.cc b/media/audio/audio_system_impl_unittest.cc
index f1ab06b..2138f901 100644
--- a/media/audio/audio_system_impl_unittest.cc
+++ b/media/audio/audio_system_impl_unittest.cc
@@ -45,8 +45,8 @@
     testing::Types<AudioSystemImplTestBase<false>,
                    AudioSystemImplTestBase<true>>;
 
-INSTANTIATE_TYPED_TEST_CASE_P(AudioSystemImpl,
-                              AudioSystemTestTemplate,
-                              AudioSystemTestBaseVariations);
+INSTANTIATE_TYPED_TEST_SUITE_P(AudioSystemImpl,
+                               AudioSystemTestTemplate,
+                               AudioSystemTestBaseVariations);
 
 }  // namespace media
diff --git a/media/audio/audio_system_test_util.h b/media/audio/audio_system_test_util.h
index 2220153..02e3942 100644
--- a/media/audio/audio_system_test_util.h
+++ b/media/audio/audio_system_test_util.h
@@ -141,7 +141,7 @@
   DISALLOW_COPY_AND_ASSIGN(AudioSystemTestTemplate);
 };
 
-TYPED_TEST_CASE_P(AudioSystemTestTemplate);
+TYPED_TEST_SUITE_P(AudioSystemTestTemplate);
 
 TYPED_TEST_P(AudioSystemTestTemplate, GetInputStreamParametersNormal) {
   base::RunLoop wait_loop;
@@ -340,7 +340,7 @@
   wait_loop.Run();
 }
 
-REGISTER_TYPED_TEST_CASE_P(
+REGISTER_TYPED_TEST_SUITE_P(
     AudioSystemTestTemplate,
     GetInputStreamParametersNormal,
     GetInputStreamParametersNoDevice,
diff --git a/media/audio/virtual_audio_input_stream_unittest.cc b/media/audio/virtual_audio_input_stream_unittest.cc
index fc9a7d0..08753634 100644
--- a/media/audio/virtual_audio_input_stream_unittest.cc
+++ b/media/audio/virtual_audio_input_stream_unittest.cc
@@ -353,8 +353,8 @@
   WaitUntilClosed();
 }
 
-INSTANTIATE_TEST_CASE_P(SingleVersusMultithreaded,
-                        VirtualAudioInputStreamTest,
-                        ::testing::Values(false, true));
+INSTANTIATE_TEST_SUITE_P(SingleVersusMultithreaded,
+                         VirtualAudioInputStreamTest,
+                         ::testing::Values(false, true));
 
 }  // namespace media
diff --git a/media/capture/video/chromeos/local_gpu_memory_buffer_manager.cc b/media/capture/video/chromeos/local_gpu_memory_buffer_manager.cc
index 49e9d03..6502cab 100644
--- a/media/capture/video/chromeos/local_gpu_memory_buffer_manager.cc
+++ b/media/capture/video/chromeos/local_gpu_memory_buffer_manager.cc
@@ -71,9 +71,9 @@
     handle_.type = gfx::NATIVE_PIXMAP;
     // Set a dummy id since this is for testing only.
     handle_.id = gfx::GpuMemoryBufferId(0);
-    handle_.native_pixmap_handle.fds.push_back(
-        base::FileDescriptor(gbm_bo_get_fd(buffer_object), false));
     for (size_t i = 0; i < gbm_bo_get_num_planes(buffer_object); ++i) {
+      handle_.native_pixmap_handle.fds.push_back(
+          base::FileDescriptor(gbm_bo_get_plane_fd(buffer_object, i), true));
       handle_.native_pixmap_handle.planes.push_back(
           gfx::NativePixmapPlane(gbm_bo_get_plane_stride(buffer_object, i),
                                  gbm_bo_get_plane_offset(buffer_object, i),
@@ -85,7 +85,12 @@
     if (mapped_) {
       Unmap();
     }
-    close(gbm_bo_get_fd(buffer_object_));
+
+    for (const auto& fd : handle_.native_pixmap_handle.fds) {
+      // Close fds.
+      DCHECK(fd.auto_close);
+      close(fd.fd);
+    }
     gbm_bo_destroy(buffer_object_);
   }
 
diff --git a/media/capture/video/chromeos/stream_buffer_manager.cc b/media/capture/video/chromeos/stream_buffer_manager.cc
index bd29f50..29d0809 100644
--- a/media/capture/video/chromeos/stream_buffer_manager.cc
+++ b/media/capture/video/chromeos/stream_buffer_manager.cc
@@ -356,25 +356,18 @@
 
   gfx::NativePixmapHandle buffer_handle =
       buffer->CloneHandle().native_pixmap_handle;
-  // Take ownership of FD at index 0.
-  base::ScopedFD fd(buffer_handle.fds[0].fd);
-  // There should be only one FD. Close all remaining FDs if there are any.
-  DCHECK_EQ(buffer_handle.fds.size(), 1U);
-  for (size_t i = 1; i < buffer_handle.fds.size(); ++i)
-    base::ScopedFD scoped_fd(buffer_handle.fds[i].fd);
 
   size_t num_planes = buffer_handle.planes.size();
+  DCHECK_EQ(num_planes, buffer_handle.fds.size());
+  // Take ownership of fds.
+  std::vector<base::ScopedFD> fds(num_planes);
+  for (size_t i = 0; i < num_planes; ++i)
+    fds[i] = base::ScopedFD(buffer_handle.fds[i].fd);
+
   std::vector<StreamCaptureInterface::Plane> planes(num_planes);
   for (size_t i = 0; i < num_planes; ++i) {
-    int dup_fd = dup(fd.get());
-    if (dup_fd == -1) {
-      device_context_->SetErrorState(
-          media::VideoCaptureError::kCrosHalV3BufferManagerFailedToDupFd,
-          FROM_HERE, "Failed to dup fd");
-      return;
-    }
     planes[i].fd =
-        mojo::WrapPlatformHandle(mojo::PlatformHandle(base::ScopedFD(dup_fd)));
+        mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(fds[i])));
     if (!planes[i].fd.is_valid()) {
       device_context_->SetErrorState(
           media::VideoCaptureError::
diff --git a/media/capture/video/mock_gpu_memory_buffer_manager.cc b/media/capture/video/mock_gpu_memory_buffer_manager.cc
index e4bb4f6..9f86633 100644
--- a/media/capture/video/mock_gpu_memory_buffer_manager.cc
+++ b/media/capture/video/mock_gpu_memory_buffer_manager.cc
@@ -37,12 +37,15 @@
 
 #if defined(OS_CHROMEOS)
     // Set a dummy fd since this is for testing only.
-    handle_.native_pixmap_handle.fds.push_back(base::FileDescriptor(0, false));
+    handle_.native_pixmap_handle.fds.push_back(base::FileDescriptor(0, true));
     handle_.native_pixmap_handle.planes.push_back(
         gfx::NativePixmapPlane(size_.width(), 0, y_plane_size));
-    handle_.native_pixmap_handle.planes.push_back(gfx::NativePixmapPlane(
-        size_.width(), handle_.native_pixmap_handle.planes[0].size,
-        uv_plane_size));
+    if (format == gfx::BufferFormat::YUV_420_BIPLANAR) {
+      handle_.native_pixmap_handle.fds.push_back(base::FileDescriptor(0, true));
+      handle_.native_pixmap_handle.planes.push_back(gfx::NativePixmapPlane(
+          size_.width(), handle_.native_pixmap_handle.planes[0].size,
+          uv_plane_size));
+    }
 
     // For faking a valid JPEG blob buffer.
     if (base::checked_cast<size_t>(size_.width()) >= sizeof(Camera3JpegBlob)) {
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 8712768..0149d86 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -660,6 +660,7 @@
     ":buildflags",
     ":gpu",
     "test:image_processor",
+    "test:render_helpers",
     "//base/test:test_support",
     "//media:test_support",
     "//mojo/core/embedder",
diff --git a/media/gpu/image_processor_test.cc b/media/gpu/image_processor_test.cc
index bc17118..c7a2048 100644
--- a/media/gpu/image_processor_test.cc
+++ b/media/gpu/image_processor_test.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
 #include <string>
 #include <tuple>
 
@@ -11,10 +12,12 @@
 #include "base/test/test_suite.h"
 #include "build/build_config.h"
 #include "media/base/video_frame.h"
+#include "media/base/video_frame_layout.h"
 #include "media/base/video_types.h"
 #include "media/gpu/image_processor.h"
+#include "media/gpu/test/image.h"
 #include "media/gpu/test/image_processor/image_processor_client.h"
-#include "media/gpu/test/video_image_info.h"
+#include "media/gpu/test/video_frame_helpers.h"
 #include "mojo/core/embedder/embedder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/size.h"
@@ -22,44 +25,35 @@
 namespace media {
 namespace {
 
-// I420 formatted 320x192 video frame. (bear)
-// TODO(crbug.com/917951): Dynamically load this info from json file.
-constexpr test::VideoImageInfo kI420Image(
-    FILE_PATH_LITERAL("bear_320x192.i420.yuv"),
-    "962820755c74b28f9385fd67219cc04a",
-    PIXEL_FORMAT_I420,
-    gfx::Size(320, 192));
-
-// NV12 formatted 320x192 video frame. (bear)
-// TODO(crbug.com/917951): Dynamically load this info from json file.
-constexpr test::VideoImageInfo kNV12Image(
-    FILE_PATH_LITERAL("bear_320x192.i420.nv12.yuv"),
-    "ce21986434743d3671056719136d46ff",
-    PIXEL_FORMAT_NV12,
-    gfx::Size(320, 192));
+constexpr const base::FilePath::CharType* kI420Image =
+    FILE_PATH_LITERAL("bear_320x192.i420.yuv");
+constexpr const base::FilePath::CharType* kNV12Image =
+    FILE_PATH_LITERAL("bear_320x192.nv12.yuv");
 
 class ImageProcessorSimpleParamTest
     : public ::testing::Test,
       public ::testing::WithParamInterface<
-          std::tuple<test::VideoImageInfo, test::VideoImageInfo>> {
+          std::tuple<base::FilePath, base::FilePath>> {
  public:
   // TODO(crbug.com/917951): Initialize Ozone once.
   void SetUp() override {}
   void TearDown() override {}
 
   std::unique_ptr<test::ImageProcessorClient> CreateImageProcessorClient(
-      const test::VideoImageInfo& input_image_info,
-      const test::VideoImageInfo& output_image_info) {
+      const test::Image& input_image,
+      const test::Image& output_image) {
     // TODO(crbug.com/917951): Pass VideoFrameProcessor.
-    auto input_config_layout = input_image_info.VideoFrameLayout();
-    auto output_config_layout = output_image_info.VideoFrameLayout();
+    auto input_config_layout = test::CreateVideoFrameLayout(
+        input_image.PixelFormat(), input_image.Size());
+    auto output_config_layout = test::CreateVideoFrameLayout(
+        output_image.PixelFormat(), output_image.Size());
     LOG_ASSERT(input_config_layout);
     LOG_ASSERT(output_config_layout);
     ImageProcessor::PortConfig input_config(*input_config_layout,
-                                            input_image_info.visible_size,
+                                            input_image.Size(),
                                             {VideoFrame::STORAGE_OWNED_MEMORY});
     ImageProcessor::PortConfig output_config(
-        *output_config_layout, output_image_info.visible_size,
+        *output_config_layout, output_image.Size(),
         {VideoFrame::STORAGE_OWNED_MEMORY});
     // TODO(crbug.com/917951): Select more appropriate number of buffers.
     constexpr size_t kNumBuffers = 1;
@@ -71,12 +65,15 @@
 };
 
 TEST_P(ImageProcessorSimpleParamTest, ConvertOneTimeFromMemToMem) {
-  test::VideoImageInfo input_image_info = std::get<0>(GetParam());
-  test::VideoImageInfo output_image_info = std::get<1>(GetParam());
-  auto ip_client =
-      CreateImageProcessorClient(input_image_info, output_image_info);
+  // Load the test input image. We only need the output image's metadata so we
+  // can compare checksums.
+  test::Image input_image(std::get<0>(GetParam()));
+  test::Image output_image(std::get<1>(GetParam()));
+  ASSERT_TRUE(input_image.Load());
+  ASSERT_TRUE(output_image.LoadMetadata());
 
-  ip_client->Process(input_image_info, output_image_info);
+  auto ip_client = CreateImageProcessorClient(input_image, output_image);
+  ip_client->Process(input_image, output_image);
   EXPECT_TRUE(ip_client->WaitUntilNumImageProcessed(1u));
   EXPECT_EQ(ip_client->GetErrorCount(), 0u);
   EXPECT_EQ(ip_client->GetNumOfProcessedImages(), 1u);
@@ -93,7 +90,7 @@
   VideoFrame::HashFrameForTesting(&context, processed_frame);
   base::MD5Digest digest;
   base::MD5Final(&digest, &context);
-  std::string expected_md5 = output_image_info.md5sum;
+  std::string expected_md5 = output_image.Checksum();
   std::string computed_md5 = MD5DigestToBase16(digest);
   EXPECT_EQ(expected_md5, computed_md5);
 };
diff --git a/media/gpu/test/BUILD.gn b/media/gpu/test/BUILD.gn
index 12334e7..33e26c71 100644
--- a/media/gpu/test/BUILD.gn
+++ b/media/gpu/test/BUILD.gn
@@ -161,11 +161,13 @@
 static_library("image_processor") {
   testonly = true
   sources = [
+    "image.cc",
+    "image.h",
     "image_processor/image_processor_client.cc",
     "image_processor/image_processor_client.h",
-    "video_image_info.h",
   ]
   deps = [
+    ":render_helpers",
     "//media:test_support",
     "//media/gpu",
     "//testing/gtest",
diff --git a/media/gpu/test/image.cc b/media/gpu/test/image.cc
new file mode 100644
index 0000000..41f60429
--- /dev/null
+++ b/media/gpu/test/image.cc
@@ -0,0 +1,178 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/gpu/test/image.h"
+
+#include <memory>
+
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/md5.h"
+#include "base/values.h"
+#include "media/base/test_data_util.h"
+
+#define VLOGF(level) VLOG(level) << __func__ << "(): "
+
+namespace media {
+namespace test {
+
+namespace {
+
+// Resolve the specified test file path to an absolute path. The path can be
+// either an absolute path, a path relative to the current directory, or a path
+// relative to the test data path.
+void ResolveTestFilePath(base::FilePath* file_path) {
+  if (!file_path->IsAbsolute()) {
+    if (!PathExists(*file_path))
+      *file_path = media::GetTestDataPath().Append(*file_path);
+    *file_path = base::MakeAbsoluteFilePath(*file_path);
+  }
+}
+
+// Converts the |pixel_format| string into a VideoPixelFormat.
+VideoPixelFormat ConvertStringtoPixelFormat(const std::string& pixel_format) {
+  if (pixel_format == "I420") {
+    return PIXEL_FORMAT_I420;
+  } else if (pixel_format == "NV12") {
+    return PIXEL_FORMAT_NV12;
+  } else {
+    VLOG(2) << pixel_format << " is not supported.";
+    return PIXEL_FORMAT_UNKNOWN;
+  }
+}
+
+}  // namespace
+
+// Suffix to append to the image file path to get the metadata file path.
+constexpr const base::FilePath::CharType* kMetadataSuffix =
+    FILE_PATH_LITERAL(".json");
+
+Image::Image(const base::FilePath& file_path) : file_path_(file_path) {}
+
+Image::~Image() {}
+
+bool Image::Load() {
+  DCHECK(!file_path_.empty());
+  DCHECK(!IsLoaded());
+
+  ResolveTestFilePath(&file_path_);
+
+  if (!mapped_file_.Initialize(file_path_)) {
+    LOG(ERROR) << "Failed to read file: " << file_path_;
+    return false;
+  }
+
+  if (!LoadMetadata()) {
+    LOG(ERROR) << "Failed to load metadata";
+    return false;
+  }
+
+  // Verify that the image's checksum matches the checksum in the metadata.
+  base::MD5Digest digest;
+  base::MD5Sum(mapped_file_.data(), mapped_file_.length(), &digest);
+  if (base::MD5DigestToBase16(digest) != checksum_) {
+    LOG(ERROR) << "Image checksum not matching metadata";
+    return false;
+  }
+
+  return true;
+}
+
+bool Image::IsLoaded() const {
+  return mapped_file_.IsValid();
+}
+
+bool Image::LoadMetadata() {
+  if (IsMetadataLoaded()) {
+    return true;
+  }
+
+  base::FilePath json_path = file_path_.AddExtension(kMetadataSuffix);
+  ResolveTestFilePath(&json_path);
+
+  if (!base::PathExists(json_path)) {
+    VLOGF(1) << "Image metadata file not found: " << json_path.BaseName();
+    return false;
+  }
+
+  std::string json_data;
+  if (!base::ReadFileToString(json_path, &json_data)) {
+    VLOGF(1) << "Failed to read image metadata file: " << json_path;
+    return false;
+  }
+
+  base::JSONReader reader;
+  std::unique_ptr<base::Value> metadata(reader.ReadToValue(json_data));
+  if (!metadata) {
+    VLOGF(1) << "Failed to parse image metadata: " << json_path << ": "
+             << reader.GetErrorMessage();
+    return false;
+  }
+
+  // Get the pixel format from the json data.
+  const base::Value* pixel_format =
+      metadata->FindKeyOfType("pixel_format", base::Value::Type::STRING);
+  if (!pixel_format) {
+    VLOGF(1) << "Key \"pixel_format\" is not found in " << json_path;
+    return false;
+  }
+  pixel_format_ = ConvertStringtoPixelFormat(pixel_format->GetString());
+  if (pixel_format_ == PIXEL_FORMAT_UNKNOWN) {
+    VLOGF(1) << pixel_format->GetString() << " is not supported";
+    return false;
+  }
+
+  // Get the image dimensions from the json data.
+  const base::Value* width =
+      metadata->FindKeyOfType("width", base::Value::Type::INTEGER);
+  if (!width) {
+    VLOGF(1) << "Key \"width\" is not found in " << json_path;
+    return false;
+  }
+  const base::Value* height =
+      metadata->FindKeyOfType("height", base::Value::Type::INTEGER);
+  if (!height) {
+    VLOGF(1) << "Key \"height\" is not found in " << json_path;
+    return false;
+  }
+  size_ = gfx::Size(width->GetInt(), height->GetInt());
+
+  // Get the image checksum from the json data.
+  const base::Value* checksum =
+      metadata->FindKeyOfType("checksum", base::Value::Type::STRING);
+  if (!checksum) {
+    VLOGF(1) << "Key \"checksum\" is not found in " << json_path;
+    return false;
+  }
+  checksum_ = checksum->GetString();
+
+  return true;
+}
+
+bool Image::IsMetadataLoaded() const {
+  return pixel_format_ != PIXEL_FORMAT_UNKNOWN;
+}
+
+uint8_t* Image::Data() const {
+  return mapped_file_.data();
+}
+
+size_t Image::DataSize() const {
+  return mapped_file_.length();
+}
+
+VideoPixelFormat Image::PixelFormat() const {
+  return pixel_format_;
+}
+
+const gfx::Size& Image::Size() const {
+  return size_;
+}
+
+const char* Image::Checksum() const {
+  return checksum_.data();
+}
+
+}  // namespace test
+}  // namespace media
diff --git a/media/gpu/test/image.h b/media/gpu/test/image.h
new file mode 100644
index 0000000..423592c
--- /dev/null
+++ b/media/gpu/test/image.h
@@ -0,0 +1,69 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_GPU_TEST_IMAGE_H_
+#define MEDIA_GPU_TEST_IMAGE_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
+#include "media/base/video_types.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+namespace test {
+
+// The Image class provides functionality to load image files and manage their
+// properties such as format, size, checksums,... Currently only raw yuv files
+// are supported.
+class Image {
+ public:
+  explicit Image(const base::FilePath& file_path);
+  ~Image();
+
+  // Load the image file and accompanying metadata from disk.
+  bool Load();
+  // Returns true if the Image file was loaded.
+  bool IsLoaded() const;
+
+  // Load image metadata from the json file accompanying the image file.
+  bool LoadMetadata();
+  // Return true if image metadata is already loaded.
+  bool IsMetadataLoaded() const;
+
+  // Get the image data.
+  uint8_t* Data() const;
+  // Get the image data size.
+  size_t DataSize() const;
+
+  // Get the image pixel format.
+  VideoPixelFormat PixelFormat() const;
+  // Get the image size.
+  const gfx::Size& Size() const;
+  // Get the image checksum.
+  const char* Checksum() const;
+
+ private:
+  // The image file path, can be absolute or relative to the test data path.
+  base::FilePath file_path_;
+
+  // The mapped image data.
+  // TODO(dstaessens@) Investigate creating const video frames from const data
+  // so we can remove mutable here.
+  mutable base::MemoryMappedFile mapped_file_;
+  // The image pixel format.
+  VideoPixelFormat pixel_format_ = PIXEL_FORMAT_UNKNOWN;
+  // The image size.
+  gfx::Size size_;
+  // The image md5 checksum.
+  std::string checksum_;
+
+  DISALLOW_COPY_AND_ASSIGN(Image);
+};
+
+}  // namespace test
+}  // namespace media
+
+#endif  // MEDIA_GPU_TEST_IMAGE_H_
diff --git a/media/gpu/test/image_processor/image_processor_client.cc b/media/gpu/test/image_processor/image_processor_client.cc
index 8bb73bb5..3ddeb54 100644
--- a/media/gpu/test/image_processor/image_processor_client.cc
+++ b/media/gpu/test/image_processor/image_processor_client.cc
@@ -10,17 +10,15 @@
 #include "third_party/libyuv/include/libyuv/planar_functions.h"
 
 #include "base/bind_helpers.h"
-#include "base/files/file_util.h"
-#include "base/files/memory_mapped_file.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/synchronization/waitable_event.h"
 #include "build/build_config.h"
 #include "media/base/bind_to_current_loop.h"
-#include "media/base/test_data_util.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_frame_layout.h"
 #include "media/gpu/image_processor_factory.h"
+#include "media/gpu/test/image.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -30,22 +28,6 @@
 namespace {
 // TODO(crbug.com/917951): Move these functions to video_frame_helpers.h
 
-// Find the file path for |file_name|.
-base::FilePath GetFilePath(const base::FilePath::CharType* const file_name) {
-  // 1. Try to find |file_name| in the current directory.
-  base::FilePath file_path =
-      base::FilePath(base::FilePath::kCurrentDirectory).Append(file_name);
-  if (base::PathExists(file_path)) {
-    return file_path;
-  }
-
-  // 2. Try media::GetTestDataFilePath(|file_name|), that is,
-  // media/test/data/file_name. This is mainly for Try bot.
-  file_path = media::GetTestDataPath().Append(file_name);
-  LOG_ASSERT(base::PathExists(file_path)) << " Cannot find " << file_name;
-  return file_path;
-}
-
 // Copy |src_frame| into a new VideoFrame with |dst_layout|. The created
 // VideoFrame's content is the same as |src_frame|. Returns nullptr on failure.
 scoped_refptr<VideoFrame> CloneVideoFrameWithLayout(
@@ -79,61 +61,6 @@
   return dst_frame;
 }
 
-// Create VideoFrame from |info| loading |info.file_name|. Return nullptr on
-// failure.
-scoped_refptr<VideoFrame> ReadVideoFrame(const VideoImageInfo& info) {
-  auto path = GetFilePath(info.file_name);
-  // First read file.
-  auto mapped_file = std::make_unique<base::MemoryMappedFile>();
-  if (!mapped_file->Initialize(path)) {
-    LOG(ERROR) << "Failed to read file: " << path;
-    return nullptr;
-  }
-
-  const auto format = info.pixel_format;
-  const auto visible_size = info.visible_size;
-  // Check the file length and md5sum.
-  LOG_ASSERT(mapped_file->length() ==
-             VideoFrame::AllocationSize(format, visible_size));
-  base::MD5Digest digest;
-  base::MD5Sum(mapped_file->data(), mapped_file->length(), &digest);
-  LOG_ASSERT(base::MD5DigestToBase16(digest) == info.md5sum);
-
-  // Create planes for layout. We cannot use WrapExternalData() because it calls
-  // GetDefaultLayout() and it supports only a few pixel formats.
-  const size_t num_planes = VideoFrame::NumPlanes(format);
-  std::vector<VideoFrameLayout::Plane> planes(num_planes);
-  const auto strides = VideoFrame::ComputeStrides(format, visible_size);
-  size_t offset = 0;
-  for (size_t i = 0; i < num_planes; ++i) {
-    planes[i].stride = strides[i];
-    planes[i].offset = offset;
-    offset += VideoFrame::PlaneSize(format, i, visible_size).GetArea();
-  }
-
-  auto layout = VideoFrameLayout::CreateWithPlanes(
-      format, visible_size, std::move(planes), {mapped_file->length()});
-  if (!layout) {
-    LOG(ERROR) << "Failed to create VideoFrameLayout";
-    return nullptr;
-  }
-
-  auto frame = VideoFrame::WrapExternalDataWithLayout(
-      *layout, gfx::Rect(visible_size), visible_size, mapped_file->data(),
-      mapped_file->length(), base::TimeDelta());
-  if (!frame) {
-    LOG(ERROR) << "Failed to create VideoFrame";
-    return nullptr;
-  }
-
-  // Automatically unmap the memory mapped file when the video frame is
-  // destroyed.
-  frame->AddDestructionObserver(base::BindOnce(
-      base::DoNothing::Once<std::unique_ptr<base::MemoryMappedFile>>(),
-      std::move(mapped_file)));
-  return frame;
-}
-
 }  // namespace
 
 // static
@@ -211,15 +138,48 @@
 }
 
 scoped_refptr<VideoFrame> ImageProcessorClient::CreateInputFrame(
-    const VideoImageInfo& input_image_info) const {
+    const Image& input_image) const {
   DCHECK_CALLED_ON_VALID_THREAD(test_main_thread_checker_);
   LOG_ASSERT(image_processor_);
+  LOG_ASSERT(input_image.IsLoaded());
+  LOG_ASSERT(input_image.DataSize() ==
+             VideoFrame::AllocationSize(input_image.PixelFormat(),
+                                        input_image.Size()));
 
-  auto mapped_frame = ReadVideoFrame(input_image_info);
+  const auto format = input_image.PixelFormat();
+  const auto visible_size = input_image.Size();
+
+  // Create planes for layout. We cannot use WrapExternalData() because it
+  // calls GetDefaultLayout() and it supports only a few pixel formats.
+  const size_t num_planes = VideoFrame::NumPlanes(format);
+  std::vector<VideoFrameLayout::Plane> planes(num_planes);
+  const auto strides = VideoFrame::ComputeStrides(format, visible_size);
+  size_t offset = 0;
+  for (size_t i = 0; i < num_planes; ++i) {
+    planes[i].stride = strides[i];
+    planes[i].offset = offset;
+    offset += VideoFrame::PlaneSize(format, i, visible_size).GetArea();
+  }
+
+  auto layout = VideoFrameLayout::CreateWithPlanes(
+      format, visible_size, std::move(planes), {input_image.DataSize()});
+  if (!layout) {
+    LOG(ERROR) << "Failed to create VideoFrameLayout";
+    return nullptr;
+  }
+
+  auto frame = VideoFrame::WrapExternalDataWithLayout(
+      *layout, gfx::Rect(visible_size), visible_size, input_image.Data(),
+      input_image.DataSize(), base::TimeDelta());
+  if (!frame) {
+    LOG(ERROR) << "Failed to create VideoFrame";
+    return nullptr;
+  }
+
   const auto& input_layout = image_processor_->input_layout();
   if (VideoFrame::IsStorageTypeMappable(
           image_processor_->input_storage_type())) {
-    return CloneVideoFrameWithLayout(mapped_frame.get(), input_layout);
+    return CloneVideoFrameWithLayout(frame.get(), input_layout);
   } else {
 #if defined(OS_CHROMEOS)
     LOG_ASSERT(image_processor_->input_storage_type() ==
@@ -232,18 +192,17 @@
 }
 
 scoped_refptr<VideoFrame> ImageProcessorClient::CreateOutputFrame(
-    const VideoImageInfo& output_image_info) const {
+    const Image& output_image) const {
   DCHECK_CALLED_ON_VALID_THREAD(test_main_thread_checker_);
+  LOG_ASSERT(output_image.IsMetadataLoaded());
   LOG_ASSERT(image_processor_);
 
   const auto& output_layout = image_processor_->output_layout();
   if (VideoFrame::IsStorageTypeMappable(
           image_processor_->input_storage_type())) {
     return VideoFrame::CreateFrameWithLayout(
-        output_layout, gfx::Rect(output_image_info.visible_size),
-        output_image_info.visible_size, base::TimeDelta(),
-        false /* zero_initialize_memory*/
-    );
+        output_layout, gfx::Rect(output_image.Size()), output_image.Size(),
+        base::TimeDelta(), false /* zero_initialize_memory*/);
   } else {
 #if defined(OS_CHROMEOS)
     LOG_ASSERT(image_processor_->input_storage_type() ==
@@ -307,12 +266,12 @@
   image_processor_error_count_++;
 }
 
-void ImageProcessorClient::Process(const VideoImageInfo& input_info,
-                                   const VideoImageInfo& output_info) {
+void ImageProcessorClient::Process(const Image& input_image,
+                                   const Image& output_image) {
   DCHECK_CALLED_ON_VALID_THREAD(test_main_thread_checker_);
-  auto input_frame = CreateInputFrame(input_info);
+  auto input_frame = CreateInputFrame(input_image);
   ASSERT_TRUE(input_frame);
-  auto output_frame = CreateOutputFrame(input_info);
+  auto output_frame = CreateOutputFrame(output_image);
   ASSERT_TRUE(output_frame);
   image_processor_client_thread_.task_runner()->PostTask(
       FROM_HERE,
diff --git a/media/gpu/test/image_processor/image_processor_client.h b/media/gpu/test/image_processor/image_processor_client.h
index 999bbc3..110f08d 100644
--- a/media/gpu/test/image_processor/image_processor_client.h
+++ b/media/gpu/test/image_processor/image_processor_client.h
@@ -17,7 +17,6 @@
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "media/gpu/image_processor.h"
-#include "media/gpu/test/video_image_info.h"
 
 namespace base {
 
@@ -31,6 +30,8 @@
 
 namespace test {
 
+class Image;
+
 // ImageProcessorClient is a client of ImageProcessor for testing purpose.
 // All the public functions must be called on the same thread, usually the test
 // main thread.
@@ -50,8 +51,9 @@
   ~ImageProcessorClient();
 
   // Process |input_frame| and |output_frame| with |image_processor_|.
-  void Process(const VideoImageInfo& input_info,
-               const VideoImageInfo& output_info);
+  // Processing is done asynchronously, the WaitUntilNumImageProcessed()
+  // function can be used to wait for the results.
+  void Process(const Image& input_image, const Image& output_image);
 
   // TODO(crbug.com/917951): Add Reset() when we test Reset() test case.
 
@@ -74,7 +76,7 @@
   size_t GetErrorCount() const;
 
  private:
-  ImageProcessorClient(bool store_processed_video_frames);
+  explicit ImageProcessorClient(bool store_processed_video_frames);
 
   // Create ImageProcessor with |input_config|, |output_config| and
   // |num_buffers|.
@@ -100,11 +102,9 @@
   // These are test helper functions to create a VideoFrame from VideoImageInfo,
   // which will be input in Process().
   // Create a VideoFrame using the input layout required by |image_processor_|.
-  scoped_refptr<VideoFrame> CreateInputFrame(
-      const VideoImageInfo& input_image_info) const;
+  scoped_refptr<VideoFrame> CreateInputFrame(const Image& input_image) const;
   // Create a VideoFrame using the output layout required by |image_processor_|.
-  scoped_refptr<VideoFrame> CreateOutputFrame(
-      const VideoImageInfo& output_image_info) const;
+  scoped_refptr<VideoFrame> CreateOutputFrame(const Image& output_image) const;
 
   std::unique_ptr<ImageProcessor> image_processor_;
 
diff --git a/media/gpu/test/video_frame_helpers.cc b/media/gpu/test/video_frame_helpers.cc
index 6d01e39..b4810b09 100644
--- a/media/gpu/test/video_frame_helpers.cc
+++ b/media/gpu/test/video_frame_helpers.cc
@@ -191,5 +191,14 @@
   return handle;
 }
 
+base::Optional<VideoFrameLayout> CreateVideoFrameLayout(
+    VideoPixelFormat pixel_format,
+    const gfx::Size& size) {
+  return VideoFrameLayout::CreateWithStrides(
+      pixel_format, size, VideoFrame::ComputeStrides(pixel_format, size),
+      std::vector<size_t>(VideoFrame::NumPlanes(pixel_format),
+                          0) /* buffer_sizes */);
+}
+
 }  // namespace test
 }  // namespace media
diff --git a/media/gpu/test/video_frame_helpers.h b/media/gpu/test/video_frame_helpers.h
index f36bfacf..6958adc9 100644
--- a/media/gpu/test/video_frame_helpers.h
+++ b/media/gpu/test/video_frame_helpers.h
@@ -6,6 +6,7 @@
 #define MEDIA_GPU_TEST_VIDEO_FRAME_HELPERS_H_
 
 #include "base/memory/scoped_refptr.h"
+#include "media/base/video_frame_layout.h"
 #include "media/base/video_types.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/geometry/size.h"
@@ -60,6 +61,12 @@
 gfx::GpuMemoryBufferHandle CreateGpuMemoryBufferHandle(
     scoped_refptr<VideoFrame> video_frame);
 
+// Create a video frame layout for the specified |pixel_format| and |size|. The
+// created layout will have a separate buffer for each plane in the format.
+base::Optional<VideoFrameLayout> CreateVideoFrameLayout(
+    VideoPixelFormat pixel_format,
+    const gfx::Size& size);
+
 }  // namespace test
 }  // namespace media
 
diff --git a/media/gpu/test/video_image_info.h b/media/gpu/test/video_image_info.h
deleted file mode 100644
index 3de0da2..0000000
--- a/media/gpu/test/video_image_info.h
+++ /dev/null
@@ -1,60 +0,0 @@
-
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_GPU_TEST_VIDEO_IMAGE_INFO_H_
-#define MEDIA_GPU_TEST_VIDEO_IMAGE_INFO_H_
-
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "base/optional.h"
-#include "media/base/video_frame.h"
-#include "media/base/video_frame_layout.h"
-#include "media/base/video_types.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace media {
-namespace test {
-
-// VideoImageInfo is the information about raw video frame in file.
-struct VideoImageInfo {
-  // TODO(crbug.com/917951): Deprecate this constructor once we load these info
-  // from json file.
-  constexpr VideoImageInfo(const base::FilePath::CharType* const file_name,
-                           const char* const md5sum,
-                           VideoPixelFormat pixel_format,
-                           gfx::Size size)
-      : file_name(file_name),
-        md5sum(md5sum),
-        pixel_format(pixel_format),
-        visible_size(size.width(), size.height()) {}
-  VideoImageInfo() = delete;
-  ~VideoImageInfo() = default;
-
-  base::Optional<VideoFrameLayout> VideoFrameLayout() const {
-    return VideoFrameLayout::CreateWithStrides(
-        pixel_format, visible_size,
-        VideoFrame::ComputeStrides(pixel_format, visible_size),
-        std::vector<size_t>(VideoFrame::NumPlanes(pixel_format),
-                            0) /* buffer_sizes */);
-  }
-
-  // |file_name| is a file name to be read(e.g. "bear_320x192.i420.yuv"), not
-  // file path.
-  const base::FilePath::CharType* const file_name;
-  //| md5sum| is the md5sum value of the video frame, whose coded_size is the
-  // same as visible size.
-  const char* const md5sum;
-
-  // |pixel_format| and |visible_size| of the video frame in file.
-  // NOTE: visible_size should be the same as coded_size, i.e., there is no
-  // extra padding in the file.
-  const VideoPixelFormat pixel_format;
-  const gfx::Size visible_size;
-};
-
-}  // namespace test
-}  // namespace media
-#endif  // MEDIA_GPU_TEST_VIDEO_IMAGE_INFO_H_
diff --git a/media/gpu/v4l2/generic_v4l2_device.cc b/media/gpu/v4l2/generic_v4l2_device.cc
index d59dc6c..6246b375 100644
--- a/media/gpu/v4l2/generic_v4l2_device.cc
+++ b/media/gpu/v4l2/generic_v4l2_device.cc
@@ -299,8 +299,14 @@
   gfx::NativePixmapHandle native_pixmap_handle;
 
   std::vector<base::ScopedFD> duped_fds;
-  for (const auto& fd : dmabuf_fds) {
-    duped_fds.emplace_back(HANDLE_EINTR(dup(fd.get())));
+  // The number of file descriptors can be less than the number of planes when
+  // v4l2 pix fmt, |fourcc|, is a single plane format. Duplicating the last
+  // file descriptor should be safely used for the later planes, because they
+  // are on the last buffer.
+  for (size_t i = 0; i < num_planes; ++i) {
+    int fd =
+        i < dmabuf_fds.size() ? dmabuf_fds[i].get() : dmabuf_fds.back().get();
+    duped_fds.emplace_back(HANDLE_EINTR(dup(fd)));
     if (!duped_fds.back().is_valid()) {
       VPLOGF(1) << "Failed duplicating a dmabuf fd";
       return nullptr;
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
index a566ca26..4f02e977 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
@@ -1492,11 +1492,29 @@
   DVLOGF(3) << "picture_buffer_id=" << picture_buffer_id;
   DCHECK(child_task_runner_->BelongsToCurrentThread());
 
-  std::vector<base::ScopedFD> passed_dmabuf_fds;
+  std::vector<base::ScopedFD> dmabuf_fds;
 #if defined(USE_OZONE)
-  for (const auto& fd : gpu_memory_buffer_handle.native_pixmap_handle.fds) {
-    DCHECK_NE(fd.fd, -1);
-    passed_dmabuf_fds.push_back(base::ScopedFD(fd.fd));
+  DCHECK_EQ(gpu_memory_buffer_handle.native_pixmap_handle.fds.size(),
+            gpu_memory_buffer_handle.native_pixmap_handle.planes.size());
+  // If the driver does not accept as many fds as we received from the client,
+  // we have to check if the additional fds are actually duplicated fds pointing
+  // to previous planes; if so, we can close the duplicates and keep only the
+  // original fd(s).
+  // Assume that an fd is a duplicate of a previous plane's fd if offset != 0.
+  // Otherwise, if offset == 0, return error as it may be pointing to a new
+  // plane.
+  for (auto& fd : gpu_memory_buffer_handle.native_pixmap_handle.fds) {
+    dmabuf_fds.emplace_back(fd.fd);
+  }
+  for (size_t i = dmabuf_fds.size() - 1; i >= output_planes_count_; i--) {
+    if (gpu_memory_buffer_handle.native_pixmap_handle.planes[i].offset == 0) {
+      VLOGF(1) << "The dmabuf fd points to a new buffer, ";
+      NOTIFY_ERROR(INVALID_ARGUMENT);
+      return;
+    }
+    // Drop safely, because this fd is duplicate dmabuf fd pointing to previous
+    // buffer and the appropriate address can be accessed by associated offset.
+    dmabuf_fds.pop_back();
   }
 #endif
 
@@ -1518,8 +1536,7 @@
       FROM_HERE,
       base::BindOnce(
           &V4L2SliceVideoDecodeAccelerator::ImportBufferForPictureTask,
-          base::Unretained(this), picture_buffer_id,
-          std::move(passed_dmabuf_fds)));
+          base::Unretained(this), picture_buffer_id, std::move(dmabuf_fds)));
 }
 
 void V4L2SliceVideoDecodeAccelerator::ImportBufferForPictureTask(
diff --git a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
index 9561839..1ad3330 100644
--- a/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
@@ -554,6 +554,40 @@
   DVLOGF(3) << "picture_buffer_id=" << picture_buffer_id;
   DCHECK(child_task_runner_->BelongsToCurrentThread());
 
+  std::vector<base::ScopedFD> dmabuf_fds;
+  int32_t stride = 0;
+#if defined(USE_OZONE)
+  DCHECK_EQ(gpu_memory_buffer_handle.native_pixmap_handle.fds.size(),
+            gpu_memory_buffer_handle.native_pixmap_handle.planes.size());
+
+  // If the driver does not accept as many fds as we received from the client,
+  // we have to check if the additional fds are actually duplicated fds pointing
+  // to previous planes; if so, we can close the duplicates and keep only the
+  // original fd(s).
+  // Assume that an fd is a duplicate of a previous plane's fd if offset != 0.
+  // Otherwise, if offset == 0, return error as it may be pointing to a new
+  // plane.
+  for (auto& fd : gpu_memory_buffer_handle.native_pixmap_handle.fds) {
+    dmabuf_fds.emplace_back(fd.fd);
+  }
+  for (size_t i = dmabuf_fds.size() - 1; i >= egl_image_planes_count_; i--) {
+    if (gpu_memory_buffer_handle.native_pixmap_handle.planes[i].offset == 0) {
+      VLOGF(1) << "The dmabuf fd points to a new buffer, ";
+      NOTIFY_ERROR(INVALID_ARGUMENT);
+      return;
+    }
+    // Drop safely, because this fd is duplicate dmabuf fd pointing to previous
+    // buffer and the appropriate address can be accessed by associated offset.
+    dmabuf_fds.pop_back();
+  }
+
+  stride = gpu_memory_buffer_handle.native_pixmap_handle.planes[0].stride;
+  for (const auto& plane :
+       gpu_memory_buffer_handle.native_pixmap_handle.planes) {
+    DVLOGF(3) << ": offset=" << plane.offset << ", stride=" << plane.stride;
+  }
+#endif
+
   if (output_mode_ != Config::OutputMode::IMPORT) {
     VLOGF(1) << "Cannot import in non-import mode";
     NOTIFY_ERROR(INVALID_ARGUMENT);
@@ -570,20 +604,6 @@
     return;
   }
 
-  std::vector<base::ScopedFD> dmabuf_fds;
-  int32_t stride = 0;
-#if defined(USE_OZONE)
-  for (const auto& fd : gpu_memory_buffer_handle.native_pixmap_handle.fds) {
-    DCHECK_NE(fd.fd, -1);
-    dmabuf_fds.push_back(base::ScopedFD(fd.fd));
-  }
-  stride = gpu_memory_buffer_handle.native_pixmap_handle.planes[0].stride;
-  for (const auto& plane :
-       gpu_memory_buffer_handle.native_pixmap_handle.planes) {
-    DVLOGF(3) << ": offset=" << plane.offset << ", stride=" << plane.stride;
-  }
-#endif
-
   decoder_thread_.task_runner()->PostTask(
       FROM_HERE,
       base::Bind(&V4L2VideoDecodeAccelerator::ImportBufferForPictureTask,
diff --git a/media/test/data/README.md b/media/test/data/README.md
index b2e84eb0..338bc5b 100644
--- a/media/test/data/README.md
+++ b/media/test/data/README.md
@@ -767,9 +767,15 @@
 #### bear\_320x192.i420.yuv
 First frame of bear\_320x192\_40frames.yuv for image\_processor_test.
 
+#### bear\_320x192.i420.yuv.json
+Metadata describing bear\_320x192.i420.yuv.
+
 #### bear\_320x192.nv12.yuv
 First frame of bear\_320x192\_40frames.nv12.yuv for image\_processor_test.
 
+#### bear\_320x192.nv12.yuv.json
+Metadata describing bear\_320x192.nv12.yuv.
+
 #### bear\_320x192.yv21.yuv
 First frame of bear\_320x192\_40frames.yv21.yuv for image\_processor_test.
 
diff --git a/media/test/data/bear_320x192.i420.yuv.json b/media/test/data/bear_320x192.i420.yuv.json
new file mode 100644
index 0000000..b1d2520c0
--- /dev/null
+++ b/media/test/data/bear_320x192.i420.yuv.json
@@ -0,0 +1,6 @@
+{
+  "pixel_format": "I420",
+  "width": 320,
+  "height": 192,
+  "checksum": "962820755c74b28f9385fd67219cc04a"
+}
diff --git a/media/test/data/bear_320x192.nv12.yuv.json b/media/test/data/bear_320x192.nv12.yuv.json
new file mode 100644
index 0000000..26c3254
--- /dev/null
+++ b/media/test/data/bear_320x192.nv12.yuv.json
@@ -0,0 +1,6 @@
+{
+  "pixel_format": "NV12",
+  "width": 320,
+  "height": 192,
+  "checksum": "ce21986434743d3671056719136d46ff"
+}
diff --git a/net/third_party/quic/core/qpack/qpack_decoder_test.cc b/net/third_party/quic/core/qpack/qpack_decoder_test.cc
index 6476aca1..dacf370 100644
--- a/net/third_party/quic/core/qpack/qpack_decoder_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_decoder_test.cc
@@ -66,8 +66,8 @@
                                FragmentMode::kOctetByOctet));
 
 TEST_P(QpackDecoderTest, NoPrefix) {
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            QuicStringPiece("Incomplete header data prefix.")));
+  EXPECT_CALL(handler_,
+              OnDecodingErrorDetected(Eq("Incomplete header data prefix.")));
 
   // Header Data Prefix is at least two bytes long.
   DecodeHeaderBlock(QuicTextUtils::HexDecode("00"));
@@ -82,8 +82,7 @@
 }
 
 TEST_P(QpackDecoderTest, LiteralEntryEmptyName) {
-  EXPECT_CALL(handler_,
-              OnHeaderDecoded(QuicStringPiece(""), QuicStringPiece("foo")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq("foo")));
   EXPECT_CALL(handler_, OnDecodingCompleted());
   EXPECT_CALL(decoder_stream_sender_delegate_,
               Write(Eq(kHeaderAcknowledgement)));
@@ -92,8 +91,7 @@
 }
 
 TEST_P(QpackDecoderTest, LiteralEntryEmptyValue) {
-  EXPECT_CALL(handler_,
-              OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
   EXPECT_CALL(handler_, OnDecodingCompleted());
   EXPECT_CALL(decoder_stream_sender_delegate_,
               Write(Eq(kHeaderAcknowledgement)));
@@ -102,8 +100,7 @@
 }
 
 TEST_P(QpackDecoderTest, LiteralEntryEmptyNameAndValue) {
-  EXPECT_CALL(handler_,
-              OnHeaderDecoded(QuicStringPiece(""), QuicStringPiece("")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq("")));
   EXPECT_CALL(handler_, OnDecodingCompleted());
   EXPECT_CALL(decoder_stream_sender_delegate_,
               Write(Eq(kHeaderAcknowledgement)));
@@ -112,8 +109,7 @@
 }
 
 TEST_P(QpackDecoderTest, SimpleLiteralEntry) {
-  EXPECT_CALL(handler_,
-              OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("bar")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
   EXPECT_CALL(handler_, OnDecodingCompleted());
   EXPECT_CALL(decoder_stream_sender_delegate_,
               Write(Eq(kHeaderAcknowledgement)));
@@ -122,11 +118,9 @@
 }
 
 TEST_P(QpackDecoderTest, MultipleLiteralEntries) {
-  EXPECT_CALL(handler_,
-              OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("bar")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
   QuicString str(127, 'a');
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("foobaar"),
-                                        QuicStringPiece(str)));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foobaar"), QuicStringPiece(str)));
   EXPECT_CALL(handler_, OnDecodingCompleted());
   EXPECT_CALL(decoder_stream_sender_delegate_,
               Write(Eq(kHeaderAcknowledgement)));
@@ -146,24 +140,24 @@
 
 // Name Length value is too large for varint decoder to decode.
 TEST_P(QpackDecoderTest, NameLenTooLargeForVarintDecoder) {
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            QuicStringPiece("Encoded integer too large.")));
+  EXPECT_CALL(handler_,
+              OnDecodingErrorDetected(Eq("Encoded integer too large.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffffffffffffffffffff"));
 }
 
 // Name Length value can be decoded by varint decoder but exceeds 1 MB limit.
 TEST_P(QpackDecoderTest, NameLenExceedsLimit) {
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            QuicStringPiece("String literal too long.")));
+  EXPECT_CALL(handler_,
+              OnDecodingErrorDetected(Eq("String literal too long.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffff7f"));
 }
 
 // Value Length value is too large for varint decoder to decode.
 TEST_P(QpackDecoderTest, ValueLenTooLargeForVarintDecoder) {
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            QuicStringPiece("Encoded integer too large.")));
+  EXPECT_CALL(handler_,
+              OnDecodingErrorDetected(Eq("Encoded integer too large.")));
 
   DecodeHeaderBlock(
       QuicTextUtils::HexDecode("000023666f6f7fffffffffffffffffffff"));
@@ -171,33 +165,31 @@
 
 // Value Length value can be decoded by varint decoder but exceeds 1 MB limit.
 TEST_P(QpackDecoderTest, ValueLenExceedsLimit) {
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            QuicStringPiece("String literal too long.")));
+  EXPECT_CALL(handler_,
+              OnDecodingErrorDetected(Eq("String literal too long.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f7fffff7f"));
 }
 
 TEST_P(QpackDecoderTest, IncompleteHeaderBlock) {
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            QuicStringPiece("Incomplete header block.")));
+  EXPECT_CALL(handler_,
+              OnDecodingErrorDetected(Eq("Incomplete header block.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode("00002366"));
 }
 
 TEST_P(QpackDecoderTest, HuffmanSimple) {
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("custom-key"),
-                                        QuicStringPiece("custom-value")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value")));
   EXPECT_CALL(handler_, OnDecodingCompleted());
   EXPECT_CALL(decoder_stream_sender_delegate_,
               Write(Eq(kHeaderAcknowledgement)));
 
-  DecodeHeaderBlock(QuicTextUtils::HexDecode(
-      QuicStringPiece("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf")));
+  DecodeHeaderBlock(
+      QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf"));
 }
 
 TEST_P(QpackDecoderTest, AlternatingHuffmanNonHuffman) {
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("custom-key"),
-                                        QuicStringPiece("custom-value")))
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value")))
       .Times(4);
   EXPECT_CALL(handler_, OnDecodingCompleted());
   EXPECT_CALL(decoder_stream_sender_delegate_,
@@ -260,26 +252,19 @@
 
 TEST_P(QpackDecoderTest, StaticTable) {
   // A header name that has multiple entries with different values.
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":method"),
-                                        QuicStringPiece("GET")));
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":method"),
-                                        QuicStringPiece("POST")));
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":method"),
-                                        QuicStringPiece("TRACE")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("POST")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("TRACE")));
 
   // A header name that has a single entry with non-empty value.
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("accept-encoding"),
-                                        QuicStringPiece("gzip, deflate, br")));
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("accept-encoding"),
-                                        QuicStringPiece("compress")));
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("accept-encoding"),
-                                        QuicStringPiece("")));
+  EXPECT_CALL(handler_,
+              OnHeaderDecoded(Eq("accept-encoding"), Eq("gzip, deflate, br")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("accept-encoding"), Eq("compress")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("accept-encoding"), Eq("")));
 
   // A header name that has a single entry with empty value.
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("location"),
-                                        QuicStringPiece("")));
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("location"),
-                                        QuicStringPiece("foo")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq("")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq("foo")));
 
   EXPECT_CALL(handler_, OnDecodingCompleted());
   EXPECT_CALL(decoder_stream_sender_delegate_,
@@ -291,12 +276,12 @@
 
 TEST_P(QpackDecoderTest, TooHighStaticTableIndex) {
   // This is the last entry in the static table with index 98.
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece("x-frame-options"),
-                                        QuicStringPiece("sameorigin")));
+  EXPECT_CALL(handler_,
+              OnHeaderDecoded(Eq("x-frame-options"), Eq("sameorigin")));
 
   // Addressing entry 99 should trigger an error.
-  EXPECT_CALL(handler_, OnDecodingErrorDetected(
-                            QuicStringPiece("Static table entry not found.")));
+  EXPECT_CALL(handler_,
+              OnDecodingErrorDetected(Eq("Static table entry not found.")));
 
   DecodeHeaderBlock(QuicTextUtils::HexDecode("0000ff23ff24"));
 }
@@ -598,8 +583,7 @@
 }
 
 TEST_P(QpackDecoderTest, NonZeroLargestReferenceButNoDynamicEntries) {
-  EXPECT_CALL(handler_, OnHeaderDecoded(QuicStringPiece(":method"),
-                                        QuicStringPiece("GET")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET")));
   EXPECT_CALL(handler_,
               OnDecodingErrorDetected(Eq("Largest Reference too large.")));
 
@@ -657,8 +641,7 @@
   // Duplicate entry.
   DecodeEncoderStreamData(QuicTextUtils::HexDecode("00"));
 
-  EXPECT_CALL(handler_,
-              OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("bar")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
   EXPECT_CALL(handler_,
               OnDecodingErrorDetected(Eq("Largest Reference too large.")));
 
@@ -670,8 +653,7 @@
                // largest reference in this header block, even though Largest
                // Reference is 2.
 
-  EXPECT_CALL(handler_,
-              OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
   EXPECT_CALL(handler_,
               OnDecodingErrorDetected(Eq("Largest Reference too large.")));
 
@@ -683,8 +665,7 @@
                  // absolute index 1.  This is the largest reference in this
                  // header block, even though Largest Reference is 2.
 
-  EXPECT_CALL(handler_,
-              OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("bar")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
   EXPECT_CALL(handler_,
               OnDecodingErrorDetected(Eq("Largest Reference too large.")));
 
@@ -696,8 +677,7 @@
                // absolute index 2.  This is the largest reference in this
                // header block, even though Largest Reference is 3.
 
-  EXPECT_CALL(handler_,
-              OnHeaderDecoded(QuicStringPiece("foo"), QuicStringPiece("")));
+  EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
   EXPECT_CALL(handler_,
               OnDecodingErrorDetected(Eq("Largest Reference too large.")));
 
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
index 06ab3f7..7b2ec4d 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
@@ -73,8 +73,7 @@
 }
 
 TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReferenceValueTooLong) {
-  EXPECT_CALL(*delegate(),
-              OnErrorDetected(QuicStringPiece("Encoded integer too large.")));
+  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
 
   Decode(QuicTextUtils::HexDecode("c57fffffffffffffffffffff"));
 }
@@ -114,8 +113,7 @@
 // Name Length value can be decoded by varint decoder but exceeds 1 MB limit.
 TEST_F(QpackEncoderStreamReceiverTest,
        InsertWithoutNameReferenceNameExceedsLimit) {
-  EXPECT_CALL(*delegate(),
-              OnErrorDetected(QuicStringPiece("String literal too long.")));
+  EXPECT_CALL(*delegate(), OnErrorDetected(Eq("String literal too long.")));
 
   Decode(QuicTextUtils::HexDecode("5fffff7f"));
 }
diff --git a/net/third_party/quic/core/qpack/qpack_encoder_test.cc b/net/third_party/quic/core/qpack/qpack_encoder_test.cc
index 25fb128..c2b6ec0 100644
--- a/net/third_party/quic/core/qpack/qpack_encoder_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_encoder_test.cc
@@ -11,6 +11,7 @@
 #include "net/third_party/quic/platform/api/quic_text_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::testing::Eq;
 using ::testing::StrictMock;
 using ::testing::Values;
 
@@ -153,7 +154,7 @@
 
 TEST_P(QpackEncoderTest, DecoderStreamError) {
   EXPECT_CALL(decoder_stream_error_delegate_,
-              OnError(QuicStringPiece("Encoded integer too large.")));
+              OnError(Eq("Encoded integer too large.")));
 
   QpackEncoder encoder(&decoder_stream_error_delegate_,
                        &encoder_stream_sender_delegate_);
diff --git a/net/third_party/quic/core/qpack/qpack_instruction_decoder_test.cc b/net/third_party/quic/core/qpack/qpack_instruction_decoder_test.cc
index 1c2c6dd..539840d 100644
--- a/net/third_party/quic/core/qpack/qpack_instruction_decoder_test.cc
+++ b/net/third_party/quic/core/qpack/qpack_instruction_decoder_test.cc
@@ -13,6 +13,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
+using ::testing::Eq;
 using ::testing::Expectation;
 using ::testing::Return;
 using ::testing::StrictMock;
@@ -140,14 +141,12 @@
 }
 
 TEST_P(QpackInstructionDecoderTest, InvalidHuffmanEncoding) {
-  EXPECT_CALL(delegate_,
-              OnError(QuicStringPiece("Error in Huffman-encoded string.")));
+  EXPECT_CALL(delegate_, OnError(Eq("Error in Huffman-encoded string.")));
   decoder_.Decode(QuicTextUtils::HexDecode("c1ff"));
 }
 
 TEST_P(QpackInstructionDecoderTest, InvalidVarintEncoding) {
-  EXPECT_CALL(delegate_,
-              OnError(QuicStringPiece("Encoded integer too large.")));
+  EXPECT_CALL(delegate_, OnError(Eq("Encoded integer too large.")));
   decoder_.Decode(QuicTextUtils::HexDecode("ffffffffffffffffffffff"));
 }
 
diff --git a/printing/printing_context_android.h b/printing/printing_context_android.h
index f0c7bd4..3471e45 100644
--- a/printing/printing_context_android.h
+++ b/printing/printing_context_android.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/android/scoped_java_ref.h"
+#include "base/file_descriptor_posix.h"
 #include "base/macros.h"
 #include "printing/printing_context.h"
 
@@ -60,8 +61,7 @@
   printing::NativeDrawingContext context() const override;
 
  private:
-  // TODO(thestig): Use |base::kInvalidFd| once available.
-  bool is_file_descriptor_valid() const { return fd_ > -1; }
+  bool is_file_descriptor_valid() const { return fd_ > base::kInvalidFd; }
 
   base::android::ScopedJavaGlobalRef<jobject> j_printing_context_;
 
@@ -69,7 +69,7 @@
   // ready on the Java side
   PrintSettingsCallback callback_;
 
-  int fd_ = -1;
+  int fd_ = base::kInvalidFd;
 
   DISALLOW_COPY_AND_ASSIGN(PrintingContextAndroid);
 };
diff --git a/services/device/public/mojom/serial.mojom b/services/device/public/mojom/serial.mojom
index d12589e..70e328b3 100644
--- a/services/device/public/mojom/serial.mojom
+++ b/services/device/public/mojom/serial.mojom
@@ -97,14 +97,9 @@
 // Performs asynchronous I/O on serial devices.
 interface SerialPort {
   // Initiates an Open of the device then returns result.
-  Open(SerialConnectionOptions options) => (bool success);
-
-  // Performs a Read operation then returns retrieved data and the
-  // result. Note that the Read may succeed partially, means that even if
-  // |error| indicates an error other than NONE, |data| may still contain
-  // some retrieved data with the max size |bytes|. Behavior is undefined if
-  // this is called while a Read is already pending.
-  Read(uint32 bytes) => (array<uint8> data, SerialReceiveError error);
+  Open(SerialConnectionOptions options,
+       handle<data_pipe_producer> out_stream,
+       associated SerialPortClient client) => (bool success);
 
   // Performs a Write operation then returns written data and the result.
   // Note that the Write may succeed partially, means that even if |error|
@@ -113,8 +108,10 @@
   // is called while a Write is already pending.
   Write(array<uint8> data) => (uint32 bytes_written, SerialSendError error);
 
-  // Attempts to cancel a pending read operation.
-  CancelRead(SerialReceiveError reason);
+  // Try to clear existing read error and reconnect the data pipe for read.
+  // This is supposed to be called after SerialPortClient#OnReadError is
+  // notified.
+  ClearReadError(handle<data_pipe_producer> producer);
 
   // Attempts to cancel a pending write operation.
   CancelWrite(SerialSendError reason);
@@ -142,3 +139,7 @@
   // state.
   ClearBreak() => (bool success);
 };
+
+interface SerialPortClient {
+  OnReadError(SerialReceiveError error);
+};
diff --git a/services/device/serial/buffer.cc b/services/device/serial/buffer.cc
index 0ab176b..3fcb1c2 100644
--- a/services/device/serial/buffer.cc
+++ b/services/device/serial/buffer.cc
@@ -4,6 +4,8 @@
 
 #include "services/device/serial/buffer.h"
 
+#include <utility>
+
 #include "base/numerics/safe_conversions.h"
 #include "net/base/io_buffer.h"
 
@@ -36,7 +38,7 @@
                            static_cast<device::mojom::SerialSendError>(error));
 }
 
-ReceiveBuffer::ReceiveBuffer(scoped_refptr<net::IOBuffer> buffer,
+ReceiveBuffer::ReceiveBuffer(char* buffer,
                              uint32_t size,
                              ReceiveCompleteCallback callback)
     : buffer_(buffer), size_(size), callback_(std::move(callback)) {}
@@ -44,7 +46,7 @@
 ReceiveBuffer::~ReceiveBuffer() = default;
 
 char* ReceiveBuffer::GetData() {
-  return buffer_->data();
+  return buffer_;
 }
 
 uint32_t ReceiveBuffer::GetSize() {
diff --git a/services/device/serial/buffer.h b/services/device/serial/buffer.h
index d70775f..3ca346f4 100644
--- a/services/device/serial/buffer.h
+++ b/services/device/serial/buffer.h
@@ -7,6 +7,8 @@
 
 #include <stdint.h>
 
+#include <vector>
+
 #include "base/callback.h"
 #include "net/base/io_buffer.h"
 #include "services/device/public/mojom/serial.mojom.h"
@@ -64,9 +66,7 @@
  public:
   using ReceiveCompleteCallback =
       base::OnceCallback<void(int, device::mojom::SerialReceiveError)>;
-  ReceiveBuffer(scoped_refptr<net::IOBuffer> buffer,
-                uint32_t size,
-                ReceiveCompleteCallback callback);
+  ReceiveBuffer(char* buffer, uint32_t size, ReceiveCompleteCallback callback);
   ~ReceiveBuffer() override;
 
   char* GetData() override;
@@ -75,7 +75,7 @@
   void DoneWithError(uint32_t bytes_written, int32_t error) override;
 
  private:
-  scoped_refptr<net::IOBuffer> buffer_;
+  char* buffer_;
   const uint32_t size_;
   ReceiveCompleteCallback callback_;
 };
diff --git a/services/device/serial/serial_port_impl.cc b/services/device/serial/serial_port_impl.cc
index c2586588..3c99c18 100644
--- a/services/device/serial/serial_port_impl.cc
+++ b/services/device/serial/serial_port_impl.cc
@@ -29,28 +29,24 @@
 SerialPortImpl::SerialPortImpl(
     const base::FilePath& path,
     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
-    : io_handler_(device::SerialIoHandler::Create(path, ui_task_runner)) {}
+    : io_handler_(device::SerialIoHandler::Create(path, ui_task_runner)),
+      out_stream_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
+      weak_factory_(this) {}
 
 SerialPortImpl::~SerialPortImpl() = default;
 
 void SerialPortImpl::Open(mojom::SerialConnectionOptionsPtr options,
+                          mojo::ScopedDataPipeProducerHandle out_stream,
+                          mojom::SerialPortClientAssociatedPtrInfo client,
                           OpenCallback callback) {
-  io_handler_->Open(*options, std::move(callback));
-}
-
-void SerialPortImpl::Read(uint32_t bytes, ReadCallback callback) {
-  auto buffer = base::MakeRefCounted<net::IOBuffer>(static_cast<size_t>(bytes));
-  io_handler_->Read(std::make_unique<ReceiveBuffer>(
-      buffer, bytes,
-      base::BindOnce(
-          [](ReadCallback callback, scoped_refptr<net::IOBuffer> buffer,
-             int bytes_read, mojom::SerialReceiveError error) {
-            std::move(callback).Run(
-                std::vector<uint8_t>(buffer->data(),
-                                     buffer->data() + bytes_read),
-                error);
-          },
-          std::move(callback), buffer)));
+  DCHECK(out_stream);
+  out_stream_ = std::move(out_stream);
+  if (client) {
+    client_.Bind(std::move(client));
+  }
+  io_handler_->Open(*options, base::BindOnce(&SerialPortImpl::OnOpenCompleted,
+                                             weak_factory_.GetWeakPtr(),
+                                             std::move(callback)));
 }
 
 void SerialPortImpl::Write(const std::vector<uint8_t>& data,
@@ -64,8 +60,22 @@
                 std::move(callback))));
 }
 
-void SerialPortImpl::CancelRead(mojom::SerialReceiveError reason) {
-  io_handler_->CancelRead(reason);
+void SerialPortImpl::ClearReadError(
+    mojo::ScopedDataPipeProducerHandle producer) {
+  // Make sure |io_handler_| is still open and the |out_stream_| has been
+  // closed.
+  if (!io_handler_ || out_stream_) {
+    return;
+  }
+  out_stream_watcher_.Cancel();
+  out_stream_.swap(producer);
+  out_stream_watcher_.Watch(
+      out_stream_.get(),
+      MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+      MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
+      base::BindRepeating(&SerialPortImpl::ReadFromPortAndWriteOut,
+                          weak_factory_.GetWeakPtr()));
+  out_stream_watcher_.ArmOrNotify();
 }
 
 void SerialPortImpl::CancelWrite(mojom::SerialSendError reason) {
@@ -89,6 +99,8 @@
 void SerialPortImpl::ConfigurePort(mojom::SerialConnectionOptionsPtr options,
                                    ConfigurePortCallback callback) {
   std::move(callback).Run(io_handler_->ConfigurePort(*options));
+  // Cancel pending reading as the new configure options are applied.
+  io_handler_->CancelRead(mojom::SerialReceiveError::NONE);
 }
 
 void SerialPortImpl::GetPortInfo(GetPortInfoCallback callback) {
@@ -103,4 +115,61 @@
   std::move(callback).Run(io_handler_->ClearBreak());
 }
 
+void SerialPortImpl::OnOpenCompleted(OpenCallback callback, bool success) {
+  if (success) {
+    out_stream_watcher_.Watch(
+        out_stream_.get(),
+        MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+        MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
+        base::BindRepeating(&SerialPortImpl::ReadFromPortAndWriteOut,
+                            weak_factory_.GetWeakPtr()));
+    out_stream_watcher_.ArmOrNotify();
+  }
+  std::move(callback).Run(success);
+}
+
+void SerialPortImpl::ReadFromPortAndWriteOut(
+    MojoResult result,
+    const mojo::HandleSignalsState& state) {
+  void* buffer;
+  uint32_t num_bytes;
+  if (result == MOJO_RESULT_OK) {
+    result = out_stream_->BeginWriteData(&buffer, &num_bytes,
+                                         MOJO_WRITE_DATA_FLAG_NONE);
+  }
+  if (result == MOJO_RESULT_OK) {
+    io_handler_->Read(std::make_unique<ReceiveBuffer>(
+        static_cast<char*>(buffer), num_bytes,
+        base::BindOnce(&SerialPortImpl::WriteToOutStream,
+                       weak_factory_.GetWeakPtr())));
+    return;
+  }
+  if (result == MOJO_RESULT_SHOULD_WAIT) {
+    // If there is no space to write, wait for more space.
+    out_stream_watcher_.ArmOrNotify();
+    return;
+  }
+  if (result == MOJO_RESULT_FAILED_PRECONDITION) {
+    // The |out_stream_| has been closed.
+    out_stream_.reset();
+    return;
+  }
+  // The code should not reach other cases.
+  NOTREACHED();
+}
+
+void SerialPortImpl::WriteToOutStream(int bytes_read,
+                                      mojom::SerialReceiveError error) {
+  out_stream_->EndWriteData(static_cast<uint32_t>(bytes_read));
+
+  if (error != mojom::SerialReceiveError::NONE) {
+    out_stream_.reset();
+    if (client_) {
+      client_->OnReadError(error);
+    }
+    return;
+  }
+  out_stream_watcher_.ArmOrNotify();
+}
+
 }  // namespace device
diff --git a/services/device/serial/serial_port_impl.h b/services/device/serial/serial_port_impl.h
index 900bc8f..e84ae88 100644
--- a/services/device/serial/serial_port_impl.h
+++ b/services/device/serial/serial_port_impl.h
@@ -10,6 +10,9 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 #include "services/device/public/mojom/serial.mojom.h"
 
 namespace base {
@@ -38,10 +41,11 @@
  private:
   // mojom::SerialPort methods:
   void Open(mojom::SerialConnectionOptionsPtr options,
+            mojo::ScopedDataPipeProducerHandle out_stream,
+            mojom::SerialPortClientAssociatedPtrInfo client,
             OpenCallback callback) override;
-  void Read(uint32_t bytes, ReadCallback callback) override;
   void Write(const std::vector<uint8_t>& data, WriteCallback callback) override;
-  void CancelRead(mojom::SerialReceiveError reason) override;
+  void ClearReadError(mojo::ScopedDataPipeProducerHandle producer) override;
   void CancelWrite(mojom::SerialSendError reason) override;
   void Flush(FlushCallback callback) override;
   void GetControlSignals(GetControlSignalsCallback callback) override;
@@ -53,8 +57,17 @@
   void SetBreak(SetBreakCallback callback) override;
   void ClearBreak(ClearBreakCallback callback) override;
 
-  scoped_refptr<SerialIoHandler> io_handler_;
+  void OnOpenCompleted(OpenCallback callback, bool success);
+  void ReadFromPortAndWriteOut(MojoResult result,
+                               const mojo::HandleSignalsState& state);
+  void WriteToOutStream(int bytes_read, mojom::SerialReceiveError error);
 
+  scoped_refptr<SerialIoHandler> io_handler_;
+  mojom::SerialPortClientAssociatedPtr client_;
+  mojo::ScopedDataPipeProducerHandle out_stream_;
+  mojo::SimpleWatcher out_stream_watcher_;
+
+  base::WeakPtrFactory<SerialPortImpl> weak_factory_;
   DISALLOW_COPY_AND_ASSIGN(SerialPortImpl);
 };
 
diff --git a/services/ws/gpu_host/BUILD.gn b/services/ws/gpu_host/BUILD.gn
index 88bdae7..20253ff 100644
--- a/services/ws/gpu_host/BUILD.gn
+++ b/services/ws/gpu_host/BUILD.gn
@@ -40,8 +40,8 @@
 
   if (is_chromeos) {
     sources += [
-      "arc_client.cc",
-      "arc_client.h",
+      "arc_gpu_client.cc",
+      "arc_gpu_client.h",
     ]
   }
 
diff --git a/services/ws/gpu_host/arc_client.cc b/services/ws/gpu_host/arc_gpu_client.cc
similarity index 72%
rename from services/ws/gpu_host/arc_client.cc
rename to services/ws/gpu_host/arc_gpu_client.cc
index 666fa695..5825d39 100644
--- a/services/ws/gpu_host/arc_client.cc
+++ b/services/ws/gpu_host/arc_gpu_client.cc
@@ -2,34 +2,34 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/ws/gpu_host/arc_client.h"
+#include "services/ws/gpu_host/arc_gpu_client.h"
 
 #include "services/viz/privileged/interfaces/gl/gpu_service.mojom.h"
 
 namespace ws {
 namespace gpu_host {
 
-ArcClient::ArcClient(viz::mojom::GpuService* gpu_service)
+ArcGpuClient::ArcGpuClient(viz::mojom::GpuService* gpu_service)
     : gpu_service_(gpu_service) {}
 
-ArcClient::~ArcClient() {}
+ArcGpuClient::~ArcGpuClient() {}
 
-void ArcClient::CreateVideoDecodeAccelerator(
+void ArcGpuClient::CreateVideoDecodeAccelerator(
     arc::mojom::VideoDecodeAcceleratorRequest vda_request) {
   gpu_service_->CreateArcVideoDecodeAccelerator(std::move(vda_request));
 }
 
-void ArcClient::CreateVideoEncodeAccelerator(
+void ArcGpuClient::CreateVideoEncodeAccelerator(
     arc::mojom::VideoEncodeAcceleratorRequest vea_request) {
   gpu_service_->CreateArcVideoEncodeAccelerator(std::move(vea_request));
 }
 
-void ArcClient::CreateVideoProtectedBufferAllocator(
+void ArcGpuClient::CreateVideoProtectedBufferAllocator(
     arc::mojom::VideoProtectedBufferAllocatorRequest pba_request) {
   gpu_service_->CreateArcVideoProtectedBufferAllocator(std::move(pba_request));
 }
 
-void ArcClient::CreateProtectedBufferManager(
+void ArcGpuClient::CreateProtectedBufferManager(
     arc::mojom::ProtectedBufferManagerRequest pbm_request) {
   gpu_service_->CreateArcProtectedBufferManager(std::move(pbm_request));
 }
diff --git a/services/ws/gpu_host/arc_client.h b/services/ws/gpu_host/arc_gpu_client.h
similarity index 72%
rename from services/ws/gpu_host/arc_client.h
rename to services/ws/gpu_host/arc_gpu_client.h
index cd2aed3..d02f8d2 100644
--- a/services/ws/gpu_host/arc_client.h
+++ b/services/ws/gpu_host/arc_gpu_client.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SERVICES_WS_GPU_HOST_ARC_CLIENT_H_
-#define SERVICES_WS_GPU_HOST_ARC_CLIENT_H_
+#ifndef SERVICES_WS_GPU_HOST_ARC_GPU_CLIENT_H_
+#define SERVICES_WS_GPU_HOST_ARC_GPU_CLIENT_H_
 
-#include "services/ws/public/mojom/arc.mojom.h"
+#include "services/ws/public/mojom/arc_gpu.mojom.h"
 
 namespace viz {
 namespace mojom {
@@ -18,13 +18,13 @@
 
 // The implementation that relays requests from clients to the real
 // service implementation in the GPU process over mojom.GpuService.
-class ArcClient : public mojom::Arc {
+class ArcGpuClient : public mojom::ArcGpu {
  public:
-  explicit ArcClient(viz::mojom::GpuService* gpu_service);
-  ~ArcClient() override;
+  explicit ArcGpuClient(viz::mojom::GpuService* gpu_service);
+  ~ArcGpuClient() override;
 
  private:
-  // mojom::Arc overrides:
+  // mojom::ArcGpu overrides:
   void CreateVideoDecodeAccelerator(
       arc::mojom::VideoDecodeAcceleratorRequest vda_request) override;
 
@@ -40,10 +40,10 @@
   // The objects these pointers refer to are owned by the GpuHost object.
   viz::mojom::GpuService* gpu_service_;
 
-  DISALLOW_COPY_AND_ASSIGN(ArcClient);
+  DISALLOW_COPY_AND_ASSIGN(ArcGpuClient);
 };
 
 }  // namespace gpu_host
 }  // namespace ws
 
-#endif  // SERVICES_WS_GPU_HOST_ARC_CLIENT_H_
+#endif  // SERVICES_WS_GPU_HOST_ARC_GPU_CLIENT_H_
diff --git a/services/ws/gpu_host/gpu_host.cc b/services/ws/gpu_host/gpu_host.cc
index 9824d2353..1c45147c3 100644
--- a/services/ws/gpu_host/gpu_host.cc
+++ b/services/ws/gpu_host/gpu_host.cc
@@ -39,7 +39,7 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-#include "services/ws/gpu_host/arc_client.h"
+#include "services/ws/gpu_host/arc_gpu_client.h"
 #endif
 
 #if defined(OS_CHROMEOS) && BUILDFLAG(USE_VAAPI)
@@ -192,9 +192,9 @@
 }
 
 #if defined(OS_CHROMEOS)
-void GpuHost::AddArc(mojom::ArcRequest request) {
-  arc_bindings_.AddBinding(
-      std::make_unique<ArcClient>(gpu_host_impl_->gpu_service()),
+void GpuHost::AddArcGpu(mojom::ArcGpuRequest request) {
+  arc_gpu_bindings_.AddBinding(
+      std::make_unique<ArcGpuClient>(gpu_host_impl_->gpu_service()),
       std::move(request));
 }
 #endif  // defined(OS_CHROMEOS)
diff --git a/services/ws/gpu_host/gpu_host.h b/services/ws/gpu_host/gpu_host.h
index b00ef82..38b523a 100644
--- a/services/ws/gpu_host/gpu_host.h
+++ b/services/ws/gpu_host/gpu_host.h
@@ -14,7 +14,7 @@
 #include "services/ws/public/mojom/gpu.mojom.h"
 
 #if defined(OS_CHROMEOS)
-#include "services/ws/public/mojom/arc.mojom.h"
+#include "services/ws/public/mojom/arc_gpu.mojom.h"
 #endif  // defined(OS_CHROMEOS)
 
 namespace base {
@@ -62,7 +62,7 @@
   void Add(mojom::GpuRequest request);
 
 #if defined(OS_CHROMEOS)
-  void AddArc(mojom::ArcRequest request);
+  void AddArcGpu(mojom::ArcGpuRequest request);
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(USE_OZONE)
@@ -127,7 +127,7 @@
   std::unique_ptr<viz::VizMainImpl> viz_main_impl_;
 
 #if defined(OS_CHROMEOS)
-  mojo::StrongBindingSet<mojom::Arc> arc_bindings_;
+  mojo::StrongBindingSet<mojom::ArcGpu> arc_gpu_bindings_;
 #endif  // defined(OS_CHROMEOS)
 
   DISALLOW_COPY_AND_ASSIGN(GpuHost);
diff --git a/services/ws/manifest.json b/services/ws/manifest.json
index 25e9f54..af91063 100644
--- a/services/ws/manifest.json
+++ b/services/ws/manifest.json
@@ -21,7 +21,7 @@
           "ws.mojom.WindowTreeFactory"
         ],
         "arc_manager" : [
-          "ws.mojom.Arc"
+          "ws.mojom.ArcGpu"
         ],
         // Interfaces provided by mus-gpu for mus-ws.
         "ozone": [
diff --git a/services/ws/public/mojom/BUILD.gn b/services/ws/public/mojom/BUILD.gn
index 598f501..31018146 100644
--- a/services/ws/public/mojom/BUILD.gn
+++ b/services/ws/public/mojom/BUILD.gn
@@ -41,7 +41,7 @@
   ]
 
   if (is_chromeos) {
-    sources += [ "arc.mojom" ]
+    sources += [ "arc_gpu.mojom" ]
     public_deps += [ "//components/arc/common:media" ]
   }
 }
diff --git a/services/ws/public/mojom/arc.mojom b/services/ws/public/mojom/arc_gpu.mojom
similarity index 97%
rename from services/ws/public/mojom/arc.mojom
rename to services/ws/public/mojom/arc_gpu.mojom
index 9cf166e..034dfc9 100644
--- a/services/ws/public/mojom/arc.mojom
+++ b/services/ws/public/mojom/arc_gpu.mojom
@@ -9,7 +9,7 @@
 import "components/arc/common/video_encode_accelerator.mojom";
 import "components/arc/common/video_protected_buffer_allocator.mojom";
 
-interface Arc {
+interface ArcGpu {
   // Create a new VideoDecodeAccelerator and binds it to |vda|.
   CreateVideoDecodeAccelerator(arc.mojom.VideoDecodeAccelerator& vda);
 
diff --git a/testing/BUILD.gn b/testing/BUILD.gn
index 8e5c39f8..fb17e192 100644
--- a/testing/BUILD.gn
+++ b/testing/BUILD.gn
@@ -30,9 +30,7 @@
 group("run_perf_test") {
   data = [
     "//testing/scripts/common.py",
-    "//testing/scripts/run_gtest_perf_test.py",
     "//testing/scripts/run_performance_tests.py",
-    "//testing/scripts/run_performance_tests_wrapper.py",
     "//testing/scripts/run_telemetry_benchmark_as_googletest.py",
     "//tools/perf/generate_legacy_perf_dashboard_json.py",
   ]
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 26e2015e..43e4103 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -8,8 +8,7 @@
         "args": [
           "--gtest-benchmark-name",
           "load_library_perf_tests",
-          "--non-telemetry=true",
-          "--migrated-test=true"
+          "--non-telemetry=true"
         ],
         "isolate_name": "load_library_perf_tests",
         "merge": {
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 13487c28..f2039308 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -128,13 +128,12 @@
     "args": [
       "angle_perftests",
       "--non-telemetry=true",
-      "--migrated-test=true",
       "--test-launcher-print-test-stdio=always",
       "--test-launcher-jobs=1",
       "--test-launcher-retry-limit=0",
     ],
     "label": "//chrome/test:angle_perftests",
-    "script": "//testing/scripts/run_performance_tests_wrapper.py",
+    "script": "//testing/scripts/run_performance_tests.py",
     "type": "script",
   },
   "angle_translator_fuzzer": {
@@ -254,13 +253,12 @@
     "args": [
       "base_perftests",
       "--non-telemetry=true",
-      "--migrated-test=true",
       "--test-launcher-print-test-stdio=always",
       "--test-launcher-jobs=1",
       "--test-launcher-retry-limit=0",
     ],
     "label": "//base:base_perftests",
-    "script": "//testing/scripts/run_performance_tests_wrapper.py",
+    "script": "//testing/scripts/run_performance_tests.py",
     "type": "script",
   },
   "base_unittests": {
@@ -624,12 +622,11 @@
     "args": [
       "command_buffer_perftests",
       "--non-telemetry=true",
-      "--migrated-test=true",
       "--adb-path",
       "src/third_party/android_tools/sdk/platform-tools/adb",
     ],
     "label": "//gpu:command_buffer_perftests",
-    "script": "//testing/scripts/run_performance_tests_wrapper.py",
+    "script": "//testing/scripts/run_performance_tests.py",
     "type": "script",
   },
   "compact_enc_det_fuzzer": {
@@ -656,11 +653,10 @@
     "args": [
       "--xvfb",
       "--non-telemetry=true",
-      "--migrated-test=true",
       "components_perftests",
     ],
     "label": "//components:components_perftests",
-    "script": "//testing/scripts/run_performance_tests_wrapper.py",
+    "script": "//testing/scripts/run_performance_tests.py",
     "type": "script",
   },
   "components_policy_junit_tests": {
@@ -1034,12 +1030,11 @@
     "args": [
       "gpu_perftests",
       "--non-telemetry=true",
-      "--migrated-test=true",
       "--adb-path",
       "src/third_party/android_tools/sdk/platform-tools/adb",
     ],
     "label": "//gpu:gpu_perftests",
-    "script": "//testing/scripts/run_performance_tests_wrapper.py",
+    "script": "//testing/scripts/run_performance_tests.py",
     "type": "script",
   },
   "gpu_raster_angle_fuzzer": {
@@ -1361,11 +1356,10 @@
     "args": [
       "load_library_perf_tests",
       "--non-telemetry=true",
-      "--migrated-test=true",
       "--test-launcher-print-test-stdio=always",
     ],
     "label": "//chrome/test:load_library_perf_tests",
-    "script": "//testing/scripts/run_performance_tests_wrapper.py",
+    "script": "//testing/scripts/run_performance_tests.py",
     "type": "script",
   },
   "lookup_affiliation_response_parser_fuzzer": {
@@ -1400,13 +1394,12 @@
     "args": [
       "media_perftests",
       "--non-telemetry=true",
-      "--migrated-test=true",
       "--single-process-tests",
       "--test-launcher-retry-limit=0",
       "--isolated-script-test-filter=*::-*_unoptimized::*_unaligned::*unoptimized_aligned",
     ],
     "label": "//media:media_perftests",
-    "script": "//testing/scripts/run_performance_tests_wrapper.py",
+    "script": "//testing/scripts/run_performance_tests.py",
     "type": "script",
   },
   "media_pipeline_integration_fuzzer": {
@@ -1801,10 +1794,9 @@
     "args": [
       "net_perftests",
       "--non-telemetry=true",
-      "--migrated-test=true",
     ],
     "label": "//net:net_perftests",
-    "script": "//testing/scripts/run_performance_tests_wrapper.py",
+    "script": "//testing/scripts/run_performance_tests.py",
     "type": "script",
   },
   "net_quic_crypto_framer_parse_message_fuzzer": {
@@ -2032,7 +2024,6 @@
     "args": [
       "performance_browser_tests",
       "--non-telemetry=true",
-      "--migrated-test=true",
       "--test-launcher-print-test-stdio=always",
       # TODO(crbug.com/759866): Figure out why CastV2PerformanceTest/0 sometimes
       # takes 15-30 seconds to start up and, once fixed, remove this workaround
@@ -2043,7 +2034,7 @@
       "--enable-gpu",
     ],
     "label": "//chrome/test:performance_browser_tests",
-    "script": "//testing/scripts/run_performance_tests_wrapper.py",
+    "script": "//testing/scripts/run_performance_tests.py",
     "type": "script",
   },
   "performance_test_suite": {
@@ -2450,13 +2441,12 @@
     "args": [
       "tracing_perftests",
       "--non-telemetry=true",
-      "--migrated-test=true",
       "--test-launcher-print-test-stdio=always",
       "--adb-path",
       "src/third_party/android_tools/sdk/platform-tools/adb",
     ],
     "label": "//components/tracing:tracing_perftests",
-    "script": "//testing/scripts/run_performance_tests_wrapper.py",
+    "script": "//testing/scripts/run_performance_tests.py",
     "type": "script",
   },
   "traffic_annotation_auditor_unittests": {
@@ -2592,11 +2582,10 @@
     "args": [
       "--xvfb",
       "--non-telemetry=true",
-      "--migrated-test=true",
       "views_perftests",
     ],
     "label": "//ui/views:views_perftests",
-    "script": "//testing/scripts/run_performance_tests_wrapper.py",
+    "script": "//testing/scripts/run_performance_tests.py",
     "type": "script",
   },
   "views_unittests": {
diff --git a/testing/scripts/run_gtest_perf_test.py b/testing/scripts/run_gtest_perf_test.py
deleted file mode 100755
index 83198f5c..0000000
--- a/testing/scripts/run_gtest_perf_test.py
+++ /dev/null
@@ -1,173 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Runs an isolated non-Telemetry perf test .
-
-The main contract is that the caller passes the arguments:
-
-  --isolated-script-test-output=[FILENAME]
-json is written to that file in the format produced by
-common.parse_common_test_results.
-
-  --isolated-script-test-chartjson-output=[FILE]
-stdout is written to this file containing chart results for the perf dashboard
-
-Optional argument:
-
-  --isolated-script-test-filter=[TEST_NAMES]
-
-is a double-colon-separated ("::") list of test names, to run just that subset
-of tests. This list is parsed by this harness and sent down via the
---gtest_filter argument.
-
-This script is intended to be the base command invoked by the isolate,
-followed by a subsequent non-python executable.  It is modeled after
-run_gpu_integration_test_as_gtest.py
-"""
-
-import argparse
-import json
-import os
-import shutil
-import sys
-import tempfile
-import traceback
-
-import common
-
-
-def GetChromiumSrcDir():
-  return os.path.abspath(
-      os.path.join(os.path.abspath(__file__), '..', '..', '..'))
-
-def GetPerfDir():
-  return os.path.join(GetChromiumSrcDir(), 'tools', 'perf')
-# Add src/tools/perf where generate_legacy_perf_dashboard_json.py lives
-sys.path.append(GetPerfDir())
-
-import generate_legacy_perf_dashboard_json
-
-# Add src/testing/ into sys.path for importing xvfb and test_env.
-sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-import xvfb
-import test_env
-
-# Unfortunately we need to copy these variables from ../test_env.py.
-# Importing it and using its get_sandbox_env breaks test runs on Linux
-# (it seems to unset DISPLAY).
-CHROME_SANDBOX_ENV = 'CHROME_DEVEL_SANDBOX'
-CHROME_SANDBOX_PATH = '/opt/chromium/chrome_sandbox'
-
-
-def IsWindows():
-  return sys.platform == 'cygwin' or sys.platform.startswith('win')
-
-
-def main():
-  parser = argparse.ArgumentParser()
-  parser.add_argument(
-      '--isolated-script-test-output', type=str,
-      required=True)
-  parser.add_argument(
-      '--isolated-script-test-chartjson-output', type=str,
-      required=False)
-  parser.add_argument(
-      '--isolated-script-test-perf-output', type=str,
-      required=False)
-  parser.add_argument(
-      '--isolated-script-test-filter', type=str, required=False)
-  parser.add_argument('--xvfb', help='Start xvfb.', action='store_true')
-
-  args, rest_args = parser.parse_known_args()
-
-  rc, charts, output_json = execute_perf_test(args, rest_args)
-
-  # TODO(eakuefner): Make isolated_script_test_perf_output mandatory after
-  # flipping flag in swarming.
-  if args.isolated_script_test_perf_output:
-    filename = args.isolated_script_test_perf_output
-  else:
-    filename = args.isolated_script_test_chartjson_output
-  # Write the returned encoded json to a the charts output file
-  with open(filename, 'w') as f:
-    f.write(charts)
-
-  with open(args.isolated_script_test_output, 'w') as fp:
-    json.dump(output_json, fp)
-
-  return rc
-
-
-def execute_perf_test(args, rest_args):
-  env = os.environ.copy()
-  # Assume we want to set up the sandbox environment variables all the
-  # time; doing so is harmless on non-Linux platforms and is needed
-  # all the time on Linux.
-  env[CHROME_SANDBOX_ENV] = CHROME_SANDBOX_PATH
-
-  rc = 0
-  try:
-    executable = rest_args[0]
-    extra_flags = []
-    if len(rest_args) > 1:
-      extra_flags = rest_args[1:]
-
-    # These flags are to make sure that test output perf metrics in the log.
-    if not '--verbose' in extra_flags:
-      extra_flags.append('--verbose')
-    if not '--test-launcher-print-test-stdio=always' in extra_flags:
-      extra_flags.append('--test-launcher-print-test-stdio=always')
-    if args.isolated_script_test_filter:
-      filter_list = common.extract_filter_list(
-        args.isolated_script_test_filter)
-      extra_flags.append('--gtest_filter=' + ':'.join(filter_list))
-
-    if IsWindows():
-      executable = '.\%s.exe' % executable
-    else:
-      executable = './%s' % executable
-    with common.temporary_file() as tempfile_path:
-      env['CHROME_HEADLESS'] = '1'
-      cmd = [executable] + extra_flags
-
-      if args.xvfb:
-        rc = xvfb.run_executable(cmd, env, stdoutfile=tempfile_path)
-      else:
-        rc = test_env.run_command_with_output(cmd, env=env,
-                                              stdoutfile=tempfile_path)
-
-      # Now get the correct json format from the stdout to write to the perf
-      # results file
-      results_processor = (
-          generate_legacy_perf_dashboard_json.LegacyResultsProcessor())
-      charts = results_processor.GenerateJsonResults(tempfile_path)
-  except Exception:
-    traceback.print_exc()
-    rc = 1
-
-  valid = (rc == 0)
-  failures = [] if valid else ['(entire test suite)']
-  output_json = {
-      'valid': valid,
-      'failures': failures,
-    }
-  return rc, charts, output_json
-
-# This is not really a "script test" so does not need to manually add
-# any additional compile targets.
-def main_compile_targets(args):
-  json.dump([], args.output)
-
-
-if __name__ == '__main__':
-  # Conform minimally to the protocol defined by ScriptTest.
-  if 'compile_targets' in sys.argv:
-    funcs = {
-      'run': None,
-      'compile_targets': main_compile_targets,
-    }
-    sys.exit(common.run_script(sys.argv[1:], funcs))
-  sys.exit(main())
-
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index 8ce37cb..61658f0 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -3,7 +3,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Runs several telemetry benchmarks.
+"""Runs telemetry benchmarks and gtest perf tests.
 
 This script attempts to emulate the contract of gtest-style tests
 invoked via recipes. The main contract is that the caller passes the
@@ -56,7 +56,25 @@
 import common
 
 import run_telemetry_benchmark_as_googletest
-import run_gtest_perf_test
+
+CHROMIUM_SRC_DIR = os.path.abspath(
+    os.path.join(os.path.dirname(__file__), '..', '..'))
+PERF_DIR = os.path.join(CHROMIUM_SRC_DIR, 'tools', 'perf')
+# Add src/tools/perf where generate_legacy_perf_dashboard_json.py lives
+sys.path.append(PERF_DIR)
+
+import generate_legacy_perf_dashboard_json
+
+# Add src/testing/ into sys.path for importing xvfb and test_env.
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+import xvfb
+import test_env
+
+# Unfortunately we need to copy these variables from ../test_env.py.
+# Importing it and using its get_sandbox_env breaks test runs on Linux
+# (it seems to unset DISPLAY).
+CHROME_SANDBOX_ENV = 'CHROME_DEVEL_SANDBOX'
+CHROME_SANDBOX_PATH = '/opt/chromium/chrome_sandbox'
 
 
 def get_sharding_map_path(args):
@@ -87,8 +105,68 @@
   print 'Duration of %s: %d seconds' % (step, time.time() - start)
 
 
-def execute_benchmark(benchmark, isolated_out_dir,
-                      args, rest_args, is_reference, stories=None):
+def IsWindows():
+  return sys.platform == 'cygwin' or sys.platform.startswith('win')
+
+
+def execute_gtest_perf_test(args, rest_args):
+  env = os.environ.copy()
+  # Assume we want to set up the sandbox environment variables all the
+  # time; doing so is harmless on non-Linux platforms and is needed
+  # all the time on Linux.
+  env[CHROME_SANDBOX_ENV] = CHROME_SANDBOX_PATH
+
+  rc = 0
+  try:
+    executable = rest_args[0]
+    extra_flags = []
+    if len(rest_args) > 1:
+      extra_flags = rest_args[1:]
+
+    # These flags are to make sure that test output perf metrics in the log.
+    if not '--verbose' in extra_flags:
+      extra_flags.append('--verbose')
+    if not '--test-launcher-print-test-stdio=always' in extra_flags:
+      extra_flags.append('--test-launcher-print-test-stdio=always')
+    if args.isolated_script_test_filter:
+      filter_list = common.extract_filter_list(
+        args.isolated_script_test_filter)
+      extra_flags.append('--gtest_filter=' + ':'.join(filter_list))
+
+    if IsWindows():
+      executable = '.\%s.exe' % executable
+    else:
+      executable = './%s' % executable
+    with common.temporary_file() as tempfile_path:
+      env['CHROME_HEADLESS'] = '1'
+      cmd = [executable] + extra_flags
+
+      if args.xvfb:
+        rc = xvfb.run_executable(cmd, env, stdoutfile=tempfile_path)
+      else:
+        rc = test_env.run_command_with_output(cmd, env=env,
+                                              stdoutfile=tempfile_path)
+
+      # Now get the correct json format from the stdout to write to the perf
+      # results file
+      results_processor = (
+          generate_legacy_perf_dashboard_json.LegacyResultsProcessor())
+      charts = results_processor.GenerateJsonResults(tempfile_path)
+  except Exception:
+    traceback.print_exc()
+    rc = 1
+
+  valid = (rc == 0)
+  failures = [] if valid else ['(entire test suite)']
+  output_json = {
+      'valid': valid,
+      'failures': failures,
+    }
+  return rc, charts, output_json
+
+
+def execute_telemetry_benchmark(
+    benchmark, isolated_out_dir, args, rest_args, is_reference, stories=None):
   start = time.time()
   # While we are between chartjson and histogram set we need
   # to determine which output format to look for or see if it was
@@ -160,6 +238,7 @@
     is_histograms = True
   return is_histograms
 
+
 def main():
   parser = argparse.ArgumentParser()
   parser.add_argument(
@@ -205,7 +284,7 @@
     # is converted to always pass --gtest-benchmark-name flag.
     if not benchmark_name:
       benchmark_name = rest_args[0]
-    return_code, charts, output_json = run_gtest_perf_test.execute_perf_test(
+    return_code, charts, output_json = execute_gtest_perf_test(
         args, rest_args)
 
     write_results(benchmark_name, charts, output_json,
@@ -217,7 +296,7 @@
     if args.benchmarks:
       benchmarks = args.benchmarks.split(',')
       for benchmark in benchmarks:
-        return_code = (execute_benchmark(
+        return_code = (execute_telemetry_benchmark(
             benchmark, isolated_out_dir, args, rest_args, False) or return_code)
     else:
       # First determine what shard we are running on to know how to
@@ -248,14 +327,15 @@
 
       for benchmark, stories in sharding.iteritems():
         # Need to run the benchmark twice on browser and reference build
-        return_code = (execute_benchmark(
+        return_code = (execute_telemetry_benchmark(
             benchmark, isolated_out_dir, args, rest_args,
             False, stories=stories) or return_code)
         # We ignore the return code of the reference build since we do not
         # monitor it.
         if args.run_ref_build:
-          execute_benchmark(benchmark, isolated_out_dir, args, rest_args, True,
-                            stories=stories)
+          execute_telemetry_benchmark(
+              benchmark, isolated_out_dir, args, rest_args, True,
+              stories=stories)
 
   return return_code
 
diff --git a/testing/scripts/run_performance_tests_wrapper.py b/testing/scripts/run_performance_tests_wrapper.py
deleted file mode 100755
index 4924fb6..0000000
--- a/testing/scripts/run_performance_tests_wrapper.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-"""This script is a wrapper script used during migration of performance
-tests to use the new recipe.  See crbug.com/757933
-
-Non-telemetry tests now will all run with this script.  The flag
---migrated-test will indicate if this test is using the new recipe or not.
-By default this script runs the legacy testing/scripts/run_gtest_perf_test.py.
-
-"""
-
-import argparse
-import os
-import subprocess
-import sys
-
-SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(
-  __file__))))
-
-GTEST = os.path.join(SRC_DIR, 'testing', 'scripts', 'run_gtest_perf_test.py')
-PERF = os.path.join(SRC_DIR, 'testing', 'scripts', 'run_performance_tests.py')
-
-
-def main():
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--migrated-test', type=bool, default=False)
-
-  args, rest_args = parser.parse_known_args()
-
-  if args.migrated_test:
-    return subprocess.call([sys.executable, PERF] + rest_args)
-  else:
-    return subprocess.call([sys.executable, GTEST] + rest_args)
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 4cbe6378..91e3cbb 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -114,7 +114,7 @@
 // WebURLLoaderClient::DidStartLoadingResponseBody() instead of
 // WebURLLoaderClient::DidReceiveData().
 const base::Feature kResourceLoadViaDataPipe{"ResourceLoadViaDataPipe",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kServiceWorkerImportedScriptUpdateCheck{
     "ServiceWorkerImportedScriptUpdateCheck",
@@ -186,5 +186,9 @@
 const base::Feature kAlwaysAccelerateCanvas{"AlwaysAccelerateCanvas",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables cache-aware WebFonts loading. See https://crbug.com/570205.
+const base::Feature kWebFontsCacheAwareTimeoutAdaption{
+    "WebFontsCacheAwareTimeoutAdaption", base::FEATURE_ENABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace blink
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 466f90a..090989b1 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -65,6 +65,7 @@
     "manifest/manifest.h",
     "manifest/manifest_icon_selector.h",
     "manifest/web_display_mode.h",
+    "media/video_capture.h",
     "mediastream/media_devices.h",
     "mediastream/media_devices_mojom_traits.h",
     "mediastream/media_stream_controls.h",
@@ -114,6 +115,7 @@
     deps += [
       "//media",
       "//media/capture:capture_base",
+      "//media/capture:capture_lib",
     ]
   }
 
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 7d133a1..3b4e1a7 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -62,6 +62,9 @@
 BLINK_COMMON_EXPORT extern const base::Feature kDecodeLossyWebPImagesToYUV;
 BLINK_COMMON_EXPORT extern const base::Feature kAlwaysAccelerateCanvas;
 
+BLINK_COMMON_EXPORT extern const base::Feature
+    kWebFontsCacheAwareTimeoutAdaption;
+
 }  // namespace features
 }  // namespace blink
 
diff --git a/content/common/media/video_capture.h b/third_party/blink/public/common/media/video_capture.h
similarity index 83%
rename from content/common/media/video_capture.h
rename to third_party/blink/public/common/media/video_capture.h
index 3605baf..1308f67 100644
--- a/content/common/media/video_capture.h
+++ b/third_party/blink/public/common/media/video_capture.h
@@ -4,14 +4,14 @@
 //
 // This file contains commonly used definitions of video capture.
 
-#ifndef CONTENT_COMMON_MEDIA_VIDEO_CAPTURE_H_
-#define CONTENT_COMMON_MEDIA_VIDEO_CAPTURE_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_MEDIA_VIDEO_CAPTURE_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_MEDIA_VIDEO_CAPTURE_H_
 
 #include "base/callback.h"
 #include "media/capture/video_capture_types.h"
 #include "media/capture/video_capturer_source.h"
 
-namespace content {
+namespace blink {
 
 using VideoCaptureDeviceFormatsCB =
     media::VideoCapturerSource::VideoCaptureDeviceFormatsCB;
@@ -36,6 +36,6 @@
 
 using VideoCaptureStateUpdateCB = base::Callback<void(VideoCaptureState)>;
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_COMMON_MEDIA_VIDEO_CAPTURE_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_MEDIA_VIDEO_CAPTURE_H_
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_container.mojom b/third_party/blink/public/mojom/service_worker/service_worker_container.mojom
index e625c65..af7fec7 100644
--- a/third_party/blink/public/mojom/service_worker/service_worker_container.mojom
+++ b/third_party/blink/public/mojom/service_worker/service_worker_container.mojom
@@ -105,6 +105,10 @@
   // The browser process can possibly coalesce hints for the same service
   // worker into a single update.
   HintToUpdateServiceWorker();
+
+  // Informs the host that the context is execution ready.
+  // https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-execution-ready-flag
+  OnExecutionReady();
 };
 
 // ServiceWorkerContainer is an interface implemented by the renderer process.
diff --git a/third_party/blink/public/platform/modules/app_banner/app_banner.mojom b/third_party/blink/public/platform/modules/app_banner/app_banner.mojom
index df58e11..9ae94fe 100644
--- a/third_party/blink/public/platform/modules/app_banner/app_banner.mojom
+++ b/third_party/blink/public/platform/modules/app_banner/app_banner.mojom
@@ -13,7 +13,7 @@
   // The browser asks the renderer if the app banner should be shown.
   BannerPromptRequest(AppBannerService service, AppBannerEvent& event,
                       array<string> platform, bool require_gesture) =>
-      (AppBannerPromptReply reply, string referrer);
+      (AppBannerPromptReply reply);
 };
 
 interface AppBannerEvent {
diff --git a/third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h b/third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h
index b6bf3a3..92a2324 100644
--- a/third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h
+++ b/third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h
@@ -53,8 +53,8 @@
   virtual void SetController(WebServiceWorkerObjectInfo,
                              bool should_notify_controller_change) = 0;
 
-  virtual void DispatchMessageEvent(WebServiceWorkerObjectInfo,
-                                    TransferableMessage) = 0;
+  virtual void ReceiveMessage(WebServiceWorkerObjectInfo source,
+                              TransferableMessage) = 0;
   virtual void CountFeature(mojom::WebFeature) = 0;
 };
 
diff --git a/third_party/blink/public/platform/web_feature.mojom b/third_party/blink/public/platform/web_feature.mojom
index 12a09b0..99530dd 100644
--- a/third_party/blink/public/platform/web_feature.mojom
+++ b/third_party/blink/public/platform/web_feature.mojom
@@ -2202,6 +2202,9 @@
   kV8RTCQuicStream_WaitForWriteBufferedAmountBelow_Method = 2767,
   kV8RTCQuicStream_WaitForReadable_Method = 2768,
   kHTMLTemplateElement = 2769,
+  kNoSysexWebMIDIWithoutPermission = 2770,
+  kNoSysexWebMIDIOnInsecureOrigin = 2771,
+  kApplicationCacheInstalledButNoManifest = 2772,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/core/css/css_font_face_src_value.cc b/third_party/blink/renderer/core/css/css_font_face_src_value.cc
index c1843a3..3db4ccb 100644
--- a/third_party/blink/renderer/core/css/css_font_face_src_value.cc
+++ b/third_party/blink/renderer/core/css/css_font_face_src_value.cc
@@ -25,6 +25,8 @@
 
 #include "third_party/blink/renderer/core/css/css_font_face_src_value.h"
 
+#include "base/feature_list.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/renderer/core/css/css_markup.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
@@ -88,8 +90,10 @@
     ResourceLoaderOptions options;
     options.initiator_info.name = fetch_initiator_type_names::kCSS;
     FetchParameters params(resource_request, options);
-    if (RuntimeEnabledFeatures::WebFontsCacheAwareTimeoutAdaptationEnabled())
+    if (base::FeatureList::IsEnabled(
+            features::kWebFontsCacheAwareTimeoutAdaption)) {
       params.SetCacheAwareLoadingEnabled(kIsCacheAwareLoadingEnabled);
+    }
     params.SetContentSecurityCheck(should_check_content_security_policy_);
     const SecurityOrigin* security_origin = context->GetSecurityOrigin();
 
diff --git a/third_party/blink/renderer/core/editing/layout_selection.cc b/third_party/blink/renderer/core/editing/layout_selection.cc
index 4c381d95d..ea96ebb 100644
--- a/third_party/blink/renderer/core/editing/layout_selection.cc
+++ b/third_party/blink/renderer/core/editing/layout_selection.cc
@@ -649,6 +649,9 @@
 
 LayoutTextSelectionStatus LayoutSelection::ComputeSelectionStatus(
     const LayoutText& layout_text) const {
+  Document& document = frame_selection_->GetDocument();
+  DCHECK_GE(document.Lifecycle().GetState(), DocumentLifecycle::kLayoutClean);
+  DCHECK(!document.IsSlotAssignmentOrLegacyDistributionDirty());
   DCHECK(!has_pending_selection_);
   const SelectionState selection_state = GetSelectionStateFor(layout_text);
   if (selection_state == SelectionState::kNone)
@@ -683,6 +686,9 @@
 // LayoutText and not of each NGPhysicalTextFragment for it.
 LayoutSelectionStatus LayoutSelection::ComputeSelectionStatus(
     const NGPaintFragment& fragment) const {
+  Document& document = frame_selection_->GetDocument();
+  DCHECK_GE(document.Lifecycle().GetState(), DocumentLifecycle::kLayoutClean);
+  DCHECK(!document.IsSlotAssignmentOrLegacyDistributionDirty());
   const NGPhysicalTextFragment& text_fragment =
       ToNGPhysicalTextFragmentOrDie(fragment.PhysicalFragment());
   // We don't paint selection on ellipsis.
diff --git a/third_party/blink/renderer/core/editing/layout_selection_test.cc b/third_party/blink/renderer/core/editing/layout_selection_test.cc
index 120eb68..8b058d1 100644
--- a/third_party/blink/renderer/core/editing/layout_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/layout_selection_test.cc
@@ -777,11 +777,13 @@
 
   Node* baz = GetDocument().body()->lastChild();
   baz->remove();
+  GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
+  Selection().CommitAppearanceIfNeeded();
   EXPECT_EQ(
       "BODY, Contain, NotInvalidate \n"
       "  'foo', Start(0,3), ShouldInvalidate \n"
       "  SPAN, Contain, NotInvalidate \n"
-      "    'bar', Inside(0,3), ShouldInvalidate ",
+      "    'bar', End(0,3), ShouldInvalidate ",
       DumpSelectionInfo());
 
   UpdateAllLifecyclePhasesForTest();
@@ -808,12 +810,13 @@
 
   Element* span_baz = ToElement(GetDocument().body()->lastChild());
   span_baz->SetInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
-  GetDocument().UpdateStyleAndLayoutTreeIgnorePendingStylesheets();
+  GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
+  Selection().CommitAppearanceIfNeeded();
   EXPECT_EQ(
       "BODY, Contain, NotInvalidate \n"
       "  'foo', Start(0,3), ShouldInvalidate \n"
       "  SPAN, Contain, NotInvalidate \n"
-      "    'bar', Inside(0,3), ShouldInvalidate \n"
+      "    'bar', End(0,3), ShouldInvalidate \n"
       "  SPAN, <null LayoutObject> \n"
       "    'baz', <null LayoutObject> ",
       DumpSelectionInfo());
@@ -851,12 +854,13 @@
       GetDocument().body()->firstChild()->GetShadowRoot()->QuerySelector(
           "slot");
   slot->setAttribute("name", "s2");
-  GetDocument().UpdateStyleAndLayoutTreeIgnorePendingStylesheets();
+  GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
+  Selection().CommitAppearanceIfNeeded();
   EXPECT_EQ(
       "BODY, Contain, NotInvalidate \n"
       "  DIV, Contain, NotInvalidate \n"
       "    #shadow-root \n"
-      "      'Foo', Start(0,3), ShouldInvalidate \n"
+      "      'Foo', StartAndEnd(0,3), ShouldInvalidate \n"
       "      SLOT, <null LayoutObject> \n"
       "    'baz', <null LayoutObject> \n"
       "    SPAN, <null LayoutObject> \n"
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
index ba64ed8..a9cbfadf 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
@@ -98,6 +98,9 @@
   asked_to_terminate_ = true;
   if (shadow_page_ && !shadow_page_->WasInitialized()) {
     client_->WorkerScriptLoadFailed();
+    // The worker thread hasn't been started yet. Immediately notify the client
+    // of worker termination.
+    client_->WorkerContextDestroyed();
     // |this| is deleted at this point.
     return;
   }
@@ -105,6 +108,9 @@
     main_script_loader_->Cancel();
     main_script_loader_ = nullptr;
     client_->WorkerScriptLoadFailed();
+    // The worker thread hasn't been started yet. Immediately notify the client
+    // of worker termination.
+    client_->WorkerContextDestroyed();
     // |this| is deleted at this point.
     return;
   }
@@ -166,6 +172,13 @@
   client_->WorkerScriptLoaded();
 }
 
+void WebSharedWorkerImpl::DidFailToFetchClassicScript() {
+  DCHECK(IsMainThread());
+  client_->WorkerScriptLoadFailed();
+  TerminateWorkerThread();
+  // DidTerminateWorkerThread() will be called asynchronously.
+}
+
 void WebSharedWorkerImpl::DidEvaluateClassicScript(bool success) {
   DCHECK(IsMainThread());
   client_->WorkerScriptEvaluated(success);
@@ -258,7 +271,11 @@
     return;
   if (main_script_loader_->Failed()) {
     main_script_loader_->Cancel();
+    main_script_loader_ = nullptr;
     client_->WorkerScriptLoadFailed();
+    // The worker thread hasn't been started yet. Immediately notify the client
+    // of worker termination.
+    client_->WorkerContextDestroyed();
     // |this| is deleted at this point.
     return;
   }
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
index 15400e25..30036f8 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
@@ -110,6 +110,7 @@
   // Callback methods for SharedWorkerReportingProxy.
   void CountFeature(WebFeature);
   void DidFetchScript();
+  void DidFailToFetchClassicScript();
   void DidEvaluateClassicScript(bool success);
   void DidCloseWorkerGlobalScope();
   void DidTerminateWorkerThread();
diff --git a/third_party/blink/renderer/core/fetch/data_consumer_handle_test_util.cc b/third_party/blink/renderer/core/fetch/data_consumer_handle_test_util.cc
index d89898bb..636dbf9 100644
--- a/third_party/blink/renderer/core/fetch/data_consumer_handle_test_util.cc
+++ b/third_party/blink/renderer/core/fetch/data_consumer_handle_test_util.cc
@@ -12,90 +12,6 @@
 
 namespace blink {
 
-namespace {
-
-class WaitingHandle final : public WebDataConsumerHandle {
- private:
-  class ReaderImpl final : public WebDataConsumerHandle::Reader {
-   public:
-    WebDataConsumerHandle::Result BeginRead(const void** buffer,
-                                            WebDataConsumerHandle::Flags,
-                                            size_t* available) override {
-      *available = 0;
-      *buffer = nullptr;
-      return kShouldWait;
-    }
-    WebDataConsumerHandle::Result EndRead(size_t) override {
-      return kUnexpectedError;
-    }
-  };
-  std::unique_ptr<Reader> ObtainReader(
-      Client*,
-      scoped_refptr<base::SingleThreadTaskRunner>) override {
-    return std::make_unique<ReaderImpl>();
-  }
-
-  const char* DebugName() const override { return "WaitingHandle"; }
-};
-
-}  // namespace
-
-DataConsumerHandleTestUtil::Thread::Thread(
-    const ThreadCreationParams& params,
-    InitializationPolicy initialization_policy)
-    : thread_(WebThreadSupportingGC::Create(params)),
-      initialization_policy_(initialization_policy),
-      waitable_event_(std::make_unique<WaitableEvent>()) {
-  thread_->PostTask(FROM_HERE, CrossThreadBind(&Thread::Initialize,
-                                               CrossThreadUnretained(this)));
-  waitable_event_->Wait();
-}
-
-DataConsumerHandleTestUtil::Thread::~Thread() {
-  thread_->PostTask(FROM_HERE, CrossThreadBind(&Thread::Shutdown,
-                                               CrossThreadUnretained(this)));
-  waitable_event_->Wait();
-}
-
-void DataConsumerHandleTestUtil::Thread::Initialize() {
-  DCHECK(thread_->IsCurrentThread());
-  if (initialization_policy_ >= kScriptExecution) {
-    isolate_holder_ = std::make_unique<gin::IsolateHolder>(
-        scheduler::GetSingleThreadTaskRunnerForTesting(),
-        gin::IsolateHolder::IsolateType::kTest);
-    GetIsolate()->Enter();
-  }
-  thread_->InitializeOnThread();
-  if (initialization_policy_ >= kScriptExecution) {
-    v8::HandleScope handle_scope(GetIsolate());
-    script_state_ = ScriptState::Create(
-        v8::Context::New(GetIsolate()),
-        DOMWrapperWorld::Create(GetIsolate(),
-                                DOMWrapperWorld::WorldType::kTesting));
-  }
-  if (initialization_policy_ >= kWithExecutionContext) {
-    execution_context_ = MakeGarbageCollected<NullExecutionContext>();
-  }
-  waitable_event_->Signal();
-}
-
-void DataConsumerHandleTestUtil::Thread::Shutdown() {
-  DCHECK(thread_->IsCurrentThread());
-  execution_context_ = nullptr;
-  if (script_state_) {
-    script_state_->DisposePerContextData();
-  }
-  script_state_ = nullptr;
-  thread_->ShutdownOnThread();
-  if (isolate_holder_) {
-    GetIsolate()->Exit();
-    GetIsolate()->RequestGarbageCollectionForTesting(
-        GetIsolate()->kFullGarbageCollection);
-    isolate_holder_ = nullptr;
-  }
-  waitable_event_->Signal();
-}
-
 class DataConsumerHandleTestUtil::ReplayingHandle::ReaderImpl final
     : public Reader {
  public:
@@ -264,9 +180,4 @@
   context_->Add(command);
 }
 
-std::unique_ptr<WebDataConsumerHandle>
-DataConsumerHandleTestUtil::CreateWaitingDataConsumerHandle() {
-  return std::make_unique<WaitingHandle>();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/fetch/data_consumer_handle_test_util.h b/third_party/blink/renderer/core/fetch/data_consumer_handle_test_util.h
index 7e68118..777b4a50 100644
--- a/third_party/blink/renderer/core/fetch/data_consumer_handle_test_util.h
+++ b/third_party/blink/renderer/core/fetch/data_consumer_handle_test_util.h
@@ -34,306 +34,6 @@
   STATIC_ONLY(DataConsumerHandleTestUtil);
 
  public:
-  class NoopClient final : public WebDataConsumerHandle::Client {
-    DISALLOW_NEW();
-
-   public:
-    void DidGetReadable() override {}
-  };
-
-  // Thread has a WebThreadSupportingGC. It initializes / shutdowns
-  // additional objects based on the given policy. The constructor and the
-  // destructor blocks during the setup and the teardown.
-  class Thread final {
-    USING_FAST_MALLOC(Thread);
-
-   public:
-    // Initialization policy of a thread.
-    enum InitializationPolicy {
-      // Only garbage collection is supported.
-      kGarbageCollection,
-      // Creating an isolate in addition to GarbageCollection.
-      kScriptExecution,
-      // Creating an execution context in addition to ScriptExecution.
-      kWithExecutionContext,
-    };
-
-    Thread(const ThreadCreationParams&,
-           InitializationPolicy = kGarbageCollection);
-    ~Thread();
-
-    WebThreadSupportingGC* GetThread() { return thread_.get(); }
-    ExecutionContext* GetExecutionContext() { return execution_context_.Get(); }
-    ScriptState* GetScriptState() { return script_state_; }
-    v8::Isolate* GetIsolate() { return isolate_holder_->isolate(); }
-
-   private:
-    void Initialize();
-    void Shutdown();
-
-    std::unique_ptr<WebThreadSupportingGC> thread_;
-    const InitializationPolicy initialization_policy_;
-    std::unique_ptr<WaitableEvent> waitable_event_;
-    Persistent<NullExecutionContext> execution_context_;
-    std::unique_ptr<gin::IsolateHolder> isolate_holder_;
-    Persistent<ScriptState> script_state_;
-  };
-
-  class ThreadingTestBase : public ThreadSafeRefCounted<ThreadingTestBase> {
-   public:
-    virtual ~ThreadingTestBase() {}
-
-    class ThreadHolder;
-
-    class Context : public ThreadSafeRefCounted<Context> {
-     public:
-      static scoped_refptr<Context> Create() {
-        return base::AdoptRef(new Context);
-      }
-      void RecordAttach(const String& handle) {
-        MutexLocker locker(logging_mutex_);
-        result_.Append("A reader is attached to ");
-        result_.Append(handle);
-        result_.Append(" on ");
-        result_.Append(CurrentThreadName());
-        result_.Append(".\n");
-      }
-      void RecordDetach(const String& handle) {
-        MutexLocker locker(logging_mutex_);
-        result_.Append("A reader is detached from ");
-        result_.Append(handle);
-        result_.Append(" on ");
-        result_.Append(CurrentThreadName());
-        result_.Append(".\n");
-      }
-
-      String Result() {
-        MutexLocker locker(logging_mutex_);
-        return result_.ToString();
-      }
-
-      void RegisterThreadHolder(ThreadHolder* holder) {
-        MutexLocker locker(holder_mutex_);
-        DCHECK(!holder_);
-        holder_ = holder;
-      }
-      void UnregisterThreadHolder() {
-        MutexLocker locker(holder_mutex_);
-        DCHECK(holder_);
-        holder_ = nullptr;
-      }
-      void PostTaskToReadingThread(const base::Location& location,
-                                   CrossThreadClosure task) {
-        MutexLocker locker(holder_mutex_);
-        DCHECK(holder_);
-        holder_->ReadingThread()->PostTask(location, std::move(task));
-      }
-      void PostTaskToUpdatingThread(const base::Location& location,
-                                    CrossThreadClosure task) {
-        MutexLocker locker(holder_mutex_);
-        DCHECK(holder_);
-        holder_->UpdatingThread()->PostTask(location, std::move(task));
-      }
-
-     private:
-      Context() : holder_(nullptr) {}
-      String CurrentThreadName() {
-        MutexLocker locker(holder_mutex_);
-        if (holder_) {
-          if (holder_->ReadingThread()->IsCurrentThread())
-            return "the reading thread";
-          if (holder_->UpdatingThread()->IsCurrentThread())
-            return "the updating thread";
-        }
-        return "an unknown thread";
-      }
-
-      // Protects |m_result|.
-      Mutex logging_mutex_;
-      StringBuilder result_;
-
-      // Protects |m_holder|.
-      Mutex holder_mutex_;
-      // Because Context outlives ThreadHolder, holding a raw pointer
-      // here is safe.
-      ThreadHolder* holder_;
-    };
-
-    // The reading/updating threads are alive while ThreadHolder is alive.
-    class ThreadHolder {
-      DISALLOW_NEW();
-
-     public:
-      ThreadHolder(ThreadingTestBase* test)
-          : context_(test->context_),
-            reading_thread_(std::make_unique<Thread>(
-                ThreadCreationParams(WebThreadType::kTestThread)
-                    .SetThreadNameForTest("reading thread"))),
-            updating_thread_(std::make_unique<Thread>(
-                ThreadCreationParams(WebThreadType::kTestThread)
-                    .SetThreadNameForTest("updating thread"))) {
-        context_->RegisterThreadHolder(this);
-      }
-      ~ThreadHolder() { context_->UnregisterThreadHolder(); }
-
-      WebThreadSupportingGC* ReadingThread() {
-        return reading_thread_->GetThread();
-      }
-      WebThreadSupportingGC* UpdatingThread() {
-        return updating_thread_->GetThread();
-      }
-
-     private:
-      scoped_refptr<Context> context_;
-      std::unique_ptr<Thread> reading_thread_;
-      std::unique_ptr<Thread> updating_thread_;
-    };
-
-    class ReaderImpl final : public WebDataConsumerHandle::Reader {
-      USING_FAST_MALLOC(ReaderImpl);
-
-     public:
-      ReaderImpl(const String& name, scoped_refptr<Context> context)
-          : name_(name.IsolatedCopy()), context_(std::move(context)) {
-        context_->RecordAttach(name_.IsolatedCopy());
-      }
-      ~ReaderImpl() override { context_->RecordDetach(name_.IsolatedCopy()); }
-
-      using Result = WebDataConsumerHandle::Result;
-      using Flags = WebDataConsumerHandle::Flags;
-      Result BeginRead(const void**, Flags, size_t*) override {
-        return WebDataConsumerHandle::kShouldWait;
-      }
-      Result EndRead(size_t) override {
-        return WebDataConsumerHandle::kUnexpectedError;
-      }
-
-     private:
-      const String name_;
-      scoped_refptr<Context> context_;
-    };
-    class DataConsumerHandle final : public WebDataConsumerHandle {
-      USING_FAST_MALLOC(DataConsumerHandle);
-
-     public:
-      static std::unique_ptr<WebDataConsumerHandle> Create(
-          const String& name,
-          scoped_refptr<Context> context) {
-        return base::WrapUnique(
-            new DataConsumerHandle(name, std::move(context)));
-      }
-
-     private:
-      DataConsumerHandle(const String& name, scoped_refptr<Context> context)
-          : name_(name.IsolatedCopy()), context_(std::move(context)) {}
-
-      std::unique_ptr<Reader> ObtainReader(
-          Client*,
-          scoped_refptr<base::SingleThreadTaskRunner> task_runner) override {
-        return std::make_unique<ReaderImpl>(name_, context_);
-      }
-      const char* DebugName() const override {
-        return "ThreadingTestBase::DataConsumerHandle";
-      }
-
-      const String name_;
-      scoped_refptr<Context> context_;
-    };
-
-    void ResetReader() { reader_ = nullptr; }
-    void SignalDone() { waitable_event_->Signal(); }
-    String Result() { return context_->Result(); }
-    void PostTaskToReadingThread(const base::Location& location,
-                                 CrossThreadClosure task) {
-      context_->PostTaskToReadingThread(location, std::move(task));
-    }
-    void PostTaskToUpdatingThread(const base::Location& location,
-                                  CrossThreadClosure task) {
-      context_->PostTaskToUpdatingThread(location, std::move(task));
-    }
-    void PostTaskToReadingThreadAndWait(const base::Location& location,
-                                        CrossThreadClosure task) {
-      PostTaskToReadingThread(location, std::move(task));
-      waitable_event_->Wait();
-    }
-    void PostTaskToUpdatingThreadAndWait(const base::Location& location,
-                                         CrossThreadClosure task) {
-      PostTaskToUpdatingThread(location, std::move(task));
-      waitable_event_->Wait();
-    }
-
-   protected:
-    ThreadingTestBase() : context_(Context::Create()) {}
-
-    scoped_refptr<Context> context_;
-    std::unique_ptr<WebDataConsumerHandle::Reader> reader_;
-    std::unique_ptr<WaitableEvent> waitable_event_;
-    NoopClient client_;
-  };
-
-  class ThreadingHandleNotificationTest : public ThreadingTestBase,
-                                          public WebDataConsumerHandle::Client {
-   public:
-    using Self = ThreadingHandleNotificationTest;
-    static scoped_refptr<Self> Create() { return base::AdoptRef(new Self); }
-
-    void Run(std::unique_ptr<WebDataConsumerHandle> handle) {
-      ThreadHolder holder(this);
-      waitable_event_ = std::make_unique<WaitableEvent>();
-      handle_ = std::move(handle);
-
-      PostTaskToReadingThreadAndWait(
-          FROM_HERE,
-          CrossThreadBind(&Self::ObtainReader, WrapRefCounted(this)));
-    }
-
-   private:
-    ThreadingHandleNotificationTest() = default;
-    void ObtainReader() {
-      reader_ = handle_->ObtainReader(
-          this, scheduler::GetSingleThreadTaskRunnerForTesting());
-    }
-    void DidGetReadable() override {
-      PostTaskToReadingThread(
-          FROM_HERE, CrossThreadBind(&Self::ResetReader, WrapRefCounted(this)));
-      PostTaskToReadingThread(
-          FROM_HERE, CrossThreadBind(&Self::SignalDone, WrapRefCounted(this)));
-    }
-
-    std::unique_ptr<WebDataConsumerHandle> handle_;
-  };
-
-  class ThreadingHandleNoNotificationTest
-      : public ThreadingTestBase,
-        public WebDataConsumerHandle::Client {
-   public:
-    using Self = ThreadingHandleNoNotificationTest;
-    static scoped_refptr<Self> Create() { return base::AdoptRef(new Self); }
-
-    void Run(std::unique_ptr<WebDataConsumerHandle> handle) {
-      ThreadHolder holder(this);
-      waitable_event_ = std::make_unique<WaitableEvent>();
-      handle_ = std::move(handle);
-
-      PostTaskToReadingThreadAndWait(
-          FROM_HERE,
-          CrossThreadBind(&Self::ObtainReader, WrapRefCounted(this)));
-    }
-
-   private:
-    ThreadingHandleNoNotificationTest() = default;
-    void ObtainReader() {
-      reader_ = handle_->ObtainReader(
-          this, scheduler::GetSingleThreadTaskRunnerForTesting());
-      reader_ = nullptr;
-      PostTaskToReadingThread(
-          FROM_HERE, CrossThreadBind(&Self::SignalDone, WrapRefCounted(this)));
-    }
-    void DidGetReadable() override { NOTREACHED(); }
-
-    std::unique_ptr<WebDataConsumerHandle> handle_;
-  };
-
   class Command final {
     DISALLOW_NEW();
 
@@ -423,9 +123,6 @@
 
     scoped_refptr<Context> context_;
   };
-
-  static std::unique_ptr<WebDataConsumerHandle>
-  CreateWaitingDataConsumerHandle();
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/deprecation.cc b/third_party/blink/renderer/core/frame/deprecation.cc
index b3e2678..ee097e4 100644
--- a/third_party/blink/renderer/core/frame/deprecation.cc
+++ b/third_party/blink/renderer/core/frame/deprecation.cc
@@ -56,6 +56,8 @@
   kM71,
   kM72,
   kM73,
+  kM74,
+  kM75,
 };
 
 // Returns estimated milestone dates as human-readable strings.
@@ -94,6 +96,10 @@
       return "M72, around January 2019";
     case kM73:
       return "M73, around March 2019";
+    case kM74:
+      return "M74, around April 2019";
+    case kM75:
+      return "M75, around June 2019";
   }
 
   NOTREACHED();
@@ -137,6 +143,10 @@
       return 1548734400000;  // January 29, 2019.
     case kM73:
       return 1552363200000;  // March 12, 2019.
+    case kM74:
+      return 1555992000000;  // April 23, 2019.
+    case kM75:
+      return 1559620800000;  // June 4, 2019.
   }
 
   NOTREACHED();
@@ -565,6 +575,25 @@
                   "https://webrtc.org/web-apis/chrome/unified-plan/.",
                   MilestoneString(kM72))};
 
+    case WebFeature::kNoSysexWebMIDIWithoutPermission:
+      return {"NoSysexWebMIDIWithoutPermission", kM75,
+              String::Format(
+                  "Web MIDI will ask a permission to use even if the sysex is "
+                  "not specified in the MIDIOptions since %s. See "
+                  "https://www.chromestatus.com/feature/5138066234671104 for "
+                  "more details.",
+                  MilestoneString(kM75))};
+
+    case WebFeature::kNoSysexWebMIDIOnInsecureOrigin:
+      return {"NoSysexWebMIDIOnInsecureOrigin", kM75,
+              String::Format(
+                  "Web MIDI will be deprecated on insecure origins since %s. "
+                  "You should consider switching your application to a secure "
+                  "origin, such as HTTPS. See "
+                  "https://www.chromestatus.com/feature/5138066234671104 for "
+                  "more details.",
+                  MilestoneString(kM75))};
+
     // Features that aren't deprecated don't have a deprecation message.
     default:
       return {"NotDeprecated", kUnknown, ""};
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 53b5e89..d1b9cc51 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2578,7 +2578,6 @@
 
       frame_->GetPage()->GetValidationMessageClient().PaintOverlay(
           graphics_context);
-      frame_->PaintFrameColorOverlay(graphics_context);
       ForAllNonThrottledLocalFrameViews(
           [&graphics_context](LocalFrameView& view) {
             view.frame_->PaintFrameColorOverlay(graphics_context);
@@ -2627,7 +2626,6 @@
     frame_->GetPage()->GetLinkHighlights().UpdateGeometry();
 
     frame_->GetPage()->GetValidationMessageClient().PaintOverlay();
-    frame_->PaintFrameColorOverlay();
     ForAllNonThrottledLocalFrameViews(
         [](LocalFrameView& view) { view.frame_->PaintFrameColorOverlay(); });
 
diff --git a/third_party/blink/renderer/core/html/html_html_element.cc b/third_party/blink/renderer/core/html/html_html_element.cc
index 6188335..7cc4857 100644
--- a/third_party/blink/renderer/core/html/html_html_element.cc
+++ b/third_party/blink/renderer/core/html/html_html_element.cc
@@ -84,11 +84,20 @@
     return;
   }
 
+  ApplicationCacheHost* host = document_loader->GetApplicationCacheHost();
+  DCHECK(host);
+
   if (manifest.IsEmpty())
-    document_loader->GetApplicationCacheHost()->SelectCacheWithoutManifest();
+    host->SelectCacheWithoutManifest();
   else
-    document_loader->GetApplicationCacheHost()->SelectCacheWithManifest(
-        GetDocument().CompleteURL(manifest));
+    host->SelectCacheWithManifest(GetDocument().CompleteURL(manifest));
+  bool app_cache_installed =
+      host->GetStatus() !=
+      blink::mojom::AppCacheStatus::APPCACHE_STATUS_UNCACHED;
+  if (app_cache_installed && manifest.IsEmpty()) {
+    UseCounter::Count(GetDocument(),
+                      WebFeature::kApplicationCacheInstalledButNoManifest);
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.cc b/third_party/blink/renderer/core/html/media/html_video_element.cc
index c159cda..642053f 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_video_element.cc
@@ -54,6 +54,7 @@
 #include "third_party/blink/renderer/core/layout/layout_image.h"
 #include "third_party/blink/renderer/core/layout/layout_video.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
@@ -238,7 +239,8 @@
         GetLayoutObject()->IsVideo())
       ToLayoutVideo(GetLayoutObject())->IntrinsicSizeChanged();
   } else if (params.name == kAutopictureinpictureAttr &&
-             RuntimeEnabledFeatures::AutoPictureInPictureEnabled()) {
+             origin_trials::AutoPictureInPictureEnabled(
+                 GetExecutionContext())) {
     if (!params.new_value.IsNull()) {
       PictureInPictureController::From(GetDocument())
           .AddToAutoPictureInPictureElementsList(this);
diff --git a/third_party/blink/renderer/core/loader/base_fetch_context.cc b/third_party/blink/renderer/core/loader/base_fetch_context.cc
index d97fcb0a..6bd278f 100644
--- a/third_party/blink/renderer/core/loader/base_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/base_fetch_context.cc
@@ -113,15 +113,12 @@
     // and store it elsewhere. See https://crbug.com/850813.
     request.SetHTTPReferrer(SecurityPolicy::GenerateReferrer(
         referrer_policy_to_use, request.Url(), referrer_to_use));
-    request.SetHTTPOriginIfNeeded(
-        fetch_client_settings_object.GetSecurityOrigin());
   } else {
     CHECK_EQ(
         SecurityPolicy::GenerateReferrer(request.GetReferrerPolicy(),
                                          request.Url(), request.HttpReferrer())
             .referrer,
         request.HttpReferrer());
-    request.SetHTTPOriginToMatchReferrerIfNeeded();
   }
 
   auto address_space = GetAddressSpace();
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
index c1a25b5..6ed04e5 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
@@ -1101,7 +1101,6 @@
 
   GetFetchContext()->AddAdditionalRequestHeaders(request);
 
-  EXPECT_EQ(origin, request.HttpHeaderField(http_names::kOrigin));
   EXPECT_EQ(String(origin + "/"),
             request.HttpHeaderField(http_names::kReferer));
   EXPECT_EQ(String(), request.HttpHeaderField("Save-Data"));
diff --git a/third_party/blink/renderer/core/loader/resource/font_resource.h b/third_party/blink/renderer/core/loader/resource/font_resource.h
index d5f2b7cc..d07e856 100644
--- a/third_party/blink/renderer/core/loader/resource/font_resource.h
+++ b/third_party/blink/renderer/core/loader/resource/font_resource.h
@@ -106,7 +106,7 @@
   TaskHandle font_load_long_limit_;
 
   friend class MemoryCache;
-  FRIEND_TEST_ALL_PREFIXES(FontResourceTest, CacheAwareFontLoading);
+  FRIEND_TEST_ALL_PREFIXES(CacheAwareFontResourceTest, CacheAwareFontLoading);
 };
 
 DEFINE_RESOURCE_TYPE_CASTS(Font);
diff --git a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
index faff11f..82a304b9 100644
--- a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
@@ -4,7 +4,9 @@
 
 #include "third_party/blink/renderer/core/loader/resource/font_resource.h"
 
+#include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
@@ -36,6 +38,18 @@
   }
 };
 
+class CacheAwareFontResourceTest : public FontResourceTest {
+ public:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kWebFontsCacheAwareTimeoutAdaption);
+    FontResourceTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
 // Tests if ResourceFetcher works fine with FontResource that requires defered
 // loading supports.
 TEST_F(FontResourceTest,
@@ -98,16 +112,13 @@
 }
 
 // Tests if cache-aware font loading works correctly.
-TEST_F(FontResourceTest, CacheAwareFontLoading) {
+TEST_F(CacheAwareFontResourceTest, CacheAwareFontLoading) {
   KURL url("http://127.0.0.1:8000/font.woff");
   ResourceResponse response(url);
   response.SetHTTPStatusCode(200);
   Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
       url, WrappedResourceResponse(response), "");
 
-  RuntimeEnabledFeatures::Backup features_backup;
-  RuntimeEnabledFeatures::SetWebFontsCacheAwareTimeoutAdaptationEnabled(true);
-
   std::unique_ptr<DummyPageHolder> dummy_page_holder =
       DummyPageHolder::Create(IntSize(800, 600));
   Document& document = dummy_page_holder->GetDocument();
@@ -166,8 +177,6 @@
 
   Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
   GetMemoryCache()->Remove(&resource);
-
-  features_backup.Restore();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/block_painter.cc b/third_party/blink/renderer/core/paint/block_painter.cc
index 4e97f68..b49f1b9 100644
--- a/third_party/blink/renderer/core/paint/block_painter.cc
+++ b/third_party/blink/renderer/core/paint/block_painter.cc
@@ -106,12 +106,22 @@
     layout_block_.PaintObject(local_paint_info, paint_offset);
   }
 
-  // We paint scrollbars after we painted the other things, so that the
-  // scrollbars will sit above them.
+  // Our scrollbar widgets paint exactly when we tell them to, so that they work
+  // properly with z-index. We paint after we painted the background/border, so
+  // that the scrollbars will sit above the background/border.
   local_paint_info.phase = original_phase;
-  if (auto* scrollable_area = layout_block_.GetScrollableArea()) {
-    ScrollableAreaPainter(*scrollable_area)
-        .PaintOverflowControls(local_paint_info, RoundedIntPoint(paint_offset));
+  PaintOverflowControlsIfNeeded(local_paint_info, paint_offset);
+}
+
+void BlockPainter::PaintOverflowControlsIfNeeded(
+    const PaintInfo& paint_info,
+    const LayoutPoint& paint_offset) {
+  if (layout_block_.HasOverflowClip() &&
+      layout_block_.StyleRef().Visibility() == EVisibility::kVisible &&
+      ShouldPaintSelfBlockBackground(paint_info.phase)) {
+    ScrollableAreaPainter(*layout_block_.Layer()->GetScrollableArea())
+        .PaintOverflowControls(paint_info, RoundedIntPoint(paint_offset),
+                               false /* painting_overlay_controls */);
   }
 }
 
diff --git a/third_party/blink/renderer/core/paint/block_painter.h b/third_party/blink/renderer/core/paint/block_painter.h
index fe5d337..93966cb 100644
--- a/third_party/blink/renderer/core/paint/block_painter.h
+++ b/third_party/blink/renderer/core/paint/block_painter.h
@@ -31,6 +31,8 @@
   void PaintContents(const PaintInfo&, const LayoutPoint& paint_offset);
   void PaintChildren(const PaintInfo&);
   void PaintChild(const LayoutBox&, const PaintInfo&);
+  void PaintOverflowControlsIfNeeded(const PaintInfo&,
+                                     const LayoutPoint& paint_offset);
 
   // See ObjectPainter::PaintAllPhasesAtomically().
   void PaintAllChildPhasesAtomically(const LayoutBox&, const PaintInfo&);
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index 6860f86..8df4a757 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -3149,6 +3149,13 @@
         paint_info.paint_layer->SubpixelAccumulation());
     PaintLayerPainter(*paint_info.paint_layer)
         .PaintLayerContents(context, painting_info, paint_layer_flags);
+
+    if (paint_info.paint_layer->ContainsDirtyOverlayScrollbars()) {
+      PaintLayerPainter(*paint_info.paint_layer)
+          .PaintLayerContents(
+              context, painting_info,
+              paint_layer_flags | kPaintLayerPaintingOverlayScrollbars);
+    }
   } else {
     PaintLayerPaintingInfo painting_info(
         paint_info.paint_layer, CullRect(dirty_rect), kGlobalPaintNormalPhase,
diff --git a/third_party/blink/renderer/core/paint/frame_painter.cc b/third_party/blink/renderer/core/paint/frame_painter.cc
index 2a4b508..1182240 100644
--- a/third_party/blink/renderer/core/paint/frame_painter.cc
+++ b/third_party/blink/renderer/core/paint/frame_painter.cc
@@ -103,6 +103,11 @@
   layer_painter.Paint(context, cull_rect, updated_global_paint_flags,
                       root_layer_paint_flags);
 
+  if (root_layer->ContainsDirtyOverlayScrollbars()) {
+    layer_painter.PaintOverlayScrollbars(context, cull_rect,
+                                         updated_global_paint_flags);
+  }
+
   // Regions may have changed as a result of the visibility/z-index of element
   // changing.
   if (document->AnnotatedRegionsDirty())
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index 990a8582..18b2bb8 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -192,13 +192,11 @@
     PaintObject(info, paint_offset);
   }
 
-  // We paint scrollbars after we painted other things, so that the scrollbars
-  // will sit above them.
+  // Our scrollbar widgets paint exactly when we tell them to, so that they work
+  // properly with z-index. We paint after we painted the background/border, so
+  // that the scrollbars will sit above the background/border.
   info.phase = original_phase;
-  if (box_fragment_.HasOverflowClip()) {
-    ScrollableAreaPainter(*PhysicalFragment().Layer()->GetScrollableArea())
-        .PaintOverflowControls(info, RoundedIntPoint(paint_offset));
-  }
+  PaintOverflowControlsIfNeeded(info, paint_offset);
 }
 
 void NGBoxFragmentPainter::RecordHitTestData(const PaintInfo& paint_info,
@@ -874,6 +872,19 @@
          box_fragment_.GetLayoutObject() == paint_info.PaintContainer();
 }
 
+// Clone of BlockPainter::PaintOverflowControlsIfNeeded
+void NGBoxFragmentPainter::PaintOverflowControlsIfNeeded(
+    const PaintInfo& paint_info,
+    const LayoutPoint& paint_offset) {
+  if (box_fragment_.HasOverflowClip() &&
+      box_fragment_.Style().Visibility() == EVisibility::kVisible &&
+      ShouldPaintSelfBlockBackground(paint_info.phase)) {
+    ScrollableAreaPainter(*PhysicalFragment().Layer()->GetScrollableArea())
+        .PaintOverflowControls(paint_info, RoundedIntPoint(paint_offset),
+                               false /* painting_overlay_controls */);
+  }
+}
+
 bool NGBoxFragmentPainter::ShouldPaint(
     const ScopedPaintState& paint_state) const {
   // TODO(layout-dev): Add support for scrolling, see BlockPainter::ShouldPaint.
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
index 1522856..41a6a37 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h
@@ -99,6 +99,8 @@
   void PaintFloatingChildren(NGPaintFragment::ChildList, const PaintInfo&);
   void PaintFloats(const PaintInfo&);
   void PaintMask(const PaintInfo&, const LayoutPoint& paint_offset);
+  void PaintOverflowControlsIfNeeded(const PaintInfo&,
+                                     const LayoutPoint& paint_offset);
   void PaintAtomicInline(const PaintInfo&);
   void PaintBackground(const PaintInfo&,
                        const LayoutRect&,
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 6fe8640..af8ef25 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -148,6 +148,7 @@
       needs_position_update_(!IsRootLayer()),
 #endif
       has3d_transformed_descendant_(false),
+      contains_dirty_overlay_scrollbars_(false),
       needs_ancestor_dependent_compositing_inputs_update_(
           !RuntimeEnabledFeatures::CompositeAfterPaintEnabled()),
       child_needs_compositing_inputs_update_(
diff --git a/third_party/blink/renderer/core/paint/paint_layer.h b/third_party/blink/renderer/core/paint/paint_layer.h
index ac350c6..920dea1 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.h
+++ b/third_party/blink/renderer/core/paint/paint_layer.h
@@ -597,6 +597,13 @@
   bool BackgroundIsKnownToBeOpaqueInRect(const LayoutRect&,
                                          bool should_check_children) const;
 
+  bool ContainsDirtyOverlayScrollbars() const {
+    return contains_dirty_overlay_scrollbars_;
+  }
+  void SetContainsDirtyOverlayScrollbars(bool dirty_scrollbars) {
+    contains_dirty_overlay_scrollbars_ = dirty_scrollbars;
+  }
+
   // If the input CompositorFilterOperation is not empty, it will be populated
   // only if |filter_on_effect_node_dirty_| is true or the reference box has
   // changed. Otherwise it will be populated unconditionally.
@@ -1265,6 +1272,8 @@
   // in a preserves3D hierarchy. Hint to do 3D-aware hit testing.
   unsigned has3d_transformed_descendant_ : 1;
 
+  unsigned contains_dirty_overlay_scrollbars_ : 1;
+
   unsigned needs_ancestor_dependent_compositing_inputs_update_ : 1;
   unsigned child_needs_compositing_inputs_update_ : 1;
 
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.cc b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
index 80bfe74a..0171009 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.cc
@@ -168,7 +168,8 @@
   if (painting_info.GetGlobalPaintFlags() &
       kGlobalPaintFlattenCompositingLayers)
     return false;
-  if (paint_flags & kPaintLayerUncachedClipRects)
+  if (paint_flags &
+      (kPaintLayerPaintingOverlayScrollbars | kPaintLayerUncachedClipRects))
     return false;
 
   // When in FOUC-avoidance mode, don't cache any subsequences, to avoid having
@@ -358,6 +359,8 @@
   AdjustForPaintProperties(context, painting_info, paint_flags);
 
   bool is_self_painting_layer = paint_layer_.IsSelfPaintingLayer();
+  bool is_painting_overlay_scrollbars =
+      paint_flags & kPaintLayerPaintingOverlayScrollbars;
   bool is_painting_scrolling_content =
       paint_flags & kPaintLayerPaintingCompositingScrollingPhase;
   bool is_painting_composited_foreground =
@@ -374,7 +377,7 @@
   // It is painted as part of the decoration phase which paints content that
   // is not scrolled and should be above scrolled content.
   bool should_paint_self_outline =
-      is_self_painting_layer &&
+      is_self_painting_layer && !is_painting_overlay_scrollbars &&
       (is_painting_composited_decoration ||
        (!is_painting_overflow_contents && !is_painting_mask)) &&
       paint_layer_.GetLayoutObject().StyleRef().HasOutline();
@@ -433,11 +436,13 @@
       // Content under a LayoutSVGHiddenContainer is auxiliary resources for
       // painting. Foreign content should never paint in this situation, as it
       // is primary, not auxiliary.
-      !paint_layer_.IsUnderSVGHiddenContainer() && is_self_painting_layer;
+      !paint_layer_.IsUnderSVGHiddenContainer() && is_self_painting_layer &&
+      !is_painting_overlay_scrollbars;
 
   PaintLayerFragments layer_fragments;
 
-  if (should_paint_content || should_paint_self_outline) {
+  if (should_paint_content || should_paint_self_outline ||
+      is_painting_overlay_scrollbars) {
     // Collect the fragments. This will compute the clip rectangles and paint
     // offsets for each layer fragment.
     LayoutPoint offset_to_clipper;
@@ -514,6 +519,7 @@
         is_painting_composited_foreground && should_paint_content;
     bool should_paint_normal_flow_and_pos_z_order_lists =
         is_painting_composited_foreground;
+    bool should_paint_overlay_scrollbars = is_painting_overlay_scrollbars;
 
     base::Optional<ScopedPaintChunkProperties>
         subsequence_forced_chunk_properties;
@@ -571,7 +577,12 @@
         result = kMayBeClippedByCullRect;
     }
 
-    if (paint_layer_.PaintsWithFilters() &&
+    if (should_paint_overlay_scrollbars) {
+      PaintOverflowControlsForFragments(layer_fragments, context,
+                                        local_painting_info, paint_flags);
+    }
+
+    if (!is_painting_overlay_scrollbars && paint_layer_.PaintsWithFilters() &&
         display_item_list_size_before_painting ==
             context.GetPaintController().NewDisplayItemList().size()) {
       // If a layer with filters painted nothing, we need to issue a no-op
@@ -711,6 +722,42 @@
   return result;
 }
 
+void PaintLayerPainter::PaintOverflowControlsForFragments(
+    const PaintLayerFragments& layer_fragments,
+    GraphicsContext& context,
+    const PaintLayerPaintingInfo& painting_info,
+    PaintLayerFlags paint_flags) {
+  PaintLayerScrollableArea* scrollable_area = paint_layer_.GetScrollableArea();
+  if (!scrollable_area)
+    return;
+
+  ForAllFragments(
+      context, layer_fragments, [&](const PaintLayerFragment& fragment) {
+        ScopedPaintChunkProperties fragment_paint_chunk_properties(
+            context.GetPaintController(),
+            fragment.fragment_data->LocalBorderBoxProperties(), paint_layer_,
+            DisplayItem::kOverflowControls);
+
+        // We need to apply the same clips and transforms that
+        // paintFragmentWithPhase would have.
+        LayoutRect cull_rect = fragment.background_rect.Rect();
+        PaintInfo paint_info(
+            context, PixelSnappedIntRect(cull_rect),
+            PaintPhase::kSelfBlockBackgroundOnly,
+            painting_info.GetGlobalPaintFlags(), paint_flags,
+            &painting_info.root_layer->GetLayoutObject(),
+            fragment.fragment_data
+                ? fragment.fragment_data->LogicalTopInFlowThread()
+                : LayoutUnit());
+        // We pass IntPoint() as the paint offset here, because
+        // ScrollableArea::paintOverflowControls just ignores it and uses the
+        // offset found in a previous pass.
+        ScrollableAreaPainter(*scrollable_area)
+            .PaintOverflowControls(paint_info, IntPoint(),
+                                   true /* painting_overlay_controls */);
+      });
+}
+
 void PaintLayerPainter::PaintFragmentWithPhase(
     PaintPhase phase,
     const PaintLayerFragment& fragment,
@@ -914,6 +961,20 @@
       });
 }
 
+void PaintLayerPainter::PaintOverlayScrollbars(
+    GraphicsContext& context,
+    const CullRect& cull_rect,
+    const GlobalPaintFlags paint_flags) {
+  if (!paint_layer_.ContainsDirtyOverlayScrollbars())
+    return;
+
+  PaintLayerPaintingInfo painting_info(&paint_layer_, cull_rect, paint_flags,
+                                       LayoutSize());
+  Paint(context, painting_info, kPaintLayerPaintingOverlayScrollbars);
+
+  paint_layer_.SetContainsDirtyOverlayScrollbars(false);
+}
+
 void PaintLayerPainter::FillMaskingFragment(GraphicsContext& context,
                                             const ClipRect& clip_rect,
                                             const DisplayItemClient& client) {
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter.h b/third_party/blink/renderer/core/paint/paint_layer_painter.h
index b26f35a5..11a2817 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter.h
@@ -49,6 +49,10 @@
                                  const PaintLayerPaintingInfo&,
                                  PaintLayerFlags);
 
+  void PaintOverlayScrollbars(GraphicsContext&,
+                              const CullRect&,
+                              const GlobalPaintFlags);
+
   // Returns true if the painted output of this PaintLayer and its children is
   // invisible and therefore can't impact painted output.
   static bool PaintedOutputInvisible(const ComputedStyle&);
@@ -91,6 +95,10 @@
                                     GraphicsContext&,
                                     const PaintLayerPaintingInfo&,
                                     PaintLayerFlags);
+  void PaintOverflowControlsForFragments(const PaintLayerFragments&,
+                                         GraphicsContext&,
+                                         const PaintLayerPaintingInfo&,
+                                         PaintLayerFlags);
   void PaintMaskForFragments(const PaintLayerFragments&,
                              GraphicsContext&,
                              const PaintLayerPaintingInfo&,
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painting_info.h b/third_party/blink/renderer/core/paint/paint_layer_painting_info.h
index eecbf5e..bee6fa8e 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painting_info.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_painting_info.h
@@ -63,6 +63,7 @@
   kPaintLayerNoFlag = 0,
   kPaintLayerHaveTransparency = 1,
   kPaintLayerUncachedClipRects = 1 << 2,
+  kPaintLayerPaintingOverlayScrollbars = 1 << 3,
   kPaintLayerPaintingCompositingBackgroundPhase = 1 << 4,
   kPaintLayerPaintingCompositingForegroundPhase = 1 << 5,
   kPaintLayerPaintingCompositingMaskPhase = 1 << 6,
@@ -139,6 +140,8 @@
     append("kPaintLayerHaveTransparency");
   if (flags & kPaintLayerUncachedClipRects)
     append("kPaintLayerUncachedClipRects");
+  if (flags & kPaintLayerPaintingOverlayScrollbars)
+    append("kPaintLayerPaintingOverlayScrollbars");
   if (flags & kPaintLayerPaintingCompositingScrollingPhase)
     append("kPaintLayerPaintingCompositingScrollingPhase");
   if (flags & kPaintLayerPaintingOverflowContents)
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index 25ba9d5..6be12e4 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -447,6 +447,13 @@
 
   LayoutScrollbarPart* Resizer() const { return resizer_; }
 
+  const IntPoint& CachedOverlayScrollbarOffset() {
+    return cached_overlay_scrollbar_offset_;
+  }
+  void SetCachedOverlayScrollbarOffset(const IntPoint& offset) {
+    cached_overlay_scrollbar_offset_ = offset;
+  }
+
   IntRect RectForHorizontalScrollbar(const IntRect& border_box_rect) const;
   IntRect RectForVerticalScrollbar(const IntRect& border_box_rect) const;
 
@@ -672,6 +679,8 @@
   // This is the offset from the beginning of content flow.
   ScrollOffset scroll_offset_;
 
+  IntPoint cached_overlay_scrollbar_offset_;
+
   // LayoutObject to hold our custom scroll corner.
   LayoutScrollbarPart* scroll_corner_;
 
diff --git a/third_party/blink/renderer/core/paint/scrollable_area_painter.cc b/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
index c02359c..f2f01a0e 100644
--- a/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
+++ b/third_party/blink/renderer/core/paint/scrollable_area_painter.cc
@@ -123,23 +123,56 @@
 
 void ScrollableAreaPainter::PaintOverflowControls(
     const PaintInfo& paint_info,
-    const IntPoint& paint_offset) {
+    const IntPoint& paint_offset,
+    bool painting_overlay_controls) {
   // Don't do anything if we have no overflow.
-  const auto& box = *GetScrollableArea().GetLayoutBox();
-  if (!box.HasOverflowClip() ||
-      box.StyleRef().Visibility() != EVisibility::kVisible)
+  if (!GetScrollableArea().GetLayoutBox()->HasOverflowClip())
     return;
 
-  // Overlay scrollbars are painted in the foreground paint phase, and normal
-  // scrollbars are painted in the background paint phase.
-  if (GetScrollableArea().HasOverlayScrollbars()) {
-    if (paint_info.phase != PaintPhase::kForeground)
+  IntPoint adjusted_paint_offset = paint_offset;
+  if (painting_overlay_controls)
+    adjusted_paint_offset = GetScrollableArea().CachedOverlayScrollbarOffset();
+
+  CullRect adjusted_cull_rect = paint_info.GetCullRect();
+  adjusted_cull_rect.MoveBy(-adjusted_paint_offset);
+  // Overlay scrollbars paint in a second pass through the layer tree so that
+  // they will paint on top of everything else. If this is the normal painting
+  // pass, paintingOverlayControls will be false, and we should just tell the
+  // root layer that there are overlay scrollbars that need to be painted. That
+  // will cause the second pass through the layer tree to run, and we'll paint
+  // the scrollbars then. In the meantime, cache tx and ty so that the second
+  // pass doesn't need to re-enter the LayoutTree to get it right.
+  if (GetScrollableArea().HasOverlayScrollbars() &&
+      !painting_overlay_controls) {
+    GetScrollableArea().SetCachedOverlayScrollbarOffset(paint_offset);
+    // It's not necessary to do the second pass if the scrollbars paint into
+    // layers.
+    if ((GetScrollableArea().HorizontalScrollbar() &&
+         GetScrollableArea().LayerForHorizontalScrollbar()) ||
+        (GetScrollableArea().VerticalScrollbar() &&
+         GetScrollableArea().LayerForVerticalScrollbar()))
       return;
-  } else if (!ShouldPaintSelfBlockBackground(paint_info.phase)) {
+    if (!OverflowControlsIntersectRect(adjusted_cull_rect))
+      return;
+
+    LayoutView* layout_view = GetScrollableArea().GetLayoutBox()->View();
+
+    PaintLayer* painting_root =
+        GetScrollableArea().Layer()->EnclosingLayerWithCompositedLayerMapping(
+            kIncludeSelf);
+    if (!painting_root)
+      painting_root = layout_view->Layer();
+
+    painting_root->SetContainsDirtyOverlayScrollbars(true);
     return;
   }
 
+  // This check is required to avoid painting custom CSS scrollbars twice.
+  if (painting_overlay_controls && !GetScrollableArea().HasOverlayScrollbars())
+    return;
+
   GraphicsContext& context = paint_info.context;
+  const auto& box = *GetScrollableArea().GetLayoutBox();
   const auto* fragment = paint_info.FragmentToPaint(box);
   if (!fragment)
     return;
@@ -157,8 +190,6 @@
     }
   }
 
-  CullRect adjusted_cull_rect = paint_info.GetCullRect();
-  adjusted_cull_rect.MoveBy(-paint_offset);
   if (GetScrollableArea().HorizontalScrollbar() &&
       !GetScrollableArea().LayerForHorizontalScrollbar()) {
     GetScrollableArea().HorizontalScrollbar()->Paint(context,
@@ -168,27 +199,51 @@
       !GetScrollableArea().LayerForVerticalScrollbar()) {
     GetScrollableArea().VerticalScrollbar()->Paint(context, adjusted_cull_rect);
   }
-
   if (!GetScrollableArea().LayerForScrollCorner()) {
     // We fill our scroll corner with white if we have a scrollbar that doesn't
     // run all the way up to the edge of the box.
-    PaintScrollCorner(context, paint_offset, paint_info.GetCullRect());
+    PaintScrollCorner(context, adjusted_paint_offset, paint_info.GetCullRect());
 
     // Paint our resizer last, since it sits on top of the scroll corner.
-    PaintResizer(context, paint_offset, paint_info.GetCullRect());
+    PaintResizer(context, adjusted_paint_offset, paint_info.GetCullRect());
   }
 }
 
-void ScrollableAreaPainter::PaintScrollCorner(GraphicsContext& context,
-                                              const IntPoint& paint_offset,
-                                              const CullRect& cull_rect) {
+bool ScrollableAreaPainter::OverflowControlsIntersectRect(
+    const CullRect& cull_rect) const {
+  const IntRect border_box =
+      GetScrollableArea().GetLayoutBox()->PixelSnappedBorderBoxRect(
+          GetScrollableArea().Layer()->SubpixelAccumulation());
+
+  if (cull_rect.Intersects(
+          GetScrollableArea().RectForHorizontalScrollbar(border_box)))
+    return true;
+
+  if (cull_rect.Intersects(
+          GetScrollableArea().RectForVerticalScrollbar(border_box)))
+    return true;
+
+  if (cull_rect.Intersects(GetScrollableArea().ScrollCornerRect()))
+    return true;
+
+  if (cull_rect.Intersects(GetScrollableArea().ResizerCornerRect(
+          border_box, kResizerForPointer)))
+    return true;
+
+  return false;
+}
+
+void ScrollableAreaPainter::PaintScrollCorner(
+    GraphicsContext& context,
+    const IntPoint& paint_offset,
+    const CullRect& adjusted_cull_rect) {
   IntRect abs_rect = GetScrollableArea().ScrollCornerRect();
   if (abs_rect.IsEmpty())
     return;
   abs_rect.MoveBy(paint_offset);
 
   if (const auto* scroll_corner = GetScrollableArea().ScrollCorner()) {
-    if (!cull_rect.Intersects(abs_rect))
+    if (!adjusted_cull_rect.Intersects(abs_rect))
       return;
     ScrollbarPainter::PaintIntoRect(*scroll_corner, context, paint_offset,
                                     LayoutRect(abs_rect));
diff --git a/third_party/blink/renderer/core/paint/scrollable_area_painter.h b/third_party/blink/renderer/core/paint/scrollable_area_painter.h
index 86346c3..d6c884a 100644
--- a/third_party/blink/renderer/core/paint/scrollable_area_painter.h
+++ b/third_party/blink/renderer/core/paint/scrollable_area_painter.h
@@ -26,7 +26,10 @@
       PaintLayerScrollableArea& paint_layer_scrollable_area)
       : scrollable_area_(&paint_layer_scrollable_area) {}
 
-  void PaintOverflowControls(const PaintInfo&, const IntPoint& paint_offset);
+  void PaintOverflowControls(const PaintInfo&,
+                             const IntPoint& paint_offset,
+                             bool painting_overlay_controls);
+
   void PaintResizer(GraphicsContext&,
                     const IntPoint& paint_offset,
                     const CullRect&);
@@ -36,6 +39,7 @@
 
  private:
   void DrawPlatformResizerImage(GraphicsContext&, IntRect resizer_corner_rect);
+  bool OverflowControlsIntersectRect(const CullRect&) const;
 
   PaintLayerScrollableArea& GetScrollableArea() const;
   const DisplayItemClient& DisplayItemClientForCorner() const;
diff --git a/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.cc b/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.cc
index 20784ff..150cbc7 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.cc
+++ b/third_party/blink/renderer/core/workers/shared_worker_reporting_proxy.cc
@@ -75,9 +75,15 @@
 }
 
 void SharedWorkerReportingProxy::DidFailToFetchClassicScript() {
+  // TODO(nhiroki): Add a runtime flag check for off-the-main-thread shared
+  // worker script fetch. This function should be called only when the flag is
+  // enabled (https://crbug.com/924041).
   DCHECK(!IsMainThread());
-  // TODO(nhiroki): Dispatch an error event at the SharedWorker object in the
-  // parent document.
+  PostCrossThreadTask(
+      *parent_execution_context_task_runners_->Get(TaskType::kInternalDefault),
+      FROM_HERE,
+      CrossThreadBind(&WebSharedWorkerImpl::DidFailToFetchClassicScript,
+                      CrossThreadUnretained(worker_)));
 }
 
 void SharedWorkerReportingProxy::DidFailToFetchModuleScript() {
diff --git a/third_party/blink/renderer/modules/app_banner/app_banner_controller.cc b/third_party/blink/renderer/modules/app_banner/app_banner_controller.cc
index dd1c147..191c61d 100644
--- a/third_party/blink/renderer/modules/app_banner/app_banner_controller.cc
+++ b/third_party/blink/renderer/modules/app_banner/app_banner_controller.cc
@@ -12,10 +12,6 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/app_banner/before_install_prompt_event.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/weborigin/referrer.h"
-#include "third_party/blink/renderer/platform/weborigin/security_policy.h"
-#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
 namespace blink {
 
@@ -39,7 +35,7 @@
     bool require_gesture,
     BannerPromptRequestCallback callback) {
   if (!frame_ || !frame_->GetDocument()) {
-    std::move(callback).Run(mojom::blink::AppBannerPromptReply::NONE, "");
+    std::move(callback).Run(mojom::blink::AppBannerPromptReply::NONE);
     return;
   }
 
@@ -51,12 +47,7 @@
           ? mojom::AppBannerPromptReply::NONE
           : mojom::AppBannerPromptReply::CANCEL;
 
-  AtomicString referrer = SecurityPolicy::GenerateReferrer(
-                              frame_->GetDocument()->GetReferrerPolicy(),
-                              KURL(), frame_->GetDocument()->OutgoingReferrer())
-                              .referrer;
-
-  std::move(callback).Run(reply, referrer.IsNull() ? g_empty_string : referrer);
+  std::move(callback).Run(reply);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc b/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
index f6571686..6f2f6da 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_promise.cc
@@ -121,8 +121,9 @@
 
 PermissionService* ClipboardPromise::GetPermissionService() {
   if (!permission_service_) {
-    ConnectToPermissionService(ExecutionContext::From(script_state_),
-                               mojo::MakeRequest(&permission_service_));
+    ConnectToPermissionService(
+        ExecutionContext::From(script_state_),
+        mojo::MakeRequest(&permission_service_, GetTaskRunner()));
   }
   return permission_service_.get();
 }
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation.cc b/third_party/blink/renderer/modules/geolocation/geolocation.cc
index cdc7edc..2395e2a 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation.cc
+++ b/third_party/blink/renderer/modules/geolocation/geolocation.cc
@@ -465,7 +465,7 @@
   GetFrame()->GetInterfaceProvider().GetInterface(&geolocation_service_,
                                                   invalidator, task_runner);
   geolocation_service_->CreateGeolocation(
-      MakeRequest(&geolocation_, invalidator),
+      MakeRequest(&geolocation_, invalidator, task_runner),
       LocalFrame::HasTransientUserActivation(GetFrame()));
 
   geolocation_.set_connection_error_handler(WTF::Bind(
diff --git a/third_party/blink/renderer/modules/mediastream/media_devices.cc b/third_party/blink/renderer/modules/mediastream/media_devices.cc
index 223370b..34ab6196 100644
--- a/third_party/blink/renderer/modules/mediastream/media_devices.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_devices.cc
@@ -336,8 +336,9 @@
 const mojom::blink::MediaDevicesDispatcherHostPtr&
 MediaDevices::GetDispatcherHost(LocalFrame* frame) {
   if (!dispatcher_host_) {
-    frame->GetInterfaceProvider().GetInterface(
-        mojo::MakeRequest(&dispatcher_host_));
+    // See https://bit.ly/2S0zRAS for task types
+    frame->GetInterfaceProvider().GetInterface(mojo::MakeRequest(
+        &dispatcher_host_, frame->GetTaskRunner(TaskType::kMiscPlatformAPI)));
     dispatcher_host_.set_connection_error_handler(
         WTF::Bind(&MediaDevices::OnDispatcherHostConnectionError,
                   WrapWeakPersistent(this)));
diff --git a/third_party/blink/renderer/modules/notifications/notification_manager.cc b/third_party/blink/renderer/modules/notifications/notification_manager.cc
index 8ccdb3c..029090c 100644
--- a/third_party/blink/renderer/modules/notifications/notification_manager.cc
+++ b/third_party/blink/renderer/modules/notifications/notification_manager.cc
@@ -69,8 +69,11 @@
   ExecutionContext* context = ExecutionContext::From(script_state);
 
   if (!permission_service_) {
-    ConnectToPermissionService(context,
-                               mojo::MakeRequest(&permission_service_));
+    // See https://bit.ly/2S0zRAS for task types.
+    ConnectToPermissionService(
+        context,
+        mojo::MakeRequest(&permission_service_,
+                          context->GetTaskRunner(TaskType::kMiscPlatformAPI)));
     permission_service_.set_connection_error_handler(
         WTF::Bind(&NotificationManager::OnPermissionServiceConnectionError,
                   WrapWeakPersistent(this)));
@@ -225,7 +228,10 @@
 NotificationManager::GetNotificationService() {
   if (!notification_service_) {
     if (auto* provider = GetSupplementable()->GetInterfaceProvider()) {
-      provider->GetInterface(mojo::MakeRequest(&notification_service_));
+      // See https://bit.ly/2S0zRAS for task types.
+      provider->GetInterface(mojo::MakeRequest(
+          &notification_service_,
+          GetSupplementable()->GetTaskRunner(TaskType::kMiscPlatformAPI)));
 
       notification_service_.set_connection_error_handler(
           WTF::Bind(&NotificationManager::OnNotificationServiceConnectionError,
diff --git a/third_party/blink/renderer/modules/payments/address_errors.idl b/third_party/blink/renderer/modules/payments/address_errors.idl
index 1b03698..71f9b30 100644
--- a/third_party/blink/renderer/modules/payments/address_errors.idl
+++ b/third_party/blink/renderer/modules/payments/address_errors.idl
@@ -15,6 +15,5 @@
     DOMString postalCode;
     DOMString recipient;
     DOMString region;
-    DOMString regionCode;
     DOMString sortingCode;
 };
diff --git a/third_party/blink/renderer/modules/payments/payment_instruments.cc b/third_party/blink/renderer/modules/payments/payment_instruments.cc
index d67dd5b2..b339f895 100644
--- a/third_party/blink/renderer/modules/payments/payment_instruments.cc
+++ b/third_party/blink/renderer/modules/payments/payment_instruments.cc
@@ -279,8 +279,11 @@
 mojom::blink::PermissionService* PaymentInstruments::GetPermissionService(
     ScriptState* script_state) {
   if (!permission_service_) {
-    ConnectToPermissionService(ExecutionContext::From(script_state),
-                               mojo::MakeRequest(&permission_service_));
+    ExecutionContext* context = ExecutionContext::From(script_state);
+    ConnectToPermissionService(
+        context,
+        mojo::MakeRequest(&permission_service_,
+                          context->GetTaskRunner(TaskType::kUserInteraction)));
   }
   return permission_service_.get();
 }
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
index 3564c977..48d34e8 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -197,8 +197,6 @@
     output->recipient =
         input->hasRecipient() ? input->recipient() : g_empty_string;
     output->region = input->hasRegion() ? input->region() : g_empty_string;
-    output->region_code =
-        input->hasRegionCode() ? input->regionCode() : g_empty_string;
     output->sorting_code =
         input->hasSortingCode() ? input->sortingCode() : g_empty_string;
     return output;
diff --git a/third_party/blink/renderer/modules/payments/payments_validators.cc b/third_party/blink/renderer/modules/payments/payments_validators.cc
index 966cb94..4dec988 100644
--- a/third_party/blink/renderer/modules/payments/payments_validators.cc
+++ b/third_party/blink/renderer/modules/payments/payments_validators.cc
@@ -156,9 +156,6 @@
           IsValidErrorMsgFormat(errors->recipient(), optional_error_message)) &&
          (!errors->hasRegion() ||
           IsValidErrorMsgFormat(errors->region(), optional_error_message)) &&
-         (!errors->hasRegionCode() ||
-          IsValidErrorMsgFormat(errors->regionCode(),
-                                optional_error_message)) &&
          (!errors->hasSortingCode() ||
           IsValidErrorMsgFormat(errors->sortingCode(), optional_error_message));
 }
diff --git a/third_party/blink/renderer/modules/payments/payments_validators_test.cc b/third_party/blink/renderer/modules/payments/payments_validators_test.cc
index bbe337f..b86bd21 100644
--- a/third_party/blink/renderer/modules/payments/payments_validators_test.cc
+++ b/third_party/blink/renderer/modules/payments/payments_validators_test.cc
@@ -280,7 +280,6 @@
   const char* m_shipping_address_postal_code = "";
   const char* m_shipping_address_recipient = "";
   const char* m_shipping_address_region = "";
-  const char* m_shipping_address_region_code = "";
   const char* m_shipping_address_sorting_code = "";
   bool expected_valid;
 };
@@ -313,7 +312,6 @@
   shipping_address->setPostalCode(test_case.m_shipping_address_postal_code);
   shipping_address->setRecipient(test_case.m_shipping_address_recipient);
   shipping_address->setRegion(test_case.m_shipping_address_region);
-  shipping_address->setRegionCode(test_case.m_shipping_address_region_code);
   shipping_address->setSortingCode(test_case.m_shipping_address_sorting_code);
 
   errors->setPayer(payer);
@@ -362,7 +360,6 @@
         VALIDATION_ERRORS_TEST_CASE(shipping_address_postal_code, "test", true),
         VALIDATION_ERRORS_TEST_CASE(shipping_address_recipient, "test", true),
         VALIDATION_ERRORS_TEST_CASE(shipping_address_region, "test", true),
-        VALIDATION_ERRORS_TEST_CASE(shipping_address_region_code, "test", true),
         VALIDATION_ERRORS_TEST_CASE(shipping_address_sorting_code,
                                     "test",
                                     true),
@@ -402,9 +399,6 @@
         VALIDATION_ERRORS_TEST_CASE(shipping_address_region,
                                     LongString2049(),
                                     false),
-        VALIDATION_ERRORS_TEST_CASE(shipping_address_region_code,
-                                    LongString2049(),
-                                    false),
         VALIDATION_ERRORS_TEST_CASE(shipping_address_sorting_code,
                                     LongString2049(),
                                     false)));
diff --git a/third_party/blink/renderer/modules/picture_in_picture/html_video_element_picture_in_picture.idl b/third_party/blink/renderer/modules/picture_in_picture/html_video_element_picture_in_picture.idl
index 5e55a11..18f4a01 100644
--- a/third_party/blink/renderer/modules/picture_in_picture/html_video_element_picture_in_picture.idl
+++ b/third_party/blink/renderer/modules/picture_in_picture/html_video_element_picture_in_picture.idl
@@ -4,17 +4,16 @@
 
 // https://wicg.github.io/picture-in-picture/#htmlvideoelement-extensions
 [
-    ImplementedAs=HTMLVideoElementPictureInPicture,
-    RuntimeEnabled=PictureInPictureAPI
+    ImplementedAs=HTMLVideoElementPictureInPicture
 ]
 partial interface HTMLVideoElement {
-    [CallWith=ScriptState, Measure, NewObject] Promise<PictureInPictureWindow> requestPictureInPicture();
+    [RuntimeEnabled=PictureInPictureAPI, CallWith=ScriptState, Measure, NewObject] Promise<PictureInPictureWindow> requestPictureInPicture();
     [RuntimeEnabled=PictureInPictureControl] void setPictureInPictureControls(sequence<PictureInPictureControl> pipControls);
 
-    attribute EventHandler onenterpictureinpicture;
-    attribute EventHandler onleavepictureinpicture;
+    [RuntimeEnabled=PictureInPictureAPI] attribute EventHandler onenterpictureinpicture;
+    [RuntimeEnabled=PictureInPictureAPI] attribute EventHandler onleavepictureinpicture;
     [RuntimeEnabled=PictureInPictureControl] attribute EventHandler onpictureinpicturecontrolclick;
 
-    [RuntimeEnabled=AutoPictureInPicture, CEReactions, Measure, Reflect] attribute boolean autoPictureInPicture;
-    [CEReactions, Measure, Reflect] attribute boolean disablePictureInPicture;
+    [OriginTrialEnabled=AutoPictureInPicture, CEReactions, Measure, Reflect] attribute boolean autoPictureInPicture;
+    [RuntimeEnabled=PictureInPictureAPI, CEReactions, Measure, Reflect] attribute boolean disablePictureInPicture;
 };
diff --git a/third_party/blink/renderer/modules/quota/storage_manager.cc b/third_party/blink/renderer/modules/quota/storage_manager.cc
index 764d93a..caa1458 100644
--- a/third_party/blink/renderer/modules/quota/storage_manager.cc
+++ b/third_party/blink/renderer/modules/quota/storage_manager.cc
@@ -147,8 +147,11 @@
 PermissionService& StorageManager::GetPermissionService(
     ExecutionContext* execution_context) {
   if (!permission_service_) {
-    ConnectToPermissionService(execution_context,
-                               mojo::MakeRequest(&permission_service_));
+    // See https://bit.ly/2S0zRAS for task types.
+    ConnectToPermissionService(
+        execution_context, mojo::MakeRequest(&permission_service_,
+                                             execution_context->GetTaskRunner(
+                                                 TaskType::kMiscPlatformAPI)));
     permission_service_.set_connection_error_handler(
         WTF::Bind(&StorageManager::PermissionServiceConnectionError,
                   WrapWeakPersistent(this)));
@@ -171,8 +174,11 @@
 mojom::blink::QuotaDispatcherHost& StorageManager::GetQuotaHost(
     ExecutionContext* execution_context) {
   if (!quota_host_) {
-    ConnectToQuotaDispatcherHost(execution_context,
-                                 mojo::MakeRequest(&quota_host_));
+    // See https://bit.ly/2S0zRAS for task types.
+    ConnectToQuotaDispatcherHost(
+        execution_context,
+        mojo::MakeRequest(&quota_host_, execution_context->GetTaskRunner(
+                                            TaskType::kMiscPlatformAPI)));
   }
   return *quota_host_;
 }
diff --git a/third_party/blink/renderer/modules/service_worker/BUILD.gn b/third_party/blink/renderer/modules/service_worker/BUILD.gn
index cbe8fd6..394ed3c 100644
--- a/third_party/blink/renderer/modules/service_worker/BUILD.gn
+++ b/third_party/blink/renderer/modules/service_worker/BUILD.gn
@@ -16,6 +16,8 @@
     "fetch_respond_with_observer.h",
     "install_event.cc",
     "install_event.h",
+    "message_from_service_worker.cc",
+    "message_from_service_worker.h",
     "navigation_preload_manager.cc",
     "navigation_preload_manager.h",
     "navigator_service_worker.cc",
diff --git a/third_party/blink/renderer/modules/service_worker/message_from_service_worker.cc b/third_party/blink/renderer/modules/service_worker/message_from_service_worker.cc
new file mode 100644
index 0000000..18c72eb4
--- /dev/null
+++ b/third_party/blink/renderer/modules/service_worker/message_from_service_worker.cc
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/service_worker/message_from_service_worker.h"
+
+#include <utility>
+
+namespace blink {
+
+MessageFromServiceWorker::MessageFromServiceWorker(
+    WebServiceWorkerObjectInfo source,
+    blink::TransferableMessage message)
+    : source(std::move(source)), message(std::move(message)) {}
+
+MessageFromServiceWorker::~MessageFromServiceWorker() = default;
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/service_worker/message_from_service_worker.h b/third_party/blink/renderer/modules/service_worker/message_from_service_worker.h
new file mode 100644
index 0000000..11c3df16
--- /dev/null
+++ b/third_party/blink/renderer/modules/service_worker/message_from_service_worker.h
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_MESSAGE_FROM_SERVICE_WORKER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_MESSAGE_FROM_SERVICE_WORKER_H_
+
+#include "third_party/blink/public/common/messaging/transferable_message.h"
+#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_object_info.h"
+
+namespace blink {
+
+// Holds info for a message event destined for ServiceWorkerContainer.onmessage.
+// https://w3c.github.io/ServiceWorker/#dom-serviceworkercontainer-onmessage
+struct MessageFromServiceWorker {
+  MessageFromServiceWorker(WebServiceWorkerObjectInfo source,
+                           blink::TransferableMessage message);
+  virtual ~MessageFromServiceWorker();
+
+  // The service worker that posted the message.
+  WebServiceWorkerObjectInfo source;
+
+  // The message.
+  blink::TransferableMessage message;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MessageFromServiceWorker);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_MESSAGE_FROM_SERVICE_WORKER_H_
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_container.cc b/third_party/blink/renderer/modules/service_worker/service_worker_container.cc
index 84ecf71..6c6a1f5 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_container.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_container.cc
@@ -43,6 +43,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
 #include "third_party/blink/renderer/core/events/message_event.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
@@ -67,6 +68,10 @@
 
 namespace {
 
+bool HasFiredDomContentLoaded(const Document& document) {
+  return !document.GetTiming().DomContentLoadedEventStart().is_null();
+}
+
 mojom::ServiceWorkerUpdateViaCache ParseUpdateViaCache(const String& value) {
   if (value == "imports")
     return mojom::ServiceWorkerUpdateViaCache::kImports;
@@ -122,6 +127,27 @@
 
 }  // namespace
 
+class ServiceWorkerContainer::DomContentLoadedListener final
+    : public NativeEventListener {
+ public:
+  void Invoke(ExecutionContext* execution_context, Event* event) override {
+    DCHECK_EQ(event->type(), "DOMContentLoaded");
+
+    Document& document = *To<Document>(execution_context);
+    DCHECK(HasFiredDomContentLoaded(document));
+
+    auto* container =
+        Supplement<Document>::From<ServiceWorkerContainer>(document);
+    if (!container) {
+      // There is no container for some reason, which means there's no message
+      // queue to start. Just abort.
+      return;
+    }
+
+    container->EnableClientMessageQueue();
+  }
+};
+
 class ServiceWorkerContainer::GetRegistrationForReadyCallback
     : public WebServiceWorkerProvider::
           WebServiceWorkerGetRegistrationForReadyCallbacks {
@@ -196,6 +222,7 @@
 void ServiceWorkerContainer::Trace(blink::Visitor* visitor) {
   visitor->Trace(controller_);
   visitor->Trace(ready_);
+  visitor->Trace(dom_content_loaded_observer_);
   visitor->Trace(service_worker_registration_objects_);
   visitor->Trace(service_worker_objects_);
   EventTargetWithInlineData::Trace(visitor);
@@ -473,28 +500,44 @@
     DispatchEvent(*Event::Create(event_type_names::kControllerchange));
 }
 
-void ServiceWorkerContainer::DispatchMessageEvent(
-    WebServiceWorkerObjectInfo info,
-    TransferableMessage message) {
-  if (!GetExecutionContext() || !GetExecutionContext()->ExecutingWindow())
+void ServiceWorkerContainer::ReceiveMessage(WebServiceWorkerObjectInfo source,
+                                            TransferableMessage message) {
+  auto* context = GetExecutionContext();
+  if (!context || !context->ExecutingWindow())
     return;
-  auto msg = ToBlinkTransferableMessage(std::move(message));
-  MessagePortArray* ports =
-      MessagePort::EntanglePorts(*GetExecutionContext(), std::move(msg.ports));
-  ServiceWorker* source =
-      ServiceWorker::From(GetExecutionContext(), std::move(info));
-  MessageEvent* event;
-  if (!msg.locked_agent_cluster_id ||
-      GetExecutionContext()->IsSameAgentCluster(*msg.locked_agent_cluster_id)) {
-    event = MessageEvent::Create(
-        ports, std::move(msg.message),
-        GetExecutionContext()->GetSecurityOrigin()->ToString(),
-        String() /* lastEventId */, source);
-  } else {
-    event = MessageEvent::CreateError(
-        GetExecutionContext()->GetSecurityOrigin()->ToString(), source);
+  // ServiceWorkerContainer is only supported on documents.
+  auto* document = DynamicTo<Document>(context);
+  DCHECK(document);
+
+  if (!is_client_message_queue_enabled_) {
+    if (!HasFiredDomContentLoaded(*document)) {
+      // Wait for DOMContentLoaded. This corresponds to the specification steps
+      // for "Parsing HTML documents": "The end" at
+      // https://html.spec.whatwg.org/multipage/parsing.html#the-end:
+      //
+      // 1. Fire an event named DOMContentLoaded at the Document object, with
+      // its bubbles attribute initialized to true.
+      // 2. Enable the client message queue of the ServiceWorkerContainer object
+      // whose associated service worker client is the Document object's
+      // relevant settings object.
+      if (!dom_content_loaded_observer_) {
+        dom_content_loaded_observer_ =
+            MakeGarbageCollected<DomContentLoadedListener>();
+        document->addEventListener(event_type_names::kDOMContentLoaded,
+                                   dom_content_loaded_observer_.Get(), false);
+      }
+      queued_messages_.emplace_back(std::make_unique<MessageFromServiceWorker>(
+          std::move(source), std::move(message)));
+      // The messages will be dispatched once EnableClientMessageQueue() is
+      // called.
+      return;
+    }
+
+    // DOMContentLoaded was fired already, so enable the queue.
+    EnableClientMessageQueue();
   }
-  EnqueueEvent(*event, TaskType::kServiceWorkerClientMessage);
+
+  DispatchMessageEvent(std::move(source), std::move(message));
 }
 
 void ServiceWorkerContainer::CountFeature(mojom::WebFeature feature) {
@@ -514,6 +557,18 @@
   return event_target_names::kServiceWorkerContainer;
 }
 
+void ServiceWorkerContainer::setOnmessage(EventListener* listener) {
+  SetAttributeEventListener(event_type_names::kMessage, listener);
+  // https://w3c.github.io/ServiceWorker/#dom-serviceworkercontainer-onmessage:
+  // "The first time the context object’s onmessage IDL attribute is set, its
+  // client message queue must be enabled."
+  EnableClientMessageQueue();
+}
+
+EventListener* ServiceWorkerContainer::onmessage() {
+  return GetAttributeEventListener(event_type_names::kMessage);
+}
+
 ServiceWorkerRegistration*
 ServiceWorkerContainer::GetOrCreateServiceWorkerRegistration(
     WebServiceWorkerRegistrationObjectInfo info) {
@@ -555,4 +610,45 @@
                                              ReadyProperty::kReady);
 }
 
+void ServiceWorkerContainer::EnableClientMessageQueue() {
+  dom_content_loaded_observer_ = nullptr;
+  if (is_client_message_queue_enabled_) {
+    DCHECK(queued_messages_.empty());
+    return;
+  }
+  is_client_message_queue_enabled_ = true;
+  std::vector<std::unique_ptr<MessageFromServiceWorker>> messages;
+  messages.swap(queued_messages_);
+  for (auto& message : messages) {
+    DispatchMessageEvent(std::move(message->source),
+                         std::move(message->message));
+  }
+}
+
+void ServiceWorkerContainer::DispatchMessageEvent(
+    WebServiceWorkerObjectInfo source,
+    TransferableMessage message) {
+  DCHECK(is_client_message_queue_enabled_);
+
+  auto msg = ToBlinkTransferableMessage(std::move(message));
+  MessagePortArray* ports =
+      MessagePort::EntanglePorts(*GetExecutionContext(), std::move(msg.ports));
+  ServiceWorker* service_worker =
+      ServiceWorker::From(GetExecutionContext(), std::move(source));
+  MessageEvent* event;
+  if (!msg.locked_agent_cluster_id ||
+      GetExecutionContext()->IsSameAgentCluster(*msg.locked_agent_cluster_id)) {
+    event = MessageEvent::Create(
+        ports, std::move(msg.message),
+        GetExecutionContext()->GetSecurityOrigin()->ToString(),
+        String() /* lastEventId */, service_worker);
+  } else {
+    event = MessageEvent::CreateError(
+        GetExecutionContext()->GetSecurityOrigin()->ToString(), service_worker);
+  }
+  // Schedule the event to be dispatched on the correct task source:
+  // https://w3c.github.io/ServiceWorker/#dfn-client-message-queue
+  EnqueueEvent(*event, TaskType::kServiceWorkerClientMessage);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_container.h b/third_party/blink/renderer/modules/service_worker/service_worker_container.h
index 7e9914fe..a15d9754 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_container.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_container.h
@@ -32,6 +32,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_CONTAINER_H_
 
 #include <memory>
+#include <vector>
+
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-blink.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h"
@@ -41,6 +43,7 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/modules/service_worker/message_from_service_worker.h"
 #include "third_party/blink/renderer/modules/service_worker/registration_options.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h"
@@ -91,8 +94,8 @@
   // WebServiceWorkerProviderClient implementation.
   void SetController(WebServiceWorkerObjectInfo,
                      bool should_notify_controller_change) override;
-  void DispatchMessageEvent(WebServiceWorkerObjectInfo,
-                            TransferableMessage) override;
+  void ReceiveMessage(WebServiceWorkerObjectInfo source,
+                      TransferableMessage) override;
   void CountFeature(mojom::WebFeature) override;
 
   // EventTarget overrides.
@@ -100,7 +103,9 @@
   const AtomicString& InterfaceName() const override;
 
   DEFINE_ATTRIBUTE_EVENT_LISTENER(controllerchange, kControllerchange);
-  DEFINE_ATTRIBUTE_EVENT_LISTENER(message, kMessage);
+
+  void setOnmessage(EventListener* listener);
+  EventListener* onmessage();
 
   // Returns the ServiceWorkerRegistration object described by the given info.
   // Creates a new object if needed, or else returns the existing one.
@@ -112,13 +117,19 @@
   ServiceWorker* GetOrCreateServiceWorker(WebServiceWorkerObjectInfo);
 
  private:
+  class DomContentLoadedListener;
   class GetRegistrationForReadyCallback;
+
   using ReadyProperty =
       ScriptPromiseProperty<Member<ServiceWorkerContainer>,
                             Member<ServiceWorkerRegistration>,
                             Member<ServiceWorkerRegistration>>;
   ReadyProperty* CreateReadyProperty();
 
+  void EnableClientMessageQueue();
+  void DispatchMessageEvent(WebServiceWorkerObjectInfo source,
+                            TransferableMessage);
+
   std::unique_ptr<WebServiceWorkerProvider> provider_;
   Member<ServiceWorker> controller_;
   Member<ReadyProperty> ready_;
@@ -137,6 +148,14 @@
               WTF::IntHash<int64_t>,
               WTF::UnsignedWithZeroKeyHashTraits<int64_t>>
       service_worker_objects_;
+
+  // For https://w3c.github.io/ServiceWorker/#dfn-client-message-queue
+  // Rather than pausing/resuming the task runner, we implement enabling the
+  // queue using this flag since the task runner is shared with other task
+  // sources.
+  bool is_client_message_queue_enabled_ = false;
+  std::vector<std::unique_ptr<MessageFromServiceWorker>> queued_messages_;
+  Member<DomContentLoadedListener> dom_content_loaded_observer_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webmidi/navigator_web_midi.cc b/third_party/blink/renderer/modules/webmidi/navigator_web_midi.cc
index 1fa441d..41f87c71 100644
--- a/third_party/blink/renderer/modules/webmidi/navigator_web_midi.cc
+++ b/third_party/blink/renderer/modules/webmidi/navigator_web_midi.cc
@@ -98,6 +98,15 @@
         document,
         WebFeature::
             kRequestMIDIAccessIframeWithSysExOption_ObscuredByFootprinting);
+  } else {
+    // In the recent spec, the step 7 below allows user-agents to prompt the
+    // user for permission regardless of sysex option.
+    // https://webaudio.github.io/web-midi-api/#dom-navigator-requestmidiaccess
+    // https://crbug.com/662000.
+    Deprecation::CountDeprecation(
+        document, document.IsSecureContext()
+                      ? WebFeature::kNoSysexWebMIDIWithoutPermission
+                      : WebFeature::kNoSysexWebMIDIOnInsecureOrigin);
   }
   UseCounter::CountCrossOriginIframe(
       document, WebFeature::kRequestMIDIAccessIframe_ObscuredByFootprinting);
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 36f63ad..09f9eea 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -739,6 +739,8 @@
     "fonts/typesetting_features.h",
     "fonts/unicode_range_set.cc",
     "fonts/unicode_range_set.h",
+    "fonts/utf16_ragel_iterator.cc",
+    "fonts/utf16_ragel_iterator.h",
     "fonts/utf16_text_iterator.cc",
     "fonts/utf16_text_iterator.h",
     "fonts/vdmx_parser.cc",
@@ -1704,6 +1706,7 @@
     "fonts/symbols_iterator_test.cc",
     "fonts/typesetting_features_test.cc",
     "fonts/unicode_range_set_test.cc",
+    "fonts/utf16_ragel_iterator_test.cc",
     "geometry/double_point_test.cc",
     "geometry/double_rect_test.cc",
     "geometry/float_box_test.cc",
diff --git a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h
index 1fb7b8d..2f9cd060 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h
@@ -135,7 +135,7 @@
       // ZWJ and modifier check in order not to split those Emoji sequences.
       if (U_GET_GC_MASK(ch) & (U_GC_M_MASK | U_GC_LM_MASK | U_GC_SK_MASK) ||
           ch == kZeroWidthJoinerCharacter || Character::IsModifier(ch) ||
-          Character::IsEmojiFlagSequenceTag(ch))
+          Character::IsEmojiTagSequence(ch) || ch == kCancelTag)
         continue;
       // Avoid delimiting COMMON/INHERITED alone, which makes harder to
       // identify the script.
diff --git a/third_party/blink/renderer/platform/fonts/symbols_iterator.cc b/third_party/blink/renderer/platform/fonts/symbols_iterator.cc
index 61d35e0b..d24adf3f 100644
--- a/third_party/blink/renderer/platform/fonts/symbols_iterator.cc
+++ b/third_party/blink/renderer/platform/fonts/symbols_iterator.cc
@@ -72,7 +72,8 @@
             next_char_ == kStaffOfAesculapiusCharacter) &&
            previous_font_fallback_priority_ ==
                FontFallbackPriority::kEmojiEmoji) &&
-         !Character::IsEmojiFlagSequenceTag(next_char_)) ||
+         !Character::IsEmojiTagSequence(next_char_) &&
+         kCancelTag != next_char_) ||
         current_font_fallback_priority_ == FontFallbackPriority::kInvalid) {
       current_font_fallback_priority_ =
           FontFallbackPriorityForCharacter(next_char_);
diff --git a/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.cc b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.cc
new file mode 100644
index 0000000..45da419
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.cc
@@ -0,0 +1,64 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h"
+
+#include "third_party/blink/renderer/platform/text/character.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+namespace blink {
+
+namespace {
+
+char EmojiSegmentationCategory(UChar32 codepoint) {
+  // For the grammar to work, we need to check for more specific character
+  // classes first, then expand towards more generic ones. So we match single
+  // characters and small ranges first, then return EMOJI and
+  // EMOJI_TEXT_PRESENTATION for the remaining ones.
+  if (codepoint == kCombiningEnclosingKeycapCharacter)
+    return UTF16RagelIterator::COMBINING_ENCLOSING_KEYCAP;
+  if (codepoint == kCombiningEnclosingCircleBackslashCharacter)
+    return UTF16RagelIterator::COMBINING_ENCLOSING_CIRCLE_BACKSLASH;
+  if (codepoint == kZeroWidthJoinerCharacter)
+    return UTF16RagelIterator::ZWJ;
+  if (codepoint == kVariationSelector15Character)
+    return UTF16RagelIterator::VS15;
+  if (codepoint == kVariationSelector16Character)
+    return UTF16RagelIterator::VS16;
+  if (codepoint == 0x1F3F4)
+    return UTF16RagelIterator::TAG_BASE;
+  if (Character::IsEmojiTagSequence(codepoint))
+    return UTF16RagelIterator::TAG_SEQUENCE;
+  if (codepoint == kCancelTag) {
+    // http://www.unicode.org/reports/tr51/#def_emoji_tag_sequence
+    // defines a TAG_TERM grammar rule for U+E007F CANCEL TAG.
+    return UTF16RagelIterator::TAG_TERM;
+  }
+  if (Character::IsEmojiModifierBase(codepoint))
+    return UTF16RagelIterator::EMOJI_MODIFIER_BASE;
+  if (Character::IsModifier(codepoint))
+    return UTF16RagelIterator::EMOJI_MODIFIER;
+  if (Character::IsRegionalIndicator(codepoint))
+    return UTF16RagelIterator::REGIONAL_INDICATOR;
+  if (Character::IsEmojiKeycapBase(codepoint))
+    return UTF16RagelIterator::KEYCAP_BASE;
+
+  if (Character::IsEmojiEmojiDefault(codepoint))
+    return UTF16RagelIterator::EMOJI_EMOJI_PRESENTATION;
+  if (Character::IsEmojiTextDefault(codepoint))
+    return UTF16RagelIterator::EMOJI_TEXT_PRESENTATION;
+  if (Character::IsEmoji(codepoint))
+    return UTF16RagelIterator::EMOJI;
+
+  // Ragel state machine will interpret unknown category as "any".
+  return UTF16RagelIterator::kMaxEmojiScannerCategory;
+}
+
+}  // namespace
+
+void UTF16RagelIterator::UpdateCachedCategory() {
+  cached_category_ = EmojiSegmentationCategory(Codepoint());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h
new file mode 100644
index 0000000..26a0cdf
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h
@@ -0,0 +1,158 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_UTF16_RAGEL_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_UTF16_RAGEL_ITERATOR_H_
+
+#include <unicode/uchar.h>
+
+#include "base/logging.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+// UTF16RagelIterator is set up on top of a UTF-16 UChar* buffer iterating over
+// a Blink internal text string and as such is used as an adapter between Blink
+// strings and the Ragel-based emoji scanner. It supports forwarding and
+// reversing using arithmetic operators. Dereferencing the iterator means
+// retrieving a character class as defined in the Ragel grammar of
+// third-party/emoji-segmenter. The dereferenced character category is cached
+// since Ragel dereferences multiple times without moving the iterator's cursor.
+class PLATFORM_EXPORT UTF16RagelIterator {
+ public:
+  UTF16RagelIterator() : buffer_(nullptr), buffer_size_(0), cursor_(0){};
+
+  UTF16RagelIterator(const UChar* buffer,
+                     unsigned buffer_size,
+                     unsigned cursor = 0)
+      : buffer_(buffer),
+        buffer_size_(buffer_size),
+        cursor_(cursor),
+        cached_category_(kMaxEmojiScannerCategory) {
+    UpdateCachedCategory();
+  };
+
+  UTF16RagelIterator end() {
+    UTF16RagelIterator ret = *this;
+    ret.cursor_ = buffer_size_;
+    return ret;
+  }
+
+  unsigned cursor() { return cursor_; }
+
+  UTF16RagelIterator& operator+=(int v) {
+    if (v > 0) {
+      U16_FWD_N(buffer_, cursor_, buffer_size_, v);
+    } else if (v < 0) {
+      U16_BACK_N(buffer_, 0, cursor_, -v);
+    }
+    UpdateCachedCategory();
+    return *this;
+  }
+
+  UTF16RagelIterator& operator-=(int v) { return *this += -v; }
+
+  UTF16RagelIterator operator+(int v) {
+    UTF16RagelIterator ret = *this;
+    return ret += v;
+  }
+
+  UTF16RagelIterator operator-(int v) { return *this + -v; }
+
+  int operator-(const UTF16RagelIterator& other) {
+    DCHECK_EQ(buffer_, other.buffer_);
+    return cursor_ - other.cursor_;
+  }
+
+  UTF16RagelIterator& operator++() {
+    DCHECK_LT(cursor_, buffer_size_);
+    U16_FWD_1(buffer_, cursor_, buffer_size_);
+    UpdateCachedCategory();
+    return *this;
+  }
+
+  UTF16RagelIterator& operator--() {
+    DCHECK_GT(cursor_, 0u);
+    U16_BACK_1(buffer_, 0, cursor_);
+    UpdateCachedCategory();
+    return *this;
+  }
+
+  UTF16RagelIterator operator++(int) {
+    UTF16RagelIterator ret = *this;
+    ++(*this);
+    return ret;
+  }
+
+  UTF16RagelIterator operator--(int) {
+    UTF16RagelIterator ret = *this;
+    --(*this);
+    return ret;
+  }
+
+  UTF16RagelIterator operator=(int v) {
+    // We need this integer assignment operator because Ragel has initialization
+    // code for assigning 0 to ts, te.
+    DCHECK_EQ(v, 0);
+    UTF16RagelIterator ret = *this;
+    ret.cursor_ = v;
+    return ret;
+  }
+
+  UChar32 operator*() {
+    CHECK(buffer_size_);
+    return cached_category_;
+  }
+
+  bool operator==(const UTF16RagelIterator& other) const {
+    return buffer_ == other.buffer_ && buffer_size_ == other.buffer_size_ &&
+           cursor_ == other.cursor_;
+  }
+
+  bool operator!=(const UTF16RagelIterator& other) const {
+    return !(*this == other);
+  }
+
+  // Must match the categories defined in third-party/emoji-segmenter/.
+  // TODO(drott): Add static asserts once emoji-segmenter is imported to
+  // third-party.
+  enum EmojiScannerCharacterClass {
+    EMOJI = 0,
+    EMOJI_TEXT_PRESENTATION = 1,
+    EMOJI_EMOJI_PRESENTATION = 2,
+    EMOJI_MODIFIER_BASE = 3,
+    EMOJI_MODIFIER = 4,
+    EMOJI_VS_BASE = 5,
+    REGIONAL_INDICATOR = 6,
+    KEYCAP_BASE = 7,
+    COMBINING_ENCLOSING_KEYCAP = 8,
+    COMBINING_ENCLOSING_CIRCLE_BACKSLASH = 9,
+    ZWJ = 10,
+    VS15 = 11,
+    VS16 = 12,
+    TAG_BASE = 13,
+    TAG_SEQUENCE = 14,
+    TAG_TERM = 15,
+    kMaxEmojiScannerCategory = 16
+  };
+
+ private:
+  UChar32 Codepoint() const {
+    DCHECK_GT(buffer_size_, 0u);
+    UChar32 output;
+    U16_GET(buffer_, 0, cursor_, buffer_size_, output);
+    return output;
+  }
+
+  void UpdateCachedCategory();
+
+  const UChar* buffer_;
+  unsigned buffer_size_;
+  unsigned cursor_;
+  unsigned char cached_category_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_UTF16_RAGEL_ITERATOR_H_
diff --git a/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator_test.cc b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator_test.cc
new file mode 100644
index 0000000..bbe5036
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator_test.cc
@@ -0,0 +1,113 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h"
+
+#include <unicode/unistr.h>
+
+#include "base/stl_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/text/character.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+namespace blink {
+
+TEST(UTF16RagelIteratorTest, CharacterClasses) {
+  UChar32 class_examples_codepoints[] = {
+      kCombiningEnclosingKeycapCharacter,
+      kCombiningEnclosingCircleBackslashCharacter,
+      kZeroWidthJoinerCharacter,
+      kVariationSelector15Character,
+      kVariationSelector16Character,
+      0x1f3f4,
+      0xE0030,
+      kCancelTag,
+      0x261D,
+      0x1F3FB,
+      0x1F1E6,
+      0x0030,
+      0x231A,
+      0x00A9};
+  icu_63::UnicodeString class_examples_unicode_string =
+      icu_63::UnicodeString::fromUTF32(class_examples_codepoints,
+                                       base::size(class_examples_codepoints));
+  char categories[] = {UTF16RagelIterator::COMBINING_ENCLOSING_KEYCAP,
+                       UTF16RagelIterator::COMBINING_ENCLOSING_CIRCLE_BACKSLASH,
+                       UTF16RagelIterator::ZWJ,
+                       UTF16RagelIterator::VS15,
+                       UTF16RagelIterator::VS16,
+                       UTF16RagelIterator::TAG_BASE,
+                       UTF16RagelIterator::TAG_SEQUENCE,
+                       UTF16RagelIterator::TAG_TERM,
+                       UTF16RagelIterator::EMOJI_MODIFIER_BASE,
+                       UTF16RagelIterator::EMOJI_MODIFIER,
+                       UTF16RagelIterator::REGIONAL_INDICATOR,
+                       UTF16RagelIterator::KEYCAP_BASE,
+                       UTF16RagelIterator::EMOJI_EMOJI_PRESENTATION,
+                       UTF16RagelIterator::EMOJI_TEXT_PRESENTATION};
+  UTF16RagelIterator ragel_iterator(
+      reinterpret_cast<const UChar*>(class_examples_unicode_string.getBuffer()),
+      class_examples_unicode_string.length());
+  for (char& category : categories) {
+    CHECK_EQ(category, *ragel_iterator);
+    ragel_iterator++;
+  }
+
+  UTF16RagelIterator reverse_ragel_iterator(
+      reinterpret_cast<const UChar*>(class_examples_unicode_string.getBuffer()),
+      class_examples_unicode_string.length(),
+      class_examples_unicode_string.length() - 1);
+  size_t i = base::size(categories) - 1;
+  while (reverse_ragel_iterator.cursor() > 0) {
+    CHECK_EQ(categories[i], *reverse_ragel_iterator);
+    i--;
+    reverse_ragel_iterator--;
+  };
+}
+
+TEST(UTF16RagelIteratorTest, ArithmeticOperators) {
+  UChar32 class_examples_codepoints[] = {
+      kVariationSelector15Character, kVariationSelector15Character,
+      kVariationSelector15Character, kVariationSelector16Character,
+      kVariationSelector16Character, kVariationSelector16Character,
+  };
+  icu_63::UnicodeString class_examples_unicode_string =
+      icu_63::UnicodeString::fromUTF32(class_examples_codepoints,
+                                       base::size(class_examples_codepoints));
+
+  UTF16RagelIterator ragel_iterator(
+      reinterpret_cast<const UChar*>(class_examples_unicode_string.getBuffer()),
+      class_examples_unicode_string.length());
+
+  CHECK_EQ(*ragel_iterator, UTF16RagelIterator::VS15);
+  CHECK_EQ(*(ragel_iterator + 2), UTF16RagelIterator::VS15);
+  CHECK_EQ(*(ragel_iterator + 3), UTF16RagelIterator::VS16);
+  CHECK_EQ(*(ragel_iterator + 5), UTF16RagelIterator::VS16);
+
+  CHECK_EQ(*(ragel_iterator += 3), UTF16RagelIterator::VS16);
+  CHECK_EQ(*(ragel_iterator += 2), UTF16RagelIterator::VS16);
+  CHECK_EQ(*(ragel_iterator -= 4), UTF16RagelIterator::VS15);
+  CHECK_EQ(*(ragel_iterator += 1), UTF16RagelIterator::VS15);
+
+  ragel_iterator += 3;
+
+  UTF16RagelIterator ragel_iterator_begin = ragel_iterator - 5;
+  CHECK(ragel_iterator != ragel_iterator_begin);
+  CHECK(ragel_iterator == ragel_iterator.end() - 1);
+
+  CHECK_EQ(*ragel_iterator, UTF16RagelIterator::VS16);
+  CHECK_EQ(*(ragel_iterator - 2), UTF16RagelIterator::VS16);
+  CHECK_EQ(*(ragel_iterator - 3), UTF16RagelIterator::VS15);
+  CHECK_EQ(*(ragel_iterator - 5), UTF16RagelIterator::VS15);
+}
+
+TEST(UTF16RagelIteratorTest, InvalidOperationOnEmpty) {
+  UTF16RagelIterator ragel_iterator;
+  CHECK_EQ(ragel_iterator.cursor(), 0u);
+  EXPECT_DEATH_IF_SUPPORTED(ragel_iterator++, "");
+  EXPECT_DEATH_IF_SUPPORTED(ragel_iterator--, "");
+  EXPECT_DEATH_IF_SUPPORTED(*ragel_iterator, "");
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index 99e8d05..6adddf90 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -884,7 +884,16 @@
         properties_->GetFetchClientSettingsObject().GetSecurityOrigin());
   }
 
+  const ResourceType resource_type = factory.GetType();
+
+  if (!RuntimeEnabledFeatures::OutOfBlinkCorsEnabled() &&
+      resource_request.RequestorOrigin()) {
+    resource_request.SetHTTPOriginIfNeeded(
+        resource_request.RequestorOrigin().get());
+  }
+
   WebScopedVirtualTimePauser pauser;
+
   base::Optional<ResourceRequestBlockedReason> blocked_reason =
       PrepareRequest(params, factory, identifier, pauser);
   if (blocked_reason) {
@@ -892,8 +901,6 @@
                                      client);
   }
 
-  ResourceType resource_type = factory.GetType();
-
   if (!params.IsSpeculativePreload()) {
     // Only log if it's not for speculative preload.
     Context().RecordLoadingActivity(resource_request, resource_type,
diff --git a/third_party/blink/renderer/platform/mojo/blink_typemaps.gni b/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
index 20c28f5..4c17940 100644
--- a/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
+++ b/third_party/blink/renderer/platform/mojo/blink_typemaps.gni
@@ -22,6 +22,7 @@
   "//third_party/blink/renderer/platform/mojo/security_origin.typemap",
   "//third_party/blink/renderer/platform/mojo/string.typemap",
   "//third_party/blink/renderer/platform/mojo/time.typemap",
+  "//third_party/blink/renderer/platform/network/http_request_headers.typemap",
   "//third_party/blink/public/platform/modules/bluetooth/bluetooth.typemap",
   "//third_party/blink/public/common/manifest/display_mode.typemap",
   "//third_party/blink/public/common/screen_orientation/screen_orientation_lock_types.typemap",
diff --git a/third_party/blink/renderer/platform/network/BUILD.gn b/third_party/blink/renderer/platform/network/BUILD.gn
index c8925e32..8db01f6 100644
--- a/third_party/blink/renderer/platform/network/BUILD.gn
+++ b/third_party/blink/renderer/platform/network/BUILD.gn
@@ -39,6 +39,8 @@
     "http_header_map.h",
     "http_parsers.cc",
     "http_parsers.h",
+    "http_request_headers_mojom_traits.cc",
+    "http_request_headers_mojom_traits.h",
     "mime/content_type.cc",
     "mime/content_type.h",
     "mime/mime_type_from_url.cc",
diff --git a/third_party/blink/renderer/platform/network/OWNERS b/third_party/blink/renderer/platform/network/OWNERS
new file mode 100644
index 0000000..7aebc8abb
--- /dev/null
+++ b/third_party/blink/renderer/platform/network/OWNERS
@@ -0,0 +1,4 @@
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/third_party/blink/renderer/platform/network/http_request_headers.typemap b/third_party/blink/renderer/platform/network/http_request_headers.typemap
new file mode 100644
index 0000000..8408b096
--- /dev/null
+++ b/third_party/blink/renderer/platform/network/http_request_headers.typemap
@@ -0,0 +1,9 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//services/network/public/mojom/http_request_headers.mojom"
+public_headers =
+    [ "//third_party/blink/renderer/platform/network/http_header_map.h" ]
+traits_headers = [ "third_party/blink/renderer/platform/network/http_request_headers_mojom_traits.h" ]
+type_mappings = [ "network.mojom.HttpRequestHeaders = blink::HTTPHeaderMap" ]
diff --git a/third_party/blink/renderer/platform/network/http_request_headers_mojom_traits.cc b/third_party/blink/renderer/platform/network/http_request_headers_mojom_traits.cc
new file mode 100644
index 0000000..bb74f26
--- /dev/null
+++ b/third_party/blink/renderer/platform/network/http_request_headers_mojom_traits.cc
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <utility>
+
+#include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
+#include "third_party/blink/renderer/platform/network/http_request_headers_mojom_traits.h"
+
+namespace mojo {
+
+// static
+WTF::Vector<network::mojom::blink::HttpRequestHeaderKeyValuePair>
+StructTraits<network::mojom::HttpRequestHeadersDataView,
+             blink::HTTPHeaderMap>::headers(const blink::HTTPHeaderMap& map) {
+  std::unique_ptr<blink::CrossThreadHTTPHeaderMapData> headers = map.CopyData();
+  WTF::Vector<network::mojom::blink::HttpRequestHeaderKeyValuePair> headers_out;
+  for (const auto& header : *headers) {
+    headers_out.emplace_back(header.first, header.second);
+  }
+  return headers_out;
+}
+
+// static
+bool StructTraits<
+    network::mojom::HttpRequestHeadersDataView,
+    blink::HTTPHeaderMap>::Read(network::mojom::HttpRequestHeadersDataView data,
+                                blink::HTTPHeaderMap* out) {
+  WTF::Vector<network::mojom::blink::HttpRequestHeaderKeyValuePairPtr> headers;
+  if (!data.ReadHeaders(&headers)) {
+    return false;
+  }
+  out->Clear();
+  for (const auto& header : headers) {
+    out->Set(AtomicString(header->key), AtomicString(header->value));
+  }
+  return true;
+}
+
+}  // namespace mojo
diff --git a/third_party/blink/renderer/platform/network/http_request_headers_mojom_traits.h b/third_party/blink/renderer/platform/network/http_request_headers_mojom_traits.h
new file mode 100644
index 0000000..da345991
--- /dev/null
+++ b/third_party/blink/renderer/platform/network/http_request_headers_mojom_traits.h
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HTTP_REQUEST_HEADERS_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HTTP_REQUEST_HEADERS_MOJOM_TRAITS_H_
+
+#include "services/network/public/mojom/http_request_headers.mojom-blink.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<network::mojom::HttpRequestHeadersDataView,
+                    blink::HTTPHeaderMap> {
+  static WTF::Vector<network::mojom::blink::HttpRequestHeaderKeyValuePair>
+  headers(const blink::HTTPHeaderMap& map);
+
+  static bool Read(network::mojom::HttpRequestHeadersDataView data,
+                   blink::HTTPHeaderMap* out);
+};
+
+}  // namespace mojo
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_NETWORK_HTTP_REQUEST_HEADERS_MOJOM_TRAITS_H_
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 1182aa6..4e3fe38 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -113,6 +113,8 @@
     },
     {
       name: "AutoPictureInPicture",
+      depends_on: ["PictureInPictureAPI"],
+      origin_trial_feature_name: "AutoPictureInPicture",
       status: "experimental",
     },
     {
@@ -1391,10 +1393,6 @@
       status: "test",
     },
     {
-      name: "WebFontsCacheAwareTimeoutAdaptation",
-      status: "experimental",
-    },
-    {
       name: "WebGL2ComputeContext",
       status: "experimental",
     },
diff --git a/third_party/blink/renderer/platform/text/character.cc b/third_party/blink/renderer/platform/text/character.cc
index 5cf83cd6..f5877135 100644
--- a/third_party/blink/renderer/platform/text/character.cc
+++ b/third_party/blink/renderer/platform/text/character.cc
@@ -258,12 +258,10 @@
   return true;
 }
 
-bool Character::IsEmojiFlagSequenceTag(UChar32 c) {
-  // Only allow valid sequences from
+bool Character::IsEmojiTagSequence(UChar32 c) {
   // http://www.unicode.org/reports/tr51/proposed.html#valid-emoji-tag-sequences
   return (c >= kTagDigitZero && c <= kTagDigitNine) ||
-         (c >= kTagLatinSmallLetterA && c <= kTagLatinSmallLetterZ) ||
-         c == kCancelTag;
+         (c >= kTagLatinSmallLetterA && c <= kTagLatinSmallLetterZ);
 }
 
 template <typename CharacterType>
diff --git a/third_party/blink/renderer/platform/text/character.h b/third_party/blink/renderer/platform/text/character.h
index f0ccecc..f7d27b4 100644
--- a/third_party/blink/renderer/platform/text/character.h
+++ b/third_party/blink/renderer/platform/text/character.h
@@ -153,7 +153,7 @@
   static bool IsRegionalIndicator(UChar32);
   static bool IsModifier(UChar32 c) { return c >= 0x1F3FB && c <= 0x1F3FF; }
   // http://www.unicode.org/reports/tr51/proposed.html#flag-emoji-tag-sequences
-  static bool IsEmojiFlagSequenceTag(UChar32);
+  static bool IsEmojiTagSequence(UChar32);
 
   static inline UChar NormalizeSpaces(UChar character) {
     if (TreatAsSpace(character))
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index cb10f09..577d999 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2318,6 +2318,11 @@
 crbug.com/400841 virtual/video-surface-layer/media/video-canvas-draw.html [ Failure ]
 crbug.com/400829 virtual/stable/media/stable/video-object-fit-stable.html [ Failure ]
 
+# These contain faulty expectations. https://chromium-review.googlesource.com/c/v8/v8/+/1350790 should fix the
+# faulty return values but to allow landing that patch, the tests have to be deactivated in the meanwhile.
+crbug.com/924308 http/tests/devtools/console/console-dir-es6.js [ Pass Failure ]
+crbug.com/924308 http/tests/devtools/console/console-format-es6-2.js [ Pass Failure ]
+
 # We only want to run one of the web-animations-api tests in stable mode.
 crbug.com/441553 virtual/stable/web-animations-api [ Skip ]
 # These tests *only* run in stable, to verify that these features are unsupported and throw exceptions.
@@ -2983,6 +2988,16 @@
 crbug.com/918664 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/block-size-with-min-or-max-content-table-1a.html [ Failure Pass ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Mac10.10 ] external/wpt/content-security-policy/inside-worker/shared-inheritance.html [ Timeout ]
+crbug.com/626703 [ Mac10.10 ] external/wpt/content-security-policy/frame-ancestors/frame-ancestors-self-block.html [ Timeout ]
+crbug.com/626703 [ Mac10.10 ] external/wpt/fetch/api/cors/cors-redirect.any.html [ Timeout ]
+crbug.com/626703 [ Mac10.10 ] external/wpt/content-security-policy/frame-ancestors/frame-ancestors-nested-cross-in-cross-self-block.html [ Timeout ]
+crbug.com/626703 [ Mac10.10 ] virtual/outofblink-cors/external/wpt/fetch/api/cors/cors-redirect-credentials.any.worker.html [ Timeout ]
+crbug.com/626703 [ Mac10.10 ] virtual/not-site-per-process/external/wpt/html/browsers/the-windowproxy-exotic-object/windowproxy-prototype-setting-same-origin-domain.sub.html [ Timeout ]
+crbug.com/626703 [ Mac10.10 ] external/wpt/html/dom/self-origin.sub.html [ Timeout ]
+crbug.com/626703 [ Mac10.10 ] external/wpt/fetch/api/cors/cors-redirect-preflight.any.html [ Timeout ]
+crbug.com/626703 [ Mac10.10 ] virtual/outofblink-cors-ns/external/wpt/fetch/api/cors/cors-cookies-redirect.any.worker.html [ Timeout ]
+crbug.com/626703 [ Mac10.10 ] external/wpt/content-security-policy/frame-ancestors/frame-ancestors-url-block.html [ Timeout ]
 crbug.com/626703 [ Retina ] external/wpt/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-width-height.html [ Crash Timeout ]
 crbug.com/626703 external/wpt/css/css-values/ic-unit-010.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-values/ic-unit-011.html [ Failure ]
@@ -3139,7 +3154,6 @@
 crbug.com/626703 external/wpt/wasm/webapi/rejected-arg.any.worker.html [ Timeout ]
 crbug.com/626703 external/wpt/wasm/webapi/origin.sub.any.serviceworker.html [ Timeout ]
 crbug.com/626703 external/wpt/wasm/webapi/rejected-arg.any.sharedworker.html [ Timeout ]
-crbug.com/626703 [ Mac10.11 ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html [ Failure ]
 crbug.com/626703 external/wpt/wasm/webapi/abort.any.worker.html [ Timeout ]
 crbug.com/626703 external/wpt/wasm/webapi/abort.any.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-will-change/will-change-abspos-cb-dynamic-001.html [ Failure ]
@@ -3366,9 +3380,6 @@
 crbug.com/626703 virtual/layout_ng/external/wpt/css/CSS2/floats/float-nowrap-7.html [ Failure ]
 crbug.com/626703 virtual/layout_ng/external/wpt/css/CSS2/floats/float-nowrap-9.html [ Failure ]
 crbug.com/626703 external/wpt/css/CSS2/floats/float-nowrap-7.html [ Failure ]
-crbug.com/626703 [ Retina ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html [ Failure ]
-crbug.com/626703 [ Mac10.10 ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html [ Failure ]
-crbug.com/626703 [ Mac10.12 ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-vertical.html [ Failure ]
 crbug.com/626703 external/wpt/css/selectors/old-tests/css3-modsel-16.xml [ Skip ]
 crbug.com/626703 external/wpt/css/selectors/old-tests/css3-modsel-83.xml [ Failure ]
@@ -4119,20 +4130,10 @@
 crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-quirks-mode.html [ Pass Crash ]
 crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/margin-collapsing-quirks/multicol-standards-mode.html [ Pass Crash Failure ]
 
-# SharedWorker are not exposed in WorkerGlobalScope.
-crbug.com/850662 external/wpt/workers/baseurl/alpha/sharedworker-in-worker.html [ Failure ]
-crbug.com/850662 virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/sharedworker-in-worker.html [ Failure ]
-
 # Android doesn't support SharedWorker.
 crbug.com/154571 [ Android ] external/wpt/workers/semantics/reporting-errors/002.html [ Skip ]
 crbug.com/154571 [ Android ] virtual/omt-worker-fetch/external/wpt/workers/semantics/reporting-errors/002.html [ Skip ]
 
-# crbug.com/861564 is ongoing for module dedicated workers.
-crbug.com/861564 external/wpt/workers/baseurl/alpha/xhr-in-moduleworker.html [ Failure ]
-crbug.com/861564 external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-module.html [ Failure ]
-crbug.com/861564 virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-moduleworker.html [ Failure ]
-crbug.com/861564 virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-module.html [ Failure ]
-
 # Other untriaged test failures, timeouts and crashes from newly-imported WPT tests.
 crbug.com/626703 external/wpt/html/browsers/history/the-location-interface/location-protocol-setter-non-broken.html [ Failure Pass ]
 
@@ -4158,11 +4159,6 @@
 # Off-the-main-thread classic worker script fetch.
 # This fails because running a worker on an opaque origin is blocked.
 crbug.com/835717 virtual/omt-worker-fetch/external/wpt/workers/opaque-origin.html [ Timeout ]
-# These fail because of wrong base url.
-crbug.com/835717 virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-worker.html [ Failure ]
-crbug.com/835717 virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/worker-in-worker.html [ Failure ]
-crbug.com/835717 virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-worker.html [ Failure ]
-crbug.com/835717 virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect.html [ Failure ]
 # This fails because off-the-main-thread script loading goes through different
 # loading path. Although we could remove this test. disable it for now.
 crbug.com/835717 virtual/omt-worker-fetch/fast/workers/chromium/worker-crash-with-invalid-location.html [ Skip ]
@@ -4171,9 +4167,6 @@
 # script loading changes the order of url revocation and script fetch.
 # See also crbug.com/800898.
 crbug.com/835717 virtual/omt-worker-fetch/external/wpt/workers/dedicated-worker-from-blob-url.window.html [ Timeout ]
-# These fail because of various reasons.
-crbug.com/835717 virtual/omt-worker-fetch/http/tests/workers/worker-performance-timeline.html [ Failure ]
-crbug.com/835717 virtual/omt-worker-fetch/http/tests/workers/worker-redirect.html [ Failure ]
 
 # This fails because AllowedByNoSniff::MimeTypeAsScript() blocks the nested worker's
 # worker script, because the script url has a .html file extension.
@@ -5502,9 +5495,7 @@
 # Flaky
 crbug.com/878315 [ Mac ] virtual/threaded/fast/scroll-behavior/overscroll-behavior.html [ Pass Failure ]
 
-# Sheriff 2018-08-23
-crbug.com/877183 [ Linux ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html [ Pass Failure ]
-crbug.com/877183 [ Win ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html [ Pass Failure ]
+crbug.com/898186 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html [ Pass Failure ]
 
 # Sheriff 2018-09-10
 crbug.com/881207 fast/js/regress/splice-to-remove.html [ Timeout Pass ]
@@ -5846,11 +5837,6 @@
 # Likely needs a rebaseline
 crbug.com/921242 [ Mac ] fast/overflow/clip-rects-fixed-ancestor.html [ Failure ]
 
-# Disable temporarily since a V8 side change requires the rebaseline of the test expectations.
-# TODO(tzik): Remove this once https://crrev.com/c/1425277 is in.
-crbug.com/v8/7804 http/tests/devtools/tracing/timeline-js/timeline-microtasks.js [ Pass Failure ]
-crbug.com/v8/7804 virtual/threaded/http/tests/devtools/tracing/timeline-js/timeline-microtasks.js [ Pass Failure ]
-
 # Sheriff 2019-01-24
 crbug.com/924954 http/tests/images/feature-policy-image-policies-with-border-radius.html [ Pass Failure ]
 
@@ -5860,21 +5846,9 @@
 # Sheriff 2019-01-25
 crbug.com/925325 [ Mac ] storage/indexeddb/index-population.html [ Pass Failure ]
 
-# Sheriff 2019-01-28
-crbug.com/925866 [ Mac10.10 ] fast/text/atsui-spacing-features.html [ Failure ]
-crbug.com/925866 [ Mac10.10 ] fast/text/emphasis-complex.html [ Failure ]
-crbug.com/925866 [ Mac10.10 ] fast/text/international/hebrew-vowels.html [ Failure ]
-crbug.com/925866 [ Mac10.10 ] svg/text/combining-character-queries.html [ Failure ]
-crbug.com/925866 [ Mac10.10 ] svg/text/ligature-queries.html [ Failure ]
-crbug.com/925866 [ Mac10.11 ] fast/text/atsui-spacing-features.html [ Failure ]
-crbug.com/925866 [ Mac10.11 ] fast/text/emphasis-complex.html [ Failure ]
-crbug.com/925866 [ Mac10.11 ] fast/text/international/hebrew-vowels.html [ Failure ]
-crbug.com/925866 [ Mac10.11 ] svg/text/combining-character-queries.html [ Failure ]
-crbug.com/925866 [ Mac10.11 ] svg/text/ligature-queries.html [ Failure ]
-crbug.com/925866 [ Mac10.11 ] svg/text/surrogate-pair-queries.html [ Failure ]
-crbug.com/925866 [ Mac10.12 ] fast/text/atsui-spacing-features.html [ Failure ]
-crbug.com/925866 [ Mac10.12 ] fast/text/emphasis-complex.html [ Failure ]
-crbug.com/925866 [ Mac10.12 ] fast/text/international/hebrew-vowels.html [ Failure ]
-crbug.com/925866 [ Mac10.12 ] svg/text/combining-character-queries.html [ Failure ]
-crbug.com/925866 [ Mac10.12 ] svg/text/ligature-queries.html [ Failure ]
-crbug.com/925866 [ Mac10.12 ] svg/text/surrogate-pair-queries.html [ Failure ]
+crbug.com/v8/8319 external/wpt/wasm/jsapi/constructor/instantiate.any.html [ Failure ]
+crbug.com/v8/8319 external/wpt/wasm/jsapi/constructor/instantiate.any.worker.html [ Failure ]
+crbug.com/v8/8319 external/wpt/wasm/webapi/instantiateStreaming.any.html [ Failure ]
+crbug.com/v8/8319 external/wpt/wasm/webapi/instantiateStreaming.any.serviceworker.html [ Failure ]
+crbug.com/v8/8319 external/wpt/wasm/webapi/instantiateStreaming.any.sharedworker.html [ Failure ]
+crbug.com/v8/8319 external/wpt/wasm/webapi/instantiateStreaming.any.worker.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index b8d5939..66a40e1 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -39659,6 +39659,18 @@
      {}
     ]
    ],
+   "css/css-display/display-none-inline-img.html": [
+    [
+     "/css/css-display/display-none-inline-img.html",
+     [
+      [
+       "/css/css-display/display-none-inline-img-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-display/select-4-option-optgroup-display-none.html": [
     [
      "/css/css-display/select-4-option-optgroup-display-none.html",
@@ -127375,6 +127387,11 @@
      {}
     ]
    ],
+   "css/css-display/display-none-inline-img-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-display/select-4-option-optgroup-display-none-ref.html": [
     [
      {}
@@ -186130,6 +186147,16 @@
      {}
     ]
    ],
+   "wasm/jsapi/constructor/instantiate.any-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "wasm/jsapi/constructor/instantiate.any.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "wasm/jsapi/global/constructor.any-expected.txt": [
     [
      {}
@@ -186170,6 +186197,16 @@
      {}
     ]
    ],
+   "wasm/jsapi/instance/constructor.any-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "wasm/jsapi/instance/constructor.any.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "wasm/jsapi/instanceTestFactory.js": [
     [
      {}
@@ -186215,6 +186252,16 @@
      {}
     ]
    ],
+   "wasm/jsapi/module/exports.any-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "wasm/jsapi/module/exports.any.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "wasm/jsapi/table/assertions.js": [
     [
      {}
@@ -186430,6 +186477,46 @@
      {}
     ]
    ],
+   "wasm/webapi/instantiateStreaming.any-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "wasm/webapi/instantiateStreaming.any.serviceworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "wasm/webapi/instantiateStreaming.any.sharedworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "wasm/webapi/instantiateStreaming.any.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "wasm/webapi/invalid-code.any-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "wasm/webapi/invalid-code.any.serviceworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "wasm/webapi/invalid-code.any.sharedworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "wasm/webapi/invalid-code.any.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "wasm/webapi/status.py": [
     [
      {}
@@ -186995,6 +187082,11 @@
      {}
     ]
    ],
+   "webaudio/the-audio-api/the-audiobuffersourcenode-interface/resources/sub-sample-scheduling.html": [
+    [
+     {}
+    ]
+   ],
    "webaudio/the-audio-api/the-audiocontext-interface/.gitkeep": [
     [
      {}
@@ -190560,6 +190652,16 @@
      {}
     ]
    ],
+   "workers/baseurl/alpha/sharedworker-in-worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "workers/baseurl/alpha/xhr-in-moduleworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "workers/baseurl/beta/import.py": [
     [
      {}
@@ -190785,6 +190887,11 @@
      {}
     ]
    ],
+   "workers/interfaces/WorkerGlobalScope/location/redirect-module-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "workers/interfaces/WorkerGlobalScope/location/redirect.js": [
     [
      {}
@@ -213091,6 +213198,12 @@
      {}
     ]
    ],
+   "css/css-tables/no-overflow-with-table-cell-margins.html": [
+    [
+     "/css/css-tables/no-overflow-with-table-cell-margins.html",
+     {}
+    ]
+   ],
    "css/css-tables/parsing/border-collapse-computed.html": [
     [
      "/css/css-tables/parsing/border-collapse-computed.html",
@@ -283129,6 +283242,12 @@
      {}
     ]
    ],
+   "webaudio/the-audio-api/the-pannernode-interface/panner-azimuth.html": [
+    [
+     "/webaudio/the-audio-api/the-pannernode-interface/panner-azimuth.html",
+     {}
+    ]
+   ],
    "webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html": [
     [
      "/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html",
@@ -333876,6 +333995,14 @@
    "f8d6e85cee2325f3ae51a950a276430d26c04189",
    "testharness"
   ],
+  "css/css-display/display-none-inline-img-ref.html": [
+   "a3e7369afbae5c35cabbda9aa1a0fe8ab4521af4",
+   "support"
+  ],
+  "css/css-display/display-none-inline-img.html": [
+   "f97ca7bbd3519453b362405c542a065a25c43368",
+   "reftest"
+  ],
   "css/css-display/inheritance.html": [
    "bfd072651cb6ec82cca7d9be5b6768afbc39fca0",
    "testharness"
@@ -352329,11 +352456,11 @@
    "testharness"
   ],
   "css/css-position/position-absolute-replaced-minmax-expected.txt": [
-   "5f793613544a689f9adfbb6eeceac8573deec813",
+   "04879688d39bde3776226c5254f7df8a8a10c638",
    "support"
   ],
   "css/css-position/position-absolute-replaced-minmax.html": [
-   "3f500a0de09943e003cc234f6ad1604418f1a02f",
+   "644b147a227e100c500de2de9e4f8e8449a4a21e",
    "testharness"
   ],
   "css/css-position/position-relative-table-left-ref.html": [
@@ -355565,7 +355692,7 @@
    "testharness"
   ],
   "css/css-syntax/escaped-eof.html": [
-   "66ac8e9442e22c8d5b445d556c667f769c0939e2",
+   "5d47c34ac512fab7eb9c37e64b0c677d09a8e3d3",
    "testharness"
   ],
   "css/css-syntax/ident-three-code-points.html": [
@@ -355852,6 +355979,10 @@
    "a8745487b6702b8b8e8ac85bd843014dc296b717",
    "reftest"
   ],
+  "css/css-tables/no-overflow-with-table-cell-margins.html": [
+   "9657da9c262cede13451978446508ff8917b53bd",
+   "testharness"
+  ],
   "css/css-tables/parsing/border-collapse-computed.html": [
    "1ad0b6d701a7bbc1b49a9f3d3a34270dbd64ed29",
    "testharness"
@@ -417821,7 +417952,7 @@
    "support"
   ],
   "interfaces/orientation-sensor.idl": [
-   "49acba9e201d28753103964db7458d324be50bdf",
+   "a9aec968e0a072df3bf1eb1452045958f693bc96",
    "support"
   ],
   "interfaces/page-visibility.idl": [
@@ -442949,7 +443080,7 @@
    "testharness"
   ],
   "service-workers/service-worker/claim-fetch.https.html": [
-   "6b7d353c6ae3b53c5c7d9398f09fb2f476201db1",
+   "400b593ac961942f6e9274aee98bac22856eaf22",
    "testharness"
   ],
   "service-workers/service-worker/claim-not-using-registration.https.html": [
@@ -450053,7 +450184,7 @@
    "support"
   ],
   "wasm/jsapi/constructor/compile.any-expected.txt": [
-   "f4d512c50239b28f934decb6559ab69b332b984a",
+   "3074f0a50e4e8cdd16d37239378e5a2a1dac108b",
    "support"
   ],
   "wasm/jsapi/constructor/compile.any.js": [
@@ -450061,17 +450192,25 @@
    "testharness"
   ],
   "wasm/jsapi/constructor/compile.any.worker-expected.txt": [
-   "f4d512c50239b28f934decb6559ab69b332b984a",
+   "3074f0a50e4e8cdd16d37239378e5a2a1dac108b",
    "support"
   ],
   "wasm/jsapi/constructor/instantiate-bad-imports.any.js": [
    "86700298dfae66de6f4d026baa29e6e3584320f7",
    "testharness"
   ],
+  "wasm/jsapi/constructor/instantiate.any-expected.txt": [
+   "aa74ef6d6227f1d27f72087e0d2414ed86fde6cd",
+   "support"
+  ],
   "wasm/jsapi/constructor/instantiate.any.js": [
    "97350c5acd2c4e7d6380538ed7983ff5c53e5bf5",
    "testharness"
   ],
+  "wasm/jsapi/constructor/instantiate.any.worker-expected.txt": [
+   "aa74ef6d6227f1d27f72087e0d2414ed86fde6cd",
+   "support"
+  ],
   "wasm/jsapi/constructor/validate.any.js": [
    "c8613cbd9b3a467a919d87d3244c4f508dce6317",
    "testharness"
@@ -450132,10 +450271,18 @@
    "24c51c10dc5df9d52c06bfb0715e435b17f24f7a",
    "testharness"
   ],
+  "wasm/jsapi/instance/constructor.any-expected.txt": [
+   "6e06ee0063f0e83fdbbf769b0ba0d723a64f064c",
+   "support"
+  ],
   "wasm/jsapi/instance/constructor.any.js": [
    "e6a0450202e94baa82eb4797fed6a7c248ed00a7",
    "testharness"
   ],
+  "wasm/jsapi/instance/constructor.any.worker-expected.txt": [
+   "6e06ee0063f0e83fdbbf769b0ba0d723a64f064c",
+   "support"
+  ],
   "wasm/jsapi/instance/exports.any.js": [
    "cad468660e099b33f0a03b83a09df0498d67a7e0",
    "testharness"
@@ -450208,10 +450355,18 @@
    "10fcc31f04439b5c498d157681c1c4a0b4c824c9",
    "support"
   ],
+  "wasm/jsapi/module/exports.any-expected.txt": [
+   "203474bb03512ceb8005bec8209244cc2bb06077",
+   "support"
+  ],
   "wasm/jsapi/module/exports.any.js": [
    "4437daa052fad56e66b7bf9991c5f045689564e3",
    "testharness"
   ],
+  "wasm/jsapi/module/exports.any.worker-expected.txt": [
+   "203474bb03512ceb8005bec8209244cc2bb06077",
+   "support"
+  ],
   "wasm/jsapi/module/imports.any.js": [
    "b3bb8598b080c376f4de1294780e6072eac2b354",
    "testharness"
@@ -450269,11 +450424,11 @@
    "testharness"
   ],
   "wasm/jsapi/wasm-constants.js": [
-   "18748e6cf540db200a83de6d151dc8d77d1b25e1",
+   "e3846386b8dccada9c24da194950627f4c39836f",
    "support"
   ],
   "wasm/jsapi/wasm-module-builder.js": [
-   "c01b71733d667b8d299971f99c3133be9bf08d8b",
+   "e13f4157873baf0896f42b9fd340588f4453a14a",
    "support"
   ],
   "wasm/resources/load_wasm.js": [
@@ -450492,18 +450647,50 @@
    "b1efba31e9192654f2790edf3c5c391bb812a9eb",
    "testharness"
   ],
+  "wasm/webapi/instantiateStreaming.any-expected.txt": [
+   "1db6c71ae5caecbd09899d7d9700f5db56d6cc5d",
+   "support"
+  ],
   "wasm/webapi/instantiateStreaming.any.js": [
    "224b0a3d0b78af89998992ec1aed212f307b6866",
    "testharness"
   ],
+  "wasm/webapi/instantiateStreaming.any.serviceworker-expected.txt": [
+   "1db6c71ae5caecbd09899d7d9700f5db56d6cc5d",
+   "support"
+  ],
+  "wasm/webapi/instantiateStreaming.any.sharedworker-expected.txt": [
+   "1db6c71ae5caecbd09899d7d9700f5db56d6cc5d",
+   "support"
+  ],
+  "wasm/webapi/instantiateStreaming.any.worker-expected.txt": [
+   "1db6c71ae5caecbd09899d7d9700f5db56d6cc5d",
+   "support"
+  ],
   "wasm/webapi/invalid-args.any.js": [
    "e6c467a4dcba76be7170ae4c1670713e801b4ce4",
    "testharness"
   ],
+  "wasm/webapi/invalid-code.any-expected.txt": [
+   "90be5923681eb21512b039c910735bc0af4c8adb",
+   "support"
+  ],
   "wasm/webapi/invalid-code.any.js": [
    "e30b7cf339458ea57b38d81ea5feaf00ca5c78fc",
    "testharness"
   ],
+  "wasm/webapi/invalid-code.any.serviceworker-expected.txt": [
+   "90be5923681eb21512b039c910735bc0af4c8adb",
+   "support"
+  ],
+  "wasm/webapi/invalid-code.any.sharedworker-expected.txt": [
+   "90be5923681eb21512b039c910735bc0af4c8adb",
+   "support"
+  ],
+  "wasm/webapi/invalid-code.any.worker-expected.txt": [
+   "90be5923681eb21512b039c910735bc0af4c8adb",
+   "support"
+  ],
   "wasm/webapi/origin.sub.any.js": [
    "a9809c0510246db065236d8412c49c5151222326",
    "testharness"
@@ -451401,7 +451588,7 @@
    "support"
   ],
   "webaudio/resources/biquad-testing.js": [
-   "7a0b6e6c1f8a3d616620425731603f0b43c5ff84",
+   "7f90a1f72bec6be2cfc3a1256a40132958632ca9",
    "support"
   ],
   "webaudio/resources/convolution-testing.js": [
@@ -451413,7 +451600,7 @@
    "support"
   ],
   "webaudio/resources/distance-model-testing.js": [
-   "1b9adde403e37933100a7ef13369aa3e40d5515b",
+   "f8a6cf940a96f197461f605f1bf527175f63670a",
    "support"
   ],
   "webaudio/resources/merger-testing.js": [
@@ -451429,7 +451616,7 @@
    "support"
   ],
   "webaudio/resources/note-grain-on-testing.js": [
-   "1e941897161228f043d9acd128bd3b44b693b7d1",
+   "ad0631670df932c63c5029ea1f267de5032c9fa9",
    "support"
   ],
   "webaudio/resources/panner-formulas.js": [
@@ -451437,7 +451624,7 @@
    "support"
   ],
   "webaudio/resources/panner-model-testing.js": [
-   "662fb1d68c5361464b21e8a08c5e23de1a558515",
+   "4df3e178134210d9bb55da1100dd98880aa86801",
    "support"
   ],
   "webaudio/resources/sin_440Hz_-6dBFS_1s.wav": [
@@ -451449,7 +451636,7 @@
    "support"
   ],
   "webaudio/resources/stereopanner-testing.js": [
-   "d6238a9cd367b00137027735fd0ff7d3c3aae3b7",
+   "2778493e3b6c12d4c00c77bd975e845063621522",
    "support"
   ],
   "webaudio/the-audio-api/the-analysernode-interface/.gitkeep": [
@@ -451545,7 +451732,7 @@
    "testharness"
   ],
   "webaudio/the-audio-api/the-audiobuffersourcenode-interface/audiobuffersource-playbackrate-zero.html": [
-   "58ee49e42d279e5a28d78aa32c34e0e511850df2",
+   "5624054e3289a18d48fab3b4942f3bb16b9d1c03",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audiobuffersourcenode-interface/audiobuffersource-start.html": [
@@ -451580,6 +451767,10 @@
    "ab9d5fe5a9dbd736a079f0cfd7966d5e064ed7ef",
    "support"
   ],
+  "webaudio/the-audio-api/the-audiobuffersourcenode-interface/resources/sub-sample-scheduling.html": [
+   "27ac0984a79ef276f6f6e32dc9814131487de31a",
+   "support"
+  ],
   "webaudio/the-audio-api/the-audiobuffersourcenode-interface/sample-accurate-scheduling.html": [
    "5fafd024eef9b476f4d97b2ebaa2190a4ca520d5",
    "testharness"
@@ -451613,7 +451804,7 @@
    "support"
   ],
   "webaudio/the-audio-api/the-audionode-interface/audionode-channel-rules.html": [
-   "a1c3273cc3fef597eff30e13c6074bd99ddb7acf",
+   "9067e6869bcf682e4f3f945d567a2d8a300d9f4b",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audionode-interface/audionode-connect-method-chaining.html": [
@@ -451733,7 +451924,7 @@
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/k-rate-panner.html": [
-   "ea2a4d1364595865d4c77e3b38449f5ffe3d5da5",
+   "60200b24712c29a92d73b6215b85f734999b8209",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/k-rate-stereo-panner.html": [
@@ -451921,7 +452112,7 @@
    "testharness"
   ],
   "webaudio/the-audio-api/the-biquadfilternode-interface/biquad-lowpass.html": [
-   "d20786e36b16d7ba36841dd5fcfb30c081113fb2",
+   "69dc85a2e2be61da92d8d48f34472e3da8d35fb1",
    "testharness"
   ],
   "webaudio/the-audio-api/the-biquadfilternode-interface/biquad-lowshelf.html": [
@@ -452121,7 +452312,7 @@
    "testharness"
   ],
   "webaudio/the-audio-api/the-gainnode-interface/gain.html": [
-   "cff609de4b795a9d6ab0c465af3b959563d45cce",
+   "c1ee0240cb9f87bc05fb4f5fb076d01b2de45899",
    "testharness"
   ],
   "webaudio/the-audio-api/the-gainnode-interface/no-dezippering.html": [
@@ -452228,6 +452419,10 @@
    "8e09e869acb4b5a0e5dd94e2401494c690954208",
    "testharness"
   ],
+  "webaudio/the-audio-api/the-pannernode-interface/panner-azimuth.html": [
+   "d09f2ec352f052e987178a7ea0cb8a2b83283ccf",
+   "testharness"
+  ],
   "webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html": [
    "96b99a2373bccdab74ebdfd409f0f8adb4acc94d",
    "testharness"
@@ -458620,6 +458815,10 @@
    "55907bf4238dcf84051a8523a00a01224c2e57fb",
    "testharness"
   ],
+  "workers/baseurl/alpha/sharedworker-in-worker-expected.txt": [
+   "882a37d0a3642f4a415edbdb040cb3e369b7f615",
+   "support"
+  ],
   "workers/baseurl/alpha/sharedworker-in-worker.html": [
    "25d2582366eb1b739edc2fb540f41a8b0c125486",
    "testharness"
@@ -458628,6 +458827,10 @@
    "284425ed3407735c8072753d1299ff2c38da1a47",
    "testharness"
   ],
+  "workers/baseurl/alpha/xhr-in-moduleworker-expected.txt": [
+   "93eb19c7be6636379fcb15875d50bed7ab7bc524",
+   "support"
+  ],
   "workers/baseurl/alpha/xhr-in-moduleworker.html": [
    "7597a1357d1dca509ec7315f516bcef9cf4b8a2a",
    "testharness"
@@ -459072,6 +459275,10 @@
    "e850b76b6e5f52389af3d213756ff7fc0e7a1c2d",
    "support"
   ],
+  "workers/interfaces/WorkerGlobalScope/location/redirect-module-expected.txt": [
+   "bbf490d8050b1f4e1d5c11f597f563fc5e5ddb1f",
+   "support"
+  ],
   "workers/interfaces/WorkerGlobalScope/location/redirect-module.html": [
    "b1a14be8f75133497311ff07c3a1d268de0b4244",
    "testharness"
diff --git a/third_party/blink/web_tests/external/wpt/css/css-syntax/escaped-eof.html b/third_party/blink/web_tests/external/wpt/css/css-syntax/escaped-eof.html
index 66ac8e9..5d47c34a 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-syntax/escaped-eof.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-syntax/escaped-eof.html
@@ -15,7 +15,7 @@
 
 <script>
 test(()=>{
-    assert_throws(new SyntaxError, ()=>{document.querySelector("#123");}, "numeric hash token is invalid in a selector");
+    assert_throws("SyntaxError", ()=>{document.querySelector("#123");}, "numeric hash token is invalid in a selector");
     document.querySelector("#foo\\"); // escaped-EOF in a hash token is valid in a selector
 }, "Escaped EOF turns into a U+FFFD in a hash token, makes it 'ID' type.");
 
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/orientation-sensor.idl b/third_party/blink/web_tests/external/wpt/interfaces/orientation-sensor.idl
index 49acba9e..a9aec96 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/orientation-sensor.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/orientation-sensor.idl
@@ -24,3 +24,10 @@
 [Constructor(optional OrientationSensorOptions sensorOptions), SecureContext, Exposed=Window]
 interface RelativeOrientationSensor : OrientationSensor {
 };
+
+dictionary AbsoluteOrientationReadingValues {
+  required FrozenArray<double>? quaternion;
+};
+
+dictionary RelativeOrientationReadingValues : AbsoluteOrientationReadingValues {
+};
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/claim-fetch.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/claim-fetch.https.html
index 6b7d353..400b593 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/claim-fetch.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/claim-fetch.https.html
@@ -7,66 +7,80 @@
 <body>
 <script>
 
-promise_test(function(t) {
-  var frame;
-  var resource = 'simple.txt';
+async function try_fetch(fetch_func, path) {
+  let response;
+  try {
+   response = await fetch_func(path);
+  } catch (err) {
+    throw (`fetch() threw: ${err}`);
+  }
 
-  var worker;
-  var scope = 'resources/';
-  var script = 'resources/claim-worker.js';
+  let response_text;
+  try {
+   response_text = await response.text();
+  } catch (err) {
+   throw (`text() threw: ${err}`);
+  }
 
-  return Promise.resolve()
-    // Create the test iframe.
-    .then(() => with_iframe('resources/blank.html'))
-    .then(f => frame = f)
+  return response_text;
+}
 
-    // Check the controller and test with fetch.
-    .then(() => assert_equals(frame.contentWindow.navigator.controller,
-                              undefined,
-                              'Should have no controller.'))
-    .then(() => frame.contentWindow.fetch(resource))
-    .then(response => response.text())
-    .then(response_text => assert_equals(response_text,
-                                         'a simple text file\n',
-                                         'fetch() should not be intercepted.'))
+promise_test(async function(t) {
+  let frame;
+  const scope = 'resources/';
+  const script = 'resources/claim-worker.js';
+  t.add_cleanup(async () => {
+    if (frame)
+      frame.remove();
+    return service_worker_unregister(t, scope);
+  });
 
-    // Register a service worker.
-    .then(() => service_worker_unregister_and_register(t, script, scope))
-    .then(r => {
-        t.add_cleanup(() => service_worker_unregister(t, scope));
+  const resource = 'simple.txt';
 
-        worker = r.installing;
+  // Create the test frame.
+  await service_worker_unregister(t, scope);
+  frame = await with_iframe('resources/blank.html');
 
-        return wait_for_state(t, worker, 'activated');
-      })
-    // Let the service worker claim the iframe.
-    .then(() => {
-      var channel = new MessageChannel();
-      var saw_message = new Promise(function(resolve) {
-        channel.port1.onmessage = t.step_func(function(e) {
-          assert_equals(e.data, 'PASS',
-                        'Worker call to claim() should fulfill.');
-          resolve();
-        });
-      });
-      worker.postMessage({port: channel.port2}, [channel.port2]);
-      return saw_message;
-    })
+  // Check the controller and test with fetch.
+  assert_equals(frame.contentWindow.navigator.controller, undefined,
+                'Should have no controller.');
+  let response;
+  try {
+    response = await try_fetch(frame.contentWindow.fetch, resource);
+  } catch (err) {
+    assert_unreached(`uncontrolled fetch failed: ${err}`);
+  }
+  assert_equals(response, 'a simple text file\n',
+                'fetch() should not be intercepted.');
 
-    // Check the controller and test with fetch.
-    .then(() => frame.contentWindow.navigator.serviceWorker.getRegistration(scope))
-    .then(r => assert_equals(frame.contentWindow.navigator.serviceWorker.controller,
-                             r.active,
-                             'Test iframe should be claimed.'))
-    .then(() => frame.contentWindow.fetch(resource))
-    .then(response => response.text())
-    .then(response_text => assert_equals(response_text,
-                                         'Intercepted!',
-                                         'fetch() should be intercepted.'))
+  // Register a service worker.
+  const registration = await navigator.serviceWorker.register(script, {scope});
+  const worker = registration.installing;
+  await wait_for_state(t, worker, 'activated');
 
-    // Cleanup this testcase.
-    .then(() => frame.remove())
-}, 'fetch() should be intercepted after the client is claimed.')
+  // Tell the service worker to claim the iframe.
+  const saw_message = new Promise((resolve) => {
+    const channel = new MessageChannel();
+    channel.port1.onmessage = t.step_func((event) => {
+      resolve(event.data);
+    });
+    worker.postMessage({port: channel.port2}, [channel.port2]);
+  });
+  const data = await saw_message;
+  assert_equals(data, 'PASS', 'Worker call to claim() should fulfill.');
+
+  // Check the controller and test with fetch.
+  const controller = frame.contentWindow.navigator.serviceWorker.controller;
+  assert_true(controller instanceof frame.contentWindow.ServiceWorker,
+              'iframe should be controlled.');
+  try {
+    response = await try_fetch(frame.contentWindow.fetch, resource);
+  } catch (err) {
+    assert_unreached(`controlled fetch failed: ${err}`);
+  }
+  assert_equals(response, 'Intercepted!',
+                'fetch() should be intercepted.');
+}, 'fetch() should be intercepted after the client is claimed.');
 
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/clients-get-resultingClientId.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/clients-get-resultingClientId.https-expected.txt
deleted file mode 100644
index f943b7f..0000000
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/clients-get-resultingClientId.https-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-PASS global setup
-PASS get(resultingClientId) for same-origin document
-PASS get(resultingClientId) on cross-origin redirect
-FAIL get(resultingClientId) for document sandboxed by CSP header assert_equals: promiseValue expected "undefinedValue" but got "client"
-PASS get(resultingClientId) for document sandboxed by CSP header with allow-same-origin
-PASS global cleanup
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/sandboxed-iframe-fetch-event.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/sandboxed-iframe-fetch-event.https-expected.txt
deleted file mode 100644
index 8e2f0cd..0000000
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/sandboxed-iframe-fetch-event.https-expected.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-This is a testharness.js-based test.
-PASS Prepare a service worker.
-PASS Prepare a normal iframe.
-PASS Prepare an iframe sandboxed by <iframe sandbox="allow-scripts">.
-PASS Prepare an iframe sandboxed by <iframe sandbox="allow-scripts allow-same-origin">.
-FAIL Prepare an iframe sandboxed by CSP HTTP header with allow-scripts. assert_false: Service worker should NOT control the sandboxed page expected false got true
-PASS Prepare an iframe sandboxed by CSP HTTP header with allow-scripts and allow-same-origin.
-PASS Fetch request from a normal iframe
-PASS Fetch request from a worker in a normal iframe
-PASS Request for an iframe in the normal iframe
-PASS Request for an sandboxed iframe with allow-scripts flag in the normal iframe
-PASS Request for an sandboxed iframe with allow-scripts and allow-same-origin flag in the normal iframe
-PASS Fetch request from iframe sandboxed by an attribute with allow-scripts flag
-PASS Fetch request from a worker in iframe sandboxed by an attribute with allow-scripts flag
-PASS Request for an iframe in the iframe sandboxed by an attribute with allow-scripts flag
-PASS Request for an sandboxed iframe with allow-scripts flag in the iframe sandboxed by an attribute with allow-scripts flag
-PASS Request for an sandboxed iframe with allow-scripts and allow-same-origin flag in the iframe sandboxed by an attribute with allow-scripts flag
-PASS Fetch request from iframe sandboxed by an attribute with allow-scripts and allow-same-origin flag
-PASS Fetch request from a worker in iframe sandboxed by an attribute with allow-scripts and allow-same-origin flag
-PASS Request for an iframe in the iframe sandboxed by an attribute with allow-scripts and allow-same-origin flag
-PASS Request for an sandboxed iframe with allow-scripts flag in the iframe sandboxed by attribute with allow-scripts and allow-same-origin flag
-PASS Request for an sandboxed iframe with allow-scripts and allow-same-origin flag in the iframe sandboxed by attribute with allow-scripts and allow-same-origin flag
-FAIL Fetch request from iframe sandboxed by CSP HTTP header with allow-scripts flag assert_equals: The request should NOT be handled by SW. expected 0 but got 1
-PASS Request for an iframe in the iframe sandboxed by CSP HTTP header with allow-scripts flag
-PASS Request for an sandboxed iframe with allow-scripts flag in the iframe sandboxed by CSP HTTP header with allow-scripts flag
-PASS Request for an sandboxed iframe with allow-scripts and allow-same-origin flag in the iframe sandboxed by CSP HTTP header with allow-scripts flag
-PASS Fetch request from iframe sandboxed by CSP HTTP header with allow-scripts and allow-same-origin flag
-PASS Request for an iframe in the iframe sandboxed by CSP HTTP header with allow-scripts and allow-same-origin flag
-PASS Request for an sandboxed iframe with allow-scripts flag in the iframe sandboxed by CSP HTTP header with allow-scripts and allow-same-origin flag
-PASS Request for an sandboxed iframe with allow-scripts and allow-same-origin flag in the iframe sandboxed by CSP HTTP header with allow-scripts and allow-same-origin flag
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/worker-in-sandboxed-iframe-by-csp-fetch-event.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/worker-in-sandboxed-iframe-by-csp-fetch-event.https-expected.txt
deleted file mode 100644
index b2dc1e1..0000000
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/worker-in-sandboxed-iframe-by-csp-fetch-event.https-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-PASS Prepare a service worker.
-FAIL Prepare an iframe sandboxed by CSP HTTP header with allow-scripts. assert_false: Service worker should NOT control the sandboxed page expected false got true
-PASS Prepare an iframe sandboxed by CSP HTTP header with allow-scripts and allow-same-origin.
-FAIL Fetch request from a worker in iframe sandboxed by CSP HTTP header allow-scripts flag assert_equals: The request should NOT be handled by SW. expected 0 but got 1
-PASS Fetch request from a worker in iframe sandboxed by CSP HTTP header with allow-scripts and allow-same-origin flag
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/compile.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/compile.any-expected.txt
index f4d512c..3074f0a 100644
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/compile.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/compile.any-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = AsyncCompile: Wasm decoding failed: expected 4 bytes, fell off end @+0
+Harness Error. harness_status.status = 1 , harness_status.message = AsyncCompile: Wasm decoding failed: expected string length @+10
 PASS Missing argument
 PASS Invalid arguments
 PASS Branding
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/compile.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/compile.any.worker-expected.txt
index f4d512c..3074f0a 100644
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/compile.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/compile.any.worker-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = AsyncCompile: Wasm decoding failed: expected 4 bytes, fell off end @+0
+Harness Error. harness_status.status = 1 , harness_status.message = AsyncCompile: Wasm decoding failed: expected string length @+10
 PASS Missing argument
 PASS Invalid arguments
 PASS Branding
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/instantiate.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/instantiate.any-expected.txt
new file mode 100644
index 0000000..aa74ef6d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/instantiate.any-expected.txt
@@ -0,0 +1,26 @@
+This is a testharness.js-based test.
+PASS Missing arguments
+PASS Branding
+PASS Invalid arguments
+PASS Promise type
+PASS Empty module without imports argument: BufferSource argument
+PASS Empty module without imports argument: Module argument
+PASS Empty module with undefined imports argument: BufferSource argument
+PASS Empty module with undefined imports argument: Module argument
+PASS Empty module with empty imports argument: BufferSource argument
+PASS Empty module with empty imports argument: Module argument
+PASS getter order for imports object: BufferSource argument
+PASS getter order for imports object: Module argument
+PASS imports: BufferSource argument
+PASS imports: Module argument
+FAIL No imports: BufferSource argument builder.setTableLength is not a function
+FAIL No imports: Module argument builder.setTableLength is not a function
+FAIL exports and imports: BufferSource argument promise_test: Unhandled rejection with value: object "CompileError: AsyncCompilation: (null): Compiling wasm function "wasm-function[0]" failed: function body must end with "end" opcode @+55"
+FAIL exports and imports: Module argument WebAssembly.Module(): Compiling wasm function "wasm-function[0]" failed: function body must end with "end" opcode @+55
+PASS stray argument: BufferSource argument
+PASS stray argument: Module argument
+PASS Empty buffer
+PASS Invalid code
+PASS Changing the buffer
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/instantiate.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/instantiate.any.worker-expected.txt
new file mode 100644
index 0000000..aa74ef6d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/constructor/instantiate.any.worker-expected.txt
@@ -0,0 +1,26 @@
+This is a testharness.js-based test.
+PASS Missing arguments
+PASS Branding
+PASS Invalid arguments
+PASS Promise type
+PASS Empty module without imports argument: BufferSource argument
+PASS Empty module without imports argument: Module argument
+PASS Empty module with undefined imports argument: BufferSource argument
+PASS Empty module with undefined imports argument: Module argument
+PASS Empty module with empty imports argument: BufferSource argument
+PASS Empty module with empty imports argument: Module argument
+PASS getter order for imports object: BufferSource argument
+PASS getter order for imports object: Module argument
+PASS imports: BufferSource argument
+PASS imports: Module argument
+FAIL No imports: BufferSource argument builder.setTableLength is not a function
+FAIL No imports: Module argument builder.setTableLength is not a function
+FAIL exports and imports: BufferSource argument promise_test: Unhandled rejection with value: object "CompileError: AsyncCompilation: (null): Compiling wasm function "wasm-function[0]" failed: function body must end with "end" opcode @+55"
+FAIL exports and imports: Module argument WebAssembly.Module(): Compiling wasm function "wasm-function[0]" failed: function body must end with "end" opcode @+55
+PASS stray argument: BufferSource argument
+PASS stray argument: Module argument
+PASS Empty buffer
+PASS Invalid code
+PASS Changing the buffer
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/instance/constructor.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/instance/constructor.any-expected.txt
new file mode 100644
index 0000000..6e06ee0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/instance/constructor.any-expected.txt
@@ -0,0 +1,16 @@
+This is a testharness.js-based test.
+PASS name
+PASS length
+PASS No arguments
+PASS Non-Module arguments
+PASS Calling
+PASS Empty module without imports argument
+PASS Empty module with undefined imports argument
+PASS Empty module with empty imports argument
+PASS getter order for imports object
+PASS imports
+FAIL No imports builder.setTableLength is not a function
+FAIL exports and imports WebAssembly.Module(): Compiling wasm function "wasm-function[0]" failed: function body must end with "end" opcode @+55
+PASS stray argument
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/instance/constructor.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/instance/constructor.any.worker-expected.txt
new file mode 100644
index 0000000..6e06ee0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/instance/constructor.any.worker-expected.txt
@@ -0,0 +1,16 @@
+This is a testharness.js-based test.
+PASS name
+PASS length
+PASS No arguments
+PASS Non-Module arguments
+PASS Calling
+PASS Empty module without imports argument
+PASS Empty module with undefined imports argument
+PASS Empty module with empty imports argument
+PASS getter order for imports object
+PASS imports
+FAIL No imports builder.setTableLength is not a function
+FAIL exports and imports WebAssembly.Module(): Compiling wasm function "wasm-function[0]" failed: function body must end with "end" opcode @+55
+PASS stray argument
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/module/exports.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/module/exports.any-expected.txt
new file mode 100644
index 0000000..203474bb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/module/exports.any-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+PASS Missing arguments
+PASS Non-Module arguments
+PASS Branding
+PASS Empty module
+PASS Empty module: array caching
+FAIL exports builder.setTableLength is not a function
+PASS Stray argument
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/module/exports.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/jsapi/module/exports.any.worker-expected.txt
new file mode 100644
index 0000000..203474bb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/module/exports.any.worker-expected.txt
@@ -0,0 +1,10 @@
+This is a testharness.js-based test.
+PASS Missing arguments
+PASS Non-Module arguments
+PASS Branding
+PASS Empty module
+PASS Empty module: array caching
+FAIL exports builder.setTableLength is not a function
+PASS Stray argument
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/wasm-constants.js b/third_party/blink/web_tests/external/wpt/wasm/jsapi/wasm-constants.js
index 18748e6..e384638 100644
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/wasm-constants.js
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/wasm-constants.js
@@ -28,6 +28,7 @@
 
 var kHeaderSize = 8;
 var kPageSize = 65536;
+var kSpecMaxPages = 65535;
 
 function bytesWithHeader() {
   var buffer = new ArrayBuffer(kHeaderSize + arguments.length);
@@ -52,18 +53,17 @@
 
 // Section declaration constants
 let kUnknownSectionCode = 0;
-let kTypeSectionCode = 1;      // Function signature declarations
-let kImportSectionCode = 2;    // Import declarations
-let kFunctionSectionCode = 3;  // Function declarations
-let kTableSectionCode = 4;     // Indirect function table and other tables
-let kMemorySectionCode = 5;    // Memory attributes
-let kGlobalSectionCode = 6;    // Global declarations
-let kExportSectionCode = 7;    // Exports
-let kStartSectionCode = 8;     // Start function declaration
-let kElementSectionCode = 9;  // Elements section
-let kCodeSectionCode = 10;      // Function code
-let kDataSectionCode = 11;     // Data segments
-let kNameSectionCode = 12;     // Name section (encoded as string)
+let kTypeSectionCode = 1;        // Function signature declarations
+let kImportSectionCode = 2;      // Import declarations
+let kFunctionSectionCode = 3;    // Function declarations
+let kTableSectionCode = 4;       // Indirect function table and other tables
+let kMemorySectionCode = 5;      // Memory attributes
+let kGlobalSectionCode = 6;      // Global declarations
+let kExportSectionCode = 7;      // Exports
+let kStartSectionCode = 8;       // Start function declaration
+let kElementSectionCode = 9;     // Elements section
+let kCodeSectionCode = 10;       // Function code
+let kDataSectionCode = 11;       // Data segments
 
 // Name section types
 let kModuleNameCode = 0;
@@ -74,6 +74,7 @@
 let kWasmAnyFunctionTypeForm = 0x70;
 
 let kHasMaximumFlag = 1;
+let kResizableMaximumFlag = 1;
 
 // Function declaration flags
 let kDeclFunctionName   = 0x01;
@@ -87,6 +88,7 @@
 let kWasmI64 = 0x7e;
 let kWasmF32 = 0x7d;
 let kWasmF64 = 0x7c;
+let kWasmS128 = 0x7b;
 
 let kExternalFunction = 0;
 let kExternalTable = 1;
@@ -102,6 +104,8 @@
 let kSig_i_l = makeSig([kWasmI64], [kWasmI32]);
 let kSig_i_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32]);
 let kSig_i_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]);
+let kSig_v_iiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], []);
+let kSig_f_ff = makeSig([kWasmF32, kWasmF32], [kWasmF32]);
 let kSig_d_dd = makeSig([kWasmF64, kWasmF64], [kWasmF64]);
 let kSig_l_ll = makeSig([kWasmI64, kWasmI64], [kWasmI64]);
 let kSig_i_dd = makeSig([kWasmF64, kWasmF64], [kWasmI32]);
@@ -118,6 +122,11 @@
 let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []);
 let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []);
 
+let kSig_v_f = makeSig([kWasmF32], []);
+let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);
+let kSig_f_d = makeSig([kWasmF64], [kWasmF32]);
+let kSig_d_d = makeSig([kWasmF64], [kWasmF64]);
+
 function makeSig(params, results) {
   return {params: params, results: results};
 }
@@ -357,19 +366,19 @@
   throw new MjsUnitAssertionError('Did not trap, expected: ' + kTrapMsgs[trap]);
 }
 
-function assertWasmThrows(value, code) {
-  assertEquals('number', typeof value);
-  try {
-    if (typeof code === 'function') {
-      code();
-    } else {
-      eval(code);
-    }
-  } catch (e) {
-    assertEquals('number', typeof e);
-    assertEquals(value, e);
-    // Success.
-    return;
+function wasmI32Const(val) {
+  let bytes = [kExprI32Const];
+  for (let i = 0; i < 4; ++i) {
+    bytes.push(0x80 | ((val >> (7 * i)) & 0x7f));
   }
-  throw new MjsUnitAssertionError('Did not throw, expected: ' + value);
+  bytes.push((val >> (7 * 4)) & 0x7f);
+  return bytes;
+}
+
+function wasmF32Const(f) {
+  return [kExprF32Const].concat(Array.from(new Uint8Array((new Float32Array([f])).buffer)));
+}
+
+function wasmF64Const(f) {
+  return [kExprF64Const].concat(Array.from(new Uint8Array((new Float64Array([f])).buffer)));
 }
diff --git a/third_party/blink/web_tests/external/wpt/wasm/jsapi/wasm-module-builder.js b/third_party/blink/web_tests/external/wpt/wasm/jsapi/wasm-module-builder.js
index c01b717..e13f4157 100644
--- a/third_party/blink/web_tests/external/wpt/wasm/jsapi/wasm-module-builder.js
+++ b/third_party/blink/web_tests/external/wpt/wasm/jsapi/wasm-module-builder.js
@@ -69,14 +69,13 @@
     // Emit section name.
     this.emit_u8(section_code);
     // Emit the section to a temporary buffer: its full length isn't know yet.
-    const section = new Binary;
+    let section = new Binary;
     content_generator(section);
     // Emit section length.
     this.emit_u32v(section.length);
     // Copy the temporary buffer.
-    for (const b of section) {
-      this.push(b);
-    }
+    // Avoid spread because {section} can be huge.
+    for (let b of section) this.push(b);
   }
 }
 
@@ -88,15 +87,6 @@
     this.body = [];
   }
 
-  numLocalNames() {
-    if (this.local_names === undefined) return 0;
-    let num_local_names = 0;
-    for (let loc_name of this.local_names) {
-      if (loc_name !== undefined) ++num_local_names;
-    }
-    return num_local_names;
-  }
-
   exportAs(name) {
     this.module.addExport(name, this.index);
     return this;
@@ -108,40 +98,12 @@
   }
 
   addBody(body) {
-    for (let b of body) {
-      if (typeof b !== 'number' || (b & (~0xFF)) !== 0 )
-        throw new Error('invalid body (entries must be 8 bit numbers): ' + body);
-    }
-    this.body = body.slice();
-    // Automatically add the end for the function block to the body.
-    this.body.push(kExprEnd);
-    return this;
-  }
-
-  addBodyWithEnd(body) {
     this.body = body;
     return this;
   }
 
-  getNumLocals() {
-    let total_locals = 0;
-    for (let l of this.locals || []) {
-      for (let type of ["i32", "i64", "f32", "f64"]) {
-        total_locals += l[type + "_count"] || 0;
-      }
-    }
-    return total_locals;
-  }
-
-  addLocals(locals, names) {
-    const old_num_locals = this.getNumLocals();
-    if (!this.locals) this.locals = []
-    this.locals.push(locals);
-    if (names) {
-      if (!this.local_names) this.local_names = [];
-      const missing_names = old_num_locals - this.local_names.length;
-      this.local_names.push(...new Array(missing_names), ...names);
-    }
+  addLocals(locals) {
+    this.locals = locals;
     return this;
   }
 
@@ -176,6 +138,7 @@
     this.table_length_max = undefined;
     this.element_segments = [];
     this.data_segments = [];
+    this.segments = [];
     this.explicit = [];
     this.num_imported_funcs = 0;
     this.num_imported_globals = 0;
@@ -197,22 +160,6 @@
     return this;
   }
 
-  stringToBytes(name) {
-    var result = new Binary();
-    result.emit_u32v(name.length);
-    for (var i = 0; i < name.length; i++) {
-      result.emit_u8(name.charCodeAt(i));
-    }
-    return result;
-  }
-
-  addCustomSection(name, bytes) {
-    name = this.stringToBytes(name);
-    var length = new Binary();
-    length.emit_u32v(name.length + bytes.length);
-    this.explicit.push([0, ...length, ...name, ...bytes]);
-  }
-
   addType(type) {
     // TODO: canonicalize types?
     this.types.push(type);
@@ -235,21 +182,15 @@
   }
 
   addImport(module = "", name, type) {
-    if (this.functions.length != 0) {
-      throw new Error('Imported functions must be declared before local ones');
-    }
     let type_index = (typeof type) == "number" ? type : this.addType(type);
     this.imports.push({module: module, name: name, kind: kExternalFunction,
                        type: type_index});
     return this.num_imported_funcs++;
   }
 
-  addImportedGlobal(module = "", name, type, mutable = false) {
-    if (this.globals.length != 0) {
-      throw new Error('Imported globals must be declared before local ones');
-    }
+  addImportedGlobal(module = "", name, type) {
     let o = {module: module, name: name, kind: kExternalGlobal, type: type,
-             mutable: mutable};
+             mutable: false}
     this.imports.push(o);
     return this.num_imported_globals++;
   }
@@ -278,7 +219,8 @@
   }
 
   addDataSegment(addr, data, is_global = false) {
-    this.data_segments.push({addr: addr, data: data, is_global: is_global});
+    this.data_segments.push(
+        {addr: addr, data: data, is_global: is_global});
     return this.data_segments.length - 1;
   }
 
@@ -309,21 +251,15 @@
     return this.addElementSegment(this.table_length_min, false, array);
   }
 
-  setTableBounds(min, max) {
+  setTableBounds(min, max = undefined) {
     this.table_length_min = min;
     this.table_length_max = max;
     return this;
   }
 
-  setTableLength(length) {
-    this.table_length_min = length;
-    this.table_length_max = length;
-    return this;
-  }
-
-  setName(name) {
-    this.name = name;
-    return this;
+  // TODO(ssauleau): legacy, remove this
+  setFunctionTableLength(length) {
+    return this.setTableBounds(length);
   }
 
   toArray(debug = false) {
@@ -385,11 +321,15 @@
     }
 
     // Add functions declarations
+    let has_names = false;
+    let names = false;
     if (wasm.functions.length > 0) {
       if (debug) print("emitting function decls @ " + binary.length);
       binary.emit_section(kFunctionSectionCode, section => {
         section.emit_u32v(wasm.functions.length);
         for (let func of wasm.functions) {
+          has_names = has_names || (func.name != undefined &&
+                                   func.name.length > 0);
           section.emit_u32v(func.type_index);
         }
       });
@@ -415,7 +355,7 @@
       binary.emit_section(kMemorySectionCode, section => {
         section.emit_u8(1);  // one memory entry
         const has_max = wasm.memory.max !== undefined;
-        section.emit_u8(has_max ? kHasMaximumFlag : 0);
+        section.emit_u8(has_max ? 1 : 0);
         section.emit_u32v(wasm.memory.min);
         if (has_max) section.emit_u32v(wasm.memory.max);
       });
@@ -438,7 +378,7 @@
               break;
             case kWasmI64:
               section.emit_u8(kExprI64Const);
-              section.emit_u32v(global.init);
+              section.emit_u8(global.init);
               break;
             case kWasmF32:
               section.emit_u8(kExprF32Const);
@@ -492,7 +432,7 @@
     }
 
     // Add start function section.
-    if (wasm.start_index !== undefined) {
+    if (wasm.start_index != undefined) {
       if (debug) print("emitting start function @ " + binary.length);
       binary.emit_section(kStartSectionCode, section => {
         section.emit_u32v(wasm.start_index);
@@ -507,7 +447,7 @@
         section.emit_u32v(inits.length);
 
         for (let init of inits) {
-          section.emit_u8(0); // table index
+          section.emit_u8(0);  // table index / flags
           if (init.is_global) {
             section.emit_u8(kExprGetGlobal);
           } else {
@@ -532,7 +472,9 @@
         for (let func of wasm.functions) {
           // Function body length will be patched later.
           let local_decls = [];
-          for (let l of func.locals || []) {
+          let l = func.locals;
+          if (l != undefined) {
+            let local_decls_count = 0;
             if (l.i32_count > 0) {
               local_decls.push({count: l.i32_count, type: kWasmI32});
             }
@@ -567,7 +509,7 @@
       binary.emit_section(kDataSectionCode, section => {
         section.emit_u32v(wasm.data_segments.length);
         for (let seg of wasm.data_segments) {
-          section.emit_u8(0);  // linear memory index 0
+          section.emit_u8(0);  // linear memory index 0 / flags
           if (seg.is_global) {
             // initializer is a global variable
             section.emit_u8(kExprGetGlobal);
@@ -590,50 +532,21 @@
       binary.emit_bytes(exp);
     }
 
-    // Add names.
-    let num_function_names = 0;
-    let num_functions_with_local_names = 0;
-    for (let func of wasm.functions) {
-      if (func.name !== undefined) ++num_function_names;
-      if (func.numLocalNames() > 0) ++num_functions_with_local_names;
-    }
-    if (num_function_names > 0 || num_functions_with_local_names > 0 ||
-        wasm.name !== undefined) {
-      if (debug) print('emitting names @ ' + binary.length);
+    // Add function names.
+    if (has_names) {
+      if (debug) print("emitting names @ " + binary.length);
       binary.emit_section(kUnknownSectionCode, section => {
-        section.emit_string('name');
-        // Emit module name.
-        if (wasm.name !== undefined) {
-          section.emit_section(kModuleNameCode, name_section => {
-            name_section.emit_string(wasm.name);
-          });
+        section.emit_string("name");
+        var count = wasm.functions.length + wasm.num_imported_funcs;
+        section.emit_u32v(count);
+        for (var i = 0; i < wasm.num_imported_funcs; i++) {
+          section.emit_u8(0); // empty string
+          section.emit_u8(0); // local names count == 0
         }
-        // Emit function names.
-        if (num_function_names > 0) {
-          section.emit_section(kFunctionNamesCode, name_section => {
-            name_section.emit_u32v(num_function_names);
-            for (let func of wasm.functions) {
-              if (func.name === undefined) continue;
-              name_section.emit_u32v(func.index);
-              name_section.emit_string(func.name);
-            }
-          });
-        }
-        // Emit local names.
-        if (num_functions_with_local_names > 0) {
-          section.emit_section(kLocalNamesCode, name_section => {
-            name_section.emit_u32v(num_functions_with_local_names);
-            for (let func of wasm.functions) {
-              if (func.numLocalNames() == 0) continue;
-              name_section.emit_u32v(func.index);
-              name_section.emit_u32v(func.numLocalNames());
-              for (let i = 0; i < func.local_names.length; ++i) {
-                if (func.local_names[i] === undefined) continue;
-                name_section.emit_u32v(i);
-                name_section.emit_string(func.local_names[i]);
-              }
-            }
-          });
+        for (let func of wasm.functions) {
+          var name = func.name == undefined ? "" : func.name;
+          section.emit_string(name);
+          section.emit_u8(0);  // local names count == 0
         }
       });
     }
@@ -650,21 +563,12 @@
       if ((typeof val) == "string") val = val.charCodeAt(0);
       view[i] = val | 0;
     }
-    return buffer;
+    return new Uint8Array(buffer);
   }
 
-  instantiate(ffi) {
+  instantiate(...args) {
     let module = new WebAssembly.Module(this.toBuffer());
-    let instance = new WebAssembly.Instance(module, ffi);
+    let instance = new WebAssembly.Instance(module, ...args);
     return instance;
   }
-
-  asyncInstantiate(ffi) {
-    return WebAssembly.instantiate(this.toBuffer(), ffi)
-        .then(({module, instance}) => instance);
-  }
-
-  toModule(debug = false) {
-    return new WebAssembly.Module(this.toBuffer(debug));
-  }
 }
diff --git a/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any-expected.txt
new file mode 100644
index 0000000..1db6c71a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+PASS Empty module without imports argument
+PASS Empty module with undefined imports argument
+PASS Empty module with empty imports argument
+PASS getter order for imports object
+PASS imports
+FAIL No imports promise_test: Unhandled rejection with value: object "TypeError: builder.setTableLength is not a function"
+FAIL exports and imports promise_test: Unhandled rejection with value: object "CompileError: AsyncCompilation: (null): Compiling wasm function "wasm-function[0]" failed: function body must end with "end" opcode @+55"
+PASS stray argument
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.serviceworker-expected.txt
new file mode 100644
index 0000000..1db6c71a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.serviceworker-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+PASS Empty module without imports argument
+PASS Empty module with undefined imports argument
+PASS Empty module with empty imports argument
+PASS getter order for imports object
+PASS imports
+FAIL No imports promise_test: Unhandled rejection with value: object "TypeError: builder.setTableLength is not a function"
+FAIL exports and imports promise_test: Unhandled rejection with value: object "CompileError: AsyncCompilation: (null): Compiling wasm function "wasm-function[0]" failed: function body must end with "end" opcode @+55"
+PASS stray argument
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.sharedworker-expected.txt
new file mode 100644
index 0000000..1db6c71a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.sharedworker-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+PASS Empty module without imports argument
+PASS Empty module with undefined imports argument
+PASS Empty module with empty imports argument
+PASS getter order for imports object
+PASS imports
+FAIL No imports promise_test: Unhandled rejection with value: object "TypeError: builder.setTableLength is not a function"
+FAIL exports and imports promise_test: Unhandled rejection with value: object "CompileError: AsyncCompilation: (null): Compiling wasm function "wasm-function[0]" failed: function body must end with "end" opcode @+55"
+PASS stray argument
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.worker-expected.txt
new file mode 100644
index 0000000..1db6c71a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/webapi/instantiateStreaming.any.worker-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+PASS Empty module without imports argument
+PASS Empty module with undefined imports argument
+PASS Empty module with empty imports argument
+PASS getter order for imports object
+PASS imports
+FAIL No imports promise_test: Unhandled rejection with value: object "TypeError: builder.setTableLength is not a function"
+FAIL exports and imports promise_test: Unhandled rejection with value: object "CompileError: AsyncCompilation: (null): Compiling wasm function "wasm-function[0]" failed: function body must end with "end" opcode @+55"
+PASS stray argument
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/webapi/invalid-code.any-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/webapi/invalid-code.any-expected.txt
new file mode 100644
index 0000000..90be592
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/webapi/invalid-code.any-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Invalid code: compileStreaming assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Invalid code: instantiateStreaming assert_unreached: Should have rejected: undefined Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/webapi/invalid-code.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/webapi/invalid-code.any.serviceworker-expected.txt
new file mode 100644
index 0000000..90be592
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/webapi/invalid-code.any.serviceworker-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Invalid code: compileStreaming assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Invalid code: instantiateStreaming assert_unreached: Should have rejected: undefined Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/webapi/invalid-code.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/webapi/invalid-code.any.sharedworker-expected.txt
new file mode 100644
index 0000000..90be592
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/webapi/invalid-code.any.sharedworker-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Invalid code: compileStreaming assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Invalid code: instantiateStreaming assert_unreached: Should have rejected: undefined Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/wasm/webapi/invalid-code.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wasm/webapi/invalid-code.any.worker-expected.txt
new file mode 100644
index 0000000..90be592
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/wasm/webapi/invalid-code.any.worker-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Invalid code: compileStreaming assert_unreached: Should have rejected: undefined Reached unreachable code
+FAIL Invalid code: instantiateStreaming assert_unreached: Should have rejected: undefined Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/workers/baseurl/alpha/sharedworker-in-worker-expected.txt b/third_party/blink/web_tests/external/wpt/workers/baseurl/alpha/sharedworker-in-worker-expected.txt
new file mode 100644
index 0000000..882a37d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/workers/baseurl/alpha/sharedworker-in-worker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Base URL in workers: new SharedWorker() assert_unreached: Got error event Reached unreachable code
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/workers/baseurl/alpha/xhr-in-moduleworker-expected.txt b/third_party/blink/web_tests/external/wpt/workers/baseurl/alpha/xhr-in-moduleworker-expected.txt
new file mode 100644
index 0000000..93eb19c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/workers/baseurl/alpha/xhr-in-moduleworker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Base URL in module dedicated workers: XHR assert_equals: expected "gamma\n" but got "beta\n"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-module-expected.txt b/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-module-expected.txt
new file mode 100644
index 0000000..bbf490d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-module-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL WorkerLocation with redirects: module dedicated workers assert_equals: expected "http://web-platform.test:8001/workers/interfaces/WorkerGlobalScope/location/post-location-members.js?a" but got "http://web-platform.test:8001/workers/interfaces/WorkerGlobalScope/location/helper-redirect.py?fail"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/auto-picture-in-picture-origin-trial-properties.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/auto-picture-in-picture-origin-trial-properties.html
new file mode 100644
index 0000000..8c843b42
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/auto-picture-in-picture-origin-trial-properties.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<!-- Generate token with the command:
+generate_token.py http://127.0.0.1:8000 AutoPictureInPicture --expire-timestamp=2000000000
+-->
+
+<meta http-equiv="origin-trial" content="At8TdACNKoUe/s/mrfCk9A0o+kksPEeI53NWPTEwQImY6MLC+HWuO53CYt/IQju7VzodQhlGFdRWJ3AuA6AxGg8AAABceyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiQXV0b1BpY3R1cmVJblBpY3R1cmUiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=" />
+<title>Auto Picture-in-Picture - properties exposed by origin trial</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/origin-trials-helper.js"></script>
+<script>
+test(t => {
+  OriginTrialsHelper.check_properties(this,
+      { 'HTMLVideoElement': ['autoPictureInPicture'] });
+}, 'Auto Picture-in-Picture properties in Origin-Trial enabled document.');
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/destroy-overlay-scrollbar-expected.txt b/third_party/blink/web_tests/paint/invalidation/scroll/destroy-overlay-scrollbar-expected.txt
index 0ba1c4a2..631ee54 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/destroy-overlay-scrollbar-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/destroy-overlay-scrollbar-expected.txt
@@ -18,7 +18,7 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "VerticalScrollbar",
+          "object": "LayoutBlockFlow (positioned) DIV",
           "rect": [193, 100, 7, 200],
           "reason": "chunk disappeared"
         }
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-worker-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-worker-expected.txt
new file mode 100644
index 0000000..4f65337
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-worker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL importScripts assert_equals: expected "gamma/script.js" but got "beta/script.js"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/worker-in-worker-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/worker-in-worker-expected.txt
new file mode 100644
index 0000000..a8ed8c5f
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/worker-in-worker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Base URL in workers: new Worker() assert_equals: expected "gamma" but got "beta"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-worker-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-worker-expected.txt
new file mode 100644
index 0000000..4bb4dcb
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-worker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL xhr-worker assert_equals: expected "gamma\n" but got "beta\n"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-expected.txt
new file mode 100644
index 0000000..0fcc95f6
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL WorkerLocation with redirects: classic dedicated workers assert_equals: expected "http://web-platform.test:8001/workers/interfaces/WorkerGlobalScope/location/post-location-members.js?a" but got "http://web-platform.test:8001/workers/interfaces/WorkerGlobalScope/location/helper-redirect.py?fail"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/http/tests/workers/worker-performance-timeline-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/http/tests/workers/worker-performance-timeline-expected.txt
new file mode 100644
index 0000000..de868f7
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/http/tests/workers/worker-performance-timeline-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+PASS User Timing
+FAIL Resource Timing assert_equals: expected 2 but got 3
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/http/tests/workers/worker-redirect-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/http/tests/workers/worker-redirect-expected.txt
new file mode 100644
index 0000000..d344f4d2
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/http/tests/workers/worker-redirect-expected.txt
@@ -0,0 +1,8 @@
+CONSOLE ERROR: Unsafe attempt to load URL http://localhost:8000/workers/resources/worker-redirect-target.js from frame with URL http://127.0.0.1:8000/resources/redirect.php?url=http://localhost:8000/workers/resources/worker-redirect-target.js. Domains, protocols and ports must match.
+
+Test that loading the worker's script does not allow a cross origin redirect (bug 26146)
+
+SUCCESS: threw exception (SecurityError: Failed to construct 'Worker': Script at 'http://localhost:8000/workers/resources/worker-target.js' cannot be accessed from origin 'http://127.0.0.1:8000'.) when attempting to cross origin while loading the worker script.
+SUCCESS: threw error when attempting to redirected cross origin while loading the worker script.
+DONE
+
diff --git a/third_party/blink/web_tests/webmidi/implicit-open-expected.txt b/third_party/blink/web_tests/webmidi/implicit-open-expected.txt
index 033860d69..d462bd1 100644
--- a/third_party/blink/web_tests/webmidi/implicit-open-expected.txt
+++ b/third_party/blink/web_tests/webmidi/implicit-open-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: line 61: Web MIDI will ask a permission to use even if the sysex is not specified in the MIDIOptions since M75, around June 2019. See https://www.chromestatus.com/feature/5138066234671104 for more details.
 Tests MIDIPort implicit open.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/blink/web_tests/webmidi/open-close-expected.txt b/third_party/blink/web_tests/webmidi/open-close-expected.txt
index 82f685d..9efdfc7b 100644
--- a/third_party/blink/web_tests/webmidi/open-close-expected.txt
+++ b/third_party/blink/web_tests/webmidi/open-close-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: line 59: Web MIDI will ask a permission to use even if the sysex is not specified in the MIDIOptions since M75, around June 2019. See https://www.chromestatus.com/feature/5138066234671104 for more details.
 Tests MIDIPort.open and MIDIPort.close.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/blink/web_tests/webmidi/requestmidiaccess-basic-expected.txt b/third_party/blink/web_tests/webmidi/requestmidiaccess-basic-expected.txt
index fa4ae18..47a1465 100644
--- a/third_party/blink/web_tests/webmidi/requestmidiaccess-basic-expected.txt
+++ b/third_party/blink/web_tests/webmidi/requestmidiaccess-basic-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: line 12: Web MIDI will ask a permission to use even if the sysex is not specified in the MIDIOptions since M75, around June 2019. See https://www.chromestatus.com/feature/5138066234671104 for more details.
 Test if requestMIDIAccess is available.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/blink/web_tests/webmidi/requestmidiaccess-expected.txt b/third_party/blink/web_tests/webmidi/requestmidiaccess-expected.txt
index 1dd4d99f..0881c4d1 100644
--- a/third_party/blink/web_tests/webmidi/requestmidiaccess-expected.txt
+++ b/third_party/blink/web_tests/webmidi/requestmidiaccess-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: line 164: Web MIDI will ask a permission to use even if the sysex is not specified in the MIDIOptions since M75, around June 2019. See https://www.chromestatus.com/feature/5138066234671104 for more details.
 Tests navigator.requestMIDIAccess.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/blink/web_tests/webmidi/requestmidiaccess-fail-expected.txt b/third_party/blink/web_tests/webmidi/requestmidiaccess-fail-expected.txt
index 473a110b..b940e28 100644
--- a/third_party/blink/web_tests/webmidi/requestmidiaccess-fail-expected.txt
+++ b/third_party/blink/web_tests/webmidi/requestmidiaccess-fail-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: line 33: Web MIDI will ask a permission to use even if the sysex is not specified in the MIDIOptions since M75, around June 2019. See https://www.chromestatus.com/feature/5138066234671104 for more details.
 Tests navigator.requestMIDIAccess failure.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/blink/web_tests/webmidi/requestmidiaccess-options-expected.txt b/third_party/blink/web_tests/webmidi/requestmidiaccess-options-expected.txt
index dbddd4f..ac3f680b 100644
--- a/third_party/blink/web_tests/webmidi/requestmidiaccess-options-expected.txt
+++ b/third_party/blink/web_tests/webmidi/requestmidiaccess-options-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: line 58: Web MIDI will ask a permission to use even if the sysex is not specified in the MIDIOptions since M75, around June 2019. See https://www.chromestatus.com/feature/5138066234671104 for more details.
 Test requestMIDIAccess with various options.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/blink/web_tests/webmidi/send-system-messages-expected.txt b/third_party/blink/web_tests/webmidi/send-system-messages-expected.txt
index 3511027..1f032d2 100644
--- a/third_party/blink/web_tests/webmidi/send-system-messages-expected.txt
+++ b/third_party/blink/web_tests/webmidi/send-system-messages-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: line 40: Web MIDI will ask a permission to use even if the sysex is not specified in the MIDIOptions since M75, around June 2019. See https://www.chromestatus.com/feature/5138066234671104 for more details.
 Test if various kinds of system messages can be validated.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/pffft/LICENSE b/third_party/pffft/LICENSE
new file mode 100644
index 0000000..ea8623f3
--- /dev/null
+++ b/third_party/pffft/LICENSE
@@ -0,0 +1,45 @@
+Copyright (c) 2013  Julien Pommier ( pommier@modartt.com )
+
+Based on original fortran 77 code from FFTPACKv4 from NETLIB,
+authored by Dr Paul Swarztrauber of NCAR, in 1985.
+
+As confirmed by the NCAR fftpack software curators, the following
+FFTPACKv5 license applies to FFTPACKv4 sources. My changes are
+released under the same terms.
+
+FFTPACK license:
+
+http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html
+
+Copyright (c) 2004 the University Corporation for Atmospheric
+Research ("UCAR"). All rights reserved. Developed by NCAR's
+Computational and Information Systems Laboratory, UCAR,
+www.cisl.ucar.edu.
+
+Redistribution and use of the Software in source and binary forms,
+with or without modification, is permitted provided that the
+following conditions are met:
+
+- Neither the names of NCAR's Computational and Information Systems
+Laboratory, the University Corporation for Atmospheric Research,
+nor the names of its sponsors or contributors may be used to
+endorse or promote products derived from this Software without
+specific prior written permission.
+
+- Redistributions of source code must retain the above copyright
+notices, this list of conditions, and the disclaimer below.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions, and the disclaimer below in the
+documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
diff --git a/third_party/pffft/OWNERS b/third_party/pffft/OWNERS
new file mode 100644
index 0000000..b956a234
--- /dev/null
+++ b/third_party/pffft/OWNERS
@@ -0,0 +1,2 @@
+maxmorin@chromium.org
+olka@chromium.org
diff --git a/third_party/pffft/README.chromium b/third_party/pffft/README.chromium
new file mode 100644
index 0000000..a0e1164
--- /dev/null
+++ b/third_party/pffft/README.chromium
@@ -0,0 +1,15 @@
+Name: PFFFT: a pretty fast FFT.
+Short Name: PFFFT
+URL: https://bitbucket.org/jpommier/pffft/
+Version: 0
+Date: 2014-08-09
+Revision: 483453d8f766
+License: BSD-like
+License File: LICENSE
+Security Critical: Yes
+License Android Compatible: Yes
+
+Description:
+PFFFT does 1D Fast Fourier Transforms, of single precision real and complex vectors. It tries do it fast, it tries to be correct, and it tries to be small. Computations do take advantage of SSE1 instructions on x86 cpus, Altivec on powerpc cpus, and NEON on ARM cpus.
+
+Local Modifications:
diff --git a/third_party/pffft/README.txt b/third_party/pffft/README.txt
new file mode 100644
index 0000000..878f4a67
--- /dev/null
+++ b/third_party/pffft/README.txt
@@ -0,0 +1,379 @@
+PFFFT: a pretty fast FFT.
+
+TL;DR
+--
+
+PFFFT does 1D Fast Fourier Transforms, of single precision real and
+complex vectors. It tries do it fast, it tries to be correct, and it
+tries to be small. Computations do take advantage of SSE1 instructions
+on x86 cpus, Altivec on powerpc cpus, and NEON on ARM cpus. The
+license is BSD-like.
+
+
+Why does it exist:
+--
+
+I was in search of a good performing FFT library , preferably very
+small and with a very liberal license.
+
+When one says "fft library", FFTW ("Fastest Fourier Transform in the
+West") is probably the first name that comes to mind -- I guess that
+99% of open-source projects that need a FFT do use FFTW, and are happy
+with it. However, it is quite a large library , which does everything
+fft related (2d transforms, 3d transforms, other transformations such
+as discrete cosine , or fast hartley). And it is licensed under the
+GNU GPL , which means that it cannot be used in non open-source
+products.
+
+An alternative to FFTW that is really small, is the venerable FFTPACK
+v4, which is available on NETLIB. A more recent version (v5) exists,
+but it is larger as it deals with multi-dimensional transforms. This
+is a library that is written in FORTRAN 77, a language that is now
+considered as a bit antiquated by many. FFTPACKv4 was written in 1985,
+by Dr Paul Swarztrauber of NCAR, more than 25 years ago ! And despite
+its age, benchmarks show it that it still a very good performing FFT
+library, see for example the 1d single precision benchmarks here:
+http://www.fftw.org/speed/opteron-2.2GHz-32bit/ . It is however not
+competitive with the fastest ones, such as FFTW, Intel MKL, AMD ACML,
+Apple vDSP. The reason for that is that those libraries do take
+advantage of the SSE SIMD instructions available on Intel CPUs,
+available since the days of the Pentium III. These instructions deal
+with small vectors of 4 floats at a time, instead of a single float
+for a traditionnal FPU, so when using these instructions one may expect
+a 4-fold performance improvement.
+
+The idea was to take this fortran fftpack v4 code, translate to C,
+modify it to deal with those SSE instructions, and check that the
+final performance is not completely ridiculous when compared to other
+SIMD FFT libraries. Translation to C was performed with f2c (
+http://www.netlib.org/f2c/ ). The resulting file was a bit edited in
+order to remove the thousands of gotos that were introduced by
+f2c. You will find the fftpack.h and fftpack.c sources in the
+repository, this a complete translation of
+http://www.netlib.org/fftpack/ , with the discrete cosine transform
+and the test program. There is no license information in the netlib
+repository, but it was confirmed to me by the fftpack v5 curators that
+the same terms do apply to fftpack v4:
+http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html . This is a
+"BSD-like" license, it is compatible with proprietary projects.
+
+Adapting fftpack to deal with the SIMD 4-element vectors instead of
+scalar single precision numbers was more complex than I originally
+thought, especially with the real transforms, and I ended up writing
+more code than I planned..
+
+
+The code:
+--
+
+Only two files, in good old C, pffft.c and pffft.h . The API is very
+very simple, just make sure that you read the comments in pffft.h.
+
+
+Comparison with other FFTs:
+--
+
+The idea was not to break speed records, but to get a decently fast
+fft that is at least 50% as fast as the fastest FFT -- especially on
+slowest computers . I'm more focused on getting the best performance
+on slow cpus (Atom, Intel Core 1, old Athlons, ARM Cortex-A9...), than
+on getting top performance on today fastest cpus.
+
+It can be used in a real-time context as the fft functions do not
+perform any memory allocation -- that is why they accept a 'work'
+array in their arguments.
+
+It is also a bit focused on performing 1D convolutions, that is why it
+provides "unordered" FFTs , and a fourier domain convolution
+operation.
+
+
+Benchmark results (cpu tested: core i7 2600, core 2 quad, core 1 duo, atom N270, cortex-A9)
+--
+
+The benchmark shows the performance of various fft implementations measured in 
+MFlops, with the number of floating point operations being defined as 5Nlog2(N)
+for a length N complex fft, and 2.5*Nlog2(N) for a real fft. 
+See http://www.fftw.org/speed/method.html for an explanation of these formulas.
+
+MacOS Lion, gcc 4.2, 64-bit, fftw 3.3 on a 3.4 GHz core i7 2600
+
+Built with:
+
+ gcc-4.2 -o test_pffft -arch x86_64 -O3 -Wall -W pffft.c test_pffft.c fftpack.c -L/usr/local/lib -I/usr/local/include/ -DHAVE_VECLIB -framework veclib -DHAVE_FFTW -lfftw3f 
+
+| input len |real FFTPack|  real vDSP |  real FFTW | real PFFFT | |cplx FFTPack|  cplx vDSP |  cplx FFTW | cplx PFFFT |
+|-----------+------------+------------+------------+------------| |------------+------------+------------+------------|
+|       64  |     2816   |     8596   |     7329   |     8187   | |     2887   |    14898   |    14668   |    11108   |
+|       96  |     3298   |      n/a   |     8378   |     7727   | |     3953   |      n/a   |    15680   |    10878   |
+|      128  |     3507   |    11575   |     9266   |    10108   | |     4233   |    17598   |    16427   |    12000   |
+|      160  |     3391   |      n/a   |     9838   |    10711   | |     4220   |      n/a   |    16653   |    11187   |
+|      192  |     3919   |      n/a   |     9868   |    10956   | |     4297   |      n/a   |    15770   |    12540   |
+|      256  |     4283   |    13179   |    10694   |    13128   | |     4545   |    19550   |    16350   |    13822   |
+|      384  |     3136   |      n/a   |    10810   |    12061   | |     3600   |      n/a   |    16103   |    13240   |
+|      480  |     3477   |      n/a   |    10632   |    12074   | |     3536   |      n/a   |    11630   |    12522   |
+|      512  |     3783   |    15141   |    11267   |    13838   | |     3649   |    20002   |    16560   |    13580   |
+|      640  |     3639   |      n/a   |    11164   |    13946   | |     3695   |      n/a   |    15416   |    13890   |
+|      768  |     3800   |      n/a   |    11245   |    13495   | |     3590   |      n/a   |    15802   |    14552   |
+|      800  |     3440   |      n/a   |    10499   |    13301   | |     3659   |      n/a   |    12056   |    13268   |
+|     1024  |     3924   |    15605   |    11450   |    15339   | |     3769   |    20963   |    13941   |    15467   |
+|     2048  |     4518   |    16195   |    11551   |    15532   | |     4258   |    20413   |    13723   |    15042   |
+|     2400  |     4294   |      n/a   |    10685   |    13078   | |     4093   |      n/a   |    12777   |    13119   |
+|     4096  |     4750   |    16596   |    11672   |    15817   | |     4157   |    19662   |    14316   |    14336   |
+|     8192  |     3820   |    16227   |    11084   |    12555   | |     3691   |    18132   |    12102   |    13813   |
+|     9216  |     3864   |      n/a   |    10254   |    12870   | |     3586   |      n/a   |    12119   |    13994   |
+|    16384  |     3822   |    15123   |    10454   |    12822   | |     3613   |    16874   |    12370   |    13881   |
+|    32768  |     4175   |    14512   |    10662   |    11095   | |     3881   |    14702   |    11619   |    11524   |
+|   262144  |     3317   |    11429   |     6269   |     9517   | |     2810   |    11729   |     7757   |    10179   |
+|  1048576  |     2913   |    10551   |     4730   |     5867   | |     2661   |     7881   |     3520   |     5350   |
+|-----------+------------+------------+------------+------------| |------------+------------+------------+------------|
+
+
+Debian 6, gcc 4.4.5, 64-bit, fftw 3.3.1 on a 3.4 GHz core i7 2600
+
+Built with:
+gcc -o test_pffft -DHAVE_FFTW -msse2 -O3 -Wall -W pffft.c test_pffft.c fftpack.c -L$HOME/local/lib -I$HOME/local/include/ -lfftw3f -lm
+
+| N (input length) | real FFTPack |   real FFTW  |  real PFFFT  | | cplx FFTPack |   cplx FFTW  |  cplx PFFFT  |
+|------------------+--------------+--------------+--------------| |--------------+--------------+--------------|
+|           64     |      3840    |      7680    |      8777    | |      4389    |     20480    |     11171    |
+|           96     |      4214    |      9633    |      8429    | |      4816    |     22477    |     11238    |
+|          128     |      3584    |     10240    |     10240    | |      5120    |     23893    |     11947    |
+|          192     |      4854    |     11095    |     12945    | |      4854    |     22191    |     14121    |
+|          256     |      4096    |     11703    |     16384    | |      5120    |     23406    |     13653    |
+|          384     |      4395    |     14651    |     12558    | |      4884    |     19535    |     14651    |
+|          512     |      5760    |     13166    |     15360    | |      4608    |     23040    |     15360    |
+|          768     |      4907    |     14020    |     16357    | |      4461    |     19628    |     14020    |
+|         1024     |      5120    |     14629    |     14629    | |      5120    |     20480    |     15754    |
+|         2048     |      5632    |     14080    |     18773    | |      4693    |     12516    |     16091    |
+|         4096     |      5120    |     13653    |     17554    | |      4726    |      7680    |     14456    |
+|         8192     |      4160    |      7396    |     13312    | |      4437    |     14791    |     13312    |
+|         9216     |      4210    |      6124    |     13473    | |      4491    |      7282    |     14970    |
+|        16384     |      3976    |     11010    |     14313    | |      4210    |     11450    |     13631    |
+|        32768     |      4260    |     10224    |     10954    | |      4260    |      6816    |     11797    |
+|       262144     |      3736    |      6896    |      9961    | |      2359    |      8965    |      9437    |
+|      1048576     |      2796    |      4534    |      6453    | |      1864    |      3078    |      5592    |
+|------------------+--------------+--------------+--------------| |--------------+--------------+--------------|
+
+
+
+MacOS Snow Leopard, gcc 4.0, 32-bit, fftw 3.3 on a 1.83 GHz core 1 duo
+
+Built with:
+
+ gcc -o test_pffft -DHAVE_FFTW -DHAVE_VECLIB -O3 -Wall -W pffft.c test_pffft.c fftpack.c -L/usr/local/lib -I/usr/local/include/ -lfftw3f -framework veclib
+
+| input len |real FFTPack|  real vDSP |  real FFTW | real PFFFT | |cplx FFTPack|  cplx vDSP |  cplx FFTW | cplx PFFFT |
+|-----------+------------+------------+------------+------------| |------------+------------+------------+------------|
+|      64   |      745   |     2145   |     1706   |     2028   | |      961   |     3356   |     3313   |     2300   |
+|      96   |      877   |      n/a   |     1976   |     1978   | |     1059   |      n/a   |     3333   |     2233   |
+|     128   |      951   |     2808   |     2213   |     2279   | |     1202   |     3803   |     3739   |     2494   |
+|     192   |     1002   |      n/a   |     2456   |     2429   | |     1186   |      n/a   |     3701   |     2508   |
+|     256   |     1065   |     3205   |     2641   |     2793   | |     1302   |     4013   |     3912   |     2663   |
+|     384   |      845   |      n/a   |     2759   |     2499   | |      948   |      n/a   |     3729   |     2504   |
+|     512   |      900   |     3476   |     2956   |     2759   | |      974   |     4057   |     3954   |     2645   |
+|     768   |      910   |      n/a   |     2912   |     2737   | |      975   |      n/a   |     3837   |     2614   |
+|    1024   |      936   |     3583   |     3107   |     3009   | |     1006   |     4124   |     3821   |     2697   |
+|    2048   |     1057   |     3585   |     3091   |     2837   | |     1089   |     3889   |     3701   |     2513   |
+|    4096   |     1083   |     3524   |     3092   |     2733   | |     1039   |     3617   |     3462   |     2364   |
+|    8192   |      874   |     3252   |     2967   |     2363   | |      911   |     3106   |     2789   |     2302   |
+|    9216   |      898   |      n/a   |     2420   |     2290   | |      865   |      n/a   |     2676   |     2204   |
+|   16384   |      903   |     2892   |     2506   |     2421   | |      899   |     3026   |     2797   |     2289   |
+|   32768   |      965   |     2837   |     2550   |     2358   | |      920   |     2922   |     2763   |     2240   |
+|  262144   |      738   |     2422   |     1589   |     1708   | |      610   |     2038   |     1436   |     1091   |
+| 1048576   |      528   |     1207   |      845   |      880   | |      606   |     1020   |      669   |     1036   |
+|-----------+------------+------------+------------+------------| |------------+------------+------------+------------|
+
+
+
+Ubuntu 11.04, gcc 4.5, 32-bit, fftw 3.2 on a 2.66 core 2 quad
+
+Built with:
+gcc -o test_pffft -DHAVE_FFTW -msse -mfpmath=sse -O3 -Wall -W pffft.c test_pffft.c fftpack.c -L/usr/local/lib -I/usr/local/include/ -lfftw3f -lm
+
+| input len |real FFTPack|  real FFTW | real PFFFT | |cplx FFTPack|  cplx FFTW | cplx PFFFT |
+|-----------+------------+------------+------------| |------------+------------+------------|
+|       64  |     1920   |     3614   |     5120   | |     2194   |     7680   |     6467   |
+|       96  |     1873   |     3549   |     5187   | |     2107   |     8429   |     5863   |
+|      128  |     2240   |     3773   |     5514   | |     2560   |     7964   |     6827   |
+|      192  |     1765   |     4569   |     7767   | |     2284   |     9137   |     7061   |
+|      256  |     2048   |     5461   |     7447   | |     2731   |     9638   |     7802   |
+|      384  |     1998   |     5861   |     6762   | |     2313   |     9253   |     7644   |
+|      512  |     2095   |     6144   |     7680   | |     2194   |    10240   |     7089   |
+|      768  |     2230   |     5773   |     7549   | |     2045   |    10331   |     7010   |
+|     1024  |     2133   |     6400   |     8533   | |     2133   |    10779   |     7877   |
+|     2048  |     2011   |     7040   |     8665   | |     1942   |    10240   |     7768   |
+|     4096  |     2194   |     6827   |     8777   | |     1755   |     9452   |     6827   |
+|     8192  |     1849   |     6656   |     6656   | |     1752   |     7831   |     6827   |
+|     9216  |     1871   |     5858   |     6416   | |     1643   |     6909   |     6266   |
+|    16384  |     1883   |     6223   |     6506   | |     1664   |     7340   |     6982   |
+|    32768  |     1826   |     6390   |     6667   | |     1631   |     7481   |     6971   |
+|   262144  |     1546   |     4075   |     5977   | |     1299   |     3415   |     3551   |
+|  1048576  |     1104   |     2071   |     1730   | |     1104   |     1149   |     1834   |
+|-----------+------------+------------+------------| |------------+------------+------------|
+
+
+
+Ubuntu 11.04, gcc 4.5, 32-bit, fftw 3.3 on a 1.6 GHz Atom N270
+
+Built with:
+gcc -o test_pffft -DHAVE_FFTW -msse -mfpmath=sse -O3 -Wall -W pffft.c test_pffft.c fftpack.c -L/usr/local/lib -I/usr/local/include/ -lfftw3f -lm
+
+| N (input length) | real FFTPack |   real FFTW  |  real PFFFT  | | cplx FFTPack |   cplx FFTW  |  cplx PFFFT  |
+|------------------+--------------+--------------+--------------| |--------------+--------------+--------------|
+|           64     |       452    |      1041    |      1336    | |       549    |      2318    |      1781    |
+|           96     |       444    |      1297    |      1297    | |       503    |      2408    |      1686    |
+|          128     |       527    |      1525    |      1707    | |       543    |      2655    |      1886    |
+|          192     |       498    |      1653    |      1849    | |       539    |      2678    |      1942    |
+|          256     |       585    |      1862    |      2156    | |       594    |      2777    |      2244    |
+|          384     |       499    |      1870    |      1998    | |       511    |      2586    |      1890    |
+|          512     |       562    |      2095    |      2194    | |       542    |      2973    |      2194    |
+|          768     |       545    |      2045    |      2133    | |       545    |      2365    |      2133    |
+|         1024     |       595    |      2133    |      2438    | |       569    |      2695    |      2179    |
+|         2048     |       587    |      2125    |      2347    | |       521    |      2230    |      1707    |
+|         4096     |       495    |      1890    |      1834    | |       492    |      1876    |      1672    |
+|         8192     |       469    |      1548    |      1729    | |       438    |      1740    |      1664    |
+|         9216     |       468    |      1663    |      1663    | |       446    |      1585    |      1531    |
+|        16384     |       453    |      1608    |      1767    | |       398    |      1476    |      1664    |
+|        32768     |       456    |      1420    |      1503    | |       387    |      1388    |      1345    |
+|       262144     |       309    |       385    |       726    | |       262    |       415    |       840    |
+|      1048576     |       280    |       351    |       739    | |       261    |       313    |       797    |
+|------------------+--------------+--------------+--------------| |--------------+--------------+--------------|
+
+
+
+Windows 7, visual c++ 2010 on a 1.6 GHz Atom N270
+
+Built with:
+cl /Ox -D_USE_MATH_DEFINES /arch:SSE test_pffft.c pffft.c fftpack.c
+
+(visual c++ is definitively not very good with SSE intrinsics...)
+
+| N (input length) | real FFTPack |  real PFFFT  | | cplx FFTPack |  cplx PFFFT  |
+|------------------+--------------+--------------| |--------------+--------------|
+|           64     |       173    |      1009    | |       174    |      1159    |
+|           96     |       169    |      1029    | |       188    |      1201    |
+|          128     |       195    |      1242    | |       191    |      1275    |
+|          192     |       178    |      1312    | |       184    |      1276    |
+|          256     |       196    |      1591    | |       186    |      1281    |
+|          384     |       172    |      1409    | |       181    |      1281    |
+|          512     |       187    |      1640    | |       181    |      1313    |
+|          768     |       171    |      1614    | |       176    |      1258    |
+|         1024     |       186    |      1812    | |       178    |      1223    |
+|         2048     |       190    |      1707    | |       186    |      1099    |
+|         4096     |       182    |      1446    | |       177    |       975    |
+|         8192     |       175    |      1345    | |       169    |      1034    |
+|         9216     |       165    |      1271    | |       168    |      1023    |
+|        16384     |       166    |      1396    | |       165    |       949    |
+|        32768     |       172    |      1311    | |       161    |       881    |
+|       262144     |       136    |       632    | |       134    |       629    |
+|      1048576     |       134    |       698    | |       127    |       623    |
+|------------------+--------------+--------------| |--------------+--------------|
+
+
+
+Ubuntu 12.04, gcc-4.7.3, 32-bit, with fftw 3.3.3 (built with --enable-neon), on a 1.2GHz ARM Cortex A9 (Tegra 3)
+
+Built with:
+gcc-4.7 -O3 -DHAVE_FFTW -march=armv7-a -mtune=cortex-a9 -mfloat-abi=hard -mfpu=neon -ffast-math test_pffft.c pffft.c -o test_pffft_arm fftpack.c -lm -I/usr/local/include/ -L/usr/local/lib/ -lfftw3f
+
+| input len |real FFTPack|  real FFTW | real PFFFT | |cplx FFTPack|  cplx FFTW | cplx PFFFT |
+|-----------+------------+------------+------------| |------------+------------+------------|
+|       64  |      549   |      452   |      731   | |      512   |      602   |      640   |
+|       96  |      421   |      272   |      702   | |      496   |      571   |      602   |
+|      128  |      498   |      512   |      815   | |      597   |      618   |      652   |
+|      160  |      521   |      536   |      815   | |      586   |      669   |      625   |
+|      192  |      539   |      571   |      883   | |      485   |      597   |      626   |
+|      256  |      640   |      539   |      975   | |      569   |      611   |      671   |
+|      384  |      499   |      610   |      879   | |      499   |      602   |      637   |
+|      480  |      518   |      507   |      877   | |      496   |      661   |      616   |
+|      512  |      524   |      591   |     1002   | |      549   |      678   |      668   |
+|      640  |      542   |      612   |      955   | |      568   |      663   |      645   |
+|      768  |      557   |      613   |      981   | |      491   |      663   |      598   |
+|      800  |      514   |      353   |      882   | |      514   |      360   |      574   |
+|     1024  |      640   |      640   |     1067   | |      492   |      683   |      602   |
+|     2048  |      587   |      640   |      908   | |      486   |      640   |      552   |
+|     2400  |      479   |      368   |      777   | |      422   |      376   |      518   |
+|     4096  |      511   |      614   |      853   | |      426   |      640   |      534   |
+|     8192  |      415   |      584   |      708   | |      386   |      622   |      516   |
+|     9216  |      419   |      571   |      687   | |      364   |      586   |      506   |
+|    16384  |      426   |      577   |      716   | |      398   |      606   |      530   |
+|    32768  |      417   |      572   |      673   | |      399   |      572   |      468   |
+|   262144  |      219   |      380   |      293   | |      255   |      431   |      343   |
+|  1048576  |      202   |      274   |      237   | |      265   |      282   |      355   |
+|-----------+------------+------------+------------| |------------+------------+------------|
+
+Same platform as above, but this time pffft and fftpack are built with clang 3.2:
+
+clang -O3 -DHAVE_FFTW -march=armv7-a -mtune=cortex-a9 -mfloat-abi=hard -mfpu=neon -ffast-math test_pffft.c pffft.c -o test_pffft_arm fftpack.c -lm -I/usr/local/include/ -L/usr/local/lib/ -lfftw3f
+
+| input len |real FFTPack|  real FFTW | real PFFFT | |cplx FFTPack|  cplx FFTW | cplx PFFFT |
+|-----------+------------+------------+------------| |------------+------------+------------|
+|       64  |      427   |      452   |      853   | |      427   |      602   |     1024   |
+|       96  |      351   |      276   |      843   | |      337   |      571   |      963   |
+|      128  |      373   |      512   |      996   | |      390   |      618   |     1054   |
+|      160  |      426   |      536   |      987   | |      375   |      669   |      914   |
+|      192  |      404   |      571   |     1079   | |      388   |      588   |     1079   |
+|      256  |      465   |      539   |     1205   | |      445   |      602   |     1170   |
+|      384  |      366   |      610   |     1099   | |      343   |      594   |     1099   |
+|      480  |      356   |      507   |     1140   | |      335   |      651   |      931   |
+|      512  |      411   |      591   |     1213   | |      384   |      649   |     1124   |
+|      640  |      398   |      612   |     1193   | |      373   |      654   |      901   |
+|      768  |      409   |      613   |     1227   | |      383   |      663   |     1044   |
+|      800  |      411   |      348   |     1073   | |      353   |      358   |      809   |
+|     1024  |      427   |      640   |     1280   | |      413   |      692   |     1004   |
+|     2048  |      414   |      626   |     1126   | |      371   |      640   |      853   |
+|     2400  |      399   |      373   |      898   | |      319   |      368   |      653   |
+|     4096  |      404   |      602   |     1059   | |      357   |      633   |      778   |
+|     8192  |      332   |      584   |      792   | |      308   |      616   |      716   |
+|     9216  |      322   |      561   |      783   | |      299   |      586   |      687   |
+|    16384  |      344   |      568   |      778   | |      314   |      617   |      745   |
+|    32768  |      342   |      564   |      737   | |      314   |      552   |      629   |
+|   262144  |      201   |      383   |      313   | |      227   |      435   |      413   |
+|  1048576  |      187   |      262   |      251   | |      228   |      281   |      409   |
+|-----------+------------+------------+------------| |------------+------------+------------|
+
+So it looks like, on ARM, gcc 4.7 is the best at scalar floating point
+(the fftpack performance numbers are better with gcc), while clang is
+the best with neon intrinsics (see how pffft perf has improved with
+clang 3.2).
+
+
+NVIDIA Jetson TK1 board, gcc-4.8.2. The cpu is a 2.3GHz cortex A15 (Tegra K1).
+
+Built with:
+gcc -O3 -march=armv7-a -mtune=native -mfloat-abi=hard -mfpu=neon -ffast-math test_pffft.c pffft.c -o test_pffft_arm fftpack.c -lm
+
+| input len |real FFTPack| real PFFFT | |cplx FFTPack| cplx PFFFT |
+|-----------+------------+------------| |------------+------------|
+|       64  |     1735   |     3308   | |     1994   |     3744   |
+|       96  |     1596   |     3448   | |     1987   |     3572   |
+|      128  |     1807   |     4076   | |     2255   |     3960   |
+|      160  |     1769   |     4083   | |     2071   |     3845   |
+|      192  |     1990   |     4233   | |     2017   |     3939   |
+|      256  |     2191   |     4882   | |     2254   |     4346   |
+|      384  |     1878   |     4492   | |     2073   |     4012   |
+|      480  |     1748   |     4398   | |     1923   |     3951   |
+|      512  |     2030   |     5064   | |     2267   |     4195   |
+|      640  |     1918   |     4756   | |     2094   |     4184   |
+|      768  |     2099   |     4907   | |     2048   |     4297   |
+|      800  |     1822   |     4555   | |     1880   |     4063   |
+|     1024  |     2232   |     5355   | |     2187   |     4420   |
+|     2048  |     2176   |     4983   | |     2027   |     3602   |
+|     2400  |     1741   |     4256   | |     1710   |     3344   |
+|     4096  |     1816   |     3914   | |     1851   |     3349   |
+|     8192  |     1716   |     3481   | |     1700   |     3255   |
+|     9216  |     1735   |     3589   | |     1653   |     3094   |
+|    16384  |     1567   |     3483   | |     1637   |     3244   |
+|    32768  |     1624   |     3240   | |     1655   |     3156   |
+|   262144  |     1012   |     1898   | |      983   |     1503   |
+|  1048576  |      876   |     1154   | |      868   |     1341   |
+|-----------+------------+------------| |------------+------------|
+
+The performance on the tegra K1 is pretty impressive. I'm not
+including the FFTW numbers as they as slightly below the scalar
+fftpack numbers, so something must be wrong (however it seems to be
+correctly configured and is using neon simd instructions).
+
+When using clang 3.4 the pffft version is even a bit faster, reaching
+5.7 GFlops for real ffts of size 1024.
diff --git a/third_party/pffft/src/fftpack.c b/third_party/pffft/src/fftpack.c
new file mode 100644
index 0000000..b6375a80
--- /dev/null
+++ b/third_party/pffft/src/fftpack.c
@@ -0,0 +1,3112 @@
+/*
+  compile with cc -DTESTING_FFTPACK fftpack.c in order to build the
+  test application.
+
+  This is an f2c translation of the full fftpack sources as found on
+  http://www.netlib.org/fftpack/ The translated code has been
+  slightlty edited to remove the ugliest artefacts of the translation
+  (a hundred of wild GOTOs were wiped during that operation).
+
+  The original fftpack file was written by Paul N. Swarztrauber
+  (Version 4, 1985), in fortran 77.
+
+   FFTPACK license:
+
+   http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html
+
+   Copyright (c) 2004 the University Corporation for Atmospheric
+   Research ("UCAR"). All rights reserved. Developed by NCAR's
+   Computational and Information Systems Laboratory, UCAR,
+   www.cisl.ucar.edu.
+
+   Redistribution and use of the Software in source and binary forms,
+   with or without modification, is permitted provided that the
+   following conditions are met:
+
+   - Neither the names of NCAR's Computational and Information Systems
+   Laboratory, the University Corporation for Atmospheric Research,
+   nor the names of its sponsors or contributors may be used to
+   endorse or promote products derived from this Software without
+   specific prior written permission.  
+
+   - Redistributions of source code must retain the above copyright
+   notices, this list of conditions, and the disclaimer below.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions, and the disclaimer below in the
+   documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
+   HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+   SOFTWARE.
+
+   ChangeLog:
+   2011/10/02: this is my first release of this file.
+*/
+
+#include "fftpack.h"
+#include <math.h>
+
+typedef fftpack_real real;
+typedef fftpack_int  integer;
+
+typedef struct f77complex {    
+  real r, i;
+} f77complex;   
+
+#ifdef TESTING_FFTPACK
+static real c_abs(f77complex *c) { return sqrt(c->r*c->r + c->i*c->i); }
+static double dmax(double a, double b) { return a < b ? b : a; }
+#endif
+
+/* translated by f2c (version 20061008), and slightly edited */
+
+static void passfb(integer *nac, integer ido, integer ip, integer l1, integer idl1, 
+                   real *cc, real *c1, real *c2, real *ch, real *ch2, const real *wa, real fsign)
+{
+  /* System generated locals */
+  integer ch_offset, cc_offset,
+    c1_offset, c2_offset, ch2_offset;
+
+  /* Local variables */
+  integer i, j, k, l, jc, lc, ik, idj, idl, inc, idp;
+  real wai, war;
+  integer ipp2, idij, idlj, idot, ipph;
+
+
+#define c1_ref(a_1,a_2,a_3) c1[((a_3)*l1 + (a_2))*ido + a_1]
+#define c2_ref(a_1,a_2) c2[(a_2)*idl1 + a_1]
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*ip + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+#define ch2_ref(a_1,a_2) ch2[(a_2)*idl1 + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  c1_offset = 1 + ido * (1 + l1);
+  c1 -= c1_offset;
+  cc_offset = 1 + ido * (1 + ip);
+  cc -= cc_offset;
+  ch2_offset = 1 + idl1;
+  ch2 -= ch2_offset;
+  c2_offset = 1 + idl1;
+  c2 -= c2_offset;
+  --wa;
+
+  /* Function Body */
+  idot = ido / 2;
+  ipp2 = ip + 2;
+  ipph = (ip + 1) / 2;
+  idp = ip * ido;
+
+  if (ido >= l1) {
+    for (j = 2; j <= ipph; ++j) {
+      jc = ipp2 - j;
+      for (k = 1; k <= l1; ++k) {
+        for (i = 1; i <= ido; ++i) {
+          ch_ref(i, k, j) = cc_ref(i, j, k) + cc_ref(i, jc, k);
+          ch_ref(i, k, jc) = cc_ref(i, j, k) - cc_ref(i, jc, k);
+        }
+      }
+    }
+    for (k = 1; k <= l1; ++k) {
+      for (i = 1; i <= ido; ++i) {
+        ch_ref(i, k, 1) = cc_ref(i, 1, k);
+      }
+    }
+  } else {
+    for (j = 2; j <= ipph; ++j) {
+      jc = ipp2 - j;
+      for (i = 1; i <= ido; ++i) {
+        for (k = 1; k <= l1; ++k) {
+          ch_ref(i, k, j) = cc_ref(i, j, k) + cc_ref(i, jc, k);
+          ch_ref(i, k, jc) = cc_ref(i, j, k) - cc_ref(i, jc, k);
+        }
+      }
+    }
+    for (i = 1; i <= ido; ++i) {
+      for (k = 1; k <= l1; ++k) {
+        ch_ref(i, k, 1) = cc_ref(i, 1, k);
+      }
+    }
+  }
+  idl = 2 - ido;
+  inc = 0;
+  for (l = 2; l <= ipph; ++l) {
+    lc = ipp2 - l;
+    idl += ido;
+    for (ik = 1; ik <= idl1; ++ik) {
+      c2_ref(ik, l) = ch2_ref(ik, 1) + wa[idl - 1] * ch2_ref(ik, 2);
+      c2_ref(ik, lc) = fsign*wa[idl] * ch2_ref(ik, ip);
+    }
+    idlj = idl;
+    inc += ido;
+    for (j = 3; j <= ipph; ++j) {
+      jc = ipp2 - j;
+      idlj += inc;
+      if (idlj > idp) {
+        idlj -= idp;
+      }
+      war = wa[idlj - 1];
+      wai = wa[idlj];
+      for (ik = 1; ik <= idl1; ++ik) {
+        c2_ref(ik, l) = c2_ref(ik, l) + war * ch2_ref(ik, j);
+        c2_ref(ik, lc) = c2_ref(ik, lc) + fsign*wai * ch2_ref(ik, jc);
+      }
+    }
+  }
+  for (j = 2; j <= ipph; ++j) {
+    for (ik = 1; ik <= idl1; ++ik) {
+      ch2_ref(ik, 1) = ch2_ref(ik, 1) + ch2_ref(ik, j);
+    }
+  }
+  for (j = 2; j <= ipph; ++j) {
+    jc = ipp2 - j;
+    for (ik = 2; ik <= idl1; ik += 2) {
+      ch2_ref(ik - 1, j) = c2_ref(ik - 1, j) - c2_ref(ik, jc);
+      ch2_ref(ik - 1, jc) = c2_ref(ik - 1, j) + c2_ref(ik, jc);
+      ch2_ref(ik, j) = c2_ref(ik, j) + c2_ref(ik - 1, jc);
+      ch2_ref(ik, jc) = c2_ref(ik, j) - c2_ref(ik - 1, jc);
+    }
+  }
+  *nac = 1;
+  if (ido == 2) {
+    return;
+  }
+  *nac = 0;
+  for (ik = 1; ik <= idl1; ++ik) {
+    c2_ref(ik, 1) = ch2_ref(ik, 1);
+  }
+  for (j = 2; j <= ip; ++j) {
+    for (k = 1; k <= l1; ++k) {
+      c1_ref(1, k, j) = ch_ref(1, k, j);
+      c1_ref(2, k, j) = ch_ref(2, k, j);
+    }
+  }
+  if (idot <= l1) {
+    idij = 0;
+    for (j = 2; j <= ip; ++j) {
+      idij += 2;
+      for (i = 4; i <= ido; i += 2) {
+        idij += 2;
+        for (k = 1; k <= l1; ++k) {
+          c1_ref(i - 1, k, j) = wa[idij - 1] * ch_ref(i - 1, k, j) - fsign*wa[idij] * ch_ref(i, k, j);
+          c1_ref(i, k, j) = wa[idij - 1] * ch_ref(i, k, j) + fsign*wa[idij] * ch_ref(i - 1, k, j);
+        }
+      }
+    }
+    return;
+  }
+  idj = 2 - ido;
+  for (j = 2; j <= ip; ++j) {
+    idj += ido;
+    for (k = 1; k <= l1; ++k) {
+      idij = idj;
+      for (i = 4; i <= ido; i += 2) {
+        idij += 2;
+        c1_ref(i - 1, k, j) = wa[idij - 1] * ch_ref(i - 1, k, j) - fsign*wa[idij] * ch_ref(i, k, j);
+        c1_ref(i, k, j) = wa[idij - 1] * ch_ref(i, k, j) + fsign*wa[idij] * ch_ref(i - 1, k, j);
+      }
+    }
+  }
+} /* passb */
+
+#undef ch2_ref
+#undef ch_ref
+#undef cc_ref
+#undef c2_ref
+#undef c1_ref
+
+
+static void passb2(integer ido, integer l1, const real *cc, real *ch, const real *wa1)
+{
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k;
+  real ti2, tr2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*2 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  cc_offset = 1 + ido * 3;
+  cc -= cc_offset;
+  --wa1;
+
+  /* Function Body */
+  if (ido <= 2) {
+    for (k = 1; k <= l1; ++k) {
+      ch_ref(1, k, 1) = cc_ref(1, 1, k) + cc_ref(1, 2, k);
+      ch_ref(1, k, 2) = cc_ref(1, 1, k) - cc_ref(1, 2, k);
+      ch_ref(2, k, 1) = cc_ref(2, 1, k) + cc_ref(2, 2, k);
+      ch_ref(2, k, 2) = cc_ref(2, 1, k) - cc_ref(2, 2, k);
+    }
+    return;
+  }
+  for (k = 1; k <= l1; ++k) {
+    for (i = 2; i <= ido; i += 2) {
+      ch_ref(i - 1, k, 1) = cc_ref(i - 1, 1, k) + cc_ref(i - 1, 2, k);
+      tr2 = cc_ref(i - 1, 1, k) - cc_ref(i - 1, 2, k);
+      ch_ref(i, k, 1) = cc_ref(i, 1, k) + cc_ref(i, 2, k);
+      ti2 = cc_ref(i, 1, k) - cc_ref(i, 2, k);
+      ch_ref(i, k, 2) = wa1[i - 1] * ti2 + wa1[i] * tr2;
+      ch_ref(i - 1, k, 2) = wa1[i - 1] * tr2 - wa1[i] * ti2;
+    }
+  }
+} /* passb2 */
+
+#undef ch_ref
+#undef cc_ref
+
+
+static void passb3(integer ido, integer l1, const real *cc, real *ch, const real *wa1, const real *wa2)
+{
+  static const real taur = -.5f;
+  static const real taui = .866025403784439f;
+
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k;
+  real ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*3 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  cc_offset = 1 + (ido << 2);
+  cc -= cc_offset;
+  --wa1;
+  --wa2;
+
+  /* Function Body */
+  if (ido == 2) {
+    for (k = 1; k <= l1; ++k) {
+      tr2 = cc_ref(1, 2, k) + cc_ref(1, 3, k);
+      cr2 = cc_ref(1, 1, k) + taur * tr2;
+      ch_ref(1, k, 1) = cc_ref(1, 1, k) + tr2;
+      ti2 = cc_ref(2, 2, k) + cc_ref(2, 3, k);
+      ci2 = cc_ref(2, 1, k) + taur * ti2;
+      ch_ref(2, k, 1) = cc_ref(2, 1, k) + ti2;
+      cr3 = taui * (cc_ref(1, 2, k) - cc_ref(1, 3, k));
+      ci3 = taui * (cc_ref(2, 2, k) - cc_ref(2, 3, k));
+      ch_ref(1, k, 2) = cr2 - ci3;
+      ch_ref(1, k, 3) = cr2 + ci3;
+      ch_ref(2, k, 2) = ci2 + cr3;
+      ch_ref(2, k, 3) = ci2 - cr3;
+    }
+  } else {
+    for (k = 1; k <= l1; ++k) {
+      for (i = 2; i <= ido; i += 2) {
+        tr2 = cc_ref(i - 1, 2, k) + cc_ref(i - 1, 3, k);
+        cr2 = cc_ref(i - 1, 1, k) + taur * tr2;
+        ch_ref(i - 1, k, 1) = cc_ref(i - 1, 1, k) + tr2;
+        ti2 = cc_ref(i, 2, k) + cc_ref(i, 3, k);
+        ci2 = cc_ref(i, 1, k) + taur * ti2;
+        ch_ref(i, k, 1) = cc_ref(i, 1, k) + ti2;
+        cr3 = taui * (cc_ref(i - 1, 2, k) - cc_ref(i - 1, 3, k));
+        ci3 = taui * (cc_ref(i, 2, k) - cc_ref(i, 3, k));
+        dr2 = cr2 - ci3;
+        dr3 = cr2 + ci3;
+        di2 = ci2 + cr3;
+        di3 = ci2 - cr3;
+        ch_ref(i, k, 2) = wa1[i - 1] * di2 + wa1[i] * dr2;
+        ch_ref(i - 1, k, 2) = wa1[i - 1] * dr2 - wa1[i] * di2;
+        ch_ref(i, k, 3) = wa2[i - 1] * di3 + wa2[i] * dr3;
+        ch_ref(i - 1, k, 3) = wa2[i - 1] * dr3 - wa2[i] * di3;
+      }
+    }
+  }
+} /* passb3 */
+
+#undef ch_ref
+#undef cc_ref
+
+
+static void passb4(integer ido, integer l1, const real *cc, real *ch, 
+                   const real *wa1, const real *wa2, const real *wa3)
+{
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k;
+  real ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*4 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  cc_offset = 1 + ido * 5;
+  cc -= cc_offset;
+  --wa1;
+  --wa2;
+  --wa3;
+
+  /* Function Body */
+  if (ido == 2) {
+    for (k = 1; k <= l1; ++k) {
+      ti1 = cc_ref(2, 1, k) - cc_ref(2, 3, k);
+      ti2 = cc_ref(2, 1, k) + cc_ref(2, 3, k);
+      tr4 = cc_ref(2, 4, k) - cc_ref(2, 2, k);
+      ti3 = cc_ref(2, 2, k) + cc_ref(2, 4, k);
+      tr1 = cc_ref(1, 1, k) - cc_ref(1, 3, k);
+      tr2 = cc_ref(1, 1, k) + cc_ref(1, 3, k);
+      ti4 = cc_ref(1, 2, k) - cc_ref(1, 4, k);
+      tr3 = cc_ref(1, 2, k) + cc_ref(1, 4, k);
+      ch_ref(1, k, 1) = tr2 + tr3;
+      ch_ref(1, k, 3) = tr2 - tr3;
+      ch_ref(2, k, 1) = ti2 + ti3;
+      ch_ref(2, k, 3) = ti2 - ti3;
+      ch_ref(1, k, 2) = tr1 + tr4;
+      ch_ref(1, k, 4) = tr1 - tr4;
+      ch_ref(2, k, 2) = ti1 + ti4;
+      ch_ref(2, k, 4) = ti1 - ti4;
+    }
+  } else {
+    for (k = 1; k <= l1; ++k) {
+      for (i = 2; i <= ido; i += 2) {
+        ti1 = cc_ref(i, 1, k) - cc_ref(i, 3, k);
+        ti2 = cc_ref(i, 1, k) + cc_ref(i, 3, k);
+        ti3 = cc_ref(i, 2, k) + cc_ref(i, 4, k);
+        tr4 = cc_ref(i, 4, k) - cc_ref(i, 2, k);
+        tr1 = cc_ref(i - 1, 1, k) - cc_ref(i - 1, 3, k);
+        tr2 = cc_ref(i - 1, 1, k) + cc_ref(i - 1, 3, k);
+        ti4 = cc_ref(i - 1, 2, k) - cc_ref(i - 1, 4, k);
+        tr3 = cc_ref(i - 1, 2, k) + cc_ref(i - 1, 4, k);
+        ch_ref(i - 1, k, 1) = tr2 + tr3;
+        cr3 = tr2 - tr3;
+        ch_ref(i, k, 1) = ti2 + ti3;
+        ci3 = ti2 - ti3;
+        cr2 = tr1 + tr4;
+        cr4 = tr1 - tr4;
+        ci2 = ti1 + ti4;
+        ci4 = ti1 - ti4;
+        ch_ref(i - 1, k, 2) = wa1[i - 1] * cr2 - wa1[i] * ci2;
+        ch_ref(i, k, 2) = wa1[i - 1] * ci2 + wa1[i] * cr2;
+        ch_ref(i - 1, k, 3) = wa2[i - 1] * cr3 - wa2[i] * ci3;
+        ch_ref(i, k, 3) = wa2[i - 1] * ci3 + wa2[i] * cr3;
+        ch_ref(i - 1, k, 4) = wa3[i - 1] * cr4 - wa3[i] * ci4;
+        ch_ref(i, k, 4) = wa3[i - 1] * ci4 + wa3[i] * cr4;
+      }
+    }
+  }
+} /* passb4 */
+
+#undef ch_ref
+#undef cc_ref
+
+/* passf5 and passb5 merged */
+static void passfb5(integer ido, integer l1, const real *cc, real *ch, 
+                    const real *wa1, const real *wa2, const real *wa3, const real *wa4, real fsign)
+{
+  const real tr11 = .309016994374947f;
+  const real ti11 = .951056516295154f*fsign;
+  const real tr12 = -.809016994374947f;
+  const real ti12 = .587785252292473f*fsign;
+
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k;
+  real ci2, ci3, ci4, ci5, di3, di4, di5, di2, cr2, cr3, cr5, cr4, ti2, ti3,
+    ti4, ti5, dr3, dr4, dr5, dr2, tr2, tr3, tr4, tr5;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*5 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  cc_offset = 1 + ido * 6;
+  cc -= cc_offset;
+  --wa1;
+  --wa2;
+  --wa3;
+  --wa4;
+
+  /* Function Body */
+  if (ido == 2) {
+    for (k = 1; k <= l1; ++k) {
+      ti5 = cc_ref(2, 2, k) - cc_ref(2, 5, k);
+      ti2 = cc_ref(2, 2, k) + cc_ref(2, 5, k);
+      ti4 = cc_ref(2, 3, k) - cc_ref(2, 4, k);
+      ti3 = cc_ref(2, 3, k) + cc_ref(2, 4, k);
+      tr5 = cc_ref(1, 2, k) - cc_ref(1, 5, k);
+      tr2 = cc_ref(1, 2, k) + cc_ref(1, 5, k);
+      tr4 = cc_ref(1, 3, k) - cc_ref(1, 4, k);
+      tr3 = cc_ref(1, 3, k) + cc_ref(1, 4, k);
+      ch_ref(1, k, 1) = cc_ref(1, 1, k) + tr2 + tr3;
+      ch_ref(2, k, 1) = cc_ref(2, 1, k) + ti2 + ti3;
+      cr2 = cc_ref(1, 1, k) + tr11 * tr2 + tr12 * tr3;
+      ci2 = cc_ref(2, 1, k) + tr11 * ti2 + tr12 * ti3;
+      cr3 = cc_ref(1, 1, k) + tr12 * tr2 + tr11 * tr3;
+      ci3 = cc_ref(2, 1, k) + tr12 * ti2 + tr11 * ti3;
+      cr5 = ti11 * tr5 + ti12 * tr4;
+      ci5 = ti11 * ti5 + ti12 * ti4;
+      cr4 = ti12 * tr5 - ti11 * tr4;
+      ci4 = ti12 * ti5 - ti11 * ti4;
+      ch_ref(1, k, 2) = cr2 - ci5;
+      ch_ref(1, k, 5) = cr2 + ci5;
+      ch_ref(2, k, 2) = ci2 + cr5;
+      ch_ref(2, k, 3) = ci3 + cr4;
+      ch_ref(1, k, 3) = cr3 - ci4;
+      ch_ref(1, k, 4) = cr3 + ci4;
+      ch_ref(2, k, 4) = ci3 - cr4;
+      ch_ref(2, k, 5) = ci2 - cr5;
+    }
+  } else {
+    for (k = 1; k <= l1; ++k) {
+      for (i = 2; i <= ido; i += 2) {
+        ti5 = cc_ref(i, 2, k) - cc_ref(i, 5, k);
+        ti2 = cc_ref(i, 2, k) + cc_ref(i, 5, k);
+        ti4 = cc_ref(i, 3, k) - cc_ref(i, 4, k);
+        ti3 = cc_ref(i, 3, k) + cc_ref(i, 4, k);
+        tr5 = cc_ref(i - 1, 2, k) - cc_ref(i - 1, 5, k);
+        tr2 = cc_ref(i - 1, 2, k) + cc_ref(i - 1, 5, k);
+        tr4 = cc_ref(i - 1, 3, k) - cc_ref(i - 1, 4, k);
+        tr3 = cc_ref(i - 1, 3, k) + cc_ref(i - 1, 4, k);
+        ch_ref(i - 1, k, 1) = cc_ref(i - 1, 1, k) + tr2 + tr3;
+        ch_ref(i, k, 1) = cc_ref(i, 1, k) + ti2 + ti3;
+        cr2 = cc_ref(i - 1, 1, k) + tr11 * tr2 + tr12 * tr3;
+        ci2 = cc_ref(i, 1, k) + tr11 * ti2 + tr12 * ti3;
+        cr3 = cc_ref(i - 1, 1, k) + tr12 * tr2 + tr11 * tr3;
+        ci3 = cc_ref(i, 1, k) + tr12 * ti2 + tr11 * ti3;
+        cr5 = ti11 * tr5 + ti12 * tr4;
+        ci5 = ti11 * ti5 + ti12 * ti4;
+        cr4 = ti12 * tr5 - ti11 * tr4;
+        ci4 = ti12 * ti5 - ti11 * ti4;
+        dr3 = cr3 - ci4;
+        dr4 = cr3 + ci4;
+        di3 = ci3 + cr4;
+        di4 = ci3 - cr4;
+        dr5 = cr2 + ci5;
+        dr2 = cr2 - ci5;
+        di5 = ci2 - cr5;
+        di2 = ci2 + cr5;
+        ch_ref(i - 1, k, 2) = wa1[i - 1] * dr2 - fsign*wa1[i] * di2;
+        ch_ref(i, k, 2) = wa1[i - 1] * di2 + fsign*wa1[i] * dr2;
+        ch_ref(i - 1, k, 3) = wa2[i - 1] * dr3 - fsign*wa2[i] * di3;
+        ch_ref(i, k, 3) = wa2[i - 1] * di3 + fsign*wa2[i] * dr3;
+        ch_ref(i - 1, k, 4) = wa3[i - 1] * dr4 - fsign*wa3[i] * di4;
+        ch_ref(i, k, 4) = wa3[i - 1] * di4 + fsign*wa3[i] * dr4;
+        ch_ref(i - 1, k, 5) = wa4[i - 1] * dr5 - fsign*wa4[i] * di5;
+        ch_ref(i, k, 5) = wa4[i - 1] * di5 + fsign*wa4[i] * dr5;
+      }
+    }
+  }
+} /* passb5 */
+
+#undef ch_ref
+#undef cc_ref
+
+static void passf2(integer ido, integer l1, const real *cc, real *ch, const real *wa1)
+{
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k;
+  real ti2, tr2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*2 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  cc_offset = 1 + ido * 3;
+  cc -= cc_offset;
+  --wa1;
+
+  /* Function Body */
+  if (ido == 2) {
+    for (k = 1; k <= l1; ++k) {
+      ch_ref(1, k, 1) = cc_ref(1, 1, k) + cc_ref(1, 2, k);
+      ch_ref(1, k, 2) = cc_ref(1, 1, k) - cc_ref(1, 2, k);
+      ch_ref(2, k, 1) = cc_ref(2, 1, k) + cc_ref(2, 2, k);
+      ch_ref(2, k, 2) = cc_ref(2, 1, k) - cc_ref(2, 2, k);
+    }
+  } else {
+    for (k = 1; k <= l1; ++k) {
+      for (i = 2; i <= ido; i += 2) {
+        ch_ref(i - 1, k, 1) = cc_ref(i - 1, 1, k) + cc_ref(i - 1, 2,
+                                                           k);
+        tr2 = cc_ref(i - 1, 1, k) - cc_ref(i - 1, 2, k);
+        ch_ref(i, k, 1) = cc_ref(i, 1, k) + cc_ref(i, 2, k);
+        ti2 = cc_ref(i, 1, k) - cc_ref(i, 2, k);
+        ch_ref(i, k, 2) = wa1[i - 1] * ti2 - wa1[i] * tr2;
+        ch_ref(i - 1, k, 2) = wa1[i - 1] * tr2 + wa1[i] * ti2;
+      }
+    }
+  }
+} /* passf2 */
+
+#undef ch_ref
+#undef cc_ref
+
+
+static void passf3(integer ido, integer l1, const real *cc, real *ch, 
+                   const real *wa1, const real *wa2)
+{
+  static const real taur = -.5f;
+  static const real taui = -.866025403784439f;
+
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k;
+  real ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*3 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  cc_offset = 1 + (ido << 2);
+  cc -= cc_offset;
+  --wa1;
+  --wa2;
+
+  /* Function Body */
+  if (ido == 2) {
+    for (k = 1; k <= l1; ++k) {
+      tr2 = cc_ref(1, 2, k) + cc_ref(1, 3, k);
+      cr2 = cc_ref(1, 1, k) + taur * tr2;
+      ch_ref(1, k, 1) = cc_ref(1, 1, k) + tr2;
+      ti2 = cc_ref(2, 2, k) + cc_ref(2, 3, k);
+      ci2 = cc_ref(2, 1, k) + taur * ti2;
+      ch_ref(2, k, 1) = cc_ref(2, 1, k) + ti2;
+      cr3 = taui * (cc_ref(1, 2, k) - cc_ref(1, 3, k));
+      ci3 = taui * (cc_ref(2, 2, k) - cc_ref(2, 3, k));
+      ch_ref(1, k, 2) = cr2 - ci3;
+      ch_ref(1, k, 3) = cr2 + ci3;
+      ch_ref(2, k, 2) = ci2 + cr3;
+      ch_ref(2, k, 3) = ci2 - cr3;
+    }
+  } else {
+    for (k = 1; k <= l1; ++k) {
+      for (i = 2; i <= ido; i += 2) {
+        tr2 = cc_ref(i - 1, 2, k) + cc_ref(i - 1, 3, k);
+        cr2 = cc_ref(i - 1, 1, k) + taur * tr2;
+        ch_ref(i - 1, k, 1) = cc_ref(i - 1, 1, k) + tr2;
+        ti2 = cc_ref(i, 2, k) + cc_ref(i, 3, k);
+        ci2 = cc_ref(i, 1, k) + taur * ti2;
+        ch_ref(i, k, 1) = cc_ref(i, 1, k) + ti2;
+        cr3 = taui * (cc_ref(i - 1, 2, k) - cc_ref(i - 1, 3, k));
+        ci3 = taui * (cc_ref(i, 2, k) - cc_ref(i, 3, k));
+        dr2 = cr2 - ci3;
+        dr3 = cr2 + ci3;
+        di2 = ci2 + cr3;
+        di3 = ci2 - cr3;
+        ch_ref(i, k, 2) = wa1[i - 1] * di2 - wa1[i] * dr2;
+        ch_ref(i - 1, k, 2) = wa1[i - 1] * dr2 + wa1[i] * di2;
+        ch_ref(i, k, 3) = wa2[i - 1] * di3 - wa2[i] * dr3;
+        ch_ref(i - 1, k, 3) = wa2[i - 1] * dr3 + wa2[i] * di3;
+      }
+    }
+  }
+} /* passf3 */
+
+#undef ch_ref
+#undef cc_ref
+
+
+static void passf4(integer ido, integer l1, const real *cc, real *ch, 
+                   const real *wa1, const real *wa2, const real *wa3)
+{
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k;
+  real ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*4 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  cc_offset = 1 + ido * 5;
+  cc -= cc_offset;
+  --wa1;
+  --wa2;
+  --wa3;
+
+  /* Function Body */
+  if (ido == 2) {
+    for (k = 1; k <= l1; ++k) {
+      ti1 = cc_ref(2, 1, k) - cc_ref(2, 3, k);
+      ti2 = cc_ref(2, 1, k) + cc_ref(2, 3, k);
+      tr4 = cc_ref(2, 2, k) - cc_ref(2, 4, k);
+      ti3 = cc_ref(2, 2, k) + cc_ref(2, 4, k);
+      tr1 = cc_ref(1, 1, k) - cc_ref(1, 3, k);
+      tr2 = cc_ref(1, 1, k) + cc_ref(1, 3, k);
+      ti4 = cc_ref(1, 4, k) - cc_ref(1, 2, k);
+      tr3 = cc_ref(1, 2, k) + cc_ref(1, 4, k);
+      ch_ref(1, k, 1) = tr2 + tr3;
+      ch_ref(1, k, 3) = tr2 - tr3;
+      ch_ref(2, k, 1) = ti2 + ti3;
+      ch_ref(2, k, 3) = ti2 - ti3;
+      ch_ref(1, k, 2) = tr1 + tr4;
+      ch_ref(1, k, 4) = tr1 - tr4;
+      ch_ref(2, k, 2) = ti1 + ti4;
+      ch_ref(2, k, 4) = ti1 - ti4;
+    }
+  } else {
+    for (k = 1; k <= l1; ++k) {
+      for (i = 2; i <= ido; i += 2) {
+        ti1 = cc_ref(i, 1, k) - cc_ref(i, 3, k);
+        ti2 = cc_ref(i, 1, k) + cc_ref(i, 3, k);
+        ti3 = cc_ref(i, 2, k) + cc_ref(i, 4, k);
+        tr4 = cc_ref(i, 2, k) - cc_ref(i, 4, k);
+        tr1 = cc_ref(i - 1, 1, k) - cc_ref(i - 1, 3, k);
+        tr2 = cc_ref(i - 1, 1, k) + cc_ref(i - 1, 3, k);
+        ti4 = cc_ref(i - 1, 4, k) - cc_ref(i - 1, 2, k);
+        tr3 = cc_ref(i - 1, 2, k) + cc_ref(i - 1, 4, k);
+        ch_ref(i - 1, k, 1) = tr2 + tr3;
+        cr3 = tr2 - tr3;
+        ch_ref(i, k, 1) = ti2 + ti3;
+        ci3 = ti2 - ti3;
+        cr2 = tr1 + tr4;
+        cr4 = tr1 - tr4;
+        ci2 = ti1 + ti4;
+        ci4 = ti1 - ti4;
+        ch_ref(i - 1, k, 2) = wa1[i - 1] * cr2 + wa1[i] * ci2;
+        ch_ref(i, k, 2) = wa1[i - 1] * ci2 - wa1[i] * cr2;
+        ch_ref(i - 1, k, 3) = wa2[i - 1] * cr3 + wa2[i] * ci3;
+        ch_ref(i, k, 3) = wa2[i - 1] * ci3 - wa2[i] * cr3;
+        ch_ref(i - 1, k, 4) = wa3[i - 1] * cr4 + wa3[i] * ci4;
+        ch_ref(i, k, 4) = wa3[i - 1] * ci4 - wa3[i] * cr4;
+      }
+    }
+  }
+} /* passf4 */
+
+#undef ch_ref
+#undef cc_ref
+
+static void radb2(integer ido, integer l1, const real *cc, real *ch, const real *wa1)
+{
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k, ic;
+  real ti2, tr2;
+  integer idp2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*2 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  cc_offset = 1 + ido * 3;
+  cc -= cc_offset;
+  --wa1;
+
+  /* Function Body */
+  for (k = 1; k <= l1; ++k) {
+    ch_ref(1, k, 1) = cc_ref(1, 1, k) + cc_ref(ido, 2, k);
+    ch_ref(1, k, 2) = cc_ref(1, 1, k) - cc_ref(ido, 2, k);
+  }
+  if (ido < 2) return;
+  else if (ido != 2) {
+    idp2 = ido + 2;
+    for (k = 1; k <= l1; ++k) {
+      for (i = 3; i <= ido; i += 2) {
+        ic = idp2 - i;
+        ch_ref(i - 1, k, 1) = cc_ref(i - 1, 1, k) + cc_ref(ic - 1, 2, 
+                                                           k);
+        tr2 = cc_ref(i - 1, 1, k) - cc_ref(ic - 1, 2, k);
+        ch_ref(i, k, 1) = cc_ref(i, 1, k) - cc_ref(ic, 2, k);
+        ti2 = cc_ref(i, 1, k) + cc_ref(ic, 2, k);
+        ch_ref(i - 1, k, 2) = wa1[i - 2] * tr2 - wa1[i - 1] * ti2;
+        ch_ref(i, k, 2) = wa1[i - 2] * ti2 + wa1[i - 1] * tr2;
+      }
+    }
+    if (ido % 2 == 1) return;
+  }
+  for (k = 1; k <= l1; ++k) {
+    ch_ref(ido, k, 1) = cc_ref(ido, 1, k) + cc_ref(ido, 1, k);
+    ch_ref(ido, k, 2) = -(cc_ref(1, 2, k) + cc_ref(1, 2, k));
+  }
+} /* radb2 */
+
+#undef ch_ref
+#undef cc_ref
+
+
+static void radb3(integer ido, integer l1, const real *cc, real *ch, 
+                  const real *wa1, const real *wa2)
+{
+  /* Initialized data */
+
+  static const real taur = -.5f;
+  static const real taui = .866025403784439f;
+
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k, ic;
+  real ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2;
+  integer idp2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*3 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  cc_offset = 1 + (ido << 2);
+  cc -= cc_offset;
+  --wa1;
+  --wa2;
+
+  /* Function Body */
+  for (k = 1; k <= l1; ++k) {
+    tr2 = cc_ref(ido, 2, k) + cc_ref(ido, 2, k);
+    cr2 = cc_ref(1, 1, k) + taur * tr2;
+    ch_ref(1, k, 1) = cc_ref(1, 1, k) + tr2;
+    ci3 = taui * (cc_ref(1, 3, k) + cc_ref(1, 3, k));
+    ch_ref(1, k, 2) = cr2 - ci3;
+    ch_ref(1, k, 3) = cr2 + ci3;
+  }
+  if (ido == 1) {
+    return;
+  }
+  idp2 = ido + 2;
+  for (k = 1; k <= l1; ++k) {
+    for (i = 3; i <= ido; i += 2) {
+      ic = idp2 - i;
+      tr2 = cc_ref(i - 1, 3, k) + cc_ref(ic - 1, 2, k);
+      cr2 = cc_ref(i - 1, 1, k) + taur * tr2;
+      ch_ref(i - 1, k, 1) = cc_ref(i - 1, 1, k) + tr2;
+      ti2 = cc_ref(i, 3, k) - cc_ref(ic, 2, k);
+      ci2 = cc_ref(i, 1, k) + taur * ti2;
+      ch_ref(i, k, 1) = cc_ref(i, 1, k) + ti2;
+      cr3 = taui * (cc_ref(i - 1, 3, k) - cc_ref(ic - 1, 2, k));
+      ci3 = taui * (cc_ref(i, 3, k) + cc_ref(ic, 2, k));
+      dr2 = cr2 - ci3;
+      dr3 = cr2 + ci3;
+      di2 = ci2 + cr3;
+      di3 = ci2 - cr3;
+      ch_ref(i - 1, k, 2) = wa1[i - 2] * dr2 - wa1[i - 1] * di2;
+      ch_ref(i, k, 2) = wa1[i - 2] * di2 + wa1[i - 1] * dr2;
+      ch_ref(i - 1, k, 3) = wa2[i - 2] * dr3 - wa2[i - 1] * di3;
+      ch_ref(i, k, 3) = wa2[i - 2] * di3 + wa2[i - 1] * dr3;
+    }
+  }
+} /* radb3 */
+
+#undef ch_ref
+#undef cc_ref
+
+
+static void radb4(integer ido, integer l1, const real *cc, real *ch, 
+                  const real *wa1, const real *wa2, const real *wa3)
+{
+  /* Initialized data */
+
+  static const real sqrt2 = 1.414213562373095f;
+
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k, ic;
+  real ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;
+  integer idp2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*4 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  cc_offset = 1 + ido * 5;
+  cc -= cc_offset;
+  --wa1;
+  --wa2;
+  --wa3;
+
+  /* Function Body */
+  for (k = 1; k <= l1; ++k) {
+    tr1 = cc_ref(1, 1, k) - cc_ref(ido, 4, k);
+    tr2 = cc_ref(1, 1, k) + cc_ref(ido, 4, k);
+    tr3 = cc_ref(ido, 2, k) + cc_ref(ido, 2, k);
+    tr4 = cc_ref(1, 3, k) + cc_ref(1, 3, k);
+    ch_ref(1, k, 1) = tr2 + tr3;
+    ch_ref(1, k, 2) = tr1 - tr4;
+    ch_ref(1, k, 3) = tr2 - tr3;
+    ch_ref(1, k, 4) = tr1 + tr4;
+  }
+  if (ido < 2) return;
+  if (ido != 2) {
+    idp2 = ido + 2;
+    for (k = 1; k <= l1; ++k) {
+      for (i = 3; i <= ido; i += 2) {
+        ic = idp2 - i;
+        ti1 = cc_ref(i, 1, k) + cc_ref(ic, 4, k);
+        ti2 = cc_ref(i, 1, k) - cc_ref(ic, 4, k);
+        ti3 = cc_ref(i, 3, k) - cc_ref(ic, 2, k);
+        tr4 = cc_ref(i, 3, k) + cc_ref(ic, 2, k);
+        tr1 = cc_ref(i - 1, 1, k) - cc_ref(ic - 1, 4, k);
+        tr2 = cc_ref(i - 1, 1, k) + cc_ref(ic - 1, 4, k);
+        ti4 = cc_ref(i - 1, 3, k) - cc_ref(ic - 1, 2, k);
+        tr3 = cc_ref(i - 1, 3, k) + cc_ref(ic - 1, 2, k);
+        ch_ref(i - 1, k, 1) = tr2 + tr3;
+        cr3 = tr2 - tr3;
+        ch_ref(i, k, 1) = ti2 + ti3;
+        ci3 = ti2 - ti3;
+        cr2 = tr1 - tr4;
+        cr4 = tr1 + tr4;
+        ci2 = ti1 + ti4;
+        ci4 = ti1 - ti4;
+        ch_ref(i - 1, k, 2) = wa1[i - 2] * cr2 - wa1[i - 1] * ci2;
+        ch_ref(i, k, 2) = wa1[i - 2] * ci2 + wa1[i - 1] * cr2;
+        ch_ref(i - 1, k, 3) = wa2[i - 2] * cr3 - wa2[i - 1] * ci3;
+        ch_ref(i, k, 3) = wa2[i - 2] * ci3 + wa2[i - 1] * cr3;
+        ch_ref(i - 1, k, 4) = wa3[i - 2] * cr4 - wa3[i - 1] * ci4;
+        ch_ref(i, k, 4) = wa3[i - 2] * ci4 + wa3[i - 1] * cr4;
+      }
+    }
+    if (ido % 2 == 1) return;
+  }
+  for (k = 1; k <= l1; ++k) {
+    ti1 = cc_ref(1, 2, k) + cc_ref(1, 4, k);
+    ti2 = cc_ref(1, 4, k) - cc_ref(1, 2, k);
+    tr1 = cc_ref(ido, 1, k) - cc_ref(ido, 3, k);
+    tr2 = cc_ref(ido, 1, k) + cc_ref(ido, 3, k);
+    ch_ref(ido, k, 1) = tr2 + tr2;
+    ch_ref(ido, k, 2) = sqrt2 * (tr1 - ti1);
+    ch_ref(ido, k, 3) = ti2 + ti2;
+    ch_ref(ido, k, 4) = -sqrt2 * (tr1 + ti1);
+  }
+} /* radb4 */
+
+#undef ch_ref
+#undef cc_ref
+
+
+static void radb5(integer ido, integer l1, const real *cc, real *ch, 
+                  const real *wa1, const real *wa2, const real *wa3, const real *wa4)
+{
+  /* Initialized data */
+
+  static const real tr11 = .309016994374947f;
+  static const real ti11 = .951056516295154f;
+  static const real tr12 = -.809016994374947f;
+  static const real ti12 = .587785252292473f;
+
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k, ic;
+  real ci2, ci3, ci4, ci5, di3, di4, di5, di2, cr2, cr3, cr5, cr4, ti2, ti3,
+    ti4, ti5, dr3, dr4, dr5, dr2, tr2, tr3, tr4, tr5;
+  integer idp2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*5 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  cc_offset = 1 + ido * 6;
+  cc -= cc_offset;
+  --wa1;
+  --wa2;
+  --wa3;
+  --wa4;
+
+  /* Function Body */
+  for (k = 1; k <= l1; ++k) {
+    ti5 = cc_ref(1, 3, k) + cc_ref(1, 3, k);
+    ti4 = cc_ref(1, 5, k) + cc_ref(1, 5, k);
+    tr2 = cc_ref(ido, 2, k) + cc_ref(ido, 2, k);
+    tr3 = cc_ref(ido, 4, k) + cc_ref(ido, 4, k);
+    ch_ref(1, k, 1) = cc_ref(1, 1, k) + tr2 + tr3;
+    cr2 = cc_ref(1, 1, k) + tr11 * tr2 + tr12 * tr3;
+    cr3 = cc_ref(1, 1, k) + tr12 * tr2 + tr11 * tr3;
+    ci5 = ti11 * ti5 + ti12 * ti4;
+    ci4 = ti12 * ti5 - ti11 * ti4;
+    ch_ref(1, k, 2) = cr2 - ci5;
+    ch_ref(1, k, 3) = cr3 - ci4;
+    ch_ref(1, k, 4) = cr3 + ci4;
+    ch_ref(1, k, 5) = cr2 + ci5;
+  }
+  if (ido == 1) {
+    return;
+  }
+  idp2 = ido + 2;
+  for (k = 1; k <= l1; ++k) {
+    for (i = 3; i <= ido; i += 2) {
+      ic = idp2 - i;
+      ti5 = cc_ref(i, 3, k) + cc_ref(ic, 2, k);
+      ti2 = cc_ref(i, 3, k) - cc_ref(ic, 2, k);
+      ti4 = cc_ref(i, 5, k) + cc_ref(ic, 4, k);
+      ti3 = cc_ref(i, 5, k) - cc_ref(ic, 4, k);
+      tr5 = cc_ref(i - 1, 3, k) - cc_ref(ic - 1, 2, k);
+      tr2 = cc_ref(i - 1, 3, k) + cc_ref(ic - 1, 2, k);
+      tr4 = cc_ref(i - 1, 5, k) - cc_ref(ic - 1, 4, k);
+      tr3 = cc_ref(i - 1, 5, k) + cc_ref(ic - 1, 4, k);
+      ch_ref(i - 1, k, 1) = cc_ref(i - 1, 1, k) + tr2 + tr3;
+      ch_ref(i, k, 1) = cc_ref(i, 1, k) + ti2 + ti3;
+      cr2 = cc_ref(i - 1, 1, k) + tr11 * tr2 + tr12 * tr3;
+      ci2 = cc_ref(i, 1, k) + tr11 * ti2 + tr12 * ti3;
+      cr3 = cc_ref(i - 1, 1, k) + tr12 * tr2 + tr11 * tr3;
+      ci3 = cc_ref(i, 1, k) + tr12 * ti2 + tr11 * ti3;
+      cr5 = ti11 * tr5 + ti12 * tr4;
+      ci5 = ti11 * ti5 + ti12 * ti4;
+      cr4 = ti12 * tr5 - ti11 * tr4;
+      ci4 = ti12 * ti5 - ti11 * ti4;
+      dr3 = cr3 - ci4;
+      dr4 = cr3 + ci4;
+      di3 = ci3 + cr4;
+      di4 = ci3 - cr4;
+      dr5 = cr2 + ci5;
+      dr2 = cr2 - ci5;
+      di5 = ci2 - cr5;
+      di2 = ci2 + cr5;
+      ch_ref(i - 1, k, 2) = wa1[i - 2] * dr2 - wa1[i - 1] * di2;
+      ch_ref(i, k, 2) = wa1[i - 2] * di2 + wa1[i - 1] * dr2;
+      ch_ref(i - 1, k, 3) = wa2[i - 2] * dr3 - wa2[i - 1] * di3;
+      ch_ref(i, k, 3) = wa2[i - 2] * di3 + wa2[i - 1] * dr3;
+      ch_ref(i - 1, k, 4) = wa3[i - 2] * dr4 - wa3[i - 1] * di4;
+      ch_ref(i, k, 4) = wa3[i - 2] * di4 + wa3[i - 1] * dr4;
+      ch_ref(i - 1, k, 5) = wa4[i - 2] * dr5 - wa4[i - 1] * di5;
+      ch_ref(i, k, 5) = wa4[i - 2] * di5 + wa4[i - 1] * dr5;
+    }
+  }
+} /* radb5 */
+
+#undef ch_ref
+#undef cc_ref
+
+
+static void radbg(integer ido, integer ip, integer l1, integer idl1, 
+                  const real *cc, real *c1, real *c2, real *ch, real *ch2, const real *wa)
+{
+  /* System generated locals */
+  integer ch_offset, cc_offset,
+    c1_offset, c2_offset, ch2_offset;
+
+  /* Local variables */
+  integer i, j, k, l, j2, ic, jc, lc, ik, is;
+  real dc2, ai1, ai2, ar1, ar2, ds2;
+  integer nbd;
+  real dcp, arg, dsp, ar1h, ar2h;
+  integer idp2, ipp2, idij, ipph;
+
+
+#define c1_ref(a_1,a_2,a_3) c1[((a_3)*l1 + (a_2))*ido + a_1]
+#define c2_ref(a_1,a_2) c2[(a_2)*idl1 + a_1]
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*ip + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+#define ch2_ref(a_1,a_2) ch2[(a_2)*idl1 + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  c1_offset = 1 + ido * (1 + l1);
+  c1 -= c1_offset;
+  cc_offset = 1 + ido * (1 + ip);
+  cc -= cc_offset;
+  ch2_offset = 1 + idl1;
+  ch2 -= ch2_offset;
+  c2_offset = 1 + idl1;
+  c2 -= c2_offset;
+  --wa;
+
+  /* Function Body */
+  arg = (2*M_PI) / (real) (ip);
+  dcp = cos(arg);
+  dsp = sin(arg);
+  idp2 = ido + 2;
+  nbd = (ido - 1) / 2;
+  ipp2 = ip + 2;
+  ipph = (ip + 1) / 2;
+  if (ido >= l1) {
+    for (k = 1; k <= l1; ++k) {
+      for (i = 1; i <= ido; ++i) {
+        ch_ref(i, k, 1) = cc_ref(i, 1, k);
+      }
+    }
+  } else {
+    for (i = 1; i <= ido; ++i) {
+      for (k = 1; k <= l1; ++k) {
+        ch_ref(i, k, 1) = cc_ref(i, 1, k);
+      }
+    }
+  }
+  for (j = 2; j <= ipph; ++j) {
+    jc = ipp2 - j;
+    j2 = j + j;
+    for (k = 1; k <= l1; ++k) {
+      ch_ref(1, k, j) = cc_ref(ido, j2 - 2, k) + cc_ref(ido, j2 - 2, k);
+      ch_ref(1, k, jc) = cc_ref(1, j2 - 1, k) + cc_ref(1, j2 - 1, k);
+    }
+  }
+  if (ido != 1) {
+    if (nbd >= l1) {
+      for (j = 2; j <= ipph; ++j) {
+        jc = ipp2 - j;
+        for (k = 1; k <= l1; ++k) {
+          for (i = 3; i <= ido; i += 2) {
+            ic = idp2 - i;
+            ch_ref(i - 1, k, j) = cc_ref(i - 1, (j << 1) - 1, k) + cc_ref(ic - 1, (j << 1) - 2, k);
+            ch_ref(i - 1, k, jc) = cc_ref(i - 1, (j << 1) - 1, k) - cc_ref(ic - 1, (j << 1) - 2, k);
+            ch_ref(i, k, j) = cc_ref(i, (j << 1) - 1, k) - cc_ref(ic, (j << 1) - 2, k);
+            ch_ref(i, k, jc) = cc_ref(i, (j << 1) - 1, k) + cc_ref(ic, (j << 1) - 2, k);
+          }
+        }
+      }
+    } else {
+      for (j = 2; j <= ipph; ++j) {
+        jc = ipp2 - j;
+        for (i = 3; i <= ido; i += 2) {
+          ic = idp2 - i;
+          for (k = 1; k <= l1; ++k) {
+            ch_ref(i - 1, k, j) = cc_ref(i - 1, (j << 1) - 1, k) + cc_ref(ic - 1, (j << 1) - 2, k);
+            ch_ref(i - 1, k, jc) = cc_ref(i - 1, (j << 1) - 1, k) - cc_ref(ic - 1, (j << 1) - 2, k);
+            ch_ref(i, k, j) = cc_ref(i, (j << 1) - 1, k) - cc_ref(ic, (j << 1) - 2, k);
+            ch_ref(i, k, jc) = cc_ref(i, (j << 1) - 1, k) + cc_ref(ic, (j << 1) - 2, k);
+          }
+        }
+      }
+    }
+  }
+  ar1 = 1.f;
+  ai1 = 0.f;
+  for (l = 2; l <= ipph; ++l) {
+    lc = ipp2 - l;
+    ar1h = dcp * ar1 - dsp * ai1;
+    ai1 = dcp * ai1 + dsp * ar1;
+    ar1 = ar1h;
+    for (ik = 1; ik <= idl1; ++ik) {
+      c2_ref(ik, l) = ch2_ref(ik, 1) + ar1 * ch2_ref(ik, 2);
+      c2_ref(ik, lc) = ai1 * ch2_ref(ik, ip);
+    }
+    dc2 = ar1;
+    ds2 = ai1;
+    ar2 = ar1;
+    ai2 = ai1;
+    for (j = 3; j <= ipph; ++j) {
+      jc = ipp2 - j;
+      ar2h = dc2 * ar2 - ds2 * ai2;
+      ai2 = dc2 * ai2 + ds2 * ar2;
+      ar2 = ar2h;
+      for (ik = 1; ik <= idl1; ++ik) {
+        c2_ref(ik, l) = c2_ref(ik, l) + ar2 * ch2_ref(ik, j);
+        c2_ref(ik, lc) = c2_ref(ik, lc) + ai2 * ch2_ref(ik, jc);
+      }
+    }
+  }
+  for (j = 2; j <= ipph; ++j) {
+    for (ik = 1; ik <= idl1; ++ik) {
+      ch2_ref(ik, 1) = ch2_ref(ik, 1) + ch2_ref(ik, j);
+    }
+  }
+  for (j = 2; j <= ipph; ++j) {
+    jc = ipp2 - j;
+    for (k = 1; k <= l1; ++k) {
+      ch_ref(1, k, j) = c1_ref(1, k, j) - c1_ref(1, k, jc);
+      ch_ref(1, k, jc) = c1_ref(1, k, j) + c1_ref(1, k, jc);
+    }
+  }
+  if (ido != 1) {
+    if (nbd >= l1) {
+      for (j = 2; j <= ipph; ++j) {
+        jc = ipp2 - j;
+        for (k = 1; k <= l1; ++k) {
+          for (i = 3; i <= ido; i += 2) {
+            ch_ref(i - 1, k, j) = c1_ref(i - 1, k, j) - c1_ref(i, k, jc);
+            ch_ref(i - 1, k, jc) = c1_ref(i - 1, k, j) + c1_ref(i, k, jc);
+            ch_ref(i, k, j) = c1_ref(i, k, j) + c1_ref(i - 1, k, jc);
+            ch_ref(i, k, jc) = c1_ref(i, k, j) - c1_ref(i - 1, k, jc);
+          }
+        }
+      }
+    } else {
+      for (j = 2; j <= ipph; ++j) {
+        jc = ipp2 - j;
+        for (i = 3; i <= ido; i += 2) {
+          for (k = 1; k <= l1; ++k) {
+            ch_ref(i - 1, k, j) = c1_ref(i - 1, k, j) - c1_ref(i, k, jc);
+            ch_ref(i - 1, k, jc) = c1_ref(i - 1, k, j) + c1_ref(i, k, jc);
+            ch_ref(i, k, j) = c1_ref(i, k, j) + c1_ref(i - 1, k, jc);
+            ch_ref(i, k, jc) = c1_ref(i, k, j) - c1_ref(i - 1, k, jc);
+          }
+        }
+      }
+    }
+  }
+  if (ido == 1) {
+    return;
+  }
+  for (ik = 1; ik <= idl1; ++ik) {
+    c2_ref(ik, 1) = ch2_ref(ik, 1);
+  }
+  for (j = 2; j <= ip; ++j) {
+    for (k = 1; k <= l1; ++k) {
+      c1_ref(1, k, j) = ch_ref(1, k, j);
+    }
+  }
+  if (nbd <= l1) {
+    is = -(ido);
+    for (j = 2; j <= ip; ++j) {
+      is += ido;
+      idij = is;
+      for (i = 3; i <= ido; i += 2) {
+        idij += 2;
+        for (k = 1; k <= l1; ++k) {
+          c1_ref(i - 1, k, j) = wa[idij - 1] * ch_ref(i - 1, k, j) 
+            - wa[idij] * ch_ref(i, k, j);
+          c1_ref(i, k, j) = wa[idij - 1] * ch_ref(i, k, j) + wa[idij] * ch_ref(i - 1, k, j);
+        }
+      }
+    }
+  } else {
+    is = -(ido);
+    for (j = 2; j <= ip; ++j) {
+      is += ido;
+      for (k = 1; k <= l1; ++k) {
+        idij = is;
+        for (i = 3; i <= ido; i += 2) {
+          idij += 2;
+          c1_ref(i - 1, k, j) = wa[idij - 1] * ch_ref(i - 1, k, j) 
+            - wa[idij] * ch_ref(i, k, j);
+          c1_ref(i, k, j) = wa[idij - 1] * ch_ref(i, k, j) + wa[idij] * ch_ref(i - 1, k, j);
+        }
+      }
+    }
+  }
+} /* radbg */
+
+#undef ch2_ref
+#undef ch_ref
+#undef cc_ref
+#undef c2_ref
+#undef c1_ref
+
+
+static void radf2(integer ido, integer l1, const real *cc, real *ch, 
+                  const real *wa1)
+{
+  /* System generated locals */
+  integer ch_offset, cc_offset;
+
+  /* Local variables */
+  integer i, k, ic;
+  real ti2, tr2;
+  integer idp2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*l1 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*2 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * 3;
+  ch -= ch_offset;
+  cc_offset = 1 + ido * (1 + l1);
+  cc -= cc_offset;
+  --wa1;
+
+  /* Function Body */
+  for (k = 1; k <= l1; ++k) {
+    ch_ref(1, 1, k) = cc_ref(1, k, 1) + cc_ref(1, k, 2);
+    ch_ref(ido, 2, k) = cc_ref(1, k, 1) - cc_ref(1, k, 2);
+  }
+  if (ido < 2) return;
+  if (ido != 2) {
+    idp2 = ido + 2;
+    for (k = 1; k <= l1; ++k) {
+      for (i = 3; i <= ido; i += 2) {
+        ic = idp2 - i;
+        tr2 = wa1[i - 2] * cc_ref(i - 1, k, 2) + wa1[i - 1] * 
+          cc_ref(i, k, 2);
+        ti2 = wa1[i - 2] * cc_ref(i, k, 2) - wa1[i - 1] * cc_ref(
+                                                                 i - 1, k, 2);
+        ch_ref(i, 1, k) = cc_ref(i, k, 1) + ti2;
+        ch_ref(ic, 2, k) = ti2 - cc_ref(i, k, 1);
+        ch_ref(i - 1, 1, k) = cc_ref(i - 1, k, 1) + tr2;
+        ch_ref(ic - 1, 2, k) = cc_ref(i - 1, k, 1) - tr2;
+      }
+    }
+    if (ido % 2 == 1) {
+      return;
+    }
+  }
+  for (k = 1; k <= l1; ++k) {
+    ch_ref(1, 2, k) = -cc_ref(ido, k, 2);
+    ch_ref(ido, 1, k) = cc_ref(ido, k, 1);
+  }
+} /* radf2 */
+
+#undef ch_ref
+#undef cc_ref
+
+
+static void radf3(integer ido, integer l1, const real *cc, real *ch, 
+                  const real *wa1, const real *wa2)
+{
+  static const real taur = -.5f;
+  static const real taui = .866025403784439f;
+
+  /* System generated locals */
+  integer ch_offset, cc_offset;
+
+  /* Local variables */
+  integer i, k, ic;
+  real ci2, di2, di3, cr2, dr2, dr3, ti2, ti3, tr2, tr3;
+  integer idp2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*l1 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*3 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + (ido << 2);
+  ch -= ch_offset;
+  cc_offset = 1 + ido * (1 + l1);
+  cc -= cc_offset;
+  --wa1;
+  --wa2;
+
+  /* Function Body */
+  for (k = 1; k <= l1; ++k) {
+    cr2 = cc_ref(1, k, 2) + cc_ref(1, k, 3);
+    ch_ref(1, 1, k) = cc_ref(1, k, 1) + cr2;
+    ch_ref(1, 3, k) = taui * (cc_ref(1, k, 3) - cc_ref(1, k, 2));
+    ch_ref(ido, 2, k) = cc_ref(1, k, 1) + taur * cr2;
+  }
+  if (ido == 1) {
+    return;
+  }
+  idp2 = ido + 2;
+  for (k = 1; k <= l1; ++k) {
+    for (i = 3; i <= ido; i += 2) {
+      ic = idp2 - i;
+      dr2 = wa1[i - 2] * cc_ref(i - 1, k, 2) + wa1[i - 1] * 
+        cc_ref(i, k, 2);
+      di2 = wa1[i - 2] * cc_ref(i, k, 2) - wa1[i - 1] * cc_ref(
+                                                               i - 1, k, 2);
+      dr3 = wa2[i - 2] * cc_ref(i - 1, k, 3) + wa2[i - 1] * 
+        cc_ref(i, k, 3);
+      di3 = wa2[i - 2] * cc_ref(i, k, 3) - wa2[i - 1] * cc_ref(
+                                                               i - 1, k, 3);
+      cr2 = dr2 + dr3;
+      ci2 = di2 + di3;
+      ch_ref(i - 1, 1, k) = cc_ref(i - 1, k, 1) + cr2;
+      ch_ref(i, 1, k) = cc_ref(i, k, 1) + ci2;
+      tr2 = cc_ref(i - 1, k, 1) + taur * cr2;
+      ti2 = cc_ref(i, k, 1) + taur * ci2;
+      tr3 = taui * (di2 - di3);
+      ti3 = taui * (dr3 - dr2);
+      ch_ref(i - 1, 3, k) = tr2 + tr3;
+      ch_ref(ic - 1, 2, k) = tr2 - tr3;
+      ch_ref(i, 3, k) = ti2 + ti3;
+      ch_ref(ic, 2, k) = ti3 - ti2;
+    }
+  }
+} /* radf3 */
+
+#undef ch_ref
+#undef cc_ref
+
+
+static void radf4(integer ido, integer l1, const real *cc, real *ch, 
+                  const real *wa1, const real *wa2, const real *wa3)
+{
+  /* Initialized data */
+
+  static const real hsqt2 = .7071067811865475f;
+
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k, ic;
+  real ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;
+  integer idp2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*l1 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*4 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * 5;
+  ch -= ch_offset;
+  cc_offset = 1 + ido * (1 + l1);
+  cc -= cc_offset;
+  --wa1;
+  --wa2;
+  --wa3;
+
+  /* Function Body */
+  for (k = 1; k <= l1; ++k) {
+    tr1 = cc_ref(1, k, 2) + cc_ref(1, k, 4);
+    tr2 = cc_ref(1, k, 1) + cc_ref(1, k, 3);
+    ch_ref(1, 1, k) = tr1 + tr2;
+    ch_ref(ido, 4, k) = tr2 - tr1;
+    ch_ref(ido, 2, k) = cc_ref(1, k, 1) - cc_ref(1, k, 3);
+    ch_ref(1, 3, k) = cc_ref(1, k, 4) - cc_ref(1, k, 2);
+  }  
+  if (ido < 2) return;
+  if (ido != 2) {
+    idp2 = ido + 2;
+    for (k = 1; k <= l1; ++k) {
+      for (i = 3; i <= ido; i += 2) {
+        ic = idp2 - i;
+        cr2 = wa1[i - 2] * cc_ref(i - 1, k, 2) + wa1[i - 1] * 
+          cc_ref(i, k, 2);
+        ci2 = wa1[i - 2] * cc_ref(i, k, 2) - wa1[i - 1] * cc_ref(
+                                                                 i - 1, k, 2);
+        cr3 = wa2[i - 2] * cc_ref(i - 1, k, 3) + wa2[i - 1] * 
+          cc_ref(i, k, 3);
+        ci3 = wa2[i - 2] * cc_ref(i, k, 3) - wa2[i - 1] * cc_ref(
+                                                                 i - 1, k, 3);
+        cr4 = wa3[i - 2] * cc_ref(i - 1, k, 4) + wa3[i - 1] * 
+          cc_ref(i, k, 4);
+        ci4 = wa3[i - 2] * cc_ref(i, k, 4) - wa3[i - 1] * cc_ref(
+                                                                 i - 1, k, 4);
+        tr1 = cr2 + cr4;
+        tr4 = cr4 - cr2;
+        ti1 = ci2 + ci4;
+        ti4 = ci2 - ci4;
+        ti2 = cc_ref(i, k, 1) + ci3;
+        ti3 = cc_ref(i, k, 1) - ci3;
+        tr2 = cc_ref(i - 1, k, 1) + cr3;
+        tr3 = cc_ref(i - 1, k, 1) - cr3;
+        ch_ref(i - 1, 1, k) = tr1 + tr2;
+        ch_ref(ic - 1, 4, k) = tr2 - tr1;
+        ch_ref(i, 1, k) = ti1 + ti2;
+        ch_ref(ic, 4, k) = ti1 - ti2;
+        ch_ref(i - 1, 3, k) = ti4 + tr3;
+        ch_ref(ic - 1, 2, k) = tr3 - ti4;
+        ch_ref(i, 3, k) = tr4 + ti3;
+        ch_ref(ic, 2, k) = tr4 - ti3;
+      }
+    }
+    if (ido % 2 == 1) {
+      return;
+    }
+  }
+  for (k = 1; k <= l1; ++k) {
+    ti1 = -hsqt2 * (cc_ref(ido, k, 2) + cc_ref(ido, k, 4));
+    tr1 = hsqt2 * (cc_ref(ido, k, 2) - cc_ref(ido, k, 4));
+    ch_ref(ido, 1, k) = tr1 + cc_ref(ido, k, 1);
+    ch_ref(ido, 3, k) = cc_ref(ido, k, 1) - tr1;
+    ch_ref(1, 2, k) = ti1 - cc_ref(ido, k, 3);
+    ch_ref(1, 4, k) = ti1 + cc_ref(ido, k, 3);
+  }
+} /* radf4 */
+
+#undef ch_ref
+#undef cc_ref
+
+
+static void radf5(integer ido, integer l1, const real *cc, real *ch, 
+                  const real *wa1, const real *wa2, const real *wa3, const real *wa4)
+{
+  /* Initialized data */
+
+  static const real tr11 = .309016994374947f;
+  static const real ti11 = .951056516295154f;
+  static const real tr12 = -.809016994374947f;
+  static const real ti12 = .587785252292473f;
+
+  /* System generated locals */
+  integer cc_offset, ch_offset;
+
+  /* Local variables */
+  integer i, k, ic;
+  real ci2, di2, ci4, ci5, di3, di4, di5, ci3, cr2, cr3, dr2, dr3, dr4, dr5,
+    cr5, cr4, ti2, ti3, ti5, ti4, tr2, tr3, tr4, tr5;
+  integer idp2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*l1 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*5 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * 6;
+  ch -= ch_offset;
+  cc_offset = 1 + ido * (1 + l1);
+  cc -= cc_offset;
+  --wa1;
+  --wa2;
+  --wa3;
+  --wa4;
+
+  /* Function Body */
+  for (k = 1; k <= l1; ++k) {
+    cr2 = cc_ref(1, k, 5) + cc_ref(1, k, 2);
+    ci5 = cc_ref(1, k, 5) - cc_ref(1, k, 2);
+    cr3 = cc_ref(1, k, 4) + cc_ref(1, k, 3);
+    ci4 = cc_ref(1, k, 4) - cc_ref(1, k, 3);
+    ch_ref(1, 1, k) = cc_ref(1, k, 1) + cr2 + cr3;
+    ch_ref(ido, 2, k) = cc_ref(1, k, 1) + tr11 * cr2 + tr12 * cr3;
+    ch_ref(1, 3, k) = ti11 * ci5 + ti12 * ci4;
+    ch_ref(ido, 4, k) = cc_ref(1, k, 1) + tr12 * cr2 + tr11 * cr3;
+    ch_ref(1, 5, k) = ti12 * ci5 - ti11 * ci4;
+  }
+  if (ido == 1) {
+    return;
+  }
+  idp2 = ido + 2;
+  for (k = 1; k <= l1; ++k) {
+    for (i = 3; i <= ido; i += 2) {
+      ic = idp2 - i;
+      dr2 = wa1[i - 2] * cc_ref(i - 1, k, 2) + wa1[i - 1] * cc_ref(i, k, 2);
+      di2 = wa1[i - 2] * cc_ref(i, k, 2) - wa1[i - 1] * cc_ref(i - 1, k, 2);
+      dr3 = wa2[i - 2] * cc_ref(i - 1, k, 3) + wa2[i - 1] * cc_ref(i, k, 3);
+      di3 = wa2[i - 2] * cc_ref(i, k, 3) - wa2[i - 1] * cc_ref(i - 1, k, 3);
+      dr4 = wa3[i - 2] * cc_ref(i - 1, k, 4) + wa3[i - 1] * cc_ref(i, k, 4);
+      di4 = wa3[i - 2] * cc_ref(i, k, 4) - wa3[i - 1] * cc_ref(i - 1, k, 4);
+      dr5 = wa4[i - 2] * cc_ref(i - 1, k, 5) + wa4[i - 1] * cc_ref(i, k, 5);
+      di5 = wa4[i - 2] * cc_ref(i, k, 5) - wa4[i - 1] * cc_ref(i - 1, k, 5);
+      cr2 = dr2 + dr5;
+      ci5 = dr5 - dr2;
+      cr5 = di2 - di5;
+      ci2 = di2 + di5;
+      cr3 = dr3 + dr4;
+      ci4 = dr4 - dr3;
+      cr4 = di3 - di4;
+      ci3 = di3 + di4;
+      ch_ref(i - 1, 1, k) = cc_ref(i - 1, k, 1) + cr2 + cr3;
+      ch_ref(i, 1, k) = cc_ref(i, k, 1) + ci2 + ci3;
+      tr2 = cc_ref(i - 1, k, 1) + tr11 * cr2 + tr12 * cr3;
+      ti2 = cc_ref(i, k, 1) + tr11 * ci2 + tr12 * ci3;
+      tr3 = cc_ref(i - 1, k, 1) + tr12 * cr2 + tr11 * cr3;
+      ti3 = cc_ref(i, k, 1) + tr12 * ci2 + tr11 * ci3;
+      tr5 = ti11 * cr5 + ti12 * cr4;
+      ti5 = ti11 * ci5 + ti12 * ci4;
+      tr4 = ti12 * cr5 - ti11 * cr4;
+      ti4 = ti12 * ci5 - ti11 * ci4;
+      ch_ref(i - 1, 3, k) = tr2 + tr5;
+      ch_ref(ic - 1, 2, k) = tr2 - tr5;
+      ch_ref(i, 3, k) = ti2 + ti5;
+      ch_ref(ic, 2, k) = ti5 - ti2;
+      ch_ref(i - 1, 5, k) = tr3 + tr4;
+      ch_ref(ic - 1, 4, k) = tr3 - tr4;
+      ch_ref(i, 5, k) = ti3 + ti4;
+      ch_ref(ic, 4, k) = ti4 - ti3;
+    }
+  }
+} /* radf5 */
+
+#undef ch_ref
+#undef cc_ref
+
+
+static void radfg(integer ido, integer ip, integer l1, integer idl1, 
+                  real *cc, real *c1, real *c2, real *ch, real *ch2, const real *wa)
+{
+  /* System generated locals */
+  integer ch_offset, cc_offset,
+    c1_offset, c2_offset, ch2_offset;
+
+  /* Local variables */
+  integer i, j, k, l, j2, ic, jc, lc, ik, is;
+  real dc2, ai1, ai2, ar1, ar2, ds2;
+  integer nbd;
+  real dcp, arg, dsp, ar1h, ar2h;
+  integer idp2, ipp2, idij, ipph;
+
+
+#define c1_ref(a_1,a_2,a_3) c1[((a_3)*l1 + (a_2))*ido + a_1]
+#define c2_ref(a_1,a_2) c2[(a_2)*idl1 + a_1]
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*ip + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+#define ch2_ref(a_1,a_2) ch2[(a_2)*idl1 + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  c1_offset = 1 + ido * (1 + l1);
+  c1 -= c1_offset;
+  cc_offset = 1 + ido * (1 + ip);
+  cc -= cc_offset;
+  ch2_offset = 1 + idl1;
+  ch2 -= ch2_offset;
+  c2_offset = 1 + idl1;
+  c2 -= c2_offset;
+  --wa;
+
+  /* Function Body */
+  arg = (2*M_PI) / (real) (ip);
+  dcp = cos(arg);
+  dsp = sin(arg);
+  ipph = (ip + 1) / 2;
+  ipp2 = ip + 2;
+  idp2 = ido + 2;
+  nbd = (ido - 1) / 2;
+  if (ido == 1) {
+    for (ik = 1; ik <= idl1; ++ik) {
+      c2_ref(ik, 1) = ch2_ref(ik, 1);
+    }
+  } else {
+    for (ik = 1; ik <= idl1; ++ik) {
+      ch2_ref(ik, 1) = c2_ref(ik, 1);
+    }
+    for (j = 2; j <= ip; ++j) {
+      for (k = 1; k <= l1; ++k) {
+        ch_ref(1, k, j) = c1_ref(1, k, j);
+      }
+    }
+    if (nbd <= l1) {
+      is = -(ido);
+      for (j = 2; j <= ip; ++j) {
+        is += ido;
+        idij = is;
+        for (i = 3; i <= ido; i += 2) {
+          idij += 2;
+          for (k = 1; k <= l1; ++k) {
+            ch_ref(i - 1, k, j) = wa[idij - 1] * c1_ref(i - 1, k, j)
+              + wa[idij] * c1_ref(i, k, j);
+            ch_ref(i, k, j) = wa[idij - 1] * c1_ref(i, k, j) - wa[
+                                                                  idij] * c1_ref(i - 1, k, j);
+          }
+        }
+      }
+    } else {
+      is = -(ido);
+      for (j = 2; j <= ip; ++j) {
+        is += ido;
+        for (k = 1; k <= l1; ++k) {
+          idij = is;
+          for (i = 3; i <= ido; i += 2) {
+            idij += 2;
+            ch_ref(i - 1, k, j) = wa[idij - 1] * c1_ref(i - 1, k, j) 
+              + wa[idij] * c1_ref(i, k, j);
+            ch_ref(i, k, j) = wa[idij - 1] * c1_ref(i, k, j) - wa[
+                                                                  idij] * c1_ref(i - 1, k, j);
+          }
+        }
+      }
+    }
+    if (nbd >= l1) {
+      for (j = 2; j <= ipph; ++j) {
+        jc = ipp2 - j;
+        for (k = 1; k <= l1; ++k) {
+          for (i = 3; i <= ido; i += 2) {
+            c1_ref(i - 1, k, j) = ch_ref(i - 1, k, j) + ch_ref(i - 
+                                                               1, k, jc);
+            c1_ref(i - 1, k, jc) = ch_ref(i, k, j) - ch_ref(i, k, 
+                                                            jc);
+            c1_ref(i, k, j) = ch_ref(i, k, j) + ch_ref(i, k, jc);
+            c1_ref(i, k, jc) = ch_ref(i - 1, k, jc) - ch_ref(i - 1, 
+                                                             k, j);
+          }
+        }
+      }
+    } else {
+      for (j = 2; j <= ipph; ++j) {
+        jc = ipp2 - j;
+        for (i = 3; i <= ido; i += 2) {
+          for (k = 1; k <= l1; ++k) {
+            c1_ref(i - 1, k, j) = ch_ref(i - 1, k, j) + ch_ref(i - 
+                                                               1, k, jc);
+            c1_ref(i - 1, k, jc) = ch_ref(i, k, j) - ch_ref(i, k, 
+                                                            jc);
+            c1_ref(i, k, j) = ch_ref(i, k, j) + ch_ref(i, k, jc);
+            c1_ref(i, k, jc) = ch_ref(i - 1, k, jc) - ch_ref(i - 1, 
+                                                             k, j);
+          }
+        }
+      }
+    }
+  }
+  for (j = 2; j <= ipph; ++j) {
+    jc = ipp2 - j;
+    for (k = 1; k <= l1; ++k) {
+      c1_ref(1, k, j) = ch_ref(1, k, j) + ch_ref(1, k, jc);
+      c1_ref(1, k, jc) = ch_ref(1, k, jc) - ch_ref(1, k, j);
+    }
+  }
+
+  ar1 = 1.f;
+  ai1 = 0.f;
+  for (l = 2; l <= ipph; ++l) {
+    lc = ipp2 - l;
+    ar1h = dcp * ar1 - dsp * ai1;
+    ai1 = dcp * ai1 + dsp * ar1;
+    ar1 = ar1h;
+    for (ik = 1; ik <= idl1; ++ik) {
+      ch2_ref(ik, l) = c2_ref(ik, 1) + ar1 * c2_ref(ik, 2);
+      ch2_ref(ik, lc) = ai1 * c2_ref(ik, ip);
+    }
+    dc2 = ar1;
+    ds2 = ai1;
+    ar2 = ar1;
+    ai2 = ai1;
+    for (j = 3; j <= ipph; ++j) {
+      jc = ipp2 - j;
+      ar2h = dc2 * ar2 - ds2 * ai2;
+      ai2 = dc2 * ai2 + ds2 * ar2;
+      ar2 = ar2h;
+      for (ik = 1; ik <= idl1; ++ik) {
+        ch2_ref(ik, l) = ch2_ref(ik, l) + ar2 * c2_ref(ik, j);
+        ch2_ref(ik, lc) = ch2_ref(ik, lc) + ai2 * c2_ref(ik, jc);
+      }
+    }
+  }
+  for (j = 2; j <= ipph; ++j) {
+    for (ik = 1; ik <= idl1; ++ik) {
+      ch2_ref(ik, 1) = ch2_ref(ik, 1) + c2_ref(ik, j);
+    }
+  }
+
+  if (ido >= l1) {
+    for (k = 1; k <= l1; ++k) {
+      for (i = 1; i <= ido; ++i) {
+        cc_ref(i, 1, k) = ch_ref(i, k, 1);
+      }
+    }
+  } else {
+    for (i = 1; i <= ido; ++i) {
+      for (k = 1; k <= l1; ++k) {
+        cc_ref(i, 1, k) = ch_ref(i, k, 1);
+      }
+    }
+  }
+  for (j = 2; j <= ipph; ++j) {
+    jc = ipp2 - j;
+    j2 = j + j;
+    for (k = 1; k <= l1; ++k) {
+      cc_ref(ido, j2 - 2, k) = ch_ref(1, k, j);
+      cc_ref(1, j2 - 1, k) = ch_ref(1, k, jc);
+    }
+  }
+  if (ido == 1) {
+    return;
+  }
+  if (nbd >= l1) {
+    for (j = 2; j <= ipph; ++j) {
+      jc = ipp2 - j;
+      j2 = j + j;
+      for (k = 1; k <= l1; ++k) {
+        for (i = 3; i <= ido; i += 2) {
+          ic = idp2 - i;
+          cc_ref(i - 1, j2 - 1, k) = ch_ref(i - 1, k, j) + ch_ref(
+                                                                  i - 1, k, jc);
+          cc_ref(ic - 1, j2 - 2, k) = ch_ref(i - 1, k, j) - ch_ref(
+                                                                   i - 1, k, jc);
+          cc_ref(i, j2 - 1, k) = ch_ref(i, k, j) + ch_ref(i, k, 
+                                                          jc);
+          cc_ref(ic, j2 - 2, k) = ch_ref(i, k, jc) - ch_ref(i, k, j)
+            ;
+        }
+      }
+    }
+  } else {
+    for (j = 2; j <= ipph; ++j) {
+      jc = ipp2 - j;
+      j2 = j + j;
+      for (i = 3; i <= ido; i += 2) {
+        ic = idp2 - i;
+        for (k = 1; k <= l1; ++k) {
+          cc_ref(i - 1, j2 - 1, k) = ch_ref(i - 1, k, j) + ch_ref(
+                                                                  i - 1, k, jc);
+          cc_ref(ic - 1, j2 - 2, k) = ch_ref(i - 1, k, j) - ch_ref(
+                                                                   i - 1, k, jc);
+          cc_ref(i, j2 - 1, k) = ch_ref(i, k, j) + ch_ref(i, k, 
+                                                          jc);
+          cc_ref(ic, j2 - 2, k) = ch_ref(i, k, jc) - ch_ref(i, k, j)
+            ;
+        }
+      }
+    }
+  }
+} /* radfg */
+
+#undef ch2_ref
+#undef ch_ref
+#undef cc_ref
+#undef c2_ref
+#undef c1_ref
+
+
+static void cfftb1(integer n, real *c, real *ch, const real *wa, integer *ifac)
+{
+  integer i, k1, l1, l2, na, nf, ip, iw, ix2, ix3, ix4, nac, ido, 
+    idl1, idot;
+
+  /* Function Body */
+  nf = ifac[1];
+  na = 0;
+  l1 = 1;
+  iw = 0;
+  for (k1 = 1; k1 <= nf; ++k1) {
+    ip = ifac[k1 + 1];
+    l2 = ip * l1;
+    ido = n / l2;
+    idot = ido + ido;
+    idl1 = idot * l1;
+    switch (ip) {
+      case 4:
+        ix2 = iw + idot;
+        ix3 = ix2 + idot;
+        passb4(idot, l1, na?ch:c, na?c:ch, &wa[iw], &wa[ix2], &wa[ix3]);
+        na = 1 - na;
+        break;
+      case 2:
+        passb2(idot, l1, na?ch:c, na?c:ch, &wa[iw]);
+        na = 1 - na;
+        break;
+      case 3:
+        ix2 = iw + idot;
+        passb3(idot, l1, na?ch:c, na?c:ch, &wa[iw], &wa[ix2]);
+        na = 1 - na;
+        break;
+      case 5:
+        ix2 = iw + idot;
+        ix3 = ix2 + idot;
+        ix4 = ix3 + idot;
+        passfb5(idot, l1, na?ch:c, na?c:ch, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4], +1);
+        na = 1 - na;
+        break;
+      default:
+        if (na == 0) {
+          passfb(&nac, idot, ip, l1, idl1, c, c, c, ch, ch, &wa[iw], +1);
+        } else {
+          passfb(&nac, idot, ip, l1, idl1, ch, ch, ch, c, c, &wa[iw], +1);
+        }
+        if (nac != 0) {
+          na = 1 - na;
+        }
+        break;
+    }
+    l1 = l2;
+    iw += (ip - 1) * idot;
+  }
+  if (na == 0) {
+    return;
+  }
+  for (i = 0; i < 2*n; ++i) {
+    c[i] = ch[i];
+  }
+} /* cfftb1 */
+
+void cfftb(integer n, real *c, real *wsave)
+{
+  integer iw1, iw2;
+
+  /* Parameter adjustments */
+  --wsave;
+  --c;
+
+  /* Function Body */
+  if (n == 1) {
+    return;
+  }
+  iw1 = 2*n + 1;
+  iw2 = iw1 + 2*n;
+  cfftb1(n, &c[1], &wsave[1], &wsave[iw1], (int*)&wsave[iw2]);
+} /* cfftb */
+
+static void cfftf1(integer n, real *c, real *ch, const real *wa, integer *ifac)
+{
+  /* Local variables */
+  integer i, k1, l1, l2, na, nf, ip, iw, ix2, ix3, ix4, nac, ido, 
+    idl1, idot;
+
+  /* Function Body */
+  nf = ifac[1];
+  na = 0;
+  l1 = 1;
+  iw = 0;
+  for (k1 = 1; k1 <= nf; ++k1) {
+    ip = ifac[k1 + 1];
+    l2 = ip * l1;
+    ido = n / l2;
+    idot = ido + ido;
+    idl1 = idot * l1;
+    switch (ip) {
+      case 4:
+        ix2 = iw + idot;
+        ix3 = ix2 + idot;
+        passf4(idot, l1, na?ch:c, na?c:ch, &wa[iw], &wa[ix2], &wa[ix3]);
+        na = 1 - na;
+        break;
+      case 2:
+        passf2(idot, l1, na?ch:c, na?c:ch, &wa[iw]);
+        na = 1 - na;
+        break;
+      case 3:
+        ix2 = iw + idot;
+        passf3(idot, l1, na?ch:c, na?c:ch, &wa[iw], &wa[ix2]);
+        na = 1 - na;
+        break;
+      case 5:
+        ix2 = iw + idot;
+        ix3 = ix2 + idot;
+        ix4 = ix3 + idot;
+        passfb5(idot, l1, na?ch:c, na?c:ch, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4], -1);
+        na = 1 - na;
+        break;
+      default:
+        if (na == 0) {
+          passfb(&nac, idot, ip, l1, idl1, c, c, c, ch, ch, &wa[iw], -1);
+        } else {
+          passfb(&nac, idot, ip, l1, idl1, ch, ch, ch, c, c, &wa[iw], -1);
+        }
+        if (nac != 0) {
+          na = 1 - na;
+        }
+        break;
+    }
+    l1 = l2;
+    iw += (ip - 1)*idot;
+  }
+  if (na == 0) {
+    return;
+  }
+  for (i = 0; i < 2*n; ++i) {
+    c[i] = ch[i];
+  }
+} /* cfftf1 */
+
+void cfftf(integer n, real *c, real *wsave)
+{
+  integer iw1, iw2;
+
+  /* Parameter adjustments */
+  --wsave;
+  --c;
+
+  /* Function Body */
+  if (n == 1) {
+    return;
+  }
+  iw1 = 2*n + 1;
+  iw2 = iw1 + 2*n;
+  cfftf1(n, &c[1], &wsave[1], &wsave[iw1], (int*)&wsave[iw2]);
+} /* cfftf */
+
+static int decompose(integer n, integer *ifac, integer ntryh[4]) {  
+  integer ntry=0, nl = n, nf = 0, nq, nr, i, j = 0;
+  do {
+    if (j < 4) {
+      ntry = ntryh[j];
+    } else {
+      ntry += 2;
+    }
+    ++j;
+  L104:
+    nq = nl / ntry;
+    nr = nl - ntry * nq;
+    if (nr != 0) continue;
+    ++nf;
+    ifac[nf + 2] = ntry;
+    nl = nq;
+    if (ntry == 2 && nf != 1) {
+      for (i = 2; i <= nf; ++i) {
+        integer ib = nf - i + 2;
+        ifac[ib + 2] = ifac[ib + 1];
+      }
+      ifac[3] = 2;
+    }
+    if (nl != 1) {
+      goto L104;
+    }
+  } while (nl != 1);
+  ifac[1] = n;
+  ifac[2] = nf;  
+  return nf;
+}
+
+static void cffti1(integer n, real *wa, integer *ifac)
+{
+  static integer ntryh[4] = { 3,4,2,5 };
+
+  /* Local variables */
+  integer i, j, i1, k1, l1, l2;
+  real fi;
+  integer ld, ii, nf, ip;
+  real arg;
+  integer ido, ipm;
+  real argh;
+  integer idot;
+  real argld;
+
+  /* Parameter adjustments */
+  --ifac;
+  --wa;
+
+  nf = decompose(n, ifac, ntryh);
+
+  argh = (2*M_PI) / (real) (n);
+  i = 2;
+  l1 = 1;
+  for (k1 = 1; k1 <= nf; ++k1) {
+    ip = ifac[k1 + 2];
+    ld = 0;
+    l2 = l1 * ip;
+    ido = n / l2;
+    idot = ido + ido + 2;
+    ipm = ip - 1;
+    for (j = 1; j <= ipm; ++j) {
+      i1 = i;
+      wa[i - 1] = 1.f;
+      wa[i] = 0.f;
+      ld += l1;
+      fi = 0.f;
+      argld = (real) ld * argh;
+      for (ii = 4; ii <= idot; ii += 2) {
+        i += 2;
+        fi += 1.f;
+        arg = fi * argld;
+        wa[i - 1] = cos(arg);
+        wa[i] = sin(arg);
+      }
+      if (ip > 5) {
+        wa[i1 - 1] = wa[i - 1];
+        wa[i1] = wa[i];
+      };
+    }
+    l1 = l2;
+  }
+} /* cffti1 */
+
+void cffti(integer n, real *wsave)
+{
+  integer iw1, iw2;
+  /* Parameter adjustments */
+  --wsave;
+
+  /* Function Body */
+  if (n == 1) {
+    return;
+  }
+  iw1 = 2*n + 1;
+  iw2 = iw1 + 2*n;
+  cffti1(n, &wsave[iw1], (int*)&wsave[iw2]);
+  return;
+} /* cffti */
+
+static void rfftb1(integer n, real *c, real *ch, const real *wa, integer *ifac)
+{
+  /* Local variables */
+  integer i, k1, l1, l2, na, nf, ip, iw, ix2, ix3, ix4, ido, idl1;
+
+  /* Function Body */
+  nf = ifac[1];
+  na = 0;
+  l1 = 1;
+  iw = 0;
+  for (k1 = 1; k1 <= nf; ++k1) {
+    ip = ifac[k1 + 1];
+    l2 = ip * l1;
+    ido = n / l2;
+    idl1 = ido * l1;
+    switch (ip) {
+      case 4:
+        ix2 = iw + ido;
+        ix3 = ix2 + ido;
+        radb4(ido, l1, na?ch:c, na?c:ch, &wa[iw], &wa[ix2], &wa[ix3]);
+        na = 1 - na;
+        break;
+      case 2:
+        radb2(ido, l1, na?ch:c, na?c:ch, &wa[iw]);
+        na = 1 - na;
+        break;
+      case 3:
+        ix2 = iw + ido;
+        radb3(ido, l1, na?ch:c, na?c:ch, &wa[iw], &wa[ix2]);
+        na = 1 - na;
+        break;
+      case 5:
+        ix2 = iw + ido;
+        ix3 = ix2 + ido;
+        ix4 = ix3 + ido;
+        radb5(ido, l1, na?ch:c, na?c:ch, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4]);
+        na = 1 - na;
+        break;
+      default:
+        if (na == 0) {
+          radbg(ido, ip, l1, idl1, c, c, c, ch, ch, &wa[iw]);
+        } else {
+          radbg(ido, ip, l1, idl1, ch, ch, ch, c, c, &wa[iw]);
+        }
+        if (ido == 1) {
+          na = 1 - na;
+        }
+        break;
+    }
+    l1 = l2;
+    iw += (ip - 1) * ido;
+  }
+  if (na == 0) {
+    return;
+  }
+  for (i = 0; i < n; ++i) {
+    c[i] = ch[i];
+  }
+} /* rfftb1 */
+
+static void rfftf1(integer n, real *c, real *ch, const real *wa, integer *ifac)
+{
+  /* Local variables */
+  integer i, k1, l1, l2, na, kh, nf, ip, iw, ix2, ix3, ix4, ido, idl1;
+
+  /* Function Body */
+  nf = ifac[1];
+  na = 1;
+  l2 = n;
+  iw = n-1;
+  for (k1 = 1; k1 <= nf; ++k1) {
+    kh = nf - k1;
+    ip = ifac[kh + 2];
+    l1 = l2 / ip;
+    ido = n / l2;
+    idl1 = ido * l1;
+    iw -= (ip - 1) * ido;
+    na = 1 - na;
+    switch (ip) {
+      case 4:
+        ix2 = iw + ido;
+        ix3 = ix2 + ido;
+        radf4(ido, l1, na ? ch : c, na ? c : ch, &wa[iw], &wa[ix2], &wa[ix3]);
+        break;
+      case 2:
+        radf2(ido, l1, na ? ch : c, na ? c : ch, &wa[iw]);
+        break;
+      case 3:        
+        ix2 = iw + ido;
+        radf3(ido, l1, na ? ch : c, na ? c : ch, &wa[iw], &wa[ix2]);
+        break;
+      case 5:
+        ix2 = iw + ido;
+        ix3 = ix2 + ido;
+        ix4 = ix3 + ido;
+        radf5(ido, l1, na ? ch : c, na ? c : ch, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4]);
+        break;
+      default:
+        if (ido == 1) {
+          na = 1 - na;
+        }
+        if (na == 0) {
+          radfg(ido, ip, l1, idl1, c, c, c, ch, ch, &wa[iw]);
+          na = 1;
+        } else {
+          radfg(ido, ip, l1, idl1, ch, ch, ch, c, c, &wa[iw]);
+          na = 0;
+        }
+        break;
+    }
+    l2 = l1;
+  }
+  if (na == 1) {
+    return;
+  }
+  for (i = 0; i < n; ++i) {
+    c[i] = ch[i];
+  }
+}
+
+void rfftb(integer n, real *r, real *wsave)
+{
+
+  /* Parameter adjustments */
+  --wsave;
+  --r;
+
+  /* Function Body */
+  if (n == 1) {
+    return;
+  }
+  rfftb1(n, &r[1], &wsave[1], &wsave[n + 1], (int*)&wsave[(n << 1) + 1]);
+} /* rfftb */
+
+static void rffti1(integer n, real *wa, integer *ifac)
+{
+  static integer ntryh[4] = { 4,2,3,5 };
+
+  /* Local variables */
+  integer i, j, k1, l1, l2;
+  real fi;
+  integer ld, ii, nf, ip, is;
+  real arg;
+  integer ido, ipm;
+  integer nfm1;
+  real argh;
+  real argld;
+
+  /* Parameter adjustments */
+  --ifac;
+  --wa;
+
+  nf = decompose(n, ifac, ntryh);
+
+  argh = (2*M_PI) / (real) (n);
+  is = 0;
+  nfm1 = nf - 1;
+  l1 = 1;
+  if (nfm1 == 0) {
+    return;
+  }
+  for (k1 = 1; k1 <= nfm1; ++k1) {
+    ip = ifac[k1 + 2];
+    ld = 0;
+    l2 = l1 * ip;
+    ido = n / l2;
+    ipm = ip - 1;
+    for (j = 1; j <= ipm; ++j) {
+      ld += l1;
+      i = is;
+      argld = (real) ld * argh;
+      fi = 0.f;
+      for (ii = 3; ii <= ido; ii += 2) {
+        i += 2;
+        fi += 1.f;
+        arg = fi * argld;
+        wa[i - 1] = cos(arg);
+        wa[i] = sin(arg);
+      }
+      is += ido;
+    }
+    l1 = l2;
+  }
+} /* rffti1 */
+
+void rfftf(integer n, real *r, real *wsave)
+{
+
+  /* Parameter adjustments */
+  --wsave;
+  --r;
+
+  /* Function Body */
+  if (n == 1) {
+    return;
+  }
+  rfftf1(n, &r[1], &wsave[1], &wsave[n + 1], (int*)&wsave[(n << 1) + 1]);
+} /* rfftf */
+
+void rffti(integer n, real *wsave)
+{
+  /* Parameter adjustments */
+  --wsave;
+
+  /* Function Body */
+  if (n == 1) {
+    return;
+  }
+  rffti1(n, &wsave[n + 1], (int*)&wsave[(n << 1) + 1]);
+  return;
+} /* rffti */
+
+static void cosqb1(integer n, real *x, real *w, real *xh)
+{
+  /* Local variables */
+  integer i, k, kc, np2, ns2;
+  real xim1;
+  integer modn;
+
+  /* Parameter adjustments */
+  --xh;
+  --w;
+  --x;
+
+  /* Function Body */
+  ns2 = (n + 1) / 2;
+  np2 = n + 2;
+  for (i = 3; i <= n; i += 2) {
+    xim1 = x[i - 1] + x[i];
+    x[i] -= x[i - 1];
+    x[i - 1] = xim1;
+  }
+  x[1] += x[1];
+  modn = n % 2;
+  if (modn == 0) {
+    x[n] += x[n];
+  }
+  rfftb(n, &x[1], &xh[1]);
+  for (k = 2; k <= ns2; ++k) {
+    kc = np2 - k;
+    xh[k] = w[k - 1] * x[kc] + w[kc - 1] * x[k];
+    xh[kc] = w[k - 1] * x[k] - w[kc - 1] * x[kc];
+  }
+  if (modn == 0) {
+    x[ns2 + 1] = w[ns2] * (x[ns2 + 1] + x[ns2 + 1]);
+  }
+  for (k = 2; k <= ns2; ++k) {
+    kc = np2 - k;
+    x[k] = xh[k] + xh[kc];
+    x[kc] = xh[k] - xh[kc];
+  }
+  x[1] += x[1];
+} /* cosqb1 */
+
+void cosqb(integer n, real *x, real *wsave)
+{
+  static const real tsqrt2 = 2.82842712474619f;
+
+  /* Local variables */
+  real x1;
+
+  /* Parameter adjustments */
+  --wsave;
+  --x;
+
+  if (n < 2) {
+    x[1] *= 4.f;
+  } else if (n == 2) {
+    x1 = (x[1] + x[2]) * 4.f;
+    x[2] = tsqrt2 * (x[1] - x[2]);
+    x[1] = x1;
+  } else {
+    cosqb1(n, &x[1], &wsave[1], &wsave[n + 1]);
+  }
+} /* cosqb */
+
+static void cosqf1(integer n, real *x, real *w, real *xh)
+{
+  /* Local variables */
+  integer i, k, kc, np2, ns2;
+  real xim1;
+  integer modn;
+
+  /* Parameter adjustments */
+  --xh;
+  --w;
+  --x;
+
+  /* Function Body */
+  ns2 = (n + 1) / 2;
+  np2 = n + 2;
+  for (k = 2; k <= ns2; ++k) {
+    kc = np2 - k;
+    xh[k] = x[k] + x[kc];
+    xh[kc] = x[k] - x[kc];
+  }
+  modn = n % 2;
+  if (modn == 0) {
+    xh[ns2 + 1] = x[ns2 + 1] + x[ns2 + 1];
+  }
+  for (k = 2; k <= ns2; ++k) {
+    kc = np2 - k;
+    x[k] = w[k - 1] * xh[kc] + w[kc - 1] * xh[k];
+    x[kc] = w[k - 1] * xh[k] - w[kc - 1] * xh[kc];
+  }
+  if (modn == 0) {
+    x[ns2 + 1] = w[ns2] * xh[ns2 + 1];
+  }
+  rfftf(n, &x[1], &xh[1]);
+  for (i = 3; i <= n; i += 2) {
+    xim1 = x[i - 1] - x[i];
+    x[i] = x[i - 1] + x[i];
+    x[i - 1] = xim1;
+  }
+} /* cosqf1 */
+
+void cosqf(integer n, real *x, real *wsave)
+{
+  static const real sqrt2 = 1.4142135623731f;
+
+  /* Local variables */
+  real tsqx;
+
+  /* Parameter adjustments */
+  --wsave;
+  --x;
+
+  if (n == 2) {
+    tsqx = sqrt2 * x[2];
+    x[2] = x[1] - tsqx;
+    x[1] += tsqx;
+  } else if (n > 2) {
+    cosqf1(n, &x[1], &wsave[1], &wsave[n + 1]);
+  }
+} /* cosqf */
+
+void cosqi(integer n, real *wsave)
+{
+  /* Local variables */
+  integer k;
+  real fk, dt;
+
+  /* Parameter adjustments */
+  --wsave;
+
+  dt = M_PI/2 / (real) (n);
+  fk = 0.f;
+  for (k = 1; k <= n; ++k) {
+    fk += 1.f;
+    wsave[k] = cos(fk * dt);
+  }
+  rffti(n, &wsave[n + 1]);
+} /* cosqi */
+
+void cost(integer n, real *x, real *wsave)
+{
+  /* Local variables */
+  integer i, k;
+  real c1, t1, t2;
+  integer kc;
+  real xi;
+  integer nm1, np1;
+  real x1h;
+  integer ns2;
+  real tx2, x1p3, xim2;
+  integer modn;
+
+  /* Parameter adjustments */
+  --wsave;
+  --x;
+
+  /* Function Body */
+  nm1 = n - 1;
+  np1 = n + 1;
+  ns2 = n / 2;
+  if (n < 2) {
+  } else if (n == 2) {
+    x1h = x[1] + x[2];
+    x[2] = x[1] - x[2];
+    x[1] = x1h;
+  } else if (n == 3) {
+    x1p3 = x[1] + x[3];
+    tx2 = x[2] + x[2];
+    x[2] = x[1] - x[3];
+    x[1] = x1p3 + tx2;
+    x[3] = x1p3 - tx2;
+  } else {
+    c1 = x[1] - x[n];
+    x[1] += x[n];
+    for (k = 2; k <= ns2; ++k) {
+      kc = np1 - k;
+      t1 = x[k] + x[kc];
+      t2 = x[k] - x[kc];
+      c1 += wsave[kc] * t2;
+      t2 = wsave[k] * t2;
+      x[k] = t1 - t2;
+      x[kc] = t1 + t2;
+    }
+    modn = n % 2;
+    if (modn != 0) {
+      x[ns2 + 1] += x[ns2 + 1];
+    }
+    rfftf(nm1, &x[1], &wsave[n + 1]);
+    xim2 = x[2];
+    x[2] = c1;
+    for (i = 4; i <= n; i += 2) {
+      xi = x[i];
+      x[i] = x[i - 2] - x[i - 1];
+      x[i - 1] = xim2;
+      xim2 = xi;
+    }
+    if (modn != 0) {
+      x[n] = xim2;
+    }
+  }
+} /* cost */
+
+void costi(integer n, real *wsave)
+{
+  /* Initialized data */
+
+  /* Local variables */
+  integer k, kc;
+  real fk, dt;
+  integer nm1, np1, ns2;
+
+  /* Parameter adjustments */
+  --wsave;
+
+  /* Function Body */
+  if (n <= 3) {
+    return;
+  }
+  nm1 = n - 1;
+  np1 = n + 1;
+  ns2 = n / 2;
+  dt = M_PI / (real) nm1;
+  fk = 0.f;
+  for (k = 2; k <= ns2; ++k) {
+    kc = np1 - k;
+    fk += 1.f;
+    wsave[k] = sin(fk * dt) * 2.f;
+    wsave[kc] = cos(fk * dt) * 2.f;
+  }
+  rffti(nm1, &wsave[n + 1]);
+} /* costi */
+
+void sinqb(integer n, real *x, real *wsave)
+{
+  /* Local variables */
+  integer k, kc, ns2;
+  real xhold;
+
+  /* Parameter adjustments */
+  --wsave;
+  --x;
+
+  /* Function Body */
+  if (n <= 1) {
+    x[1] *= 4.f;
+    return;
+  }
+  ns2 = n / 2;
+  for (k = 2; k <= n; k += 2) {
+    x[k] = -x[k];
+  }
+  cosqb(n, &x[1], &wsave[1]);
+  for (k = 1; k <= ns2; ++k) {
+    kc = n - k;
+    xhold = x[k];
+    x[k] = x[kc + 1];
+    x[kc + 1] = xhold;
+  }
+} /* sinqb */
+
+void sinqf(integer n, real *x, real *wsave)
+{
+  /* Local variables */
+  integer k, kc, ns2;
+  real xhold;
+
+  /* Parameter adjustments */
+  --wsave;
+  --x;
+
+  /* Function Body */
+  if (n == 1) {
+    return;
+  }
+  ns2 = n / 2;
+  for (k = 1; k <= ns2; ++k) {
+    kc = n - k;
+    xhold = x[k];
+    x[k] = x[kc + 1];
+    x[kc + 1] = xhold;
+  }
+  cosqf(n, &x[1], &wsave[1]);
+  for (k = 2; k <= n; k += 2) {
+    x[k] = -x[k];
+  }
+} /* sinqf */
+
+void sinqi(integer n, real *wsave)
+{
+
+  /* Parameter adjustments */
+  --wsave;
+
+  /* Function Body */
+  cosqi(n, &wsave[1]);
+} /* sinqi */
+
+static void sint1(integer n, real *war, real *was, real *xh, real *
+                  x, integer *ifac)
+{
+  /* Initialized data */
+
+  static const real sqrt3 = 1.73205080756888f;
+
+  /* Local variables */
+  integer i, k;
+  real t1, t2;
+  integer kc, np1, ns2, modn;
+  real xhold;
+
+  /* Parameter adjustments */
+  --ifac;
+  --x;
+  --xh;
+  --was;
+  --war;
+
+  /* Function Body */
+  for (i = 1; i <= n; ++i) {
+    xh[i] = war[i];
+    war[i] = x[i];
+  }
+  
+  if (n < 2) {
+    xh[1] += xh[1];
+  } else if (n == 2) {
+    xhold = sqrt3 * (xh[1] + xh[2]);
+    xh[2] = sqrt3 * (xh[1] - xh[2]);
+    xh[1] = xhold;
+  } else {
+    np1 = n + 1;
+    ns2 = n / 2;
+    x[1] = 0.f;
+    for (k = 1; k <= ns2; ++k) {
+      kc = np1 - k;
+      t1 = xh[k] - xh[kc];
+      t2 = was[k] * (xh[k] + xh[kc]);
+      x[k + 1] = t1 + t2;
+      x[kc + 1] = t2 - t1;
+    }
+    modn = n % 2;
+    if (modn != 0) {
+      x[ns2 + 2] = xh[ns2 + 1] * 4.f;
+    }
+    rfftf1(np1, &x[1], &xh[1], &war[1], &ifac[1]);
+    xh[1] = x[1] * .5f;
+    for (i = 3; i <= n; i += 2) {
+      xh[i - 1] = -x[i];
+      xh[i] = xh[i - 2] + x[i - 1];
+    }
+    if (modn == 0) {
+      xh[n] = -x[n + 1];
+    }
+  }
+  for (i = 1; i <= n; ++i) {
+    x[i] = war[i];
+    war[i] = xh[i];
+  }
+} /* sint1 */
+
+void sinti(integer n, real *wsave)
+{
+  /* Local variables */
+  integer k;
+  real dt;
+  integer np1, ns2;
+
+  /* Parameter adjustments */
+  --wsave;
+
+  /* Function Body */
+  if (n <= 1) {
+    return;
+  }
+  ns2 = n / 2;
+  np1 = n + 1;
+  dt = M_PI / (real) np1;
+  for (k = 1; k <= ns2; ++k) {
+    wsave[k] = sin(k * dt) * 2.f;
+  }
+  rffti(np1, &wsave[ns2 + 1]);
+} /* sinti */
+
+void sint(integer n, real *x, real *wsave)
+{
+  integer np1, iw1, iw2, iw3;
+
+  /* Parameter adjustments */
+  --wsave;
+  --x;
+
+  /* Function Body */
+  np1 = n + 1;
+  iw1 = n / 2 + 1;
+  iw2 = iw1 + np1;
+  iw3 = iw2 + np1;
+  sint1(n, &x[1], &wsave[1], &wsave[iw1], &wsave[iw2], (int*)&wsave[iw3]);
+} /* sint */
+
+#ifdef TESTING_FFTPACK
+#include <stdio.h>
+
+int main(void)
+{
+  static integer nd[] = { 120,91,54,49,32,28,24,8,4,3,2 };
+
+  /* System generated locals */
+  real r1, r2, r3;
+  f77complex q1, q2, q3;
+
+  /* Local variables */
+  integer i, j, k, n;
+  real w[2000], x[200], y[200], cf, fn, dt;
+  f77complex cx[200], cy[200];
+  real xh[200];
+  integer nz, nm1, np1, ns2;
+  real arg, tfn;
+  real sum, arg1, arg2;
+  real sum1, sum2, dcfb;
+  integer modn;
+  real rftb, rftf;
+  real sqrt2;
+  real rftfb;
+  real costt, sintt, dcfftb, dcfftf, cosqfb, costfb;
+  real sinqfb;
+  real sintfb;
+  real cosqbt, cosqft, sinqbt, sinqft;
+
+
+
+  /*     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+  /*                       VERSION 4  APRIL 1985 */
+
+  /*                         A TEST DRIVER FOR */
+  /*          A PACKAGE OF FORTRAN SUBPROGRAMS FOR THE FAST FOURIER */
+  /*           TRANSFORM OF PERIODIC AND OTHER SYMMETRIC SEQUENCES */
+
+  /*                              BY */
+
+  /*                       PAUL N SWARZTRAUBER */
+
+  /*       NATIONAL CENTER FOR ATMOSPHERIC RESEARCH  BOULDER,COLORADO 80307 */
+
+  /*        WHICH IS SPONSORED BY THE NATIONAL SCIENCE FOUNDATION */
+
+  /*     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+  /*             THIS PROGRAM TESTS THE PACKAGE OF FAST FOURIER */
+  /*     TRANSFORMS FOR BOTH COMPLEX AND REAL PERIODIC SEQUENCES AND */
+  /*     CERTIAN OTHER SYMMETRIC SEQUENCES THAT ARE LISTED BELOW. */
+
+  /*     1.   RFFTI     INITIALIZE  RFFTF AND RFFTB */
+  /*     2.   RFFTF     FORWARD TRANSFORM OF A REAL PERIODIC SEQUENCE */
+  /*     3.   RFFTB     BACKWARD TRANSFORM OF A REAL COEFFICIENT ARRAY */
+
+  /*     4.   EZFFTI    INITIALIZE EZFFTF AND EZFFTB */
+  /*     5.   EZFFTF    A SIMPLIFIED REAL PERIODIC FORWARD TRANSFORM */
+  /*     6.   EZFFTB    A SIMPLIFIED REAL PERIODIC BACKWARD TRANSFORM */
+
+  /*     7.   SINTI     INITIALIZE SINT */
+  /*     8.   SINT      SINE TRANSFORM OF A REAL ODD SEQUENCE */
+
+  /*     9.   COSTI     INITIALIZE COST */
+  /*     10.  COST      COSINE TRANSFORM OF A REAL EVEN SEQUENCE */
+
+  /*     11.  SINQI     INITIALIZE SINQF AND SINQB */
+  /*     12.  SINQF     FORWARD SINE TRANSFORM WITH ODD WAVE NUMBERS */
+  /*     13.  SINQB     UNNORMALIZED INVERSE OF SINQF */
+
+  /*     14.  COSQI     INITIALIZE COSQF AND COSQB */
+  /*     15.  COSQF     FORWARD COSINE TRANSFORM WITH ODD WAVE NUMBERS */
+  /*     16.  COSQB     UNNORMALIZED INVERSE OF COSQF */
+
+  /*     17.  CFFTI     INITIALIZE CFFTF AND CFFTB */
+  /*     18.  CFFTF     FORWARD TRANSFORM OF A COMPLEX PERIODIC SEQUENCE */
+  /*     19.  CFFTB     UNNORMALIZED INVERSE OF CFFTF */
+
+
+  sqrt2 = sqrt(2.f);
+  int all_ok = 1;
+  for (nz = 1; nz <= (int)(sizeof nd/sizeof nd[0]); ++nz) {
+    n = nd[nz - 1];
+    modn = n % 2;
+    fn = (real) n;
+    tfn = fn + fn;
+    np1 = n + 1;
+    nm1 = n - 1;
+    for (j = 1; j <= np1; ++j) {
+      x[j - 1] = sin((real) j * sqrt2);
+      y[j - 1] = x[j - 1];
+      xh[j - 1] = x[j - 1];
+    }
+
+    /*     TEST SUBROUTINES RFFTI,RFFTF AND RFFTB */
+
+    rffti(n, w);
+    dt = (2*M_PI) / fn;
+    ns2 = (n + 1) / 2;
+    if (ns2 < 2) {
+      goto L104;
+    }
+    for (k = 2; k <= ns2; ++k) {
+      sum1 = 0.f;
+      sum2 = 0.f;
+      arg = (real) (k - 1) * dt;
+      for (i = 1; i <= n; ++i) {
+        arg1 = (real) (i - 1) * arg;
+        sum1 += x[i - 1] * cos(arg1);
+        sum2 += x[i - 1] * sin(arg1);
+      }
+      y[(k << 1) - 3] = sum1;
+      y[(k << 1) - 2] = -sum2;
+    }
+  L104:
+    sum1 = 0.f;
+    sum2 = 0.f;
+    for (i = 1; i <= nm1; i += 2) {
+      sum1 += x[i - 1];
+      sum2 += x[i];
+    }
+    if (modn == 1) {
+      sum1 += x[n - 1];
+    }
+    y[0] = sum1 + sum2;
+    if (modn == 0) {
+      y[n - 1] = sum1 - sum2;
+    }
+    rfftf(n, x, w);
+    rftf = 0.f;
+    for (i = 1; i <= n; ++i) {
+      /* Computing MAX */
+      r2 = rftf, r3 = (r1 = x[i - 1] - y[i - 1], fabs(r1));
+      rftf = dmax(r2,r3);
+      x[i - 1] = xh[i - 1];
+    }
+    rftf /= fn;
+    for (i = 1; i <= n; ++i) {
+      sum = x[0] * .5f;
+      arg = (real) (i - 1) * dt;
+      if (ns2 < 2) {
+        goto L108;
+      }
+      for (k = 2; k <= ns2; ++k) {
+        arg1 = (real) (k - 1) * arg;
+        sum = sum + x[(k << 1) - 3] * cos(arg1) - x[(k << 1) - 2] * 
+          sin(arg1);
+      }
+    L108:
+      if (modn == 0) {
+        sum += (real)pow(-1, i-1) * .5f * x[n - 1];
+      }
+      y[i - 1] = sum + sum;
+    }
+    rfftb(n, x, w);
+    rftb = 0.f;
+    for (i = 1; i <= n; ++i) {
+      /* Computing MAX */
+      r2 = rftb, r3 = (r1 = x[i - 1] - y[i - 1], fabs(r1));
+      rftb = dmax(r2,r3);
+      x[i - 1] = xh[i - 1];
+      y[i - 1] = xh[i - 1];
+    }
+    rfftb(n, y, w);
+    rfftf(n, y, w);
+    cf = 1.f / fn;
+    rftfb = 0.f;
+    for (i = 1; i <= n; ++i) {
+      /* Computing MAX */
+      r2 = rftfb, r3 = (r1 = cf * y[i - 1] - x[i - 1], fabs(
+                                                            r1));
+      rftfb = dmax(r2,r3);
+    }
+
+    /*     TEST SUBROUTINES SINTI AND SINT */
+
+    dt = M_PI / fn;
+    for (i = 1; i <= nm1; ++i) {
+      x[i - 1] = xh[i - 1];
+    }
+    for (i = 1; i <= nm1; ++i) {
+      y[i - 1] = 0.f;
+      arg1 = (real) i * dt;
+      for (k = 1; k <= nm1; ++k) {
+        y[i - 1] += x[k - 1] * sin((real) k * arg1);
+      }
+      y[i - 1] += y[i - 1];
+    }
+    sinti(nm1, w);
+    sint(nm1, x, w);
+    cf = .5f / fn;
+    sintt = 0.f;
+    for (i = 1; i <= nm1; ++i) {
+      /* Computing MAX */
+      r2 = sintt, r3 = (r1 = x[i - 1] - y[i - 1], fabs(r1));
+      sintt = dmax(r2,r3);
+      x[i - 1] = xh[i - 1];
+      y[i - 1] = x[i - 1];
+    }
+    sintt = cf * sintt;
+    sint(nm1, x, w);
+    sint(nm1, x, w);
+    sintfb = 0.f;
+    for (i = 1; i <= nm1; ++i) {
+      /* Computing MAX */
+      r2 = sintfb, r3 = (r1 = cf * x[i - 1] - y[i - 1], fabs(
+                                                             r1));
+      sintfb = dmax(r2,r3);
+    }
+
+    /*     TEST SUBROUTINES COSTI AND COST */
+
+    for (i = 1; i <= np1; ++i) {
+      x[i - 1] = xh[i - 1];
+    }
+    for (i = 1; i <= np1; ++i) {
+      y[i - 1] = (x[0] + (real) pow(-1, i+1) * x[n]) * .5f;
+      arg = (real) (i - 1) * dt;
+      for (k = 2; k <= n; ++k) {
+        y[i - 1] += x[k - 1] * cos((real) (k - 1) * arg);
+      }
+      y[i - 1] += y[i - 1];
+    }
+    costi(np1, w);
+    cost(np1, x, w);
+    costt = 0.f;
+    for (i = 1; i <= np1; ++i) {
+      /* Computing MAX */
+      r2 = costt, r3 = (r1 = x[i - 1] - y[i - 1], fabs(r1));
+      costt = dmax(r2,r3);
+      x[i - 1] = xh[i - 1];
+      y[i - 1] = xh[i - 1];
+    }
+    costt = cf * costt;
+    cost(np1, x, w);
+    cost(np1, x, w);
+    costfb = 0.f;
+    for (i = 1; i <= np1; ++i) {
+      /* Computing MAX */
+      r2 = costfb, r3 = (r1 = cf * x[i - 1] - y[i - 1], fabs(
+                                                             r1));
+      costfb = dmax(r2,r3);
+    }
+
+    /*     TEST SUBROUTINES SINQI,SINQF AND SINQB */
+
+    cf = .25f / fn;
+    for (i = 1; i <= n; ++i) {
+      y[i - 1] = xh[i - 1];
+    }
+    dt = M_PI / (fn + fn);
+    for (i = 1; i <= n; ++i) {
+      x[i - 1] = 0.f;
+      arg = dt * (real) i;
+      for (k = 1; k <= n; ++k) {
+        x[i - 1] += y[k - 1] * sin((real) (k + k - 1) * arg);
+      }
+      x[i - 1] *= 4.f;
+    }
+    sinqi(n, w);
+    sinqb(n, y, w);
+    sinqbt = 0.f;
+    for (i = 1; i <= n; ++i) {
+      /* Computing MAX */
+      r2 = sinqbt, r3 = (r1 = y[i - 1] - x[i - 1], fabs(r1))
+        ;
+      sinqbt = dmax(r2,r3);
+      x[i - 1] = xh[i - 1];
+    }
+    sinqbt = cf * sinqbt;
+    for (i = 1; i <= n; ++i) {
+      arg = (real) (i + i - 1) * dt;
+      y[i - 1] = (real) pow(-1, i+1) * .5f * x[n - 1];
+      for (k = 1; k <= nm1; ++k) {
+        y[i - 1] += x[k - 1] * sin((real) k * arg);
+      }
+      y[i - 1] += y[i - 1];
+    }
+    sinqf(n, x, w);
+    sinqft = 0.f;
+    for (i = 1; i <= n; ++i) {
+      /* Computing MAX */
+      r2 = sinqft, r3 = (r1 = x[i - 1] - y[i - 1], fabs(r1))
+        ;
+      sinqft = dmax(r2,r3);
+      y[i - 1] = xh[i - 1];
+      x[i - 1] = xh[i - 1];
+    }
+    sinqf(n, y, w);
+    sinqb(n, y, w);
+    sinqfb = 0.f;
+    for (i = 1; i <= n; ++i) {
+      /* Computing MAX */
+      r2 = sinqfb, r3 = (r1 = cf * y[i - 1] - x[i - 1], fabs(
+                                                             r1));
+      sinqfb = dmax(r2,r3);
+    }
+
+    /*     TEST SUBROUTINES COSQI,COSQF AND COSQB */
+
+    for (i = 1; i <= n; ++i) {
+      y[i - 1] = xh[i - 1];
+    }
+    for (i = 1; i <= n; ++i) {
+      x[i - 1] = 0.f;
+      arg = (real) (i - 1) * dt;
+      for (k = 1; k <= n; ++k) {
+        x[i - 1] += y[k - 1] * cos((real) (k + k - 1) * arg);
+      }
+      x[i - 1] *= 4.f;
+    }
+    cosqi(n, w);
+    cosqb(n, y, w);
+    cosqbt = 0.f;
+    for (i = 1; i <= n; ++i) {
+      /* Computing MAX */
+      r2 = cosqbt, r3 = (r1 = x[i - 1] - y[i - 1], fabs(r1))
+        ;
+      cosqbt = dmax(r2,r3);
+      x[i - 1] = xh[i - 1];
+    }
+    cosqbt = cf * cosqbt;
+    for (i = 1; i <= n; ++i) {
+      y[i - 1] = x[0] * .5f;
+      arg = (real) (i + i - 1) * dt;
+      for (k = 2; k <= n; ++k) {
+        y[i - 1] += x[k - 1] * cos((real) (k - 1) * arg);
+      }
+      y[i - 1] += y[i - 1];
+    }
+    cosqf(n, x, w);
+    cosqft = 0.f;
+    for (i = 1; i <= n; ++i) {
+      /* Computing MAX */
+      r2 = cosqft, r3 = (r1 = y[i - 1] - x[i - 1], fabs(r1))
+        ;
+      cosqft = dmax(r2,r3);
+      x[i - 1] = xh[i - 1];
+      y[i - 1] = xh[i - 1];
+    }
+    cosqft = cf * cosqft;
+    cosqb(n, x, w);
+    cosqf(n, x, w);
+    cosqfb = 0.f;
+    for (i = 1; i <= n; ++i) {
+      /* Computing MAX */
+      r2 = cosqfb, r3 = (r1 = cf * x[i - 1] - y[i - 1], fabs(r1));
+      cosqfb = dmax(r2,r3);
+    }
+
+    /*     TEST  CFFTI,CFFTF,CFFTB */
+
+    for (i = 1; i <= n; ++i) {
+      r1 = cos(sqrt2 * (real) i);
+      r2 = sin(sqrt2 * (real) (i * i));
+      q1.r = r1, q1.i = r2;
+      cx[i-1].r = q1.r, cx[i-1].i = q1.i;
+    }
+    dt = (2*M_PI) / fn;
+    for (i = 1; i <= n; ++i) {
+      arg1 = -((real) (i - 1)) * dt;
+      cy[i-1].r = 0.f, cy[i-1].i = 0.f;
+      for (k = 1; k <= n; ++k) {
+        arg2 = (real) (k - 1) * arg1;
+        r1 = cos(arg2);
+        r2 = sin(arg2);
+        q3.r = r1, q3.i = r2;
+        q2.r = q3.r * cx[k-1].r - q3.i * cx[k-1].i, q2.i = 
+          q3.r * cx[k-1].i + q3.i * cx[k-1].r;
+        q1.r = cy[i-1].r + q2.r, q1.i = cy[i-1].i + q2.i;
+        cy[i-1].r = q1.r, cy[i-1].i = q1.i;
+      }
+    }
+    cffti(n, w);
+    cfftf(n, (real*)cx, w);
+    dcfftf = 0.f;
+    for (i = 1; i <= n; ++i) {
+      /* Computing MAX */
+      q1.r = cx[i-1].r - cy[i-1].r, q1.i = cx[i-1].i - cy[i-1]
+        .i;
+      r1 = dcfftf, r2 = c_abs(&q1);
+      dcfftf = dmax(r1,r2);
+      q1.r = cx[i-1].r / fn, q1.i = cx[i-1].i / fn;
+      cx[i-1].r = q1.r, cx[i-1].i = q1.i;
+    }
+    dcfftf /= fn;
+    for (i = 1; i <= n; ++i) {
+      arg1 = (real) (i - 1) * dt;
+      cy[i-1].r = 0.f, cy[i-1].i = 0.f;
+      for (k = 1; k <= n; ++k) {
+        arg2 = (real) (k - 1) * arg1;
+        r1 = cos(arg2);
+        r2 = sin(arg2);
+        q3.r = r1, q3.i = r2;
+        q2.r = q3.r * cx[k-1].r - q3.i * cx[k-1].i, q2.i = 
+          q3.r * cx[k-1].i + q3.i * cx[k-1].r;
+        q1.r = cy[i-1].r + q2.r, q1.i = cy[i-1].i + q2.i;
+        cy[i-1].r = q1.r, cy[i-1].i = q1.i;
+      }
+    }
+    cfftb(n, (real*)cx, w);
+    dcfftb = 0.f;
+    for (i = 1; i <= n; ++i) {
+      /* Computing MAX */
+      q1.r = cx[i-1].r - cy[i-1].r, q1.i = cx[i-1].i - cy[i-1].i;
+      r1 = dcfftb, r2 = c_abs(&q1);
+      dcfftb = dmax(r1,r2);
+      cx[i-1].r = cy[i-1].r, cx[i-1].i = cy[i-1].i;
+    }
+    cf = 1.f / fn;
+    cfftf(n, (real*)cx, w);
+    cfftb(n, (real*)cx, w);
+    dcfb = 0.f;
+    for (i = 1; i <= n; ++i) {
+      /* Computing MAX */
+      q2.r = cf * cx[i-1].r, q2.i = cf * cx[i-1].i;
+      q1.r = q2.r - cy[i-1].r, q1.i = q2.i - cy[i-1].i;
+      r1 = dcfb, r2 = c_abs(&q1);
+      dcfb = dmax(r1,r2);
+    }
+    printf("%d\tRFFTF  %10.3g\tRFFTB  %10.ge\tRFFTFB %10.3g", n, rftf, rftb, rftfb);
+    printf(  "\tSINT   %10.3g\tSINTFB %10.ge\tCOST   %10.3g\n", sintt, sintfb, costt);
+    printf(  "\tCOSTFB %10.3g\tSINQF  %10.ge\tSINQB  %10.3g", costfb, sinqft, sinqbt);
+    printf(  "\tSINQFB %10.3g\tCOSQF  %10.ge\tCOSQB  %10.3g\n", sinqfb, cosqft, cosqbt);
+    printf(  "\tCOSQFB %10.3g\t", cosqfb);
+    printf(  "\tCFFTF  %10.ge\tCFFTB  %10.3g\n", dcfftf, dcfftb);
+    printf(  "\tCFFTFB %10.3g\n", dcfb);
+
+#define CHECK(x) if (x > 1e-3) { printf(#x " failed: %g\n", x); all_ok = 0; }
+    CHECK(rftf); CHECK(rftb); CHECK(rftfb); CHECK(sintt); CHECK(sintfb); CHECK(costt);
+    CHECK(costfb); CHECK(sinqft); CHECK(sinqbt); CHECK(sinqfb); CHECK(cosqft); CHECK(cosqbt);
+    CHECK(cosqfb); CHECK(dcfftf); CHECK(dcfftb);
+  }
+
+  if (all_ok) printf("Everything looks fine.\n"); 
+  else printf("ERRORS WERE DETECTED.\n");
+  /*
+    expected:
+    120     RFFTF   2.786e-06       RFFTB   6.847e-04       RFFTFB  2.795e-07       SINT    1.312e-06       SINTFB  1.237e-06       COST    1.319e-06
+    COSTFB  4.355e-06       SINQF   3.281e-04       SINQB   1.876e-06       SINQFB  2.198e-07       COSQF   6.199e-07       COSQB   2.193e-06
+    COSQFB  2.300e-07       DEZF    5.573e-06       DEZB    1.363e-05       DEZFB   1.371e-06       CFFTF   5.590e-06       CFFTB   4.751e-05
+    CFFTFB  4.215e-07
+    54      RFFTF   4.708e-07       RFFTB   3.052e-05       RFFTFB  3.439e-07       SINT    3.532e-07       SINTFB  4.145e-07       COST    3.002e-07
+    COSTFB  6.343e-07       SINQF   4.959e-05       SINQB   4.415e-07       SINQFB  2.882e-07       COSQF   2.826e-07       COSQB   2.472e-07
+    COSQFB  3.439e-07       DEZF    9.388e-07       DEZB    5.066e-06       DEZFB   5.960e-07       CFFTF   1.426e-06       CFFTB   9.482e-06
+    CFFTFB  2.980e-07
+    49      RFFTF   4.476e-07       RFFTB   5.341e-05       RFFTFB  2.574e-07       SINT    9.196e-07       SINTFB  9.401e-07       COST    8.174e-07
+    COSTFB  1.331e-06       SINQF   4.005e-05       SINQB   9.342e-07       SINQFB  3.057e-07       COSQF   2.530e-07       COSQB   6.228e-07
+    COSQFB  4.826e-07       DEZF    9.071e-07       DEZB    4.590e-06       DEZFB   5.960e-07       CFFTF   2.095e-06       CFFTB   1.414e-05
+    CFFTFB  7.398e-07
+    32      RFFTF   4.619e-07       RFFTB   2.861e-05       RFFTFB  1.192e-07       SINT    3.874e-07       SINTFB  4.172e-07       COST    4.172e-07
+    COSTFB  1.699e-06       SINQF   2.551e-05       SINQB   6.407e-07       SINQFB  2.980e-07       COSQF   1.639e-07       COSQB   1.714e-07
+    COSQFB  2.384e-07       DEZF    1.013e-06       DEZB    2.339e-06       DEZFB   7.749e-07       CFFTF   1.127e-06       CFFTB   6.744e-06
+    CFFTFB  2.666e-07
+    4       RFFTF   1.490e-08       RFFTB   1.490e-07       RFFTFB  5.960e-08       SINT    7.451e-09       SINTFB  0.000e+00       COST    2.980e-08
+    COSTFB  1.192e-07       SINQF   4.768e-07       SINQB   2.980e-08       SINQFB  5.960e-08       COSQF   2.608e-08       COSQB   5.960e-08
+    COSQFB  1.192e-07       DEZF    2.980e-08       DEZB    5.960e-08       DEZFB   0.000e+00       CFFTF   6.664e-08       CFFTB   5.960e-08
+    CFFTFB  6.144e-08
+    3       RFFTF   3.974e-08       RFFTB   1.192e-07       RFFTFB  3.303e-08       SINT    1.987e-08       SINTFB  1.069e-08       COST    4.967e-08
+    COSTFB  5.721e-08       SINQF   8.941e-08       SINQB   2.980e-08       SINQFB  1.259e-07       COSQF   7.451e-09       COSQB   4.967e-08
+    COSQFB  7.029e-08       DEZF    1.192e-07       DEZB    5.960e-08       DEZFB   5.960e-08       CFFTF   7.947e-08       CFFTB   8.429e-08
+    CFFTFB  9.064e-08
+    2       RFFTF   0.000e+00       RFFTB   0.000e+00       RFFTFB  0.000e+00       SINT    0.000e+00       SINTFB  0.000e+00       COST    0.000e+00
+    COSTFB  0.000e+00       SINQF   1.192e-07       SINQB   2.980e-08       SINQFB  5.960e-08       COSQF   7.451e-09       COSQB   1.490e-08
+    COSQFB  0.000e+00       DEZF    0.000e+00       DEZB    0.000e+00       DEZFB   0.000e+00       CFFTF   0.000e+00       CFFTB   5.960e-08
+    CFFTFB  5.960e-08
+    Everything looks fine.
+
+  */
+
+  return all_ok ? 0 : 1;
+}
+#endif //TESTING_FFTPACK
diff --git a/third_party/pffft/src/fftpack.h b/third_party/pffft/src/fftpack.h
new file mode 100644
index 0000000..381bcc61d
--- /dev/null
+++ b/third_party/pffft/src/fftpack.h
@@ -0,0 +1,799 @@
+/*
+  Interface for the f2c translation of fftpack as found on http://www.netlib.org/fftpack/
+  
+   FFTPACK license:
+
+   http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html
+
+   Copyright (c) 2004 the University Corporation for Atmospheric
+   Research ("UCAR"). All rights reserved. Developed by NCAR's
+   Computational and Information Systems Laboratory, UCAR,
+   www.cisl.ucar.edu.
+
+   Redistribution and use of the Software in source and binary forms,
+   with or without modification, is permitted provided that the
+   following conditions are met:
+
+   - Neither the names of NCAR's Computational and Information Systems
+   Laboratory, the University Corporation for Atmospheric Research,
+   nor the names of its sponsors or contributors may be used to
+   endorse or promote products derived from this Software without
+   specific prior written permission.  
+
+   - Redistributions of source code must retain the above copyright
+   notices, this list of conditions, and the disclaimer below.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions, and the disclaimer below in the
+   documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
+   HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+   SOFTWARE.
+
+   ChangeLog:
+   2011/10/02: this is my first release of this file.
+*/
+
+#ifndef FFTPACK_H
+#define FFTPACK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// just define FFTPACK_DOUBLE_PRECISION if you want to build it as a double precision fft
+
+#ifndef FFTPACK_DOUBLE_PRECISION
+  typedef float fftpack_real;
+  typedef int   fftpack_int;
+#else
+  typedef double fftpack_real;
+  typedef int    fftpack_int;
+#endif
+
+  void cffti(fftpack_int n, fftpack_real *wsave);
+
+  void cfftf(fftpack_int n, fftpack_real *c, fftpack_real *wsave);
+
+  void cfftb(fftpack_int n, fftpack_real *c, fftpack_real *wsave);
+
+  void rffti(fftpack_int n, fftpack_real *wsave);
+  void rfftf(fftpack_int n, fftpack_real *r, fftpack_real *wsave);
+  void rfftb(fftpack_int n, fftpack_real *r, fftpack_real *wsave);
+
+  void cosqi(fftpack_int n, fftpack_real *wsave);
+  void cosqf(fftpack_int n, fftpack_real *x, fftpack_real *wsave);
+  void cosqb(fftpack_int n, fftpack_real *x, fftpack_real *wsave);
+
+  void costi(fftpack_int n, fftpack_real *wsave);
+  void cost(fftpack_int n, fftpack_real *x, fftpack_real *wsave);
+
+  void sinqi(fftpack_int n, fftpack_real *wsave);
+  void sinqb(fftpack_int n, fftpack_real *x, fftpack_real *wsave);
+  void sinqf(fftpack_int n, fftpack_real *x, fftpack_real *wsave);
+
+  void sinti(fftpack_int n, fftpack_real *wsave);
+  void sint(fftpack_int n, fftpack_real *x, fftpack_real *wsave);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FFTPACK_H */
+
+/*
+
+                      FFTPACK
+
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+                  version 4  april 1985
+
+     a package of fortran subprograms for the fast fourier
+      transform of periodic and other symmetric sequences
+
+                         by
+
+                  paul n swarztrauber
+
+  national center for atmospheric research  boulder,colorado 80307
+
+   which is sponsored by the national science foundation
+
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+
+
+this package consists of programs which perform fast fourier
+transforms for both complex and real periodic sequences and
+certain other symmetric sequences that are listed below.
+
+1.   rffti     initialize  rfftf and rfftb
+2.   rfftf     forward transform of a real periodic sequence
+3.   rfftb     backward transform of a real coefficient array
+
+4.   ezffti    initialize ezfftf and ezfftb
+5.   ezfftf    a simplified real periodic forward transform
+6.   ezfftb    a simplified real periodic backward transform
+
+7.   sinti     initialize sint
+8.   sint      sine transform of a real odd sequence
+
+9.   costi     initialize cost
+10.  cost      cosine transform of a real even sequence
+
+11.  sinqi     initialize sinqf and sinqb
+12.  sinqf     forward sine transform with odd wave numbers
+13.  sinqb     unnormalized inverse of sinqf
+
+14.  cosqi     initialize cosqf and cosqb
+15.  cosqf     forward cosine transform with odd wave numbers
+16.  cosqb     unnormalized inverse of cosqf
+
+17.  cffti     initialize cfftf and cfftb
+18.  cfftf     forward transform of a complex periodic sequence
+19.  cfftb     unnormalized inverse of cfftf
+
+
+******************************************************************
+
+subroutine rffti(n,wsave)
+
+  ****************************************************************
+
+subroutine rffti initializes the array wsave which is used in
+both rfftf and rfftb. the prime factorization of n together with
+a tabulation of the trigonometric functions are computed and
+stored in wsave.
+
+input parameter
+
+n       the length of the sequence to be transformed.
+
+output parameter
+
+wsave   a work array which must be dimensioned at least 2*n+15.
+        the same work array can be used for both rfftf and rfftb
+        as long as n remains unchanged. different wsave arrays
+        are required for different values of n. the contents of
+        wsave must not be changed between calls of rfftf or rfftb.
+
+******************************************************************
+
+subroutine rfftf(n,r,wsave)
+
+******************************************************************
+
+subroutine rfftf computes the fourier coefficients of a real
+perodic sequence (fourier analysis). the transform is defined
+below at output parameter r.
+
+input parameters
+
+n       the length of the array r to be transformed.  the method
+        is most efficient when n is a product of small primes.
+        n may change so long as different work arrays are provided
+
+r       a real array of length n which contains the sequence
+        to be transformed
+
+wsave   a work array which must be dimensioned at least 2*n+15.
+        in the program that calls rfftf. the wsave array must be
+        initialized by calling subroutine rffti(n,wsave) and a
+        different wsave array must be used for each different
+        value of n. this initialization does not have to be
+        repeated so long as n remains unchanged thus subsequent
+        transforms can be obtained faster than the first.
+        the same wsave array can be used by rfftf and rfftb.
+
+
+output parameters
+
+r       r(1) = the sum from i=1 to i=n of r(i)
+
+        if n is even set l =n/2   , if n is odd set l = (n+1)/2
+
+          then for k = 2,...,l
+
+             r(2*k-2) = the sum from i = 1 to i = n of
+
+                  r(i)*cos((k-1)*(i-1)*2*pi/n)
+
+             r(2*k-1) = the sum from i = 1 to i = n of
+
+                 -r(i)*sin((k-1)*(i-1)*2*pi/n)
+
+        if n is even
+
+             r(n) = the sum from i = 1 to i = n of
+
+                  (-1)**(i-1)*r(i)
+
+ *****  note
+             this transform is unnormalized since a call of rfftf
+             followed by a call of rfftb will multiply the input
+             sequence by n.
+
+wsave   contains results which must not be destroyed between
+        calls of rfftf or rfftb.
+
+
+******************************************************************
+
+subroutine rfftb(n,r,wsave)
+
+******************************************************************
+
+subroutine rfftb computes the real perodic sequence from its
+fourier coefficients (fourier synthesis). the transform is defined
+below at output parameter r.
+
+input parameters
+
+n       the length of the array r to be transformed.  the method
+        is most efficient when n is a product of small primes.
+        n may change so long as different work arrays are provided
+
+r       a real array of length n which contains the sequence
+        to be transformed
+
+wsave   a work array which must be dimensioned at least 2*n+15.
+        in the program that calls rfftb. the wsave array must be
+        initialized by calling subroutine rffti(n,wsave) and a
+        different wsave array must be used for each different
+        value of n. this initialization does not have to be
+        repeated so long as n remains unchanged thus subsequent
+        transforms can be obtained faster than the first.
+        the same wsave array can be used by rfftf and rfftb.
+
+
+output parameters
+
+r       for n even and for i = 1,...,n
+
+             r(i) = r(1)+(-1)**(i-1)*r(n)
+
+                  plus the sum from k=2 to k=n/2 of
+
+                   2.*r(2*k-2)*cos((k-1)*(i-1)*2*pi/n)
+
+                  -2.*r(2*k-1)*sin((k-1)*(i-1)*2*pi/n)
+
+        for n odd and for i = 1,...,n
+
+             r(i) = r(1) plus the sum from k=2 to k=(n+1)/2 of
+
+                  2.*r(2*k-2)*cos((k-1)*(i-1)*2*pi/n)
+
+                 -2.*r(2*k-1)*sin((k-1)*(i-1)*2*pi/n)
+
+ *****  note
+             this transform is unnormalized since a call of rfftf
+             followed by a call of rfftb will multiply the input
+             sequence by n.
+
+wsave   contains results which must not be destroyed between
+        calls of rfftb or rfftf.
+
+******************************************************************
+
+subroutine sinti(n,wsave)
+
+******************************************************************
+
+subroutine sinti initializes the array wsave which is used in
+subroutine sint. the prime factorization of n together with
+a tabulation of the trigonometric functions are computed and
+stored in wsave.
+
+input parameter
+
+n       the length of the sequence to be transformed.  the method
+        is most efficient when n+1 is a product of small primes.
+
+output parameter
+
+wsave   a work array with at least int(2.5*n+15) locations.
+        different wsave arrays are required for different values
+        of n. the contents of wsave must not be changed between
+        calls of sint.
+
+******************************************************************
+
+subroutine sint(n,x,wsave)
+
+******************************************************************
+
+subroutine sint computes the discrete fourier sine transform
+of an odd sequence x(i). the transform is defined below at
+output parameter x.
+
+sint is the unnormalized inverse of itself since a call of sint
+followed by another call of sint will multiply the input sequence
+x by 2*(n+1).
+
+the array wsave which is used by subroutine sint must be
+initialized by calling subroutine sinti(n,wsave).
+
+input parameters
+
+n       the length of the sequence to be transformed.  the method
+        is most efficient when n+1 is the product of small primes.
+
+x       an array which contains the sequence to be transformed
+
+
+wsave   a work array with dimension at least int(2.5*n+15)
+        in the program that calls sint. the wsave array must be
+        initialized by calling subroutine sinti(n,wsave) and a
+        different wsave array must be used for each different
+        value of n. this initialization does not have to be
+        repeated so long as n remains unchanged thus subsequent
+        transforms can be obtained faster than the first.
+
+output parameters
+
+x       for i=1,...,n
+
+             x(i)= the sum from k=1 to k=n
+
+                  2*x(k)*sin(k*i*pi/(n+1))
+
+             a call of sint followed by another call of
+             sint will multiply the sequence x by 2*(n+1).
+             hence sint is the unnormalized inverse
+             of itself.
+
+wsave   contains initialization calculations which must not be
+        destroyed between calls of sint.
+
+******************************************************************
+
+subroutine costi(n,wsave)
+
+******************************************************************
+
+subroutine costi initializes the array wsave which is used in
+subroutine cost. the prime factorization of n together with
+a tabulation of the trigonometric functions are computed and
+stored in wsave.
+
+input parameter
+
+n       the length of the sequence to be transformed.  the method
+        is most efficient when n-1 is a product of small primes.
+
+output parameter
+
+wsave   a work array which must be dimensioned at least 3*n+15.
+        different wsave arrays are required for different values
+        of n. the contents of wsave must not be changed between
+        calls of cost.
+
+******************************************************************
+
+subroutine cost(n,x,wsave)
+
+******************************************************************
+
+subroutine cost computes the discrete fourier cosine transform
+of an even sequence x(i). the transform is defined below at output
+parameter x.
+
+cost is the unnormalized inverse of itself since a call of cost
+followed by another call of cost will multiply the input sequence
+x by 2*(n-1). the transform is defined below at output parameter x
+
+the array wsave which is used by subroutine cost must be
+initialized by calling subroutine costi(n,wsave).
+
+input parameters
+
+n       the length of the sequence x. n must be greater than 1.
+        the method is most efficient when n-1 is a product of
+        small primes.
+
+x       an array which contains the sequence to be transformed
+
+wsave   a work array which must be dimensioned at least 3*n+15
+        in the program that calls cost. the wsave array must be
+        initialized by calling subroutine costi(n,wsave) and a
+        different wsave array must be used for each different
+        value of n. this initialization does not have to be
+        repeated so long as n remains unchanged thus subsequent
+        transforms can be obtained faster than the first.
+
+output parameters
+
+x       for i=1,...,n
+
+            x(i) = x(1)+(-1)**(i-1)*x(n)
+
+             + the sum from k=2 to k=n-1
+
+                 2*x(k)*cos((k-1)*(i-1)*pi/(n-1))
+
+             a call of cost followed by another call of
+             cost will multiply the sequence x by 2*(n-1)
+             hence cost is the unnormalized inverse
+             of itself.
+
+wsave   contains initialization calculations which must not be
+        destroyed between calls of cost.
+
+******************************************************************
+
+subroutine sinqi(n,wsave)
+
+******************************************************************
+
+subroutine sinqi initializes the array wsave which is used in
+both sinqf and sinqb. the prime factorization of n together with
+a tabulation of the trigonometric functions are computed and
+stored in wsave.
+
+input parameter
+
+n       the length of the sequence to be transformed. the method
+        is most efficient when n is a product of small primes.
+
+output parameter
+
+wsave   a work array which must be dimensioned at least 3*n+15.
+        the same work array can be used for both sinqf and sinqb
+        as long as n remains unchanged. different wsave arrays
+        are required for different values of n. the contents of
+        wsave must not be changed between calls of sinqf or sinqb.
+
+******************************************************************
+
+subroutine sinqf(n,x,wsave)
+
+******************************************************************
+
+subroutine sinqf computes the fast fourier transform of quarter
+wave data. that is , sinqf computes the coefficients in a sine
+series representation with only odd wave numbers. the transform
+is defined below at output parameter x.
+
+sinqb is the unnormalized inverse of sinqf since a call of sinqf
+followed by a call of sinqb will multiply the input sequence x
+by 4*n.
+
+the array wsave which is used by subroutine sinqf must be
+initialized by calling subroutine sinqi(n,wsave).
+
+
+input parameters
+
+n       the length of the array x to be transformed.  the method
+        is most efficient when n is a product of small primes.
+
+x       an array which contains the sequence to be transformed
+
+wsave   a work array which must be dimensioned at least 3*n+15.
+        in the program that calls sinqf. the wsave array must be
+        initialized by calling subroutine sinqi(n,wsave) and a
+        different wsave array must be used for each different
+        value of n. this initialization does not have to be
+        repeated so long as n remains unchanged thus subsequent
+        transforms can be obtained faster than the first.
+
+output parameters
+
+x       for i=1,...,n
+
+             x(i) = (-1)**(i-1)*x(n)
+
+                + the sum from k=1 to k=n-1 of
+
+                2*x(k)*sin((2*i-1)*k*pi/(2*n))
+
+             a call of sinqf followed by a call of
+             sinqb will multiply the sequence x by 4*n.
+             therefore sinqb is the unnormalized inverse
+             of sinqf.
+
+wsave   contains initialization calculations which must not
+        be destroyed between calls of sinqf or sinqb.
+
+******************************************************************
+
+subroutine sinqb(n,x,wsave)
+
+******************************************************************
+
+subroutine sinqb computes the fast fourier transform of quarter
+wave data. that is , sinqb computes a sequence from its
+representation in terms of a sine series with odd wave numbers.
+the transform is defined below at output parameter x.
+
+sinqf is the unnormalized inverse of sinqb since a call of sinqb
+followed by a call of sinqf will multiply the input sequence x
+by 4*n.
+
+the array wsave which is used by subroutine sinqb must be
+initialized by calling subroutine sinqi(n,wsave).
+
+
+input parameters
+
+n       the length of the array x to be transformed.  the method
+        is most efficient when n is a product of small primes.
+
+x       an array which contains the sequence to be transformed
+
+wsave   a work array which must be dimensioned at least 3*n+15.
+        in the program that calls sinqb. the wsave array must be
+        initialized by calling subroutine sinqi(n,wsave) and a
+        different wsave array must be used for each different
+        value of n. this initialization does not have to be
+        repeated so long as n remains unchanged thus subsequent
+        transforms can be obtained faster than the first.
+
+output parameters
+
+x       for i=1,...,n
+
+             x(i)= the sum from k=1 to k=n of
+
+               4*x(k)*sin((2k-1)*i*pi/(2*n))
+
+             a call of sinqb followed by a call of
+             sinqf will multiply the sequence x by 4*n.
+             therefore sinqf is the unnormalized inverse
+             of sinqb.
+
+wsave   contains initialization calculations which must not
+        be destroyed between calls of sinqb or sinqf.
+
+******************************************************************
+
+subroutine cosqi(n,wsave)
+
+******************************************************************
+
+subroutine cosqi initializes the array wsave which is used in
+both cosqf and cosqb. the prime factorization of n together with
+a tabulation of the trigonometric functions are computed and
+stored in wsave.
+
+input parameter
+
+n       the length of the array to be transformed.  the method
+        is most efficient when n is a product of small primes.
+
+output parameter
+
+wsave   a work array which must be dimensioned at least 3*n+15.
+        the same work array can be used for both cosqf and cosqb
+        as long as n remains unchanged. different wsave arrays
+        are required for different values of n. the contents of
+        wsave must not be changed between calls of cosqf or cosqb.
+
+******************************************************************
+
+subroutine cosqf(n,x,wsave)
+
+******************************************************************
+
+subroutine cosqf computes the fast fourier transform of quarter
+wave data. that is , cosqf computes the coefficients in a cosine
+series representation with only odd wave numbers. the transform
+is defined below at output parameter x
+
+cosqf is the unnormalized inverse of cosqb since a call of cosqf
+followed by a call of cosqb will multiply the input sequence x
+by 4*n.
+
+the array wsave which is used by subroutine cosqf must be
+initialized by calling subroutine cosqi(n,wsave).
+
+
+input parameters
+
+n       the length of the array x to be transformed.  the method
+        is most efficient when n is a product of small primes.
+
+x       an array which contains the sequence to be transformed
+
+wsave   a work array which must be dimensioned at least 3*n+15
+        in the program that calls cosqf. the wsave array must be
+        initialized by calling subroutine cosqi(n,wsave) and a
+        different wsave array must be used for each different
+        value of n. this initialization does not have to be
+        repeated so long as n remains unchanged thus subsequent
+        transforms can be obtained faster than the first.
+
+output parameters
+
+x       for i=1,...,n
+
+             x(i) = x(1) plus the sum from k=2 to k=n of
+
+                2*x(k)*cos((2*i-1)*(k-1)*pi/(2*n))
+
+             a call of cosqf followed by a call of
+             cosqb will multiply the sequence x by 4*n.
+             therefore cosqb is the unnormalized inverse
+             of cosqf.
+
+wsave   contains initialization calculations which must not
+        be destroyed between calls of cosqf or cosqb.
+
+******************************************************************
+
+subroutine cosqb(n,x,wsave)
+
+******************************************************************
+
+subroutine cosqb computes the fast fourier transform of quarter
+wave data. that is , cosqb computes a sequence from its
+representation in terms of a cosine series with odd wave numbers.
+the transform is defined below at output parameter x.
+
+cosqb is the unnormalized inverse of cosqf since a call of cosqb
+followed by a call of cosqf will multiply the input sequence x
+by 4*n.
+
+the array wsave which is used by subroutine cosqb must be
+initialized by calling subroutine cosqi(n,wsave).
+
+
+input parameters
+
+n       the length of the array x to be transformed.  the method
+        is most efficient when n is a product of small primes.
+
+x       an array which contains the sequence to be transformed
+
+wsave   a work array that must be dimensioned at least 3*n+15
+        in the program that calls cosqb. the wsave array must be
+        initialized by calling subroutine cosqi(n,wsave) and a
+        different wsave array must be used for each different
+        value of n. this initialization does not have to be
+        repeated so long as n remains unchanged thus subsequent
+        transforms can be obtained faster than the first.
+
+output parameters
+
+x       for i=1,...,n
+
+             x(i)= the sum from k=1 to k=n of
+
+               4*x(k)*cos((2*k-1)*(i-1)*pi/(2*n))
+
+             a call of cosqb followed by a call of
+             cosqf will multiply the sequence x by 4*n.
+             therefore cosqf is the unnormalized inverse
+             of cosqb.
+
+wsave   contains initialization calculations which must not
+        be destroyed between calls of cosqb or cosqf.
+
+******************************************************************
+
+subroutine cffti(n,wsave)
+
+******************************************************************
+
+subroutine cffti initializes the array wsave which is used in
+both cfftf and cfftb. the prime factorization of n together with
+a tabulation of the trigonometric functions are computed and
+stored in wsave.
+
+input parameter
+
+n       the length of the sequence to be transformed
+
+output parameter
+
+wsave   a work array which must be dimensioned at least 4*n+15
+        the same work array can be used for both cfftf and cfftb
+        as long as n remains unchanged. different wsave arrays
+        are required for different values of n. the contents of
+        wsave must not be changed between calls of cfftf or cfftb.
+
+******************************************************************
+
+subroutine cfftf(n,c,wsave)
+
+******************************************************************
+
+subroutine cfftf computes the forward complex discrete fourier
+transform (the fourier analysis). equivalently , cfftf computes
+the fourier coefficients of a complex periodic sequence.
+the transform is defined below at output parameter c.
+
+the transform is not normalized. to obtain a normalized transform
+the output must be divided by n. otherwise a call of cfftf
+followed by a call of cfftb will multiply the sequence by n.
+
+the array wsave which is used by subroutine cfftf must be
+initialized by calling subroutine cffti(n,wsave).
+
+input parameters
+
+
+n      the length of the complex sequence c. the method is
+       more efficient when n is the product of small primes. n
+
+c      a complex array of length n which contains the sequence
+
+wsave   a real work array which must be dimensioned at least 4n+15
+        in the program that calls cfftf. the wsave array must be
+        initialized by calling subroutine cffti(n,wsave) and a
+        different wsave array must be used for each different
+        value of n. this initialization does not have to be
+        repeated so long as n remains unchanged thus subsequent
+        transforms can be obtained faster than the first.
+        the same wsave array can be used by cfftf and cfftb.
+
+output parameters
+
+c      for j=1,...,n
+
+           c(j)=the sum from k=1,...,n of
+
+                 c(k)*exp(-i*(j-1)*(k-1)*2*pi/n)
+
+                       where i=sqrt(-1)
+
+wsave   contains initialization calculations which must not be
+        destroyed between calls of subroutine cfftf or cfftb
+
+******************************************************************
+
+subroutine cfftb(n,c,wsave)
+
+******************************************************************
+
+subroutine cfftb computes the backward complex discrete fourier
+transform (the fourier synthesis). equivalently , cfftb computes
+a complex periodic sequence from its fourier coefficients.
+the transform is defined below at output parameter c.
+
+a call of cfftf followed by a call of cfftb will multiply the
+sequence by n.
+
+the array wsave which is used by subroutine cfftb must be
+initialized by calling subroutine cffti(n,wsave).
+
+input parameters
+
+
+n      the length of the complex sequence c. the method is
+       more efficient when n is the product of small primes.
+
+c      a complex array of length n which contains the sequence
+
+wsave   a real work array which must be dimensioned at least 4n+15
+        in the program that calls cfftb. the wsave array must be
+        initialized by calling subroutine cffti(n,wsave) and a
+        different wsave array must be used for each different
+        value of n. this initialization does not have to be
+        repeated so long as n remains unchanged thus subsequent
+        transforms can be obtained faster than the first.
+        the same wsave array can be used by cfftf and cfftb.
+
+output parameters
+
+c      for j=1,...,n
+
+           c(j)=the sum from k=1,...,n of
+
+                 c(k)*exp(i*(j-1)*(k-1)*2*pi/n)
+
+                       where i=sqrt(-1)
+
+wsave   contains initialization calculations which must not be
+        destroyed between calls of subroutine cfftf or cfftb
+
+*/
diff --git a/third_party/pffft/src/pffft.c b/third_party/pffft/src/pffft.c
new file mode 100644
index 0000000..0603f2b1
--- /dev/null
+++ b/third_party/pffft/src/pffft.c
@@ -0,0 +1,1881 @@
+/* Copyright (c) 2013  Julien Pommier ( pommier@modartt.com )
+
+   Based on original fortran 77 code from FFTPACKv4 from NETLIB
+   (http://www.netlib.org/fftpack), authored by Dr Paul Swarztrauber
+   of NCAR, in 1985.
+
+   As confirmed by the NCAR fftpack software curators, the following
+   FFTPACKv5 license applies to FFTPACKv4 sources. My changes are
+   released under the same terms.
+
+   FFTPACK license:
+
+   http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html
+
+   Copyright (c) 2004 the University Corporation for Atmospheric
+   Research ("UCAR"). All rights reserved. Developed by NCAR's
+   Computational and Information Systems Laboratory, UCAR,
+   www.cisl.ucar.edu.
+
+   Redistribution and use of the Software in source and binary forms,
+   with or without modification, is permitted provided that the
+   following conditions are met:
+
+   - Neither the names of NCAR's Computational and Information Systems
+   Laboratory, the University Corporation for Atmospheric Research,
+   nor the names of its sponsors or contributors may be used to
+   endorse or promote products derived from this Software without
+   specific prior written permission.  
+
+   - Redistributions of source code must retain the above copyright
+   notices, this list of conditions, and the disclaimer below.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions, and the disclaimer below in the
+   documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
+   HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+   SOFTWARE.
+
+
+   PFFFT : a Pretty Fast FFT.
+
+   This file is largerly based on the original FFTPACK implementation, modified in
+   order to take advantage of SIMD instructions of modern CPUs.
+*/
+
+/*
+  ChangeLog: 
+  - 2011/10/02, version 1: This is the very first release of this file.
+*/
+
+#include "pffft.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+
+/* detect compiler flavour */
+#if defined(_MSC_VER)
+#  define COMPILER_MSVC
+#elif defined(__GNUC__)
+#  define COMPILER_GCC
+#endif
+
+#if defined(COMPILER_GCC)
+#  define ALWAYS_INLINE(return_type) inline return_type __attribute__ ((always_inline))
+#  define NEVER_INLINE(return_type) return_type __attribute__ ((noinline))
+#  define RESTRICT __restrict
+#  define VLA_ARRAY_ON_STACK(type__, varname__, size__) type__ varname__[size__];
+#elif defined(COMPILER_MSVC)
+#  define ALWAYS_INLINE(return_type) __forceinline return_type
+#  define NEVER_INLINE(return_type) __declspec(noinline) return_type
+#  define RESTRICT __restrict
+#  define VLA_ARRAY_ON_STACK(type__, varname__, size__) type__ *varname__ = (type__*)_alloca(size__ * sizeof(type__))
+#endif
+
+
+/* 
+   vector support macros: the rest of the code is independant of
+   SSE/Altivec/NEON -- adding support for other platforms with 4-element
+   vectors should be limited to these macros 
+*/
+
+
+// define PFFFT_SIMD_DISABLE if you want to use scalar code instead of simd code
+//#define PFFFT_SIMD_DISABLE
+
+/*
+   Altivec support macros 
+*/
+#if !defined(PFFFT_SIMD_DISABLE) && (defined(__ppc__) || defined(__ppc64__))
+typedef vector float v4sf;
+#  define SIMD_SZ 4
+#  define VZERO() ((vector float) vec_splat_u8(0))
+#  define VMUL(a,b) vec_madd(a,b, VZERO())
+#  define VADD(a,b) vec_add(a,b)
+#  define VMADD(a,b,c) vec_madd(a,b,c)
+#  define VSUB(a,b) vec_sub(a,b)
+inline v4sf ld_ps1(const float *p) { v4sf v=vec_lde(0,p); return vec_splat(vec_perm(v, v, vec_lvsl(0, p)), 0); }
+#  define LD_PS1(p) ld_ps1(&p)
+#  define INTERLEAVE2(in1, in2, out1, out2) { v4sf tmp__ = vec_mergeh(in1, in2); out2 = vec_mergel(in1, in2); out1 = tmp__; }
+#  define UNINTERLEAVE2(in1, in2, out1, out2) {                           \
+    vector unsigned char vperm1 =  (vector unsigned char)(0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27); \
+    vector unsigned char vperm2 =  (vector unsigned char)(4,5,6,7,12,13,14,15,20,21,22,23,28,29,30,31); \
+    v4sf tmp__ = vec_perm(in1, in2, vperm1); out2 = vec_perm(in1, in2, vperm2); out1 = tmp__; \
+  }
+#  define VTRANSPOSE4(x0,x1,x2,x3) {              \
+    v4sf y0 = vec_mergeh(x0, x2);               \
+    v4sf y1 = vec_mergel(x0, x2);               \
+    v4sf y2 = vec_mergeh(x1, x3);               \
+    v4sf y3 = vec_mergel(x1, x3);               \
+    x0 = vec_mergeh(y0, y2);                    \
+    x1 = vec_mergel(y0, y2);                    \
+    x2 = vec_mergeh(y1, y3);                    \
+    x3 = vec_mergel(y1, y3);                    \
+  }
+#  define VSWAPHL(a,b) vec_perm(a,b, (vector unsigned char)(16,17,18,19,20,21,22,23,8,9,10,11,12,13,14,15))
+#  define VALIGNED(ptr) ((((long)(ptr)) & 0xF) == 0)
+
+/*
+  SSE1 support macros
+*/
+#elif !defined(PFFFT_SIMD_DISABLE) && (defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(_M_IX86))
+
+#include <xmmintrin.h>
+typedef __m128 v4sf;
+#  define SIMD_SZ 4 // 4 floats by simd vector -- this is pretty much hardcoded in the preprocess/finalize functions anyway so you will have to work if you want to enable AVX with its 256-bit vectors.
+#  define VZERO() _mm_setzero_ps()
+#  define VMUL(a,b) _mm_mul_ps(a,b)
+#  define VADD(a,b) _mm_add_ps(a,b)
+#  define VMADD(a,b,c) _mm_add_ps(_mm_mul_ps(a,b), c)
+#  define VSUB(a,b) _mm_sub_ps(a,b)
+#  define LD_PS1(p) _mm_set1_ps(p)
+#  define INTERLEAVE2(in1, in2, out1, out2) { v4sf tmp__ = _mm_unpacklo_ps(in1, in2); out2 = _mm_unpackhi_ps(in1, in2); out1 = tmp__; }
+#  define UNINTERLEAVE2(in1, in2, out1, out2) { v4sf tmp__ = _mm_shuffle_ps(in1, in2, _MM_SHUFFLE(2,0,2,0)); out2 = _mm_shuffle_ps(in1, in2, _MM_SHUFFLE(3,1,3,1)); out1 = tmp__; }
+#  define VTRANSPOSE4(x0,x1,x2,x3) _MM_TRANSPOSE4_PS(x0,x1,x2,x3)
+#  define VSWAPHL(a,b) _mm_shuffle_ps(b, a, _MM_SHUFFLE(3,2,1,0))
+#  define VALIGNED(ptr) ((((long)(ptr)) & 0xF) == 0)
+
+/*
+  ARM NEON support macros
+*/
+#elif !defined(PFFFT_SIMD_DISABLE) && defined(__arm__) 
+#  include <arm_neon.h>
+typedef float32x4_t v4sf;
+#  define SIMD_SZ 4
+#  define VZERO() vdupq_n_f32(0)
+#  define VMUL(a,b) vmulq_f32(a,b)
+#  define VADD(a,b) vaddq_f32(a,b)
+#  define VMADD(a,b,c) vmlaq_f32(c,a,b)
+#  define VSUB(a,b) vsubq_f32(a,b)
+#  define LD_PS1(p) vld1q_dup_f32(&(p))
+#  define INTERLEAVE2(in1, in2, out1, out2) { float32x4x2_t tmp__ = vzipq_f32(in1,in2); out1=tmp__.val[0]; out2=tmp__.val[1]; }
+#  define UNINTERLEAVE2(in1, in2, out1, out2) { float32x4x2_t tmp__ = vuzpq_f32(in1,in2); out1=tmp__.val[0]; out2=tmp__.val[1]; }
+#  define VTRANSPOSE4(x0,x1,x2,x3) {                                    \
+    float32x4x2_t t0_ = vzipq_f32(x0, x2);                              \
+    float32x4x2_t t1_ = vzipq_f32(x1, x3);                              \
+    float32x4x2_t u0_ = vzipq_f32(t0_.val[0], t1_.val[0]);              \
+    float32x4x2_t u1_ = vzipq_f32(t0_.val[1], t1_.val[1]);              \
+    x0 = u0_.val[0]; x1 = u0_.val[1]; x2 = u1_.val[0]; x3 = u1_.val[1]; \
+  }
+// marginally faster version
+//#  define VTRANSPOSE4(x0,x1,x2,x3) { asm("vtrn.32 %q0, %q1;\n vtrn.32 %q2,%q3\n vswp %f0,%e2\n vswp %f1,%e3" : "+w"(x0), "+w"(x1), "+w"(x2), "+w"(x3)::); }
+#  define VSWAPHL(a,b) vcombine_f32(vget_low_f32(b), vget_high_f32(a))
+#  define VALIGNED(ptr) ((((long)(ptr)) & 0x3) == 0)
+#else
+#  if !defined(PFFFT_SIMD_DISABLE)
+#    warning "building with simd disabled !\n";
+#    define PFFFT_SIMD_DISABLE // fallback to scalar code
+#  endif
+#endif
+
+// fallback mode for situations where SSE/Altivec are not available, use scalar mode instead
+#ifdef PFFFT_SIMD_DISABLE
+typedef float v4sf;
+#  define SIMD_SZ 1
+#  define VZERO() 0.f
+#  define VMUL(a,b) ((a)*(b))
+#  define VADD(a,b) ((a)+(b))
+#  define VMADD(a,b,c) ((a)*(b)+(c))
+#  define VSUB(a,b) ((a)-(b))
+#  define LD_PS1(p) (p)
+#  define VALIGNED(ptr) ((((long)(ptr)) & 0x3) == 0)
+#endif
+
+// shortcuts for complex multiplcations
+#define VCPLXMUL(ar,ai,br,bi) { v4sf tmp; tmp=VMUL(ar,bi); ar=VMUL(ar,br); ar=VSUB(ar,VMUL(ai,bi)); ai=VMUL(ai,br); ai=VADD(ai,tmp); }
+#define VCPLXMULCONJ(ar,ai,br,bi) { v4sf tmp; tmp=VMUL(ar,bi); ar=VMUL(ar,br); ar=VADD(ar,VMUL(ai,bi)); ai=VMUL(ai,br); ai=VSUB(ai,tmp); }
+#ifndef SVMUL
+// multiply a scalar with a vector
+#define SVMUL(f,v) VMUL(LD_PS1(f),v)
+#endif
+
+#if !defined(PFFFT_SIMD_DISABLE)
+typedef union v4sf_union {
+  v4sf  v;
+  float f[4];
+} v4sf_union;
+
+#include <string.h>
+
+#define assertv4(v,f0,f1,f2,f3) assert(v.f[0] == (f0) && v.f[1] == (f1) && v.f[2] == (f2) && v.f[3] == (f3))
+
+/* detect bugs with the vector support macros */
+void validate_pffft_simd() {
+  float f[16] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
+  v4sf_union a0, a1, a2, a3, t, u; 
+  memcpy(a0.f, f, 4*sizeof(float));
+  memcpy(a1.f, f+4, 4*sizeof(float));
+  memcpy(a2.f, f+8, 4*sizeof(float));
+  memcpy(a3.f, f+12, 4*sizeof(float));
+
+  t = a0; u = a1; t.v = VZERO();
+  printf("VZERO=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 0, 0, 0, 0);
+  t.v = VADD(a1.v, a2.v);
+  printf("VADD(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 12, 14, 16, 18);
+  t.v = VMUL(a1.v, a2.v);
+  printf("VMUL(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 32, 45, 60, 77);
+  t.v = VMADD(a1.v, a2.v,a0.v);
+  printf("VMADD(4:7,8:11,0:3)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 32, 46, 62, 80);
+
+  INTERLEAVE2(a1.v,a2.v,t.v,u.v);
+  printf("INTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3], u.f[0], u.f[1], u.f[2], u.f[3]);
+  assertv4(t, 4, 8, 5, 9); assertv4(u, 6, 10, 7, 11);
+  UNINTERLEAVE2(a1.v,a2.v,t.v,u.v);
+  printf("UNINTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3], u.f[0], u.f[1], u.f[2], u.f[3]);
+  assertv4(t, 4, 6, 8, 10); assertv4(u, 5, 7, 9, 11);
+
+  t.v=LD_PS1(f[15]);
+  printf("LD_PS1(15)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
+  assertv4(t, 15, 15, 15, 15);
+  t.v = VSWAPHL(a1.v, a2.v);
+  printf("VSWAPHL(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
+  assertv4(t, 8, 9, 6, 7);
+  VTRANSPOSE4(a0.v, a1.v, a2.v, a3.v);
+  printf("VTRANSPOSE4(0:3,4:7,8:11,12:15)=[%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", 
+         a0.f[0], a0.f[1], a0.f[2], a0.f[3], a1.f[0], a1.f[1], a1.f[2], a1.f[3], 
+         a2.f[0], a2.f[1], a2.f[2], a2.f[3], a3.f[0], a3.f[1], a3.f[2], a3.f[3]); 
+  assertv4(a0, 0, 4, 8, 12); assertv4(a1, 1, 5, 9, 13); assertv4(a2, 2, 6, 10, 14); assertv4(a3, 3, 7, 11, 15);
+}
+#endif //!PFFFT_SIMD_DISABLE
+
+/* SSE and co like 16-bytes aligned pointers */
+#define MALLOC_V4SF_ALIGNMENT 64 // with a 64-byte alignment, we are even aligned on L2 cache lines...
+void *pffft_aligned_malloc(size_t nb_bytes) {
+  void *p, *p0 = malloc(nb_bytes + MALLOC_V4SF_ALIGNMENT);
+  if (!p0) return (void *) 0;
+  p = (void *) (((size_t) p0 + MALLOC_V4SF_ALIGNMENT) & (~((size_t) (MALLOC_V4SF_ALIGNMENT-1))));
+  *((void **) p - 1) = p0;
+  return p;
+}
+
+void pffft_aligned_free(void *p) {
+  if (p) free(*((void **) p - 1));
+}
+
+int pffft_simd_size() { return SIMD_SZ; }
+
+/*
+  passf2 and passb2 has been merged here, fsign = -1 for passf2, +1 for passb2
+*/
+static NEVER_INLINE(void) passf2_ps(int ido, int l1, const v4sf *cc, v4sf *ch, const float *wa1, float fsign) {
+  int k, i;
+  int l1ido = l1*ido;
+  if (ido <= 2) {
+    for (k=0; k < l1ido; k += ido, ch += ido, cc+= 2*ido) {
+      ch[0]         = VADD(cc[0], cc[ido+0]);
+      ch[l1ido]     = VSUB(cc[0], cc[ido+0]);
+      ch[1]         = VADD(cc[1], cc[ido+1]);
+      ch[l1ido + 1] = VSUB(cc[1], cc[ido+1]);
+    }
+  } else {
+    for (k=0; k < l1ido; k += ido, ch += ido, cc += 2*ido) {
+      for (i=0; i<ido-1; i+=2) {
+        v4sf tr2 = VSUB(cc[i+0], cc[i+ido+0]);
+        v4sf ti2 = VSUB(cc[i+1], cc[i+ido+1]);
+        v4sf wr = LD_PS1(wa1[i]), wi = VMUL(LD_PS1(fsign), LD_PS1(wa1[i+1]));
+        ch[i]   = VADD(cc[i+0], cc[i+ido+0]);
+        ch[i+1] = VADD(cc[i+1], cc[i+ido+1]);
+        VCPLXMUL(tr2, ti2, wr, wi);
+        ch[i+l1ido]   = tr2;
+        ch[i+l1ido+1] = ti2;
+      }
+    }
+  }
+}
+
+/*
+  passf3 and passb3 has been merged here, fsign = -1 for passf3, +1 for passb3
+*/
+static NEVER_INLINE(void) passf3_ps(int ido, int l1, const v4sf *cc, v4sf *ch,
+                                    const float *wa1, const float *wa2, float fsign) {
+  static const float taur = -0.5f;
+  float taui = 0.866025403784439f*fsign;
+  int i, k;
+  v4sf tr2, ti2, cr2, ci2, cr3, ci3, dr2, di2, dr3, di3;
+  int l1ido = l1*ido;
+  float wr1, wi1, wr2, wi2;
+  assert(ido > 2);
+  for (k=0; k< l1ido; k += ido, cc+= 3*ido, ch +=ido) {
+    for (i=0; i<ido-1; i+=2) {
+      tr2 = VADD(cc[i+ido], cc[i+2*ido]);
+      cr2 = VADD(cc[i], SVMUL(taur,tr2));
+      ch[i]    = VADD(cc[i], tr2);
+      ti2 = VADD(cc[i+ido+1], cc[i+2*ido+1]);
+      ci2 = VADD(cc[i    +1], SVMUL(taur,ti2));
+      ch[i+1]  = VADD(cc[i+1], ti2);
+      cr3 = SVMUL(taui, VSUB(cc[i+ido], cc[i+2*ido]));
+      ci3 = SVMUL(taui, VSUB(cc[i+ido+1], cc[i+2*ido+1]));
+      dr2 = VSUB(cr2, ci3);
+      dr3 = VADD(cr2, ci3);
+      di2 = VADD(ci2, cr3);
+      di3 = VSUB(ci2, cr3);
+      wr1=wa1[i], wi1=fsign*wa1[i+1], wr2=wa2[i], wi2=fsign*wa2[i+1]; 
+      VCPLXMUL(dr2, di2, LD_PS1(wr1), LD_PS1(wi1));
+      ch[i+l1ido] = dr2; 
+      ch[i+l1ido + 1] = di2;
+      VCPLXMUL(dr3, di3, LD_PS1(wr2), LD_PS1(wi2));
+      ch[i+2*l1ido] = dr3;
+      ch[i+2*l1ido+1] = di3;
+    }
+  }
+} /* passf3 */
+
+static NEVER_INLINE(void) passf4_ps(int ido, int l1, const v4sf *cc, v4sf *ch,
+                                    const float *wa1, const float *wa2, const float *wa3, float fsign) {
+  /* isign == -1 for forward transform and +1 for backward transform */
+
+  int i, k;
+  v4sf ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;
+  int l1ido = l1*ido;
+  if (ido == 2) {
+    for (k=0; k < l1ido; k += ido, ch += ido, cc += 4*ido) {
+      tr1 = VSUB(cc[0], cc[2*ido + 0]);
+      tr2 = VADD(cc[0], cc[2*ido + 0]);
+      ti1 = VSUB(cc[1], cc[2*ido + 1]);
+      ti2 = VADD(cc[1], cc[2*ido + 1]);
+      ti4 = VMUL(VSUB(cc[1*ido + 0], cc[3*ido + 0]), LD_PS1(fsign));
+      tr4 = VMUL(VSUB(cc[3*ido + 1], cc[1*ido + 1]), LD_PS1(fsign));
+      tr3 = VADD(cc[ido + 0], cc[3*ido + 0]);
+      ti3 = VADD(cc[ido + 1], cc[3*ido + 1]);
+
+      ch[0*l1ido + 0] = VADD(tr2, tr3);
+      ch[0*l1ido + 1] = VADD(ti2, ti3);
+      ch[1*l1ido + 0] = VADD(tr1, tr4);
+      ch[1*l1ido + 1] = VADD(ti1, ti4);
+      ch[2*l1ido + 0] = VSUB(tr2, tr3);
+      ch[2*l1ido + 1] = VSUB(ti2, ti3);        
+      ch[3*l1ido + 0] = VSUB(tr1, tr4);
+      ch[3*l1ido + 1] = VSUB(ti1, ti4);
+    }
+  } else {
+    for (k=0; k < l1ido; k += ido, ch+=ido, cc += 4*ido) {
+      for (i=0; i<ido-1; i+=2) {
+        float wr1, wi1, wr2, wi2, wr3, wi3;
+        tr1 = VSUB(cc[i + 0], cc[i + 2*ido + 0]);
+        tr2 = VADD(cc[i + 0], cc[i + 2*ido + 0]);
+        ti1 = VSUB(cc[i + 1], cc[i + 2*ido + 1]);
+        ti2 = VADD(cc[i + 1], cc[i + 2*ido + 1]);
+        tr4 = VMUL(VSUB(cc[i + 3*ido + 1], cc[i + 1*ido + 1]), LD_PS1(fsign));
+        ti4 = VMUL(VSUB(cc[i + 1*ido + 0], cc[i + 3*ido + 0]), LD_PS1(fsign));
+        tr3 = VADD(cc[i + ido + 0], cc[i + 3*ido + 0]);
+        ti3 = VADD(cc[i + ido + 1], cc[i + 3*ido + 1]);
+
+        ch[i] = VADD(tr2, tr3);
+        cr3    = VSUB(tr2, tr3);
+        ch[i + 1] = VADD(ti2, ti3);
+        ci3 = VSUB(ti2, ti3);
+
+        cr2 = VADD(tr1, tr4);
+        cr4 = VSUB(tr1, tr4);
+        ci2 = VADD(ti1, ti4);
+        ci4 = VSUB(ti1, ti4);
+        wr1=wa1[i], wi1=fsign*wa1[i+1];
+        VCPLXMUL(cr2, ci2, LD_PS1(wr1), LD_PS1(wi1));
+        wr2=wa2[i], wi2=fsign*wa2[i+1]; 
+        ch[i + l1ido] = cr2;
+        ch[i + l1ido + 1] = ci2;
+
+        VCPLXMUL(cr3, ci3, LD_PS1(wr2), LD_PS1(wi2));
+        wr3=wa3[i], wi3=fsign*wa3[i+1]; 
+        ch[i + 2*l1ido] = cr3;
+        ch[i + 2*l1ido + 1] = ci3;
+
+        VCPLXMUL(cr4, ci4, LD_PS1(wr3), LD_PS1(wi3));
+        ch[i + 3*l1ido] = cr4;
+        ch[i + 3*l1ido + 1] = ci4;
+      }
+    }
+  }
+} /* passf4 */
+
+/*
+  passf5 and passb5 has been merged here, fsign = -1 for passf5, +1 for passb5
+*/
+static NEVER_INLINE(void) passf5_ps(int ido, int l1, const v4sf *cc, v4sf *ch,
+                                    const float *wa1, const float *wa2, 
+                                    const float *wa3, const float *wa4, float fsign) {  
+  static const float tr11 = .309016994374947f;
+  const float ti11 = .951056516295154f*fsign;
+  static const float tr12 = -.809016994374947f;
+  const float ti12 = .587785252292473f*fsign;
+
+  /* Local variables */
+  int i, k;
+  v4sf ci2, ci3, ci4, ci5, di3, di4, di5, di2, cr2, cr3, cr5, cr4, ti2, ti3,
+    ti4, ti5, dr3, dr4, dr5, dr2, tr2, tr3, tr4, tr5;
+
+  float wr1, wi1, wr2, wi2, wr3, wi3, wr4, wi4;
+
+#define cc_ref(a_1,a_2) cc[(a_2-1)*ido + a_1 + 1]
+#define ch_ref(a_1,a_3) ch[(a_3-1)*l1*ido + a_1 + 1]
+
+  assert(ido > 2);
+  for (k = 0; k < l1; ++k, cc += 5*ido, ch += ido) {
+    for (i = 0; i < ido-1; i += 2) {
+      ti5 = VSUB(cc_ref(i  , 2), cc_ref(i  , 5));
+      ti2 = VADD(cc_ref(i  , 2), cc_ref(i  , 5));
+      ti4 = VSUB(cc_ref(i  , 3), cc_ref(i  , 4));
+      ti3 = VADD(cc_ref(i  , 3), cc_ref(i  , 4));
+      tr5 = VSUB(cc_ref(i-1, 2), cc_ref(i-1, 5));
+      tr2 = VADD(cc_ref(i-1, 2), cc_ref(i-1, 5));
+      tr4 = VSUB(cc_ref(i-1, 3), cc_ref(i-1, 4));
+      tr3 = VADD(cc_ref(i-1, 3), cc_ref(i-1, 4));
+      ch_ref(i-1, 1) = VADD(cc_ref(i-1, 1), VADD(tr2, tr3));
+      ch_ref(i  , 1) = VADD(cc_ref(i  , 1), VADD(ti2, ti3));
+      cr2 = VADD(cc_ref(i-1, 1), VADD(SVMUL(tr11, tr2),SVMUL(tr12, tr3)));
+      ci2 = VADD(cc_ref(i  , 1), VADD(SVMUL(tr11, ti2),SVMUL(tr12, ti3)));
+      cr3 = VADD(cc_ref(i-1, 1), VADD(SVMUL(tr12, tr2),SVMUL(tr11, tr3)));
+      ci3 = VADD(cc_ref(i  , 1), VADD(SVMUL(tr12, ti2),SVMUL(tr11, ti3)));
+      cr5 = VADD(SVMUL(ti11, tr5), SVMUL(ti12, tr4));
+      ci5 = VADD(SVMUL(ti11, ti5), SVMUL(ti12, ti4));
+      cr4 = VSUB(SVMUL(ti12, tr5), SVMUL(ti11, tr4));
+      ci4 = VSUB(SVMUL(ti12, ti5), SVMUL(ti11, ti4));
+      dr3 = VSUB(cr3, ci4);
+      dr4 = VADD(cr3, ci4);
+      di3 = VADD(ci3, cr4);
+      di4 = VSUB(ci3, cr4);
+      dr5 = VADD(cr2, ci5);
+      dr2 = VSUB(cr2, ci5);
+      di5 = VSUB(ci2, cr5);
+      di2 = VADD(ci2, cr5);
+      wr1=wa1[i], wi1=fsign*wa1[i+1], wr2=wa2[i], wi2=fsign*wa2[i+1]; 
+      wr3=wa3[i], wi3=fsign*wa3[i+1], wr4=wa4[i], wi4=fsign*wa4[i+1]; 
+      VCPLXMUL(dr2, di2, LD_PS1(wr1), LD_PS1(wi1));
+      ch_ref(i - 1, 2) = dr2;
+      ch_ref(i, 2)     = di2;
+      VCPLXMUL(dr3, di3, LD_PS1(wr2), LD_PS1(wi2));
+      ch_ref(i - 1, 3) = dr3;
+      ch_ref(i, 3)     = di3;
+      VCPLXMUL(dr4, di4, LD_PS1(wr3), LD_PS1(wi3));
+      ch_ref(i - 1, 4) = dr4;
+      ch_ref(i, 4)     = di4;
+      VCPLXMUL(dr5, di5, LD_PS1(wr4), LD_PS1(wi4));
+      ch_ref(i - 1, 5) = dr5;
+      ch_ref(i, 5)     = di5;
+    }
+  }
+#undef ch_ref
+#undef cc_ref
+}
+
+static NEVER_INLINE(void) radf2_ps(int ido, int l1, const v4sf * RESTRICT cc, v4sf * RESTRICT ch, const float *wa1) {
+  static const float minus_one = -1.f;
+  int i, k, l1ido = l1*ido;
+  for (k=0; k < l1ido; k += ido) {
+    v4sf a = cc[k], b = cc[k + l1ido];
+    ch[2*k] = VADD(a, b);
+    ch[2*(k+ido)-1] = VSUB(a, b);
+  }
+  if (ido < 2) return;
+  if (ido != 2) {
+    for (k=0; k < l1ido; k += ido) {
+      for (i=2; i<ido; i+=2) {
+        v4sf tr2 = cc[i - 1 + k + l1ido], ti2 = cc[i + k + l1ido];
+        v4sf br = cc[i - 1 + k], bi = cc[i + k];
+        VCPLXMULCONJ(tr2, ti2, LD_PS1(wa1[i - 2]), LD_PS1(wa1[i - 1])); 
+        ch[i + 2*k] = VADD(bi, ti2);
+        ch[2*(k+ido) - i] = VSUB(ti2, bi);
+        ch[i - 1 + 2*k] = VADD(br, tr2);
+        ch[2*(k+ido) - i -1] = VSUB(br, tr2);
+      }
+    }
+    if (ido % 2 == 1) return;
+  }
+  for (k=0; k < l1ido; k += ido) {
+    ch[2*k + ido] = SVMUL(minus_one, cc[ido-1 + k + l1ido]);
+    ch[2*k + ido-1] = cc[k + ido-1];
+  }
+} /* radf2 */
+
+
+static NEVER_INLINE(void) radb2_ps(int ido, int l1, const v4sf *cc, v4sf *ch, const float *wa1) {
+  static const float minus_two=-2;
+  int i, k, l1ido = l1*ido;
+  v4sf a,b,c,d, tr2, ti2;
+  for (k=0; k < l1ido; k += ido) {
+    a = cc[2*k]; b = cc[2*(k+ido) - 1];
+    ch[k] = VADD(a, b);
+    ch[k + l1ido] =VSUB(a, b);
+  }
+  if (ido < 2) return;
+  if (ido != 2) {
+    for (k = 0; k < l1ido; k += ido) {
+      for (i = 2; i < ido; i += 2) {
+        a = cc[i-1 + 2*k]; b = cc[2*(k + ido) - i - 1];
+        c = cc[i+0 + 2*k]; d = cc[2*(k + ido) - i + 0];
+        ch[i-1 + k] = VADD(a, b);
+        tr2 = VSUB(a, b);
+        ch[i+0 + k] = VSUB(c, d);
+        ti2 = VADD(c, d);
+        VCPLXMUL(tr2, ti2, LD_PS1(wa1[i - 2]), LD_PS1(wa1[i - 1]));
+        ch[i-1 + k + l1ido] = tr2;
+        ch[i+0 + k + l1ido] = ti2;
+      }
+    }
+    if (ido % 2 == 1) return;
+  }
+  for (k = 0; k < l1ido; k += ido) {
+    a = cc[2*k + ido-1]; b = cc[2*k + ido];
+    ch[k + ido-1] = VADD(a,a);
+    ch[k + ido-1 + l1ido] = SVMUL(minus_two, b);
+  }
+} /* radb2 */
+
+static void radf3_ps(int ido, int l1, const v4sf * RESTRICT cc, v4sf * RESTRICT ch,
+                     const float *wa1, const float *wa2) {
+  static const float taur = -0.5f;
+  static const float taui = 0.866025403784439f;
+  int i, k, ic;
+  v4sf ci2, di2, di3, cr2, dr2, dr3, ti2, ti3, tr2, tr3, wr1, wi1, wr2, wi2;
+  for (k=0; k<l1; k++) {
+    cr2 = VADD(cc[(k + l1)*ido], cc[(k + 2*l1)*ido]);
+    ch[3*k*ido] = VADD(cc[k*ido], cr2);
+    ch[(3*k+2)*ido] = SVMUL(taui, VSUB(cc[(k + l1*2)*ido], cc[(k + l1)*ido]));
+    ch[ido-1 + (3*k + 1)*ido] = VADD(cc[k*ido], SVMUL(taur, cr2));
+  }
+  if (ido == 1) return;
+  for (k=0; k<l1; k++) {
+    for (i=2; i<ido; i+=2) {
+      ic = ido - i;
+      wr1 = LD_PS1(wa1[i - 2]); wi1 = LD_PS1(wa1[i - 1]);
+      dr2 = cc[i - 1 + (k + l1)*ido]; di2 = cc[i + (k + l1)*ido];
+      VCPLXMULCONJ(dr2, di2, wr1, wi1);
+
+      wr2 = LD_PS1(wa2[i - 2]); wi2 = LD_PS1(wa2[i - 1]);
+      dr3 = cc[i - 1 + (k + l1*2)*ido]; di3 = cc[i + (k + l1*2)*ido];
+      VCPLXMULCONJ(dr3, di3, wr2, wi2);
+        
+      cr2 = VADD(dr2, dr3);
+      ci2 = VADD(di2, di3);
+      ch[i - 1 + 3*k*ido] = VADD(cc[i - 1 + k*ido], cr2);
+      ch[i + 3*k*ido] = VADD(cc[i + k*ido], ci2);
+      tr2 = VADD(cc[i - 1 + k*ido], SVMUL(taur, cr2));
+      ti2 = VADD(cc[i + k*ido], SVMUL(taur, ci2));
+      tr3 = SVMUL(taui, VSUB(di2, di3));
+      ti3 = SVMUL(taui, VSUB(dr3, dr2));
+      ch[i - 1 + (3*k + 2)*ido] = VADD(tr2, tr3);
+      ch[ic - 1 + (3*k + 1)*ido] = VSUB(tr2, tr3);
+      ch[i + (3*k + 2)*ido] = VADD(ti2, ti3);
+      ch[ic + (3*k + 1)*ido] = VSUB(ti3, ti2);
+    }
+  }
+} /* radf3 */
+
+
+static void radb3_ps(int ido, int l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch,
+                     const float *wa1, const float *wa2)
+{
+  static const float taur = -0.5f;
+  static const float taui = 0.866025403784439f;
+  static const float taui_2 = 0.866025403784439f*2;
+  int i, k, ic;
+  v4sf ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2;
+  for (k=0; k<l1; k++) {
+    tr2 = cc[ido-1 + (3*k + 1)*ido]; tr2 = VADD(tr2,tr2);
+    cr2 = VMADD(LD_PS1(taur), tr2, cc[3*k*ido]);
+    ch[k*ido] = VADD(cc[3*k*ido], tr2);
+    ci3 = SVMUL(taui_2, cc[(3*k + 2)*ido]);
+    ch[(k + l1)*ido] = VSUB(cr2, ci3);
+    ch[(k + 2*l1)*ido] = VADD(cr2, ci3);
+  }
+  if (ido == 1) return;
+  for (k=0; k<l1; k++) {
+    for (i=2; i<ido; i+=2) {
+      ic = ido - i;
+      tr2 = VADD(cc[i - 1 + (3*k + 2)*ido], cc[ic - 1 + (3*k + 1)*ido]);
+      cr2 = VMADD(LD_PS1(taur), tr2, cc[i - 1 + 3*k*ido]);
+      ch[i - 1 + k*ido] = VADD(cc[i - 1 + 3*k*ido], tr2);
+      ti2 = VSUB(cc[i + (3*k + 2)*ido], cc[ic + (3*k + 1)*ido]);
+      ci2 = VMADD(LD_PS1(taur), ti2, cc[i + 3*k*ido]);
+      ch[i + k*ido] = VADD(cc[i + 3*k*ido], ti2);
+      cr3 = SVMUL(taui, VSUB(cc[i - 1 + (3*k + 2)*ido], cc[ic - 1 + (3*k + 1)*ido]));
+      ci3 = SVMUL(taui, VADD(cc[i + (3*k + 2)*ido], cc[ic + (3*k + 1)*ido]));
+      dr2 = VSUB(cr2, ci3);
+      dr3 = VADD(cr2, ci3);
+      di2 = VADD(ci2, cr3);
+      di3 = VSUB(ci2, cr3);
+      VCPLXMUL(dr2, di2, LD_PS1(wa1[i-2]), LD_PS1(wa1[i-1]));
+      ch[i - 1 + (k + l1)*ido] = dr2;
+      ch[i + (k + l1)*ido] = di2;
+      VCPLXMUL(dr3, di3, LD_PS1(wa2[i-2]), LD_PS1(wa2[i-1]));
+      ch[i - 1 + (k + 2*l1)*ido] = dr3;
+      ch[i + (k + 2*l1)*ido] = di3;
+    }
+  }
+} /* radb3 */
+
+static NEVER_INLINE(void) radf4_ps(int ido, int l1, const v4sf *RESTRICT cc, v4sf * RESTRICT ch,
+                                   const float * RESTRICT wa1, const float * RESTRICT wa2, const float * RESTRICT wa3)
+{
+  static const float minus_hsqt2 = (float)-0.7071067811865475;
+  int i, k, l1ido = l1*ido;
+  {
+    const v4sf *RESTRICT cc_ = cc, * RESTRICT cc_end = cc + l1ido; 
+    v4sf * RESTRICT ch_ = ch;
+    while (cc < cc_end) {
+      // this loop represents between 25% and 40% of total radf4_ps cost !
+      v4sf a0 = cc[0], a1 = cc[l1ido];
+      v4sf a2 = cc[2*l1ido], a3 = cc[3*l1ido];
+      v4sf tr1 = VADD(a1, a3);
+      v4sf tr2 = VADD(a0, a2);
+      ch[2*ido-1] = VSUB(a0, a2);
+      ch[2*ido  ] = VSUB(a3, a1);
+      ch[0      ] = VADD(tr1, tr2);
+      ch[4*ido-1] = VSUB(tr2, tr1);
+      cc += ido; ch += 4*ido;
+    }
+    cc = cc_; ch = ch_;
+  }
+  if (ido < 2) return;
+  if (ido != 2) {
+    for (k = 0; k < l1ido; k += ido) {
+      const v4sf * RESTRICT pc = (v4sf*)(cc + 1 + k);
+      for (i=2; i<ido; i += 2, pc += 2) {
+        int ic = ido - i;
+        v4sf wr, wi, cr2, ci2, cr3, ci3, cr4, ci4;
+        v4sf tr1, ti1, tr2, ti2, tr3, ti3, tr4, ti4;
+
+        cr2 = pc[1*l1ido+0];
+        ci2 = pc[1*l1ido+1];
+        wr=LD_PS1(wa1[i - 2]);
+        wi=LD_PS1(wa1[i - 1]);
+        VCPLXMULCONJ(cr2,ci2,wr,wi);
+
+        cr3 = pc[2*l1ido+0];
+        ci3 = pc[2*l1ido+1];
+        wr = LD_PS1(wa2[i-2]); 
+        wi = LD_PS1(wa2[i-1]);
+        VCPLXMULCONJ(cr3, ci3, wr, wi);
+
+        cr4 = pc[3*l1ido];
+        ci4 = pc[3*l1ido+1];
+        wr = LD_PS1(wa3[i-2]); 
+        wi = LD_PS1(wa3[i-1]);
+        VCPLXMULCONJ(cr4, ci4, wr, wi);
+
+        /* at this point, on SSE, five of "cr2 cr3 cr4 ci2 ci3 ci4" should be loaded in registers */
+
+        tr1 = VADD(cr2,cr4);
+        tr4 = VSUB(cr4,cr2); 
+        tr2 = VADD(pc[0],cr3);
+        tr3 = VSUB(pc[0],cr3);
+        ch[i - 1 + 4*k] = VADD(tr1,tr2);
+        ch[ic - 1 + 4*k + 3*ido] = VSUB(tr2,tr1); // at this point tr1 and tr2 can be disposed
+        ti1 = VADD(ci2,ci4);
+        ti4 = VSUB(ci2,ci4);
+        ch[i - 1 + 4*k + 2*ido] = VADD(ti4,tr3);
+        ch[ic - 1 + 4*k + 1*ido] = VSUB(tr3,ti4); // dispose tr3, ti4
+        ti2 = VADD(pc[1],ci3);
+        ti3 = VSUB(pc[1],ci3);
+        ch[i + 4*k] = VADD(ti1, ti2);
+        ch[ic + 4*k + 3*ido] = VSUB(ti1, ti2);
+        ch[i + 4*k + 2*ido] = VADD(tr4, ti3);
+        ch[ic + 4*k + 1*ido] = VSUB(tr4, ti3);
+      }
+    }
+    if (ido % 2 == 1) return;
+  }
+  for (k=0; k<l1ido; k += ido) {
+    v4sf a = cc[ido-1 + k + l1ido], b = cc[ido-1 + k + 3*l1ido];
+    v4sf c = cc[ido-1 + k], d = cc[ido-1 + k + 2*l1ido];
+    v4sf ti1 = SVMUL(minus_hsqt2, VADD(a, b));
+    v4sf tr1 = SVMUL(minus_hsqt2, VSUB(b, a));
+    ch[ido-1 + 4*k] = VADD(tr1, c);
+    ch[ido-1 + 4*k + 2*ido] = VSUB(c, tr1);
+    ch[4*k + 1*ido] = VSUB(ti1, d); 
+    ch[4*k + 3*ido] = VADD(ti1, d); 
+  }
+} /* radf4 */
+
+
+static NEVER_INLINE(void) radb4_ps(int ido, int l1, const v4sf * RESTRICT cc, v4sf * RESTRICT ch,
+                                   const float * RESTRICT wa1, const float * RESTRICT wa2, const float *RESTRICT wa3)
+{
+  static const float minus_sqrt2 = (float)-1.414213562373095;
+  static const float two = 2.f;
+  int i, k, l1ido = l1*ido;
+  v4sf ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;
+  {
+    const v4sf *RESTRICT cc_ = cc, * RESTRICT ch_end = ch + l1ido; 
+    v4sf *ch_ = ch;
+    while (ch < ch_end) {
+      v4sf a = cc[0], b = cc[4*ido-1];
+      v4sf c = cc[2*ido], d = cc[2*ido-1];
+      tr3 = SVMUL(two,d);
+      tr2 = VADD(a,b);
+      tr1 = VSUB(a,b);
+      tr4 = SVMUL(two,c);
+      ch[0*l1ido] = VADD(tr2, tr3);
+      ch[2*l1ido] = VSUB(tr2, tr3);
+      ch[1*l1ido] = VSUB(tr1, tr4);
+      ch[3*l1ido] = VADD(tr1, tr4);
+      
+      cc += 4*ido; ch += ido;
+    }
+    cc = cc_; ch = ch_;
+  }
+  if (ido < 2) return;
+  if (ido != 2) {
+    for (k = 0; k < l1ido; k += ido) {
+      const v4sf * RESTRICT pc = (v4sf*)(cc - 1 + 4*k);
+      v4sf * RESTRICT ph = (v4sf*)(ch + k + 1);
+      for (i = 2; i < ido; i += 2) {
+
+        tr1 = VSUB(pc[i], pc[4*ido - i]);
+        tr2 = VADD(pc[i], pc[4*ido - i]);
+        ti4 = VSUB(pc[2*ido + i], pc[2*ido - i]);
+        tr3 = VADD(pc[2*ido + i], pc[2*ido - i]);
+        ph[0] = VADD(tr2, tr3);
+        cr3 = VSUB(tr2, tr3);
+
+        ti3 = VSUB(pc[2*ido + i + 1], pc[2*ido - i + 1]);
+        tr4 = VADD(pc[2*ido + i + 1], pc[2*ido - i + 1]);
+        cr2 = VSUB(tr1, tr4);
+        cr4 = VADD(tr1, tr4);
+
+        ti1 = VADD(pc[i + 1], pc[4*ido - i + 1]);
+        ti2 = VSUB(pc[i + 1], pc[4*ido - i + 1]);
+
+        ph[1] = VADD(ti2, ti3); ph += l1ido;
+        ci3 = VSUB(ti2, ti3);
+        ci2 = VADD(ti1, ti4);
+        ci4 = VSUB(ti1, ti4);
+        VCPLXMUL(cr2, ci2, LD_PS1(wa1[i-2]), LD_PS1(wa1[i-1]));
+        ph[0] = cr2;
+        ph[1] = ci2; ph += l1ido;
+        VCPLXMUL(cr3, ci3, LD_PS1(wa2[i-2]), LD_PS1(wa2[i-1]));
+        ph[0] = cr3;
+        ph[1] = ci3; ph += l1ido;
+        VCPLXMUL(cr4, ci4, LD_PS1(wa3[i-2]), LD_PS1(wa3[i-1]));
+        ph[0] = cr4;
+        ph[1] = ci4; ph = ph - 3*l1ido + 2;
+      }
+    }
+    if (ido % 2 == 1) return;
+  }
+  for (k=0; k < l1ido; k+=ido) {
+    int i0 = 4*k + ido;
+    v4sf c = cc[i0-1], d = cc[i0 + 2*ido-1];
+    v4sf a = cc[i0+0], b = cc[i0 + 2*ido+0];
+    tr1 = VSUB(c,d);
+    tr2 = VADD(c,d);
+    ti1 = VADD(b,a);
+    ti2 = VSUB(b,a);
+    ch[ido-1 + k + 0*l1ido] = VADD(tr2,tr2);
+    ch[ido-1 + k + 1*l1ido] = SVMUL(minus_sqrt2, VSUB(ti1, tr1));
+    ch[ido-1 + k + 2*l1ido] = VADD(ti2, ti2);
+    ch[ido-1 + k + 3*l1ido] = SVMUL(minus_sqrt2, VADD(ti1, tr1));
+  }
+} /* radb4 */
+
+static void radf5_ps(int ido, int l1, const v4sf * RESTRICT cc, v4sf * RESTRICT ch, 
+                     const float *wa1, const float *wa2, const float *wa3, const float *wa4)
+{
+  static const float tr11 = .309016994374947f;
+  static const float ti11 = .951056516295154f;
+  static const float tr12 = -.809016994374947f;
+  static const float ti12 = .587785252292473f;
+
+  /* System generated locals */
+  int cc_offset, ch_offset;
+
+  /* Local variables */
+  int i, k, ic;
+  v4sf ci2, di2, ci4, ci5, di3, di4, di5, ci3, cr2, cr3, dr2, dr3, dr4, dr5,
+    cr5, cr4, ti2, ti3, ti5, ti4, tr2, tr3, tr4, tr5;
+  int idp2;
+
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*l1 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*5 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * 6;
+  ch -= ch_offset;
+  cc_offset = 1 + ido * (1 + l1);
+  cc -= cc_offset;
+
+  /* Function Body */
+  for (k = 1; k <= l1; ++k) {
+    cr2 = VADD(cc_ref(1, k, 5), cc_ref(1, k, 2));
+    ci5 = VSUB(cc_ref(1, k, 5), cc_ref(1, k, 2));
+    cr3 = VADD(cc_ref(1, k, 4), cc_ref(1, k, 3));
+    ci4 = VSUB(cc_ref(1, k, 4), cc_ref(1, k, 3));
+    ch_ref(1, 1, k) = VADD(cc_ref(1, k, 1), VADD(cr2, cr3));
+    ch_ref(ido, 2, k) = VADD(cc_ref(1, k, 1), VADD(SVMUL(tr11, cr2), SVMUL(tr12, cr3)));
+    ch_ref(1, 3, k) = VADD(SVMUL(ti11, ci5), SVMUL(ti12, ci4));
+    ch_ref(ido, 4, k) = VADD(cc_ref(1, k, 1), VADD(SVMUL(tr12, cr2), SVMUL(tr11, cr3)));
+    ch_ref(1, 5, k) = VSUB(SVMUL(ti12, ci5), SVMUL(ti11, ci4));
+    //printf("pffft: radf5, k=%d ch_ref=%f, ci4=%f\n", k, ch_ref(1, 5, k), ci4);
+  }
+  if (ido == 1) {
+    return;
+  }
+  idp2 = ido + 2;
+  for (k = 1; k <= l1; ++k) {
+    for (i = 3; i <= ido; i += 2) {
+      ic = idp2 - i;
+      dr2 = LD_PS1(wa1[i-3]); di2 = LD_PS1(wa1[i-2]);
+      dr3 = LD_PS1(wa2[i-3]); di3 = LD_PS1(wa2[i-2]);
+      dr4 = LD_PS1(wa3[i-3]); di4 = LD_PS1(wa3[i-2]);
+      dr5 = LD_PS1(wa4[i-3]); di5 = LD_PS1(wa4[i-2]);
+      VCPLXMULCONJ(dr2, di2, cc_ref(i-1, k, 2), cc_ref(i, k, 2));
+      VCPLXMULCONJ(dr3, di3, cc_ref(i-1, k, 3), cc_ref(i, k, 3));
+      VCPLXMULCONJ(dr4, di4, cc_ref(i-1, k, 4), cc_ref(i, k, 4));
+      VCPLXMULCONJ(dr5, di5, cc_ref(i-1, k, 5), cc_ref(i, k, 5));
+      cr2 = VADD(dr2, dr5);
+      ci5 = VSUB(dr5, dr2);
+      cr5 = VSUB(di2, di5);
+      ci2 = VADD(di2, di5);
+      cr3 = VADD(dr3, dr4);
+      ci4 = VSUB(dr4, dr3);
+      cr4 = VSUB(di3, di4);
+      ci3 = VADD(di3, di4);
+      ch_ref(i - 1, 1, k) = VADD(cc_ref(i - 1, k, 1), VADD(cr2, cr3));
+      ch_ref(i, 1, k) = VSUB(cc_ref(i, k, 1), VADD(ci2, ci3));//
+      tr2 = VADD(cc_ref(i - 1, k, 1), VADD(SVMUL(tr11, cr2), SVMUL(tr12, cr3)));
+      ti2 = VSUB(cc_ref(i, k, 1), VADD(SVMUL(tr11, ci2), SVMUL(tr12, ci3)));//
+      tr3 = VADD(cc_ref(i - 1, k, 1), VADD(SVMUL(tr12, cr2), SVMUL(tr11, cr3)));
+      ti3 = VSUB(cc_ref(i, k, 1), VADD(SVMUL(tr12, ci2), SVMUL(tr11, ci3)));//
+      tr5 = VADD(SVMUL(ti11, cr5), SVMUL(ti12, cr4));
+      ti5 = VADD(SVMUL(ti11, ci5), SVMUL(ti12, ci4));
+      tr4 = VSUB(SVMUL(ti12, cr5), SVMUL(ti11, cr4));
+      ti4 = VSUB(SVMUL(ti12, ci5), SVMUL(ti11, ci4));
+      ch_ref(i - 1, 3, k) = VSUB(tr2, tr5);
+      ch_ref(ic - 1, 2, k) = VADD(tr2, tr5);
+      ch_ref(i, 3, k) = VADD(ti2, ti5);
+      ch_ref(ic, 2, k) = VSUB(ti5, ti2);
+      ch_ref(i - 1, 5, k) = VSUB(tr3, tr4);
+      ch_ref(ic - 1, 4, k) = VADD(tr3, tr4);
+      ch_ref(i, 5, k) = VADD(ti3, ti4);
+      ch_ref(ic, 4, k) = VSUB(ti4, ti3);
+    }
+  }
+#undef cc_ref
+#undef ch_ref
+} /* radf5 */
+
+static void radb5_ps(int ido, int l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch, 
+                  const float *wa1, const float *wa2, const float *wa3, const float *wa4)
+{
+  static const float tr11 = .309016994374947f;
+  static const float ti11 = .951056516295154f;
+  static const float tr12 = -.809016994374947f;
+  static const float ti12 = .587785252292473f;
+
+  int cc_offset, ch_offset;
+
+  /* Local variables */
+  int i, k, ic;
+  v4sf ci2, ci3, ci4, ci5, di3, di4, di5, di2, cr2, cr3, cr5, cr4, ti2, ti3,
+    ti4, ti5, dr3, dr4, dr5, dr2, tr2, tr3, tr4, tr5;
+  int idp2;
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*5 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+  /* Parameter adjustments */
+  ch_offset = 1 + ido * (1 + l1);
+  ch -= ch_offset;
+  cc_offset = 1 + ido * 6;
+  cc -= cc_offset;
+
+  /* Function Body */
+  for (k = 1; k <= l1; ++k) {
+    ti5 = VADD(cc_ref(1, 3, k), cc_ref(1, 3, k));
+    ti4 = VADD(cc_ref(1, 5, k), cc_ref(1, 5, k));
+    tr2 = VADD(cc_ref(ido, 2, k), cc_ref(ido, 2, k));
+    tr3 = VADD(cc_ref(ido, 4, k), cc_ref(ido, 4, k));
+    ch_ref(1, k, 1) = VADD(cc_ref(1, 1, k), VADD(tr2, tr3));
+    cr2 = VADD(cc_ref(1, 1, k), VADD(SVMUL(tr11, tr2), SVMUL(tr12, tr3)));
+    cr3 = VADD(cc_ref(1, 1, k), VADD(SVMUL(tr12, tr2), SVMUL(tr11, tr3)));
+    ci5 = VADD(SVMUL(ti11, ti5), SVMUL(ti12, ti4));
+    ci4 = VSUB(SVMUL(ti12, ti5), SVMUL(ti11, ti4));
+    ch_ref(1, k, 2) = VSUB(cr2, ci5);
+    ch_ref(1, k, 3) = VSUB(cr3, ci4);
+    ch_ref(1, k, 4) = VADD(cr3, ci4);
+    ch_ref(1, k, 5) = VADD(cr2, ci5);
+  }
+  if (ido == 1) {
+    return;
+  }
+  idp2 = ido + 2;
+  for (k = 1; k <= l1; ++k) {
+    for (i = 3; i <= ido; i += 2) {
+      ic = idp2 - i;
+      ti5 = VADD(cc_ref(i  , 3, k), cc_ref(ic  , 2, k));
+      ti2 = VSUB(cc_ref(i  , 3, k), cc_ref(ic  , 2, k));
+      ti4 = VADD(cc_ref(i  , 5, k), cc_ref(ic  , 4, k));
+      ti3 = VSUB(cc_ref(i  , 5, k), cc_ref(ic  , 4, k));
+      tr5 = VSUB(cc_ref(i-1, 3, k), cc_ref(ic-1, 2, k));
+      tr2 = VADD(cc_ref(i-1, 3, k), cc_ref(ic-1, 2, k));
+      tr4 = VSUB(cc_ref(i-1, 5, k), cc_ref(ic-1, 4, k));
+      tr3 = VADD(cc_ref(i-1, 5, k), cc_ref(ic-1, 4, k));
+      ch_ref(i - 1, k, 1) = VADD(cc_ref(i-1, 1, k), VADD(tr2, tr3));
+      ch_ref(i, k, 1) = VADD(cc_ref(i, 1, k), VADD(ti2, ti3));
+      cr2 = VADD(cc_ref(i-1, 1, k), VADD(SVMUL(tr11, tr2), SVMUL(tr12, tr3)));
+      ci2 = VADD(cc_ref(i  , 1, k), VADD(SVMUL(tr11, ti2), SVMUL(tr12, ti3)));
+      cr3 = VADD(cc_ref(i-1, 1, k), VADD(SVMUL(tr12, tr2), SVMUL(tr11, tr3)));
+      ci3 = VADD(cc_ref(i  , 1, k), VADD(SVMUL(tr12, ti2), SVMUL(tr11, ti3)));
+      cr5 = VADD(SVMUL(ti11, tr5), SVMUL(ti12, tr4));
+      ci5 = VADD(SVMUL(ti11, ti5), SVMUL(ti12, ti4));
+      cr4 = VSUB(SVMUL(ti12, tr5), SVMUL(ti11, tr4));
+      ci4 = VSUB(SVMUL(ti12, ti5), SVMUL(ti11, ti4));
+      dr3 = VSUB(cr3, ci4);
+      dr4 = VADD(cr3, ci4);
+      di3 = VADD(ci3, cr4);
+      di4 = VSUB(ci3, cr4);
+      dr5 = VADD(cr2, ci5);
+      dr2 = VSUB(cr2, ci5);
+      di5 = VSUB(ci2, cr5);
+      di2 = VADD(ci2, cr5);
+      VCPLXMUL(dr2, di2, LD_PS1(wa1[i-3]), LD_PS1(wa1[i-2]));
+      VCPLXMUL(dr3, di3, LD_PS1(wa2[i-3]), LD_PS1(wa2[i-2]));
+      VCPLXMUL(dr4, di4, LD_PS1(wa3[i-3]), LD_PS1(wa3[i-2]));
+      VCPLXMUL(dr5, di5, LD_PS1(wa4[i-3]), LD_PS1(wa4[i-2]));
+
+      ch_ref(i-1, k, 2) = dr2; ch_ref(i, k, 2) = di2;
+      ch_ref(i-1, k, 3) = dr3; ch_ref(i, k, 3) = di3;
+      ch_ref(i-1, k, 4) = dr4; ch_ref(i, k, 4) = di4;
+      ch_ref(i-1, k, 5) = dr5; ch_ref(i, k, 5) = di5;
+    }
+  }
+#undef cc_ref
+#undef ch_ref
+} /* radb5 */
+
+static NEVER_INLINE(v4sf *) rfftf1_ps(int n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, 
+                                      const float *wa, const int *ifac) {  
+  v4sf *in  = (v4sf*)input_readonly;
+  v4sf *out = (in == work2 ? work1 : work2);
+  int nf = ifac[1], k1;
+  int l2 = n;
+  int iw = n-1;
+  assert(in != out && work1 != work2);
+  for (k1 = 1; k1 <= nf; ++k1) {
+    int kh = nf - k1;
+    int ip = ifac[kh + 2];
+    int l1 = l2 / ip;
+    int ido = n / l2;
+    iw -= (ip - 1)*ido;
+    switch (ip) {
+      case 5: {
+        int ix2 = iw + ido;
+        int ix3 = ix2 + ido;
+        int ix4 = ix3 + ido;
+        radf5_ps(ido, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4]);
+      } break;
+      case 4: {
+        int ix2 = iw + ido;
+        int ix3 = ix2 + ido;
+        radf4_ps(ido, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3]);
+      } break;
+      case 3: {
+        int ix2 = iw + ido;
+        radf3_ps(ido, l1, in, out, &wa[iw], &wa[ix2]);
+      } break;
+      case 2:
+        radf2_ps(ido, l1, in, out, &wa[iw]);
+        break;
+      default:
+        assert(0);
+        break;
+    }
+    l2 = l1;
+    if (out == work2) {
+      out = work1; in = work2;
+    } else {
+      out = work2; in = work1;
+    }
+  }
+  return in; /* this is in fact the output .. */
+} /* rfftf1 */
+
+static NEVER_INLINE(v4sf *) rfftb1_ps(int n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, 
+                                      const float *wa, const int *ifac) {  
+  v4sf *in  = (v4sf*)input_readonly;
+  v4sf *out = (in == work2 ? work1 : work2);
+  int nf = ifac[1], k1;
+  int l1 = 1;
+  int iw = 0;
+  assert(in != out);
+  for (k1=1; k1<=nf; k1++) {
+    int ip = ifac[k1 + 1];
+    int l2 = ip*l1;
+    int ido = n / l2;
+    switch (ip) {
+      case 5: {
+        int ix2 = iw + ido;
+        int ix3 = ix2 + ido;
+        int ix4 = ix3 + ido;
+        radb5_ps(ido, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4]);
+      } break;
+      case 4: {
+        int ix2 = iw + ido;
+        int ix3 = ix2 + ido;
+        radb4_ps(ido, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3]);
+      } break;
+      case 3: {
+        int ix2 = iw + ido;
+        radb3_ps(ido, l1, in, out, &wa[iw], &wa[ix2]);
+      } break;
+      case 2:
+        radb2_ps(ido, l1, in, out, &wa[iw]);
+        break;
+      default:
+        assert(0);
+        break;
+    }
+    l1 = l2;
+    iw += (ip - 1)*ido;
+
+    if (out == work2) {
+      out = work1; in = work2;
+    } else {
+      out = work2; in = work1;
+    }
+  }
+  return in; /* this is in fact the output .. */
+}
+
+static int decompose(int n, int *ifac, const int *ntryh) {
+  int nl = n, nf = 0, i, j = 0;
+  for (j=0; ntryh[j]; ++j) {
+    int ntry = ntryh[j];
+    while (nl != 1) {
+      int nq = nl / ntry;
+      int nr = nl - ntry * nq;
+      if (nr == 0) {
+        ifac[2+nf++] = ntry;
+        nl = nq;
+        if (ntry == 2 && nf != 1) {
+          for (i = 2; i <= nf; ++i) {
+            int ib = nf - i + 2;
+            ifac[ib + 1] = ifac[ib];
+          }
+          ifac[2] = 2;
+        }
+      } else break;
+    }
+  }
+  ifac[0] = n;
+  ifac[1] = nf;  
+  return nf;
+}
+
+
+
+static void rffti1_ps(int n, float *wa, int *ifac)
+{
+  static const int ntryh[] = { 4,2,3,5,0 };
+  int k1, j, ii;
+
+  int nf = decompose(n,ifac,ntryh);
+  float argh = (2*M_PI) / n;
+  int is = 0;
+  int nfm1 = nf - 1;
+  int l1 = 1;
+  for (k1 = 1; k1 <= nfm1; k1++) {
+    int ip = ifac[k1 + 1];
+    int ld = 0;
+    int l2 = l1*ip;
+    int ido = n / l2;
+    int ipm = ip - 1;
+    for (j = 1; j <= ipm; ++j) {
+      float argld;
+      int i = is, fi=0;
+      ld += l1;
+      argld = ld*argh;
+      for (ii = 3; ii <= ido; ii += 2) {
+        i += 2;
+        fi += 1;
+        wa[i - 2] = cos(fi*argld);
+        wa[i - 1] = sin(fi*argld);
+      }
+      is += ido;
+    }
+    l1 = l2;
+  }
+} /* rffti1 */
+
+void cffti1_ps(int n, float *wa, int *ifac)
+{
+  static const int ntryh[] = { 5,3,4,2,0 };
+  int k1, j, ii;
+
+  int nf = decompose(n,ifac,ntryh);
+  float argh = (2*M_PI)/(float)n;
+  int i = 1;
+  int l1 = 1;
+  for (k1=1; k1<=nf; k1++) {
+    int ip = ifac[k1+1];
+    int ld = 0;
+    int l2 = l1*ip;
+    int ido = n / l2;
+    int idot = ido + ido + 2;
+    int ipm = ip - 1;
+    for (j=1; j<=ipm; j++) {
+      float argld;
+      int i1 = i, fi = 0;
+      wa[i-1] = 1;
+      wa[i] = 0;
+      ld += l1;
+      argld = ld*argh;
+      for (ii = 4; ii <= idot; ii += 2) {
+        i += 2;
+        fi += 1;
+        wa[i-1] = cos(fi*argld);
+        wa[i] = sin(fi*argld);
+      }
+      if (ip > 5) {
+        wa[i1-1] = wa[i-1];
+        wa[i1] = wa[i];
+      }
+    }
+    l1 = l2;
+  }
+} /* cffti1 */
+
+
+v4sf *cfftf1_ps(int n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, const float *wa, const int *ifac, int isign) {
+  v4sf *in  = (v4sf*)input_readonly;
+  v4sf *out = (in == work2 ? work1 : work2); 
+  int nf = ifac[1], k1;
+  int l1 = 1;
+  int iw = 0;
+  assert(in != out && work1 != work2);
+  for (k1=2; k1<=nf+1; k1++) {
+    int ip = ifac[k1];
+    int l2 = ip*l1;
+    int ido = n / l2;
+    int idot = ido + ido;
+    switch (ip) {
+      case 5: {
+        int ix2 = iw + idot;
+        int ix3 = ix2 + idot;
+        int ix4 = ix3 + idot;
+        passf5_ps(idot, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4], isign);
+      } break;
+      case 4: {
+        int ix2 = iw + idot;
+        int ix3 = ix2 + idot;
+        passf4_ps(idot, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], isign);
+      } break;
+      case 2: {
+        passf2_ps(idot, l1, in, out, &wa[iw], isign);
+      } break;
+      case 3: {
+        int ix2 = iw + idot;
+        passf3_ps(idot, l1, in, out, &wa[iw], &wa[ix2], isign);
+      } break;
+      default:
+        assert(0);
+    }
+    l1 = l2;
+    iw += (ip - 1)*idot;
+    if (out == work2) {
+      out = work1; in = work2;
+    } else {
+      out = work2; in = work1;
+    }
+  }
+
+  return in; /* this is in fact the output .. */
+}
+
+
+struct PFFFT_Setup {
+  int     N;
+  int     Ncvec; // nb of complex simd vectors (N/4 if PFFFT_COMPLEX, N/8 if PFFFT_REAL)
+  int ifac[15];
+  pffft_transform_t transform;
+  v4sf *data; // allocated room for twiddle coefs
+  float *e;    // points into 'data' , N/4*3 elements
+  float *twiddle; // points into 'data', N/4 elements
+};
+
+PFFFT_Setup *pffft_new_setup(int N, pffft_transform_t transform) {
+  PFFFT_Setup *s = (PFFFT_Setup*)malloc(sizeof(PFFFT_Setup));
+  int k, m;
+  /* unfortunately, the fft size must be a multiple of 16 for complex FFTs 
+     and 32 for real FFTs -- a lot of stuff would need to be rewritten to
+     handle other cases (or maybe just switch to a scalar fft, I don't know..) */
+  if (transform == PFFFT_REAL) { assert((N%(2*SIMD_SZ*SIMD_SZ))==0 && N>0); }
+  if (transform == PFFFT_COMPLEX) { assert((N%(SIMD_SZ*SIMD_SZ))==0 && N>0); }
+  //assert((N % 32) == 0);
+  s->N = N;
+  s->transform = transform;  
+  /* nb of complex simd vectors */
+  s->Ncvec = (transform == PFFFT_REAL ? N/2 : N)/SIMD_SZ;
+  s->data = (v4sf*)pffft_aligned_malloc(2*s->Ncvec * sizeof(v4sf));
+  s->e = (float*)s->data;
+  s->twiddle = (float*)(s->data + (2*s->Ncvec*(SIMD_SZ-1))/SIMD_SZ);  
+
+  if (transform == PFFFT_REAL) {
+    for (k=0; k < s->Ncvec; ++k) {
+      int i = k/SIMD_SZ;
+      int j = k%SIMD_SZ;
+      for (m=0; m < SIMD_SZ-1; ++m) {
+        float A = -2*M_PI*(m+1)*k / N;
+        s->e[(2*(i*3 + m) + 0) * SIMD_SZ + j] = cos(A);
+        s->e[(2*(i*3 + m) + 1) * SIMD_SZ + j] = sin(A);
+      }
+    }
+    rffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac);
+  } else {
+    for (k=0; k < s->Ncvec; ++k) {
+      int i = k/SIMD_SZ;
+      int j = k%SIMD_SZ;
+      for (m=0; m < SIMD_SZ-1; ++m) {
+        float A = -2*M_PI*(m+1)*k / N;
+        s->e[(2*(i*3 + m) + 0)*SIMD_SZ + j] = cos(A);
+        s->e[(2*(i*3 + m) + 1)*SIMD_SZ + j] = sin(A);
+      }
+    }
+    cffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac);
+  }
+
+  /* check that N is decomposable with allowed prime factors */
+  for (k=0, m=1; k < s->ifac[1]; ++k) { m *= s->ifac[2+k]; }
+  if (m != N/SIMD_SZ) {
+    pffft_destroy_setup(s); s = 0;
+  }
+
+  return s;
+}
+
+
+void pffft_destroy_setup(PFFFT_Setup *s) {
+  pffft_aligned_free(s->data);
+  free(s);
+}
+
+#if !defined(PFFFT_SIMD_DISABLE)
+
+/* [0 0 1 2 3 4 5 6 7 8] -> [0 8 7 6 5 4 3 2 1] */
+static void reversed_copy(int N, const v4sf *in, int in_stride, v4sf *out) {
+  v4sf g0, g1;
+  int k;
+  INTERLEAVE2(in[0], in[1], g0, g1); in += in_stride;
+  
+  *--out = VSWAPHL(g0, g1); // [g0l, g0h], [g1l g1h] -> [g1l, g0h]
+  for (k=1; k < N; ++k) {
+    v4sf h0, h1;
+    INTERLEAVE2(in[0], in[1], h0, h1); in += in_stride;
+    *--out = VSWAPHL(g1, h0);
+    *--out = VSWAPHL(h0, h1);
+    g1 = h1;
+  }
+  *--out = VSWAPHL(g1, g0);
+}
+
+static void unreversed_copy(int N, const v4sf *in, v4sf *out, int out_stride) {
+  v4sf g0, g1, h0, h1;
+  int k;
+  g0 = g1 = in[0]; ++in;
+  for (k=1; k < N; ++k) {
+    h0 = *in++; h1 = *in++;
+    g1 = VSWAPHL(g1, h0);
+    h0 = VSWAPHL(h0, h1);
+    UNINTERLEAVE2(h0, g1, out[0], out[1]); out += out_stride;
+    g1 = h1;
+  }
+  h0 = *in++; h1 = g0;
+  g1 = VSWAPHL(g1, h0);
+  h0 = VSWAPHL(h0, h1);
+  UNINTERLEAVE2(h0, g1, out[0], out[1]);
+}
+
+void pffft_zreorder(PFFFT_Setup *setup, const float *in, float *out, pffft_direction_t direction) {
+  int k, N = setup->N, Ncvec = setup->Ncvec;
+  const v4sf *vin = (const v4sf*)in;
+  v4sf *vout = (v4sf*)out;
+  assert(in != out);
+  if (setup->transform == PFFFT_REAL) {
+    int k, dk = N/32;
+    if (direction == PFFFT_FORWARD) {
+      for (k=0; k < dk; ++k) {
+        INTERLEAVE2(vin[k*8 + 0], vin[k*8 + 1], vout[2*(0*dk + k) + 0], vout[2*(0*dk + k) + 1]);
+        INTERLEAVE2(vin[k*8 + 4], vin[k*8 + 5], vout[2*(2*dk + k) + 0], vout[2*(2*dk + k) + 1]);
+      }
+      reversed_copy(dk, vin+2, 8, (v4sf*)(out + N/2));
+      reversed_copy(dk, vin+6, 8, (v4sf*)(out + N));
+    } else {
+      for (k=0; k < dk; ++k) {
+        UNINTERLEAVE2(vin[2*(0*dk + k) + 0], vin[2*(0*dk + k) + 1], vout[k*8 + 0], vout[k*8 + 1]);
+        UNINTERLEAVE2(vin[2*(2*dk + k) + 0], vin[2*(2*dk + k) + 1], vout[k*8 + 4], vout[k*8 + 5]);
+      }
+      unreversed_copy(dk, (v4sf*)(in + N/4), (v4sf*)(out + N - 6*SIMD_SZ), -8);
+      unreversed_copy(dk, (v4sf*)(in + 3*N/4), (v4sf*)(out + N - 2*SIMD_SZ), -8);
+    }
+  } else {
+    if (direction == PFFFT_FORWARD) {
+      for (k=0; k < Ncvec; ++k) { 
+        int kk = (k/4) + (k%4)*(Ncvec/4);
+        INTERLEAVE2(vin[k*2], vin[k*2+1], vout[kk*2], vout[kk*2+1]);
+      }
+    } else {
+      for (k=0; k < Ncvec; ++k) { 
+        int kk = (k/4) + (k%4)*(Ncvec/4);
+        UNINTERLEAVE2(vin[kk*2], vin[kk*2+1], vout[k*2], vout[k*2+1]);
+      }
+    }
+  }
+}
+
+void pffft_cplx_finalize(int Ncvec, const v4sf *in, v4sf *out, const v4sf *e) {
+  int k, dk = Ncvec/SIMD_SZ; // number of 4x4 matrix blocks
+  v4sf r0, i0, r1, i1, r2, i2, r3, i3;
+  v4sf sr0, dr0, sr1, dr1, si0, di0, si1, di1;
+  assert(in != out);
+  for (k=0; k < dk; ++k) {    
+    r0 = in[8*k+0]; i0 = in[8*k+1];
+    r1 = in[8*k+2]; i1 = in[8*k+3];
+    r2 = in[8*k+4]; i2 = in[8*k+5];
+    r3 = in[8*k+6]; i3 = in[8*k+7];
+    VTRANSPOSE4(r0,r1,r2,r3);
+    VTRANSPOSE4(i0,i1,i2,i3);
+    VCPLXMUL(r1,i1,e[k*6+0],e[k*6+1]);
+    VCPLXMUL(r2,i2,e[k*6+2],e[k*6+3]);
+    VCPLXMUL(r3,i3,e[k*6+4],e[k*6+5]);
+
+    sr0 = VADD(r0,r2); dr0 = VSUB(r0, r2);
+    sr1 = VADD(r1,r3); dr1 = VSUB(r1, r3);
+    si0 = VADD(i0,i2); di0 = VSUB(i0, i2);
+    si1 = VADD(i1,i3); di1 = VSUB(i1, i3);
+
+    /*
+      transformation for each column is:
+      
+      [1   1   1   1   0   0   0   0]   [r0]
+      [1   0  -1   0   0  -1   0   1]   [r1]
+      [1  -1   1  -1   0   0   0   0]   [r2]
+      [1   0  -1   0   0   1   0  -1]   [r3]
+      [0   0   0   0   1   1   1   1] * [i0]
+      [0   1   0  -1   1   0  -1   0]   [i1]
+      [0   0   0   0   1  -1   1  -1]   [i2]
+      [0  -1   0   1   1   0  -1   0]   [i3]    
+    */
+    
+    r0 = VADD(sr0, sr1); i0 = VADD(si0, si1);
+    r1 = VADD(dr0, di1); i1 = VSUB(di0, dr1);
+    r2 = VSUB(sr0, sr1); i2 = VSUB(si0, si1);
+    r3 = VSUB(dr0, di1); i3 = VADD(di0, dr1);
+  
+    *out++ = r0; *out++ = i0; *out++ = r1; *out++ = i1;
+    *out++ = r2; *out++ = i2; *out++ = r3; *out++ = i3;
+  }
+}
+
+void pffft_cplx_preprocess(int Ncvec, const v4sf *in, v4sf *out, const v4sf *e) {
+  int k, dk = Ncvec/SIMD_SZ; // number of 4x4 matrix blocks
+  v4sf r0, i0, r1, i1, r2, i2, r3, i3;
+  v4sf sr0, dr0, sr1, dr1, si0, di0, si1, di1;
+  assert(in != out);
+  for (k=0; k < dk; ++k) {    
+    r0 = in[8*k+0]; i0 = in[8*k+1];
+    r1 = in[8*k+2]; i1 = in[8*k+3];
+    r2 = in[8*k+4]; i2 = in[8*k+5];
+    r3 = in[8*k+6]; i3 = in[8*k+7];
+
+    sr0 = VADD(r0,r2); dr0 = VSUB(r0, r2);
+    sr1 = VADD(r1,r3); dr1 = VSUB(r1, r3);
+    si0 = VADD(i0,i2); di0 = VSUB(i0, i2);
+    si1 = VADD(i1,i3); di1 = VSUB(i1, i3);
+
+    r0 = VADD(sr0, sr1); i0 = VADD(si0, si1);
+    r1 = VSUB(dr0, di1); i1 = VADD(di0, dr1);
+    r2 = VSUB(sr0, sr1); i2 = VSUB(si0, si1);
+    r3 = VADD(dr0, di1); i3 = VSUB(di0, dr1);
+
+    VCPLXMULCONJ(r1,i1,e[k*6+0],e[k*6+1]);
+    VCPLXMULCONJ(r2,i2,e[k*6+2],e[k*6+3]);
+    VCPLXMULCONJ(r3,i3,e[k*6+4],e[k*6+5]);
+
+    VTRANSPOSE4(r0,r1,r2,r3);
+    VTRANSPOSE4(i0,i1,i2,i3);
+
+    *out++ = r0; *out++ = i0; *out++ = r1; *out++ = i1;
+    *out++ = r2; *out++ = i2; *out++ = r3; *out++ = i3;
+  }
+}
+
+
+static ALWAYS_INLINE(void) pffft_real_finalize_4x4(const v4sf *in0, const v4sf *in1, const v4sf *in,
+                            const v4sf *e, v4sf *out) {
+  v4sf r0, i0, r1, i1, r2, i2, r3, i3;
+  v4sf sr0, dr0, sr1, dr1, si0, di0, si1, di1;
+  r0 = *in0; i0 = *in1;
+  r1 = *in++; i1 = *in++; r2 = *in++; i2 = *in++; r3 = *in++; i3 = *in++;
+  VTRANSPOSE4(r0,r1,r2,r3);
+  VTRANSPOSE4(i0,i1,i2,i3);
+ 
+  /*
+    transformation for each column is:
+
+    [1   1   1   1   0   0   0   0]   [r0]
+    [1   0  -1   0   0  -1   0   1]   [r1]
+    [1   0  -1   0   0   1   0  -1]   [r2]
+    [1  -1   1  -1   0   0   0   0]   [r3]
+    [0   0   0   0   1   1   1   1] * [i0]
+    [0  -1   0   1  -1   0   1   0]   [i1]
+    [0  -1   0   1   1   0  -1   0]   [i2]
+    [0   0   0   0  -1   1  -1   1]   [i3]    
+  */
+  
+  //cerr << "matrix initial, before e , REAL:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n";
+  //cerr << "matrix initial, before e, IMAG :\n 1: " << i0 << "\n 1: " << i1 << "\n 1: " << i2 << "\n 1: " << i3 << "\n";
+
+  VCPLXMUL(r1,i1,e[0],e[1]);
+  VCPLXMUL(r2,i2,e[2],e[3]);
+  VCPLXMUL(r3,i3,e[4],e[5]);
+
+  //cerr << "matrix initial, real part:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n";
+  //cerr << "matrix initial, imag part:\n 1: " << i0 << "\n 1: " << i1 << "\n 1: " << i2 << "\n 1: " << i3 << "\n";
+
+  sr0 = VADD(r0,r2); dr0 = VSUB(r0,r2); 
+  sr1 = VADD(r1,r3); dr1 = VSUB(r3,r1);
+  si0 = VADD(i0,i2); di0 = VSUB(i0,i2); 
+  si1 = VADD(i1,i3); di1 = VSUB(i3,i1);
+
+  r0 = VADD(sr0, sr1);
+  r3 = VSUB(sr0, sr1);
+  i0 = VADD(si0, si1);
+  i3 = VSUB(si1, si0);
+  r1 = VADD(dr0, di1);
+  r2 = VSUB(dr0, di1);
+  i1 = VSUB(dr1, di0);
+  i2 = VADD(dr1, di0);
+
+  *out++ = r0;
+  *out++ = i0;
+  *out++ = r1;
+  *out++ = i1;
+  *out++ = r2;
+  *out++ = i2;
+  *out++ = r3;
+  *out++ = i3;
+
+}
+
+static NEVER_INLINE(void) pffft_real_finalize(int Ncvec, const v4sf *in, v4sf *out, const v4sf *e) {
+  int k, dk = Ncvec/SIMD_SZ; // number of 4x4 matrix blocks
+  /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */
+
+  v4sf_union cr, ci, *uout = (v4sf_union*)out;
+  v4sf save = in[7], zero=VZERO();
+  float xr0, xi0, xr1, xi1, xr2, xi2, xr3, xi3;
+  static const float s = M_SQRT2/2;
+
+  cr.v = in[0]; ci.v = in[Ncvec*2-1];
+  assert(in != out);
+  pffft_real_finalize_4x4(&zero, &zero, in+1, e, out);
+
+  /*
+    [cr0 cr1 cr2 cr3 ci0 ci1 ci2 ci3]
+
+    [Xr(1)]  ] [1   1   1   1   0   0   0   0]
+    [Xr(N/4) ] [0   0   0   0   1   s   0  -s]
+    [Xr(N/2) ] [1   0  -1   0   0   0   0   0]
+    [Xr(3N/4)] [0   0   0   0   1  -s   0   s]
+    [Xi(1)   ] [1  -1   1  -1   0   0   0   0]
+    [Xi(N/4) ] [0   0   0   0   0  -s  -1  -s]
+    [Xi(N/2) ] [0  -1   0   1   0   0   0   0]
+    [Xi(3N/4)] [0   0   0   0   0  -s   1  -s]
+  */
+
+  xr0=(cr.f[0]+cr.f[2]) + (cr.f[1]+cr.f[3]); uout[0].f[0] = xr0;
+  xi0=(cr.f[0]+cr.f[2]) - (cr.f[1]+cr.f[3]); uout[1].f[0] = xi0;
+  xr2=(cr.f[0]-cr.f[2]);                     uout[4].f[0] = xr2;
+  xi2=(cr.f[3]-cr.f[1]);                     uout[5].f[0] = xi2;
+  xr1= ci.f[0] + s*(ci.f[1]-ci.f[3]);        uout[2].f[0] = xr1;
+  xi1=-ci.f[2] - s*(ci.f[1]+ci.f[3]);        uout[3].f[0] = xi1;
+  xr3= ci.f[0] - s*(ci.f[1]-ci.f[3]);        uout[6].f[0] = xr3;
+  xi3= ci.f[2] - s*(ci.f[1]+ci.f[3]);        uout[7].f[0] = xi3; 
+
+  for (k=1; k < dk; ++k) {
+    v4sf save_next = in[8*k+7];
+    pffft_real_finalize_4x4(&save, &in[8*k+0], in + 8*k+1,
+                           e + k*6, out + k*8);
+    save = save_next;
+  }
+
+}
+
+static ALWAYS_INLINE(void) pffft_real_preprocess_4x4(const v4sf *in, 
+                                             const v4sf *e, v4sf *out, int first) {
+  v4sf r0=in[0], i0=in[1], r1=in[2], i1=in[3], r2=in[4], i2=in[5], r3=in[6], i3=in[7];
+  /*
+    transformation for each column is:
+
+    [1   1   1   1   0   0   0   0]   [r0]
+    [1   0   0  -1   0  -1  -1   0]   [r1]
+    [1  -1  -1   1   0   0   0   0]   [r2]
+    [1   0   0  -1   0   1   1   0]   [r3]
+    [0   0   0   0   1  -1   1  -1] * [i0]
+    [0  -1   1   0   1   0   0   1]   [i1]
+    [0   0   0   0   1   1  -1  -1]   [i2]
+    [0   1  -1   0   1   0   0   1]   [i3]    
+  */
+
+  v4sf sr0 = VADD(r0,r3), dr0 = VSUB(r0,r3); 
+  v4sf sr1 = VADD(r1,r2), dr1 = VSUB(r1,r2);
+  v4sf si0 = VADD(i0,i3), di0 = VSUB(i0,i3); 
+  v4sf si1 = VADD(i1,i2), di1 = VSUB(i1,i2);
+
+  r0 = VADD(sr0, sr1);
+  r2 = VSUB(sr0, sr1);
+  r1 = VSUB(dr0, si1);
+  r3 = VADD(dr0, si1);
+  i0 = VSUB(di0, di1);
+  i2 = VADD(di0, di1);
+  i1 = VSUB(si0, dr1);
+  i3 = VADD(si0, dr1);
+
+  VCPLXMULCONJ(r1,i1,e[0],e[1]);
+  VCPLXMULCONJ(r2,i2,e[2],e[3]);
+  VCPLXMULCONJ(r3,i3,e[4],e[5]);
+
+  VTRANSPOSE4(r0,r1,r2,r3);
+  VTRANSPOSE4(i0,i1,i2,i3);
+
+  if (!first) {
+    *out++ = r0;
+    *out++ = i0;
+  }
+  *out++ = r1;
+  *out++ = i1;
+  *out++ = r2;
+  *out++ = i2;
+  *out++ = r3;
+  *out++ = i3;
+}
+
+static NEVER_INLINE(void) pffft_real_preprocess(int Ncvec, const v4sf *in, v4sf *out, const v4sf *e) {
+  int k, dk = Ncvec/SIMD_SZ; // number of 4x4 matrix blocks
+  /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */
+
+  v4sf_union Xr, Xi, *uout = (v4sf_union*)out;
+  float cr0, ci0, cr1, ci1, cr2, ci2, cr3, ci3;
+  static const float s = M_SQRT2;
+  assert(in != out);
+  for (k=0; k < 4; ++k) {
+    Xr.f[k] = ((float*)in)[8*k];
+    Xi.f[k] = ((float*)in)[8*k+4];
+  }
+
+  pffft_real_preprocess_4x4(in, e, out+1, 1); // will write only 6 values
+
+  /*
+    [Xr0 Xr1 Xr2 Xr3 Xi0 Xi1 Xi2 Xi3]
+
+    [cr0] [1   0   2   0   1   0   0   0]
+    [cr1] [1   0   0   0  -1   0  -2   0]
+    [cr2] [1   0  -2   0   1   0   0   0]
+    [cr3] [1   0   0   0  -1   0   2   0]
+    [ci0] [0   2   0   2   0   0   0   0]
+    [ci1] [0   s   0  -s   0  -s   0  -s]
+    [ci2] [0   0   0   0   0  -2   0   2]
+    [ci3] [0  -s   0   s   0  -s   0  -s]
+  */
+  for (k=1; k < dk; ++k) {    
+    pffft_real_preprocess_4x4(in+8*k, e + k*6, out-1+k*8, 0);
+  }
+
+  cr0=(Xr.f[0]+Xi.f[0]) + 2*Xr.f[2]; uout[0].f[0] = cr0;
+  cr1=(Xr.f[0]-Xi.f[0]) - 2*Xi.f[2]; uout[0].f[1] = cr1;
+  cr2=(Xr.f[0]+Xi.f[0]) - 2*Xr.f[2]; uout[0].f[2] = cr2;
+  cr3=(Xr.f[0]-Xi.f[0]) + 2*Xi.f[2]; uout[0].f[3] = cr3;
+  ci0= 2*(Xr.f[1]+Xr.f[3]);                       uout[2*Ncvec-1].f[0] = ci0;
+  ci1= s*(Xr.f[1]-Xr.f[3]) - s*(Xi.f[1]+Xi.f[3]); uout[2*Ncvec-1].f[1] = ci1;
+  ci2= 2*(Xi.f[3]-Xi.f[1]);                       uout[2*Ncvec-1].f[2] = ci2;
+  ci3=-s*(Xr.f[1]-Xr.f[3]) - s*(Xi.f[1]+Xi.f[3]); uout[2*Ncvec-1].f[3] = ci3;
+}
+
+
+void pffft_transform_internal(PFFFT_Setup *setup, const float *finput, float *foutput, v4sf *scratch,
+                             pffft_direction_t direction, int ordered) {
+  int k, Ncvec   = setup->Ncvec;
+  int nf_odd = (setup->ifac[1] & 1);
+
+  // temporary buffer is allocated on the stack if the scratch pointer is NULL
+  int stack_allocate = (scratch == 0 ? Ncvec*2 : 1);
+  VLA_ARRAY_ON_STACK(v4sf, scratch_on_stack, stack_allocate);
+
+  const v4sf *vinput = (const v4sf*)finput;
+  v4sf *voutput      = (v4sf*)foutput;
+  v4sf *buff[2]      = { voutput, scratch ? scratch : scratch_on_stack };
+  int ib = (nf_odd ^ ordered ? 1 : 0);
+
+  assert(VALIGNED(finput) && VALIGNED(foutput));
+
+  //assert(finput != foutput);
+  if (direction == PFFFT_FORWARD) {
+    ib = !ib;
+    if (setup->transform == PFFFT_REAL) { 
+      ib = (rfftf1_ps(Ncvec*2, vinput, buff[ib], buff[!ib],
+                      setup->twiddle, &setup->ifac[0]) == buff[0] ? 0 : 1);      
+      pffft_real_finalize(Ncvec, buff[ib], buff[!ib], (v4sf*)setup->e);
+    } else {
+      v4sf *tmp = buff[ib];
+      for (k=0; k < Ncvec; ++k) {
+        UNINTERLEAVE2(vinput[k*2], vinput[k*2+1], tmp[k*2], tmp[k*2+1]);
+      }
+      ib = (cfftf1_ps(Ncvec, buff[ib], buff[!ib], buff[ib], 
+                      setup->twiddle, &setup->ifac[0], -1) == buff[0] ? 0 : 1);
+      pffft_cplx_finalize(Ncvec, buff[ib], buff[!ib], (v4sf*)setup->e);
+    }
+    if (ordered) {
+      pffft_zreorder(setup, (float*)buff[!ib], (float*)buff[ib], PFFFT_FORWARD);       
+    } else ib = !ib;
+  } else {
+    if (vinput == buff[ib]) { 
+      ib = !ib; // may happen when finput == foutput
+    }
+    if (ordered) {
+      pffft_zreorder(setup, (float*)vinput, (float*)buff[ib], PFFFT_BACKWARD); 
+      vinput = buff[ib]; ib = !ib;
+    }
+    if (setup->transform == PFFFT_REAL) {
+      pffft_real_preprocess(Ncvec, vinput, buff[ib], (v4sf*)setup->e);
+      ib = (rfftb1_ps(Ncvec*2, buff[ib], buff[0], buff[1], 
+                      setup->twiddle, &setup->ifac[0]) == buff[0] ? 0 : 1);
+    } else {
+      pffft_cplx_preprocess(Ncvec, vinput, buff[ib], (v4sf*)setup->e);
+      ib = (cfftf1_ps(Ncvec, buff[ib], buff[0], buff[1], 
+                      setup->twiddle, &setup->ifac[0], +1) == buff[0] ? 0 : 1);
+      for (k=0; k < Ncvec; ++k) {
+        INTERLEAVE2(buff[ib][k*2], buff[ib][k*2+1], buff[ib][k*2], buff[ib][k*2+1]);
+      }
+    }
+  }
+  
+  if (buff[ib] != voutput) {
+    /* extra copy required -- this situation should only happen when finput == foutput */
+    assert(finput==foutput);
+    for (k=0; k < Ncvec; ++k) {
+      v4sf a = buff[ib][2*k], b = buff[ib][2*k+1];
+      voutput[2*k] = a; voutput[2*k+1] = b;
+    }
+    ib = !ib;
+  }
+  assert(buff[ib] == voutput);
+}
+
+void pffft_zconvolve_accumulate(PFFFT_Setup *s, const float *a, const float *b, float *ab, float scaling) {
+  int Ncvec = s->Ncvec;
+  const v4sf * RESTRICT va = (const v4sf*)a;
+  const v4sf * RESTRICT vb = (const v4sf*)b;
+  v4sf * RESTRICT vab = (v4sf*)ab;
+
+#ifdef __arm__
+  __builtin_prefetch(va);
+  __builtin_prefetch(vb);
+  __builtin_prefetch(vab);
+  __builtin_prefetch(va+2);
+  __builtin_prefetch(vb+2);
+  __builtin_prefetch(vab+2);
+  __builtin_prefetch(va+4);
+  __builtin_prefetch(vb+4);
+  __builtin_prefetch(vab+4);
+  __builtin_prefetch(va+6);
+  __builtin_prefetch(vb+6);
+  __builtin_prefetch(vab+6);
+# ifndef __clang__
+#   define ZCONVOLVE_USING_INLINE_NEON_ASM
+# endif
+#endif
+
+  float ar, ai, br, bi, abr, abi;
+#ifndef ZCONVOLVE_USING_INLINE_ASM
+  v4sf vscal = LD_PS1(scaling);
+  int i;
+#endif
+
+  assert(VALIGNED(a) && VALIGNED(b) && VALIGNED(ab));
+  ar = ((v4sf_union*)va)[0].f[0];
+  ai = ((v4sf_union*)va)[1].f[0];
+  br = ((v4sf_union*)vb)[0].f[0];
+  bi = ((v4sf_union*)vb)[1].f[0];
+  abr = ((v4sf_union*)vab)[0].f[0];
+  abi = ((v4sf_union*)vab)[1].f[0];
+ 
+#ifdef ZCONVOLVE_USING_INLINE_ASM // inline asm version, unfortunately miscompiled by clang 3.2, at least on ubuntu.. so this will be restricted to gcc
+  const float *a_ = a, *b_ = b; float *ab_ = ab;
+  int N = Ncvec;
+  asm volatile("mov         r8, %2                  \n"
+               "vdup.f32    q15, %4                 \n"
+               "1:                                  \n"
+               "pld         [%0,#64]                \n"
+               "pld         [%1,#64]                \n"
+               "pld         [%2,#64]                \n"
+               "pld         [%0,#96]                \n"
+               "pld         [%1,#96]                \n"
+               "pld         [%2,#96]                \n"
+               "vld1.f32    {q0,q1},   [%0,:128]!         \n"
+               "vld1.f32    {q4,q5},   [%1,:128]!         \n"
+               "vld1.f32    {q2,q3},   [%0,:128]!         \n"
+               "vld1.f32    {q6,q7},   [%1,:128]!         \n"
+               "vld1.f32    {q8,q9},   [r8,:128]!          \n"
+               
+               "vmul.f32    q10, q0, q4             \n"
+               "vmul.f32    q11, q0, q5             \n"
+               "vmul.f32    q12, q2, q6             \n" 
+               "vmul.f32    q13, q2, q7             \n"                 
+               "vmls.f32    q10, q1, q5             \n"
+               "vmla.f32    q11, q1, q4             \n"
+               "vld1.f32    {q0,q1}, [r8,:128]!     \n"
+               "vmls.f32    q12, q3, q7             \n"
+               "vmla.f32    q13, q3, q6             \n"
+               "vmla.f32    q8, q10, q15            \n"
+               "vmla.f32    q9, q11, q15            \n"
+               "vmla.f32    q0, q12, q15            \n"
+               "vmla.f32    q1, q13, q15            \n"
+               "vst1.f32    {q8,q9},[%2,:128]!    \n"
+               "vst1.f32    {q0,q1},[%2,:128]!    \n"
+               "subs        %3, #2                  \n"
+               "bne         1b                      \n"
+               : "+r"(a_), "+r"(b_), "+r"(ab_), "+r"(N) : "r"(scaling) : "r8", "q0","q1","q2","q3","q4","q5","q6","q7","q8","q9", "q10","q11","q12","q13","q15","memory");
+#else // default routine, works fine for non-arm cpus with current compilers
+  for (i=0; i < Ncvec; i += 2) {
+    v4sf ar, ai, br, bi;
+    ar = va[2*i+0]; ai = va[2*i+1];
+    br = vb[2*i+0]; bi = vb[2*i+1];
+    VCPLXMUL(ar, ai, br, bi);
+    vab[2*i+0] = VMADD(ar, vscal, vab[2*i+0]);
+    vab[2*i+1] = VMADD(ai, vscal, vab[2*i+1]);
+    ar = va[2*i+2]; ai = va[2*i+3];
+    br = vb[2*i+2]; bi = vb[2*i+3];
+    VCPLXMUL(ar, ai, br, bi);
+    vab[2*i+2] = VMADD(ar, vscal, vab[2*i+2]);
+    vab[2*i+3] = VMADD(ai, vscal, vab[2*i+3]);
+  }
+#endif
+  if (s->transform == PFFFT_REAL) {
+    ((v4sf_union*)vab)[0].f[0] = abr + ar*br*scaling;
+    ((v4sf_union*)vab)[1].f[0] = abi + ai*bi*scaling;
+  }
+}
+
+
+#else // defined(PFFFT_SIMD_DISABLE)
+
+// standard routine using scalar floats, without SIMD stuff.
+
+#define pffft_zreorder_nosimd pffft_zreorder
+void pffft_zreorder_nosimd(PFFFT_Setup *setup, const float *in, float *out, pffft_direction_t direction) {
+  int k, N = setup->N;
+  if (setup->transform == PFFFT_COMPLEX) {
+    for (k=0; k < 2*N; ++k) out[k] = in[k];
+    return;
+  }
+  else if (direction == PFFFT_FORWARD) {
+    float x_N = in[N-1];
+    for (k=N-1; k > 1; --k) out[k] = in[k-1]; 
+    out[0] = in[0];
+    out[1] = x_N;
+  } else {
+    float x_N = in[1];
+    for (k=1; k < N-1; ++k) out[k] = in[k+1]; 
+    out[0] = in[0];
+    out[N-1] = x_N;
+  }
+}
+
+#define pffft_transform_internal_nosimd pffft_transform_internal
+void pffft_transform_internal_nosimd(PFFFT_Setup *setup, const float *input, float *output, float *scratch,
+                                    pffft_direction_t direction, int ordered) {
+  int Ncvec   = setup->Ncvec;
+  int nf_odd = (setup->ifac[1] & 1);
+
+  // temporary buffer is allocated on the stack if the scratch pointer is NULL
+  int stack_allocate = (scratch == 0 ? Ncvec*2 : 1);
+  VLA_ARRAY_ON_STACK(v4sf, scratch_on_stack, stack_allocate);
+  float *buff[2];
+  int ib;
+  if (scratch == 0) scratch = scratch_on_stack;
+  buff[0] = output; buff[1] = scratch;
+
+  if (setup->transform == PFFFT_COMPLEX) ordered = 0; // it is always ordered.
+  ib = (nf_odd ^ ordered ? 1 : 0);
+
+  if (direction == PFFFT_FORWARD) {
+    if (setup->transform == PFFFT_REAL) { 
+      ib = (rfftf1_ps(Ncvec*2, input, buff[ib], buff[!ib],
+                      setup->twiddle, &setup->ifac[0]) == buff[0] ? 0 : 1);      
+    } else {
+      ib = (cfftf1_ps(Ncvec, input, buff[ib], buff[!ib], 
+                      setup->twiddle, &setup->ifac[0], -1) == buff[0] ? 0 : 1);
+    }
+    if (ordered) {
+      pffft_zreorder(setup, buff[ib], buff[!ib], PFFFT_FORWARD); ib = !ib;
+    }
+  } else {    
+    if (input == buff[ib]) { 
+      ib = !ib; // may happen when finput == foutput
+    }
+    if (ordered) {
+      pffft_zreorder(setup, input, buff[!ib], PFFFT_BACKWARD); 
+      input = buff[!ib];
+    }
+    if (setup->transform == PFFFT_REAL) {
+      ib = (rfftb1_ps(Ncvec*2, input, buff[ib], buff[!ib], 
+                      setup->twiddle, &setup->ifac[0]) == buff[0] ? 0 : 1);
+    } else {
+      ib = (cfftf1_ps(Ncvec, input, buff[ib], buff[!ib], 
+                      setup->twiddle, &setup->ifac[0], +1) == buff[0] ? 0 : 1);
+    }
+  }
+  if (buff[ib] != output) {
+    int k;
+    // extra copy required -- this situation should happens only when finput == foutput
+    assert(input==output);
+    for (k=0; k < Ncvec; ++k) {
+      float a = buff[ib][2*k], b = buff[ib][2*k+1];
+      output[2*k] = a; output[2*k+1] = b;
+    }
+    ib = !ib;
+  }
+  assert(buff[ib] == output);
+}
+
+#define pffft_zconvolve_accumulate_nosimd pffft_zconvolve_accumulate
+void pffft_zconvolve_accumulate_nosimd(PFFFT_Setup *s, const float *a, const float *b,
+                                       float *ab, float scaling) {
+  int i, Ncvec = s->Ncvec;
+
+  if (s->transform == PFFFT_REAL) {
+    // take care of the fftpack ordering
+    ab[0] += a[0]*b[0]*scaling;
+    ab[2*Ncvec-1] += a[2*Ncvec-1]*b[2*Ncvec-1]*scaling;
+    ++ab; ++a; ++b; --Ncvec;
+  }
+  for (i=0; i < Ncvec; ++i) {
+    float ar, ai, br, bi;
+    ar = a[2*i+0]; ai = a[2*i+1];
+    br = b[2*i+0]; bi = b[2*i+1];
+    VCPLXMUL(ar, ai, br, bi);
+    ab[2*i+0] += ar*scaling;
+    ab[2*i+1] += ai*scaling;
+  }
+}
+
+#endif // defined(PFFFT_SIMD_DISABLE)
+
+void pffft_transform(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction) {
+  pffft_transform_internal(setup, input, output, (v4sf*)work, direction, 0);
+}
+
+void pffft_transform_ordered(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction) {
+  pffft_transform_internal(setup, input, output, (v4sf*)work, direction, 1);
+}
diff --git a/third_party/pffft/src/pffft.h b/third_party/pffft/src/pffft.h
new file mode 100644
index 0000000..2bfa7b3e
--- /dev/null
+++ b/third_party/pffft/src/pffft.h
@@ -0,0 +1,177 @@
+/* Copyright (c) 2013  Julien Pommier ( pommier@modartt.com ) 
+
+   Based on original fortran 77 code from FFTPACKv4 from NETLIB,
+   authored by Dr Paul Swarztrauber of NCAR, in 1985.
+
+   As confirmed by the NCAR fftpack software curators, the following
+   FFTPACKv5 license applies to FFTPACKv4 sources. My changes are
+   released under the same terms.
+
+   FFTPACK license:
+
+   http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html
+
+   Copyright (c) 2004 the University Corporation for Atmospheric
+   Research ("UCAR"). All rights reserved. Developed by NCAR's
+   Computational and Information Systems Laboratory, UCAR,
+   www.cisl.ucar.edu.
+
+   Redistribution and use of the Software in source and binary forms,
+   with or without modification, is permitted provided that the
+   following conditions are met:
+
+   - Neither the names of NCAR's Computational and Information Systems
+   Laboratory, the University Corporation for Atmospheric Research,
+   nor the names of its sponsors or contributors may be used to
+   endorse or promote products derived from this Software without
+   specific prior written permission.  
+
+   - Redistributions of source code must retain the above copyright
+   notices, this list of conditions, and the disclaimer below.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions, and the disclaimer below in the
+   documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
+   HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+   SOFTWARE.
+*/
+   
+/*
+   PFFFT : a Pretty Fast FFT.
+
+   This is basically an adaptation of the single precision fftpack
+   (v4) as found on netlib taking advantage of SIMD instruction found
+   on cpus such as intel x86 (SSE1), powerpc (Altivec), and arm (NEON).
+   
+   For architectures where no SIMD instruction is available, the code
+   falls back to a scalar version.  
+
+   Restrictions: 
+
+   - 1D transforms only, with 32-bit single precision.
+
+   - supports only transforms for inputs of length N of the form
+   N=(2^a)*(3^b)*(5^c), a >= 5, b >=0, c >= 0 (32, 48, 64, 96, 128,
+   144, 160, etc are all acceptable lengths). Performance is best for
+   128<=N<=8192.
+
+   - all (float*) pointers in the functions below are expected to
+   have an "simd-compatible" alignment, that is 16 bytes on x86 and
+   powerpc CPUs.
+  
+   You can allocate such buffers with the functions
+   pffft_aligned_malloc / pffft_aligned_free (or with stuff like
+   posix_memalign..)
+
+*/
+
+#ifndef PFFFT_H
+#define PFFFT_H
+
+#include <stddef.h> // for size_t
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  /* opaque struct holding internal stuff (precomputed twiddle factors)
+     this struct can be shared by many threads as it contains only
+     read-only data.  
+  */
+  typedef struct PFFFT_Setup PFFFT_Setup;
+
+  /* direction of the transform */
+  typedef enum { PFFFT_FORWARD, PFFFT_BACKWARD } pffft_direction_t;
+  
+  /* type of transform */
+  typedef enum { PFFFT_REAL, PFFFT_COMPLEX } pffft_transform_t;
+
+  /*
+    prepare for performing transforms of size N -- the returned
+    PFFFT_Setup structure is read-only so it can safely be shared by
+    multiple concurrent threads. 
+  */
+  PFFFT_Setup *pffft_new_setup(int N, pffft_transform_t transform);
+  void pffft_destroy_setup(PFFFT_Setup *);
+  /* 
+     Perform a Fourier transform , The z-domain data is stored in the
+     most efficient order for transforming it back, or using it for
+     convolution. If you need to have its content sorted in the
+     "usual" way, that is as an array of interleaved complex numbers,
+     either use pffft_transform_ordered , or call pffft_zreorder after
+     the forward fft, and before the backward fft.
+
+     Transforms are not scaled: PFFFT_BACKWARD(PFFFT_FORWARD(x)) = N*x.
+     Typically you will want to scale the backward transform by 1/N.
+     
+     The 'work' pointer should point to an area of N (2*N for complex
+     fft) floats, properly aligned. If 'work' is NULL, then stack will
+     be used instead (this is probably the best strategy for small
+     FFTs, say for N < 16384).
+
+     input and output may alias.
+  */
+  void pffft_transform(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction);
+
+  /* 
+     Similar to pffft_transform, but makes sure that the output is
+     ordered as expected (interleaved complex numbers).  This is
+     similar to calling pffft_transform and then pffft_zreorder.
+     
+     input and output may alias.
+  */
+  void pffft_transform_ordered(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction);
+
+  /* 
+     call pffft_zreorder(.., PFFFT_FORWARD) after pffft_transform(...,
+     PFFFT_FORWARD) if you want to have the frequency components in
+     the correct "canonical" order, as interleaved complex numbers.
+     
+     (for real transforms, both 0-frequency and half frequency
+     components, which are real, are assembled in the first entry as
+     F(0)+i*F(n/2+1). Note that the original fftpack did place
+     F(n/2+1) at the end of the arrays).
+     
+     input and output should not alias.
+  */
+  void pffft_zreorder(PFFFT_Setup *setup, const float *input, float *output, pffft_direction_t direction);
+
+  /* 
+     Perform a multiplication of the frequency components of dft_a and
+     dft_b and accumulate them into dft_ab. The arrays should have
+     been obtained with pffft_transform(.., PFFFT_FORWARD) and should
+     *not* have been reordered with pffft_zreorder (otherwise just
+     perform the operation yourself as the dft coefs are stored as
+     interleaved complex numbers).
+     
+     the operation performed is: dft_ab += (dft_a * fdt_b)*scaling
+     
+     The dft_a, dft_b and dft_ab pointers may alias.
+  */
+  void pffft_zconvolve_accumulate(PFFFT_Setup *setup, const float *dft_a, const float *dft_b, float *dft_ab, float scaling);
+
+  /*
+    the float buffers must have the correct alignment (16-byte boundary
+    on intel and powerpc). This function may be used to obtain such
+    correctly aligned buffers.  
+  */
+  void *pffft_aligned_malloc(size_t nb_bytes);
+  void pffft_aligned_free(void *);
+
+  /* return 4 or 1 wether support SSE/Altivec instructions was enable when building pffft.c */
+  int pffft_simd_size();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PFFFT_H
diff --git a/third_party/pffft/src/test_pffft.c b/third_party/pffft/src/test_pffft.c
new file mode 100644
index 0000000..512f0ae
--- /dev/null
+++ b/third_party/pffft/src/test_pffft.c
@@ -0,0 +1,419 @@
+/*
+  Copyright (c) 2013 Julien Pommier.
+
+  Small test & bench for PFFFT, comparing its performance with the scalar FFTPACK, FFTW, and Apple vDSP
+
+  How to build: 
+
+  on linux, with fftw3:
+  gcc -o test_pffft -DHAVE_FFTW -msse -mfpmath=sse -O3 -Wall -W pffft.c test_pffft.c fftpack.c -L/usr/local/lib -I/usr/local/include/ -lfftw3f -lm
+
+  on macos, without fftw3:
+  gcc-4.2 -o test_pffft -DHAVE_VECLIB -O3 -Wall -W pffft.c test_pffft.c fftpack.c -L/usr/local/lib -I/usr/local/include/ -framework veclib
+
+  on macos, with fftw3:
+  gcc-4.2 -o test_pffft -DHAVE_FFTW -DHAVE_VECLIB -O3 -Wall -W pffft.c test_pffft.c fftpack.c -L/usr/local/lib -I/usr/local/include/ -lfftw3f -framework veclib
+
+  on windows, with visual c++:
+  cl /Ox -D_USE_MATH_DEFINES /arch:SSE test_pffft.c pffft.c fftpack.c
+  
+  build without SIMD instructions:
+  gcc -o test_pffft -DPFFFT_SIMD_DISABLE -O3 -Wall -W pffft.c test_pffft.c fftpack.c -lm
+
+ */
+
+#include "pffft.h"
+#include "fftpack.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <assert.h>
+#include <string.h>
+
+#ifdef HAVE_SYS_TIMES
+#  include <sys/times.h>
+#  include <unistd.h>
+#endif
+
+#ifdef HAVE_VECLIB
+#  include <vecLib/vDSP.h>
+#endif
+
+#ifdef HAVE_FFTW
+#  include <fftw3.h>
+#endif
+
+#define MAX(x,y) ((x)>(y)?(x):(y))
+
+double frand() {
+  return rand()/(double)RAND_MAX;
+}
+
+#if defined(HAVE_SYS_TIMES)
+  inline double uclock_sec(void) {
+    static double ttclk = 0.;
+    if (ttclk == 0.) ttclk = sysconf(_SC_CLK_TCK);
+    struct tms t; return ((double)times(&t)) / ttclk;
+  }
+# else
+  double uclock_sec(void)
+{ return (double)clock()/(double)CLOCKS_PER_SEC; }
+#endif
+
+
+/* compare results with the regular fftpack */
+void pffft_validate_N(int N, int cplx) {
+  int Nfloat = N*(cplx?2:1);
+  int Nbytes = Nfloat * sizeof(float);
+  float *ref, *in, *out, *tmp, *tmp2;
+  PFFFT_Setup *s = pffft_new_setup(N, cplx ? PFFFT_COMPLEX : PFFFT_REAL);
+  int pass;
+
+  if (!s) { printf("Skipping N=%d, not supported\n", N); return; }
+  ref = pffft_aligned_malloc(Nbytes);
+  in = pffft_aligned_malloc(Nbytes);
+  out = pffft_aligned_malloc(Nbytes);
+  tmp = pffft_aligned_malloc(Nbytes);
+  tmp2 = pffft_aligned_malloc(Nbytes);
+
+  for (pass=0; pass < 2; ++pass) {
+    float ref_max = 0;
+    int k;
+    //printf("N=%d pass=%d cplx=%d\n", N, pass, cplx);
+    // compute reference solution with FFTPACK
+    if (pass == 0) {
+      float *wrk = malloc(2*Nbytes+15*sizeof(float));
+      for (k=0; k < Nfloat; ++k) {
+        ref[k] = in[k] = frand()*2-1; 
+        out[k] = 1e30;
+      }
+      if (!cplx) {
+        rffti(N, wrk);
+        rfftf(N, ref, wrk);
+        // use our ordering for real ffts instead of the one of fftpack
+        {
+          float refN=ref[N-1];
+          for (k=N-2; k >= 1; --k) ref[k+1] = ref[k]; 
+          ref[1] = refN;
+        }
+      } else {
+        cffti(N, wrk);
+        cfftf(N, ref, wrk);
+      }
+      free(wrk);
+    }
+
+    for (k = 0; k < Nfloat; ++k) ref_max = MAX(ref_max, fabs(ref[k]));
+
+      
+    // pass 0 : non canonical ordering of transform coefficients  
+    if (pass == 0) {
+      // test forward transform, with different input / output
+      pffft_transform(s, in, tmp, 0, PFFFT_FORWARD);
+      memcpy(tmp2, tmp, Nbytes);
+      memcpy(tmp, in, Nbytes);
+      pffft_transform(s, tmp, tmp, 0, PFFFT_FORWARD);
+      for (k = 0; k < Nfloat; ++k) {
+        assert(tmp2[k] == tmp[k]);
+      }
+
+      // test reordering
+      pffft_zreorder(s, tmp, out, PFFFT_FORWARD);
+      pffft_zreorder(s, out, tmp, PFFFT_BACKWARD);
+      for (k = 0; k < Nfloat; ++k) {
+        assert(tmp2[k] == tmp[k]);
+      }
+      pffft_zreorder(s, tmp, out, PFFFT_FORWARD);
+    } else {
+      // pass 1 : canonical ordering of transform coeffs.
+      pffft_transform_ordered(s, in, tmp, 0, PFFFT_FORWARD);
+      memcpy(tmp2, tmp, Nbytes);
+      memcpy(tmp, in, Nbytes);
+      pffft_transform_ordered(s, tmp, tmp, 0, PFFFT_FORWARD);
+      for (k = 0; k < Nfloat; ++k) {
+        assert(tmp2[k] == tmp[k]);
+      }
+      memcpy(out, tmp, Nbytes);
+    }
+
+    {
+      for (k=0; k < Nfloat; ++k) {
+        if (!(fabs(ref[k] - out[k]) < 1e-3*ref_max)) {
+          printf("%s forward PFFFT mismatch found for N=%d\n", (cplx?"CPLX":"REAL"), N);
+          exit(1);
+        }
+      }
+        
+      if (pass == 0) pffft_transform(s, tmp, out, 0, PFFFT_BACKWARD);
+      else   pffft_transform_ordered(s, tmp, out, 0, PFFFT_BACKWARD);
+      memcpy(tmp2, out, Nbytes);
+      memcpy(out, tmp, Nbytes);
+      if (pass == 0) pffft_transform(s, out, out, 0, PFFFT_BACKWARD);
+      else   pffft_transform_ordered(s, out, out, 0, PFFFT_BACKWARD);
+      for (k = 0; k < Nfloat; ++k) {
+        assert(tmp2[k] == out[k]);
+        out[k] *= 1.f/N;
+      }
+      for (k = 0; k < Nfloat; ++k) {
+        if (fabs(in[k] - out[k]) > 1e-3 * ref_max) {
+          printf("pass=%d, %s IFFFT does not match for N=%d\n", pass, (cplx?"CPLX":"REAL"), N); break;
+          exit(1);
+        }
+      }
+    }
+
+    // quick test of the circular convolution in fft domain
+    {
+      float conv_err = 0, conv_max = 0;
+
+      pffft_zreorder(s, ref, tmp, PFFFT_FORWARD);
+      memset(out, 0, Nbytes);
+      pffft_zconvolve_accumulate(s, ref, ref, out, 1.0);
+      pffft_zreorder(s, out, tmp2, PFFFT_FORWARD);
+      
+      for (k=0; k < Nfloat; k += 2) {
+        float ar = tmp[k], ai=tmp[k+1];
+        if (cplx || k > 0) {
+          tmp[k] = ar*ar - ai*ai;
+          tmp[k+1] = 2*ar*ai;
+        } else {
+          tmp[0] = ar*ar;
+          tmp[1] = ai*ai;
+        }
+      }
+      
+      for (k=0; k < Nfloat; ++k) {
+        float d = fabs(tmp[k] - tmp2[k]), e = fabs(tmp[k]);
+        if (d > conv_err) conv_err = d;
+        if (e > conv_max) conv_max = e;
+      }
+      if (conv_err > 1e-5*conv_max) {
+        printf("zconvolve error ? %g %g\n", conv_err, conv_max); exit(1);
+      }
+    }
+
+  }
+
+  printf("%s PFFFT is OK for N=%d\n", (cplx?"CPLX":"REAL"), N); fflush(stdout);
+  
+  pffft_destroy_setup(s);
+  pffft_aligned_free(ref);
+  pffft_aligned_free(in);
+  pffft_aligned_free(out);
+  pffft_aligned_free(tmp);
+  pffft_aligned_free(tmp2);
+}
+
+void pffft_validate(int cplx) {
+  static int Ntest[] = { 16, 32, 64, 96, 128, 160, 192, 256, 288, 384, 5*96, 512, 576, 5*128, 800, 864, 1024, 2048, 2592, 4000, 4096, 12000, 36864, 0};
+  int k;
+  for (k = 0; Ntest[k]; ++k) {
+    int N = Ntest[k];
+    if (N == 16 && !cplx) continue;
+    pffft_validate_N(N, cplx);
+  }
+}
+
+int array_output_format = 0;
+
+void show_output(const char *name, int N, int cplx, float flops, float t0, float t1, int max_iter) {
+  float mflops = flops/1e6/(t1 - t0 + 1e-16);
+  if (array_output_format) {
+    if (flops != -1) {
+      printf("|%9.0f   ", mflops);
+    } else printf("|      n/a   ");
+  } else {
+    if (flops != -1) {
+      printf("N=%5d, %s %16s : %6.0f MFlops [t=%6.0f ns, %d runs]\n", N, (cplx?"CPLX":"REAL"), name, mflops, (t1-t0)/2/max_iter * 1e9, max_iter);
+    }
+  }
+  fflush(stdout);
+}
+
+void benchmark_ffts(int N, int cplx) {
+  int Nfloat = (cplx ? N*2 : N);
+  int Nbytes = Nfloat * sizeof(float);
+  float *X = pffft_aligned_malloc(Nbytes), *Y = pffft_aligned_malloc(Nbytes), *Z = pffft_aligned_malloc(Nbytes);
+
+  double t0, t1, flops;
+
+  int k;
+  int max_iter = 5120000/N*4;
+#ifdef __arm__
+  max_iter /= 4;
+#endif
+  int iter;
+
+  for (k = 0; k < Nfloat; ++k) {
+    X[k] = 0; //sqrtf(k+1);
+  }
+
+  // FFTPack benchmark
+  {
+    float *wrk = malloc(2*Nbytes + 15*sizeof(float));
+    int max_iter_ = max_iter/pffft_simd_size(); if (max_iter_ == 0) max_iter_ = 1;
+    if (cplx) cffti(N, wrk);
+    else      rffti(N, wrk);
+    t0 = uclock_sec();  
+    
+    for (iter = 0; iter < max_iter_; ++iter) {
+      if (cplx) {
+        cfftf(N, X, wrk);
+        cfftb(N, X, wrk);
+      } else {
+        rfftf(N, X, wrk);
+        rfftb(N, X, wrk);
+      }
+    }
+    t1 = uclock_sec();
+    free(wrk);
+    
+    flops = (max_iter_*2) * ((cplx ? 5 : 2.5)*N*log((double)N)/M_LN2); // see http://www.fftw.org/speed/method.html
+    show_output("FFTPack", N, cplx, flops, t0, t1, max_iter_);
+  }
+
+#ifdef HAVE_VECLIB
+  int log2N = (int)(log(N)/log(2) + 0.5f);
+  if (N == (1<<log2N)) {
+    FFTSetup setup;
+
+    setup = vDSP_create_fftsetup(log2N, FFT_RADIX2);
+    DSPSplitComplex zsamples;
+    zsamples.realp = &X[0];
+    zsamples.imagp = &X[Nfloat/2];
+    t0 = uclock_sec();  
+    for (iter = 0; iter < max_iter; ++iter) {
+      if (cplx) {
+        vDSP_fft_zip(setup, &zsamples, 1, log2N, kFFTDirection_Forward);
+        vDSP_fft_zip(setup, &zsamples, 1, log2N, kFFTDirection_Inverse);
+      } else {
+        vDSP_fft_zrip(setup, &zsamples, 1, log2N, kFFTDirection_Forward); 
+        vDSP_fft_zrip(setup, &zsamples, 1, log2N, kFFTDirection_Inverse);
+      }
+    }
+    t1 = uclock_sec();
+    vDSP_destroy_fftsetup(setup);
+
+    flops = (max_iter*2) * ((cplx ? 5 : 2.5)*N*log((double)N)/M_LN2); // see http://www.fftw.org/speed/method.html
+    show_output("vDSP", N, cplx, flops, t0, t1, max_iter);
+  } else {
+    show_output("vDSP", N, cplx, -1, -1, -1, -1);
+  }
+#endif
+  
+#ifdef HAVE_FFTW
+  {
+    fftwf_plan planf, planb;
+    fftw_complex *in = (fftw_complex*) fftwf_malloc(sizeof(fftw_complex) * N);
+    fftw_complex *out = (fftw_complex*) fftwf_malloc(sizeof(fftw_complex) * N);
+    memset(in, 0, sizeof(fftw_complex) * N);
+    int flags = (N < 40000 ? FFTW_MEASURE : FFTW_ESTIMATE);  // measure takes a lot of time on largest ffts
+    //int flags = FFTW_ESTIMATE;
+    if (cplx) {
+      planf = fftwf_plan_dft_1d(N, (fftwf_complex*)in, (fftwf_complex*)out, FFTW_FORWARD, flags);
+      planb = fftwf_plan_dft_1d(N, (fftwf_complex*)in, (fftwf_complex*)out, FFTW_BACKWARD, flags);
+    } else {
+      planf = fftwf_plan_dft_r2c_1d(N, (float*)in, (fftwf_complex*)out, flags);
+      planb = fftwf_plan_dft_c2r_1d(N, (fftwf_complex*)in, (float*)out, flags);
+    }
+
+    t0 = uclock_sec();  
+    for (iter = 0; iter < max_iter; ++iter) {
+      fftwf_execute(planf);
+      fftwf_execute(planb);
+    }
+    t1 = uclock_sec();
+
+    fftwf_destroy_plan(planf);
+    fftwf_destroy_plan(planb);
+    fftwf_free(in); fftwf_free(out);
+
+    flops = (max_iter*2) * ((cplx ? 5 : 2.5)*N*log((double)N)/M_LN2); // see http://www.fftw.org/speed/method.html
+    show_output((flags == FFTW_MEASURE ? "FFTW (meas.)" : " FFTW (estim)"), N, cplx, flops, t0, t1, max_iter);
+  }
+#endif  
+
+  // PFFFT benchmark
+  {
+    PFFFT_Setup *s = pffft_new_setup(N, cplx ? PFFFT_COMPLEX : PFFFT_REAL);
+    if (s) {
+      t0 = uclock_sec();  
+      for (iter = 0; iter < max_iter; ++iter) {
+        pffft_transform(s, X, Z, Y, PFFFT_FORWARD);
+        pffft_transform(s, X, Z, Y, PFFFT_BACKWARD);
+      }
+      t1 = uclock_sec();
+      pffft_destroy_setup(s);
+    
+      flops = (max_iter*2) * ((cplx ? 5 : 2.5)*N*log((double)N)/M_LN2); // see http://www.fftw.org/speed/method.html
+      show_output("PFFFT", N, cplx, flops, t0, t1, max_iter);
+    }
+  }
+
+  if (!array_output_format) {
+    printf("--\n");
+  }
+
+  pffft_aligned_free(X);
+  pffft_aligned_free(Y);
+  pffft_aligned_free(Z);
+}
+
+#ifndef PFFFT_SIMD_DISABLE
+void validate_pffft_simd(); // a small function inside pffft.c that will detect compiler bugs with respect to simd instruction 
+#endif
+
+int main(int argc, char **argv) {
+  int Nvalues[] = { 64, 96, 128, 160, 192, 256, 384, 5*96, 512, 5*128, 3*256, 800, 1024, 2048, 2400, 4096, 8192, 9*1024, 16384, 32768, 256*1024, 1024*1024, -1 };
+  int i;
+
+  if (argc > 1 && strcmp(argv[1], "--array-format") == 0) {
+    array_output_format = 1;
+  }
+
+#ifndef PFFFT_SIMD_DISABLE
+  validate_pffft_simd();
+#endif
+  pffft_validate(1);
+  pffft_validate(0);
+  if (!array_output_format) {
+    for (i=0; Nvalues[i] > 0; ++i) {
+      benchmark_ffts(Nvalues[i], 0 /* real fft */);
+    }
+    for (i=0; Nvalues[i] > 0; ++i) {
+      benchmark_ffts(Nvalues[i], 1 /* cplx fft */);
+    }
+  } else {
+    printf("| input len ");
+    printf("|real FFTPack");
+#ifdef HAVE_VECLIB
+    printf("|  real vDSP ");
+#endif
+#ifdef HAVE_FFTW
+    printf("|  real FFTW ");
+#endif
+    printf("| real PFFFT | ");
+
+    printf("|cplx FFTPack");
+#ifdef HAVE_VECLIB
+    printf("|  cplx vDSP ");
+#endif
+#ifdef HAVE_FFTW
+    printf("|  cplx FFTW ");
+#endif
+    printf("| cplx PFFFT |\n");
+    for (i=0; Nvalues[i] > 0; ++i) {
+      printf("|%9d  ", Nvalues[i]);
+      benchmark_ffts(Nvalues[i], 0); 
+      printf("| ");
+      benchmark_ffts(Nvalues[i], 1);
+      printf("|\n");
+    }
+    printf(" (numbers are given in MFlops)\n");
+  }
+
+
+  return 0;
+}
diff --git a/tools/gritsettings/translation_expectations.pyl b/tools/gritsettings/translation_expectations.pyl
index 328b271..5c94048d 100644
--- a/tools/gritsettings/translation_expectations.pyl
+++ b/tools/gritsettings/translation_expectations.pyl
@@ -29,6 +29,7 @@
       "chrome/app/chromium_strings.grd",
       "chrome/app/generated_resources.grd",
       "chrome/app/google_chrome_strings.grd",
+      "chrome/browser/resources/chromeos/camera/strings/camera_strings.grd",
       "chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd",
       "chrome/browser/resources/chromeos/select_to_speak/strings/select_to_speak_strings.grd",
       "chrome/credential_provider/gaiacp/gaia_resources.grd",
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index baa83fc..9a6c1ad 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -21466,6 +21466,9 @@
       label="V8RTCQuicStream_WaitForWriteBufferedAmountBelow_Method"/>
   <int value="2768" label="V8RTCQuicStream_WaitForReadable_Method"/>
   <int value="2769" label="HTMLTemplateElement"/>
+  <int value="2770" label="NoSysexWebMIDIWithoutPermission"/>
+  <int value="2771" label="NoSysexWebMIDIOnInsecureOrigin"/>
+  <int value="2772" label="ApplicationCacheInstalledButNoManifest"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
diff --git a/ui/gfx/linux/client_native_pixmap_dmabuf.cc b/ui/gfx/linux/client_native_pixmap_dmabuf.cc
index 76f6b0b..dc19c4c 100644
--- a/ui/gfx/linux/client_native_pixmap_dmabuf.cc
+++ b/ui/gfx/linux/client_native_pixmap_dmabuf.cc
@@ -11,9 +11,13 @@
 #include <sys/mman.h>
 #include <xf86drm.h>
 
+#include <utility>
+
 #include "base/memory/ptr_util.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/process/memory.h"
+#include "base/process/process_metrics.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -59,6 +63,24 @@
 
 }  // namespace
 
+ClientNativePixmapDmaBuf::PlaneInfo::PlaneInfo() {}
+
+ClientNativePixmapDmaBuf::PlaneInfo::PlaneInfo(PlaneInfo&& info)
+    : fd(std::move(info.fd)),
+      data(info.data),
+      offset(info.offset),
+      size(info.size) {
+  // Set nullptr to info.data in order not to call munmap in |info| dtor.
+  info.data = nullptr;
+}
+
+ClientNativePixmapDmaBuf::PlaneInfo::~PlaneInfo() {
+  if (data) {
+    int ret = munmap(data, size);
+    DCHECK(!ret);
+  }
+}
+
 // static
 bool ClientNativePixmapDmaBuf::IsConfigurationSupported(
     gfx::BufferFormat format,
@@ -137,59 +159,74 @@
 ClientNativePixmapDmaBuf::ImportFromDmabuf(
     const gfx::NativePixmapHandle& handle,
     const gfx::Size& size) {
-  return base::WrapUnique(new ClientNativePixmapDmaBuf(handle, size));
+  std::array<PlaneInfo, kMaxPlanes> plane_info;
+  for (size_t i = 0; i < handle.fds.size(); ++i) {
+    const auto& fd = handle.fds[i];
+    DCHECK(fd.auto_close);
+    plane_info[i].fd.reset(fd.fd);
+    DCHECK(plane_info[i].fd.is_valid());
+  }
+
+  DCHECK_EQ(handle.planes.size(), handle.fds.size());
+
+  const size_t page_size = base::GetPageSize();
+  for (size_t i = 0; i < handle.fds.size(); ++i) {
+    // mmap() fails if the offset argument is not page-aligned.
+    // Since handle.planes[i].offset is possibly not page-aligned, we
+    // have to map with an additional offset to be aligned to the page.
+    const size_t extra_offset = handle.planes[i].offset % page_size;
+    size_t map_size =
+        base::checked_cast<size_t>(handle.planes[i].size + extra_offset);
+    plane_info[i].offset = extra_offset;
+    plane_info[i].size = map_size;
+
+    void* data =
+        mmap(nullptr, map_size, (PROT_READ | PROT_WRITE), MAP_SHARED,
+             plane_info[i].fd.get(), handle.planes[i].offset - extra_offset);
+    if (data == MAP_FAILED) {
+      logging::SystemErrorCode mmap_error = logging::GetLastSystemErrorCode();
+      if (mmap_error == ENOMEM)
+        base::TerminateBecauseOutOfMemory(map_size);
+      LOG(ERROR) << "Failed to mmap dmabuf: "
+                 << logging::SystemErrorCodeToString(mmap_error);
+      return nullptr;
+    }
+    plane_info[i].data = data;
+  }
+
+  return base::WrapUnique(
+      new ClientNativePixmapDmaBuf(handle, size, std::move(plane_info)));
 }
 
 ClientNativePixmapDmaBuf::ClientNativePixmapDmaBuf(
     const gfx::NativePixmapHandle& handle,
-    const gfx::Size& size)
-    : pixmap_handle_(handle), size_(size), data_{0} {
+    const gfx::Size& size,
+    std::array<PlaneInfo, kMaxPlanes> plane_info)
+    : pixmap_handle_(handle), size_(size), plane_info_(std::move(plane_info)) {
   TRACE_EVENT0("drm", "ClientNativePixmapDmaBuf");
-  // TODO(dcastagna): support multiple fds.
-  DCHECK_EQ(1u, handle.fds.size());
-  DCHECK_GE(handle.fds.front().fd, 0);
-  dmabuf_fd_.reset(handle.fds.front().fd);
-
-  DCHECK_GE(handle.planes.back().size, 0u);
-  size_t map_size = handle.planes.back().offset + handle.planes.back().size;
-  data_ = mmap(nullptr, map_size, (PROT_READ | PROT_WRITE), MAP_SHARED,
-               dmabuf_fd_.get(), 0);
-  if (data_ == MAP_FAILED) {
-    logging::SystemErrorCode mmap_error = logging::GetLastSystemErrorCode();
-    if (mmap_error == ENOMEM)
-      base::TerminateBecauseOutOfMemory(map_size);
-
-    CHECK(false) << "Failed to mmap dmabuf: "
-                 << logging::SystemErrorCodeToString(mmap_error);
-  }
 }
 
 ClientNativePixmapDmaBuf::~ClientNativePixmapDmaBuf() {
   TRACE_EVENT0("drm", "~ClientNativePixmapDmaBuf");
-  size_t map_size =
-      pixmap_handle_.planes.back().offset + pixmap_handle_.planes.back().size;
-  int ret = munmap(data_, map_size);
-  DCHECK(!ret);
 }
 
 bool ClientNativePixmapDmaBuf::Map() {
   TRACE_EVENT0("drm", "DmaBuf:Map");
-  if (data_ != nullptr) {
-    PrimeSyncStart(dmabuf_fd_.get());
-    return true;
-  }
-  return false;
+  for (size_t i = 0; i < pixmap_handle_.planes.size(); ++i)
+    PrimeSyncStart(plane_info_[i].fd.get());
+  return true;
 }
 
 void ClientNativePixmapDmaBuf::Unmap() {
   TRACE_EVENT0("drm", "DmaBuf:Unmap");
-  PrimeSyncEnd(dmabuf_fd_.get());
+  for (size_t i = 0; i < pixmap_handle_.planes.size(); ++i)
+    PrimeSyncEnd(plane_info_[i].fd.get());
 }
 
 void* ClientNativePixmapDmaBuf::GetMemoryAddress(size_t plane) const {
   DCHECK_LT(plane, pixmap_handle_.planes.size());
-  uint8_t* address = reinterpret_cast<uint8_t*>(data_);
-  return address + pixmap_handle_.planes[plane].offset;
+  return static_cast<uint8_t*>(plane_info_[plane].data) +
+         plane_info_[plane].offset;
 }
 
 int ClientNativePixmapDmaBuf::GetStride(size_t plane) const {
diff --git a/ui/gfx/linux/client_native_pixmap_dmabuf.h b/ui/gfx/linux/client_native_pixmap_dmabuf.h
index f5da2b3..ddd9006 100644
--- a/ui/gfx/linux/client_native_pixmap_dmabuf.h
+++ b/ui/gfx/linux/client_native_pixmap_dmabuf.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <array>
 #include <memory>
 
 #include "base/files/scoped_file.h"
@@ -38,13 +39,25 @@
   int GetStride(size_t plane) const override;
 
  private:
+  static constexpr size_t kMaxPlanes = 4;
+
+  struct PlaneInfo {
+    PlaneInfo();
+    PlaneInfo(PlaneInfo&& plane_info);
+    ~PlaneInfo();
+
+    base::ScopedFD fd;
+    void* data = nullptr;
+    size_t offset = 0;
+    size_t size = 0;
+  };
   ClientNativePixmapDmaBuf(const gfx::NativePixmapHandle& handle,
-                           const gfx::Size& size);
+                           const gfx::Size& size,
+                           std::array<PlaneInfo, kMaxPlanes> plane_info);
 
   const gfx::NativePixmapHandle pixmap_handle_;
   const gfx::Size size_;
-  base::ScopedFD dmabuf_fd_;
-  void* data_;
+  const std::array<PlaneInfo, kMaxPlanes> plane_info_;
 
   DISALLOW_COPY_AND_ASSIGN(ClientNativePixmapDmaBuf);
 };
diff --git a/ui/gfx/native_pixmap.h b/ui/gfx/native_pixmap.h
index 1e6fda1..a4cbe0b0 100644
--- a/ui/gfx/native_pixmap.h
+++ b/ui/gfx/native_pixmap.h
@@ -26,6 +26,8 @@
   NativePixmap() {}
 
   virtual bool AreDmaBufFdsValid() const = 0;
+  // TODO(crbug.com/911370): Remove this because the number of fds will always
+  // be equal to the number of planes.
   virtual size_t GetDmaBufFdCount() const = 0;
   virtual int GetDmaBufFd(size_t plane) const = 0;
   virtual int GetDmaBufPitch(size_t plane) const = 0;
diff --git a/ui/gl/gl_image_native_pixmap.cc b/ui/gl/gl_image_native_pixmap.cc
index a29c9c67..0bae74d4 100644
--- a/ui/gl/gl_image_native_pixmap.cc
+++ b/ui/gl/gl_image_native_pixmap.cc
@@ -181,8 +181,7 @@
 
       size_t pixmap_plane = attrs_plane;
 
-      attrs.push_back(pixmap->GetDmaBufFd(
-          pixmap_plane < pixmap->GetDmaBufFdCount() ? pixmap_plane : 0));
+      attrs.push_back(pixmap->GetDmaBufFd(pixmap_plane));
       attrs.push_back(EGL_DMA_BUF_PLANE0_OFFSET_EXT + attrs_plane * 3);
       attrs.push_back(pixmap->GetDmaBufOffset(pixmap_plane));
       attrs.push_back(EGL_DMA_BUF_PLANE0_PITCH_EXT + attrs_plane * 3);
diff --git a/ui/gl/test/gl_image_test_template.h b/ui/gl/test/gl_image_test_template.h
index a9fa046..edc0f1d 100644
--- a/ui/gl/test/gl_image_test_template.h
+++ b/ui/gl/test/gl_image_test_template.h
@@ -80,8 +80,8 @@
     return;
 
   // NOTE: On some drm devices (mediatek) the mininum width/height to add an fb
-  // for a bo must be 64.
-  const gfx::Size small_image_size(64, 64);
+  // for a bo must be 64, and YVU_420 in i915 requires at least 128 length.
+  const gfx::Size small_image_size(128, 128);
   const gfx::Size large_image_size(512, 512);
   const uint8_t* image_color = this->delegate_.GetImageColor();
 
diff --git a/ui/ozone/common/linux/gbm_buffer.h b/ui/ozone/common/linux/gbm_buffer.h
index c6e0dac..e72b531 100644
--- a/ui/ozone/common/linux/gbm_buffer.h
+++ b/ui/ozone/common/linux/gbm_buffer.h
@@ -23,6 +23,8 @@
   virtual uint32_t GetFormat() const = 0;
   virtual uint64_t GetFormatModifier() const = 0;
   virtual uint32_t GetFlags() const = 0;
+  // TODO(crbug.com/911370): Remove this because the number of fds will always
+  // be equal to the number of planes.
   virtual size_t GetFdCount() const = 0;
   // TODO(reveman): This should not be needed once crbug.com/597932 is
   // fixed, as the size would be queried directly from the underlying bo.
diff --git a/ui/ozone/common/linux/gbm_wrapper.cc b/ui/ozone/common/linux/gbm_wrapper.cc
index 15efd7b..0e0a9b56 100644
--- a/ui/ozone/common/linux/gbm_wrapper.cc
+++ b/ui/ozone/common/linux/gbm_wrapper.cc
@@ -38,7 +38,9 @@
         flags_(flags),
         fds_(std::move(fds)),
         size_(size),
-        planes_(std::move(planes)) {}
+        planes_(std::move(planes)) {
+    DCHECK_EQ(fds_.size(), planes_.size());
+  }
 
   ~Buffer() override {
     DCHECK(!mmap_data_);
@@ -93,16 +95,13 @@
     // TODO(dcastagna): Use gbm_bo_get_num_planes once all the formats we use
     // are supported by gbm.
     for (size_t i = 0; i < gfx::NumberOfPlanesForBufferFormat(format); ++i) {
-      // Some formats (e.g: YVU_420) might have less than one fd per plane.
-      if (i < fds_.size()) {
-        base::ScopedFD scoped_fd(HANDLE_EINTR(dup(GetPlaneFd(i))));
-        if (!scoped_fd.is_valid()) {
-          PLOG(ERROR) << "dup";
-          return gfx::NativePixmapHandle();
-        }
-        handle.fds.emplace_back(
-            base::FileDescriptor(scoped_fd.release(), true /* auto_close */));
+      base::ScopedFD scoped_fd(HANDLE_EINTR(dup(GetPlaneFd(i))));
+      if (!scoped_fd.is_valid()) {
+        PLOG(ERROR) << "dup";
+        return gfx::NativePixmapHandle();
       }
+      handle.fds.emplace_back(
+          base::FileDescriptor(scoped_fd.release(), true /* auto_close */));
       handle.planes.emplace_back(GetPlaneStride(i), GetPlaneOffset(i),
                                  GetPlaneSize(i), GetFormatModifier());
     }
@@ -161,16 +160,12 @@
     // kept open for the lifetime of the buffer.
     base::ScopedFD fd(gbm_bo_get_plane_fd(bo, i));
 
-    // TODO(dcastagna): support multiple fds.
-    // crbug.com/642410
-    if (!i) {
-      if (!fd.is_valid()) {
-        PLOG(ERROR) << "Failed to export buffer to dma_buf";
-        gbm_bo_destroy(bo);
-        return nullptr;
-      }
-      fds.emplace_back(std::move(fd));
+    if (!fd.is_valid()) {
+      PLOG(ERROR) << "Failed to export buffer to dma_buf";
+      gbm_bo_destroy(bo);
+      return nullptr;
     }
+    fds.emplace_back(std::move(fd));
 
     planes.emplace_back(gbm_bo_get_plane_stride(bo, i),
                         gbm_bo_get_plane_offset(bo, i),
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
index 6d08b23..c9c5c56 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
@@ -244,15 +244,15 @@
     gfx::BufferFormat format,
     const gfx::NativePixmapHandle& handle) {
   size_t num_planes = gfx::NumberOfPlanesForBufferFormat(format);
-  if (handle.planes.size() != num_planes ||
-      (handle.fds.size() != 1 && handle.fds.size() != num_planes)) {
+  DCHECK_GE(num_planes, handle.fds.size());
+  if (handle.planes.size() != num_planes) {
     return nullptr;
   }
+
   std::vector<base::ScopedFD> scoped_fds;
   for (auto& fd : handle.fds) {
     scoped_fds.emplace_back(fd.fd);
   }
-
   std::vector<gfx::NativePixmapPlane> planes;
   for (const auto& plane : handle.planes) {
     planes.push_back(plane);