diff --git a/DEPS b/DEPS
index 785bf02631..ffc73bd5 100644
--- a/DEPS
+++ b/DEPS
@@ -96,7 +96,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': '33a6a6490c5eee24cdccc9692fb0f1b29703fa02',
+  'catapult_revision': '326eb241976eb5c38a648b75c0ff94c5b667a3d7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -288,9 +288,6 @@
   'src/third_party/boringssl/src':
     Var('boringssl_git') + '/boringssl.git' + '@' +  Var('boringssl_revision'),
 
-  'src/third_party/py_trace_event/src':
-    Var('chromium_git') + '/external/py_trace_event.git' + '@' + 'dd463ea9e2c430de2b9e53dea57a77b4c3ac9b30',
-
   'src/third_party/dom_distiller_js/dist':
     Var('chromium_git') + '/external/github.com/chromium/dom-distiller-dist.git' + '@' + '232c293a4d3ebcbc4320f642af017ee054b3be93',
 
@@ -788,19 +785,6 @@
                 '-d', 'src/tools/luci-go/linux64',
     ],
   },
-  # Pull eu-strip binaries using checked-in hashes.
-  {
-    'name': 'eu-strip',
-    'pattern': '.',
-    'action': [ 'python',
-                'src/third_party/depot_tools/download_from_google_storage.py',
-                '--no_resume',
-                '--platform=linux*',
-                '--no_auth',
-                '--bucket', 'chromium-eu-strip',
-                '-s', 'src/build/linux/bin/eu-strip.sha1',
-    ],
-  },
   {
     'name': 'drmemory',
     'pattern': '.',
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index 22d70ed..d4a3b10 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -78,8 +78,7 @@
 std::unique_ptr<net::ProxyConfigService> CreateProxyConfigService() {
   std::unique_ptr<net::ProxyConfigService> config_service =
       net::ProxyService::CreateSystemProxyConfigService(
-          BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
-          nullptr /* Ignored on Android */);
+          BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
 
   // TODO(csharrison) Architect the wrapper better so we don't need a cast for
   // android ProxyConfigServices.
diff --git a/android_webview/renderer/aw_render_frame_ext.cc b/android_webview/renderer/aw_render_frame_ext.cc
index 36449ae5..c870868 100644
--- a/android_webview/renderer/aw_render_frame_ext.cc
+++ b/android_webview/renderer/aw_render_frame_ext.cc
@@ -186,15 +186,17 @@
 }
 
 void AwRenderFrameExt::OnDocumentHasImagesRequest(uint32_t id) {
-  bool hasImages = false;
-  blink::WebView* webview = GetWebView();
-  if (webview) {
-    blink::WebDocument document = webview->MainFrame()->GetDocument();
-    const blink::WebElement child_img = GetImgChild(document);
-    hasImages = !child_img.IsNull();
-  }
-  Send(
-      new AwViewHostMsg_DocumentHasImagesResponse(routing_id(), id, hasImages));
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
+
+  // AwViewMsg_DocumentHasImages should only be sent to the main frame.
+  DCHECK(frame);
+  DCHECK(!frame->Parent());
+
+  const blink::WebElement child_img = GetImgChild(frame->GetDocument());
+  bool has_images = !child_img.IsNull();
+
+  Send(new AwViewHostMsg_DocumentHasImagesResponse(routing_id(), id,
+                                                   has_images));
 }
 
 void AwRenderFrameExt::FocusedNodeChanged(const blink::WebNode& node) {
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 8288bc8..32a9a45c 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -2908,6 +2908,42 @@
   EXPECT_EQ(id2, stored.placement_list[0].display_id);
 }
 
+TEST_F(DisplayManagerTest, AccelerometerSupport) {
+  display::test::DisplayManagerTestApi(display_manager())
+      .SetFirstDisplayAsInternalDisplay();
+  display::Screen* screen = display::Screen::GetScreen();
+  EXPECT_EQ(display::Display::ACCELEROMETER_SUPPORT_UNAVAILABLE,
+            screen->GetPrimaryDisplay().accelerometer_support());
+
+  display_manager()->set_internal_display_has_accelerometer(true);
+  display_manager()->UpdateDisplays();
+  EXPECT_EQ(display::Display::ACCELEROMETER_SUPPORT_AVAILABLE,
+            screen->GetPrimaryDisplay().accelerometer_support());
+
+  UpdateDisplay("1000x1000,800x800");
+  EXPECT_EQ(display::Display::ACCELEROMETER_SUPPORT_AVAILABLE,
+            screen->GetPrimaryDisplay().accelerometer_support());
+  EXPECT_EQ(display::Display::ACCELEROMETER_SUPPORT_UNAVAILABLE,
+            display_manager()->GetSecondaryDisplay().accelerometer_support());
+
+  // Secondary is now primary and should not have accelerometer support.
+  std::vector<display::ManagedDisplayInfo> display_info_list;
+  display_info_list.push_back(
+      CreateDisplayInfo(display_manager()->GetSecondaryDisplay().id(),
+                        gfx::Rect(1, 1, 100, 100)));
+  display_manager()->OnNativeDisplaysChanged(display_info_list);
+  EXPECT_EQ(display::Display::ACCELEROMETER_SUPPORT_UNAVAILABLE,
+            screen->GetPrimaryDisplay().accelerometer_support());
+
+  // Re-enable internal display.
+  display_info_list.clear();
+  display_info_list.push_back(CreateDisplayInfo(
+      display::Display::InternalDisplayId(), gfx::Rect(1, 1, 100, 100)));
+  display_manager()->OnNativeDisplaysChanged(display_info_list);
+  EXPECT_EQ(display::Display::ACCELEROMETER_SUPPORT_AVAILABLE,
+            screen->GetPrimaryDisplay().accelerometer_support());
+}
+
 namespace {
 
 class DisplayManagerOrientationTest : public DisplayManagerTest {
diff --git a/ash/display/screen_ash.cc b/ash/display/screen_ash.cc
index e1a7a9ea..e70460f 100644
--- a/ash/display/screen_ash.cc
+++ b/ash/display/screen_ash.cc
@@ -4,12 +4,14 @@
 
 #include "ash/display/screen_ash.h"
 
+#include "ash/ash_switches.h"
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/root_window_controller.h"
 #include "ash/root_window_settings.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/wm/root_window_finder.h"
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/env.h"
@@ -184,7 +186,13 @@
   // use ash's screen.
   if (!current || current == screen_for_shutdown)
     display::Screen::SetScreenInstance(screen.get());
-  return new display::DisplayManager(std::move(screen));
+  display::DisplayManager* manager =
+      new display::DisplayManager(std::move(screen));
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kAshEnableTouchView)) {
+    manager->set_internal_display_has_accelerometer(true);
+  }
+  return manager;
 }
 
 // static
diff --git a/ash/frame/custom_frame_view_ash.cc b/ash/frame/custom_frame_view_ash.cc
index 7fd4fed..f6b23fe 100644
--- a/ash/frame/custom_frame_view_ash.cc
+++ b/ash/frame/custom_frame_view_ash.cc
@@ -376,9 +376,16 @@
     return gfx::Size();
 
   gfx::Size min_client_view_size(frame_->client_view()->GetMinimumSize());
-  return gfx::Size(
+  gfx::Size min_size(
       std::max(header_view_->GetMinimumWidth(), min_client_view_size.width()),
       NonClientTopBorderHeight() + min_client_view_size.height());
+
+  aura::Window* frame_window = frame_->GetNativeWindow();
+  const gfx::Size* min_window_size =
+      frame_window->GetProperty(aura::client::kMinimumSize);
+  if (min_window_size)
+    min_size.SetToMax(*min_window_size);
+  return min_size;
 }
 
 gfx::Size CustomFrameViewAsh::GetMaximumSize() const {
diff --git a/ash/frame/custom_frame_view_ash_unittest.cc b/ash/frame/custom_frame_view_ash_unittest.cc
index b524b63..33b4c71 100644
--- a/ash/frame/custom_frame_view_ash_unittest.cc
+++ b/ash/frame/custom_frame_view_ash_unittest.cc
@@ -173,6 +173,25 @@
             max_frame_size.height());
 }
 
+// Verify that CustomFrameViewAsh returns the correct minimum frame size when
+// the kMinimumSize property is set.
+TEST_F(CustomFrameViewAshTest, HonorsMinimumSizeProperty) {
+  const gfx::Size min_client_size(500, 500);
+  TestWidgetConstraintsDelegate* delegate = new TestWidgetConstraintsDelegate;
+  delegate->set_minimum_size(min_client_size);
+  std::unique_ptr<views::Widget> widget(CreateWidget(delegate));
+
+  // Update the native window's minimum size property.
+  const gfx::Size min_window_size(600, 700);
+  widget->GetNativeWindow()->SetProperty(aura::client::kMinimumSize,
+                                         new gfx::Size(min_window_size));
+
+  CustomFrameViewAsh* custom_frame_view = delegate->custom_frame_view();
+  const gfx::Size min_frame_size = custom_frame_view->GetMinimumSize();
+
+  EXPECT_EQ(min_window_size, min_frame_size);
+}
+
 // Verify that CustomFrameViewAsh updates the avatar icon based on the
 // avatar icon window property.
 TEST_F(CustomFrameViewAshTest, AvatarIcon) {
diff --git a/ash/public/cpp/mus_property_mirror_ash.cc b/ash/public/cpp/mus_property_mirror_ash.cc
index a9b634ee..66579aa 100644
--- a/ash/public/cpp/mus_property_mirror_ash.cc
+++ b/ash/public/cpp/mus_property_mirror_ash.cc
@@ -47,6 +47,8 @@
   } else if (key == aura::client::kDrawAttentionKey) {
     bool value = window->GetProperty(aura::client::kDrawAttentionKey);
     root_window->SetProperty(aura::client::kDrawAttentionKey, value);
+  } else if (key == aura::client::kMinimumSize) {
+    MirrorOwnedProperty(window, root_window, aura::client::kMinimumSize);
   } else if (key == aura::client::kTitleKey) {
     MirrorOwnedProperty(window, root_window, aura::client::kTitleKey);
   } else if (key == aura::client::kWindowIconKey) {
diff --git a/base/trace_event/heap_profiler_allocation_register_posix.cc b/base/trace_event/heap_profiler_allocation_register_posix.cc
index 94eeb4d..e0e61ce 100644
--- a/base/trace_event/heap_profiler_allocation_register_posix.cc
+++ b/base/trace_event/heap_profiler_allocation_register_posix.cc
@@ -16,6 +16,11 @@
 #define MAP_ANONYMOUS MAP_ANON
 #endif
 
+#if defined(OS_FUCHSIA)
+#include <magenta/process.h>
+#include <magenta/syscalls.h>
+#endif  // OS_FUCHSIA
+
 namespace base {
 namespace trace_event {
 namespace internal {
@@ -32,25 +37,53 @@
   // Add space for a guard page at the end.
   size_t map_size = size + GetGuardSize();
 
+#if defined(OS_FUCHSIA)
+  // Fuchsia does not currently support PROT_NONE, see MG-546 upstream. Instead,
+  // create a virtual mapping with the size of the guard page unmapped after the
+  // block.
+  mx_handle_t vmo;
+  CHECK(mx_vmo_create(map_size, 0, &vmo) == MX_OK);
+  mx_handle_t vmar;
+  uintptr_t addr_uint;
+  CHECK(mx_vmar_allocate(mx_vmar_root_self(), 0, map_size,
+                         MX_VM_FLAG_CAN_MAP_READ | MX_VM_FLAG_CAN_MAP_WRITE |
+                             MX_VM_FLAG_CAN_MAP_SPECIFIC,
+                         &vmar, &addr_uint) == MX_OK);
+  CHECK(mx_vmar_map(
+            vmar, 0, vmo, 0, size,
+            MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE | MX_VM_FLAG_SPECIFIC,
+            &addr_uint) == MX_OK);
+  CHECK(mx_handle_close(vmar) == MX_OK);
+  CHECK(mx_handle_close(vmo) == MX_OK);
+  void* addr = reinterpret_cast<void*>(addr_uint);
+#else
   void* addr = mmap(nullptr, map_size, PROT_READ | PROT_WRITE,
                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-
   PCHECK(addr != MAP_FAILED);
 
   // Mark the last page of the allocated address space as inaccessible
-  // (PROT_NONE). The read/write accessible space is still at least |min_size|
+  // (PROT_NONE). The read/write accessible space is still at least |size|
   // bytes.
   void* guard_addr =
       reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) + size);
   int result = mprotect(guard_addr, GetGuardSize(), PROT_NONE);
   PCHECK(result == 0);
+#endif  // defined(OS_FUCHSIA)
 
   return addr;
 }
 
 void FreeGuardedVirtualMemory(void* address, size_t allocated_size) {
   size_t size = bits::Align(allocated_size, GetPageSize()) + GetGuardSize();
+#if defined(OS_FUCHSIA)
+  mx_status_t status = mx_vmar_unmap(
+      mx_vmar_root_self(), reinterpret_cast<uintptr_t>(address), size);
+  if (status != MX_OK) {
+    DLOG(ERROR) << "mx_vmar_unmap failed, status=" << status;
+  }
+#else
   munmap(address, size);
+#endif
 }
 
 }  // namespace internal
diff --git a/build/.gitignore b/build/.gitignore
index 7459415..c58605c 100644
--- a/build/.gitignore
+++ b/build/.gitignore
@@ -13,7 +13,6 @@
 /util/LASTCHANGE*
 /util/support
 /x64/
-/linux/bin/eu-strip
 /linux/debian_*-sysroot/
 /linux/ubuntu_*-sysroot/
 /ios_files
diff --git a/build/linux/bin/eu-strip.sha1 b/build/linux/bin/eu-strip.sha1
deleted file mode 100644
index 43f290a..0000000
--- a/build/linux/bin/eu-strip.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0a9b8f68615ce388b65201e6d22da7a9cf2e729c
\ No newline at end of file
diff --git a/build/toolchain/toolchain.gni b/build/toolchain/toolchain.gni
index 63ecdbd..70e0fa9 100644
--- a/build/toolchain/toolchain.gni
+++ b/build/toolchain/toolchain.gni
@@ -16,14 +16,12 @@
   allow_posix_link_time_opt =
       is_clang && target_os == "linux" && !is_chromeos && target_cpu == "x64" &&
       is_official_build
-}
 
-declare_args() {
-  # If used with allow_posix_link_time_opt, it enables support for ThinLTO,
-  # which links 3x-10x faster than full LTO. See also
+  # If used with allow_posix_link_time_opt, it enables the experimental support
+  # of ThinLTO that links 3x-10x faster but (as of now) does not have all the
+  # important optimizations such us devirtualization implemented. See also
   # http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html
-  use_thin_lto = allow_posix_link_time_opt && target_os == "linux" &&
-                 !is_chromeos && target_cpu == "x64"
+  use_thin_lto = false
 
   # If this is set to true, or if LLVM_FORCE_HEAD_REVISION is set to 1
   # in the environment, we use the revision in the llvm repo to determine
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index 0d7ac4d0..7ca0ff8 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -2339,6 +2339,7 @@
   EXPECT_TRUE(pending_layer()->tilings()->FindTilingWithScaleKey(1.f));
   EXPECT_EQ(0u, active_layer()->tilings()->num_tilings());
 
+  host_impl()->NotifyReadyToActivate();
   ActivateTree();
   EXPECT_TRUE(active_layer()->tilings()->FindTilingWithScaleKey(1.f));
 
@@ -2355,6 +2356,7 @@
   EXPECT_EQ(2u, active_layer()->release_tile_resources_count());
   EXPECT_EQ(2u, pending_layer()->release_resources_count());
   EXPECT_EQ(2u, active_layer()->release_resources_count());
+  host_impl()->NotifyReadyToActivate();
 
   host_impl()->SetHasGpuRasterizationTrigger(true);
   host_impl()->SetContentIsSuitableForGpuRasterization(false);
@@ -4738,6 +4740,7 @@
   host_impl()->CommitComplete();
   EXPECT_EQ(host_impl()->gpu_rasterization_status(),
             GpuRasterizationStatus::OFF_VIEWPORT);
+  host_impl()->NotifyReadyToActivate();
 
   // Default tile-size for large layers.
   result = layer->CalculateTileSize(gfx::Size(10000, 10000));
@@ -4761,6 +4764,7 @@
   EXPECT_EQ(host_impl()->gpu_rasterization_status(),
             GpuRasterizationStatus::ON);
   host_impl()->SetViewportSize(gfx::Size(2000, 2000));
+  host_impl()->NotifyReadyToActivate();
 
   layer->set_gpu_raster_max_texture_size(host_impl()->device_viewport_size());
   result = layer->CalculateTileSize(gfx::Size(10000, 10000));
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 67f5b43ff..6dbf6453 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -161,6 +161,8 @@
 
 DEFINE_SCOPED_UMA_HISTOGRAM_TIMER(PendingTreeDurationHistogramTimer,
                                   "Scheduling.%s.PendingTreeDuration");
+DEFINE_SCOPED_UMA_HISTOGRAM_TIMER(PendingTreeRasterDurationHistogramTimer,
+                                  "Scheduling.%s.PendingTreeRasterDuration");
 
 LayerTreeHostImpl::FrameData::FrameData()
     : render_surface_list(nullptr),
@@ -387,6 +389,10 @@
     // Scheduler to wait for ReadyToDraw signal to avoid Checkerboard.
     if (CommitToActiveTree())
       NotifyReadyToDraw();
+  } else if (!CommitToActiveTree()) {
+    DCHECK(!pending_tree_raster_duration_timer_);
+    pending_tree_raster_duration_timer_ =
+        base::MakeUnique<PendingTreeRasterDurationHistogramTimer>();
   }
 }
 
@@ -1376,6 +1382,7 @@
 }
 
 void LayerTreeHostImpl::NotifyReadyToActivate() {
+  pending_tree_raster_duration_timer_.reset();
   client_->NotifyReadyToActivate();
 }
 
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 611f24b..b4007e0 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -66,6 +66,7 @@
 class MutatorHost;
 class PageScaleAnimation;
 class PendingTreeDurationHistogramTimer;
+class PendingTreeRasterDurationHistogramTimer;
 class RasterTilePriorityQueue;
 class RasterBufferProvider;
 class RenderingStatsInstrumentation;
@@ -861,6 +862,8 @@
 
   std::unique_ptr<PendingTreeDurationHistogramTimer>
       pending_tree_duration_timer_;
+  std::unique_ptr<PendingTreeRasterDurationHistogramTimer>
+      pending_tree_raster_duration_timer_;
 
   // The id of the scroll node to which scroll animations must latch.
   // This gets reset at ScrollAnimatedBegin, and updated the first time that a
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 5137129..d5e34a6 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -8733,12 +8733,15 @@
   host_impl_->SetHasGpuRasterizationTrigger(false);
   host_impl_->CommitComplete();
   EXPECT_FALSE(host_impl_->RequiresHighResToDraw());
+  host_impl_->NotifyReadyToActivate();
   host_impl_->SetHasGpuRasterizationTrigger(true);
   host_impl_->CommitComplete();
   EXPECT_TRUE(host_impl_->RequiresHighResToDraw());
+  host_impl_->NotifyReadyToActivate();
   host_impl_->SetHasGpuRasterizationTrigger(false);
   host_impl_->CommitComplete();
   EXPECT_TRUE(host_impl_->RequiresHighResToDraw());
+  host_impl_->NotifyReadyToActivate();
 
   host_impl_->ResetRequiresHighResToDraw();
 
@@ -8746,6 +8749,7 @@
   host_impl_->SetHasGpuRasterizationTrigger(true);
   host_impl_->CommitComplete();
   EXPECT_TRUE(host_impl_->RequiresHighResToDraw());
+  host_impl_->NotifyReadyToActivate();
 }
 
 class LayerTreeHostImplTestPrepareTiles : public LayerTreeHostImplTest {
@@ -11842,12 +11846,14 @@
   EXPECT_EQ(GpuRasterizationStatus::OFF_VIEWPORT,
             host_impl_->gpu_rasterization_status());
   EXPECT_FALSE(host_impl_->use_gpu_rasterization());
+  host_impl_->NotifyReadyToActivate();
 
   // Toggle the trigger on.
   host_impl_->SetHasGpuRasterizationTrigger(true);
   host_impl_->CommitComplete();
   EXPECT_EQ(GpuRasterizationStatus::ON, host_impl_->gpu_rasterization_status());
   EXPECT_TRUE(host_impl_->use_gpu_rasterization());
+  host_impl_->NotifyReadyToActivate();
 
   // And off.
   host_impl_->SetHasGpuRasterizationTrigger(false);
@@ -11855,6 +11861,7 @@
   EXPECT_EQ(GpuRasterizationStatus::OFF_VIEWPORT,
             host_impl_->gpu_rasterization_status());
   EXPECT_FALSE(host_impl_->use_gpu_rasterization());
+  host_impl_->NotifyReadyToActivate();
 }
 
 // Tests that SetContentIsSuitableForGpuRasterization behaves as expected.
@@ -11875,6 +11882,7 @@
   EXPECT_EQ(GpuRasterizationStatus::MSAA_CONTENT,
             host_impl_->gpu_rasterization_status());
   EXPECT_TRUE(host_impl_->use_msaa());
+  host_impl_->NotifyReadyToActivate();
 
   // Toggle suitability on.
   host_impl_->SetContentIsSuitableForGpuRasterization(true);
@@ -11882,6 +11890,7 @@
   EXPECT_EQ(GpuRasterizationStatus::ON, host_impl_->gpu_rasterization_status());
   EXPECT_TRUE(host_impl_->use_gpu_rasterization());
   EXPECT_FALSE(host_impl_->use_msaa());
+  host_impl_->NotifyReadyToActivate();
 
   // And off.
   host_impl_->SetContentIsSuitableForGpuRasterization(false);
@@ -11890,6 +11899,7 @@
             host_impl_->gpu_rasterization_status());
   EXPECT_TRUE(host_impl_->use_gpu_rasterization());
   EXPECT_TRUE(host_impl_->use_msaa());
+  host_impl_->NotifyReadyToActivate();
 }
 
 // Tests that SetDeviceScaleFactor correctly impacts GPU rasterization.
@@ -11910,6 +11920,7 @@
   host_impl_->CommitComplete();
   EXPECT_EQ(GpuRasterizationStatus::ON, host_impl_->gpu_rasterization_status());
   EXPECT_TRUE(host_impl_->use_gpu_rasterization());
+  host_impl_->NotifyReadyToActivate();
 
   // Set device scale factor to 2, which lowers the required MSAA samples from
   // 8 to 4.
@@ -11919,6 +11930,7 @@
             host_impl_->gpu_rasterization_status());
   EXPECT_TRUE(host_impl_->use_gpu_rasterization());
   EXPECT_TRUE(host_impl_->use_msaa());
+  host_impl_->NotifyReadyToActivate();
 
   // Set device scale factor back to 1.
   host_impl_->active_tree()->SetDeviceScaleFactor(1.0f);
@@ -11926,6 +11938,7 @@
   EXPECT_EQ(GpuRasterizationStatus::ON, host_impl_->gpu_rasterization_status());
   EXPECT_TRUE(host_impl_->use_gpu_rasterization());
   EXPECT_FALSE(host_impl_->use_msaa());
+  host_impl_->NotifyReadyToActivate();
 }
 
 // Tests that explicit MSAA sample count correctly impacts GPU rasterization.
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 9667101..06d784e8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -697,6 +697,10 @@
                 mLayoutManager = new LayoutManagerChromeTablet(compositorViewHolder);
             } else {
                 mLayoutManager = new LayoutManagerChromePhone(compositorViewHolder);
+                if (getBottomSheet() != null) {
+                    ((LayoutManagerChromePhone) mLayoutManager)
+                            .setForegroundTabAnimationDisabled(true);
+                }
             }
             mLayoutManager.setEnableAnimations(DeviceClassManager.enableAnimations());
             mLayoutManager.addOverviewModeObserver(this);
@@ -1045,6 +1049,11 @@
         }
         if (mLayoutManager != null && mLayoutManager.overviewVisible()
                 && mIsAccessibilityEnabled != enabled) {
+            if (getBottomSheet() != null && getBottomSheet().isShowingNewTab()) {
+                // Close the bottom sheet immediately before hiding the overview since the
+                // BottomSheet NTP UI requires overview mode to be showing.
+                getBottomSheet().setSheetState(BottomSheet.SHEET_STATE_PEEK, false);
+            }
             mLayoutManager.hideOverview(false);
             if (getTabModelSelector().getCurrentModel().getCount() == 0) {
                 getCurrentTabCreator().launchNTP();
@@ -1086,20 +1095,22 @@
                             : TabOpenType.OPEN_NEW_TAB;
                 }
 
-                // Either a new tab is opening, a tab is being clobbered, or a tab is being
-                // brought to the front. In all scenarios, the bottom sheet should be closed.
-                // If a new tab is being created from a launcher shortcut, close the panel without
-                // animation because the panel will be re-opened immediately. If a tab is being
-                // brought to the front, this indicates the user is coming back to Chrome through
-                // external means (e.g. homescreen shortcut, media notification) and animating the
-                // sheet closing is extraneous.
-                boolean animateSheetClose = !fromLauncherShortcut
-                        && (tabOpenType == TabOpenType.CLOBBER_CURRENT_TAB
-                                   || tabOpenType == TabOpenType.OPEN_NEW_TAB
-                                   || tabOpenType == TabOpenType.OPEN_NEW_INCOGNITO_TAB);
-                getBottomSheet().getBottomSheetMetrics().setSheetCloseReason(
-                        BottomSheetMetrics.CLOSED_BY_NAVIGATION);
-                getBottomSheet().setSheetState(BottomSheet.SHEET_STATE_PEEK, animateSheetClose);
+                if (!NewTabPage.isNTPUrl(url)) {
+                    // Either a url is being loaded in a new tab, a tab is being clobbered, or a tab
+                    // is being brought to the front. In all scenarios, the bottom sheet should be
+                    // closed. If a new tab is being created from a launcher shortcut, close the
+                    // panel without animation because the panel will be re-opened immediately. If
+                    // a tab is being brought to the front, this indicates the user is coming back
+                    // to Chrome through external means (e.g. homescreen shortcut, media
+                    // notification) and animating the sheet closing is extraneous.
+                    boolean animateSheetClose = !fromLauncherShortcut
+                            && (tabOpenType == TabOpenType.CLOBBER_CURRENT_TAB
+                                       || tabOpenType == TabOpenType.OPEN_NEW_TAB
+                                       || tabOpenType == TabOpenType.OPEN_NEW_INCOGNITO_TAB);
+                    getBottomSheet().getBottomSheetMetrics().setSheetCloseReason(
+                            BottomSheetMetrics.CLOSED_BY_NAVIGATION);
+                    getBottomSheet().setSheetState(BottomSheet.SHEET_STATE_PEEK, animateSheetClose);
+                }
             }
 
             // We send this intent so that we can enter WebVr presentation mode if needed. This
@@ -1828,7 +1839,7 @@
      */
     private Tab launchIntent(String url, String referer, String headers,
             String externalAppId, boolean forceNewTab, Intent intent) {
-        if (mUIInitialized) {
+        if (mUIInitialized && (getBottomSheet() == null || !NewTabPage.isNTPUrl(url))) {
             mLayoutManager.hideOverview(false);
             getToolbarManager().finishAnimations();
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LauncherShortcutActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/LauncherShortcutActivity.java
index 0c9e1a3a..2905a933 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/LauncherShortcutActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/LauncherShortcutActivity.java
@@ -17,6 +17,7 @@
 import android.os.Bundle;
 import android.os.StrictMode;
 import android.provider.Browser;
+import android.support.annotation.VisibleForTesting;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.chrome.R;
@@ -31,8 +32,8 @@
  * A helper activity for routing launcher shortcut intents.
  */
 public class LauncherShortcutActivity extends Activity {
-    private static final String ACTION_OPEN_NEW_TAB = "chromium.shortcut.action.OPEN_NEW_TAB";
-    private static final String ACTION_OPEN_NEW_INCOGNITO_TAB =
+    public static final String ACTION_OPEN_NEW_TAB = "chromium.shortcut.action.OPEN_NEW_TAB";
+    public static final String ACTION_OPEN_NEW_INCOGNITO_TAB =
             "chromium.shortcut.action.OPEN_NEW_INCOGNITO_TAB";
     private static final String DYNAMIC_OPEN_NEW_INCOGNITO_TAB_ID =
             "dynamic-new-incognito-tab-shortcut";
@@ -51,18 +52,7 @@
             return;
         }
 
-        Intent newIntent = new Intent();
-        newIntent.setAction(Intent.ACTION_VIEW);
-        newIntent.setData(Uri.parse(UrlConstants.NTP_URL));
-        newIntent.setClass(this, ChromeLauncherActivity.class);
-        newIntent.putExtra(IntentHandler.EXTRA_INVOKED_FROM_SHORTCUT, true);
-        newIntent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
-        newIntent.putExtra(Browser.EXTRA_APPLICATION_ID, getPackageName());
-        IntentHandler.addTrustedIntentExtras(newIntent);
-
-        if (intentAction.equals(ACTION_OPEN_NEW_INCOGNITO_TAB)) {
-            newIntent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, true);
-        }
+        Intent newIntent = getChromeLauncherActivityIntent(this, intentAction);
 
         // This system call is often modified by OEMs and not actionable. http://crbug.com/619646.
         StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
@@ -134,4 +124,29 @@
         shortcutManager.disableShortcuts(shortcutList);
         shortcutManager.removeDynamicShortcuts(shortcutList);
     }
+
+    /**
+     * @param context The context used to get the package and set the intent class.
+     * @param launcherShortcutIntentAction The intent action that launched the
+     *                                     LauncherShortcutActivity.
+     * @return An intent for ChromeLauncherActivity that will open a new regular or incognito tab.
+     */
+    @VisibleForTesting
+    public static Intent getChromeLauncherActivityIntent(
+            Context context, String launcherShortcutIntentAction) {
+        Intent newIntent = new Intent();
+        newIntent.setAction(Intent.ACTION_VIEW);
+        newIntent.setData(Uri.parse(UrlConstants.NTP_URL));
+        newIntent.setClass(context, ChromeLauncherActivity.class);
+        newIntent.putExtra(IntentHandler.EXTRA_INVOKED_FROM_SHORTCUT, true);
+        newIntent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
+        newIntent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
+        IntentHandler.addTrustedIntentExtras(newIntent);
+
+        if (launcherShortcutIntentAction.equals(ACTION_OPEN_NEW_INCOGNITO_TAB)) {
+            newIntent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, true);
+        }
+
+        return newIntent;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
index 4feeb81..a8cbfec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
@@ -127,14 +127,12 @@
             // smoothly.
             getActiveLayout().onTabCreating(sourceId);
         } else if (animationsEnabled()) {
-            if (!FeatureUtilities.isChromeHomeEnabled()) {
-                if (getActiveLayout() != null && getActiveLayout().isHiding()) {
-                    setNextLayout(mSimpleAnimationLayout);
-                    // The method Layout#doneHiding() will automatically show the next layout.
-                    getActiveLayout().doneHiding();
-                } else {
-                    startShowing(mSimpleAnimationLayout, false);
-                }
+            if (getActiveLayout() != null && getActiveLayout().isHiding()) {
+                setNextLayout(mSimpleAnimationLayout);
+                // The method Layout#doneHiding() will automatically show the next layout.
+                getActiveLayout().doneHiding();
+            } else {
+                startShowing(mSimpleAnimationLayout, false);
             }
             getActiveLayout().onTabCreating(sourceId);
         }
@@ -171,4 +169,13 @@
             return super.isSwipeEnabled(direction);
         }
     }
+
+    /**
+     * Sets whether the foreground tab animation is disabled.
+     * TODO(twellington): Remove this after Chrome Home NTP animations are complete.
+     * @param disabled Whether the foreground tab animation should be disabled.
+     */
+    public void setForegroundTabAnimationDisabled(boolean disabled) {
+        mSimpleAnimationLayout.setForegroundTabAnimationDisabled(disabled);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java
index dc9aed8..f37e822 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java
@@ -69,6 +69,8 @@
     private final TabListSceneLayer mSceneLayer;
     private final BlackHoleEventFilter mBlackHoleEventFilter;
 
+    private boolean mForegroundTabCreationAnimationDisabled;
+
     /**
      * Creates an instance of the {@link SimpleAnimationLayout}.
      * @param context     The current Android's context.
@@ -195,6 +197,8 @@
 
         mTabModelSelector.selectModel(newIsIncognito);
         startHiding(id, false);
+
+        if (mForegroundTabCreationAnimationDisabled) forceAnimationToFinish();
     }
 
     /**
@@ -429,4 +433,13 @@
         mSceneLayer.pushLayers(getContext(), contentViewport, contentViewport, this,
                 layerTitleCache, tabContentManager, resourceManager, fullscreenManager);
     }
+
+    /**
+     * Sets whether the foreground tab animation is disabled.
+     * TODO(twellington): Remove this after Chrome Home NTP animations are complete.
+     * @param disabled Whether the foreground tab animation should be disabled.
+     */
+    public void setForegroundTabAnimationDisabled(boolean disabled) {
+        mForegroundTabCreationAnimationDisabled = disabled;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java
index 9f73fad..32538c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java
@@ -11,7 +11,6 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
-import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 
 import org.chromium.base.VisibleForTesting;
@@ -40,7 +39,6 @@
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.MathUtils;
 import org.chromium.ui.base.LocalizationUtils;
-import org.chromium.ui.interpolators.BakedBezierInterpolator;
 import org.chromium.ui.resources.ResourceManager;
 
 import java.io.Serializable;
@@ -73,7 +71,6 @@
 
     private static final float THRESHOLD_TO_SWITCH_STACK = 0.4f;
     private static final float THRESHOLD_TIME_TO_SWITCH_STACK_INPUT_MODE = 200;
-    private static final int NEW_TAB_ANIMATION_DURATION_MS = 300;
 
     /**
      * The delta time applied on the velocity from the fling. This is to compute the kick to help
@@ -143,15 +140,6 @@
 
     private StackLayoutGestureHandler mGestureHandler;
 
-    /** A {@link LayoutTab} used for new tab animations. */
-    private LayoutTab mNewTabLayoutTab;
-
-    /**
-     * Whether or not the new layout tab has been properly initialized (a frame can occur between
-     * creation and initialization).
-     */
-    private boolean mIsNewTabInitialized;
-
     private class StackLayoutGestureHandler implements GestureHandler {
         @Override
         public void onDown(float x, float y, boolean fromMouse, int buttons) {
@@ -496,24 +484,14 @@
                 time, id, tabIndex, sourceId, newIsIncognito, background, originX, originY);
         startHiding(id, false);
         mStacks[getTabStackIndex(id)].tabCreated(time, id);
-
-        if (FeatureUtilities.isChromeHomeEnabled()) {
-            mNewTabLayoutTab = createLayoutTab(id, newIsIncognito, NO_CLOSE_BUTTON, NO_TITLE);
-            mNewTabLayoutTab.setScale(1.f);
-            mNewTabLayoutTab.setBorderScale(1.f);
-            mNewTabLayoutTab.setDecorationAlpha(0.f);
-            mNewTabLayoutTab.setY(getHeight() / 2);
-
-            mIsNewTabInitialized = true;
-
-            Interpolator interpolator = BakedBezierInterpolator.TRANSFORM_CURVE;
-            addToAnimation(mNewTabLayoutTab, LayoutTab.Property.Y, mNewTabLayoutTab.getY(), 0.f,
-                    NEW_TAB_ANIMATION_DURATION_MS, 0, false, interpolator);
-        } else {
-            startMarginAnimation(false);
-        }
-
+        startMarginAnimation(false);
         uiPreemptivelySelectTabModel(newIsIncognito);
+
+        // TODO(twellington): Add a proper tab creation animation rather than disabling the current
+        //                    animation.
+        if (FeatureUtilities.isChromeHomeEnabled()) {
+            onUpdateAnimation(System.currentTimeMillis(), true);
+        }
     }
 
     @Override
@@ -526,10 +504,6 @@
 
     @Override
     public void onTabModelSwitched(boolean toIncognitoTabModel) {
-        // There is no need to respond to the tab model switch if the stack for the newly selected
-        // tab model is already showing.
-        if ((toIncognitoTabModel ? 1 : 0) == getTabStackIndex()) return;
-
         flingStacks(toIncognitoTabModel);
         mFlingFromModelChange = true;
     }
@@ -566,10 +540,6 @@
             mTemporarySelectedStack = null;
         }
         if (mStackAnimationCount == 0) super.onAnimationFinished();
-        if (mNewTabLayoutTab != null) {
-            mIsNewTabInitialized = false;
-            mNewTabLayoutTab = null;
-        }
     }
 
     /**
@@ -1070,12 +1040,10 @@
         // computeTabPositionAndAppendLayoutTabs.
         final int tabVisibleCount = mStacks[0].getVisibleCount() + mStacks[1].getVisibleCount();
 
-        int layoutTabCount = tabVisibleCount + (mNewTabLayoutTab == null ? 0 : 1);
-
-        if (layoutTabCount == 0) {
+        if (tabVisibleCount == 0) {
             mLayoutTabs = null;
-        } else if (mLayoutTabs == null || mLayoutTabs.length != layoutTabCount) {
-            mLayoutTabs = new LayoutTab[layoutTabCount];
+        } else if (mLayoutTabs == null || mLayoutTabs.length != tabVisibleCount) {
+            mLayoutTabs = new LayoutTab[tabVisibleCount];
         }
 
         int index = 0;
@@ -1093,11 +1061,6 @@
             if (mLayoutTabs[i].updateSnap(dt)) needUpdate = true;
         }
 
-        if (mNewTabLayoutTab != null && mIsNewTabInitialized) {
-            mLayoutTabs[mLayoutTabs.length - 1] = mNewTabLayoutTab;
-            if (mNewTabLayoutTab.updateSnap(dt)) needUpdate = true;
-        }
-
         if (needUpdate) requestUpdate();
 
         // Since we've updated the positions of the stacks and tabs, let's go ahead and update
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
index 979894f3..407a222 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
@@ -398,12 +398,10 @@
      * @param id The id of the new tab to animate.
      */
     public void tabCreated(long time, int id) {
-        if (!FeatureUtilities.isChromeHomeEnabled()) {
-            if (!createTabHelper(id)) return;
-            mIsDying = false;
+        if (!createTabHelper(id)) return;
 
-            finishAnimation(time);
-        }
+        mIsDying = false;
+        finishAnimation(time);
         startAnimation(time, OverviewAnimationType.NEW_TAB_OPENED,
                 TabModelUtils.getTabIndexById(mTabModel, id), TabModel.INVALID_TAB_INDEX, false);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java
index 7df781a..acae0fd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java
@@ -365,16 +365,24 @@
      * @return              The TabSwitcherAnimation instance that will tween the
      *                      tabs to create the appropriate animation.
      */
+    // TODO(dtrainor): Remove this after confirming nothing uses this.
     protected ChromeAnimation<?> createNewTabOpenedAnimatorSet(
             StackTab[] tabs, int focusIndex, float discardRange) {
-        ChromeAnimation<Animatable<?>> set = new ChromeAnimation<>();
+        if (focusIndex < 0 || focusIndex >= tabs.length) return null;
+        ChromeAnimation<Animatable<?>> set = new ChromeAnimation<Animatable<?>>();
 
-        for (int i = 0; i < tabs.length; i++) {
-            addAnimation(set, tabs[i], StackTab.Property.SCROLL_OFFSET, tabs[i].getScrollOffset(),
-                    0.0f, TAB_OPENED_ANIMATION_DURATION, 0, false,
-                    ChromeAnimation.getDecelerateInterpolator());
-        }
-
+        StackTab tab = tabs[focusIndex];
+        tab.getLayoutTab().setVisible(false);
+        tab.setXInStackInfluence(0.0f);
+        tab.setYInStackInfluence(0.0f);
+        tab.setDiscardFromClick(true);
+        tab.setDiscardOriginX(tab.getLayoutTab().getOriginalContentWidth());
+        tab.setDiscardOriginY(tab.getLayoutTab().getOriginalContentHeight() / 2.f);
+        tab.getLayoutTab().setAlpha(0.0f);
+        tab.getLayoutTab().setBorderAlpha(0.0f);
+        addAnimation(set, tab, DISCARD_AMOUNT, getTabCreationDirection() * discardRange, 0.0f,
+                TAB_OPENED_ANIMATION_DURATION, 0, false,
+                ChromeAnimation.getAccelerateInterpolator());
         return set;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape.java
index def9331..5b37de3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationLandscape.java
@@ -196,12 +196,6 @@
     }
 
     @Override
-    protected ChromeAnimation<?> createNewTabOpenedAnimatorSet(
-            StackTab[] tabs, int focusIndex, float discardRange) {
-        return null;
-    }
-
-    @Override
     protected boolean isDefaultDiscardDirectionPositive() {
         return true;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationPortrait.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationPortrait.java
index b660d18..ae454ab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationPortrait.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimationPortrait.java
@@ -202,6 +202,12 @@
     }
 
     @Override
+    protected ChromeAnimation<?> createNewTabOpenedAnimatorSet(
+            StackTab[] tabs, int focusIndex, float discardRange) {
+        return super.createNewTabOpenedAnimatorSet(tabs, focusIndex, -discardRange);
+    }
+
+    @Override
     protected boolean isDefaultDiscardDirectionPositive() {
         // On clicking the close button, discard the tab to the right on LTR, to the left on RTL.
         return !LocalizationUtils.isLayoutRtl();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index 299aaa5..4067eb9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -396,6 +396,8 @@
         ReaderModeTabInfo info = mTabStatusMap.get(mTabModelSelector.getCurrentTabId());
         if (info != null) info.onStartedReaderMode();
 
+        // Make sure to exit fullscreen mode before navigating.
+        mTabModelSelector.getCurrentTab().toggleFullscreenMode(false);
         DomDistillerTabUtils.distillCurrentPageAndView(getBasePageWebContents());
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
index 465395e..0473d6e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
@@ -182,7 +182,8 @@
         }
 
         // An optional page to select a default search engine.
-        if (mFreProperties.getBoolean(SHOW_SEARCH_ENGINE_PAGE)) {
+        if (mFreProperties.getBoolean(SHOW_SEARCH_ENGINE_PAGE)
+                && mFirstRunFlowSequencer.shouldShowSearchEnginePage()) {
             mPages.add(pageOf(DefaultSearchEngineFirstRunFragment.class));
             mFreProgressStates.add(FRE_PROGRESS_DEFAULT_SEARCH_ENGINE_SHOWN);
             notifyAdapter = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
index d16e3d00..995ffa48 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
@@ -13,9 +13,12 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.omnibox.LocationBarLayout;
+import org.chromium.chrome.browser.omnibox.OmniboxSuggestion;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.ui.UiUtils;
 
+import java.util.List;
+
 /** Implementation of the {@link LocationBarLayout} that is displayed for widget searches. */
 public class SearchActivityLocationBarLayout extends LocationBarLayout {
     /** Delegates calls out to the containing Activity. */
@@ -28,10 +31,12 @@
     }
 
     private Delegate mDelegate;
+    private boolean mShowSuggestions;
 
     public SearchActivityLocationBarLayout(Context context, AttributeSet attrs) {
         super(context, attrs, R.layout.location_bar_base);
         setUrlBarFocusable(true);
+        mShowSuggestions = !LocaleManager.getInstance().needToCheckForSearchEnginePromo();
     }
 
     /** Set the {@link Delegate}. */
@@ -65,6 +70,14 @@
         super.onNativeLibraryReady();
         setAutocompleteProfile(Profile.getLastUsedProfile().getOriginalProfile());
         setShowCachedZeroSuggestResults(true);
+        mShowSuggestions = !LocaleManager.getInstance().needToCheckForSearchEnginePromo();
+    }
+
+    @Override
+    public void onSuggestionsReceived(
+            List<OmniboxSuggestion> newSuggestions, String inlineAutocompleteText) {
+        if (!mShowSuggestions) return;
+        super.onSuggestionsReceived(newSuggestions, inlineAutocompleteText);
     }
 
     /** Called when the SearchActivity has finished initialization. */
@@ -72,6 +85,7 @@
         SearchWidgetProvider.updateCachedVoiceSearchAvailability(isVoiceSearchEnabled());
         if (isVoiceSearchIntent && mUrlBar.isFocused()) onUrlFocusChange(true);
         if (!TextUtils.isEmpty(mUrlBar.getText())) onTextChangedForAutocomplete();
+        mShowSuggestions = true;
     }
 
     /** Begins a new query. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModel.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModel.java
index e836d0ec..c3e7aa0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModel.java
@@ -36,7 +36,6 @@
     private final ObserverList<TabModelObserver> mObservers = new ObserverList<TabModelObserver>();
     private TabModel mDelegateModel;
     private boolean mIsAddingTab;
-    private boolean mIsPendingTabAdd;
 
     /**
      * Constructor for IncognitoTabModel.
@@ -74,7 +73,7 @@
     protected void destroyIncognitoIfNecessary() {
         ThreadUtils.assertOnUiThread();
         if (!isEmpty() || mDelegateModel instanceof EmptyTabModel || mIsAddingTab
-                || mIsPendingTabAdd) {
+                || mDelegateModel.isPendingTabAdd()) {
             return;
         }
 
@@ -214,7 +213,6 @@
     @Override
     public void addTab(Tab tab, int index, TabLaunchType type) {
         mIsAddingTab = true;
-        mIsPendingTabAdd = false;
         ensureTabModelImpl();
         mDelegateModel.addTab(tab, index, type);
         mIsAddingTab = false;
@@ -246,16 +244,15 @@
 
     @Override
     public void setIsPendingTabAdd(boolean isPendingTabAdd) {
-        mIsPendingTabAdd = isPendingTabAdd;
-        if (mIsPendingTabAdd) {
-            ensureTabModelImpl();
-        } else {
-            destroyIncognitoIfNecessary();
-        }
+        mIsAddingTab = isPendingTabAdd;
+        if (isPendingTabAdd) ensureTabModelImpl();
+        mDelegateModel.setIsPendingTabAdd(isPendingTabAdd);
+        mIsAddingTab = false;
+        if (!isPendingTabAdd) destroyIncognitoIfNecessary();
     }
 
     @Override
     public boolean isPendingTabAdd() {
-        return mIsPendingTabAdd;
+        return mDelegateModel.isPendingTabAdd();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 1fd9840..a7e76498 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -299,6 +299,7 @@
 
             @Override
             public void pendingTabAdd(boolean isPendingTabAdd) {
+                if (isPendingTabAdd) mPreselectedTabId = Tab.INVALID_TAB_ID;
                 refreshSelectedTab();
             }
         };
@@ -1334,4 +1335,9 @@
             mHandler.removeMessages(MSG_ID_UPDATE_PROGRESS);
         }
     }
+
+    @VisibleForTesting
+    public ToolbarDataProvider getToolbarDataProviderForTests() {
+        return mToolbarModel;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabController.java
index 8ec5016c1..1e6c6d3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabController.java
@@ -26,6 +26,8 @@
 
     private boolean mIsShowingNewTabUi;
     private boolean mIsShowingNormalToolbar;
+    private boolean mHideOverviewOnClose;
+    private boolean mSelectIncognitoModelOnClose;
 
     /**
      * Creates a new {@link BottomSheetNewTabController}.
@@ -75,6 +77,16 @@
      */
     public void displayNewTabUi(boolean isIncognito) {
         mIsShowingNewTabUi = true;
+        mHideOverviewOnClose = !mLayoutManager.overviewVisible();
+        mSelectIncognitoModelOnClose = mTabModelSelector.isIncognitoSelected();
+
+        // Show the tab switcher if needed. The overview should be shown before the sheet is opened
+        // to ensure the toolbar ends up in the correct state.
+        if (!mLayoutManager.overviewVisible()) mLayoutManager.showOverview(true);
+
+        // Transition from the tab switcher toolbar back to the normal toolbar.
+        mToolbar.showNormalToolbar();
+        mIsShowingNormalToolbar = true;
 
         // Tell the model that a new tab may be added soon.
         mTabModelSelector.getModel(isIncognito).setIsPendingTabAdd(true);
@@ -84,27 +96,18 @@
         if (mTabModelSelector.isIncognitoSelected() != isIncognito) {
             mTabModelSelector.selectModel(isIncognito);
             mBottomSheet.endTransitionAnimations();
+            mTabModelSelector.getModel(!isIncognito).setIsPendingTabAdd(false);
         }
 
-        // Show the tab switcher if needed. The overview should be shown before the sheet is opened
-        // to ensure the toolbar ends up in the correct state.
-        if (!mLayoutManager.overviewVisible()) mLayoutManager.showOverview(true);
-
-        // Open the sheet if it isn't open already. When toggling accessibility mode, this method
-        // may get called while the sheet is open. In that case, we should finish showing the new
-        // tab UI.
-        if (!mBottomSheet.isSheetOpen()) {
-            mBottomSheet.setSheetState(mTabModelSelector.getCurrentModel().getCount() == 0
-                            ? BottomSheet.SHEET_STATE_FULL
-                            : BottomSheet.SHEET_STATE_HALF,
-                    true);
+        // Open the sheet if it isn't already open to the desired height.
+        int sheetState = mTabModelSelector.getCurrentModel().getCount() == 0
+                ? BottomSheet.SHEET_STATE_FULL
+                : BottomSheet.SHEET_STATE_HALF;
+        if (mBottomSheet.getSheetState() != sheetState) {
+            mBottomSheet.setSheetState(sheetState, true);
             mBottomSheet.getBottomSheetMetrics().recordSheetOpenReason(
                     BottomSheetMetrics.OPENED_BY_NEW_TAB_CREATION);
         }
-
-        // Transition from the tab switcher toolbar to the normal toolbar.
-        mToolbar.showNormalToolbar();
-        mIsShowingNormalToolbar = true;
     }
 
     /**
@@ -125,15 +128,23 @@
     }
 
     @Override
+    public void onLoadUrl(String url) {
+        if (!mIsShowingNewTabUi) return;
+
+        mLayoutManager.hideOverview(true);
+
+        // BottomSheet closes itself when URLs are loaded; wait to finish hiding the new tab UI
+        // until the bottom sheet is closed.
+    }
+
+    @Override
     public void onSheetReleased() {
         if (!mIsShowingNewTabUi) return;
 
         // Start transitioning back to the tab switcher toolbar when the sheet is released to help
         // smooth out animations.
-        if (mBottomSheet.getTargetSheetState() == BottomSheet.SHEET_STATE_PEEK
-                && mIsShowingNormalToolbar) {
-            mIsShowingNormalToolbar = false;
-            mToolbar.showTabSwitcherToolbar();
+        if (mBottomSheet.getTargetSheetState() == BottomSheet.SHEET_STATE_PEEK) {
+            showTabSwitcherToolbarIfNecessary();
         }
     }
 
@@ -143,11 +154,9 @@
 
         // Start transitioning to the tab switcher toolbar when the sheet is close to the bottom
         // of the screen.
-        if (mIsShowingNormalToolbar && heightFraction < 0.2f
-                && mBottomSheet.getTargetSheetState() == BottomSheet.SHEET_STATE_PEEK
-                && mLayoutManager.overviewVisible()) {
-            mIsShowingNormalToolbar = false;
-            mToolbar.showTabSwitcherToolbar();
+        if (heightFraction < 0.2f
+                && mBottomSheet.getTargetSheetState() == BottomSheet.SHEET_STATE_PEEK) {
+            showTabSwitcherToolbarIfNecessary();
         }
     }
 
@@ -157,25 +166,37 @@
 
         mIsShowingNewTabUi = false;
 
-        // If the incognito tab model is showing, but has no tabs, this indicates that the model
-        // was switched during the creation of an incognito ntp and the user closed the sheet
-        // without loading a URL. Switch back to the normal model.
-        if (mTabModelSelector.isIncognitoSelected()
-                && mTabModelSelector.getModel(true).getCount() == 0) {
-            mTabModelSelector.selectModel(false);
+        showTabSwitcherToolbarIfNecessary();
+
+        if (mLayoutManager.overviewVisible()
+                && mTabModelSelector.isIncognitoSelected() != mSelectIncognitoModelOnClose
+                && (!mSelectIncognitoModelOnClose
+                           || mTabModelSelector.getModel(true).getCount() > 0)) {
+            mTabModelSelector.selectModel(mSelectIncognitoModelOnClose);
+            // End transitions immediately to ensure previous tab model is no longer in use and
+            // can be destroyed if necessary.
+            mBottomSheet.endTransitionAnimations();
         }
 
-        if (mIsShowingNormalToolbar && mLayoutManager.overviewVisible()) {
+        mTabModelSelector.getModel(false).setIsPendingTabAdd(false);
+        mTabModelSelector.getModel(true).setIsPendingTabAdd(false);
+
+        // Hide the overview after setting pendingTabAdd to false so that the StackLayout animation
+        // knows which tab index is being selected and animates the tab stacks correctly.
+        if (mLayoutManager.overviewVisible() && mHideOverviewOnClose) {
+            // TODO(twellington): Ideally we would start hiding the overview sooner. Modifications
+            // are needed for the StackLayout to know which tab will be selected before the sheet is
+            // closed so that it can animate properly.
+            mLayoutManager.hideOverview(true);
+        }
+
+        mHideOverviewOnClose = false;
+    }
+
+    private void showTabSwitcherToolbarIfNecessary() {
+        if (mLayoutManager.overviewVisible() && !mHideOverviewOnClose && mIsShowingNormalToolbar) {
             mIsShowingNormalToolbar = false;
             mToolbar.showTabSwitcherToolbar();
         }
-
-        onNewTabUiHidden();
-    }
-
-    /** Called after the new tab UI is hidden. Resets properties on the tab models. */
-    private void onNewTabUiHidden() {
-        mTabModelSelector.getModel(false).setIsPendingTabAdd(false);
-        mTabModelSelector.getModel(true).setIsPendingTabAdd(false);
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabControllerTest.java
index 8167f9c0..7c19fe0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetNewTabControllerTest.java
@@ -25,10 +25,16 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.LauncherShortcutActivity;
 import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome;
 import org.chromium.chrome.browser.ntp.ChromeHomeNewTabPageBase;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
+import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.chrome.browser.widget.FadingBackgroundView;
 import org.chromium.chrome.test.BottomSheetTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -49,18 +55,36 @@
 @Restriction(RESTRICTION_TYPE_PHONE) // ChromeHome is only enabled on phones
 public class BottomSheetNewTabControllerTest {
     private FadingBackgroundView mFadingBackgroundView;
-    private TestTabModelObserver mTabModelObserver;
     private BottomSheet mBottomSheet;
     private ChromeTabbedActivity mActivity;
+    private TabModelSelector mTabModelSelector;
+    private TestTabModelObserver mNormalTabModelObserver;
+    private TestTabModelObserver mIncognitoTabModelObserver;
+    private TestTabModelSelectorObserver mTabModelSelectorObserver;
 
     /** An observer used to detect changes in the tab model. */
     private static class TestTabModelObserver extends EmptyTabModelObserver {
         private final CallbackHelper mDidCloseTabCallbackHelper = new CallbackHelper();
+        private final CallbackHelper mPendingTabAddCallbackHelper = new CallbackHelper();
 
         @Override
         public void didCloseTab(int tabId, boolean incognito) {
             mDidCloseTabCallbackHelper.notifyCalled();
         }
+
+        @Override
+        public void pendingTabAdd(boolean isPendingTabAdd) {
+            mPendingTabAddCallbackHelper.notifyCalled();
+        }
+    }
+
+    private static class TestTabModelSelectorObserver extends EmptyTabModelSelectorObserver {
+        private final CallbackHelper mTabModelSelectedCallbackHelper = new CallbackHelper();
+
+        @Override
+        public void onTabModelSelected(TabModel newModel, TabModel oldModel) {
+            mTabModelSelectedCallbackHelper.notifyCalled();
+        }
     }
 
     @Rule
@@ -70,21 +94,37 @@
     public void setUp() throws Exception {
         mBottomSheetTestRule.startMainActivityOnBlankPage();
         mBottomSheet = mBottomSheetTestRule.getBottomSheet();
-        mTabModelObserver = new TestTabModelObserver();
         mActivity = mBottomSheetTestRule.getActivity();
-        mActivity.getTabModelSelector().getModel(false).addObserver(mTabModelObserver);
-        mActivity.getTabModelSelector().getModel(true).addObserver(mTabModelObserver);
+        mTabModelSelector = mActivity.getTabModelSelector();
         mFadingBackgroundView = mActivity.getFadingBackgroundView();
         mBottomSheetTestRule.setSheetState(BottomSheet.SHEET_STATE_PEEK, false);
+
+        mNormalTabModelObserver = new TestTabModelObserver();
+        mIncognitoTabModelObserver = new TestTabModelObserver();
+        mTabModelSelectorObserver = new TestTabModelSelectorObserver();
+        mTabModelSelector.getModel(false).addObserver(mNormalTabModelObserver);
+        mTabModelSelector.getModel(true).addObserver(mIncognitoTabModelObserver);
+        mTabModelSelector.addObserver(mTabModelSelectorObserver);
     }
 
     @Test
     @SmallTest
-    public void testNTPOverTabSwitcher_Normal() {
-        assertEquals(1, mActivity.getTabModelSelector().getTotalTabCount());
-        assertFalse("Overview mode should not be showing",
-                mActivity.getLayoutManager().overviewVisible());
-        assertFalse(mActivity.getTabModelSelector().isIncognitoSelected());
+    public void testNTPOverTabSwitcher_Normal_FromTab() {
+        LayoutManagerChrome layoutManager = mActivity.getLayoutManager();
+        TabModel normalTabModel = mTabModelSelector.getModel(false);
+        ToolbarDataProvider toolbarDataProvider =
+                mActivity.getToolbarManager().getToolbarDataProviderForTests();
+
+        assertEquals("There should be 1 tab.", 1, mTabModelSelector.getTotalTabCount());
+        assertFalse("Overview mode should not be showing.", layoutManager.overviewVisible());
+        assertFalse("Normal model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertFalse("Normal model should not be pending tab addition.",
+                normalTabModel.isPendingTabAdd());
+        assertEquals("ToolbarDataProvider has incorrect tab.", mActivity.getActivityTab(),
+                toolbarDataProvider.getTab());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
+        assertTrue("Normal model should not have INVALID_TAB_INDEX",
+                normalTabModel.index() != TabModel.INVALID_TAB_INDEX);
 
         // Select "New tab" from the menu.
         MenuUtils.invokeCustomMenuActionSync(
@@ -93,9 +133,14 @@
         // The sheet should be opened at half height over the tab switcher and the tab count should
         // remain unchanged.
         validateState(false, BottomSheet.SHEET_STATE_HALF);
-        assertEquals(1, mActivity.getTabModelSelector().getTotalTabCount());
+        assertEquals("There should be 1 tab.", 1, mTabModelSelector.getTotalTabCount());
+        assertTrue("Overview mode should be showing.", layoutManager.overviewVisible());
         assertTrue(
-                "Overview mode should be showing", mActivity.getLayoutManager().overviewVisible());
+                "Normal model should be pending tab addition.", normalTabModel.isPendingTabAdd());
+        assertEquals("ToolbarDataProvider has incorrect tab.", null, toolbarDataProvider.getTab());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
+        assertTrue("Normal model should have INVALID_TAB_INDEX",
+                normalTabModel.index() == TabModel.INVALID_TAB_INDEX);
 
         // Load a URL in the bottom sheet.
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@@ -107,19 +152,35 @@
 
         // The sheet should now be closed and a regular tab should have been created
         validateState(false, BottomSheet.SHEET_STATE_PEEK);
-        assertEquals(2, mActivity.getTabModelSelector().getTotalTabCount());
-        assertFalse("Overview mode not should be showing",
-                mActivity.getLayoutManager().overviewVisible());
-        assertFalse(mActivity.getTabModelSelector().isIncognitoSelected());
+        assertEquals("There should be 2 tabs.", 2, mTabModelSelector.getTotalTabCount());
+        assertFalse("Overview mode not should be showing.", layoutManager.overviewVisible());
+        assertFalse("Normal model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertFalse("Normal model should not be pending tab addition.",
+                normalTabModel.isPendingTabAdd());
+        assertEquals("ToolbarDataProvider has incorrect tab.", mActivity.getActivityTab(),
+                toolbarDataProvider.getTab());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
+        assertTrue("Normal model should not have INVALID_TAB_INDEX",
+                normalTabModel.index() != TabModel.INVALID_TAB_INDEX);
     }
 
     @Test
     @SmallTest
-    public void testNTPOverTabSwitcher_Incognito() {
-        assertEquals(1, mActivity.getTabModelSelector().getTotalTabCount());
-        assertFalse("Overview mode should not be showing",
-                mActivity.getLayoutManager().overviewVisible());
-        assertFalse(mActivity.getTabModelSelector().isIncognitoSelected());
+    public void testNTPOverTabSwitcher_Incognito_FromTab() {
+        LayoutManagerChrome layoutManager = mActivity.getLayoutManager();
+        TabModel incognitoTabModel = mTabModelSelector.getModel(true);
+        ToolbarDataProvider toolbarDataProvider =
+                mActivity.getToolbarManager().getToolbarDataProviderForTests();
+        Tab originalTab = mActivity.getActivityTab();
+
+        assertEquals("There should be 1 tab.", 1, mTabModelSelector.getTotalTabCount());
+        assertFalse("Overview mode should not be showing.", layoutManager.overviewVisible());
+        assertFalse("Normal model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertFalse("Incognito model should not be pending tab addition.",
+                incognitoTabModel.isPendingTabAdd());
+        assertEquals("ToolbarDataProvider has incorrect tab.", mActivity.getActivityTab(),
+                toolbarDataProvider.getTab());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
 
         // Select "New incognito tab" from the menu.
         MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(),
@@ -127,15 +188,23 @@
         // The sheet should be opened at full height over the tab switcher and the tab count should
         // remain unchanged. The incognito model should now be selected.
         validateState(false, BottomSheet.SHEET_STATE_FULL);
-        assertEquals(1, mActivity.getTabModelSelector().getTotalTabCount());
-        assertTrue(
-                "Overview mode should be showing", mActivity.getLayoutManager().overviewVisible());
-        assertTrue(mActivity.getTabModelSelector().isIncognitoSelected());
+        assertEquals("There should be 1 tab.", 1, mTabModelSelector.getTotalTabCount());
+        assertTrue("Overview mode should be showing.", layoutManager.overviewVisible());
+        assertTrue("Incognito model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertTrue("Incognito model should be pending tab addition.",
+                incognitoTabModel.isPendingTabAdd());
+        assertEquals("ToolbarDataProvider has incorrect tab.", null, toolbarDataProvider.getTab());
+        assertTrue("Toolbar should be incognito.", toolbarDataProvider.isIncognito());
 
-        // Check that the normal model is selected after the sheet is closed without a URL being
+        // Check that the previous tab is selected after the sheet is closed without a URL being
         // loaded.
         mBottomSheetTestRule.setSheetState(BottomSheet.SHEET_STATE_PEEK, false);
-        assertFalse(mActivity.getTabModelSelector().isIncognitoSelected());
+        assertFalse("Normal model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertFalse("Incognito model should not be pending tab addition.",
+                incognitoTabModel.isPendingTabAdd());
+        assertFalse("Overview mode should not be showing.", layoutManager.overviewVisible());
+        assertEquals("Incorrect tab selected.", originalTab, mTabModelSelector.getCurrentTab());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
 
         // Select "New incognito tab" from the menu and load a URL.
         MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(),
@@ -149,11 +218,96 @@
 
         // The sheet should now be closed and an incognito tab should have been created
         validateState(false, BottomSheet.SHEET_STATE_PEEK);
-        assertEquals(2, mActivity.getTabModelSelector().getTotalTabCount());
-        assertTrue(mActivity.getActivityTab().isIncognito());
-        assertFalse("Overview mode not should be showing",
-                mActivity.getLayoutManager().overviewVisible());
-        assertTrue(mActivity.getTabModelSelector().isIncognitoSelected());
+        assertEquals("There should be 2 tabs.", 2, mTabModelSelector.getTotalTabCount());
+        assertTrue(
+                "The activity tab should be incognito.", mActivity.getActivityTab().isIncognito());
+        assertFalse("Overview mode not should be showing.", layoutManager.overviewVisible());
+        assertTrue("Incognito model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertFalse("Incognito model should not be pending tab addition.",
+                incognitoTabModel.isPendingTabAdd());
+        assertEquals("ToolbarDataProvider has incorrect tab.", mActivity.getActivityTab(),
+                toolbarDataProvider.getTab());
+        assertTrue("Toolbar should be incognito.", toolbarDataProvider.isIncognito());
+        assertTrue("Incognito model should not have INVALID_TAB_INDEX",
+                incognitoTabModel.index() != TabModel.INVALID_TAB_INDEX);
+
+        // Select "New incognito tab" from the menu again and assert state is as expected. This
+        // is designed to exercise IncognitoTabModel#index().
+        MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(),
+                mActivity, R.id.new_incognito_tab_menu_id);
+        assertTrue("Incognito model should be pending tab addition.",
+                incognitoTabModel.isPendingTabAdd());
+        assertEquals("ToolbarDataProvider has incorrect tab.", null, toolbarDataProvider.getTab());
+        assertTrue("Toolbar should be incognito.", toolbarDataProvider.isIncognito());
+        assertTrue("Incognito model should have INVALID_TAB_INDEX",
+                incognitoTabModel.index() == TabModel.INVALID_TAB_INDEX);
+    }
+
+    @Test
+    @SmallTest
+    public void testNTPOverTabSwitcher_Incognito_FromTabSwitcher() {
+        final LayoutManagerChrome layoutManager = mActivity.getLayoutManager();
+        ToolbarDataProvider toolbarDataProvider =
+                mActivity.getToolbarManager().getToolbarDataProviderForTests();
+
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                layoutManager.showOverview(false);
+            }
+        });
+
+        assertFalse("Normal model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertTrue("Overview mode should be showing.", layoutManager.overviewVisible());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
+
+        MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(),
+                mActivity, R.id.new_incognito_tab_menu_id);
+
+        validateState(false, BottomSheet.SHEET_STATE_FULL);
+        assertTrue("Incognito model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertTrue("Overview mode should be showing.", layoutManager.overviewVisible());
+        assertTrue("Toolbar should be incognito.", toolbarDataProvider.isIncognito());
+
+        mBottomSheetTestRule.setSheetState(BottomSheet.SHEET_STATE_PEEK, false);
+        assertFalse("Normal model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertTrue("Overview mode should be showing.", layoutManager.overviewVisible());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
+    }
+
+    @Test
+    @SmallTest
+    public void testNTPOverTabSwitcher_Normal_FromTabSwitcher() {
+        final LayoutManagerChrome layoutManager = mActivity.getLayoutManager();
+        ToolbarDataProvider toolbarDataProvider =
+                mActivity.getToolbarManager().getToolbarDataProviderForTests();
+
+        MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(),
+                mActivity, R.id.new_incognito_tab_menu_id);
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mBottomSheet.loadUrlInNewTab(new LoadUrlParams("about:blank"));
+                layoutManager.showOverview(false);
+            }
+        });
+
+        assertTrue("Incognito model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertTrue("Overview mode should be showing.", layoutManager.overviewVisible());
+        assertTrue("Toolbar should be incognito.", toolbarDataProvider.isIncognito());
+
+        MenuUtils.invokeCustomMenuActionSync(
+                InstrumentationRegistry.getInstrumentation(), mActivity, R.id.new_tab_menu_id);
+
+        validateState(false, BottomSheet.SHEET_STATE_HALF);
+        assertFalse("Normal model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertTrue("Overview mode should be showing.", layoutManager.overviewVisible());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
+
+        mBottomSheetTestRule.setSheetState(BottomSheet.SHEET_STATE_PEEK, false);
+        assertTrue("Incognito model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertTrue("Overview mode should be showing.", layoutManager.overviewVisible());
+        assertTrue("Toolbar should be incognito.", toolbarDataProvider.isIncognito());
     }
 
     @Test
@@ -161,7 +315,7 @@
     public void testNTPOverTabSwitcher_NoTabs() throws InterruptedException {
         // Close all tabs.
         ChromeTabUtils.closeAllTabs(InstrumentationRegistry.getInstrumentation(), mActivity);
-        assertEquals(0, mActivity.getTabModelSelector().getTotalTabCount());
+        assertEquals(0, mTabModelSelector.getTotalTabCount());
 
         // Select "New tab" from the menu.
         MenuUtils.invokeCustomMenuActionSync(
@@ -169,7 +323,200 @@
 
         // The sheet should be opened at full height.
         validateState(false, BottomSheet.SHEET_STATE_FULL);
-        assertEquals(0, mActivity.getTabModelSelector().getTotalTabCount());
+        assertEquals(0, mTabModelSelector.getTotalTabCount());
+    }
+
+    @Test
+    @SmallTest
+    public void testNTPOverTabSwitcher_SwitchModels() {
+        TabModel normalTabModel = mTabModelSelector.getModel(false);
+        TabModel incognitoTabModel = mTabModelSelector.getModel(true);
+        ToolbarDataProvider toolbarDataProvider =
+                mActivity.getToolbarManager().getToolbarDataProviderForTests();
+
+        assertFalse("Incognito model should not be pending tab addition.",
+                incognitoTabModel.isPendingTabAdd());
+        assertFalse("Normal model should not be pending tab addition.",
+                normalTabModel.isPendingTabAdd());
+        assertFalse("Normal model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
+
+        // Select "New incognito tab" from the menu.
+        MenuUtils.invokeCustomMenuActionSync(InstrumentationRegistry.getInstrumentation(),
+                mActivity, R.id.new_incognito_tab_menu_id);
+
+        assertTrue("Incognito model should be pending tab addition.",
+                incognitoTabModel.isPendingTabAdd());
+        assertFalse("Normal model should not be pending tab addition.",
+                normalTabModel.isPendingTabAdd());
+        assertTrue("Incognito model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertTrue("Toolbar should be incognito.", toolbarDataProvider.isIncognito());
+
+        // Select "New tab" from the menu.
+        MenuUtils.invokeCustomMenuActionSync(
+                InstrumentationRegistry.getInstrumentation(), mActivity, R.id.new_tab_menu_id);
+
+        assertFalse("Incognito model should not be pending tab addition.",
+                incognitoTabModel.isPendingTabAdd());
+        assertTrue(
+                "Normal model should be pending tab addition.", normalTabModel.isPendingTabAdd());
+        assertFalse("Normal model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
+    }
+
+    @Test
+    @SmallTest
+    public void testNTPOverTabSwitcher_LauncherShortcuts_NormalToIncognito()
+            throws InterruptedException, TimeoutException {
+        LayoutManagerChrome layoutManager = mActivity.getLayoutManager();
+        TabModel normalTabModel = mTabModelSelector.getModel(false);
+        TabModel incognitoTabModel = mTabModelSelector.getModel(true);
+        ToolbarDataProvider toolbarDataProvider =
+                mActivity.getToolbarManager().getToolbarDataProviderForTests();
+
+        assertFalse("Overview mode should not be showing.", layoutManager.overviewVisible());
+        assertFalse("Normal model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertFalse("Normal model should not be pending tab addition.",
+                normalTabModel.isPendingTabAdd());
+        assertEquals("ToolbarDataProvider has incorrect tab.", mActivity.getActivityTab(),
+                toolbarDataProvider.getTab());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
+
+        // Create a new tab using a launcher shortcut intent.
+        int currentPendingTabCalls =
+                mIncognitoTabModelObserver.mPendingTabAddCallbackHelper.getCallCount();
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.startActivity(LauncherShortcutActivity.getChromeLauncherActivityIntent(
+                        mActivity, LauncherShortcutActivity.ACTION_OPEN_NEW_TAB));
+            }
+        });
+        mNormalTabModelObserver.mPendingTabAddCallbackHelper.waitForCallback(
+                currentPendingTabCalls);
+
+        // The sheet should be opened at half height over the tab switcher.
+        validateState(false, BottomSheet.SHEET_STATE_HALF);
+        assertTrue("Overview mode should be showing.", layoutManager.overviewVisible());
+        assertTrue(
+                "Normal model should be pending tab addition.", normalTabModel.isPendingTabAdd());
+        assertEquals("ToolbarDataProvider has incorrect tab.", null, toolbarDataProvider.getTab());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
+
+        // Create a new incognito tab using an incognito launcher shortcut intent.
+        int currentIncognitoPendingTabCalls =
+                mIncognitoTabModelObserver.mPendingTabAddCallbackHelper.getCallCount();
+        currentPendingTabCalls =
+                mNormalTabModelObserver.mPendingTabAddCallbackHelper.getCallCount();
+        int currentTabModelSelectedCalls =
+                mTabModelSelectorObserver.mTabModelSelectedCallbackHelper.getCallCount();
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.startActivity(LauncherShortcutActivity.getChromeLauncherActivityIntent(
+                        mActivity, LauncherShortcutActivity.ACTION_OPEN_NEW_INCOGNITO_TAB));
+            }
+        });
+
+        mIncognitoTabModelObserver.mPendingTabAddCallbackHelper.waitForCallback(
+                currentIncognitoPendingTabCalls, 1);
+        mNormalTabModelObserver.mPendingTabAddCallbackHelper.waitForCallback(
+                currentPendingTabCalls, 1);
+        mTabModelSelectorObserver.mTabModelSelectedCallbackHelper.waitForCallback(
+                currentTabModelSelectedCalls);
+
+        // Check that the state is correct.
+        validateState(false, BottomSheet.SHEET_STATE_FULL);
+        assertTrue("Overview mode should be showing.", layoutManager.overviewVisible());
+        assertTrue("Incognito model should be pending tab addition.",
+                incognitoTabModel.isPendingTabAdd());
+        assertFalse("Normal model should not be pending tab addition.",
+                normalTabModel.isPendingTabAdd());
+        assertTrue("Incognito model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertTrue("Toolbar should be incognito.", toolbarDataProvider.isIncognito());
+    }
+
+    @Test
+    @SmallTest
+    public void testNTPOverTabSwitcher_LauncherShortcuts_IncognitoToNormal()
+            throws InterruptedException, TimeoutException {
+        LayoutManagerChrome layoutManager = mActivity.getLayoutManager();
+        TabModel normalTabModel = mTabModelSelector.getModel(false);
+        TabModel incognitoTabModel = mTabModelSelector.getModel(true);
+        ToolbarDataProvider toolbarDataProvider =
+                mActivity.getToolbarManager().getToolbarDataProviderForTests();
+
+        // Create a new tab using an incognito launcher shortcut intent.
+        int currentIncognitoPendingTabCalls =
+                mIncognitoTabModelObserver.mPendingTabAddCallbackHelper.getCallCount();
+        int currentTabModelSelectedCalls =
+                mTabModelSelectorObserver.mTabModelSelectedCallbackHelper.getCallCount();
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.startActivity(LauncherShortcutActivity.getChromeLauncherActivityIntent(
+                        mActivity, LauncherShortcutActivity.ACTION_OPEN_NEW_INCOGNITO_TAB));
+            }
+        });
+
+        mIncognitoTabModelObserver.mPendingTabAddCallbackHelper.waitForCallback(
+                currentIncognitoPendingTabCalls, 1);
+        mTabModelSelectorObserver.mTabModelSelectedCallbackHelper.waitForCallback(
+                currentTabModelSelectedCalls);
+
+        // Check that the state is correct.
+        validateState(false, BottomSheet.SHEET_STATE_FULL);
+        assertTrue("Overview mode should be showing.", layoutManager.overviewVisible());
+        assertTrue("Incognito model should be pending tab addition.",
+                incognitoTabModel.isPendingTabAdd());
+        assertFalse("Normal model should not be pending tab addition.",
+                normalTabModel.isPendingTabAdd());
+        assertTrue("Incognito model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertTrue("Toolbar should be incognito.", toolbarDataProvider.isIncognito());
+
+        // Create a new tab to ensure that there are no crashes when destroying the incognito
+        // profile.
+        currentIncognitoPendingTabCalls =
+                mIncognitoTabModelObserver.mPendingTabAddCallbackHelper.getCallCount();
+        int currentPendingTabCalls =
+                mNormalTabModelObserver.mPendingTabAddCallbackHelper.getCallCount();
+        currentTabModelSelectedCalls =
+                mTabModelSelectorObserver.mTabModelSelectedCallbackHelper.getCallCount();
+        ThreadUtils.runOnUiThreadBlocking(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.startActivity(LauncherShortcutActivity.getChromeLauncherActivityIntent(
+                        mActivity, LauncherShortcutActivity.ACTION_OPEN_NEW_TAB));
+            }
+        });
+
+        mIncognitoTabModelObserver.mPendingTabAddCallbackHelper.waitForCallback(
+                currentIncognitoPendingTabCalls, 1);
+        mNormalTabModelObserver.mPendingTabAddCallbackHelper.waitForCallback(
+                currentPendingTabCalls, 1);
+        mTabModelSelectorObserver.mTabModelSelectedCallbackHelper.waitForCallback(
+                currentTabModelSelectedCalls);
+        validateState(false, BottomSheet.SHEET_STATE_HALF);
+        assertFalse("Normal model should be selected.", mTabModelSelector.isIncognitoSelected());
+        assertTrue(
+                "Normal model should be pending tab addition.", normalTabModel.isPendingTabAdd());
+        assertFalse("Incognito model should not be pending tab addition.",
+                incognitoTabModel.isPendingTabAdd());
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
+
+        // Typically the BottomSheetNewTabController would select the previous model on close. In
+        // this case the previous model is incognito because the incognito NTP was showing when
+        // the regular new tab was created, but the incognito model has no tabs. Close the bottom
+        // sheet and ensure the normal model is still selected.
+        currentPendingTabCalls =
+                mNormalTabModelObserver.mPendingTabAddCallbackHelper.getCallCount();
+        mBottomSheetTestRule.setSheetState(BottomSheet.SHEET_STATE_PEEK, false);
+        assertFalse("Normal model should not be pending tab addition.",
+                normalTabModel.isPendingTabAdd());
+        mNormalTabModelObserver.mPendingTabAddCallbackHelper.waitForCallback(
+                currentPendingTabCalls, 1);
+        assertFalse("Toolbar should be normal.", toolbarDataProvider.isIncognito());
+        assertFalse("Normal model should be selected.", mTabModelSelector.isIncognitoSelected());
     }
 
     @Test
@@ -182,8 +529,8 @@
 
         // Close the new tab.
         closeNewTab();
-        assertEquals(1, mActivity.getTabModelSelector().getTotalTabCount());
-        assertFalse("Overview mode should not be showing",
+        assertEquals(1, mTabModelSelector.getTotalTabCount());
+        assertFalse("Overview mode should not be showing.",
                 mActivity.getLayoutManager().overviewVisible());
     }
 
@@ -197,8 +544,8 @@
 
         // Close the new tab.
         closeNewTab();
-        assertEquals(1, mActivity.getTabModelSelector().getTotalTabCount());
-        assertFalse("Overview mode should not be showing",
+        assertEquals(1, mTabModelSelector.getTotalTabCount());
+        assertFalse("Overview mode should not be showing.",
                 mActivity.getLayoutManager().overviewVisible());
     }
 
@@ -217,14 +564,15 @@
             @Override
             public void run() {
                 mBottomSheet.loadUrl(new LoadUrlParams("about:blank"), incognito);
-                mActivity.getLayoutManager().getActiveLayout().finishAnimationsForTests();
             }
         });
     }
 
     private void closeNewTab() throws InterruptedException, TimeoutException {
-        int currentCallCount = mTabModelObserver.mDidCloseTabCallbackHelper.getCallCount();
         Tab tab = mActivity.getActivityTab();
+        TestTabModelObserver observer =
+                tab.isIncognito() ? mIncognitoTabModelObserver : mNormalTabModelObserver;
+        int currentCallCount = observer.mDidCloseTabCallbackHelper.getCallCount();
         final ChromeHomeNewTabPageBase newTabPage = (ChromeHomeNewTabPageBase) tab.getNativePage();
 
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@@ -235,7 +583,7 @@
             }
         });
 
-        mTabModelObserver.mDidCloseTabCallbackHelper.waitForCallback(currentCallCount, 1);
+        observer.mDidCloseTabCallbackHelper.waitForCallback(currentCallCount, 1);
 
         validateState(false, BottomSheet.SHEET_STATE_PEEK);
     }
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index ccd445d7..d159361 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -6093,16 +6093,16 @@
   <message name="IDS_ARC_MIGRATE_ENCRYPTION_SUCCESS_MESSAGE" desc="Message of the toast shown on the first sign-in after the successfully completed migration needed for using Android apps.">
     Update successful. You can now use Android apps.
   </message>
-  <message name="IDS_VOICE_INTERACTION_VALUE_PROP_NO_THANKS_BUTTON" desc="tmp">
+  <message name="IDS_VOICE_INTERACTION_VALUE_PROP_NO_THANKS_BUTTON" desc="Button label for skipping voice interaction value proposition">
     No thanks
   </message>
-  <message name="IDS_VOICE_INTERACTION_VALUE_PROP_CONTINUE_BUTTION" desc="tmp">
+  <message name="IDS_VOICE_INTERACTION_VALUE_PROP_CONTINUE_BUTTION" desc="Button label for accepting voice interaction value proposition">
     Continue
   </message>
-  <message name="IDS_WAIT_FOR_CONTAINER_READY_TITLE" desc="tmp">
+  <message name="IDS_WAIT_FOR_CONTAINER_READY_TITLE" desc="Title of the wait for container ready OOBE screen.">
     Just a sec
   </message>
-  <message name="IDS_WAIT_FOR_CONTAINER_READY_INTRO_MESSAGE" desc="tmp">
+  <message name="IDS_WAIT_FOR_CONTAINER_READY_INTRO_MESSAGE" desc="Introduction message for the wait for container ready OOBE screen">
     Ask it questions. Tell it to do things. It's your personal Google, always ready to help.
   </message>
 
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index b0bc51d..2958f3f 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2749,6 +2749,11 @@
      flag_descriptions::kEnableAutofillCreditCardUploadCvcPromptDescription,
      kOsDesktop,
      FEATURE_VALUE_TYPE(autofill::kAutofillUpstreamRequestCvcIfMissing)},
+    {"enable-autofill-credit-card-bank-name-display",
+     flag_descriptions::kEnableAutofillCreditCardBankNameDisplayName,
+     flag_descriptions::kEnableAutofillCreditCardBankNameDisplayDescription,
+     kOsAll, FEATURE_VALUE_TYPE(autofill::kAutofillCreditCardBankNameDisplay)},
+
 #if defined(OS_WIN)
     {"windows10-custom-titlebar",
      flag_descriptions::kWindows10CustomTitlebarName,
diff --git a/chrome/browser/android/vr_shell/BUILD.gn b/chrome/browser/android/vr_shell/BUILD.gn
index 8135050..36f0663 100644
--- a/chrome/browser/android/vr_shell/BUILD.gn
+++ b/chrome/browser/android/vr_shell/BUILD.gn
@@ -55,8 +55,6 @@
     "textures/url_bar_texture.cc",
     "textures/url_bar_texture.h",
     "ui_browser_interface.h",
-    "ui_elements/audio_capture_indicator.cc",
-    "ui_elements/audio_capture_indicator.h",
     "ui_elements/button.cc",
     "ui_elements/button.h",
     "ui_elements/exit_prompt.cc",
@@ -67,16 +65,14 @@
     "ui_elements/exit_warning.h",
     "ui_elements/loading_indicator.cc",
     "ui_elements/loading_indicator.h",
-    "ui_elements/location_access_indicator.cc",
-    "ui_elements/location_access_indicator.h",
     "ui_elements/permanent_security_warning.cc",
     "ui_elements/permanent_security_warning.h",
     "ui_elements/presentation_toast.cc",
     "ui_elements/presentation_toast.h",
-    "ui_elements/screen_capture_indicator.cc",
-    "ui_elements/screen_capture_indicator.h",
     "ui_elements/screen_dimmer.cc",
     "ui_elements/screen_dimmer.h",
+    "ui_elements/system_indicator.cc",
+    "ui_elements/system_indicator.h",
     "ui_elements/textured_element.cc",
     "ui_elements/textured_element.h",
     "ui_elements/transient_security_warning.cc",
@@ -88,8 +84,6 @@
     "ui_elements/ui_element_debug_id.h",
     "ui_elements/url_bar.cc",
     "ui_elements/url_bar.h",
-    "ui_elements/video_capture_indicator.cc",
-    "ui_elements/video_capture_indicator.h",
     "ui_interface.h",
     "ui_scene.cc",
     "ui_scene.h",
diff --git a/chrome/browser/android/vr_shell/gl_browser_interface.h b/chrome/browser/android/vr_shell/gl_browser_interface.h
index cdc9bbd..aeec32e4 100644
--- a/chrome/browser/android/vr_shell/gl_browser_interface.h
+++ b/chrome/browser/android/vr_shell/gl_browser_interface.h
@@ -19,8 +19,9 @@
 
 namespace vr_shell {
 
-// An interface for the GL thread to communicate with VrShell. Many of the
-// functions in this interface are proxies to methods on VrShell.
+// An interface for the GL thread to communicate with the rest of the system
+// (UI, VrShell, etc). Many of the functions in this interface are proxies to
+// methods on VrShell.
 class GlBrowserInterface {
  public:
   virtual ~GlBrowserInterface() = default;
@@ -38,6 +39,7 @@
       device::mojom::VRDisplayInfoPtr* info) = 0;
   virtual void OnContentPaused(bool enabled) = 0;
   virtual void ToggleCardboardGamepad(bool enabled) = 0;
+  virtual void OnGLInitialized() = 0;
 };
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/textures/system_indicator_texture.cc b/chrome/browser/android/vr_shell/textures/system_indicator_texture.cc
index 2ee7035..d87c6b94 100644
--- a/chrome/browser/android/vr_shell/textures/system_indicator_texture.cc
+++ b/chrome/browser/android/vr_shell/textures/system_indicator_texture.cc
@@ -44,8 +44,8 @@
   gfx::Canvas gfx_canvas(&paint_canvas, 1.0f);
   gfx::Canvas* canvas = &gfx_canvas;
 
-  DCHECK(texture_size.height() * kHeightWidthRatio == texture_size.width());
   size_.set_height(texture_size.height());
+
   SkPaint paint;
   paint.setColor(color_scheme().system_indicator_background);
 
@@ -99,9 +99,10 @@
 
 gfx::Size SystemIndicatorTexture::GetPreferredTextureSize(
     int maximum_width) const {
-  // Ensuring height is a quarter of the width.
+  // All indicators need to be the same height, so compute height, and then
+  // re-compute with based on whether the indicator has text or not.
   int height = maximum_width / kHeightWidthRatio;
-  return gfx::Size(height * kHeightWidthRatio, height);
+  return gfx::Size(has_text_ ? maximum_width : height, height);
 }
 
 gfx::SizeF SystemIndicatorTexture::GetDrawnSize() const {
diff --git a/chrome/browser/android/vr_shell/ui_elements/audio_capture_indicator.cc b/chrome/browser/android/vr_shell/ui_elements/audio_capture_indicator.cc
deleted file mode 100644
index 6e3b7df..0000000
--- a/chrome/browser/android/vr_shell/ui_elements/audio_capture_indicator.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/android/vr_shell/ui_elements/audio_capture_indicator.h"
-
-#include "base/memory/ptr_util.h"
-#include "chrome/browser/android/vr_shell/textures/system_indicator_texture.h"
-#include "chrome/grit/generated_resources.h"
-#include "ui/vector_icons/vector_icons.h"
-
-namespace vr_shell {
-
-AudioCaptureIndicator::AudioCaptureIndicator(int preferred_width)
-    : TexturedElement(preferred_width),
-      texture_(base::MakeUnique<SystemIndicatorTexture>(
-          ui::kMicrophoneIcon,
-          IDS_AUDIO_CALL_NOTIFICATION_TEXT_2)) {}
-
-AudioCaptureIndicator::~AudioCaptureIndicator() = default;
-
-UiTexture* AudioCaptureIndicator::GetTexture() const {
-  return texture_.get();
-}
-
-}  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_elements/audio_capture_indicator.h b/chrome/browser/android/vr_shell/ui_elements/audio_capture_indicator.h
deleted file mode 100644
index c400e58..0000000
--- a/chrome/browser/android/vr_shell/ui_elements/audio_capture_indicator.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_AUDIO_CAPTURE_INDICATOR_H_
-#define CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_AUDIO_CAPTURE_INDICATOR_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "chrome/browser/android/vr_shell/ui_elements/textured_element.h"
-
-namespace vr_shell {
-
-class UiTexture;
-
-class AudioCaptureIndicator : public TexturedElement {
- public:
-  explicit AudioCaptureIndicator(int preferred_width);
-  ~AudioCaptureIndicator() override;
-
- private:
-  UiTexture* GetTexture() const override;
-  std::unique_ptr<UiTexture> texture_;
-
-  DISALLOW_COPY_AND_ASSIGN(AudioCaptureIndicator);
-};
-
-}  // namespace vr_shell
-
-#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_AUDIO_CAPTURE_INDICATOR_H_
diff --git a/chrome/browser/android/vr_shell/ui_elements/location_access_indicator.cc b/chrome/browser/android/vr_shell/ui_elements/location_access_indicator.cc
deleted file mode 100644
index e312a22..0000000
--- a/chrome/browser/android/vr_shell/ui_elements/location_access_indicator.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/android/vr_shell/ui_elements/location_access_indicator.h"
-
-#include "base/memory/ptr_util.h"
-#include "chrome/browser/android/vr_shell/textures/system_indicator_texture.h"
-#include "chrome/grit/generated_resources.h"
-#include "ui/vector_icons/vector_icons.h"
-
-namespace vr_shell {
-
-LocationAccessIndicator::LocationAccessIndicator(int preferred_width)
-    : TexturedElement(preferred_width),
-      texture_(base::MakeUnique<SystemIndicatorTexture>(ui::kLocationOnIcon)) {}
-
-LocationAccessIndicator::~LocationAccessIndicator() = default;
-
-UiTexture* LocationAccessIndicator::GetTexture() const {
-  return texture_.get();
-}
-
-}  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_elements/location_access_indicator.h b/chrome/browser/android/vr_shell/ui_elements/location_access_indicator.h
deleted file mode 100644
index 9d217e6..0000000
--- a/chrome/browser/android/vr_shell/ui_elements/location_access_indicator.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_LOCATION_ACCESS_INDICATOR_H_
-#define CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_LOCATION_ACCESS_INDICATOR_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "chrome/browser/android/vr_shell/ui_elements/textured_element.h"
-
-namespace vr_shell {
-
-class UiTexture;
-
-class LocationAccessIndicator : public TexturedElement {
- public:
-  explicit LocationAccessIndicator(int preferred_width);
-  ~LocationAccessIndicator() override;
-
- private:
-  UiTexture* GetTexture() const override;
-  std::unique_ptr<UiTexture> texture_;
-
-  DISALLOW_COPY_AND_ASSIGN(LocationAccessIndicator);
-};
-
-}  // namespace vr_shell
-
-#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_LOCATION_ACCESS_INDICATOR_H_
diff --git a/chrome/browser/android/vr_shell/ui_elements/screen_capture_indicator.cc b/chrome/browser/android/vr_shell/ui_elements/screen_capture_indicator.cc
deleted file mode 100644
index 8de6575..0000000
--- a/chrome/browser/android/vr_shell/ui_elements/screen_capture_indicator.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/android/vr_shell/ui_elements/screen_capture_indicator.h"
-
-#include "base/memory/ptr_util.h"
-#include "chrome/browser/android/vr_shell/textures/system_indicator_texture.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/vector_icons/vector_icons.h"
-
-namespace vr_shell {
-
-ScreenCaptureIndicator::ScreenCaptureIndicator(int preferred_width)
-    : TexturedElement(preferred_width),
-      texture_(base::MakeUnique<SystemIndicatorTexture>(
-          vector_icons::kScreenShareIcon,
-          IDS_SCREEN_CAPTURE_NOTIFICATION_TEXT_2)) {}
-
-ScreenCaptureIndicator::~ScreenCaptureIndicator() = default;
-
-UiTexture* ScreenCaptureIndicator::GetTexture() const {
-  return texture_.get();
-}
-
-}  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_elements/screen_capture_indicator.h b/chrome/browser/android/vr_shell/ui_elements/screen_capture_indicator.h
deleted file mode 100644
index 4bcc528..0000000
--- a/chrome/browser/android/vr_shell/ui_elements/screen_capture_indicator.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_SCREEN_CAPTURE_INDICATOR_H_
-#define CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_SCREEN_CAPTURE_INDICATOR_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "chrome/browser/android/vr_shell/ui_elements/textured_element.h"
-
-namespace vr_shell {
-
-class UiTexture;
-
-class ScreenCaptureIndicator : public TexturedElement {
- public:
-  explicit ScreenCaptureIndicator(int preferred_width);
-  ~ScreenCaptureIndicator() override;
-
- private:
-  UiTexture* GetTexture() const override;
-  // TODO(acondor): Make |texture_| a member instead of smart pointer.
-  std::unique_ptr<UiTexture> texture_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScreenCaptureIndicator);
-};
-
-}  // namespace vr_shell
-
-#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_SCREEN_CAPTURE_INDICATOR_H_
diff --git a/chrome/browser/android/vr_shell/ui_elements/system_indicator.cc b/chrome/browser/android/vr_shell/ui_elements/system_indicator.cc
new file mode 100644
index 0000000..bc3a53a
--- /dev/null
+++ b/chrome/browser/android/vr_shell/ui_elements/system_indicator.cc
@@ -0,0 +1,36 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/vr_shell/ui_elements/system_indicator.h"
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/android/vr_shell/textures/system_indicator_texture.h"
+
+namespace vr_shell {
+
+SystemIndicator::SystemIndicator(int preferred_width,
+                                 float height_meters,
+                                 const gfx::VectorIcon& icon,
+                                 int string_id)
+    : TexturedElement(preferred_width), height_meters_(height_meters) {
+  if (string_id != 0) {
+    texture_ = base::MakeUnique<SystemIndicatorTexture>(icon, string_id);
+  } else {
+    texture_ = base::MakeUnique<SystemIndicatorTexture>(icon);
+  }
+}
+
+void SystemIndicator::UpdateElementSize() {
+  gfx::SizeF drawn_size = GetTexture()->GetDrawnSize();
+  float width = height_meters_ * drawn_size.width() / drawn_size.height();
+  set_size({width, height_meters_, 1});
+}
+
+SystemIndicator::~SystemIndicator() = default;
+
+UiTexture* SystemIndicator::GetTexture() const {
+  return texture_.get();
+}
+
+}  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_elements/system_indicator.h b/chrome/browser/android/vr_shell/ui_elements/system_indicator.h
new file mode 100644
index 0000000..d103abb
--- /dev/null
+++ b/chrome/browser/android/vr_shell/ui_elements/system_indicator.h
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_SYSTEM_INDICATOR_H_
+#define CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_SYSTEM_INDICATOR_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/android/vr_shell/ui_elements/textured_element.h"
+#include "ui/gfx/vector_icon_types.h"
+
+namespace vr_shell {
+
+class UiTexture;
+
+class SystemIndicator : public TexturedElement {
+ public:
+  explicit SystemIndicator(int preferred_width,
+                           float heigh_meters,
+                           const gfx::VectorIcon& icon,
+                           int string_id);
+  ~SystemIndicator() override;
+
+ protected:
+  void UpdateElementSize() override;
+
+ private:
+  UiTexture* GetTexture() const override;
+  std::unique_ptr<UiTexture> texture_;
+  float height_meters_;
+
+  DISALLOW_COPY_AND_ASSIGN(SystemIndicator);
+};
+
+}  // namespace vr_shell
+
+#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_SYSTEM_INDICATOR_H_
diff --git a/chrome/browser/android/vr_shell/ui_elements/textured_element.cc b/chrome/browser/android/vr_shell/ui_elements/textured_element.cc
index b4a065a..e0a7a16 100644
--- a/chrome/browser/android/vr_shell/ui_elements/textured_element.cc
+++ b/chrome/browser/android/vr_shell/ui_elements/textured_element.cc
@@ -26,9 +26,6 @@
   initialized_ = true;
   UpdateTexture();
   set_fill(Fill::SELF);
-  gfx::SizeF drawn_size = GetTexture()->GetDrawnSize();
-  float y = drawn_size.height() / drawn_size.width() * size().x();
-  set_size({size().x(), y, 1});
 }
 
 void TexturedElement::UpdateTexture() {
@@ -38,6 +35,15 @@
       texture_size_.width(), texture_size_.height());
   GetTexture()->DrawAndLayout(surface->getCanvas(), texture_size_);
   Flush(surface.get());
+  // Update the element size's aspect ratio to match the texture.
+  UpdateElementSize();
+}
+
+void TexturedElement::UpdateElementSize() {
+  // Updating the height according to width is a hack.  This may be overridden.
+  gfx::SizeF drawn_size = GetTexture()->GetDrawnSize();
+  float y = drawn_size.height() / drawn_size.width() * size().x();
+  set_size({size().x(), y, 1});
 }
 
 void TexturedElement::Render(UiElementRenderer* renderer,
diff --git a/chrome/browser/android/vr_shell/ui_elements/textured_element.h b/chrome/browser/android/vr_shell/ui_elements/textured_element.h
index 0347b69..f3e1411 100644
--- a/chrome/browser/android/vr_shell/ui_elements/textured_element.h
+++ b/chrome/browser/android/vr_shell/ui_elements/textured_element.h
@@ -33,6 +33,7 @@
  protected:
   virtual UiTexture* GetTexture() const = 0;
   virtual void UpdateTexture();
+  virtual void UpdateElementSize();
 
  private:
   void Flush(SkSurface* surface);
diff --git a/chrome/browser/android/vr_shell/ui_elements/video_capture_indicator.cc b/chrome/browser/android/vr_shell/ui_elements/video_capture_indicator.cc
deleted file mode 100644
index 8201df6..0000000
--- a/chrome/browser/android/vr_shell/ui_elements/video_capture_indicator.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/android/vr_shell/ui_elements/video_capture_indicator.h"
-
-#include "base/memory/ptr_util.h"
-#include "chrome/browser/android/vr_shell/textures/system_indicator_texture.h"
-#include "chrome/grit/generated_resources.h"
-#include "ui/vector_icons/vector_icons.h"
-
-namespace vr_shell {
-
-VideoCaptureIndicator::VideoCaptureIndicator(int preferred_width)
-    : TexturedElement(preferred_width),
-      texture_(base::MakeUnique<SystemIndicatorTexture>(
-          ui::kVideocamIcon,
-          IDS_VIDEO_CALL_NOTIFICATION_TEXT_2)) {}
-
-VideoCaptureIndicator::~VideoCaptureIndicator() = default;
-
-UiTexture* VideoCaptureIndicator::GetTexture() const {
-  return texture_.get();
-}
-
-}  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_elements/video_capture_indicator.h b/chrome/browser/android/vr_shell/ui_elements/video_capture_indicator.h
deleted file mode 100644
index 8b39a407..0000000
--- a/chrome/browser/android/vr_shell/ui_elements/video_capture_indicator.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_VIDEO_CAPTURE_INDICATOR_H_
-#define CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_VIDEO_CAPTURE_INDICATOR_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "chrome/browser/android/vr_shell/ui_elements/textured_element.h"
-
-namespace vr_shell {
-
-class UiTexture;
-
-class VideoCaptureIndicator : public TexturedElement {
- public:
-  explicit VideoCaptureIndicator(int preferred_width);
-  ~VideoCaptureIndicator() override;
-
- private:
-  UiTexture* GetTexture() const override;
-  std::unique_ptr<UiTexture> texture_;
-
-  DISALLOW_COPY_AND_ASSIGN(VideoCaptureIndicator);
-};
-
-}  // namespace vr_shell
-
-#endif  // CHROME_BROWSER_ANDROID_VR_SHELL_UI_ELEMENTS_VIDEO_CAPTURE_INDICATOR_H_
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.cc b/chrome/browser/android/vr_shell/ui_scene_manager.cc
index cac0780..84d337bd 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.cc
@@ -9,24 +9,24 @@
 #include "chrome/browser/android/vr_shell/textures/close_button_texture.h"
 #include "chrome/browser/android/vr_shell/textures/ui_texture.h"
 #include "chrome/browser/android/vr_shell/ui_browser_interface.h"
-#include "chrome/browser/android/vr_shell/ui_elements/audio_capture_indicator.h"
 #include "chrome/browser/android/vr_shell/ui_elements/button.h"
 #include "chrome/browser/android/vr_shell/ui_elements/exit_prompt.h"
 #include "chrome/browser/android/vr_shell/ui_elements/exit_prompt_backplane.h"
 #include "chrome/browser/android/vr_shell/ui_elements/exit_warning.h"
 #include "chrome/browser/android/vr_shell/ui_elements/loading_indicator.h"
-#include "chrome/browser/android/vr_shell/ui_elements/location_access_indicator.h"
 #include "chrome/browser/android/vr_shell/ui_elements/permanent_security_warning.h"
 #include "chrome/browser/android/vr_shell/ui_elements/presentation_toast.h"
-#include "chrome/browser/android/vr_shell/ui_elements/screen_capture_indicator.h"
 #include "chrome/browser/android/vr_shell/ui_elements/screen_dimmer.h"
+#include "chrome/browser/android/vr_shell/ui_elements/system_indicator.h"
 #include "chrome/browser/android/vr_shell/ui_elements/transient_security_warning.h"
 #include "chrome/browser/android/vr_shell/ui_elements/transient_url_bar.h"
 #include "chrome/browser/android/vr_shell/ui_elements/ui_element.h"
 #include "chrome/browser/android/vr_shell/ui_elements/ui_element_debug_id.h"
 #include "chrome/browser/android/vr_shell/ui_elements/url_bar.h"
-#include "chrome/browser/android/vr_shell/ui_elements/video_capture_indicator.h"
 #include "chrome/browser/android/vr_shell/ui_scene.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/vector_icons/vector_icons.h"
+#include "ui/vector_icons/vector_icons.h"
 
 namespace vr_shell {
 
@@ -70,17 +70,10 @@
 static constexpr float kUrlBarVerticalOffset = -0.516 * kUrlBarDistance;
 static constexpr float kUrlBarRotationRad = -0.175;
 
-static constexpr float kIndicatorContentDistance = 0.1;
-static constexpr float kAudioCaptureIndicatorWidth = 0.5;
-static constexpr float kVideoCaptureIndicatorWidth = 0.5;
-static constexpr float kScreenCaptureIndicatorWidth = 0.4;
-static constexpr float kLocationIndicatorWidth = 0.088;
-
-static constexpr float kCaptureIndicatorsVerticalOffset = 0.1;
-static constexpr float kAudioCaptureHorizontalOffset = -0.6;
-static constexpr float kVideoCaptureHorizontalOffset = 0;
-static constexpr float kScreenCaptureHorizontalOffset = 0.6;
-static constexpr float kLocationAccessHorizontalOffset = 1.0;
+static constexpr float kIndicatorHeight = 0.08;
+static constexpr float kIndicatorGap = 0.05;
+static constexpr float kIndicatorVerticalOffset = 0.1;
+static constexpr float kIndicatorDistanceOffset = 0.1;
 
 static constexpr float kTransientUrlBarDistance = 1.4;
 static constexpr float kTransientUrlBarWidth =
@@ -219,57 +212,37 @@
 void UiSceneManager::CreateSystemIndicators() {
   std::unique_ptr<UiElement> element;
 
-  element = base::MakeUnique<AudioCaptureIndicator>(512);
-  element->set_debug_id(kAudioCaptureIndicator);
-  element->set_id(AllocateId());
-  element->set_translation({kAudioCaptureHorizontalOffset,
-                            kCaptureIndicatorsVerticalOffset,
-                            kIndicatorContentDistance});
-  element->set_parent_id(main_content_->id());
-  element->set_y_anchoring(YAnchoring::YTOP);
-  element->set_size({kAudioCaptureIndicatorWidth, 0, 1});
-  element->set_visible(false);
-  audio_capture_indicator_ = element.get();
-  scene_->AddUiElement(std::move(element));
+  struct Indicator {
+    UiElement** element;
+    UiElementDebugId debug_id;
+    const gfx::VectorIcon& icon;
+    int resource_string;
+  };
+  const std::vector<Indicator> indicators = {
+      {&audio_capture_indicator_, kAudioCaptureIndicator, ui::kMicrophoneIcon,
+       IDS_AUDIO_CALL_NOTIFICATION_TEXT_2},
+      {&video_capture_indicator_, kVideoCaptureIndicator, ui::kVideocamIcon,
+       IDS_VIDEO_CALL_NOTIFICATION_TEXT_2},
+      {&screen_capture_indicator_, kScreenCaptureIndicator,
+       vector_icons::kScreenShareIcon, IDS_SCREEN_CAPTURE_NOTIFICATION_TEXT_2},
+      {&location_access_indicator_, kLocationAccessIndicator,
+       ui::kLocationOnIcon, 0},
+  };
 
-  element = base::MakeUnique<LocationAccessIndicator>(250);
-  element->set_debug_id(kLocationAccessIndicator);
-  element->set_id(AllocateId());
-  element->set_translation({kLocationAccessHorizontalOffset,
-                            kCaptureIndicatorsVerticalOffset,
-                            kIndicatorContentDistance});
-  element->set_size({kLocationIndicatorWidth, 0, 1});
-  element->set_parent_id(main_content_->id());
-  element->set_y_anchoring(YAnchoring::YTOP);
-  element->set_visible(false);
-  location_access_indicator_ = element.get();
-  scene_->AddUiElement(std::move(element));
+  for (const auto& indicator : indicators) {
+    element = base::MakeUnique<SystemIndicator>(
+        512, kIndicatorHeight, indicator.icon, indicator.resource_string);
+    element->set_debug_id(indicator.debug_id);
+    element->set_id(AllocateId());
+    element->set_parent_id(main_content_->id());
+    element->set_y_anchoring(YAnchoring::YTOP);
+    element->set_visible(false);
+    *(indicator.element) = element.get();
+    system_indicators_.push_back(element.get());
+    scene_->AddUiElement(std::move(element));
+  }
 
-  element = base::MakeUnique<VideoCaptureIndicator>(512);
-  element->set_debug_id(kVideoCaptureIndicator);
-  element->set_id(AllocateId());
-  element->set_translation({kVideoCaptureHorizontalOffset,
-                            kCaptureIndicatorsVerticalOffset,
-                            kIndicatorContentDistance});
-  element->set_parent_id(main_content_->id());
-  element->set_y_anchoring(YAnchoring::YTOP);
-  element->set_size({kVideoCaptureIndicatorWidth, 0, 1});
-  element->set_visible(false);
-  video_capture_indicator_ = element.get();
-  scene_->AddUiElement(std::move(element));
-
-  element = base::MakeUnique<ScreenCaptureIndicator>(512);
-  element->set_debug_id(kScreenCaptureIndicator);
-  element->set_id(AllocateId());
-  element->set_translation({kScreenCaptureHorizontalOffset,
-                            kCaptureIndicatorsVerticalOffset,
-                            kIndicatorContentDistance});
-  element->set_parent_id(main_content_->id());
-  element->set_y_anchoring(YAnchoring::YTOP);
-  element->set_size({kScreenCaptureIndicatorWidth, 0, 1});
-  element->set_visible(false);
-  screen_capture_indicator_ = element.get();
-  scene_->AddUiElement(std::move(element));
+  ConfigureIndicators();
 }
 
 void UiSceneManager::CreateContentQuad() {
@@ -569,6 +542,13 @@
   ConfigureScene();
 }
 
+void UiSceneManager::OnGLInitialized() {
+  scene_->OnGLInitialized();
+
+  // Indicators don't know their position until they've rendered themselves.
+  ConfigureIndicators();
+}
+
 void UiSceneManager::OnAppButtonClicked() {
   // App button click exits the WebVR presentation and fullscreen.
   browser_->ExitPresent();
@@ -603,6 +583,24 @@
   video_capture_indicator_->set_visible(!web_vr_mode_ && video_capturing_);
   screen_capture_indicator_->set_visible(!web_vr_mode_ && screen_capturing_);
   location_access_indicator_->set_visible(!web_vr_mode_ && location_access_);
+
+  // Position elements dynamically relative to each other, based on which
+  // indicators are showing, and how big each one is.
+  float total_width = kIndicatorGap * (system_indicators_.size() - 1);
+  for (const UiElement* indicator : system_indicators_) {
+    if (indicator->visible())
+      total_width += indicator->size().x();
+  }
+  float x_position = -total_width / 2;
+  for (UiElement* indicator : system_indicators_) {
+    if (!indicator->visible())
+      continue;
+    float width = indicator->size().x();
+    indicator->set_translation({x_position + width / 2,
+                                kIndicatorVerticalOffset,
+                                kIndicatorDistanceOffset});
+    x_position += width + kIndicatorGap;
+  }
 }
 
 void UiSceneManager::ConfigurePresentationToast() {
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.h b/chrome/browser/android/vr_shell/ui_scene_manager.h
index 8c7c187..06ec34e6 100644
--- a/chrome/browser/android/vr_shell/ui_scene_manager.h
+++ b/chrome/browser/android/vr_shell/ui_scene_manager.h
@@ -51,6 +51,7 @@
   // These methods are currently stubbed.
   void SetHistoryButtonsEnabled(bool can_go_back, bool can_go_forward);
 
+  void OnGLInitialized();
   void OnAppButtonClicked();
   void OnAppButtonGesturePerformed(UiInterface::Direction direction);
 
@@ -113,6 +114,8 @@
   TransientUrlBar* transient_url_bar_ = nullptr;
   LoadingIndicator* loading_indicator_ = nullptr;
 
+  std::vector<UiElement*> system_indicators_;
+
   bool in_cct_;
   bool web_vr_mode_;
   bool web_vr_autopresented_ = false;
diff --git a/chrome/browser/android/vr_shell/vr_gl_thread.cc b/chrome/browser/android/vr_shell/vr_gl_thread.cc
index be1bf47..b1fc9138 100644
--- a/chrome/browser/android/vr_shell/vr_gl_thread.cc
+++ b/chrome/browser/android/vr_shell/vr_gl_thread.cc
@@ -131,6 +131,12 @@
       base::Bind(&VrShell::ToggleCardboardGamepad, weak_vr_shell_, enabled));
 }
 
+void VrGLThread::OnGLInitialized() {
+  task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&UiSceneManager::OnGLInitialized, weak_scene_manager_));
+}
+
 void VrGLThread::OnUnsupportedMode(UiUnsupportedMode mode) {
   main_thread_task_runner_->PostTask(
       FROM_HERE,
diff --git a/chrome/browser/android/vr_shell/vr_gl_thread.h b/chrome/browser/android/vr_shell/vr_gl_thread.h
index 61b9a0e..95ff30ee 100644
--- a/chrome/browser/android/vr_shell/vr_gl_thread.h
+++ b/chrome/browser/android/vr_shell/vr_gl_thread.h
@@ -46,7 +46,7 @@
     return weak_scene_manager_;
   }
 
-  // GlBrowserInterface implementation (VrShellGl calling to VrShell).
+  // GlBrowserInterface implementation (VrShellGl calling out to UI/VrShell).
   void ContentSurfaceChanged(jobject surface) override;
   void GvrDelegateReady(gvr::ViewerType viewer_type) override;
   void UpdateGamepadData(device::GvrGamepadData) override;
@@ -60,6 +60,7 @@
       device::mojom::VRDisplayInfoPtr* info) override;
   void OnContentPaused(bool enabled) override;
   void ToggleCardboardGamepad(bool enabled) override;
+  void OnGLInitialized() override;
 
   // UiBrowserInterface implementation (UI calling to VrShell).
   void ExitPresent() override;
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index 0fe0fef8..c0a97bd 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -311,7 +311,7 @@
 
   InitializeRenderer();
 
-  scene_->OnGLInitialized();
+  browser_->OnGLInitialized();
 
   gfx::Size webvr_size =
       device::GvrDelegate::GetRecommendedWebVrSize(gvr_api_.get());
diff --git a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc
index 513e7349..6ec026b 100644
--- a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc
+++ b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc
@@ -28,17 +28,16 @@
     ArcBridgeService* bridge_service,
     const AccountId& account_id)
     : ArcService(bridge_service), account_id_(account_id), binding_(this) {
-  DCHECK(thread_checker_.CalledOnValidThread());
   arc_bridge_service()->boot_phase_monitor()->AddObserver(this);
 }
 
 ArcBootPhaseMonitorBridge::~ArcBootPhaseMonitorBridge() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   arc_bridge_service()->boot_phase_monitor()->RemoveObserver(this);
 }
 
 void ArcBootPhaseMonitorBridge::OnInstanceReady() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
       arc_bridge_service()->boot_phase_monitor(), Init);
   DCHECK(instance);
@@ -48,11 +47,11 @@
 }
 
 void ArcBootPhaseMonitorBridge::OnInstanceClosed() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 }
 
 void ArcBootPhaseMonitorBridge::OnBootCompleted() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   VLOG(2) << "OnBootCompleted";
 
   chromeos::SessionManagerClient* session_manager_client =
diff --git a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h
index 4e36365..3c88d07 100644
--- a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h
+++ b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h
@@ -38,12 +38,12 @@
   void OnBootCompleted() override;
 
  private:
+  THREAD_CHECKER(thread_checker_);
+
   const AccountId account_id_;
   mojo::Binding<mojom::BootPhaseMonitorHost> binding_;
   std::unique_ptr<ArcInstanceThrottle> throttle_;
 
-  base::ThreadChecker thread_checker_;
-
   DISALLOW_COPY_AND_ASSIGN(ArcBootPhaseMonitorBridge);
 };
 
diff --git a/chrome/browser/chromeos/arc/enterprise/arc_enterprise_reporting_service.cc b/chrome/browser/chromeos/arc/enterprise/arc_enterprise_reporting_service.cc
index 70af5f4..574f5c3 100644
--- a/chrome/browser/chromeos/arc/enterprise/arc_enterprise_reporting_service.cc
+++ b/chrome/browser/chromeos/arc/enterprise/arc_enterprise_reporting_service.cc
@@ -21,12 +21,12 @@
 }
 
 ArcEnterpriseReportingService::~ArcEnterpriseReportingService() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   arc_bridge_service()->enterprise_reporting()->RemoveObserver(this);
 }
 
 void ArcEnterpriseReportingService::OnInstanceReady() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
       arc_bridge_service()->enterprise_reporting(), Init);
   DCHECK(instance);
@@ -37,7 +37,7 @@
 
 void ArcEnterpriseReportingService::ReportManagementState(
     mojom::ManagementState state) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   VLOG(1) << "ReportManagementState state=" << state;
 
   if (state == mojom::ManagementState::MANAGED_DO_LOST) {
diff --git a/chrome/browser/chromeos/arc/enterprise/arc_enterprise_reporting_service.h b/chrome/browser/chromeos/arc/enterprise/arc_enterprise_reporting_service.h
index 5e1dfab..d5d0fa73 100644
--- a/chrome/browser/chromeos/arc/enterprise/arc_enterprise_reporting_service.h
+++ b/chrome/browser/chromeos/arc/enterprise/arc_enterprise_reporting_service.h
@@ -35,7 +35,7 @@
   void ReportManagementState(mojom::ManagementState state) override;
 
  private:
-  base::ThreadChecker thread_checker_;
+  THREAD_CHECKER(thread_checker_);
 
   mojo::Binding<mojom::EnterpriseReportingHost> binding_;
 
diff --git a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.cc b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.cc
index e629200..0ab5ad1 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_arc_home_service.cc
@@ -28,6 +28,7 @@
 #include "ui/aura/window.h"
 #include "ui/snapshot/snapshot.h"
 #include "ui/wm/public/activation_client.h"
+#include "url/gurl.h"
 
 namespace arc {
 
@@ -64,10 +65,17 @@
 
 void RequestVoiceInteractionStructureCallback(
     const base::Callback<void(mojom::VoiceInteractionStructurePtr)>& callback,
+    const gfx::Rect& bounds,
+    const std::string& web_url,
     const ui::AXTreeUpdate& update) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  callback.Run(CreateVoiceInteractionStructure(
+  auto root = mojom::VoiceInteractionStructure::New();
+  root->rect = bounds;
+  root->class_name = "android.view.dummy.root.WebUrl";
+  root->text = base::UTF8ToUTF16(web_url);
+  root->children.push_back(CreateVoiceInteractionStructure(
       *ui::AXSnapshotNodeAndroid::Create(update, false)));
+  callback.Run(std::move(root));
 }
 
 }  // namespace
@@ -121,7 +129,9 @@
   }
 
   web_contents->RequestAXTreeSnapshot(
-      base::Bind(&RequestVoiceInteractionStructureCallback, callback));
+      base::Bind(&RequestVoiceInteractionStructureCallback, callback,
+                 browser->window()->GetBounds(),
+                 web_contents->GetLastCommittedURL().spec()));
 }
 
 void ArcVoiceInteractionArcHomeService::OnVoiceInteractionOobeSetupComplete() {
diff --git a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
index 29a0a38..7b01d15 100644
--- a/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
+++ b/chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.cc
@@ -950,7 +950,7 @@
 void WallpaperManager::OnColorCalculationComplete() {
   size_t num_of_calculation = color_calculator_->prominent_colors().size();
   DCHECK_EQ(1u, num_of_calculation);
-  SkColor color = color_calculator_->prominent_colors()[num_of_calculation];
+  SkColor color = color_calculator_->prominent_colors()[num_of_calculation - 1];
   color_calculator_->RemoveObserver(this);
   color_calculator_.reset();
   if (prominent_color_ == color)
diff --git a/chrome/browser/chromeos/system_logs/single_log_source.cc b/chrome/browser/chromeos/system_logs/single_log_source.cc
index 817c4be3..005cb4a 100644
--- a/chrome/browser/chromeos/system_logs/single_log_source.cc
+++ b/chrome/browser/chromeos/system_logs/single_log_source.cc
@@ -40,6 +40,8 @@
       return base::FilePath("messages");
     case SingleLogSource::SupportedSource::kUiLatest:
       return base::FilePath("ui/ui.LATEST");
+    case SingleLogSource::SupportedSource::kAtrusLog:
+      return base::FilePath("atrus.log");
   }
   NOTREACHED();
   return base::FilePath();
diff --git a/chrome/browser/chromeos/system_logs/single_log_source.h b/chrome/browser/chromeos/system_logs/single_log_source.h
index 6afca1a7..5b66b606 100644
--- a/chrome/browser/chromeos/system_logs/single_log_source.h
+++ b/chrome/browser/chromeos/system_logs/single_log_source.h
@@ -28,6 +28,9 @@
 
     // For /var/log/ui/ui.LATEST.
     kUiLatest,
+
+    // For /var/log/atrus.log.
+    kAtrusLog,
   };
 
   explicit SingleLogSource(SupportedSource source);
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index fbd5255..65023640 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2486,6 +2486,12 @@
 
 #endif  // defined(OS_ANDROID)
 
+const char kEnableAutofillCreditCardBankNameDisplayName[] =
+    "Display the issuer bank name of a credit card in autofill.";
+
+const char kEnableAutofillCreditCardBankNameDisplayDescription[] =
+    "If enabled, displays the issuer bank name of a credit card in autofill.";
+
 const char kEnableAutofillCreditCardLastUsedDateDisplayName[] =
     "Display the last used date of a credit card in autofill.";
 
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 24a390d..cc47b59 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -175,6 +175,9 @@
 extern const char kEnableAsmWasmName[];
 extern const char kEnableAsmWasmDescription[];
 
+extern const char kEnableAutofillCreditCardBankNameDisplayName[];
+extern const char kEnableAutofillCreditCardBankNameDisplayDescription[];
+
 extern const char kEnableAutofillCreditCardLastUsedDateDisplayName[];
 extern const char kEnableAutofillCreditCardLastUsedDateDisplayDescription[];
 
diff --git a/chrome/browser/icon_loader.cc b/chrome/browser/icon_loader.cc
index d679bd3..a4934f0 100644
--- a/chrome/browser/icon_loader.cc
+++ b/chrome/browser/icon_loader.cc
@@ -4,7 +4,11 @@
 
 #include "chrome/browser/icon_loader.h"
 
+#include <utility>
+
 #include "base/bind.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -20,10 +24,9 @@
 void IconLoader::Start() {
   target_task_runner_ = base::ThreadTaskRunnerHandle::Get();
 
-  BrowserThread::PostTaskAndReply(
-      BrowserThread::FILE, FROM_HERE,
-      base::BindOnce(&IconLoader::ReadGroup, base::Unretained(this)),
-      base::BindOnce(&IconLoader::OnReadGroup, base::Unretained(this)));
+  base::PostTaskWithTraits(
+      FROM_HERE, traits(),
+      base::BindOnce(&IconLoader::ReadGroup, base::Unretained(this)));
 }
 
 IconLoader::IconLoader(const base::FilePath& file_path,
@@ -35,10 +38,7 @@
 
 void IconLoader::ReadGroup() {
   group_ = GroupForFilepath(file_path_);
-}
 
-void IconLoader::OnReadGroup() {
-  BrowserThread::PostTask(
-      ReadIconThreadID(), FROM_HERE,
-      base::BindOnce(&IconLoader::ReadIcon, base::Unretained(this)));
+  GetReadIconTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&IconLoader::ReadIcon, base::Unretained(this)));
 }
diff --git a/chrome/browser/icon_loader.h b/chrome/browser/icon_loader.h
index 837be9673..fc91909 100644
--- a/chrome/browser/icon_loader.h
+++ b/chrome/browser/icon_loader.h
@@ -12,6 +12,7 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
+#include "base/task_scheduler/task_traits.h"
 #include "build/build_config.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/gfx/image/image.h"
@@ -66,13 +67,19 @@
   // Given a file path, get the group for the given file.
   static IconGroup GroupForFilepath(const base::FilePath& file_path);
 
-  // The thread ReadIcon() should be called on.
-  static content::BrowserThread::ID ReadIconThreadID();
+  // The TaskRunner that ReadIcon() must be called on.
+  static scoped_refptr<base::TaskRunner> GetReadIconTaskRunner();
 
   void ReadGroup();
-  void OnReadGroup();
   void ReadIcon();
 
+  // The traits of the tasks posted by this class. These operations may block,
+  // because they are fetching icons from the disk, yet the result will be seen
+  // by the user so they should be prioritized accordingly.
+  static constexpr base::TaskTraits traits() {
+    return {base::MayBlock(), base::TaskPriority::USER_VISIBLE};
+  }
+
   // The task runner object of the thread in which we notify the delegate.
   scoped_refptr<base::SingleThreadTaskRunner> target_task_runner_;
 
diff --git a/chrome/browser/icon_loader_android.cc b/chrome/browser/icon_loader_android.cc
index 138d109..6a7173f 100644
--- a/chrome/browser/icon_loader_android.cc
+++ b/chrome/browser/icon_loader_android.cc
@@ -13,9 +13,9 @@
 }
 
 // static
-content::BrowserThread::ID IconLoader::ReadIconThreadID() {
+scoped_refptr<base::TaskRunner> IconLoader::GetReadIconTaskRunner() {
   NOTIMPLEMENTED();
-  return content::BrowserThread::FILE;
+  return nullptr;
 }
 
 void IconLoader::ReadIcon() {
diff --git a/chrome/browser/icon_loader_auralinux.cc b/chrome/browser/icon_loader_auralinux.cc
index 9d34369..0e5ffad 100644
--- a/chrome/browser/icon_loader_auralinux.cc
+++ b/chrome/browser/icon_loader_auralinux.cc
@@ -17,10 +17,11 @@
 }
 
 // static
-content::BrowserThread::ID IconLoader::ReadIconThreadID() {
+scoped_refptr<base::TaskRunner> IconLoader::GetReadIconTaskRunner() {
   // ReadIcon() calls into views::LinuxUI and GTK2 code, so it must be on the UI
   // thread.
-  return content::BrowserThread::UI;
+  return content::BrowserThread::GetTaskRunnerForThread(
+      content::BrowserThread::UI);
 }
 
 void IconLoader::ReadIcon() {
diff --git a/chrome/browser/icon_loader_chromeos.cc b/chrome/browser/icon_loader_chromeos.cc
index 30f0005..70f78c12 100644
--- a/chrome/browser/icon_loader_chromeos.cc
+++ b/chrome/browser/icon_loader_chromeos.cc
@@ -191,10 +191,11 @@
 }
 
 // static
-content::BrowserThread::ID IconLoader::ReadIconThreadID() {
+scoped_refptr<base::TaskRunner> IconLoader::GetReadIconTaskRunner() {
   // ReadIcon touches non thread safe ResourceBundle images, so it must be on
   // the UI thread.
-  return content::BrowserThread::UI;
+  return content::BrowserThread::GetTaskRunnerForThread(
+      content::BrowserThread::UI);
 }
 
 void IconLoader::ReadIcon() {
diff --git a/chrome/browser/icon_loader_mac.mm b/chrome/browser/icon_loader_mac.mm
index 392f2b2..f10b47d2 100644
--- a/chrome/browser/icon_loader_mac.mm
+++ b/chrome/browser/icon_loader_mac.mm
@@ -11,6 +11,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/strings/sys_string_conversions.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/threading/thread.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_util_mac.h"
@@ -22,8 +23,9 @@
 }
 
 // static
-content::BrowserThread::ID IconLoader::ReadIconThreadID() {
-  return content::BrowserThread::FILE;
+scoped_refptr<base::TaskRunner> IconLoader::GetReadIconTaskRunner() {
+  // NSWorkspace is thread-safe.
+  return base::CreateTaskRunnerWithTraits(traits());
 }
 
 void IconLoader::ReadIcon() {
diff --git a/chrome/browser/icon_loader_win.cc b/chrome/browser/icon_loader_win.cc
index 035c759..1d0912b 100644
--- a/chrome/browser/icon_loader_win.cc
+++ b/chrome/browser/icon_loader_win.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/threading/thread.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/display/win/dpi.h"
@@ -30,8 +31,10 @@
 }
 
 // static
-content::BrowserThread::ID IconLoader::ReadIconThreadID() {
-  return content::BrowserThread::FILE;
+scoped_refptr<base::TaskRunner> IconLoader::GetReadIconTaskRunner() {
+  // Technically speaking, only a thread with COM is needed, not one that has
+  // a COM STA. However, this is what is available for now.
+  return base::CreateCOMSTATaskRunnerWithTraits(traits());
 }
 
 void IconLoader::ReadIcon() {
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
index e101f47c..39acd69c 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.cc
@@ -62,7 +62,8 @@
 
 MediaRouterMojoImpl::MediaRouterMojoImpl(
     extensions::EventPageTracker* event_page_tracker,
-    content::BrowserContext* context)
+    content::BrowserContext* context,
+    FirewallCheck check_firewall)
     : event_page_tracker_(event_page_tracker),
       instance_id_(base::GenerateGUID()),
       availability_(mojom::MediaRouter::SinkAvailability::UNAVAILABLE),
@@ -72,9 +73,11 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(event_page_tracker_);
 #if defined(OS_WIN)
-  CanFirewallUseLocalPorts(
-      base::Bind(&MediaRouterMojoImpl::OnFirewallCheckComplete,
-                 weak_factory_.GetWeakPtr()));
+  if (check_firewall == FirewallCheck::RUN) {
+    CanFirewallUseLocalPorts(
+        base::Bind(&MediaRouterMojoImpl::OnFirewallCheckComplete,
+                   weak_factory_.GetWeakPtr()));
+  }
 #endif
 }
 
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl.h b/chrome/browser/media/router/mojo/media_router_mojo_impl.h
index d1c6c00..f6b52ede 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl.h
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl.h
@@ -211,10 +211,19 @@
     DISALLOW_COPY_AND_ASSIGN(MediaRoutesQuery);
   };
 
+  enum class FirewallCheck {
+    // Skips the firewall check for the benefit of unit tests so they do not
+    // have to depend on the system's firewall configuration.
+    SKIP_FOR_TESTING,
+    // Perform the firewall check (default).
+    RUN,
+  };
+
   // Standard constructor, used by
   // MediaRouterMojoImplFactory::GetApiForBrowserContext.
   MediaRouterMojoImpl(extensions::EventPageTracker* event_page_tracker,
-                      content::BrowserContext* context);
+                      content::BrowserContext* context,
+                      FirewallCheck check_firewall = FirewallCheck::RUN);
 
   // Binds |this| to a Mojo interface request, so that clients can acquire a
   // handle to a MediaRouterMojoImpl instance via the Mojo service connector.
diff --git a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
index dec8aba..264a394 100644
--- a/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
+++ b/chrome/browser/media/router/mojo/media_router_mojo_impl_unittest.cc
@@ -1427,8 +1427,9 @@
     DCHECK(process_manager_);
 
     // Create MR and its proxy, so that it can be accessed through Mojo.
-    media_router_.reset(
-        new MediaRouterMojoImpl(process_manager_, profile_.get()));
+    media_router_.reset(new MediaRouterMojoImpl(
+        process_manager_, profile_.get(),
+        MediaRouterMojoImpl::FirewallCheck::SKIP_FOR_TESTING));
     ProcessEventLoop();
   }
 
@@ -1570,6 +1571,7 @@
       .WillOnce(Return(true));
   EXPECT_CALL(*process_manager_, WakeEventPage(extension_->id(), _))
       .WillOnce(testing::DoAll(media::RunCallback<1>(true), Return(true)));
+  ASSERT_TRUE(media_router_->pending_requests_.empty());
   media_router_->DetachRoute(kRouteId);
   EXPECT_EQ(1u, media_router_->pending_requests_.size());
   ExpectWakeReasonBucketCount(MediaRouteProviderWakeReason::DETACH_ROUTE, 1);
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index 0899036..a36c740 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -922,8 +922,6 @@
   // Observe history deletions for all profiles.
   registrar_.Add(this, chrome::NOTIFICATION_PROFILE_ADDED,
                  content::NotificationService::AllBrowserContextsAndSources());
-  registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
-                 content::NotificationService::AllBrowserContextsAndSources());
   for (Profile* profile :
        g_browser_process->profile_manager()->GetLoadedProfiles()) {
     RegisterForProfileEvents(profile);
@@ -958,10 +956,6 @@
 
   switch (type) {
     case chrome::NOTIFICATION_BROWSER_OPENED:
-      // May have opened an incognito window.
-      UpdateRunningServices();
-      metrics_service_->OnApplicationNotIdle();
-      break;
     case chrome::NOTIFICATION_BROWSER_CLOSED:
     case chrome::NOTIFICATION_TAB_PARENTED:
     case chrome::NOTIFICATION_TAB_CLOSING:
@@ -975,10 +969,6 @@
     case chrome::NOTIFICATION_PROFILE_ADDED:
       RegisterForProfileEvents(content::Source<Profile>(source).ptr());
       break;
-    case chrome::NOTIFICATION_PROFILE_DESTROYED:
-      // May have closed last incognito window.
-      UpdateRunningServices();
-      break;
 
     default:
       NOTREACHED();
diff --git a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
index 83594d47..f7835a84f0 100644
--- a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
@@ -286,7 +286,3 @@
 bool ChromeMetricsServicesManagerClient::IsMetricsReportingForceEnabled() {
   return ChromeMetricsServiceClient::IsMetricsReportingForceEnabled();
 }
-
-bool ChromeMetricsServicesManagerClient::IsIncognitoSessionActive() {
-  return chrome::IsIncognitoSessionActive();
-}
diff --git a/chrome/browser/metrics/chrome_metrics_services_manager_client.h b/chrome/browser/metrics/chrome_metrics_services_manager_client.h
index a851aaf62..24014ac 100644
--- a/chrome/browser/metrics/chrome_metrics_services_manager_client.h
+++ b/chrome/browser/metrics/chrome_metrics_services_manager_client.h
@@ -80,8 +80,6 @@
 
   bool IsMetricsReportingForceEnabled() override;
 
-  bool IsIncognitoSessionActive() override;
-
   // Gets the MetricsStateManager, creating it if it has not already been
   // created.
   metrics::MetricsStateManager* GetMetricsStateManager();
diff --git a/chrome/browser/metrics/ukm_browsertest.cc b/chrome/browser/metrics/ukm_browsertest.cc
deleted file mode 100644
index 0c5cb5b..0000000
--- a/chrome/browser/metrics/ukm_browsertest.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
-#include "chrome/browser/sync/test/integration/sync_test.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "components/browser_sync/profile_sync_service.h"
-#include "components/metrics_services_manager/metrics_services_manager.h"
-#include "components/sync/test/fake_server/fake_server_network_resources.h"
-#include "components/ukm/public/ukm_recorder.h"
-#include "components/ukm/ukm_service.h"
-
-namespace metrics {
-
-// Test fixture that provides access to some UKM internals.
-class UkmBrowserTest : public SyncTest {
- public:
-  UkmBrowserTest() : SyncTest(SINGLE_CLIENT) {}
-
- protected:
-  bool ukm_enabled() {
-    auto* service = ukm_service();
-    return service ? service->recording_enabled_ : false;
-  }
-  uint64_t client_id() {
-    auto* service = ukm_service();
-    return service ? service->client_id_ : 0;
-  }
-
-  void EnableSyncForProfile(Profile* profile) {
-    browser_sync::ProfileSyncService* sync_service =
-        ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
-
-    sync_service->OverrideNetworkResourcesForTest(
-        base::MakeUnique<fake_server::FakeServerNetworkResources>(
-            GetFakeServer()->AsWeakPtr()));
-
-    std::unique_ptr<ProfileSyncServiceHarness> harness =
-        ProfileSyncServiceHarness::Create(
-            profile, "user@gmail.com", "password",
-            ProfileSyncServiceHarness::SigninType::FAKE_SIGNIN);
-
-    harness->SetupSync();
-  }
-
- private:
-  ukm::UkmService* ukm_service() {
-    return static_cast<ukm::UkmService*>(ukm::UkmRecorder::Get());
-  }
-};
-
-// Make sure that UKM is disabled while an incognito window is open.
-IN_PROC_BROWSER_TEST_F(UkmBrowserTest, IncognitoCheck) {
-  // Enable metrics recording and update MetricsServicesManager.
-  bool metrics_enabled = true;
-  ChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting(
-      &metrics_enabled);
-  g_browser_process->GetMetricsServicesManager()->UpdateUploadPermissions(
-      false);
-
-  Profile* profile = ProfileManager::GetActiveUserProfile();
-  EnableSyncForProfile(profile);
-
-  CreateBrowser(ProfileManager::GetActiveUserProfile());
-  EXPECT_TRUE(ukm_enabled());
-  uint64_t original_client_id = client_id();
-
-  Browser* incognito_browser = CreateIncognitoBrowser();
-  EXPECT_FALSE(ukm_enabled());
-
-  CloseBrowserSynchronously(incognito_browser);
-  EXPECT_TRUE(ukm_enabled());
-  // Client ID should not have been reset.
-  EXPECT_EQ(original_client_id, client_id());
-
-  ChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting(nullptr);
-}
-
-// TODO(holte): Add more tests to cover multi-profile cases.
-
-}  // namespace metrics
diff --git a/chrome/browser/net/proxy_service_factory.cc b/chrome/browser/net/proxy_service_factory.cc
index dcb7c5c..f53dc2f 100644
--- a/chrome/browser/net/proxy_service_factory.cc
+++ b/chrome/browser/net/proxy_service_factory.cc
@@ -52,12 +52,8 @@
   // configuration in case nothing is configured through prefs (Note: prefs
   // include command line and configuration policy).
 
-  // TODO(port): the IO and FILE message loops are only used by Linux.  Can
-  // that code be moved to chrome/browser instead of being in net, so that it
-  // can use BrowserThread instead of raw MessageLoop pointers? See bug 25354.
   base_service = net::ProxyService::CreateSystemProxyConfigService(
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE));
+      BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
 #endif  // !defined(OS_CHROMEOS)
 
   return tracker->CreateTrackingProxyConfigService(std::move(base_service));
diff --git a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.cc
index a509bd08..07caa63 100644
--- a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.cc
@@ -6,18 +6,31 @@
 
 #include "chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
+#include "net/http/http_response_headers.h"
 #include "third_party/WebKit/public/platform/WebLoadingBehaviorFlag.h"
 
 namespace internal {
 
 const char kHistogramServiceWorkerParseStart[] =
     "PageLoad.Clients.ServiceWorker.ParseTiming.NavigationToParseStart";
+const char kHistogramServiceWorkerParseStartForwardBack[] =
+    "PageLoad.Clients.ServiceWorker.ParseTiming.NavigationToParseStart."
+    "LoadType.ForwardBackNavigation";
+const char kHistogramServiceWorkerParseStartForwardBackNoStore[] =
+    "PageLoad.Clients.ServiceWorker.ParseTiming.NavigationToParseStart."
+    "LoadType.ForwardBackNavigation.NoStore";
 const char kBackgroundHistogramServiceWorkerParseStart[] =
     "PageLoad.Clients.ServiceWorker.ParseTiming.NavigationToParseStart."
     "Background";
 const char kHistogramServiceWorkerFirstContentfulPaint[] =
     "PageLoad.Clients.ServiceWorker.PaintTiming."
     "NavigationToFirstContentfulPaint";
+const char kHistogramServiceWorkerFirstContentfulPaintForwardBack[] =
+    "PageLoad.Clients.ServiceWorker.PaintTiming."
+    "NavigationToFirstContentfulPaint.LoadType.ForwardBackNavigation";
+const char kHistogramServiceWorkerFirstContentfulPaintForwardBackNoStore[] =
+    "PageLoad.Clients.ServiceWorker.PaintTiming."
+    "NavigationToFirstContentfulPaint.LoadType.ForwardBackNavigation.NoStore";
 const char kBackgroundHistogramServiceWorkerFirstContentfulPaint[] =
     "PageLoad.Clients.ServiceWorker.PaintTiming."
     "NavigationToFirstContentfulPaint.Background";
@@ -108,10 +121,28 @@
   return url.host_piece() == "inbox.google.com";
 }
 
+bool IsForwardBackLoad(ui::PageTransition transition) {
+  return transition & ui::PAGE_TRANSITION_FORWARD_BACK;
+}
+
 }  // namespace
 
 ServiceWorkerPageLoadMetricsObserver::ServiceWorkerPageLoadMetricsObserver() {}
 
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+ServiceWorkerPageLoadMetricsObserver::OnCommit(
+    content::NavigationHandle* navigation_handle,
+    ukm::SourceId source_id) {
+  transition_ = navigation_handle->GetPageTransition();
+  const net::HttpResponseHeaders* headers =
+      navigation_handle->GetResponseHeaders();
+  if (headers) {
+    was_no_store_main_resource_ =
+        headers->HasHeaderValue("cache-control", "no-store");
+  }
+  return CONTINUE_OBSERVING;
+}
+
 void ServiceWorkerPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
     const page_load_metrics::mojom::PageLoadTiming& timing,
     const page_load_metrics::PageLoadExtraInfo& info) {
@@ -145,6 +176,18 @@
       timing.paint_timing->first_contentful_paint.value() -
           timing.parse_timing->parse_start.value());
 
+  if (IsForwardBackLoad(transition_)) {
+    PAGE_LOAD_HISTOGRAM(
+        internal::kHistogramServiceWorkerFirstContentfulPaintForwardBack,
+        timing.paint_timing->first_contentful_paint.value());
+    if (was_no_store_main_resource_) {
+      PAGE_LOAD_HISTOGRAM(
+          internal::
+              kHistogramServiceWorkerFirstContentfulPaintForwardBackNoStore,
+          timing.paint_timing->first_contentful_paint.value());
+    }
+  }
+
   if (IsInboxSite(info.url)) {
     PAGE_LOAD_HISTOGRAM(
         internal::kHistogramServiceWorkerFirstContentfulPaintInbox,
@@ -273,6 +316,16 @@
           timing.parse_timing->parse_start, info)) {
     PAGE_LOAD_HISTOGRAM(internal::kHistogramServiceWorkerParseStart,
                         timing.parse_timing->parse_start.value());
+    if (IsForwardBackLoad(transition_)) {
+      PAGE_LOAD_HISTOGRAM(
+          internal::kHistogramServiceWorkerParseStartForwardBack,
+          timing.parse_timing->parse_start.value());
+      if (was_no_store_main_resource_) {
+        PAGE_LOAD_HISTOGRAM(
+            internal::kHistogramServiceWorkerParseStartForwardBackNoStore,
+            timing.parse_timing->parse_start.value());
+      }
+    }
   } else {
     PAGE_LOAD_HISTOGRAM(internal::kBackgroundHistogramServiceWorkerParseStart,
                         timing.parse_timing->parse_start.value());
diff --git a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h
index 4b3fda6..e03159b 100644
--- a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h
@@ -7,14 +7,20 @@
 
 #include "base/macros.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+#include "components/ukm/ukm_source.h"
 
 namespace internal {
 
 // Expose metrics for tests.
 extern const char kHistogramServiceWorkerParseStart[];
 extern const char kBackgroundHistogramServiceWorkerParseStart[];
+extern const char kHistogramServiceWorkerParseStartForwardBack[];
+extern const char kHistogramServiceWorkerParseStartForwardBackNoStore[];
 extern const char kHistogramServiceWorkerFirstContentfulPaint[];
 extern const char kBackgroundHistogramServiceWorkerFirstContentfulPaint[];
+extern const char kHistogramServiceWorkerFirstContentfulPaintForwardBack[];
+extern const char
+    kHistogramServiceWorkerFirstContentfulPaintForwardBackNoStore[];
 extern const char kHistogramServiceWorkerParseStartToFirstContentfulPaint[];
 extern const char kHistogramServiceWorkerFirstMeaningfulPaint[];
 extern const char kHistogramServiceWorkerParseStartToFirstMeaningfulPaint[];
@@ -55,6 +61,8 @@
  public:
   ServiceWorkerPageLoadMetricsObserver();
   // page_load_metrics::PageLoadMetricsObserver implementation:
+  ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
+                         ukm::SourceId source_id) override;
   void OnParseStart(
       const page_load_metrics::mojom::PageLoadTiming& timing,
       const page_load_metrics::PageLoadExtraInfo& extra_info) override;
@@ -72,6 +80,9 @@
       const page_load_metrics::PageLoadExtraInfo& extra_info) override;
 
  private:
+  ui::PageTransition transition_ = ui::PAGE_TRANSITION_LINK;
+  bool was_no_store_main_resource_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerPageLoadMetricsObserver);
 };
 
diff --git a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc
index e5d11a2..14df9d8d 100644
--- a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc
@@ -36,6 +36,11 @@
     histogram_tester().ExpectTotalCount(
         internal::kBackgroundHistogramServiceWorkerFirstContentfulPaint, 0);
     histogram_tester().ExpectTotalCount(
+        internal::kHistogramServiceWorkerFirstContentfulPaintForwardBack, 0);
+    histogram_tester().ExpectTotalCount(
+        internal::kHistogramServiceWorkerFirstContentfulPaintForwardBackNoStore,
+        0);
+    histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerParseStartToFirstContentfulPaint, 0);
     histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstMeaningfulPaint, 0);
@@ -184,6 +189,13 @@
 
   histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStart, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramServiceWorkerParseStart,
+      timing.parse_timing->parse_start.value().InMilliseconds(), 1);
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerParseStartForwardBack, 0);
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerParseStartForwardBackNoStore, 0);
 
   AssertNoInboxHistogramsLogged();
   AssertNoSearchHistogramsLogged();
@@ -332,6 +344,9 @@
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
   histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStart, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramServiceWorkerParseStart,
+      timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
   AssertNoSearchHistogramsLogged();
   AssertNoSearchNoSWHistogramsLogged();
@@ -434,6 +449,9 @@
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
   histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStart, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramServiceWorkerParseStart,
+      timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
   AssertNoInboxHistogramsLogged();
   AssertNoSearchNoSWHistogramsLogged();
@@ -497,3 +515,44 @@
   AssertNoInboxHistogramsLogged();
   AssertNoSearchHistogramsLogged();
 }
+
+TEST_F(ServiceWorkerPageLoadMetricsObserverTest,
+       WithServiceWorker_ForwardBack) {
+  page_load_metrics::mojom::PageLoadTiming timing;
+  InitializeTestPageLoadTiming(&timing);
+
+  // Back navigations to a page that was reloaded report a main transition type
+  // of PAGE_TRANSITION_RELOAD with a PAGE_TRANSITION_FORWARD_BACK
+  // modifier. This test verifies that when we encounter such a page, we log it
+  // as a forward/back navigation.
+  NavigateWithPageTransitionAndCommit(
+      GURL(kDefaultTestUrl),
+      ui::PageTransitionFromInt(ui::PAGE_TRANSITION_RELOAD |
+                                ui::PAGE_TRANSITION_FORWARD_BACK));
+  page_load_metrics::mojom::PageLoadMetadata metadata;
+  metadata.behavior_flags |=
+      blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorServiceWorkerControlled;
+  SimulateTimingAndMetadataUpdate(timing, metadata);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerFirstContentfulPaint, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramServiceWorkerFirstContentfulPaint,
+      timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerFirstContentfulPaintForwardBack, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramServiceWorkerFirstContentfulPaintForwardBack,
+      timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
+
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerParseStart, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramServiceWorkerParseStart,
+      timing.parse_timing->parse_start.value().InMilliseconds(), 1);
+  histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerParseStartForwardBack, 1);
+  histogram_tester().ExpectBucketCount(
+      internal::kHistogramServiceWorkerParseStartForwardBack,
+      timing.parse_timing->parse_start.value().InMilliseconds(), 1);
+}
diff --git a/chrome/browser/resources/chromeos/login/oobe_buttons.html b/chrome/browser/resources/chromeos/login/oobe_buttons.html
index 1272d9f..36bbe31f 100644
--- a/chrome/browser/resources/chromeos/login/oobe_buttons.html
+++ b/chrome/browser/resources/chromeos/login/oobe_buttons.html
@@ -54,7 +54,7 @@
     </style>
     <paper-button id="textButton" on-tap="onClick_" disabled="[[disabled]]"
         inverse$="[[inverse]]" aria-label$="[[labelForAria]]"
-        border$="[[border]]">
+        border$="[[border]]" android$="[[android]]">
       <div id="container"
           class="flex layout horizontal center center-justified self-stretch">
         <content></content>
diff --git a/chrome/browser/resources/chromeos/login/oobe_buttons.js b/chrome/browser/resources/chromeos/login/oobe_buttons.js
index d965b76..2c04948 100644
--- a/chrome/browser/resources/chromeos/login/oobe_buttons.js
+++ b/chrome/browser/resources/chromeos/login/oobe_buttons.js
@@ -12,6 +12,8 @@
 
     border: Boolean,
 
+    android: {type: Boolean, value: false},
+
     /* Note that we are not using "aria-label" property here, because
      * we want to pass the label value but not actually declare it as an
      * ARIA property anywhere but the actual target element.
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog.css b/chrome/browser/resources/chromeos/login/oobe_dialog.css
index d8e0e01..41037150 100644
--- a/chrome/browser/resources/chromeos/login/oobe_dialog.css
+++ b/chrome/browser/resources/chromeos/login/oobe_dialog.css
@@ -6,6 +6,10 @@
   padding: 64px 64px 0 64px;
 }
 
+#header-container[android] {
+  padding: 48px 48px 0 48px;
+}
+
 .oobe-header {
   min-height: 84px; /* 64 title + 20 subtitle */
 }
@@ -28,3 +32,7 @@
 #oobe-bottom[hideShadow] {
   box-shadow: 0 0 0 rgba(0, 0, 0, 0);
 }
+
+#oobe-bottom[android] {
+  height: 60px;
+}
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog.html b/chrome/browser/resources/chromeos/login/oobe_dialog.html
index 86a4749..4b1d8e5b 100644
--- a/chrome/browser/resources/chromeos/login/oobe_dialog.html
+++ b/chrome/browser/resources/chromeos/login/oobe_dialog.html
@@ -57,7 +57,7 @@
   <template>
     <link rel="stylesheet" href="oobe_dialog_host.css">
     <link rel="stylesheet" href="oobe_dialog.css">
-    <div id="header-container" hidden="[[noHeader]]">
+    <div id="header-container" hidden="[[noHeader]]" android$="[[android]]">
       <div class="oobe-icon-div">
         <content select=".oobe-icon"></content>
       </div>
@@ -70,7 +70,7 @@
       <content select=".footer"></content>
     </div>
     <template is="dom-if" if="[[hasButtons]]">
-      <div id="oobe-bottom" hideShadow$="[[hideShadow]]"
+      <div id="oobe-bottom" hideShadow$="[[hideShadow]]" android$="[[android]]"
           class="layout horizontal center">
         <content select=".bottom-buttons"></content>
       </div>
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog.js b/chrome/browser/resources/chromeos/login/oobe_dialog.js
index 3e010971..5038f3ae2 100644
--- a/chrome/browser/resources/chromeos/login/oobe_dialog.js
+++ b/chrome/browser/resources/chromeos/login/oobe_dialog.js
@@ -46,6 +46,11 @@
       value: false,
       reflectToAttribute: true,
     },
+
+    android: {
+      type: Boolean,
+      value: false,
+    },
   },
 
   focus: function() {
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog_host.css b/chrome/browser/resources/chromeos/login/oobe_dialog_host.css
index a228e65..50cae38 100644
--- a/chrome/browser/resources/chromeos/login/oobe_dialog_host.css
+++ b/chrome/browser/resources/chromeos/login/oobe_dialog_host.css
@@ -12,3 +12,8 @@
   min-width: 768px;
   position: relative;
 }
+
+:host([android]) {
+  min-height: 480px;
+  min-width: 576px;
+}
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_voice_interaction_value_prop.css b/chrome/browser/resources/chromeos/login/oobe_screen_voice_interaction_value_prop.css
index 4758606..f5d1056 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_voice_interaction_value_prop.css
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_voice_interaction_value_prop.css
@@ -6,5 +6,5 @@
   display: flex;
   flex-flow: column;
   font-size: 16px;
-  width: 768px;
+  width: 576px;
 }
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_wait_for_container_ready.css b/chrome/browser/resources/chromeos/login/oobe_screen_wait_for_container_ready.css
index 1e0016e1..a0504a72 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screen_wait_for_container_ready.css
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_wait_for_container_ready.css
@@ -6,5 +6,4 @@
   display: flex;
   flex-flow: column;
   font-size: 16px;
-  width: 768px;
 }
diff --git a/chrome/browser/resources/chromeos/login/oobe_text_button.css b/chrome/browser/resources/chromeos/login/oobe_text_button.css
index 092924a..7bebdd3 100644
--- a/chrome/browser/resources/chromeos/login/oobe_text_button.css
+++ b/chrome/browser/resources/chromeos/login/oobe_text_button.css
@@ -16,6 +16,14 @@
   border-color: var(--oobe-text-button-focused-border-color);
 }
 
+paper-button[android] {
+  height: 27px;
+}
+
+paper-button[android]:not([border]):not([inverse]) {
+  height: 13px;
+}
+
 #container {
   border-radius: 2px;
   padding: 0 16px;
@@ -37,8 +45,17 @@
   border-width: 1px;
 }
 
+#textButton[android] #container {
+  padding: 0 18px;
+}
+
 ::content * {
   font: var(--oobe-roboto-medium), sans-serif;
   font-size: 12px;
   text-transform: none;
 }
+
+#textButton[android] #container ::content * {
+  font-size: 10px;
+  text-transform: uppercase;
+}
diff --git a/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.css b/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.css
index 87b0f79..c4846d5 100644
--- a/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.css
+++ b/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.css
@@ -2,22 +2,17 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-oobe-text-button {
-  -webkit-margin-end: 16px;
-  color: #5a5a5a;
-}
-
 #value-prop-view {
   display: block;
-  height: 496px;
+  height: 372px;
   margin: auto;
   padding: 0;
 }
 
-#cancelButton {
-  -webkit-margin-end: 4px;
+#noThanksButton {
+  color: rgb(66, 133, 244);
 }
 
-.subtitle {
-  line-height: 20px;
+#continueButton {
+  -webkit-margin-end: 18px;
 }
diff --git a/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.html b/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.html
index 67211713..e209ae8 100644
--- a/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.html
+++ b/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.html
@@ -8,17 +8,18 @@
   <template>
     <link rel="stylesheet" href="oobe_voice_interaction_value_prop.css">
     <link rel="stylesheet" href="oobe_dialog_parameters.css">
-    <oobe-dialog id="voiceInteractionValuePropDialog" role="dialog" hide-shadow
-        has-buttons no-footer-padding>
+    <oobe-dialog id="voice-dialog" role="dialog" hide-shadow
+        has-buttons no-footer-padding android>
       <div class = "header">
         <webview id="value-prop-view"></webview>
       </div>
       <div class="bottom-buttons flex layout horizontal">
-        <div class="flex"></div>
-        <oobe-text-button id="noThanksButton" border on-tap="onNoThanksTap_">
+        <oobe-text-button id="noThanksButton" android on-tap="onNoThanksTap_">
           <div i18n-content="voiceInteractionValuePropNoThanksButton"></div>
         </oobe-text-button>
-        <oobe-text-button id="continueButton" inverse on-tap="onContinueTap_">
+        <div class="flex"></div>
+        <oobe-text-button id="continueButton" inverse android
+            on-tap="onContinueTap_">
           <div i18n-content="voiceInteractionValuePropContinueButton"></div>
         </oobe-text-button>
       </div>
diff --git a/chrome/browser/resources/chromeos/login/oobe_wait_for_container_ready.css b/chrome/browser/resources/chromeos/login/oobe_wait_for_container_ready.css
index f522f0ff..c4246834 100644
--- a/chrome/browser/resources/chromeos/login/oobe_wait_for_container_ready.css
+++ b/chrome/browser/resources/chromeos/login/oobe_wait_for_container_ready.css
@@ -2,12 +2,19 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
 
-.header {
-  margin-top: 15px;
+.title {
+  font-size: 18px;
+  padding: 32px 0 0 0;
 }
 
-.subtitle {
-  line-height: 20px;
+#subtitle {
+  font-size: 12px;
+  padding: 4px 0 0 0;
+}
+
+iron-icon {
+  height: 24px;
+  width: 24px;
 }
 
 paper-progress {
@@ -15,6 +22,6 @@
   --paper-progress-container-color: #e6e6e6;
   --paper-progress-secondary-color: rgb(66, 133, 244);
   display: block;
-  padding: 48px 0 0 0;
+  padding: 40px 0 0 0;
   width: 100%;
 }
diff --git a/chrome/browser/resources/chromeos/login/oobe_wait_for_container_ready.html b/chrome/browser/resources/chromeos/login/oobe_wait_for_container_ready.html
index 1ff662c3..4ecc2048 100644
--- a/chrome/browser/resources/chromeos/login/oobe_wait_for_container_ready.html
+++ b/chrome/browser/resources/chromeos/login/oobe_wait_for_container_ready.html
@@ -8,12 +8,13 @@
   <template>
     <link rel="stylesheet" href="oobe_wait_for_container_ready.css">
     <link rel="stylesheet" href="oobe_dialog_parameters.css">
-    <oobe-dialog id="waitForContainerReadyDialog" role="dialog" hide-shadow>
+    <oobe-dialog id="waitForContainerReadyDialog" role="dialog" hide-shadow
+        no-footer android>
       <iron-icon src="https://www.gstatic.com/opa-chromeos/oobe/images/assistant_logo.png" class="oobe-icon">
       </iron-icon>
       <div class="header">
-        <h1 class="title" i18n-content="waitForContainerReadyTitle"></h1>
-        <div class="subtitle">
+        <div class="title" i18n-content="waitForContainerReadyTitle"></div>
+        <div id="subtitle">
           <div i18n-content="waitForContainerReadyIntroMessage"></div>
         </div>
         <div class="content">
diff --git a/chrome/browser/resources/md_bookmarks/reducers.js b/chrome/browser/resources/md_bookmarks/reducers.js
index c4d142b..8337e57 100644
--- a/chrome/browser/resources/md_bookmarks/reducers.js
+++ b/chrome/browser/resources/md_bookmarks/reducers.js
@@ -55,7 +55,7 @@
    * @param {!Set<string>} deleted
    * @return SelectionState
    */
-  SelectionState.deselectDeletedItems = function(selectionState, deleted) {
+  SelectionState.deselectItems = function(selectionState, deleted) {
     return /** @type {SelectionState} */ Object.assign({}, selectionState, {
       items: bookmarks.util.removeIdsFromSet(selectionState.items, deleted),
       anchor: !selectionState.anchor || deleted.has(selectionState.anchor) ?
@@ -90,8 +90,16 @@
       case 'select-items':
         return SelectionState.selectItems(selection, action);
       case 'remove-bookmark':
-        return SelectionState.deselectDeletedItems(
-            selection, action.descendants);
+        return SelectionState.deselectItems(selection, action.descendants);
+      case 'move-bookmark':
+        // Deselect items when they are moved to another folder, since they will
+        // no longer be visible on screen (for simplicity, ignores items visible
+        // in search results).
+        if (action.parentId != action.oldParentId &&
+            selection.items.has(action.id)) {
+          return SelectionState.deselectItems(selection, new Set([action.id]));
+        }
+        return selection;
       case 'update-anchor':
         return SelectionState.updateAnchor(selection, action);
       default:
diff --git a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
index 0232f03..e52c8132 100644
--- a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
@@ -81,8 +81,15 @@
             new StrictMock<safe_browsing::MockIncidentReceiver>()),
         mock_database_manager_(new StrictMock<MockSafeBrowsingDatabaseManager>),
         fake_resource_request_detector_(
-            mock_database_manager_,
-            base::WrapUnique(mock_incident_receiver_)) {}
+            base::MakeUnique<FakeResourceRequestDetector>(
+                mock_database_manager_,
+                base::WrapUnique(mock_incident_receiver_))) {}
+
+  void TearDown() override {
+    fake_resource_request_detector_.reset();
+    mock_database_manager_ = nullptr;
+    base::RunLoop().RunUntilIdle();
+  }
 
   std::unique_ptr<net::URLRequest> GetTestURLRequest(
       const std::string& url,
@@ -135,7 +142,7 @@
 
     ResourceRequestInfo info =
         ResourceRequestDetector::GetRequestInfo(request.get());
-    fake_resource_request_detector_.ProcessResourceRequest(&info);
+    fake_resource_request_detector_->ProcessResourceRequest(&info);
     base::RunLoop().RunUntilIdle();
   }
 
@@ -152,7 +159,7 @@
 
     ResourceRequestInfo info =
         ResourceRequestDetector::GetRequestInfo(request.get());
-    fake_resource_request_detector_.ProcessResourceRequest(&info);
+    fake_resource_request_detector_->ProcessResourceRequest(&info);
     base::RunLoop().RunUntilIdle();
 
     ASSERT_TRUE(incident);
@@ -168,7 +175,7 @@
 
   StrictMock<safe_browsing::MockIncidentReceiver>* mock_incident_receiver_;
   scoped_refptr<MockSafeBrowsingDatabaseManager> mock_database_manager_;
-  FakeResourceRequestDetector fake_resource_request_detector_;
+  std::unique_ptr<FakeResourceRequestDetector> fake_resource_request_detector_;
 
  private:
   // UrlRequest requires a message loop. This provides one.
diff --git a/chrome/browser/subresource_filter/chrome_subresource_filter_client.h b/chrome/browser/subresource_filter/chrome_subresource_filter_client.h
index c859124..7249f09 100644
--- a/chrome/browser/subresource_filter/chrome_subresource_filter_client.h
+++ b/chrome/browser/subresource_filter/chrome_subresource_filter_client.h
@@ -38,7 +38,6 @@
   // expanded infobar.
   kActionDetailsShown,
 
-  // TODO(csharrison): Log this once the link is in place.
   kActionClickedLearnMore,
 
   // Content settings:
diff --git a/chrome/browser/translate/chrome_translate_client.cc b/chrome/browser/translate/chrome_translate_client.cc
index 5ee1a22..fa96355 100644
--- a/chrome/browser/translate/chrome_translate_client.cc
+++ b/chrome/browser/translate/chrome_translate_client.cc
@@ -77,34 +77,6 @@
   }
 }
 
-// ========== LOG LANGUAGE DETECTION EVENT ==============
-
-void LogLanguageDetectionEvent(
-    const content::WebContents* const web_contents,
-    const translate::LanguageDetectionDetails& details) {
-  if (!FeatureList::IsEnabled(switches::kSyncUserLanguageDetectionEvents))
-    return;
-  auto* const profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-
-  syncer::UserEventService* const user_event_service =
-      browser_sync::UserEventServiceFactory::GetForProfile(profile);
-
-  const auto* const entry =
-      web_contents->GetController().GetLastCommittedEntry();
-
-  // If entry is null, we don't record the page.
-  // The navigation entry can be null in situations like download or initial
-  // blank page.
-  DCHECK(web_contents);
-  if (entry != nullptr &&
-      TranslateService::IsTranslatableURL(entry->GetVirtualURL())) {
-    user_event_service->RecordUserEvent(
-        translate::ConstructLanguageDetectionEvent(
-            entry->GetTimestamp().ToInternalValue(), details));
-  }
-}
-
 // ========== LOG TRANSLATE EVENT ==============
 
 void LogTranslateEvent(const content::WebContents* const web_contents,
@@ -350,6 +322,32 @@
 #endif
 }
 
+void ChromeTranslateClient::RecordLanguageDetectionEvent(
+    const translate::LanguageDetectionDetails& details) const {
+  if (!FeatureList::IsEnabled(switches::kSyncUserLanguageDetectionEvents))
+    return;
+
+  DCHECK(web_contents());
+  auto* const profile =
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+
+  syncer::UserEventService* const user_event_service =
+      browser_sync::UserEventServiceFactory::GetForProfile(profile);
+
+  const auto* const entry =
+      web_contents()->GetController().GetLastCommittedEntry();
+
+  // If entry is null, we don't record the page.
+  // The navigation entry can be null in situations like download or initial
+  // blank page.
+  if (entry != nullptr &&
+      TranslateService::IsTranslatableURL(entry->GetVirtualURL())) {
+    user_event_service->RecordUserEvent(
+        translate::ConstructLanguageDetectionEvent(
+            entry->GetTimestamp().ToInternalValue(), details));
+  }
+}
+
 bool ChromeTranslateClient::IsTranslatableURL(const GURL& url) {
   return TranslateService::IsTranslatableURL(url);
 }
@@ -390,7 +388,7 @@
       content::Source<content::WebContents>(web_contents()),
       content::Details<const translate::LanguageDetectionDetails>(&details));
 
-  LogLanguageDetectionEvent(web_contents(), details);
+  RecordLanguageDetectionEvent(details);
   // Unless we have no language model (e.g., in incognito), notify the model
   // about detected language of every page visited.
   if (language_model_ && details.is_cld_reliable)
diff --git a/chrome/browser/translate/chrome_translate_client.h b/chrome/browser/translate/chrome_translate_client.h
index f79433a..3c7e052 100644
--- a/chrome/browser/translate/chrome_translate_client.h
+++ b/chrome/browser/translate/chrome_translate_client.h
@@ -31,6 +31,8 @@
 class TranslateAcceptLanguages;
 class TranslatePrefs;
 class TranslateManager;
+
+struct LanguageDetectionDetails;
 }  // namespace translate
 
 enum class ShowTranslateBubbleResult;
@@ -95,6 +97,8 @@
       std::unique_ptr<translate::TranslateInfoBarDelegate> delegate)
       const override;
 #endif
+  void RecordLanguageDetectionEvent(
+      const translate::LanguageDetectionDetails& details) const override;
   void ShowTranslateUI(translate::TranslateStep step,
                        const std::string& source_language,
                        const std::string& target_language,
diff --git a/chrome/browser/ui/browser_content_setting_bubble_model_delegate.cc b/chrome/browser/ui/browser_content_setting_bubble_model_delegate.cc
index 183ac71..c8f93a47 100644
--- a/chrome/browser/ui/browser_content_setting_bubble_model_delegate.cc
+++ b/chrome/browser/ui/browser_content_setting_bubble_model_delegate.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/tab_dialogs.h"
 #include "chrome/common/url_constants.h"
 #include "components/google/core/browser/google_util.h"
+#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 
 // The URL for when the user clicks "learn more" on the mixed scripting page
 // icon bubble.
@@ -56,9 +57,18 @@
 
 void BrowserContentSettingBubbleModelDelegate::ShowLearnMorePage(
     ContentSettingsType type) {
-  if (type != CONTENT_SETTINGS_TYPE_PLUGINS)
-    return;
-  chrome::AddSelectedTabWithURL(browser_,
-                                GURL(chrome::kBlockedPluginLearnMoreURL),
+  GURL learn_more_url;
+  switch (type) {
+    case CONTENT_SETTINGS_TYPE_PLUGINS:
+      learn_more_url = GURL(chrome::kBlockedPluginLearnMoreURL);
+      break;
+    case CONTENT_SETTINGS_TYPE_ADS:
+      learn_more_url = GURL(subresource_filter::kLearnMoreLink);
+      break;
+    default:
+      return;
+  }
+  DCHECK(!learn_more_url.is_empty());
+  chrome::AddSelectedTabWithURL(browser_, learn_more_url,
                                 ui::PAGE_TRANSITION_LINK);
 }
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index 9e9988f4..8cd5df8 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -47,6 +47,7 @@
 #include "components/rappor/public/rappor_utils.h"
 #include "components/rappor/rappor_service_impl.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
 #include "components/url_formatter/elide_url.h"
 #include "content/public/browser/notification_service.h"
@@ -1235,6 +1236,10 @@
   SetMessage();
   SetManageText();
   set_done_button_text(l10n_util::GetStringUTF16(IDS_OK));
+  // TODO(csharrison): The learn more link UI layout is non ideal. Make it align
+  // with the future Harmony UI version with a link icon in the bottom left of
+  // the bubble.
+  set_learn_more_link(l10n_util::GetStringUTF16(IDS_LEARN_MORE));
   ChromeSubresourceFilterClient::LogAction(kActionDetailsShown);
 }
 
@@ -1267,6 +1272,12 @@
   is_checked_ = is_checked;
 }
 
+void ContentSettingSubresourceFilterBubbleModel::OnLearnMoreLinkClicked() {
+  DCHECK(delegate());
+  ChromeSubresourceFilterClient::LogAction(kActionClickedLearnMore);
+  delegate()->ShowLearnMorePage(CONTENT_SETTINGS_TYPE_ADS);
+}
+
 void ContentSettingSubresourceFilterBubbleModel::OnDoneClicked() {
   if (is_checked_) {
     ChromeSubresourceFilterClient::FromWebContents(web_contents())
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.h b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
index a31abba..034d95b2 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.h
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.h
@@ -333,6 +333,7 @@
   void OnManageCheckboxChecked(bool is_checked) override;
   ContentSettingSubresourceFilterBubbleModel* AsSubresourceFilterBubbleModel()
       override;
+  void OnLearnMoreLinkClicked() override;
   void OnDoneClicked() override;
 
   bool is_checked_ = false;
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
index 8389b22..e04343a 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
@@ -28,6 +28,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/infobars/core/infobar_delegate.h"
 #include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
 #include "components/url_formatter/elide_url.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/web_contents_tester.h"
@@ -942,6 +943,8 @@
             l10n_util::GetStringUTF16(IDS_BLOCKED_ADS_PROMPT_EXPLANATION));
   EXPECT_EQ(0U, bubble_content.radio_group.radio_items.size());
   EXPECT_EQ(0, bubble_content.radio_group.default_item);
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_LEARN_MORE),
+            bubble_content.learn_more_link);
   EXPECT_TRUE(bubble_content.custom_link.empty());
   EXPECT_FALSE(bubble_content.custom_link_enabled);
   EXPECT_EQ(bubble_content.manage_text,
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
index a52ceb8..7b2916b2 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -47,7 +47,9 @@
   }
 }
 
-void BrowserNonClientFrameView::OnBrowserViewInitViewsComplete() {}
+void BrowserNonClientFrameView::OnBrowserViewInitViewsComplete() {
+  UpdateMinimumSize();
+}
 
 gfx::ImageSkia BrowserNonClientFrameView::GetIncognitoAvatarIcon() const {
   const SkColor icon_color = color_utils::PickContrastingColor(
@@ -71,6 +73,8 @@
 
 void BrowserNonClientFrameView::UpdateClientArea() {}
 
+void BrowserNonClientFrameView::UpdateMinimumSize() {}
+
 void BrowserNonClientFrameView::ChildPreferredSizeChanged(views::View* child) {
   if (child == GetProfileSwitcherView()) {
     // Perform a re-layout if the avatar button has changed, since that can
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
index f3325171..d0c4452 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
@@ -60,6 +60,9 @@
   // Provided for mus. Updates the client-area of the WindowTreeHostMus.
   virtual void UpdateClientArea();
 
+  // Provided for mus to update the minimum window size property.
+  virtual void UpdateMinimumSize();
+
   // Overriden from views::View.
   void ChildPreferredSizeChanged(views::View* child) override;
   void VisibilityChanged(views::View* starting_from, bool is_visible) override;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 5d68c50..06c3b5d2 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -170,6 +170,17 @@
     window_icon_->Update();
 }
 
+void BrowserNonClientFrameViewAsh::UpdateMinimumSize() {
+  gfx::Size min_size = GetMinimumSize();
+  aura::Window* frame_window = frame()->GetNativeWindow();
+  const gfx::Size* previous_min_size =
+      frame_window->GetProperty(aura::client::kMinimumSize);
+  if (!previous_min_size || *previous_min_size != min_size) {
+    frame_window->SetProperty(aura::client::kMinimumSize,
+                              new gfx::Size(min_size));
+  }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // views::NonClientFrameView:
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
index bb5b174..c91160a4 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
@@ -34,6 +34,7 @@
   int GetTopInset(bool restored) const override;
   int GetThemeBackgroundXInset() const override;
   void UpdateThrobber(bool running) override;
+  void UpdateMinimumSize() override;
 
   // views::NonClientFrameView:
   gfx::Rect GetBoundsForClientView() const override;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 18c2b45..a3f03e3 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller_test.h"
+#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
@@ -26,6 +27,7 @@
 #include "chrome/browser/ui/views/tabs/tab.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/signin/core/account_id/account_id.h"
+#include "ui/aura/client/aura_constants.h"
 #include "ui/base/hit_test.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/views/widget/widget.h"
@@ -291,3 +293,33 @@
       bounds();
   EXPECT_EQ(initial, after_restore);
 }
+
+// Tests that browser frame minimum size constraint is updated in response to
+// browser view layout.
+IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewAshTest,
+                       FrameMinSizeIsUpdated) {
+  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+  Widget* widget = browser_view->GetWidget();
+  // We know we're using Ash, so static cast.
+  BrowserNonClientFrameViewAsh* frame_view =
+      static_cast<BrowserNonClientFrameViewAsh*>(
+          widget->non_client_view()->frame_view());
+
+  BookmarkBarView* bookmark_bar = browser_view->GetBookmarkBarView();
+  EXPECT_FALSE(bookmark_bar->visible());
+  const int min_height_no_bookmarks = frame_view->GetMinimumSize().height();
+
+  // Setting non-zero bookmark bar preferred size forces it to be visible and
+  // triggers BrowserView layout update.
+  bookmark_bar->SetPreferredSize(gfx::Size(50, 5));
+  EXPECT_TRUE(bookmark_bar->visible());
+
+  // Minimum window size should grow with the bookmark bar shown.
+  // kMinimumSize window property should get updated.
+  aura::Window* window = browser()->window()->GetNativeWindow();
+  const gfx::Size* min_window_size =
+      window->GetProperty(aura::client::kMinimumSize);
+  ASSERT_NE(nullptr, min_window_size);
+  EXPECT_GT(min_window_size->height(), min_height_no_bookmarks);
+  EXPECT_EQ(*min_window_size, frame_view->GetMinimumSize());
+}
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
index 191623f..2ed2d89a 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.cc
@@ -226,6 +226,17 @@
   }
 }
 
+void BrowserNonClientFrameViewMus::UpdateMinimumSize() {
+  gfx::Size min_size = GetMinimumSize();
+  aura::Window* frame_window = frame()->GetNativeWindow();
+  const gfx::Size* previous_min_size =
+      frame_window->GetProperty(aura::client::kMinimumSize);
+  if (!previous_min_size || *previous_min_size != min_size) {
+    frame_window->SetProperty(aura::client::kMinimumSize,
+                              new gfx::Size(min_size));
+  }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // views::NonClientFrameView:
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h
index 0209dc7..cc7b7865 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h
@@ -38,6 +38,7 @@
   void UpdateThrobber(bool running) override;
   views::View* GetProfileSwitcherView() const override;
   void UpdateClientArea() override;
+  void UpdateMinimumSize() override;
 
   // views::NonClientFrameView:
   gfx::Rect GetBoundsForClientView() const override;
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index f7c2be4..c75bcc68 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -1934,6 +1934,7 @@
   // TODO(jamescook): Why was this in the middle of layout code?
   toolbar_->location_bar()->omnibox_view()->SetFocusBehavior(
       IsToolbarVisible() ? FocusBehavior::ALWAYS : FocusBehavior::NEVER);
+  frame()->GetFrameView()->UpdateMinimumSize();
 }
 
 void BrowserView::OnGestureEvent(ui::GestureEvent* event) {
@@ -2129,6 +2130,7 @@
   GetLocationBar()->GetOmniboxView()->model()->popup_model()->AddObserver(this);
 
   frame_->OnBrowserViewInitViewsComplete();
+  frame_->GetFrameView()->UpdateMinimumSize();
 }
 
 void BrowserView::LoadingAnimationCallback() {
diff --git a/chrome/browser/ui/views/frame/top_container_view.cc b/chrome/browser/ui/views/frame/top_container_view.cc
index 41df9d4..0f9f816 100644
--- a/chrome/browser/ui/views/frame/top_container_view.cc
+++ b/chrome/browser/ui/views/frame/top_container_view.cc
@@ -33,3 +33,7 @@
   }
   View::PaintChildren(context);
 }
+
+void TopContainerView::ChildPreferredSizeChanged(views::View* child) {
+  PreferredSizeChanged();
+}
diff --git a/chrome/browser/ui/views/frame/top_container_view.h b/chrome/browser/ui/views/frame/top_container_view.h
index 2623de4..66dfa47 100644
--- a/chrome/browser/ui/views/frame/top_container_view.h
+++ b/chrome/browser/ui/views/frame/top_container_view.h
@@ -22,6 +22,7 @@
   // views::View overrides:
   const char* GetClassName() const override;
   void PaintChildren(const ui::PaintContext& context) override;
+  void ChildPreferredSizeChanged(views::View* child) override;
 
  private:
   // The parent of this view. Not owned.
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 57885aa8..bbc00684 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -458,6 +458,11 @@
   return unique_path;
 }
 
+void CreateDirectoryIfNeeded(const base::FilePath& path) {
+  if (!base::DirectoryExists(path))
+    base::CreateDirectory(path);
+}
+
 bool PrivetPrintingEnabled() {
 #if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
   return true;
@@ -1445,36 +1450,50 @@
   // Get save location from Download Preferences.
   DownloadPrefs* download_prefs = DownloadPrefs::FromBrowserContext(
       preview_web_contents()->GetBrowserContext());
-  base::FilePath file_path = download_prefs->SaveFilePath();
+  base::FilePath path = download_prefs->SaveFilePath();
   printing::StickySettings* sticky_settings = GetStickySettings();
   sticky_settings->SaveInPrefs(Profile::FromBrowserContext(
       preview_web_contents()->GetBrowserContext())->GetPrefs());
+
   // Handle the no prompting case. Like the dialog prompt, this function
   // returns and eventually FileSelected() gets called.
   if (!prompt_user) {
     base::PostTaskWithTraitsAndReplyWithResult(
         FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
-        base::Bind(&GetUniquePath,
-                   download_prefs->SaveFilePath().Append(default_filename)),
+        base::Bind(&GetUniquePath, path.Append(default_filename)),
         base::Bind(&PrintPreviewHandler::OnGotUniqueFileName,
                    weak_factory_.GetWeakPtr()));
     return;
   }
 
-  // Otherwise prompt the user.
+  // If the directory is empty there is no reason to create it.
+  if (path.empty()) {
+    OnDirectoryCreated(default_filename);
+    return;
+  }
+
+  // Create the directory to save in if it does not exist.
+  base::PostTaskWithTraitsAndReply(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
+      base::Bind(&CreateDirectoryIfNeeded, path),
+      base::Bind(&PrintPreviewHandler::OnDirectoryCreated,
+                 weak_factory_.GetWeakPtr(), path.Append(default_filename)));
+}
+
+void PrintPreviewHandler::OnDirectoryCreated(const base::FilePath& path) {
+  // Prompts the user to select the file.
   ui::SelectFileDialog::FileTypeInfo file_type_info;
   file_type_info.extensions.resize(1);
   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pdf"));
+  file_type_info.include_all_files = true;
+  file_type_info.allowed_paths =
+      ui::SelectFileDialog::FileTypeInfo::NATIVE_OR_DRIVE_PATH;
 
   select_file_dialog_ =
       ui::SelectFileDialog::Create(this, nullptr /*policy already checked*/);
   select_file_dialog_->SelectFile(
-      ui::SelectFileDialog::SELECT_SAVEAS_FILE,
-      base::string16(),
-      download_prefs->SaveFilePath().Append(default_filename),
-      &file_type_info,
-      0,
-      base::FilePath::StringType(),
+      ui::SelectFileDialog::SELECT_SAVEAS_FILE, base::string16(), path,
+      &file_type_info, 0, base::FilePath::StringType(),
       platform_util::GetTopLevel(preview_web_contents()->GetNativeView()),
       NULL);
 }
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index ecf8ff9..80446ab 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -107,8 +107,10 @@
   void SetPdfSavedClosureForTesting(const base::Closure& closure);
 
  protected:
-  // If |prompt_user| is true, displays a modal dialog, prompting the user to
-  // select a file. Otherwise, just accept |default_path| and uniquify it.
+  // If |prompt_user| is true, starts a task to create the default Save As PDF
+  // directory if needed. OnDirectoryCreated() will be called when it
+  // finishes to open the modal dialog and prompt the user. Otherwise, just
+  // accept |default_path| and uniquify it.
   // Protected so unit tests can access.
   virtual void SelectFile(const base::FilePath& default_path, bool prompt_user);
 
@@ -265,6 +267,10 @@
   // Clears initiator details for the print preview dialog.
   void ClearInitiatorDetails();
 
+  // Called when the directory to save to has been created. Opens a modal
+  // dialog to prompt the user to select the file for Save As PDF.
+  void OnDirectoryCreated(const base::FilePath& path);
+
   // Posts a task to save |data| to pdf at |print_to_pdf_path_|.
   void PostPrintToPdfTask();
 
diff --git a/chrome/browser/web_applications/web_app_unittest.cc b/chrome/browser/web_applications/web_app_unittest.cc
index fb5a1cb..e42e5f4 100644
--- a/chrome/browser/web_applications/web_app_unittest.cc
+++ b/chrome/browser/web_applications/web_app_unittest.cc
@@ -21,8 +21,6 @@
 #include "chrome/browser/favicon/favicon_utils.h"
 #endif
 
-using content::RenderViewHostTester;
-
 class WebApplicationTest : public ChromeRenderViewHostTestHarness {
  protected:
   void SetUp() override {
@@ -47,8 +45,7 @@
   content::RenderFrameHostTester::For(main_rfh())
       ->InitializeRenderFrameIfNeeded();
   content::RenderFrameHostTester::TestOnMessageReceived(
-      rvh()->GetMainFrame(),
-      ChromeFrameHostMsg_DidGetWebApplicationInfo(0, web_app_info));
+      main_rfh(), ChromeFrameHostMsg_DidGetWebApplicationInfo(0, web_app_info));
   std::unique_ptr<web_app::ShortcutInfo> info =
       web_app::GetShortcutInfoForTab(web_contents());
 
diff --git a/chrome/installer/linux/BUILD.gn b/chrome/installer/linux/BUILD.gn
index 8f05ba39..292e1e4d 100644
--- a/chrome/installer/linux/BUILD.gn
+++ b/chrome/installer/linux/BUILD.gn
@@ -85,11 +85,7 @@
     ]
   }
 
-  if (current_cpu == "x86") {
-    sources += [ "//build/linux/bin/eu-strip" ]
-  } else if (current_cpu == "x64") {
-    sources += [ "/usr/bin/eu-strip" ]
-  }
+  sources += [ "//third_party/eu-strip/bin/eu-strip" ]
 
   outputs = [
     "$root_out_dir/installer/common/{{source_file_part}}",
diff --git a/chrome/renderer/autofill/form_autocomplete_browsertest.cc b/chrome/renderer/autofill/form_autocomplete_browsertest.cc
index 4099260..cfab1fd 100644
--- a/chrome/renderer/autofill/form_autocomplete_browsertest.cc
+++ b/chrome/renderer/autofill/form_autocomplete_browsertest.cc
@@ -141,7 +141,7 @@
 
 // Simulates receiving a message from the browser to fill a form.
 void SimulateOnFillForm(autofill::AutofillAgent* autofill_agent,
-                        blink::WebFrame* main_frame) {
+                        blink::WebLocalFrame* main_frame) {
   WebDocument document = main_frame->GetDocument();
   WebElement element = document.GetElementById(WebString::FromUTF8("fname"));
   ASSERT_FALSE(element.IsNull());
diff --git a/chrome/renderer/autofill/form_autofill_browsertest.cc b/chrome/renderer/autofill/form_autofill_browsertest.cc
index cb4e1bda..c9f7d54f 100644
--- a/chrome/renderer/autofill/form_autofill_browsertest.cc
+++ b/chrome/renderer/autofill/form_autofill_browsertest.cc
@@ -36,8 +36,8 @@
 using blink::WebExceptionCode;
 using blink::WebFormControlElement;
 using blink::WebFormElement;
-using blink::WebFrame;
 using blink::WebInputElement;
+using blink::WebLocalFrame;
 using blink::WebSelectElement;
 using blink::WebString;
 using blink::WebVector;
@@ -290,7 +290,7 @@
 
     LoadHTML(html);
 
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -355,7 +355,7 @@
     else
       LoadHTML(html);
 
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -608,7 +608,7 @@
     else
       LoadHTML(html);
 
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -618,7 +618,7 @@
 
   void TestFindFormForInputElement(const char* html, bool unowned) {
     LoadHTML(html);
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -673,7 +673,7 @@
 
   void TestFindFormForTextAreaElement(const char* html, bool unowned) {
     LoadHTML(html);
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -737,7 +737,7 @@
 
   void TestFillFormMaxLength(const char* html, bool unowned) {
     LoadHTML(html);
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -827,7 +827,7 @@
 
   void TestFillFormNegativeMaxLength(const char* html, bool unowned) {
     LoadHTML(html);
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -901,7 +901,7 @@
 
   void TestFillFormEmptyName(const char* html, bool unowned) {
     LoadHTML(html);
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -978,7 +978,7 @@
 
   void TestFillFormEmptyFormNames(const char* html, bool unowned) {
     LoadHTML(html);
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -1069,7 +1069,7 @@
                                  const char* placeholder_lastname,
                                  const char* placeholder_email) {
     LoadHTML(html);
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -1212,7 +1212,7 @@
 
   void TestClearFormWithNode(const char* html, bool unowned) {
     LoadHTML(html);
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -1315,7 +1315,7 @@
   void TestClearFormWithNodeContainingSelectOne(const char* html,
                                                 bool unowned) {
     LoadHTML(html);
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -1383,7 +1383,7 @@
 
   void TestClearPreviewedFormWithElement(const char* html) {
     LoadHTML(html);
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -1437,7 +1437,7 @@
 
   void TestClearPreviewedFormWithNonEmptyInitiatingNode(const char* html) {
     LoadHTML(html);
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -1490,7 +1490,7 @@
 
   void TestClearPreviewedFormWithAutofilledInitiatingNode(const char* html) {
     LoadHTML(html);
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -1544,7 +1544,7 @@
   void TestClearOnlyAutofilledFields(const char* html) {
     LoadHTML(html);
 
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
@@ -1613,7 +1613,7 @@
 TEST_F(FormAutofillTest, WebFormControlElementToFormField) {
   LoadHTML("<INPUT type='text' id='element' value='value'/>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -1641,7 +1641,7 @@
   LoadHTML("<INPUT type='text' id='element' value='value'"
            "       autocomplete='off'/>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -1662,7 +1662,7 @@
   LoadHTML("<INPUT type='text' id='element' value='value'"
            "       maxlength='5'/>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -1681,7 +1681,7 @@
 TEST_F(FormAutofillTest, WebFormControlElementToFormFieldAutofilled) {
   LoadHTML("<INPUT type='text' id='element' value='value'/>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebInputElement element = GetInputElementById("element");
@@ -1704,7 +1704,7 @@
   LoadHTML("<INPUT type='checkbox' id='checkbox' value='mail' checked/>"
            "<INPUT type='radio' id='radio' value='male'/>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebInputElement element = GetInputElementById("checkbox");
@@ -1738,7 +1738,7 @@
            "  <OPTION value='TX'>Texas</OPTION>"
            "</SELECT>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -1781,7 +1781,7 @@
            "  <OPTION value='TX'>Texas</OPTION>"
            "</SELECT>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -1816,7 +1816,7 @@
   html += "</SELECT>";
   LoadHTML(html.c_str());
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_TRUE(frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -1834,7 +1834,7 @@
              "spans multiple lines."
            "</TEXTAREA>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -1860,7 +1860,7 @@
 TEST_F(FormAutofillTest, WebFormControlElementToFormFieldMonthInput) {
   LoadHTML("<INPUT type='month' id='element' value='2011-12'>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -1888,7 +1888,7 @@
            "  <INPUT type='submit' id='submit' value='Send'/>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("hidden");
@@ -1915,7 +1915,7 @@
            "  <INPUT type='password' id='password' value='secret'/>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("password");
@@ -1954,7 +1954,7 @@
       std::string(10000, 'x') + "'/>";
   LoadHTML(html.c_str());
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   struct TestCase {
@@ -2017,7 +2017,7 @@
            "  <INPUT type='text' id='element'>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -2032,7 +2032,7 @@
            "  <INPUT dir='rtl' type='text' id='element'/>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -2048,7 +2048,7 @@
            "  <INPUT type='text' id='element'/>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -2063,7 +2063,7 @@
            "  <INPUT type='text' id='element'/>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -2079,7 +2079,7 @@
            "  <INPUT type='text' id='element'/>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -2096,7 +2096,7 @@
            "  <INPUT type='text' id='element'/>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -2127,7 +2127,7 @@
            "  <INPUT type='text' id='element'/>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -2144,7 +2144,7 @@
            "  </SPAN>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormControlElement element = GetFormControlElementById("element");
@@ -2180,7 +2180,7 @@
            "    <INPUT type='hidden' id='notvisible' value='apple'/>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebVector<WebFormElement> forms;
@@ -2253,7 +2253,7 @@
            "  <input type='text' id='firstname' value='John'>"
            "</form>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormElement web_form =
@@ -2287,7 +2287,7 @@
   html += "</FORM>";
   LoadHTML(html.c_str());
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebVector<WebFormElement> forms;
@@ -2316,7 +2316,7 @@
       "    <INPUT type='text' id='addressline1' value='123 Test st.'/>"
       "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormElement web_form =
@@ -2345,7 +2345,7 @@
       "    <INPUT type='text' id='addressline1' value='123 Test st.'/>"
       "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormElement web_form =
@@ -2373,7 +2373,7 @@
       "    <INPUT type='text' id='addressline1'  />"
       "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormElement web_form =
@@ -2401,7 +2401,7 @@
       "    <INPUT type='text' id='addressline1' />"
       "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebFormElement web_form =
@@ -2446,7 +2446,7 @@
            "  <INPUT type='submit' name='reply-send' value='Send'/>"
            "</FORM>");
 
-  WebFrame* web_frame = GetMainFrame();
+  WebLocalFrame* web_frame = GetMainFrame();
   ASSERT_NE(nullptr, web_frame);
 
   FormCache form_cache(*web_frame);
@@ -2519,7 +2519,7 @@
       "  <INPUT type='submit' name='reply-send' value='Send'/>"
       "</FORM>");
 
-  WebFrame* web_frame = GetMainFrame();
+  WebLocalFrame* web_frame = GetMainFrame();
   ASSERT_NE(nullptr, web_frame);
 
   FormCache form_cache(*web_frame);
@@ -2626,7 +2626,7 @@
            "  <INPUT type='submit' name='reply-send' value='Send'/>"
            "</FORM>");
 
-  WebFrame* web_frame = GetMainFrame();
+  WebLocalFrame* web_frame = GetMainFrame();
   ASSERT_NE(nullptr, web_frame);
 
   FormCache form_cache(*web_frame);
@@ -2641,7 +2641,7 @@
            "  <INPUT type='text' id='lastname' value='Smith'/>"
            "</FORM>");
 
-  WebFrame* web_frame = GetMainFrame();
+  WebLocalFrame* web_frame = GetMainFrame();
   ASSERT_NE(nullptr, web_frame);
 
   FormCache form_cache(*web_frame);
@@ -2654,7 +2654,7 @@
   LoadHTML("<FORM name='TestForm' action='http://cnn.com' method='post'>"
            "</FORM>");
 
-  WebFrame* web_frame = GetMainFrame();
+  WebLocalFrame* web_frame = GetMainFrame();
   ASSERT_NE(nullptr, web_frame);
 
   FormCache form_cache(*web_frame);
@@ -2673,7 +2673,7 @@
            "  <INPUT type='submit' name='reply-send' value='Send'/>"
            "</FORM>");
 
-  WebFrame* web_frame = GetMainFrame();
+  WebLocalFrame* web_frame = GetMainFrame();
   ASSERT_NE(nullptr, web_frame);
 
   FormCache form_cache(*web_frame);
@@ -2692,7 +2692,7 @@
              "  <INPUT type='submit' name='reply-send' value='Send'/>"
              "</FORM>");
 
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     WebVector<WebFormElement> web_forms;
@@ -3982,7 +3982,7 @@
            "  <input type='submit' name='reply-send' value='Send'>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebVector<WebFormElement> forms;
@@ -4037,7 +4037,7 @@
            "  <input type='submit' name='reply-send' value='Send'>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   WebVector<WebFormElement> forms;
@@ -4337,7 +4337,7 @@
 TEST_F(FormAutofillTest, ClickElement) {
   LoadHTML("<BUTTON id='link'>Button</BUTTON>"
            "<BUTTON name='button'>Button</BUTTON>");
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   // Successful retrieval by id.
@@ -4372,7 +4372,7 @@
            "  <INPUT type='submit' name='reply-send' value='Send'/>"
            "</FORM>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   // Set the value of the select-one.
@@ -4475,7 +4475,7 @@
            "  </FIELDSET>"
            "</DIV>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   control_elements = GetUnownedAutofillableFormFieldElements(
@@ -4535,7 +4535,7 @@
            "  <INPUT type='text' id='email' value='john@example.com'/>"
            "</DIV>");
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   control_elements = GetUnownedAutofillableFormFieldElements(
@@ -4584,7 +4584,7 @@
 
   LoadHTML(kFormHtml);
 
-  WebFrame* frame = GetMainFrame();
+  WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
 
   control_elements = GetUnownedAutofillableFormFieldElements(
@@ -4656,7 +4656,7 @@
   for (auto test_case : test_cases) {
     LoadHTML(test_case.html);
 
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_NE(nullptr, web_frame);
 
     FormCache form_cache(*web_frame);
diff --git a/chrome/renderer/autofill/page_click_tracker_browsertest.cc b/chrome/renderer/autofill/page_click_tracker_browsertest.cc
index ce3b6ab..8602495 100644
--- a/chrome/renderer/autofill/page_click_tracker_browsertest.cc
+++ b/chrome/renderer/autofill/page_click_tracker_browsertest.cc
@@ -14,6 +14,7 @@
 #include "third_party/WebKit/public/platform/WebSize.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebInputElement.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebSettings.h"
 #include "third_party/WebKit/public/web/WebView.h"
 #include "ui/events/keycodes/keyboard_codes.h"
@@ -70,8 +71,7 @@
              "</form>");
     GetWebWidget()->Resize(blink::WebSize(500, 500));
     GetWebWidget()->SetFocus(true);
-    blink::WebDocument document =
-        view_->GetWebView()->MainFrame()->GetDocument();
+    blink::WebDocument document = GetMainFrame()->GetDocument();
     text_ = document.GetElementById("text_1");
     textarea_ = document.GetElementById("textarea_1");
     ASSERT_FALSE(text_.IsNull());
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index 551dd8a9..36a29f6 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -895,7 +895,7 @@
 TEST_F(PasswordAutofillAgentTest, IsWebElementVisibleTest) {
   blink::WebVector<WebFormElement> forms1, forms2, forms3;
   blink::WebVector<blink::WebFormControlElement> web_control_elements;
-  blink::WebFrame* frame;
+  blink::WebLocalFrame* frame;
 
   LoadHTML(kVisibleFormWithNoUsernameHTML);
   frame = GetMainFrame();
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 0c3fca0b..3a01ea6b 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -1367,9 +1367,8 @@
 
 std::unique_ptr<blink::WebContentSettingsClient>
 ChromeContentRendererClient::CreateWorkerContentSettingsClient(
-    content::RenderFrame* render_frame,
-    WebFrame* frame) {
-  return base::MakeUnique<WorkerContentSettingsClient>(render_frame, frame);
+    content::RenderFrame* render_frame) {
+  return base::MakeUnique<WorkerContentSettingsClient>(render_frame);
 }
 
 bool ChromeContentRendererClient::IsPluginAllowedToUseDevChannelAPIs() {
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h
index 6656c6d9..ec6a8f4 100644
--- a/chrome/renderer/chrome_content_renderer_client.h
+++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -159,8 +159,8 @@
       const base::string16& source) const override;
   bool ShouldGatherSiteIsolationStats() const override;
   std::unique_ptr<blink::WebContentSettingsClient>
-  CreateWorkerContentSettingsClient(content::RenderFrame* render_frame,
-                                    blink::WebFrame* frame) override;
+  CreateWorkerContentSettingsClient(
+      content::RenderFrame* render_frame) override;
   bool AllowPepperMediaStreamAPI(const GURL& url) override;
   void AddSupportedKeySystems(
       std::vector<std::unique_ptr<::media::KeySystemProperties>>* key_systems)
diff --git a/chrome/renderer/content_settings_observer.cc b/chrome/renderer/content_settings_observer.cc
index 9d26b89..784f773 100644
--- a/chrome/renderer/content_settings_observer.cc
+++ b/chrome/renderer/content_settings_observer.cc
@@ -54,7 +54,7 @@
   // WebRemoteFrame which does not have a document(), and the WebRemoteFrame's
   // URL is not replicated.  See https://crbug.com/628759.
   if (top_origin.unique() && frame->Top()->IsWebLocalFrame())
-    return frame->Top()->GetDocument().Url();
+    return frame->Top()->ToWebLocalFrame()->GetDocument().Url();
   return top_origin.GetURL();
 }
 
@@ -182,7 +182,7 @@
 void ContentSettingsObserver::DidCommitProvisionalLoad(
     bool is_new_navigation,
     bool is_same_document_navigation) {
-  WebFrame* frame = render_frame()->GetWebFrame();
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
   if (frame->Parent())
     return;  // Not a top-level navigation.
 
@@ -307,7 +307,7 @@
   if (is_interstitial_page_)
     return true;
 
-  WebFrame* frame = render_frame()->GetWebFrame();
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
   const auto it = cached_script_permissions_.find(frame);
   if (it != cached_script_permissions_.end())
     return it->second;
@@ -347,7 +347,7 @@
 }
 
 bool ContentSettingsObserver::AllowStorage(bool local) {
-  WebFrame* frame = render_frame()->GetWebFrame();
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
   if (frame->GetSecurityOrigin().IsUnique() ||
       frame->Top()->GetSecurityOrigin().IsUnique())
     return false;
@@ -423,7 +423,7 @@
   if (!content_setting_rules_)
     return default_value;
 
-  WebFrame* frame = render_frame()->GetWebFrame();
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
   return GetContentSettingFromRules(
              content_setting_rules_->autoplay_rules, frame,
              url::Origin(frame->GetDocument().GetSecurityOrigin()).GetURL()) ==
@@ -480,7 +480,7 @@
 
 bool ContentSettingsObserver::IsPlatformApp() {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
-  WebFrame* frame = render_frame()->GetWebFrame();
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
   WebSecurityOrigin origin = frame->GetDocument().GetSecurityOrigin();
   const extensions::Extension* extension = GetExtension(origin);
   return extension && extension->is_platform_app();
diff --git a/chrome/renderer/safe_browsing/phishing_dom_feature_extractor.cc b/chrome/renderer/safe_browsing/phishing_dom_feature_extractor.cc
index 4e1ef0be..6410179 100644
--- a/chrome/renderer/safe_browsing/phishing_dom_feature_extractor.cc
+++ b/chrome/renderer/safe_browsing/phishing_dom_feature_extractor.cc
@@ -390,8 +390,10 @@
   // Advance to the next frame that contains a document, with no wrapping.
   if (frame) {
     for (frame = frame->TraverseNext(); frame; frame = frame->TraverseNext()) {
-      if (!frame->GetDocument().IsNull()) {
-        return frame->GetDocument();
+      // TODO(dcheng): Verify if the WebDocument::IsNull check is really needed.
+      if (frame->IsWebLocalFrame() &&
+          !frame->ToWebLocalFrame()->GetDocument().IsNull()) {
+        return frame->ToWebLocalFrame()->GetDocument();
       }
     }
   } else {
diff --git a/chrome/renderer/web_apps.cc b/chrome/renderer/web_apps.cc
index ec1a1a92..eb3aed7f 100644
--- a/chrome/renderer/web_apps.cc
+++ b/chrome/renderer/web_apps.cc
@@ -23,14 +23,14 @@
 #include "third_party/WebKit/public/platform/WebURL.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebElement.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebNode.h"
 #include "ui/gfx/geometry/size.h"
 #include "url/gurl.h"
 
 using blink::WebDocument;
 using blink::WebElement;
-using blink::WebFrame;
+using blink::WebLocalFrame;
 using blink::WebNode;
 using blink::WebString;
 
@@ -121,7 +121,7 @@
   return (*is_any || !sizes->empty());
 }
 
-void ParseWebAppFromWebDocument(WebFrame* frame,
+void ParseWebAppFromWebDocument(WebLocalFrame* frame,
                                 WebApplicationInfo* app_info) {
   WebDocument document = frame->GetDocument();
   if (document.IsNull())
diff --git a/chrome/renderer/web_apps.h b/chrome/renderer/web_apps.h
index 80a1180..0437286 100644
--- a/chrome/renderer/web_apps.h
+++ b/chrome/renderer/web_apps.h
@@ -12,7 +12,7 @@
 #include "ui/gfx/geometry/size.h"
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 }
 
 struct WebApplicationInfo;
@@ -30,7 +30,7 @@
 // Parses |app_info| information out of the document in WebFrame. Note that the
 // document may contain no web application information, in which case |app_info|
 // is unchanged.
-void ParseWebAppFromWebDocument(blink::WebFrame* frame,
+void ParseWebAppFromWebDocument(blink::WebLocalFrame* frame,
                                 WebApplicationInfo* app_info);
 
 }  // namespace web_apps
diff --git a/chrome/renderer/worker_content_settings_client.cc b/chrome/renderer/worker_content_settings_client.cc
index afa0053..56d9d14 100644
--- a/chrome/renderer/worker_content_settings_client.cc
+++ b/chrome/renderer/worker_content_settings_client.cc
@@ -11,13 +11,13 @@
 #include "third_party/WebKit/public/platform/URLConversion.h"
 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "url/origin.h"
 
 WorkerContentSettingsClient::WorkerContentSettingsClient(
-    content::RenderFrame* render_frame,
-    blink::WebFrame* frame)
+    content::RenderFrame* render_frame)
     : routing_id_(render_frame->GetRoutingID()), is_unique_origin_(false) {
+  blink::WebLocalFrame* frame = render_frame->GetWebFrame();
   if (frame->GetDocument().GetSecurityOrigin().IsUnique() ||
       frame->Top()->GetSecurityOrigin().IsUnique())
     is_unique_origin_ = true;
diff --git a/chrome/renderer/worker_content_settings_client.h b/chrome/renderer/worker_content_settings_client.h
index 1f1d5d7..6f74ab0 100644
--- a/chrome/renderer/worker_content_settings_client.h
+++ b/chrome/renderer/worker_content_settings_client.h
@@ -19,7 +19,6 @@
 }
 
 namespace blink {
-class WebFrame;
 class WebSecurityOrigin;
 }
 
@@ -27,8 +26,7 @@
 // blink's worker thread.
 class WorkerContentSettingsClient : public blink::WebContentSettingsClient {
  public:
-  WorkerContentSettingsClient(content::RenderFrame* render_frame,
-                              blink::WebFrame* frame);
+  explicit WorkerContentSettingsClient(content::RenderFrame* render_frame);
   ~WorkerContentSettingsClient() override;
 
   // WebContentSettingsClient overrides.
diff --git a/chrome/service/net/service_url_request_context_getter.cc b/chrome/service/net/service_url_request_context_getter.cc
index 8475382b..a6a035ee 100644
--- a/chrome/service/net/service_url_request_context_getter.cc
+++ b/chrome/service/net/service_url_request_context_getter.cc
@@ -102,8 +102,7 @@
       network_task_runner_(g_service_process->io_task_runner()) {
   DCHECK(g_service_process);
   proxy_config_service_ = net::ProxyService::CreateSystemProxyConfigService(
-      g_service_process->io_task_runner(),
-      g_service_process->file_task_runner());
+      g_service_process->io_task_runner());
 }
 
 net::URLRequestContext*
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index d802aff..c14b8b1 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1330,7 +1330,6 @@
       "../browser/metrics/process_memory_metrics_emitter_browsertest.cc",
       "../browser/metrics/startup_metrics_browsertest.cc",
       "../browser/metrics/tab_reactivation_tracker_browsertest.cc",
-      "../browser/metrics/ukm_browsertest.cc",
       "../browser/net/chrome_network_delegate_browsertest.cc",
       "../browser/net/cookie_policy_browsertest.cc",
       "../browser/net/dns_probe_browsertest.cc",
diff --git a/chrome/test/data/webui/md_bookmarks/reducers_test.js b/chrome/test/data/webui/md_bookmarks/reducers_test.js
index a23c27e..15fae4d 100644
--- a/chrome/test/data/webui/md_bookmarks/reducers_test.js
+++ b/chrome/test/data/webui/md_bookmarks/reducers_test.js
@@ -106,16 +106,16 @@
   });
 
   test('deselects items when they are deleted', function() {
-    var nodeMap = testTree(createFolder('0', [
-      createFolder(
-          '1',
-          [
-            createItem('2'),
-            createItem('3'),
-            createItem('4'),
-          ]),
-      createItem('5'),
-    ]));
+    var nodeMap = testTree(
+        createFolder(
+            '1',
+            [
+              createItem('2'),
+              createItem('3'),
+              createItem('4'),
+            ]),
+        createItem('5'),
+    );
 
     action = select(['2', '4', '5'], '4', true, false);
     selection = bookmarks.SelectionState.updateSelection(selection, action);
@@ -126,6 +126,24 @@
     assertDeepEquals(['5'], normalizeSet(selection.items));
     assertEquals(null, selection.anchor);
   });
+
+  test('deselects items when they are moved to a different folder', function() {
+    var nodeMap = testTree(
+        createFolder('1', []),
+        createItem('2'),
+        createItem('3'),
+    );
+
+    action = select(['2', '3'], '2', true, false);
+    selection = bookmarks.SelectionState.updateSelection(selection, action);
+
+    // Move item '2' from the 1st item in '0' to the 0th item in '1'.
+    action = bookmarks.actions.moveBookmark('2', '1', 0, '0', 1);
+    selection = bookmarks.SelectionState.updateSelection(selection, action);
+
+    assertDeepEquals(['3'], normalizeSet(selection.items));
+    assertEquals(null, selection.anchor);
+  });
 });
 
 suite('closed folder state', function() {
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index ef940dd4..c464017 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -218,10 +218,6 @@
 
 DefaultCommandLineSwitch g_default_switches[] = {
 #if defined(OS_ANDROID)
-#if !BUILDFLAG(IS_CAST_AUDIO_ONLY)
-    // Disables Chromecast-specific WiFi-related features on ATV for now.
-    {switches::kNoWifi, ""},
-#endif  // !BUILDFLAG(IS_CAST_AUDIO_ONLY)
     // TODO(714676): this should probably set the no restrictions autoplay
     // policy instead.
     {switches::kIgnoreAutoplayRestrictionsForTests, ""},
diff --git a/chromeos/components/tether/BUILD.gn b/chromeos/components/tether/BUILD.gn
index b71dd677..1f0c4c1 100644
--- a/chromeos/components/tether/BUILD.gn
+++ b/chromeos/components/tether/BUILD.gn
@@ -45,8 +45,6 @@
     "keep_alive_operation.h",
     "keep_alive_scheduler.cc",
     "keep_alive_scheduler.h",
-    "local_device_data_provider.cc",
-    "local_device_data_provider.h",
     "message_transfer_operation.cc",
     "message_transfer_operation.h",
     "message_wrapper.cc",
@@ -111,8 +109,6 @@
     "fake_tether_host_fetcher.h",
     "fake_wifi_hotspot_connector.cc",
     "fake_wifi_hotspot_connector.h",
-    "mock_local_device_data_provider.cc",
-    "mock_local_device_data_provider.h",
     "mock_tether_host_response_recorder.cc",
     "mock_tether_host_response_recorder.h",
     "proto_test_util.cc",
@@ -155,7 +151,6 @@
     "initializer_unittest.cc",
     "keep_alive_operation_unittest.cc",
     "keep_alive_scheduler_unittest.cc",
-    "local_device_data_provider_unittest.cc",
     "message_transfer_operation_unittest.cc",
     "message_wrapper_unittest.cc",
     "network_configuration_remover_unittest.cc",
diff --git a/chromeos/components/tether/ble_advertiser.cc b/chromeos/components/tether/ble_advertiser.cc
index 7bf0b98a..f48087e 100644
--- a/chromeos/components/tether/ble_advertiser.cc
+++ b/chromeos/components/tether/ble_advertiser.cc
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "chromeos/components/tether/ble_constants.h"
-#include "chromeos/components/tether/local_device_data_provider.h"
+#include "components/cryptauth/local_device_data_provider.h"
 #include "components/cryptauth/proto/cryptauth_api.pb.h"
 #include "components/cryptauth/remote_beacon_seed_fetcher.h"
 #include "components/proximity_auth/logging/logging.h"
@@ -145,7 +145,7 @@
 
 BleAdvertiser::BleAdvertiser(
     scoped_refptr<device::BluetoothAdapter> adapter,
-    const LocalDeviceDataProvider* local_device_data_provider,
+    const cryptauth::LocalDeviceDataProvider* local_device_data_provider,
     const cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher)
     : BleAdvertiser(adapter,
                     base::MakeUnique<cryptauth::ForegroundEidGenerator>(),
@@ -158,7 +158,7 @@
     scoped_refptr<device::BluetoothAdapter> adapter,
     std::unique_ptr<cryptauth::ForegroundEidGenerator> eid_generator,
     const cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher,
-    const LocalDeviceDataProvider* local_device_data_provider)
+    const cryptauth::LocalDeviceDataProvider* local_device_data_provider)
     : adapter_(adapter),
       eid_generator_(std::move(eid_generator)),
       remote_beacon_seed_fetcher_(remote_beacon_seed_fetcher),
diff --git a/chromeos/components/tether/ble_advertiser.h b/chromeos/components/tether/ble_advertiser.h
index 46177c87..590b86e 100644
--- a/chromeos/components/tether/ble_advertiser.h
+++ b/chromeos/components/tether/ble_advertiser.h
@@ -15,6 +15,7 @@
 #include "device/bluetooth/bluetooth_advertisement.h"
 
 namespace cryptauth {
+class LocalDeviceDataProvider;
 class RemoteBeaconSeedFetcher;
 }
 
@@ -22,8 +23,6 @@
 
 namespace tether {
 
-class LocalDeviceDataProvider;
-
 // Advertises to a given device. When StartAdvertisingToDevice() is called, a
 // device-specific EID value is computed deterministically and is set as the
 // service data of the advertisement. If that device is nearby and scanning,
@@ -33,7 +32,7 @@
  public:
   BleAdvertiser(
       scoped_refptr<device::BluetoothAdapter> adapter,
-      const LocalDeviceDataProvider* local_device_data_provider,
+      const cryptauth::LocalDeviceDataProvider* local_device_data_provider,
       const cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher);
   virtual ~BleAdvertiser();
 
@@ -95,14 +94,14 @@
       scoped_refptr<device::BluetoothAdapter> adapter,
       std::unique_ptr<cryptauth::ForegroundEidGenerator> eid_generator,
       const cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher,
-      const LocalDeviceDataProvider* local_device_data_provider);
+      const cryptauth::LocalDeviceDataProvider* local_device_data_provider);
 
   scoped_refptr<device::BluetoothAdapter> adapter_;
 
   std::unique_ptr<cryptauth::ForegroundEidGenerator> eid_generator_;
   // Not owned by this instance and must outlive it.
   const cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher_;
-  const LocalDeviceDataProvider* local_device_data_provider_;
+  const cryptauth::LocalDeviceDataProvider* local_device_data_provider_;
 
   std::map<std::string, std::unique_ptr<IndividualAdvertisement>>
       device_id_to_advertisement_map_;
diff --git a/chromeos/components/tether/ble_advertiser_unittest.cc b/chromeos/components/tether/ble_advertiser_unittest.cc
index 841f3bca..d9676ce8 100644
--- a/chromeos/components/tether/ble_advertiser_unittest.cc
+++ b/chromeos/components/tether/ble_advertiser_unittest.cc
@@ -6,8 +6,8 @@
 
 #include "base/logging.h"
 #include "chromeos/components/tether/ble_constants.h"
-#include "chromeos/components/tether/mock_local_device_data_provider.h"
 #include "components/cryptauth/mock_foreground_eid_generator.h"
+#include "components/cryptauth/mock_local_device_data_provider.h"
 #include "components/cryptauth/mock_remote_beacon_seed_fetcher.h"
 #include "components/cryptauth/proto/cryptauth_api.pb.h"
 #include "components/cryptauth/remote_device_test_util.h"
@@ -148,7 +148,8 @@
     mock_seed_fetcher_->SetSeedsForDevice(fake_devices_[2],
                                           &device_2_beacon_seeds);
 
-    mock_local_data_provider_ = base::MakeUnique<MockLocalDeviceDataProvider>();
+    mock_local_data_provider_ =
+        base::MakeUnique<cryptauth::MockLocalDeviceDataProvider>();
     mock_local_data_provider_->SetPublicKey(
         base::MakeUnique<std::string>(kFakePublicKey));
 
@@ -233,7 +234,8 @@
       mock_adapter_;
   cryptauth::MockForegroundEidGenerator* mock_eid_generator_;
   std::unique_ptr<cryptauth::MockRemoteBeaconSeedFetcher> mock_seed_fetcher_;
-  std::unique_ptr<MockLocalDeviceDataProvider> mock_local_data_provider_;
+  std::unique_ptr<cryptauth::MockLocalDeviceDataProvider>
+      mock_local_data_provider_;
 
   std::vector<scoped_refptr<RegisterAdvertisementArgs>>
       register_advertisement_args_;
diff --git a/chromeos/components/tether/ble_connection_manager.cc b/chromeos/components/tether/ble_connection_manager.cc
index 6ce53eac..fc9fea7d5 100644
--- a/chromeos/components/tether/ble_connection_manager.cc
+++ b/chromeos/components/tether/ble_connection_manager.cc
@@ -169,7 +169,7 @@
 BleConnectionManager::BleConnectionManager(
     cryptauth::CryptAuthService* cryptauth_service,
     scoped_refptr<device::BluetoothAdapter> adapter,
-    const LocalDeviceDataProvider* local_device_data_provider,
+    const cryptauth::LocalDeviceDataProvider* local_device_data_provider,
     const cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher,
     cryptauth::BluetoothThrottler* bluetooth_throttler)
     : BleConnectionManager(
diff --git a/chromeos/components/tether/ble_connection_manager.h b/chromeos/components/tether/ble_connection_manager.h
index e69cdce..aa7809d 100644
--- a/chromeos/components/tether/ble_connection_manager.h
+++ b/chromeos/components/tether/ble_connection_manager.h
@@ -23,6 +23,7 @@
 namespace cryptauth {
 class BluetoothThrottler;
 class CryptAuthService;
+class LocalDeviceDataProvider;
 }  // namespace cryptauth
 
 namespace chromeos {
@@ -71,7 +72,7 @@
   BleConnectionManager(
       cryptauth::CryptAuthService* cryptauth_service,
       scoped_refptr<device::BluetoothAdapter> adapter,
-      const LocalDeviceDataProvider* local_device_data_provider,
+      const cryptauth::LocalDeviceDataProvider* local_device_data_provider,
       const cryptauth::RemoteBeaconSeedFetcher* remote_beacon_seed_fetcher,
       cryptauth::BluetoothThrottler* bluetooth_throttler);
   virtual ~BleConnectionManager();
diff --git a/chromeos/components/tether/ble_scanner.cc b/chromeos/components/tether/ble_scanner.cc
index 709e5f5..0a604076 100644
--- a/chromeos/components/tether/ble_scanner.cc
+++ b/chromeos/components/tether/ble_scanner.cc
@@ -46,7 +46,7 @@
 
 BleScanner::BleScanner(
     scoped_refptr<device::BluetoothAdapter> adapter,
-    const LocalDeviceDataProvider* local_device_data_provider)
+    const cryptauth::LocalDeviceDataProvider* local_device_data_provider)
     : BleScanner(base::MakeUnique<ServiceDataProviderImpl>(),
                  adapter,
                  base::WrapUnique(new cryptauth::ForegroundEidGenerator()),
@@ -56,7 +56,7 @@
     std::unique_ptr<ServiceDataProvider> service_data_provider,
     scoped_refptr<device::BluetoothAdapter> adapter,
     std::unique_ptr<cryptauth::ForegroundEidGenerator> eid_generator,
-    const LocalDeviceDataProvider* local_device_data_provider)
+    const cryptauth::LocalDeviceDataProvider* local_device_data_provider)
     : service_data_provider_(std::move(service_data_provider)),
       adapter_(adapter),
       eid_generator_(std::move(eid_generator)),
diff --git a/chromeos/components/tether/ble_scanner.h b/chromeos/components/tether/ble_scanner.h
index 7c8c171f..a7092b6 100644
--- a/chromeos/components/tether/ble_scanner.h
+++ b/chromeos/components/tether/ble_scanner.h
@@ -10,8 +10,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
-#include "chromeos/components/tether/local_device_data_provider.h"
 #include "components/cryptauth/foreground_eid_generator.h"
+#include "components/cryptauth/local_device_data_provider.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 #include "device/bluetooth/bluetooth_adapter_factory.h"
 
@@ -33,8 +33,9 @@
         cryptauth::RemoteDevice remote_device) = 0;
   };
 
-  BleScanner(scoped_refptr<device::BluetoothAdapter> adapter,
-             const LocalDeviceDataProvider* local_device_data_provider);
+  BleScanner(
+      scoped_refptr<device::BluetoothAdapter> adapter,
+      const cryptauth::LocalDeviceDataProvider* local_device_data_provider);
   ~BleScanner() override;
 
   virtual bool RegisterScanFilterForDevice(
@@ -76,10 +77,11 @@
         device::BluetoothDevice* bluetooth_device) override;
   };
 
-  BleScanner(std::unique_ptr<ServiceDataProvider> service_data_provider,
-             scoped_refptr<device::BluetoothAdapter> adapter,
-             std::unique_ptr<cryptauth::ForegroundEidGenerator> eid_generator,
-             const LocalDeviceDataProvider* local_device_data_provider);
+  BleScanner(
+      std::unique_ptr<ServiceDataProvider> service_data_provider,
+      scoped_refptr<device::BluetoothAdapter> adapter,
+      std::unique_ptr<cryptauth::ForegroundEidGenerator> eid_generator,
+      const cryptauth::LocalDeviceDataProvider* local_device_data_provider);
 
   void UpdateDiscoveryStatus();
   void StartDiscoverySession();
@@ -98,7 +100,7 @@
   std::unique_ptr<cryptauth::ForegroundEidGenerator> eid_generator_;
   // |local_device_data_provider_| is not owned by this instance and must
   // outlive it.
-  const LocalDeviceDataProvider* local_device_data_provider_;
+  const cryptauth::LocalDeviceDataProvider* local_device_data_provider_;
 
   bool is_initializing_discovery_session_;
   std::unique_ptr<device::BluetoothDiscoverySession> discovery_session_;
diff --git a/chromeos/components/tether/ble_scanner_unittest.cc b/chromeos/components/tether/ble_scanner_unittest.cc
index b3292e3..abb7b3b 100644
--- a/chromeos/components/tether/ble_scanner_unittest.cc
+++ b/chromeos/components/tether/ble_scanner_unittest.cc
@@ -6,8 +6,8 @@
 
 #include "base/logging.h"
 #include "chromeos/components/tether/ble_constants.h"
-#include "chromeos/components/tether/mock_local_device_data_provider.h"
 #include "components/cryptauth/mock_foreground_eid_generator.h"
+#include "components/cryptauth/mock_local_device_data_provider.h"
 #include "components/cryptauth/proto/cryptauth_api.pb.h"
 #include "components/cryptauth/remote_device_test_util.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
@@ -159,7 +159,7 @@
         CreateFakeBackgroundScanFilter());
 
     mock_local_device_data_provider_ =
-        base::MakeUnique<MockLocalDeviceDataProvider>();
+        base::MakeUnique<cryptauth::MockLocalDeviceDataProvider>();
     mock_local_device_data_provider_->SetPublicKey(
         base::MakeUnique<std::string>(fake_local_public_key));
     mock_local_device_data_provider_->SetBeaconSeeds(
@@ -226,7 +226,8 @@
 
   TestServiceDataProvider* test_service_data_provider_;
   cryptauth::MockForegroundEidGenerator* mock_eid_generator_;
-  std::unique_ptr<MockLocalDeviceDataProvider> mock_local_device_data_provider_;
+  std::unique_ptr<cryptauth::MockLocalDeviceDataProvider>
+      mock_local_device_data_provider_;
 
   scoped_refptr<NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
   device::MockBluetoothDiscoverySession* mock_discovery_session_;
diff --git a/chromeos/components/tether/initializer.cc b/chromeos/components/tether/initializer.cc
index 62b9ecdf..9b446e0 100644
--- a/chromeos/components/tether/initializer.cc
+++ b/chromeos/components/tether/initializer.cc
@@ -14,7 +14,6 @@
 #include "chromeos/components/tether/host_scan_scheduler.h"
 #include "chromeos/components/tether/host_scanner.h"
 #include "chromeos/components/tether/keep_alive_scheduler.h"
-#include "chromeos/components/tether/local_device_data_provider.h"
 #include "chromeos/components/tether/network_configuration_remover.h"
 #include "chromeos/components/tether/network_connection_handler_tether_delegate.h"
 #include "chromeos/components/tether/notification_presenter.h"
@@ -30,6 +29,7 @@
 #include "chromeos/network/network_state_handler.h"
 #include "components/cryptauth/bluetooth_throttler_impl.h"
 #include "components/cryptauth/cryptauth_service.h"
+#include "components/cryptauth/local_device_data_provider.h"
 #include "components/cryptauth/remote_beacon_seed_fetcher.h"
 #include "components/prefs/pref_service.h"
 #include "components/proximity_auth/logging/logging.h"
@@ -174,7 +174,7 @@
   tether_host_fetcher_ =
       base::MakeUnique<TetherHostFetcher>(cryptauth_service_);
   local_device_data_provider_ =
-      base::MakeUnique<LocalDeviceDataProvider>(cryptauth_service_);
+      base::MakeUnique<cryptauth::LocalDeviceDataProvider>(cryptauth_service_);
   remote_beacon_seed_fetcher_ =
       base::MakeUnique<cryptauth::RemoteBeaconSeedFetcher>(
           cryptauth_service_->GetCryptAuthDeviceManager());
diff --git a/chromeos/components/tether/initializer.h b/chromeos/components/tether/initializer.h
index 5ff11cc..db1449f 100644
--- a/chromeos/components/tether/initializer.h
+++ b/chromeos/components/tether/initializer.h
@@ -21,6 +21,7 @@
 
 namespace cryptauth {
 class CryptAuthService;
+class LocalDeviceDataProvider;
 class RemoteBeaconSeedFetcher;
 }
 
@@ -43,7 +44,6 @@
 class HostScanScheduler;
 class HostScanDevicePrioritizerImpl;
 class KeepAliveScheduler;
-class LocalDeviceDataProvider;
 class NetworkConfigurationRemover;
 class NotificationPresenter;
 class TetherConnector;
@@ -113,7 +113,8 @@
   // initialization to ensure that they are destroyed in the correct order. This
   // order will be enforced by InitializerTest.TestCreateAndDestroy.
   std::unique_ptr<TetherHostFetcher> tether_host_fetcher_;
-  std::unique_ptr<LocalDeviceDataProvider> local_device_data_provider_;
+  std::unique_ptr<cryptauth::LocalDeviceDataProvider>
+      local_device_data_provider_;
   std::unique_ptr<cryptauth::RemoteBeaconSeedFetcher>
       remote_beacon_seed_fetcher_;
   std::unique_ptr<BleConnectionManager> ble_connection_manager_;
diff --git a/chromeos/components/tether/mock_local_device_data_provider.h b/chromeos/components/tether/mock_local_device_data_provider.h
deleted file mode 100644
index fa1a6c5..0000000
--- a/chromeos/components/tether/mock_local_device_data_provider.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_COMPONENTS_TETHER_MOCK_LOCAL_DEVICE_DATA_PROVIDER_H_
-#define CHROMEOS_COMPONENTS_TETHER_MOCK_LOCAL_DEVICE_DATA_PROVIDER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "chromeos/components/tether/local_device_data_provider.h"
-
-namespace cryptauth {
-class BeaconSeed;
-}
-
-namespace chromeos {
-
-namespace tether {
-
-// Test double for LocalDeviceDataProvider.
-class MockLocalDeviceDataProvider : public LocalDeviceDataProvider {
- public:
-  MockLocalDeviceDataProvider();
-  ~MockLocalDeviceDataProvider() override;
-
-  void SetPublicKey(std::unique_ptr<std::string> public_key);
-  void SetBeaconSeeds(
-      std::unique_ptr<std::vector<cryptauth::BeaconSeed>> beacon_seeds);
-
-  // LocalDeviceDataProvider:
-  bool GetLocalDeviceData(
-      std::string* public_key_out,
-      std::vector<cryptauth::BeaconSeed>* beacon_seeds_out) const override;
-
- private:
-  std::unique_ptr<std::string> public_key_;
-  std::unique_ptr<std::vector<cryptauth::BeaconSeed>> beacon_seeds_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockLocalDeviceDataProvider);
-};
-
-}  // namespace tether
-
-}  // namespace chromeos
-
-#endif  // CHROMEOS_COMPONENTS_TETHER_MOCK_LOCAL_DEVICE_DATA_PROVIDER_H_
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index 71d53fc..2a32103 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -4,6 +4,8 @@
 
 #include "components/autofill/content/renderer/form_autofill_util.h"
 
+#include <algorithm>
+#include <limits>
 #include <map>
 #include <memory>
 #include <set>
@@ -43,9 +45,9 @@
 using blink::WebElementCollection;
 using blink::WebFormControlElement;
 using blink::WebFormElement;
-using blink::WebFrame;
 using blink::WebInputElement;
 using blink::WebLabelElement;
+using blink::WebLocalFrame;
 using blink::WebNode;
 using blink::WebOptionElement;
 using blink::WebSelectElement;
@@ -1194,7 +1196,7 @@
       data, NULL);
 }
 
-bool IsFormVisible(blink::WebFrame* frame,
+bool IsFormVisible(blink::WebLocalFrame* frame,
                    const blink::WebFormElement& form_element,
                    const GURL& canonical_action,
                    const GURL& canonical_origin,
@@ -1473,7 +1475,7 @@
     ExtractMask extract_mask,
     FormData* form,
     FormFieldData* field) {
-  const WebFrame* frame = form_element.GetDocument().GetFrame();
+  const WebLocalFrame* frame = form_element.GetDocument().GetFrame();
   if (!frame)
     return false;
 
@@ -1762,7 +1764,7 @@
   return true;
 }
 
-bool IsWebpageEmpty(const blink::WebFrame* frame) {
+bool IsWebpageEmpty(const blink::WebLocalFrame* frame) {
   blink::WebDocument document = frame->GetDocument();
 
   return IsWebElementEmpty(document.Head()) &&
diff --git a/components/autofill/content/renderer/form_autofill_util.h b/components/autofill/content/renderer/form_autofill_util.h
index 998d5b0..eac0abc 100644
--- a/components/autofill/content/renderer/form_autofill_util.h
+++ b/components/autofill/content/renderer/form_autofill_util.h
@@ -25,8 +25,8 @@
 class WebElement;
 class WebFormControlElement;
 class WebFormElement;
-class WebFrame;
 class WebInputElement;
+class WebLocalFrame;
 class WebNode;
 }
 
@@ -87,7 +87,7 @@
 // equals |form_element|. If |form_element| is null, checks if forms action
 // equals |action|. Returns true if so. For forms with empty or unspecified
 // actions, all form data are used for comparison.
-bool IsFormVisible(blink::WebFrame* frame,
+bool IsFormVisible(blink::WebLocalFrame* frame,
                    const blink::WebFormElement& form_element,
                    const GURL& action,
                    const GURL& origin,
@@ -255,7 +255,7 @@
 //    <body/>
 // <html/>
 // Meta, script and title tags don't influence the emptiness of a webpage.
-bool IsWebpageEmpty(const blink::WebFrame* frame);
+bool IsWebpageEmpty(const blink::WebLocalFrame* frame);
 
 // This function checks whether the children of |element|
 // are of the type <script>, <meta>, or <title>.
diff --git a/components/autofill/content/renderer/form_cache.cc b/components/autofill/content/renderer/form_cache.cc
index 2f360bc0..18a1022 100644
--- a/components/autofill/content/renderer/form_cache.cc
+++ b/components/autofill/content/renderer/form_cache.cc
@@ -4,6 +4,10 @@
 
 #include "components/autofill/content/renderer/form_cache.h"
 
+#include <algorithm>
+#include <string>
+#include <utility>
+
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/stl_util.h"
@@ -28,7 +32,7 @@
 using blink::WebElement;
 using blink::WebFormControlElement;
 using blink::WebFormElement;
-using blink::WebFrame;
+using blink::WebLocalFrame;
 using blink::WebInputElement;
 using blink::WebNode;
 using blink::WebSelectElement;
@@ -82,8 +86,7 @@
 
 }  // namespace
 
-FormCache::FormCache(const WebFrame& frame) : frame_(frame) {
-}
+FormCache::FormCache(const WebLocalFrame& frame) : frame_(frame) {}
 
 FormCache::~FormCache() {
 }
diff --git a/components/autofill/content/renderer/form_cache.h b/components/autofill/content/renderer/form_cache.h
index 4afb3a66..f2f7775 100644
--- a/components/autofill/content/renderer/form_cache.h
+++ b/components/autofill/content/renderer/form_cache.h
@@ -17,8 +17,8 @@
 
 namespace blink {
 class WebFormControlElement;
-class WebFrame;
 class WebInputElement;
+class WebLocalFrame;
 class WebSelectElement;
 }
 
@@ -29,7 +29,7 @@
 // Manages the forms in a single RenderFrame.
 class FormCache {
  public:
-  explicit FormCache(const blink::WebFrame& frame);
+  explicit FormCache(const blink::WebLocalFrame& frame);
   ~FormCache();
 
   // Scans the DOM in |frame_| extracting and storing forms that have not been
@@ -62,7 +62,7 @@
       const std::vector<blink::WebFormControlElement>& control_elements);
 
   // The frame this FormCache is associated with.
-  const blink::WebFrame& frame_;
+  const blink::WebLocalFrame& frame_;
 
   // The cached forms. Used to prevent re-extraction of forms.
   std::set<FormData> parsed_forms_;
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index e55f198..2d520f3 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -624,7 +624,7 @@
 // preceding the |password_element| either in a form, if it belongs to one, or
 // in the |frame|.
 blink::WebInputElement FindUsernameElementPrecedingPasswordElement(
-    blink::WebFrame* frame,
+    blink::WebLocalFrame* frame,
     const blink::WebInputElement& password_element) {
   DCHECK(!password_element.IsNull());
 
@@ -775,7 +775,7 @@
                                          &field_value_and_properties_map_);
   }
 
-  blink::WebFrame* const element_frame = element.GetDocument().GetFrame();
+  blink::WebLocalFrame* const element_frame = element.GetDocument().GetFrame();
   // The element's frame might have been detached in the meantime (see
   // http://crbug.com/585363, comments 5 and 6), in which case frame() will
   // return null. This was hardly caused by form submission (unless the user
@@ -998,7 +998,8 @@
   // to be the username field.
   std::unique_ptr<PasswordForm> password_form;
   if (element.Form().IsNull()) {
-    blink::WebFrame* const element_frame = element.GetDocument().GetFrame();
+    blink::WebLocalFrame* const element_frame =
+        element.GetDocument().GetFrame();
     if (!element_frame)
       return false;
 
@@ -1117,7 +1118,7 @@
 
   // Prompt to save only if the form is now gone, either invisible or
   // removed from the DOM.
-  blink::WebFrame* frame = render_frame()->GetWebFrame();
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
   const auto& password_form = provisionally_saved_form_.password_form();
   // TODO(crbug.com/720347): This method could be called often and checking form
   // visibility could be expesive. Add performance metrics for this.
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.cc b/components/autofill/content/renderer/password_form_conversion_utils.cc
index c47feeb..6b41603 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -29,15 +29,15 @@
 #include "third_party/WebKit/public/platform/WebVector.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebFormControlElement.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebInputElement.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/re2/src/re2/re2.h"
 
 using blink::WebDocument;
 using blink::WebFormControlElement;
 using blink::WebFormElement;
-using blink::WebFrame;
 using blink::WebInputElement;
+using blink::WebLocalFrame;
 using blink::WebString;
 
 namespace autofill {
@@ -711,7 +711,7 @@
 }
 
 std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements(
-    const WebFrame& frame,
+    const WebLocalFrame& frame,
     const FieldValueAndPropertiesMaskMap* field_value_and_properties_map,
     const FormsPredictionsMap* form_predictions) {
   SyntheticForm synthetic_form;
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.h b/components/autofill/content/renderer/password_form_conversion_utils.h
index 3e4f402..7385d2f 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.h
+++ b/components/autofill/content/renderer/password_form_conversion_utils.h
@@ -7,6 +7,8 @@
 
 #include <map>
 #include <memory>
+#include <string>
+#include <utility>
 #include <vector>
 
 #include "base/strings/string_piece.h"
@@ -18,8 +20,8 @@
 namespace blink {
 class WebFormElement;
 class WebFormControlElement;
-class WebFrame;
 class WebInputElement;
+class WebLocalFrame;
 }
 
 namespace autofill {
@@ -56,7 +58,7 @@
 // Same as CreatePasswordFormFromWebForm() but for input elements that are not
 // enclosed in <form> element.
 std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements(
-    const blink::WebFrame& frame,
+    const blink::WebLocalFrame& frame,
     const FieldValueAndPropertiesMaskMap* nonscript_modified_values,
     const FormsPredictionsMap* form_predictions);
 
diff --git a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
index 699caa9..74f793e 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
@@ -27,7 +27,7 @@
 
 using blink::WebFormControlElement;
 using blink::WebFormElement;
-using blink::WebFrame;
+using blink::WebLocalFrame;
 using blink::WebInputElement;
 using blink::WebVector;
 
@@ -242,7 +242,7 @@
   void LoadWebFormFromHTML(const std::string& html, WebFormElement* form) {
     LoadHTML(html.c_str());
 
-    WebFrame* frame = GetMainFrame();
+    WebLocalFrame* frame = GetMainFrame();
     ASSERT_NE(nullptr, frame);
 
     WebVector<WebFormElement> forms;
diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc
index e7986765..b4ab0cb2 100644
--- a/components/autofill/content/renderer/password_generation_agent.cc
+++ b/components/autofill/content/renderer/password_generation_agent.cc
@@ -591,7 +591,7 @@
     password_form = CreatePasswordFormFromWebForm(form, nullptr, nullptr);
     control_elements = form_util::ExtractAutofillableElementsInForm(form);
   } else {
-    const blink::WebFrame& frame = *render_frame()->GetWebFrame();
+    const blink::WebLocalFrame& frame = *render_frame()->GetWebFrame();
     blink::WebDocument doc = frame.GetDocument();
     if (doc.IsNull())
       return;
diff --git a/components/autofill/core/browser/autofill_experiments.cc b/components/autofill/core/browser/autofill_experiments.cc
index e667d89..577edaf 100644
--- a/components/autofill/core/browser/autofill_experiments.cc
+++ b/components/autofill/core/browser/autofill_experiments.cc
@@ -29,6 +29,8 @@
     "AutofillCreditCardAssist", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAutofillScanCardholderName{
     "AutofillScanCardholderName", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kAutofillCreditCardBankNameDisplay{
+    "AutofillCreditCardBankNameDisplay", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAutofillCreditCardPopupLayout{
     "AutofillCreditCardPopupLayout", base::FEATURE_DISABLED_BY_DEFAULT};
 const base::Feature kAutofillCreditCardLastUsedDateDisplay{
@@ -101,6 +103,10 @@
   return base::FeatureList::IsEnabled(kAutofillCreditCardLastUsedDateDisplay);
 }
 
+bool IsAutofillCreditCardBankNameDisplayExperimentEnabled() {
+  return base::FeatureList::IsEnabled(kAutofillCreditCardBankNameDisplay);
+}
+
 // |GetCreditCardPopupParameterUintValue| returns 0 if experiment parameter is
 // not specified. 0 == |SK_ColorTRANSPARENT|.
 SkColor GetCreditCardPopupBackgroundColor() {
diff --git a/components/autofill/core/browser/autofill_experiments.h b/components/autofill/core/browser/autofill_experiments.h
index 7ed13140..b4f0ecdb 100644
--- a/components/autofill/core/browser/autofill_experiments.h
+++ b/components/autofill/core/browser/autofill_experiments.h
@@ -27,6 +27,7 @@
 
 extern const base::Feature kAutofillCreditCardAssist;
 extern const base::Feature kAutofillScanCardholderName;
+extern const base::Feature kAutofillCreditCardBankNameDisplay;
 extern const base::Feature kAutofillCreditCardPopupLayout;
 extern const base::Feature kAutofillCreditCardLastUsedDateDisplay;
 extern const base::Feature kAutofillOfferLocalSaveIfServerCardManuallyEntered;
@@ -73,6 +74,9 @@
 // Returns whether Autofill credit card last used date shows expiration date.
 bool ShowExpirationDateInAutofillCreditCardLastUsedDate();
 
+// Returns whether Autofill credit card bank name display experiment is enabled.
+bool IsAutofillCreditCardBankNameDisplayExperimentEnabled();
+
 // Returns the background color for credit card autofill popup, or
 // |SK_ColorTRANSPARENT| if the new credit card autofill popup layout experiment
 // is not enabled.
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index 4bb5995..09cfefc22 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -1998,6 +1998,14 @@
   std::vector<Suggestion> suggestions =
       personal_data_->GetCreditCardSuggestions(
           type, SanitizeCreditCardFieldValue(field.value));
+  const std::vector<CreditCard*> cards_to_suggest =
+      personal_data_->GetCreditCardsToSuggest();
+  for (const CreditCard* credit_card : cards_to_suggest) {
+    if (!credit_card->bank_name().empty()) {
+      credit_card_form_event_logger_->SetBankNameAvailable();
+      break;
+    }
+  }
   for (size_t i = 0; i < suggestions.size(); i++) {
     suggestions[i].frontend_id =
         MakeFrontendID(suggestions[i].backend_id, std::string());
diff --git a/components/autofill/core/browser/autofill_metrics.cc b/components/autofill/core/browser/autofill_metrics.cc
index 3039a08..9bce1ca 100644
--- a/components/autofill/core/browser/autofill_metrics.cc
+++ b/components/autofill/core/browser/autofill_metrics.cc
@@ -985,6 +985,7 @@
       has_logged_suggestion_filled_(false),
       has_logged_will_submit_(false),
       has_logged_submitted_(false),
+      has_logged_bank_name_available_(false),
       logged_suggestion_filled_was_server_data_(false),
       logged_suggestion_filled_was_masked_server_card_(false),
       form_interactions_ukm_logger_(form_interactions_ukm_logger) {}
@@ -1026,6 +1027,10 @@
   if (!has_logged_suggestions_shown_) {
     has_logged_suggestions_shown_ = true;
     Log(AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE);
+    if (has_logged_bank_name_available_) {
+      Log(AutofillMetrics::
+              FORM_EVENT_SUGGESTIONS_SHOWN_WITH_BANK_NAME_AVAILABLE_ONCE);
+    }
   }
 
   if (is_for_credit_card_) {
@@ -1074,6 +1079,10 @@
               FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE);
     } else if (credit_card.record_type() == CreditCard::FULL_SERVER_CARD) {
       Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE);
+      if (has_logged_bank_name_available_) {
+        Log(AutofillMetrics::
+                FORM_EVENT_SERVER_SUGGESTION_FILLED_WITH_BANK_NAME_AVAILABLE_ONCE);
+      }
     } else {
       Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE);
     }
@@ -1161,6 +1170,10 @@
   }
 }
 
+void AutofillMetrics::FormEventLogger::SetBankNameAvailable() {
+  has_logged_bank_name_available_ = true;
+}
+
 void AutofillMetrics::FormEventLogger::Log(FormEvent event) const {
   DCHECK_LT(event, NUM_FORM_EVENTS);
   std::string name("Autofill.FormEvents.");
@@ -1192,6 +1205,13 @@
   LogUMAHistogramEnumeration(name, event, NUM_FORM_EVENTS);
 }
 
+void AutofillMetrics::FormEventLogger::Log(
+    BankNameDisplayedFormEvent event) const {
+  DCHECK_LT(event, BANK_NAME_NUM_FORM_EVENTS);
+  std::string name("Autofill.FormEvents.CreditCard.BankNameDisplayed");
+  LogUMAHistogramEnumeration(name, event, BANK_NAME_NUM_FORM_EVENTS);
+}
+
 AutofillMetrics::FormInteractionsUkmLogger::FormInteractionsUkmLogger(
     ukm::UkmRecorder* ukm_recorder)
     : ukm_recorder_(ukm_recorder) {}
diff --git a/components/autofill/core/browser/autofill_metrics.h b/components/autofill/core/browser/autofill_metrics.h
index d96e9046..9e63c96 100644
--- a/components/autofill/core/browser/autofill_metrics.h
+++ b/components/autofill/core/browser/autofill_metrics.h
@@ -545,10 +545,20 @@
     // submitted. If the submission is not interrupted by JavaScript, the "form
     // submitted" event above will also be logged.
     FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE,
-
     NUM_FORM_EVENTS,
   };
 
+  // Form Events for autofill with bank name available for display.
+  enum BankNameDisplayedFormEvent {
+    // A dropdown with suggestions was shown and at least one suggestion has a
+    // bank name. Logged at most once per page load.
+    FORM_EVENT_SUGGESTIONS_SHOWN_WITH_BANK_NAME_AVAILABLE_ONCE = 0,
+    // A server suggestion was used to fill the form and at least one suggestion
+    // has a bank name. Logged at most once per page load.
+    FORM_EVENT_SERVER_SUGGESTION_FILLED_WITH_BANK_NAME_AVAILABLE_ONCE,
+    BANK_NAME_NUM_FORM_EVENTS,
+  };
+
   // Events related to the Unmask Credit Card Prompt.
   enum UnmaskPromptEvent {
     // The prompt was shown.
@@ -940,8 +950,11 @@
 
     void OnFormSubmitted();
 
+    void SetBankNameAvailable();
+
    private:
     void Log(FormEvent event) const;
+    void Log(BankNameDisplayedFormEvent event) const;
 
     bool is_for_credit_card_;
     size_t server_record_type_count_;
@@ -953,6 +966,7 @@
     bool has_logged_suggestion_filled_;
     bool has_logged_will_submit_;
     bool has_logged_submitted_;
+    bool has_logged_bank_name_available_;
     bool logged_suggestion_filled_was_server_data_;
     bool logged_suggestion_filled_was_masked_server_card_;
 
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc
index 16df233..30543c4 100644
--- a/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -187,6 +187,20 @@
     Refresh();
   }
 
+  // Removes all existing credit cards and creates 1 server card with a bank
+  // name.
+  void RecreateServerCreditCardsWithBankName() {
+    server_credit_cards_.clear();
+    std::unique_ptr<CreditCard> credit_card =
+        base::MakeUnique<CreditCard>(CreditCard::FULL_SERVER_CARD, "server_id");
+    test::SetCreditCardInfo(credit_card.get(), "name", "4111111111111111", "12",
+                            "24", "1");
+    credit_card->set_guid("10000000-0000-0000-0000-000000000003");
+    credit_card->set_bank_name("Chase");
+    server_credit_cards_.push_back(std::move(credit_card));
+    Refresh();
+  }
+
   bool IsAutofillEnabled() const override { return autofill_enabled_; }
 
   void CreateAmbiguousProfiles() {
@@ -2533,6 +2547,11 @@
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.CreditCard",
         AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+    // Check that the bank name histogram was not recorded. ExpectBucketCount()
+    // can't be used here because it expects the histogram to exist.
+    EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+                     "Autofill.FormEvents.CreditCard")
+                     ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
   }
 
   // Reset the autofill manager state.
@@ -2550,6 +2569,11 @@
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.CreditCard",
         AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+    // Check that the bank name histogram was not recorded. ExpectBucketCount()
+    // can't be used here because it expects the histogram to exist.
+    EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+                     "Autofill.FormEvents.CreditCard")
+                     ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
   }
 
   // Reset the autofill manager state.
@@ -2567,6 +2591,59 @@
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.CreditCard",
         AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0);
+    // Check that the bank name histogram was not recorded. ExpectBucketCount()
+    // can't be used here because it expects the histogram to exist.
+    EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+                     "Autofill.FormEvents.CreditCard")
+                     ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
+  }
+
+  // Recreate server cards with bank names.
+  personal_data_->RecreateServerCreditCardsWithBankName();
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating new popup being shown.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+    autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard.BankNameDisplayed",
+        AutofillMetrics::
+            FORM_EVENT_SUGGESTIONS_SHOWN_WITH_BANK_NAME_AVAILABLE_ONCE,
+        1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating two popups in the same page load.
+    base::HistogramTester histogram_tester;
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+    autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+    autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN, 2);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard",
+        AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard.BankNameDisplayed",
+        AutofillMetrics::
+            FORM_EVENT_SUGGESTIONS_SHOWN_WITH_BANK_NAME_AVAILABLE_ONCE,
+        1);
   }
 }
 
@@ -2735,6 +2812,11 @@
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.CreditCard",
         AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE, 1);
+    // Check that the bank name histogram was not recorded. ExpectBucketCount()
+    // can't be used here because it expects the histogram to exist.
+    EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+                     "Autofill.FormEvents.CreditCard")
+                     ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
   }
 
   // Reset the autofill manager state.
@@ -2758,6 +2840,52 @@
         "Autofill.FormEvents.CreditCard",
         AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1);
   }
+
+  // Recreate server cards with bank names.
+  personal_data_->RecreateServerCreditCardsWithBankName();
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating filling a full card server suggestion.
+    base::HistogramTester histogram_tester;
+    std::string guid(
+        "10000000-0000-0000-0000-000000000003");  // full server card
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, field,
+        autofill_manager_->MakeFrontendID(guid, std::string()));
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard.BankNameDisplayed",
+        AutofillMetrics::
+            FORM_EVENT_SERVER_SUGGESTION_FILLED_WITH_BANK_NAME_AVAILABLE_ONCE,
+        1);
+  }
+
+  // Reset the autofill manager state.
+  autofill_manager_->Reset();
+  autofill_manager_->AddSeenForm(form, field_types, field_types);
+
+  {
+    // Simulating filling multiple times.
+    base::HistogramTester histogram_tester;
+    std::string guid(
+        "10000000-0000-0000-0000-000000000003");  // full server card
+    autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF());
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, field,
+        autofill_manager_->MakeFrontendID(guid, std::string()));
+    autofill_manager_->FillOrPreviewForm(
+        AutofillDriver::FORM_DATA_ACTION_FILL, 0, form, field,
+        autofill_manager_->MakeFrontendID(guid, std::string()));
+    histogram_tester.ExpectBucketCount(
+        "Autofill.FormEvents.CreditCard.BankNameDisplayed",
+        AutofillMetrics::
+            FORM_EVENT_SERVER_SUGGESTION_FILLED_WITH_BANK_NAME_AVAILABLE_ONCE,
+        1);
+  }
 }
 
 // Test that we log submitted form events for credit cards.
@@ -3451,6 +3579,11 @@
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.Address",
         AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+    // Check that the bank name histogram was not recorded. ExpectBucketCount()
+    // can't be used here because it expects the histogram to exist.
+    EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+                     "Autofill.FormEvents.CreditCard")
+                     ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
   }
 
   // Reset the autofill manager state.
@@ -3468,6 +3601,11 @@
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.Address",
         AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1);
+    // Check that the bank name histogram was not recorded. ExpectBucketCount()
+    // can't be used here because it expects the histogram to exist.
+    EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+                     "Autofill.FormEvents.CreditCard")
+                     ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
   }
 
   // Reset the autofill manager state.
@@ -3485,6 +3623,11 @@
     histogram_tester.ExpectBucketCount(
         "Autofill.FormEvents.Address",
         AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0);
+    // Check that the bank name histogram was not recorded. ExpectBucketCount()
+    // can't be used here because it expects the histogram to exist.
+    EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix(
+                     "Autofill.FormEvents.CreditCard")
+                     ["Autofill.FormEvents.CreditCard.BankNameDisplayed"]);
   }
 }
 
diff --git a/components/autofill/core/browser/credit_card.cc b/components/autofill/core/browser/credit_card.cc
index 33dc123..ee83a82 100644
--- a/components/autofill/core/browser/credit_card.cc
+++ b/components/autofill/core/browser/credit_card.cc
@@ -498,6 +498,7 @@
   server_id_ = credit_card.server_id_;
   server_status_ = credit_card.server_status_;
   billing_address_id_ = credit_card.billing_address_id_;
+  bank_name_ = credit_card.bank_name_;
 
   set_guid(credit_card.guid());
   set_origin(credit_card.origin());
@@ -564,6 +565,10 @@
   if (comparison != 0)
     return comparison;
 
+  comparison = bank_name_.compare(credit_card.bank_name_);
+  if (comparison != 0)
+    return comparison;
+
   if (static_cast<int>(server_status_) <
       static_cast<int>(credit_card.server_status_))
     return -1;
@@ -739,6 +744,7 @@
 
 base::string16 CreditCard::NetworkAndLastFourDigits() const {
   base::string16 network = NetworkForDisplay();
+  // TODO(crbug.com/734197): truncate network.
 
   base::string16 digits = LastFourDigits();
   if (digits.empty())
@@ -748,6 +754,14 @@
   return network + base::string16(kMidlineEllipsis) + digits;
 }
 
+base::string16 CreditCard::BankNameAndLastFourDigits() const {
+  base::string16 digits = LastFourDigits();
+  // TODO(crbug.com/734197): truncate bank name.
+  if (digits.empty())
+    return ASCIIToUTF16(bank_name_);
+  return ASCIIToUTF16(bank_name_) + base::string16(kMidlineEllipsis) + digits;
+}
+
 base::string16 CreditCard::AbbreviatedExpirationDateForDisplay() const {
   base::string16 month = ExpirationMonthAsString();
   base::string16 year = Expiration2DigitYearAsString();
diff --git a/components/autofill/core/browser/credit_card.h b/components/autofill/core/browser/credit_card.h
index 4dc8bb9..ef83a56 100644
--- a/components/autofill/core/browser/credit_card.h
+++ b/components/autofill/core/browser/credit_card.h
@@ -101,6 +101,9 @@
 
   const std::string& network() const { return network_; }
 
+  const std::string& bank_name() const { return bank_name_; }
+  void set_bank_name(const std::string& bank_name) { bank_name_ = bank_name; }
+
   int expiration_month() const { return expiration_month_; }
   int expiration_year() const { return expiration_year_; }
 
@@ -209,6 +212,8 @@
   base::string16 NetworkForDisplay() const;
   // A label for this card formatted as 'IssuerNetwork - 2345'.
   base::string16 NetworkAndLastFourDigits() const;
+  // A label for this card formatted as 'BankName - 2345'.
+  base::string16 BankNameAndLastFourDigits() const;
   // Localized expiration for this card formatted as 'Exp: 06/17'.
   base::string16 AbbreviatedExpirationDateForDisplay() const;
   // Returns the date when the card was last used in autofill.
@@ -249,6 +254,9 @@
   // below.
   std::string network_;
 
+  // The issuer bank name of the card.
+  std::string bank_name_;
+
   // These members are zero if not present.
   int expiration_month_;
   int expiration_year_;
diff --git a/components/autofill/core/browser/credit_card_unittest.cc b/components/autofill/core/browser/credit_card_unittest.cc
index 3857613b..91a195d 100644
--- a/components/autofill/core/browser/credit_card_unittest.cc
+++ b/components/autofill/core/browser/credit_card_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_experiments.h"
@@ -155,6 +156,37 @@
             obfuscated5);
 }
 
+// Tests credit card bank name and last four digits string generation.
+TEST(CreditCardTest, BankNameAndLastFourDigitsStrings) {
+  // Case 1: Have everything and show bank name.
+  CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com/");
+  test::SetCreditCardInfo(&credit_card1, "John Dillinger",
+                          "5105 1051 0510 5100", "01", "2010", "1");
+  credit_card1.set_bank_name("Chase");
+  base::string16 obfuscated1 = credit_card1.BankNameAndLastFourDigits();
+  EXPECT_FALSE(credit_card1.bank_name().empty());
+  EXPECT_EQ(UTF8ToUTF16(std::string("Chase") + kUTF8MidlineEllipsis + "5100"),
+            obfuscated1);
+
+  // Case 2: Have no bank name and not show bank name.
+  CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com/");
+  test::SetCreditCardInfo(&credit_card2, "John Dillinger",
+                          "5105 1051 0510 5100", "01", "2010", "1");
+  base::string16 obfuscated2 = credit_card2.BankNameAndLastFourDigits();
+  EXPECT_TRUE(credit_card2.bank_name().empty());
+  EXPECT_EQ(UTF8ToUTF16(std::string(kUTF8MidlineEllipsis) + "5100"),
+            obfuscated2);
+
+  // Case 3: Have bank name but no last four digits, only show bank name.
+  CreditCard credit_card3(base::GenerateGUID(), "https://www.example.com/");
+  test::SetCreditCardInfo(&credit_card3, "John Dillinger", "", "01", "2010",
+                          "1");
+  credit_card3.set_bank_name("Chase");
+  base::string16 obfuscated3 = credit_card3.BankNameAndLastFourDigits();
+  EXPECT_FALSE(credit_card3.bank_name().empty());
+  EXPECT_EQ(UTF8ToUTF16(std::string("Chase")), obfuscated3);
+}
+
 TEST(CreditCardTest, AssignmentOperator) {
   CreditCard a(base::GenerateGUID(), "some origin");
   test::SetCreditCardInfo(&a, "John Dillinger", "123456789012", "01", "2010",
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 8039bc4..1ed3ce5 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -1674,7 +1674,12 @@
       // Otherwise the label is the card number, or if that is empty the
       // cardholder name. The label should never repeat the value.
       if (type.GetStorableType() == CREDIT_CARD_NUMBER) {
-        suggestion->value = credit_card->NetworkAndLastFourDigits();
+        if (IsAutofillCreditCardBankNameDisplayExperimentEnabled() &&
+            !credit_card->bank_name().empty()) {
+          suggestion->value = credit_card->BankNameAndLastFourDigits();
+        } else {
+          suggestion->value = credit_card->NetworkAndLastFourDigits();
+        }
         if (IsAutofillCreditCardLastUsedDateDisplayExperimentEnabled()) {
           suggestion->label =
               credit_card->GetLastUsedDateForDisplay(app_locale_);
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 2a1b23d..eae7022 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -24,6 +24,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
@@ -3923,6 +3924,74 @@
   ASSERT_EQ(3U, suggestions.size());
 }
 
+// Tests that server cards will shown bank name when bank name available and
+// feature flag on.
+TEST_F(PersonalDataManagerTest,
+       GetCreditCardSuggestions_ShowBankNameOfServerCards) {
+  // Turn on feature flag.
+  base::test::ScopedFeatureList scoped_feature_list_;
+  scoped_feature_list_.InitAndEnableFeature(kAutofillCreditCardBankNameDisplay);
+
+  EnableWalletCardImport();
+
+  // Add a local card.
+  CreditCard credit_card0("287151C8-6AB1-487C-9095-28E80BE5DA15",
+                          "https://www.example.com");
+  test::SetCreditCardInfo(&credit_card0, "Clyde Barrow",
+                          "347666888555" /* American Express */, "04", "2999",
+                          "1");
+  credit_card0.set_use_count(3);
+  credit_card0.set_use_date(AutofillClock::Now() -
+                            base::TimeDelta::FromDays(1));
+  personal_data_->AddCreditCard(credit_card0);
+
+  std::vector<CreditCard> server_cards;
+
+  // Add a server card without bank name.
+  server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b459"));
+  test::SetCreditCardInfo(&server_cards.back(), "Emmet Dalton", "2110", "12",
+                          "2999", "1");
+  server_cards.back().set_use_count(2);
+  server_cards.back().set_use_date(AutofillClock::Now() -
+                                   base::TimeDelta::FromDays(1));
+  server_cards.back().SetNetworkForMaskedCard(kVisaCard);
+
+  // Add a server card with bank name.
+  server_cards.push_back(CreditCard(CreditCard::MASKED_SERVER_CARD, "b460"));
+  test::SetCreditCardInfo(&server_cards.back(), "Emmet Dalton", "2111", "12",
+                          "2999", "1");
+  server_cards.back().set_use_count(1);
+  server_cards.back().set_use_date(AutofillClock::Now() -
+                                   base::TimeDelta::FromDays(1));
+  server_cards.back().SetNetworkForMaskedCard(kVisaCard);
+  server_cards.back().set_bank_name("Chase");
+
+  test::SetServerCreditCards(autofill_table_, server_cards);
+
+  EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged())
+      .WillOnce(QuitMainMessageLoop());
+  base::RunLoop().Run();
+
+  std::vector<Suggestion> suggestions =
+      personal_data_->GetCreditCardSuggestions(
+          AutofillType(CREDIT_CARD_NUMBER),
+          /* field_contents= */ base::string16());
+  ASSERT_EQ(3U, suggestions.size());
+
+  // Local cards will show network.
+  EXPECT_EQ(
+      base::UTF8ToUTF16(std::string("Amex") + kUTF8MidlineEllipsis + "8555"),
+      suggestions[0].value);
+  // Server card without bank name will show network.
+  EXPECT_EQ(
+      base::UTF8ToUTF16(std::string("Visa") + kUTF8MidlineEllipsis + "2110"),
+      suggestions[1].value);
+  // Server card with bank name will show bank name.
+  EXPECT_EQ(
+      base::UTF8ToUTF16(std::string("Chase") + kUTF8MidlineEllipsis + "2111"),
+      suggestions[2].value);
+}
+
 // Tests that only the full server card is kept when deduping with a local
 // duplicate of it.
 TEST_F(PersonalDataManagerTest,
diff --git a/components/autofill/core/browser/webdata/autofill_table.cc b/components/autofill/core/browser/webdata/autofill_table.cc
index d98db89..1cf28e6 100644
--- a/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/components/autofill/core/browser/webdata/autofill_table.cc
@@ -193,6 +193,7 @@
       base::Time::FromTimeT(s.ColumnInt64(index++)));
   credit_card->set_origin(s.ColumnString(index++));
   credit_card->set_billing_address_id(s.ColumnString(index++));
+  credit_card->set_bank_name(s.ColumnString(index++));
 
   return credit_card;
 }
@@ -472,6 +473,9 @@
     case 72:
       *update_compatible_version = true;
       return MigrateToVersion72RenameCardTypeToIssuerNetwork();
+    case 73:
+      *update_compatible_version = false;
+      return MigrateToVersion73AddMaskedCardBankName();
   }
   return true;
 }
@@ -1219,7 +1223,8 @@
       "name_on_card,"                 // 7
       "exp_month,"                    // 8
       "exp_year,"                     // 9
-      "metadata.billing_address_id "  // 10
+      "metadata.billing_address_id,"  // 10
+      "bank_name "                    // 11
       "FROM masked_credit_cards masked "
       "LEFT OUTER JOIN unmasked_credit_cards USING (id) "
       "LEFT OUTER JOIN server_card_metadata metadata USING (id)"));
@@ -1261,6 +1266,7 @@
     card->SetRawInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(index++));
     card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(index++));
     card->set_billing_address_id(s.ColumnString(index++));
+    card->set_bank_name(s.ColumnString(index++));
     credit_cards->push_back(std::move(card));
   }
   return s.Succeeded();
@@ -1277,8 +1283,9 @@
                               "name_on_card,"  // 3
                               "last_four,"     // 4
                               "exp_month,"     // 5
-                              "exp_year)"      // 6
-                              "VALUES (?,?,?,?,?,?,?)"));
+                              "exp_year,"      // 6
+                              "bank_name)"     // 7
+                              "VALUES (?,?,?,?,?,?,?,?)"));
   for (const CreditCard& card : credit_cards) {
     DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type());
     masked_insert.BindString(0, card.server_id());
@@ -1290,7 +1297,7 @@
     masked_insert.BindString16(5, card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
     masked_insert.BindString16(6,
                                card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
-
+    masked_insert.BindString(7, card.bank_name());
     masked_insert.Run();
     masked_insert.Reset(true);
 
@@ -1956,7 +1963,8 @@
                       "network VARCHAR,"
                       "last_four VARCHAR,"
                       "exp_month INTEGER DEFAULT 0,"
-                      "exp_year INTEGER DEFAULT 0)")) {
+                      "exp_year INTEGER DEFAULT 0, "
+                      "bank_name VARCHAR)")) {
       NOTREACHED();
       return false;
     }
@@ -2599,4 +2607,19 @@
          transaction.Commit();
 }
 
+bool AutofillTable::MigrateToVersion73AddMaskedCardBankName() {
+  sql::Transaction transaction(db_);
+  if (!transaction.Begin())
+    return false;
+
+  // Add the new bank_name column to the masked_credit_cards table.
+  if (!db_->DoesColumnExist("masked_credit_cards", "bank_name") &&
+      !db_->Execute("ALTER TABLE masked_credit_cards ADD COLUMN "
+                    "bank_name VARCHAR")) {
+    return false;
+  }
+
+  return transaction.Commit();
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_table.h b/components/autofill/core/browser/webdata/autofill_table.h
index 161b033f..22625148 100644
--- a/components/autofill/core/browser/webdata/autofill_table.h
+++ b/components/autofill/core/browser/webdata/autofill_table.h
@@ -166,6 +166,7 @@
 //                      with locally stored cards and generating descriptions.
 //   exp_month          Expiration month: 1-12
 //   exp_year           Four-digit year: 2017
+//   bank_name          Issuer bank name of the credit card.
 //
 // unmasked_credit_cards
 //                      When a masked credit credit card is unmasked and the
@@ -464,6 +465,7 @@
   bool MigrateToVersion70AddSyncMetadata();
   bool MigrateToVersion71AddHasConvertedAndBillingAddressIdMetadata();
   bool MigrateToVersion72RenameCardTypeToIssuerNetwork();
+  bool MigrateToVersion73AddMaskedCardBankName();
 
   // Max data length saved in the table, AKA the maximum length allowed for
   // form data.
diff --git a/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
index 1930216..4e92e5c7 100644
--- a/components/autofill/core/browser/webdata/autofill_table_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -1762,6 +1762,28 @@
   outputs.clear();
 }
 
+TEST_F(AutofillTableTest, ServerCardBankName) {
+  // Add a masked card.
+  CreditCard masked_card(CreditCard::MASKED_SERVER_CARD, "a123");
+  masked_card.SetRawInfo(CREDIT_CARD_NAME_FULL,
+                         ASCIIToUTF16("Paul F. Tompkins"));
+  masked_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("1"));
+  masked_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2020"));
+  masked_card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("1111"));
+  masked_card.SetNetworkForMaskedCard(kVisaCard);
+  masked_card.set_bank_name("Chase");
+
+  // Set server credit cards
+  std::vector<CreditCard> inputs = {masked_card};
+  test::SetServerCreditCards(table_.get(), inputs);
+
+  // Get server credit cards and check bank names equal
+  std::vector<std::unique_ptr<CreditCard>> outputs;
+  table_->GetServerCreditCards(&outputs);
+  ASSERT_EQ(1u, outputs.size());
+  EXPECT_EQ("Chase", outputs[0]->bank_name());
+}
+
 TEST_F(AutofillTableTest, SetServerCardUpdateUsageStatsAndBillingAddress) {
   // Add a masked card.
   CreditCard masked_card(CreditCard::MASKED_SERVER_CARD, "a123");
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc b/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc
index 54e8be78..af81aeaa 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc
@@ -82,6 +82,7 @@
   result.SetExpirationMonth(card.exp_month());
   result.SetExpirationYear(card.exp_year());
   result.set_billing_address_id(card.billing_address_id());
+  result.set_bank_name(card.bank_name());
   return result;
 }
 
diff --git a/components/cronet/android/cronet_url_request_context_adapter.cc b/components/cronet/android/cronet_url_request_context_adapter.cc
index a01ee30..5274d9e 100644
--- a/components/cronet/android/cronet_url_request_context_adapter.cc
+++ b/components/cronet/android/cronet_url_request_context_adapter.cc
@@ -511,8 +511,8 @@
     const JavaParamRef<jobject>& jcaller) {
   base::android::ScopedJavaGlobalRef<jobject> jcaller_ref;
   jcaller_ref.Reset(env, jcaller);
-  proxy_config_service_ = net::ProxyService::CreateSystemProxyConfigService(
-      GetNetworkTaskRunner(), nullptr /* Ignored on Android */);
+  proxy_config_service_ =
+      net::ProxyService::CreateSystemProxyConfigService(GetNetworkTaskRunner());
   net::ProxyConfigServiceAndroid* android_proxy_config_service =
       static_cast<net::ProxyConfigServiceAndroid*>(proxy_config_service_.get());
   // If a PAC URL is present, ignore it and use the address and port of
diff --git a/components/cryptauth/BUILD.gn b/components/cryptauth/BUILD.gn
index b89eaaa9..d7bb0f67 100644
--- a/components/cryptauth/BUILD.gn
+++ b/components/cryptauth/BUILD.gn
@@ -50,6 +50,8 @@
     "device_to_device_secure_context.h",
     "foreground_eid_generator.cc",
     "foreground_eid_generator.h",
+    "local_device_data_provider.cc",
+    "local_device_data_provider.h",
     "pref_names.cc",
     "pref_names.h",
     "raw_eid_generator.h",
@@ -124,6 +126,8 @@
     "mock_cryptauth_client.h",
     "mock_foreground_eid_generator.cc",
     "mock_foreground_eid_generator.h",
+    "mock_local_device_data_provider.cc",
+    "mock_local_device_data_provider.h",
     "mock_remote_beacon_seed_fetcher.cc",
     "mock_remote_beacon_seed_fetcher.h",
     "mock_sync_scheduler.cc",
@@ -162,6 +166,7 @@
     "device_to_device_secure_context_unittest.cc",
     "fake_secure_message_delegate_unittest.cc",
     "foreground_eid_generator_unittest.cc",
+    "local_device_data_provider_unittest.cc",
     "raw_eid_generator_impl_unittest.cc",
     "remote_beacon_seed_fetcher_unittest.cc",
     "remote_device_loader_unittest.cc",
diff --git a/chromeos/components/tether/local_device_data_provider.cc b/components/cryptauth/local_device_data_provider.cc
similarity index 80%
rename from chromeos/components/tether/local_device_data_provider.cc
rename to components/cryptauth/local_device_data_provider.cc
index 38fbcc10..f98cad81 100644
--- a/chromeos/components/tether/local_device_data_provider.cc
+++ b/components/cryptauth/local_device_data_provider.cc
@@ -2,26 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/tether/local_device_data_provider.h"
+#include "components/cryptauth/local_device_data_provider.h"
 
 #include "components/cryptauth/cryptauth_device_manager.h"
 #include "components/cryptauth/cryptauth_enrollment_manager.h"
 #include "components/cryptauth/cryptauth_service.h"
 #include "components/cryptauth/proto/cryptauth_api.pb.h"
 
-namespace chromeos {
-
-namespace tether {
+namespace cryptauth {
 
 LocalDeviceDataProvider::LocalDeviceDataProvider(
-    cryptauth::CryptAuthService* cryptauth_service)
+    CryptAuthService* cryptauth_service)
     : cryptauth_service_(cryptauth_service) {}
 
 LocalDeviceDataProvider::~LocalDeviceDataProvider() {}
 
 bool LocalDeviceDataProvider::GetLocalDeviceData(
     std::string* public_key_out,
-    std::vector<cryptauth::BeaconSeed>* beacon_seeds_out) const {
+    std::vector<BeaconSeed>* beacon_seeds_out) const {
   DCHECK(public_key_out || beacon_seeds_out);
 
   std::string public_key =
@@ -30,7 +28,7 @@
     return false;
   }
 
-  std::vector<cryptauth::ExternalDeviceInfo> synced_devices =
+  std::vector<ExternalDeviceInfo> synced_devices =
       cryptauth_service_->GetCryptAuthDeviceManager()->GetSyncedDevices();
   for (const auto& device : synced_devices) {
     if (device.has_public_key() && device.public_key() == public_key &&
@@ -53,6 +51,4 @@
   return false;
 }
 
-}  // namespace tether
-
-}  // namespace chromeos
+}  // namespace cryptauth
diff --git a/chromeos/components/tether/local_device_data_provider.h b/components/cryptauth/local_device_data_provider.h
similarity index 66%
rename from chromeos/components/tether/local_device_data_provider.h
rename to components/cryptauth/local_device_data_provider.h
index a4e713dd..927179d0 100644
--- a/chromeos/components/tether/local_device_data_provider.h
+++ b/components/cryptauth/local_device_data_provider.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROMEOS_COMPONENTS_TETHER_LOCAL_DEVICE_DATA_PROVIDER_H_
-#define CHROMEOS_COMPONENTS_TETHER_LOCAL_DEVICE_DATA_PROVIDER_H_
+#ifndef COMPONENTS_CRYPTAUTH_LOCAL_DEVICE_DATA_PROVIDER_H_
+#define COMPONENTS_CRYPTAUTH_LOCAL_DEVICE_DATA_PROVIDER_H_
 
 #include <memory>
 #include <string>
@@ -13,20 +13,15 @@
 #include "base/memory/ptr_util.h"
 
 namespace cryptauth {
+
 class BeaconSeed;
 class CryptAuthService;
-}
-
-namespace chromeos {
-
-namespace tether {
 
 // Fetches CryptAuth data about the local device (i.e., the device on which this
 // code is running) for the current user (i.e., the one which is logged-in).
 class LocalDeviceDataProvider {
  public:
-  explicit LocalDeviceDataProvider(
-      cryptauth::CryptAuthService* cryptauth_service);
+  explicit LocalDeviceDataProvider(CryptAuthService* cryptauth_service);
   virtual ~LocalDeviceDataProvider();
 
   // Fetches the public key and/or the beacon seeds for the local device.
@@ -34,18 +29,16 @@
   // parameter, the associated data will not be fetched.
   virtual bool GetLocalDeviceData(
       std::string* public_key_out,
-      std::vector<cryptauth::BeaconSeed>* beacon_seeds_out) const;
+      std::vector<BeaconSeed>* beacon_seeds_out) const;
 
  private:
   friend class LocalDeviceDataProviderTest;
 
-  cryptauth::CryptAuthService* cryptauth_service_;
+  CryptAuthService* cryptauth_service_;
 
   DISALLOW_COPY_AND_ASSIGN(LocalDeviceDataProvider);
 };
 
-}  // namespace tether
+}  // namespace cryptauth
 
-}  // namespace chromeos
-
-#endif  // CHROMEOS_COMPONENTS_TETHER_LOCAL_DEVICE_DATA_PROVIDER_H_
+#endif  // COMPONENTS_CRYPTAUTH_LOCAL_DEVICE_DATA_PROVIDER_H_
diff --git a/chromeos/components/tether/local_device_data_provider_unittest.cc b/components/cryptauth/local_device_data_provider_unittest.cc
similarity index 74%
rename from chromeos/components/tether/local_device_data_provider_unittest.cc
rename to components/cryptauth/local_device_data_provider_unittest.cc
index 8d4d5b8..9259c5d0 100644
--- a/chromeos/components/tether/local_device_data_provider_unittest.cc
+++ b/components/cryptauth/local_device_data_provider_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/tether/local_device_data_provider.h"
+#include "components/cryptauth/local_device_data_provider.h"
 
 #include <string>
 #include <vector>
@@ -22,9 +22,7 @@
 using testing::NiceMock;
 using testing::Return;
 
-namespace chromeos {
-
-namespace tether {
+namespace cryptauth {
 
 namespace {
 
@@ -38,36 +36,33 @@
 const int64_t kBeaconSeed2StartMs = 2000L;
 const int64_t kBeaconSeed2EndMs = 3000L;
 
-class MockCryptAuthDeviceManager : public cryptauth::CryptAuthDeviceManager {
+class MockCryptAuthDeviceManager : public CryptAuthDeviceManager {
  public:
   MockCryptAuthDeviceManager() {}
   ~MockCryptAuthDeviceManager() override {}
 
-  MOCK_CONST_METHOD0(GetSyncedDevices,
-                     std::vector<cryptauth::ExternalDeviceInfo>());
+  MOCK_CONST_METHOD0(GetSyncedDevices, std::vector<ExternalDeviceInfo>());
 };
 
-class MockCryptAuthEnrollmentManager
-    : public cryptauth::CryptAuthEnrollmentManager {
+class MockCryptAuthEnrollmentManager : public CryptAuthEnrollmentManager {
  public:
   explicit MockCryptAuthEnrollmentManager(
-      cryptauth::FakeCryptAuthGCMManager* fake_cryptauth_gcm_manager)
-      : cryptauth::CryptAuthEnrollmentManager(
-            nullptr /* clock */,
-            nullptr /* enroller_factory */,
-            nullptr /* secure_message_delegate */,
-            cryptauth::GcmDeviceInfo(),
-            fake_cryptauth_gcm_manager,
-            nullptr /* pref_service */) {}
+      FakeCryptAuthGCMManager* fake_cryptauth_gcm_manager)
+      : CryptAuthEnrollmentManager(nullptr /* clock */,
+                                   nullptr /* enroller_factory */,
+                                   nullptr /* secure_message_delegate */,
+                                   GcmDeviceInfo(),
+                                   fake_cryptauth_gcm_manager,
+                                   nullptr /* pref_service */) {}
   ~MockCryptAuthEnrollmentManager() override {}
 
   MOCK_CONST_METHOD0(GetUserPublicKey, std::string());
 };
 
-cryptauth::BeaconSeed CreateBeaconSeed(const std::string& data,
-                                       int64_t start_ms,
-                                       int64_t end_ms) {
-  cryptauth::BeaconSeed seed;
+BeaconSeed CreateBeaconSeed(const std::string& data,
+                            int64_t start_ms,
+                            int64_t end_ms) {
+  BeaconSeed seed;
   seed.set_data(data);
   seed.set_start_time_millis(start_ms);
   seed.set_end_time_millis(end_ms);
@@ -85,11 +80,11 @@
         kBeaconSeed2Data, kBeaconSeed2StartMs, kBeaconSeed2EndMs));
 
     // Has no public key and no BeaconSeeds.
-    cryptauth::ExternalDeviceInfo synced_device1;
+    ExternalDeviceInfo synced_device1;
     fake_synced_devices_.push_back(synced_device1);
 
     // Has no public key and some BeaconSeeds.
-    cryptauth::ExternalDeviceInfo synced_device2;
+    ExternalDeviceInfo synced_device2;
     synced_device2.add_beacon_seeds()->CopyFrom(CreateBeaconSeed(
         kBeaconSeed1Data, kBeaconSeed1StartMs, kBeaconSeed1EndMs));
     synced_device2.add_beacon_seeds()->CopyFrom(CreateBeaconSeed(
@@ -97,12 +92,12 @@
     fake_synced_devices_.push_back(synced_device2);
 
     // Has another different public key and no BeaconSeeds.
-    cryptauth::ExternalDeviceInfo synced_device3;
+    ExternalDeviceInfo synced_device3;
     synced_device3.set_public_key("anotherPublicKey");
     fake_synced_devices_.push_back(synced_device3);
 
     // Has different public key and BeaconSeeds.
-    cryptauth::ExternalDeviceInfo synced_device4;
+    ExternalDeviceInfo synced_device4;
     synced_device4.set_public_key("otherPublicKey");
     synced_device4.add_beacon_seeds()->CopyFrom(CreateBeaconSeed(
         kBeaconSeed1Data, kBeaconSeed1StartMs, kBeaconSeed1EndMs));
@@ -111,12 +106,12 @@
     fake_synced_devices_.push_back(synced_device4);
 
     // Has public key but no BeaconSeeds.
-    cryptauth::ExternalDeviceInfo synced_device5;
+    ExternalDeviceInfo synced_device5;
     synced_device5.set_public_key(kDefaultPublicKey);
     fake_synced_devices_.push_back(synced_device5);
 
     // Has public key and BeaconSeeds.
-    cryptauth::ExternalDeviceInfo synced_device6;
+    ExternalDeviceInfo synced_device6;
     synced_device6.set_public_key(kDefaultPublicKey);
     synced_device6.add_beacon_seeds()->CopyFrom(CreateBeaconSeed(
         kBeaconSeed1Data, kBeaconSeed1StartMs, kBeaconSeed1EndMs));
@@ -129,13 +124,12 @@
     mock_device_manager_ =
         base::WrapUnique(new NiceMock<MockCryptAuthDeviceManager>());
     fake_cryptauth_gcm_manager_ =
-        base::MakeUnique<cryptauth::FakeCryptAuthGCMManager>("registrationId");
+        base::MakeUnique<FakeCryptAuthGCMManager>("registrationId");
     mock_enrollment_manager_ =
         base::WrapUnique(new NiceMock<MockCryptAuthEnrollmentManager>(
             fake_cryptauth_gcm_manager_.get()));
 
-    fake_cryptauth_service_ =
-        base::MakeUnique<cryptauth::FakeCryptAuthService>();
+    fake_cryptauth_service_ = base::MakeUnique<FakeCryptAuthService>();
     fake_cryptauth_service_->set_cryptauth_device_manager(
         mock_device_manager_.get());
     fake_cryptauth_service_->set_cryptauth_enrollment_manager(
@@ -145,15 +139,14 @@
         new LocalDeviceDataProvider(fake_cryptauth_service_.get()));
   }
 
-  std::vector<cryptauth::BeaconSeed> fake_beacon_seeds_;
-  std::vector<cryptauth::ExternalDeviceInfo> fake_synced_devices_;
+  std::vector<BeaconSeed> fake_beacon_seeds_;
+  std::vector<ExternalDeviceInfo> fake_synced_devices_;
 
-  std::unique_ptr<cryptauth::FakeCryptAuthGCMManager>
-      fake_cryptauth_gcm_manager_;
+  std::unique_ptr<FakeCryptAuthGCMManager> fake_cryptauth_gcm_manager_;
   std::unique_ptr<NiceMock<MockCryptAuthDeviceManager>> mock_device_manager_;
   std::unique_ptr<NiceMock<MockCryptAuthEnrollmentManager>>
       mock_enrollment_manager_;
-  std::unique_ptr<cryptauth::FakeCryptAuthService> fake_cryptauth_service_;
+  std::unique_ptr<FakeCryptAuthService> fake_cryptauth_service_;
 
   std::unique_ptr<LocalDeviceDataProvider> provider_;
 
@@ -168,7 +161,7 @@
       .WillByDefault(Return(fake_synced_devices_));
 
   std::string public_key;
-  std::vector<cryptauth::BeaconSeed> beacon_seeds;
+  std::vector<BeaconSeed> beacon_seeds;
 
   EXPECT_FALSE(provider_->GetLocalDeviceData(&public_key, &beacon_seeds));
 }
@@ -177,10 +170,10 @@
   ON_CALL(*mock_enrollment_manager_, GetUserPublicKey())
       .WillByDefault(Return(kDefaultPublicKey));
   ON_CALL(*mock_device_manager_, GetSyncedDevices())
-      .WillByDefault(Return(std::vector<cryptauth::ExternalDeviceInfo>()));
+      .WillByDefault(Return(std::vector<ExternalDeviceInfo>()));
 
   std::string public_key;
-  std::vector<cryptauth::BeaconSeed> beacon_seeds;
+  std::vector<BeaconSeed> beacon_seeds;
 
   EXPECT_FALSE(provider_->GetLocalDeviceData(&public_key, &beacon_seeds));
 }
@@ -190,12 +183,12 @@
   ON_CALL(*mock_enrollment_manager_, GetUserPublicKey())
       .WillByDefault(Return(kDefaultPublicKey));
   ON_CALL(*mock_device_manager_, GetSyncedDevices())
-      .WillByDefault(Return(std::vector<cryptauth::ExternalDeviceInfo>{
+      .WillByDefault(Return(std::vector<ExternalDeviceInfo>{
           fake_synced_devices_[0], fake_synced_devices_[1],
           fake_synced_devices_[2], fake_synced_devices_[3]}));
 
   std::string public_key;
-  std::vector<cryptauth::BeaconSeed> beacon_seeds;
+  std::vector<BeaconSeed> beacon_seeds;
 
   EXPECT_FALSE(provider_->GetLocalDeviceData(&public_key, &beacon_seeds));
 }
@@ -205,12 +198,12 @@
   ON_CALL(*mock_enrollment_manager_, GetUserPublicKey())
       .WillByDefault(Return(kDefaultPublicKey));
   ON_CALL(*mock_device_manager_, GetSyncedDevices())
-      .WillByDefault(Return(std::vector<cryptauth::ExternalDeviceInfo>{
+      .WillByDefault(Return(std::vector<ExternalDeviceInfo>{
           fake_synced_devices_[4],
       }));
 
   std::string public_key;
-  std::vector<cryptauth::BeaconSeed> beacon_seeds;
+  std::vector<BeaconSeed> beacon_seeds;
 
   EXPECT_FALSE(provider_->GetLocalDeviceData(&public_key, &beacon_seeds));
 }
@@ -222,7 +215,7 @@
       .WillByDefault(Return(fake_synced_devices_));
 
   std::string public_key;
-  std::vector<cryptauth::BeaconSeed> beacon_seeds;
+  std::vector<BeaconSeed> beacon_seeds;
 
   EXPECT_TRUE(provider_->GetLocalDeviceData(&public_key, &beacon_seeds));
 
@@ -232,14 +225,12 @@
   for (size_t i = 0; i < fake_beacon_seeds_.size(); i++) {
     // Note: google::protobuf::util::MessageDifferencer can only be used to diff
     // Message, but BeaconSeed derives from the incompatible MessageLite class.
-    cryptauth::BeaconSeed expected = fake_beacon_seeds_[i];
-    cryptauth::BeaconSeed actual = beacon_seeds[i];
+    BeaconSeed expected = fake_beacon_seeds_[i];
+    BeaconSeed actual = beacon_seeds[i];
     EXPECT_EQ(expected.data(), actual.data());
     EXPECT_EQ(expected.start_time_millis(), actual.start_time_millis());
     EXPECT_EQ(expected.end_time_millis(), actual.end_time_millis());
   }
 }
 
-}  // namespace tether
-
-}  // namespace chromeos
+}  // namespace cryptauth
diff --git a/chromeos/components/tether/mock_local_device_data_provider.cc b/components/cryptauth/mock_local_device_data_provider.cc
similarity index 80%
rename from chromeos/components/tether/mock_local_device_data_provider.cc
rename to components/cryptauth/mock_local_device_data_provider.cc
index 736e620..be56d0b 100644
--- a/chromeos/components/tether/mock_local_device_data_provider.cc
+++ b/components/cryptauth/mock_local_device_data_provider.cc
@@ -2,15 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chromeos/components/tether/mock_local_device_data_provider.h"
+#include "components/cryptauth/mock_local_device_data_provider.h"
 
 #include "components/cryptauth/cryptauth_device_manager.h"
 #include "components/cryptauth/cryptauth_enrollment_manager.h"
 #include "components/cryptauth/proto/cryptauth_api.pb.h"
 
-namespace chromeos {
-
-namespace tether {
+namespace cryptauth {
 
 MockLocalDeviceDataProvider::MockLocalDeviceDataProvider()
     : LocalDeviceDataProvider(nullptr /* cryptauth_service */) {}
@@ -27,7 +25,7 @@
 }
 
 void MockLocalDeviceDataProvider::SetBeaconSeeds(
-    std::unique_ptr<std::vector<cryptauth::BeaconSeed>> beacon_seeds) {
+    std::unique_ptr<std::vector<BeaconSeed>> beacon_seeds) {
   if (beacon_seeds) {
     beacon_seeds_ = std::move(beacon_seeds);
   } else {
@@ -37,7 +35,7 @@
 
 bool MockLocalDeviceDataProvider::GetLocalDeviceData(
     std::string* public_key_out,
-    std::vector<cryptauth::BeaconSeed>* beacon_seeds_out) const {
+    std::vector<BeaconSeed>* beacon_seeds_out) const {
   bool success = false;
 
   if (public_key_ && public_key_out) {
@@ -53,6 +51,4 @@
   return success;
 }
 
-}  // namespace tether
-
-}  // namespace chromeos
+}  // namespace cryptauth
diff --git a/components/cryptauth/mock_local_device_data_provider.h b/components/cryptauth/mock_local_device_data_provider.h
new file mode 100644
index 0000000..563e1e8
--- /dev/null
+++ b/components/cryptauth/mock_local_device_data_provider.h
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CRYPTAUTH_MOCK_LOCAL_DEVICE_DATA_PROVIDER_H_
+#define COMPONENTS_CRYPTAUTH_MOCK_LOCAL_DEVICE_DATA_PROVIDER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "components/cryptauth/local_device_data_provider.h"
+
+namespace cryptauth {
+
+class BeaconSeed;
+
+// Test double for LocalDeviceDataProvider.
+class MockLocalDeviceDataProvider : public LocalDeviceDataProvider {
+ public:
+  MockLocalDeviceDataProvider();
+  ~MockLocalDeviceDataProvider() override;
+
+  void SetPublicKey(std::unique_ptr<std::string> public_key);
+  void SetBeaconSeeds(std::unique_ptr<std::vector<BeaconSeed>> beacon_seeds);
+
+  // LocalDeviceDataProvider:
+  bool GetLocalDeviceData(
+      std::string* public_key_out,
+      std::vector<BeaconSeed>* beacon_seeds_out) const override;
+
+ private:
+  std::unique_ptr<std::string> public_key_;
+  std::unique_ptr<std::vector<BeaconSeed>> beacon_seeds_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockLocalDeviceDataProvider);
+};
+
+}  // namespace cryptauth
+
+#endif  // COMPONENTS_CRYPTAUTH_MOCK_LOCAL_DEVICE_DATA_PROVIDER_H_
diff --git a/components/download/content/factory/download_service_factory.cc b/components/download/content/factory/download_service_factory.cc
index d440a04..25355f7 100644
--- a/components/download/content/factory/download_service_factory.cc
+++ b/components/download/content/factory/download_service_factory.cc
@@ -33,8 +33,7 @@
   auto config = Configuration::CreateFromFinch();
 
   auto files_storage_dir = storage_dir.Append(kFilesStorageDir);
-  auto driver =
-      base::MakeUnique<DownloadDriverImpl>(download_manager, files_storage_dir);
+  auto driver = base::MakeUnique<DownloadDriverImpl>(download_manager);
 
   auto entry_db_storage_dir = storage_dir.Append(kEntryDBStorageDir);
   auto entry_db =
diff --git a/components/download/content/internal/download_driver_impl.cc b/components/download/content/internal/download_driver_impl.cc
index 415f043..9a4ca40 100644
--- a/components/download/content/internal/download_driver_impl.cc
+++ b/components/download/content/internal/download_driver_impl.cc
@@ -49,16 +49,18 @@
   entry.paused = item->IsPaused();
   entry.bytes_downloaded = item->GetReceivedBytes();
   entry.expected_total_size = item->GetTotalBytes();
-  entry.temporary_physical_file_path = item->GetFullPath();
+  entry.current_file_path =
+      item->GetState() == content::DownloadItem::DownloadState::COMPLETE
+          ? item->GetTargetFilePath()
+          : item->GetFullPath();
   entry.completion_time = item->GetEndTime();
   entry.response_headers = item->GetResponseHeaders();
   entry.url_chain = item->GetUrlChain();
   return entry;
 }
 
-DownloadDriverImpl::DownloadDriverImpl(content::DownloadManager* manager,
-                                       const base::FilePath& dir)
-    : download_manager_(manager), file_dir_(dir), client_(nullptr) {
+DownloadDriverImpl::DownloadDriverImpl(content::DownloadManager* manager)
+    : download_manager_(manager), client_(nullptr) {
   DCHECK(download_manager_);
 }
 
@@ -91,6 +93,7 @@
 void DownloadDriverImpl::Start(
     const RequestParams& request_params,
     const std::string& guid,
+    const base::FilePath& file_path,
     const net::NetworkTrafficAnnotationTag& traffic_annotation) {
   DCHECK(!request_params.url.is_empty());
   DCHECK(!guid.empty());
@@ -116,7 +119,7 @@
   download_url_params->set_guid(guid);
   download_url_params->set_transient(true);
   download_url_params->set_method(request_params.method);
-  download_url_params->set_file_path(file_dir_.AppendASCII(guid));
+  download_url_params->set_file_path(file_path);
 
   download_manager_->DownloadUrl(std::move(download_url_params));
 }
@@ -183,7 +186,7 @@
   DriverEntry entry = CreateDriverEntry(item);
 
   if (state == DownloadState::COMPLETE) {
-    client_->OnDownloadSucceeded(entry, item->GetTargetFilePath());
+    client_->OnDownloadSucceeded(entry);
     item->RemoveObserver(this);
   } else if (state == DownloadState::IN_PROGRESS) {
     client_->OnDownloadUpdated(entry);
diff --git a/components/download/content/internal/download_driver_impl.h b/components/download/content/internal/download_driver_impl.h
index 63acc5f..1d780a4 100644
--- a/components/download/content/internal/download_driver_impl.h
+++ b/components/download/content/internal/download_driver_impl.h
@@ -27,9 +27,8 @@
   // Creates a driver entry based on a download item.
   static DriverEntry CreateDriverEntry(const content::DownloadItem* item);
 
-  // Create the driver. All files downloaded will be saved to |dir|.
-  DownloadDriverImpl(content::DownloadManager* manager,
-                     const base::FilePath& dir);
+  // Create the driver.
+  DownloadDriverImpl(content::DownloadManager* manager);
   ~DownloadDriverImpl() override;
 
   // DownloadDriver implementation.
@@ -38,6 +37,7 @@
   void Start(
       const RequestParams& request_params,
       const std::string& guid,
+      const base::FilePath& file_path,
       const net::NetworkTrafficAnnotationTag& traffic_annotation) override;
   void Remove(const std::string& guid) override;
   void Pause(const std::string& guid) override;
@@ -58,9 +58,6 @@
   // Low level download handle.
   content::DownloadManager* download_manager_;
 
-  // Target directory of download files.
-  base::FilePath file_dir_;
-
   // The client that receives updates from low level download logic.
   DownloadDriver::Client* client_;
 
diff --git a/components/download/content/internal/download_driver_impl_unittest.cc b/components/download/content/internal/download_driver_impl_unittest.cc
index 5fc7c86b..bccdda7 100644
--- a/components/download/content/internal/download_driver_impl_unittest.cc
+++ b/components/download/content/internal/download_driver_impl_unittest.cc
@@ -35,7 +35,8 @@
 // driver entry.
 MATCHER_P(DriverEntryEuqual, entry, "") {
   return entry.guid == arg.guid && entry.state == arg.state &&
-         entry.bytes_downloaded == arg.bytes_downloaded;
+         entry.bytes_downloaded == arg.bytes_downloaded &&
+         entry.current_file_path.value() == arg.current_file_path.value();
 }
 
 }  // namespace
@@ -45,8 +46,7 @@
   MOCK_METHOD1(OnDriverReady, void(bool));
   MOCK_METHOD1(OnDownloadCreated, void(const DriverEntry&));
   MOCK_METHOD2(OnDownloadFailed, void(const DriverEntry&, int));
-  MOCK_METHOD2(OnDownloadSucceeded,
-               void(const DriverEntry&, const base::FilePath&));
+  MOCK_METHOD1(OnDownloadSucceeded, void(const DriverEntry&));
   MOCK_METHOD1(OnDownloadUpdated, void(const DriverEntry&));
 };
 
@@ -56,8 +56,7 @@
   ~DownloadDriverImplTest() override = default;
 
   void SetUp() override {
-    driver_ =
-        base::MakeUnique<DownloadDriverImpl>(&mock_manager_, base::FilePath());
+    driver_ = base::MakeUnique<DownloadDriverImpl>(&mock_manager_);
   }
 
   // TODO(xingliu): implements test download manager for embedders to test.
@@ -113,7 +112,7 @@
   fake_item.SetReceivedBytes(1024);
   fake_item.SetState(DownloadState::COMPLETE);
   entry = DownloadDriverImpl::CreateDriverEntry(&fake_item);
-  EXPECT_CALL(mock_client_, OnDownloadSucceeded(DriverEntryEuqual(entry), _))
+  EXPECT_CALL(mock_client_, OnDownloadSucceeded(DriverEntryEuqual(entry)))
       .Times(1)
       .RetiresOnSaturation();
   static_cast<content::DownloadItem::Observer*>(driver_.get())
diff --git a/components/download/internal/controller_impl.cc b/components/download/internal/controller_impl.cc
index 43fa783..016e5bd 100644
--- a/components/download/internal/controller_impl.cc
+++ b/components/download/internal/controller_impl.cc
@@ -321,8 +321,7 @@
   HandleCompleteDownload(CompletionType::FAIL, download.guid);
 }
 
-void ControllerImpl::OnDownloadSucceeded(const DriverEntry& download,
-                                         const base::FilePath& path) {
+void ControllerImpl::OnDownloadSucceeded(const DriverEntry& download) {
   if (initializing_internals_)
     return;
 
@@ -639,7 +638,7 @@
     if (driver_entry.has_value()) {
       driver_->Resume(entry.guid);
     } else {
-      driver_->Start(entry.request_params, entry.guid,
+      driver_->Start(entry.request_params, entry.guid, entry.target_file_path,
                      NO_TRAFFIC_ANNOTATION_YET);
     }
   }
@@ -700,15 +699,16 @@
   if (type == CompletionType::SUCCEED) {
     auto driver_entry = driver_->Find(guid);
     DCHECK(driver_entry.has_value());
-    // TODO(dtrainor): Move the FilePath generation to the controller and store
-    // it in Entry.  Then pass it into the DownloadDriver.
-    entry->target_file_path = driver_entry->temporary_physical_file_path;
+    if (driver_entry->current_file_path != entry->target_file_path) {
+      stats::LogFilePathsAreStrangelyDifferent();
+      entry->target_file_path = driver_entry->current_file_path;
+    }
     entry->completion_time = driver_entry->completion_time;
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::Bind(&ControllerImpl::SendOnDownloadSucceeded,
-                   weak_ptr_factory_.GetWeakPtr(), entry->client, guid,
-                   base::FilePath(), driver_entry->bytes_downloaded));
+        FROM_HERE, base::Bind(&ControllerImpl::SendOnDownloadSucceeded,
+                              weak_ptr_factory_.GetWeakPtr(), entry->client,
+                              guid, driver_entry->current_file_path,
+                              driver_entry->bytes_downloaded));
     TransitTo(entry, Entry::State::COMPLETE, model_.get());
     ScheduleCleanupTask();
   } else {
diff --git a/components/download/internal/controller_impl.h b/components/download/internal/controller_impl.h
index 6405057..e23a65d 100644
--- a/components/download/internal/controller_impl.h
+++ b/components/download/internal/controller_impl.h
@@ -73,8 +73,7 @@
   void OnDriverReady(bool success) override;
   void OnDownloadCreated(const DriverEntry& download) override;
   void OnDownloadFailed(const DriverEntry& download, int reason) override;
-  void OnDownloadSucceeded(const DriverEntry& download,
-                           const base::FilePath& path) override;
+  void OnDownloadSucceeded(const DriverEntry& download) override;
   void OnDownloadUpdated(const DriverEntry& download) override;
 
   // Model::Client implementation.
diff --git a/components/download/internal/controller_impl_unittest.cc b/components/download/internal/controller_impl_unittest.cc
index 5da8cdad7..3240ec9 100644
--- a/components/download/internal/controller_impl_unittest.cc
+++ b/components/download/internal/controller_impl_unittest.cc
@@ -681,12 +681,12 @@
   driver_entry.guid = entry.guid;
   driver_entry.bytes_downloaded = 1024;
   driver_entry.completion_time = base::Time::Now();
-  base::FilePath path = base::FilePath::FromUTF8Unsafe("123");
+  driver_entry.current_file_path = base::FilePath::FromUTF8Unsafe("123");
 
   EXPECT_CALL(*task_scheduler_,
               ScheduleTask(DownloadTaskType::CLEANUP_TASK, _, _, _, _))
       .Times(1);
-  driver_->NotifyDownloadSucceeded(driver_entry, path);
+  driver_->NotifyDownloadSucceeded(driver_entry);
   EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entry.guid)->state);
 
   task_runner_->RunUntilIdle();
@@ -709,12 +709,12 @@
   driver_entry.guid = entry1.guid;
   driver_entry.bytes_downloaded = 1024;
   driver_entry.completion_time = base::Time::Now();
-  base::FilePath path = base::FilePath::FromUTF8Unsafe("123");
+  driver_entry.current_file_path = base::FilePath::FromUTF8Unsafe("123");
 
   EXPECT_CALL(*task_scheduler_, ScheduleTask(DownloadTaskType::CLEANUP_TASK,
                                              false, false, 479, 779))
       .Times(1);
-  driver_->NotifyDownloadSucceeded(driver_entry, path);
+  driver_->NotifyDownloadSucceeded(driver_entry);
   EXPECT_EQ(Entry::State::COMPLETE, model_->Get(entry1.guid)->state);
 
   task_runner_->RunUntilIdle();
@@ -792,7 +792,8 @@
   EXPECT_CALL(*client_,
               OnDownloadFailed(entry2.guid, Client::FailureReason::ABORTED))
       .Times(1);
-  driver_->Start(RequestParams(), entry2.guid, NO_TRAFFIC_ANNOTATION_YET);
+  driver_->Start(RequestParams(), entry2.guid, entry2.target_file_path,
+                 NO_TRAFFIC_ANNOTATION_YET);
 
   // Test FailureReason::NETWORK.
   EXPECT_CALL(*client_,
@@ -1000,7 +1001,7 @@
   EXPECT_FALSE(driver_->Find(entry3.guid).value().paused);
 
   // Simulate a successful external download.
-  driver_->NotifyDownloadSucceeded(dentry2, base::FilePath());
+  driver_->NotifyDownloadSucceeded(dentry2);
 
   EXPECT_TRUE(driver_->Find(entry1.guid).has_value());
   EXPECT_FALSE(driver_->Find(entry1.guid).value().paused);
@@ -1044,7 +1045,8 @@
   dentry2.state = DriverEntry::State::IN_PROGRESS;
 
   // Simulate a newly created external download.
-  driver_->Start(RequestParams(), dentry2.guid, NO_TRAFFIC_ANNOTATION_YET);
+  driver_->Start(RequestParams(), dentry2.guid, dentry2.current_file_path,
+                 NO_TRAFFIC_ANNOTATION_YET);
 
   EXPECT_TRUE(driver_->Find(entry1.guid).value().paused);
   EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
@@ -1072,14 +1074,15 @@
 
   // Rebuild the download so we can simulate more.
   dentry2.state = DriverEntry::State::IN_PROGRESS;
-  driver_->Start(RequestParams(), dentry2.guid, NO_TRAFFIC_ANNOTATION_YET);
+  driver_->Start(RequestParams(), dentry2.guid, dentry2.current_file_path,
+                 NO_TRAFFIC_ANNOTATION_YET);
 
   EXPECT_TRUE(driver_->Find(entry1.guid).value().paused);
   EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
 
   // Simulate a successful external download.
   dentry2.state = DriverEntry::State::COMPLETE;
-  driver_->NotifyDownloadSucceeded(dentry2, base::FilePath());
+  driver_->NotifyDownloadSucceeded(dentry2);
 
   EXPECT_FALSE(driver_->Find(entry1.guid).value().paused);
   EXPECT_FALSE(driver_->Find(entry2.guid).value().paused);
diff --git a/components/download/internal/download_driver.h b/components/download/internal/download_driver.h
index bf385696..53b8270 100644
--- a/components/download/internal/download_driver.h
+++ b/components/download/internal/download_driver.h
@@ -41,8 +41,7 @@
     virtual void OnDownloadFailed(const DriverEntry& download, int reason) = 0;
 
     // Called when any download is successfully completed.
-    virtual void OnDownloadSucceeded(const DriverEntry& download,
-                                     const base::FilePath& path) = 0;
+    virtual void OnDownloadSucceeded(const DriverEntry& download) = 0;
 
     // Called when any download is updated.
     virtual void OnDownloadUpdated(const DriverEntry& download) = 0;
@@ -62,6 +61,7 @@
   virtual void Start(
       const RequestParams& request_params,
       const std::string& guid,
+      const base::FilePath& file_path,
       const net::NetworkTrafficAnnotationTag& traffic_annotation) = 0;
 
   // Cancels an existing download, all data associated with this download should
diff --git a/components/download/internal/driver_entry.h b/components/download/internal/driver_entry.h
index 07e2c21..c7b4163 100644
--- a/components/download/internal/driver_entry.h
+++ b/components/download/internal/driver_entry.h
@@ -57,8 +57,9 @@
 
   // The physical file path for the download. It can be different from the
   // target file path requested while the file is downloading, as it may
-  // download to a temporary path.
-  base::FilePath temporary_physical_file_path;
+  // download to a temporary path. After completion, this would be set to the
+  // target file path.
+  base::FilePath current_file_path;
 
   // Time the download was marked as complete, base::Time() if the download is
   // not yet complete.
diff --git a/components/download/internal/file_monitor_impl.cc b/components/download/internal/file_monitor_impl.cc
index 3d706cd..57232f7 100644
--- a/components/download/internal/file_monitor_impl.cc
+++ b/components/download/internal/file_monitor_impl.cc
@@ -54,7 +54,7 @@
   }
 
   for (const DriverEntry& driver_entry : known_driver_entries) {
-    download_file_paths.insert(driver_entry.temporary_physical_file_path);
+    download_file_paths.insert(driver_entry.current_file_path);
   }
 
   file_thread_task_runner_->PostTask(
diff --git a/components/download/internal/file_monitor_unittest.cc b/components/download/internal/file_monitor_unittest.cc
index 268a4284..b7443df6 100644
--- a/components/download/internal/file_monitor_unittest.cc
+++ b/components/download/internal/file_monitor_unittest.cc
@@ -66,12 +66,11 @@
 
   DriverEntry driver_entry1;
   driver_entry1.guid = entry1.guid;
-  driver_entry1.temporary_physical_file_path = entry1.target_file_path;
+  driver_entry1.current_file_path = entry1.target_file_path;
 
   DriverEntry driver_entry2;
   driver_entry2.guid = base::GenerateGUID();
-  driver_entry2.temporary_physical_file_path =
-      CreateTemporaryFile(driver_entry2.guid);
+  driver_entry2.current_file_path = CreateTemporaryFile(driver_entry2.guid);
 
   base::FilePath temp_file1 = CreateTemporaryFile("temp1");
   base::FilePath temp_file2 = CreateTemporaryFile("temp2");
@@ -80,10 +79,8 @@
                                   bool t2) {
     EXPECT_EQ(e1, base::PathExists(entry1.target_file_path));
     EXPECT_EQ(e2, base::PathExists(entry2.target_file_path));
-    EXPECT_EQ(de1,
-              base::PathExists(driver_entry1.temporary_physical_file_path));
-    EXPECT_EQ(de2,
-              base::PathExists(driver_entry2.temporary_physical_file_path));
+    EXPECT_EQ(de1, base::PathExists(driver_entry1.current_file_path));
+    EXPECT_EQ(de2, base::PathExists(driver_entry2.current_file_path));
     EXPECT_EQ(t1, base::PathExists(temp_file1));
     EXPECT_EQ(t2, base::PathExists(temp_file2));
   };
diff --git a/components/download/internal/stats.cc b/components/download/internal/stats.cc
index f0448eb4..04222c6b 100644
--- a/components/download/internal/stats.cc
+++ b/components/download/internal/stats.cc
@@ -54,5 +54,9 @@
   // TODO(shaktisahu): Log |count|.
 }
 
+void LogFilePathsAreStrangelyDifferent() {
+  // TODO(shaktisahu): Log this occurrence.
+}
+
 }  // namespace stats
 }  // namespace download
diff --git a/components/download/internal/stats.h b/components/download/internal/stats.h
index db3d40b..768d507 100644
--- a/components/download/internal/stats.h
+++ b/components/download/internal/stats.h
@@ -127,6 +127,10 @@
                           int failed_cleanups,
                           int external_cleanups);
 
+// Logs the case where the final downloaded file path turned out different from
+// the file path set at the beginning.
+void LogFilePathsAreStrangelyDifferent();
+
 }  // namespace stats
 }  // namespace download
 
diff --git a/components/download/internal/test/test_download_driver.cc b/components/download/internal/test/test_download_driver.cc
index 440ed974..05d5a14f 100644
--- a/components/download/internal/test/test_download_driver.cc
+++ b/components/download/internal/test/test_download_driver.cc
@@ -44,11 +44,10 @@
   }
 }
 
-void TestDownloadDriver::NotifyDownloadSucceeded(const DriverEntry& entry,
-                                                 const base::FilePath& path) {
+void TestDownloadDriver::NotifyDownloadSucceeded(const DriverEntry& entry) {
   if (client_) {
     entries_[entry.guid] = entry;
-    client_->OnDownloadSucceeded(entry, path);
+    client_->OnDownloadSucceeded(entry);
   }
 }
 
@@ -64,9 +63,11 @@
 void TestDownloadDriver::Start(
     const RequestParams& params,
     const std::string& guid,
+    const base::FilePath& file_path,
     const net::NetworkTrafficAnnotationTag& traffic_annotation) {
   DriverEntry entry;
   entry.guid = guid;
+  entry.current_file_path = file_path;
   entry.state = DriverEntry::State::IN_PROGRESS;
   entry.paused = false;
   entry.bytes_downloaded = 0;
diff --git a/components/download/internal/test/test_download_driver.h b/components/download/internal/test/test_download_driver.h
index 95d9b55..5711d95 100644
--- a/components/download/internal/test/test_download_driver.h
+++ b/components/download/internal/test/test_download_driver.h
@@ -31,8 +31,7 @@
   // Simulates download events from content layer.
   void NotifyDownloadUpdate(const DriverEntry& entry);
   void NotifyDownloadFailed(const DriverEntry& entry, int reason);
-  void NotifyDownloadSucceeded(const DriverEntry& entry,
-                               const base::FilePath& path);
+  void NotifyDownloadSucceeded(const DriverEntry& entry);
 
   // DownloadDriver implementation.
   void Initialize(DownloadDriver::Client* client) override;
@@ -40,6 +39,7 @@
   void Start(
       const RequestParams& params,
       const std::string& guid,
+      const base::FilePath& file_path,
       const net::NetworkTrafficAnnotationTag& traffic_annotation) override;
   void Remove(const std::string& guid) override;
   void Pause(const std::string& guid) override;
diff --git a/components/exo/pointer.cc b/components/exo/pointer.cc
index ec34b5a4..78f15c4 100644
--- a/components/exo/pointer.cc
+++ b/components/exo/pointer.cc
@@ -41,10 +41,6 @@
 // for now. See crbug.com/708378.
 const float kLargeCursorScale = 2.8f;
 
-// Scale at which cursor snapshot is captured. The resulting bitmap is scaled on
-// displays whose DSF does not match this scale.
-const float kCursorCaptureScale = 2.0f;
-
 const double kLocatedEventEpsilonSquared = 1.0 / (2000.0 * 2000.0);
 
 // Synthesized events typically lack floating point precision so to avoid
@@ -65,6 +61,16 @@
   return offset.LengthSquared() < (2 * kLocatedEventEpsilonSquared);
 }
 
+float GetCaptureScale() {
+  float capture_scale = 1.0f;
+  for (const auto& display : display::Screen::GetScreen()->GetAllDisplays()) {
+    const auto& info = WMHelper::GetInstance()->GetDisplayInfo(display.id());
+    if (info.device_scale_factor() > capture_scale)
+      capture_scale = info.device_scale_factor();
+  }
+  return capture_scale;
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -73,6 +79,7 @@
 Pointer::Pointer(PointerDelegate* delegate)
     : delegate_(delegate),
       cursor_(ui::CursorType::kNull),
+      capture_scale_(GetCaptureScale()),
       cursor_capture_source_id_(base::UnguessableToken::Create()),
       cursor_capture_weak_ptr_factory_(this) {
   auto* helper = WMHelper::GetInstance();
@@ -270,6 +277,7 @@
 
 void Pointer::OnDisplayConfigurationChanged() {
   UpdatePointerSurface(surface_);
+  capture_scale_ = GetCaptureScale();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -344,7 +352,7 @@
   display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
   auto* helper = WMHelper::GetInstance();
   float scale = helper->GetDisplayInfo(display.id()).GetEffectiveUIScale() *
-                kCursorCaptureScale / display.device_scale_factor();
+                capture_scale_ / display.device_scale_factor();
   surface_->window()->SetTransform(gfx::GetScaleTransform(gfx::Point(), scale));
 
   std::unique_ptr<cc::CopyOutputRequest> request =
@@ -379,13 +387,12 @@
     cursor_ = ui::CursorType::kNone;
   } else {
     SkBitmap bitmap = cursor_bitmap_;
-    gfx::Point hotspot =
-        gfx::ScaleToFlooredPoint(hotspot_, kCursorCaptureScale);
+    gfx::Point hotspot = gfx::ScaleToFlooredPoint(hotspot_, capture_scale_);
 
     auto* helper = WMHelper::GetInstance();
     const display::Display& display = helper->GetCursorDisplay();
     float scale = helper->GetDisplayInfo(display.id()).device_scale_factor() /
-                  kCursorCaptureScale;
+                  capture_scale_;
 
     if (helper->GetCursorSet() == ui::CURSOR_SET_LARGE)
       scale *= kLargeCursorScale;
diff --git a/components/exo/pointer.h b/components/exo/pointer.h
index 571743c..74c5b97 100644
--- a/components/exo/pointer.h
+++ b/components/exo/pointer.h
@@ -111,6 +111,10 @@
   // The current cursor.
   ui::Cursor cursor_;
 
+  // Scale at which cursor snapshot is captured. The resulting bitmap is scaled
+  // on displays whose DSF does not match this scale.
+  float capture_scale_;
+
   // Source used for cursor capture copy output requests.
   const base::UnguessableToken cursor_capture_source_id_;
 
diff --git a/components/metrics_services_manager/metrics_services_manager.cc b/components/metrics_services_manager/metrics_services_manager.cc
index 0bd70581..ef4c993 100644
--- a/components/metrics_services_manager/metrics_services_manager.cc
+++ b/components/metrics_services_manager/metrics_services_manager.cc
@@ -137,8 +137,7 @@
   bool sync_enabled =
       client_->IsMetricsReportingForceEnabled() ||
       metrics_service_client_->IsHistorySyncEnabledOnAllProfiles();
-  bool is_incognito = client_->IsIncognitoSessionActive();
-  if (may_record_ && sync_enabled & !is_incognito) {
+  if (may_record_ && sync_enabled) {
     ukm->EnableRecording();
     if (may_upload_)
       ukm->EnableReporting();
diff --git a/components/metrics_services_manager/metrics_services_manager_client.h b/components/metrics_services_manager/metrics_services_manager_client.h
index 351fc67..5d4e50dc 100644
--- a/components/metrics_services_manager/metrics_services_manager_client.h
+++ b/components/metrics_services_manager/metrics_services_manager_client.h
@@ -51,9 +51,6 @@
   // Returns whether metrics reporting is enabled.
   virtual bool IsMetricsReportingEnabled() = 0;
 
-  // Returns whether there are any Incognito browsers/tabs open.
-  virtual bool IsIncognitoSessionActive() = 0;
-
   // Update the running state of metrics services managed by the embedder, for
   // example, crash reporting.
   virtual void UpdateRunningServices(bool may_record, bool may_upload) {}
diff --git a/components/offline_pages/core/DEPS b/components/offline_pages/core/DEPS
index 5d8bc16..5ef8eb9f 100644
--- a/components/offline_pages/core/DEPS
+++ b/components/offline_pages/core/DEPS
@@ -1,4 +1,8 @@
 include_rules = [
   "+components/keyed_service",
+  "+components/gcm_driver",
+  "+components/ntp_snippets",
+  "+components/version_info",
+  "+net",
   "+sql",
 ]
diff --git a/components/offline_pages/core/prefetch/DEPS b/components/offline_pages/core/prefetch/DEPS
index 4a85e6c..9a26362 100644
--- a/components/offline_pages/core/prefetch/DEPS
+++ b/components/offline_pages/core/prefetch/DEPS
@@ -1,7 +1,3 @@
 include_rules = [
   "+google_apis",
-  "+components/gcm_driver",
-  "+components/ntp_snippets",
-  "+components/version_info",
-  "+net",
 ]
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc
index 0056537..dd0eaf0 100644
--- a/components/omnibox/browser/autocomplete_match.cc
+++ b/components/omnibox/browser/autocomplete_match.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
@@ -15,9 +16,11 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
+#include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/browser/suggestion_answer.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
@@ -485,6 +488,69 @@
   return stripped_destination_url;
 }
 
+// static
+base::string16 AutocompleteMatch::FormatUrlForSuggestionDisplay(
+    const GURL& url,
+    bool trim_scheme,
+    size_t* offset_for_adjustment) {
+  std::vector<size_t> offsets;
+  if (offset_for_adjustment)
+    offsets.push_back(*offset_for_adjustment);
+  base::string16 result =
+      FormatUrlForSuggestionDisplayWithOffsets(url, trim_scheme, &offsets);
+  if (offset_for_adjustment)
+    *offset_for_adjustment = offsets[0];
+  return result;
+}
+
+// static
+base::string16 AutocompleteMatch::FormatUrlForSuggestionDisplayWithOffsets(
+    const GURL& url,
+    bool trim_scheme,
+    std::vector<size_t>* offsets_for_adjustment) {
+  base::OffsetAdjuster::Adjustments adjustments;
+  const base::string16& format_url_return_value =
+      FormatUrlForSuggestionDisplayWithAdjustments(url, trim_scheme,
+                                                   &adjustments);
+  base::OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment);
+  if (offsets_for_adjustment) {
+    std::for_each(
+        offsets_for_adjustment->begin(), offsets_for_adjustment->end(),
+        base::LimitOffset<std::string>(format_url_return_value.length()));
+  }
+  return format_url_return_value;
+}
+
+// static
+base::string16 AutocompleteMatch::FormatUrlForSuggestionDisplayWithAdjustments(
+    const GURL& url,
+    bool trim_scheme,
+    base::OffsetAdjuster::Adjustments* adjustments) {
+  const url_formatter::FormatUrlTypes format_types =
+      url_formatter::kFormatUrlOmitAll &
+      ~(trim_scheme ? 0 : url_formatter::kFormatUrlOmitHTTP);
+  base::string16 result = url_formatter::FormatUrlWithAdjustments(
+      url, format_types, net::UnescapeRule::SPACES, nullptr, nullptr,
+      adjustments);
+
+  // Also trim HTTPS if experiment is enabled. Note this intentionally has
+  // no effect on view-source URLs.
+  if (trim_scheme && base::FeatureList::IsEnabled(
+                         omnibox::kUIExperimentHideSuggestionUrlScheme)) {
+    // TODO(tommycli): If this becomes enabled by default, investigate
+    // folding this logic into url_formatter::FormatUrlWithAdjustments.
+    if (url.SchemeIs(url::kHttpsScheme)) {
+      const size_t kHTTPSSize =
+          strlen(url::kHttpsScheme) + strlen(url::kStandardSchemeSeparator);
+      result = result.substr(kHTTPSSize);
+      adjustments->insert(adjustments->begin(),
+                          base::OffsetAdjuster::Adjustment(0, kHTTPSSize, 0));
+    }
+  }
+
+  return result;
+}
+
 void AutocompleteMatch::ComputeStrippedDestinationURL(
     const AutocompleteInput& input,
     TemplateURLService* template_url_service) {
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h
index 9c0a132..b8659cd 100644
--- a/components/omnibox/browser/autocomplete_match.h
+++ b/components/omnibox/browser/autocomplete_match.h
@@ -12,6 +12,7 @@
 #include <string>
 #include <vector>
 
+#include "base/strings/utf_offset_string_conversions.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/search_engines/template_url.h"
@@ -207,6 +208,24 @@
                                  TemplateURLService* template_url_service,
                                  const base::string16& keyword);
 
+  // These are convenience functions for formatting a URL into an abridged form
+  // for display within the Omnibox suggestions dropdown.
+  //
+  // The results are explicitly for non-security surfaces. Do not use the
+  // results for anything other than the Omnibox dropdown.
+  static base::string16 FormatUrlForSuggestionDisplay(
+      const GURL& url,
+      bool trim_scheme,
+      size_t* offset_for_adjustment);
+  static base::string16 FormatUrlForSuggestionDisplayWithOffsets(
+      const GURL& url,
+      bool trim_scheme,
+      std::vector<size_t>* offsets_for_adjustment);
+  static base::string16 FormatUrlForSuggestionDisplayWithAdjustments(
+      const GURL& url,
+      bool trim_scheme,
+      base::OffsetAdjuster::Adjustments* adjustments);
+
   // Computes the stripped destination URL (via GURLToStrippedGURL()) and
   // stores the result in |stripped_destination_url|.  |input| is used for the
   // same purpose as in GURLToStrippedGURL().
diff --git a/components/omnibox/browser/autocomplete_match_unittest.cc b/components/omnibox/browser/autocomplete_match_unittest.cc
index 8b2d5a3..dc0eb464 100644
--- a/components/omnibox/browser/autocomplete_match_unittest.cc
+++ b/components/omnibox/browser/autocomplete_match_unittest.cc
@@ -8,8 +8,11 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/browser/test_scheme_classifier.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
 
 TEST(AutocompleteMatchTest, MoreRelevant) {
   struct RelevantCases {
@@ -108,6 +111,61 @@
                   "0,2," "1,0," "5,7," "6,1," "17,0"))));
 }
 
+TEST(AutocompleteMatchTest, FormatUrlForSuggestionDisplay) {
+  struct FormatUrlTestData {
+    const std::string url;
+    bool trim_scheme;
+    size_t offset_for_adjustment;
+    const std::string expected_result;
+    size_t expected_adjusted_offset;
+
+    void Validate() {
+      SCOPED_TRACE(testing::Message()
+                   << " url= " << url << " trim_scheme=" << trim_scheme
+                   << " offset_for_adjustment=" << offset_for_adjustment
+                   << " expected_result=" << expected_result
+                   << " expected_adjusted_offset=" << expected_adjusted_offset);
+
+      size_t offset_result = offset_for_adjustment;
+      base::string16 result = AutocompleteMatch::FormatUrlForSuggestionDisplay(
+          GURL(url), trim_scheme, &offset_result);
+
+      EXPECT_EQ(expected_result, base::UTF16ToASCII(result));
+      EXPECT_EQ(expected_adjusted_offset, offset_result);
+    };
+  };
+
+  FormatUrlTestData normal_cases[] = {
+      {"http://google.com", true, 9, "google.com", 2},
+      {"https://google.com", true, 9, "https://google.com", 9},
+      {"http://google.com", false, 9, "http://google.com", 9},
+      {"https://google.com", false, 9, "https://google.com", 9},
+  };
+  for (size_t i = 0; i < arraysize(normal_cases); ++i) {
+    normal_cases[i].Validate();
+  }
+
+  std::unique_ptr<base::test::ScopedFeatureList> feature_list(
+      new base::test::ScopedFeatureList);
+  feature_list->InitAndEnableFeature(
+      omnibox::kUIExperimentHideSuggestionUrlScheme);
+
+  FormatUrlTestData omit_scheme_cases[] = {
+      {"http://google.com", true, 9, "google.com", 2},
+      {"https://google.com", true, 9, "google.com", 1},
+      {"https://username:password@google.com", true, 9, "google.com",
+       base::string16::npos},
+      {"https://username:password@google.com", true, 29, "google.com", 3},
+      {"http://google.com", false, 9, "http://google.com", 9},
+      {"https://google.com", false, 9, "https://google.com", 9},
+      {"http://username:password@google.com", false, 9, "http://google.com",
+       base::string16::npos},
+  };
+  for (size_t i = 0; i < arraysize(omit_scheme_cases); ++i) {
+    omit_scheme_cases[i].Validate();
+  }
+}
+
 TEST(AutocompleteMatchTest, SupportsDeletion) {
   // A non-deletable match with no duplicates.
   AutocompleteMatch m(NULL, 0, false,
diff --git a/components/omnibox/browser/clipboard_url_provider.cc b/components/omnibox/browser/clipboard_url_provider.cc
index f2f12d3..b3729328 100644
--- a/components/omnibox/browser/clipboard_url_provider.cc
+++ b/components/omnibox/browser/clipboard_url_provider.cc
@@ -86,9 +86,8 @@
   // Add the clipboard match. The relevance is 800 to beat ZeroSuggest results.
   AutocompleteMatch match(this, 800, false, AutocompleteMatchType::CLIPBOARD);
   match.destination_url = url;
-  match.contents.assign(url_formatter::FormatUrl(
-      url, url_formatter::kFormatUrlOmitAll, net::UnescapeRule::SPACES,
-      nullptr, nullptr, nullptr));
+  match.contents.assign(AutocompleteMatch::FormatUrlForSuggestionDisplay(
+      url, true /* trim_scheme */, nullptr));
   AutocompleteMatch::ClassifyLocationInString(
       base::string16::npos, 0, match.contents.length(),
       ACMatchClassification::URL, &match.contents_class);
diff --git a/components/omnibox/browser/history_quick_provider.cc b/components/omnibox/browser/history_quick_provider.cc
index ea045ad..f8b9a76 100644
--- a/components/omnibox/browser/history_quick_provider.cc
+++ b/components/omnibox/browser/history_quick_provider.cc
@@ -200,14 +200,10 @@
       autocomplete_input_.text(), FixupUserInput(autocomplete_input_).second,
       false, base::UTF8ToUTF16(info.url().spec()));
 
-  // Format the URL autocomplete presentation.
-  const url_formatter::FormatUrlTypes format_types =
-      url_formatter::kFormatUrlOmitAll &
-      ~(!history_match.match_in_scheme ? 0 : url_formatter::kFormatUrlOmitHTTP);
   base::OffsetAdjuster::Adjustments adjustments;
-  match.contents = url_formatter::FormatUrlWithAdjustments(
-      info.url(), format_types, net::UnescapeRule::SPACES, nullptr,
-      nullptr, &adjustments);
+  match.contents =
+      AutocompleteMatch::FormatUrlForSuggestionDisplayWithAdjustments(
+          info.url(), !history_match.match_in_scheme, &adjustments);
   match.fill_into_edit =
       AutocompleteInput::FormattedStringWithEquivalentMeaning(
           info.url(), match.contents, client()->GetSchemeClassifier());
diff --git a/components/omnibox/browser/history_url_provider.cc b/components/omnibox/browser/history_url_provider.cc
index ed391c9..d9f6b7e 100644
--- a/components/omnibox/browser/history_url_provider.cc
+++ b/components/omnibox/browser/history_url_provider.cc
@@ -571,10 +571,9 @@
     // Trim off "http://" if the user didn't type it.
     DCHECK(!trim_http ||
            !AutocompleteInput::HasHTTPScheme(input.text()));
-    base::string16 display_string(url_formatter::FormatUrl(
-        destination_url,
-        url_formatter::kFormatUrlOmitAll & ~url_formatter::kFormatUrlOmitHTTP,
-        net::UnescapeRule::SPACES, nullptr, nullptr, nullptr));
+    base::string16 display_string(
+        AutocompleteMatch::FormatUrlForSuggestionDisplay(
+            destination_url, false /* trim_scheme */, nullptr));
     const size_t offset = trim_http ? TrimHttpPrefix(&display_string) : 0;
     match.fill_into_edit =
         AutocompleteInput::FormattedStringWithEquivalentMeaning(
@@ -1174,9 +1173,9 @@
        (inline_autocomplete_offset >= match.fill_into_edit.length()));
 
   size_t match_start = history_match.input_location;
-  match.contents = url_formatter::FormatUrl(info.url(), format_types,
-                                            net::UnescapeRule::SPACES, nullptr,
-                                            nullptr, &match_start);
+  match.contents = AutocompleteMatch::FormatUrlForSuggestionDisplay(
+      info.url(), params.trim_http && !history_match.match_in_scheme,
+      &match_start);
   if ((match_start != base::string16::npos) && autocomplete_offset_valid &&
       (inline_autocomplete_offset != match_start)) {
     DCHECK(inline_autocomplete_offset > match_start);
diff --git a/components/omnibox/browser/titled_url_match_utils.cc b/components/omnibox/browser/titled_url_match_utils.cc
index f60d0d5..8c60630 100644
--- a/components/omnibox/browser/titled_url_match_utils.cc
+++ b/components/omnibox/browser/titled_url_match_utils.cc
@@ -85,10 +85,8 @@
   // |offsets|, compute how everything is transformed, then remove it from the
   // end.
   offsets.push_back(inline_autocomplete_offset);
-  match.contents = url_formatter::FormatUrlWithOffsets(
-      url, url_formatter::kFormatUrlOmitAll &
-               ~(trim_http ? 0 : url_formatter::kFormatUrlOmitHTTP),
-      net::UnescapeRule::SPACES, nullptr, nullptr, &offsets);
+  match.contents = AutocompleteMatch::FormatUrlForSuggestionDisplayWithOffsets(
+      url, trim_http, &offsets);
   inline_autocomplete_offset = offsets.back();
   offsets.pop_back();
   TitledUrlMatch::MatchPositions new_url_match_positions =
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index e900253..11fe0a0e 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -375,9 +375,8 @@
   match.destination_url = navigation.url();
 
   // Zero suggest results should always omit protocols and never appear bold.
-  match.contents = url_formatter::FormatUrl(
-      navigation.url(), url_formatter::kFormatUrlOmitAll,
-      net::UnescapeRule::SPACES, nullptr, nullptr, nullptr);
+  match.contents = AutocompleteMatch::FormatUrlForSuggestionDisplay(
+      navigation.url(), true /* trim_scheme */, nullptr);
   match.fill_into_edit +=
       AutocompleteInput::FormattedStringWithEquivalentMeaning(
           navigation.url(), match.contents, client()->GetSchemeClassifier());
diff --git a/components/proxy_config/ios/proxy_service_factory.cc b/components/proxy_config/ios/proxy_service_factory.cc
index 600191a..e0e0775a 100644
--- a/components/proxy_config/ios/proxy_service_factory.cc
+++ b/components/proxy_config/ios/proxy_service_factory.cc
@@ -17,8 +17,7 @@
 ProxyServiceFactory::CreateProxyConfigService(PrefProxyConfigTracker* tracker) {
   std::unique_ptr<net::ProxyConfigService> base_service(
       net::ProxyService::CreateSystemProxyConfigService(
-          web::WebThread::GetTaskRunnerForThread(web::WebThread::IO),
-          web::WebThread::GetTaskRunnerForThread(web::WebThread::FILE)));
+          web::WebThread::GetTaskRunnerForThread(web::WebThread::IO)));
   return tracker->CreateTrackingProxyConfigService(std::move(base_service));
 }
 
diff --git a/components/safe_browsing/renderer/threat_dom_details.cc b/components/safe_browsing/renderer/threat_dom_details.cc
index 0a36545b..c3d712b 100644
--- a/components/safe_browsing/renderer/threat_dom_details.cc
+++ b/components/safe_browsing/renderer/threat_dom_details.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <map>
+#include <string>
 #include <unordered_set>
 
 #include "base/compiler_specific.h"
@@ -108,7 +109,7 @@
     const blink::WebNode& element,
     const safe_browsing::ElementToNodeMap& element_to_node_map,
     std::vector<SafeBrowsingHostMsg_ThreatDOMDetails_Node>* resources) {
-  DCHECK(element_to_node_map.count(element) > 0);
+  DCHECK_GT(element_to_node_map.count(element), 0u);
   size_t resource_index = element_to_node_map.at(element);
   return &(resources->at(resource_index));
 }
@@ -280,7 +281,7 @@
 
 void ThreatDOMDetails::ExtractResources(
     std::vector<SafeBrowsingHostMsg_ThreatDOMDetails_Node>* resources) {
-  blink::WebFrame* frame = render_frame()->GetWebFrame();
+  blink::WebLocalFrame* frame = render_frame()->GetWebFrame();
   if (!frame)
     return;
   SafeBrowsingHostMsg_ThreatDOMDetails_Node details_node;
diff --git a/components/safe_browsing_db/BUILD.gn b/components/safe_browsing_db/BUILD.gn
index 28fd0e30..8734f115d 100644
--- a/components/safe_browsing_db/BUILD.gn
+++ b/components/safe_browsing_db/BUILD.gn
@@ -518,6 +518,8 @@
     ":whitelist_checker_client",
     "//base:base",
     "//base/test:test_support",
+    "//content/public/browser",
+    "//content/test:test_support",
     "//testing/gmock:gmock",
     "//testing/gtest:gtest",
   ]
diff --git a/components/safe_browsing_db/database_manager.cc b/components/safe_browsing_db/database_manager.cc
index 0155efc..6633c417 100644
--- a/components/safe_browsing_db/database_manager.cc
+++ b/components/safe_browsing_db/database_manager.cc
@@ -15,7 +15,11 @@
 
 namespace safe_browsing {
 
-SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager() : enabled_(false) {}
+SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager()
+    : base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager>(
+          content::BrowserThread::GetTaskRunnerForThread(
+              content::BrowserThread::IO)),
+      enabled_(false) {}
 
 SafeBrowsingDatabaseManager::~SafeBrowsingDatabaseManager() {
   DCHECK(!v4_get_hash_protocol_manager_);
diff --git a/components/safe_browsing_db/database_manager.h b/components/safe_browsing_db/database_manager.h
index c6f655e..d61671b 100644
--- a/components/safe_browsing_db/database_manager.h
+++ b/components/safe_browsing_db/database_manager.h
@@ -15,7 +15,7 @@
 
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_delete_on_sequence.h"
 #include "components/safe_browsing_db/hit_report.h"
 #include "components/safe_browsing_db/util.h"
 #include "content/public/common/resource_type.h"
@@ -40,7 +40,7 @@
 
 // Base class to either the locally-managed or a remotely-managed database.
 class SafeBrowsingDatabaseManager
-    : public base::RefCountedThreadSafe<SafeBrowsingDatabaseManager> {
+    : public base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager> {
  public:
   // Callers requesting a result should derive from this class.
   // The destructor should call db_manager->CancelCheck(client) if a
@@ -259,7 +259,8 @@
 
   virtual ~SafeBrowsingDatabaseManager();
 
-  friend class base::RefCountedThreadSafe<SafeBrowsingDatabaseManager>;
+  friend class base::RefCountedDeleteOnSequence<SafeBrowsingDatabaseManager>;
+  friend class base::DeleteHelper<SafeBrowsingDatabaseManager>;
 
   FRIEND_TEST_ALL_PREFIXES(SafeBrowsingDatabaseManagerTest,
                            CheckApiBlacklistUrlPrefixes);
diff --git a/components/safe_browsing_db/database_manager_unittest.cc b/components/safe_browsing_db/database_manager_unittest.cc
index 5180687..5669978 100644
--- a/components/safe_browsing_db/database_manager_unittest.cc
+++ b/components/safe_browsing_db/database_manager_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/safe_browsing_db/test_database_manager.h"
 #include "components/safe_browsing_db/v4_protocol_manager_util.h"
@@ -68,8 +69,9 @@
   }
 
   void TearDown() override {
-    base::RunLoop().RunUntilIdle();
     db_manager_->StopOnIOThread(false);
+    db_manager_ = nullptr;
+    base::RunLoop().RunUntilIdle();
   }
 
   std::string GetStockV4GetHashResponse() {
diff --git a/components/safe_browsing_db/v4_database.cc b/components/safe_browsing_db/v4_database.cc
index 9164951..48eaabe 100644
--- a/components/safe_browsing_db/v4_database.cc
+++ b/components/safe_browsing_db/v4_database.cc
@@ -9,6 +9,7 @@
 #include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/task_runner_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/safe_browsing_db/v4_database.h"
 #include "content/public/browser/browser_thread.h"
@@ -30,6 +31,19 @@
 base::LazyInstance<std::unique_ptr<V4StoreFactory>>::Leaky g_store_factory =
     LAZY_INSTANCE_INITIALIZER;
 
+// Verifies the checksums on a collection of stores.
+// Returns the IDs of stores whose checksums failed to verify.
+std::vector<ListIdentifier> VerifyChecksums(
+    std::vector<std::pair<ListIdentifier, V4Store*>> stores) {
+  std::vector<ListIdentifier> stores_to_reset;
+  for (const auto& store_map_iter : stores) {
+    if (!store_map_iter.second->VerifyChecksum()) {
+      stores_to_reset.push_back(store_map_iter.first);
+    }
+  }
+  return stores_to_reset;
+}
+
 }  // namespace
 
 std::unique_ptr<V4Database> V4DatabaseFactory::Create(
@@ -254,28 +268,19 @@
 void V4Database::VerifyChecksum(
     DatabaseReadyForUpdatesCallback db_ready_for_updates_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  // TODO(vakh): Consider using PostTaskAndReply instead.
-  const scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner =
-      base::ThreadTaskRunnerHandle::Get();
-  db_task_runner_->PostTask(
-      FROM_HERE,
-      base::Bind(&V4Database::VerifyChecksumOnTaskRunner,
-                 weak_factory_on_io_.GetWeakPtr(), callback_task_runner,
-                 db_ready_for_updates_callback));
-}
 
-void V4Database::VerifyChecksumOnTaskRunner(
-    const scoped_refptr<base::SingleThreadTaskRunner>& callback_task_runner,
-    DatabaseReadyForUpdatesCallback db_ready_for_updates_callback) {
-  std::vector<ListIdentifier> stores_to_reset;
-  for (const auto& store_map_iter : *store_map_) {
-    if (!store_map_iter.second->VerifyChecksum()) {
-      stores_to_reset.push_back(store_map_iter.first);
-    }
+  // Make a threadsafe copy of store_map_ w/raw pointers that we can hand to
+  // the DB thread. The V4Stores ptrs are guaranteed to be valid because their
+  // deletion would be sequenced on the DB thread, after this posted task is
+  // serviced.
+  std::vector<std::pair<ListIdentifier, V4Store*>> stores;
+  for (const auto& next_store : *store_map_) {
+    stores.push_back(std::make_pair(next_store.first, next_store.second.get()));
   }
 
-  callback_task_runner->PostTask(
-      FROM_HERE, base::Bind(db_ready_for_updates_callback, stores_to_reset));
+  base::PostTaskAndReplyWithResult(db_task_runner_.get(), FROM_HERE,
+                                   base::Bind(&VerifyChecksums, stores),
+                                   db_ready_for_updates_callback);
 }
 
 bool V4Database::IsStoreAvailable(const ListIdentifier& identifier) const {
diff --git a/components/safe_browsing_db/v4_database.h b/components/safe_browsing_db/v4_database.h
index fd8b3af..0146830f0 100644
--- a/components/safe_browsing_db/v4_database.h
+++ b/components/safe_browsing_db/v4_database.h
@@ -167,7 +167,11 @@
   V4Database(const scoped_refptr<base::SequencedTaskRunner>& db_task_runner,
              std::unique_ptr<StoreMap> store_map);
 
-  // Map of ListIdentifier to the V4Store.
+  // The collection of V4Stores, keyed by ListIdentifier.
+  // The map itself lives on the V4Database's parent thread, but its V4Store
+  // objects live on the db_task_runner_thread.
+  // TODO(vakh): Consider writing a container object which encapsulates or
+  // harmonizes thread affinity for the associative container and the data.
   const std::unique_ptr<StoreMap> store_map_;
 
  private:
diff --git a/components/safe_browsing_db/v4_local_database_manager.h b/components/safe_browsing_db/v4_local_database_manager.h
index b82d9a8..326bce5 100644
--- a/components/safe_browsing_db/v4_local_database_manager.h
+++ b/components/safe_browsing_db/v4_local_database_manager.h
@@ -331,7 +331,6 @@
 
   base::WeakPtrFactory<V4LocalDatabaseManager> weak_factory_;
 
-  friend class base::RefCountedThreadSafe<V4LocalDatabaseManager>;
   DISALLOW_COPY_AND_ASSIGN(V4LocalDatabaseManager);
 };  // class V4LocalDatabaseManager
 
diff --git a/components/safe_browsing_db/whitelist_checker_client_unittest.cc b/components/safe_browsing_db/whitelist_checker_client_unittest.cc
index 8835a96c..d437e35 100644
--- a/components/safe_browsing_db/whitelist_checker_client_unittest.cc
+++ b/components/safe_browsing_db/whitelist_checker_client_unittest.cc
@@ -10,6 +10,8 @@
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/safe_browsing_db/test_database_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_browser_thread.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -39,17 +41,22 @@
 
 class WhitelistCheckerClientTest : public testing::Test {
  public:
-  WhitelistCheckerClientTest() : target_url_("http://foo.bar"){};
+  WhitelistCheckerClientTest() : target_url_("http://foo.bar") {}
 
   void SetUp() override {
     database_manager_ = new MockSafeBrowsingDatabaseManager;
     task_runner_ = new base::TestMockTimeTaskRunner(base::Time::Now(),
                                                     base::TimeTicks::Now());
     message_loop_.reset(new base::MessageLoop);
+    io_thread_ = base::MakeUnique<content::TestBrowserThread>(
+        content::BrowserThread::IO, base::MessageLoop::current());
     message_loop_->SetTaskRunner(task_runner_);
   }
 
   void TearDown() override {
+    database_manager_ = nullptr;
+    base::RunLoop().RunUntilIdle();
+
     // Verify no callback is remaining.
     // TODO(nparker): We should somehow EXPECT that no entry is remaining,
     // rather than just invoking it.
@@ -60,6 +67,9 @@
   GURL target_url_;
   scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_;
 
+  // Needed for |database_manager_| teardown tasks.
+  std::unique_ptr<content::TestBrowserThread> io_thread_;
+
   std::unique_ptr<base::MessageLoop> message_loop_;
   scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
 };
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
index 38c8ffe..0e307f9 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
@@ -193,7 +193,17 @@
 
   void TearDown() override {
     client_.reset();
+
+    // RunUntilIdle() must be called multiple times to flush any outstanding
+    // cross-thread interactions.
+    // TODO(csharrison): Clean up test teardown logic.
     RunUntilIdle();
+    RunUntilIdle();
+
+    // RunUntilIdle() called once more, to delete the database on the IO thread.
+    fake_safe_browsing_database_ = nullptr;
+    RunUntilIdle();
+
     content::RenderViewHostTestHarness::TearDown();
   }
 
@@ -316,8 +326,8 @@
   void UsePassThroughThrottle() { fake_safe_browsing_database_ = nullptr; }
 
   void RunUntilIdle() {
-    test_io_task_runner_->RunUntilIdle();
     base::RunLoop().RunUntilIdle();
+    test_io_task_runner_->RunUntilIdle();
   }
 
   content::NavigationSimulator* navigation_simulator() {
diff --git a/components/test/data/web_database/version_72.sql b/components/test/data/web_database/version_72.sql
new file mode 100644
index 0000000..f1904a76
--- /dev/null
+++ b/components/test/data/web_database/version_72.sql
@@ -0,0 +1,32 @@
+PRAGMA foreign_keys=OFF;
+BEGIN TRANSACTION;
+CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
+INSERT INTO "meta" VALUES('mmap_status','-1');
+INSERT INTO "meta" VALUES('version','72');
+INSERT INTO "meta" VALUES('last_compatible_version','72');
+INSERT INTO "meta" VALUES('Builtin Keyword Version','98');
+CREATE TABLE token_service (service VARCHAR PRIMARY KEY NOT NULL,encrypted_token BLOB);
+CREATE TABLE keywords (id INTEGER PRIMARY KEY,short_name VARCHAR NOT NULL,keyword VARCHAR NOT NULL,favicon_url VARCHAR NOT NULL,url VARCHAR NOT NULL,safe_for_autoreplace INTEGER,originating_url VARCHAR,date_created INTEGER DEFAULT 0,usage_count INTEGER DEFAULT 0,input_encodings VARCHAR,suggest_url VARCHAR,prepopulate_id INTEGER DEFAULT 0,created_by_policy INTEGER DEFAULT 0,instant_url VARCHAR,last_modified INTEGER DEFAULT 0,sync_guid VARCHAR,alternate_urls VARCHAR,search_terms_replacement_key VARCHAR,image_url VARCHAR,search_url_post_params VARCHAR,suggest_url_post_params VARCHAR,instant_url_post_params VARCHAR,image_url_post_params VARCHAR,new_tab_url VARCHAR, last_visited INTEGER DEFAULT 0);
+INSERT INTO "keywords" VALUES(2,'Google','google.com','http://www.google.com/favicon.ico','{google:baseURL}search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:iOSSearchLanguage}{google:searchClient}{google:sourceId}{google:instantExtendedEnabledParameter}{google:contextualSearchVersion}ie={inputEncoding}',1,'',0,0,'UTF-8','{google:baseSuggestURL}search?{google:searchFieldtrialParameter}client={google:suggestClient}&gs_ri={google:suggestRid}&xssi=t&q={searchTerms}&{google:inputType}{google:cursorPosition}{google:currentPageUrl}{google:pageClassification}{google:searchVersion}{google:sessionToken}{google:prefetchQuery}sugkey={google:suggestAPIKeyParameter}',1,0,'{google:baseURL}webhp?sourceid=chrome-instant&{google:RLZ}{google:forceInstantResults}{google:instantExtendedEnabledParameter}ie={inputEncoding}',0,'746c326c-775a-4149-9777-6ea8d5cba42e','["{google:baseURL}#q={searchTerms}","{google:baseURL}search#q={searchTerms}","{google:baseURL}webhp#q={searchTerms}","{google:baseURL}s#q={searchTerms}","{google:baseURL}s?q={searchTerms}"]','espv','{google:baseURL}searchbyimage/upload','','','','encoded_image={google:imageThumbnail},image_url={google:imageURL},sbisrc={google:imageSearchSource},original_width={google:imageOriginalWidth},original_height={google:imageOriginalHeight}','{google:baseURL}_/chrome/newtab?{google:RLZ}{google:instantExtendedEnabledParameter}ie={inputEncoding}',0);
+INSERT INTO "keywords" VALUES(3,'Bing','bing.com','https://www.bing.com/s/a/bing_p.ico','https://www.bing.com/search?q={searchTerms}&PC=U316&FORM=CHROMN',1,'',0,0,'UTF-8','https://www.bing.com/osjson.aspx?query={searchTerms}&language={language}&PC=U316',3,0,'',0,'7563284f-dade-4e03-b164-17a6122ae635','[]','','https://www.bing.com/images/detail/search?iss=sbi&FORM=CHROMI#enterInsights','','','','imgurl={google:imageURL}','https://www.bing.com/chrome/newtab',0);
+INSERT INTO "keywords" VALUES(4,'Yahoo!','yahoo.com','https://search.yahoo.com/favicon.ico','https://search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}',1,'',0,0,'UTF-8','https://search.yahoo.com/sugg/chrome?output=fxjson&appid=crmas&command={searchTerms}',2,0,'',0,'5e79ca4c-0483-42c7-a217-3638ced8f6cd','[]','','','','','','','',0);
+INSERT INTO "keywords" VALUES(5,'AOL','aol.com','https://search.aol.com/favicon.ico','https://search.aol.com/aol/search?q={searchTerms}',1,'',0,0,'UTF-8','http://autocomplete.search.aol.com/autocomplete/get?output=json&it=&q={searchTerms}',35,0,'',0,'f0676c78-9a85-4720-bd74-110e473ce6e7','[]','','','','','','','',0);
+INSERT INTO "keywords" VALUES(6,'Ask','ask.com','http://sp.ask.com/sh/i/a16/favicon/favicon.ico','http://www.ask.com/web?q={searchTerms}',1,'',0,0,'UTF-8','http://ss.ask.com/query?q={searchTerms}&li=ff',4,0,'',0,'5005f7aa-40a2-44ae-b8d5-363892f5f39d','[]','','','','','','','',0);
+CREATE TABLE autofill (name VARCHAR, value VARCHAR, value_lower VARCHAR, date_created INTEGER DEFAULT 0, date_last_used INTEGER DEFAULT 0, count INTEGER DEFAULT 1, PRIMARY KEY (name, value));
+CREATE TABLE credit_cards ( guid VARCHAR PRIMARY KEY, name_on_card VARCHAR, expiration_month INTEGER, expiration_year INTEGER, card_number_encrypted BLOB, date_modified INTEGER NOT NULL DEFAULT 0, origin VARCHAR DEFAULT '', use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, billing_address_id VARCHAR);
+CREATE TABLE autofill_profiles ( guid VARCHAR PRIMARY KEY, company_name VARCHAR, street_address VARCHAR, dependent_locality VARCHAR, city VARCHAR, state VARCHAR, zipcode VARCHAR, sorting_code VARCHAR, country_code VARCHAR, date_modified INTEGER NOT NULL DEFAULT 0, origin VARCHAR DEFAULT '', language_code VARCHAR, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0);
+CREATE TABLE autofill_profile_names ( guid VARCHAR, first_name VARCHAR, middle_name VARCHAR, last_name VARCHAR, full_name VARCHAR);
+CREATE TABLE autofill_profile_emails ( guid VARCHAR, email VARCHAR);
+CREATE TABLE autofill_profile_phones ( guid VARCHAR, number VARCHAR);
+CREATE TABLE autofill_profiles_trash ( guid VARCHAR);
+CREATE TABLE masked_credit_cards (id VARCHAR,status VARCHAR,name_on_card VARCHAR,network VARCHAR,last_four VARCHAR,exp_month INTEGER DEFAULT 0,exp_year INTEGER DEFAULT 0);
+INSERT INTO "masked_credit_cards" VALUES('card_1','status','bob','VISA','1234',12,2050);
+CREATE TABLE unmasked_credit_cards (id VARCHAR,card_number_encrypted VARCHAR, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, unmask_date INTEGER NOT NULL DEFAULT 0);
+CREATE TABLE server_card_metadata (id VARCHAR NOT NULL,use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, billing_address_id VARCHAR);
+CREATE TABLE server_addresses (id VARCHAR,company_name VARCHAR,street_address VARCHAR,address_1 VARCHAR,address_2 VARCHAR,address_3 VARCHAR,address_4 VARCHAR,postal_code VARCHAR,sorting_code VARCHAR,country_code VARCHAR,language_code VARCHAR, recipient_name VARCHAR, phone_number VARCHAR);
+CREATE TABLE server_address_metadata (id VARCHAR NOT NULL,use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, has_converted BOOL NOT NULL DEFAULT FALSE);
+CREATE TABLE autofill_sync_metadata (storage_key VARCHAR PRIMARY KEY NOT NULL,value BLOB);
+CREATE TABLE autofill_model_type_state (id INTEGER PRIMARY KEY, value BLOB);
+CREATE INDEX autofill_name ON autofill (name);
+CREATE INDEX autofill_name_value_lower ON autofill (name, value_lower);
+COMMIT;
diff --git a/components/translate/core/browser/mock_translate_client.h b/components/translate/core/browser/mock_translate_client.h
index 0f364e62..6a28c71f 100644
--- a/components/translate/core/browser/mock_translate_client.h
+++ b/components/translate/core/browser/mock_translate_client.h
@@ -14,6 +14,7 @@
 #include "components/translate/core/browser/translate_client.h"
 #include "components/translate/core/browser/translate_driver.h"
 #include "components/translate/core/browser/translate_prefs.h"
+#include "components/translate/core/common/language_detection_details.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace translate {
@@ -48,6 +49,9 @@
   }
 #endif
 
+  MOCK_CONST_METHOD1(RecordLanguageDetectionEvent,
+                     void(const LanguageDetectionDetails&));
+
   MOCK_METHOD5(ShowTranslateUI,
                void(translate::TranslateStep,
                     const std::string&,
diff --git a/components/translate/core/browser/translate_client.h b/components/translate/core/browser/translate_client.h
index eda29a19..75fb592 100644
--- a/components/translate/core/browser/translate_client.h
+++ b/components/translate/core/browser/translate_client.h
@@ -30,6 +30,8 @@
 class TranslateDriver;
 class TranslateInfoBarDelegate;
 
+struct LanguageDetectionDetails;
+
 // A client interface that needs to be supplied to TranslateManager by the
 // embedder.
 //
@@ -54,6 +56,10 @@
   // Returns the resource ID of the icon to be shown for the Translate infobars.
   virtual int GetInfobarIconID() const = 0;
 
+  // Record language detection event.
+  virtual void RecordLanguageDetectionEvent(
+      const LanguageDetectionDetails& details) const = 0;
+
   // Record translate event.
   // This is for user ID keyed event logging.
   // This function will take metrics::TranslateEventProto and log the evnet that
diff --git a/components/translate/ios/browser/ios_translate_driver.mm b/components/translate/ios/browser/ios_translate_driver.mm
index e2d79467..f51ea101 100644
--- a/components/translate/ios/browser/ios_translate_driver.mm
+++ b/components/translate/ios/browser/ios_translate_driver.mm
@@ -12,6 +12,7 @@
 #include "components/translate/core/browser/language_model.h"
 #include "components/translate/core/browser/translate_client.h"
 #include "components/translate/core/browser/translate_manager.h"
+#include "components/translate/core/common/language_detection_details.h"
 #include "components/translate/core/common/translate_constants.h"
 #include "components/translate/core/common/translate_errors.h"
 #include "components/translate/core/common/translate_metrics.h"
@@ -100,6 +101,13 @@
     language_model_->OnPageVisited(details.cld_language);
   }
 
+  translate::LanguageDetectionDetails detection_details;
+  detection_details.cld_language = details.cld_language;
+  detection_details.is_cld_reliable = details.is_cld_reliable;
+  detection_details.adopted_language = details.adopted_language;
+  translate_manager_->translate_client()->RecordLanguageDetectionEvent(
+      detection_details);
+
   if (web_state())
     translate_manager_->InitiateTranslation(details.adopted_language);
 }
diff --git a/components/ukm/ukm_recorder_impl.cc b/components/ukm/ukm_recorder_impl.cc
index cb67081..0ec456d 100644
--- a/components/ukm/ukm_recorder_impl.cc
+++ b/components/ukm/ukm_recorder_impl.cc
@@ -86,12 +86,10 @@
 UkmRecorderImpl::~UkmRecorderImpl() = default;
 
 void UkmRecorderImpl::EnableRecording() {
-  DVLOG(1) << "UkmRecorderImpl::EnableRecording";
   recording_enabled_ = true;
 }
 
 void UkmRecorderImpl::DisableRecording() {
-  DVLOG(1) << "UkmRecorderImpl::DisableRecording";
   recording_enabled_ = false;
 }
 
diff --git a/components/ukm/ukm_recorder_impl.h b/components/ukm/ukm_recorder_impl.h
index 50976e4..44cd8d8e 100644
--- a/components/ukm/ukm_recorder_impl.h
+++ b/components/ukm/ukm_recorder_impl.h
@@ -13,10 +13,6 @@
 #include "components/ukm/public/interfaces/ukm_interface.mojom.h"
 #include "components/ukm/public/ukm_recorder.h"
 
-namespace metrics {
-class UkmBrowserTest;
-}
-
 namespace ukm {
 
 class UkmSource;
@@ -52,9 +48,7 @@
   const std::vector<mojom::UkmEntryPtr>& entries() const { return entries_; }
 
  private:
-  friend ::metrics::UkmBrowserTest;
   friend ::ukm::debug::DebugPage;
-
   // UkmRecorder:
   void UpdateSourceURL(SourceId source_id, const GURL& url) override;
   void AddEntry(mojom::UkmEntryPtr entry) override;
diff --git a/components/ukm/ukm_service.h b/components/ukm/ukm_service.h
index b693119..14dec3e 100644
--- a/components/ukm/ukm_service.h
+++ b/components/ukm/ukm_service.h
@@ -23,7 +23,6 @@
 
 namespace metrics {
 class MetricsServiceClient;
-class UkmBrowserTest;
 }
 
 namespace ukm {
@@ -76,7 +75,6 @@
 
  private:
   friend ::ukm::debug::DebugPage;
-  friend ::metrics::UkmBrowserTest;
 
   FRIEND_TEST_ALL_PREFIXES(UkmServiceTest, AddEntryWithEmptyMetrics);
   FRIEND_TEST_ALL_PREFIXES(UkmServiceTest, EntryBuilderAndSerialization);
diff --git a/components/webdata/common/BUILD.gn b/components/webdata/common/BUILD.gn
index 1fe0781..c107b5a 100644
--- a/components/webdata/common/BUILD.gn
+++ b/components/webdata/common/BUILD.gn
@@ -58,6 +58,7 @@
     "//components/test/data/web_database/version_69.sql",
     "//components/test/data/web_database/version_70.sql",
     "//components/test/data/web_database/version_71.sql",
+    "//components/test/data/web_database/version_72.sql",
   ]
   outputs = [
     "{{bundle_resources_dir}}/" +
diff --git a/components/webdata/common/web_database.cc b/components/webdata/common/web_database.cc
index 072f025..a58004c 100644
--- a/components/webdata/common/web_database.cc
+++ b/components/webdata/common/web_database.cc
@@ -13,7 +13,7 @@
 // corresponding changes must happen in the unit tests, and new migration test
 // added.  See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
 // static
-const int WebDatabase::kCurrentVersionNumber = 72;
+const int WebDatabase::kCurrentVersionNumber = 73;
 
 const int WebDatabase::kDeprecatedVersionNumber = 51;
 
diff --git a/components/webdata/common/web_database_migration_unittest.cc b/components/webdata/common/web_database_migration_unittest.cc
index 4417725..ac54bd8 100644
--- a/components/webdata/common/web_database_migration_unittest.cc
+++ b/components/webdata/common/web_database_migration_unittest.cc
@@ -130,7 +130,7 @@
   DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest);
 };
 
-const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 72;
+const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 73;
 
 void WebDatabaseMigrationTest::LoadDatabase(
     const base::FilePath::StringType& file) {
@@ -1287,3 +1287,42 @@
     EXPECT_EQ("VISA", s_cards_metadata.ColumnString(1));
   }
 }
+
+// Tests addition of bank_name to masked_credit_cards
+TEST_F(WebDatabaseMigrationTest, MigrateVersion72ToCurrent) {
+  ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_72.sql")));
+
+  // Verify pre-conditions.
+  {
+    sql::Connection connection;
+    ASSERT_TRUE(connection.Open(GetDatabasePath()));
+    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+    sql::MetaTable meta_table;
+    ASSERT_TRUE(meta_table.Init(&connection, 72, 72));
+
+    EXPECT_FALSE(
+        connection.DoesColumnExist("masked_credit_cards", "bank_name"));
+  }
+
+  DoMigration();
+
+  // Verify post-conditions.
+  {
+    sql::Connection connection;
+    ASSERT_TRUE(connection.Open(GetDatabasePath()));
+    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+    // Check version.
+    EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+    // The bank_name column should exist.
+    EXPECT_TRUE(connection.DoesColumnExist("masked_credit_cards", "bank_name"));
+
+    // Make sure that the default bank name value is empty.
+    sql::Statement s_masked_cards(connection.GetUniqueStatement(
+        "SELECT bank_name FROM masked_credit_cards"));
+    ASSERT_TRUE(s_masked_cards.Step());
+    EXPECT_EQ("", s_masked_cards.ColumnString(0));
+  }
+}
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index ccaaa90..511a700 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -12,6 +12,7 @@
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/storage_partition.h"
+#include "storage/browser/quota/quota_client.h"
 #include "storage/browser/quota/quota_manager.h"
 #include "storage/common/quota/quota_status_code.h"
 
@@ -19,40 +20,61 @@
 namespace protocol {
 
 namespace {
-static const char kAppCache[] = "appcache";
-static const char kCookies[] = "cookies";
-static const char kFileSystems[] = "filesystems";
-static const char kIndexedDB[] = "indexeddb";
-static const char kLocalStorage[] = "local_storage";
-static const char kShaderCache[] = "shader_cache";
-static const char kWebSQL[] = "websql";
-static const char kServiceWorkers[] = "service_workers";
-static const char kCacheStorage[] = "cache_storage";
-static const char kAll[] = "all";
+Storage::StorageType GetTypeName(storage::QuotaClient::ID id) {
+  switch (id) {
+    case storage::QuotaClient::kFileSystem:
+      return Storage::StorageTypeEnum::File_systems;
+    case storage::QuotaClient::kDatabase:
+      return Storage::StorageTypeEnum::Websql;
+    case storage::QuotaClient::kAppcache:
+      return Storage::StorageTypeEnum::Appcache;
+    case storage::QuotaClient::kIndexedDatabase:
+      return Storage::StorageTypeEnum::Indexeddb;
+    case storage::QuotaClient::kServiceWorkerCache:
+      return Storage::StorageTypeEnum::Cache_storage;
+    case storage::QuotaClient::kServiceWorker:
+      return Storage::StorageTypeEnum::Service_workers;
+    default:
+      return Storage::StorageTypeEnum::Other;
+  }
+}
 
 void ReportUsageAndQuotaDataOnUIThread(
     std::unique_ptr<StorageHandler::GetUsageAndQuotaCallback> callback,
     storage::QuotaStatusCode code,
     int64_t usage,
-    int64_t quota) {
+    int64_t quota,
+    base::flat_map<storage::QuotaClient::ID, int64_t> usage_breakdown) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (code != storage::kQuotaStatusOk) {
     return callback->sendFailure(
         Response::Error("Quota information is not available"));
   }
-  callback->sendSuccess(usage, quota);
+
+  std::unique_ptr<Array<Storage::UsageForType>> usageList =
+      Array<Storage::UsageForType>::create();
+  for (const auto& usage : usage_breakdown) {
+    std::unique_ptr<Storage::UsageForType> entry =
+        Storage::UsageForType::Create()
+            .SetStorageType(GetTypeName(usage.first))
+            .SetUsage(usage.second)
+            .Build();
+    usageList->addItem(std::move(entry));
+  }
+  callback->sendSuccess(usage, quota, std::move(usageList));
 }
 
 void GotUsageAndQuotaDataCallback(
     std::unique_ptr<StorageHandler::GetUsageAndQuotaCallback> callback,
     storage::QuotaStatusCode code,
     int64_t usage,
-    int64_t quota) {
+    int64_t quota,
+    base::flat_map<storage::QuotaClient::ID, int64_t> usage_breakdown) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(ReportUsageAndQuotaDataOnUIThread,
-                 base::Passed(std::move(callback)), code, usage, quota));
+  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+                          base::Bind(ReportUsageAndQuotaDataOnUIThread,
+                                     base::Passed(std::move(callback)), code,
+                                     usage, quota, std::move(usage_breakdown)));
 }
 
 void GetUsageAndQuotaOnIOThread(
@@ -60,7 +82,7 @@
     const GURL& url,
     std::unique_ptr<StorageHandler::GetUsageAndQuotaCallback> callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  manager->GetUsageAndQuotaForWebApps(
+  manager->GetUsageAndQuotaWithBreakdown(
       url, storage::kStorageTypeTemporary,
       base::Bind(&GotUsageAndQuotaDataCallback,
                  base::Passed(std::move(callback))));
@@ -93,25 +115,25 @@
       storage_types, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
   std::unordered_set<std::string> set(types.begin(), types.end());
   uint32_t remove_mask = 0;
-  if (set.count(kAppCache))
+  if (set.count(Storage::StorageTypeEnum::Appcache))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_APPCACHE;
-  if (set.count(kCookies))
+  if (set.count(Storage::StorageTypeEnum::Cookies))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_COOKIES;
-  if (set.count(kFileSystems))
+  if (set.count(Storage::StorageTypeEnum::File_systems))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
-  if (set.count(kIndexedDB))
+  if (set.count(Storage::StorageTypeEnum::Indexeddb))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
-  if (set.count(kLocalStorage))
+  if (set.count(Storage::StorageTypeEnum::Local_storage))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
-  if (set.count(kShaderCache))
+  if (set.count(Storage::StorageTypeEnum::Shader_cache))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE;
-  if (set.count(kWebSQL))
+  if (set.count(Storage::StorageTypeEnum::Websql))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_WEBSQL;
-  if (set.count(kServiceWorkers))
+  if (set.count(Storage::StorageTypeEnum::Service_workers))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS;
-  if (set.count(kCacheStorage))
+  if (set.count(Storage::StorageTypeEnum::Cache_storage))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_CACHE_STORAGE;
-  if (set.count(kAll))
+  if (set.count(Storage::StorageTypeEnum::All))
     remove_mask |= StoragePartition::REMOVE_DATA_MASK_ALL;
 
   if (!remove_mask)
diff --git a/content/browser/dom_storage/local_storage_context_mojo.cc b/content/browser/dom_storage/local_storage_context_mojo.cc
index 8f28fe3..0b5cd6b5 100644
--- a/content/browser/dom_storage/local_storage_context_mojo.cc
+++ b/content/browser/dom_storage/local_storage_context_mojo.cc
@@ -11,6 +11,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/sys_info.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "components/leveldb/public/cpp/util.h"
 #include "components/leveldb/public/interfaces/leveldb.mojom.h"
@@ -62,6 +63,16 @@
   MAX
 };
 
+// Limits on the cache size and number of areas in memory, over which the areas
+// are purged.
+#if defined(OS_ANDROID)
+const unsigned kMaxStorageAreaCount = 10;
+const size_t kMaxCacheSize = 2 * 1024 * 1024;
+#else
+const unsigned kMaxStorageAreaCount = 50;
+const size_t kMaxCacheSize = 20 * 1024 * 1024;
+#endif
+
 std::vector<uint8_t> CreateMetaDataKey(const url::Origin& origin) {
   auto serialized_origin = leveldb::StdStringToUint8Vector(origin.Serialize());
   std::vector<uint8_t> key;
@@ -113,6 +124,45 @@
   operations->push_back(std::move(item));
 }
 
+enum class CachePurgeReason {
+  NotNeeded,
+  SizeLimitExceeded,
+  AreaCountLimitExceeded,
+  InactiveOnLowEndDevice,
+  AggressivePurgeTriggered
+};
+
+void RecordCachePurgedHistogram(CachePurgeReason reason,
+                                size_t purged_size_kib) {
+  UMA_HISTOGRAM_COUNTS_100000("LocalStorageContext.CachePurgedInKB",
+                              purged_size_kib);
+  switch (reason) {
+    case CachePurgeReason::SizeLimitExceeded:
+      UMA_HISTOGRAM_COUNTS_100000(
+          "LocalStorageContext.CachePurgedInKB.SizeLimitExceeded",
+          purged_size_kib);
+      break;
+    case CachePurgeReason::AreaCountLimitExceeded:
+      UMA_HISTOGRAM_COUNTS_100000(
+          "LocalStorageContext.CachePurgedInKB.AreaCountLimitExceeded",
+          purged_size_kib);
+      break;
+    case CachePurgeReason::InactiveOnLowEndDevice:
+      UMA_HISTOGRAM_COUNTS_100000(
+          "LocalStorageContext.CachePurgedInKB.InactiveOnLowEndDevice",
+          purged_size_kib);
+      break;
+    case CachePurgeReason::AggressivePurgeTriggered:
+      UMA_HISTOGRAM_COUNTS_100000(
+          "LocalStorageContext.CachePurgedInKB.AggressivePurgeTriggered",
+          purged_size_kib);
+      break;
+    case CachePurgeReason::NotNeeded:
+      NOTREACHED();
+      break;
+  }
+}
+
 }  // namespace
 
 class LocalStorageContextMojo::LevelDBWrapperHolder
@@ -142,10 +192,11 @@
   LevelDBWrapperImpl* level_db_wrapper() { return level_db_wrapper_ptr_; }
 
   void OnNoBindings() override {
-    // Will delete |this|.
-    DCHECK(context_->level_db_wrappers_.find(origin_) !=
-           context_->level_db_wrappers_.end());
-    context_->level_db_wrappers_.erase(origin_);
+    has_bindings_ = false;
+    // Don't delete ourselves, but do schedule an immediate commit. Possible
+    // deletion will happen under memory pressure or when another localstorage
+    // area is opened.
+    level_db_wrapper()->ScheduleImmediateCommit();
   }
 
   std::vector<leveldb::mojom::BatchedOperationPtr> PrepareToCommit() override {
@@ -219,6 +270,13 @@
                                 leveldb_env::LEVELDB_STATUS_MAX);
   }
 
+  void Bind(mojom::LevelDBWrapperRequest request) {
+    has_bindings_ = true;
+    level_db_wrapper()->Bind(std::move(request));
+  }
+
+  bool has_bindings() const { return has_bindings_; }
+
  private:
   base::FilePath sql_db_path() const {
     if (context_->old_localstorage_path_.empty())
@@ -236,6 +294,7 @@
   // could already be null, but this field should still be valid.
   LevelDBWrapperImpl* level_db_wrapper_ptr_;
   bool deleted_old_data_ = false;
+  bool has_bindings_ = false;
 };
 
 LocalStorageContextMojo::LocalStorageContextMojo(
@@ -252,6 +311,7 @@
                                          reinterpret_cast<uintptr_t>(this))),
       task_runner_(std::move(legacy_task_runner)),
       old_localstorage_path_(old_localstorage_path),
+      is_low_end_device_(base::SysInfo::IsLowEndDevice()),
       weak_ptr_factory_(this) {
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       this, "LocalStorage", task_runner);
@@ -347,8 +407,58 @@
 }
 
 void LocalStorageContextMojo::PurgeMemory() {
-  for (const auto& it : level_db_wrappers_)
-    it.second->level_db_wrapper()->PurgeMemory();
+  size_t total_cache_size, unused_wrapper_count;
+  GetStatistics(&total_cache_size, &unused_wrapper_count);
+
+  for (auto it = level_db_wrappers_.begin(); it != level_db_wrappers_.end();) {
+    if (it->second->has_bindings()) {
+      it->second->level_db_wrapper()->PurgeMemory();
+      ++it;
+    } else {
+      it = level_db_wrappers_.erase(it);
+    }
+  }
+
+  // Track the size of cache purged.
+  size_t final_total_cache_size;
+  GetStatistics(&final_total_cache_size, &unused_wrapper_count);
+  size_t purged_size_kib = (total_cache_size - final_total_cache_size) / 1024;
+  RecordCachePurgedHistogram(CachePurgeReason::AggressivePurgeTriggered,
+                             purged_size_kib);
+}
+
+void LocalStorageContextMojo::PurgeUnusedWrappersIfNeeded() {
+  size_t total_cache_size, unused_wrapper_count;
+  GetStatistics(&total_cache_size, &unused_wrapper_count);
+
+  // Nothing to purge.
+  if (!unused_wrapper_count)
+    return;
+
+  CachePurgeReason purge_reason = CachePurgeReason::NotNeeded;
+
+  if (total_cache_size > kMaxCacheSize)
+    purge_reason = CachePurgeReason::SizeLimitExceeded;
+  else if (level_db_wrappers_.size() > kMaxStorageAreaCount)
+    purge_reason = CachePurgeReason::AreaCountLimitExceeded;
+  else if (is_low_end_device_)
+    purge_reason = CachePurgeReason::InactiveOnLowEndDevice;
+
+  if (purge_reason == CachePurgeReason::NotNeeded)
+    return;
+
+  for (auto it = level_db_wrappers_.begin(); it != level_db_wrappers_.end();) {
+    if (it->second->has_bindings())
+      ++it;
+    else
+      it = level_db_wrappers_.erase(it);
+  }
+
+  // Track the size of cache purged.
+  size_t final_total_cache_size;
+  GetStatistics(&final_total_cache_size, &unused_wrapper_count);
+  size_t purged_size_kib = (total_cache_size - final_total_cache_size) / 1024;
+  RecordCachePurgedHistogram(purge_reason, purged_size_kib);
 }
 
 void LocalStorageContextMojo::SetDatabaseForTesting(
@@ -379,9 +489,8 @@
 
   if (args.level_of_detail ==
       base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) {
-    size_t total_cache_size = 0;
-    for (const auto& it : level_db_wrappers_)
-      total_cache_size += it.second->level_db_wrapper()->bytes_used();
+    size_t total_cache_size, unused_wrapper_count;
+    GetStatistics(&total_cache_size, &unused_wrapper_count);
     auto* mad = pmd->CreateAllocatorDump(context_name + "/cache_size");
     mad->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
                    base::trace_event::MemoryAllocatorDump::kUnitsBytes,
@@ -661,17 +770,27 @@
   GetOrCreateDBWrapper(origin)->Bind(std::move(request));
 }
 
-LevelDBWrapperImpl* LocalStorageContextMojo::GetOrCreateDBWrapper(
-    const url::Origin& origin) {
+LocalStorageContextMojo::LevelDBWrapperHolder*
+LocalStorageContextMojo::GetOrCreateDBWrapper(const url::Origin& origin) {
   DCHECK_EQ(connection_state_, CONNECTION_FINISHED);
   auto found = level_db_wrappers_.find(origin);
-  if (found != level_db_wrappers_.end())
-    return found->second->level_db_wrapper();
+  if (found != level_db_wrappers_.end()) {
+    return found->second.get();
+  }
+
+  size_t total_cache_size, unused_wrapper_count;
+  GetStatistics(&total_cache_size, &unused_wrapper_count);
+
+  // Track the total localStorage cache size.
+  UMA_HISTOGRAM_COUNTS_100000("LocalStorageContext.CacheSizeInKB",
+                              total_cache_size / 1024);
+
+  PurgeUnusedWrappersIfNeeded();
 
   auto holder = base::MakeUnique<LevelDBWrapperHolder>(this, origin);
-  LevelDBWrapperImpl* wrapper_ptr = holder->level_db_wrapper();
+  LevelDBWrapperHolder* holder_ptr = holder.get();
   level_db_wrappers_[origin] = std::move(holder);
-  return wrapper_ptr;
+  return holder_ptr;
 }
 
 void LocalStorageContextMojo::RetrieveStorageUsage(
@@ -775,4 +894,15 @@
   delete this;
 }
 
+void LocalStorageContextMojo::GetStatistics(size_t* total_cache_size,
+                                            size_t* unused_wrapper_count) {
+  *total_cache_size = 0;
+  *unused_wrapper_count = 0;
+  for (const auto& it : level_db_wrappers_) {
+    *total_cache_size += it.second->level_db_wrapper()->bytes_used();
+    if (!it.second->has_bindings())
+      (*unused_wrapper_count)++;
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/dom_storage/local_storage_context_mojo.h b/content/browser/dom_storage/local_storage_context_mojo.h
index 7fb9aa44..d471de2 100644
--- a/content/browser/dom_storage/local_storage_context_mojo.h
+++ b/content/browser/dom_storage/local_storage_context_mojo.h
@@ -26,7 +26,6 @@
 namespace content {
 
 class DOMStorageTaskRunner;
-class LevelDBWrapperImpl;
 struct LocalStorageUsageInfo;
 
 // Used for mojo-based LocalStorage implementation (can be disabled with
@@ -73,6 +72,9 @@
   // storage for a particular origin will reload the data from the database.
   void PurgeMemory();
 
+  // Clears unused leveldb wrappers, when thresholds are reached.
+  void PurgeUnusedWrappersIfNeeded();
+
   void SetDatabaseForTesting(
       leveldb::mojom::LevelDBDatabaseAssociatedPtr database);
 
@@ -110,7 +112,7 @@
   // directly from that function, or through |on_database_open_callbacks_|.
   void BindLocalStorage(const url::Origin& origin,
                         mojom::LevelDBWrapperRequest request);
-  LevelDBWrapperImpl* GetOrCreateDBWrapper(const url::Origin& origin);
+  LevelDBWrapperHolder* GetOrCreateDBWrapper(const url::Origin& origin);
 
   // The (possibly delayed) implementation of GetStorageUsage(). Can be called
   // directly from that function, or through |on_database_open_callbacks_|.
@@ -126,6 +128,8 @@
   void OnGotStorageUsageForShutdown(std::vector<LocalStorageUsageInfo> usage);
   void OnShutdownComplete(leveldb::mojom::DatabaseError error);
 
+  void GetStatistics(size_t* total_cache_size, size_t* unused_wrapper_count);
+
   std::unique_ptr<service_manager::Connector> connector_;
   const base::FilePath subdirectory_;
 
@@ -159,6 +163,8 @@
   scoped_refptr<DOMStorageTaskRunner> task_runner_;
   base::FilePath old_localstorage_path_;
 
+  bool is_low_end_device_;
+
   base::WeakPtrFactory<LocalStorageContextMojo> weak_ptr_factory_;
 };
 
diff --git a/content/browser/dom_storage/local_storage_context_mojo_unittest.cc b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
index 3ff5841..70e9cb4 100644
--- a/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
+++ b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
@@ -172,6 +172,8 @@
     return mock_data_;
   }
 
+  void clear_mock_data() { mock_data_.clear(); }
+
   void set_mock_data(const std::string& key, const std::string& value) {
     mock_data_[StdStringToUint8Vector(key)] = StdStringToUint8Vector(value);
   }
@@ -239,6 +241,94 @@
   ASSERT_EQ(5u, mock_data().size());
 }
 
+TEST_F(LocalStorageContextMojoTest, WrapperOutlivesMojoConnection) {
+  auto key = StdStringToUint8Vector("key");
+  auto value = StdStringToUint8Vector("value");
+
+  // Write some data to the DB.
+  mojom::LevelDBWrapperPtr wrapper;
+  context()->OpenLocalStorage(url::Origin(GURL("http://foobar.com")),
+                              MakeRequest(&wrapper));
+  wrapper->Put(key, value, "source", base::Bind(&NoOpSuccess));
+  wrapper.reset();
+  base::RunLoop().RunUntilIdle();
+
+  // Clear all the data from the backing database.
+  EXPECT_FALSE(mock_data().empty());
+  clear_mock_data();
+
+  // Data should still be readable, because despite closing the wrapper
+  // connection above, the actual wrapper instance should have been kept alive.
+  {
+    base::RunLoop run_loop;
+    bool success = false;
+    std::vector<uint8_t> result;
+    context()->OpenLocalStorage(url::Origin(GURL("http://foobar.com")),
+                                MakeRequest(&wrapper));
+    wrapper->Get(key, base::Bind(&GetCallback, run_loop.QuitClosure(), &success,
+                                 &result));
+    run_loop.Run();
+    EXPECT_TRUE(success);
+    EXPECT_EQ(value, result);
+    wrapper.reset();
+  }
+
+  // Now purge memory.
+  context()->PurgeMemory();
+
+  // And make sure caches were actually cleared.
+  {
+    base::RunLoop run_loop;
+    bool success = false;
+    std::vector<uint8_t> result;
+    context()->OpenLocalStorage(url::Origin(GURL("http://foobar.com")),
+                                MakeRequest(&wrapper));
+    wrapper->Get(key, base::Bind(&GetCallback, run_loop.QuitClosure(), &success,
+                                 &result));
+    run_loop.Run();
+    EXPECT_FALSE(success);
+    wrapper.reset();
+  }
+}
+
+TEST_F(LocalStorageContextMojoTest, OpeningWrappersPurgesInactiveWrappers) {
+  auto key = StdStringToUint8Vector("key");
+  auto value = StdStringToUint8Vector("value");
+
+  // Write some data to the DB.
+  mojom::LevelDBWrapperPtr wrapper;
+  context()->OpenLocalStorage(url::Origin(GURL("http://foobar.com")),
+                              MakeRequest(&wrapper));
+  wrapper->Put(key, value, "source", base::Bind(&NoOpSuccess));
+  wrapper.reset();
+  base::RunLoop().RunUntilIdle();
+
+  // Clear all the data from the backing database.
+  EXPECT_FALSE(mock_data().empty());
+  clear_mock_data();
+
+  // Now open many new wrappers (for different origins) to trigger clean up.
+  for (int i = 1; i <= 100; ++i) {
+    context()->OpenLocalStorage(
+        url::Origin::UnsafelyCreateOriginWithoutNormalization(
+            "http", "example.com", i, ""),
+        MakeRequest(&wrapper));
+    wrapper.reset();
+  }
+
+  // And make sure caches were actually cleared.
+  base::RunLoop run_loop;
+  bool success = true;
+  std::vector<uint8_t> result;
+  context()->OpenLocalStorage(url::Origin(GURL("http://foobar.com")),
+                              MakeRequest(&wrapper));
+  wrapper->Get(
+      key, base::Bind(&GetCallback, run_loop.QuitClosure(), &success, &result));
+  run_loop.Run();
+  EXPECT_FALSE(success);
+  wrapper.reset();
+}
+
 TEST_F(LocalStorageContextMojoTest, ValidVersion) {
   set_mock_data("VERSION", "1");
   set_mock_data(std::string("_http://foobar.com") + '\x00' + "key", "value");
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 1f07ec6..ec704d4 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -1880,10 +1880,18 @@
       pending_entry_->GetTransitionType() & ui::PAGE_TRANSITION_FORWARD_BACK) {
     delegate_->Stop();
 
-    // If an interstitial page is showing, we want to close it to get back
-    // to what was showing before.
-    if (delegate_->GetInterstitialPage())
-      delegate_->GetInterstitialPage()->DontProceed();
+    // If an interstitial page is showing, we want to close it to get back to
+    // what was showing before.
+    //
+    // There are two ways to get the interstitial page given a WebContents.
+    // Because WebContents::GetInterstitialPage() returns null between the
+    // interstitial's Show() method being called and the interstitial becoming
+    // visible, while InterstitialPage::GetInterstitialPage() returns the
+    // interstitial during that time, use the latter.
+    InterstitialPage* interstitial =
+        InterstitialPage::GetInterstitialPage(GetWebContents());
+    if (interstitial)
+      interstitial->DontProceed();
 
     DiscardNonCommittedEntries();
     return;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 5fdc4d0..1d68687 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -988,6 +988,8 @@
   }
   if (browser_plugin_embedder_)
     browser_plugin_embedder_->CancelGuestDialogs();
+  if (delegate_)
+    delegate_->HideValidationMessage(this);
 }
 
 void WebContentsImpl::ClosePage() {
@@ -4694,9 +4696,6 @@
   // Cancel any visible dialogs so they are not left dangling over the sad tab.
   CancelActiveAndPendingDialogs();
 
-  if (delegate_)
-    delegate_->HideValidationMessage(this);
-
   audio_stream_monitor_.RenderProcessGone(rvh->GetProcess()->GetID());
 
   // Reset the loading progress. TODO(avi): What does it mean to have a
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 2f161b01..fea1eb6 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -1467,4 +1467,78 @@
   wc->SetJavaScriptDialogManagerForTesting(nullptr);
 }
 
+class FormBubbleDelegate : public WebContentsDelegate {
+ public:
+  FormBubbleDelegate() = default;
+
+  void WaitUntilShown() {
+    while (!is_visible_) {
+      message_loop_runner_ = new MessageLoopRunner;
+      message_loop_runner_->Run();
+    }
+  }
+
+  void WaitUntilHidden() {
+    while (is_visible_) {
+      message_loop_runner_ = new MessageLoopRunner;
+      message_loop_runner_->Run();
+    }
+  }
+
+ private:
+  void ShowValidationMessage(WebContents* web_contents,
+                             const gfx::Rect& anchor_in_root_view,
+                             const base::string16& main_text,
+                             const base::string16& sub_text) override {
+    is_visible_ = true;
+    if (message_loop_runner_)
+      message_loop_runner_->Quit();
+  }
+
+  void HideValidationMessage(WebContents* web_contents) override {
+    is_visible_ = false;
+    if (message_loop_runner_)
+      message_loop_runner_->Quit();
+  }
+
+  bool is_visible_ = false;
+  scoped_refptr<MessageLoopRunner> message_loop_runner_;
+};
+
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
+                       NavigationHidesFormValidationBubble) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
+
+  // Start listening for requests to show or hide the form validation bubble.
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+  FormBubbleDelegate bubble_delegate;
+  web_contents->SetDelegate(&bubble_delegate);
+
+  // Trigger a form validation bubble and verify that the bubble is shown.
+  std::string script = R"(
+      var input_field = document.createElement('input');
+      input_field.required = true;
+      var form = document.createElement('form');
+      form.appendChild(input_field);
+      document.body.appendChild(form);
+
+      setTimeout(function() {
+              input_field.setCustomValidity('Custom validity message');
+              input_field.reportValidity();
+          },
+          0);
+      )";
+  ASSERT_TRUE(ExecuteScript(web_contents, script));
+  bubble_delegate.WaitUntilShown();
+
+  // Navigate to another page and verify that the form validation bubble is
+  // hidden.
+  EXPECT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("b.com", "/title2.html")));
+  bubble_delegate.WaitUntilHidden();
+}
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index afcdaf9..995b962 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -2215,6 +2215,51 @@
   EXPECT_TRUE(deleted);
 }
 
+// Test for https://crbug.com/703655, where navigating a tab and showing an
+// interstitial could race.
+TEST_F(WebContentsImplTest, TabNavigationDoesntRaceInterstitial) {
+  // Navigate to a page.
+  GURL url1("http://www.google.com");
+  main_test_rfh()->NavigateAndCommitRendererInitiated(true, url1);
+  EXPECT_EQ(1, controller().GetEntryCount());
+
+  // Initiate a browser navigation that will trigger an interstitial.
+  GURL evil_url("http://www.evil.com");
+  controller().LoadURL(evil_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+                       std::string());
+  NavigationEntry* entry = contents()->GetController().GetPendingEntry();
+  ASSERT_TRUE(entry);
+  EXPECT_EQ(evil_url, entry->GetURL());
+
+  // Show an interstitial.
+  TestInterstitialPage::InterstitialState state = TestInterstitialPage::INVALID;
+  bool deleted = false;
+  GURL url2("http://interstitial");
+  TestInterstitialPage* interstitial =
+      new TestInterstitialPage(contents(), true, url2, &state, &deleted);
+  TestInterstitialPageStateGuard state_guard(interstitial);
+  interstitial->Show();
+  // The interstitial should not show until its navigation has committed.
+  EXPECT_FALSE(interstitial->is_showing());
+  EXPECT_FALSE(contents()->ShowingInterstitialPage());
+  EXPECT_EQ(nullptr, contents()->GetInterstitialPage());
+
+  // At this point, there is an interstitial that has been instructed to show
+  // but has not yet committed its own navigation. This is a window; navigate
+  // back one page within this window.
+  //
+  // Because the page with the interstitial did not commit, this invokes an
+  // early return in NavigationControllerImpl::NavigateToPendingEntry which just
+  // drops the pending entry, so no committing is required.
+  controller().GoBack();
+  entry = contents()->GetController().GetPendingEntry();
+  ASSERT_FALSE(entry);
+
+  // The interstitial should be gone.
+  RunAllPendingInMessageLoop();
+  EXPECT_TRUE(deleted);
+}
+
 // Test that after Proceed is called and an interstitial is still shown, no more
 // commands get executed.
 TEST_F(WebContentsImplTest, ShowInterstitialProceedMultipleCommands) {
diff --git a/content/public/browser/interstitial_page.h b/content/public/browser/interstitial_page.h
index e22f402..bdc03b3 100644
--- a/content/public/browser/interstitial_page.h
+++ b/content/public/browser/interstitial_page.h
@@ -48,13 +48,16 @@
       const GURL& url,
       InterstitialPageDelegate* delegate);
 
-  // Retrieves the InterstitialPage if any associated with the specified
-  // |web_contents|.
+  // Returns the InterstitialPage, if any, associated with the specified
+  // |web_contents|. Note: This returns a value from the time the interstitial
+  // page has Show() called on it.
+  //
+  // Compare to WebContents::GetInterstitialPage.
   CONTENT_EXPORT static InterstitialPage* GetInterstitialPage(
       WebContents* web_contents);
 
-  // Retrieves the InterstitialPage that hosts the RenderFrameHost, or nullptr
-  // if |rfh| is not a part of any InterstitialPage.
+  // Returns the InterstitialPage that hosts the RenderFrameHost, or nullptr if
+  // |rfh| is not a part of any InterstitialPage.
   CONTENT_EXPORT static InterstitialPage* FromRenderFrameHost(
       RenderFrameHost* rfh);
 
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index ea487a2..78cd763 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -562,8 +562,12 @@
   // Various other systems need to know about our interstitials.
   virtual bool ShowingInterstitialPage() const = 0;
 
-  // Returns the currently showing interstitial, nullptr if no interstitial is
-  // showing.
+  // Returns the currently visible interstitial, nullptr if no interstitial is
+  // visible. Note: This returns nullptr from the time the interstitial page has
+  // Show() called on it until the interstitial content is ready and the
+  // interstitial is displayed.
+  //
+  // Compare to InterstitialPage::GetInterstitialPage.
   virtual InterstitialPage* GetInterstitialPage() const = 0;
 
   // Misc state & callbacks ----------------------------------------------------
diff --git a/content/public/renderer/associated_resource_fetcher.h b/content/public/renderer/associated_resource_fetcher.h
index 68549345..18d445c 100644
--- a/content/public/renderer/associated_resource_fetcher.h
+++ b/content/public/renderer/associated_resource_fetcher.h
@@ -15,7 +15,7 @@
 class GURL;
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 class WebURLResponse;
 struct WebAssociatedURLLoaderOptions;
 }
@@ -53,7 +53,7 @@
 
   // Starts the request using the specified frame.  Calls |callback| when
   // done.
-  virtual void Start(blink::WebFrame* frame,
+  virtual void Start(blink::WebLocalFrame* frame,
                      blink::WebURLRequest::RequestContext request_context,
                      blink::WebURLRequest::FrameType frame_type,
                      const Callback& callback) = 0;
diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc
index 06afdb7..d50a7db 100644
--- a/content/public/renderer/content_renderer_client.cc
+++ b/content/public/renderer/content_renderer_client.cc
@@ -215,8 +215,7 @@
 
 std::unique_ptr<blink::WebContentSettingsClient>
 ContentRendererClient::CreateWorkerContentSettingsClient(
-    RenderFrame* render_frame,
-    blink::WebFrame* frame) {
+    RenderFrame* render_frame) {
   return nullptr;
 }
 
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index 989fd178..565bdd2 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -292,8 +292,7 @@
 
   // Creates a permission client for in-renderer worker.
   virtual std::unique_ptr<blink::WebContentSettingsClient>
-  CreateWorkerContentSettingsClient(RenderFrame* render_frame,
-                                    blink::WebFrame* frame);
+  CreateWorkerContentSettingsClient(RenderFrame* render_frame);
 
   // Returns true if the page at |url| can use Pepper CameraDevice APIs.
   virtual bool IsPluginAllowedToUseCameraDeviceAPI(const GURL& url);
diff --git a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
index d9c4d005..1a77980 100644
--- a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
+++ b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
@@ -21,6 +21,7 @@
 #include "third_party/WebKit/public/platform/WebSize.h"
 #include "third_party/WebKit/public/web/WebAXObject.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebView.h"
 #include "ui/accessibility/ax_node_data.h"
 
@@ -96,8 +97,8 @@
  protected:
   IPC::TestSink* sink_;
 
+ private:
   DISALLOW_COPY_AND_ASSIGN(RenderAccessibilityImplTest);
-
 };
 
 TEST_F(RenderAccessibilityImplTest, SendFullAccessibilityTreeOnReload) {
@@ -125,7 +126,7 @@
   // If we post another event but the tree doesn't change,
   // we should only send 1 node to the browser.
   sink_->ClearMessages();
-  WebDocument document = view()->GetWebView()->MainFrame()->GetDocument();
+  WebDocument document = GetMainFrame()->GetDocument();
   WebAXObject root_obj = WebAXObject::FromWebDocument(document);
   accessibility->HandleAXEvent(
       root_obj,
@@ -143,7 +144,7 @@
   // all 4 nodes to the browser. Also double-check that we didn't
   // leak any of the old BrowserTreeNodes.
   LoadHTML(html.c_str());
-  document = view()->GetWebView()->MainFrame()->GetDocument();
+  document = GetMainFrame()->GetDocument();
   root_obj = WebAXObject::FromWebDocument(document);
   sink_->ClearMessages();
   accessibility->HandleAXEvent(
@@ -156,7 +157,7 @@
   // the root, the whole tree should be updated because we know
   // the browser doesn't have the root element.
   LoadHTML(html.c_str());
-  document = view()->GetWebView()->MainFrame()->GetDocument();
+  document = GetMainFrame()->GetDocument();
   root_obj = WebAXObject::FromWebDocument(document);
   sink_->ClearMessages();
   const WebAXObject& first_child = root_obj.ChildAt(0);
@@ -187,7 +188,7 @@
   accessibility->SendPendingAccessibilityEvents();
   EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
 
-  WebDocument document = view()->GetWebView()->MainFrame()->GetDocument();
+  WebDocument document = GetMainFrame()->GetDocument();
   WebAXObject root_obj = WebAXObject::FromWebDocument(document);
   WebAXObject node_a = root_obj.ChildAt(0);
   WebAXObject node_b = node_a.ChildAt(0);
@@ -245,7 +246,7 @@
   ExecuteJavaScriptForTests("document.getElementById('B').offsetLeft;");
 
   sink_->ClearMessages();
-  WebDocument document = view()->GetWebView()->MainFrame()->GetDocument();
+  WebDocument document = GetMainFrame()->GetDocument();
   WebAXObject root_obj = WebAXObject::FromWebDocument(document);
   WebAXObject node_a = root_obj.ChildAt(0);
   WebAXObject node_b = node_a.ChildAt(0);
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index 64eaa78..301ea0e 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -5,7 +5,11 @@
 #include "content/renderer/browser_plugin/browser_plugin.h"
 
 #include <stddef.h>
+
+#include <map>
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "base/command_line.h"
 #include "base/location.h"
@@ -159,7 +163,11 @@
     blink::WebLocalFrame* frame = Container()->GetDocument().GetFrame();
     attach_params.is_full_page_plugin =
         frame->View()->MainFrame()->IsWebLocalFrame() &&
-        frame->View()->MainFrame()->GetDocument().IsPluginDocument();
+        frame->View()
+            ->MainFrame()
+            ->ToWebLocalFrame()
+            ->GetDocument()
+            .IsPluginDocument();
   }
   BrowserPluginManager::Get()->Send(new BrowserPluginHostMsg_Attach(
       render_frame_routing_id_,
diff --git a/content/renderer/dom_serializer_browsertest.cc b/content/renderer/dom_serializer_browsertest.cc
index f0aa764e..1b8df680 100644
--- a/content/renderer/dom_serializer_browsertest.cc
+++ b/content/renderer/dom_serializer_browsertest.cc
@@ -110,11 +110,12 @@
     return GetRenderView()->GetMainRenderFrame()->GetWebFrame();
   }
 
-  WebFrame* FindSubFrameByURL(const GURL& url) {
+  WebLocalFrame* FindSubFrameByURL(const GURL& url) {
     for (WebFrame* frame = GetWebView()->MainFrame(); frame;
          frame = frame->TraverseNext()) {
-      if (GURL(frame->GetDocument().Url()) == url)
-        return frame;
+      DCHECK(frame->IsWebLocalFrame());
+      if (GURL(frame->ToWebLocalFrame()->GetDocument().Url()) == url)
+        return frame->ToWebLocalFrame();
     }
     return nullptr;
   }
@@ -178,7 +179,7 @@
 
   void SerializeHTMLDOMWithDocTypeOnRenderer(const GURL& file_url) {
     // Make sure original contents have document type.
-    WebFrame* web_frame = FindSubFrameByURL(file_url);
+    WebLocalFrame* web_frame = FindSubFrameByURL(file_url);
     ASSERT_TRUE(web_frame != NULL);
     WebDocument doc = web_frame->GetDocument();
     ASSERT_TRUE(HasDocType(doc));
@@ -196,7 +197,7 @@
 
   void SerializeHTMLDOMWithoutDocTypeOnRenderer(const GURL& file_url) {
     // Make sure original contents do not have document type.
-    WebFrame* web_frame = FindSubFrameByURL(file_url);
+    WebLocalFrame* web_frame = FindSubFrameByURL(file_url);
     ASSERT_TRUE(web_frame != NULL);
     WebDocument doc = web_frame->GetDocument();
     ASSERT_TRUE(!HasDocType(doc));
@@ -242,7 +243,7 @@
   void SerializeHTMLDOMWithNoMetaCharsetInOriginalDocOnRenderer(
       const GURL& file_url) {
     // Make sure there is no META charset declaration in original document.
-    WebFrame* web_frame = FindSubFrameByURL(file_url);
+    WebLocalFrame* web_frame = FindSubFrameByURL(file_url);
     ASSERT_TRUE(web_frame != NULL);
     WebDocument doc = web_frame->GetDocument();
     ASSERT_TRUE(doc.IsHTMLDocument());
@@ -291,7 +292,7 @@
       const GURL& file_url) {
     // Make sure there are multiple META charset declarations in original
     // document.
-    WebFrame* web_frame = FindSubFrameByURL(file_url);
+    WebLocalFrame* web_frame = FindSubFrameByURL(file_url);
     ASSERT_TRUE(web_frame != NULL);
     WebDocument doc = web_frame->GetDocument();
     ASSERT_TRUE(doc.IsHTMLDocument());
@@ -307,7 +308,7 @@
         ++charset_declaration_count;
     }
     // The original doc has more than META tags which have charset declaration.
-    ASSERT_TRUE(charset_declaration_count > 1);
+    ASSERT_GT(charset_declaration_count, 1);
 
     // Do serialization.
     SerializeDomForURL(file_url);
@@ -355,7 +356,7 @@
     LoadContents(original_contents, file_url, WebString());
 
     // Get BODY's text content in DOM.
-    WebFrame* web_frame = FindSubFrameByURL(file_url);
+    WebLocalFrame* web_frame = FindSubFrameByURL(file_url);
     ASSERT_TRUE(web_frame != NULL);
     WebDocument doc = web_frame->GetDocument();
     ASSERT_TRUE(doc.IsHTMLDocument());
@@ -363,7 +364,7 @@
     ASSERT_TRUE(!body_ele.IsNull());
     WebNode text_node = body_ele.FirstChild();
     ASSERT_TRUE(text_node.IsTextNode());
-    ASSERT_TRUE(std::string(text_node.NodeValue().Utf8()) == "&<>\"\'");
+    ASSERT_EQ(text_node.NodeValue().Utf8(), "&<>\"\'");
     // Do serialization.
     SerializeDomForURL(file_url);
     // Compare the serialized contents with original contents.
@@ -407,14 +408,14 @@
     // Load the test contents.
     LoadContents(original_contents, file_url, WebString());
     // Get value of BODY's title attribute in DOM.
-    WebFrame* web_frame = FindSubFrameByURL(file_url);
+    WebLocalFrame* web_frame = FindSubFrameByURL(file_url);
     ASSERT_TRUE(web_frame != NULL);
     WebDocument doc = web_frame->GetDocument();
     ASSERT_TRUE(doc.IsHTMLDocument());
     WebElement body_ele = doc.Body();
     ASSERT_TRUE(!body_ele.IsNull());
     WebString value = body_ele.GetAttribute("title");
-    ASSERT_TRUE(std::string(value.Utf8()) == "&<>\"\'");
+    ASSERT_EQ(value.Utf8(), "&<>\"\'");
     // Do serialization.
     SerializeDomForURL(file_url);
     // Compare the serialized contents with original contents.
@@ -441,7 +442,7 @@
 
   void SerializeHTMLDOMWithNonStandardEntitiesOnRenderer(const GURL& file_url) {
     // Get value of BODY's title attribute in DOM.
-    WebFrame* web_frame = FindSubFrameByURL(file_url);
+    WebLocalFrame* web_frame = FindSubFrameByURL(file_url);
     WebDocument doc = web_frame->GetDocument();
     ASSERT_TRUE(doc.IsHTMLDocument());
     WebElement body_element = doc.Body();
@@ -473,7 +474,7 @@
     // Since for this test, we assume there is no savable sub-resource links for
     // this test file, also all links are relative URLs in this test file, so we
     // need to check those relative URLs and make sure document has BASE tag.
-    WebFrame* web_frame = FindSubFrameByURL(file_url);
+    WebLocalFrame* web_frame = FindSubFrameByURL(file_url);
     ASSERT_TRUE(web_frame != NULL);
     WebDocument doc = web_frame->GetDocument();
     ASSERT_TRUE(doc.IsHTMLDocument());
@@ -564,7 +565,7 @@
     LoadContents(empty_head_contents, file_url, WebString());
 
     // Make sure the head tag is empty.
-    WebFrame* web_frame = GetMainFrame();
+    WebLocalFrame* web_frame = GetMainFrame();
     ASSERT_TRUE(web_frame != NULL);
     WebDocument doc = web_frame->GetDocument();
     ASSERT_TRUE(doc.IsHTMLDocument());
@@ -604,7 +605,7 @@
 
   void SubResourceForElementsInNonHTMLNamespaceOnRenderer(
       const GURL& file_url) {
-    WebFrame* web_frame = FindSubFrameByURL(file_url);
+    WebLocalFrame* web_frame = FindSubFrameByURL(file_url);
     ASSERT_TRUE(web_frame != NULL);
     WebDocument doc = web_frame->GetDocument();
     WebNode lastNodeInBody = doc.Body().LastChild();
diff --git a/content/renderer/fetchers/associated_resource_fetcher_impl.cc b/content/renderer/fetchers/associated_resource_fetcher_impl.cc
index efafd1e..2d1b0d0 100644
--- a/content/renderer/fetchers/associated_resource_fetcher_impl.cc
+++ b/content/renderer/fetchers/associated_resource_fetcher_impl.cc
@@ -20,8 +20,8 @@
 #include "third_party/WebKit/public/web/WebAssociatedURLLoader.h"
 #include "third_party/WebKit/public/web/WebAssociatedURLLoaderClient.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
 #include "third_party/WebKit/public/web/WebKit.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
 
 namespace content {
@@ -152,7 +152,7 @@
 }
 
 void AssociatedResourceFetcherImpl::Start(
-    blink::WebFrame* frame,
+    blink::WebLocalFrame* frame,
     blink::WebURLRequest::RequestContext request_context,
     blink::WebURLRequest::FrameType frame_type,
     const Callback& callback) {
diff --git a/content/renderer/fetchers/associated_resource_fetcher_impl.h b/content/renderer/fetchers/associated_resource_fetcher_impl.h
index 6e665ac..d1dd0955 100644
--- a/content/renderer/fetchers/associated_resource_fetcher_impl.h
+++ b/content/renderer/fetchers/associated_resource_fetcher_impl.h
@@ -21,7 +21,7 @@
 
 namespace blink {
 class WebAssociatedURLLoader;
-class WebFrame;
+class WebLocalFrame;
 }
 
 namespace content {
@@ -34,7 +34,7 @@
   void SetCachePolicy(blink::WebCachePolicy policy) override;
   void SetLoaderOptions(
       const blink::WebAssociatedURLLoaderOptions& options) override;
-  void Start(blink::WebFrame* frame,
+  void Start(blink::WebLocalFrame* frame,
              blink::WebURLRequest::RequestContext request_context,
              blink::WebURLRequest::FrameType frame_type,
              const Callback& callback) override;
diff --git a/content/renderer/fetchers/manifest_fetcher.cc b/content/renderer/fetchers/manifest_fetcher.cc
index f809d53..d930682 100644
--- a/content/renderer/fetchers/manifest_fetcher.cc
+++ b/content/renderer/fetchers/manifest_fetcher.cc
@@ -9,7 +9,7 @@
 #include "content/public/renderer/associated_resource_fetcher.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
 #include "third_party/WebKit/public/web/WebAssociatedURLLoaderOptions.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 
 namespace content {
 
@@ -23,7 +23,7 @@
     Cancel();
 }
 
-void ManifestFetcher::Start(blink::WebFrame* frame,
+void ManifestFetcher::Start(blink::WebLocalFrame* frame,
                             bool use_credentials,
                             const Callback& callback) {
   callback_ = callback;
@@ -57,4 +57,4 @@
   callback.Run(response, data);
 }
 
-} // namespace content
+}  // namespace content
diff --git a/content/renderer/fetchers/manifest_fetcher.h b/content/renderer/fetchers/manifest_fetcher.h
index b769630..e831309 100644
--- a/content/renderer/fetchers/manifest_fetcher.h
+++ b/content/renderer/fetchers/manifest_fetcher.h
@@ -16,7 +16,7 @@
 class GURL;
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 }
 
 namespace content {
@@ -38,7 +38,7 @@
   explicit ManifestFetcher(const GURL& url);
   virtual ~ManifestFetcher();
 
-  void Start(blink::WebFrame* frame,
+  void Start(blink::WebLocalFrame* frame,
              bool use_credentials,
              const Callback& callback);
   void Cancel();
@@ -54,6 +54,6 @@
   DISALLOW_COPY_AND_ASSIGN(ManifestFetcher);
 };
 
-} // namespace content
+}  // namespace content
 
 #endif  // CONTENT_RENDERER_FETCHERS_MANIFEST_FETCHER_H_
diff --git a/content/renderer/fetchers/multi_resolution_image_resource_fetcher.cc b/content/renderer/fetchers/multi_resolution_image_resource_fetcher.cc
index f992ccd..0427810 100644
--- a/content/renderer/fetchers/multi_resolution_image_resource_fetcher.cc
+++ b/content/renderer/fetchers/multi_resolution_image_resource_fetcher.cc
@@ -10,11 +10,11 @@
 #include "content/public/renderer/associated_resource_fetcher.h"
 #include "third_party/WebKit/public/platform/WebURLResponse.h"
 #include "third_party/WebKit/public/web/WebAssociatedURLLoaderOptions.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/geometry/size.h"
 
-using blink::WebFrame;
+using blink::WebLocalFrame;
 using blink::WebAssociatedURLLoaderOptions;
 using blink::WebURLRequest;
 using blink::WebURLResponse;
@@ -23,7 +23,7 @@
 
 MultiResolutionImageResourceFetcher::MultiResolutionImageResourceFetcher(
     const GURL& image_url,
-    WebFrame* frame,
+    WebLocalFrame* frame,
     int id,
     WebURLRequest::RequestContext request_context,
     blink::WebCachePolicy cache_policy,
diff --git a/content/renderer/fetchers/multi_resolution_image_resource_fetcher.h b/content/renderer/fetchers/multi_resolution_image_resource_fetcher.h
index fac1bedb..98020c7 100644
--- a/content/renderer/fetchers/multi_resolution_image_resource_fetcher.h
+++ b/content/renderer/fetchers/multi_resolution_image_resource_fetcher.h
@@ -18,7 +18,7 @@
 class SkBitmap;
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 class WebURLResponse;
 }
 
@@ -35,7 +35,7 @@
 
   MultiResolutionImageResourceFetcher(
       const GURL& image_url,
-      blink::WebFrame* frame,
+      blink::WebLocalFrame* frame,
       int id,
       blink::WebURLRequest::RequestContext request_context,
       blink::WebCachePolicy cache_policy,
diff --git a/content/renderer/media/android/media_info_loader.cc b/content/renderer/media/android/media_info_loader.cc
index 9e19233..2a669bd 100644
--- a/content/renderer/media/android/media_info_loader.cc
+++ b/content/renderer/media/android/media_info_loader.cc
@@ -13,11 +13,11 @@
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
 #include "third_party/WebKit/public/platform/WebURLResponse.h"
 #include "third_party/WebKit/public/web/WebAssociatedURLLoader.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 
 using blink::WebAssociatedURLLoader;
 using blink::WebAssociatedURLLoaderOptions;
-using blink::WebFrame;
+using blink::WebLocalFrame;
 using blink::WebURLError;
 using blink::WebURLRequest;
 using blink::WebURLResponse;
@@ -40,7 +40,7 @@
 
 MediaInfoLoader::~MediaInfoLoader() {}
 
-void MediaInfoLoader::Start(blink::WebFrame* frame) {
+void MediaInfoLoader::Start(blink::WebLocalFrame* frame) {
   // Make sure we have not started.
   DCHECK(!ready_cb_.is_null());
   CHECK(frame);
diff --git a/content/renderer/media/android/media_info_loader.h b/content/renderer/media/android/media_info_loader.h
index e8a55ce..624d3427 100644
--- a/content/renderer/media/android/media_info_loader.h
+++ b/content/renderer/media/android/media_info_loader.h
@@ -22,7 +22,7 @@
 
 namespace blink {
 class WebAssociatedURLLoader;
-class WebFrame;
+class WebLocalFrame;
 class WebURLRequest;
 }
 
@@ -66,7 +66,7 @@
   ~MediaInfoLoader() override;
 
   // Start loading media info.
-  void Start(blink::WebFrame* frame);
+  void Start(blink::WebLocalFrame* frame);
 
   // Returns true if the media resource has a single origin, false otherwise.
   // Only valid to call after the loader becomes ready.
diff --git a/content/renderer/media/android/media_info_loader_unittest.cc b/content/renderer/media/android/media_info_loader_unittest.cc
index f3ccadf..710db34 100644
--- a/content/renderer/media/android/media_info_loader_unittest.cc
+++ b/content/renderer/media/android/media_info_loader_unittest.cc
@@ -66,7 +66,7 @@
   void Start() {
     InSequence s;
     EXPECT_CALL(*url_loader_, LoadAsynchronously(_, _));
-    loader_->Start(view_->MainFrame());
+    loader_->Start(view_->MainFrame()->ToWebLocalFrame());
   }
 
   void Stop() {
diff --git a/content/renderer/media/peer_connection_tracker.cc b/content/renderer/media/peer_connection_tracker.cc
index 40cb37c..3b3bac5 100644
--- a/content/renderer/media/peer_connection_tracker.cc
+++ b/content/renderer/media/peer_connection_tracker.cc
@@ -8,7 +8,9 @@
 #include <stdint.h>
 
 #include <memory>
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_number_conversions.h"
@@ -27,7 +29,7 @@
 #include "third_party/WebKit/public/platform/WebRTCOfferOptions.h"
 #include "third_party/WebKit/public/platform/WebRTCPeerConnectionHandlerClient.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebUserMediaRequest.h"
 
 using webrtc::MediaConstraintsInterface;
@@ -136,7 +138,7 @@
     break;
   default:
     NOTREACHED();
-  };
+  }
   return transport_type;
 }
 
@@ -155,7 +157,7 @@
     break;
   default:
     NOTREACHED();
-  };
+  }
   return policy_str;
 }
 
@@ -171,7 +173,7 @@
     break;
   default:
     NOTREACHED();
-  };
+  }
   return policy_str;
 }
 
@@ -312,7 +314,7 @@
 
 class InternalStatsObserver : public webrtc::StatsObserver {
  public:
-  InternalStatsObserver(int lid)
+  explicit InternalStatsObserver(int lid)
       : lid_(lid), main_thread_(base::ThreadTaskRunnerHandle::Get()) {}
 
   void OnComplete(const StatsReports& reports) override {
@@ -436,7 +438,7 @@
     RTCPeerConnectionHandler* pc_handler,
     const webrtc::PeerConnectionInterface::RTCConfiguration& config,
     const blink::WebMediaConstraints& constraints,
-    const blink::WebFrame* frame) {
+    const blink::WebLocalFrame* frame) {
   DCHECK(main_thread_.CalledOnValidThread());
   DCHECK_EQ(GetLocalIDForHandler(pc_handler), -1);
   DVLOG(1) << "PeerConnectionTracker::RegisterPeerConnection()";
@@ -588,7 +590,7 @@
 void PeerConnectionTracker::TrackRemoveStream(
     RTCPeerConnectionHandler* pc_handler,
     const blink::WebMediaStream& stream,
-    Source source){
+    Source source) {
   DCHECK(main_thread_.CalledOnValidThread());
   int id = GetLocalIDForHandler(pc_handler);
   if (id == -1)
diff --git a/content/renderer/media/peer_connection_tracker.h b/content/renderer/media/peer_connection_tracker.h
index 5f1673f7..1bb740a 100644
--- a/content/renderer/media/peer_connection_tracker.h
+++ b/content/renderer/media/peer_connection_tracker.h
@@ -19,7 +19,7 @@
 #include "third_party/webrtc/api/peerconnectioninterface.h"
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 class WebMediaConstraints;
 class WebRTCAnswerOptions;
 class WebRTCICECandidate;
@@ -68,18 +68,18 @@
   // UnregisterPeerConnection, otherwise the Track* call has no effect.
   //
 
-  // Sends an update when a PeerConnection has been created in Javascript.
-  // This should be called once and only once for each PeerConnection.
-  // The |pc_handler| is the handler object associated with the PeerConnection,
-  // the |servers| are the server configurations used to establish the
-  // connection, the |constraints| are the media constraints used to initialize
-  // the PeerConnection, the |frame| is the WebFrame object representing the
+  // Sends an update when a PeerConnection has been created in Javascript.  This
+  // should be called once and only once for each PeerConnection.  The
+  // |pc_handler| is the handler object associated with the PeerConnection, the
+  // |servers| are the server configurations used to establish the connection,
+  // the |constraints| are the media constraints used to initialize the
+  // PeerConnection, the |frame| is the WebLocalFrame object representing the
   // page in which the PeerConnection is created.
   void RegisterPeerConnection(
       RTCPeerConnectionHandler* pc_handler,
       const webrtc::PeerConnectionInterface::RTCConfiguration& config,
       const blink::WebMediaConstraints& constraints,
-      const blink::WebFrame* frame);
+      const blink::WebLocalFrame* frame);
 
   // Sends an update when a PeerConnection has been destroyed.
   virtual void UnregisterPeerConnection(RTCPeerConnectionHandler* pc_handler);
diff --git a/content/renderer/media/rtc_peer_connection_handler.cc b/content/renderer/media/rtc_peer_connection_handler.cc
index 91af43c..1afd649 100644
--- a/content/renderer/media/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/rtc_peer_connection_handler.cc
@@ -1133,7 +1133,7 @@
     handler->client_->ReleasePeerConnectionHandler();
 }
 
-void RTCPeerConnectionHandler::associateWithFrame(blink::WebFrame* frame) {
+void RTCPeerConnectionHandler::associateWithFrame(blink::WebLocalFrame* frame) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(frame);
   frame_ = frame;
diff --git a/content/renderer/media/rtc_peer_connection_handler.h b/content/renderer/media/rtc_peer_connection_handler.h
index bd58e2df..77de858f 100644
--- a/content/renderer/media/rtc_peer_connection_handler.h
+++ b/content/renderer/media/rtc_peer_connection_handler.h
@@ -29,7 +29,7 @@
 #include "third_party/WebKit/public/platform/WebRTCStatsResponse.h"
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 class WebRTCAnswerOptions;
 class WebRTCDataChannelHandler;
 class WebRTCLegacyStats;
@@ -102,7 +102,7 @@
   // Destroy all existing RTCPeerConnectionHandler objects.
   static void DestructAllHandlers();
 
-  void associateWithFrame(blink::WebFrame* frame);
+  void associateWithFrame(blink::WebLocalFrame* frame);
 
   // Initialize method only used for unit test.
   bool InitializeForTest(
@@ -252,7 +252,7 @@
   // RenderThreadImpl.
   PeerConnectionDependencyFactory* const dependency_factory_;
 
-  blink::WebFrame* frame_ = nullptr;
+  blink::WebLocalFrame* frame_ = nullptr;
 
   // Map and owners of track adapters. Every track that is in use by the peer
   // connection has an associated blink and webrtc layer representation of it.
diff --git a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc
index 121a0ae..6d395f14 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc
+++ b/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc
@@ -329,7 +329,7 @@
 scoped_refptr<webrtc::PeerConnectionInterface>
 MockPeerConnectionDependencyFactory::CreatePeerConnection(
     const webrtc::PeerConnectionInterface::RTCConfiguration& config,
-    blink::WebFrame* frame,
+    blink::WebLocalFrame* frame,
     webrtc::PeerConnectionObserver* observer) {
   return new rtc::RefCountedObject<MockPeerConnectionImpl>(this, observer);
 }
diff --git a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h b/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h
index f8bdfcc3..7a59178 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h
+++ b/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h
@@ -125,7 +125,7 @@
 
   scoped_refptr<webrtc::PeerConnectionInterface> CreatePeerConnection(
       const webrtc::PeerConnectionInterface::RTCConfiguration& config,
-      blink::WebFrame* frame,
+      blink::WebLocalFrame* frame,
       webrtc::PeerConnectionObserver* observer) override;
   scoped_refptr<webrtc::VideoTrackSourceInterface> CreateVideoTrackSourceProxy(
       webrtc::VideoTrackSourceInterface* source) override;
diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
index 963de65..8305daaa 100644
--- a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
+++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include <memory>
 #include <utility>
 #include <vector>
 
@@ -61,7 +62,7 @@
 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/webrtc/api/audio_codecs/builtin_audio_decoder_factory.h"
 #include "third_party/webrtc/api/audio_codecs/builtin_audio_encoder_factory.h"
 #include "third_party/webrtc/api/mediaconstraintsinterface.h"
@@ -277,14 +278,14 @@
 scoped_refptr<webrtc::PeerConnectionInterface>
 PeerConnectionDependencyFactory::CreatePeerConnection(
     const webrtc::PeerConnectionInterface::RTCConfiguration& config,
-    blink::WebFrame* web_frame,
+    blink::WebLocalFrame* web_frame,
     webrtc::PeerConnectionObserver* observer) {
   CHECK(web_frame);
   CHECK(observer);
   if (!GetPcFactory().get())
     return NULL;
 
-  // Copy the flag from Preference associated with this WebFrame.
+  // Copy the flag from Preference associated with this WebLocalFrame.
   P2PPortAllocator::Config port_config;
   uint16_t min_port = 0;
   uint16_t max_port = 0;
diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.h b/content/renderer/media/webrtc/peer_connection_dependency_factory.h
index dcf5f5ea..049bcc1 100644
--- a/content/renderer/media/webrtc/peer_connection_dependency_factory.h
+++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.h
@@ -33,7 +33,7 @@
 }
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 class WebRTCPeerConnectionHandler;
 class WebRTCPeerConnectionHandlerClient;
 }
@@ -75,11 +75,10 @@
   // Asks the libjingle PeerConnection factory to create a libjingle
   // PeerConnection object.
   // The PeerConnection object is owned by PeerConnectionHandler.
-  virtual scoped_refptr<webrtc::PeerConnectionInterface>
-      CreatePeerConnection(
-          const webrtc::PeerConnectionInterface::RTCConfiguration& config,
-          blink::WebFrame* web_frame,
-          webrtc::PeerConnectionObserver* observer);
+  virtual scoped_refptr<webrtc::PeerConnectionInterface> CreatePeerConnection(
+      const webrtc::PeerConnectionInterface::RTCConfiguration& config,
+      blink::WebLocalFrame* web_frame,
+      webrtc::PeerConnectionObserver* observer);
 
   // Creates a libjingle representation of a Session description. Used by a
   // RTCPeerConnectionHandler instance.
diff --git a/content/renderer/pepper/pepper_file_system_host.cc b/content/renderer/pepper/pepper_file_system_host.cc
index 679b3de..894076f 100644
--- a/content/renderer/pepper/pepper_file_system_host.cc
+++ b/content/renderer/pepper/pepper_file_system_host.cc
@@ -20,7 +20,7 @@
 #include "ppapi/shared_impl/file_type_conversion.h"
 #include "storage/common/fileapi/file_system_util.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebView.h"
 
 namespace content {
@@ -132,12 +132,13 @@
   if (!view)
     return PP_ERROR_FAILED;
 
-  const GURL& url = view->GetWebView()->MainFrame()->GetDocument().Url();
+  url::Origin main_frame_origin(
+      view->GetWebView()->MainFrame()->GetSecurityOrigin());
   const std::string root_name = ppapi::IsolatedFileSystemTypeToRootName(type);
   if (root_name.empty())
     return PP_ERROR_BADARGUMENT;
   root_url_ = GURL(storage::GetIsolatedFileSystemRootURIString(
-      url.GetOrigin(), fsid, root_name));
+      main_frame_origin.GetURL(), fsid, root_name));
   opened_ = true;
   return PP_OK;
 }
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index 9b606fdc..623827f 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -3190,7 +3190,11 @@
 bool PepperPluginInstanceImpl::IsFullPagePlugin() {
   WebLocalFrame* frame = container()->GetDocument().GetFrame();
   return frame->View()->MainFrame()->IsWebLocalFrame() &&
-         frame->View()->MainFrame()->GetDocument().IsPluginDocument();
+         frame->View()
+             ->MainFrame()
+             ->ToWebLocalFrame()
+             ->GetDocument()
+             .IsPluginDocument();
 }
 
 bool PepperPluginInstanceImpl::FlashSetFullscreen(bool fullscreen,
@@ -3319,11 +3323,11 @@
       !containing_document.GetFrame()->View()->MainFrame()) {
     return false;
   }
-  blink::WebDocument main_document =
-      containing_document.GetFrame()->View()->MainFrame()->GetDocument();
+  blink::WebFrame* main_frame =
+      containing_document.GetFrame()->View()->MainFrame();
 
   return containing_document.GetSecurityOrigin().CanAccess(
-      main_document.GetSecurityOrigin());
+      main_frame->GetSecurityOrigin());
 }
 
 void PepperPluginInstanceImpl::KeepSizeAttributesBeforeFullscreen() {
diff --git a/content/renderer/pepper/ppb_graphics_3d_impl.cc b/content/renderer/pepper/ppb_graphics_3d_impl.cc
index 493b7a3..d8b0967 100644
--- a/content/renderer/pepper/ppb_graphics_3d_impl.cc
+++ b/content/renderer/pepper/ppb_graphics_3d_impl.cc
@@ -190,7 +190,7 @@
 #if defined(OS_MACOSX)
         use_image_chromium_ ? GL_TEXTURE_RECTANGLE_ARB : GL_TEXTURE_2D,
 #else
-        use_image_chromium_ ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D,
+        GL_TEXTURE_2D,
 #endif
         size, is_overlay_candidate, false);
     taken_front_buffer_.SetZero();
diff --git a/content/renderer/pepper/url_request_info_util.cc b/content/renderer/pepper/url_request_info_util.cc
index d6ef08e..f01f0b73 100644
--- a/content/renderer/pepper/url_request_info_util.cc
+++ b/content/renderer/pepper/url_request_info_util.cc
@@ -30,7 +30,7 @@
 #include "third_party/WebKit/public/platform/WebURL.h"
 #include "third_party/WebKit/public/platform/WebURLRequest.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "url/gurl.h"
 #include "url/url_util.h"
 
@@ -40,7 +40,7 @@
 using blink::WebData;
 using blink::WebHTTPBody;
 using blink::WebString;
-using blink::WebFrame;
+using blink::WebLocalFrame;
 using blink::WebURL;
 using blink::WebURLRequest;
 
@@ -146,7 +146,7 @@
 
 bool CreateWebURLRequest(PP_Instance instance,
                          URLRequestInfoData* data,
-                         WebFrame* frame,
+                         WebLocalFrame* frame,
                          WebURLRequest* dest) {
   // In the out-of-process case, we've received the URLRequestInfoData
   // from the untrusted plugin and done no validation on it. We need to be
@@ -154,38 +154,37 @@
   if (!ValidateURLRequestData(*data))
     return false;
 
-   std::string name_version;
+  std::string name_version;
 
-   // Allow instance to be 0 or -1 for testing purposes.
-   if (instance && instance != -1) {
-     PepperPluginInstanceImpl* instance_impl =
-         HostGlobals::Get()->GetInstance(instance);
-     if (instance_impl) {
-       name_version = MakeXRequestedWithValue(
-           instance_impl->module()->name(),
-           instance_impl->module()->version());
-     }
-   } else {
-     name_version = "internal_testing_only";
-   }
+  // Allow instance to be 0 or -1 for testing purposes.
+  if (instance && instance != -1) {
+    PepperPluginInstanceImpl* instance_impl =
+        HostGlobals::Get()->GetInstance(instance);
+    if (instance_impl) {
+      name_version = MakeXRequestedWithValue(
+          instance_impl->module()->name(), instance_impl->module()->version());
+    }
+  } else {
+    name_version = "internal_testing_only";
+  }
 
-   dest->SetURL(
-       frame->GetDocument().CompleteURL(WebString::FromUTF8(data->url)));
-   dest->SetDownloadToFile(data->stream_to_file);
-   dest->SetReportUploadProgress(data->record_upload_progress);
+  dest->SetURL(
+      frame->GetDocument().CompleteURL(WebString::FromUTF8(data->url)));
+  dest->SetDownloadToFile(data->stream_to_file);
+  dest->SetReportUploadProgress(data->record_upload_progress);
 
-   if (!data->method.empty())
-     dest->SetHTTPMethod(WebString::FromUTF8(data->method));
+  if (!data->method.empty())
+    dest->SetHTTPMethod(WebString::FromUTF8(data->method));
 
-   dest->SetFirstPartyForCookies(frame->GetDocument().FirstPartyForCookies());
+  dest->SetFirstPartyForCookies(frame->GetDocument().FirstPartyForCookies());
 
-   const std::string& headers = data->headers;
-   if (!headers.empty()) {
-     net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n\r");
-     while (it.GetNext()) {
-       dest->AddHTTPHeaderField(WebString::FromUTF8(it.name()),
-                                WebString::FromUTF8(it.values()));
-     }
+  const std::string& headers = data->headers;
+  if (!headers.empty()) {
+    net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n\r");
+    while (it.GetNext()) {
+      dest->AddHTTPHeaderField(WebString::FromUTF8(it.name()),
+                               WebString::FromUTF8(it.values()));
+    }
   }
 
   // Append the upload data.
diff --git a/content/renderer/pepper/url_request_info_util.h b/content/renderer/pepper/url_request_info_util.h
index 7eabfef..22ab684 100644
--- a/content/renderer/pepper/url_request_info_util.h
+++ b/content/renderer/pepper/url_request_info_util.h
@@ -14,7 +14,7 @@
 }
 
 namespace blink {
-class WebFrame;
+class WebLocalFrame;
 class WebURLRequest;
 }
 
@@ -26,7 +26,7 @@
 // pointers) will be populated by this function on success.
 CONTENT_EXPORT bool CreateWebURLRequest(PP_Instance instance,
                                         ppapi::URLRequestInfoData* data,
-                                        blink::WebFrame* frame,
+                                        blink::WebLocalFrame* frame,
                                         blink::WebURLRequest* dest);
 
 // Returns true if universal access is required to use the given request.
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 4c17102..2222e34 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -4,6 +4,7 @@
 
 #include "content/renderer/render_frame_impl.h"
 
+#include <algorithm>
 #include <map>
 #include <string>
 #include <utility>
@@ -2899,9 +2900,9 @@
 std::unique_ptr<blink::WebContentSettingsClient>
 RenderFrameImpl::CreateWorkerContentSettingsClient() {
   if (!frame_ || !frame_->View())
-    return NULL;
+    return nullptr;
   return GetContentClient()->renderer()->CreateWorkerContentSettingsClient(
-      this, frame_);
+      this);
 }
 
 std::unique_ptr<blink::WebWorkerFetchContext>
@@ -3763,7 +3764,7 @@
       // them different).
       render_view_->Send(new ViewHostMsg_DocumentAvailableInMainFrame(
           render_view_->GetRoutingID(),
-          main_frame->GetDocument().IsPluginDocument()));
+          frame->GetDocument().IsPluginDocument()));
     }
   }
 
@@ -4907,6 +4908,7 @@
     if (render_view_->webview()->MainFrame()->IsWebLocalFrame() &&
         render_view_->webview()
             ->MainFrame()
+            ->ToWebLocalFrame()
             ->GetDocument()
             .IsPluginDocument()) {
       // Reset the zoom levels for plugins.
@@ -6749,7 +6751,7 @@
                                     int ordinal,
                                     const WebRect& selection_rect,
                                     bool final_status_update) {
-  DCHECK(ordinal >= -1);
+  DCHECK_GE(ordinal, -1);
 
   Send(new FrameHostMsg_Find_Reply(routing_id_,
                                    request_id,
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 50d6749..506a5a1a 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1138,7 +1138,8 @@
 bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) {
   WebFrame* main_frame = webview() ? webview()->MainFrame() : NULL;
   if (main_frame && main_frame->IsWebLocalFrame())
-    GetContentClient()->SetActiveURL(main_frame->GetDocument().Url());
+    GetContentClient()->SetActiveURL(
+        main_frame->ToWebLocalFrame()->GetDocument().Url());
 
   // Input IPC messages must not be processed if the RenderView is in
   // swapped out state.
@@ -2238,7 +2239,7 @@
 GURL RenderViewImpl::GetURLForGraphicsContext3D() {
   DCHECK(webview());
   if (webview()->MainFrame()->IsWebLocalFrame())
-    return GURL(webview()->MainFrame()->GetDocument().Url());
+    return GURL(webview()->MainFrame()->ToWebLocalFrame()->GetDocument().Url());
   else
     return GURL("chrome://gpu/RenderViewImpl::CreateGraphicsContext3D");
 }
diff --git a/content/renderer/savable_resources.cc b/content/renderer/savable_resources.cc
index ce7c9cd8a..24bcdb7 100644
--- a/content/renderer/savable_resources.cc
+++ b/content/renderer/savable_resources.cc
@@ -37,10 +37,10 @@
 
 // Returns |true| if |web_frame| contains (or should be assumed to contain)
 // a html document.
-bool DoesFrameContainHtmlDocument(const WebFrame& web_frame,
+bool DoesFrameContainHtmlDocument(WebFrame* web_frame,
                                   const WebElement& element) {
-  if (web_frame.IsWebLocalFrame()) {
-    WebDocument doc = web_frame.GetDocument();
+  if (web_frame->IsWebLocalFrame()) {
+    WebDocument doc = web_frame->ToWebLocalFrame()->GetDocument();
     return doc.IsHTMLDocument() || doc.IsXHTMLDocument();
   }
 
@@ -68,7 +68,7 @@
 
   // See whether to report this element as a subframe.
   WebFrame* web_frame = WebFrame::FromFrameOwnerElement(element);
-  if (web_frame && DoesFrameContainHtmlDocument(*web_frame, element)) {
+  if (web_frame && DoesFrameContainHtmlDocument(web_frame, element)) {
     SavableSubframe subframe;
     subframe.original_url = element_url;
     subframe.routing_id = RenderFrame::GetRoutingIdForWebFrame(web_frame);
@@ -96,7 +96,7 @@
 
 }  // namespace
 
-bool GetSavableResourceLinksForFrame(WebFrame* current_frame,
+bool GetSavableResourceLinksForFrame(WebLocalFrame* current_frame,
                                      SavableResourcesResult* result) {
   // Get current frame's URL.
   GURL current_frame_url = current_frame->GetDocument().Url();
diff --git a/content/renderer/savable_resources.h b/content/renderer/savable_resources.h
index 93e6653..2ccefe0 100644
--- a/content/renderer/savable_resources.h
+++ b/content/renderer/savable_resources.h
@@ -16,7 +16,7 @@
 
 namespace blink {
 class WebElement;
-class WebFrame;
+class WebLocalFrame;
 class WebString;
 }
 
@@ -45,11 +45,11 @@
   DISALLOW_COPY_AND_ASSIGN(SavableResourcesResult);
 };
 
-// Get all savable resource links from specified webframe.
+// Get all the savable resource links from the specified |frame|.
 // Returns true if the saved resources links have been saved successfully.
 // Otherwise returns false (i.e. if the frame contains a non-savable content).
 CONTENT_EXPORT bool GetSavableResourceLinksForFrame(
-    blink::WebFrame* frame,
+    blink::WebLocalFrame* frame,
     SavableResourcesResult* result);
 
 // Returns the value in an elements resource url attribute. For IMG, SCRIPT or
diff --git a/content/shell/browser/layout_test/layout_test_browser_context.cc b/content/shell/browser/layout_test/layout_test_browser_context.cc
index e5adfbfb..3ba2ba7 100644
--- a/content/shell/browser/layout_test/layout_test_browser_context.cc
+++ b/content/shell/browser/layout_test/layout_test_browser_context.cc
@@ -97,7 +97,6 @@
   return new LayoutTestURLRequestContextGetter(
       ignore_certificate_errors(), GetPath(),
       BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE),
       protocol_handlers, std::move(request_interceptors), net_log());
 }
 
diff --git a/content/shell/browser/layout_test/layout_test_url_request_context_getter.cc b/content/shell/browser/layout_test/layout_test_url_request_context_getter.cc
index ff6b6b3..0421518 100644
--- a/content/shell/browser/layout_test/layout_test_url_request_context_getter.cc
+++ b/content/shell/browser/layout_test/layout_test_url_request_context_getter.cc
@@ -19,14 +19,12 @@
     bool ignore_certificate_errors,
     const base::FilePath& base_path,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
     ProtocolHandlerMap* protocol_handlers,
     URLRequestInterceptorScopedVector request_interceptors,
     net::NetLog* net_log)
     : ShellURLRequestContextGetter(ignore_certificate_errors,
                                    base_path,
                                    std::move(io_task_runner),
-                                   std::move(file_task_runner),
                                    protocol_handlers,
                                    std::move(request_interceptors),
                                    net_log) {
diff --git a/content/shell/browser/layout_test/layout_test_url_request_context_getter.h b/content/shell/browser/layout_test/layout_test_url_request_context_getter.h
index fe48ceb..1fc56da 100644
--- a/content/shell/browser/layout_test/layout_test_url_request_context_getter.h
+++ b/content/shell/browser/layout_test/layout_test_url_request_context_getter.h
@@ -30,7 +30,6 @@
       bool ignore_certificate_errors,
       const base::FilePath& base_path,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-      scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
       ProtocolHandlerMap* protocol_handlers,
       URLRequestInterceptorScopedVector request_interceptors,
       net::NetLog* net_log);
diff --git a/content/shell/browser/shell_browser_context.cc b/content/shell/browser/shell_browser_context.cc
index 957d746..870e9233 100644
--- a/content/shell/browser/shell_browser_context.cc
+++ b/content/shell/browser/shell_browser_context.cc
@@ -156,7 +156,6 @@
   return new ShellURLRequestContextGetter(
       ignore_certificate_errors_, GetPath(),
       BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
-      BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE),
       protocol_handlers, std::move(request_interceptors), net_log_);
 }
 
diff --git a/content/shell/browser/shell_url_request_context_getter.cc b/content/shell/browser/shell_url_request_context_getter.cc
index dcad965..d161f06 100644
--- a/content/shell/browser/shell_url_request_context_getter.cc
+++ b/content/shell/browser/shell_url_request_context_getter.cc
@@ -88,14 +88,12 @@
     bool ignore_certificate_errors,
     const base::FilePath& base_path,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
     ProtocolHandlerMap* protocol_handlers,
     URLRequestInterceptorScopedVector request_interceptors,
     net::NetLog* net_log)
     : ignore_certificate_errors_(ignore_certificate_errors),
       base_path_(base_path),
       io_task_runner_(std::move(io_task_runner)),
-      file_task_runner_(std::move(file_task_runner)),
       net_log_(net_log),
       request_interceptors_(std::move(request_interceptors)) {
   // Must first be created on the UI thread.
@@ -119,8 +117,7 @@
 
 std::unique_ptr<net::ProxyConfigService>
 ShellURLRequestContextGetter::GetProxyConfigService() {
-  return net::ProxyService::CreateSystemProxyConfigService(io_task_runner_,
-                                                           file_task_runner_);
+  return net::ProxyService::CreateSystemProxyConfigService(io_task_runner_);
 }
 
 std::unique_ptr<net::ProxyService>
diff --git a/content/shell/browser/shell_url_request_context_getter.h b/content/shell/browser/shell_url_request_context_getter.h
index 75c1619..fdfdac2 100644
--- a/content/shell/browser/shell_url_request_context_getter.h
+++ b/content/shell/browser/shell_url_request_context_getter.h
@@ -34,7 +34,6 @@
       bool ignore_certificate_errors,
       const base::FilePath& base_path,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-      scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
       ProtocolHandlerMap* protocol_handlers,
       URLRequestInterceptorScopedVector request_interceptors,
       net::NetLog* net_log);
@@ -59,7 +58,6 @@
   bool ignore_certificate_errors_;
   base::FilePath base_path_;
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
-  scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
   net::NetLog* net_log_;
 
   std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
diff --git a/content/shell/renderer/layout_test/blink_test_runner.cc b/content/shell/renderer/layout_test/blink_test_runner.cc
index 09c91b67..18ed153 100644
--- a/content/shell/renderer/layout_test/blink_test_runner.cc
+++ b/content/shell/renderer/layout_test/blink_test_runner.cc
@@ -10,6 +10,7 @@
 #include <clocale>
 #include <cmath>
 #include <memory>
+#include <string>
 #include <utility>
 
 #include "base/base64.h"
@@ -1050,8 +1051,12 @@
 
 void BlinkTestRunner::OnTryLeakDetection() {
   blink::WebFrame* main_frame = render_view()->GetWebView()->MainFrame();
-  DCHECK_EQ(GURL(url::kAboutBlankURL), GURL(main_frame->GetDocument().Url()));
+
   DCHECK(!main_frame->IsLoading());
+  if (main_frame->IsWebLocalFrame()) {
+    DCHECK_EQ(GURL(url::kAboutBlankURL),
+              GURL(main_frame->ToWebLocalFrame()->GetDocument().Url()));
+  }
 
   leak_detector_->TryLeakDetection(main_frame);
 }
diff --git a/content/shell/test_runner/accessibility_controller.cc b/content/shell/test_runner/accessibility_controller.cc
index 233f8f0..639655fc 100644
--- a/content/shell/test_runner/accessibility_controller.cc
+++ b/content/shell/test_runner/accessibility_controller.cc
@@ -4,6 +4,8 @@
 
 #include "content/shell/test_runner/accessibility_controller.h"
 
+#include <string>
+
 #include "base/macros.h"
 #include "content/shell/test_runner/web_view_test_proxy.h"
 #include "gin/handle.h"
@@ -216,23 +218,25 @@
   if (!frame)
     return v8::Local<v8::Object>();
 
+  // TODO(lukasza): Finish adding OOPIF support to the layout tests harness.
+  CHECK(frame->IsWebLocalFrame())
+      << "This function cannot be called if the main frame is not a "
+         "local frame.";
   blink::WebAXObject focused_element =
-      blink::WebAXObject::FromWebDocumentFocused(frame->GetDocument());
+      blink::WebAXObject::FromWebDocumentFocused(
+          frame->ToWebLocalFrame()->GetDocument());
   if (focused_element.IsNull())
-    focused_element = blink::WebAXObject::FromWebView(*web_view());
+    focused_element = GetAccessibilityObjectForMainFrame();
   return elements_.GetOrCreate(focused_element);
 }
 
 v8::Local<v8::Object> AccessibilityController::RootElement() {
-  blink::WebAXObject root_element =
-      blink::WebAXObject::FromWebView(*web_view());
-  return elements_.GetOrCreate(root_element);
+  return elements_.GetOrCreate(GetAccessibilityObjectForMainFrame());
 }
 
 v8::Local<v8::Object> AccessibilityController::AccessibleElementById(
     const std::string& id) {
-  blink::WebAXObject root_element =
-      blink::WebAXObject::FromWebView(*web_view());
+  blink::WebAXObject root_element = GetAccessibilityObjectForMainFrame();
 
   if (!root_element.UpdateLayoutAndCheckValidity())
     return v8::Local<v8::Object>();
@@ -271,4 +275,16 @@
   return web_view_test_proxy_base_->web_view();
 }
 
+blink::WebAXObject
+AccessibilityController::GetAccessibilityObjectForMainFrame() {
+  blink::WebFrame* frame = web_view()->MainFrame();
+
+  // TODO(lukasza): Finish adding OOPIF support to the layout tests harness.
+  CHECK(frame && frame->IsWebLocalFrame())
+      << "This function cannot be called if the main frame is not a "
+         "local frame.";
+  return blink::WebAXObject::FromWebDocument(
+      web_view()->MainFrame()->ToWebLocalFrame()->GetDocument());
+}
+
 }  // namespace test_runner
diff --git a/content/shell/test_runner/accessibility_controller.h b/content/shell/test_runner/accessibility_controller.h
index ab35a90..17219e1 100644
--- a/content/shell/test_runner/accessibility_controller.h
+++ b/content/shell/test_runner/accessibility_controller.h
@@ -50,6 +50,8 @@
       const blink::WebAXObject&,
       const blink::WebString& id);
 
+  blink::WebAXObject GetAccessibilityObjectForMainFrame();
+
   // If true, will log all accessibility notifications.
   bool log_accessibility_events_;
 
diff --git a/content/shell/test_runner/pixel_dump.cc b/content/shell/test_runner/pixel_dump.cc
index 7711760..fe3459b 100644
--- a/content/shell/test_runner/pixel_dump.cc
+++ b/content/shell/test_runner/pixel_dump.cc
@@ -75,10 +75,13 @@
   // The rect should be drawn after everything is laid out and painted.
   if (!dump_request.layout_test_runtime_flags.dump_selection_rect())
     return;
-  // If there is a selection rect - draw a red 1px border enclosing rect
+
+  // TODO(lukasza): https://crbug.com/667551: Support OOPIFs in pixel dumps.
   CHECK(dump_request.web_view->MainFrame()->IsWebLocalFrame())
       << "This function cannot be called if the main frame is not a "
          "local frame.";
+
+  // If there is a selection rect - draw a red 1px border enclosing rect
   blink::WebRect wr = dump_request.web_view->MainFrame()
                           ->ToWebLocalFrame()
                           ->GetSelectionBoundsRectForTesting();
@@ -100,6 +103,7 @@
 
   blink::WebSize page_size_in_pixels = dump_request->web_view->Size();
 
+  // TODO(lukasza): https://crbug.com/667551: Support OOPIFs in pixel dumps.
   CHECK(dump_request->web_view->MainFrame()->IsWebLocalFrame())
       << "This function cannot be called if the main frame is not a "
          "local frame.";
diff --git a/content/shell/test_runner/test_runner.cc b/content/shell/test_runner/test_runner.cc
index 9763472d..14d4915 100644
--- a/content/shell/test_runner/test_runner.cc
+++ b/content/shell/test_runner/test_runner.cc
@@ -5,6 +5,8 @@
 #include "content/shell/test_runner/test_runner.h"
 
 #include <stddef.h>
+
+#include <algorithm>
 #include <limits>
 #include <utility>
 
@@ -1999,7 +2001,7 @@
 
 class WorkItemBackForward : public TestRunner::WorkItem {
  public:
-  WorkItemBackForward(int distance) : distance_(distance) {}
+  explicit WorkItemBackForward(int distance) : distance_(distance) {}
 
   bool Run(WebTestDelegate* delegate, WebView*) override {
     delegate->GoToOffset(distance_);
@@ -2037,7 +2039,7 @@
 
 class WorkItemLoadingScript : public TestRunner::WorkItem {
  public:
-  WorkItemLoadingScript(const std::string& script) : script_(script) {}
+  explicit WorkItemLoadingScript(const std::string& script) : script_(script) {}
 
   bool Run(WebTestDelegate*, WebView* web_view) override {
     blink::WebFrame* main_frame = web_view->MainFrame();
@@ -2061,7 +2063,8 @@
 
 class WorkItemNonLoadingScript : public TestRunner::WorkItem {
  public:
-  WorkItemNonLoadingScript(const std::string& script) : script_(script) {}
+  explicit WorkItemNonLoadingScript(const std::string& script)
+      : script_(script) {}
 
   bool Run(WebTestDelegate*, WebView* web_view) override {
     blink::WebFrame* main_frame = web_view->MainFrame();
@@ -2102,8 +2105,17 @@
   if (!main_view_)
     return;
 
+  // TODO(lukasza): testRunner.queueLoad(...) should work even if the main frame
+  // is remote (ideally testRunner.queueLoad would bind to and execute in the
+  // context of a specific local frame - resolving relative urls should be done
+  // on relative to the calling frame's url).
+  CHECK(main_view_->MainFrame()->IsWebLocalFrame())
+      << "This function cannot be called if the main frame is not "
+         "a local frame.";
+
   // FIXME: Implement WebURL::resolve() and avoid GURL.
-  GURL current_url = main_view_->MainFrame()->GetDocument().Url();
+  GURL current_url =
+      main_view_->MainFrame()->ToWebLocalFrame()->GetDocument().Url();
   GURL full_url = current_url.Resolve(url);
   work_queue_.AddWork(new WorkItemLoad(full_url, target));
 }
diff --git a/content/shell/test_runner/test_runner_for_specific_view.cc b/content/shell/test_runner/test_runner_for_specific_view.cc
index 461646c1..2cd0214 100644
--- a/content/shell/test_runner/test_runner_for_specific_view.cc
+++ b/content/shell/test_runner/test_runner_for_specific_view.cc
@@ -298,11 +298,17 @@
 
 void TestRunnerForSpecificView::GetManifestThen(
     v8::Local<v8::Function> callback) {
+  if (!web_view()->MainFrame()->IsWebLocalFrame()) {
+    CHECK(false) << "This function cannot be called if the main frame is not a "
+                    "local frame.";
+  }
+
   v8::UniquePersistent<v8::Function> persistent_callback(
       blink::MainThreadIsolate(), callback);
 
   delegate()->FetchManifest(
-      web_view(), web_view()->MainFrame()->GetDocument().ManifestURL(),
+      web_view(),
+      web_view()->MainFrame()->ToWebLocalFrame()->GetDocument().ManifestURL(),
       base::Bind(&TestRunnerForSpecificView::GetManifestCallback,
                  weak_factory_.GetWeakPtr(),
                  base::Passed(std::move(persistent_callback))));
diff --git a/content/shell/test_runner/text_input_controller.cc b/content/shell/test_runner/text_input_controller.cc
index 672c7e419..9e7d7ba 100644
--- a/content/shell/test_runner/text_input_controller.cc
+++ b/content/shell/test_runner/text_input_controller.cc
@@ -322,6 +322,7 @@
 }
 
 void TextInputController::ForceTextInputStateUpdate() {
+  // TODO(lukasza): Finish adding OOPIF support to the layout tests harness.
   CHECK(view()->MainFrame()->IsWebLocalFrame())
       << "WebView does not have a local main frame and"
          " cannot handle input method controller tasks.";
@@ -338,6 +339,7 @@
   if (!view()->MainFrame())
     return nullptr;
 
+  // TODO(lukasza): Finish adding OOPIF support to the layout tests harness.
   CHECK(view()->MainFrame()->IsWebLocalFrame())
       << "WebView does not have a local main frame and"
          " cannot handle input method controller tasks.";
diff --git a/content/test/layouttest_support.cc b/content/test/layouttest_support.cc
index 64cbf87..3d11b82 100644
--- a/content/test/layouttest_support.cc
+++ b/content/test/layouttest_support.cc
@@ -5,6 +5,9 @@
 #include "content/public/test/layouttest_support.h"
 
 #include <stddef.h>
+
+#include <algorithm>
+#include <unordered_map>
 #include <utility>
 
 #include "base/callback.h"
@@ -260,7 +263,10 @@
   // A raw pointer is used instead of a scoped_ptr as base::Passes passes
   // ownership and thus nulls the scoped_ptr. On MSVS this happens before
   // the call to Start, resulting in a crash.
-  fetcher->Start(view->MainFrame(), false,
+  CHECK(view->MainFrame()->IsWebLocalFrame())
+      << "This function cannot be called if the main frame is not a "
+         "local frame.";
+  fetcher->Start(view->MainFrame()->ToWebLocalFrame(), false,
                  base::Bind(&FetchManifestDoneCallback,
                             base::Passed(&autodeleter), callback));
 }
@@ -551,7 +557,7 @@
   const blink::WebHistoryItem& item = node->item();
   if (is_current_index) {
     result.append("curr->");
-    result.append(indent - 6, ' '); // 6 == "curr->".length()
+    result.append(indent - 6, ' ');  // 6 == "curr->".length()
   } else {
     result.append(indent, ' ');
   }
diff --git a/docs/memory-infra/heap_profiler.md b/docs/memory-infra/heap_profiler.md
index 0910b64..7601867 100644
--- a/docs/memory-infra/heap_profiler.md
+++ b/docs/memory-infra/heap_profiler.md
@@ -38,20 +38,47 @@
 By default heap profiling collects pseudo allocation traces, which are based
 on trace events. I.e. frames in allocation traces correspond to trace events
 that were active at the time of allocations, and are not real function names.
-However, you can build a special Linux / Android build that will collect
-real C/C++ stack traces.
+It's also possible to use heap profiling with native, symbolized stack traces.
+
+#### Native stack traces (Chrome - macOS/Windows)
+
+1. Using any officially distributed build of Chrome, navigate to chrome://flags,
+   and set "enable-heap-profiling" to Enabled (native mode).
+
+2. Use the [TraceOnTap][extension-link] extension to grab a trace.
+
+3. Run the following script to symbolize the trace.
+
+        third_party/catapult/tracing/bin/symbolize_trace <trace file>
+
+4. Load the trace file in `chrome://tracing`. Locate a purple ![M][m-purple]
+   dot, and continue from step *3* from the instructions above. Native stack
+   traces will be shown in the _Heap Details_ pane.
+
+[extension-link]: https://cs.chromium.org/chromium/src/third_party/catapult/experimental/trace_on_tap/?q=traceontap+package:%5Echromium$&dr=CSs
+
+#### Native stack traces (Chromium - all OSes)
+
+On Linux / Android, you need to build Chromium with special flags to use native
+heap profiling. On macOS / Windows, it's also possible to use native heap
+profiling with Chromium.
 
  1. Build with the following GN flags:
 
-	Linux
+	macOS / Windows
+
+        symbol_level = 1
+
+  Linux
 
         enable_profiling = true
-
+        symbol_level = 1
 
 	Android
 
         arm_use_thumb = false
         enable_profiling = true
+        symbol_level = 1
 
  2. Start Chrome with `--enable-heap-profiling=native` switch (notice
  	`=native` part).
diff --git a/extensions/renderer/api/automation/automation_api_helper.cc b/extensions/renderer/api/automation/automation_api_helper.cc
index 45c6202..bcb4ad8 100644
--- a/extensions/renderer/api/automation/automation_api_helper.cc
+++ b/extensions/renderer/api/automation/automation_api_helper.cc
@@ -10,7 +10,7 @@
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebElement.h"
 #include "third_party/WebKit/public/web/WebExceptionCode.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebNode.h"
 #include "third_party/WebKit/public/web/WebView.h"
 
@@ -47,8 +47,15 @@
         routing_id(), request_id, error, 0));
     return;
   }
-  blink::WebDocument document =
-      render_view()->GetWebView()->MainFrame()->GetDocument();
+
+  // ExtensionMsg_AutomationQuerySelector should only be sent to an active view.
+  DCHECK(render_view()->GetWebView()->MainFrame()->IsWebLocalFrame());
+
+  blink::WebDocument document = render_view()
+                                    ->GetWebView()
+                                    ->MainFrame()
+                                    ->ToWebLocalFrame()
+                                    ->GetDocument();
   if (document.IsNull()) {
     error.value = ExtensionHostMsg_AutomationQuerySelector_Error::kNoDocument;
     Send(new ExtensionHostMsg_AutomationQuerySelector_Result(
diff --git a/extensions/renderer/script_context.cc b/extensions/renderer/script_context.cc
index 197e6c0..9a60177a 100644
--- a/extensions/renderer/script_context.cc
+++ b/extensions/renderer/script_context.cc
@@ -300,7 +300,7 @@
 }
 
 // static
-GURL ScriptContext::GetEffectiveDocumentURL(const blink::WebLocalFrame* frame,
+GURL ScriptContext::GetEffectiveDocumentURL(blink::WebLocalFrame* frame,
                                             const GURL& document_url,
                                             bool match_about_blank) {
   // Common scenario. If |match_about_blank| is false (as is the case in most
@@ -312,7 +312,8 @@
   // Non-sandboxed about:blank and about:srcdoc pages inherit their security
   // origin from their parent frame/window. So, traverse the frame/window
   // hierarchy to find the closest non-about:-page and return its URL.
-  const blink::WebFrame* parent = frame;
+  blink::WebFrame* parent = frame;
+  blink::WebDocument parent_document;
   do {
     if (parent->Parent())
       parent = parent->Parent();
@@ -320,12 +321,15 @@
       parent = parent->Opener();
     else
       parent = nullptr;
-  } while (parent && !parent->GetDocument().IsNull() &&
-           GURL(parent->GetDocument().Url()).SchemeIs(url::kAboutScheme));
 
-  if (parent && !parent->GetDocument().IsNull()) {
+    parent_document = parent && parent->IsWebLocalFrame()
+                          ? parent->ToWebLocalFrame()->GetDocument()
+                          : blink::WebDocument();
+  } while (!parent_document.IsNull() &&
+           GURL(parent_document.Url()).SchemeIs(url::kAboutScheme));
+
+  if (!parent_document.IsNull()) {
     // Only return the parent URL if the frame can access it.
-    const blink::WebDocument& parent_document = parent->GetDocument();
     if (frame->GetDocument().GetSecurityOrigin().CanAccess(
             parent_document.GetSecurityOrigin())) {
       return parent_document.Url();
diff --git a/extensions/renderer/script_context.h b/extensions/renderer/script_context.h
index c140894..63a17d0 100644
--- a/extensions/renderer/script_context.h
+++ b/extensions/renderer/script_context.h
@@ -177,7 +177,7 @@
   // Returns the first non-about:-URL in the document hierarchy above and
   // including |frame|. The document hierarchy is only traversed if
   // |document_url| is an about:-URL and if |match_about_blank| is true.
-  static GURL GetEffectiveDocumentURL(const blink::WebLocalFrame* frame,
+  static GURL GetEffectiveDocumentURL(blink::WebLocalFrame* frame,
                                       const GURL& document_url,
                                       bool match_about_blank);
 
diff --git a/extensions/renderer/script_context_browsertest.cc b/extensions/renderer/script_context_browsertest.cc
index d7047ef3..c436106 100644
--- a/extensions/renderer/script_context_browsertest.cc
+++ b/extensions/renderer/script_context_browsertest.cc
@@ -17,7 +17,7 @@
 
 class ScriptContextTest : public ChromeRenderViewTest {
  protected:
-  GURL GetEffectiveDocumentURL(const WebLocalFrame* frame) {
+  GURL GetEffectiveDocumentURL(WebLocalFrame* frame) {
     return ScriptContext::GetEffectiveDocumentURL(
         frame, frame->GetDocument().Url(), true);
   }
diff --git a/extensions/renderer/script_context_set.cc b/extensions/renderer/script_context_set.cc
index 2b0875d9..226bc118 100644
--- a/extensions/renderer/script_context_set.cc
+++ b/extensions/renderer/script_context_set.cc
@@ -131,7 +131,7 @@
 }
 
 const Extension* ScriptContextSet::GetExtensionFromFrameAndWorld(
-    const blink::WebLocalFrame* frame,
+    blink::WebLocalFrame* frame,
     int world_id,
     bool use_effective_url) {
   std::string extension_id;
diff --git a/extensions/renderer/script_context_set.h b/extensions/renderer/script_context_set.h
index 9097038..1c600ac 100644
--- a/extensions/renderer/script_context_set.h
+++ b/extensions/renderer/script_context_set.h
@@ -118,10 +118,9 @@
   // specified |frame| and isolated world. If |world_id| is zero, finds the
   // extension ID associated with the main world's JavaScript context. If the
   // JavaScript context isn't from an extension, returns empty string.
-  const Extension* GetExtensionFromFrameAndWorld(
-      const blink::WebLocalFrame* frame,
-      int world_id,
-      bool use_effective_url);
+  const Extension* GetExtensionFromFrameAndWorld(blink::WebLocalFrame* frame,
+                                                 int world_id,
+                                                 bool use_effective_url);
 
   // Returns the Feature::Context type of context for a JavaScript context.
   Feature::Context ClassifyJavaScriptContext(
diff --git a/extensions/shell/browser/shell_browser_context.cc b/extensions/shell/browser/shell_browser_context.cc
index b1a6676..c147c92 100644
--- a/extensions/shell/browser/shell_browser_context.cc
+++ b/extensions/shell/browser/shell_browser_context.cc
@@ -68,8 +68,6 @@
       this, IgnoreCertificateErrors(), GetPath(),
       content::BrowserThread::GetTaskRunnerForThread(
           content::BrowserThread::IO),
-      content::BrowserThread::GetTaskRunnerForThread(
-          content::BrowserThread::FILE),
       protocol_handlers, std::move(request_interceptors), nullptr /* net_log */,
       extension_info_map));
   resource_context_->set_url_request_context_getter(
diff --git a/extensions/shell/browser/shell_url_request_context_getter.cc b/extensions/shell/browser/shell_url_request_context_getter.cc
index 654f468c..757e53a 100644
--- a/extensions/shell/browser/shell_url_request_context_getter.cc
+++ b/extensions/shell/browser/shell_url_request_context_getter.cc
@@ -18,7 +18,6 @@
     bool ignore_certificate_errors,
     const base::FilePath& base_path,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
     content::ProtocolHandlerMap* protocol_handlers,
     content::URLRequestInterceptorScopedVector request_interceptors,
     net::NetLog* net_log,
@@ -26,7 +25,6 @@
     : content::ShellURLRequestContextGetter(ignore_certificate_errors,
                                             base_path,
                                             std::move(io_task_runner),
-                                            std::move(file_task_runner),
                                             protocol_handlers,
                                             std::move(request_interceptors),
                                             net_log),
diff --git a/extensions/shell/browser/shell_url_request_context_getter.h b/extensions/shell/browser/shell_url_request_context_getter.h
index 33cfe891..3fe6fd04 100644
--- a/extensions/shell/browser/shell_url_request_context_getter.h
+++ b/extensions/shell/browser/shell_url_request_context_getter.h
@@ -31,7 +31,6 @@
       bool ignore_certificate_errors,
       const base::FilePath& base_path,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-      scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
       content::ProtocolHandlerMap* protocol_handlers,
       content::URLRequestInterceptorScopedVector request_interceptors,
       net::NetLog* net_log,
diff --git a/gpu/ipc/service/gpu_memory_buffer_factory_native_pixmap.cc b/gpu/ipc/service/gpu_memory_buffer_factory_native_pixmap.cc
index cc08eb86..4bb54f3 100644
--- a/gpu/ipc/service/gpu_memory_buffer_factory_native_pixmap.cc
+++ b/gpu/ipc/service/gpu_memory_buffer_factory_native_pixmap.cc
@@ -157,7 +157,7 @@
 }
 
 unsigned GpuMemoryBufferFactoryNativePixmap::RequiredTextureType() {
-  return GL_TEXTURE_EXTERNAL_OES;
+  return GL_TEXTURE_2D;
 }
 
 }  // namespace gpu
diff --git a/headless/lib/browser/headless_url_request_context_getter.cc b/headless/lib/browser/headless_url_request_context_getter.cc
index cf3d9b3..ef04cb92 100644
--- a/headless/lib/browser/headless_url_request_context_getter.cc
+++ b/headless/lib/browser/headless_url_request_context_getter.cc
@@ -51,8 +51,8 @@
   // must synchronously run on the glib message loop. This will be passed to
   // the URLRequestContextStorage on the IO thread in GetURLRequestContext().
   if (proxy_server_.IsEmpty()) {
-    proxy_config_service_ = net::ProxyService::CreateSystemProxyConfigService(
-        io_task_runner_, file_task_runner_);
+    proxy_config_service_ =
+        net::ProxyService::CreateSystemProxyConfigService(io_task_runner_);
   }
 }
 
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h
index 1f4e92fd..7aa390d 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.h
@@ -44,7 +44,6 @@
 
   net::URLRequestContextGetter* GetURLRequestContext() override;
   bool IsMetricsReportingEnabled() override;
-  bool IsIncognitoSessionActive() override;
 
   // Gets the MetricsStateManager, creating it if it has not already been
   // created.
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm
index e6693d3c..d45a42d0 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_services_manager_client.mm
@@ -109,11 +109,3 @@
   }
   return metrics_state_manager_.get();
 }
-
-bool IOSChromeMetricsServicesManagerClient::IsIncognitoSessionActive() {
-  // return ::IsOffTheRecordSessionActive();
-  // TODO(crbug.com/734091): Conservatively set to true until there is a test
-  // to ensure it gets re-queried when an incognito tab is opened.  This
-  // effectively disables UKM.
-  return true;
-}
diff --git a/ios/chrome/browser/translate/chrome_ios_translate_client.h b/ios/chrome/browser/translate/chrome_ios_translate_client.h
index 94de42c..0a0ebeb 100644
--- a/ios/chrome/browser/translate/chrome_ios_translate_client.h
+++ b/ios/chrome/browser/translate/chrome_ios_translate_client.h
@@ -26,6 +26,8 @@
 class TranslateAcceptLanguages;
 class TranslatePrefs;
 class TranslateManager;
+
+struct LanguageDetectionDetails;
 }  // namespace translate
 
 namespace web {
@@ -52,6 +54,9 @@
   std::unique_ptr<translate::TranslatePrefs> GetTranslatePrefs() override;
   translate::TranslateAcceptLanguages* GetTranslateAcceptLanguages() override;
   int GetInfobarIconID() const override;
+  // Record language detection event.
+  void RecordLanguageDetectionEvent(
+      const translate::LanguageDetectionDetails& details) const override;
   void RecordTranslateEvent(const metrics::TranslateEventProto&) override;
   std::unique_ptr<infobars::InfoBar> CreateInfoBar(
       std::unique_ptr<translate::TranslateInfoBarDelegate> delegate)
diff --git a/ios/chrome/browser/translate/chrome_ios_translate_client.mm b/ios/chrome/browser/translate/chrome_ios_translate_client.mm
index b374335..623ad385 100644
--- a/ios/chrome/browser/translate/chrome_ios_translate_client.mm
+++ b/ios/chrome/browser/translate/chrome_ios_translate_client.mm
@@ -18,6 +18,7 @@
 #include "components/translate/core/browser/translate_manager.h"
 #include "components/translate/core/browser/translate_prefs.h"
 #include "components/translate/core/browser/translate_step.h"
+#include "components/translate/core/common/language_detection_details.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/infobars/infobar.h"
 #include "ios/chrome/browser/infobars/infobar_controller.h"
@@ -156,6 +157,11 @@
   return IDR_IOS_INFOBAR_TRANSLATE;
 }
 
+void ChromeIOSTranslateClient::RecordLanguageDetectionEvent(
+    const translate::LanguageDetectionDetails& details) const {
+  // TODO(crbug.com/728491): Implement this.
+}
+
 bool ChromeIOSTranslateClient::IsTranslatableURL(const GURL& url) {
   return TranslateServiceIOS::IsTranslatableURL(url);
 }
diff --git a/ios/crnet/crnet_environment.mm b/ios/crnet/crnet_environment.mm
index f47dafe..02bfd737 100644
--- a/ios/crnet/crnet_environment.mm
+++ b/ios/crnet/crnet_environment.mm
@@ -300,7 +300,7 @@
   // delegates will receive callbacks.
   network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
   proxy_config_service_ = net::ProxyService::CreateSystemProxyConfigService(
-      network_io_thread_->task_runner(), nullptr);
+      network_io_thread_->task_runner());
 
   main_context_getter_ = new CrNetURLRequestContextGetter(
       main_context_.get(), network_io_thread_->task_runner());
diff --git a/ios/web/navigation/legacy_navigation_manager_impl.h b/ios/web/navigation/legacy_navigation_manager_impl.h
index 04951cf..1be4fc5 100644
--- a/ios/web/navigation/legacy_navigation_manager_impl.h
+++ b/ios/web/navigation/legacy_navigation_manager_impl.h
@@ -56,6 +56,7 @@
   GetTransientURLRewriters() override;
   void RemoveTransientURLRewriters() override;
   int GetIndexForOffset(int offset) const override;
+  int GetPreviousItemIndex() const override;
 
   // NavigationManager:
   BrowserState* GetBrowserState() const override;
@@ -93,7 +94,6 @@
 
   // NavigationManagerImpl methods used by SessionStorageBuilder.
   NavigationItemImpl* GetNavigationItemImplAtIndex(size_t index) const override;
-  size_t GetPreviousItemIndex() const override;
 
   // Returns true if the PageTransition for the underlying navigation item at
   // |index| has ui::PAGE_TRANSITION_IS_REDIRECT_MASK.
diff --git a/ios/web/navigation/legacy_navigation_manager_impl.mm b/ios/web/navigation/legacy_navigation_manager_impl.mm
index 8609620..ad44adc 100644
--- a/ios/web/navigation/legacy_navigation_manager_impl.mm
+++ b/ios/web/navigation/legacy_navigation_manager_impl.mm
@@ -456,8 +456,8 @@
   return nullptr;
 }
 
-size_t LegacyNavigationManagerImpl::GetPreviousItemIndex() const {
-  return [session_controller_ previousItemIndex];
+int LegacyNavigationManagerImpl::GetPreviousItemIndex() const {
+  return base::checked_cast<int>([session_controller_ previousItemIndex]);
 }
 
 }  // namespace web
diff --git a/ios/web/navigation/navigation_manager_impl.h b/ios/web/navigation/navigation_manager_impl.h
index 0e2a9e3..92f7fbb 100644
--- a/ios/web/navigation/navigation_manager_impl.h
+++ b/ios/web/navigation/navigation_manager_impl.h
@@ -124,6 +124,9 @@
   // moved from CRWWebController to NavigationManagerImpl.
   virtual int GetIndexForOffset(int offset) const = 0;
 
+  // Returns the index of the previous item. Only used by SessionStorageBuilder.
+  virtual int GetPreviousItemIndex() const = 0;
+
  protected:
   // The SessionStorageBuilder functions require access to private variables of
   // NavigationManagerImpl.
@@ -134,9 +137,6 @@
   // SessionStorageBuilder to persist session state.
   virtual NavigationItemImpl* GetNavigationItemImplAtIndex(
       size_t index) const = 0;
-
-  // Returns the index of the previous item. Only used by SessionStorageBuilder.
-  virtual size_t GetPreviousItemIndex() const = 0;
 };
 
 }  // namespace web
diff --git a/ios/web/navigation/navigation_manager_impl_unittest.mm b/ios/web/navigation/navigation_manager_impl_unittest.mm
index e57607ae..91b4491c 100644
--- a/ios/web/navigation/navigation_manager_impl_unittest.mm
+++ b/ios/web/navigation/navigation_manager_impl_unittest.mm
@@ -103,6 +103,7 @@
   EXPECT_FALSE(navigation_manager()->GetPendingItem());
   EXPECT_EQ(-1, navigation_manager()->GetPendingItemIndex());
   EXPECT_EQ(-1, navigation_manager()->GetIndexForOffset(0));
+  EXPECT_EQ(-1, navigation_manager()->GetPreviousItemIndex());
 }
 
 // Tests that GetPendingItemIndex() returns -1 if there is no pending entry.
diff --git a/ios/web/navigation/session_storage_builder.mm b/ios/web/navigation/session_storage_builder.mm
index 323fd82..621b5b8a 100644
--- a/ios/web/navigation/session_storage_builder.mm
+++ b/ios/web/navigation/session_storage_builder.mm
@@ -46,7 +46,7 @@
   session_storage.lastCommittedItemIndex =
       navigation_manager->GetLastCommittedItemIndex();
   session_storage.previousItemIndex =
-      static_cast<NSInteger>(navigation_manager->GetPreviousItemIndex());
+      navigation_manager->GetPreviousItemIndex();
   NSMutableArray* item_storages = [[NSMutableArray alloc] init];
   NavigationItemStorageBuilder item_storage_builder;
   for (size_t index = 0;
diff --git a/ios/web_view/internal/translate/web_view_translate_client.h b/ios/web_view/internal/translate/web_view_translate_client.h
index d38a71d..8af4620 100644
--- a/ios/web_view/internal/translate/web_view_translate_client.h
+++ b/ios/web_view/internal/translate/web_view_translate_client.h
@@ -28,6 +28,8 @@
 class TranslateAcceptLanguages;
 class TranslatePrefs;
 class TranslateManager;
+
+struct LanguageDetectionDetails;
 }  // namespace translate
 
 namespace web {
@@ -64,6 +66,8 @@
   std::unique_ptr<translate::TranslatePrefs> GetTranslatePrefs() override;
   translate::TranslateAcceptLanguages* GetTranslateAcceptLanguages() override;
   int GetInfobarIconID() const override;
+  void RecordLanguageDetectionEvent(
+      const translate::LanguageDetectionDetails& details) const override;
   void RecordTranslateEvent(const metrics::TranslateEventProto&) override;
   std::unique_ptr<infobars::InfoBar> CreateInfoBar(
       std::unique_ptr<translate::TranslateInfoBarDelegate> delegate)
diff --git a/ios/web_view/internal/translate/web_view_translate_client.mm b/ios/web_view/internal/translate/web_view_translate_client.mm
index 7ade020..e5746d7 100644
--- a/ios/web_view/internal/translate/web_view_translate_client.mm
+++ b/ios/web_view/internal/translate/web_view_translate_client.mm
@@ -17,6 +17,7 @@
 #include "components/translate/core/browser/translate_manager.h"
 #include "components/translate/core/browser/translate_prefs.h"
 #include "components/translate/core/browser/translate_step.h"
+#include "components/translate/core/common/language_detection_details.h"
 #include "ios/web/public/browser_state.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "ios/web_view/internal/pref_names.h"
@@ -112,6 +113,11 @@
   return 0;
 }
 
+void WebViewTranslateClient::RecordLanguageDetectionEvent(
+    const translate::LanguageDetectionDetails& details) const {
+  // TODO(crbug.com/722679): Implementing gaia-keyed logging.
+}
+
 void WebViewTranslateClient::RecordTranslateEvent(
     const metrics::TranslateEventProto&) {
   // TODO(crbug.com/728491): Implementing gaia-keyed logging.
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn
index e062e8f..a2da06e 100644
--- a/ipc/BUILD.gn
+++ b/ipc/BUILD.gn
@@ -207,6 +207,13 @@
     if (is_mac) {
       deps += [ "//sandbox/mac:seatbelt" ]
     }
+
+    if (is_fuchsia) {
+      sources -= [
+        # No AF_UNIX domain sockets on Fuchsia.
+        "sync_socket_unittest.cc",
+      ]
+    }
   }
 
   test("ipc_perftests") {
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index 3f429b66..348ff7e 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -208,10 +208,10 @@
 //      very few IPCs that cross this boundary.
 //   2) We also need to keep it for Linux for two reasons: int64_t is typedef'd
 //      to long, and gfx::PluginWindow is long and is used in one GPU IPC.
-//   3) Android 64 bit also has int64_t typedef'd to long.
+//   3) Android 64 bit and Fuchsia also have int64_t typedef'd to long.
 // Since we want to support Android 32<>64 bit IPC, as long as we don't have
 // these traits for 32 bit ARM then that'll catch any errors.
-#if defined(OS_WIN) || defined(OS_LINUX) || \
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_FUCHSIA) || \
     (defined(OS_ANDROID) && defined(ARCH_CPU_64_BITS))
 template <>
 struct ParamTraits<long> {
diff --git a/media/filters/source_buffer_range.cc b/media/filters/source_buffer_range.cc
index e2ff5ab..3484024 100644
--- a/media/filters/source_buffer_range.cc
+++ b/media/filters/source_buffer_range.cc
@@ -62,7 +62,9 @@
        itr != new_buffers.end();
        ++itr) {
     DCHECK((*itr)->GetDecodeTimestamp() != kNoDecodeTimestamp());
+
     buffers_.push_back(*itr);
+    UpdateEndTime(*itr);
     size_in_bytes_ += (*itr)->data_size();
 
     if ((*itr)->is_key_frame()) {
@@ -93,6 +95,11 @@
                << ") from previous range-end with derived duration ("
                << timestamp_delta << ").";
       last_appended_buffer->set_duration(timestamp_delta);
+      // To update the range end time here, there is no need to inspect an
+      // entire GOP or even reset the currently tracked |highest_frame_| because
+      // estimated durations should only occur in WebM, which doesn't contain
+      // out-of-order presentation/decode sequences.
+      CHECK_EQ(last_appended_buffer.get(), highest_frame_.get());
     }
   }
 }
@@ -187,8 +194,9 @@
       GetFirstKeyframeAt(timestamp, false);
 
   // If there is no keyframe after |timestamp|, we can't split the range.
-  if (new_beginning_keyframe == keyframe_map_.end())
+  if (new_beginning_keyframe == keyframe_map_.end()) {
     return NULL;
+  }
 
   // Remove the data beginning at |keyframe_index| from |buffers_| and save it
   // into |removed_buffers|.
@@ -209,6 +217,7 @@
 
   keyframe_map_.erase(new_beginning_keyframe, keyframe_map_.end());
   FreeBufferRange(starting_point, buffers_.end());
+  UpdateEndTimeUsingLastGOP();
 
   // Create a new range with |removed_buffers|.
   std::unique_ptr<SourceBufferRange> split_range =
@@ -324,8 +333,13 @@
   }
 
   // Invalidate range start time if we've deleted the first buffer of the range.
-  if (buffers_deleted > 0)
+  if (buffers_deleted > 0) {
     range_start_time_ = kNoDecodeTimestamp();
+    // Reset the range end time tracking if there are no more buffers in the
+    // range.
+    if (buffers_.empty())
+      highest_frame_ = nullptr;
+  }
 
   return total_bytes_deleted;
 }
@@ -358,6 +372,8 @@
     buffers_.pop_back();
   }
 
+  UpdateEndTimeUsingLastGOP();
+
   return total_bytes_deleted;
 }
 
@@ -481,6 +497,8 @@
 
   // Remove everything from |starting_point| onward.
   FreeBufferRange(starting_point, buffers_.end());
+
+  UpdateEndTimeUsingLastGOP();
   return buffers_.empty();
 }
 
@@ -601,6 +619,20 @@
   return GetEndTimestamp() + duration;
 }
 
+void SourceBufferRange::GetRangeEndTimesForTesting(
+    base::TimeDelta* highest_pts,
+    base::TimeDelta* end_time) const {
+  if (highest_frame_) {
+    *highest_pts = highest_frame_->timestamp();
+    *end_time = *highest_pts + highest_frame_->duration();
+    DCHECK_NE(*highest_pts, kNoTimestamp);
+    DCHECK_NE(*end_time, kNoTimestamp);
+    return;
+  }
+
+  *highest_pts = *end_time = kNoTimestamp;
+}
+
 DecodeTimestamp SourceBufferRange::NextKeyframeTimestamp(
     DecodeTimestamp timestamp) {
   DCHECK(!keyframe_map_.empty());
@@ -646,6 +678,71 @@
   return max_interbuffer_distance;
 }
 
+void SourceBufferRange::UpdateEndTime(
+    const scoped_refptr<StreamParserBuffer>& new_buffer) {
+  base::TimeDelta timestamp = new_buffer->timestamp();
+  base::TimeDelta duration = new_buffer->duration();
+  DVLOG(1) << __func__ << " timestamp=" << timestamp
+           << ", duration=" << duration;
+  DCHECK_NE(timestamp, kNoTimestamp);
+  DCHECK_GE(timestamp, base::TimeDelta());
+  DCHECK_GE(duration, base::TimeDelta());
+
+  if (!highest_frame_) {
+    DVLOG(1) << "Updating range end time from <empty> to " << timestamp << ", "
+             << timestamp + duration;
+    highest_frame_ = new_buffer;
+    return;
+  }
+
+  if (highest_frame_->timestamp() < timestamp ||
+      (highest_frame_->timestamp() == timestamp &&
+       highest_frame_->duration() <= duration)) {
+    DVLOG(1) << "Updating range end time from " << highest_frame_->timestamp()
+             << ", " << highest_frame_->timestamp() + highest_frame_->duration()
+             << " to " << timestamp << ", " << timestamp + duration;
+    highest_frame_ = new_buffer;
+  }
+}
+
+void SourceBufferRange::UpdateEndTimeUsingLastGOP() {
+  if (buffers_.empty()) {
+    DVLOG(1) << __func__ << " Empty range, resetting range end";
+    highest_frame_ = nullptr;
+    return;
+  }
+
+  highest_frame_ = nullptr;
+
+  KeyframeMap::const_iterator last_gop = keyframe_map_.end();
+  CHECK_GT(keyframe_map_.size(), 0u);
+  --last_gop;
+
+  // Iterate through the frames of the last GOP in this range, finding the
+  // frame with the highest PTS.
+  for (BufferQueue::const_iterator buffer_itr =
+           buffers_.begin() + (last_gop->second - keyframe_map_index_base_);
+       buffer_itr != buffers_.end(); ++buffer_itr) {
+    UpdateEndTime(*buffer_itr);
+  }
+
+  DVLOG(1) << __func__ << " Updated range end time to "
+           << highest_frame_->timestamp() << ", "
+           << highest_frame_->timestamp() + highest_frame_->duration();
+}
+
+bool SourceBufferRange::IsNextInPresentationSequence(
+    base::TimeDelta timestamp) const {
+  DCHECK_NE(timestamp, kNoTimestamp);
+  CHECK(!buffers_.empty());
+  base::TimeDelta highest_timestamp = highest_frame_->timestamp();
+  DCHECK_NE(highest_timestamp, kNoTimestamp);
+  return (highest_timestamp == timestamp ||
+          (highest_timestamp < timestamp &&
+           (gap_policy_ == ALLOW_GAPS ||
+            timestamp <= highest_timestamp + GetFudgeRoom())));
+}
+
 bool SourceBufferRange::IsNextInDecodeSequence(
     DecodeTimestamp decode_timestamp) const {
   CHECK(!buffers_.empty());
diff --git a/media/filters/source_buffer_range.h b/media/filters/source_buffer_range.h
index b2521eae..0929a17 100644
--- a/media/filters/source_buffer_range.h
+++ b/media/filters/source_buffer_range.h
@@ -13,6 +13,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "media/base/media_export.h"
 #include "media/base/stream_parser_buffer.h"
 
 namespace media {
@@ -20,7 +21,7 @@
 // Helper class representing a continuous range of buffered data in the
 // presentation timeline. All buffers in a SourceBufferRange are ordered
 // sequentially in decode timestamp order with no gaps.
-class SourceBufferRange {
+class MEDIA_EXPORT SourceBufferRange {
  public:
   // Returns the maximum distance in time between any buffer seen in the stream
   // of which this range is a part. Used to estimate the duration of a buffer if
@@ -78,6 +79,7 @@
   // room). The latter scenario is required when a muxed coded frame group has
   // such a large jagged start across tracks that its first buffer is not within
   // the fudge room, yet its group start was.
+  // During append, |highest_frame_| is updated, if necessary.
   void AppendBuffersToEnd(const BufferQueue& buffers,
                           DecodeTimestamp new_buffers_group_start_timestamp);
   bool CanAppendBuffersToEnd(
@@ -92,6 +94,7 @@
   // Note: Use these only to merge existing ranges. |range|'s first buffer
   // timestamp must be adjacent to this range. No group start timestamp
   // adjacency is involved in these methods.
+  // During append, |highest_frame_| is updated, if necessary.
   void AppendRangeToEnd(const SourceBufferRange& range,
                         bool transfer_current_position);
   bool CanAppendRangeToEnd(const SourceBufferRange& range) const;
@@ -126,6 +129,7 @@
   // this range. If there is no keyframe at or after |timestamp|, SplitRange()
   // returns null and this range is unmodified. This range can become empty if
   // |timestamp| <= the DTS of the first buffer in this range.
+  // |highest_frame_| is updated, if necessary.
   std::unique_ptr<SourceBufferRange> SplitRange(DecodeTimestamp timestamp);
 
   // Deletes the buffers from this range starting at |timestamp|, exclusive if
@@ -133,6 +137,7 @@
   // Resets |next_buffer_index_| if the buffer at |next_buffer_index_| was
   // deleted, and deletes the |keyframe_map_| entries for the buffers that
   // were removed.
+  // |highest_frame_| is updated, if necessary.
   // |deleted_buffers| contains the buffers that were deleted from this range,
   // starting at the buffer that had been at |next_buffer_index_|.
   // Returns true if everything in the range was deleted. Otherwise
@@ -145,6 +150,7 @@
   // Deletes a GOP from the front or back of the range and moves these
   // buffers into |deleted_buffers|. Returns the number of bytes deleted from
   // the range (i.e. the size in bytes of |deleted_buffers|).
+  // |highest_frame_| is updated, if necessary.
   // This range must NOT be empty when these methods are called.
   // The GOP being deleted must NOT contain the next buffer position.
   size_t DeleteGOPFromFront(BufferQueue* deleted_buffers);
@@ -203,6 +209,12 @@
   // is unset.
   DecodeTimestamp GetBufferedEndTimestamp() const;
 
+  // TODO(wolenetz): Remove in favor of
+  // GetEndTimestamp()/GetBufferedEndTimestamp() once they report in PTS, not
+  // DTS. See https://crbug.com/718641.
+  void GetRangeEndTimesForTesting(base::TimeDelta* highest_pts,
+                                  base::TimeDelta* end_time) const;
+
   // Gets the timestamp for the keyframe that is after |timestamp|. If
   // there isn't a keyframe in the range after |timestamp| then kNoTimestamp
   // is returned. If |timestamp| is in the "gap" between the value  returned by
@@ -232,12 +244,6 @@
   // the beginning of |range|.
   bool EndOverlaps(const SourceBufferRange& range) const;
 
-  // Returns true if |decode_timestamp| is allowed in this range as the decode
-  // timestamp of the next buffer in decode sequence at or after the last buffer
-  // in |buffers_|'s decode timestamp.  |buffers_| must not be empty. Uses
-  // |gap_policy_| to potentially allow gaps.
-  bool IsNextInDecodeSequence(DecodeTimestamp decode_timestamp) const;
-
   // Adds all buffers which overlap [start, end) to the end of |buffers|.  If
   // no buffers exist in the range returns false, true otherwise.
   bool GetBuffersInRange(DecodeTimestamp start, DecodeTimestamp end,
@@ -246,6 +252,9 @@
   size_t size_in_bytes() const { return size_in_bytes_; }
 
  private:
+  // Friend of private is only for IsNextInPresentationSequence testing.
+  friend class SourceBufferStreamTest;
+
   typedef std::map<DecodeTimestamp, int> KeyframeMap;
 
   // Called during AppendBuffersToEnd to adjust estimated duration at the
@@ -294,6 +303,48 @@
   // Returns the approximate duration of a buffer in this range.
   base::TimeDelta GetApproximateDuration() const;
 
+  // Updates |highest_frame_| if |new_buffer| has a higher PTS than
+  // |highest_frame_| or if the range was previously empty.
+  void UpdateEndTime(const scoped_refptr<StreamParserBuffer>& new_buffer);
+
+  // Updates |highest_frame_| to be the frame with highest PTS in the last GOP
+  // in this range.  If there are no buffers in this range, resets
+  // |highest_frame_|.
+  // Normally, incremental additions to this range should just use
+  // UpdateEndTime(). When removing buffers from this range (which could be out
+  // of order presentation vs decode order), inspecting the last buffer in
+  // decode order of this range can be insufficient to determine the correct
+  // presentation end time of this range. Hence this helper method.
+  void UpdateEndTimeUsingLastGOP();
+
+  // Returns true if |timestamp| is allowed in this range as the timestamp of
+  // the next buffer in presentation sequence at or after |highest_frame_|.
+  // |buffers_| must not be empty, and |highest_frame_| must not be nullptr.
+  // Uses |gap_policy_| to potentially allow gaps.
+  // TODO(wolenetz): Switch to using this helper in CanAppendBuffersToEnd(),
+  // etc, when switching to managing ranges by their presentation interval, and
+  // not necessarily just their decode times. See https://crbug.com/718641. Once
+  // being used and not just tested, the following also applies:
+  // Due to potential for out-of-order decode vs presentation time, this method
+  // should only be used to determine adjacency of keyframes with the end of
+  // |buffers_|.
+  bool IsNextInPresentationSequence(base::TimeDelta timestamp) const;
+
+  // Returns true if |decode_timestamp| is allowed in this range as the decode
+  // timestamp of the next buffer in decode sequence at or after the last buffer
+  // in |buffers_|'s decode timestamp.  |buffers_| must not be empty. Uses
+  // |gap_policy_| to potentially allow gaps.
+  // TODO(wolenetz): Switch to using this helper in CanAppendBuffersToEnd(),
+  // etc, appropriately when switching to managing ranges by their presentation
+  // interval between GOPs, and by their decode sequence within GOPs. See
+  // https://crbug.com/718641. Once that's done, the following also would apply:
+  // Due to potential for out-of-order decode vs presentation time, this method
+  // should only be used to determine adjacency of non-keyframes with the end of
+  // |buffers_|, when determining if a non-keyframe with |decode_timestamp|
+  // continues the decode sequence of the coded frame group at the end of
+  // |buffers_|.
+  bool IsNextInDecodeSequence(DecodeTimestamp decode_timestamp) const;
+
   // Keeps track of whether gaps are allowed.
   const GapPolicy gap_policy_;
 
@@ -324,6 +375,14 @@
   // for the new range.
   DecodeTimestamp range_start_time_;
 
+  // Caches the buffer, if any, with the highest PTS currently in |buffers_|.
+  // This is nullptr if this range is empty.
+  // This is useful in determining range membership and adjacency.
+  // TODO(wolenetz): Switch to using this in CanAppendBuffersToEnd(), etc., when
+  // switching to managing ranges by their presentation interval between GOPs,
+  // and by their decode sequence within GOPs. See https://crbug.com/718641.
+  scoped_refptr<StreamParserBuffer> highest_frame_;
+
   // Called to get the largest interbuffer distance seen so far in the stream.
   InterbufferDistanceCB interbuffer_distance_cb_;
 
diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc
index b14ecaa1..9a58003 100644
--- a/media/filters/source_buffer_stream.cc
+++ b/media/filters/source_buffer_stream.cc
@@ -1066,7 +1066,7 @@
   // At most one buffer should exist containing the time of the newly appended
   // buffer's start. GetBuffersInRange does not currently return buffers with
   // zero duration.
-  DCHECK_EQ(overlapped_buffers.size(), 1U)
+  CHECK_EQ(overlapped_buffers.size(), 1U)
       << __func__ << " Found more than one overlapped buffer";
   StreamParserBuffer* overlapped_buffer = overlapped_buffers.front().get();
 
@@ -1110,6 +1110,15 @@
   overlapped_buffer->set_duration(overlapped_buffer->duration() -
                                   overlap_duration);
 
+  // Note that the range's end time tracking shouldn't need explicit updating
+  // here due to the overlapped buffer's truncation because the range tracks
+  // that end time using a pointer to the buffer (which should be
+  // |overlapped_buffer| if the overlap occurred at the end of the range).
+  // Every audio frame is a keyframe, so there is no out-of-order PTS vs DTS
+  // sequencing to overcome. If the overlap occurs in the middle of the range,
+  // the caller invokes methods on the range which internally update the end
+  // time(s) of the resulting range(s) involved in the append.
+
   std::stringstream log_string;
   log_string << "Audio buffer splice at PTS="
              << splice_timestamp.InMicroseconds()
diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc
index c9099ae15..3224e4c 100644
--- a/media/filters/source_buffer_stream_unittest.cc
+++ b/media/filters/source_buffer_stream_unittest.cc
@@ -26,6 +26,7 @@
 #include "media/base/test_helpers.h"
 #include "media/base/text_track_config.h"
 #include "media/base/timestamp_constants.h"
+#include "media/filters/source_buffer_range.h"
 #include "media/filters/webvtt_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -218,6 +219,11 @@
     std::stringstream ss;
     ss << "{ ";
     for (size_t i = 0; i < r.size(); ++i) {
+      // TODO(wolenetz): Once SourceBufferRange is using PTS for buffered range
+      // reporting, also verify the reported range end time matches the
+      // internally tracked range end time, and that the "highest presentation
+      // timestamp" for the stream matches the last range's highest pts.  See
+      // https://crbug.com/718641.
       int64_t start = (r.start(i) / frame_duration_);
       int64_t end = (r.end(i) / frame_duration_) - 1;
       ss << "[" << start << "," << end << ") ";
@@ -232,6 +238,11 @@
     std::stringstream ss;
     ss << "{ ";
     for (size_t i = 0; i < r.size(); ++i) {
+      // TODO(wolenetz): Once SourceBufferRange is using PTS for buffered range
+      // reporting, also verify the reported range end time matches the
+      // internally tracked range end time, and that the "highest presentation
+      // timestamp" for the stream matches the last range's highest pts.  See
+      // https://crbug.com/718641.
       int64_t start = r.start(i).InMilliseconds();
       int64_t end = r.end(i).InMilliseconds();
       ss << "[" << start << "," << end << ") ";
@@ -240,6 +251,28 @@
     EXPECT_EQ(expected, ss.str());
   }
 
+  void CheckExpectedRangeEndTimes(const std::string& expected) {
+    std::stringstream ss;
+    ss << "{ ";
+    for (const auto& r : stream_->ranges_) {
+      base::TimeDelta highest_pts;
+      base::TimeDelta end_time;
+      r->GetRangeEndTimesForTesting(&highest_pts, &end_time);
+      ss << "<" << highest_pts.InMilliseconds() << ","
+         << end_time.InMilliseconds() << "> ";
+    }
+    ss << "}";
+    EXPECT_EQ(expected, ss.str());
+  }
+
+  void CheckIsNextInPTSSequenceWithFirstRange(int64_t pts_in_ms,
+                                              bool expectation) {
+    ASSERT_GE(stream_->ranges_.size(), 1u);
+    const auto& range_ptr = *(stream_->ranges_.begin());
+    EXPECT_EQ(expectation, range_ptr->IsNextInPresentationSequence(
+                               base::TimeDelta::FromMilliseconds(pts_in_ms)));
+  }
+
   void CheckExpectedBuffers(
       int starting_position, int ending_position) {
     CheckExpectedBuffers(starting_position, ending_position, false, NULL, 0);
@@ -612,14 +645,17 @@
     BufferQueue buffers = StringToBufferQueue(buffers_to_append);
 
     if (start_new_coded_frame_group) {
-      base::TimeDelta start_timestamp = coded_frame_group_start_timestamp;
-      if (start_timestamp == kNoTimestamp)
-        start_timestamp = buffers[0]->timestamp();
+      // TODO(wolenetz): Switch to signalling based on PTS, not DTS, once
+      // production code does that, too.  See https://crbug.com/718641.
+      DecodeTimestamp start_timestamp = DecodeTimestamp::FromPresentationTime(
+          coded_frame_group_start_timestamp);
 
-      ASSERT_TRUE(start_timestamp <= buffers[0]->timestamp());
+      if (start_timestamp == kNoDecodeTimestamp())
+        start_timestamp = buffers[0]->GetDecodeTimestamp();
 
-      stream_->OnStartOfCodedFrameGroup(
-          DecodeTimestamp::FromPresentationTime(start_timestamp));
+      ASSERT_TRUE(start_timestamp <= buffers[0]->GetDecodeTimestamp());
+
+      stream_->OnStartOfCodedFrameGroup(start_timestamp);
     }
 
     if (!one_by_one) {
@@ -4830,6 +4866,133 @@
   CheckExpectedRangesByTimestamp("{ [9,16) }");
 }
 
+struct VideoEndTimeCase {
+  // Times in Milliseconds
+  int64_t new_frame_pts;
+  int64_t new_frame_duration;
+  int64_t expected_highest_pts;
+  int64_t expected_end_time;
+};
+
+TEST_F(SourceBufferStreamTest, VideoRangeEndTimeCases) {
+  // With a basic range containing just a single keyframe [10,20), verify
+  // various keyframe overlap append cases' results on the range end time.
+  const VideoEndTimeCase kCases[] = {
+      {0, 10, 10, 20},
+      {20, 1, 20, 21},
+      {15, 3, 15, 18},
+      {15, 5, 15, 20},
+      {15, 8, 15, 23},
+
+      // Cases where the new frame removes the previous frame:
+      {10, 3, 10, 13},
+      {10, 10, 10, 20},
+      {10, 13, 10, 23},
+      {5, 8, 5, 13},
+      {5, 15, 5, 20},
+      {5, 20, 5, 25}};
+
+  for (const auto& c : kCases) {
+    RemoveInMs(0, 100, 100);
+    NewCodedFrameGroupAppend("10D10K");
+    CheckExpectedRangesByTimestamp("{ [10,20) }");
+    CheckExpectedRangeEndTimes("{ <10,20> }");
+
+    std::stringstream ss;
+    ss << c.new_frame_pts << "D" << c.new_frame_duration << "K";
+    DVLOG(1) << "Appending " << ss.str();
+    NewCodedFrameGroupAppend(ss.str());
+
+    std::stringstream expected;
+    expected << "{ <" << c.expected_highest_pts << "," << c.expected_end_time
+             << "> }";
+    CheckExpectedRangeEndTimes(expected.str());
+  }
+}
+
+struct AudioEndTimeCase {
+  // Times in Milliseconds
+  int64_t new_frame_pts;
+  int64_t new_frame_duration;
+  int64_t expected_highest_pts;
+  int64_t expected_end_time;
+  bool expect_splice;
+};
+
+TEST_F(SourceBufferStreamTest, AudioRangeEndTimeCases) {
+  // With a basic range containing just a single keyframe [10,20), verify
+  // various keyframe overlap append cases' results on the range end time.
+  const AudioEndTimeCase kCases[] = {
+      {0, 10, 10, 20, false},
+      {20, 1, 20, 21, false},
+      {15, 3, 15, 18, true},
+      {15, 5, 15, 20, true},
+      {15, 8, 15, 23, true},
+
+      // Cases where the new frame removes the previous frame:
+      {10, 3, 10, 13, false},
+      {10, 10, 10, 20, false},
+      {10, 13, 10, 23, false},
+      {5, 8, 5, 13, false},
+      {5, 15, 5, 20, false},
+      {5, 20, 5, 25, false}};
+
+  SetAudioStream();
+  for (const auto& c : kCases) {
+    InSequence s;
+
+    RemoveInMs(0, 100, 100);
+    NewCodedFrameGroupAppend("10D10K");
+    CheckExpectedRangesByTimestamp("{ [10,20) }");
+    CheckExpectedRangeEndTimes("{ <10,20> }");
+
+    std::stringstream ss;
+    ss << c.new_frame_pts << "D" << c.new_frame_duration << "K";
+    if (c.expect_splice) {
+      EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(c.new_frame_pts * 1000, 10000,
+                                            (20 - c.new_frame_pts) * 1000));
+    }
+    DVLOG(1) << "Appending " << ss.str();
+    NewCodedFrameGroupAppend(ss.str());
+
+    std::stringstream expected;
+    expected << "{ <" << c.expected_highest_pts << "," << c.expected_end_time
+             << "> }";
+    CheckExpectedRangeEndTimes(expected.str());
+  }
+}
+
+TEST_F(SourceBufferStreamTest, RangeIsNextInPTS_Simple) {
+  // Append a simple GOP where DTS==PTS, perform basic PTS continuity checks.
+  NewCodedFrameGroupAppend("10D10K");
+  CheckIsNextInPTSSequenceWithFirstRange(9, false);
+  CheckIsNextInPTSSequenceWithFirstRange(10, true);
+  CheckIsNextInPTSSequenceWithFirstRange(20, true);
+  CheckIsNextInPTSSequenceWithFirstRange(30, true);
+  CheckIsNextInPTSSequenceWithFirstRange(31, false);
+}
+
+TEST_F(SourceBufferStreamTest, RangeIsNextInPTS_OutOfOrder) {
+  // Append a GOP where DTS != PTS such that a timestamp used as DTS would not
+  // be continuous, but used as PTS is, and verify PTS continuity.
+  NewCodedFrameGroupAppend("1000|0K 1120|30 1030|60 1060|90 1090|120");
+  CheckIsNextInPTSSequenceWithFirstRange(0, false);
+  CheckIsNextInPTSSequenceWithFirstRange(30, false);
+  CheckIsNextInPTSSequenceWithFirstRange(60, false);
+  CheckIsNextInPTSSequenceWithFirstRange(90, false);
+  CheckIsNextInPTSSequenceWithFirstRange(120, false);
+  CheckIsNextInPTSSequenceWithFirstRange(150, false);
+  CheckIsNextInPTSSequenceWithFirstRange(1000, false);
+  CheckIsNextInPTSSequenceWithFirstRange(1030, false);
+  CheckIsNextInPTSSequenceWithFirstRange(1060, false);
+  CheckIsNextInPTSSequenceWithFirstRange(1090, false);
+  CheckIsNextInPTSSequenceWithFirstRange(1119, false);
+  CheckIsNextInPTSSequenceWithFirstRange(1120, true);
+  CheckIsNextInPTSSequenceWithFirstRange(1150, true);
+  CheckIsNextInPTSSequenceWithFirstRange(1180, true);
+  CheckIsNextInPTSSequenceWithFirstRange(1181, false);
+}
+
 // TODO(vrk): Add unit tests where keyframes are unaligned between streams.
 // (crbug.com/133557)
 
diff --git a/media/gpu/surface_texture_gl_owner.cc b/media/gpu/surface_texture_gl_owner.cc
index 22726ae..36f6b18 100644
--- a/media/gpu/surface_texture_gl_owner.cc
+++ b/media/gpu/surface_texture_gl_owner.cc
@@ -70,6 +70,12 @@
   // Make sure that the SurfaceTexture isn't using the GL objects.
   surface_texture_ = nullptr;
 
+  if (gl::GLSurface::GetCurrent() == nullptr) {
+    // This happens during GpuCommandBufferStub teardown.  Just leave -- the gpu
+    // process is going away.  crbug.com/718117 .
+    return;
+  }
+
   ui::ScopedMakeCurrent scoped_make_current(context_.get(), surface_.get());
   if (scoped_make_current.Succeeded()) {
     glDeleteTextures(1, &texture_id_);
diff --git a/mojo/edk/js/tests/BUILD.gn b/mojo/edk/js/tests/BUILD.gn
index 1edd4ae6..53e79d2 100644
--- a/mojo/edk/js/tests/BUILD.gn
+++ b/mojo/edk/js/tests/BUILD.gn
@@ -15,48 +15,10 @@
 group("tests") {
   testonly = true
   deps = [
-    ":mojo_js_integration_tests",
     ":mojo_js_unittests",
   ]
 }
 
-test("mojo_js_integration_tests") {
-  deps = [
-    ":js_to_cpp_bindings",
-    "//base/test:test_support",
-    "//gin:gin_test",
-    "//mojo/common",
-    "//mojo/edk/js",
-    "//mojo/edk/test:run_all_unittests",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/system",
-    "//mojo/public/js:bindings",
-  ]
-
-  sources = [
-    "js_to_cpp_tests.cc",
-  ]
-
-  data = [
-    "js_to_cpp_tests.js",
-  ]
-
-  data_deps = [
-    ":js_to_cpp_bindings_js_data_deps",
-  ]
-
-  configs += [ "//v8:external_startup_data" ]
-}
-
-mojom("js_to_cpp_bindings") {
-  sources = [
-    "js_to_cpp.mojom",
-  ]
-
-  # TODO(crbug.com/699569): Convert to use the new JS bindings.
-  use_new_js_bindings = false
-}
-
 test("mojo_js_unittests") {
   deps = [
     "//base",
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.cc b/mojo/edk/js/tests/js_to_cpp_tests.cc
deleted file mode 100644
index 0ce95e8..0000000
--- a/mojo/edk/js/tests/js_to_cpp_tests.cc
+++ /dev/null
@@ -1,459 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/at_exit.h"
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/macros.h"
-#include "base/run_loop.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "gin/array_buffer.h"
-#include "gin/public/isolate_holder.h"
-#include "gin/v8_initializer.h"
-#include "mojo/common/data_pipe_utils.h"
-#include "mojo/edk/js/mojo_runner_delegate.h"
-#include "mojo/edk/js/tests/js_to_cpp.mojom.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/lib/validation_errors.h"
-#include "mojo/public/cpp/system/core.h"
-#include "mojo/public/cpp/system/wait.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace edk {
-namespace js {
-
-// Global value updated by some checks to prevent compilers from optimizing
-// reads out of existence.
-uint32_t g_waste_accumulator = 0;
-
-namespace {
-
-// Negative numbers with different values in each byte, the last of
-// which can survive promotion to double and back.
-const int8_t kExpectedInt8Value = -65;
-const int16_t kExpectedInt16Value = -16961;
-const int32_t kExpectedInt32Value = -1145258561;
-const int64_t kExpectedInt64Value = -77263311946305LL;
-
-// Positive numbers with different values in each byte, the last of
-// which can survive promotion to double and back.
-const uint8_t kExpectedUInt8Value = 65;
-const uint16_t kExpectedUInt16Value = 16961;
-const uint32_t kExpectedUInt32Value = 1145258561;
-const uint64_t kExpectedUInt64Value = 77263311946305LL;
-
-// Double/float values, including special case constants.
-const double kExpectedDoubleVal = 3.14159265358979323846;
-const double kExpectedDoubleInf = std::numeric_limits<double>::infinity();
-const double kExpectedDoubleNan = std::numeric_limits<double>::quiet_NaN();
-const float kExpectedFloatVal = static_cast<float>(kExpectedDoubleVal);
-const float kExpectedFloatInf = std::numeric_limits<float>::infinity();
-const float kExpectedFloatNan = std::numeric_limits<float>::quiet_NaN();
-
-// NaN has the property that it is not equal to itself.
-#define EXPECT_NAN(x) EXPECT_NE(x, x)
-
-void CheckDataPipe(ScopedDataPipeConsumerHandle data_pipe_handle) {
-  std::string buffer;
-  bool result = common::BlockingCopyToString(std::move(data_pipe_handle),
-                                             &buffer);
-  EXPECT_TRUE(result);
-  EXPECT_EQ(64u, buffer.size());
-  for (int i = 0; i < 64; ++i) {
-    EXPECT_EQ(i, buffer[i]);
-  }
-}
-
-void CheckMessagePipe(MessagePipeHandle message_pipe_handle) {
-  MojoResult result = Wait(message_pipe_handle, MOJO_HANDLE_SIGNAL_READABLE);
-  EXPECT_EQ(MOJO_RESULT_OK, result);
-  std::vector<uint8_t> bytes;
-  std::vector<ScopedHandle> handles;
-  result = ReadMessageRaw(message_pipe_handle, &bytes, &handles, 0);
-  EXPECT_EQ(MOJO_RESULT_OK, result);
-  EXPECT_EQ(64u, bytes.size());
-  for (int i = 0; i < 64; ++i) {
-    EXPECT_EQ(255 - i, bytes[i]);
-  }
-}
-
-js_to_cpp::EchoArgsPtr BuildSampleEchoArgs() {
-  js_to_cpp::EchoArgsPtr args(js_to_cpp::EchoArgs::New());
-  args->si64 = kExpectedInt64Value;
-  args->si32 = kExpectedInt32Value;
-  args->si16 = kExpectedInt16Value;
-  args->si8 = kExpectedInt8Value;
-  args->ui64 = kExpectedUInt64Value;
-  args->ui32 = kExpectedUInt32Value;
-  args->ui16 = kExpectedUInt16Value;
-  args->ui8 = kExpectedUInt8Value;
-  args->float_val = kExpectedFloatVal;
-  args->float_inf = kExpectedFloatInf;
-  args->float_nan = kExpectedFloatNan;
-  args->double_val = kExpectedDoubleVal;
-  args->double_inf = kExpectedDoubleInf;
-  args->double_nan = kExpectedDoubleNan;
-  args->name.emplace("coming");
-  args->string_array.emplace(3);
-  (*args->string_array)[0] = "one";
-  (*args->string_array)[1] = "two";
-  (*args->string_array)[2] = "three";
-  return args;
-}
-
-void CheckSampleEchoArgs(js_to_cpp::EchoArgsPtr arg) {
-  EXPECT_EQ(kExpectedInt64Value, arg->si64);
-  EXPECT_EQ(kExpectedInt32Value, arg->si32);
-  EXPECT_EQ(kExpectedInt16Value, arg->si16);
-  EXPECT_EQ(kExpectedInt8Value, arg->si8);
-  EXPECT_EQ(kExpectedUInt64Value, arg->ui64);
-  EXPECT_EQ(kExpectedUInt32Value, arg->ui32);
-  EXPECT_EQ(kExpectedUInt16Value, arg->ui16);
-  EXPECT_EQ(kExpectedUInt8Value, arg->ui8);
-  EXPECT_EQ(kExpectedFloatVal, arg->float_val);
-  EXPECT_EQ(kExpectedFloatInf, arg->float_inf);
-  EXPECT_NAN(arg->float_nan);
-  EXPECT_EQ(kExpectedDoubleVal, arg->double_val);
-  EXPECT_EQ(kExpectedDoubleInf, arg->double_inf);
-  EXPECT_NAN(arg->double_nan);
-  EXPECT_EQ(std::string("coming"), *arg->name);
-  EXPECT_EQ(std::string("one"), (*arg->string_array)[0]);
-  EXPECT_EQ(std::string("two"), (*arg->string_array)[1]);
-  EXPECT_EQ(std::string("three"), (*arg->string_array)[2]);
-  CheckDataPipe(std::move(arg->data_handle));
-  CheckMessagePipe(arg->message_handle.get());
-}
-
-void CheckSampleEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) {
-  if (list.is_null())
-    return;
-  CheckSampleEchoArgs(std::move(list->item));
-  CheckSampleEchoArgsList(list->next);
-}
-
-// More forgiving checks are needed in the face of potentially corrupt
-// messages. The values don't matter so long as all accesses are within
-// bounds.
-void CheckCorruptedString(const std::string& arg) {
-  for (size_t i = 0; i < arg.size(); ++i)
-    g_waste_accumulator += arg[i];
-}
-
-void CheckCorruptedString(const base::Optional<std::string>& arg) {
-  if (!arg)
-    return;
-  CheckCorruptedString(*arg);
-}
-
-void CheckCorruptedStringArray(
-    const base::Optional<std::vector<std::string>>& string_array) {
-  if (!string_array)
-    return;
-  for (size_t i = 0; i < string_array->size(); ++i)
-    CheckCorruptedString((*string_array)[i]);
-}
-
-void CheckCorruptedDataPipe(MojoHandle data_pipe_handle) {
-  unsigned char buffer[100];
-  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
-  MojoResult result = MojoReadData(
-      data_pipe_handle, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE);
-  if (result != MOJO_RESULT_OK)
-    return;
-  for (uint32_t i = 0; i < buffer_size; ++i)
-    g_waste_accumulator += buffer[i];
-}
-
-void CheckCorruptedMessagePipe(MojoHandle message_pipe_handle) {
-  std::vector<uint8_t> bytes;
-  std::vector<ScopedHandle> handles;
-  MojoResult result = ReadMessageRaw(MessagePipeHandle(message_pipe_handle),
-                                     &bytes, &handles, 0);
-  if (result != MOJO_RESULT_OK)
-    return;
-  for (uint32_t i = 0; i < bytes.size(); ++i)
-    g_waste_accumulator += bytes[i];
-}
-
-void CheckCorruptedEchoArgs(const js_to_cpp::EchoArgsPtr& arg) {
-  if (arg.is_null())
-    return;
-  CheckCorruptedString(arg->name);
-  CheckCorruptedStringArray(arg->string_array);
-  if (arg->data_handle.is_valid())
-    CheckCorruptedDataPipe(arg->data_handle.get().value());
-  if (arg->message_handle.is_valid())
-    CheckCorruptedMessagePipe(arg->message_handle.get().value());
-}
-
-void CheckCorruptedEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) {
-  if (list.is_null())
-    return;
-  CheckCorruptedEchoArgs(list->item);
-  CheckCorruptedEchoArgsList(list->next);
-}
-
-// Base Provider implementation class. It's expected that tests subclass and
-// override the appropriate Provider functions. When test is done quit the
-// run_loop().
-class CppSideConnection : public js_to_cpp::CppSide {
- public:
-  CppSideConnection()
-      : run_loop_(nullptr),
-        js_side_(nullptr),
-        mishandled_messages_(0),
-        binding_(this) {}
-  ~CppSideConnection() override {}
-
-  void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
-  base::RunLoop* run_loop() { return run_loop_; }
-
-  void set_js_side(js_to_cpp::JsSide* js_side) { js_side_ = js_side; }
-  js_to_cpp::JsSide* js_side() { return js_side_; }
-
-  void Bind(InterfaceRequest<js_to_cpp::CppSide> request) {
-    binding_.Bind(std::move(request));
-    // Keep the pipe open even after validation errors.
-    binding_.EnableTestingMode();
-  }
-
-  // js_to_cpp::CppSide:
-  void StartTest() override { NOTREACHED(); }
-
-  void TestFinished() override { NOTREACHED(); }
-
-  void PingResponse() override { mishandled_messages_ += 1; }
-
-  void EchoResponse(js_to_cpp::EchoArgsListPtr list) override {
-    mishandled_messages_ += 1;
-  }
-
-  void BitFlipResponse(
-      js_to_cpp::EchoArgsListPtr list,
-      js_to_cpp::ForTestingAssociatedPtrInfo not_used) override {
-    mishandled_messages_ += 1;
-  }
-
-  void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override {
-    mishandled_messages_ += 1;
-  }
-
- protected:
-  base::RunLoop* run_loop_;
-  js_to_cpp::JsSide* js_side_;
-  int mishandled_messages_;
-  mojo::Binding<js_to_cpp::CppSide> binding_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(CppSideConnection);
-};
-
-// Trivial test to verify a message sent from JS is received.
-class PingCppSideConnection : public CppSideConnection {
- public:
-  PingCppSideConnection() : got_message_(false) {}
-  ~PingCppSideConnection() override {}
-
-  // js_to_cpp::CppSide:
-  void StartTest() override { js_side_->Ping(); }
-
-  void PingResponse() override {
-    got_message_ = true;
-    run_loop()->Quit();
-  }
-
-  bool DidSucceed() {
-    return got_message_ && !mishandled_messages_;
-  }
-
- private:
-  bool got_message_;
-  DISALLOW_COPY_AND_ASSIGN(PingCppSideConnection);
-};
-
-// Test that parameters are passed with correct values.
-class EchoCppSideConnection : public CppSideConnection {
- public:
-  EchoCppSideConnection() :
-      message_count_(0),
-      termination_seen_(false) {
-  }
-  ~EchoCppSideConnection() override {}
-
-  // js_to_cpp::CppSide:
-  void StartTest() override {
-    js_side_->Echo(kExpectedMessageCount, BuildSampleEchoArgs());
-  }
-
-  void EchoResponse(js_to_cpp::EchoArgsListPtr list) override {
-    const js_to_cpp::EchoArgsPtr& special_arg = list->item;
-    message_count_ += 1;
-    EXPECT_EQ(-1, special_arg->si64);
-    EXPECT_EQ(-1, special_arg->si32);
-    EXPECT_EQ(-1, special_arg->si16);
-    EXPECT_EQ(-1, special_arg->si8);
-    EXPECT_EQ(std::string("going"), *special_arg->name);
-    CheckSampleEchoArgsList(list->next);
-  }
-
-  void TestFinished() override {
-    termination_seen_ = true;
-    run_loop()->Quit();
-  }
-
-  bool DidSucceed() {
-    return termination_seen_ &&
-        !mishandled_messages_ &&
-        message_count_ == kExpectedMessageCount;
-  }
-
- private:
-  static const int kExpectedMessageCount = 10;
-  int message_count_;
-  bool termination_seen_;
-  DISALLOW_COPY_AND_ASSIGN(EchoCppSideConnection);
-};
-
-// Test that corrupted messages don't wreak havoc.
-class BitFlipCppSideConnection : public CppSideConnection {
- public:
-  BitFlipCppSideConnection() : termination_seen_(false) {}
-  ~BitFlipCppSideConnection() override {}
-
-  // js_to_cpp::CppSide:
-  void StartTest() override { js_side_->BitFlip(BuildSampleEchoArgs()); }
-
-  void BitFlipResponse(
-      js_to_cpp::EchoArgsListPtr list,
-      js_to_cpp::ForTestingAssociatedPtrInfo not_used) override {
-    CheckCorruptedEchoArgsList(list);
-  }
-
-  void TestFinished() override {
-    termination_seen_ = true;
-    run_loop()->Quit();
-  }
-
-  bool DidSucceed() {
-    return termination_seen_;
-  }
-
- private:
-  bool termination_seen_;
-  DISALLOW_COPY_AND_ASSIGN(BitFlipCppSideConnection);
-};
-
-// Test that severely random messages don't wreak havoc.
-class BackPointerCppSideConnection : public CppSideConnection {
- public:
-  BackPointerCppSideConnection() : termination_seen_(false) {}
-  ~BackPointerCppSideConnection() override {}
-
-  // js_to_cpp::CppSide:
-  void StartTest() override { js_side_->BackPointer(BuildSampleEchoArgs()); }
-
-  void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override {
-    CheckCorruptedEchoArgsList(list);
-  }
-
-  void TestFinished() override {
-    termination_seen_ = true;
-    run_loop()->Quit();
-  }
-
-  bool DidSucceed() {
-    return termination_seen_;
-  }
-
- private:
-  bool termination_seen_;
-  DISALLOW_COPY_AND_ASSIGN(BackPointerCppSideConnection);
-};
-
-}  // namespace
-
-class JsToCppTest : public testing::Test {
- public:
-  JsToCppTest() {}
-
-  void RunTest(const std::string& test, CppSideConnection* cpp_side) {
-    cpp_side->set_run_loop(&run_loop_);
-
-    js_to_cpp::JsSidePtr js_side;
-    auto js_side_proxy = MakeRequest(&js_side);
-
-    cpp_side->set_js_side(js_side.get());
-    js_to_cpp::CppSidePtr cpp_side_ptr;
-    cpp_side->Bind(MakeRequest(&cpp_side_ptr));
-
-    js_side->SetCppSide(std::move(cpp_side_ptr));
-
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-    gin::V8Initializer::LoadV8Snapshot();
-    gin::V8Initializer::LoadV8Natives();
-#endif
-
-    gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
-                                   gin::IsolateHolder::kStableV8Extras,
-                                   gin::ArrayBufferAllocator::SharedInstance());
-    gin::IsolateHolder instance(base::ThreadTaskRunnerHandle::Get());
-    MojoRunnerDelegate delegate;
-    gin::ShellRunner runner(&delegate, instance.isolate());
-    delegate.Start(&runner, js_side_proxy.PassMessagePipe().release().value(),
-                   test);
-
-    run_loop_.Run();
-  }
-
- private:
-  base::ShadowingAtExitManager at_exit_;
-  base::test::ScopedTaskEnvironment scoped_task_environment_;
-  base::RunLoop run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(JsToCppTest);
-};
-
-TEST_F(JsToCppTest, Ping) {
-  PingCppSideConnection cpp_side_connection;
-  RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
-  EXPECT_TRUE(cpp_side_connection.DidSucceed());
-}
-
-TEST_F(JsToCppTest, Echo) {
-  EchoCppSideConnection cpp_side_connection;
-  RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
-  EXPECT_TRUE(cpp_side_connection.DidSucceed());
-}
-
-TEST_F(JsToCppTest, BitFlip) {
-  // These tests generate a lot of expected validation errors. Suppress logging.
-  mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
-
-  BitFlipCppSideConnection cpp_side_connection;
-  RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
-  EXPECT_TRUE(cpp_side_connection.DidSucceed());
-}
-
-TEST_F(JsToCppTest, BackPointer) {
-  // These tests generate a lot of expected validation errors. Suppress logging.
-  mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
-
-  BackPointerCppSideConnection cpp_side_connection;
-  RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
-  EXPECT_TRUE(cpp_side_connection.DidSucceed());
-}
-
-}  // namespace js
-}  // namespace edk
-}  // namespace mojo
diff --git a/net/android/java/src/org/chromium/net/X509Util.java b/net/android/java/src/org/chromium/net/X509Util.java
index 8536b2d..2bf8a19 100644
--- a/net/android/java/src/org/chromium/net/X509Util.java
+++ b/net/android/java/src/org/chromium/net/X509Util.java
@@ -12,11 +12,11 @@
 import android.net.http.X509TrustManagerExtensions;
 import android.os.Build;
 import android.security.KeyChain;
+import android.util.Log;
 import android.util.Pair;
 
 import org.chromium.base.BuildInfo;
 import org.chromium.base.ContextUtils;
-import org.chromium.base.Log;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.SuppressFBWarnings;
 
@@ -34,7 +34,6 @@
 import java.security.cert.CertificateFactory;
 import java.security.cert.CertificateNotYetValidException;
 import java.security.cert.X509Certificate;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
@@ -162,12 +161,24 @@
     private static final String OID_SERVER_GATED_MICROSOFT = "1.3.6.1.4.1.311.10.3.3";
 
     /**
+     * Trust manager backed up by the read-only system certificate store.
+     */
+    private static X509TrustManagerImplementation sDefaultTrustManager;
+
+    /**
      * BroadcastReceiver that listens to change in the system keystore to invalidate certificate
      * caches.
      */
     private static TrustStorageListener sTrustStorageListener;
 
     /**
+     * Trust manager backed up by a custom certificate store. We need such manager to plant test
+     * root CA to the trust store in testing.
+     */
+    private static X509TrustManagerImplementation sTestTrustManager;
+    private static KeyStore sTestKeyStore;
+
+    /**
      * The system key store. This is used to determine whether a trust anchor is a system trust
      * anchor or user-installed.
      */
@@ -182,25 +193,20 @@
     private static File sSystemCertificateDirectory;
 
     /**
-     * True if ensureInitialized has run successfully.
+     * An in-memory cache of which trust anchors are system trust roots. This avoids reading and
+     * decoding the root from disk on every verification. Mirrors a similar in-memory cache in
+     * Conscrypt's X509TrustManager implementation.
      */
-    private static boolean sInitialized;
+    private static Set<Pair<X500Principal, PublicKey>> sSystemTrustAnchorCache;
 
     /**
-     * The list of test root certificates to inject via testing. This list is protected by sLock.
+     * True if the system key store has been loaded. If the "AndroidCAStore" KeyStore instance
+     * was not found, sSystemKeyStore may be null while sLoadedSystemKeyStore is true.
      */
-    private static List<X509Certificate> sTestRoots = new ArrayList<X509Certificate>();
+    private static boolean sLoadedSystemKeyStore;
 
     /**
-     * Wraps all reloadable state in the verifier. When the backing KeyStores change, this field
-     * should be reset to null. Any verifications currently using the old instance will run to
-     * completion, but new ones use fresh state.
-     */
-    private static CertificateVerifier sVerifier;
-
-    /**
-     * Lock object used to synchronize all calls that modify or depend on the above globals. All
-     * fields except sVerifier are final once ensureInitialized completes successfully.
+     * Lock object used to synchronize all calls that modify or depend on the trust managers.
      */
     private static final Object sLock = new Object();
 
@@ -211,225 +217,6 @@
      */
     private static boolean sDisableNativeCodeForTest;
 
-    private static final class CertificateVerifier {
-        /**
-         * X509TrustManager wrapping the default KeyStore.
-         */
-        private X509TrustManagerImplementation mDefaultTrustManager;
-
-        /**
-         * X509TrustManager wrapping any test roots which were configured when the
-         * CertificateVerifier was created.
-         */
-        private X509TrustManagerImplementation mTestTrustManager;
-
-        /**
-         * An in-memory cache of which trust anchors are system trust roots. This avoids reading and
-         * decoding the root from disk on every verification and mirrors a similar in-memory cache
-         * in Conscrypt's X509TrustManager implementation.
-         */
-        private Set<Pair<X500Principal, PublicKey>> mSystemTrustAnchorCache;
-
-        /**
-         * Lock object used to synchronize mSystemTrustAnchorCache.
-         */
-        private Object mSystemTrustAnchorCacheLock;
-
-        public CertificateVerifier()
-                throws CertificateException, KeyStoreException, NoSuchAlgorithmException {
-            assert Thread.holdsLock(sLock);
-            ensureInitializedLocked();
-
-            mDefaultTrustManager = createTrustManager(null);
-            if (!sTestRoots.isEmpty()) {
-                KeyStore testKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
-                try {
-                    testKeyStore.load(null);
-                } catch (IOException e) {
-                    // No IO operation is attempted
-                }
-                for (int i = 0; i < sTestRoots.size(); i++) {
-                    testKeyStore.setCertificateEntry(
-                            "root_cert_" + Integer.toString(i), sTestRoots.get(i));
-                }
-                mTestTrustManager = createTrustManager(testKeyStore);
-            }
-            mSystemTrustAnchorCache = new HashSet<Pair<X500Principal, PublicKey>>();
-            mSystemTrustAnchorCacheLock = new Object();
-        }
-
-        /**
-         * Creates a X509TrustManagerImplementation backed up by the given key store. When null is
-         * passed as a key store, system default trust store is used. Returns null if no created
-         * TrustManager was suitable.
-         * @throws KeyStoreException, NoSuchAlgorithmException on error initializing the
-         * TrustManager.
-         */
-        private static X509TrustManagerImplementation createTrustManager(KeyStore keyStore)
-                throws KeyStoreException, NoSuchAlgorithmException {
-            String algorithm = TrustManagerFactory.getDefaultAlgorithm();
-            TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
-            tmf.init(keyStore);
-
-            for (TrustManager tm : tmf.getTrustManagers()) {
-                if (tm instanceof X509TrustManager) {
-                    try {
-                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-                            return new X509TrustManagerJellyBean((X509TrustManager) tm);
-                        } else {
-                            return new X509TrustManagerIceCreamSandwich((X509TrustManager) tm);
-                        }
-                    } catch (IllegalArgumentException e) {
-                        String className = tm.getClass().getName();
-                        Log.e(TAG, "Error creating trust manager (" + className + "): " + e);
-                    }
-                }
-            }
-            Log.e(TAG, "Could not find suitable trust manager");
-            return null;
-        }
-
-        private static final char[] HEX_DIGITS = {
-                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
-        };
-
-        private static String hashPrincipal(X500Principal principal)
-                throws NoSuchAlgorithmException {
-            // Android hashes a principal as the first four bytes of its MD5 digest, encoded in
-            // lowercase hex and reversed. Verified in 4.2, 4.3, and 4.4.
-            byte[] digest = MessageDigest.getInstance("MD5").digest(principal.getEncoded());
-            char[] hexChars = new char[8];
-            for (int i = 0; i < 4; i++) {
-                hexChars[2 * i] = HEX_DIGITS[(digest[3 - i] >> 4) & 0xf];
-                hexChars[2 * i + 1] = HEX_DIGITS[digest[3 - i] & 0xf];
-            }
-            return new String(hexChars);
-        }
-
-        private boolean isKnownRoot(X509Certificate root)
-                throws NoSuchAlgorithmException, KeyStoreException {
-            // Could not find the system key store. Conservatively report false.
-            if (sSystemKeyStore == null) return false;
-
-            // Check the in-memory cache first; avoid decoding the anchor from disk
-            // if it has been seen before.
-            Pair<X500Principal, PublicKey> key = new Pair<X500Principal, PublicKey>(
-                    root.getSubjectX500Principal(), root.getPublicKey());
-
-            synchronized (mSystemTrustAnchorCacheLock) {
-                if (mSystemTrustAnchorCache.contains(key)) return true;
-            }
-
-            // Note: It is not sufficient to call sSystemKeyStore.getCertificiateAlias. If the
-            // server supplies a copy of a trust anchor, X509TrustManagerExtensions returns the
-            // server's version rather than the system one. getCertificiateAlias will then fail to
-            // find an anchor name. This is fixed upstream in
-            // https://android-review.googlesource.com/#/c/91605/
-            //
-            // TODO(davidben): When the change trickles into an Android release, query
-            // sSystemKeyStore directly.
-
-            // System trust anchors are stored under a hash of the principal. In case of collisions,
-            // a number is appended.
-            String hash = hashPrincipal(root.getSubjectX500Principal());
-            for (int i = 0; true; i++) {
-                String alias = hash + '.' + i;
-                if (!new File(sSystemCertificateDirectory, alias).exists()) break;
-
-                Certificate anchor = sSystemKeyStore.getCertificate("system:" + alias);
-                // It is possible for this to return null if the user deleted a trust anchor. In
-                // that case, the certificate remains in the system directory but is also added to
-                // another file. Continue iterating as there may be further collisions after the
-                // deleted anchor.
-                if (anchor == null) continue;
-
-                if (!(anchor instanceof X509Certificate)) {
-                    // This should never happen.
-                    String className = anchor.getClass().getName();
-                    Log.e(TAG, "Anchor " + alias + " not an X509Certificate: " + className);
-                    continue;
-                }
-
-                // If the subject and public key match, this is a system root.
-                X509Certificate anchorX509 = (X509Certificate) anchor;
-                if (root.getSubjectX500Principal().equals(anchorX509.getSubjectX500Principal())
-                        && root.getPublicKey().equals(anchorX509.getPublicKey())) {
-                    synchronized (mSystemTrustAnchorCacheLock) {
-                        mSystemTrustAnchorCache.add(key);
-                    }
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        public AndroidCertVerifyResult verifyServerCertificates(byte[][] certChain, String authType,
-                String host) throws KeyStoreException, NoSuchAlgorithmException {
-            if (certChain == null || certChain.length == 0 || certChain[0] == null) {
-                throw new IllegalArgumentException("Expected non-null and non-empty certificate "
-                        + "chain passed as |certChain|. |certChain|="
-                        + Arrays.deepToString(certChain));
-            }
-
-            X509Certificate[] serverCertificates = new X509Certificate[certChain.length];
-            try {
-                for (int i = 0; i < certChain.length; ++i) {
-                    serverCertificates[i] = createCertificateFromBytes(certChain[i]);
-                }
-            } catch (CertificateException e) {
-                return new AndroidCertVerifyResult(CertVerifyStatusAndroid.UNABLE_TO_PARSE);
-            }
-
-            // Expired and not yet valid certificates would be rejected by the trust managers, but
-            // the trust managers report all certificate errors using the general
-            // CertificateException. In order to get more granular error information, cert validity
-            // time range is being checked separately.
-            try {
-                serverCertificates[0].checkValidity();
-                if (!verifyKeyUsage(serverCertificates[0])) {
-                    return new AndroidCertVerifyResult(CertVerifyStatusAndroid.INCORRECT_KEY_USAGE);
-                }
-            } catch (CertificateExpiredException e) {
-                return new AndroidCertVerifyResult(CertVerifyStatusAndroid.EXPIRED);
-            } catch (CertificateNotYetValidException e) {
-                return new AndroidCertVerifyResult(CertVerifyStatusAndroid.NOT_YET_VALID);
-            } catch (CertificateException e) {
-                return new AndroidCertVerifyResult(CertVerifyStatusAndroid.FAILED);
-            }
-
-            List<X509Certificate> verifiedChain;
-            try {
-                verifiedChain =
-                        mDefaultTrustManager.checkServerTrusted(serverCertificates, authType, host);
-            } catch (CertificateException eDefaultManager) {
-                try {
-                    if (mTestTrustManager == null) {
-                        throw new CertificateException();
-                    }
-                    verifiedChain = mTestTrustManager.checkServerTrusted(
-                            serverCertificates, authType, host);
-                } catch (CertificateException eTestManager) {
-                    // Neither of the trust managers confirms the validity of the certificate chain,
-                    // log the error message returned by the system trust manager.
-                    Log.i(TAG,
-                            "Failed to validate the certificate chain, error: "
-                                    + eDefaultManager.getMessage());
-                    return new AndroidCertVerifyResult(CertVerifyStatusAndroid.NO_TRUSTED_ROOT);
-                }
-            }
-
-            boolean isIssuedByKnownRoot = false;
-            if (verifiedChain.size() > 0) {
-                X509Certificate root = verifiedChain.get(verifiedChain.size() - 1);
-                isIssuedByKnownRoot = isKnownRoot(root);
-            }
-
-            return new AndroidCertVerifyResult(
-                    CertVerifyStatusAndroid.OK, isIssuedByKnownRoot, verifiedChain);
-        }
-    }
-
     /**
      * Ensures that the trust managers and certificate factory are initialized.
      */
@@ -451,27 +238,45 @@
             throws CertificateException, KeyStoreException, NoSuchAlgorithmException {
         assert Thread.holdsLock(sLock);
 
-        if (sInitialized) return;
-
-        sCertificateFactory = CertificateFactory.getInstance("X.509");
-
-        try {
-            sSystemKeyStore = KeyStore.getInstance("AndroidCAStore");
+        if (sCertificateFactory == null) {
+            sCertificateFactory = CertificateFactory.getInstance("X.509");
+        }
+        if (sDefaultTrustManager == null) {
+            sDefaultTrustManager = X509Util.createTrustManager(null);
+        }
+        if (!sLoadedSystemKeyStore) {
             try {
-                sSystemKeyStore.load(null);
+                sSystemKeyStore = KeyStore.getInstance("AndroidCAStore");
+                try {
+                    sSystemKeyStore.load(null);
+                } catch (IOException e) {
+                    // No IO operation is attempted.
+                }
+                sSystemCertificateDirectory =
+                        new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts");
+            } catch (KeyStoreException e) {
+                // Could not load AndroidCAStore. Continue anyway; isKnownRoot will always
+                // return false.
+            }
+            if (!sDisableNativeCodeForTest) {
+                nativeRecordCertVerifyCapabilitiesHistogram(sSystemKeyStore != null);
+            }
+            sLoadedSystemKeyStore = true;
+        }
+        if (sSystemTrustAnchorCache == null) {
+            sSystemTrustAnchorCache = new HashSet<Pair<X500Principal, PublicKey>>();
+        }
+        if (sTestKeyStore == null) {
+            sTestKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+            try {
+                sTestKeyStore.load(null);
             } catch (IOException e) {
                 // No IO operation is attempted.
             }
-            sSystemCertificateDirectory =
-                    new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts");
-        } catch (KeyStoreException e) {
-            // Could not load AndroidCAStore. Continue anyway; isKnownRoot will always
-            // return false.
         }
-        if (!sDisableNativeCodeForTest) {
-            nativeRecordCertVerifyCapabilitiesHistogram(sSystemKeyStore != null);
+        if (sTestTrustManager == null) {
+            sTestTrustManager = X509Util.createTrustManager(sTestKeyStore);
         }
-
         if (!sDisableNativeCodeForTest && sTrustStorageListener == null) {
             sTrustStorageListener = new TrustStorageListener();
             IntentFilter filter = new IntentFilter();
@@ -484,32 +289,57 @@
             }
             ContextUtils.getApplicationContext().registerReceiver(sTrustStorageListener, filter);
         }
-
-        sInitialized = true;
     }
 
     /**
-     * Returns the current CertificateVerifier instance.
+     * Creates a X509TrustManagerImplementation backed up by the given key
+     * store. When null is passed as a key store, system default trust store is
+     * used. Returns null if no created TrustManager was suitable.
+     * @throws KeyStoreException, NoSuchAlgorithmException on error initializing the TrustManager.
      */
-    private static CertificateVerifier getVerifier()
-            throws CertificateException, KeyStoreException, NoSuchAlgorithmException {
-        synchronized (sLock) {
-            if (sVerifier == null) {
-                sVerifier = new CertificateVerifier();
+    private static X509TrustManagerImplementation createTrustManager(KeyStore keyStore) throws
+            KeyStoreException, NoSuchAlgorithmException {
+        String algorithm = TrustManagerFactory.getDefaultAlgorithm();
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
+        tmf.init(keyStore);
+
+        for (TrustManager tm : tmf.getTrustManagers()) {
+            if (tm instanceof X509TrustManager) {
+                try {
+                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+                        return new X509TrustManagerJellyBean((X509TrustManager) tm);
+                    } else {
+                        return new X509TrustManagerIceCreamSandwich((X509TrustManager) tm);
+                    }
+                } catch (IllegalArgumentException e) {
+                    String className = tm.getClass().getName();
+                    Log.e(TAG, "Error creating trust manager (" + className + "): " + e);
+                }
             }
-            return sVerifier;
         }
+        Log.e(TAG, "Could not find suitable trust manager");
+        return null;
+    }
+
+    /**
+     * After each modification of test key store, trust manager has to be generated again.
+     */
+    private static void reloadTestTrustManager() throws KeyStoreException,
+            NoSuchAlgorithmException {
+        assert Thread.holdsLock(sLock);
+
+        sTestTrustManager = X509Util.createTrustManager(sTestKeyStore);
     }
 
     /**
      * After each modification by the system of the key store, trust manager has to be regenerated.
      */
-    private static void reloadDefaultTrustManager()
-            throws KeyStoreException, NoSuchAlgorithmException, CertificateException {
+    private static void reloadDefaultTrustManager() throws KeyStoreException,
+            NoSuchAlgorithmException, CertificateException {
         synchronized (sLock) {
-            // Invalidate the current verifier. Future certificate requests will
-            // use fresh state.
-            sVerifier = null;
+            sDefaultTrustManager = null;
+            sSystemTrustAnchorCache = null;
+            ensureInitializedLocked();
         }
         nativeNotifyKeyChainChanged();
     }
@@ -526,21 +356,100 @@
 
     public static void addTestRootCertificate(byte[] rootCertBytes) throws CertificateException,
             KeyStoreException, NoSuchAlgorithmException {
+        ensureInitialized();
         X509Certificate rootCert = createCertificateFromBytes(rootCertBytes);
         synchronized (sLock) {
-            sTestRoots.add(rootCert);
-            sVerifier = null;
+            sTestKeyStore.setCertificateEntry(
+                    "root_cert_" + Integer.toString(sTestKeyStore.size()), rootCert);
+            reloadTestTrustManager();
         }
     }
 
-    public static void clearTestRootCertificates()
-            throws NoSuchAlgorithmException, CertificateException, KeyStoreException {
+    public static void clearTestRootCertificates() throws NoSuchAlgorithmException,
+            CertificateException, KeyStoreException {
+        ensureInitialized();
         synchronized (sLock) {
-            sTestRoots.clear();
-            sVerifier = null;
+            try {
+                sTestKeyStore.load(null);
+                reloadTestTrustManager();
+            } catch (IOException e) {
+                // No IO operation is attempted.
+            }
         }
     }
 
+    private static final char[] HEX_DIGITS = {
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+        'a', 'b', 'c', 'd', 'e', 'f',
+    };
+
+    private static String hashPrincipal(X500Principal principal) throws NoSuchAlgorithmException {
+        // Android hashes a principal as the first four bytes of its MD5 digest, encoded in
+        // lowercase hex and reversed. Verified in 4.2, 4.3, and 4.4.
+        byte[] digest = MessageDigest.getInstance("MD5").digest(principal.getEncoded());
+        char[] hexChars = new char[8];
+        for (int i = 0; i < 4; i++) {
+            hexChars[2 * i] = HEX_DIGITS[(digest[3 - i] >> 4) & 0xf];
+            hexChars[2 * i + 1] = HEX_DIGITS[digest[3 - i] & 0xf];
+        }
+        return new String(hexChars);
+    }
+
+    private static boolean isKnownRoot(X509Certificate root)
+            throws NoSuchAlgorithmException, KeyStoreException {
+        assert Thread.holdsLock(sLock);
+
+        // Could not find the system key store. Conservatively report false.
+        if (sSystemKeyStore == null) return false;
+
+        // Check the in-memory cache first; avoid decoding the anchor from disk
+        // if it has been seen before.
+        Pair<X500Principal, PublicKey> key = new Pair<X500Principal, PublicKey>(
+                root.getSubjectX500Principal(), root.getPublicKey());
+
+        if (sSystemTrustAnchorCache.contains(key)) return true;
+
+        // Note: It is not sufficient to call sSystemKeyStore.getCertificiateAlias. If the server
+        // supplies a copy of a trust anchor, X509TrustManagerExtensions returns the server's
+        // version rather than the system one. getCertificiateAlias will then fail to find an anchor
+        // name. This is fixed upstream in https://android-review.googlesource.com/#/c/91605/
+        //
+        // TODO(davidben): When the change trickles into an Android release, query sSystemKeyStore
+        // directly.
+
+        // System trust anchors are stored under a hash of the principal. In case of collisions,
+        // a number is appended.
+        String hash = hashPrincipal(root.getSubjectX500Principal());
+        for (int i = 0; true; i++) {
+            String alias = hash + '.' + i;
+            if (!new File(sSystemCertificateDirectory, alias).exists()) break;
+
+            Certificate anchor = sSystemKeyStore.getCertificate("system:" + alias);
+            // It is possible for this to return null if the user deleted a trust anchor. In
+            // that case, the certificate remains in the system directory but is also added to
+            // another file. Continue iterating as there may be further collisions after the
+            // deleted anchor.
+            if (anchor == null) continue;
+
+            if (!(anchor instanceof X509Certificate)) {
+                // This should never happen.
+                String className = anchor.getClass().getName();
+                Log.e(TAG, "Anchor " + alias + " not an X509Certificate: " + className);
+                continue;
+            }
+
+            // If the subject and public key match, this is a system root.
+            X509Certificate anchorX509 = (X509Certificate) anchor;
+            if (root.getSubjectX500Principal().equals(anchorX509.getSubjectX500Principal())
+                    && root.getPublicKey().equals(anchorX509.getPublicKey())) {
+                sSystemTrustAnchorCache.add(key);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     /**
      * If an EKU extension is present in the end-entity certificate, it MUST contain either the
      * anyEKU or serverAuth or netscapeSGC or Microsoft SGC EKUs.
@@ -579,14 +488,78 @@
                                                                    String authType,
                                                                    String host)
             throws KeyStoreException, NoSuchAlgorithmException {
-        CertificateVerifier verifier;
+        if (certChain == null || certChain.length == 0 || certChain[0] == null) {
+            throw new IllegalArgumentException("Expected non-null and non-empty certificate "
+                    + "chain passed as |certChain|. |certChain|=" + Arrays.deepToString(certChain));
+        }
+
+
         try {
-            verifier = getVerifier();
+            ensureInitialized();
         } catch (CertificateException e) {
             return new AndroidCertVerifyResult(CertVerifyStatusAndroid.FAILED);
         }
 
-        return verifier.verifyServerCertificates(certChain, authType, host);
+        X509Certificate[] serverCertificates = new X509Certificate[certChain.length];
+        try {
+            for (int i = 0; i < certChain.length; ++i) {
+                serverCertificates[i] = createCertificateFromBytes(certChain[i]);
+            }
+        } catch (CertificateException e) {
+            return new AndroidCertVerifyResult(CertVerifyStatusAndroid.UNABLE_TO_PARSE);
+        }
+
+        // Expired and not yet valid certificates would be rejected by the trust managers, but the
+        // trust managers report all certificate errors using the general CertificateException. In
+        // order to get more granular error information, cert validity time range is being checked
+        // separately.
+        try {
+            serverCertificates[0].checkValidity();
+            if (!verifyKeyUsage(serverCertificates[0])) {
+                return new AndroidCertVerifyResult(
+                        CertVerifyStatusAndroid.INCORRECT_KEY_USAGE);
+            }
+        } catch (CertificateExpiredException e) {
+            return new AndroidCertVerifyResult(CertVerifyStatusAndroid.EXPIRED);
+        } catch (CertificateNotYetValidException e) {
+            return new AndroidCertVerifyResult(CertVerifyStatusAndroid.NOT_YET_VALID);
+        } catch (CertificateException e) {
+            return new AndroidCertVerifyResult(CertVerifyStatusAndroid.FAILED);
+        }
+
+        synchronized (sLock) {
+            // If no trust manager was found, fail without crashing on the null pointer.
+            if (sDefaultTrustManager == null) {
+                return new AndroidCertVerifyResult(CertVerifyStatusAndroid.FAILED);
+            }
+
+            List<X509Certificate> verifiedChain;
+            try {
+                verifiedChain = sDefaultTrustManager.checkServerTrusted(serverCertificates,
+                                                                        authType, host);
+            } catch (CertificateException eDefaultManager) {
+                try {
+                    verifiedChain = sTestTrustManager.checkServerTrusted(serverCertificates,
+                                                                         authType, host);
+                } catch (CertificateException eTestManager) {
+                    // Neither of the trust managers confirms the validity of the certificate chain,
+                    // log the error message returned by the system trust manager.
+                    Log.i(TAG, "Failed to validate the certificate chain, error: "
+                            + eDefaultManager.getMessage());
+                    return new AndroidCertVerifyResult(
+                            CertVerifyStatusAndroid.NO_TRUSTED_ROOT);
+                }
+            }
+
+            boolean isIssuedByKnownRoot = false;
+            if (verifiedChain.size() > 0) {
+                X509Certificate root = verifiedChain.get(verifiedChain.size() - 1);
+                isIssuedByKnownRoot = isKnownRoot(root);
+            }
+
+            return new AndroidCertVerifyResult(CertVerifyStatusAndroid.OK,
+                                               isIssuedByKnownRoot, verifiedChain);
+        }
     }
 
     public static void setDisableNativeCodeForTest(boolean disabled) {
diff --git a/net/proxy/proxy_config_service_linux.cc b/net/proxy/proxy_config_service_linux.cc
index a8b2b7c..c2ae1a8a 100644
--- a/net/proxy/proxy_config_service_linux.cc
+++ b/net/proxy/proxy_config_service_linux.cc
@@ -27,10 +27,13 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/nix/xdg_util.h"
+#include "base/sequenced_task_runner.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_tokenizer.h"
 #include "base/strings/string_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/timer/timer.h"
 #include "net/base/net_errors.h"
@@ -220,7 +223,7 @@
     // and pending tasks may then be deleted without being run.
     if (client_) {
       // gconf client was not cleaned up.
-      if (task_runner_->BelongsToCurrentThread()) {
+      if (task_runner_->RunsTasksInCurrentSequence()) {
         // We are on the UI thread so we can clean it safely. This is
         // the case at least for ui_tests running under Valgrind in
         // bug 16076.
@@ -238,10 +241,9 @@
     DCHECK(!client_);
   }
 
-  bool Init(const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
-            const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner)
+  bool Init(const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner)
       override {
-    DCHECK(glib_task_runner->BelongsToCurrentThread());
+    DCHECK(glib_task_runner->RunsTasksInCurrentSequence());
     DCHECK(!client_);
     DCHECK(!task_runner_.get());
     task_runner_ = glib_task_runner;
@@ -281,7 +283,7 @@
 
   void ShutDown() override {
     if (client_) {
-      DCHECK(task_runner_->BelongsToCurrentThread());
+      DCHECK(task_runner_->RunsTasksInCurrentSequence());
       // We must explicitly disable gconf notifications here, because the gconf
       // client will be shared between all setting getters, and they do not all
       // have the same lifetimes. (For instance, incognito sessions get their
@@ -300,7 +302,7 @@
   bool SetUpNotifications(
       ProxyConfigServiceLinux::Delegate* delegate) override {
     DCHECK(client_);
-    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
     GError* error = nullptr;
     notify_delegate_ = delegate;
     // We have to keep track of the IDs returned by gconf_client_notify_add() so
@@ -326,7 +328,7 @@
     return false;
   }
 
-  const scoped_refptr<base::SingleThreadTaskRunner>& GetNotificationTaskRunner()
+  const scoped_refptr<base::SequencedTaskRunner>& GetNotificationTaskRunner()
       override {
     return task_runner_;
   }
@@ -395,7 +397,7 @@
  private:
   bool GetStringByPath(base::StringPiece key, std::string* result) {
     DCHECK(client_);
-    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
     GError* error = nullptr;
     gchar* value = gconf_client_get_string(client_, key.data(), &error);
     if (HandleGError(error, key.data()))
@@ -408,7 +410,7 @@
   }
   bool GetBoolByPath(base::StringPiece key, bool* result) {
     DCHECK(client_);
-    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
     GError* error = nullptr;
     // We want to distinguish unset values from values defaulting to
     // false. For that we need to use the type-generic
@@ -431,7 +433,7 @@
   }
   bool GetIntByPath(base::StringPiece key, int* result) {
     DCHECK(client_);
-    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
     GError* error = nullptr;
     int value = gconf_client_get_int(client_, key.data(), &error);
     if (HandleGError(error, key.data()))
@@ -444,7 +446,7 @@
   bool GetStringListByPath(base::StringPiece key,
                            std::vector<std::string>* result) {
     DCHECK(client_);
-    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
     GError* error = nullptr;
     GSList* list =
         gconf_client_get_list(client_, key.data(), GCONF_VALUE_STRING, &error);
@@ -474,7 +476,7 @@
 
   // This is the callback from the debounce timer.
   void OnDebouncedNotification() {
-    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
     CHECK(notify_delegate_);
     // Forward to a method on the proxy config service delegate object.
     notify_delegate_->OnCheckProxyConfigSettings();
@@ -512,7 +514,7 @@
   // Task runner for the thread that we make gconf calls on. It should
   // be the UI thread and all our methods should be called on this
   // thread. Only for assertions.
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(SettingGetterImplGConf);
 };
@@ -543,7 +545,7 @@
     // without being run.
     if (client_) {
       // gconf client was not cleaned up.
-      if (task_runner_->BelongsToCurrentThread()) {
+      if (task_runner_->RunsTasksInCurrentSequence()) {
         // We are on the UI thread so we can clean it safely. This is
         // the case at least for ui_tests running under Valgrind in
         // bug 16076.
@@ -570,10 +572,9 @@
   // LoadAndCheckVersion() must be called *before* Init()!
   bool LoadAndCheckVersion(base::Environment* env);
 
-  bool Init(const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
-            const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner)
+  bool Init(const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner)
       override {
-    DCHECK(glib_task_runner->BelongsToCurrentThread());
+    DCHECK(glib_task_runner->RunsTasksInCurrentSequence());
     DCHECK(!client_);
     DCHECK(!task_runner_.get());
 
@@ -595,7 +596,7 @@
 
   void ShutDown() override {
     if (client_) {
-      DCHECK(task_runner_->BelongsToCurrentThread());
+      DCHECK(task_runner_->RunsTasksInCurrentSequence());
       // This also disables gsettings notifications.
       g_object_unref(socks_client_);
       g_object_unref(ftp_client_);
@@ -612,7 +613,7 @@
   bool SetUpNotifications(
       ProxyConfigServiceLinux::Delegate* delegate) override {
     DCHECK(client_);
-    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
     notify_delegate_ = delegate;
     // We could watch for the change-event signal instead of changed, but
     // since we have to watch more than one object, we'd still have to
@@ -632,7 +633,7 @@
     return true;
   }
 
-  const scoped_refptr<base::SingleThreadTaskRunner>& GetNotificationTaskRunner()
+  const scoped_refptr<base::SequencedTaskRunner>& GetNotificationTaskRunner()
       override {
     return task_runner_;
   }
@@ -712,7 +713,7 @@
   bool GetStringByPath(GSettings* client,
                        base::StringPiece key,
                        std::string* result) {
-    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
     gchar* value = libgio_loader_.g_settings_get_string(client, key.data());
     if (!value)
       return false;
@@ -721,20 +722,20 @@
     return true;
   }
   bool GetBoolByPath(GSettings* client, base::StringPiece key, bool* result) {
-    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
     *result = static_cast<bool>(
         libgio_loader_.g_settings_get_boolean(client, key.data()));
     return true;
   }
   bool GetIntByPath(GSettings* client, base::StringPiece key, int* result) {
-    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
     *result = libgio_loader_.g_settings_get_int(client, key.data());
     return true;
   }
   bool GetStringListByPath(GSettings* client,
                            base::StringPiece key,
                            std::vector<std::string>* result) {
-    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
     gchar** list = libgio_loader_.g_settings_get_strv(client, key.data());
     if (!list)
       return false;
@@ -748,7 +749,7 @@
 
   // This is the callback from the debounce timer.
   void OnDebouncedNotification() {
-    DCHECK(task_runner_->BelongsToCurrentThread());
+    DCHECK(task_runner_->RunsTasksInCurrentSequence());
     CHECK(notify_delegate_);
     // Forward to a method on the proxy config service delegate object.
     notify_delegate_->OnCheckProxyConfigSettings();
@@ -784,7 +785,7 @@
   // Task runner for the thread that we make gsettings calls on. It should
   // be the UI thread and all our methods should be called on this
   // thread. Only for assertions.
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
   LibGioLoader libgio_loader_;
 
@@ -941,8 +942,7 @@
     DCHECK_LT(inotify_fd_, 0);
   }
 
-  bool Init(const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
-            const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner)
+  bool Init(const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner)
       override {
     // This has to be called on the UI thread (http://crbug.com/69057).
     base::ThreadRestrictions::ScopedAllowIO allow_io;
@@ -958,7 +958,11 @@
       inotify_fd_ = -1;
       return false;
     }
-    file_task_runner_ = file_task_runner;
+
+    constexpr base::TaskTraits kTraits = {base::TaskPriority::USER_VISIBLE,
+                                          base::MayBlock()};
+    file_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(kTraits);
+
     // The initial read is done on the current thread, not
     // |file_task_runner_|, since we will need to have it for
     // SetUpAndFetchInitialConfig().
@@ -979,7 +983,7 @@
   bool SetUpNotifications(
       ProxyConfigServiceLinux::Delegate* delegate) override {
     DCHECK_GE(inotify_fd_, 0);
-    DCHECK(file_task_runner_->BelongsToCurrentThread());
+    DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
     // We can't just watch the kioslaverc file directly, since KDE will write
     // a new copy of it and then rename it whenever settings are changed and
     // inotify watches inodes (so we'll be watching the old deleted file after
@@ -999,7 +1003,7 @@
     return true;
   }
 
-  const scoped_refptr<base::SingleThreadTaskRunner>& GetNotificationTaskRunner()
+  const scoped_refptr<base::SequencedTaskRunner>& GetNotificationTaskRunner()
       override {
     return file_task_runner_;
   }
@@ -1259,7 +1263,7 @@
 
   // This is the callback from the debounce timer.
   void OnDebouncedNotification() {
-    DCHECK(file_task_runner_->BelongsToCurrentThread());
+    DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
     VLOG(1) << "inotify change notification for kioslaverc";
     UpdateCachedSettings();
     CHECK(notify_delegate_);
@@ -1272,7 +1276,7 @@
   // an event for kioslaverc is seen.
   void OnChangeNotification() {
     DCHECK_GE(inotify_fd_,  0);
-    DCHECK(file_task_runner_->BelongsToCurrentThread());
+    DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
     char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4];
     bool kioslaverc_touched = false;
     ssize_t r;
@@ -1343,10 +1347,9 @@
   string_map_type string_table_;
   strings_map_type strings_table_;
 
-  // Task runner of the file thread, for reading kioslaverc. If NULL,
-  // just read it directly (for testing). We also handle inotify events
-  // on this thread.
-  scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
+  // Task runner for doing blocking file IO on, as well as handling inotify
+  // events on.
+  scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(SettingGetterImplKDE);
 };
@@ -1567,18 +1570,17 @@
 
 void ProxyConfigServiceLinux::Delegate::SetUpAndFetchInitialConfig(
     const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
-    const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
-    const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner) {
+    const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) {
   // We should be running on the default glib main loop thread right
   // now. gconf can only be accessed from this thread.
-  DCHECK(glib_task_runner->BelongsToCurrentThread());
+  DCHECK(glib_task_runner->RunsTasksInCurrentSequence());
   glib_task_runner_ = glib_task_runner;
   io_task_runner_ = io_task_runner;
 
-  // If we are passed a NULL |io_task_runner| or |file_task_runner|, then we
-  // don't set up proxy setting change notifications. This should not be the
-  // usual case but is intended to/ simplify test setups.
-  if (!io_task_runner_.get() || !file_task_runner.get())
+  // If we are passed a NULL |io_task_runner|, then don't set up proxy
+  // setting change notifications. This should not be the usual case but is
+  // intended to/ simplify test setups.
+  if (!io_task_runner_.get())
     VLOG(1) << "Monitoring of proxy setting changes is disabled";
 
   // Fetch and cache the current proxy config. The config is left in
@@ -1594,8 +1596,7 @@
   // mislead us.
 
   bool got_config = false;
-  if (setting_getter_ &&
-      setting_getter_->Init(glib_task_runner, file_task_runner) &&
+  if (setting_getter_ && setting_getter_->Init(glib_task_runner) &&
       GetConfigFromSettings(&cached_config_)) {
     cached_config_.set_id(1);  // Mark it as valid.
     cached_config_.set_source(setting_getter_->GetConfigSource());
@@ -1620,10 +1621,10 @@
     // that we won't lose any updates that may have happened after the initial
     // fetch and before setting up notifications. We'll detect the common case
     // of no changes in OnCheckProxyConfigSettings() (or sooner) and ignore it.
-    if (io_task_runner.get() && file_task_runner.get()) {
-      scoped_refptr<base::SingleThreadTaskRunner> required_loop =
+    if (io_task_runner.get()) {
+      scoped_refptr<base::SequencedTaskRunner> required_loop =
           setting_getter_->GetNotificationTaskRunner();
-      if (!required_loop.get() || required_loop->BelongsToCurrentThread()) {
+      if (!required_loop.get() || required_loop->RunsTasksInCurrentSequence()) {
         // In this case we are already on an acceptable thread.
         SetUpNotifications();
       } else {
@@ -1650,9 +1651,9 @@
 // Depending on the SettingGetter in use, this method will be called
 // on either the UI thread (GConf) or the file thread (KDE).
 void ProxyConfigServiceLinux::Delegate::SetUpNotifications() {
-  scoped_refptr<base::SingleThreadTaskRunner> required_loop =
+  scoped_refptr<base::SequencedTaskRunner> required_loop =
       setting_getter_->GetNotificationTaskRunner();
-  DCHECK(!required_loop.get() || required_loop->BelongsToCurrentThread());
+  DCHECK(!required_loop.get() || required_loop->RunsTasksInCurrentSequence());
   if (!setting_getter_->SetUpNotifications(this))
     LOG(ERROR) << "Unable to set up proxy configuration change notifications";
 }
@@ -1670,7 +1671,7 @@
         ProxyConfig* config) {
   // This is called from the IO thread.
   DCHECK(!io_task_runner_.get() ||
-         io_task_runner_->BelongsToCurrentThread());
+         io_task_runner_->RunsTasksInCurrentSequence());
 
   // Simply return the last proxy configuration that glib_default_loop
   // notified us of.
@@ -1692,9 +1693,9 @@
 // Depending on the SettingGetter in use, this method will be called
 // on either the UI thread (GConf) or the file thread (KDE).
 void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() {
-  scoped_refptr<base::SingleThreadTaskRunner> required_loop =
+  scoped_refptr<base::SequencedTaskRunner> required_loop =
       setting_getter_->GetNotificationTaskRunner();
-  DCHECK(!required_loop.get() || required_loop->BelongsToCurrentThread());
+  DCHECK(!required_loop.get() || required_loop->RunsTasksInCurrentSequence());
   ProxyConfig new_config;
   bool valid = GetConfigFromSettings(&new_config);
   if (valid)
@@ -1717,7 +1718,7 @@
 
 void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
     const ProxyConfig& new_config) {
-  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
   VLOG(1) << "Proxy configuration changed";
   cached_config_ = new_config;
   for (auto& observer : observers_)
@@ -1728,9 +1729,9 @@
   if (!setting_getter_)
     return;
 
-  scoped_refptr<base::SingleThreadTaskRunner> shutdown_loop =
+  scoped_refptr<base::SequencedTaskRunner> shutdown_loop =
       setting_getter_->GetNotificationTaskRunner();
-  if (!shutdown_loop.get() || shutdown_loop->BelongsToCurrentThread()) {
+  if (!shutdown_loop.get() || shutdown_loop->RunsTasksInCurrentSequence()) {
     // Already on the right thread, call directly.
     // This is the case for the unittests.
     OnDestroy();
@@ -1742,9 +1743,9 @@
   }
 }
 void ProxyConfigServiceLinux::Delegate::OnDestroy() {
-  scoped_refptr<base::SingleThreadTaskRunner> shutdown_loop =
+  scoped_refptr<base::SequencedTaskRunner> shutdown_loop =
       setting_getter_->GetNotificationTaskRunner();
-  DCHECK(!shutdown_loop.get() || shutdown_loop->BelongsToCurrentThread());
+  DCHECK(!shutdown_loop.get() || shutdown_loop->RunsTasksInCurrentSequence());
   setting_getter_->ShutDown();
 }
 
diff --git a/net/proxy/proxy_config_service_linux.h b/net/proxy/proxy_config_service_linux.h
index 4832c20..d5ea597 100644
--- a/net/proxy/proxy_config_service_linux.h
+++ b/net/proxy/proxy_config_service_linux.h
@@ -23,6 +23,7 @@
 
 namespace base {
 class SingleThreadTaskRunner;
+class SequencedTaskRunner;
 }  // namespace base
 
 namespace net {
@@ -46,13 +47,14 @@
     // one, in the concrete implementations. Returns true on success. Must be
     // called before using other methods, and should be called on the thread
     // running the glib main loop.
-    // One of |glib_task_runner| and |file_task_runner| will be
-    // used for gconf/gsettings calls or reading necessary files, depending on
-    // the implementation.
-    virtual bool Init(
-        const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
-        const scoped_refptr<base::SingleThreadTaskRunner>&
-            file_task_runner) = 0;
+    // This interface supports both GNOME and KDE implementations. In the
+    // case of GNOME, the glib_task_runner will be used for interacting with
+    // gconf/gsettings as those APIs have thread affinity. Whereas in the case
+    // of KDE, its configuration files will be monitored at well-known locations
+    // and glib_task_runner will not be used. Instead, blocking file I/O
+    // operations will be posted directly using the task scheduler.
+    virtual bool Init(const scoped_refptr<base::SingleThreadTaskRunner>&
+                          glib_task_runner) = 0;
 
     // Releases the gconf/gsettings client, which clears cached directories and
     // stops notifications.
@@ -65,8 +67,8 @@
     // Returns the message loop for the thread on which this object
     // handles notifications, and also on which it must be destroyed.
     // Returns NULL if it does not matter.
-    virtual const scoped_refptr<base::SingleThreadTaskRunner>&
-        GetNotificationTaskRunner() = 0;
+    virtual const scoped_refptr<base::SequencedTaskRunner>&
+    GetNotificationTaskRunner() = 0;
 
     // Returns the source of proxy settings.
     virtual ProxyConfigSource GetConfigSource() = 0;
@@ -150,9 +152,9 @@
   // RemoveObserver) from the IO thread.
   //
   // Setting change notification callbacks can occur at any time and are
-  // run on either the UI thread (gconf/gsettings) or the file thread
-  // (KDE). The new settings are fetched on that thread, and the resulting
-  // proxy config is posted to the IO thread through
+  // run on either the UI thread (gconf/gsettings) or the sequenced task
+  // runner (KDE). The new settings are fetched on that thread, and the
+  // resulting proxy config is posted to the IO thread through
   // Delegate::SetNewProxyConfig(). We then notify observers on the IO
   // thread of the configuration change.
   //
@@ -161,8 +163,9 @@
   // The substance of the ProxyConfigServiceLinux implementation is
   // wrapped in the Delegate ref counted class. On deleting the
   // ProxyConfigServiceLinux, Delegate::OnDestroy() is posted to either
-  // the UI thread (gconf/gsettings) or the file thread (KDE) where change
-  // notifications will be safely stopped before releasing Delegate.
+  // the UI thread (gconf/gsettings) or a sequenced task runner (KDE)
+  // where change notifications will be safely stopped before releasing
+  // Delegate.
 
   class Delegate : public base::RefCountedThreadSafe<Delegate> {
    public:
@@ -179,12 +182,10 @@
     // thread running the default glib main loop, and so this method
     // must be called from the UI thread. The message loop for the IO
     // thread is specified so that notifications can post tasks to it
-    // (and for assertions). The message loop for the file thread is
-    // used to read any files needed to determine proxy settings.
+    // (and for assertions).
     void SetUpAndFetchInitialConfig(
         const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
-        const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
-        const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner);
+        const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
 
     // Handler for setting change notifications: fetches a new proxy
     // configuration from settings, and if this config is different
@@ -199,11 +200,12 @@
     ProxyConfigService::ConfigAvailability GetLatestProxyConfig(
         ProxyConfig* config);
 
-    // Posts a call to OnDestroy() to the UI or FILE thread, depending on the
-    // setting getter in use. Called from ProxyConfigServiceLinux's destructor.
+    // Posts a call to OnDestroy() to the UI or sequenced task runner,
+    // depending on the setting getter in use. Called from
+    // ProxyConfigServiceLinux's destructor.
     void PostDestroyTask();
-    // Safely stops change notifications. Posted to either the UI or FILE
-    // thread, depending on the setting getter in use.
+    // Safely stops change notifications. Posted to either the UI or
+    // sequenced task runner, depending on the setting getter in use.
     void OnDestroy();
 
    private:
@@ -287,11 +289,8 @@
 
   void SetupAndFetchInitialConfig(
       const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner) {
-    delegate_->SetUpAndFetchInitialConfig(glib_task_runner,
-                                          io_task_runner,
-                                          file_task_runner);
+      const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) {
+    delegate_->SetUpAndFetchInitialConfig(glib_task_runner, io_task_runner);
   }
   void OnCheckProxyConfigSettings() {
     delegate_->OnCheckProxyConfigSettings();
diff --git a/net/proxy/proxy_config_service_linux_unittest.cc b/net/proxy/proxy_config_service_linux_unittest.cc
index 07b4ee9..6650825a 100644
--- a/net/proxy/proxy_config_service_linux_unittest.cc
+++ b/net/proxy/proxy_config_service_linux_unittest.cc
@@ -194,8 +194,7 @@
     values = zero_values;
   }
 
-  bool Init(const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner,
-            const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner)
+  bool Init(const scoped_refptr<base::SingleThreadTaskRunner>& glib_task_runner)
       override {
     task_runner_ = glib_task_runner;
     return true;
@@ -208,7 +207,7 @@
     return true;
   }
 
-  const scoped_refptr<base::SingleThreadTaskRunner>& GetNotificationTaskRunner()
+  const scoped_refptr<base::SequencedTaskRunner>& GetNotificationTaskRunner()
       override {
     return task_runner_;
   }
@@ -261,7 +260,7 @@
   GConfValues values;
 
  private:
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
   SettingsTable<StringSetting, const char*> strings_table;
   SettingsTable<BoolSetting, BoolSettingValue> bools_table;
   SettingsTable<IntSetting, int> ints_table;
@@ -305,10 +304,8 @@
   // all on the calling thread (meant to be the thread with the
   // default glib main loop, which is the UI thread).
   void SetupAndInitialFetch() {
-    // We pass the mock IO thread as both the IO and file threads.
     config_service_->SetupAndFetchInitialConfig(
-        base::ThreadTaskRunnerHandle::Get(), io_thread_.task_runner(),
-        io_thread_.task_runner());
+        base::ThreadTaskRunnerHandle::Get(), io_thread_.task_runner());
   }
   // Synchronously gets the proxy config.
   ProxyConfigService::ConfigAvailability SyncGetLatestProxyConfig(
diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc
index c2d0a45..66873ee 100644
--- a/net/proxy/proxy_service.cc
+++ b/net/proxy/proxy_service.cc
@@ -1523,8 +1523,7 @@
 // static
 std::unique_ptr<ProxyConfigService>
 ProxyService::CreateSystemProxyConfigService(
-    const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
-    const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner) {
+    const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) {
 #if defined(OS_WIN)
   return base::MakeUnique<ProxyConfigServiceWin>();
 #elif defined(OS_IOS)
@@ -1548,10 +1547,10 @@
 
   // Synchronously fetch the current proxy config (since we are running on
   // glib_default_loop). Additionally register for notifications (delivered in
-  // either |glib_default_loop| or |file_task_runner|) to keep us updated when
-  // the proxy config changes.
-  linux_config_service->SetupAndFetchInitialConfig(
-      glib_thread_task_runner, io_task_runner, file_task_runner);
+  // either |glib_default_loop| or an internal sequenced task runner) to
+  // keep us updated when the proxy config changes.
+  linux_config_service->SetupAndFetchInitialConfig(glib_thread_task_runner,
+                                                   io_task_runner);
 
   return std::move(linux_config_service);
 #elif defined(OS_ANDROID)
diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h
index 5b22b3e..3e87bc8c 100644
--- a/net/proxy/proxy_service.h
+++ b/net/proxy/proxy_service.h
@@ -292,8 +292,7 @@
   // Creates a config service appropriate for this platform that fetches the
   // system proxy settings.
   static std::unique_ptr<ProxyConfigService> CreateSystemProxyConfigService(
-      const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& file_task_runner);
+      const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
 
   // This method should only be used by unit tests.
   void set_stall_proxy_auto_config_delay(base::TimeDelta delay) {
diff --git a/net/tools/net_watcher/net_watcher.cc b/net/tools/net_watcher/net_watcher.cc
index 02cb398..53ca175 100644
--- a/net/tools/net_watcher/net_watcher.cc
+++ b/net/tools/net_watcher/net_watcher.cc
@@ -181,7 +181,7 @@
   // Use the network loop as the file loop also.
   std::unique_ptr<net::ProxyConfigService> proxy_config_service(
       net::ProxyService::CreateSystemProxyConfigService(
-          network_loop.task_runner(), network_loop.task_runner()));
+          network_loop.task_runner()));
 
   // Uses |network_change_notifier|.
   net::NetworkChangeNotifier::AddIPAddressObserver(&net_watcher);
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc
index a1e384d..6d5e797 100644
--- a/net/url_request/url_request_context_builder.cc
+++ b/net/url_request/url_request_context_builder.cc
@@ -428,8 +428,7 @@
     // ProxyService::CreateSystemProxyConfigService()'s signature doesn't suck.
     if (!proxy_config_service_) {
       proxy_config_service_ = ProxyService::CreateSystemProxyConfigService(
-          base::ThreadTaskRunnerHandle::Get().get(),
-          context->GetFileTaskRunner());
+          base::ThreadTaskRunnerHandle::Get().get());
     }
 #endif  // !defined(OS_LINUX) && !defined(OS_ANDROID)
     proxy_service_ =
diff --git a/remoting/base/url_request_context_getter.cc b/remoting/base/url_request_context_getter.cc
index 09780bd..693726f 100644
--- a/remoting/base/url_request_context_getter.cc
+++ b/remoting/base/url_request_context_getter.cc
@@ -19,9 +19,8 @@
     scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
     : network_task_runner_(network_task_runner),
       file_task_runner_(file_task_runner),
-      proxy_config_service_(
-          net::ProxyService::CreateSystemProxyConfigService(
-              network_task_runner, file_task_runner)) {}
+      proxy_config_service_(net::ProxyService::CreateSystemProxyConfigService(
+          network_task_runner)) {}
 
 net::URLRequestContext* URLRequestContextGetter::GetURLRequestContext() {
   if (!url_request_context_.get()) {
diff --git a/services/service_manager/README.md b/services/service_manager/README.md
index 99578695..089af9d68 100644
--- a/services/service_manager/README.md
+++ b/services/service_manager/README.md
@@ -19,10 +19,10 @@
   system state.
 
 The Service Manager presents a series of Mojo
-[interfaces](https://cs.chromium.org/chromium/src/services/service_manager/public/interfaces)
+[interfaces](https://cs.chromium.org/chromium/src/services/service_manager/public/interfaces/)
 to services, though in practice most interaction with the Service Manager is
 made simpler by using its corresponding
-[C++ client library](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp).
+[C++ client library](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/).
 
 ## Mojo Recap
 
@@ -56,9 +56,9 @@
 
 The Service Manager is responsible for starting new service instances on-demand,
 and a given service many have any number of concurrently running instances. The
-Service Manager disambiguates service instances by their unique
-**identity**. A service's identity is represented by the 3-tuple of the its
-**service name**, **user ID**, and **instance qualifier**:
+Service Manager disambiguates service instances by their unique **identity**. A
+service's identity is represented by the 3-tuple of its **service name**, **user
+ID**, and **instance qualifier**:
 
 * The service name is a free-form -- typically short -- string identifying the
   the specific service being run in the instance.
@@ -93,7 +93,7 @@
 
 Consider this implementation of the `service_manager::Service` interface:
 
-**`//services//my_service/my_service.h`**
+**`//services/my_service/my_service.h`**
 ``` cpp
 #include "base/macros.h"
 #include "services/service_manager/public/cpp/service.h"
@@ -117,7 +117,7 @@
 }  // namespace my_service
 ```
 
-**`//services//my_service/my_service.cc`**
+**`//services/my_service/my_service.cc`**
 ``` cpp
 #include "services/my_service/my_service.h"
 
@@ -167,7 +167,7 @@
 {
   "name": "my_service",
   "display_name": "My Service",
-  "interface_provider_spec": {
+  "interface_provider_specs": {
     "service_manager:connector": {}
   }
 }
@@ -225,7 +225,7 @@
 
 The `Service` implementation is guaranteed to receive a single `OnStart()`
 invocation from the Service Manager before anything else hapens. Once this
-method is called, the implementation can access it
+method is called, the implementation can access its
 `service_manager::ServiceContext` via `context()`. This object itself exposes a
 few values:
 
@@ -272,13 +272,12 @@
   the remote service expects us to bind to a concrete implementation of
   the requested interface.
 
-The Service Manager client library provides a
-`service_manager::InterfaceRegistry` class definition which can make it easier
-for services to bind incoming interface requests. Typesafe binding callbacks are
-added to an `InterfaceRegistry` ahead of time, and the incoming arguments to
-`OnBindInterface` can be forwarded to the registry, which will bind the message
-pipe if it knows how. For example, we could modify our `MyService`
-implementation as follows:
+The Service Manager client library provides a `service_manager::BinderRegistry`
+class definition which can make it easier for services to bind incoming
+interface requests. Typesafe binding callbacks are added to an `BinderRegistry`
+ahead of time, and the incoming arguments to `OnBindInterface` can be forwarded
+to the registry, which will bind the message pipe if it knows how. For example,
+we could modify our `MyService` implementation as follows:
 
 ``` cpp
 namespace {
@@ -292,7 +291,7 @@
 
 MyService::MyService() {
   // Imagine |registry_| is added as a member of MyService, with type
-  // service_manager::InterfaceRegistry.
+  // service_manager::BinderRegistry.
 
   // The |my_service::mojom::Database| interface type is inferred by the
   // compiler in the AddInterface call, and this effectively adds the bound
@@ -315,7 +314,7 @@
 
 ## Service Manifests
 
-If some service were to come along and attempt to connect to `my_servce` and
+If some service were to come along and attempt to connect to `my_service` and
 bind the `my_service::mojom::Database` interface, we might see the Service
 Manager spit out an error log complaining that `InterfaceProviderSpec` prevented
 a connection to `my_service`.
@@ -349,7 +348,7 @@
 {
   "name": "my_service",
   "display_name": "My Service",
-  "interface_provider_spec": {
+  "interface_provider_specs": {
     "service_manager:connector": {
       "provides": {
         "database": [
@@ -372,7 +371,7 @@
 {
   "name": "other_service",
   "display_name": "Other Service",
-  "interface_provider_spec": {
+  "interface_provider_specs": {
     "service_manager:connector": {
       "requires": {
         "my_service": [ "database" ]
@@ -423,12 +422,12 @@
 }
 
 TEST_F(MyServiceTest, Basic) {
-  my_service::mojom::DatabsaePtr database;
+  my_service::mojom::DatabasePtr database;
   connector()->BindInterface("my_service", &database);
 
   base::RunLoop loop;
 
-  // This assumes DropTable expects a response with no arugments. When the
+  // This assumes DropTable expects a response with no arguments. When the
   // response is received, the RunLoop is quit.
   database->DropTable("foo", loop.QuitClosure());
 
@@ -478,7 +477,7 @@
 {
   "name": "my_service_unittests",
   "display_name": "my_service tests",
-  "interface_provider_spec": {
+  "interface_provider_specs": {
     "service_manager:connector": {
       "requires": {
         "my_service": [ "database" ]
diff --git a/services/ui/public/interfaces/window_manager.mojom b/services/ui/public/interfaces/window_manager.mojom
index e3896e35..09a909f 100644
--- a/services/ui/public/interfaces/window_manager.mojom
+++ b/services/ui/public/interfaces/window_manager.mojom
@@ -102,6 +102,10 @@
   // Type: bool.
   const string kImmersiveFullscreen_Property = "prop:immersive-fullscreen";
 
+  // The window's minimum size as defined by its content. Maps to
+  // aura::client::kMinimumSize_Property. Type: gfx::Size.
+  const string kMinimumSize_Property = "prop:minimum-size";
+
   // Internal window name. Useful for debugging. Maps to aura::client::kNameKey.
   // Type: mojom::String
   const string kName_Property = "prop:name";
diff --git a/storage/public/interfaces/blobs.mojom b/storage/public/interfaces/blobs.mojom
index 0d4bb62..c89f648f 100644
--- a/storage/public/interfaces/blobs.mojom
+++ b/storage/public/interfaces/blobs.mojom
@@ -51,6 +51,11 @@
 // optionally also directly contain the data, in which case the blob registry
 // can decide to either use the embedded data or later request the data again.
 struct DataElementBytes {
+  // Maximum size of all DataElementBytes that have embedded data included in
+  // any particular call to register a new blob.
+  // Equivalent in meaning to storage::kDefaultIPCMemorySize.
+  const uint64 kMaximumEmbeddedDataSize = 256000;
+
   // Size of the data.
   uint64 length;
   // Optionally embedded data. If present, the size of this array should be
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index 492e0e5..01b0745 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -4000,12 +4000,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_js_integration_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "mojo_js_unittests"
       },
       {
@@ -4681,12 +4675,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_js_integration_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "mojo_js_unittests"
       },
       {
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 201ce29..3aa62dd 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -26319,7 +26319,7 @@
           "dimension_sets": [
             {
               "gpu": "102b:0534",
-              "id": "build149-m1",
+              "id": "build150-m1",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
             }
@@ -26349,7 +26349,7 @@
           "dimension_sets": [
             {
               "gpu": "102b:0534",
-              "id": "build149-m1",
+              "id": "build150-m1",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
             }
@@ -29200,7 +29200,7 @@
           "dimension_sets": [
             {
               "gpu": "102b:0534",
-              "id": "build148-m1",
+              "id": "build151-m1",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
             }
@@ -29230,7 +29230,7 @@
           "dimension_sets": [
             {
               "gpu": "102b:0534",
-              "id": "build148-m1",
+              "id": "build151-m1",
               "os": "Ubuntu-14.04",
               "pool": "Chrome-perf"
             }
@@ -44030,7 +44030,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:1626",
-              "id": "build123-b1",
+              "id": "build125-b1",
               "os": "Mac-10.11",
               "pool": "Chrome-perf"
             }
@@ -44060,7 +44060,7 @@
           "dimension_sets": [
             {
               "gpu": "8086:1626",
-              "id": "build123-b1",
+              "id": "build125-b1",
               "os": "Mac-10.11",
               "pool": "Chrome-perf"
             }
diff --git a/testing/buildbot/client.v8.chromium.json b/testing/buildbot/client.v8.chromium.json
index bb493a0..09e30211 100644
--- a/testing/buildbot/client.v8.chromium.json
+++ b/testing/buildbot/client.v8.chromium.json
@@ -236,12 +236,6 @@
         "swarming": {
           "can_use_on_swarming_builders": true
         },
-        "test": "mojo_js_integration_tests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true
-        },
         "test": "mojo_js_unittests"
       },
       {
diff --git a/testing/buildbot/filters/fuchsia.base_unittests.filter b/testing/buildbot/filters/fuchsia.base_unittests.filter
index 3cb4886ad..1141a424 100644
--- a/testing/buildbot/filters/fuchsia.base_unittests.filter
+++ b/testing/buildbot/filters/fuchsia.base_unittests.filter
@@ -4,14 +4,6 @@
 -ActivityTrackerTest.CreateWithFileTest
 -ActivityTrackerTest.ProcessDeathTest
 -ActivityTrackerTest.ThreadDeathTest
--AllocationRegisterTest.ChangeContextAfterInsertion
--AllocationRegisterTest.DoubleFreeIsAllowed
--AllocationRegisterTest.DoubleInsertOverwrites
--AllocationRegisterTest.InsertRemove
--AllocationRegisterTest.InsertRemoveCollisions
--AllocationRegisterTest.InsertRemoveRandomOrder
--AllocationRegisterTest.OverflowAllocationTest
--AllocationRegisterTest.OverflowBacktraceTest
 -CrashLoggingTest.ChunkValue
 -CrashLoggingTest.InitSize
 -CrashLoggingTest.ScopedCrashKey
@@ -28,9 +20,6 @@
 -DiscardableSharedMemoryTest.MappedSize
 -DiscardableSharedMemoryTest.Purge
 -DiscardableSharedMemoryTest.ZeroSize
--EventWriterTest.HeapDumpAggregation
--EventWriterTest.HeapDumpNoBacktraceNoType
--EventWriterTest.SerializeHeapProfileEventData
 -FeatureListTest.StoreAndRetrieveAssociatedFeaturesFromSharedMemory
 -FeatureListTest.StoreAndRetrieveFeaturesFromSharedMemory
 -FieldTrialListTest.AddTrialsToAllocator
@@ -77,8 +66,6 @@
 -FileUtilTest.ExecutableExistsInPath
 -FileUtilTest.FileToFILE
 -FileUtilTest.NormalizeFilePathSymlinks
--HeapDumpWriterTest.BacktraceIndex
--HeapDumpWriterTest.TypeId
 -LoggingTest.CheckCausesDistinctBreakpoints
 -MemoryMappedFileTest.ExtendableFile
 -MemoryMappedFileTest.MapLargePartialRegionInTheMiddle
@@ -198,7 +185,6 @@
 -TaskSchedulerWorkerPoolImplPostTaskBeforeStartTest.PostTasksBeforeStart
 -TaskSchedulerWorkerPoolStandbyPolicyTest.InitLazy
 -TaskSchedulerWorkerPoolStandbyPolicyTest.InitOne
--TraceEventTestFixture.TestTraceFlush
 -TrackedObjectsTest.ReuseRetiredThreadData
 -TrackedTimeTest.TrackedTimerDuration
 -VerifyPathControlledByUserTest.BadPaths
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index f305fcf..154f73e8 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -712,10 +712,6 @@
     "label": "//mojo/common:mojo_common_unittests",
     "type": "console_test_launcher",
   },
-  "mojo_js_integration_tests": {
-    "label": "//mojo/edk/js/tests:mojo_js_integration_tests",
-    "type": "console_test_launcher",
-  },
   "mojo_js_unittests": {
     "label": "//mojo/edk/js/tests:mojo_js_unittests",
     "type": "console_test_launcher",
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 71c9bcd..19e98164 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -17318,9 +17318,9 @@
 crbug.com/591099 inspector-protocol/accessibility/accessibility-nameSources-input.js [ Crash Timeout ]
 crbug.com/591099 inspector-protocol/accessibility/accessibility-nameSources-labelledby.js [ Crash Timeout ]
 crbug.com/591099 inspector-protocol/accessibility/accessibility-nameSources-visiblity.js [ Crash Timeout ]
-crbug.com/591099 inspector-protocol/cpu-profiler/enable-disable.html [ Failure ]
-crbug.com/591099 inspector-protocol/cpu-profiler/record-cpu-profile.html [ Failure ]
-crbug.com/591099 inspector-protocol/cpu-profiler/stop-without-preceeding-start.html [ Failure ]
+crbug.com/591099 inspector-protocol/cpu-profiler/enable-disable.js [ Failure ]
+crbug.com/591099 inspector-protocol/cpu-profiler/record-cpu-profile.js [ Failure ]
+crbug.com/591099 inspector-protocol/cpu-profiler/stop-without-preceeding-start.js [ Failure ]
 crbug.com/591099 inspector-protocol/css/css-add-rule.html [ Timeout ]
 crbug.com/591099 inspector-protocol/css/css-coverage-poll.html [ Failure ]
 crbug.com/591099 inspector-protocol/css/css-fonts-updated-event.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
index 1fb0a15f3..50c24b1 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-network-service
@@ -1524,11 +1524,15 @@
 Bug(none) external/wpt/websockets/Secure-Send-binary-blob.htm [ Failure Timeout ]
 Bug(none) external/wpt/websockets/Send-binary-blob.htm [ Failure Timeout ]
 Bug(none) external/wpt/webusb/idlharness.https.html [ Failure Timeout ]
+Bug(none) external/wpt/webusb/usbConnectionEvent.https.html [ Timeout ]
 Bug(none) external/wpt/webusb/usb-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html [ Timeout ]
 Bug(none) external/wpt/webusb/usb-allowed-by-feature-policy-attribute.https.sub.html [ Failure Timeout ]
 Bug(none) external/wpt/webusb/usb-allowed-by-feature-policy.https.sub.html [ Failure Timeout ]
 Bug(none) external/wpt/webusb/usb-default-feature-policy.https.sub.html [ Failure Timeout ]
 Bug(none) external/wpt/webusb/usb-disabled-by-feature-policy.https.sub.html [ Failure Timeout ]
+Bug(none) external/wpt/webusb/usb-manual.https.html [ Timeout ]
+Bug(none) external/wpt/webusb/usbDevice-iframe.https.html [ Timeout ]
+Bug(none) external/wpt/webusb/usbDevice.https.html [ Timeout ]
 Bug(none) external/wpt/webvtt/rendering/cues-with-video/processing-model/selectors/cue_function/class_object/class_timestamp_future.html [ Failure Timeout ]
 Bug(none) external/wpt/workers/SharedWorker_blobUrl.html [ Failure Timeout ]
 Bug(none) external/wpt/workers/constructors/Worker/Blob-url.html [ Failure Timeout ]
@@ -2660,8 +2664,8 @@
 Bug(none) inspector-protocol/layout-fonts/unicode-range-combining-chars-fallback.html [ Failure ]
 Bug(none) inspector-protocol/network/resource-type.html [ Timeout ]
 Bug(none) inspector-protocol/network/websocket-initiator.html [ Timeout ]
-Bug(none) inspector-protocol/worker/exception-from-worker-contains-stack.html [ Timeout ]
-Bug(none) inspector-protocol/worker/worker-console.html [ Timeout ]
+Bug(none) inspector-protocol/worker/exception-from-worker-contains-stack.js [ Timeout ]
+Bug(none) inspector-protocol/worker/worker-console.js [ Timeout ]
 Bug(none) inspector/agents-enable-disable.html [ Timeout ]
 Bug(none) inspector/animation/animation-KeyframeEffectReadOnly-crash.html [ Timeout ]
 Bug(none) inspector/animation/animation-group-matching-animations.html [ Timeout ]
@@ -3245,11 +3249,6 @@
 Bug(none) tables/mozilla_expected_failures/marvin/backgr_fixed-bg.html [ Failure ]
 Bug(none) traversal/node-iterator-009.html [ Failure ]
 Bug(none) traversal/tree-walker-006.html [ Failure ]
-Bug(none) usb/test-polyfil.html [ Timeout ]
-Bug(none) usb/usb-connection-event.html [ Timeout ]
-Bug(none) usb/usb.html [ Timeout ]
-Bug(none) usb/usbDevice-iframe.html [ Timeout ]
-Bug(none) usb/usbDevice.html [ Timeout ]
 Bug(none) vibration/vibration-iframe.html [ Timeout ]
 Bug(none) vibration/vibration.html [ Timeout ]
 Bug(none) virtual [ Crash Failure Timeout ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index af0e605..3b30542 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -63,7 +63,6 @@
 Bug(none) transforms/ [ Skip ]
 Bug(none) traversal/ [ Skip ]
 Bug(none) typedcssom/ [ Skip ]
-Bug(none) usb/ [ Skip ]
 Bug(none) vibration/ [ Skip ]
 Bug(none) wake_lock/ [ Skip ]
 Bug(none) webaudio/ [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/MSANExpectations b/third_party/WebKit/LayoutTests/MSANExpectations
index 03420a8..48c4698 100644
--- a/third_party/WebKit/LayoutTests/MSANExpectations
+++ b/third_party/WebKit/LayoutTests/MSANExpectations
@@ -5,24 +5,16 @@
 
 # Those stress V8's simulator, making them very slow under MSan.
 crbug.com/420198 [ Linux ] fast/js/regress [ Skip ]
-crbug.com/420198 [ Linux ] virtual/slimmingpaint/fast/js/regress [ Skip ]
-crbug.com/420198 [ Linux ] editing/selection/move-by-word-visually-crash-test-5.html [ Skip ]
-crbug.com/420198 [ Linux ] perf/array-nested-loop.html [ Skip ]
 crbug.com/420198 [ Linux ] fast/css/fontface-arraybuffer.html [ Skip ]
-crbug.com/420198 [ Linux ] virtual/slimmingpaint/fast/css/fontface-arraybuffer.html [ Skip ]
 
 # Deliberate infinite recursion. A JS exception is expected, but may crash with
 # a stack overflow due to bloated stack frames under MSan.
 crbug.com/420606 [ Linux ] fast/workers/shared-worker-constructor.html [ Skip ]
-crbug.com/420606 [ Linux ] virtual/slimmingpaint/fast/workers/shared-worker-constructor.html [ Skip ]
 crbug.com/420606 [ Linux ] fast/workers/worker-constructor.html [ Skip ]
-crbug.com/420606 [ Linux ] virtual/slimmingpaint/fast/workers/worker-constructor.html [ Skip ]
 
 # Flaky under MSan (hang forever).
 crbug.com/422982 [ Linux ] virtual/threaded [ Skip ]
 
-crbug.com/450639 [ Linux ] inspector/tracing/animations.html [ Timeout ]
-crbug.com/450639 [ Linux ] virtual/deferred/inspector/tracing/animations.html [ Timeout ]
 crbug.com/700795 [ Linux ] inspector/animation/animation-transition-setTiming-crash.html [ Skip ]
 
 crbug.com/454267 [ Linux ] virtual/gpu/fast/canvas/canvas-arc-360-winding.html [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index 99808d1..1690590 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -345,7 +345,7 @@
 crbug.com/584807 printing/webgl-oversized-printing.html [ Slow ]
 crbug.com/584807 virtual/threaded/printing/webgl-oversized-printing.html [ Slow ]
 
-crbug.com/592183 usb/usbDevice.html [ Slow ]
+crbug.com/592183 external/wpt/webusb/usbDevice.https.html [ Slow ]
 
 crbug.com/594189 virtual/spv2/fast/overflow/lots-of-sibling-inline-boxes.html [ Slow ]
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cssom-view/HTMLBody-ScrollArea_quirksmode-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cssom-view/HTMLBody-ScrollArea_quirksmode-expected.txt
deleted file mode 100644
index 5729e39..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/cssom-view/HTMLBody-ScrollArea_quirksmode-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This is a testharness.js-based test.
-PASS Ensure that body element is loaded. 
-PASS Ensure that style.overflowY can be set properly. 
-PASS document.compatMode should be BackCompat in quirks. 
-FAIL document.scrollingElement should be body element in quirks. assert_equals: scrollingElement in quirks mode should default to body element. expected Element node <body id="thebody" style="overflow-y: scroll;">
-    <div ... but got null
-PASS scrollingElement in quirks should be null when body is potentially scrollable. 
-FAIL scrollingElement in quirks should be body if any of document and body has a visible overflow. assert_equals: expected Element node <body id="thebody" style="overflow-y: scroll;">
-    <div ... but got null
-PASS When body potentially scrollable, document.body.scrollHeight changes when changing the height of the body content in quirks. 
-FAIL When body not potentially scrollable, document.body.scrollHeight always equals window.innerHeight in quirks. (cond. visible, scroll) assert_equals: expected 600 but got 240
-PASS When body not potentially scrollable, document.body.scrollHeight always equals window.innerHeight in quirks. (cond. scroll, visible) 
-PASS When body not potentially scrollable, document.body.scrollHeight always equals window.innerHeight in quirks. (cond. visible, visible) 
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cssom-view/HTMLBody-ScrollArea_quirksmode.html b/third_party/WebKit/LayoutTests/external/wpt/cssom-view/HTMLBody-ScrollArea_quirksmode.html
index 46fd5010..8255d130 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/cssom-view/HTMLBody-ScrollArea_quirksmode.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/cssom-view/HTMLBody-ScrollArea_quirksmode.html
@@ -44,6 +44,8 @@
     assert_equals(document.body.style.overflowY, "scroll", "Could not set document.body.style.overflowY to 'scroll'.");
     document.documentElement.style.overflowY = "scroll";
     assert_equals(document.documentElement.style.overflowY, "scroll", "Could not set document.documentElement.style.overflow to 'scroll'.");
+    document.documentElement.style.overflowY = "";
+    document.body.style.overflowY = "";
 }, "Ensure that style.overflowY can be set properly.")
 
 test(function() {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-expected.txt
deleted file mode 100644
index 86c987b..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL scrollingElement in quirks mode assert_equals: expected Element node <body style="overflow: scroll;"></body> but got null
-PASS scrollingElement in no-quirks mode 
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-quirks-dynamic-001-ref.html b/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-quirks-dynamic-001-ref.html
new file mode 100644
index 0000000..683198a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-quirks-dynamic-001-ref.html
@@ -0,0 +1,3 @@
+<!-- quirks mode -->
+<html style="overflow:scroll">
+<body style="overflow:scroll">The body box should have scrollbars.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-quirks-dynamic-001.html b/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-quirks-dynamic-001.html
new file mode 100644
index 0000000..344e299
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-quirks-dynamic-001.html
@@ -0,0 +1,17 @@
+<!-- quirks mode -->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>CSSOM View Test: Dynamically changing scrollingElement to html in quirks mode</title>
+    <link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+    <link rel="help" href="https://www.w3.org/TR/cssom-view-1/#dom-document-scrollingelement">
+    <link rel="match" href="scrollingElement-quirks-dynamic-001-ref.html">
+    <meta name="assert" content="Checks that setting the overflow on html to scroll will stop propagating body scrollbars to viewport.">
+  </head>
+  <body style="overflow:scroll">The body box should have scrollbars.
+    <script>
+      document.body.offsetTop; // force layout
+      document.documentElement.style.overflow = "scroll";
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-quirks-dynamic-002-ref.html b/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-quirks-dynamic-002-ref.html
new file mode 100644
index 0000000..c8a78398
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-quirks-dynamic-002-ref.html
@@ -0,0 +1,2 @@
+<!-- quirks mode -->
+<body style="overflow:scroll">The body box should not have scrollbars.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-quirks-dynamic-002.html b/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-quirks-dynamic-002.html
new file mode 100644
index 0000000..8495be2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cssom-view/scrollingElement-quirks-dynamic-002.html
@@ -0,0 +1,17 @@
+<!-- quirks mode -->
+<html style="overflow:scroll">
+  <head>
+    <meta charset="utf-8">
+    <title>CSSOM View Test: Dynamically changing scrollingElement to body in quirks mode</title>
+    <link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+    <link rel="help" href="https://www.w3.org/TR/cssom-view-1/#dom-document-scrollingelement">
+    <link rel="match" href="scrollingElement-quirks-dynamic-002-ref.html">
+    <meta name="assert" content="Checks that setting the overflow on html to visible will propagate body scrollbars to viewport.">
+  </head>
+  <body style="overflow:scroll">The body box should not have scrollbars.
+    <script>
+      document.body.offsetTop; // force layout
+      document.documentElement.style.overflow = "visible";
+    </script>
+  </body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/README.md b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/README.md
new file mode 100644
index 0000000..a06b986b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/README.md
@@ -0,0 +1,4 @@
+This directory contains Chromium-specific test resources.
+
+The files `mojo_bindings.js` and `*.mojom.js` are automatically generated by the
+Chromium build process and should not be edited manually.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js
new file mode 100644
index 0000000..f125fac
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/chooser_service.mojom.js
@@ -0,0 +1,281 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+(function() {
+  var mojomId = 'device/usb/public/interfaces/chooser_service.mojom';
+  if (mojo.internal.isMojomLoaded(mojomId)) {
+    console.warn('The following mojom is loaded multiple times: ' + mojomId);
+    return;
+  }
+  mojo.internal.markMojomLoaded(mojomId);
+
+  // TODO(yzshen): Define these aliases to minimize the differences between the
+  // old/new modes. Remove them when the old mode goes away.
+  var bindings = mojo;
+  var associatedBindings = mojo;
+  var codec = mojo.internal;
+  var validator = mojo.internal;
+  var device$ =
+      mojo.internal.exposeNamespace('device.mojom');
+  if (mojo.config.autoLoadMojomDeps) {
+    mojo.internal.loadMojomIfNecessary(
+        'device/usb/public/interfaces/device.mojom',
+        new URL('device.mojom.js',
+                document.currentScript.src).href);
+  }
+  var device_manager$ =
+      mojo.internal.exposeNamespace('device.mojom');
+  if (mojo.config.autoLoadMojomDeps) {
+    mojo.internal.loadMojomIfNecessary(
+        'device/usb/public/interfaces/device_manager.mojom',
+        new URL('device_manager.mojom.js',
+                document.currentScript.src).href);
+  }
+
+
+
+  function UsbChooserService_GetPermission_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbChooserService_GetPermission_Params.prototype.initDefaults_ = function() {
+    this.deviceFilters = null;
+  };
+  UsbChooserService_GetPermission_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbChooserService_GetPermission_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbChooserService_GetPermission_Params.deviceFilters
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 8, new codec.PointerTo(device_manager$.UsbDeviceFilter), false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbChooserService_GetPermission_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbChooserService_GetPermission_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbChooserService_GetPermission_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.deviceFilters = decoder.decodeArrayPointer(new codec.PointerTo(device_manager$.UsbDeviceFilter));
+    return val;
+  };
+
+  UsbChooserService_GetPermission_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbChooserService_GetPermission_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeArrayPointer(new codec.PointerTo(device_manager$.UsbDeviceFilter), val.deviceFilters);
+  };
+  function UsbChooserService_GetPermission_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbChooserService_GetPermission_ResponseParams.prototype.initDefaults_ = function() {
+    this.result = null;
+  };
+  UsbChooserService_GetPermission_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbChooserService_GetPermission_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbChooserService_GetPermission_ResponseParams.result
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, device$.UsbDeviceInfo, true);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbChooserService_GetPermission_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbChooserService_GetPermission_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbChooserService_GetPermission_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.result = decoder.decodeStructPointer(device$.UsbDeviceInfo);
+    return val;
+  };
+
+  UsbChooserService_GetPermission_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbChooserService_GetPermission_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStructPointer(device$.UsbDeviceInfo, val.result);
+  };
+  var kUsbChooserService_GetPermission_Name = 0;
+
+  function UsbChooserServicePtr(handleOrPtrInfo) {
+    this.ptr = new bindings.InterfacePtrController(UsbChooserService,
+                                                   handleOrPtrInfo);
+  }
+
+  function UsbChooserServiceAssociatedPtr(associatedInterfacePtrInfo) {
+    this.ptr = new associatedBindings.AssociatedInterfacePtrController(
+        UsbChooserService, associatedInterfacePtrInfo);
+  }
+
+  UsbChooserServiceAssociatedPtr.prototype =
+      Object.create(UsbChooserServicePtr.prototype);
+  UsbChooserServiceAssociatedPtr.prototype.constructor =
+      UsbChooserServiceAssociatedPtr;
+
+  function UsbChooserServiceProxy(receiver) {
+    this.receiver_ = receiver;
+  }
+  UsbChooserServicePtr.prototype.getPermission = function() {
+    return UsbChooserServiceProxy.prototype.getPermission
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbChooserServiceProxy.prototype.getPermission = function(deviceFilters) {
+    var params = new UsbChooserService_GetPermission_Params();
+    params.deviceFilters = deviceFilters;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbChooserService_GetPermission_Name,
+          codec.align(UsbChooserService_GetPermission_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbChooserService_GetPermission_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbChooserService_GetPermission_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+
+  function UsbChooserServiceStub(delegate) {
+    this.delegate_ = delegate;
+  }
+  UsbChooserServiceStub.prototype.getPermission = function(deviceFilters) {
+    return this.delegate_ && this.delegate_.getPermission && this.delegate_.getPermission(deviceFilters);
+  }
+
+  UsbChooserServiceStub.prototype.accept = function(message) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    default:
+      return false;
+    }
+  };
+
+  UsbChooserServiceStub.prototype.acceptWithResponder =
+      function(message, responder) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    case kUsbChooserService_GetPermission_Name:
+      var params = reader.decodeStruct(UsbChooserService_GetPermission_Params);
+      this.getPermission(params.deviceFilters).then(function(response) {
+        var responseParams =
+            new UsbChooserService_GetPermission_ResponseParams();
+        responseParams.result = response.result;
+        var builder = new codec.MessageV1Builder(
+            kUsbChooserService_GetPermission_Name,
+            codec.align(UsbChooserService_GetPermission_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbChooserService_GetPermission_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    default:
+      return false;
+    }
+  };
+
+  function validateUsbChooserServiceRequest(messageValidator) {
+    var message = messageValidator.message;
+    var paramsClass = null;
+    switch (message.getName()) {
+      case kUsbChooserService_GetPermission_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbChooserService_GetPermission_Params;
+      break;
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+  }
+
+  function validateUsbChooserServiceResponse(messageValidator) {
+   var message = messageValidator.message;
+   var paramsClass = null;
+   switch (message.getName()) {
+      case kUsbChooserService_GetPermission_Name:
+        if (message.isResponse())
+          paramsClass = UsbChooserService_GetPermission_ResponseParams;
+        break;
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+  }
+
+  var UsbChooserService = {
+    name: 'device::mojom::UsbChooserService',
+    kVersion: 0,
+    ptrClass: UsbChooserServicePtr,
+    proxyClass: UsbChooserServiceProxy,
+    stubClass: UsbChooserServiceStub,
+    validateRequest: validateUsbChooserServiceRequest,
+    validateResponse: validateUsbChooserServiceResponse,
+  };
+  UsbChooserServiceStub.prototype.validator = validateUsbChooserServiceRequest;
+  UsbChooserServiceProxy.prototype.validator = validateUsbChooserServiceResponse;
+  var exports = mojo.internal.exposeNamespace("device.mojom");
+  exports.UsbChooserService = UsbChooserService;
+  exports.UsbChooserServicePtr = UsbChooserServicePtr;
+  exports.UsbChooserServiceAssociatedPtr = UsbChooserServiceAssociatedPtr;
+})();
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device.mojom.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device.mojom.js
new file mode 100644
index 0000000..5e618ec
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device.mojom.js
@@ -0,0 +1,3452 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+(function() {
+  var mojomId = 'device/usb/public/interfaces/device.mojom';
+  if (mojo.internal.isMojomLoaded(mojomId)) {
+    console.warn('The following mojom is loaded multiple times: ' + mojomId);
+    return;
+  }
+  mojo.internal.markMojomLoaded(mojomId);
+
+  // TODO(yzshen): Define these aliases to minimize the differences between the
+  // old/new modes. Remove them when the old mode goes away.
+  var bindings = mojo;
+  var associatedBindings = mojo;
+  var codec = mojo.internal;
+  var validator = mojo.internal;
+
+
+  var UsbOpenDeviceError = {};
+  UsbOpenDeviceError.OK = 0;
+  UsbOpenDeviceError.ACCESS_DENIED = UsbOpenDeviceError.OK + 1;
+  UsbOpenDeviceError.ALREADY_OPEN = UsbOpenDeviceError.ACCESS_DENIED + 1;
+
+  UsbOpenDeviceError.isKnownEnumValue = function(value) {
+    switch (value) {
+    case 0:
+    case 1:
+    case 2:
+      return true;
+    }
+    return false;
+  };
+
+  UsbOpenDeviceError.validate = function(enumValue) {
+    var isExtensible = false;
+    if (isExtensible || this.isKnownEnumValue(enumValue))
+      return validator.validationError.NONE;
+
+    return validator.validationError.UNKNOWN_ENUM_VALUE;
+  };
+  var UsbTransferDirection = {};
+  UsbTransferDirection.INBOUND = 0;
+  UsbTransferDirection.OUTBOUND = UsbTransferDirection.INBOUND + 1;
+
+  UsbTransferDirection.isKnownEnumValue = function(value) {
+    switch (value) {
+    case 0:
+    case 1:
+      return true;
+    }
+    return false;
+  };
+
+  UsbTransferDirection.validate = function(enumValue) {
+    var isExtensible = false;
+    if (isExtensible || this.isKnownEnumValue(enumValue))
+      return validator.validationError.NONE;
+
+    return validator.validationError.UNKNOWN_ENUM_VALUE;
+  };
+  var UsbControlTransferType = {};
+  UsbControlTransferType.STANDARD = 0;
+  UsbControlTransferType.CLASS = UsbControlTransferType.STANDARD + 1;
+  UsbControlTransferType.VENDOR = UsbControlTransferType.CLASS + 1;
+  UsbControlTransferType.RESERVED = UsbControlTransferType.VENDOR + 1;
+
+  UsbControlTransferType.isKnownEnumValue = function(value) {
+    switch (value) {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+      return true;
+    }
+    return false;
+  };
+
+  UsbControlTransferType.validate = function(enumValue) {
+    var isExtensible = false;
+    if (isExtensible || this.isKnownEnumValue(enumValue))
+      return validator.validationError.NONE;
+
+    return validator.validationError.UNKNOWN_ENUM_VALUE;
+  };
+  var UsbControlTransferRecipient = {};
+  UsbControlTransferRecipient.DEVICE = 0;
+  UsbControlTransferRecipient.INTERFACE = UsbControlTransferRecipient.DEVICE + 1;
+  UsbControlTransferRecipient.ENDPOINT = UsbControlTransferRecipient.INTERFACE + 1;
+  UsbControlTransferRecipient.OTHER = UsbControlTransferRecipient.ENDPOINT + 1;
+
+  UsbControlTransferRecipient.isKnownEnumValue = function(value) {
+    switch (value) {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+      return true;
+    }
+    return false;
+  };
+
+  UsbControlTransferRecipient.validate = function(enumValue) {
+    var isExtensible = false;
+    if (isExtensible || this.isKnownEnumValue(enumValue))
+      return validator.validationError.NONE;
+
+    return validator.validationError.UNKNOWN_ENUM_VALUE;
+  };
+  var UsbTransferType = {};
+  UsbTransferType.CONTROL = 0;
+  UsbTransferType.ISOCHRONOUS = UsbTransferType.CONTROL + 1;
+  UsbTransferType.BULK = UsbTransferType.ISOCHRONOUS + 1;
+  UsbTransferType.INTERRUPT = UsbTransferType.BULK + 1;
+
+  UsbTransferType.isKnownEnumValue = function(value) {
+    switch (value) {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+      return true;
+    }
+    return false;
+  };
+
+  UsbTransferType.validate = function(enumValue) {
+    var isExtensible = false;
+    if (isExtensible || this.isKnownEnumValue(enumValue))
+      return validator.validationError.NONE;
+
+    return validator.validationError.UNKNOWN_ENUM_VALUE;
+  };
+  var UsbTransferStatus = {};
+  UsbTransferStatus.COMPLETED = 0;
+  UsbTransferStatus.TRANSFER_ERROR = UsbTransferStatus.COMPLETED + 1;
+  UsbTransferStatus.TIMEOUT = UsbTransferStatus.TRANSFER_ERROR + 1;
+  UsbTransferStatus.CANCELLED = UsbTransferStatus.TIMEOUT + 1;
+  UsbTransferStatus.STALLED = UsbTransferStatus.CANCELLED + 1;
+  UsbTransferStatus.DISCONNECT = UsbTransferStatus.STALLED + 1;
+  UsbTransferStatus.BABBLE = UsbTransferStatus.DISCONNECT + 1;
+  UsbTransferStatus.SHORT_PACKET = UsbTransferStatus.BABBLE + 1;
+  UsbTransferStatus.PERMISSION_DENIED = UsbTransferStatus.SHORT_PACKET + 1;
+
+  UsbTransferStatus.isKnownEnumValue = function(value) {
+    switch (value) {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+    case 5:
+    case 6:
+    case 7:
+    case 8:
+      return true;
+    }
+    return false;
+  };
+
+  UsbTransferStatus.validate = function(enumValue) {
+    var isExtensible = false;
+    if (isExtensible || this.isKnownEnumValue(enumValue))
+      return validator.validationError.NONE;
+
+    return validator.validationError.UNKNOWN_ENUM_VALUE;
+  };
+
+  function UsbEndpointInfo(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbEndpointInfo.prototype.initDefaults_ = function() {
+    this.endpointNumber = 0;
+    this.direction = 0;
+    this.type = 0;
+    this.packetSize = 0;
+  };
+  UsbEndpointInfo.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbEndpointInfo.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    
+    // validate UsbEndpointInfo.direction
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 4, UsbTransferDirection);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbEndpointInfo.type
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 8, UsbTransferType);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbEndpointInfo.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbEndpointInfo.decode = function(decoder) {
+    var packed;
+    var val = new UsbEndpointInfo();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.endpointNumber = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.direction = decoder.decodeStruct(codec.Int32);
+    val.type = decoder.decodeStruct(codec.Int32);
+    val.packetSize = decoder.decodeStruct(codec.Uint32);
+    return val;
+  };
+
+  UsbEndpointInfo.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbEndpointInfo.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.endpointNumber);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStruct(codec.Int32, val.direction);
+    encoder.encodeStruct(codec.Int32, val.type);
+    encoder.encodeStruct(codec.Uint32, val.packetSize);
+  };
+  function UsbAlternateInterfaceInfo(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbAlternateInterfaceInfo.prototype.initDefaults_ = function() {
+    this.alternateSetting = 0;
+    this.classCode = 0;
+    this.subclassCode = 0;
+    this.protocolCode = 0;
+    this.interfaceName = null;
+    this.endpoints = null;
+  };
+  UsbAlternateInterfaceInfo.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbAlternateInterfaceInfo.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 32}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+
+
+
+    
+    // validate UsbAlternateInterfaceInfo.interfaceName
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 8, true)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbAlternateInterfaceInfo.endpoints
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 16, 8, new codec.PointerTo(UsbEndpointInfo), false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbAlternateInterfaceInfo.encodedSize = codec.kStructHeaderSize + 24;
+
+  UsbAlternateInterfaceInfo.decode = function(decoder) {
+    var packed;
+    var val = new UsbAlternateInterfaceInfo();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.alternateSetting = decoder.decodeStruct(codec.Uint8);
+    val.classCode = decoder.decodeStruct(codec.Uint8);
+    val.subclassCode = decoder.decodeStruct(codec.Uint8);
+    val.protocolCode = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.interfaceName = decoder.decodeStruct(codec.NullableString);
+    val.endpoints = decoder.decodeArrayPointer(new codec.PointerTo(UsbEndpointInfo));
+    return val;
+  };
+
+  UsbAlternateInterfaceInfo.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbAlternateInterfaceInfo.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.alternateSetting);
+    encoder.encodeStruct(codec.Uint8, val.classCode);
+    encoder.encodeStruct(codec.Uint8, val.subclassCode);
+    encoder.encodeStruct(codec.Uint8, val.protocolCode);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStruct(codec.NullableString, val.interfaceName);
+    encoder.encodeArrayPointer(new codec.PointerTo(UsbEndpointInfo), val.endpoints);
+  };
+  function UsbInterfaceInfo(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbInterfaceInfo.prototype.initDefaults_ = function() {
+    this.interfaceNumber = 0;
+    this.alternates = null;
+  };
+  UsbInterfaceInfo.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbInterfaceInfo.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    
+    // validate UsbInterfaceInfo.alternates
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 8, 8, new codec.PointerTo(UsbAlternateInterfaceInfo), false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbInterfaceInfo.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbInterfaceInfo.decode = function(decoder) {
+    var packed;
+    var val = new UsbInterfaceInfo();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.interfaceNumber = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.alternates = decoder.decodeArrayPointer(new codec.PointerTo(UsbAlternateInterfaceInfo));
+    return val;
+  };
+
+  UsbInterfaceInfo.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbInterfaceInfo.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.interfaceNumber);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeArrayPointer(new codec.PointerTo(UsbAlternateInterfaceInfo), val.alternates);
+  };
+  function UsbConfigurationInfo(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbConfigurationInfo.prototype.initDefaults_ = function() {
+    this.configurationValue = 0;
+    this.configurationName = null;
+    this.interfaces = null;
+  };
+  UsbConfigurationInfo.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbConfigurationInfo.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 32}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    
+    // validate UsbConfigurationInfo.configurationName
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 8, true)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbConfigurationInfo.interfaces
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 16, 8, new codec.PointerTo(UsbInterfaceInfo), false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbConfigurationInfo.encodedSize = codec.kStructHeaderSize + 24;
+
+  UsbConfigurationInfo.decode = function(decoder) {
+    var packed;
+    var val = new UsbConfigurationInfo();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.configurationValue = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.configurationName = decoder.decodeStruct(codec.NullableString);
+    val.interfaces = decoder.decodeArrayPointer(new codec.PointerTo(UsbInterfaceInfo));
+    return val;
+  };
+
+  UsbConfigurationInfo.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbConfigurationInfo.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.configurationValue);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStruct(codec.NullableString, val.configurationName);
+    encoder.encodeArrayPointer(new codec.PointerTo(UsbInterfaceInfo), val.interfaces);
+  };
+  function UsbDeviceInfo(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDeviceInfo.prototype.initDefaults_ = function() {
+    this.guid = null;
+    this.usbVersionMajor = 0;
+    this.usbVersionMinor = 0;
+    this.usbVersionSubminor = 0;
+    this.classCode = 0;
+    this.subclassCode = 0;
+    this.protocolCode = 0;
+    this.vendorId = 0;
+    this.productId = 0;
+    this.deviceVersionMajor = 0;
+    this.deviceVersionMinor = 0;
+    this.deviceVersionSubminor = 0;
+    this.activeConfiguration = 0;
+    this.manufacturerName = null;
+    this.productName = null;
+    this.serialNumber = null;
+    this.configurations = null;
+  };
+  UsbDeviceInfo.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDeviceInfo.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 64}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDeviceInfo.guid
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 0, false)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+
+
+
+
+
+
+
+
+
+
+    
+    // validate UsbDeviceInfo.manufacturerName
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 24, true)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDeviceInfo.productName
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 32, true)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDeviceInfo.serialNumber
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 40, true)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    
+    // validate UsbDeviceInfo.configurations
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 48, 8, new codec.PointerTo(UsbConfigurationInfo), false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDeviceInfo.encodedSize = codec.kStructHeaderSize + 56;
+
+  UsbDeviceInfo.decode = function(decoder) {
+    var packed;
+    var val = new UsbDeviceInfo();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.guid = decoder.decodeStruct(codec.String);
+    val.usbVersionMajor = decoder.decodeStruct(codec.Uint8);
+    val.usbVersionMinor = decoder.decodeStruct(codec.Uint8);
+    val.usbVersionSubminor = decoder.decodeStruct(codec.Uint8);
+    val.classCode = decoder.decodeStruct(codec.Uint8);
+    val.subclassCode = decoder.decodeStruct(codec.Uint8);
+    val.protocolCode = decoder.decodeStruct(codec.Uint8);
+    val.vendorId = decoder.decodeStruct(codec.Uint16);
+    val.productId = decoder.decodeStruct(codec.Uint16);
+    val.deviceVersionMajor = decoder.decodeStruct(codec.Uint8);
+    val.deviceVersionMinor = decoder.decodeStruct(codec.Uint8);
+    val.deviceVersionSubminor = decoder.decodeStruct(codec.Uint8);
+    val.activeConfiguration = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.manufacturerName = decoder.decodeStruct(codec.NullableString);
+    val.productName = decoder.decodeStruct(codec.NullableString);
+    val.serialNumber = decoder.decodeStruct(codec.NullableString);
+    val.configurations = decoder.decodeArrayPointer(new codec.PointerTo(UsbConfigurationInfo));
+    return val;
+  };
+
+  UsbDeviceInfo.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDeviceInfo.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.String, val.guid);
+    encoder.encodeStruct(codec.Uint8, val.usbVersionMajor);
+    encoder.encodeStruct(codec.Uint8, val.usbVersionMinor);
+    encoder.encodeStruct(codec.Uint8, val.usbVersionSubminor);
+    encoder.encodeStruct(codec.Uint8, val.classCode);
+    encoder.encodeStruct(codec.Uint8, val.subclassCode);
+    encoder.encodeStruct(codec.Uint8, val.protocolCode);
+    encoder.encodeStruct(codec.Uint16, val.vendorId);
+    encoder.encodeStruct(codec.Uint16, val.productId);
+    encoder.encodeStruct(codec.Uint8, val.deviceVersionMajor);
+    encoder.encodeStruct(codec.Uint8, val.deviceVersionMinor);
+    encoder.encodeStruct(codec.Uint8, val.deviceVersionSubminor);
+    encoder.encodeStruct(codec.Uint8, val.activeConfiguration);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStruct(codec.NullableString, val.manufacturerName);
+    encoder.encodeStruct(codec.NullableString, val.productName);
+    encoder.encodeStruct(codec.NullableString, val.serialNumber);
+    encoder.encodeArrayPointer(new codec.PointerTo(UsbConfigurationInfo), val.configurations);
+  };
+  function UsbControlTransferParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbControlTransferParams.prototype.initDefaults_ = function() {
+    this.type = 0;
+    this.recipient = 0;
+    this.request = 0;
+    this.value = 0;
+    this.index = 0;
+  };
+  UsbControlTransferParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbControlTransferParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbControlTransferParams.type
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 0, UsbControlTransferType);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbControlTransferParams.recipient
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 4, UsbControlTransferRecipient);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbControlTransferParams.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbControlTransferParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbControlTransferParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.type = decoder.decodeStruct(codec.Int32);
+    val.recipient = decoder.decodeStruct(codec.Int32);
+    val.request = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    val.value = decoder.decodeStruct(codec.Uint16);
+    val.index = decoder.decodeStruct(codec.Uint16);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbControlTransferParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbControlTransferParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Int32, val.type);
+    encoder.encodeStruct(codec.Int32, val.recipient);
+    encoder.encodeStruct(codec.Uint8, val.request);
+    encoder.skip(1);
+    encoder.encodeStruct(codec.Uint16, val.value);
+    encoder.encodeStruct(codec.Uint16, val.index);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbIsochronousPacket(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbIsochronousPacket.prototype.initDefaults_ = function() {
+    this.length = 0;
+    this.transferredLength = 0;
+    this.status = 0;
+  };
+  UsbIsochronousPacket.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbIsochronousPacket.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+
+    
+    // validate UsbIsochronousPacket.status
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 8, UsbTransferStatus);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbIsochronousPacket.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbIsochronousPacket.decode = function(decoder) {
+    var packed;
+    var val = new UsbIsochronousPacket();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.length = decoder.decodeStruct(codec.Uint32);
+    val.transferredLength = decoder.decodeStruct(codec.Uint32);
+    val.status = decoder.decodeStruct(codec.Int32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbIsochronousPacket.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbIsochronousPacket.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint32, val.length);
+    encoder.encodeStruct(codec.Uint32, val.transferredLength);
+    encoder.encodeStruct(codec.Int32, val.status);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_Open_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_Open_Params.prototype.initDefaults_ = function() {
+  };
+  UsbDevice_Open_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_Open_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 8}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_Open_Params.encodedSize = codec.kStructHeaderSize + 0;
+
+  UsbDevice_Open_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_Open_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    return val;
+  };
+
+  UsbDevice_Open_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_Open_Params.encodedSize);
+    encoder.writeUint32(0);
+  };
+  function UsbDevice_Open_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_Open_ResponseParams.prototype.initDefaults_ = function() {
+    this.error = 0;
+  };
+  UsbDevice_Open_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_Open_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_Open_ResponseParams.error
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 0, UsbOpenDeviceError);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_Open_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_Open_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_Open_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.error = decoder.decodeStruct(codec.Int32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_Open_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_Open_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Int32, val.error);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_Close_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_Close_Params.prototype.initDefaults_ = function() {
+  };
+  UsbDevice_Close_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_Close_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 8}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_Close_Params.encodedSize = codec.kStructHeaderSize + 0;
+
+  UsbDevice_Close_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_Close_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    return val;
+  };
+
+  UsbDevice_Close_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_Close_Params.encodedSize);
+    encoder.writeUint32(0);
+  };
+  function UsbDevice_Close_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_Close_ResponseParams.prototype.initDefaults_ = function() {
+  };
+  UsbDevice_Close_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_Close_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 8}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_Close_ResponseParams.encodedSize = codec.kStructHeaderSize + 0;
+
+  UsbDevice_Close_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_Close_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    return val;
+  };
+
+  UsbDevice_Close_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_Close_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+  };
+  function UsbDevice_SetConfiguration_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_SetConfiguration_Params.prototype.initDefaults_ = function() {
+    this.value = 0;
+  };
+  UsbDevice_SetConfiguration_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_SetConfiguration_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_SetConfiguration_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_SetConfiguration_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_SetConfiguration_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.value = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_SetConfiguration_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_SetConfiguration_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.value);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_SetConfiguration_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_SetConfiguration_ResponseParams.prototype.initDefaults_ = function() {
+    this.success = false;
+  };
+  UsbDevice_SetConfiguration_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_SetConfiguration_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_SetConfiguration_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_SetConfiguration_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_SetConfiguration_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    packed = decoder.readUint8();
+    val.success = (packed >> 0) & 1 ? true : false;
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_SetConfiguration_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_SetConfiguration_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    packed = 0;
+    packed |= (val.success & 1) << 0
+    encoder.writeUint8(packed);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_ClaimInterface_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_ClaimInterface_Params.prototype.initDefaults_ = function() {
+    this.interfaceNumber = 0;
+  };
+  UsbDevice_ClaimInterface_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_ClaimInterface_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_ClaimInterface_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_ClaimInterface_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_ClaimInterface_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.interfaceNumber = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_ClaimInterface_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_ClaimInterface_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.interfaceNumber);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_ClaimInterface_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_ClaimInterface_ResponseParams.prototype.initDefaults_ = function() {
+    this.success = false;
+  };
+  UsbDevice_ClaimInterface_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_ClaimInterface_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_ClaimInterface_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_ClaimInterface_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_ClaimInterface_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    packed = decoder.readUint8();
+    val.success = (packed >> 0) & 1 ? true : false;
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_ClaimInterface_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_ClaimInterface_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    packed = 0;
+    packed |= (val.success & 1) << 0
+    encoder.writeUint8(packed);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_ReleaseInterface_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_ReleaseInterface_Params.prototype.initDefaults_ = function() {
+    this.interfaceNumber = 0;
+  };
+  UsbDevice_ReleaseInterface_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_ReleaseInterface_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_ReleaseInterface_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_ReleaseInterface_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_ReleaseInterface_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.interfaceNumber = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_ReleaseInterface_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_ReleaseInterface_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.interfaceNumber);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_ReleaseInterface_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_ReleaseInterface_ResponseParams.prototype.initDefaults_ = function() {
+    this.success = false;
+  };
+  UsbDevice_ReleaseInterface_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_ReleaseInterface_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_ReleaseInterface_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_ReleaseInterface_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_ReleaseInterface_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    packed = decoder.readUint8();
+    val.success = (packed >> 0) & 1 ? true : false;
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_ReleaseInterface_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_ReleaseInterface_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    packed = 0;
+    packed |= (val.success & 1) << 0
+    encoder.writeUint8(packed);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_SetInterfaceAlternateSetting_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_SetInterfaceAlternateSetting_Params.prototype.initDefaults_ = function() {
+    this.interfaceNumber = 0;
+    this.alternateSetting = 0;
+  };
+  UsbDevice_SetInterfaceAlternateSetting_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_SetInterfaceAlternateSetting_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_SetInterfaceAlternateSetting_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_SetInterfaceAlternateSetting_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_SetInterfaceAlternateSetting_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.interfaceNumber = decoder.decodeStruct(codec.Uint8);
+    val.alternateSetting = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_SetInterfaceAlternateSetting_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_SetInterfaceAlternateSetting_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.interfaceNumber);
+    encoder.encodeStruct(codec.Uint8, val.alternateSetting);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_SetInterfaceAlternateSetting_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_SetInterfaceAlternateSetting_ResponseParams.prototype.initDefaults_ = function() {
+    this.success = false;
+  };
+  UsbDevice_SetInterfaceAlternateSetting_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_SetInterfaceAlternateSetting_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_SetInterfaceAlternateSetting_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_SetInterfaceAlternateSetting_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_SetInterfaceAlternateSetting_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    packed = decoder.readUint8();
+    val.success = (packed >> 0) & 1 ? true : false;
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_SetInterfaceAlternateSetting_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_SetInterfaceAlternateSetting_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    packed = 0;
+    packed |= (val.success & 1) << 0
+    encoder.writeUint8(packed);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_Reset_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_Reset_Params.prototype.initDefaults_ = function() {
+  };
+  UsbDevice_Reset_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_Reset_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 8}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_Reset_Params.encodedSize = codec.kStructHeaderSize + 0;
+
+  UsbDevice_Reset_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_Reset_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    return val;
+  };
+
+  UsbDevice_Reset_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_Reset_Params.encodedSize);
+    encoder.writeUint32(0);
+  };
+  function UsbDevice_Reset_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_Reset_ResponseParams.prototype.initDefaults_ = function() {
+    this.success = false;
+  };
+  UsbDevice_Reset_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_Reset_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_Reset_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_Reset_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_Reset_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    packed = decoder.readUint8();
+    val.success = (packed >> 0) & 1 ? true : false;
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_Reset_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_Reset_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    packed = 0;
+    packed |= (val.success & 1) << 0
+    encoder.writeUint8(packed);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_ClearHalt_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_ClearHalt_Params.prototype.initDefaults_ = function() {
+    this.endpoint = 0;
+  };
+  UsbDevice_ClearHalt_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_ClearHalt_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_ClearHalt_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_ClearHalt_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_ClearHalt_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.endpoint = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_ClearHalt_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_ClearHalt_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.endpoint);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_ClearHalt_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_ClearHalt_ResponseParams.prototype.initDefaults_ = function() {
+    this.success = false;
+  };
+  UsbDevice_ClearHalt_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_ClearHalt_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_ClearHalt_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_ClearHalt_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_ClearHalt_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    packed = decoder.readUint8();
+    val.success = (packed >> 0) & 1 ? true : false;
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_ClearHalt_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_ClearHalt_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    packed = 0;
+    packed |= (val.success & 1) << 0
+    encoder.writeUint8(packed);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_ControlTransferIn_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_ControlTransferIn_Params.prototype.initDefaults_ = function() {
+    this.params = null;
+    this.length = 0;
+    this.timeout = 0;
+  };
+  UsbDevice_ControlTransferIn_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_ControlTransferIn_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_ControlTransferIn_Params.params
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, UsbControlTransferParams, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_ControlTransferIn_Params.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbDevice_ControlTransferIn_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_ControlTransferIn_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.params = decoder.decodeStructPointer(UsbControlTransferParams);
+    val.length = decoder.decodeStruct(codec.Uint32);
+    val.timeout = decoder.decodeStruct(codec.Uint32);
+    return val;
+  };
+
+  UsbDevice_ControlTransferIn_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_ControlTransferIn_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStructPointer(UsbControlTransferParams, val.params);
+    encoder.encodeStruct(codec.Uint32, val.length);
+    encoder.encodeStruct(codec.Uint32, val.timeout);
+  };
+  function UsbDevice_ControlTransferIn_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_ControlTransferIn_ResponseParams.prototype.initDefaults_ = function() {
+    this.status = 0;
+    this.data = null;
+  };
+  UsbDevice_ControlTransferIn_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_ControlTransferIn_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_ControlTransferIn_ResponseParams.status
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 0, UsbTransferStatus);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_ControlTransferIn_ResponseParams.data
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 8, 1, codec.Uint8, true, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_ControlTransferIn_ResponseParams.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbDevice_ControlTransferIn_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_ControlTransferIn_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.status = decoder.decodeStruct(codec.Int32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.data = decoder.decodeArrayPointer(codec.Uint8);
+    return val;
+  };
+
+  UsbDevice_ControlTransferIn_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_ControlTransferIn_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Int32, val.status);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeArrayPointer(codec.Uint8, val.data);
+  };
+  function UsbDevice_ControlTransferOut_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_ControlTransferOut_Params.prototype.initDefaults_ = function() {
+    this.params = null;
+    this.data = null;
+    this.timeout = 0;
+  };
+  UsbDevice_ControlTransferOut_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_ControlTransferOut_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 32}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_ControlTransferOut_Params.params
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, UsbControlTransferParams, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_ControlTransferOut_Params.data
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 8, 1, codec.Uint8, false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_ControlTransferOut_Params.encodedSize = codec.kStructHeaderSize + 24;
+
+  UsbDevice_ControlTransferOut_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_ControlTransferOut_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.params = decoder.decodeStructPointer(UsbControlTransferParams);
+    val.data = decoder.decodeArrayPointer(codec.Uint8);
+    val.timeout = decoder.decodeStruct(codec.Uint32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_ControlTransferOut_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_ControlTransferOut_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStructPointer(UsbControlTransferParams, val.params);
+    encoder.encodeArrayPointer(codec.Uint8, val.data);
+    encoder.encodeStruct(codec.Uint32, val.timeout);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_ControlTransferOut_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_ControlTransferOut_ResponseParams.prototype.initDefaults_ = function() {
+    this.status = 0;
+  };
+  UsbDevice_ControlTransferOut_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_ControlTransferOut_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_ControlTransferOut_ResponseParams.status
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 0, UsbTransferStatus);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_ControlTransferOut_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_ControlTransferOut_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_ControlTransferOut_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.status = decoder.decodeStruct(codec.Int32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_ControlTransferOut_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_ControlTransferOut_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Int32, val.status);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_GenericTransferIn_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_GenericTransferIn_Params.prototype.initDefaults_ = function() {
+    this.endpointNumber = 0;
+    this.length = 0;
+    this.timeout = 0;
+  };
+  UsbDevice_GenericTransferIn_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_GenericTransferIn_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_GenericTransferIn_Params.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbDevice_GenericTransferIn_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_GenericTransferIn_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.endpointNumber = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.length = decoder.decodeStruct(codec.Uint32);
+    val.timeout = decoder.decodeStruct(codec.Uint32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_GenericTransferIn_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_GenericTransferIn_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.endpointNumber);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStruct(codec.Uint32, val.length);
+    encoder.encodeStruct(codec.Uint32, val.timeout);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_GenericTransferIn_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_GenericTransferIn_ResponseParams.prototype.initDefaults_ = function() {
+    this.status = 0;
+    this.data = null;
+  };
+  UsbDevice_GenericTransferIn_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_GenericTransferIn_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_GenericTransferIn_ResponseParams.status
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 0, UsbTransferStatus);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_GenericTransferIn_ResponseParams.data
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 8, 1, codec.Uint8, true, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_GenericTransferIn_ResponseParams.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbDevice_GenericTransferIn_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_GenericTransferIn_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.status = decoder.decodeStruct(codec.Int32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.data = decoder.decodeArrayPointer(codec.Uint8);
+    return val;
+  };
+
+  UsbDevice_GenericTransferIn_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_GenericTransferIn_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Int32, val.status);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeArrayPointer(codec.Uint8, val.data);
+  };
+  function UsbDevice_GenericTransferOut_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_GenericTransferOut_Params.prototype.initDefaults_ = function() {
+    this.endpointNumber = 0;
+    this.timeout = 0;
+    this.data = null;
+  };
+  UsbDevice_GenericTransferOut_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_GenericTransferOut_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    
+    // validate UsbDevice_GenericTransferOut_Params.data
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 8, 1, codec.Uint8, false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_GenericTransferOut_Params.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbDevice_GenericTransferOut_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_GenericTransferOut_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.endpointNumber = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.timeout = decoder.decodeStruct(codec.Uint32);
+    val.data = decoder.decodeArrayPointer(codec.Uint8);
+    return val;
+  };
+
+  UsbDevice_GenericTransferOut_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_GenericTransferOut_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.endpointNumber);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStruct(codec.Uint32, val.timeout);
+    encoder.encodeArrayPointer(codec.Uint8, val.data);
+  };
+  function UsbDevice_GenericTransferOut_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_GenericTransferOut_ResponseParams.prototype.initDefaults_ = function() {
+    this.status = 0;
+  };
+  UsbDevice_GenericTransferOut_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_GenericTransferOut_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_GenericTransferOut_ResponseParams.status
+    err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 0, UsbTransferStatus);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_GenericTransferOut_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_GenericTransferOut_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_GenericTransferOut_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.status = decoder.decodeStruct(codec.Int32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDevice_GenericTransferOut_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_GenericTransferOut_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Int32, val.status);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDevice_IsochronousTransferIn_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_IsochronousTransferIn_Params.prototype.initDefaults_ = function() {
+    this.endpointNumber = 0;
+    this.timeout = 0;
+    this.packetLengths = null;
+  };
+  UsbDevice_IsochronousTransferIn_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_IsochronousTransferIn_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    
+    // validate UsbDevice_IsochronousTransferIn_Params.packetLengths
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 8, 4, codec.Uint32, false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_IsochronousTransferIn_Params.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbDevice_IsochronousTransferIn_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_IsochronousTransferIn_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.endpointNumber = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.timeout = decoder.decodeStruct(codec.Uint32);
+    val.packetLengths = decoder.decodeArrayPointer(codec.Uint32);
+    return val;
+  };
+
+  UsbDevice_IsochronousTransferIn_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_IsochronousTransferIn_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.endpointNumber);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStruct(codec.Uint32, val.timeout);
+    encoder.encodeArrayPointer(codec.Uint32, val.packetLengths);
+  };
+  function UsbDevice_IsochronousTransferIn_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_IsochronousTransferIn_ResponseParams.prototype.initDefaults_ = function() {
+    this.data = null;
+    this.packets = null;
+  };
+  UsbDevice_IsochronousTransferIn_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_IsochronousTransferIn_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_IsochronousTransferIn_ResponseParams.data
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 1, codec.Uint8, true, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_IsochronousTransferIn_ResponseParams.packets
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 8, 8, new codec.PointerTo(UsbIsochronousPacket), false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_IsochronousTransferIn_ResponseParams.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbDevice_IsochronousTransferIn_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_IsochronousTransferIn_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.data = decoder.decodeArrayPointer(codec.Uint8);
+    val.packets = decoder.decodeArrayPointer(new codec.PointerTo(UsbIsochronousPacket));
+    return val;
+  };
+
+  UsbDevice_IsochronousTransferIn_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_IsochronousTransferIn_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeArrayPointer(codec.Uint8, val.data);
+    encoder.encodeArrayPointer(new codec.PointerTo(UsbIsochronousPacket), val.packets);
+  };
+  function UsbDevice_IsochronousTransferOut_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_IsochronousTransferOut_Params.prototype.initDefaults_ = function() {
+    this.endpointNumber = 0;
+    this.timeout = 0;
+    this.data = null;
+    this.packetLengths = null;
+  };
+  UsbDevice_IsochronousTransferOut_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_IsochronousTransferOut_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 32}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    
+    // validate UsbDevice_IsochronousTransferOut_Params.data
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 8, 1, codec.Uint8, false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_IsochronousTransferOut_Params.packetLengths
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 16, 4, codec.Uint32, false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_IsochronousTransferOut_Params.encodedSize = codec.kStructHeaderSize + 24;
+
+  UsbDevice_IsochronousTransferOut_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_IsochronousTransferOut_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.endpointNumber = decoder.decodeStruct(codec.Uint8);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.timeout = decoder.decodeStruct(codec.Uint32);
+    val.data = decoder.decodeArrayPointer(codec.Uint8);
+    val.packetLengths = decoder.decodeArrayPointer(codec.Uint32);
+    return val;
+  };
+
+  UsbDevice_IsochronousTransferOut_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_IsochronousTransferOut_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint8, val.endpointNumber);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStruct(codec.Uint32, val.timeout);
+    encoder.encodeArrayPointer(codec.Uint8, val.data);
+    encoder.encodeArrayPointer(codec.Uint32, val.packetLengths);
+  };
+  function UsbDevice_IsochronousTransferOut_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDevice_IsochronousTransferOut_ResponseParams.prototype.initDefaults_ = function() {
+    this.packets = null;
+  };
+  UsbDevice_IsochronousTransferOut_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDevice_IsochronousTransferOut_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDevice_IsochronousTransferOut_ResponseParams.packets
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 8, new codec.PointerTo(UsbIsochronousPacket), false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDevice_IsochronousTransferOut_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDevice_IsochronousTransferOut_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDevice_IsochronousTransferOut_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.packets = decoder.decodeArrayPointer(new codec.PointerTo(UsbIsochronousPacket));
+    return val;
+  };
+
+  UsbDevice_IsochronousTransferOut_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDevice_IsochronousTransferOut_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeArrayPointer(new codec.PointerTo(UsbIsochronousPacket), val.packets);
+  };
+  var kUsbDevice_Open_Name = 0;
+  var kUsbDevice_Close_Name = 1;
+  var kUsbDevice_SetConfiguration_Name = 2;
+  var kUsbDevice_ClaimInterface_Name = 3;
+  var kUsbDevice_ReleaseInterface_Name = 4;
+  var kUsbDevice_SetInterfaceAlternateSetting_Name = 5;
+  var kUsbDevice_Reset_Name = 6;
+  var kUsbDevice_ClearHalt_Name = 7;
+  var kUsbDevice_ControlTransferIn_Name = 8;
+  var kUsbDevice_ControlTransferOut_Name = 9;
+  var kUsbDevice_GenericTransferIn_Name = 10;
+  var kUsbDevice_GenericTransferOut_Name = 11;
+  var kUsbDevice_IsochronousTransferIn_Name = 12;
+  var kUsbDevice_IsochronousTransferOut_Name = 13;
+
+  function UsbDevicePtr(handleOrPtrInfo) {
+    this.ptr = new bindings.InterfacePtrController(UsbDevice,
+                                                   handleOrPtrInfo);
+  }
+
+  function UsbDeviceAssociatedPtr(associatedInterfacePtrInfo) {
+    this.ptr = new associatedBindings.AssociatedInterfacePtrController(
+        UsbDevice, associatedInterfacePtrInfo);
+  }
+
+  UsbDeviceAssociatedPtr.prototype =
+      Object.create(UsbDevicePtr.prototype);
+  UsbDeviceAssociatedPtr.prototype.constructor =
+      UsbDeviceAssociatedPtr;
+
+  function UsbDeviceProxy(receiver) {
+    this.receiver_ = receiver;
+  }
+  UsbDevicePtr.prototype.open = function() {
+    return UsbDeviceProxy.prototype.open
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.open = function() {
+    var params = new UsbDevice_Open_Params();
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_Open_Name,
+          codec.align(UsbDevice_Open_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_Open_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_Open_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.close = function() {
+    return UsbDeviceProxy.prototype.close
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.close = function() {
+    var params = new UsbDevice_Close_Params();
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_Close_Name,
+          codec.align(UsbDevice_Close_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_Close_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_Close_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.setConfiguration = function() {
+    return UsbDeviceProxy.prototype.setConfiguration
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.setConfiguration = function(value) {
+    var params = new UsbDevice_SetConfiguration_Params();
+    params.value = value;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_SetConfiguration_Name,
+          codec.align(UsbDevice_SetConfiguration_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_SetConfiguration_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_SetConfiguration_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.claimInterface = function() {
+    return UsbDeviceProxy.prototype.claimInterface
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.claimInterface = function(interfaceNumber) {
+    var params = new UsbDevice_ClaimInterface_Params();
+    params.interfaceNumber = interfaceNumber;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_ClaimInterface_Name,
+          codec.align(UsbDevice_ClaimInterface_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_ClaimInterface_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_ClaimInterface_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.releaseInterface = function() {
+    return UsbDeviceProxy.prototype.releaseInterface
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.releaseInterface = function(interfaceNumber) {
+    var params = new UsbDevice_ReleaseInterface_Params();
+    params.interfaceNumber = interfaceNumber;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_ReleaseInterface_Name,
+          codec.align(UsbDevice_ReleaseInterface_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_ReleaseInterface_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_ReleaseInterface_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.setInterfaceAlternateSetting = function() {
+    return UsbDeviceProxy.prototype.setInterfaceAlternateSetting
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.setInterfaceAlternateSetting = function(interfaceNumber, alternateSetting) {
+    var params = new UsbDevice_SetInterfaceAlternateSetting_Params();
+    params.interfaceNumber = interfaceNumber;
+    params.alternateSetting = alternateSetting;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_SetInterfaceAlternateSetting_Name,
+          codec.align(UsbDevice_SetInterfaceAlternateSetting_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_SetInterfaceAlternateSetting_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_SetInterfaceAlternateSetting_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.reset = function() {
+    return UsbDeviceProxy.prototype.reset
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.reset = function() {
+    var params = new UsbDevice_Reset_Params();
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_Reset_Name,
+          codec.align(UsbDevice_Reset_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_Reset_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_Reset_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.clearHalt = function() {
+    return UsbDeviceProxy.prototype.clearHalt
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.clearHalt = function(endpoint) {
+    var params = new UsbDevice_ClearHalt_Params();
+    params.endpoint = endpoint;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_ClearHalt_Name,
+          codec.align(UsbDevice_ClearHalt_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_ClearHalt_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_ClearHalt_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.controlTransferIn = function() {
+    return UsbDeviceProxy.prototype.controlTransferIn
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.controlTransferIn = function(params, length, timeout) {
+    var params = new UsbDevice_ControlTransferIn_Params();
+    params.params = params;
+    params.length = length;
+    params.timeout = timeout;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_ControlTransferIn_Name,
+          codec.align(UsbDevice_ControlTransferIn_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_ControlTransferIn_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_ControlTransferIn_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.controlTransferOut = function() {
+    return UsbDeviceProxy.prototype.controlTransferOut
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.controlTransferOut = function(params, data, timeout) {
+    var params = new UsbDevice_ControlTransferOut_Params();
+    params.params = params;
+    params.data = data;
+    params.timeout = timeout;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_ControlTransferOut_Name,
+          codec.align(UsbDevice_ControlTransferOut_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_ControlTransferOut_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_ControlTransferOut_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.genericTransferIn = function() {
+    return UsbDeviceProxy.prototype.genericTransferIn
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.genericTransferIn = function(endpointNumber, length, timeout) {
+    var params = new UsbDevice_GenericTransferIn_Params();
+    params.endpointNumber = endpointNumber;
+    params.length = length;
+    params.timeout = timeout;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_GenericTransferIn_Name,
+          codec.align(UsbDevice_GenericTransferIn_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_GenericTransferIn_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_GenericTransferIn_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.genericTransferOut = function() {
+    return UsbDeviceProxy.prototype.genericTransferOut
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.genericTransferOut = function(endpointNumber, data, timeout) {
+    var params = new UsbDevice_GenericTransferOut_Params();
+    params.endpointNumber = endpointNumber;
+    params.data = data;
+    params.timeout = timeout;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_GenericTransferOut_Name,
+          codec.align(UsbDevice_GenericTransferOut_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_GenericTransferOut_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_GenericTransferOut_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.isochronousTransferIn = function() {
+    return UsbDeviceProxy.prototype.isochronousTransferIn
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.isochronousTransferIn = function(endpointNumber, packetLengths, timeout) {
+    var params = new UsbDevice_IsochronousTransferIn_Params();
+    params.endpointNumber = endpointNumber;
+    params.packetLengths = packetLengths;
+    params.timeout = timeout;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_IsochronousTransferIn_Name,
+          codec.align(UsbDevice_IsochronousTransferIn_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_IsochronousTransferIn_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_IsochronousTransferIn_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDevicePtr.prototype.isochronousTransferOut = function() {
+    return UsbDeviceProxy.prototype.isochronousTransferOut
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceProxy.prototype.isochronousTransferOut = function(endpointNumber, data, packetLengths, timeout) {
+    var params = new UsbDevice_IsochronousTransferOut_Params();
+    params.endpointNumber = endpointNumber;
+    params.data = data;
+    params.packetLengths = packetLengths;
+    params.timeout = timeout;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDevice_IsochronousTransferOut_Name,
+          codec.align(UsbDevice_IsochronousTransferOut_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDevice_IsochronousTransferOut_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDevice_IsochronousTransferOut_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+
+  function UsbDeviceStub(delegate) {
+    this.delegate_ = delegate;
+  }
+  UsbDeviceStub.prototype.open = function() {
+    return this.delegate_ && this.delegate_.open && this.delegate_.open();
+  }
+  UsbDeviceStub.prototype.close = function() {
+    return this.delegate_ && this.delegate_.close && this.delegate_.close();
+  }
+  UsbDeviceStub.prototype.setConfiguration = function(value) {
+    return this.delegate_ && this.delegate_.setConfiguration && this.delegate_.setConfiguration(value);
+  }
+  UsbDeviceStub.prototype.claimInterface = function(interfaceNumber) {
+    return this.delegate_ && this.delegate_.claimInterface && this.delegate_.claimInterface(interfaceNumber);
+  }
+  UsbDeviceStub.prototype.releaseInterface = function(interfaceNumber) {
+    return this.delegate_ && this.delegate_.releaseInterface && this.delegate_.releaseInterface(interfaceNumber);
+  }
+  UsbDeviceStub.prototype.setInterfaceAlternateSetting = function(interfaceNumber, alternateSetting) {
+    return this.delegate_ && this.delegate_.setInterfaceAlternateSetting && this.delegate_.setInterfaceAlternateSetting(interfaceNumber, alternateSetting);
+  }
+  UsbDeviceStub.prototype.reset = function() {
+    return this.delegate_ && this.delegate_.reset && this.delegate_.reset();
+  }
+  UsbDeviceStub.prototype.clearHalt = function(endpoint) {
+    return this.delegate_ && this.delegate_.clearHalt && this.delegate_.clearHalt(endpoint);
+  }
+  UsbDeviceStub.prototype.controlTransferIn = function(params, length, timeout) {
+    return this.delegate_ && this.delegate_.controlTransferIn && this.delegate_.controlTransferIn(params, length, timeout);
+  }
+  UsbDeviceStub.prototype.controlTransferOut = function(params, data, timeout) {
+    return this.delegate_ && this.delegate_.controlTransferOut && this.delegate_.controlTransferOut(params, data, timeout);
+  }
+  UsbDeviceStub.prototype.genericTransferIn = function(endpointNumber, length, timeout) {
+    return this.delegate_ && this.delegate_.genericTransferIn && this.delegate_.genericTransferIn(endpointNumber, length, timeout);
+  }
+  UsbDeviceStub.prototype.genericTransferOut = function(endpointNumber, data, timeout) {
+    return this.delegate_ && this.delegate_.genericTransferOut && this.delegate_.genericTransferOut(endpointNumber, data, timeout);
+  }
+  UsbDeviceStub.prototype.isochronousTransferIn = function(endpointNumber, packetLengths, timeout) {
+    return this.delegate_ && this.delegate_.isochronousTransferIn && this.delegate_.isochronousTransferIn(endpointNumber, packetLengths, timeout);
+  }
+  UsbDeviceStub.prototype.isochronousTransferOut = function(endpointNumber, data, packetLengths, timeout) {
+    return this.delegate_ && this.delegate_.isochronousTransferOut && this.delegate_.isochronousTransferOut(endpointNumber, data, packetLengths, timeout);
+  }
+
+  UsbDeviceStub.prototype.accept = function(message) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    default:
+      return false;
+    }
+  };
+
+  UsbDeviceStub.prototype.acceptWithResponder =
+      function(message, responder) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    case kUsbDevice_Open_Name:
+      var params = reader.decodeStruct(UsbDevice_Open_Params);
+      this.open().then(function(response) {
+        var responseParams =
+            new UsbDevice_Open_ResponseParams();
+        responseParams.error = response.error;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_Open_Name,
+            codec.align(UsbDevice_Open_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_Open_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_Close_Name:
+      var params = reader.decodeStruct(UsbDevice_Close_Params);
+      this.close().then(function(response) {
+        var responseParams =
+            new UsbDevice_Close_ResponseParams();
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_Close_Name,
+            codec.align(UsbDevice_Close_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_Close_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_SetConfiguration_Name:
+      var params = reader.decodeStruct(UsbDevice_SetConfiguration_Params);
+      this.setConfiguration(params.value).then(function(response) {
+        var responseParams =
+            new UsbDevice_SetConfiguration_ResponseParams();
+        responseParams.success = response.success;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_SetConfiguration_Name,
+            codec.align(UsbDevice_SetConfiguration_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_SetConfiguration_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_ClaimInterface_Name:
+      var params = reader.decodeStruct(UsbDevice_ClaimInterface_Params);
+      this.claimInterface(params.interfaceNumber).then(function(response) {
+        var responseParams =
+            new UsbDevice_ClaimInterface_ResponseParams();
+        responseParams.success = response.success;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_ClaimInterface_Name,
+            codec.align(UsbDevice_ClaimInterface_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_ClaimInterface_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_ReleaseInterface_Name:
+      var params = reader.decodeStruct(UsbDevice_ReleaseInterface_Params);
+      this.releaseInterface(params.interfaceNumber).then(function(response) {
+        var responseParams =
+            new UsbDevice_ReleaseInterface_ResponseParams();
+        responseParams.success = response.success;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_ReleaseInterface_Name,
+            codec.align(UsbDevice_ReleaseInterface_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_ReleaseInterface_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_SetInterfaceAlternateSetting_Name:
+      var params = reader.decodeStruct(UsbDevice_SetInterfaceAlternateSetting_Params);
+      this.setInterfaceAlternateSetting(params.interfaceNumber, params.alternateSetting).then(function(response) {
+        var responseParams =
+            new UsbDevice_SetInterfaceAlternateSetting_ResponseParams();
+        responseParams.success = response.success;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_SetInterfaceAlternateSetting_Name,
+            codec.align(UsbDevice_SetInterfaceAlternateSetting_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_SetInterfaceAlternateSetting_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_Reset_Name:
+      var params = reader.decodeStruct(UsbDevice_Reset_Params);
+      this.reset().then(function(response) {
+        var responseParams =
+            new UsbDevice_Reset_ResponseParams();
+        responseParams.success = response.success;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_Reset_Name,
+            codec.align(UsbDevice_Reset_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_Reset_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_ClearHalt_Name:
+      var params = reader.decodeStruct(UsbDevice_ClearHalt_Params);
+      this.clearHalt(params.endpoint).then(function(response) {
+        var responseParams =
+            new UsbDevice_ClearHalt_ResponseParams();
+        responseParams.success = response.success;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_ClearHalt_Name,
+            codec.align(UsbDevice_ClearHalt_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_ClearHalt_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_ControlTransferIn_Name:
+      var params = reader.decodeStruct(UsbDevice_ControlTransferIn_Params);
+      this.controlTransferIn(params.params, params.length, params.timeout).then(function(response) {
+        var responseParams =
+            new UsbDevice_ControlTransferIn_ResponseParams();
+        responseParams.status = response.status;
+        responseParams.data = response.data;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_ControlTransferIn_Name,
+            codec.align(UsbDevice_ControlTransferIn_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_ControlTransferIn_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_ControlTransferOut_Name:
+      var params = reader.decodeStruct(UsbDevice_ControlTransferOut_Params);
+      this.controlTransferOut(params.params, params.data, params.timeout).then(function(response) {
+        var responseParams =
+            new UsbDevice_ControlTransferOut_ResponseParams();
+        responseParams.status = response.status;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_ControlTransferOut_Name,
+            codec.align(UsbDevice_ControlTransferOut_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_ControlTransferOut_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_GenericTransferIn_Name:
+      var params = reader.decodeStruct(UsbDevice_GenericTransferIn_Params);
+      this.genericTransferIn(params.endpointNumber, params.length, params.timeout).then(function(response) {
+        var responseParams =
+            new UsbDevice_GenericTransferIn_ResponseParams();
+        responseParams.status = response.status;
+        responseParams.data = response.data;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_GenericTransferIn_Name,
+            codec.align(UsbDevice_GenericTransferIn_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_GenericTransferIn_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_GenericTransferOut_Name:
+      var params = reader.decodeStruct(UsbDevice_GenericTransferOut_Params);
+      this.genericTransferOut(params.endpointNumber, params.data, params.timeout).then(function(response) {
+        var responseParams =
+            new UsbDevice_GenericTransferOut_ResponseParams();
+        responseParams.status = response.status;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_GenericTransferOut_Name,
+            codec.align(UsbDevice_GenericTransferOut_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_GenericTransferOut_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_IsochronousTransferIn_Name:
+      var params = reader.decodeStruct(UsbDevice_IsochronousTransferIn_Params);
+      this.isochronousTransferIn(params.endpointNumber, params.packetLengths, params.timeout).then(function(response) {
+        var responseParams =
+            new UsbDevice_IsochronousTransferIn_ResponseParams();
+        responseParams.data = response.data;
+        responseParams.packets = response.packets;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_IsochronousTransferIn_Name,
+            codec.align(UsbDevice_IsochronousTransferIn_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_IsochronousTransferIn_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    case kUsbDevice_IsochronousTransferOut_Name:
+      var params = reader.decodeStruct(UsbDevice_IsochronousTransferOut_Params);
+      this.isochronousTransferOut(params.endpointNumber, params.data, params.packetLengths, params.timeout).then(function(response) {
+        var responseParams =
+            new UsbDevice_IsochronousTransferOut_ResponseParams();
+        responseParams.packets = response.packets;
+        var builder = new codec.MessageV1Builder(
+            kUsbDevice_IsochronousTransferOut_Name,
+            codec.align(UsbDevice_IsochronousTransferOut_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDevice_IsochronousTransferOut_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    default:
+      return false;
+    }
+  };
+
+  function validateUsbDeviceRequest(messageValidator) {
+    var message = messageValidator.message;
+    var paramsClass = null;
+    switch (message.getName()) {
+      case kUsbDevice_Open_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_Open_Params;
+      break;
+      case kUsbDevice_Close_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_Close_Params;
+      break;
+      case kUsbDevice_SetConfiguration_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_SetConfiguration_Params;
+      break;
+      case kUsbDevice_ClaimInterface_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_ClaimInterface_Params;
+      break;
+      case kUsbDevice_ReleaseInterface_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_ReleaseInterface_Params;
+      break;
+      case kUsbDevice_SetInterfaceAlternateSetting_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_SetInterfaceAlternateSetting_Params;
+      break;
+      case kUsbDevice_Reset_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_Reset_Params;
+      break;
+      case kUsbDevice_ClearHalt_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_ClearHalt_Params;
+      break;
+      case kUsbDevice_ControlTransferIn_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_ControlTransferIn_Params;
+      break;
+      case kUsbDevice_ControlTransferOut_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_ControlTransferOut_Params;
+      break;
+      case kUsbDevice_GenericTransferIn_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_GenericTransferIn_Params;
+      break;
+      case kUsbDevice_GenericTransferOut_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_GenericTransferOut_Params;
+      break;
+      case kUsbDevice_IsochronousTransferIn_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_IsochronousTransferIn_Params;
+      break;
+      case kUsbDevice_IsochronousTransferOut_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDevice_IsochronousTransferOut_Params;
+      break;
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+  }
+
+  function validateUsbDeviceResponse(messageValidator) {
+   var message = messageValidator.message;
+   var paramsClass = null;
+   switch (message.getName()) {
+      case kUsbDevice_Open_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_Open_ResponseParams;
+        break;
+      case kUsbDevice_Close_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_Close_ResponseParams;
+        break;
+      case kUsbDevice_SetConfiguration_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_SetConfiguration_ResponseParams;
+        break;
+      case kUsbDevice_ClaimInterface_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_ClaimInterface_ResponseParams;
+        break;
+      case kUsbDevice_ReleaseInterface_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_ReleaseInterface_ResponseParams;
+        break;
+      case kUsbDevice_SetInterfaceAlternateSetting_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_SetInterfaceAlternateSetting_ResponseParams;
+        break;
+      case kUsbDevice_Reset_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_Reset_ResponseParams;
+        break;
+      case kUsbDevice_ClearHalt_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_ClearHalt_ResponseParams;
+        break;
+      case kUsbDevice_ControlTransferIn_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_ControlTransferIn_ResponseParams;
+        break;
+      case kUsbDevice_ControlTransferOut_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_ControlTransferOut_ResponseParams;
+        break;
+      case kUsbDevice_GenericTransferIn_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_GenericTransferIn_ResponseParams;
+        break;
+      case kUsbDevice_GenericTransferOut_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_GenericTransferOut_ResponseParams;
+        break;
+      case kUsbDevice_IsochronousTransferIn_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_IsochronousTransferIn_ResponseParams;
+        break;
+      case kUsbDevice_IsochronousTransferOut_Name:
+        if (message.isResponse())
+          paramsClass = UsbDevice_IsochronousTransferOut_ResponseParams;
+        break;
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+  }
+
+  var UsbDevice = {
+    name: 'device::mojom::UsbDevice',
+    kVersion: 0,
+    ptrClass: UsbDevicePtr,
+    proxyClass: UsbDeviceProxy,
+    stubClass: UsbDeviceStub,
+    validateRequest: validateUsbDeviceRequest,
+    validateResponse: validateUsbDeviceResponse,
+  };
+  UsbDeviceStub.prototype.validator = validateUsbDeviceRequest;
+  UsbDeviceProxy.prototype.validator = validateUsbDeviceResponse;
+  var exports = mojo.internal.exposeNamespace("device.mojom");
+  exports.UsbOpenDeviceError = UsbOpenDeviceError;
+  exports.UsbTransferDirection = UsbTransferDirection;
+  exports.UsbControlTransferType = UsbControlTransferType;
+  exports.UsbControlTransferRecipient = UsbControlTransferRecipient;
+  exports.UsbTransferType = UsbTransferType;
+  exports.UsbTransferStatus = UsbTransferStatus;
+  exports.UsbEndpointInfo = UsbEndpointInfo;
+  exports.UsbAlternateInterfaceInfo = UsbAlternateInterfaceInfo;
+  exports.UsbInterfaceInfo = UsbInterfaceInfo;
+  exports.UsbConfigurationInfo = UsbConfigurationInfo;
+  exports.UsbDeviceInfo = UsbDeviceInfo;
+  exports.UsbControlTransferParams = UsbControlTransferParams;
+  exports.UsbIsochronousPacket = UsbIsochronousPacket;
+  exports.UsbDevice = UsbDevice;
+  exports.UsbDevicePtr = UsbDevicePtr;
+  exports.UsbDeviceAssociatedPtr = UsbDeviceAssociatedPtr;
+})();
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device_manager.mojom.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device_manager.mojom.js
new file mode 100644
index 0000000..5a03393
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/device_manager.mojom.js
@@ -0,0 +1,850 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+(function() {
+  var mojomId = 'device/usb/public/interfaces/device_manager.mojom';
+  if (mojo.internal.isMojomLoaded(mojomId)) {
+    console.warn('The following mojom is loaded multiple times: ' + mojomId);
+    return;
+  }
+  mojo.internal.markMojomLoaded(mojomId);
+
+  // TODO(yzshen): Define these aliases to minimize the differences between the
+  // old/new modes. Remove them when the old mode goes away.
+  var bindings = mojo;
+  var associatedBindings = mojo;
+  var codec = mojo.internal;
+  var validator = mojo.internal;
+  var device$ =
+      mojo.internal.exposeNamespace('device.mojom');
+  if (mojo.config.autoLoadMojomDeps) {
+    mojo.internal.loadMojomIfNecessary(
+        'device/usb/public/interfaces/device.mojom',
+        new URL('device.mojom.js',
+                document.currentScript.src).href);
+  }
+
+
+
+  function UsbDeviceFilter(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDeviceFilter.prototype.initDefaults_ = function() {
+    this.hasVendorId = false;
+    this.hasProductId = false;
+    this.hasClassCode = false;
+    this.hasSubclassCode = false;
+    this.hasProtocolCode = false;
+    this.classCode = 0;
+    this.vendorId = 0;
+    this.productId = 0;
+    this.subclassCode = 0;
+    this.protocolCode = 0;
+    this.serialNumber = null;
+  };
+  UsbDeviceFilter.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDeviceFilter.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+
+
+
+
+
+
+
+
+
+    
+    // validate UsbDeviceFilter.serialNumber
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 8, true)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDeviceFilter.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbDeviceFilter.decode = function(decoder) {
+    var packed;
+    var val = new UsbDeviceFilter();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    packed = decoder.readUint8();
+    val.hasVendorId = (packed >> 0) & 1 ? true : false;
+    val.hasProductId = (packed >> 1) & 1 ? true : false;
+    val.hasClassCode = (packed >> 2) & 1 ? true : false;
+    val.hasSubclassCode = (packed >> 3) & 1 ? true : false;
+    val.hasProtocolCode = (packed >> 4) & 1 ? true : false;
+    val.classCode = decoder.decodeStruct(codec.Uint8);
+    val.vendorId = decoder.decodeStruct(codec.Uint16);
+    val.productId = decoder.decodeStruct(codec.Uint16);
+    val.subclassCode = decoder.decodeStruct(codec.Uint8);
+    val.protocolCode = decoder.decodeStruct(codec.Uint8);
+    val.serialNumber = decoder.decodeStruct(codec.NullableString);
+    return val;
+  };
+
+  UsbDeviceFilter.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDeviceFilter.encodedSize);
+    encoder.writeUint32(0);
+    packed = 0;
+    packed |= (val.hasVendorId & 1) << 0
+    packed |= (val.hasProductId & 1) << 1
+    packed |= (val.hasClassCode & 1) << 2
+    packed |= (val.hasSubclassCode & 1) << 3
+    packed |= (val.hasProtocolCode & 1) << 4
+    encoder.writeUint8(packed);
+    encoder.encodeStruct(codec.Uint8, val.classCode);
+    encoder.encodeStruct(codec.Uint16, val.vendorId);
+    encoder.encodeStruct(codec.Uint16, val.productId);
+    encoder.encodeStruct(codec.Uint8, val.subclassCode);
+    encoder.encodeStruct(codec.Uint8, val.protocolCode);
+    encoder.encodeStruct(codec.NullableString, val.serialNumber);
+  };
+  function UsbEnumerationOptions(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbEnumerationOptions.prototype.initDefaults_ = function() {
+    this.filters = null;
+  };
+  UsbEnumerationOptions.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbEnumerationOptions.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbEnumerationOptions.filters
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 8, new codec.PointerTo(UsbDeviceFilter), false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbEnumerationOptions.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbEnumerationOptions.decode = function(decoder) {
+    var packed;
+    var val = new UsbEnumerationOptions();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.filters = decoder.decodeArrayPointer(new codec.PointerTo(UsbDeviceFilter));
+    return val;
+  };
+
+  UsbEnumerationOptions.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbEnumerationOptions.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeArrayPointer(new codec.PointerTo(UsbDeviceFilter), val.filters);
+  };
+  function UsbDeviceManager_GetDevices_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDeviceManager_GetDevices_Params.prototype.initDefaults_ = function() {
+    this.options = null;
+  };
+  UsbDeviceManager_GetDevices_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDeviceManager_GetDevices_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDeviceManager_GetDevices_Params.options
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, UsbEnumerationOptions, true);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDeviceManager_GetDevices_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDeviceManager_GetDevices_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDeviceManager_GetDevices_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.options = decoder.decodeStructPointer(UsbEnumerationOptions);
+    return val;
+  };
+
+  UsbDeviceManager_GetDevices_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDeviceManager_GetDevices_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStructPointer(UsbEnumerationOptions, val.options);
+  };
+  function UsbDeviceManager_GetDevices_ResponseParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDeviceManager_GetDevices_ResponseParams.prototype.initDefaults_ = function() {
+    this.results = null;
+  };
+  UsbDeviceManager_GetDevices_ResponseParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDeviceManager_GetDevices_ResponseParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDeviceManager_GetDevices_ResponseParams.results
+    err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 0, 8, new codec.PointerTo(device$.UsbDeviceInfo), false, [0], 0);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDeviceManager_GetDevices_ResponseParams.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDeviceManager_GetDevices_ResponseParams.decode = function(decoder) {
+    var packed;
+    var val = new UsbDeviceManager_GetDevices_ResponseParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.results = decoder.decodeArrayPointer(new codec.PointerTo(device$.UsbDeviceInfo));
+    return val;
+  };
+
+  UsbDeviceManager_GetDevices_ResponseParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDeviceManager_GetDevices_ResponseParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeArrayPointer(new codec.PointerTo(device$.UsbDeviceInfo), val.results);
+  };
+  function UsbDeviceManager_GetDevice_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDeviceManager_GetDevice_Params.prototype.initDefaults_ = function() {
+    this.guid = null;
+    this.deviceRequest = new bindings.InterfaceRequest();
+  };
+  UsbDeviceManager_GetDevice_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDeviceManager_GetDevice_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDeviceManager_GetDevice_Params.guid
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 0, false)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDeviceManager_GetDevice_Params.deviceRequest
+    err = messageValidator.validateInterfaceRequest(offset + codec.kStructHeaderSize + 8, false)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDeviceManager_GetDevice_Params.encodedSize = codec.kStructHeaderSize + 16;
+
+  UsbDeviceManager_GetDevice_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDeviceManager_GetDevice_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.guid = decoder.decodeStruct(codec.String);
+    val.deviceRequest = decoder.decodeStruct(codec.InterfaceRequest);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  UsbDeviceManager_GetDevice_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDeviceManager_GetDevice_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.String, val.guid);
+    encoder.encodeStruct(codec.InterfaceRequest, val.deviceRequest);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function UsbDeviceManager_SetClient_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDeviceManager_SetClient_Params.prototype.initDefaults_ = function() {
+    this.client = new UsbDeviceManagerClientPtr();
+  };
+  UsbDeviceManager_SetClient_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDeviceManager_SetClient_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDeviceManager_SetClient_Params.client
+    err = messageValidator.validateInterface(offset + codec.kStructHeaderSize + 0, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDeviceManager_SetClient_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDeviceManager_SetClient_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDeviceManager_SetClient_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.client = decoder.decodeStruct(new codec.Interface(UsbDeviceManagerClientPtr));
+    return val;
+  };
+
+  UsbDeviceManager_SetClient_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDeviceManager_SetClient_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(new codec.Interface(UsbDeviceManagerClientPtr), val.client);
+  };
+  function UsbDeviceManagerClient_OnDeviceAdded_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDeviceManagerClient_OnDeviceAdded_Params.prototype.initDefaults_ = function() {
+    this.deviceInfo = null;
+  };
+  UsbDeviceManagerClient_OnDeviceAdded_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDeviceManagerClient_OnDeviceAdded_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDeviceManagerClient_OnDeviceAdded_Params.deviceInfo
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, device$.UsbDeviceInfo, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDeviceManagerClient_OnDeviceAdded_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDeviceManagerClient_OnDeviceAdded_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDeviceManagerClient_OnDeviceAdded_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.deviceInfo = decoder.decodeStructPointer(device$.UsbDeviceInfo);
+    return val;
+  };
+
+  UsbDeviceManagerClient_OnDeviceAdded_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDeviceManagerClient_OnDeviceAdded_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStructPointer(device$.UsbDeviceInfo, val.deviceInfo);
+  };
+  function UsbDeviceManagerClient_OnDeviceRemoved_Params(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  UsbDeviceManagerClient_OnDeviceRemoved_Params.prototype.initDefaults_ = function() {
+    this.deviceInfo = null;
+  };
+  UsbDeviceManagerClient_OnDeviceRemoved_Params.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  UsbDeviceManagerClient_OnDeviceRemoved_Params.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    
+    // validate UsbDeviceManagerClient_OnDeviceRemoved_Params.deviceInfo
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 0, device$.UsbDeviceInfo, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  UsbDeviceManagerClient_OnDeviceRemoved_Params.encodedSize = codec.kStructHeaderSize + 8;
+
+  UsbDeviceManagerClient_OnDeviceRemoved_Params.decode = function(decoder) {
+    var packed;
+    var val = new UsbDeviceManagerClient_OnDeviceRemoved_Params();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.deviceInfo = decoder.decodeStructPointer(device$.UsbDeviceInfo);
+    return val;
+  };
+
+  UsbDeviceManagerClient_OnDeviceRemoved_Params.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(UsbDeviceManagerClient_OnDeviceRemoved_Params.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStructPointer(device$.UsbDeviceInfo, val.deviceInfo);
+  };
+  var kUsbDeviceManager_GetDevices_Name = 0;
+  var kUsbDeviceManager_GetDevice_Name = 1;
+  var kUsbDeviceManager_SetClient_Name = 2;
+
+  function UsbDeviceManagerPtr(handleOrPtrInfo) {
+    this.ptr = new bindings.InterfacePtrController(UsbDeviceManager,
+                                                   handleOrPtrInfo);
+  }
+
+  function UsbDeviceManagerAssociatedPtr(associatedInterfacePtrInfo) {
+    this.ptr = new associatedBindings.AssociatedInterfacePtrController(
+        UsbDeviceManager, associatedInterfacePtrInfo);
+  }
+
+  UsbDeviceManagerAssociatedPtr.prototype =
+      Object.create(UsbDeviceManagerPtr.prototype);
+  UsbDeviceManagerAssociatedPtr.prototype.constructor =
+      UsbDeviceManagerAssociatedPtr;
+
+  function UsbDeviceManagerProxy(receiver) {
+    this.receiver_ = receiver;
+  }
+  UsbDeviceManagerPtr.prototype.getDevices = function() {
+    return UsbDeviceManagerProxy.prototype.getDevices
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceManagerProxy.prototype.getDevices = function(options) {
+    var params = new UsbDeviceManager_GetDevices_Params();
+    params.options = options;
+    return new Promise(function(resolve, reject) {
+      var builder = new codec.MessageV1Builder(
+          kUsbDeviceManager_GetDevices_Name,
+          codec.align(UsbDeviceManager_GetDevices_Params.encodedSize),
+          codec.kMessageExpectsResponse, 0);
+      builder.encodeStruct(UsbDeviceManager_GetDevices_Params, params);
+      var message = builder.finish();
+      this.receiver_.acceptAndExpectResponse(message).then(function(message) {
+        var reader = new codec.MessageReader(message);
+        var responseParams =
+            reader.decodeStruct(UsbDeviceManager_GetDevices_ResponseParams);
+        resolve(responseParams);
+      }).catch(function(result) {
+        reject(Error("Connection error: " + result));
+      });
+    }.bind(this));
+  };
+  UsbDeviceManagerPtr.prototype.getDevice = function() {
+    return UsbDeviceManagerProxy.prototype.getDevice
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceManagerProxy.prototype.getDevice = function(guid, deviceRequest) {
+    var params = new UsbDeviceManager_GetDevice_Params();
+    params.guid = guid;
+    params.deviceRequest = deviceRequest;
+    var builder = new codec.MessageV0Builder(
+        kUsbDeviceManager_GetDevice_Name,
+        codec.align(UsbDeviceManager_GetDevice_Params.encodedSize));
+    builder.encodeStruct(UsbDeviceManager_GetDevice_Params, params);
+    var message = builder.finish();
+    this.receiver_.accept(message);
+  };
+  UsbDeviceManagerPtr.prototype.setClient = function() {
+    return UsbDeviceManagerProxy.prototype.setClient
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceManagerProxy.prototype.setClient = function(client) {
+    var params = new UsbDeviceManager_SetClient_Params();
+    params.client = client;
+    var builder = new codec.MessageV0Builder(
+        kUsbDeviceManager_SetClient_Name,
+        codec.align(UsbDeviceManager_SetClient_Params.encodedSize));
+    builder.encodeStruct(UsbDeviceManager_SetClient_Params, params);
+    var message = builder.finish();
+    this.receiver_.accept(message);
+  };
+
+  function UsbDeviceManagerStub(delegate) {
+    this.delegate_ = delegate;
+  }
+  UsbDeviceManagerStub.prototype.getDevices = function(options) {
+    return this.delegate_ && this.delegate_.getDevices && this.delegate_.getDevices(options);
+  }
+  UsbDeviceManagerStub.prototype.getDevice = function(guid, deviceRequest) {
+    return this.delegate_ && this.delegate_.getDevice && this.delegate_.getDevice(guid, deviceRequest);
+  }
+  UsbDeviceManagerStub.prototype.setClient = function(client) {
+    return this.delegate_ && this.delegate_.setClient && this.delegate_.setClient(client);
+  }
+
+  UsbDeviceManagerStub.prototype.accept = function(message) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    case kUsbDeviceManager_GetDevice_Name:
+      var params = reader.decodeStruct(UsbDeviceManager_GetDevice_Params);
+      this.getDevice(params.guid, params.deviceRequest);
+      return true;
+    case kUsbDeviceManager_SetClient_Name:
+      var params = reader.decodeStruct(UsbDeviceManager_SetClient_Params);
+      this.setClient(params.client);
+      return true;
+    default:
+      return false;
+    }
+  };
+
+  UsbDeviceManagerStub.prototype.acceptWithResponder =
+      function(message, responder) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    case kUsbDeviceManager_GetDevices_Name:
+      var params = reader.decodeStruct(UsbDeviceManager_GetDevices_Params);
+      this.getDevices(params.options).then(function(response) {
+        var responseParams =
+            new UsbDeviceManager_GetDevices_ResponseParams();
+        responseParams.results = response.results;
+        var builder = new codec.MessageV1Builder(
+            kUsbDeviceManager_GetDevices_Name,
+            codec.align(UsbDeviceManager_GetDevices_ResponseParams.encodedSize),
+            codec.kMessageIsResponse, reader.requestID);
+        builder.encodeStruct(UsbDeviceManager_GetDevices_ResponseParams,
+                             responseParams);
+        var message = builder.finish();
+        responder.accept(message);
+      });
+      return true;
+    default:
+      return false;
+    }
+  };
+
+  function validateUsbDeviceManagerRequest(messageValidator) {
+    var message = messageValidator.message;
+    var paramsClass = null;
+    switch (message.getName()) {
+      case kUsbDeviceManager_GetDevices_Name:
+        if (message.expectsResponse())
+          paramsClass = UsbDeviceManager_GetDevices_Params;
+      break;
+      case kUsbDeviceManager_GetDevice_Name:
+        if (!message.expectsResponse() && !message.isResponse())
+          paramsClass = UsbDeviceManager_GetDevice_Params;
+      break;
+      case kUsbDeviceManager_SetClient_Name:
+        if (!message.expectsResponse() && !message.isResponse())
+          paramsClass = UsbDeviceManager_SetClient_Params;
+      break;
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+  }
+
+  function validateUsbDeviceManagerResponse(messageValidator) {
+   var message = messageValidator.message;
+   var paramsClass = null;
+   switch (message.getName()) {
+      case kUsbDeviceManager_GetDevices_Name:
+        if (message.isResponse())
+          paramsClass = UsbDeviceManager_GetDevices_ResponseParams;
+        break;
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+  }
+
+  var UsbDeviceManager = {
+    name: 'device::mojom::UsbDeviceManager',
+    kVersion: 0,
+    ptrClass: UsbDeviceManagerPtr,
+    proxyClass: UsbDeviceManagerProxy,
+    stubClass: UsbDeviceManagerStub,
+    validateRequest: validateUsbDeviceManagerRequest,
+    validateResponse: validateUsbDeviceManagerResponse,
+  };
+  UsbDeviceManagerStub.prototype.validator = validateUsbDeviceManagerRequest;
+  UsbDeviceManagerProxy.prototype.validator = validateUsbDeviceManagerResponse;
+  var kUsbDeviceManagerClient_OnDeviceAdded_Name = 0;
+  var kUsbDeviceManagerClient_OnDeviceRemoved_Name = 1;
+
+  function UsbDeviceManagerClientPtr(handleOrPtrInfo) {
+    this.ptr = new bindings.InterfacePtrController(UsbDeviceManagerClient,
+                                                   handleOrPtrInfo);
+  }
+
+  function UsbDeviceManagerClientAssociatedPtr(associatedInterfacePtrInfo) {
+    this.ptr = new associatedBindings.AssociatedInterfacePtrController(
+        UsbDeviceManagerClient, associatedInterfacePtrInfo);
+  }
+
+  UsbDeviceManagerClientAssociatedPtr.prototype =
+      Object.create(UsbDeviceManagerClientPtr.prototype);
+  UsbDeviceManagerClientAssociatedPtr.prototype.constructor =
+      UsbDeviceManagerClientAssociatedPtr;
+
+  function UsbDeviceManagerClientProxy(receiver) {
+    this.receiver_ = receiver;
+  }
+  UsbDeviceManagerClientPtr.prototype.onDeviceAdded = function() {
+    return UsbDeviceManagerClientProxy.prototype.onDeviceAdded
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceManagerClientProxy.prototype.onDeviceAdded = function(deviceInfo) {
+    var params = new UsbDeviceManagerClient_OnDeviceAdded_Params();
+    params.deviceInfo = deviceInfo;
+    var builder = new codec.MessageV0Builder(
+        kUsbDeviceManagerClient_OnDeviceAdded_Name,
+        codec.align(UsbDeviceManagerClient_OnDeviceAdded_Params.encodedSize));
+    builder.encodeStruct(UsbDeviceManagerClient_OnDeviceAdded_Params, params);
+    var message = builder.finish();
+    this.receiver_.accept(message);
+  };
+  UsbDeviceManagerClientPtr.prototype.onDeviceRemoved = function() {
+    return UsbDeviceManagerClientProxy.prototype.onDeviceRemoved
+        .apply(this.ptr.getProxy(), arguments);
+  };
+
+  UsbDeviceManagerClientProxy.prototype.onDeviceRemoved = function(deviceInfo) {
+    var params = new UsbDeviceManagerClient_OnDeviceRemoved_Params();
+    params.deviceInfo = deviceInfo;
+    var builder = new codec.MessageV0Builder(
+        kUsbDeviceManagerClient_OnDeviceRemoved_Name,
+        codec.align(UsbDeviceManagerClient_OnDeviceRemoved_Params.encodedSize));
+    builder.encodeStruct(UsbDeviceManagerClient_OnDeviceRemoved_Params, params);
+    var message = builder.finish();
+    this.receiver_.accept(message);
+  };
+
+  function UsbDeviceManagerClientStub(delegate) {
+    this.delegate_ = delegate;
+  }
+  UsbDeviceManagerClientStub.prototype.onDeviceAdded = function(deviceInfo) {
+    return this.delegate_ && this.delegate_.onDeviceAdded && this.delegate_.onDeviceAdded(deviceInfo);
+  }
+  UsbDeviceManagerClientStub.prototype.onDeviceRemoved = function(deviceInfo) {
+    return this.delegate_ && this.delegate_.onDeviceRemoved && this.delegate_.onDeviceRemoved(deviceInfo);
+  }
+
+  UsbDeviceManagerClientStub.prototype.accept = function(message) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    case kUsbDeviceManagerClient_OnDeviceAdded_Name:
+      var params = reader.decodeStruct(UsbDeviceManagerClient_OnDeviceAdded_Params);
+      this.onDeviceAdded(params.deviceInfo);
+      return true;
+    case kUsbDeviceManagerClient_OnDeviceRemoved_Name:
+      var params = reader.decodeStruct(UsbDeviceManagerClient_OnDeviceRemoved_Params);
+      this.onDeviceRemoved(params.deviceInfo);
+      return true;
+    default:
+      return false;
+    }
+  };
+
+  UsbDeviceManagerClientStub.prototype.acceptWithResponder =
+      function(message, responder) {
+    var reader = new codec.MessageReader(message);
+    switch (reader.messageName) {
+    default:
+      return false;
+    }
+  };
+
+  function validateUsbDeviceManagerClientRequest(messageValidator) {
+    var message = messageValidator.message;
+    var paramsClass = null;
+    switch (message.getName()) {
+      case kUsbDeviceManagerClient_OnDeviceAdded_Name:
+        if (!message.expectsResponse() && !message.isResponse())
+          paramsClass = UsbDeviceManagerClient_OnDeviceAdded_Params;
+      break;
+      case kUsbDeviceManagerClient_OnDeviceRemoved_Name:
+        if (!message.expectsResponse() && !message.isResponse())
+          paramsClass = UsbDeviceManagerClient_OnDeviceRemoved_Params;
+      break;
+    }
+    if (paramsClass === null)
+      return validator.validationError.NONE;
+    return paramsClass.validate(messageValidator, messageValidator.message.getHeaderNumBytes());
+  }
+
+  function validateUsbDeviceManagerClientResponse(messageValidator) {
+    return validator.validationError.NONE;
+  }
+
+  var UsbDeviceManagerClient = {
+    name: 'device::mojom::UsbDeviceManagerClient',
+    kVersion: 0,
+    ptrClass: UsbDeviceManagerClientPtr,
+    proxyClass: UsbDeviceManagerClientProxy,
+    stubClass: UsbDeviceManagerClientStub,
+    validateRequest: validateUsbDeviceManagerClientRequest,
+    validateResponse: null,
+  };
+  UsbDeviceManagerClientStub.prototype.validator = validateUsbDeviceManagerClientRequest;
+  UsbDeviceManagerClientProxy.prototype.validator = null;
+  var exports = mojo.internal.exposeNamespace("device.mojom");
+  exports.UsbDeviceFilter = UsbDeviceFilter;
+  exports.UsbEnumerationOptions = UsbEnumerationOptions;
+  exports.UsbDeviceManager = UsbDeviceManager;
+  exports.UsbDeviceManagerPtr = UsbDeviceManagerPtr;
+  exports.UsbDeviceManagerAssociatedPtr = UsbDeviceManagerAssociatedPtr;
+  exports.UsbDeviceManagerClient = UsbDeviceManagerClient;
+  exports.UsbDeviceManagerClientPtr = UsbDeviceManagerClientPtr;
+  exports.UsbDeviceManagerClientAssociatedPtr = UsbDeviceManagerClientAssociatedPtr;
+})();
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/mojo_bindings.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/mojo_bindings.js
new file mode 100644
index 0000000..af8d859
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/mojo_bindings.js
@@ -0,0 +1,5110 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+if (mojo && mojo.internal) {
+  throw new Error('The Mojo bindings library has been initialized.');
+}
+
+var mojo = mojo || {};
+mojo.internal = {};
+mojo.internal.global = this;
+mojo.config = {
+  // Whether to automatically load mojom dependencies.
+  // For example, if foo.mojom imports bar.mojom, |autoLoadMojomDeps| set to
+  // true means that loading foo.mojom.js will insert a <script> tag to load
+  // bar.mojom.js, if it hasn't been loaded.
+  //
+  // The URL of bar.mojom.js is determined by the relative path of bar.mojom
+  // (relative to the position of foo.mojom at build time) and the URL of
+  // foo.mojom.js. For exmple, if at build time the two mojom files are
+  // located at:
+  //   a/b/c/foo.mojom
+  //   a/b/d/bar.mojom
+  // and the URL of foo.mojom.js is:
+  //   http://example.org/scripts/b/c/foo.mojom.js
+  // then the URL of bar.mojom.js will be:
+  //   http://example.org/scripts/b/d/bar.mojom.js
+  //
+  // If you would like bar.mojom.js to live at a different location, you need
+  // to turn off |autoLoadMojomDeps| before loading foo.mojom.js, and manually
+  // load bar.mojom.js yourself. Similarly, you need to turn off the option if
+  // you merge bar.mojom.js and foo.mojom.js into a single file.
+  //
+  // Performance tip: Avoid loading the same mojom.js file multiple times.
+  // Assume that |autoLoadMojomDeps| is set to true:
+  // <!-- No duplicate loading; recommended. -->
+  // <script src="http://example.org/scripts/b/c/foo.mojom.js"></script>
+  //
+  // <!-- No duplicate loading, although unnecessary. -->
+  // <script src="http://example.org/scripts/b/d/bar.mojom.js"></script>
+  // <script src="http://example.org/scripts/b/c/foo.mojom.js"></script>
+  //
+  // <!-- Load bar.mojom.js twice; should be avoided. -->
+  // <script src="http://example.org/scripts/b/c/foo.mojom.js"></script>
+  // <script src="http://example.org/scripts/b/d/bar.mojom.js"></script>
+  autoLoadMojomDeps: false
+};
+
+(function() {
+  var internal = mojo.internal;
+
+  var LoadState = {
+    PENDING_LOAD: 1,
+    LOADED: 2
+  };
+
+  var mojomRegistry = new Map();
+
+  function exposeNamespace(namespace) {
+    var current = internal.global;
+    var parts = namespace.split('.');
+
+    for (var part; parts.length && (part = parts.shift());) {
+      if (!current[part]) {
+        current[part] = {};
+      }
+      current = current[part];
+    }
+
+    return current;
+  }
+
+  function isMojomPendingLoad(id) {
+    return mojomRegistry.get(id) === LoadState.PENDING_LOAD;
+  }
+
+  function isMojomLoaded(id) {
+    return mojomRegistry.get(id) === LoadState.LOADED;
+  }
+
+  function markMojomPendingLoad(id) {
+    if (isMojomLoaded(id)) {
+      throw new Error('The following mojom file has been loaded: ' + id);
+    }
+
+    mojomRegistry.set(id, LoadState.PENDING_LOAD);
+  }
+
+  function markMojomLoaded(id) {
+    mojomRegistry.set(id, LoadState.LOADED);
+  }
+
+  function loadMojomIfNecessary(id, url) {
+    if (mojomRegistry.has(id)) {
+      return;
+    }
+
+    markMojomPendingLoad(id);
+    internal.global.document.write('<script type="text/javascript" src="' +
+                                   url + '"></script>');
+  }
+
+  internal.exposeNamespace = exposeNamespace;
+  internal.isMojomPendingLoad = isMojomPendingLoad;
+  internal.isMojomLoaded = isMojomLoaded;
+  internal.markMojomPendingLoad = markMojomPendingLoad;
+  internal.markMojomLoaded = markMojomLoaded;
+  internal.loadMojomIfNecessary = loadMojomIfNecessary;
+})();
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  // ---------------------------------------------------------------------------
+
+  // |output| could be an interface pointer, InterfacePtrInfo or
+  // AssociatedInterfacePtrInfo.
+  function makeRequest(output) {
+    if (output instanceof mojo.AssociatedInterfacePtrInfo) {
+      var {handle0, handle1} = internal.createPairPendingAssociation();
+      output.interfaceEndpointHandle = handle0;
+      output.version = 0;
+
+      return new mojo.AssociatedInterfaceRequest(handle1);
+    }
+
+    if (output instanceof mojo.InterfacePtrInfo) {
+      var pipe = Mojo.createMessagePipe();
+      output.handle = pipe.handle0;
+      output.version = 0;
+
+      return new mojo.InterfaceRequest(pipe.handle1);
+    }
+
+    var pipe = Mojo.createMessagePipe();
+    output.ptr.bind(new mojo.InterfacePtrInfo(pipe.handle0, 0));
+    return new mojo.InterfaceRequest(pipe.handle1);
+  }
+
+  // ---------------------------------------------------------------------------
+
+  // Operations used to setup/configure an interface pointer. Exposed as the
+  // |ptr| field of generated interface pointer classes.
+  // |ptrInfoOrHandle| could be omitted and passed into bind() later.
+  function InterfacePtrController(interfaceType, ptrInfoOrHandle) {
+    this.version = 0;
+
+    this.interfaceType_ = interfaceType;
+    this.router_ = null;
+    this.interfaceEndpointClient_ = null;
+    this.proxy_ = null;
+
+    // |router_| and |interfaceEndpointClient_| are lazily initialized.
+    // |handle_| is valid between bind() and
+    // the initialization of |router_| and |interfaceEndpointClient_|.
+    this.handle_ = null;
+
+    if (ptrInfoOrHandle)
+      this.bind(ptrInfoOrHandle);
+  }
+
+  InterfacePtrController.prototype.bind = function(ptrInfoOrHandle) {
+    this.reset();
+
+    if (ptrInfoOrHandle instanceof mojo.InterfacePtrInfo) {
+      this.version = ptrInfoOrHandle.version;
+      this.handle_ = ptrInfoOrHandle.handle;
+    } else {
+      this.handle_ = ptrInfoOrHandle;
+    }
+  };
+
+  InterfacePtrController.prototype.isBound = function() {
+    return this.interfaceEndpointClient_ !== null || this.handle_ !== null;
+  };
+
+  // Although users could just discard the object, reset() closes the pipe
+  // immediately.
+  InterfacePtrController.prototype.reset = function() {
+    this.version = 0;
+    if (this.interfaceEndpointClient_) {
+      this.interfaceEndpointClient_.close();
+      this.interfaceEndpointClient_ = null;
+    }
+    if (this.router_) {
+      this.router_.close();
+      this.router_ = null;
+
+      this.proxy_ = null;
+    }
+    if (this.handle_) {
+      this.handle_.close();
+      this.handle_ = null;
+    }
+  };
+
+  InterfacePtrController.prototype.resetWithReason = function(reason) {
+    if (this.isBound()) {
+      this.configureProxyIfNecessary_();
+      this.interfaceEndpointClient_.close(reason);
+      this.interfaceEndpointClient_ = null;
+    }
+    this.reset();
+  };
+
+  InterfacePtrController.prototype.setConnectionErrorHandler = function(
+      callback) {
+    if (!this.isBound())
+      throw new Error("Cannot set connection error handler if not bound.");
+
+    this.configureProxyIfNecessary_();
+    this.interfaceEndpointClient_.setConnectionErrorHandler(callback);
+  };
+
+  InterfacePtrController.prototype.passInterface = function() {
+    var result;
+    if (this.router_) {
+      // TODO(yzshen): Fix Router interface to support extracting handle.
+      result = new mojo.InterfacePtrInfo(
+          this.router_.connector_.handle_, this.version);
+      this.router_.connector_.handle_ = null;
+    } else {
+      // This also handles the case when this object is not bound.
+      result = new mojo.InterfacePtrInfo(this.handle_, this.version);
+      this.handle_ = null;
+    }
+
+    this.reset();
+    return result;
+  };
+
+  InterfacePtrController.prototype.getProxy = function() {
+    this.configureProxyIfNecessary_();
+    return this.proxy_;
+  };
+
+  InterfacePtrController.prototype.configureProxyIfNecessary_ = function() {
+    if (!this.handle_)
+      return;
+
+    this.router_ = new internal.Router(this.handle_, true);
+    this.handle_ = null;
+
+    this.interfaceEndpointClient_ = new internal.InterfaceEndpointClient(
+        this.router_.createLocalEndpointHandle(internal.kMasterInterfaceId));
+
+    this.interfaceEndpointClient_ .setPayloadValidators([
+        this.interfaceType_.validateResponse]);
+    this.proxy_ = new this.interfaceType_.proxyClass(
+        this.interfaceEndpointClient_);
+  };
+
+  InterfacePtrController.prototype.queryVersion = function() {
+    function onQueryVersion(version) {
+      this.version = version;
+      return version;
+    }
+
+    this.configureProxyIfNecessary_();
+    return this.interfaceEndpointClient_.queryVersion().then(
+      onQueryVersion.bind(this));
+  };
+
+  InterfacePtrController.prototype.requireVersion = function(version) {
+    this.configureProxyIfNecessary_();
+
+    if (this.version >= version) {
+      return;
+    }
+    this.version = version;
+    this.interfaceEndpointClient_.requireVersion(version);
+  };
+
+  // ---------------------------------------------------------------------------
+
+  // |request| could be omitted and passed into bind() later.
+  //
+  // Example:
+  //
+  //    // FooImpl implements mojom.Foo.
+  //    function FooImpl() { ... }
+  //    FooImpl.prototype.fooMethod1 = function() { ... }
+  //    FooImpl.prototype.fooMethod2 = function() { ... }
+  //
+  //    var fooPtr = new mojom.FooPtr();
+  //    var request = makeRequest(fooPtr);
+  //    var binding = new Binding(mojom.Foo, new FooImpl(), request);
+  //    fooPtr.fooMethod1();
+  function Binding(interfaceType, impl, requestOrHandle) {
+    this.interfaceType_ = interfaceType;
+    this.impl_ = impl;
+    this.router_ = null;
+    this.interfaceEndpointClient_ = null;
+    this.stub_ = null;
+
+    if (requestOrHandle)
+      this.bind(requestOrHandle);
+  }
+
+  Binding.prototype.isBound = function() {
+    return this.router_ !== null;
+  };
+
+  Binding.prototype.createInterfacePtrAndBind = function() {
+    var ptr = new this.interfaceType_.ptrClass();
+    // TODO(yzshen): Set the version of the interface pointer.
+    this.bind(makeRequest(ptr));
+    return ptr;
+  };
+
+  Binding.prototype.bind = function(requestOrHandle) {
+    this.close();
+
+    var handle = requestOrHandle instanceof mojo.InterfaceRequest ?
+        requestOrHandle.handle : requestOrHandle;
+    if (!(handle instanceof MojoHandle))
+      return;
+
+    this.router_ = new internal.Router(handle);
+
+    this.stub_ = new this.interfaceType_.stubClass(this.impl_);
+    this.interfaceEndpointClient_ = new internal.InterfaceEndpointClient(
+        this.router_.createLocalEndpointHandle(internal.kMasterInterfaceId),
+        this.stub_, this.interfaceType_.kVersion);
+
+    this.interfaceEndpointClient_ .setPayloadValidators([
+        this.interfaceType_.validateRequest]);
+  };
+
+  Binding.prototype.close = function() {
+    if (!this.isBound())
+      return;
+
+    if (this.interfaceEndpointClient_) {
+      this.interfaceEndpointClient_.close();
+      this.interfaceEndpointClient_ = null;
+    }
+
+    this.router_.close();
+    this.router_ = null;
+    this.stub_ = null;
+  };
+
+  Binding.prototype.closeWithReason = function(reason) {
+    if (this.interfaceEndpointClient_) {
+      this.interfaceEndpointClient_.close(reason);
+      this.interfaceEndpointClient_ = null;
+    }
+    this.close();
+  };
+
+  Binding.prototype.setConnectionErrorHandler = function(callback) {
+    if (!this.isBound()) {
+      throw new Error("Cannot set connection error handler if not bound.");
+    }
+    this.interfaceEndpointClient_.setConnectionErrorHandler(callback);
+  };
+
+  Binding.prototype.unbind = function() {
+    if (!this.isBound())
+      return new mojo.InterfaceRequest(null);
+
+    var result = new mojo.InterfaceRequest(this.router_.connector_.handle_);
+    this.router_.connector_.handle_ = null;
+    this.close();
+    return result;
+  };
+
+  // ---------------------------------------------------------------------------
+
+  function BindingSetEntry(bindingSet, interfaceType, bindingType, impl,
+      requestOrHandle, bindingId) {
+    this.bindingSet_ = bindingSet;
+    this.bindingId_ = bindingId;
+    this.binding_ = new bindingType(interfaceType, impl,
+        requestOrHandle);
+
+    this.binding_.setConnectionErrorHandler(function(reason) {
+      this.bindingSet_.onConnectionError(bindingId, reason);
+    }.bind(this));
+  }
+
+  BindingSetEntry.prototype.close = function() {
+    this.binding_.close();
+  };
+
+  function BindingSet(interfaceType) {
+    this.interfaceType_ = interfaceType;
+    this.nextBindingId_ = 0;
+    this.bindings_ = new Map();
+    this.errorHandler_ = null;
+    this.bindingType_ = Binding;
+  }
+
+  BindingSet.prototype.isEmpty = function() {
+    return this.bindings_.size == 0;
+  };
+
+  BindingSet.prototype.addBinding = function(impl, requestOrHandle) {
+    this.bindings_.set(
+        this.nextBindingId_,
+        new BindingSetEntry(this, this.interfaceType_, this.bindingType_, impl,
+            requestOrHandle, this.nextBindingId_));
+    ++this.nextBindingId_;
+  };
+
+  BindingSet.prototype.closeAllBindings = function() {
+    for (var entry of this.bindings_.values())
+      entry.close();
+    this.bindings_.clear();
+  };
+
+  BindingSet.prototype.setConnectionErrorHandler = function(callback) {
+    this.errorHandler_ = callback;
+  };
+
+  BindingSet.prototype.onConnectionError = function(bindingId, reason) {
+    this.bindings_.delete(bindingId);
+
+    if (this.errorHandler_)
+      this.errorHandler_(reason);
+  };
+
+  // ---------------------------------------------------------------------------
+
+  // Operations used to setup/configure an associated interface pointer.
+  // Exposed as |ptr| field of generated associated interface pointer classes.
+  // |associatedPtrInfo| could be omitted and passed into bind() later.
+  //
+  // Example:
+  //    // IntegerSenderImpl implements mojom.IntegerSender
+  //    function IntegerSenderImpl() { ... }
+  //    IntegerSenderImpl.prototype.echo = function() { ... }
+  //
+  //    // IntegerSenderConnectionImpl implements mojom.IntegerSenderConnection
+  //    function IntegerSenderConnectionImpl() {
+  //      this.senderBinding_ = null;
+  //    }
+  //    IntegerSenderConnectionImpl.prototype.getSender = function(
+  //        associatedRequest) {
+  //      this.senderBinding_ = new AssociatedBinding(mojom.IntegerSender,
+  //          new IntegerSenderImpl(),
+  //          associatedRequest);
+  //    }
+  //
+  //    var integerSenderConnection = new mojom.IntegerSenderConnectionPtr();
+  //    var integerSenderConnectionBinding = new Binding(
+  //        mojom.IntegerSenderConnection,
+  //        new IntegerSenderConnectionImpl(),
+  //        mojo.makeRequest(integerSenderConnection));
+  //
+  //    // A locally-created associated interface pointer can only be used to
+  //    // make calls when the corresponding associated request is sent over
+  //    // another interface (either the master interface or another
+  //    // associated interface).
+  //    var associatedInterfacePtrInfo = new AssociatedInterfacePtrInfo();
+  //    var associatedRequest = makeRequest(interfacePtrInfo);
+  //
+  //    integerSenderConnection.getSender(associatedRequest);
+  //
+  //    // Create an associated interface and bind the associated handle.
+  //    var integerSender = new mojom.AssociatedIntegerSenderPtr();
+  //    integerSender.ptr.bind(associatedInterfacePtrInfo);
+  //    integerSender.echo();
+
+  function AssociatedInterfacePtrController(interfaceType, associatedPtrInfo) {
+    this.version = 0;
+
+    this.interfaceType_ = interfaceType;
+    this.interfaceEndpointClient_ = null;
+    this.proxy_ = null;
+
+    if (associatedPtrInfo) {
+      this.bind(associatedPtrInfo);
+    }
+  }
+
+  AssociatedInterfacePtrController.prototype.bind = function(
+      associatedPtrInfo) {
+    this.reset();
+    this.version = associatedPtrInfo.version;
+
+    this.interfaceEndpointClient_ = new internal.InterfaceEndpointClient(
+        associatedPtrInfo.interfaceEndpointHandle);
+
+    this.interfaceEndpointClient_ .setPayloadValidators([
+        this.interfaceType_.validateResponse]);
+    this.proxy_ = new this.interfaceType_.proxyClass(
+        this.interfaceEndpointClient_);
+  };
+
+  AssociatedInterfacePtrController.prototype.isBound = function() {
+    return this.interfaceEndpointClient_ !== null;
+  };
+
+  AssociatedInterfacePtrController.prototype.reset = function() {
+    this.version = 0;
+    if (this.interfaceEndpointClient_) {
+      this.interfaceEndpointClient_.close();
+      this.interfaceEndpointClient_ = null;
+    }
+    if (this.proxy_) {
+      this.proxy_ = null;
+    }
+  };
+
+  AssociatedInterfacePtrController.prototype.resetWithReason = function(
+      reason) {
+    if (this.isBound()) {
+      this.interfaceEndpointClient_.close(reason);
+      this.interfaceEndpointClient_ = null;
+    }
+    this.reset();
+  };
+
+  // Indicates whether an error has been encountered. If true, method calls
+  // on this interface will be dropped (and may already have been dropped).
+  AssociatedInterfacePtrController.prototype.getEncounteredError = function() {
+    return this.interfaceEndpointClient_ ?
+        this.interfaceEndpointClient_.getEncounteredError() : false;
+  };
+
+  AssociatedInterfacePtrController.prototype.setConnectionErrorHandler =
+      function(callback) {
+    if (!this.isBound()) {
+      throw new Error("Cannot set connection error handler if not bound.");
+    }
+
+    this.interfaceEndpointClient_.setConnectionErrorHandler(callback);
+  };
+
+  AssociatedInterfacePtrController.prototype.passInterface = function() {
+    if (!this.isBound()) {
+      return new mojo.AssociatedInterfacePtrInfo(null);
+    }
+
+    var result = new mojo.AssociatedInterfacePtrInfo(
+        this.interfaceEndpointClient_.passHandle(), this.version);
+    this.reset();
+    return result;
+  };
+
+  AssociatedInterfacePtrController.prototype.getProxy = function() {
+    return this.proxy_;
+  };
+
+  AssociatedInterfacePtrController.prototype.queryVersion = function() {
+    function onQueryVersion(version) {
+      this.version = version;
+      return version;
+    }
+
+    return this.interfaceEndpointClient_.queryVersion().then(
+      onQueryVersion.bind(this));
+  };
+
+  AssociatedInterfacePtrController.prototype.requireVersion = function(
+      version) {
+    if (this.version >= version) {
+      return;
+    }
+    this.version = version;
+    this.interfaceEndpointClient_.requireVersion(version);
+  };
+
+  // ---------------------------------------------------------------------------
+
+  // |associatedInterfaceRequest| could be omitted and passed into bind()
+  // later.
+  function AssociatedBinding(interfaceType, impl, associatedInterfaceRequest) {
+    this.interfaceType_ = interfaceType;
+    this.impl_ = impl;
+    this.interfaceEndpointClient_ = null;
+    this.stub_ = null;
+
+    if (associatedInterfaceRequest) {
+      this.bind(associatedInterfaceRequest);
+    }
+  }
+
+  AssociatedBinding.prototype.isBound = function() {
+    return this.interfaceEndpointClient_ !== null;
+  };
+
+  AssociatedBinding.prototype.bind = function(associatedInterfaceRequest) {
+    this.close();
+
+    this.stub_ = new this.interfaceType_.stubClass(this.impl_);
+    this.interfaceEndpointClient_ = new internal.InterfaceEndpointClient(
+        associatedInterfaceRequest.interfaceEndpointHandle, this.stub_,
+        this.interfaceType_.kVersion);
+
+    this.interfaceEndpointClient_ .setPayloadValidators([
+        this.interfaceType_.validateRequest]);
+  };
+
+
+  AssociatedBinding.prototype.close = function() {
+    if (!this.isBound()) {
+      return;
+    }
+
+    if (this.interfaceEndpointClient_) {
+      this.interfaceEndpointClient_.close();
+      this.interfaceEndpointClient_ = null;
+    }
+
+    this.stub_ = null;
+  };
+
+  AssociatedBinding.prototype.closeWithReason = function(reason) {
+    if (this.interfaceEndpointClient_) {
+      this.interfaceEndpointClient_.close(reason);
+      this.interfaceEndpointClient_ = null;
+    }
+    this.close();
+  };
+
+  AssociatedBinding.prototype.setConnectionErrorHandler = function(callback) {
+    if (!this.isBound()) {
+      throw new Error("Cannot set connection error handler if not bound.");
+    }
+    this.interfaceEndpointClient_.setConnectionErrorHandler(callback);
+  };
+
+  AssociatedBinding.prototype.unbind = function() {
+    if (!this.isBound()) {
+      return new mojo.AssociatedInterfaceRequest(null);
+    }
+
+    var result = new mojo.AssociatedInterfaceRequest(
+        this.interfaceEndpointClient_.passHandle());
+    this.close();
+    return result;
+  };
+
+  // ---------------------------------------------------------------------------
+
+  function AssociatedBindingSet(interfaceType) {
+    mojo.BindingSet.call(this, interfaceType);
+    this.bindingType_ = AssociatedBinding;
+  }
+
+  AssociatedBindingSet.prototype = Object.create(BindingSet.prototype);
+  AssociatedBindingSet.prototype.constructor = AssociatedBindingSet;
+
+  mojo.makeRequest = makeRequest;
+  mojo.AssociatedInterfacePtrController = AssociatedInterfacePtrController;
+  mojo.AssociatedBinding = AssociatedBinding;
+  mojo.AssociatedBindingSet = AssociatedBindingSet;
+  mojo.Binding = Binding;
+  mojo.BindingSet = BindingSet;
+  mojo.InterfacePtrController = InterfacePtrController;
+})();
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  var kHostIsLittleEndian = (function () {
+    var endianArrayBuffer = new ArrayBuffer(2);
+    var endianUint8Array = new Uint8Array(endianArrayBuffer);
+    var endianUint16Array = new Uint16Array(endianArrayBuffer);
+    endianUint16Array[0] = 1;
+    return endianUint8Array[0] == 1;
+  })();
+
+  var kHighWordMultiplier = 0x100000000;
+
+  function Buffer(sizeOrArrayBuffer) {
+    if (sizeOrArrayBuffer instanceof ArrayBuffer)
+      this.arrayBuffer = sizeOrArrayBuffer;
+    else
+      this.arrayBuffer = new ArrayBuffer(sizeOrArrayBuffer);
+
+    this.dataView = new DataView(this.arrayBuffer);
+    this.next = 0;
+  }
+
+  Object.defineProperty(Buffer.prototype, "byteLength", {
+    get: function() { return this.arrayBuffer.byteLength; }
+  });
+
+  Buffer.prototype.alloc = function(size) {
+    var pointer = this.next;
+    this.next += size;
+    if (this.next > this.byteLength) {
+      var newSize = (1.5 * (this.byteLength + size)) | 0;
+      this.grow(newSize);
+    }
+    return pointer;
+  };
+
+  function copyArrayBuffer(dstArrayBuffer, srcArrayBuffer) {
+    (new Uint8Array(dstArrayBuffer)).set(new Uint8Array(srcArrayBuffer));
+  }
+
+  Buffer.prototype.grow = function(size) {
+    var newArrayBuffer = new ArrayBuffer(size);
+    copyArrayBuffer(newArrayBuffer, this.arrayBuffer);
+    this.arrayBuffer = newArrayBuffer;
+    this.dataView = new DataView(this.arrayBuffer);
+  };
+
+  Buffer.prototype.trim = function() {
+    this.arrayBuffer = this.arrayBuffer.slice(0, this.next);
+    this.dataView = new DataView(this.arrayBuffer);
+  };
+
+  Buffer.prototype.getUint8 = function(offset) {
+    return this.dataView.getUint8(offset);
+  }
+  Buffer.prototype.getUint16 = function(offset) {
+    return this.dataView.getUint16(offset, kHostIsLittleEndian);
+  }
+  Buffer.prototype.getUint32 = function(offset) {
+    return this.dataView.getUint32(offset, kHostIsLittleEndian);
+  }
+  Buffer.prototype.getUint64 = function(offset) {
+    var lo, hi;
+    if (kHostIsLittleEndian) {
+      lo = this.dataView.getUint32(offset, kHostIsLittleEndian);
+      hi = this.dataView.getUint32(offset + 4, kHostIsLittleEndian);
+    } else {
+      hi = this.dataView.getUint32(offset, kHostIsLittleEndian);
+      lo = this.dataView.getUint32(offset + 4, kHostIsLittleEndian);
+    }
+    return lo + hi * kHighWordMultiplier;
+  }
+
+  Buffer.prototype.getInt8 = function(offset) {
+    return this.dataView.getInt8(offset);
+  }
+  Buffer.prototype.getInt16 = function(offset) {
+    return this.dataView.getInt16(offset, kHostIsLittleEndian);
+  }
+  Buffer.prototype.getInt32 = function(offset) {
+    return this.dataView.getInt32(offset, kHostIsLittleEndian);
+  }
+  Buffer.prototype.getInt64 = function(offset) {
+    var lo, hi;
+    if (kHostIsLittleEndian) {
+      lo = this.dataView.getUint32(offset, kHostIsLittleEndian);
+      hi = this.dataView.getInt32(offset + 4, kHostIsLittleEndian);
+    } else {
+      hi = this.dataView.getInt32(offset, kHostIsLittleEndian);
+      lo = this.dataView.getUint32(offset + 4, kHostIsLittleEndian);
+    }
+    return lo + hi * kHighWordMultiplier;
+  }
+
+  Buffer.prototype.getFloat32 = function(offset) {
+    return this.dataView.getFloat32(offset, kHostIsLittleEndian);
+  }
+  Buffer.prototype.getFloat64 = function(offset) {
+    return this.dataView.getFloat64(offset, kHostIsLittleEndian);
+  }
+
+  Buffer.prototype.setUint8 = function(offset, value) {
+    this.dataView.setUint8(offset, value);
+  }
+  Buffer.prototype.setUint16 = function(offset, value) {
+    this.dataView.setUint16(offset, value, kHostIsLittleEndian);
+  }
+  Buffer.prototype.setUint32 = function(offset, value) {
+    this.dataView.setUint32(offset, value, kHostIsLittleEndian);
+  }
+  Buffer.prototype.setUint64 = function(offset, value) {
+    var hi = (value / kHighWordMultiplier) | 0;
+    if (kHostIsLittleEndian) {
+      this.dataView.setInt32(offset, value, kHostIsLittleEndian);
+      this.dataView.setInt32(offset + 4, hi, kHostIsLittleEndian);
+    } else {
+      this.dataView.setInt32(offset, hi, kHostIsLittleEndian);
+      this.dataView.setInt32(offset + 4, value, kHostIsLittleEndian);
+    }
+  }
+
+  Buffer.prototype.setInt8 = function(offset, value) {
+    this.dataView.setInt8(offset, value);
+  }
+  Buffer.prototype.setInt16 = function(offset, value) {
+    this.dataView.setInt16(offset, value, kHostIsLittleEndian);
+  }
+  Buffer.prototype.setInt32 = function(offset, value) {
+    this.dataView.setInt32(offset, value, kHostIsLittleEndian);
+  }
+  Buffer.prototype.setInt64 = function(offset, value) {
+    var hi = Math.floor(value / kHighWordMultiplier);
+    if (kHostIsLittleEndian) {
+      this.dataView.setInt32(offset, value, kHostIsLittleEndian);
+      this.dataView.setInt32(offset + 4, hi, kHostIsLittleEndian);
+    } else {
+      this.dataView.setInt32(offset, hi, kHostIsLittleEndian);
+      this.dataView.setInt32(offset + 4, value, kHostIsLittleEndian);
+    }
+  }
+
+  Buffer.prototype.setFloat32 = function(offset, value) {
+    this.dataView.setFloat32(offset, value, kHostIsLittleEndian);
+  }
+  Buffer.prototype.setFloat64 = function(offset, value) {
+    this.dataView.setFloat64(offset, value, kHostIsLittleEndian);
+  }
+
+  internal.Buffer = Buffer;
+})();
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  var kErrorUnsigned = "Passing negative value to unsigned";
+  var kErrorArray = "Passing non Array for array type";
+  var kErrorString = "Passing non String for string type";
+  var kErrorMap = "Passing non Map for map type";
+
+  // Memory -------------------------------------------------------------------
+
+  var kAlignment = 8;
+
+  function align(size) {
+    return size + (kAlignment - (size % kAlignment)) % kAlignment;
+  }
+
+  function isAligned(offset) {
+    return offset >= 0 && (offset % kAlignment) === 0;
+  }
+
+  // Constants ----------------------------------------------------------------
+
+  var kArrayHeaderSize = 8;
+  var kStructHeaderSize = 8;
+  var kMessageV0HeaderSize = 24;
+  var kMessageV1HeaderSize = 32;
+  var kMessageV2HeaderSize = 48;
+  var kMapStructPayloadSize = 16;
+
+  var kStructHeaderNumBytesOffset = 0;
+  var kStructHeaderVersionOffset = 4;
+
+  var kEncodedInvalidHandleValue = 0xFFFFFFFF;
+
+  // Decoder ------------------------------------------------------------------
+
+  function Decoder(buffer, handles, associatedEndpointHandles, base) {
+    this.buffer = buffer;
+    this.handles = handles;
+    this.associatedEndpointHandles = associatedEndpointHandles;
+    this.base = base;
+    this.next = base;
+  }
+
+  Decoder.prototype.align = function() {
+    this.next = align(this.next);
+  };
+
+  Decoder.prototype.skip = function(offset) {
+    this.next += offset;
+  };
+
+  Decoder.prototype.readInt8 = function() {
+    var result = this.buffer.getInt8(this.next);
+    this.next += 1;
+    return result;
+  };
+
+  Decoder.prototype.readUint8 = function() {
+    var result = this.buffer.getUint8(this.next);
+    this.next += 1;
+    return result;
+  };
+
+  Decoder.prototype.readInt16 = function() {
+    var result = this.buffer.getInt16(this.next);
+    this.next += 2;
+    return result;
+  };
+
+  Decoder.prototype.readUint16 = function() {
+    var result = this.buffer.getUint16(this.next);
+    this.next += 2;
+    return result;
+  };
+
+  Decoder.prototype.readInt32 = function() {
+    var result = this.buffer.getInt32(this.next);
+    this.next += 4;
+    return result;
+  };
+
+  Decoder.prototype.readUint32 = function() {
+    var result = this.buffer.getUint32(this.next);
+    this.next += 4;
+    return result;
+  };
+
+  Decoder.prototype.readInt64 = function() {
+    var result = this.buffer.getInt64(this.next);
+    this.next += 8;
+    return result;
+  };
+
+  Decoder.prototype.readUint64 = function() {
+    var result = this.buffer.getUint64(this.next);
+    this.next += 8;
+    return result;
+  };
+
+  Decoder.prototype.readFloat = function() {
+    var result = this.buffer.getFloat32(this.next);
+    this.next += 4;
+    return result;
+  };
+
+  Decoder.prototype.readDouble = function() {
+    var result = this.buffer.getFloat64(this.next);
+    this.next += 8;
+    return result;
+  };
+
+  Decoder.prototype.decodePointer = function() {
+    // TODO(abarth): To correctly decode a pointer, we need to know the real
+    // base address of the array buffer.
+    var offsetPointer = this.next;
+    var offset = this.readUint64();
+    if (!offset)
+      return 0;
+    return offsetPointer + offset;
+  };
+
+  Decoder.prototype.decodeAndCreateDecoder = function(pointer) {
+    return new Decoder(this.buffer, this.handles,
+        this.associatedEndpointHandles, pointer);
+  };
+
+  Decoder.prototype.decodeHandle = function() {
+    return this.handles[this.readUint32()] || null;
+  };
+
+  Decoder.prototype.decodeAssociatedEndpointHandle = function() {
+    return this.associatedEndpointHandles[this.readUint32()] || null;
+  };
+
+  Decoder.prototype.decodeString = function() {
+    var numberOfBytes = this.readUint32();
+    var numberOfElements = this.readUint32();
+    var base = this.next;
+    this.next += numberOfElements;
+    return internal.decodeUtf8String(
+        new Uint8Array(this.buffer.arrayBuffer, base, numberOfElements));
+  };
+
+  Decoder.prototype.decodeArray = function(cls) {
+    var numberOfBytes = this.readUint32();
+    var numberOfElements = this.readUint32();
+    var val = new Array(numberOfElements);
+    if (cls === PackedBool) {
+      var byte;
+      for (var i = 0; i < numberOfElements; ++i) {
+        if (i % 8 === 0)
+          byte = this.readUint8();
+        val[i] = (byte & (1 << i % 8)) ? true : false;
+      }
+    } else {
+      for (var i = 0; i < numberOfElements; ++i) {
+        val[i] = cls.decode(this);
+      }
+    }
+    return val;
+  };
+
+  Decoder.prototype.decodeStruct = function(cls) {
+    return cls.decode(this);
+  };
+
+  Decoder.prototype.decodeStructPointer = function(cls) {
+    var pointer = this.decodePointer();
+    if (!pointer) {
+      return null;
+    }
+    return cls.decode(this.decodeAndCreateDecoder(pointer));
+  };
+
+  Decoder.prototype.decodeArrayPointer = function(cls) {
+    var pointer = this.decodePointer();
+    if (!pointer) {
+      return null;
+    }
+    return this.decodeAndCreateDecoder(pointer).decodeArray(cls);
+  };
+
+  Decoder.prototype.decodeStringPointer = function() {
+    var pointer = this.decodePointer();
+    if (!pointer) {
+      return null;
+    }
+    return this.decodeAndCreateDecoder(pointer).decodeString();
+  };
+
+  Decoder.prototype.decodeMap = function(keyClass, valueClass) {
+    this.skip(4); // numberOfBytes
+    this.skip(4); // version
+    var keys = this.decodeArrayPointer(keyClass);
+    var values = this.decodeArrayPointer(valueClass);
+    var val = new Map();
+    for (var i = 0; i < keys.length; i++)
+      val.set(keys[i], values[i]);
+    return val;
+  };
+
+  Decoder.prototype.decodeMapPointer = function(keyClass, valueClass) {
+    var pointer = this.decodePointer();
+    if (!pointer) {
+      return null;
+    }
+    var decoder = this.decodeAndCreateDecoder(pointer);
+    return decoder.decodeMap(keyClass, valueClass);
+  };
+
+  // Encoder ------------------------------------------------------------------
+
+  function Encoder(buffer, handles, associatedEndpointHandles, base) {
+    this.buffer = buffer;
+    this.handles = handles;
+    this.associatedEndpointHandles = associatedEndpointHandles;
+    this.base = base;
+    this.next = base;
+  }
+
+  Encoder.prototype.align = function() {
+    this.next = align(this.next);
+  };
+
+  Encoder.prototype.skip = function(offset) {
+    this.next += offset;
+  };
+
+  Encoder.prototype.writeInt8 = function(val) {
+    this.buffer.setInt8(this.next, val);
+    this.next += 1;
+  };
+
+  Encoder.prototype.writeUint8 = function(val) {
+    if (val < 0) {
+      throw new Error(kErrorUnsigned);
+    }
+    this.buffer.setUint8(this.next, val);
+    this.next += 1;
+  };
+
+  Encoder.prototype.writeInt16 = function(val) {
+    this.buffer.setInt16(this.next, val);
+    this.next += 2;
+  };
+
+  Encoder.prototype.writeUint16 = function(val) {
+    if (val < 0) {
+      throw new Error(kErrorUnsigned);
+    }
+    this.buffer.setUint16(this.next, val);
+    this.next += 2;
+  };
+
+  Encoder.prototype.writeInt32 = function(val) {
+    this.buffer.setInt32(this.next, val);
+    this.next += 4;
+  };
+
+  Encoder.prototype.writeUint32 = function(val) {
+    if (val < 0) {
+      throw new Error(kErrorUnsigned);
+    }
+    this.buffer.setUint32(this.next, val);
+    this.next += 4;
+  };
+
+  Encoder.prototype.writeInt64 = function(val) {
+    this.buffer.setInt64(this.next, val);
+    this.next += 8;
+  };
+
+  Encoder.prototype.writeUint64 = function(val) {
+    if (val < 0) {
+      throw new Error(kErrorUnsigned);
+    }
+    this.buffer.setUint64(this.next, val);
+    this.next += 8;
+  };
+
+  Encoder.prototype.writeFloat = function(val) {
+    this.buffer.setFloat32(this.next, val);
+    this.next += 4;
+  };
+
+  Encoder.prototype.writeDouble = function(val) {
+    this.buffer.setFloat64(this.next, val);
+    this.next += 8;
+  };
+
+  Encoder.prototype.encodePointer = function(pointer) {
+    if (!pointer)
+      return this.writeUint64(0);
+    // TODO(abarth): To correctly encode a pointer, we need to know the real
+    // base address of the array buffer.
+    var offset = pointer - this.next;
+    this.writeUint64(offset);
+  };
+
+  Encoder.prototype.createAndEncodeEncoder = function(size) {
+    var pointer = this.buffer.alloc(align(size));
+    this.encodePointer(pointer);
+    return new Encoder(this.buffer, this.handles,
+        this.associatedEndpointHandles, pointer);
+  };
+
+  Encoder.prototype.encodeHandle = function(handle) {
+    if (handle) {
+      this.handles.push(handle);
+      this.writeUint32(this.handles.length - 1);
+    } else {
+      this.writeUint32(kEncodedInvalidHandleValue);
+    }
+  };
+
+  Encoder.prototype.encodeAssociatedEndpointHandle = function(endpointHandle) {
+    if (endpointHandle) {
+      this.associatedEndpointHandles.push(endpointHandle);
+      this.writeUint32(this.associatedEndpointHandles.length - 1);
+    } else {
+      this.writeUint32(kEncodedInvalidHandleValue);
+    }
+  };
+
+  Encoder.prototype.encodeString = function(val) {
+    var base = this.next + kArrayHeaderSize;
+    var numberOfElements = internal.encodeUtf8String(
+        val, new Uint8Array(this.buffer.arrayBuffer, base));
+    var numberOfBytes = kArrayHeaderSize + numberOfElements;
+    this.writeUint32(numberOfBytes);
+    this.writeUint32(numberOfElements);
+    this.next += numberOfElements;
+  };
+
+  Encoder.prototype.encodeArray =
+      function(cls, val, numberOfElements, encodedSize) {
+    if (numberOfElements === undefined)
+      numberOfElements = val.length;
+    if (encodedSize === undefined)
+      encodedSize = kArrayHeaderSize + cls.encodedSize * numberOfElements;
+
+    this.writeUint32(encodedSize);
+    this.writeUint32(numberOfElements);
+
+    if (cls === PackedBool) {
+      var byte = 0;
+      for (i = 0; i < numberOfElements; ++i) {
+        if (val[i])
+          byte |= (1 << i % 8);
+        if (i % 8 === 7 || i == numberOfElements - 1) {
+          Uint8.encode(this, byte);
+          byte = 0;
+        }
+      }
+    } else {
+      for (var i = 0; i < numberOfElements; ++i)
+        cls.encode(this, val[i]);
+    }
+  };
+
+  Encoder.prototype.encodeStruct = function(cls, val) {
+    return cls.encode(this, val);
+  };
+
+  Encoder.prototype.encodeStructPointer = function(cls, val) {
+    if (val == null) {
+      // Also handles undefined, since undefined == null.
+      this.encodePointer(val);
+      return;
+    }
+    var encoder = this.createAndEncodeEncoder(cls.encodedSize);
+    cls.encode(encoder, val);
+  };
+
+  Encoder.prototype.encodeArrayPointer = function(cls, val) {
+    if (val == null) {
+      // Also handles undefined, since undefined == null.
+      this.encodePointer(val);
+      return;
+    }
+
+    var numberOfElements = val.length;
+    if (!Number.isSafeInteger(numberOfElements) || numberOfElements < 0)
+      throw new Error(kErrorArray);
+
+    var encodedSize = kArrayHeaderSize + ((cls === PackedBool) ?
+        Math.ceil(numberOfElements / 8) : cls.encodedSize * numberOfElements);
+    var encoder = this.createAndEncodeEncoder(encodedSize);
+    encoder.encodeArray(cls, val, numberOfElements, encodedSize);
+  };
+
+  Encoder.prototype.encodeStringPointer = function(val) {
+    if (val == null) {
+      // Also handles undefined, since undefined == null.
+      this.encodePointer(val);
+      return;
+    }
+    // Only accepts string primivites, not String Objects like new String("foo")
+    if (typeof(val) !== "string") {
+      throw new Error(kErrorString);
+    }
+    var encodedSize = kArrayHeaderSize + internal.utf8Length(val);
+    var encoder = this.createAndEncodeEncoder(encodedSize);
+    encoder.encodeString(val);
+  };
+
+  Encoder.prototype.encodeMap = function(keyClass, valueClass, val) {
+    var keys = new Array(val.size);
+    var values = new Array(val.size);
+    var i = 0;
+    val.forEach(function(value, key) {
+      values[i] = value;
+      keys[i++] = key;
+    });
+    this.writeUint32(kStructHeaderSize + kMapStructPayloadSize);
+    this.writeUint32(0);  // version
+    this.encodeArrayPointer(keyClass, keys);
+    this.encodeArrayPointer(valueClass, values);
+  }
+
+  Encoder.prototype.encodeMapPointer = function(keyClass, valueClass, val) {
+    if (val == null) {
+      // Also handles undefined, since undefined == null.
+      this.encodePointer(val);
+      return;
+    }
+    if (!(val instanceof Map)) {
+      throw new Error(kErrorMap);
+    }
+    var encodedSize = kStructHeaderSize + kMapStructPayloadSize;
+    var encoder = this.createAndEncodeEncoder(encodedSize);
+    encoder.encodeMap(keyClass, valueClass, val);
+  };
+
+  // Message ------------------------------------------------------------------
+
+  var kMessageInterfaceIdOffset = kStructHeaderSize;
+  var kMessageNameOffset = kMessageInterfaceIdOffset + 4;
+  var kMessageFlagsOffset = kMessageNameOffset + 4;
+  var kMessageRequestIDOffset = kMessageFlagsOffset + 8;
+  var kMessagePayloadInterfaceIdsPointerOffset = kMessageV2HeaderSize - 8;
+
+  var kMessageExpectsResponse = 1 << 0;
+  var kMessageIsResponse      = 1 << 1;
+
+  function Message(buffer, handles, associatedEndpointHandles) {
+    if (associatedEndpointHandles === undefined) {
+      associatedEndpointHandles = [];
+    }
+
+    this.buffer = buffer;
+    this.handles = handles;
+    this.associatedEndpointHandles = associatedEndpointHandles;
+  }
+
+  Message.prototype.getHeaderNumBytes = function() {
+    return this.buffer.getUint32(kStructHeaderNumBytesOffset);
+  };
+
+  Message.prototype.getHeaderVersion = function() {
+    return this.buffer.getUint32(kStructHeaderVersionOffset);
+  };
+
+  Message.prototype.getName = function() {
+    return this.buffer.getUint32(kMessageNameOffset);
+  };
+
+  Message.prototype.getFlags = function() {
+    return this.buffer.getUint32(kMessageFlagsOffset);
+  };
+
+  Message.prototype.getInterfaceId = function() {
+    return this.buffer.getUint32(kMessageInterfaceIdOffset);
+  };
+
+  Message.prototype.getPayloadInterfaceIds = function() {
+    if (this.getHeaderVersion() < 2) {
+      return null;
+    }
+
+    var decoder = new Decoder(this.buffer, this.handles,
+        this.associatedEndpointHandles,
+        kMessagePayloadInterfaceIdsPointerOffset);
+    var payloadInterfaceIds = decoder.decodeArrayPointer(Uint32);
+    return payloadInterfaceIds;
+  };
+
+  Message.prototype.isResponse = function() {
+    return (this.getFlags() & kMessageIsResponse) != 0;
+  };
+
+  Message.prototype.expectsResponse = function() {
+    return (this.getFlags() & kMessageExpectsResponse) != 0;
+  };
+
+  Message.prototype.setRequestID = function(requestID) {
+    // TODO(darin): Verify that space was reserved for this field!
+    this.buffer.setUint64(kMessageRequestIDOffset, requestID);
+  };
+
+  Message.prototype.setInterfaceId = function(interfaceId) {
+    this.buffer.setUint32(kMessageInterfaceIdOffset, interfaceId);
+  };
+
+  Message.prototype.setPayloadInterfaceIds_ = function(payloadInterfaceIds) {
+    if (this.getHeaderVersion() < 2) {
+      throw new Error(
+          "Version of message does not support payload interface ids");
+    }
+
+    var decoder = new Decoder(this.buffer, this.handles,
+        this.associatedEndpointHandles,
+        kMessagePayloadInterfaceIdsPointerOffset);
+    var payloadInterfaceIdsOffset = decoder.decodePointer();
+    var encoder = new Encoder(this.buffer, this.handles,
+        this.associatedEndpointHandles,
+        payloadInterfaceIdsOffset);
+    encoder.encodeArray(Uint32, payloadInterfaceIds);
+  };
+
+  Message.prototype.serializeAssociatedEndpointHandles = function(
+      associatedGroupController) {
+    if (this.associatedEndpointHandles.length > 0) {
+      if (this.getHeaderVersion() < 2) {
+        throw new Error(
+            "Version of message does not support associated endpoint handles");
+      }
+
+      var data = [];
+      for (var i = 0; i < this.associatedEndpointHandles.length; i++) {
+        var handle = this.associatedEndpointHandles[i];
+        data.push(associatedGroupController.associateInterface(handle));
+      }
+      this.associatedEndpointHandles = [];
+      this.setPayloadInterfaceIds_(data);
+    }
+  };
+
+  Message.prototype.deserializeAssociatedEndpointHandles = function(
+      associatedGroupController) {
+    if (this.getHeaderVersion() < 2) {
+      return true;
+    }
+
+    this.associatedEndpointHandles = [];
+    var ids = this.getPayloadInterfaceIds();
+
+    var result = true;
+    for (var i = 0; i < ids.length; i++) {
+      var handle = associatedGroupController.createLocalEndpointHandle(ids[i]);
+      if (internal.isValidInterfaceId(ids[i]) && !handle.isValid()) {
+        // |ids[i]| itself is valid but handle creation failed. In that case,
+        // mark deserialization as failed but continue to deserialize the
+        // rest of handles.
+        result = false;
+      }
+      this.associatedEndpointHandles.push(handle);
+      ids[i] = internal.kInvalidInterfaceId;
+    }
+
+    this.setPayloadInterfaceIds_(ids);
+    return result;
+  };
+
+
+  // MessageV0Builder ---------------------------------------------------------
+
+  function MessageV0Builder(messageName, payloadSize) {
+    // Currently, we don't compute the payload size correctly ahead of time.
+    // Instead, we resize the buffer at the end.
+    var numberOfBytes = kMessageV0HeaderSize + payloadSize;
+    this.buffer = new internal.Buffer(numberOfBytes);
+    this.handles = [];
+    var encoder = this.createEncoder(kMessageV0HeaderSize);
+    encoder.writeUint32(kMessageV0HeaderSize);
+    encoder.writeUint32(0);  // version.
+    encoder.writeUint32(0);  // interface ID.
+    encoder.writeUint32(messageName);
+    encoder.writeUint32(0);  // flags.
+    encoder.writeUint32(0);  // padding.
+  }
+
+  MessageV0Builder.prototype.createEncoder = function(size) {
+    var pointer = this.buffer.alloc(size);
+    return new Encoder(this.buffer, this.handles, [], pointer);
+  };
+
+  MessageV0Builder.prototype.encodeStruct = function(cls, val) {
+    cls.encode(this.createEncoder(cls.encodedSize), val);
+  };
+
+  MessageV0Builder.prototype.finish = function() {
+    // TODO(abarth): Rather than resizing the buffer at the end, we could
+    // compute the size we need ahead of time, like we do in C++.
+    this.buffer.trim();
+    var message = new Message(this.buffer, this.handles);
+    this.buffer = null;
+    this.handles = null;
+    this.encoder = null;
+    return message;
+  };
+
+  // MessageV1Builder -----------------------------------------------
+
+  function MessageV1Builder(messageName, payloadSize, flags,
+                                       requestID) {
+    // Currently, we don't compute the payload size correctly ahead of time.
+    // Instead, we resize the buffer at the end.
+    var numberOfBytes = kMessageV1HeaderSize + payloadSize;
+    this.buffer = new internal.Buffer(numberOfBytes);
+    this.handles = [];
+    var encoder = this.createEncoder(kMessageV1HeaderSize);
+    encoder.writeUint32(kMessageV1HeaderSize);
+    encoder.writeUint32(1);  // version.
+    encoder.writeUint32(0);  // interface ID.
+    encoder.writeUint32(messageName);
+    encoder.writeUint32(flags);
+    encoder.writeUint32(0);  // padding.
+    encoder.writeUint64(requestID);
+  }
+
+  MessageV1Builder.prototype =
+      Object.create(MessageV0Builder.prototype);
+
+  MessageV1Builder.prototype.constructor =
+      MessageV1Builder;
+
+  // MessageV2 -----------------------------------------------
+
+  function MessageV2Builder(messageName, payloadSize, flags, requestID) {
+    // Currently, we don't compute the payload size correctly ahead of time.
+    // Instead, we resize the buffer at the end.
+    var numberOfBytes = kMessageV2HeaderSize + payloadSize;
+    this.buffer = new internal.Buffer(numberOfBytes);
+    this.handles = [];
+
+    this.payload = null;
+    this.associatedEndpointHandles = [];
+
+    this.encoder = this.createEncoder(kMessageV2HeaderSize);
+    this.encoder.writeUint32(kMessageV2HeaderSize);
+    this.encoder.writeUint32(2);  // version.
+    // Gets set to an appropriate interfaceId for the endpoint by the Router.
+    this.encoder.writeUint32(0);  // interface ID.
+    this.encoder.writeUint32(messageName);
+    this.encoder.writeUint32(flags);
+    this.encoder.writeUint32(0);  // padding.
+    this.encoder.writeUint64(requestID);
+  }
+
+  MessageV2Builder.prototype.createEncoder = function(size) {
+    var pointer = this.buffer.alloc(size);
+    return new Encoder(this.buffer, this.handles,
+        this.associatedEndpointHandles, pointer);
+  };
+
+  MessageV2Builder.prototype.setPayload = function(cls, val) {
+    this.payload = {cls: cls, val: val};
+  };
+
+  MessageV2Builder.prototype.finish = function() {
+    if (!this.payload) {
+      throw new Error("Payload needs to be set before calling finish");
+    }
+
+    this.encoder.encodeStructPointer(this.payload.cls, this.payload.val);
+    this.encoder.encodeArrayPointer(Uint32,
+        new Array(this.associatedEndpointHandles.length));
+
+    this.buffer.trim();
+    var message = new Message(this.buffer, this.handles,
+        this.associatedEndpointHandles);
+    this.buffer = null;
+    this.handles = null;
+    this.encoder = null;
+    this.payload = null;
+    this.associatedEndpointHandles = null;
+
+    return message;
+  };
+
+  // MessageReader ------------------------------------------------------------
+
+  function MessageReader(message) {
+    this.decoder = new Decoder(message.buffer, message.handles,
+        message.associatedEndpointHandles, 0);
+    var messageHeaderSize = this.decoder.readUint32();
+    this.payloadSize = message.buffer.byteLength - messageHeaderSize;
+    var version = this.decoder.readUint32();
+    var interface_id = this.decoder.readUint32();
+    this.messageName = this.decoder.readUint32();
+    this.flags = this.decoder.readUint32();
+    // Skip the padding.
+    this.decoder.skip(4);
+    if (version >= 1)
+      this.requestID = this.decoder.readUint64();
+    this.decoder.skip(messageHeaderSize - this.decoder.next);
+  }
+
+  MessageReader.prototype.decodeStruct = function(cls) {
+    return cls.decode(this.decoder);
+  };
+
+  // Built-in types -----------------------------------------------------------
+
+  // This type is only used with ArrayOf(PackedBool).
+  function PackedBool() {
+  }
+
+  function Int8() {
+  }
+
+  Int8.encodedSize = 1;
+
+  Int8.decode = function(decoder) {
+    return decoder.readInt8();
+  };
+
+  Int8.encode = function(encoder, val) {
+    encoder.writeInt8(val);
+  };
+
+  Uint8.encode = function(encoder, val) {
+    encoder.writeUint8(val);
+  };
+
+  function Uint8() {
+  }
+
+  Uint8.encodedSize = 1;
+
+  Uint8.decode = function(decoder) {
+    return decoder.readUint8();
+  };
+
+  Uint8.encode = function(encoder, val) {
+    encoder.writeUint8(val);
+  };
+
+  function Int16() {
+  }
+
+  Int16.encodedSize = 2;
+
+  Int16.decode = function(decoder) {
+    return decoder.readInt16();
+  };
+
+  Int16.encode = function(encoder, val) {
+    encoder.writeInt16(val);
+  };
+
+  function Uint16() {
+  }
+
+  Uint16.encodedSize = 2;
+
+  Uint16.decode = function(decoder) {
+    return decoder.readUint16();
+  };
+
+  Uint16.encode = function(encoder, val) {
+    encoder.writeUint16(val);
+  };
+
+  function Int32() {
+  }
+
+  Int32.encodedSize = 4;
+
+  Int32.decode = function(decoder) {
+    return decoder.readInt32();
+  };
+
+  Int32.encode = function(encoder, val) {
+    encoder.writeInt32(val);
+  };
+
+  function Uint32() {
+  }
+
+  Uint32.encodedSize = 4;
+
+  Uint32.decode = function(decoder) {
+    return decoder.readUint32();
+  };
+
+  Uint32.encode = function(encoder, val) {
+    encoder.writeUint32(val);
+  };
+
+  function Int64() {
+  }
+
+  Int64.encodedSize = 8;
+
+  Int64.decode = function(decoder) {
+    return decoder.readInt64();
+  };
+
+  Int64.encode = function(encoder, val) {
+    encoder.writeInt64(val);
+  };
+
+  function Uint64() {
+  }
+
+  Uint64.encodedSize = 8;
+
+  Uint64.decode = function(decoder) {
+    return decoder.readUint64();
+  };
+
+  Uint64.encode = function(encoder, val) {
+    encoder.writeUint64(val);
+  };
+
+  function String() {
+  };
+
+  String.encodedSize = 8;
+
+  String.decode = function(decoder) {
+    return decoder.decodeStringPointer();
+  };
+
+  String.encode = function(encoder, val) {
+    encoder.encodeStringPointer(val);
+  };
+
+  function NullableString() {
+  }
+
+  NullableString.encodedSize = String.encodedSize;
+
+  NullableString.decode = String.decode;
+
+  NullableString.encode = String.encode;
+
+  function Float() {
+  }
+
+  Float.encodedSize = 4;
+
+  Float.decode = function(decoder) {
+    return decoder.readFloat();
+  };
+
+  Float.encode = function(encoder, val) {
+    encoder.writeFloat(val);
+  };
+
+  function Double() {
+  }
+
+  Double.encodedSize = 8;
+
+  Double.decode = function(decoder) {
+    return decoder.readDouble();
+  };
+
+  Double.encode = function(encoder, val) {
+    encoder.writeDouble(val);
+  };
+
+  function Enum(cls) {
+    this.cls = cls;
+  }
+
+  Enum.prototype.encodedSize = 4;
+
+  Enum.prototype.decode = function(decoder) {
+    return decoder.readInt32();
+  };
+
+  Enum.prototype.encode = function(encoder, val) {
+    encoder.writeInt32(val);
+  };
+
+  function PointerTo(cls) {
+    this.cls = cls;
+  }
+
+  PointerTo.prototype.encodedSize = 8;
+
+  PointerTo.prototype.decode = function(decoder) {
+    var pointer = decoder.decodePointer();
+    if (!pointer) {
+      return null;
+    }
+    return this.cls.decode(decoder.decodeAndCreateDecoder(pointer));
+  };
+
+  PointerTo.prototype.encode = function(encoder, val) {
+    if (!val) {
+      encoder.encodePointer(val);
+      return;
+    }
+    var objectEncoder = encoder.createAndEncodeEncoder(this.cls.encodedSize);
+    this.cls.encode(objectEncoder, val);
+  };
+
+  function NullablePointerTo(cls) {
+    PointerTo.call(this, cls);
+  }
+
+  NullablePointerTo.prototype = Object.create(PointerTo.prototype);
+
+  function ArrayOf(cls, length) {
+    this.cls = cls;
+    this.length = length || 0;
+  }
+
+  ArrayOf.prototype.encodedSize = 8;
+
+  ArrayOf.prototype.dimensions = function() {
+    return [this.length].concat(
+      (this.cls instanceof ArrayOf) ? this.cls.dimensions() : []);
+  }
+
+  ArrayOf.prototype.decode = function(decoder) {
+    return decoder.decodeArrayPointer(this.cls);
+  };
+
+  ArrayOf.prototype.encode = function(encoder, val) {
+    encoder.encodeArrayPointer(this.cls, val);
+  };
+
+  function NullableArrayOf(cls) {
+    ArrayOf.call(this, cls);
+  }
+
+  NullableArrayOf.prototype = Object.create(ArrayOf.prototype);
+
+  function Handle() {
+  }
+
+  Handle.encodedSize = 4;
+
+  Handle.decode = function(decoder) {
+    return decoder.decodeHandle();
+  };
+
+  Handle.encode = function(encoder, val) {
+    encoder.encodeHandle(val);
+  };
+
+  function NullableHandle() {
+  }
+
+  NullableHandle.encodedSize = Handle.encodedSize;
+
+  NullableHandle.decode = Handle.decode;
+
+  NullableHandle.encode = Handle.encode;
+
+  function Interface(cls) {
+    this.cls = cls;
+  }
+
+  Interface.prototype.encodedSize = 8;
+
+  Interface.prototype.decode = function(decoder) {
+    var interfacePtrInfo = new mojo.InterfacePtrInfo(
+        decoder.decodeHandle(), decoder.readUint32());
+    var interfacePtr = new this.cls();
+    interfacePtr.ptr.bind(interfacePtrInfo);
+    return interfacePtr;
+  };
+
+  Interface.prototype.encode = function(encoder, val) {
+    var interfacePtrInfo =
+        val ? val.ptr.passInterface() : new mojo.InterfacePtrInfo(null, 0);
+    encoder.encodeHandle(interfacePtrInfo.handle);
+    encoder.writeUint32(interfacePtrInfo.version);
+  };
+
+  function NullableInterface(cls) {
+    Interface.call(this, cls);
+  }
+
+  NullableInterface.prototype = Object.create(Interface.prototype);
+
+  function AssociatedInterfacePtrInfo() {
+  }
+
+  AssociatedInterfacePtrInfo.prototype.encodedSize = 8;
+
+  AssociatedInterfacePtrInfo.decode = function(decoder) {
+    return new mojo.AssociatedInterfacePtrInfo(
+      decoder.decodeAssociatedEndpointHandle(), decoder.readUint32());
+  };
+
+  AssociatedInterfacePtrInfo.encode = function(encoder, val) {
+    var associatedinterfacePtrInfo =
+        val ? val : new mojo.AssociatedInterfacePtrInfo(null, 0);
+    encoder.encodeAssociatedEndpointHandle(
+        associatedinterfacePtrInfo.interfaceEndpointHandle);
+    encoder.writeUint32(associatedinterfacePtrInfo.version);
+  };
+
+  function NullableAssociatedInterfacePtrInfo() {
+  }
+
+  NullableAssociatedInterfacePtrInfo.encodedSize =
+      AssociatedInterfacePtrInfo.encodedSize;
+
+  NullableAssociatedInterfacePtrInfo.decode =
+      AssociatedInterfacePtrInfo.decode;
+
+  NullableAssociatedInterfacePtrInfo.encode =
+      AssociatedInterfacePtrInfo.encode;
+
+  function InterfaceRequest() {
+  }
+
+  InterfaceRequest.encodedSize = 4;
+
+  InterfaceRequest.decode = function(decoder) {
+    return new mojo.InterfaceRequest(decoder.decodeHandle());
+  };
+
+  InterfaceRequest.encode = function(encoder, val) {
+    encoder.encodeHandle(val ? val.handle : null);
+  };
+
+  function NullableInterfaceRequest() {
+  }
+
+  NullableInterfaceRequest.encodedSize = InterfaceRequest.encodedSize;
+
+  NullableInterfaceRequest.decode = InterfaceRequest.decode;
+
+  NullableInterfaceRequest.encode = InterfaceRequest.encode;
+
+  function AssociatedInterfaceRequest() {
+  }
+
+  AssociatedInterfaceRequest.decode = function(decoder) {
+    var handle = decoder.decodeAssociatedEndpointHandle();
+    return new mojo.AssociatedInterfaceRequest(handle);
+  };
+
+  AssociatedInterfaceRequest.encode = function(encoder, val) {
+    encoder.encodeAssociatedEndpointHandle(
+        val ? val.interfaceEndpointHandle : null);
+  };
+
+  AssociatedInterfaceRequest.encodedSize = 4;
+
+  function NullableAssociatedInterfaceRequest() {
+  }
+
+  NullableAssociatedInterfaceRequest.encodedSize =
+      AssociatedInterfaceRequest.encodedSize;
+
+  NullableAssociatedInterfaceRequest.decode =
+      AssociatedInterfaceRequest.decode;
+
+  NullableAssociatedInterfaceRequest.encode =
+      AssociatedInterfaceRequest.encode;
+
+  function MapOf(keyClass, valueClass) {
+    this.keyClass = keyClass;
+    this.valueClass = valueClass;
+  }
+
+  MapOf.prototype.encodedSize = 8;
+
+  MapOf.prototype.decode = function(decoder) {
+    return decoder.decodeMapPointer(this.keyClass, this.valueClass);
+  };
+
+  MapOf.prototype.encode = function(encoder, val) {
+    encoder.encodeMapPointer(this.keyClass, this.valueClass, val);
+  };
+
+  function NullableMapOf(keyClass, valueClass) {
+    MapOf.call(this, keyClass, valueClass);
+  }
+
+  NullableMapOf.prototype = Object.create(MapOf.prototype);
+
+  internal.align = align;
+  internal.isAligned = isAligned;
+  internal.Message = Message;
+  internal.MessageV0Builder = MessageV0Builder;
+  internal.MessageV1Builder = MessageV1Builder;
+  internal.MessageV2Builder = MessageV2Builder;
+  internal.MessageReader = MessageReader;
+  internal.kArrayHeaderSize = kArrayHeaderSize;
+  internal.kMapStructPayloadSize = kMapStructPayloadSize;
+  internal.kStructHeaderSize = kStructHeaderSize;
+  internal.kEncodedInvalidHandleValue = kEncodedInvalidHandleValue;
+  internal.kMessageV0HeaderSize = kMessageV0HeaderSize;
+  internal.kMessageV1HeaderSize = kMessageV1HeaderSize;
+  internal.kMessageV2HeaderSize = kMessageV2HeaderSize;
+  internal.kMessagePayloadInterfaceIdsPointerOffset =
+      kMessagePayloadInterfaceIdsPointerOffset;
+  internal.kMessageExpectsResponse = kMessageExpectsResponse;
+  internal.kMessageIsResponse = kMessageIsResponse;
+  internal.Int8 = Int8;
+  internal.Uint8 = Uint8;
+  internal.Int16 = Int16;
+  internal.Uint16 = Uint16;
+  internal.Int32 = Int32;
+  internal.Uint32 = Uint32;
+  internal.Int64 = Int64;
+  internal.Uint64 = Uint64;
+  internal.Float = Float;
+  internal.Double = Double;
+  internal.String = String;
+  internal.Enum = Enum;
+  internal.NullableString = NullableString;
+  internal.PointerTo = PointerTo;
+  internal.NullablePointerTo = NullablePointerTo;
+  internal.ArrayOf = ArrayOf;
+  internal.NullableArrayOf = NullableArrayOf;
+  internal.PackedBool = PackedBool;
+  internal.Handle = Handle;
+  internal.NullableHandle = NullableHandle;
+  internal.Interface = Interface;
+  internal.NullableInterface = NullableInterface;
+  internal.InterfaceRequest = InterfaceRequest;
+  internal.NullableInterfaceRequest = NullableInterfaceRequest;
+  internal.AssociatedInterfacePtrInfo = AssociatedInterfacePtrInfo;
+  internal.NullableAssociatedInterfacePtrInfo =
+      NullableAssociatedInterfacePtrInfo;
+  internal.AssociatedInterfaceRequest = AssociatedInterfaceRequest;
+  internal.NullableAssociatedInterfaceRequest =
+      NullableAssociatedInterfaceRequest;
+  internal.MapOf = MapOf;
+  internal.NullableMapOf = NullableMapOf;
+})();
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  function Connector(handle) {
+    if (!(handle instanceof MojoHandle))
+      throw new Error("Connector: not a handle " + handle);
+    this.handle_ = handle;
+    this.dropWrites_ = false;
+    this.error_ = false;
+    this.incomingReceiver_ = null;
+    this.readWatcher_ = null;
+    this.errorHandler_ = null;
+    this.paused_ = false;
+
+    this.waitToReadMore();
+  }
+
+  Connector.prototype.close = function() {
+    this.cancelWait();
+    if (this.handle_ != null) {
+      this.handle_.close();
+      this.handle_ = null;
+    }
+  };
+
+  Connector.prototype.pauseIncomingMethodCallProcessing = function() {
+    if (this.paused_) {
+      return;
+    }
+    this.paused_= true;
+    this.cancelWait();
+  };
+
+  Connector.prototype.resumeIncomingMethodCallProcessing = function() {
+    if (!this.paused_) {
+      return;
+    }
+    this.paused_= false;
+    this.waitToReadMore();
+  };
+
+  Connector.prototype.accept = function(message) {
+    if (this.error_)
+      return false;
+
+    if (this.dropWrites_)
+      return true;
+
+    var result = this.handle_.writeMessage(
+        new Uint8Array(message.buffer.arrayBuffer), message.handles);
+    switch (result) {
+      case Mojo.RESULT_OK:
+        // The handles were successfully transferred, so we don't own them
+        // anymore.
+        message.handles = [];
+        break;
+      case Mojo.RESULT_FAILED_PRECONDITION:
+        // There's no point in continuing to write to this pipe since the other
+        // end is gone. Avoid writing any future messages. Hide write failures
+        // from the caller since we'd like them to continue consuming any
+        // backlog of incoming messages before regarding the message pipe as
+        // closed.
+        this.dropWrites_ = true;
+        break;
+      default:
+        // This particular write was rejected, presumably because of bad input.
+        // The pipe is not necessarily in a bad state.
+        return false;
+    }
+    return true;
+  };
+
+  Connector.prototype.setIncomingReceiver = function(receiver) {
+    this.incomingReceiver_ = receiver;
+  };
+
+  Connector.prototype.setErrorHandler = function(handler) {
+    this.errorHandler_ = handler;
+  };
+
+  Connector.prototype.readMore_ = function(result) {
+    for (;;) {
+      if (this.paused_) {
+        return;
+      }
+
+      var read = this.handle_.readMessage();
+      if (this.handle_ == null) // The connector has been closed.
+        return;
+      if (read.result == Mojo.RESULT_SHOULD_WAIT)
+        return;
+      if (read.result != Mojo.RESULT_OK) {
+        this.handleError(read.result !== Mojo.RESULT_FAILED_PRECONDITION,
+            false);
+        return;
+      }
+      var messageBuffer = new internal.Buffer(read.buffer);
+      var message = new internal.Message(messageBuffer, read.handles);
+      var receiverResult = this.incomingReceiver_ &&
+          this.incomingReceiver_.accept(message);
+
+      // Handle invalid incoming message.
+      if (!internal.isTestingMode() && !receiverResult) {
+        // TODO(yzshen): Consider notifying the embedder.
+        this.handleError(true, false);
+      }
+    }
+  };
+
+  Connector.prototype.cancelWait = function() {
+    if (this.readWatcher_) {
+      this.readWatcher_.cancel();
+      this.readWatcher_ = null;
+    }
+  };
+
+  Connector.prototype.waitToReadMore = function() {
+    if (this.handle_) {
+      this.readWatcher_ = this.handle_.watch({readable: true},
+                                             this.readMore_.bind(this));
+    }
+  };
+
+  Connector.prototype.handleError = function(forcePipeReset,
+                                             forceAsyncHandler) {
+    if (this.error_ || this.handle_ === null) {
+      return;
+    }
+
+    if (this.paused_) {
+      // Enforce calling the error handler asynchronously if the user has
+      // paused receiving messages. We need to wait until the user starts
+      // receiving messages again.
+      forceAsyncHandler = true;
+    }
+
+    if (!forcePipeReset && forceAsyncHandler) {
+      forcePipeReset = true;
+    }
+
+    this.cancelWait();
+    if (forcePipeReset) {
+      this.handle_.close();
+      var dummyPipe = Mojo.createMessagePipe();
+      this.handle_ = dummyPipe.handle0;
+    }
+
+    if (forceAsyncHandler) {
+      if (!this.paused_) {
+        this.waitToReadMore();
+      }
+    } else {
+      this.error_ = true;
+      if (this.errorHandler_) {
+        this.errorHandler_.onError();
+      }
+    }
+  };
+
+  internal.Connector = Connector;
+})();
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  // Constants ----------------------------------------------------------------
+  var kInterfaceIdNamespaceMask = 0x80000000;
+  var kMasterInterfaceId = 0x00000000;
+  var kInvalidInterfaceId = 0xFFFFFFFF;
+
+  // ---------------------------------------------------------------------------
+
+  function InterfacePtrInfo(handle, version) {
+    this.handle = handle;
+    this.version = version;
+  }
+
+  InterfacePtrInfo.prototype.isValid = function() {
+    return this.handle instanceof MojoHandle;
+  };
+
+  InterfacePtrInfo.prototype.close = function() {
+    if (!this.isValid())
+      return;
+
+    this.handle.close();
+    this.handle = null;
+    this.version = 0;
+  };
+
+  function AssociatedInterfacePtrInfo(interfaceEndpointHandle, version) {
+    this.interfaceEndpointHandle = interfaceEndpointHandle;
+    this.version = version;
+  }
+
+  AssociatedInterfacePtrInfo.prototype.isValid = function() {
+    return this.interfaceEndpointHandle.isValid();
+  };
+
+  // ---------------------------------------------------------------------------
+
+  function InterfaceRequest(handle) {
+    this.handle = handle;
+  }
+
+  InterfaceRequest.prototype.isValid = function() {
+    return this.handle instanceof MojoHandle;
+  };
+
+  InterfaceRequest.prototype.close = function() {
+    if (!this.isValid())
+      return;
+
+    this.handle.close();
+    this.handle = null;
+  };
+
+  function AssociatedInterfaceRequest(interfaceEndpointHandle) {
+    this.interfaceEndpointHandle = interfaceEndpointHandle;
+  }
+
+  AssociatedInterfaceRequest.prototype.isValid = function() {
+    return this.interfaceEndpointHandle.isValid();
+  };
+
+  AssociatedInterfaceRequest.prototype.resetWithReason = function(reason) {
+    this.interfaceEndpointHandle.reset(reason);
+  };
+
+  function isMasterInterfaceId(interfaceId) {
+    return interfaceId === kMasterInterfaceId;
+  }
+
+  function isValidInterfaceId(interfaceId) {
+    return interfaceId !== kInvalidInterfaceId;
+  }
+
+  mojo.InterfacePtrInfo = InterfacePtrInfo;
+  mojo.InterfaceRequest = InterfaceRequest;
+  mojo.AssociatedInterfacePtrInfo = AssociatedInterfacePtrInfo;
+  mojo.AssociatedInterfaceRequest = AssociatedInterfaceRequest;
+  internal.isMasterInterfaceId = isMasterInterfaceId;
+  internal.isValidInterfaceId = isValidInterfaceId;
+  internal.kInvalidInterfaceId = kInvalidInterfaceId;
+  internal.kMasterInterfaceId = kMasterInterfaceId;
+  internal.kInterfaceIdNamespaceMask = kInterfaceIdNamespaceMask;
+})();
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  function validateControlRequestWithResponse(message) {
+    var messageValidator = new internal.Validator(message);
+    var error = messageValidator.validateMessageIsRequestExpectingResponse();
+    if (error !== internal.validationError.NONE) {
+      throw error;
+    }
+
+    if (message.getName() != mojo.interfaceControl2.kRunMessageId) {
+      throw new Error("Control message name is not kRunMessageId");
+    }
+
+    // Validate payload.
+    error = mojo.interfaceControl2.RunMessageParams.validate(messageValidator,
+        message.getHeaderNumBytes());
+    if (error != internal.validationError.NONE) {
+      throw error;
+    }
+  }
+
+  function validateControlRequestWithoutResponse(message) {
+    var messageValidator = new internal.Validator(message);
+    var error = messageValidator.validateMessageIsRequestWithoutResponse();
+    if (error != internal.validationError.NONE) {
+      throw error;
+    }
+
+    if (message.getName() != mojo.interfaceControl2.kRunOrClosePipeMessageId) {
+      throw new Error("Control message name is not kRunOrClosePipeMessageId");
+    }
+
+    // Validate payload.
+    error = mojo.interfaceControl2.RunOrClosePipeMessageParams.validate(
+        messageValidator, message.getHeaderNumBytes());
+    if (error != internal.validationError.NONE) {
+      throw error;
+    }
+  }
+
+  function runOrClosePipe(message, interfaceVersion) {
+    var reader = new internal.MessageReader(message);
+    var runOrClosePipeMessageParams = reader.decodeStruct(
+        mojo.interfaceControl2.RunOrClosePipeMessageParams);
+    return interfaceVersion >=
+        runOrClosePipeMessageParams.input.requireVersion.version;
+  }
+
+  function run(message, responder, interfaceVersion) {
+    var reader = new internal.MessageReader(message);
+    var runMessageParams =
+        reader.decodeStruct(mojo.interfaceControl2.RunMessageParams);
+    var runOutput = null;
+
+    if (runMessageParams.input.queryVersion) {
+      runOutput = new mojo.interfaceControl2.RunOutput();
+      runOutput.queryVersionResult = new
+          mojo.interfaceControl2.QueryVersionResult(
+              {'version': interfaceVersion});
+    }
+
+    var runResponseMessageParams = new
+        mojo.interfaceControl2.RunResponseMessageParams();
+    runResponseMessageParams.output = runOutput;
+
+    var messageName = mojo.interfaceControl2.kRunMessageId;
+    var payloadSize =
+        mojo.interfaceControl2.RunResponseMessageParams.encodedSize;
+    var requestID = reader.requestID;
+    var builder = new internal.MessageV1Builder(messageName,
+        payloadSize, internal.kMessageIsResponse, requestID);
+    builder.encodeStruct(mojo.interfaceControl2.RunResponseMessageParams,
+                         runResponseMessageParams);
+    responder.accept(builder.finish());
+    return true;
+  }
+
+  function isInterfaceControlMessage(message) {
+    return message.getName() == mojo.interfaceControl2.kRunMessageId ||
+           message.getName() == mojo.interfaceControl2.kRunOrClosePipeMessageId;
+  }
+
+  function ControlMessageHandler(interfaceVersion) {
+    this.interfaceVersion_ = interfaceVersion;
+  }
+
+  ControlMessageHandler.prototype.accept = function(message) {
+    validateControlRequestWithoutResponse(message);
+    return runOrClosePipe(message, this.interfaceVersion_);
+  };
+
+  ControlMessageHandler.prototype.acceptWithResponder = function(message,
+      responder) {
+    validateControlRequestWithResponse(message);
+    return run(message, responder, this.interfaceVersion_);
+  };
+
+  internal.ControlMessageHandler = ControlMessageHandler;
+  internal.isInterfaceControlMessage = isInterfaceControlMessage;
+})();
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  function constructRunOrClosePipeMessage(runOrClosePipeInput) {
+    var runOrClosePipeMessageParams = new
+        mojo.interfaceControl2.RunOrClosePipeMessageParams();
+    runOrClosePipeMessageParams.input = runOrClosePipeInput;
+
+    var messageName = mojo.interfaceControl2.kRunOrClosePipeMessageId;
+    var payloadSize =
+        mojo.interfaceControl2.RunOrClosePipeMessageParams.encodedSize;
+    var builder = new internal.MessageV0Builder(messageName, payloadSize);
+    builder.encodeStruct(mojo.interfaceControl2.RunOrClosePipeMessageParams,
+                         runOrClosePipeMessageParams);
+    var message = builder.finish();
+    return message;
+  }
+
+  function validateControlResponse(message) {
+    var messageValidator = new internal.Validator(message);
+    var error = messageValidator.validateMessageIsResponse();
+    if (error != internal.validationError.NONE) {
+      throw error;
+    }
+
+    if (message.getName() != mojo.interfaceControl2.kRunMessageId) {
+      throw new Error("Control message name is not kRunMessageId");
+    }
+
+    // Validate payload.
+    error = mojo.interfaceControl2.RunResponseMessageParams.validate(
+        messageValidator, message.getHeaderNumBytes());
+    if (error != internal.validationError.NONE) {
+      throw error;
+    }
+  }
+
+  function acceptRunResponse(message) {
+    validateControlResponse(message);
+
+    var reader = new internal.MessageReader(message);
+    var runResponseMessageParams = reader.decodeStruct(
+        mojo.interfaceControl2.RunResponseMessageParams);
+
+    return Promise.resolve(runResponseMessageParams);
+  }
+
+ /**
+  * Sends the given run message through the receiver.
+  * Accepts the response message from the receiver and decodes the message
+  * struct to RunResponseMessageParams.
+  *
+  * @param  {Router} receiver.
+  * @param  {RunMessageParams} runMessageParams to be sent via a message.
+  * @return {Promise} that resolves to a RunResponseMessageParams.
+  */
+  function sendRunMessage(receiver, runMessageParams) {
+    var messageName = mojo.interfaceControl2.kRunMessageId;
+    var payloadSize = mojo.interfaceControl2.RunMessageParams.encodedSize;
+    // |requestID| is set to 0, but is later properly set by Router.
+    var builder = new internal.MessageV1Builder(messageName,
+        payloadSize, internal.kMessageExpectsResponse, 0);
+    builder.encodeStruct(mojo.interfaceControl2.RunMessageParams,
+                         runMessageParams);
+    var message = builder.finish();
+
+    return receiver.acceptAndExpectResponse(message).then(acceptRunResponse);
+  }
+
+  function ControlMessageProxy(receiver) {
+    this.receiver_ = receiver;
+  }
+
+  ControlMessageProxy.prototype.queryVersion = function() {
+    var runMessageParams = new mojo.interfaceControl2.RunMessageParams();
+    runMessageParams.input = new mojo.interfaceControl2.RunInput();
+    runMessageParams.input.queryVersion =
+        new mojo.interfaceControl2.QueryVersion();
+
+    return sendRunMessage(this.receiver_, runMessageParams).then(function(
+        runResponseMessageParams) {
+      return runResponseMessageParams.output.queryVersionResult.version;
+    });
+  };
+
+  ControlMessageProxy.prototype.requireVersion = function(version) {
+    var runOrClosePipeInput = new mojo.interfaceControl2.RunOrClosePipeInput();
+    runOrClosePipeInput.requireVersion =
+        new mojo.interfaceControl2.RequireVersion({'version': version});
+    var message = constructRunOrClosePipeMessage(runOrClosePipeInput);
+    this.receiver_.accept(message);
+  };
+
+  internal.ControlMessageProxy = ControlMessageProxy;
+})();
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  function InterfaceEndpointClient(interfaceEndpointHandle, receiver,
+      interfaceVersion) {
+    this.controller_ = null;
+    this.encounteredError_ = false;
+    this.handle_ = interfaceEndpointHandle;
+    this.incomingReceiver_ = receiver;
+
+    if (interfaceVersion !== undefined) {
+      this.controlMessageHandler_ = new internal.ControlMessageHandler(
+          interfaceVersion);
+    } else {
+      this.controlMessageProxy_ = new internal.ControlMessageProxy(this);
+    }
+
+    this.nextRequestID_ = 0;
+    this.completers_ = new Map();
+    this.payloadValidators_ = [];
+    this.connectionErrorHandler_ = null;
+
+    if (interfaceEndpointHandle.pendingAssociation()) {
+      interfaceEndpointHandle.setAssociationEventHandler(
+          this.onAssociationEvent.bind(this));
+    } else {
+      this.initControllerIfNecessary_();
+    }
+  }
+
+  InterfaceEndpointClient.prototype.initControllerIfNecessary_ = function() {
+    if (this.controller_ || this.handle_.pendingAssociation()) {
+      return;
+    }
+
+    this.controller_ = this.handle_.groupController().attachEndpointClient(
+        this.handle_, this);
+  };
+
+  InterfaceEndpointClient.prototype.onAssociationEvent = function(
+      associationEvent) {
+    if (associationEvent === internal.AssociationEvent.ASSOCIATED) {
+      this.initControllerIfNecessary_();
+    } else if (associationEvent ===
+          internal.AssociationEvent.PEER_CLOSED_BEFORE_ASSOCIATION) {
+      setTimeout(this.notifyError.bind(this, this.handle_.disconnectReason()),
+                 0);
+    }
+  };
+
+  InterfaceEndpointClient.prototype.passHandle = function() {
+    if (!this.handle_.isValid()) {
+      return new internal.InterfaceEndpointHandle();
+    }
+
+    // Used to clear the previously set callback.
+    this.handle_.setAssociationEventHandler(undefined);
+
+    if (this.controller_) {
+      this.controller_ = null;
+      this.handle_.groupController().detachEndpointClient(this.handle_);
+    }
+    var handle = this.handle_;
+    this.handle_ = null;
+    return handle;
+  };
+
+  InterfaceEndpointClient.prototype.close = function(reason) {
+    var handle = this.passHandle();
+    handle.reset(reason);
+  };
+
+  InterfaceEndpointClient.prototype.accept = function(message) {
+    if (message.associatedEndpointHandles.length > 0) {
+      message.serializeAssociatedEndpointHandles(
+          this.handle_.groupController());
+    }
+
+    if (this.encounteredError_) {
+      return false;
+    }
+
+    this.initControllerIfNecessary_();
+    return this.controller_.sendMessage(message);
+  };
+
+  InterfaceEndpointClient.prototype.acceptAndExpectResponse = function(
+      message) {
+    if (message.associatedEndpointHandles.length > 0) {
+      message.serializeAssociatedEndpointHandles(
+          this.handle_.groupController());
+    }
+
+    if (this.encounteredError_) {
+      return Promise.reject();
+    }
+
+    this.initControllerIfNecessary_();
+
+    // Reserve 0 in case we want it to convey special meaning in the future.
+    var requestID = this.nextRequestID_++;
+    if (requestID === 0)
+      requestID = this.nextRequestID_++;
+
+    message.setRequestID(requestID);
+    var result = this.controller_.sendMessage(message);
+    if (!result)
+      return Promise.reject(Error("Connection error"));
+
+    var completer = {};
+    this.completers_.set(requestID, completer);
+    return new Promise(function(resolve, reject) {
+      completer.resolve = resolve;
+      completer.reject = reject;
+    });
+  };
+
+  InterfaceEndpointClient.prototype.setPayloadValidators = function(
+      payloadValidators) {
+    this.payloadValidators_ = payloadValidators;
+  };
+
+  InterfaceEndpointClient.prototype.setIncomingReceiver = function(receiver) {
+    this.incomingReceiver_ = receiver;
+  };
+
+  InterfaceEndpointClient.prototype.setConnectionErrorHandler = function(
+      handler) {
+    this.connectionErrorHandler_ = handler;
+  };
+
+  InterfaceEndpointClient.prototype.handleIncomingMessage = function(message,
+      messageValidator) {
+    var noError = internal.validationError.NONE;
+    var err = noError;
+    for (var i = 0; err === noError && i < this.payloadValidators_.length; ++i)
+      err = this.payloadValidators_[i](messageValidator);
+
+    if (err == noError) {
+      return this.handleValidIncomingMessage_(message);
+    } else {
+      internal.reportValidationError(err);
+      return false;
+    }
+  };
+
+  InterfaceEndpointClient.prototype.handleValidIncomingMessage_ = function(
+      message) {
+    if (internal.isTestingMode()) {
+      return true;
+    }
+
+    if (this.encounteredError_) {
+      return false;
+    }
+
+    var ok = false;
+
+    if (message.expectsResponse()) {
+      if (internal.isInterfaceControlMessage(message) &&
+          this.controlMessageHandler_) {
+        ok = this.controlMessageHandler_.acceptWithResponder(message, this);
+      } else if (this.incomingReceiver_) {
+        ok = this.incomingReceiver_.acceptWithResponder(message, this);
+      }
+    } else if (message.isResponse()) {
+      var reader = new internal.MessageReader(message);
+      var requestID = reader.requestID;
+      var completer = this.completers_.get(requestID);
+      if (completer) {
+        this.completers_.delete(requestID);
+        completer.resolve(message);
+        ok = true;
+      } else {
+        console.log("Unexpected response with request ID: " + requestID);
+      }
+    } else {
+      if (internal.isInterfaceControlMessage(message) &&
+          this.controlMessageHandler_) {
+        ok = this.controlMessageHandler_.accept(message);
+      } else if (this.incomingReceiver_) {
+        ok = this.incomingReceiver_.accept(message);
+      }
+    }
+    return ok;
+  };
+
+  InterfaceEndpointClient.prototype.notifyError = function(reason) {
+    if (this.encounteredError_) {
+      return;
+    }
+    this.encounteredError_ = true;
+
+    this.completers_.forEach(function(value) {
+      value.reject();
+    });
+    this.completers_.clear();  // Drop any responders.
+
+    if (this.connectionErrorHandler_) {
+      this.connectionErrorHandler_(reason);
+    }
+  };
+
+  InterfaceEndpointClient.prototype.queryVersion = function() {
+    return this.controlMessageProxy_.queryVersion();
+  };
+
+  InterfaceEndpointClient.prototype.requireVersion = function(version) {
+    this.controlMessageProxy_.requireVersion(version);
+  };
+
+  InterfaceEndpointClient.prototype.getEncounteredError = function() {
+    return this.encounteredError_;
+  };
+
+  internal.InterfaceEndpointClient = InterfaceEndpointClient;
+})();
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  var AssociationEvent = {
+    // The interface has been associated with a message pipe.
+    ASSOCIATED: 'associated',
+    // The peer of this object has been closed before association.
+    PEER_CLOSED_BEFORE_ASSOCIATION: 'peer_closed_before_association'
+  };
+
+  function State(interfaceId, associatedGroupController) {
+    if (interfaceId === undefined) {
+      interfaceId = internal.kInvalidInterfaceId;
+    }
+
+    this.interfaceId = interfaceId;
+    this.associatedGroupController = associatedGroupController;
+    this.pendingAssociation = false;
+    this.disconnectReason = null;
+    this.peerState_ = null;
+    this.associationEventHandler_ = null;
+  }
+
+  State.prototype.initPendingState = function(peer) {
+    this.pendingAssociation = true;
+    this.peerState_ = peer;
+  };
+
+  State.prototype.isValid = function() {
+    return this.pendingAssociation ||
+        internal.isValidInterfaceId(this.interfaceId);
+  };
+
+  State.prototype.close = function(disconnectReason) {
+    var cachedGroupController;
+    var cachedPeerState;
+    var cachedId = internal.kInvalidInterfaceId;
+
+    if (!this.pendingAssociation) {
+      if (internal.isValidInterfaceId(this.interfaceId)) {
+        cachedGroupController = this.associatedGroupController;
+        this.associatedGroupController = null;
+        cachedId = this.interfaceId;
+        this.interfaceId = internal.kInvalidInterfaceId;
+      }
+    } else {
+      this.pendingAssociation = false;
+      cachedPeerState = this.peerState_;
+      this.peerState_ = null;
+    }
+
+    if (cachedGroupController) {
+      cachedGroupController.closeEndpointHandle(cachedId,
+          disconnectReason);
+    } else if (cachedPeerState) {
+      cachedPeerState.onPeerClosedBeforeAssociation(disconnectReason);
+    }
+  };
+
+  State.prototype.runAssociationEventHandler = function(associationEvent) {
+    if (this.associationEventHandler_) {
+      var handler = this.associationEventHandler_;
+      this.associationEventHandler_ = null;
+      handler(associationEvent);
+    }
+  };
+
+  State.prototype.setAssociationEventHandler = function(handler) {
+    if (!this.pendingAssociation &&
+        !internal.isValidInterfaceId(this.interfaceId)) {
+      return;
+    }
+
+    if (!handler) {
+      this.associationEventHandler_ = null;
+      return;
+    }
+
+    this.associationEventHandler_ = handler;
+    if (!this.pendingAssociation) {
+      setTimeout(this.runAssociationEventHandler.bind(this,
+          AssociationEvent.ASSOCIATED), 0);
+    } else if (!this.peerState_) {
+      setTimeout(this.runAssociationEventHandler.bind(this,
+          AssociationEvent.PEER_CLOSED_BEFORE_ASSOCIATION), 0);
+    }
+  };
+
+  State.prototype.notifyAssociation = function(interfaceId,
+                                               peerGroupController) {
+    var cachedPeerState = this.peerState_;
+    this.peerState_ = null;
+
+    this.pendingAssociation = false;
+
+    if (cachedPeerState) {
+      cachedPeerState.onAssociated(interfaceId, peerGroupController);
+      return true;
+    }
+    return false;
+  };
+
+  State.prototype.onAssociated = function(interfaceId,
+      associatedGroupController) {
+    if (!this.pendingAssociation) {
+      return;
+    }
+
+    this.pendingAssociation = false;
+    this.peerState_ = null;
+    this.interfaceId = interfaceId;
+    this.associatedGroupController = associatedGroupController;
+    this.runAssociationEventHandler(AssociationEvent.ASSOCIATED);
+  };
+
+  State.prototype.onPeerClosedBeforeAssociation = function(disconnectReason) {
+    if (!this.pendingAssociation) {
+      return;
+    }
+
+    this.peerState_ = null;
+    this.disconnectReason = disconnectReason;
+
+    this.runAssociationEventHandler(
+        AssociationEvent.PEER_CLOSED_BEFORE_ASSOCIATION);
+  };
+
+  function createPairPendingAssociation() {
+    var handle0 = new InterfaceEndpointHandle();
+    var handle1 = new InterfaceEndpointHandle();
+    handle0.state_.initPendingState(handle1.state_);
+    handle1.state_.initPendingState(handle0.state_);
+    return {handle0: handle0, handle1: handle1};
+  }
+
+  function InterfaceEndpointHandle(interfaceId, associatedGroupController) {
+    this.state_ = new State(interfaceId, associatedGroupController);
+  }
+
+  InterfaceEndpointHandle.prototype.isValid = function() {
+    return this.state_.isValid();
+  };
+
+  InterfaceEndpointHandle.prototype.pendingAssociation = function() {
+    return this.state_.pendingAssociation;
+  };
+
+  InterfaceEndpointHandle.prototype.id = function() {
+    return this.state_.interfaceId;
+  };
+
+  InterfaceEndpointHandle.prototype.groupController = function() {
+    return this.state_.associatedGroupController;
+  };
+
+  InterfaceEndpointHandle.prototype.disconnectReason = function() {
+    return this.state_.disconnectReason;
+  };
+
+  InterfaceEndpointHandle.prototype.setAssociationEventHandler = function(
+      handler) {
+    this.state_.setAssociationEventHandler(handler);
+  };
+
+  InterfaceEndpointHandle.prototype.notifyAssociation = function(interfaceId,
+      peerGroupController) {
+    return this.state_.notifyAssociation(interfaceId, peerGroupController);
+  };
+
+  InterfaceEndpointHandle.prototype.reset = function(reason) {
+    this.state_.close(reason);
+    this.state_ = new State();
+  };
+
+  internal.AssociationEvent = AssociationEvent;
+  internal.InterfaceEndpointHandle = InterfaceEndpointHandle;
+  internal.createPairPendingAssociation = createPairPendingAssociation;
+})();
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  function validateControlRequestWithoutResponse(message) {
+    var messageValidator = new internal.Validator(message);
+    var error = messageValidator.validateMessageIsRequestWithoutResponse();
+    if (error != internal.validationError.NONE) {
+      throw error;
+    }
+
+    if (message.getName() != mojo.pipeControl2.kRunOrClosePipeMessageId) {
+      throw new Error("Control message name is not kRunOrClosePipeMessageId");
+    }
+
+    // Validate payload.
+    error = mojo.pipeControl2.RunOrClosePipeMessageParams.validate(
+        messageValidator, message.getHeaderNumBytes());
+    if (error != internal.validationError.NONE) {
+      throw error;
+    }
+  }
+
+  function runOrClosePipe(message, delegate) {
+    var reader = new internal.MessageReader(message);
+    var runOrClosePipeMessageParams = reader.decodeStruct(
+        mojo.pipeControl2.RunOrClosePipeMessageParams);
+    var event = runOrClosePipeMessageParams.input
+        .peerAssociatedEndpointClosedEvent;
+    return delegate.onPeerAssociatedEndpointClosed(event.id,
+        event.disconnectReason);
+  }
+
+  function isPipeControlMessage(message) {
+    return !internal.isValidInterfaceId(message.getInterfaceId());
+  }
+
+  function PipeControlMessageHandler(delegate) {
+    this.delegate_ = delegate;
+  }
+
+  PipeControlMessageHandler.prototype.accept = function(message) {
+    validateControlRequestWithoutResponse(message);
+    return runOrClosePipe(message, this.delegate_);
+  };
+
+  internal.PipeControlMessageHandler = PipeControlMessageHandler;
+  internal.isPipeControlMessage = isPipeControlMessage;
+})();
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  function constructRunOrClosePipeMessage(runOrClosePipeInput) {
+    var runOrClosePipeMessageParams = new
+        mojo.pipeControl2.RunOrClosePipeMessageParams();
+    runOrClosePipeMessageParams.input = runOrClosePipeInput;
+
+    var messageName = mojo.pipeControl2.kRunOrClosePipeMessageId;
+    var payloadSize =
+        mojo.pipeControl2.RunOrClosePipeMessageParams.encodedSize;
+
+    var builder = new internal.MessageV0Builder(messageName, payloadSize);
+    builder.encodeStruct(mojo.pipeControl2.RunOrClosePipeMessageParams,
+                         runOrClosePipeMessageParams);
+    var message = builder.finish();
+    message.setInterfaceId(internal.kInvalidInterfaceId);
+    return message;
+  }
+
+  function PipeControlMessageProxy(receiver) {
+    this.receiver_ = receiver;
+  }
+
+  PipeControlMessageProxy.prototype.notifyPeerEndpointClosed = function(
+      interfaceId, reason) {
+    var message = this.constructPeerEndpointClosedMessage(interfaceId, reason);
+    this.receiver_.accept(message);
+  };
+
+  PipeControlMessageProxy.prototype.constructPeerEndpointClosedMessage =
+      function(interfaceId, reason) {
+    var event = new mojo.pipeControl2.PeerAssociatedEndpointClosedEvent();
+    event.id = interfaceId;
+    if (reason) {
+      event.disconnectReason = new mojo.pipeControl2.DisconnectReason({
+          customReason: reason.customReason,
+          description: reason.description});
+    }
+    var runOrClosePipeInput = new mojo.pipeControl2.RunOrClosePipeInput();
+    runOrClosePipeInput.peerAssociatedEndpointClosedEvent = event;
+    return constructRunOrClosePipeMessage(runOrClosePipeInput);
+  };
+
+  internal.PipeControlMessageProxy = PipeControlMessageProxy;
+})();
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  /**
+   * The state of |endpoint|. If both the endpoint and its peer have been
+   * closed, removes it from |endpoints_|.
+   * @enum {string}
+   */
+  var EndpointStateUpdateType = {
+    ENDPOINT_CLOSED: 'endpoint_closed',
+    PEER_ENDPOINT_CLOSED: 'peer_endpoint_closed'
+  };
+
+  function check(condition, output) {
+    if (!condition) {
+      // testharness.js does not rethrow errors so the error stack needs to be
+      // included as a string in the error we throw for debugging layout tests.
+      throw new Error((new Error()).stack);
+    }
+  }
+
+  function InterfaceEndpoint(router, interfaceId) {
+    this.router_ = router;
+    this.id = interfaceId;
+    this.closed = false;
+    this.peerClosed = false;
+    this.handleCreated = false;
+    this.disconnectReason = null;
+    this.client = null;
+  }
+
+  InterfaceEndpoint.prototype.sendMessage = function(message) {
+    message.setInterfaceId(this.id);
+    return this.router_.connector_.accept(message);
+  };
+
+  function Router(handle, setInterfaceIdNamespaceBit) {
+    if (!(handle instanceof MojoHandle)) {
+      throw new Error("Router constructor: Not a handle");
+    }
+    if (setInterfaceIdNamespaceBit === undefined) {
+      setInterfaceIdNamespaceBit = false;
+    }
+
+    this.connector_ = new internal.Connector(handle);
+
+    this.connector_.setIncomingReceiver({
+        accept: this.accept.bind(this),
+    });
+    this.connector_.setErrorHandler({
+        onError: this.onPipeConnectionError.bind(this),
+    });
+
+    this.setInterfaceIdNamespaceBit_ = setInterfaceIdNamespaceBit;
+    // |cachedMessageData| caches infomation about a message, so it can be
+    // processed later if a client is not yet attached to the target endpoint.
+    this.cachedMessageData = null;
+    this.controlMessageHandler_ = new internal.PipeControlMessageHandler(this);
+    this.controlMessageProxy_ =
+        new internal.PipeControlMessageProxy(this.connector_);
+    this.nextInterfaceIdValue_ = 1;
+    this.encounteredError_ = false;
+    this.endpoints_ = new Map();
+  }
+
+  Router.prototype.associateInterface = function(handleToSend) {
+    if (!handleToSend.pendingAssociation()) {
+      return internal.kInvalidInterfaceId;
+    }
+
+    var id = 0;
+    do {
+      if (this.nextInterfaceIdValue_ >= internal.kInterfaceIdNamespaceMask) {
+        this.nextInterfaceIdValue_ = 1;
+      }
+      id = this.nextInterfaceIdValue_++;
+      if (this.setInterfaceIdNamespaceBit_) {
+        id += internal.kInterfaceIdNamespaceMask;
+      }
+    } while (this.endpoints_.has(id));
+
+    var endpoint = new InterfaceEndpoint(this, id);
+    this.endpoints_.set(id, endpoint);
+    if (this.encounteredError_) {
+      this.updateEndpointStateMayRemove(endpoint,
+          EndpointStateUpdateType.PEER_ENDPOINT_CLOSED);
+    }
+    endpoint.handleCreated = true;
+
+    if (!handleToSend.notifyAssociation(id, this)) {
+      // The peer handle of |handleToSend|, which is supposed to join this
+      // associated group, has been closed.
+      this.updateEndpointStateMayRemove(endpoint,
+          EndpointStateUpdateType.ENDPOINT_CLOSED);
+
+      pipeControlMessageproxy.notifyPeerEndpointClosed(id,
+          handleToSend.disconnectReason());
+    }
+
+    return id;
+  };
+
+  Router.prototype.attachEndpointClient = function(
+      interfaceEndpointHandle, interfaceEndpointClient) {
+    check(internal.isValidInterfaceId(interfaceEndpointHandle.id()));
+    check(interfaceEndpointClient);
+
+    var endpoint = this.endpoints_.get(interfaceEndpointHandle.id());
+    check(endpoint);
+    check(!endpoint.client);
+    check(!endpoint.closed);
+    endpoint.client = interfaceEndpointClient;
+
+    if (endpoint.peerClosed) {
+      setTimeout(endpoint.client.notifyError.bind(endpoint.client), 0);
+    }
+
+    if (this.cachedMessageData && interfaceEndpointHandle.id() ===
+        this.cachedMessageData.message.getInterfaceId()) {
+      setTimeout((function() {
+        if (!this.cachedMessageData) {
+          return;
+        }
+
+        var targetEndpoint = this.endpoints_.get(
+            this.cachedMessageData.message.getInterfaceId());
+        // Check that the target endpoint's client still exists.
+        if (targetEndpoint && targetEndpoint.client) {
+          var message = this.cachedMessageData.message;
+          var messageValidator = this.cachedMessageData.messageValidator;
+          this.cachedMessageData = null;
+          this.connector_.resumeIncomingMethodCallProcessing();
+          var ok = endpoint.client.handleIncomingMessage(message,
+              messageValidator);
+
+          // Handle invalid cached incoming message.
+          if (!internal.isTestingMode() && !ok) {
+            this.connector_.handleError(true, true);
+          }
+        }
+      }).bind(this), 0);
+    }
+
+    return endpoint;
+  };
+
+  Router.prototype.detachEndpointClient = function(
+      interfaceEndpointHandle) {
+    check(internal.isValidInterfaceId(interfaceEndpointHandle.id()));
+    var endpoint = this.endpoints_.get(interfaceEndpointHandle.id());
+    check(endpoint);
+    check(endpoint.client);
+    check(!endpoint.closed);
+
+    endpoint.client = null;
+  };
+
+  Router.prototype.createLocalEndpointHandle = function(
+      interfaceId) {
+    if (!internal.isValidInterfaceId(interfaceId)) {
+      return new internal.InterfaceEndpointHandle();
+    }
+
+    var endpoint = this.endpoints_.get(interfaceId);
+
+    if (!endpoint) {
+      endpoint = new InterfaceEndpoint(this, interfaceId);
+      this.endpoints_.set(interfaceId, endpoint);
+
+      check(!endpoint.handleCreated);
+
+      if (this.encounteredError_) {
+        this.updateEndpointStateMayRemove(endpoint,
+            EndpointStateUpdateType.PEER_ENDPOINT_CLOSED);
+      }
+    } else {
+      // If the endpoint already exist, it is because we have received a
+      // notification that the peer endpoint has closed.
+      check(!endpoint.closed);
+      check(endpoint.peerClosed);
+
+      if (endpoint.handleCreated) {
+        return new internal.InterfaceEndpointHandle();
+      }
+    }
+
+    endpoint.handleCreated = true;
+    return new internal.InterfaceEndpointHandle(interfaceId, this);
+  };
+
+  Router.prototype.accept = function(message) {
+    var messageValidator = new internal.Validator(message);
+    var err = messageValidator.validateMessageHeader();
+
+    var ok = false;
+    if (err !== internal.validationError.NONE) {
+      internal.reportValidationError(err);
+    } else if (message.deserializeAssociatedEndpointHandles(this)) {
+      if (internal.isPipeControlMessage(message)) {
+        ok = this.controlMessageHandler_.accept(message);
+      } else {
+        var interfaceId = message.getInterfaceId();
+        var endpoint = this.endpoints_.get(interfaceId);
+        if (!endpoint || endpoint.closed) {
+          return true;
+        }
+
+        if (!endpoint.client) {
+          // We need to wait until a client is attached in order to dispatch
+          // further messages.
+          this.cachedMessageData = {message: message,
+              messageValidator: messageValidator};
+          this.connector_.pauseIncomingMethodCallProcessing();
+          return true;
+        }
+        ok = endpoint.client.handleIncomingMessage(message, messageValidator);
+      }
+    }
+    return ok;
+  };
+
+  Router.prototype.close = function() {
+    this.connector_.close();
+    // Closing the message pipe won't trigger connection error handler.
+    // Explicitly call onPipeConnectionError() so that associated endpoints
+    // will get notified.
+    this.onPipeConnectionError();
+  };
+
+  Router.prototype.onPeerAssociatedEndpointClosed = function(interfaceId,
+      reason) {
+    check(!internal.isMasterInterfaceId(interfaceId) || reason);
+
+    var endpoint = this.endpoints_.get(interfaceId);
+    if (!endpoint) {
+      endpoint = new InterfaceEndpoint(this, interfaceId);
+      this.endpoints_.set(interfaceId, endpoint);
+    }
+
+    if (reason) {
+      endpoint.disconnectReason = reason;
+    }
+
+    if (!endpoint.peerClosed) {
+      if (endpoint.client) {
+        setTimeout(endpoint.client.notifyError.bind(endpoint.client, reason),
+                   0);
+      }
+      this.updateEndpointStateMayRemove(endpoint,
+          EndpointStateUpdateType.PEER_ENDPOINT_CLOSED);
+    }
+    return true;
+  };
+
+  Router.prototype.onPipeConnectionError = function() {
+    this.encounteredError_ = true;
+
+    for (var endpoint of this.endpoints_.values()) {
+      if (endpoint.client) {
+        setTimeout(
+            endpoint.client.notifyError.bind(
+                endpoint.client, endpoint.disconnectReason),
+            0);
+      }
+      this.updateEndpointStateMayRemove(endpoint,
+          EndpointStateUpdateType.PEER_ENDPOINT_CLOSED);
+    }
+  };
+
+  Router.prototype.closeEndpointHandle = function(interfaceId, reason) {
+    if (!internal.isValidInterfaceId(interfaceId)) {
+      return;
+    }
+    var endpoint = this.endpoints_.get(interfaceId);
+    check(endpoint);
+    check(!endpoint.client);
+    check(!endpoint.closed);
+
+    this.updateEndpointStateMayRemove(endpoint,
+        EndpointStateUpdateType.ENDPOINT_CLOSED);
+
+    if (!internal.isMasterInterfaceId(interfaceId) || reason) {
+      this.controlMessageProxy_.notifyPeerEndpointClosed(interfaceId, reason);
+    }
+
+    if (this.cachedMessageData && interfaceId ===
+        this.cachedMessageData.message.getInterfaceId()) {
+      this.cachedMessageData = null;
+      this.connector_.resumeIncomingMethodCallProcessing();
+    }
+  };
+
+  Router.prototype.updateEndpointStateMayRemove = function(endpoint,
+      endpointStateUpdateType) {
+    if (endpointStateUpdateType === EndpointStateUpdateType.ENDPOINT_CLOSED) {
+      endpoint.closed = true;
+    } else {
+      endpoint.peerClosed = true;
+    }
+    if (endpoint.closed && endpoint.peerClosed) {
+      this.endpoints_.delete(endpoint.id);
+    }
+  };
+
+  internal.Router = Router;
+})();
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * Defines functions for translating between JavaScript strings and UTF8 strings
+ * stored in ArrayBuffers. There is much room for optimization in this code if
+ * it proves necessary.
+ */
+(function() {
+  var internal = mojo.internal;
+
+  /**
+   * Decodes the UTF8 string from the given buffer.
+   * @param {ArrayBufferView} buffer The buffer containing UTF8 string data.
+   * @return {string} The corresponding JavaScript string.
+   */
+  function decodeUtf8String(buffer) {
+    return decodeURIComponent(escape(String.fromCharCode.apply(null, buffer)));
+  }
+
+  /**
+   * Encodes the given JavaScript string into UTF8.
+   * @param {string} str The string to encode.
+   * @param {ArrayBufferView} outputBuffer The buffer to contain the result.
+   * Should be pre-allocated to hold enough space. Use |utf8Length| to determine
+   * how much space is required.
+   * @return {number} The number of bytes written to |outputBuffer|.
+   */
+  function encodeUtf8String(str, outputBuffer) {
+    var utf8String = unescape(encodeURIComponent(str));
+    if (outputBuffer.length < utf8String.length)
+      throw new Error("Buffer too small for encodeUtf8String");
+    for (var i = 0; i < outputBuffer.length && i < utf8String.length; i++)
+      outputBuffer[i] = utf8String.charCodeAt(i);
+    return i;
+  }
+
+  /**
+   * Returns the number of bytes that a UTF8 encoding of the JavaScript string
+   * |str| would occupy.
+   */
+  function utf8Length(str) {
+    var utf8String = unescape(encodeURIComponent(str));
+    return utf8String.length;
+  }
+
+  internal.decodeUtf8String = decodeUtf8String;
+  internal.encodeUtf8String = encodeUtf8String;
+  internal.utf8Length = utf8Length;
+})();
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function() {
+  var internal = mojo.internal;
+
+  var validationError = {
+    NONE: 'VALIDATION_ERROR_NONE',
+    MISALIGNED_OBJECT: 'VALIDATION_ERROR_MISALIGNED_OBJECT',
+    ILLEGAL_MEMORY_RANGE: 'VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE',
+    UNEXPECTED_STRUCT_HEADER: 'VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER',
+    UNEXPECTED_ARRAY_HEADER: 'VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER',
+    ILLEGAL_HANDLE: 'VALIDATION_ERROR_ILLEGAL_HANDLE',
+    UNEXPECTED_INVALID_HANDLE: 'VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE',
+    ILLEGAL_POINTER: 'VALIDATION_ERROR_ILLEGAL_POINTER',
+    UNEXPECTED_NULL_POINTER: 'VALIDATION_ERROR_UNEXPECTED_NULL_POINTER',
+    ILLEGAL_INTERFACE_ID: 'VALIDATION_ERROR_ILLEGAL_INTERFACE_ID',
+    UNEXPECTED_INVALID_INTERFACE_ID:
+        'VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID',
+    MESSAGE_HEADER_INVALID_FLAGS:
+        'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS',
+    MESSAGE_HEADER_MISSING_REQUEST_ID:
+        'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID',
+    DIFFERENT_SIZED_ARRAYS_IN_MAP:
+        'VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP',
+    INVALID_UNION_SIZE: 'VALIDATION_ERROR_INVALID_UNION_SIZE',
+    UNEXPECTED_NULL_UNION: 'VALIDATION_ERROR_UNEXPECTED_NULL_UNION',
+    UNKNOWN_ENUM_VALUE: 'VALIDATION_ERROR_UNKNOWN_ENUM_VALUE',
+  };
+
+  var NULL_MOJO_POINTER = "NULL_MOJO_POINTER";
+  var gValidationErrorObserver = null;
+
+  function reportValidationError(error) {
+    if (gValidationErrorObserver) {
+      gValidationErrorObserver.lastError = error;
+    } else {
+      console.warn('Invalid message: ' + error);
+    }
+  }
+
+  var ValidationErrorObserverForTesting = (function() {
+    function Observer() {
+      this.lastError = validationError.NONE;
+    }
+
+    Observer.prototype.reset = function() {
+      this.lastError = validationError.NONE;
+    };
+
+    return {
+      getInstance: function() {
+        if (!gValidationErrorObserver) {
+          gValidationErrorObserver = new Observer();
+        }
+        return gValidationErrorObserver;
+      }
+    };
+  })();
+
+  function isTestingMode() {
+    return Boolean(gValidationErrorObserver);
+  }
+
+  function clearTestingMode() {
+    gValidationErrorObserver = null;
+  }
+
+  function isEnumClass(cls) {
+    return cls instanceof internal.Enum;
+  }
+
+  function isStringClass(cls) {
+    return cls === internal.String || cls === internal.NullableString;
+  }
+
+  function isHandleClass(cls) {
+    return cls === internal.Handle || cls === internal.NullableHandle;
+  }
+
+  function isInterfaceClass(cls) {
+    return cls instanceof internal.Interface;
+  }
+
+  function isInterfaceRequestClass(cls) {
+    return cls === internal.InterfaceRequest ||
+        cls === internal.NullableInterfaceRequest;
+  }
+
+  function isAssociatedInterfaceClass(cls) {
+    return cls === internal.AssociatedInterfacePtrInfo ||
+        cls === internal.NullableAssociatedInterfacePtrInfo;
+  }
+
+  function isAssociatedInterfaceRequestClass(cls) {
+    return cls === internal.AssociatedInterfaceRequest ||
+        cls === internal.NullableAssociatedInterfaceRequest;
+  }
+
+  function isNullable(type) {
+    return type === internal.NullableString ||
+        type === internal.NullableHandle ||
+        type === internal.NullableAssociatedInterfacePtrInfo ||
+        type === internal.NullableAssociatedInterfaceRequest ||
+        type === internal.NullableInterface ||
+        type === internal.NullableInterfaceRequest ||
+        type instanceof internal.NullableArrayOf ||
+        type instanceof internal.NullablePointerTo;
+  }
+
+  function Validator(message) {
+    this.message = message;
+    this.offset = 0;
+    this.handleIndex = 0;
+    this.associatedEndpointHandleIndex = 0;
+    this.payloadInterfaceIds = null;
+    this.offsetLimit = this.message.buffer.byteLength;
+  }
+
+  Object.defineProperty(Validator.prototype, "handleIndexLimit", {
+    get: function() { return this.message.handles.length; }
+  });
+
+  Object.defineProperty(Validator.prototype, "associatedHandleIndexLimit", {
+    get: function() {
+      return this.payloadInterfaceIds ? this.payloadInterfaceIds.length : 0;
+    }
+  });
+
+  // True if we can safely allocate a block of bytes from start to
+  // to start + numBytes.
+  Validator.prototype.isValidRange = function(start, numBytes) {
+    // Only positive JavaScript integers that are less than 2^53
+    // (Number.MAX_SAFE_INTEGER) can be represented exactly.
+    if (start < this.offset || numBytes <= 0 ||
+        !Number.isSafeInteger(start) ||
+        !Number.isSafeInteger(numBytes))
+      return false;
+
+    var newOffset = start + numBytes;
+    if (!Number.isSafeInteger(newOffset) || newOffset > this.offsetLimit)
+      return false;
+
+    return true;
+  };
+
+  Validator.prototype.claimRange = function(start, numBytes) {
+    if (this.isValidRange(start, numBytes)) {
+      this.offset = start + numBytes;
+      return true;
+    }
+    return false;
+  };
+
+  Validator.prototype.claimHandle = function(index) {
+    if (index === internal.kEncodedInvalidHandleValue)
+      return true;
+
+    if (index < this.handleIndex || index >= this.handleIndexLimit)
+      return false;
+
+    // This is safe because handle indices are uint32.
+    this.handleIndex = index + 1;
+    return true;
+  };
+
+  Validator.prototype.claimAssociatedEndpointHandle = function(index) {
+    if (index === internal.kEncodedInvalidHandleValue) {
+      return true;
+    }
+
+    if (index < this.associatedEndpointHandleIndex ||
+        index >= this.associatedHandleIndexLimit) {
+      return false;
+    }
+
+    // This is safe because handle indices are uint32.
+    this.associatedEndpointHandleIndex = index + 1;
+    return true;
+  };
+
+  Validator.prototype.validateEnum = function(offset, enumClass) {
+    // Note: Assumes that enums are always 32 bits! But this matches
+    // mojom::generate::pack::PackedField::GetSizeForKind, so it should be okay.
+    var value = this.message.buffer.getInt32(offset);
+    return enumClass.validate(value);
+  }
+
+  Validator.prototype.validateHandle = function(offset, nullable) {
+    var index = this.message.buffer.getUint32(offset);
+
+    if (index === internal.kEncodedInvalidHandleValue)
+      return nullable ?
+          validationError.NONE : validationError.UNEXPECTED_INVALID_HANDLE;
+
+    if (!this.claimHandle(index))
+      return validationError.ILLEGAL_HANDLE;
+
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateAssociatedEndpointHandle = function(offset,
+      nullable) {
+    var index = this.message.buffer.getUint32(offset);
+
+    if (index === internal.kEncodedInvalidHandleValue) {
+      return nullable ? validationError.NONE :
+          validationError.UNEXPECTED_INVALID_INTERFACE_ID;
+    }
+
+    if (!this.claimAssociatedEndpointHandle(index)) {
+      return validationError.ILLEGAL_INTERFACE_ID;
+    }
+
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateInterface = function(offset, nullable) {
+    return this.validateHandle(offset, nullable);
+  };
+
+  Validator.prototype.validateInterfaceRequest = function(offset, nullable) {
+    return this.validateHandle(offset, nullable);
+  };
+
+  Validator.prototype.validateAssociatedInterface = function(offset,
+      nullable) {
+    return this.validateAssociatedEndpointHandle(offset, nullable);
+  };
+
+  Validator.prototype.validateAssociatedInterfaceRequest = function(
+      offset, nullable) {
+    return this.validateAssociatedEndpointHandle(offset, nullable);
+  };
+
+  Validator.prototype.validateStructHeader = function(offset, minNumBytes) {
+    if (!internal.isAligned(offset))
+      return validationError.MISALIGNED_OBJECT;
+
+    if (!this.isValidRange(offset, internal.kStructHeaderSize))
+      return validationError.ILLEGAL_MEMORY_RANGE;
+
+    var numBytes = this.message.buffer.getUint32(offset);
+
+    if (numBytes < minNumBytes)
+      return validationError.UNEXPECTED_STRUCT_HEADER;
+
+    if (!this.claimRange(offset, numBytes))
+      return validationError.ILLEGAL_MEMORY_RANGE;
+
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateStructVersion = function(offset, versionSizes) {
+    var numBytes = this.message.buffer.getUint32(offset);
+    var version = this.message.buffer.getUint32(offset + 4);
+
+    if (version <= versionSizes[versionSizes.length - 1].version) {
+      // Scan in reverse order to optimize for more recent versionSizes.
+      for (var i = versionSizes.length - 1; i >= 0; --i) {
+        if (version >= versionSizes[i].version) {
+          if (numBytes == versionSizes[i].numBytes)
+            break;
+          return validationError.UNEXPECTED_STRUCT_HEADER;
+        }
+      }
+    } else if (numBytes < versionSizes[versionSizes.length-1].numBytes) {
+      return validationError.UNEXPECTED_STRUCT_HEADER;
+    }
+
+    return validationError.NONE;
+  };
+
+  Validator.prototype.isFieldInStructVersion = function(offset, fieldVersion) {
+    var structVersion = this.message.buffer.getUint32(offset + 4);
+    return fieldVersion <= structVersion;
+  };
+
+  Validator.prototype.validateMessageHeader = function() {
+    var err = this.validateStructHeader(0, internal.kMessageV0HeaderSize);
+    if (err != validationError.NONE) {
+      return err;
+    }
+
+    var numBytes = this.message.getHeaderNumBytes();
+    var version = this.message.getHeaderVersion();
+
+    var validVersionAndNumBytes =
+        (version == 0 && numBytes == internal.kMessageV0HeaderSize) ||
+        (version == 1 && numBytes == internal.kMessageV1HeaderSize) ||
+        (version == 2 && numBytes == internal.kMessageV2HeaderSize) ||
+        (version > 2 && numBytes >= internal.kMessageV2HeaderSize);
+
+    if (!validVersionAndNumBytes) {
+      return validationError.UNEXPECTED_STRUCT_HEADER;
+    }
+
+    var expectsResponse = this.message.expectsResponse();
+    var isResponse = this.message.isResponse();
+
+    if (version == 0 && (expectsResponse || isResponse)) {
+      return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID;
+    }
+
+    if (isResponse && expectsResponse) {
+      return validationError.MESSAGE_HEADER_INVALID_FLAGS;
+    }
+
+    if (version < 2) {
+      return validationError.NONE;
+    }
+
+    var err = this.validateArrayPointer(
+        internal.kMessagePayloadInterfaceIdsPointerOffset,
+        internal.Uint32.encodedSize, internal.Uint32, true, [0], 0);
+
+    if (err != validationError.NONE) {
+      return err;
+    }
+
+    this.payloadInterfaceIds = this.message.getPayloadInterfaceIds();
+    if (this.payloadInterfaceIds) {
+      for (var interfaceId of this.payloadInterfaceIds) {
+        if (!internal.isValidInterfaceId(interfaceId) ||
+            internal.isMasterInterfaceId(interfaceId)) {
+          return validationError.ILLEGAL_INTERFACE_ID;
+        }
+      }
+    }
+
+    // Set offset to the start of the payload and offsetLimit to the start of
+    // the payload interface Ids so that payload can be validated using the
+    // same messageValidator.
+    this.offset = this.message.getHeaderNumBytes();
+    this.offsetLimit = this.decodePointer(
+        internal.kMessagePayloadInterfaceIdsPointerOffset);
+
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateMessageIsRequestWithoutResponse = function() {
+    if (this.message.isResponse() || this.message.expectsResponse()) {
+      return validationError.MESSAGE_HEADER_INVALID_FLAGS;
+    }
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateMessageIsRequestExpectingResponse = function() {
+    if (this.message.isResponse() || !this.message.expectsResponse()) {
+      return validationError.MESSAGE_HEADER_INVALID_FLAGS;
+    }
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateMessageIsResponse = function() {
+    if (this.message.expectsResponse() || !this.message.isResponse()) {
+      return validationError.MESSAGE_HEADER_INVALID_FLAGS;
+    }
+    return validationError.NONE;
+  };
+
+  // Returns the message.buffer relative offset this pointer "points to",
+  // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the
+  // pointer's value is not valid.
+  Validator.prototype.decodePointer = function(offset) {
+    var pointerValue = this.message.buffer.getUint64(offset);
+    if (pointerValue === 0)
+      return NULL_MOJO_POINTER;
+    var bufferOffset = offset + pointerValue;
+    return Number.isSafeInteger(bufferOffset) ? bufferOffset : null;
+  };
+
+  Validator.prototype.decodeUnionSize = function(offset) {
+    return this.message.buffer.getUint32(offset);
+  };
+
+  Validator.prototype.decodeUnionTag = function(offset) {
+    return this.message.buffer.getUint32(offset + 4);
+  };
+
+  Validator.prototype.validateArrayPointer = function(
+      offset, elementSize, elementType, nullable, expectedDimensionSizes,
+      currentDimension) {
+    var arrayOffset = this.decodePointer(offset);
+    if (arrayOffset === null)
+      return validationError.ILLEGAL_POINTER;
+
+    if (arrayOffset === NULL_MOJO_POINTER)
+      return nullable ?
+          validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
+
+    return this.validateArray(arrayOffset, elementSize, elementType,
+                              expectedDimensionSizes, currentDimension);
+  };
+
+  Validator.prototype.validateStructPointer = function(
+      offset, structClass, nullable) {
+    var structOffset = this.decodePointer(offset);
+    if (structOffset === null)
+      return validationError.ILLEGAL_POINTER;
+
+    if (structOffset === NULL_MOJO_POINTER)
+      return nullable ?
+          validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
+
+    return structClass.validate(this, structOffset);
+  };
+
+  Validator.prototype.validateUnion = function(
+      offset, unionClass, nullable) {
+    var size = this.message.buffer.getUint32(offset);
+    if (size == 0) {
+      return nullable ?
+          validationError.NONE : validationError.UNEXPECTED_NULL_UNION;
+    }
+
+    return unionClass.validate(this, offset);
+  };
+
+  Validator.prototype.validateNestedUnion = function(
+      offset, unionClass, nullable) {
+    var unionOffset = this.decodePointer(offset);
+    if (unionOffset === null)
+      return validationError.ILLEGAL_POINTER;
+
+    if (unionOffset === NULL_MOJO_POINTER)
+      return nullable ?
+          validationError.NONE : validationError.UNEXPECTED_NULL_UNION;
+
+    return this.validateUnion(unionOffset, unionClass, nullable);
+  };
+
+  // This method assumes that the array at arrayPointerOffset has
+  // been validated.
+
+  Validator.prototype.arrayLength = function(arrayPointerOffset) {
+    var arrayOffset = this.decodePointer(arrayPointerOffset);
+    return this.message.buffer.getUint32(arrayOffset + 4);
+  };
+
+  Validator.prototype.validateMapPointer = function(
+      offset, mapIsNullable, keyClass, valueClass, valueIsNullable) {
+    // Validate the implicit map struct:
+    // struct {array<keyClass> keys; array<valueClass> values};
+    var structOffset = this.decodePointer(offset);
+    if (structOffset === null)
+      return validationError.ILLEGAL_POINTER;
+
+    if (structOffset === NULL_MOJO_POINTER)
+      return mapIsNullable ?
+          validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
+
+    var mapEncodedSize = internal.kStructHeaderSize +
+                         internal.kMapStructPayloadSize;
+    var err = this.validateStructHeader(structOffset, mapEncodedSize);
+    if (err !== validationError.NONE)
+        return err;
+
+    // Validate the keys array.
+    var keysArrayPointerOffset = structOffset + internal.kStructHeaderSize;
+    err = this.validateArrayPointer(
+        keysArrayPointerOffset, keyClass.encodedSize, keyClass, false, [0], 0);
+    if (err !== validationError.NONE)
+        return err;
+
+    // Validate the values array.
+    var valuesArrayPointerOffset = keysArrayPointerOffset + 8;
+    var valuesArrayDimensions = [0]; // Validate the actual length below.
+    if (valueClass instanceof internal.ArrayOf)
+      valuesArrayDimensions =
+          valuesArrayDimensions.concat(valueClass.dimensions());
+    var err = this.validateArrayPointer(valuesArrayPointerOffset,
+                                        valueClass.encodedSize,
+                                        valueClass,
+                                        valueIsNullable,
+                                        valuesArrayDimensions,
+                                        0);
+    if (err !== validationError.NONE)
+        return err;
+
+    // Validate the lengths of the keys and values arrays.
+    var keysArrayLength = this.arrayLength(keysArrayPointerOffset);
+    var valuesArrayLength = this.arrayLength(valuesArrayPointerOffset);
+    if (keysArrayLength != valuesArrayLength)
+      return validationError.DIFFERENT_SIZED_ARRAYS_IN_MAP;
+
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateStringPointer = function(offset, nullable) {
+    return this.validateArrayPointer(
+        offset, internal.Uint8.encodedSize, internal.Uint8, nullable, [0], 0);
+  };
+
+  // Similar to Array_Data<T>::Validate()
+  // mojo/public/cpp/bindings/lib/array_internal.h
+
+  Validator.prototype.validateArray =
+      function (offset, elementSize, elementType, expectedDimensionSizes,
+                currentDimension) {
+    if (!internal.isAligned(offset))
+      return validationError.MISALIGNED_OBJECT;
+
+    if (!this.isValidRange(offset, internal.kArrayHeaderSize))
+      return validationError.ILLEGAL_MEMORY_RANGE;
+
+    var numBytes = this.message.buffer.getUint32(offset);
+    var numElements = this.message.buffer.getUint32(offset + 4);
+
+    // Note: this computation is "safe" because elementSize <= 8 and
+    // numElements is a uint32.
+    var elementsTotalSize = (elementType === internal.PackedBool) ?
+        Math.ceil(numElements / 8) : (elementSize * numElements);
+
+    if (numBytes < internal.kArrayHeaderSize + elementsTotalSize)
+      return validationError.UNEXPECTED_ARRAY_HEADER;
+
+    if (expectedDimensionSizes[currentDimension] != 0 &&
+        numElements != expectedDimensionSizes[currentDimension]) {
+      return validationError.UNEXPECTED_ARRAY_HEADER;
+    }
+
+    if (!this.claimRange(offset, numBytes))
+      return validationError.ILLEGAL_MEMORY_RANGE;
+
+    // Validate the array's elements if they are pointers or handles.
+
+    var elementsOffset = offset + internal.kArrayHeaderSize;
+    var nullable = isNullable(elementType);
+
+    if (isHandleClass(elementType))
+      return this.validateHandleElements(elementsOffset, numElements, nullable);
+    if (isInterfaceClass(elementType))
+      return this.validateInterfaceElements(
+          elementsOffset, numElements, nullable);
+    if (isInterfaceRequestClass(elementType))
+      return this.validateInterfaceRequestElements(
+          elementsOffset, numElements, nullable);
+    if (isAssociatedInterfaceClass(elementType))
+      return this.validateAssociatedInterfaceElements(
+          elementsOffset, numElements, nullable);
+    if (isAssociatedInterfaceRequestClass(elementType))
+      return this.validateAssociatedInterfaceRequestElements(
+          elementsOffset, numElements, nullable);
+    if (isStringClass(elementType))
+      return this.validateArrayElements(
+          elementsOffset, numElements, internal.Uint8, nullable, [0], 0);
+    if (elementType instanceof internal.PointerTo)
+      return this.validateStructElements(
+          elementsOffset, numElements, elementType.cls, nullable);
+    if (elementType instanceof internal.ArrayOf)
+      return this.validateArrayElements(
+          elementsOffset, numElements, elementType.cls, nullable,
+          expectedDimensionSizes, currentDimension + 1);
+    if (isEnumClass(elementType))
+      return this.validateEnumElements(elementsOffset, numElements,
+                                       elementType.cls);
+
+    return validationError.NONE;
+  };
+
+  // Note: the |offset + i * elementSize| computation in the validateFooElements
+  // methods below is "safe" because elementSize <= 8, offset and
+  // numElements are uint32, and 0 <= i < numElements.
+
+  Validator.prototype.validateHandleElements =
+      function(offset, numElements, nullable) {
+    var elementSize = internal.Handle.encodedSize;
+    for (var i = 0; i < numElements; i++) {
+      var elementOffset = offset + i * elementSize;
+      var err = this.validateHandle(elementOffset, nullable);
+      if (err != validationError.NONE)
+        return err;
+    }
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateInterfaceElements =
+      function(offset, numElements, nullable) {
+    var elementSize = internal.Interface.prototype.encodedSize;
+    for (var i = 0; i < numElements; i++) {
+      var elementOffset = offset + i * elementSize;
+      var err = this.validateInterface(elementOffset, nullable);
+      if (err != validationError.NONE)
+        return err;
+    }
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateInterfaceRequestElements =
+      function(offset, numElements, nullable) {
+    var elementSize = internal.InterfaceRequest.encodedSize;
+    for (var i = 0; i < numElements; i++) {
+      var elementOffset = offset + i * elementSize;
+      var err = this.validateInterfaceRequest(elementOffset, nullable);
+      if (err != validationError.NONE)
+        return err;
+    }
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateAssociatedInterfaceElements =
+      function(offset, numElements, nullable) {
+    var elementSize = internal.AssociatedInterfacePtrInfo.prototype.encodedSize;
+    for (var i = 0; i < numElements; i++) {
+      var elementOffset = offset + i * elementSize;
+      var err = this.validateAssociatedInterface(elementOffset, nullable);
+      if (err != validationError.NONE) {
+        return err;
+      }
+    }
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateAssociatedInterfaceRequestElements =
+      function(offset, numElements, nullable) {
+    var elementSize = internal.AssociatedInterfaceRequest.encodedSize;
+    for (var i = 0; i < numElements; i++) {
+      var elementOffset = offset + i * elementSize;
+      var err = this.validateAssociatedInterfaceRequest(elementOffset,
+          nullable);
+      if (err != validationError.NONE) {
+        return err;
+      }
+    }
+    return validationError.NONE;
+  };
+
+  // The elementClass parameter is the element type of the element arrays.
+  Validator.prototype.validateArrayElements =
+      function(offset, numElements, elementClass, nullable,
+               expectedDimensionSizes, currentDimension) {
+    var elementSize = internal.PointerTo.prototype.encodedSize;
+    for (var i = 0; i < numElements; i++) {
+      var elementOffset = offset + i * elementSize;
+      var err = this.validateArrayPointer(
+          elementOffset, elementClass.encodedSize, elementClass, nullable,
+          expectedDimensionSizes, currentDimension);
+      if (err != validationError.NONE)
+        return err;
+    }
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateStructElements =
+      function(offset, numElements, structClass, nullable) {
+    var elementSize = internal.PointerTo.prototype.encodedSize;
+    for (var i = 0; i < numElements; i++) {
+      var elementOffset = offset + i * elementSize;
+      var err =
+          this.validateStructPointer(elementOffset, structClass, nullable);
+      if (err != validationError.NONE)
+        return err;
+    }
+    return validationError.NONE;
+  };
+
+  Validator.prototype.validateEnumElements =
+      function(offset, numElements, enumClass) {
+    var elementSize = internal.Enum.prototype.encodedSize;
+    for (var i = 0; i < numElements; i++) {
+      var elementOffset = offset + i * elementSize;
+      var err = this.validateEnum(elementOffset, enumClass);
+      if (err != validationError.NONE)
+        return err;
+    }
+    return validationError.NONE;
+  };
+
+  internal.validationError = validationError;
+  internal.Validator = Validator;
+  internal.ValidationErrorObserverForTesting =
+      ValidationErrorObserverForTesting;
+  internal.reportValidationError = reportValidationError;
+  internal.isTestingMode = isTestingMode;
+  internal.clearTestingMode = clearTestingMode;
+})();
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+(function() {
+  var mojomId = 'mojo/public/interfaces/bindings/new_bindings/interface_control_messages.mojom';
+  if (mojo.internal.isMojomLoaded(mojomId)) {
+    console.warn('The following mojom is loaded multiple times: ' + mojomId);
+    return;
+  }
+  mojo.internal.markMojomLoaded(mojomId);
+
+  // TODO(yzshen): Define these aliases to minimize the differences between the
+  // old/new modes. Remove them when the old mode goes away.
+  var bindings = mojo;
+  var associatedBindings = mojo;
+  var codec = mojo.internal;
+  var validator = mojo.internal;
+
+
+  var kRunMessageId = 0xFFFFFFFF;
+  var kRunOrClosePipeMessageId = 0xFFFFFFFE;
+
+  function RunMessageParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  RunMessageParams.prototype.initDefaults_ = function() {
+    this.input = null;
+  };
+  RunMessageParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  RunMessageParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate RunMessageParams.input
+    err = messageValidator.validateUnion(offset + codec.kStructHeaderSize + 0, RunInput, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  RunMessageParams.encodedSize = codec.kStructHeaderSize + 16;
+
+  RunMessageParams.decode = function(decoder) {
+    var packed;
+    var val = new RunMessageParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.input = decoder.decodeStruct(RunInput);
+    return val;
+  };
+
+  RunMessageParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(RunMessageParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(RunInput, val.input);
+  };
+  function RunResponseMessageParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  RunResponseMessageParams.prototype.initDefaults_ = function() {
+    this.output = null;
+  };
+  RunResponseMessageParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  RunResponseMessageParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate RunResponseMessageParams.output
+    err = messageValidator.validateUnion(offset + codec.kStructHeaderSize + 0, RunOutput, true);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  RunResponseMessageParams.encodedSize = codec.kStructHeaderSize + 16;
+
+  RunResponseMessageParams.decode = function(decoder) {
+    var packed;
+    var val = new RunResponseMessageParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.output = decoder.decodeStruct(RunOutput);
+    return val;
+  };
+
+  RunResponseMessageParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(RunResponseMessageParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(RunOutput, val.output);
+  };
+  function QueryVersion(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  QueryVersion.prototype.initDefaults_ = function() {
+  };
+  QueryVersion.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  QueryVersion.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 8}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  QueryVersion.encodedSize = codec.kStructHeaderSize + 0;
+
+  QueryVersion.decode = function(decoder) {
+    var packed;
+    var val = new QueryVersion();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    return val;
+  };
+
+  QueryVersion.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(QueryVersion.encodedSize);
+    encoder.writeUint32(0);
+  };
+  function QueryVersionResult(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  QueryVersionResult.prototype.initDefaults_ = function() {
+    this.version = 0;
+  };
+  QueryVersionResult.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  QueryVersionResult.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  QueryVersionResult.encodedSize = codec.kStructHeaderSize + 8;
+
+  QueryVersionResult.decode = function(decoder) {
+    var packed;
+    var val = new QueryVersionResult();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.version = decoder.decodeStruct(codec.Uint32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  QueryVersionResult.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(QueryVersionResult.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint32, val.version);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+  function FlushForTesting(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  FlushForTesting.prototype.initDefaults_ = function() {
+  };
+  FlushForTesting.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  FlushForTesting.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 8}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  FlushForTesting.encodedSize = codec.kStructHeaderSize + 0;
+
+  FlushForTesting.decode = function(decoder) {
+    var packed;
+    var val = new FlushForTesting();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    return val;
+  };
+
+  FlushForTesting.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(FlushForTesting.encodedSize);
+    encoder.writeUint32(0);
+  };
+  function RunOrClosePipeMessageParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  RunOrClosePipeMessageParams.prototype.initDefaults_ = function() {
+    this.input = null;
+  };
+  RunOrClosePipeMessageParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  RunOrClosePipeMessageParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate RunOrClosePipeMessageParams.input
+    err = messageValidator.validateUnion(offset + codec.kStructHeaderSize + 0, RunOrClosePipeInput, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  RunOrClosePipeMessageParams.encodedSize = codec.kStructHeaderSize + 16;
+
+  RunOrClosePipeMessageParams.decode = function(decoder) {
+    var packed;
+    var val = new RunOrClosePipeMessageParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.input = decoder.decodeStruct(RunOrClosePipeInput);
+    return val;
+  };
+
+  RunOrClosePipeMessageParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(RunOrClosePipeMessageParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(RunOrClosePipeInput, val.input);
+  };
+  function RequireVersion(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  RequireVersion.prototype.initDefaults_ = function() {
+    this.version = 0;
+  };
+  RequireVersion.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  RequireVersion.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 16}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    return validator.validationError.NONE;
+  };
+
+  RequireVersion.encodedSize = codec.kStructHeaderSize + 8;
+
+  RequireVersion.decode = function(decoder) {
+    var packed;
+    var val = new RequireVersion();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.version = decoder.decodeStruct(codec.Uint32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    return val;
+  };
+
+  RequireVersion.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(RequireVersion.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint32, val.version);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+  };
+
+  function RunInput(value) {
+    this.initDefault_();
+    this.initValue_(value);
+  }
+  
+  
+  RunInput.Tags = {
+    queryVersion: 0,
+    flushForTesting: 1,
+  };
+  
+  RunInput.prototype.initDefault_ = function() {
+    this.$data = null;
+    this.$tag = undefined;
+  }
+  
+  RunInput.prototype.initValue_ = function(value) {
+    if (value == undefined) {
+      return;
+    }
+  
+    var keys = Object.keys(value);
+    if (keys.length == 0) {
+      return;
+    }
+  
+    if (keys.length > 1) {
+      throw new TypeError("You may set only one member on a union.");
+    }
+  
+    var fields = [
+        "queryVersion",
+        "flushForTesting",
+    ];
+  
+    if (fields.indexOf(keys[0]) < 0) {
+      throw new ReferenceError(keys[0] + " is not a RunInput member.");
+  
+    }
+  
+    this[keys[0]] = value[keys[0]];
+  }
+  Object.defineProperty(RunInput.prototype, "queryVersion", {
+    get: function() {
+      if (this.$tag != RunInput.Tags.queryVersion) {
+        throw new ReferenceError(
+            "RunInput.queryVersion is not currently set.");
+      }
+      return this.$data;
+    },
+  
+    set: function(value) {
+      this.$tag = RunInput.Tags.queryVersion;
+      this.$data = value;
+    }
+  });
+  Object.defineProperty(RunInput.prototype, "flushForTesting", {
+    get: function() {
+      if (this.$tag != RunInput.Tags.flushForTesting) {
+        throw new ReferenceError(
+            "RunInput.flushForTesting is not currently set.");
+      }
+      return this.$data;
+    },
+  
+    set: function(value) {
+      this.$tag = RunInput.Tags.flushForTesting;
+      this.$data = value;
+    }
+  });
+  
+  
+    RunInput.encode = function(encoder, val) {
+      if (val == null) {
+        encoder.writeUint64(0);
+        encoder.writeUint64(0);
+        return;
+      }
+      if (val.$tag == undefined) {
+        throw new TypeError("Cannot encode unions with an unknown member set.");
+      }
+    
+      encoder.writeUint32(16);
+      encoder.writeUint32(val.$tag);
+      switch (val.$tag) {
+        case RunInput.Tags.queryVersion:
+          encoder.encodeStructPointer(QueryVersion, val.queryVersion);
+          break;
+        case RunInput.Tags.flushForTesting:
+          encoder.encodeStructPointer(FlushForTesting, val.flushForTesting);
+          break;
+      }
+      encoder.align();
+    };
+  
+  
+    RunInput.decode = function(decoder) {
+      var size = decoder.readUint32();
+      if (size == 0) {
+        decoder.readUint32();
+        decoder.readUint64();
+        return null;
+      }
+    
+      var result = new RunInput();
+      var tag = decoder.readUint32();
+      switch (tag) {
+        case RunInput.Tags.queryVersion:
+          result.queryVersion = decoder.decodeStructPointer(QueryVersion);
+          break;
+        case RunInput.Tags.flushForTesting:
+          result.flushForTesting = decoder.decodeStructPointer(FlushForTesting);
+          break;
+      }
+      decoder.align();
+    
+      return result;
+    };
+  
+  
+    RunInput.validate = function(messageValidator, offset) {
+      var size = messageValidator.decodeUnionSize(offset);
+      if (size != 16) {
+        return validator.validationError.INVALID_UNION_SIZE;
+      }
+    
+      var tag = messageValidator.decodeUnionTag(offset);
+      var data_offset = offset + 8;
+      var err;
+      switch (tag) {
+        case RunInput.Tags.queryVersion:
+          
+    
+    // validate RunInput.queryVersion
+    err = messageValidator.validateStructPointer(data_offset, QueryVersion, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+          break;
+        case RunInput.Tags.flushForTesting:
+          
+    
+    // validate RunInput.flushForTesting
+    err = messageValidator.validateStructPointer(data_offset, FlushForTesting, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+          break;
+      }
+    
+      return validator.validationError.NONE;
+    };
+  
+  RunInput.encodedSize = 16;
+
+  function RunOutput(value) {
+    this.initDefault_();
+    this.initValue_(value);
+  }
+  
+  
+  RunOutput.Tags = {
+    queryVersionResult: 0,
+  };
+  
+  RunOutput.prototype.initDefault_ = function() {
+    this.$data = null;
+    this.$tag = undefined;
+  }
+  
+  RunOutput.prototype.initValue_ = function(value) {
+    if (value == undefined) {
+      return;
+    }
+  
+    var keys = Object.keys(value);
+    if (keys.length == 0) {
+      return;
+    }
+  
+    if (keys.length > 1) {
+      throw new TypeError("You may set only one member on a union.");
+    }
+  
+    var fields = [
+        "queryVersionResult",
+    ];
+  
+    if (fields.indexOf(keys[0]) < 0) {
+      throw new ReferenceError(keys[0] + " is not a RunOutput member.");
+  
+    }
+  
+    this[keys[0]] = value[keys[0]];
+  }
+  Object.defineProperty(RunOutput.prototype, "queryVersionResult", {
+    get: function() {
+      if (this.$tag != RunOutput.Tags.queryVersionResult) {
+        throw new ReferenceError(
+            "RunOutput.queryVersionResult is not currently set.");
+      }
+      return this.$data;
+    },
+  
+    set: function(value) {
+      this.$tag = RunOutput.Tags.queryVersionResult;
+      this.$data = value;
+    }
+  });
+  
+  
+    RunOutput.encode = function(encoder, val) {
+      if (val == null) {
+        encoder.writeUint64(0);
+        encoder.writeUint64(0);
+        return;
+      }
+      if (val.$tag == undefined) {
+        throw new TypeError("Cannot encode unions with an unknown member set.");
+      }
+    
+      encoder.writeUint32(16);
+      encoder.writeUint32(val.$tag);
+      switch (val.$tag) {
+        case RunOutput.Tags.queryVersionResult:
+          encoder.encodeStructPointer(QueryVersionResult, val.queryVersionResult);
+          break;
+      }
+      encoder.align();
+    };
+  
+  
+    RunOutput.decode = function(decoder) {
+      var size = decoder.readUint32();
+      if (size == 0) {
+        decoder.readUint32();
+        decoder.readUint64();
+        return null;
+      }
+    
+      var result = new RunOutput();
+      var tag = decoder.readUint32();
+      switch (tag) {
+        case RunOutput.Tags.queryVersionResult:
+          result.queryVersionResult = decoder.decodeStructPointer(QueryVersionResult);
+          break;
+      }
+      decoder.align();
+    
+      return result;
+    };
+  
+  
+    RunOutput.validate = function(messageValidator, offset) {
+      var size = messageValidator.decodeUnionSize(offset);
+      if (size != 16) {
+        return validator.validationError.INVALID_UNION_SIZE;
+      }
+    
+      var tag = messageValidator.decodeUnionTag(offset);
+      var data_offset = offset + 8;
+      var err;
+      switch (tag) {
+        case RunOutput.Tags.queryVersionResult:
+          
+    
+    // validate RunOutput.queryVersionResult
+    err = messageValidator.validateStructPointer(data_offset, QueryVersionResult, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+          break;
+      }
+    
+      return validator.validationError.NONE;
+    };
+  
+  RunOutput.encodedSize = 16;
+
+  function RunOrClosePipeInput(value) {
+    this.initDefault_();
+    this.initValue_(value);
+  }
+  
+  
+  RunOrClosePipeInput.Tags = {
+    requireVersion: 0,
+  };
+  
+  RunOrClosePipeInput.prototype.initDefault_ = function() {
+    this.$data = null;
+    this.$tag = undefined;
+  }
+  
+  RunOrClosePipeInput.prototype.initValue_ = function(value) {
+    if (value == undefined) {
+      return;
+    }
+  
+    var keys = Object.keys(value);
+    if (keys.length == 0) {
+      return;
+    }
+  
+    if (keys.length > 1) {
+      throw new TypeError("You may set only one member on a union.");
+    }
+  
+    var fields = [
+        "requireVersion",
+    ];
+  
+    if (fields.indexOf(keys[0]) < 0) {
+      throw new ReferenceError(keys[0] + " is not a RunOrClosePipeInput member.");
+  
+    }
+  
+    this[keys[0]] = value[keys[0]];
+  }
+  Object.defineProperty(RunOrClosePipeInput.prototype, "requireVersion", {
+    get: function() {
+      if (this.$tag != RunOrClosePipeInput.Tags.requireVersion) {
+        throw new ReferenceError(
+            "RunOrClosePipeInput.requireVersion is not currently set.");
+      }
+      return this.$data;
+    },
+  
+    set: function(value) {
+      this.$tag = RunOrClosePipeInput.Tags.requireVersion;
+      this.$data = value;
+    }
+  });
+  
+  
+    RunOrClosePipeInput.encode = function(encoder, val) {
+      if (val == null) {
+        encoder.writeUint64(0);
+        encoder.writeUint64(0);
+        return;
+      }
+      if (val.$tag == undefined) {
+        throw new TypeError("Cannot encode unions with an unknown member set.");
+      }
+    
+      encoder.writeUint32(16);
+      encoder.writeUint32(val.$tag);
+      switch (val.$tag) {
+        case RunOrClosePipeInput.Tags.requireVersion:
+          encoder.encodeStructPointer(RequireVersion, val.requireVersion);
+          break;
+      }
+      encoder.align();
+    };
+  
+  
+    RunOrClosePipeInput.decode = function(decoder) {
+      var size = decoder.readUint32();
+      if (size == 0) {
+        decoder.readUint32();
+        decoder.readUint64();
+        return null;
+      }
+    
+      var result = new RunOrClosePipeInput();
+      var tag = decoder.readUint32();
+      switch (tag) {
+        case RunOrClosePipeInput.Tags.requireVersion:
+          result.requireVersion = decoder.decodeStructPointer(RequireVersion);
+          break;
+      }
+      decoder.align();
+    
+      return result;
+    };
+  
+  
+    RunOrClosePipeInput.validate = function(messageValidator, offset) {
+      var size = messageValidator.decodeUnionSize(offset);
+      if (size != 16) {
+        return validator.validationError.INVALID_UNION_SIZE;
+      }
+    
+      var tag = messageValidator.decodeUnionTag(offset);
+      var data_offset = offset + 8;
+      var err;
+      switch (tag) {
+        case RunOrClosePipeInput.Tags.requireVersion:
+          
+    
+    // validate RunOrClosePipeInput.requireVersion
+    err = messageValidator.validateStructPointer(data_offset, RequireVersion, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+          break;
+      }
+    
+      return validator.validationError.NONE;
+    };
+  
+  RunOrClosePipeInput.encodedSize = 16;
+  var exports = mojo.internal.exposeNamespace("mojo.interfaceControl2");
+  exports.kRunMessageId = kRunMessageId;
+  exports.kRunOrClosePipeMessageId = kRunOrClosePipeMessageId;
+  exports.RunMessageParams = RunMessageParams;
+  exports.RunResponseMessageParams = RunResponseMessageParams;
+  exports.QueryVersion = QueryVersion;
+  exports.QueryVersionResult = QueryVersionResult;
+  exports.FlushForTesting = FlushForTesting;
+  exports.RunOrClosePipeMessageParams = RunOrClosePipeMessageParams;
+  exports.RequireVersion = RequireVersion;
+  exports.RunInput = RunInput;
+  exports.RunOutput = RunOutput;
+  exports.RunOrClosePipeInput = RunOrClosePipeInput;
+})();// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+'use strict';
+
+(function() {
+  var mojomId = 'mojo/public/interfaces/bindings/new_bindings/pipe_control_messages.mojom';
+  if (mojo.internal.isMojomLoaded(mojomId)) {
+    console.warn('The following mojom is loaded multiple times: ' + mojomId);
+    return;
+  }
+  mojo.internal.markMojomLoaded(mojomId);
+
+  // TODO(yzshen): Define these aliases to minimize the differences between the
+  // old/new modes. Remove them when the old mode goes away.
+  var bindings = mojo;
+  var associatedBindings = mojo;
+  var codec = mojo.internal;
+  var validator = mojo.internal;
+
+
+  var kRunOrClosePipeMessageId = 0xFFFFFFFE;
+
+  function RunOrClosePipeMessageParams(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  RunOrClosePipeMessageParams.prototype.initDefaults_ = function() {
+    this.input = null;
+  };
+  RunOrClosePipeMessageParams.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  RunOrClosePipeMessageParams.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+    // validate RunOrClosePipeMessageParams.input
+    err = messageValidator.validateUnion(offset + codec.kStructHeaderSize + 0, RunOrClosePipeInput, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  RunOrClosePipeMessageParams.encodedSize = codec.kStructHeaderSize + 16;
+
+  RunOrClosePipeMessageParams.decode = function(decoder) {
+    var packed;
+    var val = new RunOrClosePipeMessageParams();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.input = decoder.decodeStruct(RunOrClosePipeInput);
+    return val;
+  };
+
+  RunOrClosePipeMessageParams.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(RunOrClosePipeMessageParams.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(RunOrClosePipeInput, val.input);
+  };
+  function DisconnectReason(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  DisconnectReason.prototype.initDefaults_ = function() {
+    this.customReason = 0;
+    this.description = null;
+  };
+  DisconnectReason.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  DisconnectReason.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    
+    // validate DisconnectReason.description
+    err = messageValidator.validateStringPointer(offset + codec.kStructHeaderSize + 8, false)
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  DisconnectReason.encodedSize = codec.kStructHeaderSize + 16;
+
+  DisconnectReason.decode = function(decoder) {
+    var packed;
+    var val = new DisconnectReason();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.customReason = decoder.decodeStruct(codec.Uint32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.description = decoder.decodeStruct(codec.String);
+    return val;
+  };
+
+  DisconnectReason.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(DisconnectReason.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint32, val.customReason);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStruct(codec.String, val.description);
+  };
+  function PeerAssociatedEndpointClosedEvent(values) {
+    this.initDefaults_();
+    this.initFields_(values);
+  }
+
+
+  PeerAssociatedEndpointClosedEvent.prototype.initDefaults_ = function() {
+    this.id = 0;
+    this.disconnectReason = null;
+  };
+  PeerAssociatedEndpointClosedEvent.prototype.initFields_ = function(fields) {
+    for(var field in fields) {
+        if (this.hasOwnProperty(field))
+          this[field] = fields[field];
+    }
+  };
+
+  PeerAssociatedEndpointClosedEvent.validate = function(messageValidator, offset) {
+    var err;
+    err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    var kVersionSizes = [
+      {version: 0, numBytes: 24}
+    ];
+    err = messageValidator.validateStructVersion(offset, kVersionSizes);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+
+
+    
+    // validate PeerAssociatedEndpointClosedEvent.disconnectReason
+    err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 8, DisconnectReason, true);
+    if (err !== validator.validationError.NONE)
+        return err;
+
+    return validator.validationError.NONE;
+  };
+
+  PeerAssociatedEndpointClosedEvent.encodedSize = codec.kStructHeaderSize + 16;
+
+  PeerAssociatedEndpointClosedEvent.decode = function(decoder) {
+    var packed;
+    var val = new PeerAssociatedEndpointClosedEvent();
+    var numberOfBytes = decoder.readUint32();
+    var version = decoder.readUint32();
+    val.id = decoder.decodeStruct(codec.Uint32);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    decoder.skip(1);
+    val.disconnectReason = decoder.decodeStructPointer(DisconnectReason);
+    return val;
+  };
+
+  PeerAssociatedEndpointClosedEvent.encode = function(encoder, val) {
+    var packed;
+    encoder.writeUint32(PeerAssociatedEndpointClosedEvent.encodedSize);
+    encoder.writeUint32(0);
+    encoder.encodeStruct(codec.Uint32, val.id);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.skip(1);
+    encoder.encodeStructPointer(DisconnectReason, val.disconnectReason);
+  };
+
+  function RunOrClosePipeInput(value) {
+    this.initDefault_();
+    this.initValue_(value);
+  }
+  
+  
+  RunOrClosePipeInput.Tags = {
+    peerAssociatedEndpointClosedEvent: 0,
+  };
+  
+  RunOrClosePipeInput.prototype.initDefault_ = function() {
+    this.$data = null;
+    this.$tag = undefined;
+  }
+  
+  RunOrClosePipeInput.prototype.initValue_ = function(value) {
+    if (value == undefined) {
+      return;
+    }
+  
+    var keys = Object.keys(value);
+    if (keys.length == 0) {
+      return;
+    }
+  
+    if (keys.length > 1) {
+      throw new TypeError("You may set only one member on a union.");
+    }
+  
+    var fields = [
+        "peerAssociatedEndpointClosedEvent",
+    ];
+  
+    if (fields.indexOf(keys[0]) < 0) {
+      throw new ReferenceError(keys[0] + " is not a RunOrClosePipeInput member.");
+  
+    }
+  
+    this[keys[0]] = value[keys[0]];
+  }
+  Object.defineProperty(RunOrClosePipeInput.prototype, "peerAssociatedEndpointClosedEvent", {
+    get: function() {
+      if (this.$tag != RunOrClosePipeInput.Tags.peerAssociatedEndpointClosedEvent) {
+        throw new ReferenceError(
+            "RunOrClosePipeInput.peerAssociatedEndpointClosedEvent is not currently set.");
+      }
+      return this.$data;
+    },
+  
+    set: function(value) {
+      this.$tag = RunOrClosePipeInput.Tags.peerAssociatedEndpointClosedEvent;
+      this.$data = value;
+    }
+  });
+  
+  
+    RunOrClosePipeInput.encode = function(encoder, val) {
+      if (val == null) {
+        encoder.writeUint64(0);
+        encoder.writeUint64(0);
+        return;
+      }
+      if (val.$tag == undefined) {
+        throw new TypeError("Cannot encode unions with an unknown member set.");
+      }
+    
+      encoder.writeUint32(16);
+      encoder.writeUint32(val.$tag);
+      switch (val.$tag) {
+        case RunOrClosePipeInput.Tags.peerAssociatedEndpointClosedEvent:
+          encoder.encodeStructPointer(PeerAssociatedEndpointClosedEvent, val.peerAssociatedEndpointClosedEvent);
+          break;
+      }
+      encoder.align();
+    };
+  
+  
+    RunOrClosePipeInput.decode = function(decoder) {
+      var size = decoder.readUint32();
+      if (size == 0) {
+        decoder.readUint32();
+        decoder.readUint64();
+        return null;
+      }
+    
+      var result = new RunOrClosePipeInput();
+      var tag = decoder.readUint32();
+      switch (tag) {
+        case RunOrClosePipeInput.Tags.peerAssociatedEndpointClosedEvent:
+          result.peerAssociatedEndpointClosedEvent = decoder.decodeStructPointer(PeerAssociatedEndpointClosedEvent);
+          break;
+      }
+      decoder.align();
+    
+      return result;
+    };
+  
+  
+    RunOrClosePipeInput.validate = function(messageValidator, offset) {
+      var size = messageValidator.decodeUnionSize(offset);
+      if (size != 16) {
+        return validator.validationError.INVALID_UNION_SIZE;
+      }
+    
+      var tag = messageValidator.decodeUnionTag(offset);
+      var data_offset = offset + 8;
+      var err;
+      switch (tag) {
+        case RunOrClosePipeInput.Tags.peerAssociatedEndpointClosedEvent:
+          
+    
+    // validate RunOrClosePipeInput.peerAssociatedEndpointClosedEvent
+    err = messageValidator.validateStructPointer(data_offset, PeerAssociatedEndpointClosedEvent, false);
+    if (err !== validator.validationError.NONE)
+        return err;
+          break;
+      }
+    
+      return validator.validationError.NONE;
+    };
+  
+  RunOrClosePipeInput.encodedSize = 16;
+  var exports = mojo.internal.exposeNamespace("mojo.pipeControl2");
+  exports.kRunOrClosePipeMessageId = kRunOrClosePipeMessageId;
+  exports.RunOrClosePipeMessageParams = RunOrClosePipeMessageParams;
+  exports.DisconnectReason = DisconnectReason;
+  exports.PeerAssociatedEndpointClosedEvent = PeerAssociatedEndpointClosedEvent;
+  exports.RunOrClosePipeInput = RunOrClosePipeInput;
+})();
diff --git a/third_party/WebKit/LayoutTests/usb/resources/webusb-test-impl.js b/third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webusb-test.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/usb/resources/webusb-test-impl.js
rename to third_party/WebKit/LayoutTests/external/wpt/resources/chromium/webusb-test.js
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webusb/OWNERS b/third_party/WebKit/LayoutTests/external/wpt/webusb/OWNERS
index 2a5357e..7cabb58 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webusb/OWNERS
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/OWNERS
@@ -1,2 +1,2 @@
-# TEAM: device-dev@chromium.org
 # COMPONENT: Blink>USB
+# TEAM: device-dev@chromium.org
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webusb/idlharness.https.html b/third_party/WebKit/LayoutTests/external/wpt/webusb/idlharness.https.html
index b4c79c3..1761277 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webusb/idlharness.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/idlharness.https.html
@@ -8,14 +8,24 @@
     <script src=/resources/testharnessreport.js></script>
     <script src=/resources/WebIDLParser.js></script>
     <script src=/resources/idlharness.js></script>
+    <script src=resources/fake-devices.js></script>
+    <script src=resources/usb-helpers.js></script>
   </head>
   <body>
     <script>
       'use strict';
 
+      // Object instances used by the IDL test.
+      var usbDevice;
+      var usbConfiguration;
+      var usbInterface;
+      var usbAlternateInterface;
+      var usbEndpoint;
+      var usbConnectionEvent;
+
       promise_test(async () => {
         let response = await fetch('/interfaces/webusb.idl');
-        let idl_text = response.text();
+        let idl_text = await response.text();
         let idl_array = new IdlArray();
         idl_array.add_idls(idl_text);
 
@@ -37,6 +47,34 @@
           USBIsochronousOutTransferPacket: ['new USBIsochronousOutTransferPacket("ok")'],
         });
 
+        // If the WebUSB Test API is available then interfaces requiring an
+        // instance of USBDevice can be tested.
+        try {
+          await navigator.usb.test.initialize();
+          navigator.usb.test.addFakeDevice(fakeDeviceInit);
+          usbDevice = await new Promise(resolve => {
+            navigator.usb.onconnect = e => resolve(e.device);
+          });
+
+          usbConfiguration = usbDevice.configurations[0];
+          usbInterface = usbConfiguration.interfaces[0];
+          usbAlternateInterface = usbInterface.alternates[0];
+          usbEndpoint = usbAlternateInterface.endpoints[0];
+          usbConnectionEvent =
+              new USBConnectionEvent('connect', { device: usbDevice })
+
+          idl_array.add_objects({
+            USBAlternateInterface: ['usbAlternateInterface'],
+            USBConfiguration: ['usbConfiguration'],
+            USBConnectionEvent: ['usbConnectionEvent'],
+            USBDevice: ['usbDevice'],
+            USBEndpoint: ['usbEndpoint'],
+            USBInterface: ['usbInterface']
+          });
+        } catch (e) {
+          // Do nothing.
+        }
+
         idl_array.test();
       }, 'WebUSB IDL test');
     </script>
diff --git a/third_party/WebKit/LayoutTests/usb/resources/fake-devices.js b/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/fake-devices.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/usb/resources/fake-devices.js
rename to third_party/WebKit/LayoutTests/external/wpt/webusb/resources/fake-devices.js
diff --git a/third_party/WebKit/LayoutTests/usb/resources/open-in-iframe.html b/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/open-in-iframe.html
similarity index 93%
rename from third_party/WebKit/LayoutTests/usb/resources/open-in-iframe.html
rename to third_party/WebKit/LayoutTests/external/wpt/webusb/resources/open-in-iframe.html
index 83c5a30..0b04a3e0 100644
--- a/third_party/WebKit/LayoutTests/usb/resources/open-in-iframe.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/open-in-iframe.html
@@ -7,7 +7,6 @@
       connectEvent.device.open().then(() => {
         parent.postMessage('Success', '*');
       }).catch(error => {
-        console.log(error);
         parent.postMessage('FAIL: open rejected ' + error, '*');
       });
     });
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/usb-helpers.js b/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/usb-helpers.js
new file mode 100644
index 0000000..452c04f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/resources/usb-helpers.js
@@ -0,0 +1,128 @@
+'use strict';
+
+// These tests rely on the User Agent providing an implementation of the
+// WebUSB Testing API (https://wicg.github.io/webusb/test/).
+//
+// In Chromium-based browsers this implementation is provided by a polyfill
+// in order to reduce the amount of test-only code shipped to users. To enable
+// these tests the browser must be run with these options:
+//
+//   --enable-blink-features=MojoJS,MojoJSTest
+let loadChromiumResources = Promise.resolve().then(() => {
+  if (!MojoInterfaceInterceptor) {
+    // Do nothing on non-Chromium-based browsers or when the Mojo bindings are
+    // not present in the global namespace.
+    return;
+  }
+
+  let chain = Promise.resolve();
+  [
+    '/resources/chromium/mojo_bindings.js',
+    '/resources/chromium/device.mojom.js',
+    '/resources/chromium/device_manager.mojom.js',
+    '/resources/chromium/chooser_service.mojom.js',
+    '/resources/chromium/webusb-test.js',
+  ].forEach(path => {
+    let script = document.createElement('script');
+    script.src = path;
+    script.async = false;
+    chain = chain.then(() => new Promise(resolve => {
+      script.onload = () => resolve();
+    }));
+    document.head.appendChild(script);
+  });
+
+  return chain;
+});
+
+function usb_test(func, name, properties) {
+  promise_test(async () => {
+    if (navigator.usb.test === undefined) {
+      // Try loading a polyfill for the WebUSB Testing API.
+      await loadChromiumResources;
+    }
+
+    await navigator.usb.test.initialize();
+    try {
+      await func();
+    } finally {
+      await navigator.usb.test.reset();
+    }
+  }, name, properties);
+}
+
+// Returns a promise that is resolved when the next USBConnectionEvent of the
+// given type is received.
+function connectionEventPromise(eventType) {
+  return new Promise(resolve => {
+    let eventHandler = e => {
+      assert_true(e instanceof USBConnectionEvent);
+      navigator.usb.removeEventListener(eventType, eventHandler);
+      resolve(e.device);
+    };
+    navigator.usb.addEventListener(eventType, eventHandler);
+  });
+}
+
+// Creates a fake device and returns a promise that resolves once the
+// 'connect' event is fired for the fake device. The promise is resolved with
+// an object containing the fake USB device and the corresponding USBDevice.
+function getFakeDevice() {
+  let promise = connectionEventPromise('connect');
+  let fakeDevice = navigator.usb.test.addFakeDevice(fakeDeviceInit);
+  return promise.then(device => {
+    return { device: device, fakeDevice: fakeDevice };
+  });
+}
+
+// Disconnects the given device and returns a promise that is resolved when it
+// is done.
+function waitForDisconnect(fakeDevice) {
+  let promise = connectionEventPromise('disconnect');
+  fakeDevice.disconnect();
+  return promise;
+}
+
+function assertRejectsWithError(promise, name, message) {
+  return promise.then(() => {
+    assert_unreached('expected promise to reject with ' + name);
+  }, error => {
+    assert_equals(error.name, name);
+    if (message !== undefined)
+      assert_equals(error.message, message);
+  });
+}
+
+function assertDeviceInfoEquals(usbDevice, deviceInit) {
+  for (var property in deviceInit) {
+    if (property == 'activeConfigurationValue') {
+      if (deviceInit.activeConfigurationValue == 0) {
+        assert_equals(usbDevice.configuration, null);
+      } else {
+        assert_equals(usbDevice.configuration.configurationValue,
+                      deviceInit.activeConfigurationValue);
+      }
+    } else if (Array.isArray(deviceInit[property])) {
+      assert_equals(usbDevice[property].length, deviceInit[property].length);
+      for (var i = 0; i < usbDevice[property].length; ++i)
+        assertDeviceInfoEquals(usbDevice[property][i], deviceInit[property][i]);
+    } else {
+      assert_equals(usbDevice[property], deviceInit[property], property);
+    }
+  }
+}
+
+function callWithTrustedClick(callback) {
+  return new Promise(resolve => {
+    let button = document.createElement('button');
+    button.textContent = 'click to continue test';
+    button.style.display = 'block';
+    button.style.fontSize = '20px';
+    button.style.padding = '10px';
+    button.onclick = () => {
+      resolve(callback());
+      document.body.removeChild(button);
+    };
+    document.body.appendChild(button);
+  });
+}
diff --git a/third_party/WebKit/LayoutTests/usb/usb.html b/third_party/WebKit/LayoutTests/external/wpt/webusb/usb-manual.https.html
similarity index 78%
rename from third_party/WebKit/LayoutTests/usb/usb.html
rename to third_party/WebKit/LayoutTests/external/wpt/webusb/usb-manual.https.html
index 16c659c..7a8c03a 100644
--- a/third_party/WebKit/LayoutTests/usb/usb.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/usb-manual.https.html
@@ -1,9 +1,8 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script src="resources/fake-devices.js"></script>
 <script src="resources/usb-helpers.js"></script>
-<script src="resources/webusb-test.js"></script>
 <script>
 'use strict';
 
@@ -39,7 +38,7 @@
 }, 'requestDevice rejects when called without a user gesture');
 
 usb_test(() => {
-  return callWithKeyDown(() => navigator.usb.requestDevice({ filters: [] })
+  return callWithTrustedClick(() => navigator.usb.requestDevice({ filters: [] })
     .then(device => {
       assert_unreachable('requestDevice should reject when no device selected');
     })
@@ -52,7 +51,7 @@
 usb_test(() => {
   return getFakeDevice().then(({ device, fakeDevice }) => {
     navigator.usb.test.chosenDevice = fakeDevice;
-    return callWithKeyDown(() => {
+    return callWithTrustedClick(() => {
       return navigator.usb.requestDevice({ filters: [] }).then(chosenDevice => {
         assert_equals(chosenDevice, device);
       });
@@ -63,7 +62,7 @@
 usb_test(() => {
   return getFakeDevice().then(({ device, fakeDevice }) => {
     navigator.usb.test.chosenDevice = fakeDevice;
-    return callWithKeyDown(() => {
+    return callWithTrustedClick(() => {
       return navigator.usb.requestDevice({ filters: [] }).then(chosenDevice => {
         assert_equals(chosenDevice, device);
         return navigator.usb.getDevices().then(devices => {
@@ -81,18 +80,19 @@
     { vendorId: 5678, productId: 0xF00F }
   ];
 
-  return callWithKeyDown(() => navigator.usb.requestDevice({ filters: expectedFilters })
-    .then(device => {
-      assert_unreachable('requestDevice should reject because no device selected');
-    })
-    .catch(error => {
-      assert_equals(error.code, DOMException.NOT_FOUND_ERR);
-      let actualFilters = navigator.usb.test.lastFilters;
-      assert_equals(actualFilters.length, expectedFilters.length);
-      for (var i = 0; i < actualFilters.length; ++i)
-        assert_object_equals(actualFilters[i], expectedFilters[i]);
-    })
-  );
+  return callWithTrustedClick(() => {
+    return navigator.usb.requestDevice({ filters: expectedFilters })
+      .then(device => {
+        assert_unreachable('requestDevice should reject because no device selected');
+      })
+      .catch(error => {
+        assert_equals(error.code, DOMException.NOT_FOUND_ERR);
+        let actualFilters = navigator.usb.test.lastFilters;
+        assert_equals(actualFilters.length, expectedFilters.length);
+        for (var i = 0; i < actualFilters.length; ++i)
+          assert_object_equals(actualFilters[i], expectedFilters[i]);
+      });
+  });
 }, 'filters are sent correctly');
 
 usb_test(() => {
diff --git a/third_party/WebKit/LayoutTests/usb/usb-connection-event.html b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbConnectionEvent.https.html
similarity index 80%
rename from third_party/WebKit/LayoutTests/usb/usb-connection-event.html
rename to third_party/WebKit/LayoutTests/external/wpt/webusb/usbConnectionEvent.https.html
index 59753f9..90b547f 100644
--- a/third_party/WebKit/LayoutTests/usb/usb-connection-event.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbConnectionEvent.https.html
@@ -1,9 +1,8 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script src="resources/fake-devices.js"></script>
 <script src="resources/usb-helpers.js"></script>
-<script src="resources/webusb-test.js"></script>
 <script>
 'use strict';
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webusb/usbDevice-iframe.https.html b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbDevice-iframe.https.html
new file mode 100644
index 0000000..02b2c50
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbDevice-iframe.https.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/fake-devices.js"></script>
+<script src="resources/usb-helpers.js"></script>
+<script>
+'use strict';
+
+function runIframeDisconnectTest(onDeviceConnected) {
+  return new Promise((resolve, reject) => {
+    let opened = false;
+
+    let iframe = document.createElement('iframe');
+    iframe.src = 'resources/open-in-iframe.html';
+    iframe.onload = () => {
+      navigator.usb.test.attachToWindow(iframe.contentWindow).then(() => {
+        iframe.contentWindow.postMessage('Ready', '*');
+      });
+    };
+
+    window.onmessage = messageEvent => {
+      if (messageEvent.data == 'Ready') {
+        let fakeDevice = navigator.usb.test.addFakeDevice(fakeDeviceInit);
+        fakeDevice.onclose = () => {
+          assert_true(opened);
+          resolve();
+        };
+      } else if (messageEvent.data == 'Success') {
+        opened = true;
+        onDeviceConnected(iframe);
+      } else {
+        reject(messageEvent.data);
+      }
+    };
+
+    document.body.appendChild(iframe);
+  });
+}
+
+usb_test(() => {
+  return runIframeDisconnectTest(iframe => {
+    document.body.removeChild(iframe);
+  });
+}, 'detaching iframe disconnects device.');
+
+usb_test(() => {
+  return runIframeDisconnectTest(iframe => {
+    iframe.src = 'about:blank';
+  });
+}, 'navigating iframe disconnects device.');
+</script>
diff --git a/third_party/WebKit/LayoutTests/usb/usbDevice.html b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbDevice.https.html
similarity index 98%
rename from third_party/WebKit/LayoutTests/usb/usbDevice.html
rename to third_party/WebKit/LayoutTests/external/wpt/webusb/usbDevice.https.html
index 56a467ef..b167e70 100644
--- a/third_party/WebKit/LayoutTests/usb/usbDevice.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbDevice.https.html
@@ -1,9 +1,8 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script src="resources/fake-devices.js"></script>
 <script src="resources/usb-helpers.js"></script>
-<script src="resources/webusb-test.js"></script>
 <script>
 'use strict';
 
diff --git a/third_party/WebKit/LayoutTests/usb/usb-in-transfer-result.html b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbInTransferResult.https.html
similarity index 88%
rename from third_party/WebKit/LayoutTests/usb/usb-in-transfer-result.html
rename to third_party/WebKit/LayoutTests/external/wpt/webusb/usbInTransferResult.https.html
index 8f08030..c176d28 100644
--- a/third_party/WebKit/LayoutTests/usb/usb-in-transfer-result.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbInTransferResult.https.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 'use strict';
 
diff --git a/third_party/WebKit/LayoutTests/usb/usb-isochronous-in-transfer-packet.html b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbIsochronousInTransferPacket.https.html
similarity index 89%
rename from third_party/WebKit/LayoutTests/usb/usb-isochronous-in-transfer-packet.html
rename to third_party/WebKit/LayoutTests/external/wpt/webusb/usbIsochronousInTransferPacket.https.html
index a2c0a013..c2aa78a 100644
--- a/third_party/WebKit/LayoutTests/usb/usb-isochronous-in-transfer-packet.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbIsochronousInTransferPacket.https.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 'use strict';
 
diff --git a/third_party/WebKit/LayoutTests/usb/usb-isochronous-in-transfer-result.html b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbIsochronousInTransferResult.https.html
similarity index 92%
rename from third_party/WebKit/LayoutTests/usb/usb-isochronous-in-transfer-result.html
rename to third_party/WebKit/LayoutTests/external/wpt/webusb/usbIsochronousInTransferResult.https.html
index 8241150..da7c395 100644
--- a/third_party/WebKit/LayoutTests/usb/usb-isochronous-in-transfer-result.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbIsochronousInTransferResult.https.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 'use strict';
 
diff --git a/third_party/WebKit/LayoutTests/usb/usb-isochronous-out-transfer-packet.html b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbIsochronousOutTransferPacket.https.html
similarity index 86%
rename from third_party/WebKit/LayoutTests/usb/usb-isochronous-out-transfer-packet.html
rename to third_party/WebKit/LayoutTests/external/wpt/webusb/usbIsochronousOutTransferPacket.https.html
index af5dbc6..137d0fb 100644
--- a/third_party/WebKit/LayoutTests/usb/usb-isochronous-out-transfer-packet.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbIsochronousOutTransferPacket.https.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 'use strict';
 
diff --git a/third_party/WebKit/LayoutTests/usb/usb-isochronous-out-transfer-result.html b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbIsochronousOutTransferResult.https.html
similarity index 86%
rename from third_party/WebKit/LayoutTests/usb/usb-isochronous-out-transfer-result.html
rename to third_party/WebKit/LayoutTests/external/wpt/webusb/usbIsochronousOutTransferResult.https.html
index 8d7c6c3f..ae105ca 100644
--- a/third_party/WebKit/LayoutTests/usb/usb-isochronous-out-transfer-result.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbIsochronousOutTransferResult.https.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 'use strict';
 
diff --git a/third_party/WebKit/LayoutTests/usb/usb-out-transfer-result.html b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbOutTransferResult.https.html
similarity index 85%
rename from third_party/WebKit/LayoutTests/usb/usb-out-transfer-result.html
rename to third_party/WebKit/LayoutTests/external/wpt/webusb/usbOutTransferResult.https.html
index 92f4de5..7d6a63c 100644
--- a/third_party/WebKit/LayoutTests/usb/usb-out-transfer-result.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/webusb/usbOutTransferResult.https.html
@@ -1,6 +1,6 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <script>
 'use strict';
 
diff --git a/third_party/WebKit/LayoutTests/fast/text/hide-atomic-inlines-after-ellipsis-expected.txt b/third_party/WebKit/LayoutTests/fast/text/hide-atomic-inlines-after-ellipsis-expected.txt
new file mode 100644
index 0000000..385ad22
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/hide-atomic-inlines-after-ellipsis-expected.txt
@@ -0,0 +1,52 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x144
+  LayoutBlockFlow {HTML} at (0,0) size 800x144
+    LayoutBlockFlow {BODY} at (8,16) size 784x120
+      LayoutBlockFlow {P} at (0,0) size 784x20
+        LayoutText {#text} at (0,0) size 486x19
+          text run at (0,0) width 486: "crbug.com/534798: Atomic inline elements should be hidden after an ellipsis."
+layer at (8,52) size 500x42 scrollWidth 701
+  LayoutBlockFlow {DIV} at (0,36) size 500x42 [bgcolor=#D3D3D3]
+    LayoutBlockFlow (floating) {SPAN} at (88,0) size 12x12 [bgcolor=#0000FF] [border: (1px solid #000000)]
+    LayoutInline {SPAN} at (0,0) size 162x21 [border: (1px solid #000000)]
+      LayoutText {#text} at (1,21) size 160x19
+        text run at (1,21) width 160: "01234567890123456789"
+    LayoutBlockFlow {SPAN} at (162,0) size 113x42 [border: (1px solid #000000)]
+      LayoutInline {SPAN} at (0,0) size 25x21 [border: (1px solid #000000)]
+        LayoutText {#text} at (2,1) size 23x19
+          text run at (2,1) width 23: "text"
+      LayoutText {#text} at (26,1) size 75x19
+        text run at (26,1) width 75: "inline-block"
+      LayoutBR {BR} at (101,1) size 0x19
+      LayoutText {#text} at (1,21) size 111x19
+        text run at (1,21) width 111: "more inline-block"
+    LayoutBlockFlow {SPAN} at (275,34) size 2x2 [border: (1px solid #000000)]
+    LayoutBlockFlow {IMG} at (277,21) size 24x20
+      LayoutInline {SPAN} at (0,0) size 24x19
+        LayoutInline {SPAN} at (0,0) size 24x19
+          LayoutText {#text} at (0,0) size 24x19
+            text run at (0,0) width 24: "img"
+    LayoutText {#text} at (0,0) size 0x0
+layer at (8,94) size 500x42 scrollX 201.00 scrollWidth 701
+  LayoutBlockFlow {DIV} at (0,78) size 500x42 [bgcolor=#D3D3D3]
+    LayoutBlockFlow (floating) {SPAN} at (400,0) size 12x12 [bgcolor=#0000FF] [border: (1px solid #000000)]
+    LayoutInline {SPAN} at (0,0) size 162x21 [border: (1px solid #000000)]
+      LayoutText {#text} at (339,21) size 160x19
+        text run at (339,21) width 160: "01234567890123456789"
+    LayoutBlockFlow {SPAN} at (225,0) size 113x42 [border: (1px solid #000000)]
+      LayoutInline {SPAN} at (0,0) size 25x21 [border: (1px solid #000000)]
+        LayoutText {#text} at (13,1) size 23x19
+          text run at (13,1) width 23: "text"
+      LayoutText {#text} at (37,1) size 75x19
+        text run at (37,1) width 75: "inline-block"
+      LayoutBR {BR} at (12,1) size 0x19
+      LayoutText {#text} at (1,21) size 111x19
+        text run at (1,21) width 111: "more inline-block"
+    LayoutBlockFlow {SPAN} at (223,34) size 2x2 [border: (1px solid #000000)]
+    LayoutBlockFlow {IMG} at (199,21) size 24x20
+      LayoutInline {SPAN} at (0,0) size 24x19
+        LayoutInline {SPAN} at (0,0) size 24x19
+          LayoutText {#text} at (0,0) size 24x19
+            text run at (0,0) width 24: "img"
+    LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/fast/text/hide-atomic-inlines-after-ellipsis.html b/third_party/WebKit/LayoutTests/fast/text/hide-atomic-inlines-after-ellipsis.html
new file mode 100644
index 0000000..25ed9a40
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/text/hide-atomic-inlines-after-ellipsis.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<style>
+.container {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  width: 100px;
+  padding-right: 400px;
+  background: lightgrey;
+}
+
+.rtl {
+  direction: rtl;
+  padding-left: 400px;
+  padding-right: 0px;
+}
+
+.float {
+  float: right;
+  width: 10px;
+  height: 10px;
+  background-color: blue;
+}
+.inline-block {
+  display: inline-block;
+  border: 1px solid black;
+}
+span {
+  border: 1px solid black;
+}
+</style>
+<p>crbug.com/534798: Atomic inline elements should be hidden after an ellipsis.</p>
+<div class="container">
+  <span class="float"></span><span>01234567890123456789</span><span class="inline-block"><span>text</span>inline-block<br>more inline-block</span><span class="inline-block"></span><img src="f" alt="img">
+</div>
+<div class="container rtl">
+  <span class="float" style="float:left;"></span><span>01234567890123456789</span><span class="inline-block"><span>text</span>inline-block<br>more inline-block</span><span class="inline-block"></span><img src="f" alt="img">
+</div>
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota-expected.txt
index b2222f9..e3ffed62 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota-expected.txt
@@ -5,8 +5,11 @@
 
 Tree element found: true
 Clear storage view is visible: true
-0 B storage quota used out of -
+0 B used out of -- storage quota
+Usage breakdown:
 
 Running: Now with data
-9.5 MB storage quota used out of -
+9.5 MB used out of -- storage quota
+Usage breakdown:
+IndexedDB: 9.5 MB
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota.html b/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota.html
index 20fdf24..4f83275 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/application-panel/storage-view-reports-quota.html
@@ -30,8 +30,21 @@
     });
     // Quota will vary between setups, rather strip it altogether
     var clean = view._quotaRow.innerHTML.replace(/\&nbsp;/g, ' ');
-    var quotaStripped = clean.replace(/(.*) \d+ .?B([^\d]*)/, '$1 -$2');
+    var quotaStripped = clean.replace(/(.*) \d+ .?B([^\d]*)/, '$1 --$2');
     InspectorTest.addResult(quotaStripped);
+
+    InspectorTest.addResult('Usage breakdown:');
+    for (var i = 0; i < view._pieChartLegend.children.length; i++) {
+      var typeUsage = ': ';
+      var children = view._pieChartLegend.children[i].children;
+      for (var j = 0; j < children.length; j++) {
+        if (children[j].classList.contains('usage-breakdown-legend-title'))
+          typeUsage = children[j].textContent + typeUsage;
+        if (children[j].classList.contains('usage-breakdown-legend-value'))
+          typeUsage = typeUsage + children[j].textContent;
+      }
+      InspectorTest.addResult(typeUsage);
+    }
   }
   UI.viewManager.showView('resources');
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-create-start-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-create-start-expected.txt
index 692dd303..45f747e2 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-create-start-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-create-start-expected.txt
@@ -1,3 +1,4 @@
+
 Animation created
 Animation started
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-create-start.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-create-start.html
deleted file mode 100644
index b60d5b5..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-create-start.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function startAnimation()
-{
-    node.animate([{ width: "100px" }, { width: "200px" }], 2000);
-}
-
-function test()
-{
-    InspectorTest.eventHandler["Animation.animationCreated"] = onCreated;
-    InspectorTest.eventHandler["Animation.animationStarted"] = onStarted;
-    InspectorTest.sendCommand("Animation.enable", {});
-    InspectorTest.evaluateInPage("startAnimation()", function() {});
-
-    function onCreated()
-    {
-        InspectorTest.log("Animation created");
-    }
-
-    function onStarted()
-    {
-        InspectorTest.log("Animation started");
-        InspectorTest.completeTest();
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-    <div id="node" style="background-color: red; height: 100px"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-create-start.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-create-start.js
new file mode 100644
index 0000000..b9beac3f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-create-start.js
@@ -0,0 +1,13 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='node' style='background-color: red; height: 100px'></div>
+  `, '');
+
+  dp.Animation.enable();
+  session.evaluate(`node.animate([{ width: '100px' }, { width: '200px' }], 2000);`);
+  await dp.Animation.onceAnimationCreated();
+  testRunner.log('Animation created');
+  await dp.Animation.onceAnimationStarted();
+  testRunner.log('Animation started');
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-css-replay-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-css-replay-expected.txt
index 2dc4bc2..63e18fa 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-css-replay-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-css-replay-expected.txt
@@ -1,3 +1,4 @@
+
 Animation started
 Animation seeked
 Animation seeked
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-css-replay.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-css-replay.html
deleted file mode 100644
index d958879..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-css-replay.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-var player;
-
-function startAnimation()
-{
-    node.style.width = "200px";
-}
-
-function test()
-{
-    InspectorTest.eventHandler["Animation.animationStarted"] = onStarted;
-    InspectorTest.sendCommand("Animation.enable", {});
-    InspectorTest.evaluateInPage("startAnimation()", function() {});
-    setTimeout(InspectorTest.completeTest.bind(InspectorTest), 2000);
-    function onStarted(response)
-    {
-        InspectorTest.log("Animation started");
-        seekAnimations(response.params.animation.id, 5);
-    }
-
-    function seekAnimations(id, runs)
-    {
-        if (runs == 0)
-            InspectorTest.completeTest();
-
-        InspectorTest.sendCommand("Animation.seekAnimations", { animations: [ id ], currentTime: 0 }, seekAnimations.bind(null, id, --runs));
-        InspectorTest.log("Animation seeked");
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-    <div id="node" style="background-color: red; height: 100px; transition: width 100ms; width: 100px"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-css-replay.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-css-replay.js
new file mode 100644
index 0000000..ed77716
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-css-replay.js
@@ -0,0 +1,17 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='node' style='background-color: red; height: 100px; transition: width 100ms; width: 100px'></div>
+  `, '');
+
+  dp.Animation.enable();
+  session.evaluate(`node.style.width = '200px';`);
+
+  var response = await dp.Animation.onceAnimationStarted();
+  testRunner.log("Animation started");
+
+  for (var run = 0; run < 5; run++) {
+    await dp.Animation.seekAnimations({ animations: [ response.params.animation.id ], currentTime: 0 });
+    testRunner.log("Animation seeked");
+  }
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-empty-transition-cancel-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-empty-transition-cancel-expected.txt
index a725a91..411484a7 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-empty-transition-cancel-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-empty-transition-cancel-expected.txt
@@ -1,3 +1,4 @@
+
 Animation created
 Animation canceled
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-empty-transition-cancel.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-empty-transition-cancel.html
deleted file mode 100644
index 910a0aa..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-empty-transition-cancel.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function triggerEmptyTransition()
-{
-    node.offsetTop;
-    node.style.transition = "1s";
-    node.offsetTop;
-    node.style.width = "200px";
-    node.offsetTop;
-    node.style.transition = "";
-    node.offsetTop;
-}
-
-function test()
-{
-    InspectorTest.eventHandler["Animation.animationCreated"] = onCreated;
-    InspectorTest.eventHandler["Animation.animationCanceled"] = onCanceled;
-    InspectorTest.sendCommand("Animation.enable", {});
-    InspectorTest.evaluateInPage("triggerEmptyTransition()", function() {});
-
-    function onCreated()
-    {
-        InspectorTest.log("Animation created");
-    }
-
-    function onCanceled()
-    {
-        InspectorTest.log("Animation canceled");
-        InspectorTest.completeTest();
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-    <div id="node" style="background-color: red; width: 100px"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-empty-transition-cancel.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-empty-transition-cancel.js
new file mode 100644
index 0000000..0fd2256
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-empty-transition-cancel.js
@@ -0,0 +1,19 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='node' style='background-color: red; width: 100px'></div>
+  `, '');
+
+  dp.Animation.onAnimationCreated(() => testRunner.log('Animation created'));
+  dp.Animation.onAnimationCanceled(() => testRunner.log('Animation canceled'));
+  dp.Animation.enable();
+  await session.evaluate(`
+    node.offsetTop;
+    node.style.transition = "1s";
+    node.offsetTop;
+    node.style.width = "200px";
+    node.offsetTop;
+    node.style.transition = "";
+    node.offsetTop;
+  `);
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-multiple-frames.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-multiple-frames.html
deleted file mode 100644
index 4ca4899..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-multiple-frames.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!doctype html>
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-function appendIframe()
-{
-    var frame = document.createElement("iframe");
-    frame.src = "../resources/test-page-trigger-animation.html";
-    document.body.appendChild(frame);
-}
-
-function test()
-{
-    InspectorTest.eventHandler["Animation.animationStarted"] = onStarted;
-    InspectorTest.sendCommand("Animation.enable", {});
-    InspectorTest.evaluateInPage("appendIframe()", frameAttached);
-    var numberAnimationsCaptured = 0;
-    var lastStartTime = undefined;
-
-    function frameAttached()
-    {
-        InspectorTest.log("Frame appended");
-    }
-
-    function onStarted(data)
-    {
-        var animation = data.params.animation;
-
-        if (!lastStartTime || animation.startTime >= lastStartTime)
-            InspectorTest.log("Animation started: start time is valid");
-        else if (lastStartTime)
-            InspectorTest.log("Animation started: invalid startTime!" + animation.startTime + "." + lastStartTime);
-        lastStartTime = animation.startTime;
-        numberAnimationsCaptured++;
-
-        if (numberAnimationsCaptured < 10)
-            InspectorTest.evaluateInPage("appendIframe()", frameAttached);
-        else
-            InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onload="runTest()">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-multiple-frames.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-multiple-frames.js
new file mode 100644
index 0000000..dcf19cf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-multiple-frames.js
@@ -0,0 +1,38 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  await session.evaluate(`
+    function appendIframe() {
+      var frame = document.createElement('iframe');
+      frame.src = '${testRunner.url('../resources/test-page-trigger-animation.html')}';
+      document.body.appendChild(frame);
+    }
+  `);
+
+  async function appendFrame() {
+    await session.evaluate(`appendIframe()`);
+    testRunner.log('Frame appended');
+  }
+
+  var numberAnimationsCaptured = 0;
+  var lastStartTime = undefined;
+
+  dp.Animation.onAnimationStarted(data => {
+    var animation = data.params.animation;
+
+    if (!lastStartTime || animation.startTime >= lastStartTime)
+      testRunner.log('Animation started: start time is valid');
+    else if (lastStartTime)
+      testRunner.log('Animation started: invalid startTime!' + animation.startTime + '.' + lastStartTime);
+    lastStartTime = animation.startTime;
+    numberAnimationsCaptured++;
+
+    if (numberAnimationsCaptured < 10)
+      appendFrame();
+    else
+      testRunner.completeTest();
+  });
+
+  dp.Animation.enable();
+  appendFrame();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-close-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-close-expected.txt
index 66dce0e..4873c3a2 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-close-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-close-expected.txt
@@ -1,3 +1,4 @@
+
 0
 1
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-close.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-close.html
deleted file mode 100644
index 2a75c5abf..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-close.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function test()
-{
-    InspectorTest.sendCommandOrDie("Animation.enable", {});
-    InspectorTest.sendCommandOrDie("Animation.setPlaybackRate", { playbackRate: 0 }, pagePaused);
-
-    function pagePaused()
-    {
-        InspectorTest.sendCommandOrDie("Animation.getPlaybackRate", {}, logPlaybackRate);
-    }
-
-    function logPlaybackRate(result)
-    {
-        InspectorTest.log(result.playbackRate);
-        InspectorTest.sendCommandOrDie("Animation.disable", {}, pageDisabled);
-    }
-
-    function pageDisabled(message)
-    {
-        InspectorTest.sendCommandOrDie("Animation.enable", {});
-        InspectorTest.sendCommandOrDie("Animation.getPlaybackRate", {}, animationsDisabled);
-    }
-
-    function animationsDisabled(result)
-    {
-        InspectorTest.log(result.playbackRate);
-        InspectorTest.completeTest();
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-close.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-close.js
new file mode 100644
index 0000000..f099a2cf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-close.js
@@ -0,0 +1,11 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  dp.Animation.enable();
+  await dp.Animation.setPlaybackRate({ playbackRate: 0 });
+  testRunner.log((await dp.Animation.getPlaybackRate()).result.playbackRate);
+  await dp.Animation.disable();
+  await dp.Animation.enable();
+  testRunner.log((await dp.Animation.getPlaybackRate()).result.playbackRate);
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-infinite.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-infinite.html
deleted file mode 100644
index 7560b28e..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-infinite.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-var animation;
-
-function startAnimation()
-{
-    animation = node.animate([{ width: "100px" }, { width: "2000px" }], { duration: 10000, iterations: Infinity });
-}
-
-function getWidth()
-{
-    return node.offsetWidth;
-}
-
-function rafWidth()
-{
-    var callback;
-    var promise = new Promise((fulfill) => callback = fulfill);
-    if (window.testRunner)
-        testRunner.layoutAndPaintAsyncThen(() => callback(node.offsetWidth));
-    return promise;
-}
-
-function test()
-{
-    InspectorTest.eventHandler["Animation.animationStarted"] = onStarted;
-    InspectorTest.sendCommand("Animation.enable", {});
-    InspectorTest.evaluateInPage("startAnimation()", function() {});
-
-    function onStarted(response)
-    {
-        InspectorTest.log("Animation started");
-        InspectorTest.sendCommand("Animation.setPaused", { animations: [ response.params.animation.id ], paused: true }, animPaused);
-    }
-
-    function animPaused()
-    {
-        InspectorTest.evaluateInPage("getWidth()", saveWidth);
-    }
-
-    function saveWidth(nodeWidth)
-    {
-        InspectorTest.evaluateInPageAsync("rafWidth()")
-            .then(function(result) {
-                InspectorTest.log(result === nodeWidth);
-                InspectorTest.completeTest();
-            });
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-    Tests that the animation is correctly paused.
-    <div id="node" style="background-color: red; height: 100px"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-infinite.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-infinite.js
new file mode 100644
index 0000000..f094c19
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause-infinite.js
@@ -0,0 +1,27 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='node' style='background-color: red; height: 100px'></div>
+  `, 'Tests that the animation is correctly paused.');
+
+  dp.Animation.enable();
+  session.evaluate(`
+    window.animation = node.animate([{ width: '100px' }, { width: '2000px' }], { duration: 10000, iterations: Infinity });
+  `);
+
+  var response = await dp.Animation.onceAnimationStarted();
+  testRunner.log('Animation started');
+  await dp.Animation.setPaused({ animations: [ response.params.animation.id ], paused: true });
+
+  var nodeWidth = await session.evaluate('node.offsetWidth');
+  var rafWidth = await session.evaluateAsync(`
+    (function rafWidth() {
+        var callback;
+        var promise = new Promise((fulfill) => callback = fulfill);
+        if (window.testRunner)
+            testRunner.layoutAndPaintAsyncThen(() => callback(node.offsetWidth));
+        return promise;
+    })()
+  `);
+  testRunner.log(rafWidth === nodeWidth);
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause.html
deleted file mode 100644
index 3a73546..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-var animation;
-
-function startAnimation()
-{
-    animation = node.animate([{ width: "100px" }, { width: "200px" }], 2000);
-}
-
-function getWidth()
-{
-    return node.offsetWidth;
-}
-
-function rafWidth()
-{
-    var callback;
-    var promise = new Promise((fulfill) => callback = fulfill);
-    if (window.testRunner)
-        testRunner.layoutAndPaintAsyncThen(() => callback(node.offsetWidth));
-    return promise;
-}
-
-function test()
-{
-    InspectorTest.eventHandler["Animation.animationStarted"] = onStarted;
-    InspectorTest.sendCommand("Animation.enable", {});
-    InspectorTest.evaluateInPage("startAnimation()", function() {});
-
-    function onStarted(response)
-    {
-        InspectorTest.log("Animation started");
-        InspectorTest.sendCommand("Animation.setPaused", { animations: [ response.params.animation.id ], paused: true }, animPaused);
-    }
-
-    function animPaused()
-    {
-        InspectorTest.evaluateInPage("getWidth()", saveWidth);
-    }
-
-    function saveWidth(nodeWidth)
-    {
-        InspectorTest.evaluateInPageAsync("rafWidth()")
-            .then(function(result) {
-                InspectorTest.log(result === nodeWidth);
-                InspectorTest.completeTest();
-            });
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-    Tests that the animation is correctly paused.
-    <div id="node" style="background-color: red; height: 100px"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause.js
new file mode 100644
index 0000000..842206bf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-pause.js
@@ -0,0 +1,27 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='node' style='background-color: red; height: 100px'></div>
+  `, 'Tests that the animation is correctly paused.');
+
+  dp.Animation.enable();
+  session.evaluate(`
+    window.animation = node.animate([{ width: '100px' }, { width: '200px' }], 2000);
+  `);
+
+  var response = await dp.Animation.onceAnimationStarted();
+  testRunner.log('Animation started');
+  await dp.Animation.setPaused({ animations: [ response.params.animation.id ], paused: true });
+
+  var nodeWidth = await session.evaluate('node.offsetWidth');
+  var rafWidth = await session.evaluateAsync(`
+    (function rafWidth() {
+        var callback;
+        var promise = new Promise((fulfill) => callback = fulfill);
+        if (window.testRunner)
+            testRunner.layoutAndPaintAsyncThen(() => callback(node.offsetWidth));
+        return promise;
+    })()
+  `);
+  testRunner.log(rafWidth === nodeWidth);
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-paused-css-animation-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-paused-css-animation-expected.txt
index a725a91..411484a7 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-paused-css-animation-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-paused-css-animation-expected.txt
@@ -1,3 +1,4 @@
+
 Animation created
 Animation canceled
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-paused-css-animation.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-paused-css-animation.html
deleted file mode 100644
index 6c0eb91b..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-paused-css-animation.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<style type="text/css">
-#node.anim {
-    animation: anim 300ms ease-in-out paused;
-}
-
-@keyframes anim {
-    from {
-        width: 100px;
-    }
-    to {
-        width: 200px;
-    }
-}
-</style>
-<script>
-
-function startAnimation()
-{
-    node.classList.add("anim");
-}
-
-function test()
-{
-    InspectorTest.eventHandler["Animation.animationCreated"] = onCreated;
-    InspectorTest.eventHandler["Animation.animationCanceled"] = onCanceled;
-    InspectorTest.sendCommand("Animation.enable", {});
-    InspectorTest.evaluateInPage("startAnimation()", function() {});
-
-    function onCreated()
-    {
-        InspectorTest.log("Animation created");
-    }
-
-    function onCanceled()
-    {
-        InspectorTest.log("Animation canceled");
-        InspectorTest.completeTest();
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-    <div id="node" style="background-color: red; width: 100px"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-paused-css-animation.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-paused-css-animation.js
new file mode 100644
index 0000000..a52764b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-paused-css-animation.js
@@ -0,0 +1,28 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <style type='text/css'>
+    #node.anim {
+        animation: anim 300ms ease-in-out paused;
+    }
+
+    @keyframes anim {
+        from {
+            width: 100px;
+        }
+        to {
+            width: 200px;
+        }
+    }
+    </style>
+    <div id='node' style='background-color: red; width: 100px'></div>
+  `, '');
+
+
+  dp.Animation.enable();
+  session.evaluate('node.classList.add("anim");');
+  await dp.Animation.onceAnimationCreated();
+  testRunner.log('Animation created');
+  await dp.Animation.onceAnimationCanceled();
+  testRunner.log('Animation canceled');
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-release.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-release.html
deleted file mode 100644
index a120ce8a..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-release.html
+++ /dev/null
@@ -1,65 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-var animation;
-
-function startAnimation()
-{
-    animation = node.animate([{ width: "100px" }, { width: "2000px" }], { duration: 0, fill: "forwards" });
-}
-
-function cancelAnimation()
-{
-    animation.cancel();
-}
-
-function getWidth()
-{
-    return node.offsetWidth;
-}
-
-function test()
-{
-    InspectorTest.eventHandler["Animation.animationStarted"] = onStarted;
-    InspectorTest.sendCommand("Animation.enable", {});
-    InspectorTest.evaluateInPage("startAnimation()", function() {});
-
-    function onStarted(response)
-    {
-        InspectorTest.log("Animation started");
-        InspectorTest.evaluateInPage("getWidth()", pause.bind(null, response.params.animation.id));
-    }
-
-    function pause(id, width)
-    {
-        InspectorTest.log("Box is animating: " + (width != 100).toString());
-        InspectorTest.sendCommand("Animation.setPaused", { animations: [ id ], paused: true });
-        InspectorTest.evaluateInPage("cancelAnimation()", function() {});
-        InspectorTest.evaluateInPage("getWidth()", release.bind(null, id));
-    }
-
-    function release(id, width)
-    {
-        InspectorTest.log("Animation paused");
-        InspectorTest.log("Box is animating: " + (width != 100).toString());
-        InspectorTest.sendCommand("Animation.releaseAnimations", { animations: [ id ] });
-        InspectorTest.evaluateInPage("getWidth()", released);
-    }
-
-    function released(width)
-    {
-        InspectorTest.log("Animation released");
-        InspectorTest.log("Box is animating: " + (width != 100).toString());
-        InspectorTest.completeTest();
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-    Tests that the animation is correctly paused.
-    <div id="node" style="background-color: red; width: 100px"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-release.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-release.js
new file mode 100644
index 0000000..2103a62
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-release.js
@@ -0,0 +1,25 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='node' style='background-color: red; width: 100px'></div>
+  `, 'Tests that the animation is correctly paused.');
+
+  dp.Animation.enable();
+  session.evaluate(`
+    window.animation = node.animate([{ width: '100px' }, { width: '2000px' }], { duration: 0, fill: 'forwards' });
+  `);
+
+  var id = (await dp.Animation.onceAnimationStarted()).params.animation.id;
+  testRunner.log('Animation started');
+  var width = await session.evaluate('node.offsetWidth');
+  testRunner.log('Box is animating: ' + (width != 100).toString());
+  dp.Animation.setPaused({ animations: [ id ], paused: true });
+  session.evaluate('animation.cancel()');
+  width = await session.evaluate('node.offsetWidth');
+  testRunner.log('Animation paused');
+  testRunner.log('Box is animating: ' + (width != 100).toString());
+  dp.Animation.releaseAnimations({ animations: [ id ] });
+  width = await session.evaluate('node.offsetWidth');
+  testRunner.log('Animation released');
+  testRunner.log('Box is animating: ' + (width != 100).toString());
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-resolve-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-resolve-expected.txt
index d515803..9e19f321 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-resolve-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-resolve-expected.txt
@@ -1,3 +1,4 @@
+
 Animation started
 Remote object:
 Animation
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-resolve.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-resolve.html
deleted file mode 100644
index 8c2f6f64..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-resolve.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-var player;
-
-function startAnimation()
-{
-    player = node.animate([{ width: "100px" }, { width: "200px" }], 2000);
-}
-
-function test()
-{
-    InspectorTest.eventHandler["Animation.animationStarted"] = onStarted;
-    InspectorTest.sendCommand("Animation.enable", {});
-    InspectorTest.evaluateInPage("startAnimation()", function() {});
-
-    function onStarted(result)
-    {
-        InspectorTest.log("Animation started");
-        InspectorTest.sendCommand("Animation.resolveAnimation", { animationId: result.params.animation.id }, animationResolved);
-    }
-
-    function animationResolved(response)
-    {
-        InspectorTest.log("Remote object:");
-        InspectorTest.log(response.result.remoteObject.className);
-        InspectorTest.completeTest();
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-    <div id="node" style="background-color: red; height: 100px"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-resolve.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-resolve.js
new file mode 100644
index 0000000..6bc0e5a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-resolve.js
@@ -0,0 +1,17 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='node' style='background-color: red; height: 100px'></div>
+  `, '');
+
+  dp.Animation.enable();
+  session.evaluate(`
+    window.player = node.animate([{ width: '100px' }, { width: '200px' }], 2000);
+  `);
+
+  var result = await dp.Animation.onceAnimationStarted();
+  testRunner.log('Animation started');
+  var response = await dp.Animation.resolveAnimation({ animationId: result.params.animation.id });
+  testRunner.log('Remote object:');
+  testRunner.log(response.result.remoteObject.className);
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-seek-past-end-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-seek-past-end-expected.txt
index 1de3003..797aa06 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-seek-past-end-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-seek-past-end-expected.txt
@@ -1,3 +1,4 @@
+
 Animation started
 100
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-seek-past-end.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-seek-past-end.html
deleted file mode 100644
index bad36fe8..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-seek-past-end.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-function startAnimation()
-{
-    node.animate([{ width: "1000px" }, { width: "2000px" }], 1000);
-}
-
-function rafWidth(resolve, reject)
-{
-    var callback;
-    var promise = new Promise((fulfill) => callback = fulfill);
-    if (window.testRunner)
-        testRunner.layoutAndPaintAsyncThen(() => callback(node.offsetWidth));
-    return promise;
-}
-
-function test()
-{
-    InspectorTest.eventHandler["Animation.animationStarted"] = onStarted;
-    InspectorTest.sendCommand("Animation.enable", {});
-    InspectorTest.evaluateInPage("startAnimation()", function() {});
-
-    function onStarted(response)
-    {
-        InspectorTest.log("Animation started");
-        seekAnimation(response.params.animation.id);
-    }
-
-    function seekAnimation(id)
-    {
-        InspectorTest.sendCommand("Animation.seekAnimations", { animations: [ id ], currentTime: 2000 }, logWidth);
-    }
-
-    function logWidth()
-    {
-        InspectorTest.evaluateInPageAsync("rafWidth()")
-            .then(function(result) {
-                InspectorTest.log(result);
-                InspectorTest.completeTest();
-            });
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-    <div id="node" style="background-color: red; height: 100px; width: 100px"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-seek-past-end.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-seek-past-end.js
new file mode 100644
index 0000000..53ecb41
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-seek-past-end.js
@@ -0,0 +1,26 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='node' style='background-color: red; height: 100px; width: 100px'></div>
+  `, '');
+
+  dp.Animation.enable();
+  session.evaluate(`
+    node.animate([{ width: '1000px' }, { width: '2000px' }], 1000);
+  `);
+
+  var response = await dp.Animation.onceAnimationStarted();
+  testRunner.log('Animation started');
+  await dp.Animation.seekAnimations({ animations: [ response.params.animation.id ], currentTime: 2000 });
+
+  var rafWidth = await session.evaluateAsync(`
+    (function rafWidth() {
+        var callback;
+        var promise = new Promise((fulfill) => callback = fulfill);
+        if (window.testRunner)
+            testRunner.layoutAndPaintAsyncThen(() => callback(node.offsetWidth));
+        return promise;
+    })()
+  `);
+  testRunner.log(rafWidth);
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-start-cancel-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-start-cancel-expected.txt
index 39a3de5..3fc6b00 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-start-cancel-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-start-cancel-expected.txt
@@ -1,3 +1,4 @@
+
 Animation created
 Animation started
 Animation canceled
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-start-cancel.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-start-cancel.html
deleted file mode 100644
index 094d53a..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-start-cancel.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function triggerEmptyTransition()
-{
-    node.style.transition = "1s";
-    node.offsetTop;
-    node.style.width = "200px";
-    node.offsetTop;
-    // Deliberately delay for two RAFs, which causes the animation to start
-    // before we cancel it by clearing the transition.
-    window.requestAnimationFrame(function() {
-      window.requestAnimationFrame(function() {
-        node.style.transition = "";
-      });
-    });
-}
-
-function test()
-{
-    InspectorTest.eventHandler["Animation.animationCreated"] = onCreated;
-    InspectorTest.eventHandler["Animation.animationStarted"] = onStarted;
-    InspectorTest.eventHandler["Animation.animationCanceled"] = onCanceled;
-    InspectorTest.sendCommand("Animation.enable", {});
-    InspectorTest.evaluateInPage("triggerEmptyTransition()", function() {});
-
-    function onCreated()
-    {
-        InspectorTest.log("Animation created");
-    }
-
-    function onStarted()
-    {
-        InspectorTest.log("Animation started");
-    }
-
-    function onCanceled()
-    {
-        InspectorTest.log("Animation canceled");
-        InspectorTest.completeTest();
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-    <div id="node" style="background-color: red; width: 100px"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-start-cancel.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-start-cancel.js
new file mode 100644
index 0000000..015d7a37
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-start-cancel.js
@@ -0,0 +1,27 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='node' style='background-color: red; width: 100px'></div>
+  `, '');
+
+  dp.Animation.onAnimationCreated(() => testRunner.log('Animation created'));
+  dp.Animation.onAnimationStarted(() => testRunner.log('Animation started'));
+  dp.Animation.onAnimationCanceled(() => {
+    testRunner.log('Animation canceled')
+    testRunner.completeTest();
+  });
+  dp.Animation.enable();
+
+  session.evaluate(`
+    node.style.transition = '1s';
+    node.offsetTop;
+    node.style.width = '200px';
+    node.offsetTop;
+    // Deliberately delay for two RAFs, which causes the animation to start
+    // before we cancel it by clearing the transition.
+    window.requestAnimationFrame(function() {
+      window.requestAnimationFrame(function() {
+        node.style.transition = '';
+      });
+    });
+  `);
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-zero-duration-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-zero-duration-expected.txt
index 692dd303..45f747e2 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-zero-duration-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-zero-duration-expected.txt
@@ -1,3 +1,4 @@
+
 Animation created
 Animation started
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-zero-duration.html b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-zero-duration.html
deleted file mode 100644
index b4df7455..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-zero-duration.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<style type="text/css">
-#node.anim {
-    animation: anim 0 ease-in-out;
-}
-
-@keyframes anim {
-    from {
-        width: 100px;
-    }
-    to {
-        width: 200px;
-    }
-}
-</style>
-<script>
-
-function startAnimation()
-{
-    node.classList.add("anim");
-}
-
-function test()
-{
-    InspectorTest.eventHandler["Animation.animationCreated"] = onCreated;
-    InspectorTest.eventHandler["Animation.animationStarted"] = onStarted;
-    InspectorTest.sendCommand("Animation.enable", {});
-    InspectorTest.evaluateInPage("startAnimation()", function() {});
-
-    function onCreated()
-    {
-        InspectorTest.log("Animation created");
-    }
-
-    function onStarted()
-    {
-        InspectorTest.log("Animation started");
-        InspectorTest.completeTest();
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-    <div id="node" style="background-color: red; width: 100px"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-zero-duration.js b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-zero-duration.js
new file mode 100644
index 0000000..d39a9402
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/animation/animation-zero-duration.js
@@ -0,0 +1,27 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <style type='text/css'>
+    #node.anim {
+        animation: anim 0 ease-in-out;
+    }
+
+    @keyframes anim {
+        from {
+            width: 100px;
+        }
+        to {
+            width: 200px;
+        }
+    }
+    </style>
+    <div id='node' style='background-color: red; width: 100px'></div>
+  `, '');
+
+  dp.Animation.enable();
+  session.evaluate('node.classList.add("anim")');
+  await dp.Animation.onceAnimationCreated();
+  testRunner.log('Animation created');
+  await dp.Animation.onceAnimationStarted();
+  testRunner.log('Animation started');
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-dom-mutation-violations-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-dom-mutation-violations-expected.txt
index 326e772..03b0db9b 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-dom-mutation-violations-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-dom-mutation-violations-expected.txt
@@ -1,3 +1,4 @@
+
 {
     method : Log.entryAdded
     params : {
@@ -11,13 +12,13 @@
                         columnNumber : 14
                         functionName : 
                         lineNumber : 0
-                        scriptId : <string>
+                        scriptId : <scriptId>
                         url : 
                     }
                 ]
             }
             text : Added synchronous DOM mutation listener to a 'DOMSubtreeModified' event. Consider using MutationObserver to make the page more responsive.
-            timestamp : <number>
+            timestamp : <timestamp>
         }
     }
 }
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-dom-mutation-violations.html b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-dom-mutation-violations.html
deleted file mode 100644
index cec90b3..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-dom-mutation-violations.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function test()
-{
-    InspectorTest.eventHandler["Log.entryAdded"] = (message) => {
-        InspectorTest.logObject(message, '', ['timestamp', 'scriptId']);
-    };
-
-    InspectorTest.sendCommand("Log.enable", {});
-    InspectorTest.sendCommand("Log.startViolationsReport", {config: [{name: 'discouragedAPIUse', threshold: -1}]});
-    InspectorTest.sendCommand("Runtime.evaluate", { expression: "document.body.addEventListener('DOMSubtreeModified', () => {})" },
-        InspectorTest.completeTest.bind(InspectorTest));
-    
-}
-
-</script>
-</head>
-<body onload="runTest()">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-dom-mutation-violations.js b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-dom-mutation-violations.js
new file mode 100644
index 0000000..c1c53c6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-dom-mutation-violations.js
@@ -0,0 +1,8 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+  dp.Log.onEntryAdded(testRunner.logMessage.bind(testRunner));
+  dp.Log.enable();
+  dp.Log.startViolationsReport({config: [{name: 'discouragedAPIUse', threshold: -1}]});
+  await session.evaluate(`document.body.addEventListener('DOMSubtreeModified', () => {})`);
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api-expected.txt
index 678f00d..96202017 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api-expected.txt
@@ -1,5 +1,6 @@
-first "let a = 1;" result: wasThrown = false
-second "let a = 1;" result: wasThrown = true
+
+first 'let a = 1;' result: wasThrown = false
+second 'let a = 1;' result: wasThrown = true
 exception message: Uncaught SyntaxError: Identifier 'a' has already been declared
     at <anonymous>:1:1
 {"result":{"type":"number","value":42,"description":"42"}}
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api.html b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api.html
deleted file mode 100644
index f7d785d..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function test()
-{
-    InspectorTest.sendCommand("Runtime.evaluate", { expression: "let a = 42;" }, step2);
-
-    function step2(response)
-    {
-        failIfError(response);
-        InspectorTest.log("first \"let a = 1;\" result: wasThrown = " + !!response.result.exceptionDetails);
-        InspectorTest.sendCommand("Runtime.evaluate", { expression: "let a = 239;" }, step3);
-    }
-
-    function step3(response)
-    {
-        failIfError(response);
-        InspectorTest.log("second \"let a = 1;\" result: wasThrown = " + !!response.result.exceptionDetails);
-        if (response.result.exceptionDetails)
-            InspectorTest.log("exception message: " + response.result.exceptionDetails.text + " " + response.result.exceptionDetails.exception.description);
-        InspectorTest.sendCommand("Runtime.evaluate", { expression: "a" }, step4);
-    }
-
-    function step4(response)
-    {
-        failIfError(response);
-        InspectorTest.log(JSON.stringify(response.result));
-        checkMethod(null);
-    }
-
-    var methods = [ "$", "$$", "$x", "dir", "dirxml", "keys", "values", "profile", "profileEnd",
-        "monitorEvents", "unmonitorEvents", "inspect", "copy", "clear", "getEventListeners",
-        "debug", "undebug", "monitor", "unmonitor", "table" ];
-
-    function checkMethod(response)
-    {
-        failIfError(response);
-
-        if (response)
-            InspectorTest.log(response.result.result.description);
-
-        var method = methods.shift();
-        if (!method)
-            InspectorTest.completeTest();
-
-        InspectorTest.sendCommand("Runtime.evaluate", { expression: method, includeCommandLineAPI: true }, checkMethod);
-    }
-
-    function failIfError(response)
-    {
-        if (response && response.error) {
-            InspectorTest.log("FAIL: " + JSON.stringify(response.error));
-        }
-    }
-}
-
-</script>
-</head>
-<body onload="runTest()">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api.js b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api.js
new file mode 100644
index 0000000..fb650b9f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-let-const-with-api.js
@@ -0,0 +1,34 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  var response = await dp.Runtime.evaluate({expression: 'let a = 42;'});
+  failIfError(response);
+  testRunner.log(`first 'let a = 1;' result: wasThrown = ${!!response.result.exceptionDetails}`);
+
+  response = await dp.Runtime.evaluate({expression: 'let a = 239;'});
+  failIfError(response);
+  testRunner.log(`second 'let a = 1;' result: wasThrown = ${!!response.result.exceptionDetails}`);
+  if (response.result.exceptionDetails)
+    testRunner.log('exception message: ' + response.result.exceptionDetails.text + ' ' + response.result.exceptionDetails.exception.description);
+
+  response = await dp.Runtime.evaluate({expression: 'a'});
+  failIfError(response);
+  testRunner.log(JSON.stringify(response.result));
+
+  var methods = [ '$', '$$', '$x', 'dir', 'dirxml', 'keys', 'values', 'profile', 'profileEnd',
+      'monitorEvents', 'unmonitorEvents', 'inspect', 'copy', 'clear', 'getEventListeners',
+      'debug', 'undebug', 'monitor', 'unmonitor', 'table' ];
+  for (var method of methods) {
+    response = await dp.Runtime.evaluate({expression: method, includeCommandLineAPI: true});
+    failIfError(response);
+    testRunner.log(response.result.result.description);
+  }
+  testRunner.completeTest();
+
+  function failIfError(response) {
+    if (response && response.error) {
+      testRunner.log('FAIL: ' + JSON.stringify(response.error));
+      testRunner.completeTest();
+    }
+  }
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-memory-setter-in-strict-mode-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-memory-setter-in-strict-mode-expected.txt
index f432d7a..fd36400 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-memory-setter-in-strict-mode-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-memory-setter-in-strict-mode-expected.txt
@@ -1,6 +1,6 @@
 Tests checks that console.memory property can be set in strict mode (crbug.com/468611).
 {
-    id : 0
+    id : <messageId>
     result : {
         result : {
             type : undefined
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-memory-setter-in-strict-mode.html b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-memory-setter-in-strict-mode.html
deleted file mode 100644
index 9843704..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-memory-setter-in-strict-mode.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-function test()
-{
-    InspectorTest.sendCommand("Runtime.evaluate", { expression: "\"use strict\"\nconsole.memory = {};undefined" }, dumpResult);
-
-    function dumpResult(result)
-    {
-        result.id = 0;
-        InspectorTest.logObject(result);
-        InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onload="runTest()">Tests checks that console.memory property can be set in strict mode (crbug.com/468611).</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-memory-setter-in-strict-mode.js b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-memory-setter-in-strict-mode.js
new file mode 100644
index 0000000..d9c89df
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-memory-setter-in-strict-mode.js
@@ -0,0 +1,6 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('Tests checks that console.memory property can be set in strict mode (crbug.com/468611).');
+  var message = await dp.Runtime.evaluate({expression: '"use strict"\nconsole.memory = {};undefined'});
+  testRunner.logMessage(message);
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profile-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profile-expected.txt
index 25e7707..23f26cfd 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profile-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profile-expected.txt
@@ -1,4 +1,4 @@
 Tests that console.profile/profileEnd will record CPU profile when inspector front-end is connected.
-SUCCESS: retrieved '42' profile
-SUCCESS: found 'collectProfiles' function in the profile
+SUCCESS: retrieved "42" profile
+SUCCESS: found "collectProfiles" function in the profile
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profile.html b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profile.html
deleted file mode 100644
index 492816fb..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profile.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-function collectProfiles()
-{
-    console.profile("outer");
-    console.profile(42);
-    console.profileEnd("outer");
-    console.profileEnd(42);
-}
-
-function test()
-{
-    InspectorTest.fail = function(message)
-    {
-        InspectorTest.log("FAIL: " + message);
-        InspectorTest.completeTest();
-    }
-
-    InspectorTest.sendCommand("Profiler.enable", {});
-    InspectorTest.sendCommand("Runtime.evaluate", { expression: "collectProfiles()"}, didCollectProfiles);
-
-    var headers = [];
-    InspectorTest.eventHandler["Profiler.consoleProfileFinished"] = function(messageObject)
-    {
-        headers.push({
-            profile: messageObject["params"]["profile"],
-            title: messageObject["params"]["title"]
-        });
-    }
-
-    function didCollectProfiles(messageObject)
-    {
-        if (headers.length !== 2)
-            return InspectorTest.fail("Cannot retrive headers: " + JSON.stringify(messageObject, null, 4));
-        for (var i = 0; i < headers.length; i++) {
-            if (headers[i].title === "42") {
-                checkInnerProfile(headers[i].profile);
-                return;
-            }
-        }
-        InspectorTest.fail("Cannot find '42' profile header");
-    }
-
-    function checkInnerProfile(profile)
-    {
-        InspectorTest.log("SUCCESS: retrieved '42' profile");
-        if (!findFunctionInProfile(profile.nodes, "collectProfiles"))
-            return InspectorTest.fail("collectProfiles function not found in the profile: " + JSON.stringify(profile, null, 4));
-        InspectorTest.log("SUCCESS: found 'collectProfiles' function in the profile");
-        InspectorTest.completeTest();
-    }
-
-    function findFunctionInProfile(nodes, functionName)
-    {
-        return nodes.some(n => n.callFrame.functionName === functionName);
-    }
-}
-</script>
-</head>
-<body onload="runTest()">
-<p>
-Tests that console.profile/profileEnd will record CPU profile when inspector front-end is connected.<br>
-</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profile.js b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profile.js
new file mode 100644
index 0000000..91abc27
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profile.js
@@ -0,0 +1,43 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('Tests that console.profile/profileEnd will record CPU profile when inspector front-end is connected.');
+
+  function fail(message) {
+    testRunner.log('FAIL: ' + message);
+    testRunner.completeTest();
+  }
+
+  function findFunctionInProfile(nodes, functionName) {
+    return nodes.some(n => n.callFrame.functionName === functionName);
+  }
+
+  var headers = [];
+  dp.Profiler.onConsoleProfileFinished(messageObject => {
+    headers.push({profile: messageObject['params']['profile'], title: messageObject['params']['title']});
+  });
+
+  dp.Profiler.enable();
+  await session.evaluate(`
+    (function collectProfiles() {
+      console.profile('outer');
+      console.profile(42);
+      console.profileEnd('outer');
+      console.profileEnd(42);
+    })();
+  `);
+
+  if (headers.length !== 2)
+    return fail('Cannot retrive headers: ' + JSON.stringify(messageObject, null, 4));
+
+  for (var header of headers) {
+    if (header.title === '42') {
+      testRunner.log('SUCCESS: retrieved "42" profile');
+      if (!findFunctionInProfile(header.profile.nodes, 'collectProfiles'))
+        return fail('collectProfiles function not found in the profile: ' + JSON.stringify(header.profile, null, 4));
+      testRunner.log('SUCCESS: found "collectProfiles" function in the profile');
+      testRunner.completeTest();
+      return;
+    }
+  }
+
+  fail('Cannot find "42" profile header');
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profileEnd-parameterless-crash.html b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profileEnd-parameterless-crash.html
deleted file mode 100644
index 6a9d4c3..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profileEnd-parameterless-crash.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-function collectProfiles()
-{
-    console.profile();
-    console.profile("titled");
-    console.profileEnd();
-    console.profileEnd();
-}
-
-function test()
-{
-    InspectorTest.fail = function(message)
-    {
-        InspectorTest.log("FAIL: " + message);
-        InspectorTest.completeTest();
-    }
-
-    InspectorTest.sendCommand("Profiler.enable", {});
-    InspectorTest.sendCommand("Runtime.evaluate", { expression: "collectProfiles()"}, didCollectProfiles);
-
-    var headers = [];
-    InspectorTest.eventHandler["Profiler.consoleProfileFinished"] = function(messageObject)
-    {
-        headers.push({
-            title: messageObject["params"]["title"]
-        });
-    }
-
-    function didCollectProfiles(messageObject)
-    {
-        if (headers.length !== 2)
-            return InspectorTest.fail("Cannot retrive headers: " + JSON.stringify(messageObject, null, 4));
-        InspectorTest.log("SUCCESS: found 2 profile headers");
-        for (var i = 0; i < headers.length; i++) {
-            if (headers[i].title === "titled") {
-                InspectorTest.log("SUCCESS: titled profile found");
-                InspectorTest.completeTest();
-                return;
-            }
-        }
-        InspectorTest.fail("Cannot find titled profile");
-    }
-}
-</script>
-</head>
-<body onload="runTest()">
-<p>
-Tests that "console.profileEnd()" does not cause crash.<br>
-<a href="https://bugs.webkit.org/show_bug.cgi?id=105759">Bug 105759.</a><br>
-</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profileEnd-parameterless-crash.js b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profileEnd-parameterless-crash.js
new file mode 100644
index 0000000..6cdc9ee4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/console-profileEnd-parameterless-crash.js
@@ -0,0 +1,38 @@
+(async function(testRunner) {
+  // <a href='https://bugs.webkit.org/show_bug.cgi?id=105759'>Bug 105759.</a>
+  let {page, session, dp} = await testRunner.startBlank('Tests that "console.profileEnd()" does not cause crash.\nBug 105759.');
+
+  function fail(message) {
+    testRunner.log('FAIL: ' + message);
+    testRunner.completeTest();
+  }
+
+  var headers = [];
+  dp.Profiler.onConsoleProfileFinished(messageObject => {
+    headers.push({title: messageObject['params']['title']});
+  });
+
+  dp.Profiler.enable();
+  await session.evaluate(`
+    (function collectProfiles() {
+      console.profile();
+      console.profile('titled');
+      console.profileEnd();
+      console.profileEnd();
+    })();
+  `);
+
+
+  if (headers.length !== 2)
+    return fail('Cannot retrive headers: ' + JSON.stringify(messageObject, null, 4));
+
+  testRunner.log('SUCCESS: found 2 profile headers');
+  for (var header of headers) {
+    if (header.title === 'titled') {
+      testRunner.log('SUCCESS: titled profile found');
+      testRunner.completeTest();
+      return;
+    }
+  }
+  fail('Cannot find titled profile');
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/enable-disable-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/enable-disable-expected.txt
index c7d8f7c..59be7ce 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/enable-disable-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/enable-disable-expected.txt
@@ -1,5 +1,4 @@
 Test that profiling can only be started when Profiler was enabled and that Profiler.disable command will stop recording all profiles.
-
 PASS: didFailToStartWhenDisabled
 PASS: didStartFrontendProfile
 PASS: console initiated profile started
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/enable-disable.html b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/enable-disable.html
deleted file mode 100644
index 713348d..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/enable-disable.html
+++ /dev/null
@@ -1,89 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function test()
-{
-    InspectorTest.sendCommand("Profiler.start", {}, didFailToStartWhenDisabled);
-    disallowConsoleProfiles();
-
-    function disallowConsoleProfiles()
-    {
-        InspectorTest.eventHandler["Profiler.consoleProfileStarted"] = function(messageObject)
-        {
-            InspectorTest.log("FAIL: console profile started " + JSON.stringify(messageObject, null, 4));
-        }
-        InspectorTest.eventHandler["Profiler.consoleProfileFinished"] = function(messageObject)
-        {
-            InspectorTest.log("FAIL: unexpected profile received " + JSON.stringify(messageObject, null, 4));
-        }
-    }
-    function allowConsoleProfiles()
-    {
-        InspectorTest.eventHandler["Profiler.consoleProfileStarted"] = function(messageObject)
-        {
-            InspectorTest.log("PASS: console initiated profile started");
-        }
-        InspectorTest.eventHandler["Profiler.consoleProfileFinished"] = function(messageObject)
-        {
-            InspectorTest.log("PASS: console initiated profile received");
-        }
-    }
-    function didFailToStartWhenDisabled(messageObject)
-    {
-        if (!InspectorTest.expectedError("didFailToStartWhenDisabled", messageObject))
-            return;
-        allowConsoleProfiles();
-        InspectorTest.sendCommand("Profiler.enable", {});
-        InspectorTest.sendCommand("Profiler.start", {}, didStartFrontendProfile);
-    }
-    function didStartFrontendProfile(messageObject)
-    {
-        if (!InspectorTest.expectedSuccess("didStartFrontendProfile", messageObject))
-            return;
-        InspectorTest.sendCommand("Runtime.evaluate", {expression: "console.profile('p1');"}, didStartConsoleProfile);
-    }
-
-    function didStartConsoleProfile(messageObject)
-    {
-        if (!InspectorTest.expectedSuccess("didStartConsoleProfile", messageObject))
-            return;
-        InspectorTest.sendCommand("Profiler.disable", {}, didDisableProfiler);
-    }
-
-    function didDisableProfiler(messageObject)
-    {
-        if (!InspectorTest.expectedSuccess("didDisableProfiler", messageObject))
-            return;
-        InspectorTest.sendCommand("Profiler.enable", {});
-        InspectorTest.sendCommand("Profiler.stop", {}, didStopFrontendProfile);
-    }
-
-    function didStopFrontendProfile(messageObject)
-    {
-        if (!InspectorTest.expectedError("no front-end initiated profiles found", messageObject))
-            return;
-        disallowConsoleProfiles();
-        InspectorTest.sendCommand("Runtime.evaluate", {expression: "console.profileEnd();"}, didStopConsoleProfile);
-    }
-
-    function didStopConsoleProfile(messageObject)
-    {
-        if (!InspectorTest.expectedSuccess("didStopConsoleProfile", messageObject))
-            return;
-        InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onload="runTest()">
-<p>
-Test that profiling can only be started when Profiler was enabled and that
-Profiler.disable command will stop recording all profiles.
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/enable-disable.js b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/enable-disable.js
new file mode 100644
index 0000000..3e59026
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/enable-disable.js
@@ -0,0 +1,48 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      'Test that profiling can only be started when Profiler was enabled and that ' +
+      'Profiler.disable command will stop recording all profiles.');
+
+  var allowConsoleProfiles = false;
+  dp.Profiler.onConsoleProfileStarted(messageObject => {
+    if (allowConsoleProfiles)
+      testRunner.log('PASS: console initiated profile started')
+    else
+      testRunner.log('FAIL: console profile started ' + JSON.stringify(messageObject, null, 4));
+  });
+  dp.Profiler.onConsoleProfileFinished(messageObject => {
+    if (allowConsoleProfiles)
+      testRunner.log('PASS: console initiated profile received')
+    else
+      testRunner.log('FAIL: unexpected profile received ' + JSON.stringify(messageObject, null, 4));
+  });
+
+  var messageObject = await dp.Profiler.start();
+  if (!testRunner.expectedError('didFailToStartWhenDisabled', messageObject))
+    return;
+
+  allowConsoleProfiles = true;
+  dp.Profiler.enable();
+  messageObject = await dp.Profiler.start();
+  if (!testRunner.expectedSuccess('didStartFrontendProfile', messageObject))
+    return;
+
+  messageObject = await dp.Runtime.evaluate({expression: 'console.profile("p1");'});
+  if (!testRunner.expectedSuccess('didStartConsoleProfile', messageObject))
+    return;
+
+  messageObject = await dp.Profiler.disable();
+  if (!testRunner.expectedSuccess('didDisableProfiler', messageObject))
+    return;
+
+  dp.Profiler.enable();
+  messageObject = await dp.Profiler.stop();
+  if (!testRunner.expectedError('no front-end initiated profiles found', messageObject))
+    return;
+  allowConsoleProfiles = false;
+
+  messageObject = await dp.Runtime.evaluate({expression: 'console.profileEnd();'});
+  if (!testRunner.expectedSuccess('didStopConsoleProfile', messageObject))
+    return;
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/record-cpu-profile-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/record-cpu-profile-expected.txt
index 26666d6..068cc0c 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/record-cpu-profile-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/record-cpu-profile-expected.txt
@@ -1,5 +1,4 @@
 Test that profiler is able to record a profile. Also it tests that profiler returns an error when it unable to find the profile.
-
 PASS: startFrontendProfile
 PASS: startConsoleProfile
 PASS: stopConsoleProfile
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/record-cpu-profile.html b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/record-cpu-profile.html
deleted file mode 100644
index 48387011..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/record-cpu-profile.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function test()
-{
-    InspectorTest.sendCommand("Profiler.enable", {});
-    InspectorTest.sendCommand("Profiler.start", {}, didStartFrontendProfile);
-    function didStartFrontendProfile(messageObject)
-    {
-        if (!InspectorTest.expectedSuccess("startFrontendProfile", messageObject))
-            return;
-        InspectorTest.sendCommand("Runtime.evaluate", {expression: "console.profile('Profile 1');"}, didStartConsoleProfile);
-    }
-
-    function didStartConsoleProfile(messageObject)
-    {
-        if (!InspectorTest.expectedSuccess("startConsoleProfile", messageObject))
-            return;
-        InspectorTest.sendCommand("Runtime.evaluate", {expression: "console.profileEnd('Profile 1');"}, didStopConsoleProfile);
-    }
-
-    function didStopConsoleProfile(messageObject)
-    {
-        if (!InspectorTest.expectedSuccess("stopConsoleProfile", messageObject))
-            return;
-        InspectorTest.sendCommand("Profiler.stop", {}, didStopFrontendProfile);
-    }
-
-    function didStopFrontendProfile(messageObject)
-    {
-        if (!InspectorTest.expectedSuccess("stoppedFrontendProfile", messageObject))
-            return;
-        InspectorTest.sendCommand("Profiler.start", {}, didStartFrontendProfile2);
-    }
-
-    function didStartFrontendProfile2(messageObject)
-    {
-        if (!InspectorTest.expectedSuccess("startFrontendProfileSecondTime", messageObject))
-            return;
-        InspectorTest.sendCommand("Profiler.stop", {}, didStopFrontendProfile2);
-    }
-
-    function didStopFrontendProfile2(messageObject)
-    {
-        InspectorTest.expectedSuccess("stopFrontendProfileSecondTime", messageObject)
-        InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onload="runTest()">
-<p>
-Test that profiler is able to record a profile.
-Also it tests that profiler returns an error when it unable to find the profile.
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/record-cpu-profile.js b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/record-cpu-profile.js
new file mode 100644
index 0000000..c1b44fda
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/record-cpu-profile.js
@@ -0,0 +1,30 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      'Test that profiler is able to record a profile. ' +
+      'Also it tests that profiler returns an error when it unable to find the profile.');
+
+  dp.Profiler.enable();
+  var messageObject = await dp.Profiler.start();
+  if (!testRunner.expectedSuccess('startFrontendProfile', messageObject))
+    return;
+
+  messageObject = await dp.Runtime.evaluate({expression: 'console.profile("Profile 1");'});
+  if (!testRunner.expectedSuccess('startConsoleProfile', messageObject))
+    return;
+
+  messageObject = await dp.Runtime.evaluate({expression: 'console.profileEnd("Profile 1");'});
+  if (!testRunner.expectedSuccess('stopConsoleProfile', messageObject))
+    return;
+
+  messageObject = await dp.Profiler.stop();
+  if (!testRunner.expectedSuccess('stoppedFrontendProfile', messageObject))
+    return;
+
+  messageObject = await dp.Profiler.start();
+  if (!testRunner.expectedSuccess('startFrontendProfileSecondTime', messageObject))
+    return;
+
+  messageObject = await dp.Profiler.stop();
+  testRunner.expectedSuccess('stopFrontendProfileSecondTime', messageObject);
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/stop-without-preceeding-start-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/stop-without-preceeding-start-expected.txt
index b99e955..758b4ca2 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/stop-without-preceeding-start-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/stop-without-preceeding-start-expected.txt
@@ -1,4 +1,3 @@
 Test that profiler doesn't crash when we call stop without preceeding start.
-
 PASS: ProfileAgent.stop
 
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/stop-without-preceeding-start.html b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/stop-without-preceeding-start.html
deleted file mode 100644
index d46bb664..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/stop-without-preceeding-start.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function test()
-{
-    InspectorTest.sendCommand("Profiler.stop", {}, didStopProfile);
-    function didStopProfile(messageObject)
-    {
-        InspectorTest.expectedError("ProfileAgent.stop", messageObject);
-        InspectorTest.completeTest();
-    }
-
-}
-</script>
-</head>
-<body onload="runTest()">
-<p>
-Test that profiler doesn't crash when we call stop without preceeding start.
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/stop-without-preceeding-start.js b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/stop-without-preceeding-start.js
new file mode 100644
index 0000000..f67203b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/cpu-profiler/stop-without-preceeding-start.js
@@ -0,0 +1,7 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank(
+      'Test that profiler doesn\'t crash when we call stop without preceeding start.');
+  var messageObject = await dp.Profiler.stop();
+  testRunner.expectedError('ProfileAgent.stop', messageObject);
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/resources/tracing-test.js b/third_party/WebKit/LayoutTests/inspector-protocol/resources/tracing-test.js
index 0c545b5..8f24f87 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/resources/tracing-test.js
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/resources/tracing-test.js
@@ -2,135 +2,117 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-var evalCallbackCallId = 3;
+(class TracingHelper {
+  constructor(testRunner, session) {
+    this._testRunner = testRunner;
+    this._session = session;
+  }
 
-initialize_tracingHarness = function()
-{
+  startTracing() {
+    return this.startTracingWithArguments({ "categories": "-*,disabled-by-default-devtools.timeline,devtools.timeline", "type": "", "options": "" });
+  }
 
-InspectorTest.startTracing = function(callback)
-{
-    InspectorTest.startTracingWithArguments({ "categories": "-*,disabled-by-default-devtools.timeline,devtools.timeline", "type": "", "options": "" }, callback);
-}
-
-InspectorTest.startTracingAndSaveAsStream = function(callback)
-{
+  startTracingAndSaveAsStream() {
     var args = {
-        "categories": "-*,disabled-by-default-devtools.timeline,devtools.timeline",
-        "type": "",
-        "options": "",
-        "transferMode": "ReturnAsStream"
+      "categories": "-*,disabled-by-default-devtools.timeline,devtools.timeline",
+      "type": "",
+      "options": "",
+      "transferMode": "ReturnAsStream"
     };
-    InspectorTest.startTracingWithArguments(args, callback);
-}
+    return this.startTracingWithArguments(args);
+  }
 
-InspectorTest.startTracingWithArguments = function(args, callback)
-{
-    InspectorTest.sendCommand("Tracing.start", args, onStart);
+  async startTracingWithArguments(args) {
+    await this._session.protocol.Tracing.start(args);
+    this._testRunner.log("Recording started");
+  }
 
-    function onStart(response)
-    {
-        InspectorTest.log("Recording started");
-        callback();
-    }
-}
+  async stopTracing() {
+    var devtoolsEvents = [];
 
-InspectorTest.stopTracing = function(callback)
-{
-    InspectorTest.eventHandler["Tracing.tracingComplete"] = tracingComplete;
-    InspectorTest.eventHandler["Tracing.dataCollected"] = dataCollected;
-    InspectorTest.sendCommand("Tracing.end", { });
+    function dataCollected(reply) {
+      var allEvents = reply.params.value;
+      var filteredEvents = allEvents.filter(e => /devtools.timeline/.test(e.cat));
+      devtoolsEvents = devtoolsEvents.concat(filteredEvents);
+    };
 
-    InspectorTest.devtoolsEvents = [];
-    function dataCollected(reply)
-    {
-        var allEvents = reply.params.value;
-        InspectorTest.devtoolsEvents = InspectorTest.devtoolsEvents.concat(allEvents.filter(function(e)
-        {
-            return /devtools.timeline/.test(e.cat);
-        }));
+    this._session.protocol.Tracing.onDataCollected(dataCollected);
+    this._session.protocol.Tracing.end();
+    await this._session.protocol.Tracing.onceTracingComplete();
+    this._testRunner.log("Tracing complete");
+    this._session.protocol.Tracing.offDataCollected(dataCollected);
+    this._devtoolsEvents = devtoolsEvents;
+    return devtoolsEvents;
+  }
+
+  async stopTracingAndReturnStream() {
+    function dataCollected() {
+      this._testRunner.log("FAIL: dataCollected event should not be fired when returning trace as stream.");
     }
 
-    function tracingComplete(event)
-    {
-        InspectorTest.log("Tracing complete");
-        InspectorTest.eventHandler["Tracing.tracingComplete"] = null;
-        InspectorTest.eventHandler["Tracing.dataCollected"] = null;
-        callback(InspectorTest.devtoolsEvents);
-    }
-}
+    this._session.protocol.Tracing.onDataCollected(dataCollected);
+    this._session.protocol.Tracing.end();
+    var event = await this._session.protocol.Tracing.onceTracingComplete();
+    this._testRunner.log("Tracing complete");
+    this._session.protocol.Tracing.offDataCollected(dataCollected);
+    return event.params.stream;
+  }
 
-InspectorTest.stopTracingAndReturnStream = function(callback)
-{
-    InspectorTest.eventHandler["Tracing.tracingComplete"] = tracingComplete;
-    InspectorTest.eventHandler["Tracing.dataCollected"] = dataCollected;
-    InspectorTest.sendCommand("Tracing.end");
-
-    function dataCollected(reply)
-    {
-        InspectorTest.log("FAIL: dataCollected event should not be fired when returning trace as stream.");
-
-    }
-
-    function tracingComplete(event)
-    {
-        InspectorTest.log("Tracing complete");
-        InspectorTest.eventHandler["Tracing.tracingComplete"] = null;
-        InspectorTest.eventHandler["Tracing.dataCollected"] = null;
-        callback(event.params.stream);
-    }
-}
-
-InspectorTest.retrieveStream = function(streamHandle, offset, chunkSize, callback)
-{
+  retrieveStream(streamHandle, offset, chunkSize) {
+    var callback;
+    var promise = new Promise(f => callback = f);
     var result = "";
     var had_eof = false;
 
     var readArguments = { handle: streamHandle };
     if (typeof chunkSize === "number")
-        readArguments.size = chunkSize;
+      readArguments.size = chunkSize;
     var firstReadArguments = JSON.parse(JSON.stringify(readArguments));
     if (typeof offset === "number")
-        firstReadArguments.offset = 0;
-    InspectorTest.sendCommandOrDie("IO.read", firstReadArguments, onChunkRead);
-    // Assure multiple in-lfight reads are fine (also, save on latencies).
-    InspectorTest.sendCommandOrDie("IO.read", readArguments, onChunkRead);
+      firstReadArguments.offset = 0;
+    this._session.protocol.IO.read(firstReadArguments).then(message => onChunkRead.call(this, message.result));
+    // Assure multiple in-flight reads are fine (also, save on latencies).
+    this._session.protocol.IO.read(readArguments).then(message => onChunkRead.call(this, message.result));
+    return promise;
 
-    function onChunkRead(response)
-    {
-        if (had_eof)
-            return;
-        result += response.data;
-        if (response.eof) {
-            // Ignore stray callbacks from proactive read requests.
-            had_eof = true;
-            callback(result);
-            return;
-        }
-        InspectorTest.sendCommandOrDie("IO.read", readArguments, onChunkRead);
+    function onChunkRead(response) {
+      if (had_eof)
+        return;
+      result += response.data;
+      if (response.eof) {
+        // Ignore stray callbacks from proactive read requests.
+        had_eof = true;
+        callback(result);
+        return;
+      }
+      this._session.protocol.IO.read(readArguments).then(message => onChunkRead.call(this, message.result));
     }
-}
+  }
 
-InspectorTest.findEvents = function(name, ph, condition)
-{
-    return InspectorTest.devtoolsEvents.filter(e => e.name === name && e.ph === ph && (!condition || condition(e)));
-}
+  findEvents(name, ph, condition) {
+    return this._devtoolsEvents.filter(e => e.name === name && e.ph === ph && (!condition || condition(e)));
+  }
 
-InspectorTest.findEvent = function(name, ph, condition)
-{
-    var events = InspectorTest.findEvents(name, ph, condition);
+  findEvent(name, ph, condition) {
+    var events = this.findEvents(name, ph, condition);
     if (events.length)
-        return events[0];
-    throw new Error("Couldn't find event " + name + " / " + ph + "\n\n in " + JSON.stringify(InspectorTest.devtoolsEvents, null, 2));
-}
+      return events[0];
+    throw new Error("Couldn't find event " + name + " / " + ph + "\n\n in " + JSON.stringify(this.devtoolsEvents, null, 2));
+  }
 
-InspectorTest.invokeAsyncWithTracing = function(functionName, callback)
-{
-    InspectorTest.startTracing(onStart);
+  filterEvents(callback) {
+    return this._devtoolsEvents.filter(callback);
+  }
 
-    function onStart()
-    {
-        InspectorTest.evaluateInPageAsync(functionName + "()").then((data) => InspectorTest.stopTracing((devtoolsEvents) => callback(devtoolsEvents, data)));
-    }
-}
+  async invokeAsyncWithTracing(performActions) {
+    await this.startTracing();
+    var data = await this._session.evaluateAsync(`(${performActions.toString()})()`);
+    await this.stopTracing();
+    return data;
+  }
 
-}
+  formattedEvents() {
+    var formattedEvents = this._devtoolsEvents.map(e => e.name + (e.args.data ? '(' + e.args.data.type + ')' : ''));
+    return JSON.stringify(formattedEvents, null, 2);
+  }
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/worker/resources/worker-with-throw.js b/third_party/WebKit/LayoutTests/inspector-protocol/resources/worker-with-throw.js
similarity index 100%
rename from third_party/WebKit/LayoutTests/inspector-protocol/worker/resources/worker-with-throw.js
rename to third_party/WebKit/LayoutTests/inspector-protocol/resources/worker-with-throw.js
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/fetch-as-stream-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/fetch-as-stream-expected.txt
index 6187058..2bae8ca 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/fetch-as-stream-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/fetch-as-stream-expected.txt
@@ -1,3 +1,4 @@
+
 Recording started
 Tracing complete
 Error after legit close: undefined
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/fetch-as-stream.html b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/fetch-as-stream.html
deleted file mode 100644
index b6edcb3..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/fetch-as-stream.html
+++ /dev/null
@@ -1,109 +0,0 @@
-<html>
-<head>
-<style>
-div#test {
-    display: none;
-    background-color: blue;
-    width: 100px;
-    height: 100px;
-}
-</style>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script type="text/javascript" src="../resources/tracing-test.js"></script>
-<script>
-function performActions()
-{
-    var element = document.getElementById("test");
-    element.style.display = "block";
-    var unused = element.clientWidth;
-}
-
-function test()
-{
-    InspectorTest.startTracingAndSaveAsStream(onStart);
-
-    function onStart()
-    {
-        InspectorTest.evaluateInPage("performActions()", evalDone);
-    }
-
-    function evalDone()
-    {
-        InspectorTest.stopTracingAndReturnStream(onStop);
-    }
-
-    function onStop(streamHandle)
-    {
-        var data1;
-        InspectorTest.retrieveStream(streamHandle, null, null, onGotStream1);
-
-        function onGotStream1(data)
-        {
-            data1 = data;
-            InspectorTest.retrieveStream(streamHandle, 0, 1000, onGotStream2);
-        }
-        function onGotStream2(data)
-        {
-            if (data1 !== data)
-                InspectorTest.log("FAIL: got different data for cunked vs. non-chunked reads");
-            InspectorTest.sendCommandOrDie("IO.close", { handle: streamHandle }, onCloseDone);
-        }
-        function onCloseDone(response)
-        {
-            InspectorTest.log("Error after legit close: " + JSON.stringify(response.error));
-            InspectorTest.sendCommand("IO.read", { handle: streamHandle }, onReadAfterClose);
-        }
-        function onReadAfterClose(response)
-        {
-            InspectorTest.log("Error after illegal read: " + JSON.stringify(response.error));
-            InspectorTest.sendCommand("IO.close", { handle: streamHandle }, onCloseAfterClose);
-        }
-        function onCloseAfterClose(response)
-        {
-            InspectorTest.log("Error after illegal close: " + JSON.stringify(response.error));
-            var trace = JSON.parse(data1);
-            performEventsSanityCheck(trace["traceEvents"]);
-InspectorTest.log("Metadata: " + typeof trace["metadata"] + (trace["metadata"] ? ", not null" : ""));
-            InspectorTest.completeTest();
-        }
-    }
-
-    function assertGreaterOrEqual(a, b, message)
-    {
-        if (a >= b)
-            return;
-        InspectorTest.log(message + " (" + a + " < " + b + ")");
-        InspectorTest.completeTest();
-    }
-
-    function performEventsSanityCheck(events)
-    {
-        var phaseComplete = 0;
-
-        var knownEvents = {
-            "MessageLoop::PostTask": 0,
-            "FunctionCall": 0,
-            "UpdateLayoutTree": 0,
-            "Layout": 0
-        };
-
-        for (var i = 0; i < events.length; ++i) {
-            var event = events[i];
-            if (event.phase === "X")
-                ++phaseComplete;
-            if (event.name in knownEvents)
-                ++knownEvents[event.name];
-        }
-        assertGreaterOrEqual(events.length, 10, "Too few trace events recorded");
-        assertGreaterOrEqual(knownEvents["UpdateLayoutTree"], 1, "Too few UpdateLayoutTree events");
-        assertGreaterOrEqual(knownEvents["Layout"], 1, "Too few Layout events");
-        InspectorTest.log("Event sanity test done");
-    }
-}
-</script>
-</head>
-<body onload="runTest()">
-<div id="test">
-</div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/fetch-as-stream.js b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/fetch-as-stream.js
new file mode 100644
index 0000000..ea02efa9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/fetch-as-stream.js
@@ -0,0 +1,71 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <style>
+    div#test {
+        display: none;
+        background-color: blue;
+        width: 100px;
+        height: 100px;
+    }
+    </style>
+    <div id='test'>
+    </div>
+  `, '');
+
+  var TracingHelper = await testRunner.loadScript('../resources/tracing-test.js');
+  var tracingHelper = new TracingHelper(testRunner, session);
+  await tracingHelper.startTracingAndSaveAsStream();
+  await session.evaluate(`
+    (function performActions() {
+      var element = document.getElementById('test');
+      element.style.display = 'block';
+      var unused = element.clientWidth;
+    })();
+  `);
+
+  var streamHandle = await tracingHelper.stopTracingAndReturnStream();
+  var data1 = await tracingHelper.retrieveStream(streamHandle, null, null);
+  var data2 = await tracingHelper.retrieveStream(streamHandle, 0, 1000);
+  if (data1 !== data2)
+    testRunner.log('FAIL: got different data for cunked vs. non-chunked reads');
+  var response = await dp.IO.close({ handle: streamHandle });
+  testRunner.log('Error after legit close: ' + JSON.stringify(response.error));
+  response = await dp.IO.read({ handle: streamHandle });
+  testRunner.log('Error after illegal read: ' + JSON.stringify(response.error));
+  response = await dp.IO.close({ handle: streamHandle });
+  testRunner.log('Error after illegal close: ' + JSON.stringify(response.error));
+  var trace = JSON.parse(data1);
+  performEventsSanityCheck(trace['traceEvents']);
+  testRunner.log('Metadata: ' + typeof trace['metadata'] + (trace['metadata'] ? ', not null' : ''));
+  testRunner.completeTest();
+
+  function assertGreaterOrEqual(a, b, message) {
+    if (a >= b)
+      return;
+    testRunner.log(message + ' (' + a + ' < ' + b + ')');
+    testRunner.completeTest();
+  }
+
+  function performEventsSanityCheck(events) {
+    var phaseComplete = 0;
+
+    var knownEvents = {
+      'MessageLoop::PostTask': 0,
+      'FunctionCall': 0,
+      'UpdateLayoutTree': 0,
+      'Layout': 0
+    };
+
+    for (var i = 0; i < events.length; ++i) {
+      var event = events[i];
+      if (event.phase === 'X')
+        ++phaseComplete;
+      if (event.name in knownEvents)
+        ++knownEvents[event.name];
+    }
+    assertGreaterOrEqual(events.length, 10, 'Too few trace events recorded');
+    assertGreaterOrEqual(knownEvents['UpdateLayoutTree'], 1, 'Too few UpdateLayoutTree events');
+    assertGreaterOrEqual(knownEvents['Layout'], 1, 'Too few Layout events');
+    testRunner.log('Event sanity test done');
+  }
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames-expected.txt
index 77259bd..aac0ec27 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames-expected.txt
@@ -2,7 +2,7 @@
 Recording started
 Tracing complete
 Frames in TracingStartedInPage
-url: inspector-protocol/timeline/page-frames.html name:  parent: undefined nodeId: undefined
+url: inspector-protocol/resources/inspector-protocol-page.html name:  parent: undefined nodeId: undefined
 url: data:text/html,<script>window.foo = 42</script> name: frame0 parent: string nodeId: number
 Frames in CommitLoad events
 url: about:blank name: Frame No. 1 parent: string nodeId: number
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames.html b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames.html
deleted file mode 100644
index e4167d9..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script type="text/javascript" src="../resources/tracing-test.js"></script>
-<script>
-function performActions()
-{
-    var frame1 = document.createElement("iframe");
-    frame1.name = "Frame No. 1";
-    document.body.appendChild(frame1);
-    frame1.contentWindow.document.write("console.log('frame2')");
-
-    var frame2 = document.createElement("iframe");
-    frame2.src = "../resources/blank.html";
-    document.body.appendChild(frame2);
-
-    return new Promise(fulfill => { frame2.addEventListener("load", fulfill, false) });
-}
-
-function test()
-{
-    InspectorTest.invokeAsyncWithTracing("performActions", finish);
-
-    function finish(devtoolsEvents, data)
-    {
-        InspectorTest.log("Frames in TracingStartedInPage");
-        var tracingStarted = InspectorTest.findEvent("TracingStartedInPage", "I");
-        for (var frame of tracingStarted.args["data"]["frames"] || [])
-            dumpFrame(frame);
-
-        InspectorTest.log("Frames in CommitLoad events");
-        var commitLoads = InspectorTest.findEvents("CommitLoad", "X");
-        for (var event of commitLoads)
-            dumpFrame(event.args["data"]);
-        InspectorTest.completeTest();
-    }
-
-    function dumpFrame(frame)
-    {
-        var url = frame.url.replace(/.*\/(([^/]*\/){2}[^/]*$)/, "$1");
-        InspectorTest.log(`url: ${url} name: ${frame.name} parent: ${typeof frame.parent} nodeId: ${typeof frame.nodeId}`);
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-<iframe src="data:text/html,<script>window.foo = 42</script>" name="frame0"></iframe>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames.js b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames.js
new file mode 100644
index 0000000..c3801fc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/page-frames.js
@@ -0,0 +1,38 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <iframe src='data:text/html,<script>window.foo = 42</script>' name='frame0'></iframe>
+  `, '');
+
+  function performActions() {
+    var frame1 = document.createElement('iframe');
+    frame1.name = 'Frame No. 1';
+    document.body.appendChild(frame1);
+    frame1.contentWindow.document.write('console.log("frame2")');
+
+    var frame2 = document.createElement('iframe');
+    frame2.src = 'blank.html';
+    document.body.appendChild(frame2);
+
+    return new Promise(fulfill => { frame2.addEventListener('load', fulfill, false) });
+  }
+
+  var TracingHelper = await testRunner.loadScript('../resources/tracing-test.js');
+  var tracingHelper = new TracingHelper(testRunner, session);
+  var data = await tracingHelper.invokeAsyncWithTracing(performActions);
+
+  testRunner.log('Frames in TracingStartedInPage');
+  var tracingStarted = tracingHelper.findEvent('TracingStartedInPage', 'I');
+  for (var frame of tracingStarted.args['data']['frames'] || [])
+    dumpFrame(frame);
+
+  testRunner.log('Frames in CommitLoad events');
+  var commitLoads = tracingHelper.findEvents('CommitLoad', 'X');
+  for (var event of commitLoads)
+    dumpFrame(event.args['data']);
+  testRunner.completeTest();
+
+  function dumpFrame(frame) {
+    var url = frame.url.replace(/.*\/(([^/]*\/){2}[^/]*$)/, '$1');
+    testRunner.log(`url: ${url} name: ${frame.name} parent: ${typeof frame.parent} nodeId: ${typeof frame.nodeId}`);
+  }
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-dispatchEvent.html b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-dispatchEvent.html
deleted file mode 100644
index c500060..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-dispatchEvent.html
+++ /dev/null
@@ -1,60 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script type="text/javascript" src="../resources/tracing-test.js"></script>
-<script>
-
-function performAction()
-{
-    var div = document.querySelector("#my-div");
-    div.addEventListener("click", function(e) {  }, false);
-    div.click();
-
-    var iframe = document.createElement("iframe");
-    div.appendChild(iframe);
-    return new Promise(resolve => {
-        iframe.onload = resolve;
-        iframe.src = "../resources/blank.html";
-    });
-}
-
-function test()
-{
-    InspectorTest.invokeAsyncWithTracing("performAction", finish);
-
-    function finish(devtoolsEvents)
-    {
-        function windowEventFilter(type, e)
-        {
-            return e.name === "EventDispatch" && e.args.data.type === type;
-        };
-
-        var windowEventNames = [ "click", "beforeunload", "unload", "load" ];
-        for (var i = 0; i < windowEventNames.length; i++) {
-            var eventName = windowEventNames[i];
-            var events = devtoolsEvents.filter(windowEventFilter.bind(this, eventName));
-            if (events.length >= 1) {
-                InspectorTest.log("SUCCESS: found " + eventName + " event");
-            } else {
-                fail(eventName + " event is missing", devtoolsEvents);
-            }
-        }
-
-        InspectorTest.completeTest();
-    }
-
-    function fail(message, devtoolsEvents)
-    {
-        var formattedEvents = devtoolsEvents.map(function(e)
-        {
-            return e.name + (e.args.data ? "(" + e.args.data.type + ")" : "");
-        });
-        InspectorTest.log("FAIL: " + message + " devtools.timeline events: " + JSON.stringify(formattedEvents, null, 2));
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-<div id="my-div"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-dispatchEvent.js b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-dispatchEvent.js
new file mode 100644
index 0000000..bf55a78
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-dispatchEvent.js
@@ -0,0 +1,33 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='my-div'></div>
+  `, '');
+
+  function performAction() {
+    var div = document.querySelector('#my-div');
+    div.addEventListener('click', function(e) {  }, false);
+    div.click();
+
+    var iframe = document.createElement('iframe');
+    div.appendChild(iframe);
+    return new Promise(resolve => {
+      iframe.onload = resolve;
+      iframe.src = 'blank.html';
+    });
+  }
+
+  var TracingHelper = await testRunner.loadScript('../resources/tracing-test.js');
+  var tracingHelper = new TracingHelper(testRunner, session);
+  await tracingHelper.invokeAsyncWithTracing(performAction);
+
+  var windowEventNames = [ 'click', 'beforeunload', 'unload', 'load' ];
+  for (var eventName of windowEventNames) {
+    var events = tracingHelper.filterEvents(e => e.name === 'EventDispatch' && e.args.data.type === eventName);
+    if (events.length >= 1)
+      testRunner.log('SUCCESS: found ' + eventName + ' event');
+    else
+      testRunner.log('FAIL: ' + eventName + ' event is missing; devtools.timeline events: ' + tracingHelper.formattedEvents());
+  }
+
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-layout-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-layout-expected.txt
index cf2c0db5..17a2c83 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-layout-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-layout-expected.txt
@@ -1,4 +1,4 @@
-DIV
+
 Recording started
 Tracing complete
 UpdateLayoutTree frames match: true
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-layout.html b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-layout.html
deleted file mode 100644
index e2f0691..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-layout.html
+++ /dev/null
@@ -1,58 +0,0 @@
-<html>
-<head>
-<style>
-.my-class {
-    min-width: 100px;
-    background-color: red;
-}
-</style>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script type="text/javascript" src="../resources/tracing-test.js"></script>
-<script>
-
-function performActions()
-{
-    var div = document.querySelector("#myDiv");
-    div.classList.add("my-class");
-    div.offsetWidth;
-    return Promise.resolve();
-}
-
-function test()
-{
-    InspectorTest.invokeAsyncWithTracing("performActions", finish);
-
-    function finish(devtoolsEvents)
-    {
-        var schedRecalc = InspectorTest.findEvent("ScheduleStyleRecalculation", "I");
-        var recalcBegin = InspectorTest.findEvent("UpdateLayoutTree", "B");
-        var recalcEnd = InspectorTest.findEvent("UpdateLayoutTree", "E");
-        InspectorTest.log("UpdateLayoutTree frames match: " + (schedRecalc.args.data.frame === recalcBegin.args.beginData.frame));
-        InspectorTest.log("UpdateLayoutTree elementCount > 0: " + (recalcEnd.args.elementCount > 0));
-
-        var invalidate = InspectorTest.findEvent("InvalidateLayout", "I");
-        var layoutBegin = InspectorTest.findEvent("Layout", "B");
-        var layoutEnd = InspectorTest.findEvent("Layout", "E");
-
-        InspectorTest.log("InvalidateLayout frames match: " + (recalcBegin.args.beginData.frame === invalidate.args.data.frame));
-
-        var beginData = layoutBegin.args.beginData;
-        InspectorTest.log("Layout frames match: " + (invalidate.args.data.frame === beginData.frame));
-        InspectorTest.log("dirtyObjects > 0: " + (beginData.dirtyObjects > 0));
-        InspectorTest.log("totalObjects > 0: " + (beginData.totalObjects > 0));
-
-        var endData = layoutEnd.args.endData;
-        InspectorTest.log("has rootNode id: " + (endData.rootNode > 0));
-        InspectorTest.log("has root quad: " + !!endData.root);
-
-        InspectorTest.log("SUCCESS: found all expected events.");
-        InspectorTest.completeTest();
-    }
-
-}
-</script>
-</head>
-<body onLoad="runTest();">
-<div id="myDiv">DIV</div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-layout.js b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-layout.js
new file mode 100644
index 0000000..e22295cb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-layout.js
@@ -0,0 +1,46 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <style>
+    .my-class {
+        min-width: 100px;
+        background-color: red;
+    }
+    </style>
+    <div id='myDiv'>DIV</div>
+  `, '');
+
+  function performActions() {
+    var div = document.querySelector('#myDiv');
+    div.classList.add('my-class');
+    div.offsetWidth;
+    return Promise.resolve();
+  }
+
+  var TracingHelper = await testRunner.loadScript('../resources/tracing-test.js');
+  var tracingHelper = new TracingHelper(testRunner, session);
+  await tracingHelper.invokeAsyncWithTracing(performActions);
+
+  var schedRecalc = tracingHelper.findEvent('ScheduleStyleRecalculation', 'I');
+  var recalcBegin = tracingHelper.findEvent('UpdateLayoutTree', 'B');
+  var recalcEnd = tracingHelper.findEvent('UpdateLayoutTree', 'E');
+  testRunner.log('UpdateLayoutTree frames match: ' + (schedRecalc.args.data.frame === recalcBegin.args.beginData.frame));
+  testRunner.log('UpdateLayoutTree elementCount > 0: ' + (recalcEnd.args.elementCount > 0));
+
+  var invalidate = tracingHelper.findEvent('InvalidateLayout', 'I');
+  var layoutBegin = tracingHelper.findEvent('Layout', 'B');
+  var layoutEnd = tracingHelper.findEvent('Layout', 'E');
+
+  testRunner.log('InvalidateLayout frames match: ' + (recalcBegin.args.beginData.frame === invalidate.args.data.frame));
+
+  var beginData = layoutBegin.args.beginData;
+  testRunner.log('Layout frames match: ' + (invalidate.args.data.frame === beginData.frame));
+  testRunner.log('dirtyObjects > 0: ' + (beginData.dirtyObjects > 0));
+  testRunner.log('totalObjects > 0: ' + (beginData.totalObjects > 0));
+
+  var endData = layoutEnd.args.endData;
+  testRunner.log('has rootNode id: ' + (endData.rootNode > 0));
+  testRunner.log('has root quad: ' + !!endData.root);
+
+  testRunner.log('SUCCESS: found all expected events.');
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-raf-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-raf-expected.txt
index c7986ef9..a23325c 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-raf-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-raf-expected.txt
@@ -1,4 +1,4 @@
-DIV
+
 Recording started
 Tracing complete
 RequestAnimationFrame has frame: true
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-raf.html b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-raf.html
deleted file mode 100644
index c5376f0..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-raf.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script type="text/javascript" src="../resources/tracing-test.js"></script>
-<script>
-
-function performActions()
-{
-    var callback;
-    var promise = new Promise((fulfill) => callback = fulfill);
-    var rafId2;
-    var rafId1 = requestAnimationFrame(() => callback({ rafId1: rafId1, rafId2: rafId2 }));
-    rafId2 = requestAnimationFrame(function() { });
-    cancelAnimationFrame(rafId2);
-    return promise;
-}
-
-function test()
-{
-    InspectorTest.invokeAsyncWithTracing("performActions", finish);
-
-    function finish(devtoolsEvents, data)
-    {
-        var firedRaf = data.rafId1;
-        var canceledRaf = data.rafId2;
-
-        function hasRafId(id, e) { return e.args.data.id === id}
-
-        var raf1 = InspectorTest.findEvent("RequestAnimationFrame", "I", hasRafId.bind(this, firedRaf));
-        var raf2 = InspectorTest.findEvent("RequestAnimationFrame", "I", hasRafId.bind(this, canceledRaf));
-
-        InspectorTest.log("RequestAnimationFrame has frame: " + !!raf1.args.data.frame);
-        InspectorTest.log("RequestAnimationFrame frames match: " + (raf1.args.data.frame === raf2.args.data.frame));
-
-        InspectorTest.findEvent("CancelAnimationFrame", "I", hasRafId.bind(this, canceledRaf));
-        InspectorTest.findEvent("FireAnimationFrame", "X", hasRafId.bind(this, firedRaf));
-
-        InspectorTest.log("SUCCESS: found all expected events.");
-        InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-<div id="myDiv">DIV</div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-raf.js b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-raf.js
new file mode 100644
index 0000000..6cb273f6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-raf.js
@@ -0,0 +1,37 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='myDiv'>DIV</div>
+  `, '');
+
+  function performActions() {
+    var callback;
+    var promise = new Promise((fulfill) => callback = fulfill);
+    var rafId2;
+    var rafId1 = requestAnimationFrame(() => callback({ rafId1: rafId1, rafId2: rafId2 }));
+    rafId2 = requestAnimationFrame(function() { });
+    cancelAnimationFrame(rafId2);
+    return promise;
+  }
+
+  function hasRafId(id, e) {
+    return e.args.data.id === id;
+  }
+
+  var TracingHelper = await testRunner.loadScript('../resources/tracing-test.js');
+  var tracingHelper = new TracingHelper(testRunner, session);
+  var data = await tracingHelper.invokeAsyncWithTracing(performActions);
+  var firedRaf = data.rafId1;
+  var canceledRaf = data.rafId2;
+
+  var raf1 = tracingHelper.findEvent('RequestAnimationFrame', 'I', hasRafId.bind(null, firedRaf));
+  var raf2 = tracingHelper.findEvent('RequestAnimationFrame', 'I', hasRafId.bind(null, canceledRaf));
+
+  testRunner.log('RequestAnimationFrame has frame: ' + !!raf1.args.data.frame);
+  testRunner.log('RequestAnimationFrame frames match: ' + (raf1.args.data.frame === raf2.args.data.frame));
+
+  tracingHelper.findEvent('CancelAnimationFrame', 'I', hasRafId.bind(null, canceledRaf));
+  tracingHelper.findEvent('FireAnimationFrame', 'X', hasRafId.bind(null, firedRaf));
+
+  testRunner.log('SUCCESS: found all expected events.');
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-timer-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-timer-expected.txt
index 05ac6ae..aaf1656 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-timer-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-timer-expected.txt
@@ -1,6 +1,5 @@
-DIV
+
 Recording started
-SUCCESS: testFunctionTimerFired
 Tracing complete
 TimerInstall has frame: true
 TimerInstall frames match: true
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-timer.html b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-timer.html
deleted file mode 100644
index 5a3db8f..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-timer.html
+++ /dev/null
@@ -1,57 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script type="text/javascript" src="../resources/tracing-test.js"></script>
-<script>
-
-function performActions()
-{
-    var callback;
-    var promise = new Promise((fulfill) => callback = fulfill);
-    var timerId = setTimeout(function()
-    {
-        evaluateInFrontend("InspectorTest.testFunctionTimerFired(" + timerId + ", " + timerId2 + ")");
-        callback();
-    }, 0);
-
-    var timerId2 = setTimeout(function() { }, 0);
-    clearTimeout(timerId2);
-    return promise;
-}
-
-function test()
-{
-    InspectorTest.invokeAsyncWithTracing("performActions", finish);
-
-    var firedTimerId;
-    var removedTimerId;
-    InspectorTest.testFunctionTimerFired = function(timerId1, timerId2)
-    {
-        firedTimerId = timerId1;
-        removedTimerId = timerId2;
-        InspectorTest.log("SUCCESS: testFunctionTimerFired");
-    }
-
-    function finish(devtoolsEvents)
-    {
-        function hasTimerId(id, e) { return e.args.data.timerId === id}
-
-        var installTimer1 = InspectorTest.findEvent("TimerInstall", "I", hasTimerId.bind(this, firedTimerId));
-        var installTimer2 = InspectorTest.findEvent("TimerInstall", "I", hasTimerId.bind(this, removedTimerId));
-
-        InspectorTest.log("TimerInstall has frame: " + !!installTimer1.args.data.frame);
-        InspectorTest.log("TimerInstall frames match: " + (installTimer1.args.data.frame === installTimer2.args.data.frame));
-
-        InspectorTest.findEvent("TimerRemove", "I", hasTimerId.bind(this, removedTimerId));
-        InspectorTest.findEvent("TimerFire", "X", hasTimerId.bind(this, firedTimerId));
-
-        InspectorTest.log("SUCCESS: found all expected events.");
-        InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onLoad="runTest();">
-<div id="myDiv">DIV</div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-timer.js b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-timer.js
new file mode 100644
index 0000000..84e086fe7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/timeline/timeline-timer.js
@@ -0,0 +1,40 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startHTML(`
+    <div id='myDiv'>DIV</div>
+  `, '');
+
+  function performActions() {
+    var callback;
+    var promise = new Promise((fulfill) => callback = fulfill);
+    var timerId = setTimeout(function() {
+      callback({timerId: timerId, timerId2: timerId2});
+    }, 0);
+
+    var timerId2 = setTimeout(function() { }, 0);
+    clearTimeout(timerId2);
+    return promise;
+  }
+
+  function hasTimerId(id, e) {
+    return e.args.data.timerId === id;
+  }
+
+  var TracingHelper = await testRunner.loadScript('../resources/tracing-test.js');
+  var tracingHelper = new TracingHelper(testRunner, session);
+  var data = await tracingHelper.invokeAsyncWithTracing(performActions);
+
+  var firedTimerId = data.timerId;
+  var removedTimerId = data.timerId2;
+
+  var installTimer1 = tracingHelper.findEvent('TimerInstall', 'I', hasTimerId.bind(this, firedTimerId));
+  var installTimer2 = tracingHelper.findEvent('TimerInstall', 'I', hasTimerId.bind(this, removedTimerId));
+
+  testRunner.log('TimerInstall has frame: ' + !!installTimer1.args.data.frame);
+  testRunner.log('TimerInstall frames match: ' + (installTimer1.args.data.frame === installTimer2.args.data.frame));
+
+  tracingHelper.findEvent('TimerRemove', 'I', hasTimerId.bind(this, removedTimerId));
+  tracingHelper.findEvent('TimerFire', 'X', hasTimerId.bind(this, firedTimerId));
+
+  testRunner.log('SUCCESS: found all expected events.');
+  testRunner.completeTest();
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/worker/exception-from-worker-contains-stack-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/worker/exception-from-worker-contains-stack-expected.txt
index 44e1f4f..bc38565 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/worker/exception-from-worker-contains-stack-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/worker/exception-from-worker-contains-stack-expected.txt
@@ -1,4 +1,3 @@
-CONSOLE ERROR: line 3: Uncaught Error
 Tests that console message from worker contains stack trace.
 Worker created
 Worker created
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/worker/exception-from-worker-contains-stack.html b/third_party/WebKit/LayoutTests/inspector-protocol/worker/exception-from-worker-contains-stack.html
deleted file mode 100644
index 97a9a86..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/worker/exception-from-worker-contains-stack.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-var worker1;
-var worker2;
-
-function startWorkers()
-{
-    worker1 = new Worker("resources/worker-with-throw.js");
-    worker1.onerror = function(e) {
-        e.preventDefault();
-        worker1.terminate();
-    }
-    worker2 = new Worker("resources/worker-with-throw.js");
-}
-
-function test()
-{
-    InspectorTest.sendCommandOrDie("Target.setAutoAttach", {autoAttach: true, waitForDebuggerOnStart: false}, didEnableWorkerDebugging);
-
-    function didEnableWorkerDebugging(event)
-    {
-        InspectorTest.sendCommandOrDie("Runtime.evaluate", { expression: "startWorkers()" });
-    }
-
-    var workerRequestId = 1;
-    function sendCommandToWorker(method, params, workerId)
-    {
-        InspectorTest.sendCommand("Target.sendMessageToTarget",
-            {
-                "targetId": workerId,
-                "message": JSON.stringify({ "method": method,
-                                            "params": params,
-                                            "id": workerRequestId++ })
-            });
-    }
-
-    var waitForWorkers = 2;
-    InspectorTest.eventHandler["Target.attachedToTarget"] = function(messageObject)
-    {
-        var workerId = messageObject["params"]["targetInfo"]["targetId"];
-        InspectorTest.log("Worker created");
-        sendCommandToWorker("Runtime.enable", {}, workerId);
-        if (!--waitForWorkers)
-            InspectorTest.sendCommandOrDie("Runtime.evaluate", { expression: "worker1.postMessage(239);worker2.postMessage(42);" });
-    }
-
-    var workerTerminated = false;
-    var messageReceived = false;
-    InspectorTest.eventHandler["Target.receivedMessageFromTarget"] = function(messageObject)
-    {
-        var message = JSON.parse(messageObject["params"]["message"]);
-        if (message["method"] === "Runtime.exceptionThrown") {
-            var callFrames = message.params.exceptionDetails.stackTrace ? message.params.exceptionDetails.stackTrace.callFrames : [];
-            InspectorTest.log(callFrames.length > 0 ? "Message with stack trace received." : "[FAIL] Message contains empty stack trace");
-            messageReceived = true;
-            if (messageReceived && workerTerminated)
-                InspectorTest.completeTest();
-        }
-    }
-
-    InspectorTest.eventHandler["Target.detachedFromTarget"] = function(messageObject)
-    {
-        InspectorTest.eventHandler["Target.detachedFromTarget"] = undefined;
-        workerTerminated = true;
-        if (messageReceived && workerTerminated)
-            InspectorTest.completeTest();
-    }
-}
-</script>
-</head>
-<body onload="runTest()">Tests that console message from worker contains stack trace.</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/worker/exception-from-worker-contains-stack.js b/third_party/WebKit/LayoutTests/inspector-protocol/worker/exception-from-worker-contains-stack.js
new file mode 100644
index 0000000..37b6fa1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/worker/exception-from-worker-contains-stack.js
@@ -0,0 +1,51 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('Tests that console message from worker contains stack trace.');
+
+  var workerRequestId = 1;
+  function sendCommandToWorker(method, params, workerId) {
+    dp.Target.sendMessageToTarget({
+      targetId: workerId,
+      message: JSON.stringify({ method: method, params: params, id: workerRequestId++ })
+    });
+  }
+
+  var waitForWorkers = 2;
+  dp.Target.onAttachedToTarget(messageObject => {
+    var workerId = messageObject['params']['targetInfo']['targetId'];
+    testRunner.log('Worker created');
+    sendCommandToWorker('Runtime.enable', {}, workerId);
+    if (!--waitForWorkers)
+      session.evaluate('worker1.postMessage(239);worker2.postMessage(42);');
+  });
+
+  var workerTerminated = false;
+  var messageReceived = false;
+  dp.Target.onReceivedMessageFromTarget(messageObject => {
+    var message = JSON.parse(messageObject['params']['message']);
+    if (message['method'] === 'Runtime.exceptionThrown') {
+      var callFrames = message.params.exceptionDetails.stackTrace ? message.params.exceptionDetails.stackTrace.callFrames : [];
+      testRunner.log(callFrames.length > 0 ? 'Message with stack trace received.' : '[FAIL] Message contains empty stack trace');
+      messageReceived = true;
+      if (messageReceived && workerTerminated)
+        testRunner.completeTest();
+    }
+  });
+
+  function onDetached(messageObject) {
+    dp.Target.offDetachedFromTarget(onDetached);
+    workerTerminated = true;
+    if (messageReceived && workerTerminated)
+      testRunner.completeTest();
+  }
+  dp.Target.onDetachedFromTarget(onDetached);
+
+  await dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: false});
+  session.evaluate(`
+    window.worker1 = new Worker('${testRunner.url('../resources/worker-with-throw.js')}');
+    window.worker1.onerror = function(e) {
+      e.preventDefault();
+      worker1.terminate();
+    }
+    window.worker2 = new Worker('${testRunner.url('../resources/worker-with-throw.js')}');
+  `);
+})
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/worker/worker-console-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/worker/worker-console-expected.txt
index b2658f7..759dc25 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/worker/worker-console-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/worker/worker-console-expected.txt
@@ -1,11 +1,4 @@
-CONSOLE MESSAGE: line 6: message0
-CONSOLE MESSAGE: line 6: message1
-CONSOLE MESSAGE: line 6: message2
-CONSOLE ERROR: line 4: Uncaught (in promise) throw1
-CONSOLE MESSAGE: line 6: message3
-CONSOLE MESSAGE: line 6: message4
-CONSOLE MESSAGE: line 6: message5
-CONSOLE MESSAGE: line 6: message6
+
 Starting worker
 Logging in worker: message0
 Got log message from page: message0
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/worker/worker-console.html b/third_party/WebKit/LayoutTests/inspector-protocol/worker/worker-console.html
deleted file mode 100644
index 978d326..0000000
--- a/third_party/WebKit/LayoutTests/inspector-protocol/worker/worker-console.html
+++ /dev/null
@@ -1,289 +0,0 @@
-<html>
-<head>
-<script type="text/javascript" src="../../http/tests/inspector-protocol/resources/inspector-protocol-test.js"></script>
-<script>
-
-var worker;
-var onMessageCallbacks = {};
-
-function startWorker()
-{
-    var callback;
-    var promise = new Promise((fulfill) => callback = fulfill);
-    worker = new Worker("../resources/worker-console-worker.js");
-    worker.onmessage = function(event) {
-        worker.onmessage = onMessageFromWorker;
-        callback();
-    };
-    return promise;
-}
-
-function logInWorkerFromPage(message, callback)
-{
-    onMessageCallbacks[message] = callback;
-    worker.postMessage(message);
-}
-
-function onMessageFromWorker(event)
-{
-    var callback = onMessageCallbacks[event.data];
-    delete onMessageCallbacks[event.data];
-    if (callback)
-        callback();
-}
-
-function stopWorker()
-{
-    worker.terminate();
-    worker = null;
-}
-
-function test()
-{
-    var workerEventHandler = {};
-    InspectorTest.eventHandler["Target.attachedToTarget"] = onWorkerCreated;
-    InspectorTest.eventHandler["Target.receivedMessageFromTarget"] = onWorkerMessage;
-    workerEventHandler["Runtime.consoleAPICalled"] = onConsoleAPICalledFromWorker;
-
-    var workerId;
-
-    function onWorkerCreated(payload)
-    {
-        InspectorTest.log("Worker.created");
-        workerId = payload.params.targetInfo.targetId;
-    }
-
-    var requestId = 0;
-    var dispatchTable = [];
-
-    function sendCommandToWorker(method, params, callback)
-    {
-        dispatchTable[++requestId] = callback;
-        var messageObject = {
-            "method": method,
-            "params": params,
-            "id": requestId
-        };
-        InspectorTest.sendCommandOrDie("Target.sendMessageToTarget", {
-            targetId: workerId,
-            message: JSON.stringify(messageObject)
-        });
-    }
-
-    function onWorkerMessage(payload)
-    {
-        if (payload.params.targetId !== workerId)
-            InspectorTest.log("targetId mismatch");
-        var messageObject = JSON.parse(payload.params.message);
-        var messageId = messageObject["id"];
-        if (typeof messageId === "number") {
-            var handler = dispatchTable[messageId];
-            dispatchTable[messageId] = null;
-            if (handler && typeof handler === "function")
-                handler(messageObject);
-        } else {
-            var eventName = messageObject["method"];
-            var eventHandler = workerEventHandler[eventName];
-            if (eventHandler)
-                eventHandler(messageObject);
-        }
-    }
-
-    function logInWorker(message, next)
-    {
-        InspectorTest.log("Logging in worker: " + message);
-        InspectorTest.eventHandler["Log.entryAdded"] = onLogEntry;
-        InspectorTest.evaluateInPage("logInWorkerFromPage(\"" + message + "\")");
-
-        function onLogEntry(payload)
-        {
-            InspectorTest.log("Got log message from page: " + payload.params.entry.text);
-            delete InspectorTest.eventHandler["Log.entryAdded"];
-            next();
-        }
-    }
-
-    var gotMessages = [];
-    var waitingForMessage;
-    var waitingForMessageCallback;
-
-    function onConsoleAPICalledFromWorker(payload)
-    {
-        var message = payload.params.args[0].value;
-        InspectorTest.log("Got console API call from worker: " + message);
-        gotMessages.push(message);
-        if (message === waitingForMessage)
-            waitingForMessageCallback();
-    }
-
-    function waitForMessage(message, next)
-    {
-        if (gotMessages.indexOf(message) !== -1) {
-            next();
-            return;
-        }
-        waitingForMessage = message;
-        waitingForMessageCallback = next;
-    }
-
-    var steps = [
-        function listenToConsole(next)
-        {
-            InspectorTest.sendCommandOrDie("Log.enable", {}, next);
-        },
-
-        function start0(next)
-        {
-            InspectorTest.log("Starting worker");
-            InspectorTest.evaluateInPageAsync("startWorker()").then(next);
-        },
-
-        function log0(next)
-        {
-            logInWorker("message0", next);
-        },
-
-        function stop0(next)
-        {
-            InspectorTest.log("Stopping worker");
-            InspectorTest.evaluateInPage("stopWorker()", next);
-        },
-
-        function start1(next)
-        {
-            InspectorTest.log("Starting worker");
-            InspectorTest.evaluateInPageAsync("startWorker()").then(next);
-        },
-
-        function log1(next)
-        {
-            logInWorker("message1", next);
-        },
-
-        function enable1(next)
-        {
-            InspectorTest.log("Starting autoattach");
-            InspectorTest.sendCommandOrDie("Target.setAutoAttach", {autoAttach: true, waitForDebuggerOnStart: false}, next);
-        },
-
-        function consoleEnable1(next)
-        {
-            InspectorTest.log("Sending Runtime.enable to worker");
-            waitForMessage("message1", next);
-            sendCommandToWorker("Runtime.enable", {});
-        },
-
-        function log2(next)
-        {
-            logInWorker("message2", next);
-        },
-
-        function waitForMessage2(next)
-        {
-            waitForMessage("message2", next);
-        },
-
-        function throw1(next)
-        {
-            logInWorker("throw1", next);
-        },
-
-        function disable1(next)
-        {
-            InspectorTest.log("Stopping autoattach");
-            InspectorTest.sendCommandOrDie("Target.setAutoAttach", {autoAttach: false, waitForDebuggerOnStart: false}, next);
-        },
-
-        function log3(next)
-        {
-            logInWorker("message3", next);
-        },
-
-        function stop1(next)
-        {
-            InspectorTest.log("Stopping worker");
-            InspectorTest.evaluateInPage("stopWorker()", next);
-        },
-
-
-        function enable2(next)
-        {
-            InspectorTest.log("Starting autoattach");
-            InspectorTest.sendCommandOrDie("Target.setAutoAttach", {autoAttach: true, waitForDebuggerOnStart: false}, next);
-        },
-
-        function start2(next)
-        {
-            InspectorTest.log("Starting worker");
-            InspectorTest.evaluateInPageAsync("startWorker()").then(next);
-        },
-
-        function log4(next)
-        {
-            logInWorker("message4", next);
-        },
-
-        function consoleEnable2(next)
-        {
-            InspectorTest.log("Sending Runtime.enable to worker");
-            waitForMessage("message4", next);
-            sendCommandToWorker("Runtime.enable", {});
-        },
-
-        function log5(next)
-        {
-            logInWorker("message5", next);
-        },
-
-        function waitForMessage5(next)
-        {
-            waitForMessage("message5", next);
-        },
-
-        function stop2(next)
-        {
-            InspectorTest.log("Stopping worker");
-            InspectorTest.evaluateInPage("stopWorker()", next);
-        },
-
-        function start3(next)
-        {
-            InspectorTest.log("Starting worker");
-            InspectorTest.evaluateInPageAsync("startWorker()").then(next);
-        },
-
-        function log6(next)
-        {
-            logInWorker("message6", next);
-        },
-
-        function stop3(next)
-        {
-            InspectorTest.log("Stopping worker");
-            InspectorTest.evaluateInPage("stopWorker()", next);
-        },
-
-        function disable2(next)
-        {
-            InspectorTest.log("Stopping autoattach");
-            InspectorTest.sendCommandOrDie("Target.setAutoAttach", {autoAttach: false, waitForDebuggerOnStart: false}, next);
-        }
-    ];
-
-    function runNextStep()
-    {
-        if (!steps.length) {
-            InspectorTest.completeTest();
-            return;
-        }
-        var nextStep = steps.shift();
-        InspectorTest.safeWrap(nextStep)(runNextStep);
-    }
-
-    runNextStep();
-}
-</script>
-</head>
-<body onload="runTest()">
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/worker/worker-console.js b/third_party/WebKit/LayoutTests/inspector-protocol/worker/worker-console.js
new file mode 100644
index 0000000..a294421
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/worker/worker-console.js
@@ -0,0 +1,244 @@
+(async function(testRunner) {
+  let {page, session, dp} = await testRunner.startBlank('');
+
+  await session.evaluate(`
+    var worker = null;
+    var onMessageCallbacks = {};
+
+    function startWorker() {
+      var callback;
+      var promise = new Promise((fulfill) => callback = fulfill);
+      worker = new Worker('${testRunner.url('../resources/worker-console-worker.js')}');
+      worker.onmessage = function(event) {
+        worker.onmessage = onMessageFromWorker;
+        callback();
+      };
+      return promise;
+    }
+
+    function logInWorkerFromPage(message, callback) {
+      onMessageCallbacks[message] = callback;
+      worker.postMessage(message);
+    }
+
+    function onMessageFromWorker(event) {
+      var callback = onMessageCallbacks[event.data];
+      delete onMessageCallbacks[event.data];
+      if (callback)
+        callback();
+    }
+
+    function stopWorker() {
+      worker.terminate();
+      worker = null;
+    }
+  `);
+
+  var workerEventHandler = {};
+  dp.Target.onAttachedToTarget(onWorkerCreated);
+  dp.Target.onReceivedMessageFromTarget(onWorkerMessage);
+  workerEventHandler['Runtime.consoleAPICalled'] = onConsoleAPICalledFromWorker;
+
+  var workerId;
+
+  function onWorkerCreated(payload) {
+    testRunner.log('Worker.created');
+    workerId = payload.params.targetInfo.targetId;
+  }
+
+  var requestId = 0;
+  var dispatchTable = [];
+
+  function sendCommandToWorker(method, params, callback) {
+    dispatchTable[++requestId] = callback;
+    var messageObject = {
+      'method': method,
+      'params': params,
+      'id': requestId
+    };
+    dp.Target.sendMessageToTarget({
+      targetId: workerId,
+      message: JSON.stringify(messageObject)
+    });
+  }
+
+  function onWorkerMessage(payload) {
+    if (payload.params.targetId !== workerId)
+      testRunner.log('targetId mismatch');
+    var messageObject = JSON.parse(payload.params.message);
+    var messageId = messageObject['id'];
+    if (typeof messageId === 'number') {
+      var handler = dispatchTable[messageId];
+      dispatchTable[messageId] = null;
+      if (handler && typeof handler === 'function')
+        handler(messageObject);
+    } else {
+      var eventName = messageObject['method'];
+      var eventHandler = workerEventHandler[eventName];
+      if (eventHandler)
+        eventHandler(messageObject);
+    }
+  }
+
+  function logInWorker(message, next) {
+    testRunner.log('Logging in worker: ' + message);
+    dp.Log.onEntryAdded(onLogEntry);
+    session.evaluate('logInWorkerFromPage(\'' + message + '\')');
+
+    function onLogEntry(payload) {
+      testRunner.log('Got log message from page: ' + payload.params.entry.text);
+      dp.Log.offEntryAdded(onLogEntry);
+      next();
+    }
+  }
+
+  var gotMessages = [];
+  var waitingForMessage;
+  var waitingForMessageCallback;
+
+  function onConsoleAPICalledFromWorker(payload) {
+    var message = payload.params.args[0].value;
+    testRunner.log('Got console API call from worker: ' + message);
+    gotMessages.push(message);
+    if (message === waitingForMessage)
+      waitingForMessageCallback();
+  }
+
+  function waitForMessage(message, next) {
+    if (gotMessages.indexOf(message) !== -1) {
+      next();
+      return;
+    }
+    waitingForMessage = message;
+    waitingForMessageCallback = next;
+  }
+
+  var steps = [
+    function listenToConsole(next) {
+      dp.Log.enable().then(next);
+    },
+
+    function start0(next) {
+      testRunner.log('Starting worker');
+      session.evaluateAsync('startWorker()').then(next);
+    },
+
+    function log0(next) {
+      logInWorker('message0', next);
+    },
+
+    function stop0(next) {
+      testRunner.log('Stopping worker');
+      session.evaluate('stopWorker()').then(next);
+    },
+
+    function start1(next) {
+      testRunner.log('Starting worker');
+      session.evaluateAsync('startWorker()').then(next);
+    },
+
+    function log1(next) {
+      logInWorker('message1', next);
+    },
+
+    function enable1(next) {
+      testRunner.log('Starting autoattach');
+      dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: false}).then(next);
+    },
+
+    function consoleEnable1(next) {
+      testRunner.log('Sending Runtime.enable to worker');
+      waitForMessage('message1', next);
+      sendCommandToWorker('Runtime.enable', {});
+    },
+
+    function log2(next) {
+      logInWorker('message2', next);
+    },
+
+    function waitForMessage2(next) {
+      waitForMessage('message2', next);
+    },
+
+    function throw1(next) {
+      logInWorker('throw1', next);
+    },
+
+    function disable1(next) {
+      testRunner.log('Stopping autoattach');
+      dp.Target.setAutoAttach({autoAttach: false, waitForDebuggerOnStart: false}).then(next);
+    },
+
+    function log3(next) {
+      logInWorker('message3', next);
+    },
+
+    function stop1(next) {
+      testRunner.log('Stopping worker');
+      session.evaluate('stopWorker()').then(next);
+    },
+
+    function enable2(next) {
+      testRunner.log('Starting autoattach');
+      dp.Target.setAutoAttach({autoAttach: true, waitForDebuggerOnStart: false}).then(next);
+    },
+
+    function start2(next) {
+      testRunner.log('Starting worker');
+      session.evaluateAsync('startWorker()').then(next);
+    },
+
+    function log4(next) {
+      logInWorker('message4', next);
+    },
+
+    function consoleEnable2(next) {
+      testRunner.log('Sending Runtime.enable to worker');
+      waitForMessage('message4', next);
+      sendCommandToWorker('Runtime.enable', {});
+    },
+
+    function log5(next) {
+      logInWorker('message5', next);
+    },
+
+    function waitForMessage5(next) {
+      waitForMessage('message5', next);
+    },
+
+    function stop2(next) {
+      testRunner.log('Stopping worker');
+      session.evaluate('stopWorker()').then(next);
+    },
+
+    function start3(next) {
+      testRunner.log('Starting worker');
+      session.evaluateAsync('startWorker()').then(next);
+    },
+
+    function log6(next) {
+      logInWorker('message6', next);
+    },
+
+    function stop3(next) {
+      testRunner.log('Stopping worker');
+      session.evaluate('stopWorker()').then(next);
+    },
+
+    function disable2(next) {
+      testRunner.log('Stopping autoattach');
+      dp.Target.setAutoAttach({autoAttach: false, waitForDebuggerOnStart: false}).then(next);
+    }
+  ];
+
+  function runNextStep() {
+    if (!steps.length) {
+      testRunner.completeTest();
+      return;
+    }
+    var nextStep = steps.shift();
+    nextStep(runNextStep);
+  }
+
+  runNextStep();
+})
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/hide-atomic-inlines-after-ellipsis-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/text/hide-atomic-inlines-after-ellipsis-expected.png
new file mode 100644
index 0000000..5f3a060
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/text/hide-atomic-inlines-after-ellipsis-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/hide-atomic-inlines-after-ellipsis-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/hide-atomic-inlines-after-ellipsis-expected.png
new file mode 100644
index 0000000..b889c638
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/fast/text/hide-atomic-inlines-after-ellipsis-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/hide-atomic-inlines-after-ellipsis-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/hide-atomic-inlines-after-ellipsis-expected.png
new file mode 100644
index 0000000..80eb4b84
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/fast/text/hide-atomic-inlines-after-ellipsis-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/text/hide-atomic-inlines-after-ellipsis-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/text/hide-atomic-inlines-after-ellipsis-expected.png
new file mode 100644
index 0000000..b21b7ce
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/text/hide-atomic-inlines-after-ellipsis-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/text/hide-atomic-inlines-after-ellipsis-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/text/hide-atomic-inlines-after-ellipsis-expected.png
new file mode 100644
index 0000000..351a4fd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/text/hide-atomic-inlines-after-ellipsis-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/fast/text/hide-atomic-inlines-after-ellipsis-expected.png b/third_party/WebKit/LayoutTests/platform/win7/fast/text/hide-atomic-inlines-after-ellipsis-expected.png
new file mode 100644
index 0000000..8ff6f6d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/fast/text/hide-atomic-inlines-after-ellipsis-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/resources/testharnessreport.js b/third_party/WebKit/LayoutTests/resources/testharnessreport.js
index 1d3d729..72ddacd0 100644
--- a/third_party/WebKit/LayoutTests/resources/testharnessreport.js
+++ b/third_party/WebKit/LayoutTests/resources/testharnessreport.js
@@ -78,9 +78,11 @@
 
     function isWPTManualTest() {
         var path = location.pathname;
-        if (location.hostname == 'web-platform.test' && path.endsWith('-manual.html'))
+        if (location.hostname == 'web-platform.test'
+            && /.*-manual(\.https)?\.html$/.test(path)) {
             return true;
-        return /\/external\/wpt\/.*-manual\.html$/.test(path);
+        }
+        return /\/external\/wpt\/.*-manual(\.https)?\.html$/.test(path);
     }
 
     // Returns a directory part relative to WPT root and a basename part of the
@@ -113,8 +115,10 @@
         }
 
         var src;
-        if (pathAndBase.startsWith('/fullscreen/')) {
-            // Fullscreen tests all use the same automation script.
+        if (pathAndBase.startsWith('/fullscreen/')
+            || pathAndBase.startsWith('/webusb/')) {
+            // Fullscreen tests all use the same automation script and WebUSB
+            // tests borrow it.
             src = automationPath + '/fullscreen/auto-click.js';
         } else if (pathAndBase.startsWith('/pointerevents/')
                    || pathAndBase.startsWith('/uievents/')
@@ -268,5 +272,4 @@
             window.addEventListener('load', done);
         }
     });
-
 })();
diff --git a/third_party/WebKit/LayoutTests/usb/resources/usb-helpers.js b/third_party/WebKit/LayoutTests/usb/resources/usb-helpers.js
deleted file mode 100644
index 35ba6ea..0000000
--- a/third_party/WebKit/LayoutTests/usb/resources/usb-helpers.js
+++ /dev/null
@@ -1,95 +0,0 @@
-'use strict';
-
-function usb_test(func, name, properties) {
-  promise_test(async () => {
-    await navigator.usb.test.initialize()
-    try {
-      await func();
-    } finally {
-      await navigator.usb.test.reset();
-    }
-  }, name, properties);
-}
-
-// Returns a promise that is resolved when the next USBConnectionEvent of the
-// given type is received.
-function connectionEventPromise(eventType) {
-  return new Promise(resolve => {
-    let eventHandler = e => {
-      assert_true(e instanceof USBConnectionEvent);
-      navigator.usb.removeEventListener(eventType, eventHandler);
-      resolve(e.device);
-    };
-    navigator.usb.addEventListener(eventType, eventHandler);
-  });
-}
-
-// Creates a fake device and returns a promise that resolves once the
-// 'connect' event is fired for the fake device. The promise is resolved with
-// an object containing the fake USB device and the corresponding USBDevice.
-function getFakeDevice() {
-  let promise = connectionEventPromise('connect');
-  let fakeDevice = navigator.usb.test.addFakeDevice(fakeDeviceInit);
-  return promise.then(device => {
-    return { device: device, fakeDevice: fakeDevice };
-  });
-}
-
-// Disconnects the given device and returns a promise that is resolved when it
-// is done.
-function waitForDisconnect(fakeDevice) {
-  let promise = connectionEventPromise('disconnect');
-  fakeDevice.disconnect();
-  return promise;
-}
-
-function assertRejectsWithError(promise, name, message) {
-  return promise.then(() => {
-    assert_unreached('expected promise to reject with ' + name);
-  }, error => {
-    assert_equals(error.name, name);
-    if (message !== undefined)
-      assert_equals(error.message, message);
-  });
-}
-
-function assertDeviceInfoEquals(usbDevice, deviceInit) {
-  for (var property in deviceInit) {
-    if (property == 'activeConfigurationValue') {
-      if (deviceInit.activeConfigurationValue == 0) {
-        assert_equals(usbDevice.configuration, null);
-      } else {
-        assert_equals(usbDevice.configuration.configurationValue,
-                      deviceInit.activeConfigurationValue);
-      }
-    } else if (Array.isArray(deviceInit[property])) {
-      assert_equals(usbDevice[property].length, deviceInit[property].length);
-      for (var i = 0; i < usbDevice[property].length; ++i)
-        assertDeviceInfoEquals(usbDevice[property][i], deviceInit[property][i]);
-    } else {
-      assert_equals(usbDevice[property], deviceInit[property], property);
-    }
-  }
-}
-
-// TODO(reillyg): Remove when jyasskin upstreams this to testharness.js:
-// https://crbug.com/509058.
-function callWithKeyDown(functionCalledOnKeyPress) {
-  return new Promise(resolve => {
-    function onKeyPress() {
-      document.removeEventListener('keypress', onKeyPress, false);
-      resolve(functionCalledOnKeyPress());
-    }
-    document.addEventListener('keypress', onKeyPress, false);
-
-    eventSender.keyDown(' ', []);
-  });
-}
-
-function runGarbageCollection() {
-  // Run gc() as a promise.
-  return new Promise((resolve, reject) => {
-    GCController.collect();
-    setTimeout(resolve, 0);
-  });
-}
diff --git a/third_party/WebKit/LayoutTests/usb/resources/webusb-test.js b/third_party/WebKit/LayoutTests/usb/resources/webusb-test.js
deleted file mode 100644
index 42a9495c..0000000
--- a/third_party/WebKit/LayoutTests/usb/resources/webusb-test.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict';
-
-(() => {
-  let dependencies = [
-    "file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js",
-    "file:///gen/device/usb/public/interfaces/device.mojom.js",
-    "file:///gen/device/usb/public/interfaces/device_manager.mojom.js",
-    "file:///gen/device/usb/public/interfaces/chooser_service.mojom.js",
-    "resources/webusb-test-impl.js",
-  ];
-  for (let dep of dependencies)
-    document.write("<script src='" + dep + "'></script>");
-})();
diff --git a/third_party/WebKit/LayoutTests/usb/test-polyfil.html b/third_party/WebKit/LayoutTests/usb/test-polyfil.html
deleted file mode 100644
index 6e549ae..0000000
--- a/third_party/WebKit/LayoutTests/usb/test-polyfil.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="resources/webusb-test.js"></script>
-<script>
-'use strict';
-
-promise_test(() => {
-  return navigator.usb.test.initialize();
-}, 'WebUSB Test API available');
-</script>
diff --git a/third_party/WebKit/LayoutTests/usb/usbDevice-iframe.html b/third_party/WebKit/LayoutTests/usb/usbDevice-iframe.html
index d2c4aab..98256f1 100644
--- a/third_party/WebKit/LayoutTests/usb/usbDevice-iframe.html
+++ b/third_party/WebKit/LayoutTests/usb/usbDevice-iframe.html
@@ -1,58 +1,16 @@
 <!DOCTYPE html>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
-<script src="resources/fake-devices.js"></script>
-<script src="resources/usb-helpers.js"></script>
-<script src="resources/webusb-test.js"></script>
+<script src="../external/wpt/webusb/resources/fake-devices.js"></script>
+<script src="../external/wpt/resources/chromium/mojo_bindings.js"></script>
+<script src="../external/wpt/resources/chromium/device.mojom.js"></script>
+<script src="../external/wpt/resources/chromium/device_manager.mojom.js"></script>
+<script src="../external/wpt/resources/chromium/chooser_service.mojom.js"></script>
+<script src="../external/wpt/resources/chromium/webusb-test.js"></script>
 <body>
 <script>
 'use strict';
 
-function runIframeDisconnectTest(onDeviceConnected) {
-  return navigator.usb.test.initialize().then(() => {
-    return new Promise((resolve, reject) => {
-      let opened = false;
-
-      let iframe = document.createElement('iframe');
-      iframe.src = 'resources/open-in-iframe.html';
-      iframe.onload = () => {
-        navigator.usb.test.attachToWindow(iframe.contentWindow).then(() => {
-          iframe.contentWindow.postMessage('Ready', '*');
-        });
-      };
-
-      window.onmessage = messageEvent => {
-        if (messageEvent.data == 'Ready') {
-          let fakeDevice = navigator.usb.test.addFakeDevice(fakeDeviceInit);
-          fakeDevice.onclose = () => {
-            assert_true(opened);
-            resolve();
-          };
-        } else if (messageEvent.data == 'Success') {
-          opened = true;
-          onDeviceConnected(iframe);
-        } else {
-          reject(messageEvent.data);
-        }
-      };
-
-      document.body.appendChild(iframe);
-    });
-  });
-}
-
-promise_test(() => {
-  return runIframeDisconnectTest(iframe => {
-    document.body.removeChild(iframe);
-  });
-}, 'detaching iframe disconnects device.');
-
-promise_test(() => {
-  return runIframeDisconnectTest(iframe => {
-    iframe.src = 'about:blank';
-  });
-}, 'navigating iframe disconnects device.');
-
 promise_test(() => {
   return navigator.usb.test.initialize().then(() => {
     return new Promise((resolve, reject) => {
@@ -60,7 +18,7 @@
       let fakeDeviceGuid = null;
 
       let iframe = document.createElement('iframe');
-      iframe.src = 'resources/open-in-iframe.html';
+      iframe.src = '../external/wpt/webusb/resources/open-in-iframe.html';
       iframe.onload = () => {
         navigator.usb.test.attachToWindow(iframe.contentWindow).then(() => {
           iframe.contentWindow.postMessage('Ready', '*');
@@ -70,10 +28,9 @@
       window.onmessage = messageEvent => {
         if (messageEvent.data == 'Ready') {
           document.body.removeChild(iframe);
-          runGarbageCollection().then(() => {
-            navigator.usb.test.addFakeDevice(fakeDeviceInit);
-            resolve();
-          });
+          GCController.collect();
+          navigator.usb.test.addFakeDevice(fakeDeviceInit);
+          resolve();
         } else {
           reject(messageEvent.data);
         }
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index eefc614..17972230 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1506,6 +1506,7 @@
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/WebKit/Source/core/editing:unit_tests",
+    "//third_party/WebKit/Source/core/mojo:unit_tests",
   ]
 }
 
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.json5 b/third_party/WebKit/Source/core/css/CSSProperties.json5
index 4c83d8a8..13bf69d 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.json5
+++ b/third_party/WebKit/Source/core/css/CSSProperties.json5
@@ -2953,11 +2953,19 @@
       api_methods: ["parseSingleValue"],
       custom_all: true,
       interpolable: true,
+      field_template: "storage_only",
+      include_paths: ["platform/graphics/Color.h"],
+      type_name: "Color",
+      default_value: "0",
+      field_group: "rare-non-inherited->multi-col",
     },
     {
       name: "column-rule-style",
-      initial: "InitialColumnRuleStyle",
       type_name: "EBorderStyle",
+      field_template: "keyword",
+      default_value: "none",
+      keywords: ["none", "hidden", "inset", "groove", "outset", "ridge", "dotted", "dashed", "solid", "double"],
+      field_group: "rare-non-inherited->multi-col",
     },
     {
       name: "column-rule-width",
@@ -2965,6 +2973,11 @@
       api_methods: ["parseSingleValue"],
       converter: "ConvertLineWidth<unsigned short>",
       interpolable: true,
+      field_template: "storage_only",
+      include_paths: ["platform/LayoutUnit.h"],
+      type_name: "LayoutUnit",
+      default_value: "LayoutUnit(3)",
+      field_group: "rare-non-inherited->multi-col",
     },
     {
       name: "column-span",
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleDiffFunctions.json5 b/third_party/WebKit/Source/core/css/ComputedStyleDiffFunctions.json5
index 874286b..89e5ee9 100644
--- a/third_party/WebKit/Source/core/css/ComputedStyleDiffFunctions.json5
+++ b/third_party/WebKit/Source/core/css/ComputedStyleDiffFunctions.json5
@@ -74,7 +74,8 @@
                 "flex-shrink", "flex-grow", "flex-direction", "flex-wrap", "-webkit-box-align", 
                 "-webkit-box-pack", "-webkit-box-lines", "-webkit-box-orient",
                 "grid-row-start", "grid-row-end", "grid-column-start", "grid-column-end",
-                "column-gap", "column-width", "ColumnRule", "VisitedLinkColumnRuleColor", 
+                "column-gap", "column-width", "column-rule-style",
+                "column-rule-width", "column-rule-color", "ColumnRuleColorIsCurrentColor", "VisitedLinkColumnRuleColor",
                 "column-count", "ColumnAutoCount", "ColumnAutoWidth", "column-fill", "ColumnNormalGap", "column-span",],
         methods_to_diff: [
           {
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5 b/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5
index 4845481..f121ba3 100644
--- a/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5
+++ b/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5
@@ -845,12 +845,11 @@
       default_value: "false",
     },
     {
-      name: "ColumnRule",
-      field_template: "storage_only",
-      type_name: "BorderValue",
+      name: "ColumnRuleColorIsCurrentColor",
+      field_template: "primitive",
+      default_value: "true",
+      type_name: "bool",
       field_group: "rare-non-inherited->multi-col",
-      default_value: "BorderValue()",
-      include_paths: ["core/style/BorderValue.h"],
     },
     {
       name: "VisitedLinkColumnRuleColor",
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 3c48c5c5..8b987e68 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -2164,7 +2164,10 @@
     InheritHtmlAndBodyElementStyles(change);
     if (document_element->ShouldCallRecalcStyle(change)) {
       TRACE_EVENT0("blink,blink_style", "Document::recalcStyle");
+      Element* viewport_defining = ViewportDefiningElement();
       document_element->RecalcStyle(change);
+      if (viewport_defining != ViewportDefiningElement())
+        ViewportDefiningElementDidChange();
     }
     if (document_element->NeedsReattachLayoutTree() ||
         document_element->ChildNeedsReattachLayoutTree()) {
@@ -2205,6 +2208,23 @@
   CSSTiming::From(*this).RecordUpdateDuration(update_duration_seconds);
 }
 
+void Document::ViewportDefiningElementDidChange() {
+  HTMLBodyElement* body = FirstBodyElement();
+  if (!body)
+    return;
+  LayoutObject* layout_object = body->GetLayoutObject();
+  if (layout_object && layout_object->IsLayoutBlock()) {
+    // When the overflow style for documentElement changes to or from visible,
+    // it changes whether the body element's box should have scrollable overflow
+    // on its own box or propagated to the viewport. If the body style did not
+    // need a recalc, this will not be updated as its done as part of setting
+    // ComputedStyle on the LayoutObject. Force a SetStyle for body when the
+    // ViewportDefiningElement changes in order to trigger an update of
+    // HasOverflowClip() and the PaintLayer in StyleDidChange().
+    layout_object->SetStyle(ComputedStyle::Clone(*layout_object->Style()));
+  }
+}
+
 void Document::NotifyLayoutTreeOfSubtreeChanges() {
   if (!GetLayoutViewItem().WasNotifiedOfSubtreeChange())
     return;
@@ -2985,11 +3005,11 @@
   Element* root_element = documentElement();
   Element* body_element = body();
   if (!root_element)
-    return 0;
+    return nullptr;
   if (!root_style) {
     root_style = root_element->GetComputedStyle();
     if (!root_style)
-      return 0;
+      return nullptr;
   }
   if (body_element && root_style->IsOverflowVisible() &&
       isHTMLHtmlElement(*root_element))
@@ -3230,8 +3250,6 @@
     return;
 
   if (load_event_progress_ <= kUnloadEventInProgress) {
-    if (GetPage())
-      GetPage()->WillUnloadDocument(*this);
     Element* current_focused_element = FocusedElement();
     if (isHTMLInputElement(current_focused_element))
       toHTMLInputElement(*current_focused_element).EndEditing();
@@ -5713,6 +5731,9 @@
   if (!RuntimeEnabledFeatures::FeaturePolicyEnabled())
     return;
 
+  if (!feature_policy_header.IsEmpty())
+    UseCounter::Count(*this, WebFeature::kFeaturePolicyHeader);
+
   WebFeaturePolicy* parent_feature_policy = nullptr;
   WebParsedFeaturePolicy container_policy;
   Vector<String> messages;
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h
index 758c631f..d7ba059 100644
--- a/third_party/WebKit/Source/core/dom/Document.h
+++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -1447,6 +1447,7 @@
   void SendSensitiveInputVisibilityInternal();
 
   bool HaveImportsLoaded() const;
+  void ViewportDefiningElementDidChange();
 
   DocumentLifecycle lifecycle_;
 
diff --git a/third_party/WebKit/Source/core/dom/DocumentTest.cpp b/third_party/WebKit/Source/core/dom/DocumentTest.cpp
index 6ebc468..582986e 100644
--- a/third_party/WebKit/Source/core/dom/DocumentTest.cpp
+++ b/third_party/WebKit/Source/core/dom/DocumentTest.cpp
@@ -294,12 +294,8 @@
   MockValidationMessageClient() { Reset(); }
   void Reset() {
     show_validation_message_was_called = false;
-    will_unload_document_was_called = false;
-    document_detached_was_called = false;
   }
   bool show_validation_message_was_called;
-  bool will_unload_document_was_called;
-  bool document_detached_was_called;
 
   // ValidationMessageClient functions.
   void ShowValidationMessage(const Element& anchor,
@@ -313,12 +309,6 @@
   bool IsValidationMessageVisible(const Element& anchor) override {
     return true;
   }
-  void WillUnloadDocument(const Document&) override {
-    will_unload_document_was_called = true;
-  }
-  void DocumentDetached(const Document&) override {
-    document_detached_was_called = true;
-  }
   void WillBeDestroyed() override {}
 
   // DEFINE_INLINE_VIRTUAL_TRACE() { ValidationMessageClient::trace(visitor); }
@@ -826,8 +816,6 @@
 
   // prepareForCommit() unloads the document, and shutdown.
   GetDocument().GetFrame()->PrepareForCommit();
-  EXPECT_TRUE(mock_client->will_unload_document_was_called);
-  EXPECT_TRUE(mock_client->document_detached_was_called);
   // Unload handler tried to show a validation message, but it should fail.
   EXPECT_FALSE(mock_client->show_validation_message_was_called);
 
diff --git a/third_party/WebKit/Source/core/editing/EditingUtilitiesTest.cpp b/third_party/WebKit/Source/core/editing/EditingUtilitiesTest.cpp
index 2f2866d..eb00000 100644
--- a/third_party/WebKit/Source/core/editing/EditingUtilitiesTest.cpp
+++ b/third_party/WebKit/Source/core/editing/EditingUtilitiesTest.cpp
@@ -131,9 +131,9 @@
   Node* host = GetDocument().getElementById("host");
   Node* table = GetDocument().getElementById("table");
 
-  EXPECT_EQ(table, TableElementJustBefore(VisiblePosition::AfterNode(table)));
+  EXPECT_EQ(table, TableElementJustBefore(VisiblePosition::AfterNode(*table)));
   EXPECT_EQ(table, TableElementJustBefore(
-                       VisiblePositionInFlatTree::AfterNode(table)));
+                       VisiblePositionInFlatTree::AfterNode(*table)));
 
   EXPECT_EQ(table,
             TableElementJustBefore(VisiblePosition::LastPositionInNode(table)));
@@ -145,9 +145,9 @@
   EXPECT_EQ(table, TableElementJustBefore(
                        CreateVisiblePosition(PositionInFlatTree(host, 2))));
 
-  EXPECT_EQ(nullptr, TableElementJustBefore(VisiblePosition::AfterNode(host)));
-  EXPECT_EQ(nullptr,
-            TableElementJustBefore(VisiblePositionInFlatTree::AfterNode(host)));
+  EXPECT_EQ(nullptr, TableElementJustBefore(VisiblePosition::AfterNode(*host)));
+  EXPECT_EQ(nullptr, TableElementJustBefore(
+                         VisiblePositionInFlatTree::AfterNode(*host)));
 
   EXPECT_EQ(nullptr,
             TableElementJustBefore(VisiblePosition::LastPositionInNode(host)));
diff --git a/third_party/WebKit/Source/core/editing/SelectionController.cpp b/third_party/WebKit/Source/core/editing/SelectionController.cpp
index 2bdb9123..26338841 100644
--- a/third_party/WebKit/Source/core/editing/SelectionController.cpp
+++ b/third_party/WebKit/Source/core/editing/SelectionController.cpp
@@ -527,28 +527,37 @@
     const HitTestResult& result,
     AppendTrailingWhitespace append_trailing_whitespace) {
   Node* inner_node = result.InnerNode();
-  VisibleSelectionInFlatTree new_selection;
 
   if (!inner_node || !inner_node->GetLayoutObject())
     return;
 
   const VisiblePositionInFlatTree& pos = VisiblePositionOfHitTestResult(result);
-  if (pos.IsNotNull()) {
-    const PositionInFlatTree& marker_position =
-        pos.DeepEquivalent().ParentAnchoredEquivalent();
-    const DocumentMarker* const marker =
-        inner_node->GetDocument().Markers().MarkerAtPosition(
-            ToPositionInDOMTree(marker_position),
-            DocumentMarker::MisspellingMarkers());
-    if (marker) {
-      Node* container_node = marker_position.ComputeContainerNode();
-      const PositionInFlatTree start(container_node, marker->StartOffset());
-      const PositionInFlatTree end(container_node, marker->EndOffset());
-      new_selection = CreateVisibleSelection(
-          SelectionInFlatTree::Builder().Collapse(start).Extend(end).Build());
-    }
+  if (pos.IsNull()) {
+    UpdateSelectionForMouseDownDispatchingSelectStart(
+        inner_node, VisibleSelectionInFlatTree(), kWordGranularity,
+        HandleVisibility::kNotVisible);
+    return;
   }
 
+  const PositionInFlatTree& marker_position =
+      pos.DeepEquivalent().ParentAnchoredEquivalent();
+  const DocumentMarker* const marker =
+      inner_node->GetDocument().Markers().MarkerAtPosition(
+          ToPositionInDOMTree(marker_position),
+          DocumentMarker::MisspellingMarkers());
+  if (!marker) {
+    UpdateSelectionForMouseDownDispatchingSelectStart(
+        inner_node, VisibleSelectionInFlatTree(), kWordGranularity,
+        HandleVisibility::kNotVisible);
+    return;
+  }
+
+  Node* container_node = marker_position.ComputeContainerNode();
+  const PositionInFlatTree start(container_node, marker->StartOffset());
+  const PositionInFlatTree end(container_node, marker->EndOffset());
+  VisibleSelectionInFlatTree new_selection = CreateVisibleSelection(
+      SelectionInFlatTree::Builder().Collapse(start).Extend(end).Build());
+
   if (append_trailing_whitespace == AppendTrailingWhitespace::kShouldAppend)
     new_selection.AppendTrailingWhitespace();
 
diff --git a/third_party/WebKit/Source/core/editing/VisiblePosition.cpp b/third_party/WebKit/Source/core/editing/VisiblePosition.cpp
index 61cda151..80b34b7 100644
--- a/third_party/WebKit/Source/core/editing/VisiblePosition.cpp
+++ b/third_party/WebKit/Source/core/editing/VisiblePosition.cpp
@@ -102,9 +102,9 @@
 
 template <typename Strategy>
 VisiblePositionTemplate<Strategy> VisiblePositionTemplate<Strategy>::AfterNode(
-    Node* node) {
+    const Node& node) {
   return Create(PositionWithAffinityTemplate<Strategy>(
-      PositionTemplate<Strategy>::AfterNode(*node)));
+      PositionTemplate<Strategy>::AfterNode(node)));
 }
 
 template <typename Strategy>
diff --git a/third_party/WebKit/Source/core/editing/VisiblePosition.h b/third_party/WebKit/Source/core/editing/VisiblePosition.h
index 33caac8..496ad6a4 100644
--- a/third_party/WebKit/Source/core/editing/VisiblePosition.h
+++ b/third_party/WebKit/Source/core/editing/VisiblePosition.h
@@ -104,7 +104,7 @@
   }
   TextAffinity Affinity() const { return position_with_affinity_.Affinity(); }
 
-  static VisiblePositionTemplate<Strategy> AfterNode(Node*);
+  static VisiblePositionTemplate<Strategy> AfterNode(const Node&);
   static VisiblePositionTemplate<Strategy> BeforeNode(Node*);
   static VisiblePositionTemplate<Strategy> FirstPositionInNode(Node*);
   static VisiblePositionTemplate<Strategy> InParentAfterNode(const Node&);
diff --git a/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp b/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp
index e064273..12c6052 100644
--- a/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp
@@ -433,7 +433,7 @@
     if (!(IsStartOfBlock(
               VisiblePosition::BeforeNode(node_after_upstream_start)) &&
           IsEndOfBlock(
-              VisiblePosition::AfterNode(node_after_upstream_start)))) {
+              VisiblePosition::AfterNode(*node_after_upstream_start)))) {
       starts_at_empty_line_ = true;
       ending_position_ = downstream_end_;
     }
diff --git a/third_party/WebKit/Source/core/editing/commands/IndentOutdentCommand.cpp b/third_party/WebKit/Source/core/editing/commands/IndentOutdentCommand.cpp
index 28f1a25..bfaba53 100644
--- a/third_party/WebKit/Source/core/editing/commands/IndentOutdentCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/IndentOutdentCommand.cpp
@@ -109,7 +109,7 @@
   const VisiblePosition& end_of_paragraph_to_move =
       should_keep_selected_list
           ? CreateVisiblePosition(end)
-          : VisiblePosition::AfterNode(selected_list_item->lastChild());
+          : VisiblePosition::AfterNode(*selected_list_item->lastChild());
 
   // The insertion of |newList| may change the computed style of other
   // elements, resulting in failure in visible canonicalization.
diff --git a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
index ad70811..c0178ca 100644
--- a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
+++ b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.cpp
@@ -113,12 +113,6 @@
   return GetFrame()->GetPage()->GetChromeClient().GetWebView();
 }
 
-WebDocument WebRemoteFrameImpl::GetDocument() const {
-  // TODO(dcheng): this should also ASSERT_NOT_REACHED, but a lot of
-  // code tries to access the document of a remote frame at the moment.
-  return WebDocument();
-}
-
 WebPerformance WebRemoteFrameImpl::Performance() const {
   NOTREACHED();
   return WebPerformance();
diff --git a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h
index a26058a..9551d92 100644
--- a/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h
+++ b/third_party/WebKit/Source/core/exported/WebRemoteFrameImpl.h
@@ -38,7 +38,6 @@
   bool HasVisibleContent() const override;
   WebRect VisibleContentRect() const override;
   WebView* View() const override;
-  WebDocument GetDocument() const override;
   WebPerformance Performance() const override;
   void StopLoading() override;
   void EnableViewSourceMode(bool enable) override;
diff --git a/third_party/WebKit/Source/core/html/HTMLFormControlElementTest.cpp b/third_party/WebKit/Source/core/html/HTMLFormControlElementTest.cpp
index 470da5e..11b697b0 100644
--- a/third_party/WebKit/Source/core/html/HTMLFormControlElementTest.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLFormControlElementTest.cpp
@@ -41,8 +41,6 @@
     return anchor_ == &anchor;
   }
 
-  void WillUnloadDocument(const Document&) override {}
-  void DocumentDetached(const Document&) override {}
   void WillBeDestroyed() override {}
   DEFINE_INLINE_VIRTUAL_TRACE() {
     visitor->Trace(anchor_);
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp b/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp
index 76d3554..6aa3ab9 100644
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp
@@ -191,6 +191,7 @@
     }
     FrameOwnerPropertiesChanged();
     UpdateContainerPolicy();
+    UseCounter::Count(GetDocument(), WebFeature::kFeaturePolicyAllowAttribute);
   } else {
     if (name == srcAttr)
       LogUpdateAttributeIfIsolatedWorldAndInDocument("iframe", params);
diff --git a/third_party/WebKit/Source/core/inspector/browser_protocol.json b/third_party/WebKit/Source/core/inspector/browser_protocol.json
index 2c398966..aeb5803 100644
--- a/third_party/WebKit/Source/core/inspector/browser_protocol.json
+++ b/third_party/WebKit/Source/core/inspector/browser_protocol.json
@@ -4593,9 +4593,19 @@
                     "websql",
                     "service_workers",
                     "cache_storage",
-                    "all"
+                    "all",
+                    "other"
                 ],
                 "description": "Enum of possible storage types."
+            },
+            {
+                "id": "UsageForType",
+                "type": "object",
+                "description": "Usage for a storage type.",
+                "properties": [
+                    { "name": "storageType", "$ref": "StorageType", "description": "Name of storage type." },
+                    { "name": "usage", "type": "number", "description": "Storage usage (bytes)." }
+                ]
             }
         ],
         "commands": [
@@ -4614,7 +4624,8 @@
                 ],
                 "returns": [
                     { "name": "usage", "type": "number", "description": "Storage usage (bytes)." },
-                    { "name": "quota", "type": "number", "description": "Storage quota (bytes)." }
+                    { "name": "quota", "type": "number", "description": "Storage quota (bytes)." },
+                    { "name": "usageBreakdown", "type": "array", "items": { "$ref": "UsageForType" }, "description": "Storage usage per type (bytes)." }
                 ],
                 "description": "Returns usage and quota in bytes."
             }
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.h b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.h
index 4c055f9..11753b74 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlockFlow.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBlockFlow.h
@@ -966,7 +966,8 @@
                                          LayoutUnit block_right_edge,
                                          LayoutUnit block_left_edge,
                                          LayoutUnit width,
-                                         const AtomicString&);
+                                         const AtomicString&,
+                                         bool found_box);
   void MarkLinesDirtyInBlockRange(LayoutUnit logical_top,
                                   LayoutUnit logical_bottom,
                                   RootInlineBox* highest = nullptr);
diff --git a/third_party/WebKit/Source/core/layout/LayoutBlockFlowLine.cpp b/third_party/WebKit/Source/core/layout/LayoutBlockFlowLine.cpp
index 2752f74..89d78a1 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBlockFlowLine.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBlockFlowLine.cpp
@@ -2468,14 +2468,14 @@
       LayoutUnit width(indent_text == kIndentText ? first_line_ellipsis_width
                                                   : ellipsis_width);
       LayoutUnit block_edge = ltr ? block_right_edge : block_left_edge;
+      bool found_box = false;
       if (curr->LineCanAccommodateEllipsis(ltr, block_edge, line_box_edge,
                                            width)) {
         LayoutUnit total_logical_width =
             curr->PlaceEllipsis(selected_ellipsis_str, ltr, block_left_edge,
                                 block_right_edge, width, LayoutUnit(), false);
-        LayoutUnit
-            logical_left;  // We are only interested in the delta from the
-        // base position.
+        // We are only interested in the delta from the base position.
+        LayoutUnit logical_left;
         LayoutUnit available_logical_width = block_right_edge - block_left_edge;
         UpdateLogicalWidthForAlignment(text_align, curr, 0, logical_left,
                                        total_logical_width,
@@ -2485,11 +2485,11 @@
         else
           curr->MoveInInlineDirection(
               logical_left - (available_logical_width - total_logical_width));
-      } else {
-        TryPlacingEllipsisOnAtomicInlines(curr, LogicalRightOffsetForContent(),
-                                          LogicalLeftOffsetForContent(), width,
-                                          selected_ellipsis_str);
+        found_box = true;
       }
+      TryPlacingEllipsisOnAtomicInlines(curr, LogicalRightOffsetForContent(),
+                                        LogicalLeftOffsetForContent(), width,
+                                        selected_ellipsis_str, found_box);
     }
     indent_text = kDoNotIndentText;
   }
@@ -2500,16 +2500,14 @@
     LayoutUnit block_right_edge,
     LayoutUnit block_left_edge,
     LayoutUnit ellipsis_width,
-    const AtomicString& selected_ellipsis_str) {
+    const AtomicString& selected_ellipsis_str,
+    bool found_box) {
   bool ltr = Style()->IsLeftToRightDirection();
   LayoutUnit logical_left_offset = block_left_edge;
 
   // Each atomic inline block (e.g. a <span>) inside a blockflow is managed by
-  // an
-  // InlineBox that allows us to access the lineboxes that live inside the
-  // atomic
-  // inline block.
-  bool found_box = false;
+  // an InlineBox that allows us to access the lineboxes that live inside the
+  // atomic inline block.
   for (InlineBox* box = ltr ? root->FirstChild() : root->LastChild(); box;
        box = ltr ? box->NextOnLine() : box->PrevOnLine()) {
     if (!box->GetLineLayoutItem().IsAtomicInlineLevel() ||
@@ -2519,6 +2517,11 @@
       continue;
     }
 
+    if (found_box) {
+      box->GetLineLayoutItem().SetIsTruncated(true);
+      continue;
+    }
+
     RootInlineBox* first_root_box =
         LineLayoutBlockFlow(box->GetLineLayoutItem()).FirstRootBox();
     if (!first_root_box)
@@ -2535,7 +2538,7 @@
             logical_left_offset + curr->LogicalLeft();
         LayoutUnit ellipsis_edge =
             curr_logical_left + curr->LogicalWidth() + ellipsis_width;
-        if (ellipsis_edge <= block_right_edge)
+        if (!found_box && ellipsis_edge <= block_right_edge)
           continue;
         curr->PlaceEllipsis(selected_ellipsis_str, ltr, block_left_edge,
                             block_right_edge, ellipsis_width,
@@ -2548,7 +2551,7 @@
            curr = curr->NextRootBox()) {
         LayoutUnit ellipsis_edge =
             box->LogicalLeft() + curr->LogicalLeft() - ellipsis_width;
-        if (ellipsis_edge >= block_left_edge)
+        if (!found_box && ellipsis_edge >= block_left_edge)
           continue;
         // Root boxes can vary in width so move our offset out to allow
         // comparison with the right hand edge of the block.
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index 1c73a70..80dab500 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -608,6 +608,11 @@
     bitfields_.SetLayoutNGInline(layout_ng_inline);
   }
 
+  bool IsTruncated() const { return bitfields_.IsTruncated(); }
+  void SetIsTruncated(bool is_truncated) {
+    bitfields_.SetIsTruncated(is_truncated);
+  }
+
   bool EverHadLayout() const { return bitfields_.EverHadLayout(); }
 
   bool ChildrenInline() const { return bitfields_.ChildrenInline(); }
@@ -2305,6 +2310,7 @@
           outline_may_be_affected_by_descendants_(false),
           previous_outline_may_be_affected_by_descendants_(false),
           layout_ng_inline_(false),
+          is_truncated_(false),
           positioned_state_(kIsStaticallyPositioned),
           selection_state_(static_cast<unsigned>(SelectionState::kNone)),
           background_obscuration_state_(kBackgroundObscurationStatusInvalid),
@@ -2506,9 +2512,11 @@
 
     ADD_BOOLEAN_BITFIELD(layout_ng_inline_, LayoutNGInline);
 
+    ADD_BOOLEAN_BITFIELD(is_truncated_, IsTruncated);
+
    protected:
     // Use protected to avoid warning about unused variable.
-    unsigned unused_bits_ : 3;
+    unsigned unused_bits_ : 2;
 
    private:
     // This is the cached 'position' value of this object
diff --git a/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp b/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
index 5c28245..fa43ddc9 100644
--- a/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
+++ b/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
@@ -46,7 +46,7 @@
     FloatClipRect geometry_mapper_rect((FloatRect(local_rect)));
     if (object.PaintProperties() || object.LocalBorderBoxProperties()) {
       geometry_mapper_rect.MoveBy(FloatPoint(object.PaintOffset()));
-      GeometryMapper::SourceToDestinationVisualRect(
+      GeometryMapper::LocalToAncestorVisualRect(
           *object.LocalBorderBoxProperties(), ancestor.ContentsProperties(),
           geometry_mapper_rect);
       geometry_mapper_rect.MoveBy(-FloatPoint(ancestor.PaintOffset()));
diff --git a/third_party/WebKit/Source/core/layout/api/LineLayoutItem.h b/third_party/WebKit/Source/core/layout/api/LineLayoutItem.h
index 3d1e2f03..1ed4d25 100644
--- a/third_party/WebKit/Source/core/layout/api/LineLayoutItem.h
+++ b/third_party/WebKit/Source/core/layout/api/LineLayoutItem.h
@@ -305,6 +305,10 @@
     ObjectPaintInvalidator(*layout_object_).SlowSetPaintingLayerNeedsRepaint();
   }
 
+  void SetIsTruncated(bool set_truncation) {
+    layout_object_->SetIsTruncated(set_truncation);
+  }
+
   struct LineLayoutItemHash {
     STATIC_ONLY(LineLayoutItemHash);
     static unsigned GetHash(const LineLayoutItem& key) {
diff --git a/third_party/WebKit/Source/core/mojo/BUILD.gn b/third_party/WebKit/Source/core/mojo/BUILD.gn
index 202d78e..2e06679a 100644
--- a/third_party/WebKit/Source/core/mojo/BUILD.gn
+++ b/third_party/WebKit/Source/core/mojo/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//mojo/public/tools/bindings/mojom.gni")
 import("//third_party/WebKit/Source/core/core.gni")
 
 blink_core_sources("mojo") {
@@ -23,3 +24,39 @@
     "//services/service_manager/public/cpp",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "tests/JsToCppTest.cpp",
+  ]
+
+  data = [
+    "tests/JsToCppTest.js",
+  ]
+
+  configs += [
+    "//third_party/WebKit/Source/core:blink_core_pch",
+    "//third_party/WebKit/Source:config",
+    "//third_party/WebKit/Source:inside_blink",
+  ]
+
+  deps = [
+    ":test_bindings_blink",
+    "//mojo/public/cpp/bindings",
+    "//testing/gtest",
+    "//third_party/WebKit/Source/core:core",
+    "//third_party/WebKit/Source/core:testing",
+  ]
+
+  data_deps = [
+    ":test_bindings_js_data_deps",
+    "//mojo/public/js:new_bindings",
+  ]
+}
+
+mojom("test_bindings") {
+  sources = [
+    "tests/JsToCpp.mojom",
+  ]
+}
diff --git a/mojo/edk/js/tests/js_to_cpp.mojom b/third_party/WebKit/Source/core/mojo/tests/JsToCpp.mojom
similarity index 100%
rename from mojo/edk/js/tests/js_to_cpp.mojom
rename to third_party/WebKit/Source/core/mojo/tests/JsToCpp.mojom
diff --git a/third_party/WebKit/Source/core/mojo/tests/JsToCppTest.cpp b/third_party/WebKit/Source/core/mojo/tests/JsToCppTest.cpp
new file mode 100644
index 0000000..08411ee3
--- /dev/null
+++ b/third_party/WebKit/Source/core/mojo/tests/JsToCppTest.cpp
@@ -0,0 +1,436 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "bindings/core/v8/ScriptController.h"
+#include "bindings/core/v8/ScriptSourceCode.h"
+#include "bindings/core/v8/V8BindingForCore.h"
+#include "bindings/core/v8/V8BindingForTesting.h"
+#include "bindings/core/v8/V8ScriptRunner.h"
+#include "core/frame/Settings.h"
+#include "core/mojo/MojoHandle.h"
+#include "core/page/Page.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/system/wait.h"
+#include "platform/testing/UnitTestHelpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/core/mojo/tests/JsToCpp.mojom-blink.h"
+
+namespace blink {
+namespace {
+
+// Global value updated by some checks to prevent compilers from optimizing
+// reads out of existence.
+uint32_t g_waste_accumulator = 0;
+
+// Negative numbers with different values in each byte, the last of
+// which can survive promotion to double and back.
+const int8_t kExpectedInt8Value = -65;
+const int16_t kExpectedInt16Value = -16961;
+const int32_t kExpectedInt32Value = -1145258561;
+const int64_t kExpectedInt64Value = -77263311946305LL;
+
+// Positive numbers with different values in each byte, the last of
+// which can survive promotion to double and back.
+const uint8_t kExpectedUInt8Value = 65;
+const uint16_t kExpectedUInt16Value = 16961;
+const uint32_t kExpectedUInt32Value = 1145258561;
+const uint64_t kExpectedUInt64Value = 77263311946305LL;
+
+// Double/float values, including special case constants.
+const double kExpectedDoubleVal = 3.14159265358979323846;
+const double kExpectedDoubleInf = std::numeric_limits<double>::infinity();
+const double kExpectedDoubleNan = std::numeric_limits<double>::quiet_NaN();
+const float kExpectedFloatVal = static_cast<float>(kExpectedDoubleVal);
+const float kExpectedFloatInf = std::numeric_limits<float>::infinity();
+const float kExpectedFloatNan = std::numeric_limits<float>::quiet_NaN();
+
+// NaN has the property that it is not equal to itself.
+#define EXPECT_NAN(x) EXPECT_NE(x, x)
+
+String MojoBindingsScriptPath() {
+  String filepath = testing::ExecutableDir();
+  filepath.append("/gen/mojo/public/js/mojo_bindings.js");
+  return filepath;
+}
+
+String TestBindingsScriptPath() {
+  String filepath = testing::ExecutableDir();
+  filepath.append(
+      "/gen/third_party/WebKit/Source/core/mojo/tests/JsToCpp.mojom.js");
+  return filepath;
+}
+
+String TestScriptPath() {
+  String filepath = testing::BlinkRootDir();
+  filepath.append("/Source/core/mojo/tests/JsToCppTest.js");
+  return filepath;
+}
+
+v8::Local<v8::Value> ExecuteScript(const String& script_path,
+                                   LocalFrame& frame) {
+  RefPtr<SharedBuffer> script_src = testing::ReadFromFile(script_path);
+  return frame.GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
+      ScriptSourceCode(String(script_src->Data(), script_src->size())));
+}
+
+void CheckDataPipe(mojo::DataPipeConsumerHandle data_pipe_handle) {
+  MojoResult result = Wait(data_pipe_handle, MOJO_HANDLE_SIGNAL_READABLE);
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+
+  const void* buffer = nullptr;
+  unsigned num_bytes = 0;
+  result = BeginReadDataRaw(data_pipe_handle, &buffer, &num_bytes,
+                            MOJO_READ_DATA_FLAG_NONE);
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(64u, num_bytes);
+  for (unsigned i = 0; i < num_bytes; ++i) {
+    EXPECT_EQ(i, static_cast<unsigned>(static_cast<const char*>(buffer)[i]));
+  }
+  EndReadDataRaw(data_pipe_handle, num_bytes);
+}
+
+void CheckMessagePipe(mojo::MessagePipeHandle message_pipe_handle) {
+  MojoResult result = Wait(message_pipe_handle, MOJO_HANDLE_SIGNAL_READABLE);
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+
+  std::vector<uint8_t> bytes;
+  std::vector<mojo::ScopedHandle> handles;
+  result = ReadMessageRaw(message_pipe_handle, &bytes, &handles, 0);
+  EXPECT_EQ(MOJO_RESULT_OK, result);
+  EXPECT_EQ(64u, bytes.size());
+  for (int i = 0; i < 64; ++i) {
+    EXPECT_EQ(255 - i, bytes[i]);
+  }
+}
+
+js_to_cpp::blink::EchoArgsPtr BuildSampleEchoArgs() {
+  auto args = js_to_cpp::blink::EchoArgs::New();
+  args->si64 = kExpectedInt64Value;
+  args->si32 = kExpectedInt32Value;
+  args->si16 = kExpectedInt16Value;
+  args->si8 = kExpectedInt8Value;
+  args->ui64 = kExpectedUInt64Value;
+  args->ui32 = kExpectedUInt32Value;
+  args->ui16 = kExpectedUInt16Value;
+  args->ui8 = kExpectedUInt8Value;
+  args->float_val = kExpectedFloatVal;
+  args->float_inf = kExpectedFloatInf;
+  args->float_nan = kExpectedFloatNan;
+  args->double_val = kExpectedDoubleVal;
+  args->double_inf = kExpectedDoubleInf;
+  args->double_nan = kExpectedDoubleNan;
+  args->name = "coming";
+  args->string_array.emplace(3);
+  (*args->string_array)[0] = "one";
+  (*args->string_array)[1] = "two";
+  (*args->string_array)[2] = "three";
+  return args;
+}
+
+void CheckSampleEchoArgs(const js_to_cpp::blink::EchoArgsPtr& arg) {
+  EXPECT_EQ(kExpectedInt64Value, arg->si64);
+  EXPECT_EQ(kExpectedInt32Value, arg->si32);
+  EXPECT_EQ(kExpectedInt16Value, arg->si16);
+  EXPECT_EQ(kExpectedInt8Value, arg->si8);
+  EXPECT_EQ(kExpectedUInt64Value, arg->ui64);
+  EXPECT_EQ(kExpectedUInt32Value, arg->ui32);
+  EXPECT_EQ(kExpectedUInt16Value, arg->ui16);
+  EXPECT_EQ(kExpectedUInt8Value, arg->ui8);
+  EXPECT_EQ(kExpectedFloatVal, arg->float_val);
+  EXPECT_EQ(kExpectedFloatInf, arg->float_inf);
+  EXPECT_NAN(arg->float_nan);
+  EXPECT_EQ(kExpectedDoubleVal, arg->double_val);
+  EXPECT_EQ(kExpectedDoubleInf, arg->double_inf);
+  EXPECT_NAN(arg->double_nan);
+  EXPECT_EQ(String("coming"), arg->name);
+  EXPECT_EQ(String("one"), (*arg->string_array)[0]);
+  EXPECT_EQ(String("two"), (*arg->string_array)[1]);
+  EXPECT_EQ(String("three"), (*arg->string_array)[2]);
+  CheckDataPipe(arg->data_handle.get());
+  CheckMessagePipe(arg->message_handle.get());
+}
+
+void CheckSampleEchoArgsList(const js_to_cpp::blink::EchoArgsListPtr& list) {
+  if (list.is_null())
+    return;
+  CheckSampleEchoArgs(list->item);
+  CheckSampleEchoArgsList(list->next);
+}
+
+// More forgiving checks are needed in the face of potentially corrupt
+// messages. The values don't matter so long as all accesses are within
+// bounds.
+void CheckCorruptedString(const String& arg) {
+  for (size_t i = 0; i < arg.length(); ++i)
+    g_waste_accumulator += arg[i];
+}
+
+void CheckCorruptedStringArray(const Optional<Vector<String>>& string_array) {
+  if (!string_array)
+    return;
+  for (size_t i = 0; i < string_array->size(); ++i)
+    CheckCorruptedString((*string_array)[i]);
+}
+
+void CheckCorruptedDataPipe(mojo::DataPipeConsumerHandle data_pipe_handle) {
+  unsigned char buffer[100];
+  uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+  MojoResult result = ReadDataRaw(data_pipe_handle, buffer, &buffer_size,
+                                  MOJO_READ_DATA_FLAG_NONE);
+  if (result != MOJO_RESULT_OK)
+    return;
+  for (uint32_t i = 0; i < buffer_size; ++i)
+    g_waste_accumulator += buffer[i];
+}
+
+void CheckCorruptedMessagePipe(mojo::MessagePipeHandle message_pipe_handle) {
+  std::vector<uint8_t> bytes;
+  std::vector<mojo::ScopedHandle> handles;
+  MojoResult result = ReadMessageRaw(message_pipe_handle, &bytes, &handles, 0);
+  if (result != MOJO_RESULT_OK)
+    return;
+  for (uint32_t i = 0; i < bytes.size(); ++i)
+    g_waste_accumulator += bytes[i];
+}
+
+void CheckCorruptedEchoArgs(const js_to_cpp::blink::EchoArgsPtr& arg) {
+  if (arg.is_null())
+    return;
+  CheckCorruptedString(arg->name);
+  CheckCorruptedStringArray(arg->string_array);
+  if (arg->data_handle.is_valid())
+    CheckCorruptedDataPipe(arg->data_handle.get());
+  if (arg->message_handle.is_valid())
+    CheckCorruptedMessagePipe(arg->message_handle.get());
+}
+
+void CheckCorruptedEchoArgsList(const js_to_cpp::blink::EchoArgsListPtr& list) {
+  if (list.is_null())
+    return;
+  CheckCorruptedEchoArgs(list->item);
+  CheckCorruptedEchoArgsList(list->next);
+}
+
+// Base Provider implementation class. It's expected that tests subclass and
+// override the appropriate Provider functions. When test is done quit the
+// run_loop().
+class CppSideConnection : public js_to_cpp::blink::CppSide {
+ public:
+  CppSideConnection() : mishandled_messages_(0), binding_(this) {}
+  ~CppSideConnection() override {}
+
+  void set_js_side(js_to_cpp::blink::JsSidePtr js_side) {
+    js_side_ = std::move(js_side);
+  }
+  js_to_cpp::blink::JsSide* js_side() { return js_side_.get(); }
+
+  void Bind(mojo::InterfaceRequest<js_to_cpp::blink::CppSide> request) {
+    binding_.Bind(std::move(request));
+    // Keep the pipe open even after validation errors.
+    binding_.EnableTestingMode();
+  }
+
+  // js_to_cpp::CppSide:
+  void StartTest() override { NOTREACHED(); }
+
+  void TestFinished() override { NOTREACHED(); }
+
+  void PingResponse() override { mishandled_messages_ += 1; }
+
+  void EchoResponse(js_to_cpp::blink::EchoArgsListPtr list) override {
+    mishandled_messages_ += 1;
+  }
+
+  void BitFlipResponse(
+      js_to_cpp::blink::EchoArgsListPtr list,
+      js_to_cpp::blink::ForTestingAssociatedPtrInfo not_used) override {
+    mishandled_messages_ += 1;
+  }
+
+  void BackPointerResponse(js_to_cpp::blink::EchoArgsListPtr list) override {
+    mishandled_messages_ += 1;
+  }
+
+ protected:
+  js_to_cpp::blink::JsSidePtr js_side_;
+  int mishandled_messages_;
+  mojo::Binding<js_to_cpp::blink::CppSide> binding_;
+};
+
+// Trivial test to verify a message sent from JS is received.
+class PingCppSideConnection : public CppSideConnection {
+ public:
+  PingCppSideConnection() : got_message_(false) {}
+  ~PingCppSideConnection() override {}
+
+  // js_to_cpp::CppSide:
+  void StartTest() override { js_side_->Ping(); }
+
+  void PingResponse() override {
+    got_message_ = true;
+    testing::ExitRunLoop();
+  }
+
+  bool DidSucceed() { return got_message_ && !mishandled_messages_; }
+
+ private:
+  bool got_message_;
+};
+
+// Test that parameters are passed with correct values.
+class EchoCppSideConnection : public CppSideConnection {
+ public:
+  EchoCppSideConnection() : message_count_(0), termination_seen_(false) {}
+  ~EchoCppSideConnection() override {}
+
+  // js_to_cpp::CppSide:
+  void StartTest() override {
+    js_side_->Echo(kExpectedMessageCount, BuildSampleEchoArgs());
+  }
+
+  void EchoResponse(js_to_cpp::blink::EchoArgsListPtr list) override {
+    message_count_ += 1;
+
+    const js_to_cpp::blink::EchoArgsPtr& special_arg = list->item;
+    EXPECT_EQ(-1, special_arg->si64);
+    EXPECT_EQ(-1, special_arg->si32);
+    EXPECT_EQ(-1, special_arg->si16);
+    EXPECT_EQ(-1, special_arg->si8);
+    EXPECT_EQ(String("going"), special_arg->name);
+    CheckDataPipe(special_arg->data_handle.get());
+    CheckMessagePipe(special_arg->message_handle.get());
+
+    CheckSampleEchoArgsList(list->next);
+  }
+
+  void TestFinished() override {
+    termination_seen_ = true;
+    testing::ExitRunLoop();
+  }
+
+  bool DidSucceed() {
+    return termination_seen_ && !mishandled_messages_ &&
+           message_count_ == kExpectedMessageCount;
+  }
+
+ private:
+  static const int kExpectedMessageCount = 10;
+  int message_count_;
+  bool termination_seen_;
+};
+
+// Test that corrupted messages don't wreak havoc.
+class BitFlipCppSideConnection : public CppSideConnection {
+ public:
+  BitFlipCppSideConnection() : termination_seen_(false) {}
+  ~BitFlipCppSideConnection() override {}
+
+  // js_to_cpp::CppSide:
+  void StartTest() override { js_side_->BitFlip(BuildSampleEchoArgs()); }
+
+  void BitFlipResponse(
+      js_to_cpp::blink::EchoArgsListPtr list,
+      js_to_cpp::blink::ForTestingAssociatedPtrInfo not_used) override {
+    CheckCorruptedEchoArgsList(list);
+  }
+
+  void TestFinished() override {
+    termination_seen_ = true;
+    testing::ExitRunLoop();
+  }
+
+  bool DidSucceed() { return termination_seen_; }
+
+ private:
+  bool termination_seen_;
+};
+
+// Test that severely random messages don't wreak havoc.
+class BackPointerCppSideConnection : public CppSideConnection {
+ public:
+  BackPointerCppSideConnection() : termination_seen_(false) {}
+  ~BackPointerCppSideConnection() override {}
+
+  // js_to_cpp::CppSide:
+  void StartTest() override { js_side_->BackPointer(BuildSampleEchoArgs()); }
+
+  void BackPointerResponse(js_to_cpp::blink::EchoArgsListPtr list) override {
+    CheckCorruptedEchoArgsList(list);
+  }
+
+  void TestFinished() override {
+    termination_seen_ = true;
+    testing::ExitRunLoop();
+  }
+
+  bool DidSucceed() { return termination_seen_; }
+
+ private:
+  bool termination_seen_;
+};
+
+class JsToCppTest : public ::testing::Test {
+ public:
+  void RunTest(CppSideConnection* cpp_side) {
+    js_to_cpp::blink::CppSidePtr cpp_side_ptr;
+    cpp_side->Bind(MakeRequest(&cpp_side_ptr));
+
+    js_to_cpp::blink::JsSidePtr js_side_ptr;
+    auto js_side_request = MakeRequest(&js_side_ptr);
+    js_side_ptr->SetCppSide(std::move(cpp_side_ptr));
+    cpp_side->set_js_side(std::move(js_side_ptr));
+
+    V8TestingScope scope;
+    scope.GetPage().GetSettings().SetScriptEnabled(true);
+    ExecuteScript(MojoBindingsScriptPath(), scope.GetFrame());
+    ExecuteScript(TestBindingsScriptPath(), scope.GetFrame());
+
+    v8::Local<v8::Value> start_fn =
+        ExecuteScript(TestScriptPath(), scope.GetFrame());
+    ASSERT_FALSE(start_fn.IsEmpty());
+    ASSERT_TRUE(start_fn->IsFunction());
+    v8::Local<v8::Object> global_proxy = scope.GetContext()->Global();
+    v8::Local<v8::Value> args[1] = {
+        ToV8(MojoHandle::Create(
+                 mojo::ScopedHandle::From(js_side_request.PassMessagePipe())),
+             global_proxy, scope.GetIsolate())};
+    V8ScriptRunner::CallFunction(
+        start_fn.As<v8::Function>(), scope.GetExecutionContext(), global_proxy,
+        WTF_ARRAY_LENGTH(args), args, scope.GetIsolate());
+    testing::EnterRunLoop();
+  }
+};
+
+TEST_F(JsToCppTest, Ping) {
+  PingCppSideConnection cpp_side_connection;
+  RunTest(&cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, Echo) {
+  EchoCppSideConnection cpp_side_connection;
+  RunTest(&cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, BitFlip) {
+  // These tests generate a lot of expected validation errors. Suppress logging.
+  mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
+
+  BitFlipCppSideConnection cpp_side_connection;
+  RunTest(&cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, BackPointer) {
+  // These tests generate a lot of expected validation errors. Suppress logging.
+  mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
+
+  BackPointerCppSideConnection cpp_side_connection;
+  RunTest(&cpp_side_connection);
+  EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.js b/third_party/WebKit/Source/core/mojo/tests/JsToCppTest.js
similarity index 74%
rename from mojo/edk/js/tests/js_to_cpp_tests.js
rename to third_party/WebKit/Source/core/mojo/tests/JsToCppTest.js
index 6ffce09..139687a 100644
--- a/mojo/edk/js/tests/js_to_cpp_tests.js
+++ b/third_party/WebKit/Source/core/mojo/tests/JsToCppTest.js
@@ -2,26 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-define('mojo/edk/js/tests/js_to_cpp_tests', [
-  'console',
-  'mojo/edk/js/tests/js_to_cpp.mojom',
-  'mojo/public/js/bindings',
-  'mojo/public/js/connector',
-  'mojo/public/js/core',
-], function (console, jsToCpp, bindings, connector, core) {
+(function () {
   var retainedJsSide;
-  var retainedJsSideStub;
   var sampleData;
   var sampleMessage;
   var BAD_VALUE = 13;
   var DATA_PIPE_PARAMS = {
-    flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
     elementNumBytes: 1,
     capacityNumBytes: 64
   };
 
   function JsSideConnection() {
-    this.binding = new bindings.Binding(jsToCpp.JsSide, this);
+    this.binding = new mojo.Binding(jsToCpp.JsSide, this);
   }
 
   JsSideConnection.prototype.setCppSide = function(cppSide) {
@@ -55,13 +47,13 @@
       arg.si8 = BAD_VALUE;
 
     for (i = 0; i < numIterations; ++i) {
-      dataPipe1 = core.createDataPipe(DATA_PIPE_PARAMS);
-      dataPipe2 = core.createDataPipe(DATA_PIPE_PARAMS);
-      messagePipe1 = core.createMessagePipe();
-      messagePipe2 = core.createMessagePipe();
+      dataPipe1 = Mojo.createDataPipe(DATA_PIPE_PARAMS);
+      dataPipe2 = Mojo.createDataPipe(DATA_PIPE_PARAMS);
+      messagePipe1 = Mojo.createMessagePipe();
+      messagePipe2 = Mojo.createMessagePipe();
 
-      arg.data_handle = dataPipe1.consumerHandle;
-      arg.message_handle = messagePipe1.handle1;
+      arg.dataHandle = dataPipe1.consumer;
+      arg.messageHandle = messagePipe1.handle1;
 
       specialArg = new jsToCpp.EchoArgs();
       specialArg.si64 = -1;
@@ -69,20 +61,19 @@
       specialArg.si16 = -1;
       specialArg.si8 = -1;
       specialArg.name = 'going';
-      specialArg.data_handle = dataPipe2.consumerHandle;
-      specialArg.message_handle = messagePipe2.handle1;
+      specialArg.dataHandle = dataPipe2.consumer;
+      specialArg.messageHandle = messagePipe2.handle1;
 
       writeDataPipe(dataPipe1, sampleData);
       writeDataPipe(dataPipe2, sampleData);
       writeMessagePipe(messagePipe1, sampleMessage);
       writeMessagePipe(messagePipe2, sampleMessage);
-
       this.cppSide_.echoResponse(createEchoArgsList(specialArg, arg));
 
-      core.close(dataPipe1.producerHandle);
-      core.close(dataPipe2.producerHandle);
-      core.close(messagePipe1.handle0);
-      core.close(messagePipe2.handle0);
+      dataPipe1.producer.close();
+      dataPipe2.producer.close();
+      messagePipe1.handle0.close();
+      messagePipe2.handle0.close();
     }
     this.cppSide_.testFinished();
   };
@@ -91,7 +82,7 @@
     var iteration = 0;
     var dataPipe;
     var messagePipe;
-    var proto = connector.Connector.prototype;
+    var proto = mojo.internal.Connector.prototype;
     var stopSignalled = false;
 
     proto.realAccept = proto.accept;
@@ -110,13 +101,13 @@
     };
 
     while (!stopSignalled) {
-      messagePipe = core.createMessagePipe();
+      messagePipe = Mojo.createMessagePipe();
       writeMessagePipe(messagePipe, sampleMessage);
-      arg.message_handle = messagePipe.handle1;
+      arg.messageHandle = messagePipe.handle1;
 
       this.cppSide_.bitFlipResponse(createEchoArgsList(arg), null);
 
-      core.close(messagePipe.handle0);
+      messagePipe.handle0.close();
       iteration += 1;
     }
 
@@ -129,7 +120,7 @@
     var iteration = 0;
     var dataPipe;
     var messagePipe;
-    var proto = connector.Connector.prototype;
+    var proto = mojo.internal.Connector.prototype;
     var stopSignalled = false;
 
     proto.realAccept = proto.accept;
@@ -146,13 +137,13 @@
     };
 
     while (!stopSignalled) {
-      messagePipe = core.createMessagePipe();
+      messagePipe = Mojo.createMessagePipe();
       writeMessagePipe(messagePipe, sampleMessage);
-      arg.message_handle = messagePipe.handle1;
+      arg.messageHandle = messagePipe.handle1;
 
       this.cppSide_.backPointerResponse(createEchoArgsList(arg));
 
-      core.close(messagePipe.handle0);
+      messagePipe.handle0.close();
       iteration += 1;
     }
 
@@ -162,10 +153,9 @@
   };
 
   function writeDataPipe(pipe, data) {
-    var writeResult = core.writeData(
-      pipe.producerHandle, data, core.WRITE_DATA_FLAG_ALL_OR_NONE);
+    var writeResult = pipe.producer.writeData(data);
 
-    if (writeResult.result != core.RESULT_OK) {
+    if (writeResult.result != Mojo.RESULT_OK) {
       console.log('ERROR: Data pipe write result was ' + writeResult.result);
       return false;
     }
@@ -177,8 +167,8 @@
   }
 
   function writeMessagePipe(pipe, arrayBuffer) {
-    var result = core.writeMessage(pipe.handle0, arrayBuffer, [], 0);
-    if (result != core.RESULT_OK) {
+    var result = pipe.handle0.writeMessage(arrayBuffer, []);
+    if (result != Mojo.RESULT_OK) {
       console.log('ERROR: Message pipe write result was ' + result);
       return false;
     }
@@ -212,4 +202,4 @@
     retainedJsSide = new JsSideConnection;
     retainedJsSide.binding.bind(jsSideRequestHandle);
   };
-});
+})();
diff --git a/third_party/WebKit/Source/core/mojo/tests/OWNERS b/third_party/WebKit/Source/core/mojo/tests/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/third_party/WebKit/Source/core/mojo/tests/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp
index 4a08302..38312cd3 100644
--- a/third_party/WebKit/Source/core/page/Page.cpp
+++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -253,16 +253,9 @@
   main_frame_ = main_frame;
 }
 
-void Page::WillUnloadDocument(const Document& document) {
-  if (validation_message_client_)
-    validation_message_client_->WillUnloadDocument(document);
-}
-
 void Page::DocumentDetached(Document* document) {
   pointer_lock_controller_->DocumentDetached(document);
   context_menu_controller_->DocumentDetached(document);
-  if (validation_message_client_)
-    validation_message_client_->DocumentDetached(*document);
   hosts_using_features_.DocumentDetached(*document);
 }
 
diff --git a/third_party/WebKit/Source/core/page/Page.h b/third_party/WebKit/Source/core/page/Page.h
index ccaea49b..6af63ad9 100644
--- a/third_party/WebKit/Source/core/page/Page.h
+++ b/third_party/WebKit/Source/core/page/Page.h
@@ -152,7 +152,6 @@
     return ToLocalFrame(main_frame_);
   }
 
-  void WillUnloadDocument(const Document&);
   void DocumentDetached(Document*);
 
   bool OpenedByDOM() const;
diff --git a/third_party/WebKit/Source/core/page/ValidationMessageClient.h b/third_party/WebKit/Source/core/page/ValidationMessageClient.h
index 396f4b5..667f927 100644
--- a/third_party/WebKit/Source/core/page/ValidationMessageClient.h
+++ b/third_party/WebKit/Source/core/page/ValidationMessageClient.h
@@ -32,7 +32,6 @@
 
 namespace blink {
 
-class Document;
 class Element;
 
 class ValidationMessageClient : public GarbageCollectedMixin {
@@ -56,9 +55,6 @@
   // is visible.
   virtual bool IsValidationMessageVisible(const Element& anchor) = 0;
 
-  virtual void WillUnloadDocument(const Document&) = 0;
-  virtual void DocumentDetached(const Document&) = 0;
-
   virtual void WillBeDestroyed() = 0;
 
   DEFINE_INLINE_VIRTUAL_TRACE() {}
diff --git a/third_party/WebKit/Source/core/page/ValidationMessageClientImpl.cpp b/third_party/WebKit/Source/core/page/ValidationMessageClientImpl.cpp
index f2e3c3b..575f9bf 100644
--- a/third_party/WebKit/Source/core/page/ValidationMessageClientImpl.cpp
+++ b/third_party/WebKit/Source/core/page/ValidationMessageClientImpl.cpp
@@ -115,16 +115,6 @@
   return current_anchor_ == &anchor;
 }
 
-void ValidationMessageClientImpl::WillUnloadDocument(const Document& document) {
-  if (current_anchor_ && current_anchor_->GetDocument() == document)
-    HideValidationMessage(*current_anchor_);
-}
-
-void ValidationMessageClientImpl::DocumentDetached(const Document& document) {
-  DCHECK(!current_anchor_ || current_anchor_->GetDocument() != document)
-      << "willUnloadDocument() should be called beforehand.";
-}
-
 void ValidationMessageClientImpl::CheckAnchorStatus(TimerBase*) {
   DCHECK(current_anchor_);
   if (MonotonicallyIncreasingTime() >= finish_time_ || !CurrentView()) {
diff --git a/third_party/WebKit/Source/core/page/ValidationMessageClientImpl.h b/third_party/WebKit/Source/core/page/ValidationMessageClientImpl.h
index 0816472..1b61b2c 100644
--- a/third_party/WebKit/Source/core/page/ValidationMessageClientImpl.h
+++ b/third_party/WebKit/Source/core/page/ValidationMessageClientImpl.h
@@ -63,8 +63,6 @@
                              TextDirection sub_message_dir) override;
   void HideValidationMessage(const Element& anchor) override;
   bool IsValidationMessageVisible(const Element& anchor) override;
-  void WillUnloadDocument(const Document&) override;
-  void DocumentDetached(const Document&) override;
   void WillBeDestroyed() override;
 
   // PopupOpeningObserver function
diff --git a/third_party/WebKit/Source/core/paint/BlockPainter.cpp b/third_party/WebKit/Source/core/paint/BlockPainter.cpp
index 2dddb643..dc91c069 100644
--- a/third_party/WebKit/Source/core/paint/BlockPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/BlockPainter.cpp
@@ -167,6 +167,9 @@
 DISABLE_CFI_PERF
 void BlockPainter::PaintObject(const PaintInfo& paint_info,
                                const LayoutPoint& paint_offset) {
+  if (layout_block_.IsTruncated())
+    return;
+
   const PaintPhase paint_phase = paint_info.phase;
 
   if (ShouldPaintSelfBlockBackground(paint_phase)) {
diff --git a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
index 0afa0588..e30d569 100644
--- a/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintInvalidator.cpp
@@ -126,7 +126,7 @@
           context.tree_builder_context_->current.clip, nullptr);
 
       FloatClipRect float_rect((FloatRect(rect)));
-      GeometryMapper::SourceToDestinationVisualRect(
+      GeometryMapper::LocalToAncestorVisualRect(
           current_tree_state, container_contents_properties, float_rect);
       result = LayoutRect(float_rect.Rect());
     }
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp b/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
index 3a703ca..238cf7c9d 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerClipper.cpp
@@ -458,12 +458,12 @@
   if (HasOverflowClip(layer_)) {
     FloatClipRect clip_rect((FloatRect(LocalVisualRect())));
     clip_rect.MoveBy(FloatPoint(layer_.GetLayoutObject().PaintOffset()));
-    GeometryMapper::SourceToDestinationVisualRect(
+    GeometryMapper::LocalToAncestorVisualRect(
         source_property_tree_state, destination_property_tree_state, clip_rect);
     output.SetRect(clip_rect);
   } else {
     const FloatClipRect& clipped_rect_in_root_layer_space =
-        GeometryMapper::SourceToDestinationClipRect(
+        GeometryMapper::LocalToAncestorClipRect(
             source_property_tree_state, destination_property_tree_state);
     output.SetRect(clipped_rect_in_root_layer_space);
   }
diff --git a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
index 4e0b191..f3ec46e9 100644
--- a/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilderTest.cpp
@@ -87,7 +87,7 @@
       source.MoveBy((source_object)->PaintOffset());                           \
       auto contents_properties = (ancestor)->ContentsProperties();             \
       FloatClipRect actual_float_rect((FloatRect(source)));                    \
-      GeometryMapper::SourceToDestinationVisualRect(                           \
+      GeometryMapper::LocalToAncestorVisualRect(                               \
           *(source_object)->LocalBorderBoxProperties(), contents_properties,   \
           actual_float_rect);                                                  \
       LayoutRect actual(actual_float_rect.Rect());                             \
diff --git a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
index 1e15ce2..b477830 100644
--- a/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
+++ b/third_party/WebKit/Source/core/paint/PrePaintTreeWalk.cpp
@@ -140,7 +140,7 @@
   PropertyTreeState local_state(context.transform, context.clip, effect);
 
   const auto& clip_rect =
-      GeometryMapper::SourceToDestinationClipRect(local_state, ancestor_state);
+      GeometryMapper::LocalToAncestorClipRect(local_state, ancestor_state);
   // HasRadius() is ignored because it doesn't affect descendants' visual rects.
   LayoutRect result(clip_rect.Rect());
   if (!clip_rect.IsInfinite())
diff --git a/third_party/WebKit/Source/core/paint/ReplacedPainter.cpp b/third_party/WebKit/Source/core/paint/ReplacedPainter.cpp
index dcd5dae..a6a01ee 100644
--- a/third_party/WebKit/Source/core/paint/ReplacedPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/ReplacedPainter.cpp
@@ -148,6 +148,9 @@
       !ShouldPaintSelfBlockBackground(paint_info.phase))
     return false;
 
+  if (layout_replaced_.IsTruncated())
+    return false;
+
   // If we're invisible or haven't received a layout yet, just bail.
   // But if it's an SVG root, there can be children, so we'll check visibility
   // later.
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
index 86e852c7..ca40e3ab 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -1709,7 +1709,10 @@
 void ComputedStyle::ClearMultiCol() {
   SetColumnGapInternal(InitialColumnGap());
   SetColumnWidthInternal(InitialColumnWidth());
-  SetColumnRuleInternal(InitialColumnRule());
+  SetColumnRuleStyle(InitialColumnRuleStyle());
+  SetColumnRuleWidthInternal(LayoutUnit(InitialColumnRuleWidth()));
+  SetColumnRuleColorInternal(InitialColumnRuleColor());
+  SetColumnRuleColorIsCurrentColor(InitialColumnRuleColorIsCurrentColor());
   SetVisitedLinkColumnRuleColorInternal(InitialVisitedLinkColumnRuleColor());
   SetColumnCountInternal(InitialColumnCount());
   SetColumnAutoCountInternal(InitialColumnAutoCount());
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h
index 7fc9bf9..5755a20b6 100644
--- a/third_party/WebKit/Source/core/style/ComputedStyle.h
+++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -350,36 +350,33 @@
   // backdrop-filter
   static const FilterOperations& InitialBackdropFilter();
   const FilterOperations& BackdropFilter() const {
-    return rare_non_inherited_data_->backdrop_filter_->operations_;
+    return BackdropFilterInternal()->operations_;
   }
   FilterOperations& MutableBackdropFilter() {
-    return rare_non_inherited_data_.Access()
-        ->backdrop_filter_.Access()
-        ->operations_;
+    return MutableBackdropFilterInternal().Access()->operations_;
   }
   bool HasBackdropFilter() const {
-    return !rare_non_inherited_data_->backdrop_filter_->operations_.Operations()
-                .IsEmpty();
+    return !BackdropFilterInternal()->operations_.Operations().IsEmpty();
   }
   void SetBackdropFilter(const FilterOperations& ops) {
-    SET_NESTED_VAR(rare_non_inherited_data_, backdrop_filter_, operations_,
-                   ops);
+    if (BackdropFilterInternal()->operations_ != ops)
+      MutableBackdropFilterInternal().Access()->operations_ = ops;
   }
 
   // filter (aka -webkit-filter)
   static const FilterOperations& InitialFilter();
   FilterOperations& MutableFilter() {
-    return rare_non_inherited_data_.Access()->filter_.Access()->operations_;
+    return MutableFilterInternal().Access()->operations_;
   }
   const FilterOperations& Filter() const {
-    return rare_non_inherited_data_->filter_->operations_;
+    return FilterInternal()->operations_;
   }
   bool HasFilter() const {
-    return !rare_non_inherited_data_->filter_->operations_.Operations()
-                .IsEmpty();
+    return !FilterInternal()->operations_.Operations().IsEmpty();
   }
-  void SetFilter(const FilterOperations& ops) {
-    SET_NESTED_VAR(rare_non_inherited_data_, filter_, operations_, ops);
+  void SetFilter(const FilterOperations& v) {
+    if (FilterInternal()->operations_ != v)
+      MutableFilterInternal().Access()->operations_ = v;
   }
 
   // Background properties.
@@ -483,8 +480,6 @@
     return BorderRightWidth() && (BorderRightStyle() != EBorderStyle::kNone);
   }
 
-  static EBorderStyle InitialColumnRuleStyle() { return EBorderStyle::kNone; }
-
   // Border color properties.
   // border-left-color
   void SetBorderLeftColor(const StyleColor& color) {
@@ -520,9 +515,7 @@
 
   // box-shadow (aka -webkit-box-shadow)
   static ShadowList* InitialBoxShadow() { return 0; }
-  ShadowList* BoxShadow() const {
-    return rare_non_inherited_data_->box_shadow_.Get();
-  }
+  ShadowList* BoxShadow() const { return BoxShadowInternal().Get(); }
   void SetBoxShadow(RefPtr<ShadowList>);
   bool BoxShadowDataEquivalent(const ComputedStyle& other) const {
     return DataEquivalent(BoxShadow(), other.BoxShadow());
@@ -582,32 +575,22 @@
 
   // column-rule-color (aka -webkit-column-rule-color)
   void SetColumnRuleColor(const StyleColor& c) {
-    SET_BORDERVALUE_COLOR(rare_non_inherited_data_.Access()->multi_col_data_,
-                          column_rule_, c);
-  }
-
-  // column-rule-style (aka -webkit-column-rule-style)
-  EBorderStyle ColumnRuleStyle() const {
-    return rare_non_inherited_data_->multi_col_data_->column_rule_.Style();
-  }
-  void SetColumnRuleStyle(EBorderStyle b) {
-    SET_NESTED_VAR(rare_non_inherited_data_, multi_col_data_,
-                   column_rule_.style_, static_cast<unsigned>(b));
+    if (!compareEqual(ColumnRuleColor(), c)) {
+      SetColumnRuleColorInternal(c.Resolve(Color()));
+      SetColumnRuleColorIsCurrentColor(c.IsCurrentColor());
+    }
   }
 
   // column-rule-width (aka -webkit-column-rule-width)
   static unsigned short InitialColumnRuleWidth() { return 3; }
   unsigned short ColumnRuleWidth() const {
-    const BorderValue& rule =
-        rare_non_inherited_data_->multi_col_data_->column_rule_;
-    if (rule.Style() == EBorderStyle::kNone ||
-        rule.Style() == EBorderStyle::kHidden)
+    if (ColumnRuleStyle() == EBorderStyle::kNone ||
+        ColumnRuleStyle() == EBorderStyle::kHidden)
       return 0;
-    return rule.Width();
+    return ColumnRuleWidthInternal().ToFloat();
   }
   void SetColumnRuleWidth(unsigned short w) {
-    SET_NESTED_BORDER_WIDTH(rare_non_inherited_data_, multi_col_data_,
-                            column_rule_, w);
+    SetColumnRuleWidthInternal(LayoutUnit(w));
   }
 
   // column-width (aka -webkit-column-width)
@@ -630,17 +613,11 @@
 
   // contain
   static Containment InitialContain() { return kContainsNone; }
-  Containment Contain() const {
-    return static_cast<Containment>(rare_non_inherited_data_->contain_);
-  }
-  void SetContain(Containment contain) {
-    SET_VAR(rare_non_inherited_data_, contain_, contain);
-  }
+  Containment Contain() const { return ContainInternal(); }
+  void SetContain(Containment contain) { SetContainInternal(contain); }
 
   // content
-  ContentData* GetContentData() const {
-    return rare_non_inherited_data_->content_.Get();
-  }
+  ContentData* GetContentData() const { return ContentInternal().Get(); }
   void SetContent(ContentData*);
 
   // -webkit-box-ordinal-group
@@ -657,12 +634,9 @@
 
   // -webkit-box-reflect
   static StyleReflection* InitialBoxReflect() { return 0; }
-  StyleReflection* BoxReflect() const {
-    return rare_non_inherited_data_->box_reflect_.Get();
-  }
+  StyleReflection* BoxReflect() const { return BoxReflectInternal().Get(); }
   void SetBoxReflect(RefPtr<StyleReflection> reflect) {
-    if (rare_non_inherited_data_->box_reflect_ != reflect)
-      rare_non_inherited_data_.Access()->box_reflect_ = std::move(reflect);
+    SetBoxReflectInternal(std::move(reflect));
   }
 
   // Grid properties.
@@ -673,9 +647,7 @@
 
   // grid-auto-flow
   static GridAutoFlow InitialGridAutoFlow() { return kAutoFlowRow; }
-  void SetGridAutoFlow(GridAutoFlow flow) {
-    SET_NESTED_VAR(rare_non_inherited_data_, grid_data_, grid_auto_flow_, flow);
-  }
+  void SetGridAutoFlow(GridAutoFlow flow) { SetGridAutoFlowInternal(flow); }
 
   // offset-path
   static BasicShape* InitialOffsetPath() { return nullptr; }
@@ -850,13 +822,8 @@
   // Scroll properties.
   // scroll-behavior
   static ScrollBehavior InitialScrollBehavior() { return kScrollBehaviorAuto; }
-  ScrollBehavior GetScrollBehavior() const {
-    return static_cast<ScrollBehavior>(
-        rare_non_inherited_data_->scroll_behavior_);
-  }
-  void SetScrollBehavior(ScrollBehavior b) {
-    SET_VAR(rare_non_inherited_data_, scroll_behavior_, b);
-  }
+  ScrollBehavior GetScrollBehavior() const { return ScrollBehaviorInternal(); }
+  void SetScrollBehavior(ScrollBehavior b) { SetScrollBehaviorInternal(b); }
 
   // scroll-padding-block-start
   const Length& ScrollPaddingBlockStart() const {
@@ -966,14 +933,8 @@
 
   // shape-outside (aka -webkit-shape-outside)
   static ShapeValue* InitialShapeOutside() { return 0; }
-  ShapeValue* ShapeOutside() const {
-    return rare_non_inherited_data_->shape_outside_.Get();
-  }
-  void SetShapeOutside(ShapeValue* value) {
-    if (rare_non_inherited_data_->shape_outside_ == value)
-      return;
-    rare_non_inherited_data_.Access()->shape_outside_ = value;
-  }
+  ShapeValue* ShapeOutside() const { return ShapeOutsideInternal().Get(); }
+  void SetShapeOutside(ShapeValue* value) { SetShapeOutsideInternal(value); }
   bool ShapeOutsideDataEquivalent(const ComputedStyle& other) const {
     return DataEquivalent(ShapeOutside(), other.ShapeOutside());
   }
@@ -1003,12 +964,8 @@
   static TouchAction InitialTouchAction() {
     return TouchAction::kTouchActionAuto;
   }
-  TouchAction GetTouchAction() const {
-    return static_cast<TouchAction>(rare_non_inherited_data_->touch_action_);
-  }
-  void SetTouchAction(TouchAction t) {
-    SET_VAR(rare_non_inherited_data_, touch_action_, t);
-  }
+  TouchAction GetTouchAction() const { return TouchActionInternal(); }
+  void SetTouchAction(TouchAction t) { return SetTouchActionInternal(t); }
 
   // vertical-align
   static EVerticalAlign InitialVerticalAlign() {
@@ -1045,21 +1002,14 @@
 
   // -webkit-appearance
   static ControlPart InitialAppearance() { return kNoControlPart; }
-  ControlPart Appearance() const {
-    return static_cast<ControlPart>(rare_non_inherited_data_->appearance_);
-  }
-  void SetAppearance(ControlPart a) {
-    SET_VAR(rare_non_inherited_data_, appearance_, a);
-  }
+  ControlPart Appearance() const { return AppearanceInternal(); }
+  void SetAppearance(ControlPart a) { SetAppearanceInternal(a); }
 
   // -webkit-clip-path
   static ClipPathOperation* InitialClipPath() { return 0; }
-  ClipPathOperation* ClipPath() const {
-    return rare_non_inherited_data_->clip_path_.Get();
-  }
+  ClipPathOperation* ClipPath() const { return ClipPathInternal().Get(); }
   void SetClipPath(RefPtr<ClipPathOperation> operation) {
-    if (rare_non_inherited_data_->clip_path_ != operation)
-      rare_non_inherited_data_.Access()->clip_path_ = std::move(operation);
+    SetClipPathInternal(std::move(operation));
   }
   bool ClipPathDataEquivalent(const ComputedStyle& other) const {
     return DataEquivalent(ClipPath(), other.ClipPath());
@@ -1068,27 +1018,27 @@
   // Mask properties.
   // -webkit-mask-box-image-outset
   const BorderImageLengthBox& MaskBoxImageOutset() const {
-    return rare_non_inherited_data_->mask_box_image_.Outset();
+    return MaskBoxImageInternal().Outset();
   }
   void SetMaskBoxImageOutset(const BorderImageLengthBox& outset) {
-    rare_non_inherited_data_.Access()->mask_box_image_.SetOutset(outset);
+    MutableMaskBoxImageInternal().SetOutset(outset);
   }
 
   // -webkit-mask-box-image-slice
   const LengthBox& MaskBoxImageSlices() const {
-    return rare_non_inherited_data_->mask_box_image_.ImageSlices();
+    return MaskBoxImageInternal().ImageSlices();
   }
   void SetMaskBoxImageSlices(const LengthBox& slices) {
-    rare_non_inherited_data_.Access()->mask_box_image_.SetImageSlices(slices);
+    MutableMaskBoxImageInternal().SetImageSlices(slices);
   }
 
   // -webkit-mask-box-image-source
   static StyleImage* InitialMaskBoxImageSource() { return 0; }
   StyleImage* MaskBoxImageSource() const {
-    return rare_non_inherited_data_->mask_box_image_.GetImage();
+    return MaskBoxImageInternal().GetImage();
   }
   void SetMaskBoxImageSource(StyleImage* v) {
-    rare_non_inherited_data_.Access()->mask_box_image_.SetImage(v);
+    MutableMaskBoxImageInternal().SetImage(v);
   }
 
   // -webkit-mask-box-image-width
@@ -1148,12 +1098,8 @@
 
   // -webkit-line-clamp
   static LineClampValue InitialLineClamp() { return LineClampValue(); }
-  const LineClampValue& LineClamp() const {
-    return rare_non_inherited_data_->line_clamp_;
-  }
-  void SetLineClamp(LineClampValue c) {
-    SET_VAR(rare_non_inherited_data_, line_clamp_, c);
-  }
+  const LineClampValue& LineClamp() const { return LineClampInternal(); }
+  void SetLineClamp(LineClampValue c) { SetLineClampInternal(c); }
 
   // -webkit-text-fill-color
   void SetTextFillColor(const StyleColor& color) {
@@ -1390,18 +1336,18 @@
   // Animations.
   CSSAnimationData& AccessAnimations();
   const CSSAnimationData* Animations() const {
-    return rare_non_inherited_data_->animations_.get();
+    return AnimationsInternal().get();
   }
 
   // Transitions.
   const CSSTransitionData* Transitions() const {
-    return rare_non_inherited_data_->transitions_.get();
+    return TransitionsInternal().get();
   }
   CSSTransitionData& AccessTransitions();
 
   // Callback selectors.
   const Vector<String>& CallbackSelectors() const {
-    return rare_non_inherited_data_->callback_selectors_;
+    return CallbackSelectorsInternal();
   }
   void AddCallbackSelector(const String& selector);
 
@@ -1423,8 +1369,8 @@
     return !HasAutoColumnCount() || !HasAutoColumnWidth();
   }
   bool ColumnRuleIsTransparent() const {
-    return rare_non_inherited_data_->multi_col_data_->column_rule_
-        .IsTransparent();
+    return !ColumnRuleColorIsCurrentColor() &&
+           !ColumnRuleColorInternal().Alpha();
   }
   bool ColumnRuleEquivalent(const ComputedStyle* other_style) const;
   void InheritColumnPropertiesFrom(const ComputedStyle& parent) {
@@ -1508,107 +1454,100 @@
 
   // align-content utility functions.
   ContentPosition AlignContentPosition() const {
-    return rare_non_inherited_data_->align_content_.GetPosition();
+    return AlignContent().GetPosition();
   }
   ContentDistributionType AlignContentDistribution() const {
-    return rare_non_inherited_data_->align_content_.Distribution();
+    return AlignContent().Distribution();
   }
   OverflowAlignment AlignContentOverflowAlignment() const {
-    return rare_non_inherited_data_->align_content_.Overflow();
+    return AlignContent().Overflow();
   }
   void SetAlignContentPosition(ContentPosition position) {
-    rare_non_inherited_data_.Access()->align_content_.SetPosition(position);
+    MutableAlignContentInternal().SetPosition(position);
   }
   void SetAlignContentDistribution(ContentDistributionType distribution) {
-    rare_non_inherited_data_.Access()->align_content_.SetDistribution(
-        distribution);
+    MutableAlignContentInternal().SetDistribution(distribution);
   }
   void SetAlignContentOverflow(OverflowAlignment overflow) {
-    rare_non_inherited_data_.Access()->align_content_.SetOverflow(overflow);
+    MutableAlignContentInternal().SetOverflow(overflow);
   }
 
   // justify-content utility functions.
   ContentPosition JustifyContentPosition() const {
-    return rare_non_inherited_data_->justify_content_.GetPosition();
+    return JustifyContent().GetPosition();
   }
   ContentDistributionType JustifyContentDistribution() const {
-    return rare_non_inherited_data_->justify_content_.Distribution();
+    return JustifyContent().Distribution();
   }
   OverflowAlignment JustifyContentOverflowAlignment() const {
-    return rare_non_inherited_data_->justify_content_.Overflow();
+    return JustifyContent().Overflow();
   }
   void SetJustifyContentPosition(ContentPosition position) {
-    rare_non_inherited_data_.Access()->justify_content_.SetPosition(position);
+    MutableJustifyContentInternal().SetPosition(position);
   }
   void SetJustifyContentDistribution(ContentDistributionType distribution) {
-    rare_non_inherited_data_.Access()->justify_content_.SetDistribution(
-        distribution);
+    MutableJustifyContentInternal().SetDistribution(distribution);
   }
   void SetJustifyContentOverflow(OverflowAlignment overflow) {
-    rare_non_inherited_data_.Access()->justify_content_.SetOverflow(overflow);
+    MutableJustifyContentInternal().SetOverflow(overflow);
   }
 
   // align-items utility functions.
-  ItemPosition AlignItemsPosition() const {
-    return rare_non_inherited_data_->align_items_.GetPosition();
-  }
+  ItemPosition AlignItemsPosition() const { return AlignItems().GetPosition(); }
   OverflowAlignment AlignItemsOverflowAlignment() const {
-    return rare_non_inherited_data_->align_items_.Overflow();
+    return AlignItems().Overflow();
   }
   void SetAlignItemsPosition(ItemPosition position) {
-    rare_non_inherited_data_.Access()->align_items_.SetPosition(position);
+    MutableAlignItemsInternal().SetPosition(position);
   }
   void SetAlignItemsOverflow(OverflowAlignment overflow) {
-    rare_non_inherited_data_.Access()->align_items_.SetOverflow(overflow);
+    MutableAlignItemsInternal().SetOverflow(overflow);
   }
 
   // justify-items utility functions.
   ItemPosition JustifyItemsPosition() const {
-    return rare_non_inherited_data_->justify_items_.GetPosition();
+    return JustifyItems().GetPosition();
   }
   OverflowAlignment JustifyItemsOverflowAlignment() const {
-    return rare_non_inherited_data_->justify_items_.Overflow();
+    return JustifyItems().Overflow();
   }
   ItemPositionType JustifyItemsPositionType() const {
-    return rare_non_inherited_data_->justify_items_.PositionType();
+    return JustifyItems().PositionType();
   }
   void SetJustifyItemsPosition(ItemPosition position) {
-    rare_non_inherited_data_.Access()->justify_items_.SetPosition(position);
+    MutableJustifyItemsInternal().SetPosition(position);
   }
   void SetJustifyItemsOverflow(OverflowAlignment overflow) {
-    rare_non_inherited_data_.Access()->justify_items_.SetOverflow(overflow);
+    MutableJustifyItemsInternal().SetOverflow(overflow);
   }
   void SetJustifyItemsPositionType(ItemPositionType position_type) {
-    rare_non_inherited_data_.Access()->justify_items_.SetPositionType(
-        position_type);
+    MutableJustifyItemsInternal().SetPositionType(position_type);
   }
 
   // align-self utility functions.
-  ItemPosition AlignSelfPosition() const {
-    return rare_non_inherited_data_->align_self_.GetPosition();
-  }
+  ItemPosition AlignSelfPosition() const { return AlignSelf().GetPosition(); }
   OverflowAlignment AlignSelfOverflowAlignment() const {
-    return rare_non_inherited_data_->align_self_.Overflow();
+    return AlignSelf().Overflow();
   }
   void SetAlignSelfPosition(ItemPosition position) {
-    rare_non_inherited_data_.Access()->align_self_.SetPosition(position);
+    MutableAlignSelfInternal().SetPosition(position);
   }
   void SetAlignSelfOverflow(OverflowAlignment overflow) {
-    rare_non_inherited_data_.Access()->align_self_.SetOverflow(overflow);
+    MutableAlignSelfInternal().SetOverflow(overflow);
   }
 
   // justify-self utility functions.
   ItemPosition JustifySelfPosition() const {
-    return rare_non_inherited_data_->justify_self_.GetPosition();
+    return JustifySelf().GetPosition();
   }
   OverflowAlignment JustifySelfOverflowAlignment() const {
-    return rare_non_inherited_data_->justify_self_.Overflow();
+    return JustifySelf().Overflow();
   }
   void SetJustifySelfPosition(ItemPosition position) {
-    rare_non_inherited_data_.Access()->justify_self_.SetPosition(position);
+    MutableJustifySelfInternal().SetPosition(position);
   }
   void SetJustifySelfOverflow(OverflowAlignment overflow) {
-    rare_non_inherited_data_.Access()->justify_self_.SetOverflow(overflow);
+    MutableJustifySelfInternal().SetOverflow(overflow);
   }
 
   // Writing mode utility functions.
@@ -2047,9 +1986,7 @@
   }
 
   // Perspective utility functions.
-  bool HasPerspective() const {
-    return rare_non_inherited_data_->perspective_ > 0;
-  }
+  bool HasPerspective() const { return Perspective() > 0; }
 
   // Outline utility functions.
   bool HasOutline() const {
@@ -2125,18 +2062,10 @@
   }
 
   // Contain utility functions.
-  bool ContainsPaint() const {
-    return rare_non_inherited_data_->contain_ & kContainsPaint;
-  }
-  bool ContainsStyle() const {
-    return rare_non_inherited_data_->contain_ & kContainsStyle;
-  }
-  bool ContainsLayout() const {
-    return rare_non_inherited_data_->contain_ & kContainsLayout;
-  }
-  bool ContainsSize() const {
-    return rare_non_inherited_data_->contain_ & kContainsSize;
-  }
+  bool ContainsPaint() const { return Contain() & kContainsPaint; }
+  bool ContainsStyle() const { return Contain() & kContainsStyle; }
+  bool ContainsLayout() const { return Contain() & kContainsLayout; }
+  bool ContainsSize() const { return Contain() & kContainsSize; }
 
   // Display utility functions.
   bool IsDisplayReplacedType() const {
@@ -2480,29 +2409,28 @@
 
  private:
   void SetVisitedLinkBackgroundColor(const StyleColor& v) {
-    SET_VAR(rare_non_inherited_data_, visited_link_background_color_, v);
+    SetVisitedLinkBackgroundColorInternal(v);
   }
   void SetVisitedLinkBorderLeftColor(const StyleColor& v) {
-    SET_VAR(rare_non_inherited_data_, visited_link_border_left_color_, v);
+    SetVisitedLinkBorderLeftColorInternal(v);
   }
   void SetVisitedLinkBorderRightColor(const StyleColor& v) {
-    SET_VAR(rare_non_inherited_data_, visited_link_border_right_color_, v);
+    SetVisitedLinkBorderRightColorInternal(v);
   }
   void SetVisitedLinkBorderBottomColor(const StyleColor& v) {
-    SET_VAR(rare_non_inherited_data_, visited_link_border_bottom_color_, v);
+    SetVisitedLinkBorderBottomColorInternal(v);
   }
   void SetVisitedLinkBorderTopColor(const StyleColor& v) {
-    SET_VAR(rare_non_inherited_data_, visited_link_border_top_color_, v);
+    SetVisitedLinkBorderTopColorInternal(v);
   }
   void SetVisitedLinkOutlineColor(const StyleColor& v) {
-    SET_VAR(rare_non_inherited_data_, visited_link_outline_color_, v);
+    SetVisitedLinkOutlineColorInternal(v);
   }
   void SetVisitedLinkColumnRuleColor(const StyleColor& v) {
-    SET_NESTED_VAR(rare_non_inherited_data_, multi_col_data_,
-                   visited_link_column_rule_color_, v);
+    SetVisitedLinkColumnRuleColorInternal(v);
   }
   void SetVisitedLinkTextDecorationColor(const StyleColor& v) {
-    SET_VAR(rare_non_inherited_data_, visited_link_text_decoration_color_, v);
+    SetVisitedLinkTextDecorationColorInternal(v);
   }
   void SetVisitedLinkTextEmphasisColor(const StyleColor& color) {
     SetVisitedLinkTextEmphasisColorInternal(color.Resolve(Color()));
@@ -2595,7 +2523,9 @@
   }
   Color GetColor() const;
   StyleColor ColumnRuleColor() const {
-    return rare_non_inherited_data_->multi_col_data_->column_rule_.GetColor();
+    return ColumnRuleColorIsCurrentColor()
+               ? StyleColor::CurrentColor()
+               : StyleColor(ColumnRuleColorInternal());
   }
   StyleColor OutlineColor() const {
     return OutlineColorIsCurrentColor() ? StyleColor::CurrentColor()
@@ -2624,10 +2554,10 @@
     return StyleAutoColor(VisitedLinkCaretColorInternal());
   }
   StyleColor VisitedLinkBackgroundColor() const {
-    return rare_non_inherited_data_->visited_link_background_color_;
+    return VisitedLinkBackgroundColorInternal();
   }
   StyleColor VisitedLinkBorderLeftColor() const {
-    return rare_non_inherited_data_->visited_link_border_left_color_;
+    return VisitedLinkBorderLeftColorInternal();
   }
   bool VisitedLinkBorderLeftColorHasNotChanged(
       const ComputedStyle& other) const {
@@ -2636,7 +2566,7 @@
             !BorderLeftWidth());
   }
   StyleColor VisitedLinkBorderRightColor() const {
-    return rare_non_inherited_data_->visited_link_border_right_color_;
+    return VisitedLinkBorderRightColorInternal();
   }
   bool VisitedLinkBorderRightColorHasNotChanged(
       const ComputedStyle& other) const {
@@ -2645,7 +2575,7 @@
             !BorderRightWidth());
   }
   StyleColor VisitedLinkBorderBottomColor() const {
-    return rare_non_inherited_data_->visited_link_border_bottom_color_;
+    return VisitedLinkBorderBottomColorInternal();
   }
   bool VisitedLinkBorderBottomColorHasNotChanged(
       const ComputedStyle& other) const {
@@ -2654,7 +2584,7 @@
             !BorderBottomWidth());
   }
   StyleColor VisitedLinkBorderTopColor() const {
-    return rare_non_inherited_data_->visited_link_border_top_color_;
+    return VisitedLinkBorderTopColorInternal();
   }
   bool VisitedLinkBorderTopColorHasNotChanged(
       const ComputedStyle& other) const {
@@ -2662,18 +2592,17 @@
             !BorderTopWidth());
   }
   StyleColor VisitedLinkOutlineColor() const {
-    return rare_non_inherited_data_->visited_link_outline_color_;
+    return VisitedLinkOutlineColorInternal();
   }
   bool VisitedLinkOutlineColorHasNotChanged(const ComputedStyle& other) const {
     return (VisitedLinkOutlineColor() == other.VisitedLinkOutlineColor() ||
             !OutlineWidth());
   }
   StyleColor VisitedLinkColumnRuleColor() const {
-    return rare_non_inherited_data_->multi_col_data_
-        ->visited_link_column_rule_color_;
+    return VisitedLinkColumnRuleColorInternal();
   }
   StyleColor VisitedLinkTextDecorationColor() const {
-    return rare_non_inherited_data_->visited_link_text_decoration_color_;
+    return VisitedLinkTextDecorationColorInternal();
   }
   StyleColor VisitedLinkTextEmphasisColor() const {
     return VisitedLinkTextEmphasisColorIsCurrentColorInternal()
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/ClearStorageView.js b/third_party/WebKit/Source/devtools/front_end/resources/ClearStorageView.js
index 4c07cfc1..b91730f1 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/ClearStorageView.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/ClearStorageView.js
@@ -7,6 +7,15 @@
 Resources.ClearStorageView = class extends UI.ThrottledWidget {
   constructor() {
     super(true, 1000);
+    this._pieColors = [
+      'rgba(110, 161, 226, 1)',  // blue
+      'rgba(229, 113, 113, 1)',  // red
+      'rgba(239, 196, 87, 1)',   // yellow
+      'rgba(155, 127, 230, 1)',  // purple
+      'rgba(116, 178, 102, 1)',  // green
+      'rgba(255, 167, 36, 1)',   // orange
+      'rgba(203, 220, 56, 1)',   // lime
+    ];
 
     this._reportView = new UI.ReportView(Common.UIString('Clear storage'));
     this._reportView.registerRequiredCSS('resources/clearStorageView.css');
@@ -27,13 +36,18 @@
 
     var quota = this._reportView.appendSection(Common.UIString('Usage'));
     this._quotaRow = quota.appendRow();
+    this._pieChart = new PerfUI.PieChart(110, Number.bytesToString, true);
+    this._pieChartLegend = createElementWithClass('div', 'legend');
+    var usageBreakdownRow = quota.appendRow();
+    usageBreakdownRow.appendChild(this._pieChart.element);
+    usageBreakdownRow.appendChild(this._pieChartLegend);
 
     var application = this._reportView.appendSection(Common.UIString('Application'));
     this._appendItem(application, Common.UIString('Unregister service workers'), 'service_workers');
 
     var storage = this._reportView.appendSection(Common.UIString('Storage'));
     this._appendItem(storage, Common.UIString('Local and session storage'), 'local_storage');
-    this._appendItem(storage, Common.UIString('Indexed DB'), 'indexeddb');
+    this._appendItem(storage, Common.UIString('IndexedDB'), 'indexeddb');
     this._appendItem(storage, Common.UIString('Web SQL'), 'websql');
     this._appendItem(storage, Common.UIString('Cookies'), 'cookies');
 
@@ -176,19 +190,82 @@
     var response = await this._target.storageAgent().invoke_getUsageAndQuota({origin: securityOrigin});
     if (response[Protocol.Error]) {
       this._quotaRow.textContent = '';
+      this._resetPieChart(0);
       return;
     }
     this._quotaRow.textContent = Common.UIString(
-        '%s storage quota used out of %s', Number.bytesToString(response.usage), Number.bytesToString(response.quota));
+        '%s used out of %s storage quota', Number.bytesToString(response.usage), Number.bytesToString(response.quota));
 
-    this._usageUpdatedForTest(response.usage, response.quota);
+    this._resetPieChart(response.usage);
+    var colorIndex = 0;
+    for (var usageForType of response.usageBreakdown) {
+      if (!usageForType.usage)
+        continue;
+      if (colorIndex === this._pieColors.length)
+        colorIndex = 0;
+      this._appendLegendRow(
+          this._getStorageTypeName(usageForType.storageType), usageForType.usage, this._pieColors[colorIndex++]);
+    }
+
+    this._usageUpdatedForTest(response.usage, response.quota, response.usageBreakdown);
     this.update();
   }
 
+  _formatPieChartBytes(value) {
+    return Number.bytesToString(value);
+  }
+
+  /**
+   * @param {number} total
+   */
+  _resetPieChart(total) {
+    this._pieChart.setTotal(total);
+    this._pieChartLegend.removeChildren();
+  }
+
+  /**
+   * @param {string} title
+   * @param {number} value
+   * @param {string} color
+   */
+  _appendLegendRow(title, value, color) {
+    if (!value)
+      return;
+    this._pieChart.addSlice(value, color);
+    var rowElement = this._pieChartLegend.createChild('div');
+    rowElement.createChild('span', 'usage-breakdown-legend-value').textContent = Number.bytesToString(value);
+    rowElement.createChild('span', 'usage-breakdown-legend-swatch').style.backgroundColor = color;
+    rowElement.createChild('span', 'usage-breakdown-legend-title').textContent = title;
+  }
+
+  /**
+   * @param {string} type
+   * @return {string}
+   */
+  _getStorageTypeName(type) {
+    switch (type) {
+      case Protocol.Storage.StorageType.File_systems:
+        return Common.UIString('File System');
+      case Protocol.Storage.StorageType.Websql:
+        return Common.UIString('Web SQL');
+      case Protocol.Storage.StorageType.Appcache:
+        return Common.UIString('Application Cache');
+      case Protocol.Storage.StorageType.Indexeddb:
+        return Common.UIString('IndexedDB');
+      case Protocol.Storage.StorageType.Cache_storage:
+        return Common.UIString('Cache Storage');
+      case Protocol.Storage.StorageType.Service_workers:
+        return Common.UIString('Service Workers');
+      default:
+        return Common.UIString('Other');
+    }
+  }
+
   /**
    * @param {number} usage
    * @param {number} quota
+   * @param {!Array<!Protocol.Storage.UsageForType>} usageBreakdown
    */
-  _usageUpdatedForTest(usage, quota) {
+  _usageUpdatedForTest(usage, quota, usageBreakdown) {
   }
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/clearStorageView.css b/third_party/WebKit/Source/devtools/front_end/resources/clearStorageView.css
index 41bf839a..a2ba5f4 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/clearStorageView.css
+++ b/third_party/WebKit/Source/devtools/front_end/resources/clearStorageView.css
@@ -22,3 +22,23 @@
 .report-row:hover .link {
     display: inline;
 }
+
+.usage-breakdown-legend-title {
+    display: inline-block;
+}
+
+.usage-breakdown-legend-value {
+    display: inline-block;
+    width: 70px;
+    text-align: right;
+}
+
+.usage-breakdown-legend-swatch {
+    display: inline-block;
+    width: 11px;
+    height: 11px;
+    margin: 0 6px;
+    position: relative;
+    top: 1px;
+    border: 1px solid rgba(0, 0, 0, 0.2);
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/module.json b/third_party/WebKit/Source/devtools/front_end/resources/module.json
index 484ce12..f1ce7dfa 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/resources/module.json
@@ -23,6 +23,7 @@
         "data_grid",
         "components",
         "object_ui",
+        "perf_ui",
         "mobile_throttling"
     ],
     "scripts": [
diff --git a/third_party/WebKit/Source/modules/exported/WebAXObject.cpp b/third_party/WebKit/Source/modules/exported/WebAXObject.cpp
index 4263073..591c8f3 100644
--- a/third_party/WebKit/Source/modules/exported/WebAXObject.cpp
+++ b/third_party/WebKit/Source/modules/exported/WebAXObject.cpp
@@ -1559,16 +1559,6 @@
 }
 
 // static
-WebAXObject WebAXObject::FromWebView(WebView& web_view) {
-  auto main_frame = web_view.MainFrame();
-  if (!main_frame)
-    return WebAXObject();
-
-  Document* document = main_frame->GetDocument();
-  return WebAXObject(ToAXObjectCacheImpl(document->AxObjectCache())->Root());
-}
-
-// static
 WebAXObject WebAXObject::FromWebDocument(const WebDocument& web_document) {
   const Document* document = web_document.ConstUnwrap<Document>();
   AXObjectCacheImpl* cache = ToAXObjectCacheImpl(document->AxObjectCache());
diff --git a/third_party/WebKit/Source/platform/blob/BlobData.cpp b/third_party/WebKit/Source/platform/blob/BlobData.cpp
index 085285b..190479b 100644
--- a/third_party/WebKit/Source/platform/blob/BlobData.cpp
+++ b/third_party/WebKit/Source/platform/blob/BlobData.cpp
@@ -44,6 +44,17 @@
 #include "public/platform/InterfaceProvider.h"
 #include "public/platform/Platform.h"
 
+using storage::mojom::blink::BlobPtr;
+using storage::mojom::blink::BlobRegistryPtr;
+using storage::mojom::blink::BytesProviderPtr;
+using storage::mojom::blink::DataElement;
+using storage::mojom::blink::DataElementBlob;
+using storage::mojom::blink::DataElementPtr;
+using storage::mojom::blink::DataElementBytes;
+using storage::mojom::blink::DataElementBytesPtr;
+using storage::mojom::blink::DataElementFile;
+using storage::mojom::blink::DataElementFilesystemURL;
+
 namespace blink {
 
 namespace {
@@ -249,7 +260,7 @@
     // TODO(mek): Going through InterfaceProvider to get a BlobRegistryPtr
     // ends up going through the main thread. Ideally workers wouldn't need
     // to do that.
-    storage::mojom::blink::BlobRegistryPtr registry;
+    BlobRegistryPtr registry;
     Platform::Current()->GetInterfaceProvider()->GetInterface(
         MakeRequest(&registry));
     registry->Register(MakeRequest(&blob_), uuid_, "", "", {});
@@ -267,12 +278,90 @@
     // TODO(mek): Going through InterfaceProvider to get a BlobRegistryPtr
     // ends up going through the main thread. Ideally workers wouldn't need
     // to do that.
-    storage::mojom::blink::BlobRegistryPtr registry;
+    BlobRegistryPtr registry;
     Platform::Current()->GetInterfaceProvider()->GetInterface(
         MakeRequest(&registry));
-    // TODO(mek): Pass elements from |data| to Register.
+
+    size_t current_memory_population = 0;
+    Vector<DataElementPtr> elements;
+    const DataElementPtr null_element = nullptr;
+
+    // TODO(mek): When the mojo code path is the default BlobData should
+    // directly create mojom::DataElements rather than BlobDataItems,
+    // eliminating the need for this loop.
+    for (const auto& item : data->Items()) {
+      // Skip zero-byte elements, as they don't matter for the contents of
+      // the blob.
+      if (item.length == 0)
+        continue;
+      switch (item.type) {
+        case BlobDataItem::kData: {
+          // kData elements don't set item.length, so separately check for zero
+          // byte kData elements.
+          if (item.data->length() == 0)
+            continue;
+          // Since blobs are often constructed with arrays with single bytes,
+          // consolidate all adjacent memory blob items into one. This should
+          // massively reduce the overhead of describing all these byte
+          // elements.
+          const DataElementPtr& last_element =
+              elements.IsEmpty() ? null_element : elements.back();
+          bool should_embed_bytes =
+              current_memory_population + item.data->length() <=
+              DataElementBytes::kMaximumEmbeddedDataSize;
+          bool last_element_is_bytes = last_element && last_element->is_bytes();
+          if (last_element_is_bytes) {
+            // Append bytes to previous element.
+            const auto& bytes_element = last_element->get_bytes();
+            bytes_element->length += item.data->length();
+            if (should_embed_bytes && bytes_element->embedded_data) {
+              bytes_element->embedded_data->Append(item.data->data(),
+                                                   item.data->length());
+              current_memory_population += item.data->length();
+            } else if (bytes_element->embedded_data) {
+              current_memory_population -= bytes_element->embedded_data->size();
+              bytes_element->embedded_data = WTF::nullopt;
+            }
+            // TODO(mek): Append data to previous element's BytesProvider.
+          } else {
+            BytesProviderPtr bytes_provider;
+            // TODO(mek): Bind bytes provider to something.
+            MakeRequest(&bytes_provider);
+            DataElementBytesPtr bytes_element = DataElementBytes::New(
+                item.data->length(), WTF::nullopt, std::move(bytes_provider));
+            if (should_embed_bytes) {
+              bytes_element->embedded_data = Vector<uint8_t>();
+              bytes_element->embedded_data->Append(item.data->data(),
+                                                   item.data->length());
+              current_memory_population += item.data->length();
+            }
+            elements.push_back(DataElement::NewBytes(std::move(bytes_element)));
+          }
+          break;
+        }
+        case BlobDataItem::kFile:
+          elements.push_back(DataElement::NewFile(DataElementFile::New(
+              item.path.IsNull() ? "" : item.path, item.offset, item.length,
+              WTF::Time::FromDoubleT(item.expected_modification_time))));
+          break;
+        case BlobDataItem::kFileSystemURL:
+          elements.push_back(
+              DataElement::NewFileFilesystem(DataElementFilesystemURL::New(
+                  item.file_system_url, item.offset, item.length,
+                  WTF::Time::FromDoubleT(item.expected_modification_time))));
+          break;
+        case BlobDataItem::kBlob: {
+          BlobPtr blob_clone;
+          item.blob_data_handle->blob_->Clone(MakeRequest(&blob_clone));
+          elements.push_back(DataElement::NewBlob(DataElementBlob::New(
+              std::move(blob_clone), item.offset, item.length)));
+          break;
+        }
+      }
+    }
+
     registry->Register(MakeRequest(&blob_), uuid_, type_.IsNull() ? "" : type_,
-                       "", {});
+                       "", std::move(elements));
   } else {
     BlobRegistry::RegisterBlobData(uuid_, std::move(data));
   }
diff --git a/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp b/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp
index 90c471ab..834c8b34 100644
--- a/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp
+++ b/third_party/WebKit/Source/platform/blob/BlobDataTest.cpp
@@ -8,6 +8,7 @@
 #include <utility>
 #include "base/test/scoped_task_environment.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
 #include "platform/UUID.h"
 #include "platform/testing/RuntimeEnabledFeaturesTestHelpers.h"
 #include "platform/testing/TestingPlatformSupport.h"
@@ -16,10 +17,24 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using storage::mojom::blink::Blob;
+using storage::mojom::blink::BlobRegistry;
+using storage::mojom::blink::BlobRegistryRequest;
+using storage::mojom::blink::BlobRequest;
+using storage::mojom::blink::DataElement;
+using storage::mojom::blink::DataElementBlob;
+using storage::mojom::blink::DataElementBytes;
+using storage::mojom::blink::DataElementFile;
+using storage::mojom::blink::DataElementFilesystemURL;
+using storage::mojom::blink::DataElementPtr;
+
 namespace blink {
 
+namespace {
+const size_t kMaxConsolidatedItemSizeInBytes = 15 * 1024;
+}
+
 TEST(BlobDataTest, Consolidation) {
-  const size_t kMaxConsolidatedItemSizeInBytes = 15 * 1024;
   BlobData data(BlobData::FileCompositionStatus::NO_UNKNOWN_SIZE_FILES);
   const char* text1 = "abc";
   const char* text2 = "def";
@@ -43,36 +58,51 @@
 
 namespace {
 
-class MockBlobRegistry : public storage::mojom::blink::BlobRegistry {
+class MockBlob : public Blob {
  public:
-  void Register(storage::mojom::blink::BlobRequest blob,
+  explicit MockBlob(const String& uuid) : uuid_(uuid) {}
+
+  void Clone(BlobRequest request) override {
+    mojo::MakeStrongBinding(WTF::MakeUnique<MockBlob>(uuid_),
+                            std::move(request));
+  }
+
+  void GetInternalUUID(GetInternalUUIDCallback callback) override {
+    std::move(callback).Run(uuid_);
+  }
+
+ private:
+  String uuid_;
+};
+
+class MockBlobRegistry : public BlobRegistry {
+ public:
+  void Register(BlobRequest blob,
                 const String& uuid,
                 const String& content_type,
                 const String& content_disposition,
-                Vector<storage::mojom::blink::DataElementPtr> elements,
+                Vector<DataElementPtr> elements,
                 RegisterCallback callback) override {
-    registrations.push_back(Registration{std::move(blob), uuid, content_type,
-                                         content_disposition,
-                                         std::move(elements)});
+    registrations.push_back(Registration{
+        uuid, content_type, content_disposition, std::move(elements)});
+    mojo::MakeStrongBinding(WTF::MakeUnique<MockBlob>(uuid), std::move(blob));
     std::move(callback).Run();
   }
 
-  void GetBlobFromUUID(storage::mojom::blink::BlobRequest blob,
-                       const String& uuid) override {
-    binding_requests.push_back(BindingRequest{std::move(blob), uuid});
+  void GetBlobFromUUID(BlobRequest blob, const String& uuid) override {
+    binding_requests.push_back(BindingRequest{uuid});
+    mojo::MakeStrongBinding(WTF::MakeUnique<MockBlob>(uuid), std::move(blob));
   }
 
   struct Registration {
-    storage::mojom::blink::BlobRequest request;
     String uuid;
     String content_type;
     String content_disposition;
-    Vector<storage::mojom::blink::DataElementPtr> elements;
+    Vector<DataElementPtr> elements;
   };
   Vector<Registration> registrations;
 
   struct BindingRequest {
-    storage::mojom::blink::BlobRequest request;
     String uuid;
   };
   Vector<BindingRequest> binding_requests;
@@ -80,16 +110,14 @@
 
 class MojoBlobInterfaceProvider : public InterfaceProvider {
  public:
-  explicit MojoBlobInterfaceProvider(
-      storage::mojom::blink::BlobRegistry* mock_registry)
+  explicit MojoBlobInterfaceProvider(BlobRegistry* mock_registry)
       : mock_registry_(mock_registry) {}
 
   void GetInterface(const char* name,
                     mojo::ScopedMessagePipeHandle handle) override {
-    if (std::string(name) == storage::mojom::blink::BlobRegistry::Name_) {
-      registry_bindings_.AddBinding(
-          mock_registry_,
-          storage::mojom::blink::BlobRegistryRequest(std::move(handle)));
+    if (std::string(name) == BlobRegistry::Name_) {
+      registry_bindings_.AddBinding(mock_registry_,
+                                    BlobRegistryRequest(std::move(handle)));
       return;
     }
   }
@@ -97,14 +125,13 @@
   void Flush() { registry_bindings_.FlushForTesting(); }
 
  private:
-  storage::mojom::blink::BlobRegistry* mock_registry_;
-  mojo::BindingSet<storage::mojom::blink::BlobRegistry> registry_bindings_;
+  BlobRegistry* mock_registry_;
+  mojo::BindingSet<BlobRegistry> registry_bindings_;
 };
 
 class MojoBlobTestPlatform : public TestingPlatformSupport {
  public:
-  explicit MojoBlobTestPlatform(
-      storage::mojom::blink::BlobRegistry* mock_registry)
+  explicit MojoBlobTestPlatform(BlobRegistry* mock_registry)
       : interface_provider_(
             WTF::MakeUnique<MojoBlobInterfaceProvider>(mock_registry)) {}
 
@@ -118,6 +145,46 @@
   std::unique_ptr<MojoBlobInterfaceProvider> interface_provider_;
 };
 
+struct ExpectedElement {
+  DataElementPtr element;
+  String blob_uuid;
+
+  static ExpectedElement EmbeddedBytes(Vector<uint8_t> embedded_data) {
+    uint64_t size = embedded_data.size();
+    return ExpectedElement{DataElement::NewBytes(
+        DataElementBytes::New(size, std::move(embedded_data), nullptr))};
+  }
+
+  static ExpectedElement LargeBytes(uint64_t size) {
+    return ExpectedElement{DataElement::NewBytes(
+        DataElementBytes::New(size, WTF::nullopt, nullptr))};
+  }
+
+  static ExpectedElement File(const String& path,
+                              uint64_t offset,
+                              uint64_t length,
+                              WTF::Time time) {
+    return ExpectedElement{
+        DataElement::NewFile(DataElementFile::New(path, offset, length, time))};
+  }
+
+  static ExpectedElement FileFilesystem(const KURL& url,
+                                        uint64_t offset,
+                                        uint64_t length,
+                                        WTF::Time time) {
+    return ExpectedElement{DataElement::NewFileFilesystem(
+        DataElementFilesystemURL::New(url, offset, length, time))};
+  }
+
+  static ExpectedElement Blob(const String& uuid,
+                              uint64_t offset,
+                              uint64_t length) {
+    return ExpectedElement{
+        DataElement::NewBlob(DataElementBlob::New(nullptr, offset, length)),
+        uuid};
+  }
+};
+
 }  // namespace
 
 class BlobDataHandleTest : public testing::Test {
@@ -125,13 +192,122 @@
   BlobDataHandleTest()
       : enable_mojo_blobs_(true), testing_platform_(&mock_blob_registry_) {}
 
+  void SetUp() override {
+    small_test_data_.resize(1024);
+    medium_test_data_.resize(1024 * 32);
+    large_test_data_.resize(1024 * 512);
+    for (size_t i = 0; i < small_test_data_.size(); ++i)
+      small_test_data_[i] = i;
+    for (size_t i = 0; i < medium_test_data_.size(); ++i)
+      medium_test_data_[i] = i % 191;
+    for (size_t i = 0; i < large_test_data_.size(); ++i)
+      large_test_data_[i] = i % 251;
+
+    ASSERT_LT(small_test_data_.size(), kMaxConsolidatedItemSizeInBytes);
+    ASSERT_LT(medium_test_data_.size(),
+              DataElementBytes::kMaximumEmbeddedDataSize);
+    ASSERT_GT(medium_test_data_.size(), kMaxConsolidatedItemSizeInBytes);
+    ASSERT_GT(large_test_data_.size(),
+              DataElementBytes::kMaximumEmbeddedDataSize);
+
+    empty_blob_ = BlobDataHandle::Create();
+
+    std::unique_ptr<BlobData> test_data = BlobData::Create();
+    test_data->AppendBytes(large_test_data_.data(), large_test_data_.size());
+    test_blob_ =
+        BlobDataHandle::Create(std::move(test_data), large_test_data_.size());
+
+    testing_platform_->Flush();
+    ASSERT_EQ(2u, mock_blob_registry_.registrations.size());
+    empty_blob_uuid_ = mock_blob_registry_.registrations[0].uuid;
+    test_blob_uuid_ = mock_blob_registry_.registrations[1].uuid;
+    mock_blob_registry_.registrations.clear();
+  }
+
+  void TestCreateBlob(std::unique_ptr<BlobData> data,
+                      Vector<ExpectedElement> expected_elements) {
+    size_t blob_size = data->length();
+    String type = data->ContentType();
+    bool is_single_unknown_size_file = data->IsSingleUnknownSizeFile();
+
+    RefPtr<BlobDataHandle> handle =
+        BlobDataHandle::Create(std::move(data), blob_size);
+    EXPECT_EQ(blob_size, handle->size());
+    EXPECT_EQ(type, handle->GetType());
+    EXPECT_EQ(is_single_unknown_size_file, handle->IsSingleUnknownSizeFile());
+
+    testing_platform_->Flush();
+    EXPECT_EQ(0u, mock_blob_registry_.binding_requests.size());
+    ASSERT_EQ(1u, mock_blob_registry_.registrations.size());
+    const auto& reg = mock_blob_registry_.registrations[0];
+    EXPECT_EQ(handle->Uuid(), reg.uuid);
+    EXPECT_EQ(type.IsNull() ? "" : type, reg.content_type);
+    EXPECT_EQ("", reg.content_disposition);
+    ASSERT_EQ(expected_elements.size(), reg.elements.size());
+    for (size_t i = 0; i < expected_elements.size(); ++i) {
+      const auto& expected = expected_elements[i].element;
+      const auto& actual = reg.elements[i];
+      if (expected->is_bytes()) {
+        ASSERT_TRUE(actual->is_bytes());
+        EXPECT_EQ(expected->get_bytes()->length, actual->get_bytes()->length);
+        EXPECT_EQ(expected->get_bytes()->embedded_data,
+                  actual->get_bytes()->embedded_data);
+      } else if (expected->is_file()) {
+        ASSERT_TRUE(actual->is_file());
+        EXPECT_EQ(expected->get_file()->path, actual->get_file()->path);
+        EXPECT_EQ(expected->get_file()->length, actual->get_file()->length);
+        EXPECT_EQ(expected->get_file()->offset, actual->get_file()->offset);
+        EXPECT_EQ(expected->get_file()->expected_modification_time,
+                  actual->get_file()->expected_modification_time);
+      } else if (expected->is_file_filesystem()) {
+        ASSERT_TRUE(actual->is_file_filesystem());
+        EXPECT_EQ(expected->get_file_filesystem()->url,
+                  actual->get_file_filesystem()->url);
+        EXPECT_EQ(expected->get_file_filesystem()->length,
+                  actual->get_file_filesystem()->length);
+        EXPECT_EQ(expected->get_file_filesystem()->offset,
+                  actual->get_file_filesystem()->offset);
+        EXPECT_EQ(expected->get_file_filesystem()->expected_modification_time,
+                  actual->get_file_filesystem()->expected_modification_time);
+      } else if (expected->is_blob()) {
+        ASSERT_TRUE(actual->is_blob());
+        EXPECT_EQ(expected->get_blob()->length, actual->get_blob()->length);
+        EXPECT_EQ(expected->get_blob()->offset, actual->get_blob()->offset);
+
+        base::RunLoop loop;
+        String received_uuid;
+        actual->get_blob()->blob->GetInternalUUID(base::Bind(
+            [](base::Closure quit_closure, String* uuid_out,
+               const String& uuid) {
+              *uuid_out = uuid;
+              quit_closure.Run();
+            },
+            loop.QuitClosure(), &received_uuid));
+        loop.Run();
+        EXPECT_EQ(expected_elements[i].blob_uuid, received_uuid);
+      }
+    }
+    mock_blob_registry_.registrations.clear();
+  }
+
  protected:
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   ScopedMojoBlobsForTest enable_mojo_blobs_;
-  ScopedTestingPlatformSupport<MojoBlobTestPlatform,
-                               storage::mojom::blink::BlobRegistry*>
+  ScopedTestingPlatformSupport<MojoBlobTestPlatform, BlobRegistry*>
       testing_platform_;
   MockBlobRegistry mock_blob_registry_;
+
+  // Significantly less than BlobData's kMaxConsolidatedItemSizeInBytes.
+  Vector<uint8_t> small_test_data_;
+  // Larger than kMaxConsolidatedItemSizeInBytes, but smaller than
+  // max_data_population.
+  Vector<uint8_t> medium_test_data_;
+  // Larger than max_data_population.
+  Vector<uint8_t> large_test_data_;
+  RefPtr<BlobDataHandle> empty_blob_;
+  String empty_blob_uuid_;
+  RefPtr<BlobDataHandle> test_blob_;
+  String test_blob_uuid_;
 };
 
 TEST_F(BlobDataHandleTest, CreateEmpty) {
@@ -156,19 +332,7 @@
   std::unique_ptr<BlobData> data = BlobData::Create();
   data->SetContentType(kType);
 
-  RefPtr<BlobDataHandle> handle = BlobDataHandle::Create(std::move(data), 0);
-  EXPECT_EQ(kType, handle->GetType());
-  EXPECT_EQ(0u, handle->size());
-  EXPECT_FALSE(handle->IsSingleUnknownSizeFile());
-
-  testing_platform_->Flush();
-  EXPECT_EQ(0u, mock_blob_registry_.binding_requests.size());
-  ASSERT_EQ(1u, mock_blob_registry_.registrations.size());
-  const auto& reg = mock_blob_registry_.registrations[0];
-  EXPECT_EQ(handle->Uuid(), reg.uuid);
-  EXPECT_EQ(kType, reg.content_type);
-  EXPECT_EQ("", reg.content_disposition);
-  EXPECT_EQ(0u, reg.elements.size());
+  TestCreateBlob(std::move(data), {});
 }
 
 TEST_F(BlobDataHandleTest, CreateFromUUID) {
@@ -188,4 +352,182 @@
   EXPECT_EQ(kUuid, mock_blob_registry_.binding_requests[0].uuid);
 }
 
+TEST_F(BlobDataHandleTest, CreateFromEmptyElements) {
+  std::unique_ptr<BlobData> data = BlobData::Create();
+  data->AppendBytes(small_test_data_.data(), 0);
+  data->AppendBlob(empty_blob_, 0, 0);
+  data->AppendFile("path", 0, 0, 0.0);
+  data->AppendFileSystemURL(KURL(), 0, 0, 0.0);
+
+  TestCreateBlob(std::move(data), {});
+}
+
+TEST_F(BlobDataHandleTest, CreateFromSmallBytes) {
+  std::unique_ptr<BlobData> data = BlobData::Create();
+  data->AppendBytes(small_test_data_.data(), small_test_data_.size());
+
+  Vector<ExpectedElement> expected_elements;
+  expected_elements.push_back(ExpectedElement::EmbeddedBytes(small_test_data_));
+
+  TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromLargeBytes) {
+  std::unique_ptr<BlobData> data = BlobData::Create();
+  data->AppendBytes(large_test_data_.data(), large_test_data_.size());
+
+  Vector<ExpectedElement> expected_elements;
+  expected_elements.push_back(
+      ExpectedElement::LargeBytes(large_test_data_.size()));
+
+  TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromMergedBytes) {
+  std::unique_ptr<BlobData> data = BlobData::Create();
+  data->AppendBytes(medium_test_data_.data(), medium_test_data_.size());
+  data->AppendBytes(small_test_data_.data(), small_test_data_.size());
+  EXPECT_EQ(2u, data->Items().size());
+
+  Vector<uint8_t> expected_data = medium_test_data_;
+  expected_data.AppendVector(small_test_data_);
+
+  Vector<ExpectedElement> expected_elements;
+  expected_elements.push_back(
+      ExpectedElement::EmbeddedBytes(std::move(expected_data)));
+
+  TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromMergedLargeAndSmallBytes) {
+  std::unique_ptr<BlobData> data = BlobData::Create();
+  data->AppendBytes(large_test_data_.data(), large_test_data_.size());
+  data->AppendBytes(small_test_data_.data(), small_test_data_.size());
+  EXPECT_EQ(2u, data->Items().size());
+
+  Vector<ExpectedElement> expected_elements;
+  expected_elements.push_back(ExpectedElement::LargeBytes(
+      large_test_data_.size() + small_test_data_.size()));
+
+  TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromMergedSmallAndLargeBytes) {
+  std::unique_ptr<BlobData> data = BlobData::Create();
+  data->AppendBytes(small_test_data_.data(), small_test_data_.size());
+  data->AppendBytes(large_test_data_.data(), large_test_data_.size());
+  EXPECT_EQ(2u, data->Items().size());
+
+  Vector<ExpectedElement> expected_elements;
+  expected_elements.push_back(ExpectedElement::LargeBytes(
+      large_test_data_.size() + small_test_data_.size()));
+
+  TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromFileAndFileSystemURL) {
+  double timestamp1 = CurrentTime();
+  double timestamp2 = timestamp1 + 1;
+  KURL url(KURL(), "http://example.com/");
+  std::unique_ptr<BlobData> data = BlobData::Create();
+  data->AppendFile("path", 4, 32, timestamp1);
+  data->AppendFileSystemURL(url, 15, 876, timestamp2);
+
+  Vector<ExpectedElement> expected_elements;
+  expected_elements.push_back(
+      ExpectedElement::File("path", 4, 32, WTF::Time::FromDoubleT(timestamp1)));
+  expected_elements.push_back(ExpectedElement::FileFilesystem(
+      url, 15, 876, WTF::Time::FromDoubleT(timestamp2)));
+
+  TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromFileWithUnknownSize) {
+  Vector<ExpectedElement> expected_elements;
+  expected_elements.push_back(
+      ExpectedElement::File("path", 0, uint64_t(-1), WTF::Time()));
+
+  TestCreateBlob(BlobData::CreateForFileWithUnknownSize("path"),
+                 std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromFilesystemFileWithUnknownSize) {
+  double timestamp = CurrentTime();
+  KURL url(KURL(), "http://example.com/");
+  Vector<ExpectedElement> expected_elements;
+  expected_elements.push_back(ExpectedElement::FileFilesystem(
+      url, 0, uint64_t(-1), WTF::Time::FromDoubleT(timestamp)));
+
+  TestCreateBlob(
+      BlobData::CreateForFileSystemURLWithUnknownSize(url, timestamp),
+      std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromBlob) {
+  std::unique_ptr<BlobData> data = BlobData::Create();
+  data->AppendBlob(test_blob_, 13, 765);
+
+  Vector<ExpectedElement> expected_elements;
+  expected_elements.push_back(ExpectedElement::Blob(test_blob_uuid_, 13, 765));
+
+  TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromBlobsAndBytes) {
+  std::unique_ptr<BlobData> data = BlobData::Create();
+  data->AppendBlob(test_blob_, 10, 10);
+  data->AppendBytes(medium_test_data_.data(), medium_test_data_.size());
+  data->AppendBlob(test_blob_, 0, 0);
+  data->AppendBytes(small_test_data_.data(), small_test_data_.size());
+  data->AppendBlob(test_blob_, 0, 10);
+  data->AppendBytes(large_test_data_.data(), large_test_data_.size());
+
+  Vector<uint8_t> expected_data = medium_test_data_;
+  expected_data.AppendVector(small_test_data_);
+
+  Vector<ExpectedElement> expected_elements;
+  expected_elements.push_back(ExpectedElement::Blob(test_blob_uuid_, 10, 10));
+  expected_elements.push_back(
+      ExpectedElement::EmbeddedBytes(std::move(expected_data)));
+  expected_elements.push_back(ExpectedElement::Blob(test_blob_uuid_, 0, 10));
+  expected_elements.push_back(
+      ExpectedElement::LargeBytes(large_test_data_.size()));
+
+  TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromSmallBytesAfterLargeBytes) {
+  std::unique_ptr<BlobData> data = BlobData::Create();
+  data->AppendBytes(large_test_data_.data(), large_test_data_.size());
+  data->AppendBlob(test_blob_, 0, 10);
+  data->AppendBytes(small_test_data_.data(), small_test_data_.size());
+
+  Vector<ExpectedElement> expected_elements;
+  expected_elements.push_back(
+      ExpectedElement::LargeBytes(large_test_data_.size()));
+  expected_elements.push_back(ExpectedElement::Blob(test_blob_uuid_, 0, 10));
+  expected_elements.push_back(ExpectedElement::EmbeddedBytes(small_test_data_));
+
+  TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
+TEST_F(BlobDataHandleTest, CreateFromManyMergedBytes) {
+  std::unique_ptr<BlobData> data = BlobData::Create();
+  uint64_t merged_size = 0;
+  while (merged_size <= DataElementBytes::kMaximumEmbeddedDataSize) {
+    data->AppendBytes(medium_test_data_.data(), medium_test_data_.size());
+    merged_size += medium_test_data_.size();
+  }
+  data->AppendBlob(test_blob_, 0, 10);
+  data->AppendBytes(medium_test_data_.data(), medium_test_data_.size());
+
+  Vector<ExpectedElement> expected_elements;
+  expected_elements.push_back(ExpectedElement::LargeBytes(merged_size));
+  expected_elements.push_back(ExpectedElement::Blob(test_blob_uuid_, 0, 10));
+  expected_elements.push_back(
+      ExpectedElement::EmbeddedBytes(medium_test_data_));
+
+  TestCreateBlob(std::move(data), std::move(expected_elements));
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
index 171e6a32..44f49632 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
@@ -31,8 +31,8 @@
       local_state.Clip()->LocalTransformSpace();
   if (transform_node != ancestor_state.Transform()) {
     const TransformationMatrix& local_to_ancestor_matrix =
-        GeometryMapper::LocalToAncestorMatrix(transform_node,
-                                              ancestor_state.Transform());
+        GeometryMapper::SourceToDestinationProjection(
+            transform_node, ancestor_state.Transform());
     // Clips are only in descendant spaces that are transformed by one
     // or more scrolls.
     DCHECK(local_to_ancestor_matrix.IsIdentityOrTranslation());
@@ -121,8 +121,8 @@
           const TransformPaintPropertyNode* transform_node =
               paired_state->Effect()->LocalTransformSpace();
           const TransformationMatrix& local_to_ancestor_matrix =
-              GeometryMapper::LocalToAncestorMatrix(transform_node,
-                                                    paired_state->Transform());
+              GeometryMapper::SourceToDestinationProjection(
+                  transform_node, paired_state->Transform());
           // Effects are only in descendant spaces that are transformed by one
           // or more scrolls.
           DCHECK(local_to_ancestor_matrix.IsIdentityOrTranslation());
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.cpp b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.cpp
index d744746..0d93b3a 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.cpp
@@ -9,60 +9,111 @@
 
 namespace blink {
 
-const TransformationMatrix& GeometryMapper::IdentityMatrix() {
-  DEFINE_STATIC_LOCAL(TransformationMatrix, identity, (TransformationMatrix()));
-  return identity;
-}
-
-const FloatClipRect& GeometryMapper::InfiniteClip() {
-  DEFINE_STATIC_LOCAL(FloatClipRect, infinite, (FloatClipRect()));
-  return infinite;
-}
-
-FloatClipRect& GeometryMapper::TempRect() {
-  DEFINE_STATIC_LOCAL(FloatClipRect, temp, (FloatClipRect()));
-  return temp;
-}
-
-void GeometryMapper::SourceToDestinationVisualRect(
-    const PropertyTreeState& source_state,
-    const PropertyTreeState& destination_state,
-    FloatClipRect& rect) {
+const TransformationMatrix& GeometryMapper::SourceToDestinationProjection(
+    const TransformPaintPropertyNode* source,
+    const TransformPaintPropertyNode* destination) {
+  DCHECK(source && destination);
   bool success = false;
-  SourceToDestinationVisualRectInternal(source_state, destination_state, rect,
-                                        success);
+  const auto& result =
+      SourceToDestinationProjectionInternal(source, destination, success);
   DCHECK(success);
+  return result;
 }
 
-void GeometryMapper::SourceToDestinationVisualRectInternal(
-    const PropertyTreeState& source_state,
-    const PropertyTreeState& destination_state,
-    FloatClipRect& mapping_rect,
+// Returns flatten(destination_to_screen)^-1 * flatten(source_to_screen)
+//
+// In case that source and destination are coplanar in tree hierarchy [1],
+// computes destination_to_plane_root ^ -1 * source_to_plane_root.
+// It can be proved that [2] the result will be the same (except numerical
+// errors) when the plane root has invertible screen projection, and this
+// offers fallback definition when plane root is singular. For example:
+// <div style="transform:rotateY(90deg); overflow:scroll;">
+//   <div id="A" style="opacity:0.5;">
+//     <div id="B" style="position:absolute;"></div>
+//   </div>
+// </div>
+// Both A and B have non-invertible screen projection, nevertheless it is
+// useful to define projection between A and B. Say, the transform may be
+// animated in compositor thus become visible.
+// As SPv1 treats 3D transforms as compositing trigger, that implies mappings
+// within the same compositing layer can only contain 2D transforms, thus
+// intra-composited-layer queries are guaranteed to be handled correctly.
+//
+// [1] As defined by that all local transforms between source and some common
+//     ancestor 'plane root' and all local transforms between the destination
+//     and the plane root being flat.
+// [2] destination_to_screen = plane_root_to_screen * destination_to_plane_root
+//     source_to_screen = plane_root_to_screen * source_to_plane_root
+//     output = flatten(destination_to_screen)^-1 * flatten(source_to_screen)
+//     = flatten(plane_root_to_screen * destination_to_plane_root)^-1 *
+//       flatten(plane_root_to_screen * source_to_plane_root)
+//     Because both destination_to_plane_root and source_to_plane_root are
+//     already flat,
+//     = flatten(plane_root_to_screen * flatten(destination_to_plane_root))^-1 *
+//       flatten(plane_root_to_screen * flatten(source_to_plane_root))
+//     By flatten lemma [3] flatten(A * flatten(B)) = flatten(A) * flatten(B),
+//     = flatten(destination_to_plane_root)^-1 *
+//       flatten(plane_root_to_screen)^-1 *
+//       flatten(plane_root_to_screen) * flatten(source_to_plane_root)
+//     If flatten(plane_root_to_screen) is invertible, they cancel out:
+//     = flatten(destination_to_plane_root)^-1 * flatten(source_to_plane_root)
+//     = destination_to_plane_root^-1 * source_to_plane_root
+// [3] Flatten lemma: https://goo.gl/DNKyOc
+const TransformationMatrix&
+GeometryMapper::SourceToDestinationProjectionInternal(
+    const TransformPaintPropertyNode* source,
+    const TransformPaintPropertyNode* destination,
     bool& success) {
-  LocalToAncestorVisualRectInternal(source_state, destination_state,
-                                    mapping_rect, success);
-  // Success if destinationState is an ancestor state.
-  if (success)
-    return;
+  DCHECK(source && destination);
+  DEFINE_STATIC_LOCAL(TransformationMatrix, identity, (TransformationMatrix()));
+  DEFINE_STATIC_LOCAL(TransformationMatrix, temp, (TransformationMatrix()));
 
-  // Otherwise first map to the lowest common ancestor, then map to destination.
-  const TransformPaintPropertyNode* lca_transform = LowestCommonAncestor(
-      source_state.Transform(), destination_state.Transform());
-  DCHECK(lca_transform);
+  if (source == destination) {
+    success = true;
+    return identity;
+  }
 
-  // Assume that the clip of destinationState is an ancestor of the clip of
-  // sourceState and is under the space of lcaTransform. Otherwise
-  // localToAncestorVisualRect() will fail.
-  PropertyTreeState lca_state = destination_state;
-  lca_state.SetTransform(lca_transform);
+  const GeometryMapperTransformCache& source_cache =
+      source->GetTransformCache();
+  const GeometryMapperTransformCache& destination_cache =
+      destination->GetTransformCache();
 
-  LocalToAncestorVisualRectInternal(source_state, lca_state, mapping_rect,
-                                    success);
-  if (!success)
-    return;
+  // Case 1: Check if source and destination are known to be coplanar.
+  // Even if destination may have invertible screen projection,
+  // this formula is likely to be numerically more stable.
+  if (source_cache.plane_root() == destination_cache.plane_root()) {
+    success = true;
+    if (source == destination_cache.plane_root())
+      return destination_cache.from_plane_root();
+    if (destination == source_cache.plane_root())
+      return source_cache.to_plane_root();
+    temp = destination_cache.from_plane_root();
+    temp.Multiply(source_cache.to_plane_root());
+    return temp;
+  }
 
-  AncestorToLocalRect(lca_transform, destination_state.Transform(),
-                      mapping_rect.Rect());
+  // Case 2: Check if we can fallback to the canonical definition of
+  // flatten(destination_to_screen)^-1 * flatten(source_to_screen)
+  // If flatten(destination_to_screen)^-1 is invalid, we are out of luck.
+  if (!destination_cache.projection_from_screen_is_valid()) {
+    success = false;
+    return identity;
+  }
+
+  // Case 3: Compute:
+  // flatten(destination_to_screen)^-1 * flatten(source_to_screen)
+  const auto* root = TransformPaintPropertyNode::Root();
+  success = true;
+  if (source == root)
+    return destination_cache.projection_from_screen();
+  if (destination == root) {
+    temp = source_cache.to_screen();
+  } else {
+    temp = destination_cache.projection_from_screen();
+    temp.Multiply(source_cache.to_screen());
+  }
+  temp.FlattenTo2d();
+  return temp;
 }
 
 void GeometryMapper::SourceToDestinationRect(
@@ -70,19 +121,11 @@
     const TransformPaintPropertyNode* destination_transform_node,
     FloatRect& mapping_rect) {
   bool success = false;
-  LocalToAncestorRectInternal(source_transform_node, destination_transform_node,
-                              mapping_rect, success);
-  // Success if destinationTransformNode is an ancestor of sourceTransformNode.
-  if (success)
-    return;
-
-  // Otherwise first map to the least common ancestor, then map to destination.
-  const TransformPaintPropertyNode* lca_transform =
-      LowestCommonAncestor(source_transform_node, destination_transform_node);
-  DCHECK(lca_transform);
-
-  LocalToAncestorRect(source_transform_node, lca_transform, mapping_rect);
-  AncestorToLocalRect(lca_transform, destination_transform_node, mapping_rect);
+  const TransformationMatrix& source_to_destination =
+      SourceToDestinationProjectionInternal(
+          source_transform_node, destination_transform_node, success);
+  mapping_rect =
+      success ? source_to_destination.MapRect(mapping_rect) : FloatRect();
 }
 
 void GeometryMapper::LocalToAncestorVisualRect(
@@ -111,18 +154,29 @@
     return;
   }
 
-  const auto& transform_matrix = LocalToAncestorMatrixInternal(
+  const auto& transform_matrix = SourceToDestinationProjectionInternal(
       local_state.Transform(), ancestor_state.Transform(), success);
   if (!success) {
+    // A failure implies either source-to-plane or destination-to-plane being
+    // singular. A notable example of singular source-to-plane from valid CSS:
+    // <div id="plane" style="transform:rotateY(180deg)">
+    //   <div style="overflow:overflow">
+    //     <div id="ancestor" style="opacity:0.5;">
+    //       <div id="local" style="position:absolute; transform:scaleX(0);">
+    //       </div>
+    //     </div>
+    //   </div>
+    // </div>
+    // Either way, the element won't be renderable thus returning empty rect.
+    success = true;
+    rect_to_map = FloatClipRect(FloatRect());
     return;
   }
-
   FloatRect mapped_rect = transform_matrix.MapRect(rect_to_map.Rect());
 
   const FloatClipRect& clip_rect =
       LocalToAncestorClipRectInternal(local_state.Clip(), ancestor_state.Clip(),
                                       ancestor_state.Transform(), success);
-
   if (success) {
     // This is where we propagate the rounded-ness of |clipRect| to
     // |rectToMap|.
@@ -155,11 +209,14 @@
 
     PropertyTreeState transform_and_clip_state(effect->LocalTransformSpace(),
                                                effect->OutputClip(), nullptr);
-    SourceToDestinationVisualRectInternal(last_transform_and_clip_state,
-                                          transform_and_clip_state,
-                                          mapping_rect, success);
-    if (!success)
+    LocalToAncestorVisualRectInternal(last_transform_and_clip_state,
+                                      transform_and_clip_state, mapping_rect,
+                                      success);
+    if (!success) {
+      success = true;
+      mapping_rect = FloatClipRect(FloatRect());
       return;
+    }
 
     mapping_rect.SetRect(effect->MapRect(mapping_rect.Rect()));
     last_transform_and_clip_state = transform_and_clip_state;
@@ -167,51 +224,9 @@
 
   PropertyTreeState final_transform_and_clip_state(
       ancestor_state.Transform(), ancestor_state.Clip(), nullptr);
-  SourceToDestinationVisualRectInternal(last_transform_and_clip_state,
-                                        final_transform_and_clip_state,
-                                        mapping_rect, success);
-}
-
-void GeometryMapper::LocalToAncestorRect(
-    const TransformPaintPropertyNode* local_transform_node,
-    const TransformPaintPropertyNode* ancestor_transform_node,
-    FloatRect& mapping_rect) {
-  bool success = false;
-  LocalToAncestorRectInternal(local_transform_node, ancestor_transform_node,
-                              mapping_rect, success);
-  DCHECK(success);
-}
-
-void GeometryMapper::LocalToAncestorRectInternal(
-    const TransformPaintPropertyNode* local_transform_node,
-    const TransformPaintPropertyNode* ancestor_transform_node,
-    FloatRect& mapping_rect,
-    bool& success) {
-  if (local_transform_node == ancestor_transform_node) {
-    success = true;
-    return;
-  }
-
-  const auto& transform_matrix = LocalToAncestorMatrixInternal(
-      local_transform_node, ancestor_transform_node, success);
-  if (!success)
-    return;
-  mapping_rect = transform_matrix.MapRect(mapping_rect);
-}
-
-void GeometryMapper::AncestorToLocalRect(
-    const TransformPaintPropertyNode* ancestor_transform_node,
-    const TransformPaintPropertyNode* local_transform_node,
-    FloatRect& rect) {
-  if (local_transform_node == ancestor_transform_node)
-    return;
-
-  const auto& transform_matrix =
-      LocalToAncestorMatrix(local_transform_node, ancestor_transform_node);
-  DCHECK(transform_matrix.IsInvertible());
-
-  // TODO(chrishtr): Cache the inverse?
-  rect = transform_matrix.Inverse().MapRect(rect);
+  LocalToAncestorVisualRectInternal(last_transform_and_clip_state,
+                                    final_transform_and_clip_state,
+                                    mapping_rect, success);
 }
 
 const FloatClipRect& GeometryMapper::LocalToAncestorClipRect(
@@ -227,75 +242,18 @@
   return result;
 }
 
-const FloatClipRect& GeometryMapper::SourceToDestinationClipRect(
-    const PropertyTreeState& source_state,
-    const PropertyTreeState& destination_state) {
-  bool success = false;
-  const FloatClipRect& result = SourceToDestinationClipRectInternal(
-      source_state, destination_state, success);
-  DCHECK(success);
-
-  return result;
-}
-
-const FloatClipRect& GeometryMapper::SourceToDestinationClipRectInternal(
-    const PropertyTreeState& source_state,
-    const PropertyTreeState& destination_state,
-    bool& success) {
-  const FloatClipRect& result = LocalToAncestorClipRectInternal(
-      source_state.Clip(), destination_state.Clip(),
-      destination_state.Transform(), success);
-  // Success if destinationState is an ancestor state.
-  if (success)
-    return result;
-
-  // Otherwise first map to the lowest common ancestor, then map to
-  // destination.
-  const TransformPaintPropertyNode* lca_transform = LowestCommonAncestor(
-      source_state.Transform(), destination_state.Transform());
-  DCHECK(lca_transform);
-
-  // Assume that the clip of destinationState is an ancestor of the clip of
-  // sourceState and is under the space of lcaTransform. Otherwise
-  // localToAncestorClipRectInternal() will fail.
-  PropertyTreeState lca_state = destination_state;
-  lca_state.SetTransform(lca_transform);
-
-  const FloatClipRect& result2 = LocalToAncestorClipRectInternal(
-      source_state.Clip(), lca_state.Clip(), lca_state.Transform(), success);
-  if (!success) {
-    if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
-      // On SPv1 we may fail when the paint invalidation container creates an
-      // overflow clip (in ancestorState) which is not in localState of an
-      // out-of-flow positioned descendant. See crbug.com/513108 and layout
-      // test compositing/overflow/handle-non-ancestor-clip-parent.html (run
-      // with --enable-prefer-compositing-to-lcd-text) for details.
-      // Ignore it for SPv1 for now.
-      success = true;
-    }
-    return result2;
-  }
-  if (!result2.IsInfinite()) {
-    FloatRect rect = result2.Rect();
-    AncestorToLocalRect(lca_transform, destination_state.Transform(), rect);
-    FloatClipRect& temp = TempRect();
-    temp.SetRect(rect);
-    if (result2.HasRadius())
-      temp.SetHasRadius();
-    return temp;
-  }
-  return result2;
-}
-
 const FloatClipRect& GeometryMapper::LocalToAncestorClipRectInternal(
     const ClipPaintPropertyNode* descendant,
     const ClipPaintPropertyNode* ancestor_clip,
     const TransformPaintPropertyNode* ancestor_transform,
     bool& success) {
+  DEFINE_STATIC_LOCAL(FloatClipRect, infinite, (FloatClipRect()));
+  DEFINE_STATIC_LOCAL(FloatClipRect, empty, (FloatRect()));
+
   FloatClipRect clip;
   if (descendant == ancestor_clip) {
     success = true;
-    return InfiniteClip();
+    return infinite;
   }
 
   const ClipPaintPropertyNode* clip_node = descendant;
@@ -317,19 +275,29 @@
   }
   if (!clip_node) {
     success = false;
-    return InfiniteClip();
+    if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
+      // On SPv1 we may fail when the paint invalidation container creates an
+      // overflow clip (in ancestorState) which is not in localState of an
+      // out-of-flow positioned descendant. See crbug.com/513108 and layout
+      // test compositing/overflow/handle-non-ancestor-clip-parent.html (run
+      // with --enable-prefer-compositing-to-lcd-text) for details.
+      // Ignore it for SPv1 for now.
+      success = true;
+    }
+    return infinite;
   }
 
   // Iterate down from the top intermediate node found in the previous loop,
   // computing and memoizing clip rects as we go.
   for (auto it = intermediate_nodes.rbegin(); it != intermediate_nodes.rend();
        ++it) {
-    success = false;
     const TransformationMatrix& transform_matrix =
-        LocalToAncestorMatrixInternal((*it)->LocalTransformSpace(),
-                                      ancestor_transform, success);
-    if (!success)
-      return InfiniteClip();
+        SourceToDestinationProjectionInternal((*it)->LocalTransformSpace(),
+                                              ancestor_transform, success);
+    if (!success) {
+      success = true;
+      return empty;
+    }
     FloatRect mapped_rect = transform_matrix.MapRect((*it)->ClipRect().Rect());
     clip.Intersect(mapped_rect);
     if ((*it)->ClipRect().IsRounded())
@@ -338,81 +306,13 @@
     (*it)->GetClipCache().SetCachedClip(clip_and_transform, clip);
   }
 
-  success = true;
-
   const FloatClipRect* cached_clip =
       descendant->GetClipCache().GetCachedClip(clip_and_transform);
   DCHECK(cached_clip);
   CHECK(clip.HasRadius() == cached_clip->HasRadius());
-  return *cached_clip;
-}
 
-const TransformationMatrix& GeometryMapper::LocalToAncestorMatrix(
-    const TransformPaintPropertyNode* local_transform_node,
-    const TransformPaintPropertyNode* ancestor_transform_node) {
-  bool success = false;
-  const auto& result = LocalToAncestorMatrixInternal(
-      local_transform_node, ancestor_transform_node, success);
-  DCHECK(success);
-  return result;
-}
-
-const TransformationMatrix& GeometryMapper::LocalToAncestorMatrixInternal(
-    const TransformPaintPropertyNode* local_transform_node,
-    const TransformPaintPropertyNode* ancestor_transform_node,
-    bool& success) {
-  if (local_transform_node == ancestor_transform_node) {
-    success = true;
-    return IdentityMatrix();
-  }
-
-  const TransformPaintPropertyNode* transform_node = local_transform_node;
-  Vector<const TransformPaintPropertyNode*> intermediate_nodes;
-  TransformationMatrix transform_matrix;
-
-  // Iterate over the path from localTransformNode to ancestorState.transform.
-  // Stop if we've found a memoized (precomputed) transform for any particular
-  // node.
-  while (transform_node && transform_node != ancestor_transform_node) {
-    if (const TransformationMatrix* cached_matrix =
-            transform_node->GetTransformCache().GetCachedTransform(
-                ancestor_transform_node)) {
-      transform_matrix = *cached_matrix;
-      break;
-    }
-
-    intermediate_nodes.push_back(transform_node);
-    transform_node = transform_node->Parent();
-  }
-  if (!transform_node) {
-    success = false;
-    return IdentityMatrix();
-  }
-
-  // Iterate down from the top intermediate node found in the previous loop,
-  // computing and memoizing transforms as we go.
-  for (auto it = intermediate_nodes.rbegin(); it != intermediate_nodes.rend();
-       it++) {
-    TransformationMatrix local_transform_matrix = (*it)->Matrix();
-    local_transform_matrix.ApplyTransformOrigin((*it)->Origin());
-
-    // Flattening Lemma: flatten(A * flatten(B)) = flatten(flatten(A) * B).
-    // goo.gl/DNKyOc. Thus we can flatten transformMatrix rather than
-    // localTransformMatrix, because GeometryMapper only supports transforms
-    // into a flattened destination space.
-    if ((*it)->FlattensInheritedTransform())
-      transform_matrix.FlattenTo2d();
-
-    transform_matrix = transform_matrix * local_transform_matrix;
-    (*it)->GetTransformCache().SetCachedTransform(ancestor_transform_node,
-                                                  transform_matrix);
-  }
   success = true;
-  const TransformationMatrix* cached_matrix =
-      local_transform_node->GetTransformCache().GetCachedTransform(
-          ancestor_transform_node);
-  DCHECK(cached_matrix);
-  return *cached_matrix;
+  return *cached_clip;
 }
 
 void GeometryMapper::ClearCache() {
@@ -464,17 +364,17 @@
 // Explicitly instantiate the template for all supported types. This allows
 // placing the template implementation in this .cpp file. See
 // http://stackoverflow.com/a/488989 for more.
-template const EffectPaintPropertyNode* GeometryMapper::LowestCommonAncestor(
-    const EffectPaintPropertyNode*,
-    const EffectPaintPropertyNode*);
-template const TransformPaintPropertyNode* GeometryMapper::LowestCommonAncestor(
-    const TransformPaintPropertyNode*,
-    const TransformPaintPropertyNode*);
-template const ClipPaintPropertyNode* GeometryMapper::LowestCommonAncestor(
-    const ClipPaintPropertyNode*,
-    const ClipPaintPropertyNode*);
-template const ScrollPaintPropertyNode* GeometryMapper::LowestCommonAncestor(
-    const ScrollPaintPropertyNode*,
-    const ScrollPaintPropertyNode*);
+template PLATFORM_EXPORT const EffectPaintPropertyNode*
+GeometryMapper::LowestCommonAncestor(const EffectPaintPropertyNode*,
+                                     const EffectPaintPropertyNode*);
+template PLATFORM_EXPORT const TransformPaintPropertyNode*
+GeometryMapper::LowestCommonAncestor(const TransformPaintPropertyNode*,
+                                     const TransformPaintPropertyNode*);
+template PLATFORM_EXPORT const ClipPaintPropertyNode*
+GeometryMapper::LowestCommonAncestor(const ClipPaintPropertyNode*,
+                                     const ClipPaintPropertyNode*);
+template PLATFORM_EXPORT const ScrollPaintPropertyNode*
+GeometryMapper::LowestCommonAncestor(const ScrollPaintPropertyNode*,
+                                     const ScrollPaintPropertyNode*);
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h
index 559e131c..200ca82b 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h
@@ -7,11 +7,12 @@
 
 #include "platform/graphics/paint/FloatClipRect.h"
 #include "platform/graphics/paint/PropertyTreeState.h"
-#include "platform/transforms/TransformationMatrix.h"
 #include "platform/wtf/HashMap.h"
 
 namespace blink {
 
+class TransformationMatrix;
+
 // GeometryMapper is a helper class for fast computations of transformed and
 // visual rects in different PropertyTreeStates. The design document has a
 // number of details on use cases, algorithmic definitions, and running times.
@@ -31,34 +32,19 @@
   STATIC_ONLY(GeometryMapper);
 
  public:
-  // The runtime of m calls among localToAncestorVisualRect, localToAncestorRect
-  // or ancestorToLocalRect with the same |ancestorState| parameter is
-  // guaranteed to be O(n + m), where n is the number of transform and clip
-  // nodes in their respective property trees.
-
-  // If the clips and transforms of |sourceState| are equal to or descendants of
-  // those of |destinationState|, returns the same value as
-  // localToAncestorVisualRect. Otherwise, maps the input rect to the
-  // transform state which is the lowest common ancestor of
-  // |sourceState.transform| and |destinationState.transform|, then multiplies
-  // it by the the inverse transform mapping from the lowest common ancestor to
-  // |destinationState.transform|.
-  //
-  // DCHECK fails if the clip of |destinationState| is not an ancestor of the
-  // clip of |sourceState|, or the inverse transform is not invertible.
-  //
-  // |mappingRect| is both input and output.
-  //
-  // The output FloatClipRect may contain false positives for rounded-ness
-  // if a rounded clip is clipped out, and overly conservative results
-  // in the presences of transforms.
-  //
-  // TODO(chrishtr): we should provide a variant of these methods that
-  // guarantees a tight result, or else signals an error. crbug.com/708741
-  static void SourceToDestinationVisualRect(
-      const PropertyTreeState& source_state,
-      const PropertyTreeState& destination_state,
-      FloatClipRect& mapping_rect);
+  // Returns the matrix that is suitable to map geometries on the source plane
+  // to some backing in the destination plane.
+  // Formal definition:
+  //   output = flatten(destination_to_screen)^-1 * flatten(source_to_screen)
+  // There are some cases that flatten(destination_to_screen) being
+  // singular yet we can still define a reasonable projection, for example:
+  // 1. Both nodes inherited a common singular flat ancestor:
+  // 2. Both nodes are co-planar to a common singular ancestor:
+  // Not every cases outlined above are supported!
+  // Read implementation comments for specific restrictions.
+  static const TransformationMatrix& SourceToDestinationProjection(
+      const TransformPaintPropertyNode* source,
+      const TransformPaintPropertyNode* destination);
 
   // Same as sourceToDestinationVisualRect() except that only transforms are
   // applied.
@@ -69,6 +55,16 @@
       const TransformPaintPropertyNode* destination_transform_node,
       FloatRect& mapping_rect);
 
+  // Returns the "clip visual rect" between |localTransformState| and
+  // |ancestorState|. See above for the definition of "clip visual rect".
+  //
+  // The output FloatClipRect may contain false positives for rounded-ness
+  // if a rounded clip is clipped out, and overly conservative results
+  // in the presences of transforms.
+  static const FloatClipRect& LocalToAncestorClipRect(
+      const PropertyTreeState& local_transform_state,
+      const PropertyTreeState& ancestor_state);
+
   // Maps from a rect in |localTransformSpace| to its visual rect in
   // |ancestorState|. This is computed by multiplying the rect by its combined
   // transform between |localTransformSpace| and |ancestorSpace|, then
@@ -87,71 +83,14 @@
   // The output FloatClipRect may contain false positives for rounded-ness
   // if a rounded clip is clipped out, and overly conservative results
   // in the presences of transforms.
+  //
+  // TODO(chrishtr): we should provide a variant of these methods that
+  // guarantees a tight result, or else signals an error. crbug.com/708741
   static void LocalToAncestorVisualRect(
       const PropertyTreeState& local_transform_state,
       const PropertyTreeState& ancestor_state,
       FloatClipRect& mapping_rect);
 
-  // Maps from a rect in |localTransformNode| space to its transformed rect in
-  // |ancestorTransformNode| space. This is computed by multiplying the rect by
-  // the combined transform between |localTransformNode| and
-  // |ancestorTransformNode|, then flattening into 2D space.
-  //
-  // DCHECK fails if |localTransformNode| is not equal to or a descendant of
-  // |ancestorTransformNode|.
-  //
-  //|mappingRect| is both input and output.
-  static void LocalToAncestorRect(
-      const TransformPaintPropertyNode* local_transform_node,
-      const TransformPaintPropertyNode* ancestor_transform_node,
-      FloatRect& mapping_rect);
-
-  // Maps from a rect in |ancestorTransformNode| space to its transformed rect
-  // in |localTransformNode| space. This is computed by multiplying the rect by
-  // the inverse combined transform between |localTransformNode| and
-  // |ancestorTransformNode|, if the transform is invertible.
-  //
-  // DCHECK fails if the combined transform is not invertible, or
-  // |localTransformNode| is not equal to or a descendant of
-  // |ancestorTransformNode|.
-  //
-  // |mappingRect| is both input and output.
-  static void AncestorToLocalRect(
-      const TransformPaintPropertyNode* ancestor_transform_node,
-      const TransformPaintPropertyNode* local_transform_node,
-      FloatRect& mapping_rect);
-
-  // Returns the matrix used in |LocalToAncestorRect|. DCHECK fails iff
-  // |localTransformNode| is not equal to or a descendant of
-  // |ancestorTransformNode|.
-  // This matrix may not be flattened. Since GeometryMapper only supports
-  // flattened ancestor spaces, the returned matrix must be flattened to have
-  // the correct semantics (calling mapRect() on it implicitly applies
-  // flattening to the input; flattenTo2d() does it explicitly to tme matrix).
-  static const TransformationMatrix& LocalToAncestorMatrix(
-      const TransformPaintPropertyNode* local_transform_node,
-      const TransformPaintPropertyNode* ancestor_transform_node);
-
-  // Returns the "clip visual rect" between |localTransformState| and
-  // |ancestorState|. See above for the definition of "clip visual rect".
-  //
-  // The output FloatClipRect may contain false positives for rounded-ness
-  // if a rounded clip is clipped out, and overly conservative results
-  // in the presences of transforms.
-  static const FloatClipRect& LocalToAncestorClipRect(
-      const PropertyTreeState& local_transform_state,
-      const PropertyTreeState& ancestor_state);
-
-  // Like localToAncestorClipRect, except it can handle destination transform
-  // spaces which are not direct ancestors of the source transform space.
-  //
-  // The output FloatClipRect may contain false positives for rounded-ness
-  // if a rounded clip is clipped out, and overly conservative results
-  // in the presences of transforms.
-  static const FloatClipRect& SourceToDestinationClipRect(
-      const PropertyTreeState& source_state,
-      const PropertyTreeState& destination_state);
-
   // Returns the lowest common ancestor in the paint property tree.
   template <typename NodeType>
   static PLATFORM_EXPORT const NodeType* LowestCommonAncestor(const NodeType*,
@@ -165,27 +104,9 @@
   // successful on return. See comments of the public functions for failure
   // conditions.
 
-  static void SourceToDestinationVisualRectInternal(
-      const PropertyTreeState& source_state,
-      const PropertyTreeState& destination_state,
-      FloatClipRect& mapping_rect,
-      bool& success);
-
-  static void LocalToAncestorVisualRectInternal(
-      const PropertyTreeState& local_transform_state,
-      const PropertyTreeState& ancestor_state,
-      FloatClipRect& mapping_rect,
-      bool& success);
-
-  static void LocalToAncestorRectInternal(
-      const TransformPaintPropertyNode* local_transform_node,
-      const TransformPaintPropertyNode* ancestor_transform_node,
-      FloatRect&,
-      bool& success);
-
-  static const TransformationMatrix& LocalToAncestorMatrixInternal(
-      const TransformPaintPropertyNode* local_transform_node,
-      const TransformPaintPropertyNode* ancestor_transform_node,
+  static const TransformationMatrix& SourceToDestinationProjectionInternal(
+      const TransformPaintPropertyNode* source,
+      const TransformPaintPropertyNode* destination,
       bool& success);
 
   static const FloatClipRect& LocalToAncestorClipRectInternal(
@@ -194,9 +115,10 @@
       const TransformPaintPropertyNode* ancestor_transform,
       bool& success);
 
-  static const FloatClipRect& SourceToDestinationClipRectInternal(
-      const PropertyTreeState& source_state,
-      const PropertyTreeState& destination_state,
+  static void LocalToAncestorVisualRectInternal(
+      const PropertyTreeState& local_transform_state,
+      const PropertyTreeState& ancestor_state,
+      FloatClipRect& mapping_rect,
       bool& success);
 
   static void SlowLocalToAncestorVisualRectWithEffects(
@@ -205,10 +127,6 @@
       FloatClipRect& mapping_rect,
       bool& success);
 
-  static const TransformationMatrix& IdentityMatrix();
-  static const FloatClipRect& InfiniteClip();
-  static FloatClipRect& TempRect();
-
   friend class GeometryMapperTest;
   friend class PaintLayerClipperTest;
 };
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp
index 9649b49..d809cb7 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp
@@ -34,30 +34,12 @@
     return descendant_clip->GetClipCache().GetCachedClip(clip_and_transform);
   }
 
-  const TransformationMatrix* GetTransform(
-      const TransformPaintPropertyNode* descendant_transform,
-      const TransformPaintPropertyNode* ancestor_transform) {
-    return descendant_transform->GetTransformCache().GetCachedTransform(
-        ancestor_transform);
-  }
-
   const TransformPaintPropertyNode* LowestCommonAncestor(
       const TransformPaintPropertyNode* a,
       const TransformPaintPropertyNode* b) {
     return GeometryMapper::LowestCommonAncestor(a, b);
   }
 
-  void SourceToDestinationVisualRectInternal(
-      const PropertyTreeState& source_state,
-      const PropertyTreeState& destination_state,
-      FloatRect& mapping_rect,
-      bool& success) {
-    FloatClipRect float_clip_rect(mapping_rect);
-    GeometryMapper::LocalToAncestorVisualRectInternal(
-        source_state, destination_state, float_clip_rect, success);
-    mapping_rect = float_clip_rect.Rect();
-  }
-
   void LocalToAncestorVisualRectInternal(
       const PropertyTreeState& local_state,
       const PropertyTreeState& ancestor_state,
@@ -69,15 +51,6 @@
     mapping_rect = float_clip_rect.Rect();
   }
 
-  void LocalToAncestorRectInternal(
-      const TransformPaintPropertyNode* local_transform_node,
-      const TransformPaintPropertyNode* ancestor_transform_node,
-      FloatRect& rect,
-      bool& success) {
-    GeometryMapper::LocalToAncestorRectInternal(
-        local_transform_node, ancestor_transform_node, rect, success);
-  }
-
  private:
 };
 
@@ -129,14 +102,14 @@
     EXPECT_EQ(has_radius, float_clip_rect.HasRadius());                        \
     EXPECT_CLIP_RECT_EQ(expectedClipInAncestorSpace, float_clip_rect);         \
     float_rect.SetRect(inputRect);                                             \
-    GeometryMapper::SourceToDestinationVisualRect(                             \
+    GeometryMapper::LocalToAncestorVisualRect(                                 \
         localPropertyTreeState, ancestorPropertyTreeState, float_rect);        \
     EXPECT_RECT_EQ(expectedVisualRect, float_rect.Rect());                     \
     EXPECT_EQ(has_radius, float_rect.HasRadius());                             \
     FloatRect test_mapped_rect = inputRect;                                    \
-    GeometryMapper::LocalToAncestorRect(localPropertyTreeState.Transform(),    \
-                                        ancestorPropertyTreeState.Transform(), \
-                                        test_mapped_rect);                     \
+    GeometryMapper::SourceToDestinationRect(                                   \
+        localPropertyTreeState.Transform(),                                    \
+        ancestorPropertyTreeState.Transform(), test_mapped_rect);              \
     EXPECT_RECT_EQ(expectedTransformedRect, test_mapped_rect);                 \
     test_mapped_rect = inputRect;                                              \
     GeometryMapper::SourceToDestinationRect(                                   \
@@ -145,11 +118,11 @@
     EXPECT_RECT_EQ(expectedTransformedRect, test_mapped_rect);                 \
     if (ancestorPropertyTreeState.Transform() !=                               \
         localPropertyTreeState.Transform()) {                                  \
-      const TransformationMatrix* transform_for_testing =                      \
-          GetTransform(localPropertyTreeState.Transform(),                     \
-                       ancestorPropertyTreeState.Transform());                 \
-      CHECK(transform_for_testing);                                            \
-      EXPECT_EQ(expectedTransformToAncestor, *transform_for_testing);          \
+      const TransformationMatrix& transform_for_testing =                      \
+          GeometryMapper::SourceToDestinationProjection(                       \
+              localPropertyTreeState.Transform(),                              \
+              ancestorPropertyTreeState.Transform());                          \
+      EXPECT_EQ(expectedTransformToAncestor, transform_for_testing);           \
     }                                                                          \
     if (ancestorPropertyTreeState.Clip() != localPropertyTreeState.Clip()) {   \
       const FloatClipRect* output_clip_for_testing =                           \
@@ -204,8 +177,8 @@
   CHECK_MAPPINGS(input, output, output, transform->Matrix(), FloatClipRect(),
                  local_state, PropertyTreeState::Root());
 
-  GeometryMapper::AncestorToLocalRect(TransformPaintPropertyNode::Root(),
-                                      local_state.Transform(), output);
+  GeometryMapper::SourceToDestinationRect(TransformPaintPropertyNode::Root(),
+                                          local_state.Transform(), output);
   EXPECT_RECT_EQ(input, output);
 }
 
@@ -271,11 +244,6 @@
   bool has_radius = false;
   CHECK_MAPPINGS(input, output, output, final, FloatClipRect(), local_state,
                  PropertyTreeState::Root());
-
-  // Check the cached matrix for the intermediate transform.
-  EXPECT_EQ(
-      rotate_transform,
-      *GetTransform(transform1.Get(), TransformPaintPropertyNode::Root()));
 }
 
 TEST_P(GeometryMapperTest, NestedTransformsFlattening) {
@@ -297,10 +265,11 @@
 
   FloatRect input(0, 0, 100, 100);
   rotate_transform.FlattenTo2d();
-  TransformationMatrix final = rotate_transform * inverse_rotate_transform;
-  FloatRect output = final.MapRect(input);
+  TransformationMatrix combined = rotate_transform * inverse_rotate_transform;
+  combined.FlattenTo2d();
+  FloatRect output = combined.MapRect(input);
   bool has_radius = false;
-  CHECK_MAPPINGS(input, output, output, final, FloatClipRect(), local_state,
+  CHECK_MAPPINGS(input, output, output, combined, FloatClipRect(), local_state,
                  PropertyTreeState::Root());
 }
 
@@ -329,10 +298,6 @@
   bool has_radius = false;
   CHECK_MAPPINGS(input, output, output, final, FloatClipRect(), local_state,
                  PropertyTreeState::Root());
-
-  // Check the cached matrix for the intermediate transform.
-  EXPECT_EQ(scale_transform, *GetTransform(transform1.Get(),
-                                           TransformPaintPropertyNode::Root()));
 }
 
 TEST_P(GeometryMapperTest, NestedTransformsIntermediateDestination) {
@@ -650,49 +615,26 @@
   PropertyTreeState transform2_state = PropertyTreeState::Root();
   transform2_state.SetTransform(transform2.Get());
 
-  bool success;
   FloatRect input(0, 0, 100, 100);
+  FloatClipRect result_clip(input);
+  GeometryMapper::LocalToAncestorVisualRect(transform1_state, transform2_state,
+                                            result_clip);
+  EXPECT_RECT_EQ(FloatRect(-100, 0, 100, 100), result_clip.Rect());
+
   FloatRect result = input;
-  LocalToAncestorVisualRectInternal(transform1_state, transform2_state, result,
-                                    success);
-  // Fails, because the transform2state is not an ancestor of transform1State.
-  EXPECT_FALSE(success);
-  EXPECT_RECT_EQ(input, result);
-
-  result = input;
-  LocalToAncestorRectInternal(transform1.Get(), transform2.Get(), result,
-                              success);
-  // Fails, because the transform2state is not an ancestor of transform1State.
-  EXPECT_FALSE(success);
-  EXPECT_RECT_EQ(input, result);
-
-  result = input;
-  LocalToAncestorVisualRectInternal(transform2_state, transform1_state, result,
-                                    success);
-  // Fails, because the transform1state is not an ancestor of transform2State.
-  EXPECT_FALSE(success);
-  EXPECT_RECT_EQ(input, result);
-
-  result = input;
-  LocalToAncestorRectInternal(transform2.Get(), transform1.Get(), result,
-                              success);
-  // Fails, because the transform1state is not an ancestor of transform2State.
-  EXPECT_FALSE(success);
-  EXPECT_RECT_EQ(input, result);
-
-  FloatRect expected =
-      rotate_transform2.Inverse().MapRect(rotate_transform1.MapRect(input));
-  result = input;
-  FloatClipRect float_clip_rect(result);
-  GeometryMapper::SourceToDestinationVisualRect(
-      transform1_state, transform2_state, float_clip_rect);
-  result = float_clip_rect.Rect();
-  EXPECT_RECT_EQ(expected, result);
-
-  result = input;
   GeometryMapper::SourceToDestinationRect(transform1.Get(), transform2.Get(),
                                           result);
-  EXPECT_RECT_EQ(expected, result);
+  EXPECT_RECT_EQ(FloatRect(-100, 0, 100, 100), result);
+
+  result_clip = FloatClipRect(input);
+  GeometryMapper::LocalToAncestorVisualRect(transform2_state, transform1_state,
+                                            result_clip);
+  EXPECT_RECT_EQ(FloatRect(0, -100, 100, 100), result_clip.Rect());
+
+  result = input;
+  GeometryMapper::SourceToDestinationRect(transform2.Get(), transform1.Get(),
+                                          result);
+  EXPECT_RECT_EQ(FloatRect(0, -100, 100, 100), result);
 }
 
 TEST_P(GeometryMapperTest, SiblingTransformsWithClip) {
@@ -712,7 +654,7 @@
 
   RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::Create(
       ClipPaintPropertyNode::Root(), transform2.Get(),
-      FloatRoundedRect(10, 10, 70, 70));
+      FloatRoundedRect(10, 20, 30, 40));
 
   PropertyTreeState transform1_state = PropertyTreeState::Root();
   transform1_state.SetTransform(transform1.Get());
@@ -722,46 +664,23 @@
 
   bool success;
   FloatRect input(0, 0, 100, 100);
-
-  // Test map from transform1State to transform2AndClipState.
-  FloatRect expected =
-      rotate_transform2.Inverse().MapRect(rotate_transform1.MapRect(input));
-
-  // sourceToDestinationVisualRect ignores clip from the common ancestor to
-  // destination.
   FloatRect result = input;
-  SourceToDestinationVisualRectInternal(
-      transform1_state, transform2_and_clip_state, result, success);
+  LocalToAncestorVisualRectInternal(transform1_state, transform2_and_clip_state,
+                                    result, success);
   // Fails, because the clip of the destination state is not an ancestor of the
-  // clip of the source state.
-  EXPECT_FALSE(success);
+  // clip of the source state. A known bug in SPv1 would make such query,
+  // in such case, no clips are applied.
+  if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
+    EXPECT_FALSE(success);
+  } else {
+    EXPECT_TRUE(success);
+    EXPECT_EQ(FloatRect(-100, 0, 100, 100), result);
+  }
 
-  // sourceToDestinationRect applies transforms only.
-  result = input;
-  GeometryMapper::SourceToDestinationRect(transform1.Get(), transform2.Get(),
-                                          result);
-  EXPECT_RECT_EQ(expected, result);
-
-  // Test map from transform2AndClipState to transform1State.
-  FloatRect expected_unclipped =
-      rotate_transform1.Inverse().MapRect(rotate_transform2.MapRect(input));
-  FloatRect expected_clipped = rotate_transform1.Inverse().MapRect(
-      rotate_transform2.MapRect(FloatRect(10, 10, 70, 70)));
-
-  // sourceToDestinationVisualRect ignores clip from the common ancestor to
-  // destination.
-  result = input;
-  FloatClipRect float_clip_rect(result);
-  GeometryMapper::SourceToDestinationVisualRect(
-      transform2_and_clip_state, transform1_state, float_clip_rect);
-  result = float_clip_rect.Rect();
-  EXPECT_RECT_EQ(expected_clipped, result);
-
-  // sourceToDestinationRect applies transforms only.
-  result = input;
-  GeometryMapper::SourceToDestinationRect(transform2.Get(), transform1.Get(),
-                                          result);
-  EXPECT_RECT_EQ(expected_unclipped, result);
+  FloatClipRect float_clip_rect(input);
+  GeometryMapper::LocalToAncestorVisualRect(transform2_and_clip_state,
+                                            transform1_state, float_clip_rect);
+  EXPECT_RECT_EQ(FloatRect(20, -40, 40, 30), float_clip_rect.Rect());
 }
 
 TEST_P(GeometryMapperTest, LowestCommonAncestor) {
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTransformCache.cpp b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTransformCache.cpp
index c94c50a5..1634d58 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTransformCache.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTransformCache.cpp
@@ -4,49 +4,70 @@
 
 #include "platform/graphics/paint/GeometryMapperTransformCache.h"
 
+#include "platform/graphics/paint/TransformPaintPropertyNode.h"
+
 namespace blink {
 
 // All transform caches invalidate themselves by tracking a local cache
 // generation, and invalidating their cache if their cache generation disagrees
-// with s_transformCacheGeneration.
-static unsigned g_transform_cache_generation = 0;
-
-GeometryMapperTransformCache::GeometryMapperTransformCache()
-    : cache_generation_(g_transform_cache_generation) {}
+// with s_global_generation.
+unsigned GeometryMapperTransformCache::s_global_generation;
 
 void GeometryMapperTransformCache::ClearCache() {
-  g_transform_cache_generation++;
+  s_global_generation++;
 }
 
-void GeometryMapperTransformCache::InvalidateCacheIfNeeded() {
-  if (cache_generation_ != g_transform_cache_generation) {
-    transform_cache_.clear();
-    cache_generation_ = g_transform_cache_generation;
-  }
+// Computes flatten(m) ^ -1, return true if the inversion succeeded.
+static bool InverseProjection(TransformationMatrix m,
+                              TransformationMatrix& out) {
+  m.FlattenTo2d();
+  if (!m.IsInvertible())
+    return false;
+  out = m.Inverse();
+  return true;
 }
 
-const TransformationMatrix* GeometryMapperTransformCache::GetCachedTransform(
-    const TransformPaintPropertyNode* ancestor_transform) {
-  InvalidateCacheIfNeeded();
-  for (const auto& entry : transform_cache_) {
-    if (entry.ancestor_node == ancestor_transform) {
-      return &entry.to_ancestor;
-    }
-  }
-  return nullptr;
-}
+void GeometryMapperTransformCache::Update(
+    const TransformPaintPropertyNode& node) {
+  DCHECK_NE(cache_generation_, s_global_generation);
+  cache_generation_ = s_global_generation;
 
-void GeometryMapperTransformCache::SetCachedTransform(
-    const TransformPaintPropertyNode* ancestor_transform,
-    const TransformationMatrix& matrix) {
-  InvalidateCacheIfNeeded();
-#if DCHECK_IS_ON()
-  for (const auto& entry : transform_cache_) {
-    if (entry.ancestor_node == ancestor_transform)
-      DCHECK(false);  // There should be no existing entry.
+  if (!node.Parent()) {
+    to_screen_.MakeIdentity();
+    to_screen_is_invertible_ = true;
+    projection_from_screen_.MakeIdentity();
+    projection_from_screen_is_valid_ = true;
+    plane_root_ = &node;
+    to_plane_root_.MakeIdentity();
+    from_plane_root_.MakeIdentity();
+    return;
   }
-#endif
-  transform_cache_.push_back(TransformCacheEntry(ancestor_transform, matrix));
+
+  const GeometryMapperTransformCache& parent =
+      node.Parent()->GetTransformCache();
+
+  TransformationMatrix local = node.Matrix();
+  local.ApplyTransformOrigin(node.Origin());
+
+  to_screen_ = parent.to_screen_;
+  if (node.FlattensInheritedTransform())
+    to_screen_.FlattenTo2d();
+  to_screen_.Multiply(local);
+  to_screen_is_invertible_ = to_screen_.IsInvertible();
+  projection_from_screen_is_valid_ =
+      InverseProjection(to_screen_, projection_from_screen_);
+
+  if (!local.IsFlat() || !local.IsInvertible()) {
+    plane_root_ = &node;
+    to_plane_root_.MakeIdentity();
+    from_plane_root_.MakeIdentity();
+  } else {  // (local.IsFlat() && local.IsInvertible())
+    plane_root_ = parent.plane_root_;
+    to_plane_root_ = parent.to_plane_root_;
+    to_plane_root_.Multiply(local);
+    from_plane_root_ = local.Inverse();
+    from_plane_root_.Multiply(parent.from_plane_root_);
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTransformCache.h b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTransformCache.h
index 398fc0e..af40d94b 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTransformCache.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTransformCache.h
@@ -14,44 +14,108 @@
 
 class TransformPaintPropertyNode;
 
-// A GeometryMapperTransformCache hangs off a TransformPaintPropertyNode. It
-// stores cached "transformed rects" (See GeometryMapper.h) from that node in
-// ancestor spaces.
+// A GeometryMapperTransformCache hangs off a TransformPaintPropertyNode.
+// It stores useful intermediate results such as screen matrix for geometry
+// queries.
 class PLATFORM_EXPORT GeometryMapperTransformCache {
   USING_FAST_MALLOC(GeometryMapperTransformCache);
-
  public:
-  GeometryMapperTransformCache();
-
-  // Returns the transformed rect (see GeometryMapper.h) of |this| in the
-  // space of |ancestorTransform|, if there is one cached. Otherwise returns
-  // null.
-  //
-  // These transforms are not flattened to 2d.
-  const TransformationMatrix* GetCachedTransform(
-      const TransformPaintPropertyNode* ancestor_transform);
-
-  // Stores the "transformed rect" of |this| in the space of |ancestors|,
-  // into a local cache.
-  void SetCachedTransform(const TransformPaintPropertyNode* ancestor_transform,
-                          const TransformationMatrix& to_ancestor);
+  GeometryMapperTransformCache() = default;
 
   static void ClearCache();
 
+  void UpdateIfNeeded(const TransformPaintPropertyNode& node) {
+    if (cache_generation_ != s_global_generation)
+      Update(node);
+    DCHECK_EQ(cache_generation_, s_global_generation);
+  }
+
+  const TransformationMatrix& to_screen() const { return to_screen_; }
+  bool to_screen_is_invertible() const { return to_screen_is_invertible_; }
+
+  const TransformationMatrix& projection_from_screen() const {
+    return projection_from_screen_;
+  }
+  bool projection_from_screen_is_valid() const {
+    return projection_from_screen_is_valid_;
+  }
+
+  const TransformationMatrix& to_plane_root() const { return to_plane_root_; }
+  const TransformationMatrix& from_plane_root() const {
+    return from_plane_root_;
+  }
+  const TransformPaintPropertyNode* plane_root() const { return plane_root_; }
+
  private:
-  struct TransformCacheEntry {
-    const TransformPaintPropertyNode* ancestor_node;
-    TransformationMatrix to_ancestor;
-    TransformCacheEntry(const TransformPaintPropertyNode* ancestor_node_arg,
-                        const TransformationMatrix& to_ancestor_arg)
-        : ancestor_node(ancestor_node_arg), to_ancestor(to_ancestor_arg) {}
-  };
+  void Update(const TransformPaintPropertyNode&);
 
-  void InvalidateCacheIfNeeded();
+  static unsigned s_global_generation;
 
-  Vector<TransformCacheEntry> transform_cache_;
-  unsigned cache_generation_;
-
+  // The cached values here can be categorized in two logical groups:
+  //
+  // [ Screen Transform ]
+  // to_screen : The screen matrix of the node, as defined by:
+  //   to_screen = (flattens_inherited_transform ?
+  //       flatten(parent.to_screen) : parent.to_screen) * local
+  // to_screen_is_invertible : Whether to_screen is invertible.
+  // projection_from_screen : Back projection from screen.
+  //   projection_from_screen = flatten(to_screen) ^ -1
+  //   Undefined if the inverse projection doesn't exist.
+  //   Guaranteed to be flat.
+  // projection_from_screen_is_valid : Whether projection_from_screen
+  //   is defined.
+  //
+  // [ Plane Root Transform ]
+  // plane_root : The oldest ancestor node such that every intermediate node
+  //   in the ancestor chain has a flat and invertible local matrix. In other
+  //   words, a node inherits its parent's plane_root if its local matrix is
+  //   flat and invertible. Otherwise, it becomes its own plane root.
+  //   For example:
+  //   <xfrm id="A" matrix="rotateY(10deg)">
+  //     <xfrm id="B" flatten_inherited matrix="translateX(10px)"/>
+  //     <xfrm id="C" matrix="scaleX(0)">
+  //       <xfrm id="D" matrix="scaleX(2)"/>
+  //       <xfrm id="E" matrix="rotate(30deg)"/>
+  //     </xfrm>
+  //     <xfrm id="F" matrix="scaleZ(0)"/>
+  //   </xfrm>
+  //   A is the plane root of itself because its local matrix is 3D.
+  //   B's plane root is A because its local matrix is flat.
+  //   C is the plane root of itself because its local matrix is non-invertible.
+  //   D and E's plane root is C because their local matrix is flat.
+  //   F is the plane root of itself because its local matrix is 3D and
+  //     non-invertible.
+  // to_plane_root : The accumulated matrix between this node and plane_root.
+  //   to_plane_root = (plane_root == this) ? I : parent.to_plane_root * local
+  //   Guaranteed to be flat.
+  // from_plane_root :
+  //   from_plane_root = to_plane_root ^ -1
+  //   Guaranteed to exist because each local matrices are invertible.
+  //   Guaranteed to be flat.
+  // An important invariant is that
+  //   flatten(to_screen) = flatten(plane_root.to_screen) * to_plane_root
+  //   Proof by induction:
+  //   If plane_root == this,
+  //     flatten(plane_root.to_screen) * to_plane_root = flatten(to_screen) * I
+  //     = flatten(to_screen)
+  //   Otherwise,
+  //     flatten(to_screen) = flatten((flattens_inherited_transform ?
+  //         flatten(parent.to_screen) : parent.to_screen) * local)
+  //     Because local is known to be flat,
+  //     = flatten((flattens_inherited_transform ?
+  //         flatten(parent.to_screen) : parent.to_screen) * flatten(local))
+  //     Then by flatten lemma (https://goo.gl/DNKyOc),
+  //     = flatten(parent.to_screen) * local
+  //     = flatten(parent.plane_root.to_screen) * parent.to_plane_root * local
+  //     = flatten(plane_root.to_screen) * to_plane_root
+  TransformationMatrix to_screen_;
+  TransformationMatrix projection_from_screen_;
+  TransformationMatrix to_plane_root_;
+  TransformationMatrix from_plane_root_;
+  const TransformPaintPropertyNode* plane_root_ = nullptr;
+  unsigned cache_generation_ = s_global_generation - 1;
+  unsigned to_screen_is_invertible_ : 1;
+  unsigned projection_from_screen_is_valid_ : 1;
   DISALLOW_COPY_AND_ASSIGN(GeometryMapperTransformCache);
 };
 
diff --git a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
index 3ac42f2..5b39615 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/TransformPaintPropertyNode.h
@@ -215,16 +215,13 @@
   // For access to getTransformCache() and setCachedTransform.
   friend class GeometryMapper;
   friend class GeometryMapperTest;
+  friend class GeometryMapperTransformCache;
 
-  GeometryMapperTransformCache& GetTransformCache() const {
-    return const_cast<TransformPaintPropertyNode*>(this)->GetTransformCache();
-  }
-
-  GeometryMapperTransformCache& GetTransformCache() {
-    if (!geometry_mapper_transform_cache_)
-      geometry_mapper_transform_cache_.reset(
-          new GeometryMapperTransformCache());
-    return *geometry_mapper_transform_cache_.get();
+  const GeometryMapperTransformCache& GetTransformCache() const {
+    if (!transform_cache_)
+      transform_cache_.reset(new GeometryMapperTransformCache);
+    transform_cache_->UpdateIfNeeded(*this);
+    return *transform_cache_;
   }
 
   RefPtr<const TransformPaintPropertyNode> parent_;
@@ -236,8 +233,7 @@
   CompositorElementId compositor_element_id_;
   RefPtr<ScrollPaintPropertyNode> scroll_;
 
-  std::unique_ptr<GeometryMapperTransformCache>
-      geometry_mapper_transform_cache_;
+  mutable std::unique_ptr<GeometryMapperTransformCache> transform_cache_;
 };
 
 // Redeclared here to avoid ODR issues.
diff --git a/third_party/WebKit/Source/platform/testing/UnitTestHelpers.cpp b/third_party/WebKit/Source/platform/testing/UnitTestHelpers.cpp
index d0e10b6..2f6d1541 100644
--- a/third_party/WebKit/Source/platform/testing/UnitTestHelpers.cpp
+++ b/third_party/WebKit/Source/platform/testing/UnitTestHelpers.cpp
@@ -89,6 +89,12 @@
   return FilePathToWebString(BlinkRootFilePath());
 }
 
+String ExecutableDir() {
+  base::FilePath path;
+  base::PathService::Get(base::DIR_EXE, &path);
+  return FilePathToWebString(base::MakeAbsoluteFilePath(path));
+}
+
 String WebTestDataPath(const String& relative_path) {
   return FilePathToWebString(
       BlinkRootFilePath()
diff --git a/third_party/WebKit/Source/platform/testing/UnitTestHelpers.h b/third_party/WebKit/Source/platform/testing/UnitTestHelpers.h
index 3b94f961..6f18cad 100644
--- a/third_party/WebKit/Source/platform/testing/UnitTestHelpers.h
+++ b/third_party/WebKit/Source/platform/testing/UnitTestHelpers.h
@@ -52,6 +52,9 @@
 // /src/third_party/WebKit.
 String BlinkRootDir();
 
+// Returns directory containing the current executable as absolute path.
+String ExecutableDir();
+
 // Returns test data absolute path for webkit_unit_tests, i.e.
 // <blinkRootDir>/Source/web/tests/data/<relativePath>.
 // It returns the top web test directory if |relativePath| was not specified.
diff --git a/third_party/WebKit/Source/platform/transforms/TransformationMatrix.h b/third_party/WebKit/Source/platform/transforms/TransformationMatrix.h
index 6e8e2b94..8975c7c 100644
--- a/third_party/WebKit/Source/platform/transforms/TransformationMatrix.h
+++ b/third_party/WebKit/Source/platform/transforms/TransformationMatrix.h
@@ -439,6 +439,12 @@
     return result;
   }
 
+  bool IsFlat() const {
+    return matrix_[0][2] == 0.f && matrix_[1][2] == 0.f &&
+           matrix_[2][0] == 0.f && matrix_[2][1] == 0.f &&
+           matrix_[2][2] == 1.f && matrix_[2][3] == 0.f && matrix_[3][2] == 0.f;
+  }
+
   bool IsIdentityOrTranslation() const {
     return matrix_[0][0] == 1 && matrix_[0][1] == 0 && matrix_[0][2] == 0 &&
            matrix_[0][3] == 0 && matrix_[1][0] == 0 && matrix_[1][1] == 1 &&
diff --git a/third_party/WebKit/Source/web/tests/BrowserControlsTest.cpp b/third_party/WebKit/Source/web/tests/BrowserControlsTest.cpp
index e54a7ddb..5ac1145 100644
--- a/third_party/WebKit/Source/web/tests/BrowserControlsTest.cpp
+++ b/third_party/WebKit/Source/web/tests/BrowserControlsTest.cpp
@@ -123,7 +123,7 @@
 
   Element* GetElementById(const WebString& id) {
     return static_cast<Element*>(
-        GetWebView()->MainFrame()->GetDocument().GetElementById(id));
+        GetWebView()->MainFrameImpl()->GetDocument().GetElementById(id));
   }
 
   WebViewBase* GetWebView() const { return helper_.WebView(); }
diff --git a/third_party/WebKit/Source/web/tests/TouchActionTest.cpp b/third_party/WebKit/Source/web/tests/TouchActionTest.cpp
index e8dce12..1492ecd2 100644
--- a/third_party/WebKit/Source/web/tests/TouchActionTest.cpp
+++ b/third_party/WebKit/Source/web/tests/TouchActionTest.cpp
@@ -38,6 +38,7 @@
 #include "core/frame/FrameTestHelpers.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/LocalFrameView.h"
+#include "core/frame/WebLocalFrameBase.h"
 #include "core/html/HTMLIFrameElement.h"
 #include "core/input/EventHandler.h"
 #include "core/layout/HitTestResult.h"
@@ -116,7 +117,7 @@
   void RunShadowDOMTest(std::string file);
   void RunIFrameTest(std::string file);
   void SendTouchEvent(WebView*, WebInputEvent::Type, IntPoint client_point);
-  WebView* SetupTest(std::string file, TouchActionTrackingWebWidgetClient&);
+  WebViewBase* SetupTest(std::string file, TouchActionTrackingWebWidgetClient&);
   void RunTestOnTree(ContainerNode* root,
                      WebView*,
                      TouchActionTrackingWebWidgetClient&);
@@ -138,10 +139,10 @@
   // turn them into persistent, stack allocated references. This
   // workaround is sufficient to handle this artificial test
   // scenario.
-  WebView* web_view = SetupTest(file, client);
+  WebViewBase* web_view = SetupTest(file, client);
 
   Persistent<Document> document =
-      static_cast<Document*>(web_view->MainFrame()->GetDocument());
+      static_cast<Document*>(web_view->MainFrameImpl()->GetDocument());
   RunTestOnTree(document.Get(), web_view, client);
 
   // Explicitly reset to break dependency on locally scoped client.
@@ -151,14 +152,14 @@
 void TouchActionTest::RunShadowDOMTest(std::string file) {
   TouchActionTrackingWebWidgetClient client;
 
-  WebView* web_view = SetupTest(file, client);
+  WebViewBase* web_view = SetupTest(file, client);
 
   DummyExceptionStateForTesting es;
 
   // Oilpan: see runTouchActionTest() comment why these are persistent
   // references.
   Persistent<Document> document =
-      static_cast<Document*>(web_view->MainFrame()->GetDocument());
+      static_cast<Document*>(web_view->MainFrameImpl()->GetDocument());
   Persistent<StaticElementList> host_nodes =
       document->QuerySelectorAll("[shadow-host]", es);
   ASSERT_FALSE(es.HadException());
@@ -179,7 +180,7 @@
 void TouchActionTest::RunIFrameTest(std::string file) {
   TouchActionTrackingWebWidgetClient client;
 
-  WebView* web_view = SetupTest(file, client);
+  WebViewBase* web_view = SetupTest(file, client);
   WebFrame* cur_frame = web_view->MainFrame()->FirstChild();
   ASSERT_TRUE(cur_frame);
 
@@ -187,7 +188,7 @@
     // Oilpan: see runTouchActionTest() comment why these are persistent
     // references.
     Persistent<Document> content_doc =
-        static_cast<Document*>(cur_frame->GetDocument());
+        static_cast<Document*>(cur_frame->ToWebLocalFrame()->GetDocument());
     RunTestOnTree(content_doc.Get(), web_view, client);
   }
 
@@ -195,14 +196,14 @@
   web_view_helper_.Reset();
 }
 
-WebView* TouchActionTest::SetupTest(
+WebViewBase* TouchActionTest::SetupTest(
     std::string file,
     TouchActionTrackingWebWidgetClient& client) {
   URLTestHelpers::RegisterMockedURLLoadFromBase(WebString::FromUTF8(base_url_),
                                                 testing::WebTestDataPath(),
                                                 WebString::FromUTF8(file));
   // Note that JavaScript must be enabled for shadow DOM tests.
-  WebView* web_view = web_view_helper_.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper_.InitializeAndLoad(
       base_url_ + file, nullptr, nullptr, &client);
 
   // Set size to enable hit testing, and avoid line wrapping for consistency
@@ -212,7 +213,7 @@
   // Scroll to verify the code properly transforms windows to client co-ords.
   const int kScrollOffset = 100;
   Document* document =
-      static_cast<Document*>(web_view->MainFrame()->GetDocument());
+      static_cast<Document*>(web_view->MainFrameImpl()->GetDocument());
   document->GetFrame()->View()->LayoutViewportScrollableArea()->SetScrollOffset(
       ScrollOffset(0, kScrollOffset), kProgrammaticScroll);
 
diff --git a/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp b/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
index 76963e3..fee0182 100644
--- a/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
+++ b/third_party/WebKit/Source/web/tests/VisualViewportTest.cpp
@@ -4,6 +4,8 @@
 
 #include "core/frame/VisualViewport.h"
 
+#include <memory>
+
 #include "core/dom/Document.h"
 #include "core/frame/BrowserControls.h"
 #include "core/frame/FrameTestHelpers.h"
@@ -1876,7 +1878,7 @@
   WebViewImpl()->Resize(IntSize(500, 500));
   WebViewImpl()->UpdateAllLifecyclePhases();
 
-  WebDocument web_doc = WebViewImpl()->MainFrame()->GetDocument();
+  WebDocument web_doc = WebViewImpl()->MainFrameImpl()->GetDocument();
   LocalFrameView& frame_view = *WebViewImpl()->MainFrameImpl()->GetFrameView();
 
   WebViewImpl()->SetPageScaleFactor(2);
diff --git a/third_party/WebKit/Source/web/tests/WebDocumentTest.cpp b/third_party/WebKit/Source/web/tests/WebDocumentTest.cpp
index 8f431d6..618cce9 100644
--- a/third_party/WebKit/Source/web/tests/WebDocumentTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebDocumentTest.cpp
@@ -4,12 +4,15 @@
 
 #include "public/web/WebDocument.h"
 
+#include <string>
+
 #include "core/CSSPropertyNames.h"
 #include "core/HTMLNames.h"
 #include "core/dom/NodeComputedStyle.h"
 #include "core/dom/StyleEngine.h"
 #include "core/frame/FrameTestHelpers.h"
 #include "core/frame/LocalFrame.h"
+#include "core/frame/WebLocalFrameBase.h"
 #include "core/html/HTMLElement.h"
 #include "core/html/HTMLLinkElement.h"
 #include "core/page/Page.h"
@@ -57,7 +60,7 @@
 }
 
 WebDocument WebDocumentTest::TopWebDocument() const {
-  return web_view_helper_.WebView()->MainFrame()->GetDocument();
+  return web_view_helper_.WebView()->MainFrameImpl()->GetDocument();
 }
 
 TEST_F(WebDocumentTest, InsertAndRemoveStyleSheet) {
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index fb67c0a..d9c4025 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -32,8 +32,10 @@
 
 #include <stdarg.h>
 
+#include <limits>
 #include <map>
 #include <memory>
+#include <set>
 
 #include "SkBitmap.h"
 #include "SkCanvas.h"
@@ -622,7 +624,7 @@
   web_view_helper.InitializeAndLoad(base_url_ + "form.html");
 
   WebVector<WebFormElement> forms;
-  web_view_helper.WebView()->MainFrame()->GetDocument().Forms(forms);
+  web_view_helper.WebView()->MainFrameImpl()->GetDocument().Forms(forms);
   web_view_helper.Reset();
 
   EXPECT_EQ(forms.size(), 1U);
@@ -1010,7 +1012,8 @@
   // Send a message with the correct origin.
   WebSecurityOrigin correct_origin(
       WebSecurityOrigin::Create(ToKURL(base_url_)));
-  WebDocument document = web_view_helper.WebView()->MainFrame()->GetDocument();
+  WebDocument document =
+      web_view_helper.WebView()->MainFrameImpl()->GetDocument();
   WebSerializedScriptValue data(WebSerializedScriptValue::CreateInvalid());
   WebDOMMessageEvent message(data, "http://origin.com");
   web_view_helper.WebView()
@@ -3913,7 +3916,7 @@
   web_view_helper.WebView()->AdvanceFocus(false);
   // Set the caret to the end of the input box.
   web_view_helper.WebView()
-      ->MainFrame()
+      ->MainFrameImpl()
       ->GetDocument()
       .GetElementById("EditBoxWithText")
       .To<WebInputElement>()
@@ -4010,7 +4013,7 @@
   web_view_helper.WebView()->AdvanceFocus(false);
   // Set the caret to the begining of the input box.
   web_view_helper.WebView()
-      ->MainFrame()
+      ->MainFrameImpl()
       ->GetDocument()
       .GetElementById("EditBoxWithText")
       .To<WebInputElement>()
@@ -5310,7 +5313,7 @@
   return WebPoint(rect.x + rect.width - 1, rect.y + rect.height - 1);
 }
 
-static WebRect ElementBounds(WebFrame* frame, const WebString& id) {
+static WebRect ElementBounds(WebLocalFrame* frame, const WebString& id) {
   return frame->GetDocument().GetElementById(id).BoundsInViewport();
 }
 
@@ -6702,9 +6705,8 @@
   StubbornTextCheckClient() : completion_(0) {}
   virtual ~StubbornTextCheckClient() {}
 
-  virtual void RequestCheckingOfText(
-      const WebString&,
-      WebTextCheckingCompletion* completion) override {
+  void RequestCheckingOfText(const WebString&,
+                             WebTextCheckingCompletion* completion) override {
     completion_ = completion;
   }
 
@@ -7173,7 +7175,7 @@
   FrameTestHelpers::WebViewHelper web_view_helper;
   web_view_helper.InitializeAndLoad(base_url_ + "first_party_redirect.html");
   EXPECT_TRUE(web_view_helper.WebView()
-                  ->MainFrame()
+                  ->MainFrameImpl()
                   ->GetDocument()
                   .FirstPartyForCookies() == redirect_url);
 }
@@ -7216,12 +7218,12 @@
 
 class TestNewWindowWebViewClient : public FrameTestHelpers::TestWebViewClient {
  public:
-  virtual WebView* CreateView(WebLocalFrame*,
-                              const WebURLRequest&,
-                              const WebWindowFeatures&,
-                              const WebString&,
-                              WebNavigationPolicy,
-                              bool) override {
+  WebView* CreateView(WebLocalFrame*,
+                      const WebURLRequest&,
+                      const WebWindowFeatures&,
+                      const WebString&,
+                      WebNavigationPolicy,
+                      bool) override {
     EXPECT_TRUE(false);
     return 0;
   }
@@ -7700,8 +7702,8 @@
   frame->ExecuteScript(WebScriptSource(WebString::FromUTF8(
       "document.body.appendChild(document.createElement('iframe'))")));
 
-  WebLocalFrame* iframe = frame->FirstChild()->ToWebLocalFrame();
-  ASSERT_EQ(&client.ChildClient(), ToWebLocalFrameBase(iframe)->Client());
+  WebLocalFrameBase* iframe = ToWebLocalFrameBase(frame->FirstChild());
+  ASSERT_EQ(&client.ChildClient(), iframe->Client());
 
   std::string url1 = base_url_ + "history.html";
   FrameTestHelpers::LoadFrame(iframe, url1);
@@ -9454,8 +9456,7 @@
 
 class CommitTypeWebFrameClient : public FrameTestHelpers::TestWebFrameClient {
  public:
-  explicit CommitTypeWebFrameClient()
-      : history_commit_type_(kWebHistoryInertCommit) {}
+  CommitTypeWebFrameClient() : history_commit_type_(kWebHistoryInertCommit) {}
 
   void DidCommitProvisionalLoad(
       const WebHistoryItem&,
@@ -10528,7 +10529,7 @@
           ->ReadRawImage(WebClipboard::Buffer());
 
   EXPECT_EQ(SkColorSetARGB(255, 255, 0, 0), image.GetSkBitmap().getColor(0, 0));
-};
+}
 
 TEST_F(WebFrameTest, CopyImageAtWithPinchZoom) {
   std::string url = base_url_ + "canvas-copy-image.html";
@@ -10555,7 +10556,7 @@
           ->ReadRawImage(WebClipboard::Buffer());
 
   EXPECT_EQ(SkColorSetARGB(255, 255, 0, 0), image.GetSkBitmap().getColor(0, 0));
-};
+}
 
 TEST_F(WebFrameTest, CopyImageWithImageMap) {
   SaveImageFromDataURLWebFrameClient client;
@@ -11856,7 +11857,7 @@
 
 class ContextMenuWebFrameClient : public FrameTestHelpers::TestWebFrameClient {
  public:
-  ContextMenuWebFrameClient(){};
+  ContextMenuWebFrameClient() {}
   // WebFrameClient methods
   void ShowContextMenu(const WebContextMenuData& data) override {
     menu_data_ = data;
@@ -11947,7 +11948,7 @@
 
 class TestFallbackWebFrameClient : public FrameTestHelpers::TestWebFrameClient {
  public:
-  explicit TestFallbackWebFrameClient() : child_client_(nullptr) {}
+  TestFallbackWebFrameClient() : child_client_(nullptr) {}
 
   void SetChildWebFrameClient(TestFallbackWebFrameClient* client) {
     child_client_ = client;
diff --git a/third_party/WebKit/Source/web/tests/WebPluginContainerTest.cpp b/third_party/WebKit/Source/web/tests/WebPluginContainerTest.cpp
index acf9098..7b311255 100644
--- a/third_party/WebKit/Source/web/tests/WebPluginContainerTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebPluginContainerTest.cpp
@@ -166,9 +166,10 @@
   test_client_->OnPrintPage();
 }
 
-WebPluginContainer* GetWebPluginContainer(WebView* web_view,
+WebPluginContainer* GetWebPluginContainer(WebViewBase* web_view,
                                           const WebString& id) {
-  WebElement element = web_view->MainFrame()->GetDocument().GetElementById(id);
+  WebElement element =
+      web_view->MainFrameImpl()->GetDocument().GetElementById(id);
   return element.PluginContainer();
 }
 
@@ -179,7 +180,7 @@
   TestPluginWebFrameClient
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -218,12 +219,12 @@
   TestPluginWebFrameClient
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "test.pdf", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->UpdateAllLifecyclePhases();
 
-  WebDocument document = web_view->MainFrame()->GetDocument();
+  WebDocument document = web_view->MainFrameImpl()->GetDocument();
   EXPECT_TRUE(document.IsPluginDocument());
   WebPluginContainer* plugin_container =
       GetWebPluginContainer(web_view, "plugin");
@@ -237,13 +238,14 @@
   TestPluginWebFrameClient
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "iframe_pdf.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->UpdateAllLifecyclePhases();
 
-  WebDocument document = web_view->MainFrame()->GetDocument();
-  WebFrame* iframe = web_view->MainFrame()->FirstChild();
+  WebDocument document = web_view->MainFrameImpl()->GetDocument();
+  WebLocalFrame* iframe =
+      web_view->MainFrame()->FirstChild()->ToWebLocalFrame();
   EXPECT_TRUE(iframe->GetDocument().IsPluginDocument());
   WebPluginContainer* plugin_container =
       iframe->GetDocument().GetElementById("plugin").PluginContainer();
@@ -305,7 +307,7 @@
   TestPluginWebFrameClient
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -343,7 +345,7 @@
   TestPluginWebFrameClient
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -351,7 +353,7 @@
   web_view->UpdateAllLifecyclePhases();
   RunPendingTasks();
 
-  web_view->MainFrame()
+  web_view->MainFrameImpl()
       ->GetDocument()
       .Unwrap<Document>()
       ->body()
@@ -367,7 +369,7 @@
   TestPluginWebFrameClient
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -398,7 +400,7 @@
   web_view->ClearFocusedElement();
   // 3) Copy should still operate on the context node, even though the focus had
   //    shifted.
-  EXPECT_TRUE(web_view->MainFrame()->ToWebLocalFrame()->ExecuteCommand("Copy"));
+  EXPECT_TRUE(web_view->MainFrameImpl()->ExecuteCommand("Copy"));
   EXPECT_EQ(WebString("x"), Platform::Current()->Clipboard()->ReadPlainText(
                                 WebClipboard::Buffer()));
 }
@@ -410,7 +412,7 @@
   TestPluginWebFrameClient
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -419,7 +421,7 @@
   RunPendingTasks();
 
   WebElement plugin_container_one_element =
-      web_view->MainFrame()->GetDocument().GetElementById(
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
           WebString::FromUTF8("translated-plugin"));
   WebInputEvent::Modifiers modifier_key = static_cast<WebInputEvent::Modifiers>(
       WebInputEvent::kControlKey | WebInputEvent::kNumLockOn |
@@ -507,7 +509,7 @@
   CustomPluginWebFrameClient<EventTestPlugin>
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -516,7 +518,7 @@
   RunPendingTasks();
 
   WebElement plugin_container_one_element =
-      web_view->MainFrame()->GetDocument().GetElementById(
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
           WebString::FromUTF8("translated-plugin"));
   WebPlugin* plugin = static_cast<WebPluginContainerBase*>(
                           plugin_container_one_element.PluginContainer())
@@ -556,7 +558,7 @@
   CustomPluginWebFrameClient<EventTestPlugin>
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -565,7 +567,7 @@
   RunPendingTasks();
 
   WebElement plugin_container_one_element =
-      web_view->MainFrame()->GetDocument().GetElementById(
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
           WebString::FromUTF8("translated-plugin"));
   WebPlugin* plugin = static_cast<WebPluginContainerBase*>(
                           plugin_container_one_element.PluginContainer())
@@ -592,7 +594,7 @@
   CustomPluginWebFrameClient<EventTestPlugin>
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -604,7 +606,7 @@
   RunPendingTasks();
 
   WebElement plugin_container_one_element =
-      web_view->MainFrame()->GetDocument().GetElementById(
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
           WebString::FromUTF8("scrolled-plugin"));
   plugin_container_one_element.PluginContainer()->RequestTouchEventType(
       WebPluginContainer::kTouchEventRequestTypeRaw);
@@ -634,7 +636,7 @@
   CustomPluginWebFrameClient<EventTestPlugin>
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -646,7 +648,7 @@
   RunPendingTasks();
 
   WebElement plugin_container_one_element =
-      web_view->MainFrame()->GetDocument().GetElementById(
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
           WebString::FromUTF8("scrolled-plugin"));
   plugin_container_one_element.PluginContainer()->RequestTouchEventType(
       WebPluginContainer::kTouchEventRequestTypeRawLowLatency);
@@ -716,7 +718,7 @@
   CustomPluginWebFrameClient<EventTestPlugin>
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -728,7 +730,7 @@
   RunPendingTasks();
 
   WebElement plugin_container_one_element =
-      web_view->MainFrame()->GetDocument().GetElementById(
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
           WebString::FromUTF8("scrolled-plugin"));
   plugin_container_one_element.PluginContainer()->RequestTouchEventType(
       WebPluginContainer::kTouchEventRequestTypeRaw);
@@ -757,7 +759,7 @@
   CustomPluginWebFrameClient<EventTestPlugin>
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -769,7 +771,7 @@
   RunPendingTasks();
 
   WebElement plugin_container_one_element =
-      web_view->MainFrame()->GetDocument().GetElementById(
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
           WebString::FromUTF8("scrolled-plugin"));
   plugin_container_one_element.PluginContainer()->RequestTouchEventType(
       WebPluginContainer::kTouchEventRequestTypeRaw);
@@ -797,7 +799,7 @@
   CustomPluginWebFrameClient<EventTestPlugin>
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -808,7 +810,7 @@
   RunPendingTasks();
 
   WebElement plugin_container_one_element =
-      web_view->MainFrame()->GetDocument().GetElementById(
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
           WebString::FromUTF8("scrolled-plugin"));
   plugin_container_one_element.PluginContainer()->RequestTouchEventType(
       WebPluginContainer::kTouchEventRequestTypeRaw);
@@ -838,7 +840,7 @@
   CustomPluginWebFrameClient<EventTestPlugin>
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -849,7 +851,7 @@
   RunPendingTasks();
 
   WebElement plugin_container_one_element =
-      web_view->MainFrame()->GetDocument().GetElementById(
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
           WebString::FromUTF8("scrolled-plugin"));
   plugin_container_one_element.PluginContainer()->RequestTouchEventType(
       WebPluginContainer::kTouchEventRequestTypeRaw);
@@ -880,7 +882,7 @@
   CustomPluginWebFrameClient<EventTestPlugin>
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -891,7 +893,7 @@
   RunPendingTasks();
 
   WebElement plugin_container_one_element =
-      web_view->MainFrame()->GetDocument().GetElementById(
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
           WebString::FromUTF8("scrolled-plugin"));
   plugin_container_one_element.PluginContainer()->RequestTouchEventType(
       WebPluginContainer::kTouchEventRequestTypeRaw);
@@ -925,7 +927,7 @@
   TestPluginWebFrameClient
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -971,9 +973,11 @@
   web_view->UpdateAllLifecyclePhases();
   RunPendingTasks();
 
-  WebElement plugin_element =
-      web_view->MainFrame()->FirstChild()->GetDocument().GetElementById(
-          "translated-plugin");
+  WebElement plugin_element = web_view->MainFrame()
+                                  ->FirstChild()
+                                  ->ToWebLocalFrame()
+                                  ->GetDocument()
+                                  .GetElementById("translated-plugin");
   WebPluginContainerBase* plugin_container_impl =
       ToWebPluginContainerBase(plugin_element.PluginContainer());
 
@@ -996,7 +1000,7 @@
   TestPluginWebFrameClient
       plugin_web_frame_client;  // Must outlive webViewHelper.
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -1005,7 +1009,7 @@
   RunPendingTasks();
 
   WebElement plugin_element =
-      web_view->MainFrame()->GetDocument().GetElementById(
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
           "subpixel-positioned-plugin");
   WebPluginContainerBase* plugin_container_impl =
       ToWebPluginContainerBase(plugin_element.PluginContainer());
@@ -1045,7 +1049,7 @@
   // The client must outlive WebViewHelper.
   CustomPluginWebFrameClient<TopmostPlugin> plugin_web_frame_client;
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin_container.html", &plugin_web_frame_client);
   DCHECK(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -1105,7 +1109,7 @@
   RegisterMockedURL("plugin.html");
   CustomPluginWebFrameClient<CompositedPlugin> web_frame_client;
   FrameTestHelpers::WebViewHelper web_view_helper;
-  WebView* web_view = web_view_helper.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper.InitializeAndLoad(
       base_url_ + "plugin.html", &web_frame_client);
   ASSERT_TRUE(web_view);
   web_view->GetSettings()->SetPluginsEnabled(true);
@@ -1156,7 +1160,7 @@
   RunPendingTasks();
 
   WebElement plugin_container_one_element =
-      web_view->MainFrame()->GetDocument().GetElementById(
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
           WebString::FromUTF8("translated-plugin"));
   plugin_container_one_element.PluginContainer()->SetWantsWheelEvents(true);
 
diff --git a/third_party/WebKit/Source/web/tests/WebSearchableFormDataTest.cpp b/third_party/WebKit/Source/web/tests/WebSearchableFormDataTest.cpp
index 94003ce..bbb959a 100644
--- a/third_party/WebKit/Source/web/tests/WebSearchableFormDataTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebSearchableFormDataTest.cpp
@@ -30,13 +30,17 @@
 
 #include "public/web/WebSearchableFormData.h"
 
+#include <string>
+
 #include "core/frame/FrameTestHelpers.h"
+#include "core/frame/WebLocalFrameBase.h"
 #include "platform/testing/URLTestHelpers.h"
 #include "platform/testing/UnitTestHelpers.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebURLLoaderMockFactory.h"
 #include "public/web/WebDocument.h"
 #include "public/web/WebFrame.h"
+#include "public/web/WebLocalFrame.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -68,11 +72,11 @@
 TEST_F(WebSearchableFormDataTest, HttpSearchString) {
   std::string base_url("http://www.test.com/");
   RegisterMockedURLLoadFromBaseURL(base_url, "search_form_http.html");
-  WebView* web_view =
+  WebViewBase* web_view =
       web_view_helper_.InitializeAndLoad(base_url + "search_form_http.html");
 
   WebVector<WebFormElement> forms;
-  web_view->MainFrame()->GetDocument().Forms(forms);
+  web_view->MainFrameImpl()->GetDocument().Forms(forms);
 
   EXPECT_EQ(forms.size(), 1U);
 
@@ -84,11 +88,11 @@
 TEST_F(WebSearchableFormDataTest, HttpsSearchString) {
   std::string base_url("https://www.test.com/");
   RegisterMockedURLLoadFromBaseURL(base_url, "search_form_https.html");
-  WebView* web_view =
+  WebViewBase* web_view =
       web_view_helper_.InitializeAndLoad(base_url + "search_form_https.html");
 
   WebVector<WebFormElement> forms;
-  web_view->MainFrame()->GetDocument().Forms(forms);
+  web_view->MainFrameImpl()->GetDocument().Forms(forms);
 
   EXPECT_EQ(forms.size(), 1U);
 
diff --git a/third_party/WebKit/Source/web/tests/WebViewTest.cpp b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
index 4a8579c92..ce6e933 100644
--- a/third_party/WebKit/Source/web/tests/WebViewTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebViewTest.cpp
@@ -1340,7 +1340,7 @@
 
   // Scroll the input field out of the viewport.
   Element* element = static_cast<Element*>(
-      web_view->MainFrame()->GetDocument().GetElementById("btn"));
+      web_view->MainFrameImpl()->GetDocument().GetElementById("btn"));
   element->scrollIntoView();
   float offset_height = web_view->MainFrameImpl()->GetScrollOffset().height;
   EXPECT_EQ(0, web_view->MainFrameImpl()->GetScrollOffset().width);
@@ -1540,7 +1540,7 @@
   underlines[0] = WebCompositionUnderline(0, 4, 0, false, 0);
   WebLocalFrameBase* frame = web_view->MainFrameImpl();
   frame->SetEditableSelectionOffsets(1, 1);
-  WebDocument document = web_view->MainFrame()->GetDocument();
+  WebDocument document = web_view->MainFrameImpl()->GetDocument();
   EXPECT_FALSE(document.GetElementById("bold").IsNull());
   frame->SetCompositionFromExistingText(0, 4, underlines);
   EXPECT_FALSE(document.GetElementById("bold").IsNull());
@@ -1961,12 +1961,12 @@
   // Drag and drop barUrl and verify that we've navigated to it.
   DragAndDropURL(web_view, bar_url);
   EXPECT_EQ(bar_url,
-            web_view->MainFrame()->GetDocument().Url().GetString().Utf8());
+            web_view->MainFrameImpl()->GetDocument().Url().GetString().Utf8());
 
   // Drag and drop fooUrl and verify that we've navigated back to it.
   DragAndDropURL(web_view, foo_url);
   EXPECT_EQ(foo_url,
-            web_view->MainFrame()->GetDocument().Url().GetString().Utf8());
+            web_view->MainFrameImpl()->GetDocument().Url().GetString().Utf8());
 
   // Disable navigation on drag-and-drop.
   web_view->SettingsImpl()->SetNavigateOnDragDrop(false);
@@ -1975,7 +1975,7 @@
   // occurred.
   DragAndDropURL(web_view, bar_url);
   EXPECT_EQ(foo_url,
-            web_view->MainFrame()->GetDocument().Url().GetString().Utf8());
+            web_view->MainFrameImpl()->GetDocument().Url().GetString().Utf8());
 }
 
 bool WebViewTest::TapElement(WebInputEvent::Type type, Element* element) {
@@ -2011,7 +2011,7 @@
                                  const WebString& id) {
   DCHECK(web_view_helper_.WebView());
   Element* element = static_cast<Element*>(
-      web_view_helper_.WebView()->MainFrame()->GetDocument().GetElementById(
+      web_view_helper_.WebView()->MainFrameImpl()->GetDocument().GetElementById(
           id));
   return TapElement(type, element);
 }
@@ -2138,8 +2138,8 @@
   EXPECT_NE(WebInputEventResult::kHandledSystem,
             web_view->HandleInputEvent(WebCoalescedInputEvent(event)));
 
-  HTMLElement* element =
-      ToHTMLElement(web_view->MainFrame()->GetDocument().GetElementById("obj"));
+  HTMLElement* element = ToHTMLElement(
+      web_view->MainFrameImpl()->GetDocument().GetElementById("obj"));
   EXPECT_FALSE(element->CanStartSelection());
 }
 
@@ -2163,8 +2163,8 @@
   EXPECT_EQ(WebInputEventResult::kHandledSystem,
             web_view->HandleInputEvent(WebCoalescedInputEvent(event)));
 
-  HTMLElement* element =
-      ToHTMLElement(web_view->MainFrame()->GetDocument().GetElementById("obj"));
+  HTMLElement* element = ToHTMLElement(
+      web_view->MainFrameImpl()->GetDocument().GetElementById("obj"));
   EXPECT_TRUE(element->CanStartSelection());
 }
 
@@ -2250,11 +2250,11 @@
 
   EXPECT_TRUE(TapElementById(WebInputEvent::kGestureLongPress, anchor_tag_id));
   EXPECT_STREQ("anchor contextmenu",
-               web_view->MainFrame()->GetDocument().Title().Utf8().data());
+               web_view->MainFrameImpl()->GetDocument().Title().Utf8().data());
 
   EXPECT_TRUE(TapElementById(WebInputEvent::kGestureLongPress, image_tag_id));
   EXPECT_STREQ("image contextmenu",
-               web_view->MainFrame()->GetDocument().Title().Utf8().data());
+               web_view->MainFrameImpl()->GetDocument().Title().Utf8().data());
 }
 
 TEST_P(WebViewTest, LongPressEmptyEditableSelection) {
@@ -2385,7 +2385,8 @@
   EXPECT_TRUE(frame->SelectionAsText().IsEmpty());
 
   HTMLTextAreaElement* text_area_element = toHTMLTextAreaElement(
-      web_view->MainFrame()->GetDocument().GetElementById(blanklinestextbox));
+      web_view->MainFrameImpl()->GetDocument().GetElementById(
+          blanklinestextbox));
   text_area_element->setValue("hello");
 
   // Long-press past last word of textbox.
@@ -2740,7 +2741,7 @@
   frame->SetAutofillClient(&client);
   web_view->SetInitialFocus(false);
 
-  WebDocument document = web_view->MainFrame()->GetDocument();
+  WebDocument document = web_view->MainFrameImpl()->GetDocument();
   HTMLFormControlElement* form =
       ToHTMLFormControlElement(document.GetElementById("sample"));
 
@@ -2795,7 +2796,7 @@
 
   EXPECT_EQ(0, client.TextChanges());
 
-  WebDocument document = web_view->MainFrame()->GetDocument();
+  WebDocument document = web_view->MainFrameImpl()->GetDocument();
   EXPECT_EQ(WebString::FromUTF8("none"),
             document.GetElementById("inputEvent").FirstChild().NodeValue());
 
@@ -2882,7 +2883,7 @@
 
 TEST_P(WebViewTest, DispatchesFocusOutFocusInOnViewToggleFocus) {
   RegisterMockedHttpURLLoad("focusout_focusin_events.html");
-  WebView* web_view = web_view_helper_.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper_.InitializeAndLoad(
       base_url_ + "focusout_focusin_events.html");
 
   web_view->SetFocus(true);
@@ -2890,13 +2891,13 @@
   web_view->SetFocus(true);
 
   WebElement element =
-      web_view->MainFrame()->GetDocument().GetElementById("message");
+      web_view->MainFrameImpl()->GetDocument().GetElementById("message");
   EXPECT_STREQ("focusoutfocusin", element.TextContent().Utf8().data());
 }
 
 TEST_P(WebViewTest, DispatchesDomFocusOutDomFocusInOnViewToggleFocus) {
   RegisterMockedHttpURLLoad("domfocusout_domfocusin_events.html");
-  WebView* web_view = web_view_helper_.InitializeAndLoad(
+  WebViewBase* web_view = web_view_helper_.InitializeAndLoad(
       base_url_ + "domfocusout_domfocusin_events.html");
 
   web_view->SetFocus(true);
@@ -2904,7 +2905,7 @@
   web_view->SetFocus(true);
 
   WebElement element =
-      web_view->MainFrame()->GetDocument().GetElementById("message");
+      web_view->MainFrameImpl()->GetDocument().GetElementById("message");
   EXPECT_STREQ("DOMFocusOutDOMFocusIn", element.TextContent().Utf8().data());
 }
 
@@ -3011,7 +3012,7 @@
 
 TEST_P(WebViewTest, DispatchesFocusBlurOnViewToggle) {
   RegisterMockedHttpURLLoad("focus_blur_events.html");
-  WebView* web_view =
+  WebViewBase* web_view =
       web_view_helper_.InitializeAndLoad(base_url_ + "focus_blur_events.html");
 
   web_view->SetFocus(true);
@@ -3019,7 +3020,7 @@
   web_view->SetFocus(true);
 
   WebElement element =
-      web_view->MainFrame()->GetDocument().GetElementById("message");
+      web_view->MainFrameImpl()->GetDocument().GetElementById("message");
   // Expect not to see duplication of events.
   EXPECT_STREQ("blurfocus", element.TextContent().Utf8().data());
 }
@@ -3605,9 +3606,9 @@
   std::string url = base_url_ + "specify_size.html?100px:100px";
   URLTestHelpers::RegisterMockedURLLoad(
       ToKURL(url), testing::WebTestDataPath("specify_size.html"));
-  WebView* web_view = web_view_helper_.InitializeAndLoad(url);
+  WebViewBase* web_view = web_view_helper_.InitializeAndLoad(url);
   WebElement document_element =
-      web_view->MainFrame()->GetDocument().DocumentElement();
+      web_view->MainFrameImpl()->GetDocument().DocumentElement();
 
   WebSize size = web_view->ContentsPreferredMinimumSize();
   EXPECT_EQ(100, size.width);
@@ -3655,8 +3656,8 @@
   RegisterMockedHttpURLLoad("Ahem.ttf");
   RegisterMockedHttpURLLoad(test_file);
   UnhandledTapWebViewClient client;
-  WebView* web_view = web_view_helper_.InitializeAndLoad(base_url_ + test_file,
-                                                         nullptr, &client);
+  WebViewBase* web_view = web_view_helper_.InitializeAndLoad(
+      base_url_ + test_file, nullptr, &client);
   web_view->Resize(WebSize(500, 300));
   web_view->UpdateAllLifecyclePhases();
   RunPendingTasks();
@@ -3682,7 +3683,7 @@
   EXPECT_TRUE(client.GetWebNode().IsTextNode());
   // Make sure the returned text node has the parent element that was our
   // target.
-  EXPECT_EQ(web_view->MainFrame()->GetDocument().GetElementById("target"),
+  EXPECT_EQ(web_view->MainFrameImpl()->GetDocument().GetElementById("target"),
             client.GetWebNode().ParentNode());
 
   // Test correct conversion of coordinates to viewport space under pinch-zoom.
diff --git a/third_party/WebKit/public/platform/WebFeature.h b/third_party/WebKit/public/platform/WebFeature.h
index 01a97c9..1fc23fd1 100644
--- a/third_party/WebKit/public/platform/WebFeature.h
+++ b/third_party/WebKit/public/platform/WebFeature.h
@@ -1559,6 +1559,8 @@
   kDeprecatedTimingFunctionStepMiddle = 2024,
   kDocumentDomainSetWithNonDefaultPort = 2025,
   kDocumentDomainSetWithDefaultPort = 2026,
+  kFeaturePolicyHeader = 2027,
+  kFeaturePolicyAllowAttribute = 2028,
 
   // 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/WebKit/public/web/WebAXObject.h b/third_party/WebKit/public/web/WebAXObject.h
index 9e2af54..85ddac8 100644
--- a/third_party/WebKit/public/web/WebAXObject.h
+++ b/third_party/WebKit/public/web/WebAXObject.h
@@ -50,7 +50,6 @@
 class WebDocument;
 class WebString;
 class WebURL;
-class WebView;
 struct WebFloatRect;
 struct WebPoint;
 struct WebRect;
@@ -95,7 +94,6 @@
   }
 
   BLINK_EXPORT static WebAXObject FromWebNode(const WebNode&);
-  BLINK_EXPORT static WebAXObject FromWebView(WebView&);
   BLINK_EXPORT static WebAXObject FromWebDocument(const WebDocument&);
   BLINK_EXPORT static WebAXObject FromWebDocumentByID(const WebDocument&, int);
   BLINK_EXPORT static WebAXObject FromWebDocumentFocused(const WebDocument&);
diff --git a/third_party/WebKit/public/web/WebFrame.h b/third_party/WebKit/public/web/WebFrame.h
index f385d6dac..4481109 100644
--- a/third_party/WebKit/public/web/WebFrame.h
+++ b/third_party/WebKit/public/web/WebFrame.h
@@ -50,7 +50,6 @@
 class Visitor;
 class WebAssociatedURLLoader;
 struct WebAssociatedURLLoaderOptions;
-class WebDocument;
 class WebElement;
 class WebLocalFrame;
 class WebPerformance;
@@ -189,8 +188,6 @@
 
   // Content ------------------------------------------------------------
 
-  virtual WebDocument GetDocument() const = 0;
-
   virtual WebPerformance Performance() const = 0;
 
   // Scripting ----------------------------------------------------------
diff --git a/third_party/WebKit/public/web/WebLocalFrame.h b/third_party/WebKit/public/web/WebLocalFrame.h
index 0e48d686..e8798be 100644
--- a/third_party/WebKit/public/web/WebLocalFrame.h
+++ b/third_party/WebKit/public/web/WebLocalFrame.h
@@ -32,6 +32,7 @@
 class WebDataSource;
 class WebDevToolsAgent;
 class WebDevToolsAgentClient;
+class WebDocument;
 class WebDoubleSize;
 class WebDOMEvent;
 class WebFrameClient;
@@ -137,6 +138,8 @@
   // URLs
   virtual WebVector<WebIconURL> IconURLs(int icon_types_mask) const = 0;
 
+  virtual WebDocument GetDocument() const = 0;
+
   // Hierarchy ----------------------------------------------------------
 
   // Get the highest-level LocalFrame in this frame's in-process subtree.
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
index f9abd662..5e07ba6 100644
--- a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
@@ -62,6 +62,14 @@
 
 namespace {
 
+int local_isnanf(float x) {
+  uint32_t bits;
+  memcpy(&bits, &x, sizeof bits);
+  if ((bits & 0x7f800000) != 0x7f800000)
+    return 0;
+  return (bits & 0x7fffff) ? 1 : 0;
+}
+
 typedef SharedLibrary::linker_function_t linker_function_t;
 typedef int (*JNI_OnLoadFunctionPtr)(void* vm, void* reserved);
 typedef void (*JNI_OnUnloadFunctionPtr)(void* vm, void* reserved);
@@ -157,10 +165,15 @@
       // isnanf never need be resolved in gcc builds.
       //
       // http://code.google.com/p/chromium/issues/detail?id=376828
-      if (!address &&
-          !strcmp(symbol_name, "isnanf") &&
-          !strcmp(wrap->GetName(), "libm.so"))
+      if (!address && !strcmp(symbol_name, "isnanf") &&
+          !strcmp(wrap->GetName(), "libm.so")) {
         address = ::dlsym(wrap->GetSystem(), "__isnanf");
+        if (!address) {
+          // __isnanf only exists on Android 21+, so use a local fallback
+          // if that doesn't exist either.
+          address = reinterpret_cast<void*>(&local_isnanf);
+        }
+      }
       return address;
     }
 
diff --git a/third_party/eu-strip/OWNERS b/third_party/eu-strip/OWNERS
new file mode 100644
index 0000000..4644c96
--- /dev/null
+++ b/third_party/eu-strip/OWNERS
@@ -0,0 +1,3 @@
+dpranke@chromium.org
+thestig@chromium.org
+thomasanderson@chromium.org
diff --git a/third_party/eu-strip/README.chromium b/third_party/eu-strip/README.chromium
new file mode 100644
index 0000000..e84974d
--- /dev/null
+++ b/third_party/eu-strip/README.chromium
@@ -0,0 +1,24 @@
+Name: eu-strip
+URL: https://sourceware.org/elfutils/
+Version: 0.158
+Security Critical: no
+License: LGPL 3
+License File: NOT_SHIPPED
+
+Description:
+
+Patched eu-strip from elfutils.
+
+Build instructions (on Trusty; note that this will build the
+Ubuntu-patched version of elfutils):
+$ mkdir elfutils
+$ cd elfutils
+$ apt-get source elfutils
+$ cd elfutils-0.158
+[ Edit libelf/elf_end.c and remove the free() on line 164. ]
+$ ./configure
+$ make
+$ gcc -std=gnu99 -Wall -Wshadow -Wunused -Wextra -fgnu89-inline
+  -Wformat=2 -Werror -g -O2 -Wl,-rpath-link,libelf:libdw -o eu-strip
+  src/strip.o libebl/libebl.a libelf/libelf.a lib/libeu.a -ldl
+$ eu-strip ./eu-strip  # Keep the binary small, please.
diff --git a/third_party/eu-strip/bin/eu-strip b/third_party/eu-strip/bin/eu-strip
new file mode 100755
index 0000000..994e226
--- /dev/null
+++ b/third_party/eu-strip/bin/eu-strip
Binary files differ
diff --git a/third_party/py_trace_event/OWNERS b/third_party/py_trace_event/OWNERS
deleted file mode 100644
index e27f138..0000000
--- a/third_party/py_trace_event/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-nduca@chromium.org
-dtu@chromium.org
diff --git a/third_party/py_trace_event/README.chromium b/third_party/py_trace_event/README.chromium
deleted file mode 100644
index 85aecf2..0000000
--- a/third_party/py_trace_event/README.chromium
+++ /dev/null
@@ -1,21 +0,0 @@
-Name: py_trace_event
-URL: https://github.com/natduca/py_trace_event
-Version: 0
-Date: August 18, 2014
-Revision: dd463ea9e2c430de2b9e53dea57a77b4c3ac9b30
-License: BSD
-License File: NOT_SHIPPED
-Security Critical: no
-License Android Compatible: yes
-
-Description:
-py_trace_event allows low-overhead instrumentation of a multi-threaded,
-multi-process application in order to study its global performance
-characteristics. It uses the trace event format used in Chromium/Chrome's
-about:tracing system.
-
-Trace files generated by py_trace_event can be viewed and manipulated by
-trace_event_viewer.
-
-Local Modifications:
-None.
diff --git a/tools/determinism/deterministic_build_whitelist.pyl b/tools/determinism/deterministic_build_whitelist.pyl
index 5721b9d..c9c4472 100644
--- a/tools/determinism/deterministic_build_whitelist.pyl
+++ b/tools/determinism/deterministic_build_whitelist.pyl
@@ -119,7 +119,6 @@
     'message_center_unittests',
     'midi_unittests',
     'mojo_common_unittests',
-    'mojo_js_integration_tests',
     'mojo_js_unittests',
     'mojo_public_bindings_unittests',
     'mojo_public_system_unittests',
@@ -276,7 +275,6 @@
     'midi_unittests.exe',
     'mini_installer.exe',
     'mksnapshot.exe',
-    'mojo_js_integration_tests.exe',
     'mojo_js_unittests.exe',
     'mojo_message_pipe_perftests.exe',
     'mojo_public_bindings_perftests.exe',
diff --git a/tools/idl_parser/idl_lexer.py b/tools/idl_parser/idl_lexer.py
index 5baf0d2..c810558 100755
--- a/tools/idl_parser/idl_lexer.py
+++ b/tools/idl_parser/idl_lexer.py
@@ -3,12 +3,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-""" Lexer for PPAPI IDL
+""" Lexer for Web IDL
 
-The lexer uses the PLY library to build a tokenizer which understands both
-WebIDL and Pepper tokens.
+The lexer uses the PLY library to build a tokenizer which understands
+Web IDL tokens.
 
-WebIDL, and WebIDL regular expressions can be found at:
+Web IDL, and Web IDL regular expressions can be found at:
    http://heycam.github.io/webidl/
 PLY can be found at:
    http://www.dabeaz.com/ply/
diff --git a/tools/idl_parser/idl_lexer_test.py b/tools/idl_parser/idl_lexer_test.py
index f8d8bb9a..f9e8a36 100755
--- a/tools/idl_parser/idl_lexer_test.py
+++ b/tools/idl_parser/idl_lexer_test.py
@@ -7,7 +7,6 @@
 import unittest
 
 from idl_lexer import IDLLexer
-from idl_ppapi_lexer import IDLPPAPILexer
 
 
 #
@@ -89,15 +88,5 @@
         self.assertEqual(expect_type, actual_type, msg)
 
 
-class PepperIDLLexer(WebIDLLexer):
-  def setUp(self):
-    self.lexer = IDLPPAPILexer()
-    cur_dir = os.path.dirname(os.path.realpath(__file__))
-    self.filenames = [
-        os.path.join(cur_dir, 'test_lexer/values_ppapi.in'),
-        os.path.join(cur_dir, 'test_lexer/keywords_ppapi.in')
-    ]
-
-
 if __name__ == '__main__':
   unittest.main()
diff --git a/tools/idl_parser/idl_parser.py b/tools/idl_parser/idl_parser.py
index a28a578..e7b9510 100755
--- a/tools/idl_parser/idl_parser.py
+++ b/tools/idl_parser/idl_parser.py
@@ -3,15 +3,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-""" Parser for PPAPI IDL """
+""" Parser for Web IDL """
 
 #
 # IDL Parser
 #
 # The parser is uses the PLY yacc library to build a set of parsing rules based
-# on WebIDL.
+# on Web IDL.
 #
-# WebIDL, and WebIDL grammar can be found at:
+# Web IDL, and Web IDL grammar can be found at:
 #   http://heycam.github.io/webidl/
 # PLY can be found at:
 #   http://www.dabeaz.com/ply/
diff --git a/tools/idl_parser/idl_parser_test.py b/tools/idl_parser/idl_parser_test.py
index 76a9571b..59a5cf23 100755
--- a/tools/idl_parser/idl_parser_test.py
+++ b/tools/idl_parser/idl_parser_test.py
@@ -8,8 +8,6 @@
 
 from idl_lexer import IDLLexer
 from idl_parser import IDLParser, ParseFile
-from idl_ppapi_lexer import IDLPPAPILexer
-from idl_ppapi_parser import IDLPPAPIParser
 
 def ParseCommentTest(comment):
   comment = comment.strip()
@@ -58,49 +56,5 @@
         self._TestNode(node)
 
 
-class PepperIDLParser(unittest.TestCase):
-  def setUp(self):
-    self.parser = IDLPPAPIParser(IDLPPAPILexer(), mute_error=True)
-    self.filenames = glob.glob('test_parser/*_ppapi.idl')
-
-  def _TestNode(self, filename, node):
-    comments = node.GetListOf('Comment')
-    for comment in comments:
-      check, value = ParseCommentTest(comment.GetName())
-      if check == 'BUILD':
-        msg = '%s - Expecting %s, but found %s.\n' % (
-            filename, value, str(node))
-        self.assertEqual(value, str(node), msg)
-
-      if check == 'ERROR':
-        msg = node.GetLogLine('%s - Expecting\n\t%s\nbut found \n\t%s\n' % (
-                              filename, value, str(node)))
-        self.assertEqual(value, node.GetName(), msg)
-
-      if check == 'PROP':
-        key, expect = value.split('=')
-        actual = str(node.GetProperty(key))
-        msg = '%s - Mismatched property %s: %s vs %s.\n' % (
-                              filename, key, expect, actual)
-        self.assertEqual(expect, actual, msg)
-
-      if check == 'TREE':
-        quick = '\n'.join(node.Tree())
-        lineno = node.GetProperty('LINENO')
-        msg = '%s - Mismatched tree at line %d:\n%sVS\n%s' % (
-                              filename, lineno, value, quick)
-        self.assertEqual(value, quick, msg)
-
-  def testExpectedNodes(self):
-    for filename in self.filenames:
-      filenode = ParseFile(self.parser, filename)
-      children = filenode.GetChildren()
-      self.assertTrue(len(children) > 2, 'Expecting children in %s.' %
-                      filename)
-
-      for node in filenode.GetChildren()[2:]:
-        self._TestNode(filename, node)
-
 if __name__ == '__main__':
   unittest.main(verbosity=2)
-
diff --git a/tools/idl_parser/idl_ppapi_lexer.py b/tools/idl_parser/idl_ppapi_lexer.py
deleted file mode 100755
index a13c4e4a..0000000
--- a/tools/idl_parser/idl_ppapi_lexer.py
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-""" Lexer for PPAPI IDL
-
-The lexer uses the PLY library to build a tokenizer which understands both
-WebIDL and Pepper tokens.
-
-WebIDL, and WebIDL regular expressions can be found at:
-   http://heycam.github.io/webidl/
-PLY can be found at:
-   http://www.dabeaz.com/ply/
-"""
-
-from idl_lexer import IDLLexer
-
-
-#
-# IDL PPAPI Lexer
-#
-class IDLPPAPILexer(IDLLexer):
-  # Token definitions
-  #
-  # These need to be methods for lexer construction, despite not using self.
-  # pylint: disable=R0201
-
-  # Special multi-character operators
-  def t_LSHIFT(self, t):
-    r'<<'
-    return t
-
-  def t_RSHIFT(self, t):
-    r'>>'
-    return t
-
-  def t_INLINE(self, t):
-    r'\#inline (.|\n)*?\#endinl.*'
-    self.AddLines(t.value.count('\n'))
-    return t
-
-  # Return a "preprocessor" inline block
-  def __init__(self):
-    IDLLexer.__init__(self)
-    self._AddTokens(['INLINE', 'LSHIFT', 'RSHIFT'])
-    self._AddKeywords(['label', 'struct'])
-
-    # Add number types
-    self._AddKeywords(['char', 'int8_t', 'int16_t', 'int32_t', 'int64_t'])
-    self._AddKeywords(['uint8_t', 'uint16_t', 'uint32_t', 'uint64_t'])
-    self._AddKeywords(['double_t', 'float_t'])
-
-    # Add handle types
-    self._AddKeywords(['handle_t', 'PP_FileHandle'])
-
-    # Add pointer types (void*, char*, const char*, const void*)
-    self._AddKeywords(['mem_t', 'str_t', 'cstr_t', 'interface_t'])
-
-    # Remove JS types
-    self._DelKeywords(['boolean', 'byte', 'ByteString', 'Date', 'DOMString',
-                       'double', 'float', 'long', 'object', 'octet', 'Promise',
-                       'record', 'RegExp', 'short', 'unsigned', 'USVString'])
-
-
-# If run by itself, attempt to build the lexer
-if __name__ == '__main__':
-  lexer = IDLPPAPILexer()
-  lexer.Tokenize(open('test_parser/inline_ppapi.idl').read())
-  for tok in lexer.GetTokens():
-    print '\n' + str(tok)
diff --git a/tools/idl_parser/idl_ppapi_parser.py b/tools/idl_parser/idl_ppapi_parser.py
deleted file mode 100755
index 2556ffb..0000000
--- a/tools/idl_parser/idl_ppapi_parser.py
+++ /dev/null
@@ -1,329 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-""" Parser for PPAPI IDL """
-
-#
-# IDL Parser
-#
-# The parser is uses the PLY yacc library to build a set of parsing rules based
-# on WebIDL.
-#
-# WebIDL, and WebIDL grammar can be found at:
-#   http://heycam.github.io/webidl/
-# PLY can be found at:
-#   http://www.dabeaz.com/ply/
-#
-# The parser generates a tree by recursively matching sets of items against
-# defined patterns.  When a match is made, that set of items is reduced
-# to a new item.   The new item can provide a match for parent patterns.
-# In this way an AST is built (reduced) depth first.
-#
-
-#
-# Disable check for line length and Member as Function due to how grammar rules
-# are defined with PLY
-#
-# pylint: disable=R0201
-# pylint: disable=C0301
-
-import sys
-
-from idl_ppapi_lexer import IDLPPAPILexer
-from idl_parser import IDLParser, ListFromConcat, ParseFile
-from idl_node import IDLNode
-
-class IDLPPAPIParser(IDLParser):
-#
-# We force all input files to start with two comments.  The first comment is a
-# Copyright notice followed by a file comment and finally by file level
-# productions.
-#
-  # [0] Insert a TOP definition for Copyright and Comments
-  def p_Top(self, p):
-    """Top : COMMENT COMMENT Definitions"""
-    Copyright = self.BuildComment('Copyright', p, 1)
-    Filedoc = self.BuildComment('Comment', p, 2)
-    p[0] = ListFromConcat(Copyright, Filedoc, p[3])
-
-#
-#The parser is based on the WebIDL standard.  See:
-# http://heycam.github.io/webidl/#idl-grammar
-#
-  # [1]
-  def p_Definitions(self, p):
-    """Definitions : ExtendedAttributeList Definition Definitions
-           | """
-    if len(p) > 1:
-      p[2].AddChildren(p[1])
-      p[0] = ListFromConcat(p[2], p[3])
-
-      # [2] Add INLINE definition
-  def p_Definition(self, p):
-    """Definition : CallbackOrInterface
-                  | Struct
-                  | Partial
-                  | Dictionary
-                  | Exception
-                  | Enum
-                  | Typedef
-                  | ImplementsStatement
-                  | Label
-                  | Inline"""
-    p[0] = p[1]
-
-  def p_Inline(self, p):
-    """Inline : INLINE"""
-    words = p[1].split()
-    name = self.BuildAttribute('NAME', words[1])
-    lines = p[1].split('\n')
-    value = self.BuildAttribute('VALUE', '\n'.join(lines[1:-1]) + '\n')
-    children = ListFromConcat(name, value)
-    p[0] = self.BuildProduction('Inline', p, 1, children)
-
-#
-# Label
-#
-# A label is a special kind of enumeration which allows us to go from a
-# set of version numbrs to releases
-#
-  def p_Label(self, p):
-    """Label : LABEL identifier '{' LabelList '}' ';'"""
-    p[0] = self.BuildNamed('Label', p, 2, p[4])
-
-  def p_LabelList(self, p):
-    """LabelList : identifier '=' float LabelCont"""
-    val  = self.BuildAttribute('VALUE', p[3])
-    label = self.BuildNamed('LabelItem', p, 1, val)
-    p[0] = ListFromConcat(label, p[4])
-
-  def p_LabelCont(self, p):
-    """LabelCont : ',' LabelList
-                 |"""
-    if len(p) > 1:
-      p[0] = p[2]
-
-  def p_LabelContError(self, p):
-    """LabelCont : error LabelCont"""
-    p[0] = p[2]
-
-  # [5.1] Add "struct" style interface
-  def p_Struct(self, p):
-    """Struct : STRUCT identifier Inheritance '{' StructMembers '}' ';'"""
-    p[0] = self.BuildNamed('Struct', p, 2, ListFromConcat(p[3], p[5]))
-
-  def p_StructMembers(self, p):
-    """StructMembers : StructMember StructMembers
-                     |"""
-    if len(p) > 1:
-      p[0] = ListFromConcat(p[1], p[2])
-
-  def p_StructMember(self, p):
-    """StructMember : ExtendedAttributeList Type identifier ';'"""
-    p[0] = self.BuildNamed('Member', p, 3, ListFromConcat(p[1], p[2]))
-
-  def p_Typedef(self, p):
-    """Typedef : TYPEDEF ExtendedAttributeListNoComments Type identifier ';'"""
-    p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3]))
-
-  def p_TypedefFunc(self, p):
-    """Typedef : TYPEDEF ExtendedAttributeListNoComments ReturnType identifier '(' ArgumentList ')' ';'"""
-    args = self.BuildProduction('Arguments', p, 5, p[6])
-    p[0] = self.BuildNamed('Callback', p, 4, ListFromConcat(p[2], p[3], args))
-
-  def p_ConstValue(self, p):
-    """ConstValue : integer
-                  | integer LSHIFT integer
-                  | integer RSHIFT integer"""
-    val = str(p[1])
-    if len(p) > 2:
-      val = "%s %s %s" % (p[1], p[2], p[3])
-    p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'),
-                          self.BuildAttribute('VALUE', val))
-
-  def p_ConstValueStr(self, p):
-    """ConstValue : string"""
-    p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'string'),
-                          self.BuildAttribute('VALUE', p[1]))
-
-  # Boolean & Float Literals area already BuildAttributes
-  def p_ConstValueLiteral(self, p):
-    """ConstValue : FloatLiteral
-                  | BooleanLiteral """
-    p[0] = p[1]
-
-  def p_EnumValueList(self, p):
-    """EnumValueList : EnumValue EnumValues"""
-    p[0] = ListFromConcat(p[1], p[2])
-
-  def p_EnumValues(self, p):
-    """EnumValues : ',' EnumValue EnumValues
-                  |"""
-    if len(p) > 1:
-      p[0] = ListFromConcat(p[2], p[3])
-
-  def p_EnumValue(self, p):
-    """EnumValue : ExtendedAttributeList identifier
-                 | ExtendedAttributeList identifier '=' ConstValue"""
-    p[0] = self.BuildNamed('EnumItem', p, 2, p[1])
-    if len(p) > 3:
-      p[0].AddChildren(p[4])
-
-  # Omit PromiseType, as it is a JS type.
-  def p_NonAnyType(self, p):
-    """NonAnyType : PrimitiveType TypeSuffix
-                  | identifier TypeSuffix
-                  | SEQUENCE '<' Type '>' Null"""
-    IDLParser.p_NonAnyType(self, p)
-
-  def p_PrimitiveType(self, p):
-    """PrimitiveType : IntegerType
-                     | UnsignedIntegerType
-                     | FloatType
-                     | HandleType
-                     | PointerType"""
-    if type(p[1]) == str:
-      p[0] = self.BuildNamed('PrimitiveType', p, 1)
-    else:
-      p[0] = p[1]
-
-  def p_PointerType(self, p):
-    """PointerType : STR_T
-                   | MEM_T
-                   | CSTR_T
-                   | INTERFACE_T
-                   | NULL"""
-    p[0] = p[1]
-
-  def p_HandleType(self, p):
-    """HandleType : HANDLE_T
-                  | PP_FILEHANDLE"""
-    p[0] = p[1]
-
-  def p_FloatType(self, p):
-    """FloatType : FLOAT_T
-                 | DOUBLE_T"""
-    p[0] = p[1]
-
-  def p_UnsignedIntegerType(self, p):
-    """UnsignedIntegerType : UINT8_T
-                           | UINT16_T
-                           | UINT32_T
-                           | UINT64_T"""
-    p[0] = p[1]
-
-
-  def p_IntegerType(self, p):
-    """IntegerType : CHAR
-                   | INT8_T
-                   | INT16_T
-                   | INT32_T
-                   | INT64_T"""
-    p[0] = p[1]
-
-  # These targets are no longer used
-  def p_OptionalLong(self, p):
-    """ """
-    pass
-
-  def p_UnrestrictedFloatType(self, p):
-    """ """
-    pass
-
-  def p_null(self, p):
-    """ """
-    pass
-
-  def p_PromiseType(self, p):
-    """ """
-    pass
-
-  def p_EnumValueListComma(self, p):
-    """ """
-    pass
-
-  def p_EnumValueListString(self, p):
-    """ """
-    pass
-
-  def p_StringType(self, p):
-    """ """
-    pass
-
-  def p_RecordType(self, p):
-    """ """
-    pass
-
-  def p_RecordTypeError(self, p):
-    """ """
-    pass
-
-  # We only support:
-  #    [ identifier ]
-  #    [ identifier ( ArgumentList )]
-  #    [ identifier ( ValueList )]
-  #    [ identifier = identifier ]
-  #    [ identifier = ( IdentifierList )]
-  #    [ identifier = ConstValue ]
-  #    [ identifier = identifier ( ArgumentList )]
-  # [51] map directly to 74-77
-  # [52-54, 56] are unsupported
-  def p_ExtendedAttribute(self, p):
-    """ExtendedAttribute : ExtendedAttributeNoArgs
-                         | ExtendedAttributeArgList
-                         | ExtendedAttributeValList
-                         | ExtendedAttributeIdent
-                         | ExtendedAttributeIdentList
-                         | ExtendedAttributeIdentConst
-                         | ExtendedAttributeNamedArgList"""
-    p[0] = p[1]
-
-  def p_ExtendedAttributeValList(self, p):
-    """ExtendedAttributeValList : identifier '(' ValueList ')'"""
-    arguments = self.BuildProduction('Values', p, 2, p[3])
-    p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments)
-
-  def p_ValueList(self, p):
-    """ValueList : ConstValue ValueListCont"""
-    p[0] = ListFromConcat(p[1], p[2])
-
-  def p_ValueListCont(self, p):
-    """ValueListCont : ValueList
-                     |"""
-    if len(p) > 1:
-      p[0] = p[1]
-
-  def p_ExtendedAttributeIdentConst(self, p):
-    """ExtendedAttributeIdentConst : identifier '=' ConstValue"""
-    p[0] = self.BuildNamed('ExtAttribute', p, 1, p[3])
-
-
-  def __init__(self, lexer, verbose=False, debug=False, mute_error=False):
-    IDLParser.__init__(self, lexer, verbose, debug, mute_error)
-
-
-def main(argv):
-  nodes = []
-  parser = IDLPPAPIParser(IDLPPAPILexer())
-  errors = 0
-
-  for filename in argv:
-    filenode = ParseFile(parser, filename)
-    if filenode:
-      errors += filenode.GetProperty('ERRORS')
-      nodes.append(filenode)
-
-  ast = IDLNode('AST', '__AST__', 0, 0, nodes)
-
-  print '\n'.join(ast.Tree(accept_props=['PROD', 'TYPE', 'VALUE']))
-  if errors:
-    print '\nFound %d errors.\n' % errors
-
-
-  return errors
-
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv[1:]))
diff --git a/tools/idl_parser/test_lexer/keywords_ppapi.in b/tools/idl_parser/test_lexer/keywords_ppapi.in
deleted file mode 100644
index 62567e4..0000000
--- a/tools/idl_parser/test_lexer/keywords_ppapi.in
+++ /dev/null
@@ -1,44 +0,0 @@
-ANY any
-ATTRIBUTE attribute
-CALLBACK callback
-CONST const
-CREATOR creator
-DELETER deleter
-DICTIONARY dictionary
-FALSE false
-EXCEPTION exception
-GETTER getter
-IMPLEMENTS implements
-INFINITY Infinity
-INTERFACE interface
-LABEL label
-LEGACYCALLER legacycaller
-NAN Nan
-NULL null
-OPTIONAL optional
-OR or
-PARTIAL partial
-READONLY readonly
-SETTER setter
-STATIC static
-STRINGIFIER stringifier
-TYPEDEF typedef
-TRUE true
-VOID void
-CHAR char
-INT8_T int8_t
-INT16_T int16_t
-INT32_T int32_t
-INT64_T int64_t
-UINT8_T uint8_t
-UINT16_T uint16_t
-UINT32_T uint32_t
-UINT64_T uint64_t
-DOUBLE_T double_t
-FLOAT_T float_t
-MEM_T mem_t
-STR_T str_t
-CSTR_T cstr_t
-INTERFACE_T interface_t
-HANDLE_T handle_t
-PP_FILEHANDLE PP_FileHandle
\ No newline at end of file
diff --git a/tools/idl_parser/test_lexer/values_ppapi.in b/tools/idl_parser/test_lexer/values_ppapi.in
deleted file mode 100644
index 33fa577..0000000
--- a/tools/idl_parser/test_lexer/values_ppapi.in
+++ /dev/null
@@ -1,50 +0,0 @@
-integer 1 integer 123 integer 12345
-identifier A123 identifier A_A
-
-COMMENT /*XXXX*/
-COMMENT //XXXX
-
-COMMENT /*MULTI LINE*/
-
-[ [
-] ]
-* *
-. .
-( (
-) )
-{ {
-} }
-[ [
-] ]
-, ,
-; ;
-: :
-= =
-+ +
-- -
-/ /
-~ ~
-| |
-& &
-^ ^
-> >
-< <
-
-LSHIFT <<
-RSHIFT >>
-ELLIPSIS ...
-
-float 1.1
-float 1e1
-float -1.1
-float -1e1
-float 1e-1
-float -1e-1
-float 1.0e1
-float -1.0e-1
-
-integer 00
-integer 01
-integer 0123
-
-identifier blah
diff --git a/tools/idl_parser/test_parser/enum_ppapi.idl b/tools/idl_parser/test_parser/enum_ppapi.idl
deleted file mode 100644
index 1b088b84..0000000
--- a/tools/idl_parser/test_parser/enum_ppapi.idl
+++ /dev/null
@@ -1,126 +0,0 @@
-/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
-   Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file. */
-
-/* Test Enum productions
-
-Run with --test to generate an AST and verify that all comments accurately
-reflect the state of the Nodes.
-
-BUILD Type(Name)
-This comment signals that a node of type <Type> is created with the
-name <Name>.
-
-ERROR Error String
-This comment signals that a error of <Error String> is generated.  The error
-is not assigned to a node, but are expected in order.
-
-PROP Key=Value
-This comment signals that a property has been set on the Node such that
-<Key> = <Value>.
-
-TREE
-Type(Name)
-  Type(Name)
-  Type(Name)
-    Type(Name)
-    ...
-This comment signals that a tree of nodes matching the BUILD comment
-symatics should exist.  This is an exact match.
-*/
-
-/* TREE
- *Enum(MealType1)
- *  EnumItem(rice)
- *  EnumItem(noodles)
- *  EnumItem(other)
-*/
-enum MealType1 {
-  /* BUILD EnumItem (rice) */
-  rice,
-  /* BUILD EnumItem (noodles) */
-  noodles,
-  /* BUILD EnumItem(other) */
-  other
-};
-
-/* BUILD Error(Enum missing name.) */
-/* ERROR Enum missing name. */
-enum {
-  rice,
-  noodles,
-  other,
-};
-
-/* TREE
- *Enum(MealType2)
- *  EnumItem(rice)
- *  EnumItem(noodles)
- *  EnumItem(other)
-*/
-enum MealType2 {
-  /* BUILD EnumItem(rice) */
-  rice,
-  /* BUILD EnumItem(noodles) */
-  noodles = 1,
-  /* BUILD EnumItem(other) */
-  other
-};
-
-/* BUILD Error(Unexpected identifier "noodles" after identifier "rice".) */
-/* ERROR Unexpected identifier "noodles" after identifier "rice". */
-enum MissingComma {
-  rice
-  noodles,
-  other
-};
-
-/* BUILD Error(Trailing comma in block.) */
-/* ERROR Trailing comma in block. */
-enum TrailingComma {
-  rice,
-  noodles,
-  other,
-};
-
-/* BUILD Error(Unexpected "," after ",".) */
-/* ERROR Unexpected "," after ",". */
-enum ExtraComma {
-  rice,
-  noodles,
-  ,other,
-};
-
-/* BUILD Error(Unexpected keyword "interface" after "{".) */
-/* ERROR Unexpected keyword "interface" after "{". */
-enum ExtraComma {
-  interface,
-  noodles,
-  ,other,
-};
-
-/* BUILD Error(Unexpected string "somename" after "{".) */
-/* ERROR Unexpected string "somename" after "{". */
-enum ExtraComma {
-  "somename",
-  noodles,
-  other,
-};
-
-/* BUILD Enum(MealType3) */
-enum MealType3 {
-  /* BUILD EnumItem(rice) */
-  rice = 1 << 1,
-  /* BUILD EnumItem(noodles) */
-  noodles = 0x1 << 0x2,
-  /* BUILD EnumItem(other) */
-  other = 012 << 777
-};
-
-/* BUILD Enum(MealType4) */
-enum MealType4 {
-  /* BUILD EnumItem(rice) */
-  rice = true,
-  /* BUILD EnumItem(noodles) */
-  noodles = false
-};
diff --git a/tools/idl_parser/test_parser/extattr_ppapi.idl b/tools/idl_parser/test_parser/extattr_ppapi.idl
deleted file mode 100644
index 07afbc0..0000000
--- a/tools/idl_parser/test_parser/extattr_ppapi.idl
+++ /dev/null
@@ -1,99 +0,0 @@
-/* Copyright 2013 The Chromium Authors. All rights reserved.
-   Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file. */
-
-/* Test ExtendedAttribute productions
-
-Run with --test to generate an AST and verify that all comments accurately
-reflect the state of the Nodes.
-
-BUILD Type(Name)
-This comment signals that a node of type <Type> is created with the
-name <Name>.
-
-ERROR Error String
-This comment signals that a error of <Error String> is generated.  The error
-is not assigned to a node, but are expected in order.
-
-PROP Key=Value
-This comment signals that a property has been set on the Node such that
-<Key> = <Value>.
-
-TREE
-Type(Name)
-  Type(Name)
-  Type(Name)
-    Type(Name)
-    ...
-This comment signals that a tree of nodes matching the BUILD comment
-symatics should exist.  This is an exact match.
-*/
-
-/* TREE
- *Interface(Foo)
- *  ExtAttributes()
- *    ExtAttribute(foo)
- *      Arguments()
- */
-
-[foo()] interface Foo {};
-
-/* TREE
- *Interface(Foo)
- *  ExtAttributes()
- *    ExtAttribute(foo)
- *      Values()
- */
-
-[foo(1)] interface Foo {};
-
-/* TREE
- *Interface(Foo)
- *  ExtAttributes()
- *    ExtAttribute(foo)
- *      Values()
- */
-
-[foo(1 true 1.2e-3)] interface Foo {};
-
-/* TREE
- *Interface(Foo)
- *  ExtAttributes()
- *    ExtAttribute(foo)
- *      Arguments()
- *        Error(Unexpected ).)
- */
-
-[foo(null)] interface Foo {};
-
-/* TREE
- *Interface(Foo)
- *  ExtAttributes()
- *    ExtAttribute(foo)
- */
-
-[foo=1] interface Foo {};
-
-/* TREE
- *Interface(Foo)
- *  ExtAttributes()
- *    ExtAttribute(foo)
- */
-
-[foo=true] interface Foo {};
-
-/* TREE
- *Interface(Foo)
- *  ExtAttributes()
- *    ExtAttribute(foo)
- */
-
-[foo=1.2e-3] interface Foo {};
-
-/* TREE
- *Interface(Foo)
- *  ExtAttributes()
- *    ExtAttribute(foo)
- */
-
-[foo=(bar, baz)] interface Foo {};
diff --git a/tools/idl_parser/test_parser/inline_ppapi.idl b/tools/idl_parser/test_parser/inline_ppapi.idl
deleted file mode 100644
index 134f60d..0000000
--- a/tools/idl_parser/test_parser/inline_ppapi.idl
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
-   Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file. */
-
-/* Test Typedef productions
-
-Run with --test to generate an AST and verify that all comments accurately
-reflect the state of the Nodes.
-
-BUILD Type(Name)
-This comment signals that a node of type <Type> is created with the
-name <Name>.
-
-ERROR Error String
-This comment signals that a error of <Error String> is generated.  The error
-is not assigned to a node, but are expected in order.
-
-PROP Key=Value
-This comment signals that a property has been set on the Node such that
-<Key> = <Value>.
-
-TREE
-Type(Name)
-  Type(Name)
-  Type(Name)
-    Type(Name)
-    ...
-This comment signals that a tree of nodes matching the BUILD comment
-symatics should exist.  This is an exact match.
-*/
-
-/* TREE
- *Inline(C)
- */
-
-#inline C
-This is my block of C code
-#endinl
-
-/* TREE
- *Inline(CC)
- */
-#inline CC
-This is my block of CC code
-#endinl
-
diff --git a/tools/idl_parser/test_parser/label_ppapi.idl b/tools/idl_parser/test_parser/label_ppapi.idl
deleted file mode 100644
index 264699dd..0000000
--- a/tools/idl_parser/test_parser/label_ppapi.idl
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
-   Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file. */
-
-/* Test Typedef productions
-
-Run with --test to generate an AST and verify that all comments accurately
-reflect the state of the Nodes.
-
-BUILD Type(Name)
-This comment signals that a node of type <Type> is created with the
-name <Name>.
-
-ERROR Error String
-This comment signals that a error of <Error String> is generated.  The error
-is not assigned to a node, but are expected in order.
-
-PROP Key=Value
-This comment signals that a property has been set on the Node such that
-<Key> = <Value>.
-
-TREE
-Type(Name)
-  Type(Name)
-  Type(Name)
-    Type(Name)
-    ...
-This comment signals that a tree of nodes matching the BUILD comment
-symatics should exist.  This is an exact match.
-*/
-
-/* TREE
- *Label(Chrome1)
- *  LabelItem(M13)
- */
-label Chrome1 {
-  M13 = 0.0
-};
-
-/* TREE
- *Label(Chrome2)
- *  LabelItem(M12)
- *  LabelItem(M13)
- */
-label Chrome2 {
-  M12 = 1.0,
-  M13 = 2.0,
-};
\ No newline at end of file
diff --git a/tools/idl_parser/test_parser/struct_ppapi.idl b/tools/idl_parser/test_parser/struct_ppapi.idl
deleted file mode 100644
index 59bc7eb..0000000
--- a/tools/idl_parser/test_parser/struct_ppapi.idl
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
-   Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file. */
-
-/* Test Struct productions
-
-Run with --test to generate an AST and verify that all comments accurately
-reflect the state of the Nodes.
-
-BUILD Type(Name)
-This comment signals that a node of type <Type> is created with the
-name <Name>.
-
-ERROR Error String
-This comment signals that a error of <Error String> is generated.  The error
-is not assigned to a node, but are expected in order.
-
-PROP Key=Value
-This comment signals that a property has been set on the Node such that
-<Key> = <Value>.
-
-TREE
-Type(Name)
-  Type(Name)
-  Type(Name)
-    Type(Name)
-    ...
-This comment signals that a tree of nodes matching the BUILD comment
-symatics should exist.  This is an exact match.
-*/
-
-/* TREE
- *Struct(MyStruct)
- *  Member(x)
- *    Type()
- *      PrimitiveType(uint32_t)
- *  Member(y)
- *    Type()
- *      PrimitiveType(uint64_t)
- *  Member(string)
- *    ExtAttributes()
- *      ExtAttribute(fake_attribute)
- *    Type()
- *      PrimitiveType(str_t)
- *  Member(z)
- *    Type()
- *      Typeref(Promise)
- *  ExtAttributes()
- *    ExtAttribute(union)
- */
-[union] struct MyStruct {
-  uint32_t x;
-  uint64_t y;
-  [fake_attribute] str_t string;
-  Promise z;
-};
diff --git a/tools/idl_parser/test_parser/typedef_ppapi.idl b/tools/idl_parser/test_parser/typedef_ppapi.idl
deleted file mode 100644
index 1a80415..0000000
--- a/tools/idl_parser/test_parser/typedef_ppapi.idl
+++ /dev/null
@@ -1,92 +0,0 @@
-/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
-   Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file. */
-
-/* Test Typedef productions
-
-Run with --test to generate an AST and verify that all comments accurately
-reflect the state of the Nodes.
-
-BUILD Type(Name)
-This comment signals that a node of type <Type> is created with the
-name <Name>.
-
-ERROR Error String
-This comment signals that a error of <Error String> is generated.  The error
-is not assigned to a node, but are expected in order.
-
-PROP Key=Value
-This comment signals that a property has been set on the Node such that
-<Key> = <Value>.
-
-TREE
-Type(Name)
-  Type(Name)
-  Type(Name)
-    Type(Name)
-    ...
-This comment signals that a tree of nodes matching the BUILD comment
-symatics should exist.  This is an exact match.
-*/
-
-/* TREE
- *Callback(foo)
- *  Type()
- *    PrimitiveType(void)
- *  Arguments()
- *    Argument(x)
- *      Type()
- *        PrimitiveType(int32_t)
- */
-callback foo = void (int32_t x);
-
-/* TREE
- *Callback(foo)
- *  Type()
- *    PrimitiveType(void)
- *  Arguments()
- *    Argument(x)
- *      Type()
- *        PrimitiveType(int32_t)
- */
-typedef void foo(int32_t x);
-
-/* TREE
- *Typedef(MyLong)
- *  Type()
- *    PrimitiveType(int32_t)
- */
-typedef int32_t MyLong;
-
-/* TREE
- *Typedef(MyLongArray)
- *  Type()
- *    PrimitiveType(str_t)
- *    Array()
- */
-typedef str_t[] MyLongArray;
-
-/* TREE
- *Typedef(MyLongArray5)
- *  Type()
- *    PrimitiveType(mem_t)
- *    Array(5)
- */
-typedef mem_t[5] MyLongArray5;
-
-/* TREE
- *Typedef(MyLongArrayN5)
- *  Type()
- *    PrimitiveType(handle_t)
- *    Array()
- *      Array(5)
- */
-typedef handle_t[][5] MyLongArrayN5;
-
-
-/* TREE
- *Typedef(bar)
- *  Type()
- *    Typeref(foo)
- */
-typedef foo bar;
\ No newline at end of file
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 5633395..4952d9a 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -2589,6 +2589,12 @@
   <int value="4" label="Bad disable reasons"/>
 </enum>
 
+<enum name="BankNameDisplayedFormEvent">
+  <int value="0" label="Suggestions shown with bank name available (once)"/>
+  <int value="1"
+      label="Server suggestion filled with bank name available (once)"/>
+</enum>
+
 <enum name="BaseRelocationType">
   <int value="0" label="IMAGE_REL_BASED_ABSOLUTE"/>
   <int value="1" label="IMAGE_REL_BASED_HIGH"/>
@@ -15499,7 +15505,8 @@
   <int value="2024" label="DeprecatedTimingFunctionStepMiddle"/>
   <int value="2025" label="DocumentDomainSetWithNonDefaultPort"/>
   <int value="2026" label="DocumentDomainSetWithDefaultPort"/>
-  <int value="2027" label="HTMLMediaElementPlayRejected"/>
+  <int value="2027" label="FeaturePolicyHeader"/>
+  <int value="2028" label="FeaturePolicyAllowAttribute"/>
 </enum>
 
 <enum name="FeedbackSource">
@@ -22084,6 +22091,7 @@
   <int value="-1847835522" label="disable-touch-adjustment"/>
   <int value="-1847776781"
       label="enable-loading-ipc-optimization-for-small-resources"/>
+  <int value="-1839637286" label="AutofillCreditCardBankNameDisplay:disabled"/>
   <int value="-1838482444" label="disable-settings-window"/>
   <int value="-1835975804" label="disable-offline-auto-reload"/>
   <int value="-1833149810" label="enable-accessibility-tab-switcher"/>
@@ -22391,6 +22399,7 @@
   <int value="-770319039" label="enable-touch-editing"/>
   <int value="-763759697" label="enable-audio-support-for-desktop-share"/>
   <int value="-759830869" label="enable-tab-discarding"/>
+  <int value="-751273871" label="AutofillCreditCardBankNameDisplay:enabled"/>
   <int value="-750175757" label="ClientLoFi:enabled"/>
   <int value="-749048160" label="enable-panels"/>
   <int value="-747463111" label="ContentSuggestionsNotifications:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 4b29ebf..16c1736 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -4131,6 +4131,25 @@
   </details>
 </histogram>
 
+<histogram name="Autofill.FormEvents.CreditCard.BankNameDisplayed"
+    enum="BankNameDisplayedFormEvent">
+  <owner>szhangcs@google.com</owner>
+  <summary>
+    Autofill form events for credit card forms. These are recorded when the user
+    interacts with a form requesting a credit card, a dropdown of suggestions is
+    shown and at least one of the suggestions has a bank name. Form events are
+    logged at most once per page load.
+  </summary>
+  <details>
+    These metrics are used to measure the impact of the bank name experiment.
+    They are used to calculate the CTR of the autofill UI with bank names
+    available. Not all credit cards will have bank names even if we launch the
+    experiment. With these metrics we can run the experiment on 2 groups. For
+    one group, we will show bank names if available. For the other, we won't
+    shown.
+  </details>
+</histogram>
+
 <histogram name="Autofill.FormEvents.CreditCard.OnNonsecurePage"
     enum="AutofillFormEvent">
   <owner>estark@chromium.org</owner>
@@ -27210,6 +27229,22 @@
   </summary>
 </histogram>
 
+<histogram name="LocalStorageContext.CachePurgedInKB" units="KB">
+  <owner>mek@chromium.org</owner>
+  <summary>
+    Total size of the HTML5 LocalStorage browser-side cache purged in KB when
+    memory pressure signal was triggered or cache size hit the limits.
+  </summary>
+</histogram>
+
+<histogram name="LocalStorageContext.CacheSizeInKB" units="KB">
+  <owner>mek@chromium.org</owner>
+  <summary>
+    Total size of the HTML5 LocalStorage databases in KB in the browser-side
+    cache. Recorded each time a new database is opened in memory.
+  </summary>
+</histogram>
+
 <histogram name="LocalStorageContext.CommitResult" enum="LevelDBStatus">
   <owner>mek@chromium.org</owner>
   <summary>
@@ -65608,6 +65643,20 @@
   </summary>
 </histogram>
 
+<histogram name="Scheduling.PendingTreeRasterDuration" units="microseconds">
+  <owner>khushalsagar@chromium.org</owner>
+  <summary>
+    Time between starting raster work on the pending tree and when it is ready
+    to activate. Unlike PendingTreeDuration which - includes the time to commit
+    to this tree, the raster duration and the time for which the pending
+    tree waits before it can be activated - this only measures the time taken to
+    rasterize tiles required for activation.
+
+    The interval is recorded each time we are notifed that a pending tree is
+    ready for activation.
+  </summary>
+</histogram>
+
 <histogram name="Scheduling.PrepareTilesDuration" units="microseconds">
   <obsolete>
     Replaced by PrepareTIlesDuration2, due to inefficient bucketing scheme.
@@ -87635,6 +87684,7 @@
   <suffix name="Browser"/>
   <suffix name="Renderer"/>
   <affected-histogram name="Scheduling.PendingTreeDuration"/>
+  <affected-histogram name="Scheduling.PendingTreeRasterDuration"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="CompositorTimingHistoryProcessObsolete" separator="."
@@ -90000,6 +90050,7 @@
       label="Moderate purge was triggered on memory pressure."/>
   <suffix name="SizeLimitExceeded" label="Cache size exceeded limit."/>
   <affected-histogram name="LocalStorage.BrowserLocalStorageCachePurgedInKB"/>
+  <affected-histogram name="LocalStorageContext.CachePurgedInKB"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="LocalStorageSizes" separator="">
@@ -92638,6 +92689,10 @@
 <histogram_suffixes name="PageLoadMetricsCacheInfo" separator=".">
   <suffix name="NoStore" label="Main resource had cache-control: no-store"/>
   <affected-histogram
+      name="PageLoad.Clients.ServiceWorker.PaintTiming.NavigationToFirstContentfulPaint.LoadType.ForwardBackNavigation"/>
+  <affected-histogram
+      name="PageLoad.Clients.ServiceWorker.ParseTiming.NavigationToParseStart.LoadType.ForwardBackNavigation"/>
+  <affected-histogram
       name="PageLoad.PaintTiming.NavigationToFirstContentfulPaint"/>
   <affected-histogram
       name="PageLoad.PaintTiming.NavigationToFirstContentfulPaint.LoadType.ForwardBackNavigation"/>
@@ -93118,6 +93173,10 @@
       label="Restricted to new navigations (link clicks, URLs typed into the
              URL box, etc)."/>
   <affected-histogram
+      name="PageLoad.Clients.ServiceWorker.PaintTiming.NavigationToFirstContentfulPaint"/>
+  <affected-histogram
+      name="PageLoad.Clients.ServiceWorker.ParseTiming.NavigationToParseStart"/>
+  <affected-histogram
       name="PageLoad.Clients.SubresourceFilter.ActivationDecision"/>
   <affected-histogram name="PageLoad.Experimental.Bytes.Cache"/>
   <affected-histogram name="PageLoad.Experimental.Bytes.Network"/>
diff --git a/tools/perf/core/benchmark_sharding_map.json b/tools/perf/core/benchmark_sharding_map.json
index 7676a7a1..8227037 100644
--- a/tools/perf/core/benchmark_sharding_map.json
+++ b/tools/perf/core/benchmark_sharding_map.json
@@ -1992,8 +1992,7 @@
         "system_health.memory_desktop",
         "thread_times.key_mobile_sites_smooth",
         "thread_times.key_silk_cases",
-        "tracing.tracing_with_debug_overhead",
-        "v8.runtime_stats.top_25"
+        "tracing.tracing_with_debug_overhead"
       ]
     },
     "build149-m1": {
@@ -2005,7 +2004,6 @@
         "loading.desktop",
         "octane",
         "oortonline_tbmv2",
-        "page_cycler_v2.basic_oopif",
         "page_cycler_v2.intl_ar_fa_he",
         "power.idle_platform",
         "rasterize_and_record_micro.key_silk_cases",
@@ -2041,6 +2039,7 @@
         "image_decoding.image_decoding_measurement",
         "memory.dual_browser_test",
         "memory.long_running_idle_gmail_background_tbmv2",
+        "page_cycler_v2.basic_oopif",
         "page_cycler_v2.intl_hi_ru",
         "page_cycler_v2.intl_ja_zh",
         "page_cycler_v2.top_10_mobile",
@@ -2095,7 +2094,8 @@
         "system_health.webview_startup_multiprocess",
         "v8.browsing_desktop",
         "v8.detached_context_age_in_gc",
-        "v8.mobile_infinite_scroll-classic_tbmv2"
+        "v8.mobile_infinite_scroll-classic_tbmv2",
+        "v8.runtime_stats.top_25"
       ]
     },
     "build152-m1": {
@@ -2490,8 +2490,7 @@
         "system_health.memory_desktop",
         "thread_times.key_mobile_sites_smooth",
         "thread_times.key_silk_cases",
-        "tracing.tracing_with_debug_overhead",
-        "v8.runtime_stats.top_25"
+        "tracing.tracing_with_debug_overhead"
       ]
     },
     "build124-b1": {
@@ -2558,7 +2557,8 @@
         "v8.browsing_desktop_turbo",
         "v8.infinite_scroll-turbo_tbmv2",
         "v8.runtimestats.browsing_desktop_classic",
-        "v8.runtimestats.browsing_desktop_turbo"
+        "v8.runtimestats.browsing_desktop_turbo",
+        "v8.runtime_stats.top_25"
       ]
     },
     "build126-b1": {
diff --git a/tools/perf/page_sets/system_health/expectations.py b/tools/perf/page_sets/system_health/expectations.py
index ed17aec..2af2bd8 100644
--- a/tools/perf/page_sets/system_health/expectations.py
+++ b/tools/perf/page_sets/system_health/expectations.py
@@ -15,8 +15,6 @@
                       'crbug.com/712694')
     self.DisableStory('browse:tools:earth', [expectations.ALL],
                       'crbug.com/708590')
-    self.DisableStory('load:games:miniclip', [expectations.ALL_MAC],
-                      'crbug.com/664661')
     self.DisableStory('play:media:google_play_music', [expectations.ALL],
                       'crbug.com/649392')
     self.DisableStory('play:media:soundcloud', [expectations.ALL_WIN],
diff --git a/ui/app_list/BUILD.gn b/ui/app_list/BUILD.gn
index f70b844..8dd18cd 100644
--- a/ui/app_list/BUILD.gn
+++ b/ui/app_list/BUILD.gn
@@ -145,6 +145,8 @@
       "views/folder_header_view_delegate.h",
       "views/image_shadow_animator.cc",
       "views/image_shadow_animator.h",
+      "views/indicator_chip_view.cc",
+      "views/indicator_chip_view.h",
       "views/page_switcher.h",
       "views/page_switcher_horizontal.cc",
       "views/page_switcher_horizontal.h",
diff --git a/ui/app_list/app_list_constants.cc b/ui/app_list/app_list_constants.cc
index 56eff09..1a71df4 100644
--- a/ui/app_list/app_list_constants.cc
+++ b/ui/app_list/app_list_constants.cc
@@ -37,6 +37,13 @@
 const SkColor kResultURLTextColor = SkColorSetRGB(0x00, 0x99, 0x33);
 
 const SkColor kGridTitleColor = SkColorSetRGB(0x33, 0x33, 0x33);
+const SkColor kGridTitleColorFullscreen = SK_ColorWHITE;
+
+const int kGridTileWidth = 96;
+const int kGridTileHeight = 99;
+const int kGridIconTopPadding = 24;
+const int kGridTitleSpacing = 10;
+const int kGridTitleHorizontalPadding = 8;
 
 const SkColor kFolderTitleColor = SkColorSetRGB(0x33, 0x33, 0x33);
 const SkColor kFolderTitleHintTextColor = SkColorSetRGB(0xA0, 0xA0, 0xA0);
diff --git a/ui/app_list/app_list_constants.h b/ui/app_list/app_list_constants.h
index 90d93b17..f615589 100644
--- a/ui/app_list/app_list_constants.h
+++ b/ui/app_list/app_list_constants.h
@@ -40,6 +40,13 @@
 APP_LIST_EXPORT extern const SkColor kResultURLTextColor;
 
 APP_LIST_EXPORT extern const SkColor kGridTitleColor;
+APP_LIST_EXPORT extern const SkColor kGridTitleColorFullscreen;
+
+APP_LIST_EXPORT extern const int kGridTileWidth;
+APP_LIST_EXPORT extern const int kGridTileHeight;
+APP_LIST_EXPORT extern const int kGridIconTopPadding;
+APP_LIST_EXPORT extern const int kGridTitleSpacing;
+APP_LIST_EXPORT extern const int kGridTitleHorizontalPadding;
 
 APP_LIST_EXPORT extern const SkColor kFolderTitleColor;
 APP_LIST_EXPORT extern const SkColor kFolderTitleHintTextColor;
diff --git a/ui/app_list/views/app_list_item_view.cc b/ui/app_list/views/app_list_item_view.cc
index b3053fb..67b0d665 100644
--- a/ui/app_list/views/app_list_item_view.cc
+++ b/ui/app_list/views/app_list_item_view.cc
@@ -10,6 +10,7 @@
 #include "build/build_config.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/app_list/app_list_constants.h"
+#include "ui/app_list/app_list_features.h"
 #include "ui/app_list/app_list_folder_item.h"
 #include "ui/app_list/app_list_item.h"
 #include "ui/app_list/app_list_switches.h"
@@ -38,16 +39,16 @@
 
 namespace {
 
-const int kTopPadding = 18;
-const int kIconTitleSpacing = 6;
+constexpr int kTopPadding = 18;
+constexpr int kIconTitleSpacing = 6;
 
 // Radius of the folder dropping preview circle.
-const int kFolderPreviewRadius = 40;
+constexpr int kFolderPreviewRadius = 40;
 
-const int kLeftRightPaddingChars = 1;
+constexpr int kLeftRightPaddingChars = 1;
 
 // Delay in milliseconds of when the dragging UI should be shown for mouse drag.
-const int kMouseDragUIDelayInMs = 200;
+constexpr int kMouseDragUIDelayInMs = 200;
 
 gfx::FontList GetFontList() {
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
@@ -69,25 +70,31 @@
       icon_(new views::ImageView),
       title_(new views::Label),
       progress_bar_(new views::ProgressBar),
-      ui_state_(UI_STATE_NORMAL),
-      touch_dragging_(false),
       shadow_animator_(this),
-      is_installing_(false),
-      is_highlighted_(false) {
+      is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) {
   shadow_animator_.animation()->SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
   shadow_animator_.SetStartAndEndShadows(IconStartShadows(), IconEndShadows());
 
   icon_->set_can_process_events_within_subtree(false);
   icon_->SetVerticalAlignment(views::ImageView::LEADING);
 
-  title_->SetBackgroundColor(0);
+  title_->SetBackgroundColor(SK_ColorTRANSPARENT);
   title_->SetAutoColorReadabilityEnabled(false);
-  title_->SetEnabledColor(kGridTitleColor);
   title_->SetHandlesTooltips(false);
 
-  static const gfx::FontList font_list = GetFontList();
-  title_->SetFontList(font_list);
-  title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  if (is_fullscreen_app_list_enabled_) {
+    const gfx::FontList& base_font =
+        ui::ResourceBundle::GetSharedInstance().GetFontList(
+            ui::ResourceBundle::BaseFont);
+    title_->SetFontList(base_font.DeriveWithSizeDelta(1));
+    title_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+    title_->SetEnabledColor(kGridTitleColorFullscreen);
+  } else {
+    const gfx::FontList& font_list = GetFontList();
+    title_->SetFontList(font_list);
+    title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    title_->SetEnabledColor(kGridTitleColor);
+  }
   SetTitleSubpixelAA();
 
   AddChildView(icon_);
@@ -313,27 +320,54 @@
 
 void AppListItemView::Layout() {
   gfx::Rect rect(GetContentsBounds());
+  if (rect.IsEmpty())
+    return;
 
-  const int left_right_padding =
-      title_->font_list().GetExpectedTextWidth(kLeftRightPaddingChars);
-  rect.Inset(left_right_padding, kTopPadding, left_right_padding, 0);
-  const int y = rect.y();
+  if (is_fullscreen_app_list_enabled_) {
+    icon_->SetBoundsRect(GetIconBoundsForTargetViewBounds(GetContentsBounds()));
 
-  icon_->SetBoundsRect(GetIconBoundsForTargetViewBounds(GetContentsBounds()));
+    rect.Inset(kGridTitleHorizontalPadding,
+               kGridIconTopPadding + kGridIconDimension + kGridTitleSpacing,
+               kGridTitleHorizontalPadding, 0);
+    rect.set_height(title_->GetPreferredSize().height());
+    title_->SetBoundsRect(rect);
+    SetTitleSubpixelAA();
 
-  const gfx::Size title_size = title_->GetPreferredSize();
-  gfx::Rect title_bounds(rect.x() + (rect.width() - title_size.width()) / 2,
-                         y + kGridIconDimension + kIconTitleSpacing,
-                         title_size.width(), title_size.height());
-  title_bounds.Intersect(rect);
-  title_->SetBoundsRect(title_bounds);
-  SetTitleSubpixelAA();
+    gfx::Rect progress_bar_bounds(progress_bar_->GetPreferredSize());
+    progress_bar_bounds.set_x(
+        (GetContentsBounds().width() - progress_bar_bounds.width()) / 2);
+    progress_bar_bounds.set_y(rect.y());
+    progress_bar_->SetBoundsRect(progress_bar_bounds);
+  } else {
+    icon_->SetBoundsRect(GetIconBoundsForTargetViewBounds(GetContentsBounds()));
 
-  gfx::Rect progress_bar_bounds(progress_bar_->GetPreferredSize());
-  progress_bar_bounds.set_x(
-      (GetContentsBounds().width() - progress_bar_bounds.width()) / 2);
-  progress_bar_bounds.set_y(title_bounds.y());
-  progress_bar_->SetBoundsRect(progress_bar_bounds);
+    const int left_right_padding =
+        title_->font_list().GetExpectedTextWidth(kLeftRightPaddingChars);
+    rect.Inset(left_right_padding, kTopPadding, left_right_padding, 0);
+    const int y = rect.y();
+
+    const gfx::Size title_size = title_->GetPreferredSize();
+    gfx::Rect title_bounds(rect.x() + (rect.width() - title_size.width()) / 2,
+                           y + kGridIconDimension + kIconTitleSpacing,
+                           title_size.width(), title_size.height());
+    title_bounds.Intersect(rect);
+    title_->SetBoundsRect(title_bounds);
+    SetTitleSubpixelAA();
+
+    gfx::Rect progress_bar_bounds(progress_bar_->GetPreferredSize());
+    progress_bar_bounds.set_x(
+        (GetContentsBounds().width() - progress_bar_bounds.width()) / 2);
+    progress_bar_bounds.set_y(title_bounds.y());
+    progress_bar_->SetBoundsRect(progress_bar_bounds);
+  }
+}
+
+gfx::Size AppListItemView::CalculatePreferredSize() const {
+  if (is_fullscreen_app_list_enabled_) {
+    return gfx::Size(kGridTileWidth, kGridTileHeight);
+  }
+
+  return views::View::CalculatePreferredSize();
 }
 
 bool AppListItemView::OnKeyPressed(const ui::KeyEvent& event) {
@@ -460,7 +494,9 @@
 gfx::Rect AppListItemView::GetIconBoundsForTargetViewBounds(
     const gfx::Rect& target_bounds) {
   gfx::Rect rect(target_bounds);
-  rect.Inset(0, kTopPadding, 0, 0);
+  rect.Inset(
+      0, is_fullscreen_app_list_enabled_ ? kGridIconTopPadding : kTopPadding, 0,
+      0);
   rect.set_height(icon_->GetImage().height());
   rect.ClampToCenteredSize(icon_->GetImage().size());
   return rect;
diff --git a/ui/app_list/views/app_list_item_view.h b/ui/app_list/views/app_list_item_view.h
index 9670438..f77a9be3 100644
--- a/ui/app_list/views/app_list_item_view.h
+++ b/ui/app_list/views/app_list_item_view.h
@@ -127,6 +127,7 @@
   // views::View overrides:
   const char* GetClassName() const override;
   void Layout() override;
+  gfx::Size CalculatePreferredSize() const override;
   bool OnKeyPressed(const ui::KeyEvent& event) override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
   void OnMouseReleased(const ui::MouseEvent& event) override;
@@ -151,15 +152,17 @@
 
   std::unique_ptr<views::MenuRunner> context_menu_runner_;
 
-  UIState ui_state_;
+  UIState ui_state_ = UI_STATE_NORMAL;
 
   // True if scroll gestures should contribute to dragging.
-  bool touch_dragging_;
+  bool touch_dragging_ = false;
 
   ImageShadowAnimator shadow_animator_;
 
-  bool is_installing_;
-  bool is_highlighted_;
+  bool is_installing_ = false;
+  bool is_highlighted_ = false;
+
+  const bool is_fullscreen_app_list_enabled_;
 
   base::string16 tooltip_text_;
 
diff --git a/ui/app_list/views/indicator_chip_view.cc b/ui/app_list/views/indicator_chip_view.cc
new file mode 100644
index 0000000..e2fe267
--- /dev/null
+++ b/ui/app_list/views/indicator_chip_view.cc
@@ -0,0 +1,84 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/app_list/views/indicator_chip_view.h"
+
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/views/background.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/fill_layout.h"
+
+namespace app_list {
+
+namespace {
+
+constexpr int kIndicatorHeight = 16;
+constexpr int kHorizontalPadding = 16;
+constexpr int kBorderCornerRadius = 8;
+
+constexpr SkColor kLabelColor = SkColorSetARGBMacro(0x8A, 0xFF, 0xFF, 0xFF);
+constexpr SkColor kBackgroundColor =
+    SkColorSetARGBMacro(0x14, 0xFF, 0xFF, 0xFF);
+
+class IndicatorBackground : public views::Background {
+ public:
+  IndicatorBackground() = default;
+  ~IndicatorBackground() override = default;
+
+ private:
+  // views::Background overrides:
+  void Paint(gfx::Canvas* canvas, views::View* view) const override {
+    cc::PaintFlags flags;
+    flags.setStyle(cc::PaintFlags::kFill_Style);
+    flags.setColor(kBackgroundColor);
+    flags.setAntiAlias(true);
+    canvas->DrawRoundRect(view->GetContentsBounds(), kBorderCornerRadius,
+                          flags);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(IndicatorBackground);
+};
+
+}  // namespace
+
+IndicatorChipView::IndicatorChipView(const base::string16& text) {
+  container_ = new views::View();
+  container_->set_can_process_events_within_subtree(false);
+  AddChildView(container_);
+
+  container_->SetBackground(base::MakeUnique<IndicatorBackground>());
+
+  label_ = new views::Label(text);
+  const gfx::FontList& small_font =
+      ui::ResourceBundle::GetSharedInstance().GetFontList(
+          ui::ResourceBundle::SmallFont);
+  label_->SetFontList(small_font.DeriveWithSizeDelta(-2));
+  label_->SetEnabledColor(kLabelColor);
+  label_->SetAutoColorReadabilityEnabled(false);
+  label_->SetBackgroundColor(kBackgroundColor);
+  label_->SetHandlesTooltips(false);
+  label_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+
+  container_->AddChildView(label_);
+}
+
+IndicatorChipView::~IndicatorChipView() = default;
+
+gfx::Size IndicatorChipView::CalculatePreferredSize() const {
+  const int label_width = label_->GetPreferredSize().width();
+  return gfx::Size(
+      label_width + 2 * kHorizontalPadding + 2 * kBorderCornerRadius,
+      kIndicatorHeight);
+}
+
+void IndicatorChipView::Layout() {
+  const int label_width = label_->GetPreferredSize().width();
+  container_->SetSize(
+      gfx::Size(label_width + 2 * kHorizontalPadding, kIndicatorHeight));
+  label_->SetBoundsRect(container_->GetContentsBounds());
+}
+
+}  // namespace app_list
diff --git a/ui/app_list/views/indicator_chip_view.h b/ui/app_list/views/indicator_chip_view.h
new file mode 100644
index 0000000..edf7344
--- /dev/null
+++ b/ui/app_list/views/indicator_chip_view.h
@@ -0,0 +1,38 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_APP_LIST_VIEWS_INDICATOR_CHIP_VIEW_H_
+#define UI_APP_LIST_VIEWS_INDICATOR_CHIP_VIEW_H_
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "ui/views/view.h"
+
+namespace views {
+class Label;
+}  // namespace views
+
+namespace app_list {
+
+// IndicatorChipView consists of a label and is used on top of app tiles for
+// indication purpose.
+class IndicatorChipView : public views::View {
+ public:
+  explicit IndicatorChipView(const base::string16& text);
+  ~IndicatorChipView() override;
+
+ private:
+  // views::View overridden:
+  gfx::Size CalculatePreferredSize() const override;
+  void Layout() override;
+
+  views::View* container_ = nullptr;  // Owned by views hierarchy.
+  views::Label* label_ = nullptr;     // Owned by views hierarchy.
+
+  DISALLOW_COPY_AND_ASSIGN(IndicatorChipView);
+};
+
+}  // namespace app_list
+
+#endif  // UI_APP_LIST_VIEWS_INDICATOR_CHIP_VIEW_H_
diff --git a/ui/app_list/views/search_result_tile_item_view.cc b/ui/app_list/views/search_result_tile_item_view.cc
index 6df3a3b0..b5705e48 100644
--- a/ui/app_list/views/search_result_tile_item_view.cc
+++ b/ui/app_list/views/search_result_tile_item_view.cc
@@ -18,19 +18,11 @@
 
 namespace {
 
-constexpr int kRecommendationTileWidth = 96;
-constexpr int kRecommendationTileHeight = 99;
-constexpr int kRecommendationIconTopPadding = 24;
-constexpr int kRecommendationTitleSpacing = 10;
-constexpr int kRecommendationTileMaxWidth = 80;
-
 constexpr int kSearchTileWidth = 80;
 constexpr int kSearchTileHeight = 92;
 constexpr int kSearchTileTopPadding = 4;
 constexpr int kSearchTitleSpacing = 6;
 
-constexpr SkColor kRecommendationTileColor = SK_ColorWHITE;
-
 constexpr SkColor kSearchTitleColor =
     SkColorSetARGBMacro(0xDF, 0x00, 0x00, 0x00);
 constexpr SkColor kSearchAppRatingColor =
@@ -108,7 +100,7 @@
     // Customize title UI
     if (item_->display_type() == SearchResult::DISPLAY_RECOMMENDATION) {
       title()->SetFontList(base_font.DeriveWithSizeDelta(1));
-      title()->SetEnabledColor(kRecommendationTileColor);
+      title()->SetEnabledColor(kGridTitleColorFullscreen);
     } else if (item_->display_type() == SearchResult::DISPLAY_TILE) {
       title()->SetFontList(base_font.DeriveWithSizeDelta(1));
       title()->SetEnabledColor(kSearchTitleColor);
@@ -221,12 +213,13 @@
   }
 
   if (item_->display_type() == SearchResult::DISPLAY_RECOMMENDATION) {
-    rect.Inset(0, kRecommendationIconTopPadding, 0, 0);
+    rect.Inset(0, kGridIconTopPadding, 0, 0);
     icon()->SetBoundsRect(rect);
 
-    rect.Inset(0, kGridIconDimension + kRecommendationTitleSpacing, 0, 0);
+    rect.Inset(kGridTitleHorizontalPadding,
+               kGridIconDimension + kGridTitleSpacing,
+               kGridTitleHorizontalPadding, 0);
     rect.set_height(title()->GetPreferredSize().height());
-    rect.set_width(kRecommendationTileMaxWidth);
     title()->SetBoundsRect(rect);
   } else if (item_->display_type() == SearchResult::DISPLAY_TILE) {
     rect.Inset(0, kSearchTileTopPadding, 0, 0);
@@ -266,7 +259,7 @@
 gfx::Size SearchResultTileItemView::CalculatePreferredSize() const {
   if (is_fullscreen_app_list_enabled_ && item_) {
     if (item_->display_type() == SearchResult::DISPLAY_RECOMMENDATION)
-      return gfx::Size(kRecommendationTileWidth, kRecommendationTileHeight);
+      return gfx::Size(kGridTileWidth, kGridTileHeight);
     if (item_->display_type() == SearchResult::DISPLAY_TILE)
       return gfx::Size(kSearchTileWidth, kSearchTileHeight);
   }
diff --git a/ui/app_list/views/start_page_view.cc b/ui/app_list/views/start_page_view.cc
index e1319360..ed255d7 100644
--- a/ui/app_list/views/start_page_view.cc
+++ b/ui/app_list/views/start_page_view.cc
@@ -23,12 +23,15 @@
 #include "ui/app_list/views/app_list_main_view.h"
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/custom_launcher_page_view.h"
+#include "ui/app_list/views/indicator_chip_view.h"
 #include "ui/app_list/views/search_box_view.h"
 #include "ui/app_list/views/search_result_container_view.h"
 #include "ui/app_list/views/search_result_tile_item_view.h"
 #include "ui/app_list/views/tile_item_view.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/strings/grit/ui_strings.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
@@ -42,9 +45,12 @@
 namespace {
 
 // Layout constants.
+constexpr int kSearchBoxTopPadding = 24;
 constexpr int kInstantContainerSpacing = 24;
 constexpr int kSearchBoxAndTilesSpacing = 35;
+constexpr int kSearchBoxAndIndicatorSpacing = 21;
 constexpr int kStartPageSearchBoxWidth = 480;
+constexpr int kStartPageSearchBoxWidthFullscreen = 544;
 
 // WebView constants.
 constexpr int kWebViewWidth = 700;
@@ -285,15 +291,23 @@
           app_list_main_view->contents_view(),
           new AllAppsTileItemView(app_list_main_view_->contents_view(),
                                   app_list_view),
-          view_delegate)) {
+          view_delegate)),
+      is_fullscreen_app_list_enabled_(features::IsFullscreenAppListEnabled()) {
   search_box_spacer_view_->SetPreferredSize(gfx::Size(
-      kStartPageSearchBoxWidth,
+      is_fullscreen_app_list_enabled_ ? kStartPageSearchBoxWidthFullscreen
+                                      : kStartPageSearchBoxWidth,
       app_list_main_view->search_box_view()->GetPreferredSize().height()));
 
   // The view containing the start page WebContents and SearchBoxSpacerView.
   InitInstantContainer();
   AddChildView(instant_container_);
 
+  if (is_fullscreen_app_list_enabled_) {
+    indicator_ = new IndicatorChipView(
+        l10n_util::GetStringUTF16(IDS_SUGGESTED_APPS_INDICATOR));
+    AddChildView(indicator_);
+  }
+
   // The view containing the start page tiles.
   AddChildView(tiles_container_);
   AddChildView(custom_launcher_page_background_);
@@ -301,14 +315,18 @@
   tiles_container_->SetResults(view_delegate_->GetModel()->results());
 }
 
-StartPageView::~StartPageView() {
-}
+StartPageView::~StartPageView() = default;
 
 void StartPageView::InitInstantContainer() {
   views::BoxLayout* instant_layout_manager = new views::BoxLayout(
       views::BoxLayout::kVertical, gfx::Insets(), kInstantContainerSpacing);
-  instant_layout_manager->set_inside_border_insets(
-      gfx::Insets(0, 0, kSearchBoxAndTilesSpacing, 0));
+  if (is_fullscreen_app_list_enabled_) {
+    instant_layout_manager->set_inside_border_insets(
+        gfx::Insets(kSearchBoxTopPadding, 0, kSearchBoxAndIndicatorSpacing, 0));
+  } else {
+    instant_layout_manager->set_inside_border_insets(
+        gfx::Insets(0, 0, kSearchBoxAndTilesSpacing, 0));
+  }
   instant_layout_manager->set_main_axis_alignment(
       views::BoxLayout::MAIN_AXIS_ALIGNMENT_END);
   instant_layout_manager->set_cross_axis_alignment(
@@ -317,7 +335,7 @@
 
   // Create the view for the Google Doodle if the fullscreen launcher is not
   // enabled.
-  if (!features::IsFullscreenAppListEnabled()) {
+  if (!is_fullscreen_app_list_enabled_) {
     views::View* web_view = view_delegate_->CreateStartPageWebView(
         gfx::Size(kWebViewWidth, kWebViewHeight));
 
@@ -393,8 +411,17 @@
   bounds.set_height(instant_container_->GetHeightForWidth(bounds.width()));
   instant_container_->SetBoundsRect(bounds);
 
-  // Tiles begin where the instant container ends.
+  // For old launcher, tiles begin where the instant container ends; for
+  // fullscreen app list launcher, tiles begin where the |indicator_| ends.
   bounds.set_y(bounds.bottom());
+  if (indicator_) {
+    gfx::Rect indicator_rect(bounds);
+    indicator_rect.Offset(
+        (indicator_rect.width() - indicator_->GetPreferredSize().width()) / 2,
+        0);
+    indicator_->SetBoundsRect(indicator_rect);
+    bounds.Inset(0, indicator_->GetPreferredSize().height(), 0, 0);
+  }
   bounds.set_height(tiles_container_->GetHeightForWidth(bounds.width()));
   tiles_container_->SetBoundsRect(bounds);
 
diff --git a/ui/app_list/views/start_page_view.h b/ui/app_list/views/start_page_view.h
index ef68aa0d..21c51ff 100644
--- a/ui/app_list/views/start_page_view.h
+++ b/ui/app_list/views/start_page_view.h
@@ -19,6 +19,7 @@
 class AppListView;
 class AppListViewDelegate;
 class CustomLauncherPageBackgroundView;
+class IndicatorChipView;
 class SearchResultTileItemView;
 class TileItemView;
 
@@ -75,9 +76,12 @@
 
   views::View* instant_container_;  // Owned by views hierarchy.
   CustomLauncherPageBackgroundView*
-      custom_launcher_page_background_;       // Owned by view hierarchy.
+      custom_launcher_page_background_;       // Owned by views hierarchy.
+  IndicatorChipView* indicator_ = nullptr;    // Owned by views hierarchy.
   StartPageTilesContainer* tiles_container_;  // Owned by views hierarchy.
 
+  const bool is_fullscreen_app_list_enabled_;
+
   DISALLOW_COPY_AND_ASSIGN(StartPageView);
 };
 
diff --git a/ui/aura/client/aura_constants.cc b/ui/aura/client/aura_constants.cc
index 9b61b7a..2792820c 100644
--- a/ui/aura/client/aura_constants.cc
+++ b/ui/aura/client/aura_constants.cc
@@ -45,6 +45,7 @@
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHasBackdrop, false);
 DEFINE_UI_CLASS_PROPERTY_KEY(Window*, kHostWindowKey, nullptr);
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kImmersiveFullscreenKey, false);
+DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Size, kMinimumSize, nullptr);
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kMirroringEnabledKey, false);
 DEFINE_UI_CLASS_PROPERTY_KEY(WindowEmbedType,
                              kEmbedType,
diff --git a/ui/aura/client/aura_constants.h b/ui/aura/client/aura_constants.h
index cfe7c20..05c9b97 100644
--- a/ui/aura/client/aura_constants.h
+++ b/ui/aura/client/aura_constants.h
@@ -86,6 +86,9 @@
 // top portion of the window through a touch / mouse gesture.
 AURA_EXPORT extern const WindowProperty<bool>* const kImmersiveFullscreenKey;
 
+// A property key to store the minimum size of the window.
+AURA_EXPORT extern const WindowProperty<gfx::Size*>* const kMinimumSize;
+
 // A property key to indicate that a window is being "mirrored" and its contents
 // should render regardless of its actual visibility state.
 AURA_EXPORT extern const WindowProperty<bool>* const kMirroringEnabledKey;
diff --git a/ui/aura/mus/property_converter.cc b/ui/aura/mus/property_converter.cc
index 3fef63e..f4a6c7a 100644
--- a/ui/aura/mus/property_converter.cc
+++ b/ui/aura/mus/property_converter.cc
@@ -87,6 +87,8 @@
                        ui::mojom::WindowManager::kRestoreBounds_Property);
   RegisterSizeProperty(client::kPreferredSize,
                        ui::mojom::WindowManager::kPreferredSize_Property);
+  RegisterSizeProperty(client::kMinimumSize,
+                       ui::mojom::WindowManager::kMinimumSize_Property);
   RegisterStringProperty(client::kNameKey,
                          ui::mojom::WindowManager::kName_Property);
   RegisterString16Property(client::kTitleKey,
diff --git a/ui/display/display.h b/ui/display/display.h
index acb4535..2f30a59 100644
--- a/ui/display/display.h
+++ b/ui/display/display.h
@@ -66,6 +66,13 @@
     TOUCH_SUPPORT_UNAVAILABLE,
   };
 
+  // Accelerometer support for the display.
+  enum AccelerometerSupport {
+    ACCELEROMETER_SUPPORT_UNKNOWN,
+    ACCELEROMETER_SUPPORT_AVAILABLE,
+    ACCELEROMETER_SUPPORT_UNAVAILABLE,
+  };
+
   // Creates a display with kInvalidDisplayId as default.
   Display();
   explicit Display(int64_t id);
@@ -117,6 +124,13 @@
   TouchSupport touch_support() const { return touch_support_; }
   void set_touch_support(TouchSupport support) { touch_support_ = support; }
 
+  AccelerometerSupport accelerometer_support() const {
+    return accelerometer_support_;
+  }
+  void set_accelerometer_support(AccelerometerSupport support) {
+    accelerometer_support_ = support;
+  }
+
   // Utility functions that just return the size of display and
   // work area.
   const gfx::Size& size() const { return bounds_.size(); }
@@ -208,6 +222,7 @@
   float device_scale_factor_;
   Rotation rotation_ = ROTATE_0;
   TouchSupport touch_support_ = TOUCH_SUPPORT_UNKNOWN;
+  AccelerometerSupport accelerometer_support_ = ACCELEROMETER_SUPPORT_UNKNOWN;
   gfx::Size maximum_cursor_size_;
   // NOTE: this is not currently written to the mojom as it is not used in
   // aura.
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index 05825cac..7390e32 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -1391,6 +1391,14 @@
   new_display.set_rotation(display_info.GetActiveRotation());
   new_display.set_touch_support(display_info.touch_support());
   new_display.set_maximum_cursor_size(display_info.maximum_cursor_size());
+
+  if (internal_display_has_accelerometer_ && Display::IsInternalDisplayId(id)) {
+    new_display.set_accelerometer_support(
+        Display::ACCELEROMETER_SUPPORT_AVAILABLE);
+  } else {
+    new_display.set_accelerometer_support(
+        Display::ACCELEROMETER_SUPPORT_UNAVAILABLE);
+  }
   return new_display;
 }
 
diff --git a/ui/display/manager/display_manager.h b/ui/display/manager/display_manager.h
index f8e6c9f..8c28cbf 100644
--- a/ui/display/manager/display_manager.h
+++ b/ui/display/manager/display_manager.h
@@ -113,6 +113,10 @@
     configure_displays_ = configure_displays;
   }
 
+  void set_internal_display_has_accelerometer(bool has_accelerometer) {
+    internal_display_has_accelerometer_ = has_accelerometer;
+  }
+
   // Returns the display id of the first display in the outupt list.
   int64_t first_display_id() const { return first_display_id_; }
 
@@ -490,6 +494,8 @@
 
   bool unified_desktop_enabled_ = false;
 
+  bool internal_display_has_accelerometer_ = false;
+
   base::Closure created_mirror_window_;
 
   base::ObserverList<DisplayObserver> observers_;
diff --git a/ui/native_theme/native_theme_mac.mm b/ui/native_theme/native_theme_mac.mm
index 5b46fbb4..a88fafc 100644
--- a/ui/native_theme/native_theme_mac.mm
+++ b/ui/native_theme/native_theme_mac.mm
@@ -181,8 +181,12 @@
     // Buttons and labels.
     case kColorId_ButtonEnabledColor:
     case kColorId_LabelEnabledColor:
-    case kColorId_ProminentButtonColor:
       return NSSystemColorToSkColor([NSColor controlTextColor]);
+    // NSColor doesn't offer a color for prominent buttons. Use the Aura color,
+    // but apply the system tint. This is a good match for the blue Cocoa uses
+    // to draw buttons that are given a \n key equivalent.
+    case kColorId_ProminentButtonColor:
+      return ApplySystemControlTint(GetAuraColor(color_id, this));
     case kColorId_ButtonDisabledColor:
     case kColorId_LabelDisabledColor:
       return NSSystemColorToSkColor([NSColor disabledControlTextColor]);
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 78f9fe7..4672e6a 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -675,6 +675,12 @@
       </if>
 
       <!-- App list -->
+      <message name="IDS_SUGGESTED_APPS_INDICATOR" desc="Indicator text in app list on top of suggested apps.">
+        SUGGESTED APPS
+      </message>
+      <message name="IDS_ALL_APPS_INDICATOR" desc="Indicator text in app list on top of all apps.">
+        ALL APPS
+      </message>
       <message name="IDS_APP_LIST_BACK" desc="The name of the button that goes to the previous screen in the launcher.">
         Back
       </message>