diff --git a/DEPS b/DEPS
index abaaf49..c5265323 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'd1aeddeb8aa2d678a24511542935f1f33c2eebcd',
+  'skia_revision': '6162e03cd76aab55da4b579f4d5af72796a280f0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
diff --git a/base/memory/shared_memory_tracker.cc b/base/memory/shared_memory_tracker.cc
index 0ce272a..7f16a68 100644
--- a/base/memory/shared_memory_tracker.cc
+++ b/base/memory/shared_memory_tracker.cc
@@ -6,17 +6,38 @@
 
 #include "base/memory/shared_memory.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/trace_event/memory_allocator_dump_guid.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/process_memory_dump.h"
 
 namespace base {
 
+namespace {
+
+std::string GetDumpNameForTracing(const UnguessableToken& id) {
+  return "shared_memory/" + id.ToString();
+}
+}
+
 // static
 SharedMemoryTracker* SharedMemoryTracker::GetInstance() {
   static SharedMemoryTracker* instance = new SharedMemoryTracker;
   return instance;
 }
 
+// static
+trace_event::MemoryAllocatorDumpGuid SharedMemoryTracker::GetDumpGUIDForTracing(
+    const UnguessableToken& id) {
+  std::string dump_name = GetDumpNameForTracing(id);
+  return trace_event::MemoryAllocatorDumpGuid(dump_name);
+}
+
+// static
+trace_event::MemoryAllocatorDumpGuid
+SharedMemoryTracker::GetGlobalDumpGUIDForTracing(const UnguessableToken& id) {
+  return GetDumpGUIDForTracing(id);
+}
+
 void SharedMemoryTracker::IncrementMemoryUsage(
     const SharedMemory& shared_memory) {
   AutoLock hold(usages_lock_);
@@ -45,16 +66,16 @@
     const UnguessableToken& memory_guid = std::get<0>(usage);
     uintptr_t address = std::get<1>(usage);
     size_t size = std::get<2>(usage);
-    std::string dump_name = "shared_memory/";
+    std::string dump_name;
     if (memory_guid.is_empty()) {
       // TODO(hajimehoshi): As passing ID across mojo is not implemented yet
       // (crbug/713763), ID can be empty. For such case, use an address instead
       // of GUID so that approximate memory usages are available.
-      dump_name += Uint64ToString(address);
+      dump_name = "shared_memory/" + Uint64ToString(address);
     } else {
-      dump_name += memory_guid.ToString();
+      dump_name = GetDumpNameForTracing(memory_guid);
     }
-    auto dump_guid = trace_event::MemoryAllocatorDumpGuid(dump_name);
+    auto dump_guid = GetDumpGUIDForTracing(memory_guid);
     // Discard duplicates that might be seen in single-process mode.
     if (pmd->GetAllocatorDump(dump_name))
       continue;
diff --git a/base/memory/shared_memory_tracker.h b/base/memory/shared_memory_tracker.h
index 007bcd29..72ee114 100644
--- a/base/memory/shared_memory_tracker.h
+++ b/base/memory/shared_memory_tracker.h
@@ -14,6 +14,7 @@
 namespace base {
 
 namespace trace_event {
+class MemoryAllocatorDumpGuid;
 class ProcessMemoryDump;
 }
 
@@ -23,6 +24,12 @@
   // Returns a singleton instance.
   static SharedMemoryTracker* GetInstance();
 
+  static trace_event::MemoryAllocatorDumpGuid GetDumpGUIDForTracing(
+      const UnguessableToken& id);
+
+  static trace_event::MemoryAllocatorDumpGuid GetGlobalDumpGUIDForTracing(
+      const UnguessableToken& id);
+
   // Records shared memory usage on mapping.
   void IncrementMemoryUsage(const SharedMemory& shared_memory);
 
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index bde1f0c..0050c8e6 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -18,10 +18,12 @@
 #include "cc/debug/debug_colors.h"
 #include "cc/output/begin_frame_args.h"
 #include "cc/quads/texture_draw_quad.h"
+#include "cc/raster/scoped_gpu_raster.h"
 #include "cc/resources/memory_history.h"
 #include "cc/trees/frame_rate_counter.h"
 #include "cc/trees/layer_tree_host_impl.h"
 #include "cc/trees/layer_tree_impl.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkPaint.h"
@@ -95,9 +97,10 @@
   }
 
   auto resource = base::MakeUnique<ScopedResource>(resource_provider);
-  resource->Allocate(
-      internal_content_bounds_, ResourceProvider::TEXTURE_HINT_IMMUTABLE,
-      resource_provider->best_texture_format(), gfx::ColorSpace());
+  resource->Allocate(internal_content_bounds_,
+                     ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER,
+                     resource_provider->best_render_buffer_format(),
+                     gfx::ColorSpace());
   resources_.push_back(std::move(resource));
 }
 
@@ -157,48 +160,71 @@
 
 void HeadsUpDisplayLayerImpl::UpdateHudTexture(
     DrawMode draw_mode,
-    ResourceProvider* resource_provider) {
+    ResourceProvider* resource_provider,
+    ContextProvider* context_provider) {
   if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE || !resources_.back()->id())
     return;
 
-  SkISize canvas_size;
-  if (hud_surface_)
-    canvas_size = hud_surface_->getCanvas()->getBaseLayerSize();
-  else
-    canvas_size.set(0, 0);
+  if (context_provider) {
+    gpu::gles2::GLES2Interface* gl = context_provider->ContextGL();
+    DCHECK(gl);
+    ScopedGpuRaster gpu_raster(context_provider);
+    bool using_worker_context = false;
+    ResourceProvider::ScopedWriteLockGL lock(
+        resource_provider, resources_.back()->id(), using_worker_context);
 
-  if (canvas_size.width() != internal_content_bounds_.width() ||
-      canvas_size.height() != internal_content_bounds_.height() ||
-      !hud_surface_) {
-    TRACE_EVENT0("cc", "ResizeHudCanvas");
+    TRACE_EVENT_BEGIN0("cc", "CreateHudCanvas");
+    bool use_distance_field_text = false;
+    bool can_use_lcd_text = false;
+    int msaa_sample_count = 0;
+    ResourceProvider::ScopedSkSurfaceProvider scoped_surface(
+        context_provider, &lock, using_worker_context, use_distance_field_text,
+        can_use_lcd_text, msaa_sample_count);
+    SkCanvas* gpu_raster_canvas = scoped_surface.sk_surface()->getCanvas();
+    TRACE_EVENT_END0("cc", "CreateHudCanvas");
 
-    hud_surface_ = SkSurface::MakeRasterN32Premul(
-        internal_content_bounds_.width(), internal_content_bounds_.height());
-  }
+    UpdateHudContents();
 
-  UpdateHudContents();
+    DrawHudContents(gpu_raster_canvas);
 
-  {
-    TRACE_EVENT0("cc", "DrawHudContents");
-    hud_surface_->getCanvas()->clear(SkColorSetARGB(0, 0, 0, 0));
-    hud_surface_->getCanvas()->save();
-    hud_surface_->getCanvas()->scale(internal_contents_scale_,
-                                     internal_contents_scale_);
+    TRACE_EVENT_BEGIN0("cc", "UploadHudTexture");
+    const uint64_t fence = gl->InsertFenceSyncCHROMIUM();
+    gl->OrderingBarrierCHROMIUM();
+    gpu::SyncToken sync_token;
+    gl->GenSyncTokenCHROMIUM(fence, sync_token.GetData());
+    lock.set_sync_token(sync_token);
+    lock.set_synchronized(true);
+    TRACE_EVENT_END0("cc", "UploadHudTexture");
+  } else {
+    SkISize canvas_size;
+    if (hud_surface_)
+      canvas_size = hud_surface_->getCanvas()->getBaseLayerSize();
+    else
+      canvas_size.set(0, 0);
+
+    if (canvas_size.width() != internal_content_bounds_.width() ||
+        canvas_size.height() != internal_content_bounds_.height() ||
+        !hud_surface_) {
+      TRACE_EVENT0("cc", "ResizeHudCanvas");
+
+      hud_surface_ = SkSurface::MakeRasterN32Premul(
+          internal_content_bounds_.width(), internal_content_bounds_.height());
+    }
+
+    UpdateHudContents();
 
     DrawHudContents(hud_surface_->getCanvas());
 
-    hud_surface_->getCanvas()->restore();
+    TRACE_EVENT0("cc", "UploadHudTexture");
+    SkPixmap pixmap;
+    hud_surface_->peekPixels(&pixmap);
+    DCHECK(pixmap.addr());
+    DCHECK(pixmap.info().colorType() == kN32_SkColorType);
+    resource_provider->CopyToResource(
+        resources_.back()->id(), static_cast<const uint8_t*>(pixmap.addr()),
+        internal_content_bounds_);
+    resource_provider->GenerateSyncTokenForResource(resources_.back()->id());
   }
-
-  TRACE_EVENT0("cc", "UploadHudTexture");
-  SkPixmap pixmap;
-  hud_surface_->peekPixels(&pixmap);
-  DCHECK(pixmap.addr());
-  DCHECK(pixmap.info().colorType() == kN32_SkColorType);
-  resource_provider->CopyToResource(resources_.back()->id(),
-                                    static_cast<const uint8_t*>(pixmap.addr()),
-                                    internal_content_bounds_);
-  resource_provider->GenerateSyncTokenForResource(resources_.back()->id());
 }
 
 void HeadsUpDisplayLayerImpl::ReleaseResources() {
@@ -258,6 +284,11 @@
 void HeadsUpDisplayLayerImpl::DrawHudContents(SkCanvas* canvas) {
   const LayerTreeDebugState& debug_state = layer_tree_impl()->debug_state();
 
+  TRACE_EVENT0("cc", "DrawHudContents");
+  canvas->clear(SkColorSetARGB(0, 0, 0, 0));
+  canvas->save();
+  canvas->scale(internal_contents_scale_, internal_contents_scale_);
+
   if (debug_state.ShowHudRects()) {
     DrawDebugRects(canvas, layer_tree_impl()->debug_rect_history());
     if (IsAnimatingHUDContents()) {
@@ -265,8 +296,10 @@
     }
   }
 
-  if (!debug_state.show_fps_counter)
+  if (!debug_state.show_fps_counter) {
+    canvas->restore();
     return;
+  }
 
   SkRect area =
       DrawFPSDisplay(canvas, layer_tree_impl()->frame_rate_counter(), 0, 0);
@@ -275,6 +308,8 @@
 
   if (debug_state.ShowMemoryStats() && memory_entry_.total_bytes_used)
     DrawMemoryDisplay(canvas, 0, area.bottom(), SkMaxScalar(area.width(), 150));
+
+  canvas->restore();
 }
 int HeadsUpDisplayLayerImpl::MeasureText(SkPaint* paint,
                                          const std::string& text,
diff --git a/cc/layers/heads_up_display_layer_impl.h b/cc/layers/heads_up_display_layer_impl.h
index b808ebc..b9f6afcd 100644
--- a/cc/layers/heads_up_display_layer_impl.h
+++ b/cc/layers/heads_up_display_layer_impl.h
@@ -44,7 +44,8 @@
   void AppendQuads(RenderPass* render_pass,
                    AppendQuadsData* append_quads_data) override;
   void UpdateHudTexture(DrawMode draw_mode,
-                        ResourceProvider* resource_provider);
+                        ResourceProvider* resource_provider,
+                        ContextProvider* context_provider);
 
   void ReleaseResources() override;
 
diff --git a/cc/layers/heads_up_display_layer_impl_unittest.cc b/cc/layers/heads_up_display_layer_impl_unittest.cc
index 722e5615..ddfb058 100644
--- a/cc/layers/heads_up_display_layer_impl_unittest.cc
+++ b/cc/layers/heads_up_display_layer_impl_unittest.cc
@@ -18,18 +18,21 @@
 
 void CheckDrawLayer(HeadsUpDisplayLayerImpl* layer,
                     ResourceProvider* resource_provider,
+                    ContextProvider* context_provider,
                     DrawMode draw_mode) {
   std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
   AppendQuadsData data;
   bool will_draw = layer->WillDraw(draw_mode, resource_provider);
   if (will_draw)
     layer->AppendQuads(render_pass.get(), &data);
-  layer->UpdateHudTexture(draw_mode, resource_provider);
+  layer->UpdateHudTexture(draw_mode, resource_provider, context_provider);
   if (will_draw)
     layer->DidDraw(resource_provider);
 
   size_t expected_quad_list_size = will_draw ? 1 : 0;
   EXPECT_EQ(expected_quad_list_size, render_pass->quad_list.size());
+  EXPECT_EQ(0u, data.num_missing_tiles);
+  EXPECT_EQ(0u, data.num_incomplete_tiles);
 }
 
 TEST(HeadsUpDisplayLayerImplTest, ResourcelessSoftwareDrawAfterResourceLoss) {
@@ -51,15 +54,44 @@
   host_impl.pending_tree()->BuildLayerListAndPropertyTreesForTesting();
 
   // Check regular hardware draw is ok.
-  CheckDrawLayer(layer, host_impl.resource_provider(), DRAW_MODE_HARDWARE);
+  CheckDrawLayer(layer, host_impl.resource_provider(),
+                 compositor_frame_sink->context_provider(), DRAW_MODE_HARDWARE);
 
   // Simulate a resource loss on transitioning to resourceless software mode.
   layer->ReleaseResources();
 
   // Should skip resourceless software draw and not crash in UpdateHudTexture.
   CheckDrawLayer(layer, host_impl.resource_provider(),
+                 compositor_frame_sink->context_provider(),
                  DRAW_MODE_RESOURCELESS_SOFTWARE);
 }
 
+TEST(HeadsUpDisplayLayerImplTest, CPUAndGPURasterCanvas) {
+  FakeImplTaskRunnerProvider task_runner_provider;
+  TestTaskGraphRunner task_graph_runner;
+  std::unique_ptr<CompositorFrameSink> compositor_frame_sink =
+      FakeCompositorFrameSink::Create3d();
+  FakeLayerTreeHostImpl host_impl(&task_runner_provider, &task_graph_runner);
+  host_impl.CreatePendingTree();
+  host_impl.SetVisible(true);
+  host_impl.InitializeRenderer(compositor_frame_sink.get());
+  std::unique_ptr<HeadsUpDisplayLayerImpl> layer_ptr =
+      HeadsUpDisplayLayerImpl::Create(host_impl.pending_tree(), 1);
+  layer_ptr->SetBounds(gfx::Size(100, 100));
+
+  HeadsUpDisplayLayerImpl* layer = layer_ptr.get();
+
+  host_impl.pending_tree()->SetRootLayerForTesting(std::move(layer_ptr));
+  host_impl.pending_tree()->BuildLayerListAndPropertyTreesForTesting();
+
+  // Check Ganesh canvas drawing is ok.
+  CheckDrawLayer(layer, host_impl.resource_provider(),
+                 compositor_frame_sink->context_provider(), DRAW_MODE_HARDWARE);
+
+  // Check SW canvas drawing is ok.
+  CheckDrawLayer(layer, host_impl.resource_provider(), NULL,
+                 DRAW_MODE_SOFTWARE);
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/scheduler/begin_frame_source.cc b/cc/scheduler/begin_frame_source.cc
index 5b4cd40..c5ec595 100644
--- a/cc/scheduler/begin_frame_source.cc
+++ b/cc/scheduler/begin_frame_source.cc
@@ -337,7 +337,7 @@
 
   const BeginFrameArgs& last_args = obs->LastUsedBeginFrameArgs();
   if (last_args.IsValid() &&
-      last_begin_frame_args_.frame_time == last_args.frame_time) {
+      last_begin_frame_args_.frame_time <= last_args.frame_time) {
     return BeginFrameArgs();
   }
 
diff --git a/cc/scheduler/begin_frame_source_unittest.cc b/cc/scheduler/begin_frame_source_unittest.cc
index a4cf4b5..bff0d46 100644
--- a/cc/scheduler/begin_frame_source_unittest.cc
+++ b/cc/scheduler/begin_frame_source_unittest.cc
@@ -15,6 +15,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using testing::NiceMock;
+using testing::_;
 
 namespace cc {
 namespace {
@@ -573,5 +574,29 @@
   source2.OnBeginFrame(args);
 }
 
+// https://crbug.com/730218: Avoid DCHECK crash in
+// ExternalBeginFrameSource::GetMissedBeginFrameArgs.
+TEST_F(ExternalBeginFrameSourceTest, GetMissedBeginFrameArgs) {
+  BeginFrameArgs args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0,
+                                                       2, 10000, 10100, 100);
+  source_->OnBeginFrame(args);
+
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
+  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 0, 2, 10000, 10100, 100);
+  source_->AddObserver(obs_.get());
+  source_->RemoveObserver(obs_.get());
+
+  // Out of order frame_time. This might not be valid but still shouldn't
+  // cause a DCHECK in ExternalBeginFrameSource code.
+  args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, 9999, 10100,
+                                        101);
+  source_->OnBeginFrame(args);
+
+  EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1);
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
+  EXPECT_CALL(*obs_, OnBeginFrame(_)).Times(0);
+  source_->AddObserver(obs_.get());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/surfaces/surface_aggregator.cc b/cc/surfaces/surface_aggregator.cc
index 20d1ac71..e19a997 100644
--- a/cc/surfaces/surface_aggregator.cc
+++ b/cc/surfaces/surface_aggregator.cc
@@ -787,6 +787,7 @@
   std::vector<SurfaceId> surfaces_to_copy(
       prewalk_result->undrawn_surfaces.begin(),
       prewalk_result->undrawn_surfaces.end());
+  DCHECK(referenced_surfaces_.empty());
 
   for (size_t i = 0; i < surfaces_to_copy.size(); i++) {
     SurfaceId surface_id = surfaces_to_copy[i];
@@ -814,9 +815,10 @@
         }
       }
     } else {
-      auto it = referenced_surfaces_.insert(surface_id).first;
+      referenced_surfaces_.insert(surface_id);
       CopyPasses(frame, surface);
-      referenced_surfaces_.erase(it);
+      // CopyPasses may have mutated container, need to re-query to erase.
+      referenced_surfaces_.erase(referenced_surfaces_.find(surface_id));
     }
   }
 }
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 8ec875f65..1dd9f10 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -1224,7 +1224,7 @@
   int upload_scale_mip_level = CalculateUploadScaleMipLevel(draw_image);
   auto params = SkImage::DeferredTextureImageUsageParams(
       draw_image.matrix(), CalculateUploadScaleFilterQuality(draw_image),
-      upload_scale_mip_level);
+      upload_scale_mip_level, ResourceFormatToClosestSkColorType(format_));
   size_t data_size = draw_image.image()->getDeferredTextureImageData(
       *context_threadsafe_proxy_.get(), &params, 1, nullptr, nullptr);
 
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index b29d67a0..52b6bc4 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1707,8 +1707,9 @@
   // drawn.
   if (active_tree_->hud_layer()) {
     TRACE_EVENT0("cc", "DrawLayers.UpdateHudTexture");
-    active_tree_->hud_layer()->UpdateHudTexture(draw_mode,
-                                                resource_provider_.get());
+    active_tree_->hud_layer()->UpdateHudTexture(
+        draw_mode, resource_provider_.get(),
+        compositor_frame_sink_->context_provider());
   }
 
   CompositorFrameMetadata metadata = MakeCompositorFrameMetadata();
diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc
index c3584d3..557d88e 100644
--- a/cc/trees/layer_tree_host_unittest_context.cc
+++ b/cc/trees/layer_tree_host_unittest_context.cc
@@ -1001,11 +1001,6 @@
   DrawResult PrepareToDrawOnThread(LayerTreeHostImpl* host_impl,
                                    LayerTreeHostImpl::FrameData* frame,
                                    DrawResult draw_result) override {
-    if (host_impl->active_tree()->source_frame_number() == 2) {
-      // Lose the context during draw on the second commit. This will cause
-      // a third commit to recover.
-      context3d_->set_times_bind_texture_succeeds(0);
-    }
     return draw_result;
   }
 
@@ -1021,6 +1016,13 @@
 
   void DidCommitAndDrawFrame() override {
     ASSERT_TRUE(layer_tree_host()->hud_layer());
+
+    if (layer_tree_host()->SourceFrameNumber() == 2) {
+      // Lose the context after draw on the second commit. This will cause
+      // a third commit to recover.
+      context3d_->set_times_bind_texture_succeeds(0);
+    }
+
     // End the test once we know the 3nd frame drew.
     if (layer_tree_host()->SourceFrameNumber() < 5) {
       layer_tree_host()->root_layer()->SetNeedsDisplay();
diff --git a/chrome/VERSION b/chrome/VERSION
index d346d676..4680c1c 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=61
 MINOR=0
-BUILD=3124
+BUILD=3125
 PATCH=0
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 5d453a5..9c96342 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -6069,6 +6069,12 @@
   <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">
+    No thanks
+  </message>
+  <message name="IDS_VOICE_INTERACTION_VALUE_PROP_CONTINUE_BUTTION" desc="tmp">
+    Continue
+  </message>
 
   <!-- Print Job Notification -->
   <message name="IDS_PRINT_JOB_PRINTING_NOTIFICATION_TITLE" desc="Title of the printing-in-progress notification.">
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 49acb1b..4a65615 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -1867,11 +1867,6 @@
       </message>
 
       <!-- Bookmark app bubble -->
-      <if expr="is_macosx">
-        <message name="IDS_ADD_TO_APPLICATIONS_BUBBLE_TITLE" desc="Title of the bubble adding a bookmark app to the Applications Folder.">
-          Add to Applications
-        </message>
-      </if>
       <if expr="use_ash">
         <message name="IDS_ADD_TO_SHELF_BUBBLE_TITLE" desc="Title of the bubble for adding a bookmark app to the shelf.">
           Add to shelf
@@ -1882,11 +1877,16 @@
           Add to desktop
         </message>
       </if>
-      <if expr="use_titlecase">
+      <if expr="use_titlecase and not is_macosx">
         <message name="IDS_ADD_TO_DESKTOP_BUBBLE_TITLE" desc="Title of the bubble for adding a bookmark app to the desktop.">
           Add to Desktop
         </message>
       </if>
+      <if expr="is_macosx">
+        <message name="IDS_ADD_TO_DESKTOP_BUBBLE_TITLE" desc="Title of the bubble adding a bookmark app to the Applications Folder on Mac.">
+          Add to Applications
+        </message>
+      </if>
 
       <message name="IDS_BOOKMARK_APP_AX_BUBBLE_NAME_LABEL" desc="Text preceding the name of a bookmark app, read by spoken feedback.">
         Shortcut name
@@ -5382,6 +5382,9 @@
       <message name="IDS_OMNIBOX_ACCESSIBLE_ANSWER" desc="Readable text represening a query typed by the user in the omnibox, followed by an indication that an answer to that query will follow, followed by the answer. The commas are significant as they will introduce a pause in the spoken text.">
         <ph name="QUERY">$1<ex>weather in los angeles</ex></ph>, answer, <ph name="ANSWER">$2<ex>sunny and 84 degrees</ex></ph>
       </message>
+      <message name="IDS_OMNIBOX_CLEAR_ALL" desc="Accessibility text for the button that deletes all text input from the omnibox on touch devices.">
+        Clear input
+      </message>
 
       <!--Tooltip strings-->
       <message name="IDS_TOOLTIP_BACK" desc="The tooltip for back button">
diff --git a/chrome/browser/android/vr_shell/textures/ui_texture.cc b/chrome/browser/android/vr_shell/textures/ui_texture.cc
index ef2630d..8216da5 100644
--- a/chrome/browser/android/vr_shell/textures/ui_texture.cc
+++ b/chrome/browser/android/vr_shell/textures/ui_texture.cc
@@ -193,11 +193,13 @@
     bool found_name = GetFallbackFontNameForChar(default_font, c, "", &name);
     if (!found_name)
       return false;
-    if (name.empty())
+    if (!name.empty())
       names.insert(name);
   }
-  for (const auto& name : names)
+  for (const auto& name : names) {
+    DCHECK(!name.empty());
     fonts.push_back(gfx::Font(name, size));
+  }
   *font_list = gfx::FontList(fonts);
   return true;
 }
diff --git a/chrome/browser/android/vr_shell/textures/url_bar_texture_unittest.cc b/chrome/browser/android/vr_shell/textures/url_bar_texture_unittest.cc
index f6e2cc5..a7e7b726 100644
--- a/chrome/browser/android/vr_shell/textures/url_bar_texture_unittest.cc
+++ b/chrome/browser/android/vr_shell/textures/url_bar_texture_unittest.cc
@@ -54,6 +54,19 @@
     SetForceFontFallbackFailureForTesting(force);
   }
 
+  size_t GetNumberOfFontFallbacksForURL(const GURL& gurl) {
+    url::Parsed parsed;
+    const base::string16 text = url_formatter::FormatUrl(
+        gurl, url_formatter::kFormatUrlOmitAll, net::UnescapeRule::NORMAL,
+        &parsed, nullptr, nullptr);
+
+    gfx::FontList font_list;
+    if (!GetFontList(kUrlHeight, text, &font_list))
+      return 0;
+
+    return font_list.GetFonts().size();
+  }
+
   // Reports the last unsupported mode that was encountered. Returns kCount if
   // no unsupported mode was encountered.
   UiUnsupportedMode unsupported_mode() const { return unsupported_mode_; }
@@ -114,6 +127,12 @@
   MockRenderText mock_;
 };
 
+TEST(UrlBarTextureTest, WillNotFailOnNonAsciiURLs) {
+  TestUrlBarTexture texture;
+  EXPECT_EQ(3lu, texture.GetNumberOfFontFallbacksForURL(
+                     GURL("http://中央大学.ಠ_ಠ.tw/")));
+}
+
 TEST_F(UrlEmphasisTest, SecureHttpsHost) {
   EXPECT_CALL(mock_, SetColor(kDeemphasizedColor));
   EXPECT_CALL(mock_, ApplyColor(kEmphasizedColor, gfx::Range(8, 16)));
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index acfe027..7e3eb5f0 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -895,6 +895,9 @@
     "login/screens/user_image_view.h",
     "login/screens/user_selection_screen.cc",
     "login/screens/user_selection_screen.h",
+    "login/screens/voice_interaction_value_prop_screen.cc",
+    "login/screens/voice_interaction_value_prop_screen.h",
+    "login/screens/voice_interaction_value_prop_screen_view.h",
     "login/screens/wrong_hwid_screen.cc",
     "login/screens/wrong_hwid_screen.h",
     "login/screens/wrong_hwid_screen_view.h",
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc
index 288b8fb2..e623a1d 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -169,6 +169,8 @@
   registry->RegisterBooleanPref(prefs::kArcEnabled, false);
   registry->RegisterBooleanPref(prefs::kArcSignedIn, false);
   registry->RegisterBooleanPref(prefs::kArcTermsAccepted, false);
+  registry->RegisterBooleanPref(prefs::kArcVoiceInteractionValuePropAccepted,
+                                false);
   // Note that ArcBackupRestoreEnabled and ArcLocationServiceEnabled prefs have
   // to be off by default, until an explicit gesture from the user to enable
   // them is received. This is crucial in the cases when these prefs transition
diff --git a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.cc b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.cc
index 289aaf0..d3b1adb8 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.cc
@@ -305,6 +305,17 @@
   SetMetalayerVisibility(false);
 }
 
+void ArcVoiceInteractionFrameworkService::StartVoiceInteractionSetupWizard() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  arc::mojom::VoiceInteractionFrameworkInstance* framework_instance =
+      ARC_GET_INSTANCE_FOR_METHOD(
+          arc_bridge_service()->voice_interaction_framework(),
+          StartVoiceInteractionSetupWizard);
+  if (!framework_instance)
+    return;
+  framework_instance->StartVoiceInteractionSetupWizard();
+}
+
 void ArcVoiceInteractionFrameworkService::SetMetalayerVisibility(bool visible) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   mojom::VoiceInteractionFrameworkInstance* framework_instance =
diff --git a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h
index 1d44a4c6..652222a 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h
+++ b/chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h
@@ -77,6 +77,9 @@
   // initiated interaction. Logs UMA metric when it's not.
   bool ValidateTimeSinceUserInteraction();
 
+  // Start the voice interaction setup wizard in container.
+  void StartVoiceInteractionSetupWizard();
+
   // For supporting ArcServiceManager::GetService<T>().
   static const char kArcServiceName[];
 
diff --git a/chrome/browser/chromeos/first_run/first_run.cc b/chrome/browser/chromeos/first_run/first_run.cc
index e4ea95643..4ddc83d 100644
--- a/chrome/browser/chromeos/first_run/first_run.cc
+++ b/chrome/browser/chromeos/first_run/first_run.cc
@@ -8,6 +8,8 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/first_run/first_run_controller.h"
+#include "chrome/browser/chromeos/login/ui/login_display_host.h"
+#include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/prefs/pref_service_syncable_util.h"
@@ -102,7 +104,12 @@
     DCHECK(type == chrome::NOTIFICATION_SESSION_STARTED);
     DCHECK(content::Details<const user_manager::User>(details).ptr() ==
            ProfileHelper::Get()->GetUserByProfile(profile_));
-    TryLaunchFirstRunDialog(profile_);
+
+    // If voice interaction value prop has been accepted, the tutorial will be
+    // shown after the voice interaction OOBE flow.
+    if (!profile_->GetPrefs()->GetBoolean(
+            prefs::kArcVoiceInteractionValuePropAccepted))
+      TryLaunchFirstRunDialog(profile_);
     delete this;
   }
 
diff --git a/chrome/browser/chromeos/login/oobe_screen.cc b/chrome/browser/chromeos/login/oobe_screen.cc
index d54495a0..d140857 100644
--- a/chrome/browser/chromeos/login/oobe_screen.cc
+++ b/chrome/browser/chromeos/login/oobe_screen.cc
@@ -44,11 +44,12 @@
     "userBoard",                       // SCREEN_USER_SELECTION
     // SCREEN_ACTIVE_DIRECTORY_PASSWORD_CHANGE
     "ad-password-change",
-    "encryption-migration",  // SCREEN_ENCRYPTION_MIGRATION
-    "login",                 // SCREEN_SPECIAL_LOGIN
-    "oobe",                  // SCREEN_SPECIAL_OOBE
-    "test:nowindow",         // SCREEN_TEST_NO_WINDOW
-    "unknown",               // SCREEN_UNKNOWN
+    "encryption-migration",          // SCREEN_ENCRYPTION_MIGRATION
+    "voice-interaction-value-prop",  // SCREEN_VOICE_INTERACTION_VALUE_PROP
+    "login",                         // SCREEN_SPECIAL_LOGIN
+    "oobe",                          // SCREEN_SPECIAL_OOBE
+    "test:nowindow",                 // SCREEN_TEST_NO_WINDOW
+    "unknown",                       // SCREEN_UNKNOWN
 };
 
 static_assert(static_cast<size_t>(OobeScreen::SCREEN_UNKNOWN) ==
diff --git a/chrome/browser/chromeos/login/oobe_screen.h b/chrome/browser/chromeos/login/oobe_screen.h
index 6c74afa..bdacc3e 100644
--- a/chrome/browser/chromeos/login/oobe_screen.h
+++ b/chrome/browser/chromeos/login/oobe_screen.h
@@ -46,6 +46,7 @@
   SCREEN_USER_SELECTION,
   SCREEN_ACTIVE_DIRECTORY_PASSWORD_CHANGE,
   SCREEN_ENCRYPTION_MIGRATION,
+  SCREEN_VOICE_INTERACTION_VALUE_PROP,
 
   // Special "first screen" that initiates login flow.
   SCREEN_SPECIAL_LOGIN,
diff --git a/chrome/browser/chromeos/login/screens/screen_exit_code.cc b/chrome/browser/chromeos/login/screens/screen_exit_code.cc
index 87820726..0acb7054 100644
--- a/chrome/browser/chromeos/login/screens/screen_exit_code.cc
+++ b/chrome/browser/chromeos/login/screens/screen_exit_code.cc
@@ -60,6 +60,10 @@
       return "ARC_TERMS_OF_SERVICE_FINISHED";
     case ScreenExitCode::UPDATE_ERROR_UPDATING_CRITICAL_UPDATE:
       return "UPDATE_ERROR_UPDATING_CRITICAL_UPDATE";
+    case ScreenExitCode::VOICE_INTERACTION_VALUE_PROP_SKIPPED:
+      return "VOICE_INTERACTION_VALUE_PROP_SKIPPED";
+    case ScreenExitCode::VOICE_INTERACTION_VALUE_PROP_ACCEPTED:
+      return "VOICE_INTERACTION_VALUE_PROP_ACCEPTED";
     case ScreenExitCode::EXIT_CODES_COUNT:
     default:
       NOTREACHED();
diff --git a/chrome/browser/chromeos/login/screens/screen_exit_code.h b/chrome/browser/chromeos/login/screens/screen_exit_code.h
index 87a93c2..7742e348b 100644
--- a/chrome/browser/chromeos/login/screens/screen_exit_code.h
+++ b/chrome/browser/chromeos/login/screens/screen_exit_code.h
@@ -51,6 +51,8 @@
   UPDATE_ERROR_UPDATING_CRITICAL_UPDATE = 24,
   ENCRYPTION_MIGRATION_FINISHED = 25,
   ENCRYPTION_MIGRATION_SKIPPED = 26,
+  VOICE_INTERACTION_VALUE_PROP_SKIPPED = 27,
+  VOICE_INTERACTION_VALUE_PROP_ACCEPTED = 28,
   EXIT_CODES_COUNT  // not a real code, must be the last
 };
 
diff --git a/chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen.cc b/chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen.cc
new file mode 100644
index 0000000..c9a7fc3
--- /dev/null
+++ b/chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen.cc
@@ -0,0 +1,76 @@
+// 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/chromeos/login/screens/voice_interaction_value_prop_screen.h"
+
+#include "chrome/browser/chromeos/login/screens/base_screen_delegate.h"
+#include "chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen_view.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+
+namespace chromeos {
+namespace {
+
+constexpr const char kUserActionNoThanksPressed[] = "no-thanks-pressed";
+constexpr const char kUserActionContinuePressed[] = "continue-pressed";
+
+}  // namespace
+
+VoiceInteractionValuePropScreen::VoiceInteractionValuePropScreen(
+    BaseScreenDelegate* base_screen_delegate,
+    VoiceInteractionValuePropScreenView* view)
+    : BaseScreen(base_screen_delegate,
+                 OobeScreen::SCREEN_VOICE_INTERACTION_VALUE_PROP),
+      view_(view) {
+  DCHECK(view_);
+  if (view_)
+    view_->Bind(this);
+}
+
+VoiceInteractionValuePropScreen::~VoiceInteractionValuePropScreen() {
+  if (view_)
+    view_->Unbind();
+}
+
+void VoiceInteractionValuePropScreen::Show() {
+  if (!view_)
+    return;
+
+  view_->Show();
+}
+
+void VoiceInteractionValuePropScreen::Hide() {
+  if (view_)
+    view_->Hide();
+}
+
+void VoiceInteractionValuePropScreen::OnViewDestroyed(
+    VoiceInteractionValuePropScreenView* view) {
+  if (view_ == view)
+    view_ = nullptr;
+}
+
+void VoiceInteractionValuePropScreen::OnUserAction(
+    const std::string& action_id) {
+  if (action_id == kUserActionNoThanksPressed)
+    OnNoThanksPressed();
+  else if (action_id == kUserActionContinuePressed)
+    OnContinuePressed();
+  else
+    BaseScreen::OnUserAction(action_id);
+}
+
+void VoiceInteractionValuePropScreen::OnNoThanksPressed() {
+  Finish(ScreenExitCode::VOICE_INTERACTION_VALUE_PROP_SKIPPED);
+}
+
+void VoiceInteractionValuePropScreen::OnContinuePressed() {
+  ProfileManager::GetActiveUserProfile()->GetPrefs()->SetBoolean(
+      prefs::kArcVoiceInteractionValuePropAccepted, true);
+  Finish(ScreenExitCode::VOICE_INTERACTION_VALUE_PROP_ACCEPTED);
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen.h b/chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen.h
new file mode 100644
index 0000000..d7a6808d
--- /dev/null
+++ b/chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen.h
@@ -0,0 +1,43 @@
+// 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_CHROMEOS_LOGIN_SCREENS_VOICE_INTERACTION_VALUE_PROP_SCREEN_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_VOICE_INTERACTION_VALUE_PROP_SCREEN_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/base_screen.h"
+
+namespace chromeos {
+
+class VoiceInteractionValuePropScreenView;
+class BaseScreenDelegate;
+
+class VoiceInteractionValuePropScreen : public BaseScreen {
+ public:
+  VoiceInteractionValuePropScreen(BaseScreenDelegate* base_screen_delegate,
+                                  VoiceInteractionValuePropScreenView* view);
+  ~VoiceInteractionValuePropScreen() override;
+
+  // Called when view is destroyed so there's no dead reference to it.
+  void OnViewDestroyed(VoiceInteractionValuePropScreenView* view_);
+
+  // BaseScreen:
+  void Show() override;
+  void Hide() override;
+  void OnUserAction(const std::string& action_id) override;
+
+ private:
+  void OnNoThanksPressed();
+  void OnContinuePressed();
+
+  VoiceInteractionValuePropScreenView* view_;
+
+  DISALLOW_COPY_AND_ASSIGN(VoiceInteractionValuePropScreen);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_VOICE_INTERACTION_VALUE_PROP_SCREEN_H_
diff --git a/chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen_view.h b/chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen_view.h
new file mode 100644
index 0000000..bb403b7
--- /dev/null
+++ b/chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen_view.h
@@ -0,0 +1,37 @@
+// 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_CHROMEOS_LOGIN_SCREENS_VOICE_INTERACTION_VALUE_PROP_SCREEN_VIEW_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_VOICE_INTERACTION_VALUE_PROP_SCREEN_VIEW_H_
+
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+
+namespace chromeos {
+
+class VoiceInteractionValuePropScreen;
+
+// Interface for dependency injection between VoiceInteractionValuePropScreen
+// and its WebUI representation.
+class VoiceInteractionValuePropScreenView {
+ public:
+  constexpr static OobeScreen kScreenId =
+      OobeScreen::SCREEN_VOICE_INTERACTION_VALUE_PROP;
+
+  virtual ~VoiceInteractionValuePropScreenView() {}
+
+  virtual void Bind(VoiceInteractionValuePropScreen* screen) = 0;
+  virtual void Unbind() = 0;
+  virtual void Show() = 0;
+  virtual void Hide() = 0;
+
+ protected:
+  VoiceInteractionValuePropScreenView() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(VoiceInteractionValuePropScreenView);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_VOICE_INTERACTION_VALUE_PROP_SCREEN_VIEW_H_
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 4470346a..76a3644 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/arc/voice_interaction/arc_voice_interaction_framework_service.h"
 #include "chrome/browser/chromeos/customization/customization_document.h"
 #include "chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.h"
 #include "chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h"
@@ -52,6 +53,7 @@
 #include "chrome/browser/chromeos/login/screens/terms_of_service_screen.h"
 #include "chrome/browser/chromeos/login/screens/update_screen.h"
 #include "chrome/browser/chromeos/login/screens/user_image_screen.h"
+#include "chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen.h"
 #include "chrome/browser/chromeos/login/screens/wrong_hwid_screen.h"
 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
@@ -85,6 +87,8 @@
 #include "chromeos/settings/cros_settings_provider.h"
 #include "chromeos/settings/timezone_settings.h"
 #include "chromeos/timezone/timezone_provider.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/arc_service_manager.h"
 #include "components/crash/content/app/breakpad_linux.h"
 #include "components/pairing/bluetooth_controller_pairing_controller.h"
 #include "components/pairing/bluetooth_host_pairing_controller.h"
@@ -424,6 +428,9 @@
   } else if (screen == OobeScreen::SCREEN_ENCRYPTION_MIGRATION) {
     return new EncryptionMigrationScreen(
         this, oobe_ui_->GetEncryptionMigrationScreenView());
+  } else if (screen == OobeScreen::SCREEN_VOICE_INTERACTION_VALUE_PROP) {
+    return new VoiceInteractionValuePropScreen(
+        this, oobe_ui_->GetVoiceInteractionValuePropScreenView());
   }
 
   return nullptr;
@@ -545,27 +552,7 @@
 }
 
 void WizardController::ShowArcTermsOfServiceScreen() {
-  bool show_arc_terms = false;
-  const Profile* profile = ProfileManager::GetActiveUserProfile();
-
-  const base::CommandLine* command_line =
-      base::CommandLine::ForCurrentProcess();
-  if (!command_line->HasSwitch(chromeos::switches::kEnableArcOOBEOptIn)) {
-    VLOG(1) << "Skip ARC Terms of Service screen because ARC OOBE OptIn is "
-            << "disabled.";
-  } else if (!user_manager::UserManager::Get()->IsUserLoggedIn()) {
-    VLOG(1) << "Skip ARC Terms of Service screen because user is not "
-            << "logged in.";
-  } else if (!arc::IsArcAllowedForProfile(profile)) {
-    VLOG(1) << "Skip ARC Terms of Service screen because ARC is not allowed.";
-  } else if (profile->GetPrefs()->IsManagedPreference(prefs::kArcEnabled) &&
-             !profile->GetPrefs()->GetBoolean(prefs::kArcEnabled)) {
-    VLOG(1) << "Skip ARC Terms of Service screen because ARC is disabled.";
-  } else {
-    show_arc_terms = true;
-  }
-
-  if (show_arc_terms) {
+  if (ShouldShowArcTerms()) {
     VLOG(1) << "Showing ARC Terms of Service screen.";
     UpdateStatusAreaVisibilityForScreen(
         OobeScreen::SCREEN_ARC_TERMS_OF_SERVICE);
@@ -640,6 +627,18 @@
   SetCurrentScreen(GetScreen(OobeScreen::SCREEN_ENCRYPTION_MIGRATION));
 }
 
+void WizardController::ShowVoiceInteractionValuePropScreen() {
+  if (ShouldShowVoiceInteractionValueProp()) {
+    VLOG(1) << "Showing voice interaction value prop screen.";
+    UpdateStatusAreaVisibilityForScreen(
+        OobeScreen::SCREEN_VOICE_INTERACTION_VALUE_PROP);
+    SetCurrentScreen(
+        GetScreen(OobeScreen::SCREEN_VOICE_INTERACTION_VALUE_PROP));
+  } else {
+    OnOobeFlowFinished();
+  }
+}
+
 void WizardController::SkipToLoginForTesting(
     const LoginScreenContext& context) {
   VLOG(1) << "SkipToLoginForTesting.";
@@ -763,24 +762,7 @@
       return;
     }
   }
-  if (!time_oobe_started_.is_null()) {
-    base::TimeDelta delta = base::Time::Now() - time_oobe_started_;
-    UMA_HISTOGRAM_CUSTOM_TIMES(
-        "OOBE.BootToSignInCompleted",
-        delta,
-        base::TimeDelta::FromMilliseconds(10),
-        base::TimeDelta::FromMinutes(30),
-        100);
-    time_oobe_started_ = base::Time();
-  }
-
-  // Launch browser and delete login host controller.
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::Bind(&UserSessionManager::DoBrowserLaunch,
-                 base::Unretained(UserSessionManager::GetInstance()),
-                 ProfileManager::GetActiveUserProfile(), host_));
-  host_ = nullptr;
+  ShowVoiceInteractionValuePropScreen();
 }
 
 void WizardController::OnUserImageSkipped() {
@@ -851,6 +833,20 @@
   ShowUserImageScreen();
 }
 
+void WizardController::OnVoiceInteractionValuePropSkipped() {
+  OnOobeFlowFinished();
+}
+
+void WizardController::OnVoiceInteractionValuePropAccepted() {
+  // Start voice interaction setup wizard in container
+  arc::ArcVoiceInteractionFrameworkService* service =
+      arc::ArcServiceManager::Get()
+          ->GetService<arc::ArcVoiceInteractionFrameworkService>();
+  if (service)
+    service->StartVoiceInteractionSetupWizard();
+  OnOobeFlowFinished();
+}
+
 void WizardController::OnControllerPairingFinished() {
   ShowAutoEnrollmentCheckScreen();
 }
@@ -865,6 +861,24 @@
           weak_factory_.GetWeakPtr()));
 }
 
+void WizardController::OnOobeFlowFinished() {
+  if (!time_oobe_started_.is_null()) {
+    base::TimeDelta delta = base::Time::Now() - time_oobe_started_;
+    UMA_HISTOGRAM_CUSTOM_TIMES("OOBE.BootToSignInCompleted", delta,
+                               base::TimeDelta::FromMilliseconds(10),
+                               base::TimeDelta::FromMinutes(30), 100);
+    time_oobe_started_ = base::Time();
+  }
+
+  // Launch browser and delete login host controller.
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::Bind(&UserSessionManager::DoBrowserLaunch,
+                 base::Unretained(UserSessionManager::GetInstance()),
+                 ProfileManager::GetActiveUserProfile(), host_));
+  host_ = nullptr;
+}
+
 void WizardController::OnDeviceDisabledChecked(bool device_disabled) {
   prescribed_enrollment_config_ = g_browser_process->platform_part()
                                       ->browser_policy_connector_chromeos()
@@ -1089,6 +1103,8 @@
     ShowDeviceDisabledScreen();
   } else if (screen == OobeScreen::SCREEN_ENCRYPTION_MIGRATION) {
     ShowEncryptionMigrationScreen();
+  } else if (screen == OobeScreen::SCREEN_VOICE_INTERACTION_VALUE_PROP) {
+    ShowVoiceInteractionValuePropScreen();
   } else if (screen != OobeScreen::SCREEN_TEST_NO_WINDOW) {
     if (is_out_of_box_) {
       time_oobe_started_ = base::Time::Now();
@@ -1197,6 +1213,12 @@
     case ScreenExitCode::CONTROLLER_PAIRING_FINISHED:
       OnControllerPairingFinished();
       break;
+    case ScreenExitCode::VOICE_INTERACTION_VALUE_PROP_SKIPPED:
+      OnVoiceInteractionValuePropSkipped();
+      break;
+    case ScreenExitCode::VOICE_INTERACTION_VALUE_PROP_ACCEPTED:
+      OnVoiceInteractionValuePropAccepted();
+      break;
     default:
       NOTREACHED();
   }
@@ -1494,6 +1516,48 @@
       switches::kHostPairingOobe);
 }
 
+bool WizardController::ShouldShowArcTerms() const {
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(chromeos::switches::kEnableArcOOBEOptIn)) {
+    VLOG(1) << "Skip ARC Terms of Service screen because ARC OOBE OptIn is "
+            << "disabled.";
+    return false;
+  }
+  if (!user_manager::UserManager::Get()->IsUserLoggedIn()) {
+    VLOG(1) << "Skip ARC Terms of Service screen because user is not "
+            << "logged in.";
+    return false;
+  }
+
+  const Profile* profile = ProfileManager::GetActiveUserProfile();
+  if (!arc::IsArcAllowedForProfile(profile)) {
+    VLOG(1) << "Skip ARC Terms of Service screen because ARC is not allowed.";
+    return false;
+  }
+  if (profile->GetPrefs()->IsManagedPreference(prefs::kArcEnabled) &&
+      !profile->GetPrefs()->GetBoolean(prefs::kArcEnabled)) {
+    VLOG(1) << "Skip ARC Terms of Service screen because ARC is disabled.";
+    return false;
+  }
+  return true;
+}
+
+bool WizardController::ShouldShowVoiceInteractionValueProp() const {
+  if (!arc::IsArcPlayStoreEnabledForProfile(
+          ProfileManager::GetActiveUserProfile())) {
+    VLOG(1) << "Skip Voice Interaction Value Prop screen because Arc Terms is "
+            << "skipped.";
+    return false;
+  }
+  if (!chromeos::switches::IsVoiceInteractionEnabled()) {
+    VLOG(1) << "Skip Voice Interaction Value Prop screen because voice "
+            << "interaction service is disabled.";
+    return false;
+  }
+  return true;
+}
+
 void WizardController::MaybeStartListeningForSharkConnection() {
   // We shouldn't be here if we are running pairing OOBE already.
   if (IsControllerDetected())
diff --git a/chrome/browser/chromeos/login/wizard_controller.h b/chrome/browser/chromeos/login/wizard_controller.h
index da04696..4ed308a 100644
--- a/chrome/browser/chromeos/login/wizard_controller.h
+++ b/chrome/browser/chromeos/login/wizard_controller.h
@@ -152,6 +152,7 @@
   void ShowHostPairingScreen();
   void ShowDeviceDisabledScreen();
   void ShowEncryptionMigrationScreen();
+  void ShowVoiceInteractionValuePropScreen();
 
   // Shows images login screen.
   void ShowLoginScreen(const LoginScreenContext& context);
@@ -177,8 +178,11 @@
   void OnTermsOfServiceDeclined();
   void OnTermsOfServiceAccepted();
   void OnArcTermsOfServiceFinished();
+  void OnVoiceInteractionValuePropSkipped();
+  void OnVoiceInteractionValuePropAccepted();
   void OnControllerPairingFinished();
   void OnAutoEnrollmentCheckCompleted();
+  void OnOobeFlowFinished();
 
   // Callback invoked once it has been determined whether the device is disabled
   // or not.
@@ -292,6 +296,12 @@
   // detected or not.
   bool IsRemoraPairingOobe() const;
 
+  // Returns true if arc terms of service should be shown.
+  bool ShouldShowArcTerms() const;
+
+  // Returns true if voice interaction value prop should be shown.
+  bool ShouldShowVoiceInteractionValueProp() const;
+
   // Starts listening for an incoming shark controller connection, if we are
   // running remora OOBE.
   void MaybeStartListeningForSharkConnection();
diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
index ce47813..e183986 100644
--- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
@@ -1347,7 +1347,9 @@
 
 // TODO(fukino): Add tests for encryption migration UI.
 // http://crbug.com/706017
-static_assert(static_cast<int>(ScreenExitCode::EXIT_CODES_COUNT) == 27,
+
+// TODO(updowndota): Add tests for Voice Interaction value prop flow.
+static_assert(static_cast<int>(ScreenExitCode::EXIT_CODES_COUNT) == 29,
               "tests for new control flow are missing");
 
 }  // namespace chromeos
diff --git a/chrome/browser/extensions/bookmark_app_helper.cc b/chrome/browser/extensions/bookmark_app_helper.cc
index 1119372..9e9dd82 100644
--- a/chrome/browser/extensions/bookmark_app_helper.cc
+++ b/chrome/browser/extensions/bookmark_app_helper.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_service.h"
 #include "chrome/browser/ui/app_list/app_list_util.h"
+#include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/web_applications/web_app.h"
@@ -667,9 +668,10 @@
     OnBubbleCompleted(true, web_app_info_);
     return;
   }
-  browser->window()->ShowBookmarkAppBubble(
-      web_app_info_, base::Bind(&BookmarkAppHelper::OnBubbleCompleted,
-                                weak_factory_.GetWeakPtr()));
+  chrome::ShowBookmarkAppDialog(
+      browser->window()->GetNativeWindow(), web_app_info_,
+      base::Bind(&BookmarkAppHelper::OnBubbleCompleted,
+                 weak_factory_.GetWeakPtr()));
 }
 
 void BookmarkAppHelper::OnBubbleCompleted(
diff --git a/chrome/browser/extensions/bookmark_app_helper.h b/chrome/browser/extensions/bookmark_app_helper.h
index 174ede8d..414548933 100644
--- a/chrome/browser/extensions/bookmark_app_helper.h
+++ b/chrome/browser/extensions/bookmark_app_helper.h
@@ -109,8 +109,8 @@
   void CreateFromAppBanner(const CreateBookmarkAppCallback& callback,
                            const content::Manifest& manifest);
 
- private:
-  friend class TestBookmarkAppHelper;
+ protected:
+  // Protected methods for testing.
 
   // Called by the WebContents when the manifest has been downloaded. If there
   // is no manifest, or the WebContents is destroyed before the manifest could
@@ -119,9 +119,11 @@
                         const content::Manifest& manifest);
 
   // Performs post icon download tasks including installing the bookmark app.
-  void OnIconsDownloaded(bool success,
-                         const std::map<GURL, std::vector<SkBitmap> >& bitmaps);
+  virtual void OnIconsDownloaded(
+      bool success,
+      const std::map<GURL, std::vector<SkBitmap>>& bitmaps);
 
+ private:
   // Called after the bubble has been shown, and the user has either accepted or
   // the dialog was dismissed.
   void OnBubbleCompleted(bool user_accepted,
diff --git a/chrome/browser/extensions/bookmark_app_helper_browsertest.cc b/chrome/browser/extensions/bookmark_app_helper_browsertest.cc
new file mode 100644
index 0000000..065080e
--- /dev/null
+++ b/chrome/browser/extensions/bookmark_app_helper_browsertest.cc
@@ -0,0 +1,152 @@
+// 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/extensions/bookmark_app_helper.h"
+
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "chrome/common/render_messages.h"
+#include "content/public/browser/browser_message_filter.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+
+namespace extensions {
+namespace {
+
+content::RenderWidgetHost* GetActiveRenderWidgetHost(Browser* browser) {
+  return browser->tab_strip_model()
+      ->GetActiveWebContents()
+      ->GetRenderWidgetHostView()
+      ->GetRenderWidgetHost();
+}
+
+// Extends BookmarkAppHelper to see the call to OnIconsDownloaded.
+class TestBookmarkAppHelper : public BookmarkAppHelper {
+ public:
+  TestBookmarkAppHelper(Profile* profile,
+                        WebApplicationInfo web_app_info,
+                        content::WebContents* contents,
+                        base::Closure on_icons_downloaded_closure)
+      : BookmarkAppHelper(profile, web_app_info, contents),
+        on_icons_downloaded_closure_(on_icons_downloaded_closure) {}
+
+  // TestBookmarkAppHelper:
+  void OnIconsDownloaded(
+      bool success,
+      const std::map<GURL, std::vector<SkBitmap>>& bitmaps) override {
+    on_icons_downloaded_closure_.Run();
+    BookmarkAppHelper::OnIconsDownloaded(success, bitmaps);
+  }
+
+ private:
+  base::Closure on_icons_downloaded_closure_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestBookmarkAppHelper);
+};
+
+// Intercepts the ChromeViewHostMsg_DidGetWebApplicationInfo that would usually
+// get sent to extensions::TabHelper to create a BookmarkAppHelper that lets us
+// detect when icons are downloaded and the dialog is ready to show.
+class WebAppReadyMsgWatcher : public content::BrowserMessageFilter {
+ public:
+  explicit WebAppReadyMsgWatcher(Browser* browser)
+      : BrowserMessageFilter(ChromeMsgStart), browser_(browser) {}
+
+  content::WebContents* web_contents() {
+    return browser_->tab_strip_model()->GetActiveWebContents();
+  }
+
+  void OnDidGetWebApplicationInfo(const WebApplicationInfo& const_info) {
+    WebApplicationInfo info = const_info;
+    // Mimic extensions::TabHelper for fields missing from the manifest.
+    if (info.app_url.is_empty())
+      info.app_url = web_contents()->GetURL();
+    if (info.title.empty())
+      info.title = web_contents()->GetTitle();
+    if (info.title.empty())
+      info.title = base::UTF8ToUTF16(info.app_url.spec());
+
+    bookmark_app_helper_ = base::MakeUnique<TestBookmarkAppHelper>(
+        browser_->profile(), info, web_contents(), quit_closure_);
+    bookmark_app_helper_->Create(
+        base::Bind(&WebAppReadyMsgWatcher::FinishCreateBookmarkApp, this));
+  }
+
+  void FinishCreateBookmarkApp(const Extension* extension,
+                               const WebApplicationInfo& web_app_info) {
+    // ~WebAppReadyMsgWatcher() is called on the IO thread, but
+    // |bookmark_app_helper_| must be destroyed on the UI thread.
+    bookmark_app_helper_.reset();
+  }
+
+  void Wait() {
+    base::RunLoop run_loop;
+    quit_closure_ = run_loop.QuitClosure();
+    run_loop.Run();
+  }
+
+  // BrowserMessageFilter:
+  void OverrideThreadForMessage(const IPC::Message& message,
+                                content::BrowserThread::ID* thread) override {
+    if (message.type() == ChromeViewHostMsg_DidGetWebApplicationInfo::ID)
+      *thread = content::BrowserThread::UI;
+  }
+
+  bool OnMessageReceived(const IPC::Message& message) override {
+    bool handled = true;
+    IPC_BEGIN_MESSAGE_MAP(WebAppReadyMsgWatcher, message)
+      IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidGetWebApplicationInfo,
+                          OnDidGetWebApplicationInfo)
+      IPC_MESSAGE_UNHANDLED(handled = false)
+    IPC_END_MESSAGE_MAP()
+    return handled;
+  }
+
+ private:
+  ~WebAppReadyMsgWatcher() override {}
+
+  Browser* browser_;
+  base::Closure quit_closure_;
+  std::unique_ptr<TestBookmarkAppHelper> bookmark_app_helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebAppReadyMsgWatcher);
+};
+
+}  // namespace
+
+class BookmarkAppHelperTest : public DialogBrowserTest {
+ public:
+  BookmarkAppHelperTest() {}
+
+  // DialogBrowserTest:
+  void ShowDialog(const std::string& name) override {
+    ASSERT_TRUE(embedded_test_server()->Start());
+    AddTabAtIndex(
+        1,
+        GURL(embedded_test_server()->GetURL("/favicon/page_with_favicon.html")),
+        ui::PAGE_TRANSITION_LINK);
+
+    scoped_refptr<WebAppReadyMsgWatcher> filter =
+        new WebAppReadyMsgWatcher(browser());
+    GetActiveRenderWidgetHost(browser())->GetProcess()->AddFilter(filter.get());
+    chrome::ExecuteCommand(browser(), IDC_CREATE_HOSTED_APP);
+    filter->Wait();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BookmarkAppHelperTest);
+};
+
+IN_PROC_BROWSER_TEST_F(BookmarkAppHelperTest, InvokeDialog_create) {
+  RunDialog();
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/extensions/bookmark_app_helper_unittest.cc b/chrome/browser/extensions/bookmark_app_helper_unittest.cc
index 7294811..da3146b2 100644
--- a/chrome/browser/extensions/bookmark_app_helper_unittest.cc
+++ b/chrome/browser/extensions/bookmark_app_helper_unittest.cc
@@ -277,8 +277,6 @@
                                             expected_resized);
 }
 
-}  // namespace
-
 class TestBookmarkAppHelper : public BookmarkAppHelper {
  public:
   TestBookmarkAppHelper(ExtensionService* service,
@@ -311,6 +309,8 @@
   DISALLOW_COPY_AND_ASSIGN(TestBookmarkAppHelper);
 };
 
+}  // namespace
+
 TEST_F(BookmarkAppHelperExtensionServiceTest, CreateBookmarkApp) {
   WebApplicationInfo web_app_info;
   web_app_info.app_url = GURL(kAppUrl);
diff --git a/chrome/browser/notifications/platform_notification_service_impl.cc b/chrome/browser/notifications/platform_notification_service_impl.cc
index 5d82dad..cce7df8 100644
--- a/chrome/browser/notifications/platform_notification_service_impl.cc
+++ b/chrome/browser/notifications/platform_notification_service_impl.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -20,6 +21,7 @@
 #include "chrome/browser/notifications/notification_display_service_factory.h"
 #include "chrome/browser/notifications/notification_object_proxy.h"
 #include "chrome/browser/notifications/persistent_notification_delegate.h"
+#include "chrome/browser/permissions/permission_decision_auto_blocker.h"
 #include "chrome/browser/permissions/permission_manager.h"
 #include "chrome/browser/permissions/permission_result.h"
 #include "chrome/browser/profiles/profile.h"
@@ -293,7 +295,18 @@
   if (setting == CONTENT_SETTING_BLOCK)
     return blink::mojom::PermissionStatus::DENIED;
 
-  return blink::mojom::PermissionStatus::ASK;
+  // Check whether the permission has been embargoed (automatically blocked).
+  // TODO(crbug.com/658020): make PermissionManager::GetPermissionStatus thread
+  // safe so it isn't necessary to do this HostContentSettingsMap and embargo
+  // check outside of the permissions code.
+  PermissionResult result = PermissionDecisionAutoBlocker::GetEmbargoResult(
+      host_content_settings_map, origin, CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
+      base::Time::Now());
+  DCHECK(result.content_setting == CONTENT_SETTING_ASK ||
+         result.content_setting == CONTENT_SETTING_BLOCK);
+  return result.content_setting == CONTENT_SETTING_ASK
+             ? blink::mojom::PermissionStatus::ASK
+             : blink::mojom::PermissionStatus::DENIED;
 }
 
 void PlatformNotificationServiceImpl::DisplayNotification(
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker.cc b/chrome/browser/permissions/permission_decision_auto_blocker.cc
index 4b994ec..2ca703f6 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker.cc
+++ b/chrome/browser/permissions/permission_decision_auto_blocker.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/common/chrome_features.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/safe_browsing_db/database_manager.h"
 #include "components/variations/variations_associated_data.h"
@@ -197,6 +196,43 @@
 }
 
 // static
+PermissionResult PermissionDecisionAutoBlocker::GetEmbargoResult(
+    HostContentSettingsMap* settings_map,
+    const GURL& request_origin,
+    ContentSettingsType permission,
+    base::Time current_time) {
+  DCHECK(settings_map);
+  std::unique_ptr<base::DictionaryValue> dict =
+      GetOriginDict(settings_map, request_origin);
+  base::DictionaryValue* permission_dict = GetOrCreatePermissionDict(
+      dict.get(), PermissionUtil::GetPermissionString(permission));
+
+  if (IsUnderEmbargo(permission_dict, features::kPermissionsBlacklist,
+                     kPermissionBlacklistEmbargoKey, current_time,
+                     base::TimeDelta::FromDays(g_blacklist_embargo_days))) {
+    return PermissionResult(CONTENT_SETTING_BLOCK,
+                            PermissionStatusSource::SAFE_BROWSING_BLACKLIST);
+  }
+
+  if (IsUnderEmbargo(permission_dict, features::kBlockPromptsIfDismissedOften,
+                     kPermissionDismissalEmbargoKey, current_time,
+                     base::TimeDelta::FromDays(g_dismissal_embargo_days))) {
+    return PermissionResult(CONTENT_SETTING_BLOCK,
+                            PermissionStatusSource::MULTIPLE_DISMISSALS);
+  }
+
+  if (IsUnderEmbargo(permission_dict, features::kBlockPromptsIfIgnoredOften,
+                     kPermissionIgnoreEmbargoKey, current_time,
+                     base::TimeDelta::FromDays(g_ignore_embargo_days))) {
+    return PermissionResult(CONTENT_SETTING_BLOCK,
+                            PermissionStatusSource::MULTIPLE_IGNORES);
+  }
+
+  return PermissionResult(CONTENT_SETTING_ASK,
+                          PermissionStatusSource::UNSPECIFIED);
+}
+
+// static
 void PermissionDecisionAutoBlocker::UpdateFromVariations() {
   int dismissals_before_block = -1;
   int ignores_before_block = -1;
@@ -275,37 +311,9 @@
 PermissionResult PermissionDecisionAutoBlocker::GetEmbargoResult(
     const GURL& request_origin,
     ContentSettingsType permission) {
-  HostContentSettingsMap* map =
-      HostContentSettingsMapFactory::GetForProfile(profile_);
-  std::unique_ptr<base::DictionaryValue> dict =
-      GetOriginDict(map, request_origin);
-  base::DictionaryValue* permission_dict = GetOrCreatePermissionDict(
-      dict.get(), PermissionUtil::GetPermissionString(permission));
-
-  base::Time current_time = clock_->Now();
-  if (IsUnderEmbargo(permission_dict, features::kPermissionsBlacklist,
-                     kPermissionBlacklistEmbargoKey, current_time,
-                     base::TimeDelta::FromDays(g_blacklist_embargo_days))) {
-    return PermissionResult(CONTENT_SETTING_BLOCK,
-                            PermissionStatusSource::SAFE_BROWSING_BLACKLIST);
-  }
-
-  if (IsUnderEmbargo(permission_dict, features::kBlockPromptsIfDismissedOften,
-                     kPermissionDismissalEmbargoKey, current_time,
-                     base::TimeDelta::FromDays(g_dismissal_embargo_days))) {
-    return PermissionResult(CONTENT_SETTING_BLOCK,
-                            PermissionStatusSource::MULTIPLE_DISMISSALS);
-  }
-
-  if (IsUnderEmbargo(permission_dict, features::kBlockPromptsIfIgnoredOften,
-                     kPermissionIgnoreEmbargoKey, current_time,
-                     base::TimeDelta::FromDays(g_ignore_embargo_days))) {
-    return PermissionResult(CONTENT_SETTING_BLOCK,
-                            PermissionStatusSource::MULTIPLE_IGNORES);
-  }
-
-  return PermissionResult(CONTENT_SETTING_ASK,
-                          PermissionStatusSource::UNSPECIFIED);
+  return GetEmbargoResult(
+      HostContentSettingsMapFactory::GetForProfile(profile_), request_origin,
+      permission, clock_->Now());
 }
 
 int PermissionDecisionAutoBlocker::GetDismissCount(
diff --git a/chrome/browser/permissions/permission_decision_auto_blocker.h b/chrome/browser/permissions/permission_decision_auto_blocker.h
index 79bc114..89bc81c7 100644
--- a/chrome/browser/permissions/permission_decision_auto_blocker.h
+++ b/chrome/browser/permissions/permission_decision_auto_blocker.h
@@ -11,6 +11,7 @@
 #include "base/memory/singleton.h"
 #include "base/time/default_clock.h"
 #include "chrome/browser/permissions/permission_result.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -58,6 +59,17 @@
 
   static PermissionDecisionAutoBlocker* GetForProfile(Profile* profile);
 
+  // Checks the status of the content setting to determine if |request_origin|
+  // is under embargo for |permission|. This checks both embargo for Permissions
+  // Blacklisting and repeated dismissals. Prefer to use
+  // PermissionManager::GetPermissionStatus when possible. This method is only
+  // exposed to facilitate permission checks from threads other than the
+  // UI thread. See crbug.com/658020.
+  static PermissionResult GetEmbargoResult(HostContentSettingsMap* settings_map,
+                                           const GURL& request_origin,
+                                           ContentSettingsType permission,
+                                           base::Time current_time);
+
   // Updates the threshold to start blocking prompts from the field trial.
   static void UpdateFromVariations();
 
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.html b/chrome/browser/resources/chromeos/login/custom_elements_login.html
index 7e0334d..7ee55095 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_login.html
+++ b/chrome/browser/resources/chromeos/login/custom_elements_login.html
@@ -23,6 +23,7 @@
 <include src="oobe_dialog.html">
 <include src="oobe_reset.html">
 <include src="oobe_reset_confirmation_overlay.html">
+<include src="oobe_voice_interaction_value_prop.html">
 <include src="encryption_migration.html">
 
 <script src="chrome://oobe/custom_elements.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.js b/chrome/browser/resources/chromeos/login/custom_elements_login.js
index 65b9992..b1d2df30 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_login.js
+++ b/chrome/browser/resources/chromeos/login/custom_elements_login.js
@@ -24,3 +24,4 @@
 // <include src="oobe_reset.js">
 // <include src="oobe_reset_confirmation_overlay.js">
 // <include src="encryption_migration.js">
+// <include src="oobe_voice_interaction_value_prop.js">
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
index f1492550..10976d8 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
+++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
@@ -26,6 +26,7 @@
 <include src="oobe_i18n_dropdown.html">
 <include src="oobe_welcome_dialog.html">
 <include src="oobe_welcome.html">
+<include src="oobe_voice_interaction_value_prop.html">
 <include src="offline_ad_login.html">
 <include src="active_directory_password_change.html">
 <include src="arc_terms_of_service.html">
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
index 2c323dd..8edd2792 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
+++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
@@ -39,3 +39,4 @@
 // <include src="offline_ad_login.js">
 // <include src="active_directory_password_change.js">
 // <include src="arc_terms_of_service.js">
+// <include src="oobe_voice_interaction_value_prop.js">
diff --git a/chrome/browser/resources/chromeos/login/login.js b/chrome/browser/resources/chromeos/login/login.js
index 1ce1c5ec..0b54a70 100644
--- a/chrome/browser/resources/chromeos/login/login.js
+++ b/chrome/browser/resources/chromeos/login/login.js
@@ -52,6 +52,7 @@
       login.UnrecoverableCryptohomeErrorScreen.register();
       login.ActiveDirectoryPasswordChangeScreen.register(/* lazyInit= */ true);
       login.EncryptionMigrationScreen.register();
+      login.VoiceInteractionValuePropScreen.register();
 
       cr.ui.Bubble.decorate($('bubble'));
       login.HeaderBar.decorate($('login-header-bar'));
diff --git a/chrome/browser/resources/chromeos/login/login_non_lock_shared.html b/chrome/browser/resources/chromeos/login/login_non_lock_shared.html
index cfdc7ea..2f3923c5 100644
--- a/chrome/browser/resources/chromeos/login/login_non_lock_shared.html
+++ b/chrome/browser/resources/chromeos/login/login_non_lock_shared.html
@@ -19,6 +19,7 @@
 <link rel="stylesheet" href="oobe_screen_update.css">
 <link rel="stylesheet" href="oobe_screen_auto_enrollment_check.css">
 <link rel="stylesheet" href="oobe_screen_user_image.css">
+<link rel="stylesheet" href="oobe_screen_voice_interaction_value_prop.css">
 
 <link rel="stylesheet" href="screen_app_launch_splash.css">
 <link rel="stylesheet" href="screen_arc_kiosk_splash.css">
diff --git a/chrome/browser/resources/chromeos/login/login_non_lock_shared.js b/chrome/browser/resources/chromeos/login/login_non_lock_shared.js
index 0e7e2713..8a4a08b 100644
--- a/chrome/browser/resources/chromeos/login/login_non_lock_shared.js
+++ b/chrome/browser/resources/chromeos/login/login_non_lock_shared.js
@@ -16,6 +16,7 @@
 // <include src="oobe_screen_enable_kiosk.js">
 // <include src="oobe_screen_terms_of_service.js">
 // <include src="oobe_screen_user_image.js">
+// <include src="oobe_screen_voice_interaction_value_prop.js">
 
 // <include src="screen_app_launch_splash.js">
 // <include src="screen_arc_kiosk_splash.js">
diff --git a/chrome/browser/resources/chromeos/login/login_screens.html b/chrome/browser/resources/chromeos/login/login_screens.html
index 81eab0e..c2dd13e 100644
--- a/chrome/browser/resources/chromeos/login/login_screens.html
+++ b/chrome/browser/resources/chromeos/login/login_screens.html
@@ -3,6 +3,7 @@
 <include src="oobe_screen_enable_kiosk.html">
 <include src="oobe_screen_terms_of_service.html">
 <include src="oobe_screen_user_image.html">
+<include src="oobe_screen_voice_interaction_value_prop.html">
 <include src="../../../../../ui/login/account_picker/screen_account_picker.html">
 <include src="screen_arc_terms_of_service.html">
 <include src="screen_error_message.html">
diff --git a/chrome/browser/resources/chromeos/login/md_login.js b/chrome/browser/resources/chromeos/login/md_login.js
index e4e4e7d..6bd75aa 100644
--- a/chrome/browser/resources/chromeos/login/md_login.js
+++ b/chrome/browser/resources/chromeos/login/md_login.js
@@ -52,6 +52,7 @@
       login.UnrecoverableCryptohomeErrorScreen.register();
       login.ActiveDirectoryPasswordChangeScreen.register(/* lazyInit= */ true);
       login.EncryptionMigrationScreen.register();
+      login.VoiceInteractionValuePropScreen.register();
 
       cr.ui.Bubble.decorate($('bubble'));
       login.HeaderBar.decorate($('login-header-bar'));
diff --git a/chrome/browser/resources/chromeos/login/md_login_screens.html b/chrome/browser/resources/chromeos/login/md_login_screens.html
index dab643e..48c3aab 100644
--- a/chrome/browser/resources/chromeos/login/md_login_screens.html
+++ b/chrome/browser/resources/chromeos/login/md_login_screens.html
@@ -3,6 +3,7 @@
 <include src="oobe_screen_enable_kiosk.html">
 <include src="oobe_screen_terms_of_service.html">
 <include src="oobe_screen_user_image.html">
+<include src="oobe_screen_voice_interaction_value_prop.html">
 <include src="../../../../../ui/login/account_picker/md_screen_account_picker.html">
 <include src="screen_arc_terms_of_service.html">
 <include src="screen_error_message.html">
diff --git a/chrome/browser/resources/chromeos/login/oobe.js b/chrome/browser/resources/chromeos/login/oobe.js
index 14962b8..e240b1d2 100644
--- a/chrome/browser/resources/chromeos/login/oobe.js
+++ b/chrome/browser/resources/chromeos/login/oobe.js
@@ -134,6 +134,7 @@
       login.HostPairingScreen.register();
       login.DeviceDisabledScreen.register();
       login.ActiveDirectoryPasswordChangeScreen.register(/* lazyInit= */ true);
+      login.VoiceInteractionValuePropScreen.register();
 
       cr.ui.Bubble.decorate($('bubble'));
       login.HeaderBar.decorate($('login-header-bar'));
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog.css b/chrome/browser/resources/chromeos/login/oobe_dialog.css
index 5dabdd2..aea06c5c 100644
--- a/chrome/browser/resources/chromeos/login/oobe_dialog.css
+++ b/chrome/browser/resources/chromeos/login/oobe_dialog.css
@@ -14,9 +14,17 @@
   padding: 44px 64px 0 64px;
 }
 
+#footer-container[noFooter] {
+  padding: 0;
+}
+
 #oobe-bottom {
   box-shadow: 0 -1px 1px rgba(0, 0, 0, 0.14);
   height: 80px;
   padding: 0;
   z-index: 1;
 }
+
+#oobe-bottom[hideShadow] {
+  box-shadow: 0 0 0 rgba(0, 0, 0, 0);
+}
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog.html b/chrome/browser/resources/chromeos/login/oobe_dialog.html
index 7127a263..beb98fc 100644
--- a/chrome/browser/resources/chromeos/login/oobe_dialog.html
+++ b/chrome/browser/resources/chromeos/login/oobe_dialog.html
@@ -57,11 +57,13 @@
         <content select=".header"></content>
       </div>
     </div>
-    <div id="footer-container" class="flex layout vertical">
+    <div id="footer-container" noFooter$="[[noFooter]]"
+        class="flex layout vertical">
       <content select=".footer"></content>
     </div>
     <template is="dom-if" if="[[hasButtons]]">
-      <div id="oobe-bottom" class="layout horizontal center">
+      <div id="oobe-bottom" hideShadow$="[[hideShadow]]"
+          class="layout horizontal center">
         <content select=".bottom-buttons"></content>
       </div>
     </template>
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog.js b/chrome/browser/resources/chromeos/login/oobe_dialog.js
index 7dc9d22..41abb1d 100644
--- a/chrome/browser/resources/chromeos/login/oobe_dialog.js
+++ b/chrome/browser/resources/chromeos/login/oobe_dialog.js
@@ -15,6 +15,22 @@
     },
 
     /**
+     * Hide the box shadow on the top of oobe-bottom
+     */
+    hideShadow: {
+      type: Boolean,
+      value: false,
+    },
+
+    /**
+     * Control visibility of the footer container.
+     */
+    noFooter: {
+      type: Boolean,
+      value: false,
+    },
+
+    /**
      * Switches styles to "Welcome screen".
      */
     welcomeScreen: {
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
new file mode 100644
index 0000000..4758606
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_voice_interaction_value_prop.css
@@ -0,0 +1,10 @@
+/* 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. */
+
+#voice-interaction-value-prop {
+  display: flex;
+  flex-flow: column;
+  font-size: 16px;
+  width: 768px;
+}
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_voice_interaction_value_prop.html b/chrome/browser/resources/chromeos/login/oobe_screen_voice_interaction_value_prop.html
new file mode 100644
index 0000000..f32e62f6
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_voice_interaction_value_prop.html
@@ -0,0 +1,10 @@
+<!-- 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. -->
+
+<div class="step faded hidden no-logo" id="voice-interaction-value-prop"
+    role="group">
+  <voice-interaction-value-prop-md
+      id="voice-interaction-value-prop-md">
+  </voice-interaction-value-prop-md>
+</div>
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_voice_interaction_value_prop.js b/chrome/browser/resources/chromeos/login/oobe_screen_voice_interaction_value_prop.js
new file mode 100644
index 0000000..67e4b40
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/oobe_screen_voice_interaction_value_prop.js
@@ -0,0 +1,47 @@
+// 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.
+
+/**
+ * @fileoverview Oobe Voice Interaction Value Prop screen implementation.
+ */
+
+login.createScreen('VoiceInteractionValuePropScreen',
+    'voice-interaction-value-prop', function() {
+  return {
+
+    /** @Override */
+    onBeforeShow: function(data) {
+      var valueView = $('voice-interaction-value-prop-md').
+          getElement('value-prop-view');
+
+      valueView.addContentScripts([
+          {
+            name: 'stripLinks',
+            matches: ['<all_urls>'],
+            js: { code:
+              "document.querySelectorAll('a').forEach(" +
+                  "function(anchor){anchor.href='javascript:void(0)';})"
+            },
+            run_at: 'document_end'
+          }]);
+
+      // TODO(updowndota): provide static content later for the final fallback.
+      valueView.request.onHeadersReceived.addListener(function(details) {
+        if (details.statusCode == '404') {
+          if (valueView.src !=
+              'https://www.gstatic.com/opa-chromeos/oobe/en/value_proposition.html') {
+            valueView.src =
+              'https://www.gstatic.com/opa-chromeos/oobe/en/value_proposition.html';
+          }
+        }
+      }, {urls: ['<all_urls>'], types: ['main_frame']});
+
+      var locale = loadTimeData.getString('locale');
+      valueView.src = 'https://www.gstatic.com/opa-chromeos/oobe/' + locale
+          + '/value_proposition.html';
+
+      Oobe.getInstance().headerHidden = true;
+    }
+  };
+});
diff --git a/chrome/browser/resources/chromeos/login/oobe_screens.html b/chrome/browser/resources/chromeos/login/oobe_screens.html
index ec19bb07..2252e13 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screens.html
+++ b/chrome/browser/resources/chromeos/login/oobe_screens.html
@@ -11,6 +11,7 @@
 <include src="oobe_screen_auto_enrollment_check.html">
 <include src="oobe_screen_user_image.html">
 <include src="oobe_screen_hid_detection.html">
+<include src="oobe_screen_voice_interaction_value_prop.html">
 <include src="../../../../../ui/login/account_picker/screen_account_picker.html">
 <include src="screen_error_message.html">
 <include src="screen_arc_terms_of_service.html">
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
new file mode 100644
index 0000000..87b0f79
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.css
@@ -0,0 +1,23 @@
+/* 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. */
+
+oobe-text-button {
+  -webkit-margin-end: 16px;
+  color: #5a5a5a;
+}
+
+#value-prop-view {
+  display: block;
+  height: 496px;
+  margin: auto;
+  padding: 0;
+}
+
+#cancelButton {
+  -webkit-margin-end: 4px;
+}
+
+.subtitle {
+  line-height: 20px;
+}
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
new file mode 100644
index 0000000..1312074
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.html
@@ -0,0 +1,27 @@
+<!-- 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. -->
+
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html">
+
+<dom-module id="voice-interaction-value-prop-md">
+  <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>
+      <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_">
+          <div i18n-content="voiceInteractionValuePropNoThanksButton"></div>
+        </oobe-text-button>
+        <oobe-text-button inverse on-tap="onContinueTap_">
+          <div i18n-content="voiceInteractionValuePropContinueButton"></div>
+        </oobe-text-button>
+      </div>
+    </oobe-dialog>
+  </template>
+</dom-module>
diff --git a/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.js b/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.js
new file mode 100644
index 0000000..6a86000
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/oobe_voice_interaction_value_prop.js
@@ -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.
+
+/**
+ * @fileoverview Polymer element for displaying material design voice
+ * interaction value prop screen.
+ */
+
+Polymer({
+  is: 'voice-interaction-value-prop-md',
+
+  /**
+   * Returns element by its id.
+   */
+  getElement: function(id) {
+    return this.$[id];
+  },
+
+  /**
+   * On-tap event handler for no thanks button.
+   *
+   * @private
+   */
+  onNoThanksTap_: function() {
+    chrome.send('login.VoiceInteractionValuePropScreen.userActed',
+        ['no-thanks-pressed']);
+  },
+
+  /**
+   * On-tap event handler for continue button.
+   *
+   * @private
+   */
+  onContinueTap_: function() {
+    chrome.send('login.VoiceInteractionValuePropScreen.userActed',
+        ['continue-pressed']);
+  },
+});
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index 30bc22b4..6897affa 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -124,7 +124,6 @@
     FILE_SELECTION_COMPLETE:
         'print_preview.NativeLayer.FILE_SELECTION_COMPLETE',
     GET_CAPABILITIES_FAIL: 'print_preview.NativeLayer.GET_CAPABILITIES_FAIL',
-    LOCAL_DESTINATIONS_SET: 'print_preview.NativeLayer.LOCAL_DESTINATIONS_SET',
     MANIPULATE_SETTINGS_FOR_TEST:
         'print_preview.NativeLayer.MANIPULATE_SETTINGS_FOR_TEST',
     PAGE_COUNT_READY: 'print_preview.NativeLayer.PAGE_COUNT_READY',
@@ -224,8 +223,8 @@
     },
 
     /**
-     * Requests the system's local print destinations. A LOCAL_DESTINATIONS_SET
-     * event will be dispatched in response.
+     * Requests the system's local print destinations. The promise will be
+     * resolved with a list of the local destinations.
      * @return {!Promise<!Array<print_preview.LocalDestinationInfo>>}
      */
     getPrinters: function() {
diff --git a/chrome/browser/resources/settings/controls/extension_controlled_indicator.html b/chrome/browser/resources/settings/controls/extension_controlled_indicator.html
index 85702c8..ff53538c 100644
--- a/chrome/browser/resources/settings/controls/extension_controlled_indicator.html
+++ b/chrome/browser/resources/settings/controls/extension_controlled_indicator.html
@@ -21,7 +21,10 @@
         -webkit-margin-end: 16px;
       }
 
-      span {
+      /* Using ">" operator to ensure that this CSS rule will not accidentally
+       * be applied to a search highlight span (which is inserted dynamically if
+       * when search "hit" occurs within this element. */
+      :host > span {
         -webkit-margin-end: 8px;
         flex: 1;
       }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 89e2159..9dff41e 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -308,6 +308,8 @@
     "webui/chromeos/login/user_board_screen_handler.h",
     "webui/chromeos/login/user_image_screen_handler.cc",
     "webui/chromeos/login/user_image_screen_handler.h",
+    "webui/chromeos/login/voice_interaction_value_prop_screen_handler.cc",
+    "webui/chromeos/login/voice_interaction_value_prop_screen_handler.h",
     "webui/chromeos/login/wrong_hwid_screen_handler.cc",
     "webui/chromeos/login/wrong_hwid_screen_handler.h",
     "webui/chromeos/mobile_setup_dialog.cc",
@@ -1496,6 +1498,8 @@
       "views/exclusive_access_bubble_views.cc",
       "views/exclusive_access_bubble_views.h",
       "views/exclusive_access_bubble_views_context.h",
+      "views/extensions/bookmark_app_confirmation_view.cc",
+      "views/extensions/bookmark_app_confirmation_view.h",
       "views/extensions/chooser_dialog_view.cc",
       "views/extensions/chooser_dialog_view.h",
       "views/extensions/extension_install_dialog_view.cc",
@@ -1655,8 +1659,6 @@
     }
     if (enable_extensions && (!is_mac || mac_views_browser)) {
       sources += [
-        "views/extensions/bookmark_app_confirmation_view.cc",
-        "views/extensions/bookmark_app_confirmation_view.h",
         "views/extensions/browser_action_drag_data.cc",
         "views/extensions/browser_action_drag_data.h",
         "views/extensions/extension_action_platform_delegate_views.cc",
@@ -2143,6 +2145,8 @@
       "views/sad_tab_view.h",
       "views/tab_contents/chrome_web_contents_view_delegate_views.cc",
       "views/tab_contents/chrome_web_contents_view_delegate_views.h",
+      "views/theme_profile_key.cc",
+      "views/theme_profile_key.h",
       "window_sizer/window_sizer_aura.cc",
     ]
     deps += [
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h
index 6207ab0a..6445c77 100644
--- a/chrome/browser/ui/browser_dialogs.h
+++ b/chrome/browser/ui/browser_dialogs.h
@@ -25,6 +25,7 @@
 class GURL;
 class LoginHandler;
 class Profile;
+struct WebApplicationInfo;
 
 namespace content {
 class BrowserContext;
@@ -102,6 +103,21 @@
     const extensions::Extension* app,
     const base::Callback<void(bool /* created */)>& close_callback);
 
+// Callback type used with the ShowBookmarkAppDialog() method. The boolean
+// parameter is true when the user accepts the dialog. The WebApplicationInfo
+// parameter contains the WebApplicationInfo as edited by the user.
+using ShowBookmarkAppDialogCallback =
+    base::OnceCallback<void(bool, const WebApplicationInfo&)>;
+
+// Shows the Bookmark App bubble.
+// See Extension::InitFromValueFlags::FROM_BOOKMARK for a description of
+// bookmark apps.
+//
+// |web_app_info| is the WebApplicationInfo being converted into an app.
+void ShowBookmarkAppDialog(gfx::NativeWindow parent_window,
+                           const WebApplicationInfo& web_app_info,
+                           ShowBookmarkAppDialogCallback callback);
+
 // Shows a color chooser that reports to the given WebContents.
 content::ColorChooser* ShowColorChooser(content::WebContents* web_contents,
                                         SkColor initial_color);
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index ae556ea..954e5b4 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -32,8 +32,6 @@
 class StatusBubble;
 class ToolbarActionsBar;
 
-struct WebApplicationInfo;
-
 namespace autofill {
 class SaveCardBubbleController;
 class SaveCardBubbleView;
@@ -240,21 +238,6 @@
   // |already_bookmarked| is true if the url is already bookmarked.
   virtual void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) = 0;
 
-  // Callback type used with the ShowBookmarkAppBubble() method. The boolean
-  // parameter is true when the user accepts the dialog. The WebApplicationInfo
-  // parameter contains the WebApplicationInfo as edited by the user.
-  typedef base::Callback<void(bool, const WebApplicationInfo&)>
-      ShowBookmarkAppBubbleCallback;
-
-  // Shows the Bookmark App bubble.
-  // See Extension::InitFromValueFlags::FROM_BOOKMARK for a description of
-  // bookmark apps.
-  //
-  // |web_app_info| is the WebApplicationInfo being converted into an app.
-  virtual void ShowBookmarkAppBubble(
-      const WebApplicationInfo& web_app_info,
-      const ShowBookmarkAppBubbleCallback& callback) = 0;
-
   // Shows the "Save credit card" bubble.
   virtual autofill::SaveCardBubbleView* ShowSaveCreditCardBubble(
       content::WebContents* contents,
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.h b/chrome/browser/ui/cocoa/browser_window_cocoa.h
index d7a5dd2..57adf46 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.h
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.h
@@ -105,9 +105,6 @@
   bool IsToolbarShowing() const override;
   void ShowUpdateChromeDialog() override;
   void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) override;
-  void ShowBookmarkAppBubble(
-      const WebApplicationInfo& web_app_info,
-      const ShowBookmarkAppBubbleCallback& callback) override;
   autofill::SaveCardBubbleView* ShowSaveCreditCardBubble(
       content::WebContents* contents,
       autofill::SaveCardBubbleController* controller,
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
index 01d1e5e6..3ddde63b 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
@@ -14,7 +14,6 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/download/download_shelf.h"
-#include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/metrics/browser_window_histogram_helper.h"
 #include "chrome/browser/profiles/profile.h"
@@ -51,7 +50,6 @@
 #include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
 #include "chrome/browser/ui/profile_chooser_constants.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/web_app.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/features.h"
 #include "chrome/common/pref_names.h"
@@ -81,58 +79,6 @@
 using content::NativeWebKeyboardEvent;
 using content::WebContents;
 
-namespace {
-
-// These UI constants are used in BrowserWindowCocoa::ShowBookmarkAppBubble.
-// Used for defining the layout of the NSAlert and NSTextField within the
-// accessory view.
-const int kAppTextFieldVerticalSpacing = 2;
-const int kAppTextFieldWidth = 200;
-const int kAppTextFieldHeight = 22;
-const int kBookmarkAppBubbleViewWidth = 200;
-const int kBookmarkAppBubbleViewHeight = 46;
-
-const int kIconPreviewTargetSize = 128;
-
-base::string16 TrimText(NSString* controlText) {
-  base::string16 text = base::SysNSStringToUTF16(controlText);
-  base::TrimWhitespace(text, base::TRIM_ALL, &text);
-  return text;
-}
-
-}  // namespace
-
-@interface TextRequiringDelegate : NSObject<NSTextFieldDelegate> {
- @private
-  // Disable |control_| when text changes to just whitespace or empty string.
-  NSControl* control_;
-}
-- (id)initWithControl:(NSControl*)control text:(NSString*)text;
-- (void)controlTextDidChange:(NSNotification*)notification;
-@end
-
-@interface TextRequiringDelegate ()
-- (void)validateText:(NSString*)text;
-@end
-
-@implementation TextRequiringDelegate
-- (id)initWithControl:(NSControl*)control text:(NSString*)text {
-  if ((self = [super init])) {
-    control_ = control;
-    [self validateText:text];
-  }
-  return self;
-}
-
-- (void)controlTextDidChange:(NSNotification*)notification {
-  [self validateText:[[notification object] stringValue]];
-}
-
-- (void)validateText:(NSString*)text {
-  [control_ setEnabled:TrimText(text).empty() ? NO : YES];
-}
-@end
-
 BrowserWindowCocoa::BrowserWindowCocoa(Browser* browser,
                                        BrowserWindowController* controller)
   : browser_(browser),
@@ -556,83 +502,6 @@
                       alreadyBookmarked:(already_bookmarked ? YES : NO)];
 }
 
-void BrowserWindowCocoa::ShowBookmarkAppBubble(
-    const WebApplicationInfo& web_app_info,
-    const ShowBookmarkAppBubbleCallback& callback) {
-  base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
-  [alert setMessageText:l10n_util::GetNSString(
-      IDS_ADD_TO_APPLICATIONS_BUBBLE_TITLE)];
-  [alert setAlertStyle:NSInformationalAlertStyle];
-
-  NSButton* continue_button =
-      [alert addButtonWithTitle:l10n_util::GetNSString(IDS_OK)];
-  [continue_button setKeyEquivalent:kKeyEquivalentReturn];
-  NSButton* cancel_button =
-      [alert addButtonWithTitle:l10n_util::GetNSString(IDS_CANCEL)];
-  [cancel_button setKeyEquivalent:kKeyEquivalentEscape];
-
-  base::scoped_nsobject<NSButton> open_as_window_checkbox(
-      [[NSButton alloc] initWithFrame:NSZeroRect]);
-  [open_as_window_checkbox setButtonType:NSSwitchButton];
-  [open_as_window_checkbox
-      setTitle:l10n_util::GetNSString(IDS_BOOKMARK_APP_BUBBLE_OPEN_AS_WINDOW)];
-  [open_as_window_checkbox setState:web_app_info.open_as_window];
-  [open_as_window_checkbox sizeToFit];
-
-  base::scoped_nsobject<NSTextField> app_title([[NSTextField alloc]
-      initWithFrame:NSMakeRect(0, kAppTextFieldHeight +
-                                      kAppTextFieldVerticalSpacing,
-                               kAppTextFieldWidth, kAppTextFieldHeight)]);
-  NSString* original_title = SysUTF16ToNSString(web_app_info.title);
-  [[app_title cell] setWraps:NO];
-  [[app_title cell] setScrollable:YES];
-  [app_title setStringValue:original_title];
-  base::scoped_nsobject<TextRequiringDelegate> delegate(
-      [[TextRequiringDelegate alloc] initWithControl:continue_button
-                                                text:[app_title stringValue]]);
-  [app_title setDelegate:delegate];
-
-  base::scoped_nsobject<NSView> view([[NSView alloc]
-      initWithFrame:NSMakeRect(0, 0, kBookmarkAppBubbleViewWidth,
-                               kBookmarkAppBubbleViewHeight)]);
-
-  // When CanHostedAppsOpenInWindows() returns false, do not show the open as
-  // window checkbox to avoid confusing users.
-  if (extensions::util::CanHostedAppsOpenInWindows())
-    [view addSubview:open_as_window_checkbox];
-  [view addSubview:app_title];
-  [alert setAccessoryView:view];
-
-  // Find the image with target size.
-  // Assumes that the icons are sorted in ascending order of size.
-  if (!web_app_info.icons.empty()) {
-    for (const WebApplicationInfo::IconInfo& info : web_app_info.icons) {
-      if (info.width == kIconPreviewTargetSize &&
-          info.height == kIconPreviewTargetSize) {
-        gfx::Image icon_image = gfx::Image::CreateFrom1xBitmap(info.data);
-        [alert setIcon:icon_image.ToNSImage()];
-        break;
-      }
-    }
-  }
-
-  NSInteger response = [alert runModal];
-
-  // Prevent |app_title| from accessing |delegate| after it's destroyed.
-  [app_title setDelegate:nil];
-
-  if (response == NSAlertFirstButtonReturn) {
-    WebApplicationInfo updated_info = web_app_info;
-    updated_info.open_as_window = [open_as_window_checkbox state] == NSOnState;
-    updated_info.title = TrimText([app_title stringValue]);
-
-    callback.Run(true, updated_info);
-    return;
-  }
-
-  callback.Run(false, web_app_info);
-}
-
 autofill::SaveCardBubbleView* BrowserWindowCocoa::ShowSaveCreditCardBubble(
     content::WebContents* web_contents,
     autofill::SaveCardBubbleController* controller,
diff --git a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
index 09cd609..74adde50 100644
--- a/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
+++ b/chrome/browser/ui/views/chrome_browser_main_extra_parts_views_linux.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/libgtkui/gtk_ui.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/theme_profile_key.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "ui/aura/env.h"
@@ -33,8 +34,7 @@
   if (!window)
     return nullptr;
 
-  Profile* profile = reinterpret_cast<Profile*>(
-      window->GetNativeWindowProperty(Profile::kProfileKey));
+  Profile* profile = GetThemeProfileForWindow(window);
 
   // If using the system (GTK) theme, don't use an Aura NativeTheme at all.
   // NB: ThemeService::UsingSystemTheme() might lag behind this pref. See
diff --git a/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.cc b/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.cc
index 9cba2d5..afec849 100644
--- a/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.cc
+++ b/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.cc
@@ -8,7 +8,7 @@
 #include "base/strings/string16.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
-#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/constrained_window/constrained_window_views.h"
@@ -56,21 +56,11 @@
 
 BookmarkAppConfirmationView::~BookmarkAppConfirmationView() {}
 
-// static
-void BookmarkAppConfirmationView::CreateAndShow(
-    gfx::NativeWindow parent,
-    const WebApplicationInfo& web_app_info,
-    const BrowserWindow::ShowBookmarkAppBubbleCallback& callback) {
-  constrained_window::CreateBrowserModalDialogViews(
-      new BookmarkAppConfirmationView(web_app_info, callback), parent)
-      ->Show();
-}
-
 BookmarkAppConfirmationView::BookmarkAppConfirmationView(
     const WebApplicationInfo& web_app_info,
-    const BrowserWindow::ShowBookmarkAppBubbleCallback& callback)
+    chrome::ShowBookmarkAppDialogCallback callback)
     : web_app_info_(web_app_info),
-      callback_(callback),
+      callback_(std::move(callback)),
       open_as_window_checkbox_(nullptr),
       title_tf_(nullptr) {
   const ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get();
@@ -109,12 +99,16 @@
   layout->AddPaddingRow(
       0, layout_provider->GetDistanceMetric(DISTANCE_CONTROL_LIST_VERTICAL));
 
-  open_as_window_checkbox_ = new views::Checkbox(
-      l10n_util::GetStringUTF16(IDS_BOOKMARK_APP_BUBBLE_OPEN_AS_WINDOW));
-  open_as_window_checkbox_->SetChecked(web_app_info_.open_as_window);
-  layout->StartRow(0, column_set_id);
-  layout->SkipColumns(1);
-  layout->AddView(open_as_window_checkbox_);
+  // When CanHostedAppsOpenInWindows() returns false, do not show the open as
+  // window checkbox to avoid confusing users.
+  if (extensions::util::CanHostedAppsOpenInWindows()) {
+    open_as_window_checkbox_ = new views::Checkbox(
+        l10n_util::GetStringUTF16(IDS_BOOKMARK_APP_BUBBLE_OPEN_AS_WINDOW));
+    open_as_window_checkbox_->SetChecked(web_app_info_.open_as_window);
+    layout->StartRow(0, column_set_id);
+    layout->SkipColumns(1);
+    layout->AddView(open_as_window_checkbox_);
+  }
 
   title_tf_->SelectAll(true);
   chrome::RecordDialogCreation(
@@ -145,12 +139,13 @@
 
 void BookmarkAppConfirmationView::WindowClosing() {
   if (!callback_.is_null())
-    callback_.Run(false, web_app_info_);
+    std::move(callback_).Run(false, web_app_info_);
 }
 
 bool BookmarkAppConfirmationView::Accept() {
   web_app_info_.title = GetTrimmedTitle();
-  web_app_info_.open_as_window = open_as_window_checkbox_->checked();
+  web_app_info_.open_as_window =
+      open_as_window_checkbox_ && open_as_window_checkbox_->checked();
   base::ResetAndReturn(&callback_).Run(true, web_app_info_);
   return true;
 }
@@ -184,3 +179,16 @@
   base::TrimWhitespace(title, base::TRIM_ALL, &title);
   return title;
 }
+
+namespace chrome {
+
+void ShowBookmarkAppDialog(gfx::NativeWindow parent,
+                           const WebApplicationInfo& web_app_info,
+                           ShowBookmarkAppDialogCallback callback) {
+  constrained_window::CreateBrowserModalDialogViews(
+      new BookmarkAppConfirmationView(web_app_info, std::move(callback)),
+      parent)
+      ->Show();
+}
+
+}  // namespace chrome
diff --git a/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.h b/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.h
index d7c6b76a..529abb8 100644
--- a/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.h
+++ b/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "base/strings/string16.h"
-#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/common/web_application_info.h"
 #include "ui/views/controls/textfield/textfield_controller.h"
 #include "ui/views/window/dialog_delegate.h"
@@ -22,18 +22,11 @@
 class BookmarkAppConfirmationView : public views::DialogDelegateView,
                                     public views::TextfieldController {
  public:
+  BookmarkAppConfirmationView(const WebApplicationInfo& web_app_info,
+                              chrome::ShowBookmarkAppDialogCallback callback);
   ~BookmarkAppConfirmationView() override;
 
-  static void CreateAndShow(
-      gfx::NativeWindow parent,
-      const WebApplicationInfo& web_app_info,
-      const BrowserWindow::ShowBookmarkAppBubbleCallback& callback);
-
  private:
-  BookmarkAppConfirmationView(
-      const WebApplicationInfo& web_app_info,
-      const BrowserWindow::ShowBookmarkAppBubbleCallback& callback);
-
   // Overridden from views::WidgetDelegate:
   views::View* GetInitiallyFocusedView() override;
   ui::ModalType GetModalType() const override;
@@ -63,7 +56,7 @@
   WebApplicationInfo web_app_info_;
 
   // The callback to be invoked when the dialog is completed.
-  BrowserWindow::ShowBookmarkAppBubbleCallback callback_;
+  chrome::ShowBookmarkAppDialogCallback callback_;
 
   // Checkbox to launch as a window.
   views::Checkbox* open_as_window_checkbox_;
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 5f7dd2b..e9fe68b 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -64,7 +64,6 @@
 #include "chrome/browser/ui/views/download/download_in_progress_dialog_view.h"
 #include "chrome/browser/ui/views/download/download_shelf_view.h"
 #include "chrome/browser/ui/views/exclusive_access_bubble_views.h"
-#include "chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.h"
 #include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h"
 #include "chrome/browser/ui/views/find_bar_host.h"
 #include "chrome/browser/ui/views/frame/browser_view_layout.h"
@@ -153,6 +152,7 @@
 #endif  // !defined(OS_CHROMEOS)
 
 #if defined(USE_AURA)
+#include "chrome/browser/ui/views/theme_profile_key.h"
 #include "ui/aura/client/window_parenting_client.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
@@ -1192,13 +1192,6 @@
                                bookmark_bar_view_.get());
 }
 
-void BrowserView::ShowBookmarkAppBubble(
-    const WebApplicationInfo& web_app_info,
-    const ShowBookmarkAppBubbleCallback& callback) {
-  BookmarkAppConfirmationView::CreateAndShow(GetNativeWindow(), web_app_info,
-                                             callback);
-}
-
 autofill::SaveCardBubbleView* BrowserView::ShowSaveCreditCardBubble(
     content::WebContents* web_contents,
     autofill::SaveCardBubbleController* controller,
@@ -2079,6 +2072,12 @@
   GetWidget()->SetNativeWindowProperty(Profile::kProfileKey,
                                        browser_->profile());
 
+#if defined(USE_AURA)
+  // Stow a pointer to the browser's profile onto the window handle so
+  // that windows will be styled with the appropriate NativeTheme.
+  SetThemeProfileForWindow(GetNativeWindow(), browser_->profile());
+#endif
+
   LoadAccelerators();
 
   contents_web_view_ = new ContentsWebView(browser_->profile());
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 51d6159a..0b714ec4 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -315,9 +315,6 @@
   bool IsToolbarShowing() const override;
   void ShowUpdateChromeDialog() override;
   void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) override;
-  void ShowBookmarkAppBubble(
-      const WebApplicationInfo& web_app_info,
-      const ShowBookmarkAppBubbleCallback& callback) override;
   autofill::SaveCardBubbleView* ShowSaveCreditCardBubble(
       content::WebContents* contents,
       autofill::SaveCardBubbleController* controller,
diff --git a/chrome/browser/ui/views/location_bar/keyword_hint_view.cc b/chrome/browser/ui/views/location_bar/keyword_hint_view.cc
index 5a19d58..8d80d95 100644
--- a/chrome/browser/ui/views/location_bar/keyword_hint_view.cc
+++ b/chrome/browser/ui/views/location_bar/keyword_hint_view.cc
@@ -23,22 +23,6 @@
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/fill_layout.h"
 
-#if defined(USE_AURA)
-#include "ui/keyboard/keyboard_util.h"
-#endif
-
-namespace {
-
-bool IsVirtualKeyboardVisible() {
-#if defined(USE_AURA)
-  return keyboard::IsKeyboardVisible();
-#else
-  return false;
-#endif
-}
-
-}  // namespace
-
 KeywordHintView::KeywordHintView(views::ButtonListener* listener,
                                  Profile* profile,
                                  const gfx::FontList& font_list,
@@ -80,6 +64,13 @@
 KeywordHintView::~KeywordHintView() {}
 
 void KeywordHintView::SetKeyword(const base::string16& keyword) {
+  // When the virtual keyboard is visible, we show a modified touch UI
+  // containing only the chip and no surrounding labels.
+  const bool was_touch_ui = leading_label_->text().empty();
+  const bool is_touch_ui = LocationBarView::IsVirtualKeyboardVisible();
+  if (is_touch_ui == was_touch_ui && keyword_ == keyword)
+    return;
+
   keyword_ = keyword;
   if (keyword_.empty())
     return;
@@ -93,7 +84,7 @@
   base::string16 short_name(
       url_service->GetKeywordShortName(keyword, &is_extension_keyword));
 
-  if (IsVirtualKeyboardVisible()) {
+  if (is_touch_ui) {
     int message_id = is_extension_keyword
                          ? IDS_OMNIBOX_EXTENSION_KEYWORD_HINT_TOUCH
                          : IDS_OMNIBOX_KEYWORD_HINT_TOUCH;
diff --git a/chrome/browser/ui/views/location_bar/keyword_hint_view.h b/chrome/browser/ui/views/location_bar/keyword_hint_view.h
index 3f6b662..e9431e1 100644
--- a/chrome/browser/ui/views/location_bar/keyword_hint_view.h
+++ b/chrome/browser/ui/views/location_bar/keyword_hint_view.h
@@ -42,7 +42,6 @@
   ~KeywordHintView() override;
 
   void SetKeyword(const base::string16& keyword);
-  base::string16 keyword() const { return keyword_; }
 
  private:
   // views::View:
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 0d88605..f23d8467 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -12,6 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/command_updater.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
@@ -47,6 +48,7 @@
 #include "chrome/browser/ui/views/translate/translate_bubble_view.h"
 #include "chrome/browser/ui/views/translate/translate_icon_view.h"
 #include "chrome/grit/chromium_strings.h"
+#include "chrome/grit/generated_resources.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/favicon/content/content_favicon_driver.h"
 #include "components/omnibox/browser/omnibox_popup_model.h"
@@ -86,6 +88,8 @@
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/button_drag_utils.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/button/image_button_factory.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/widget/widget.h"
 
@@ -93,6 +97,10 @@
 #include "chrome/browser/ui/views/first_run_bubble.h"
 #endif
 
+#if defined(USE_AURA)
+#include "ui/keyboard/keyboard_util.h"
+#endif
+
 using content::WebContents;
 using views::View;
 
@@ -110,21 +118,8 @@
     : LocationBar(profile),
       ChromeOmniboxEditController(command_updater),
       browser_(browser),
-      omnibox_view_(nullptr),
       delegate_(delegate),
-      location_icon_view_(nullptr),
-      ime_inline_autocomplete_view_(nullptr),
-      selected_keyword_view_(nullptr),
-      keyword_hint_view_(nullptr),
-      zoom_view_(nullptr),
-      manage_passwords_icon_view_(nullptr),
-      save_credit_card_icon_view_(nullptr),
-      translate_icon_view_(nullptr),
-      star_view_(nullptr),
-      size_animation_(this),
-      is_popup_mode_(is_popup_mode),
-      show_focus_rect_(false),
-      template_url_service_(NULL) {
+      is_popup_mode_(is_popup_mode) {
   edit_bookmarks_enabled_.Init(
       bookmarks::prefs::kEditBookmarksEnabled, profile->GetPrefs(),
       base::Bind(&LocationBarView::UpdateWithoutTabRestore,
@@ -248,6 +243,12 @@
   star_view_->SetVisible(false);
   AddChildView(star_view_);
 
+  clear_all_button_ = views::CreateVectorImageButton(this);
+  clear_all_button_->SetTooltipText(
+      l10n_util::GetStringUTF16(IDS_OMNIBOX_CLEAR_ALL));
+  RefreshClearAllButtonIcon();
+  AddChildView(clear_all_button_);
+
   // Initialize the location entry. We do this to avoid a black flash which is
   // visible when the location entry has just been initialized.
   Update(nullptr);
@@ -539,8 +540,12 @@
     trailing_decorations.AddDecoration(vertical_padding, location_height, true,
                                        0, item_padding, item_padding,
                                        keyword_hint_view_);
-    if (keyword_hint_view_->keyword() != keyword)
-      keyword_hint_view_->SetKeyword(keyword);
+    keyword_hint_view_->SetKeyword(keyword);
+  }
+
+  if (clear_all_button_->visible()) {
+    trailing_decorations.AddDecoration(vertical_padding, location_height,
+                                       clear_all_button_);
   }
 
   const int edge_thickness = GetHorizontalEdgeThickness();
@@ -586,6 +591,7 @@
 
 void LocationBarView::OnNativeThemeChanged(const ui::NativeTheme* theme) {
   RefreshLocationIcon();
+  RefreshClearAllButtonIcon();
   if (is_popup_mode_) {
     SetBackground(views::CreateSolidBackground(GetColor(BACKGROUND)));
   } else {
@@ -708,11 +714,23 @@
 
 void LocationBarView::ButtonPressed(views::Button* sender,
                                     const ui::Event& event) {
-  DCHECK_EQ(keyword_hint_view_, sender);
   DCHECK(event.IsMouseEvent() || event.IsGestureEvent());
-  omnibox_view_->model()->AcceptKeyword(
-      event.IsMouseEvent() ? KeywordModeEntryMethod::CLICK_ON_VIEW
-                           : KeywordModeEntryMethod::TAP_ON_VIEW);
+  if (keyword_hint_view_ == sender) {
+    omnibox_view_->model()->AcceptKeyword(
+        event.IsMouseEvent() ? KeywordModeEntryMethod::CLICK_ON_VIEW
+                             : KeywordModeEntryMethod::TAP_ON_VIEW);
+  } else {
+    DCHECK_EQ(clear_all_button_, sender);
+    omnibox_view_->SetUserText(base::string16());
+  }
+}
+
+bool LocationBarView::IsVirtualKeyboardVisible() {
+#if defined(USE_AURA)
+  return keyboard::IsKeyboardVisible();
+#else
+  return false;
+#endif
 }
 
 bool LocationBarView::RefreshSaveCreditCardIconView() {
@@ -756,6 +774,15 @@
   return was_visible != manage_passwords_icon_view_->visible();
 }
 
+void LocationBarView::RefreshClearAllButtonIcon() {
+  if (!clear_all_button_)
+    return;
+
+  SetImageFromVectorIcon(clear_all_button_, kTabCloseNormalIcon,
+                         GetNativeTheme()->GetSystemColor(
+                             ui::NativeTheme::kColorId_TextfieldDefaultColor));
+}
+
 void LocationBarView::ShowFirstRunBubbleInternal() {
   // First run bubble doesn't make sense for Chrome OS.
 #if !defined(OS_CHROMEOS)
@@ -1024,6 +1051,8 @@
 void LocationBarView::OnChanged() {
   RefreshLocationIcon();
   location_icon_view_->set_show_tooltip(!GetOmniboxView()->IsEditingOrEmpty());
+  clear_all_button_->SetVisible(GetToolbarModel()->input_in_progress() &&
+                                LocationBarView::IsVirtualKeyboardVisible());
   Layout();
   SchedulePaint();
 }
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h
index 506285c7..32b599e0 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -49,6 +49,7 @@
 }
 
 namespace views {
+class ImageButton;
 class Label;
 }
 
@@ -238,6 +239,8 @@
   // views::ButtonListener:
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
+  static bool IsVirtualKeyboardVisible();
+
  private:
   using ContentSettingViews = std::vector<ContentSettingImageView*>;
 
@@ -276,6 +279,9 @@
   // Updates |manage_passwords_icon_view_|. Returns true if visibility changed.
   bool RefreshManagePasswordsIconView();
 
+  // Updates the color of the icon for the "clear all" button.
+  void RefreshClearAllButtonIcon();
+
   // Helper to show the first run info bubble.
   void ShowFirstRunBubbleInternal();
 
@@ -352,20 +358,20 @@
   // window, so this may be NULL.
   Browser* browser_;
 
-  OmniboxViewViews* omnibox_view_;
+  OmniboxViewViews* omnibox_view_ = nullptr;
 
   // Our delegate.
   Delegate* delegate_;
 
   // An icon to the left of the edit field: the HTTPS lock, blank page icon,
   // search icon, EV HTTPS bubble, etc.
-  LocationIconView* location_icon_view_;
+  LocationIconView* location_icon_view_ = nullptr;
 
   // A view to show inline autocompletion when an IME is active.  In this case,
   // we shouldn't change the text or selection inside the OmniboxView itself,
   // since this will conflict with the IME's control over the text.  So instead
   // we show any autocompletion in a separate field after the OmniboxView.
-  views::Label* ime_inline_autocomplete_view_;
+  views::Label* ime_inline_autocomplete_view_ = nullptr;
 
   // The following views are used to provide hints and remind the user as to
   // what is going in the edit. They are all added a children of the
@@ -374,31 +380,35 @@
   // These autocollapse when the edit needs the room.
 
   // Shown if the user has selected a keyword.
-  SelectedKeywordView* selected_keyword_view_;
+  SelectedKeywordView* selected_keyword_view_ = nullptr;
 
   // Shown if the selected url has a corresponding keyword.
-  KeywordHintView* keyword_hint_view_;
+  KeywordHintView* keyword_hint_view_ = nullptr;
 
   // The content setting views.
   ContentSettingViews content_setting_views_;
 
   // The zoom icon.
-  ZoomView* zoom_view_;
+  ZoomView* zoom_view_ = nullptr;
 
   // The manage passwords icon.
-  ManagePasswordsIconViews* manage_passwords_icon_view_;
+  ManagePasswordsIconViews* manage_passwords_icon_view_ = nullptr;
 
   // The save credit card icon.
-  autofill::SaveCardIconView* save_credit_card_icon_view_;
+  autofill::SaveCardIconView* save_credit_card_icon_view_ = nullptr;
 
   // The icon for Translate.
-  TranslateIconView* translate_icon_view_;
+  TranslateIconView* translate_icon_view_ = nullptr;
 
-  // The star.
-  StarView* star_view_;
+  // The star for bookmarking.
+  StarView* star_view_ = nullptr;
+
+  // An [x] that appears in touch mode (when the OSK is visible) and allows the
+  // user to clear all text.
+  views::ImageButton* clear_all_button_ = nullptr;
 
   // Animation to control showing / hiding the location bar.
-  gfx::SlideAnimation size_animation_;
+  gfx::SlideAnimation size_animation_{this};
 
   // Whether we're in popup mode. This value also controls whether the location
   // bar is read-only.
@@ -406,11 +416,11 @@
 
   // True if we should show a focus rect while the location entry field is
   // focused. Used when the toolbar is in full keyboard accessibility mode.
-  bool show_focus_rect_;
+  bool show_focus_rect_ = false;
 
   // This is in case we're destroyed before the model loads. We need to make
   // Add/RemoveObserver calls.
-  TemplateURLService* template_url_service_;
+  TemplateURLService* template_url_service_ = nullptr;
 
   // Tracks this preference to determine whether bookmark editing is allowed.
   BooleanPrefMember edit_bookmarks_enabled_;
diff --git a/chrome/browser/ui/views/native_widget_factory.cc b/chrome/browser/ui/views/native_widget_factory.cc
index b4ad4ae..b72680b4 100644
--- a/chrome/browser/ui/views/native_widget_factory.cc
+++ b/chrome/browser/ui/views/native_widget_factory.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/native_widget_factory.h"
 
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/views/theme_profile_key.h"
 #include "ui/aura/window.h"
 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
 #include "ui/views/widget/native_widget_aura.h"
@@ -18,30 +19,36 @@
   // it's possible that there is no contextual state that we can use.
   gfx::NativeWindow parent_or_context =
       params->parent ? params->parent : params->context;
-  // Set the profile key based on the profile of |parent_or_context|
-  // so that the widget will be styled with the apropriate
-  // NativeTheme.  For browser windows, BrowserView will reset the
-  // profile key to profile of the corresponding Browser.
   Profile* profile = nullptr;
-  if (parent_or_context) {
-    profile = reinterpret_cast<Profile*>(
-        parent_or_context->GetNativeWindowProperty(Profile::kProfileKey));
+  if (parent_or_context)
+    profile = GetThemeProfileForWindow(parent_or_context);
+  views::NativeWidget* native_widget = nullptr;
+  aura::Window* window = nullptr;
+  if (type == NativeWidgetType::DESKTOP_NATIVE_WIDGET_AURA ||
+      (!params->parent && !params->context && !params->child)) {
+    // In the desktop case, do not always set the profile window
+    // property from the parent since there are windows (like the task
+    // manager) that are not associated with a specific profile.
+    views::DesktopNativeWidgetAura* desktop_native_widget =
+        new views::DesktopNativeWidgetAura(delegate);
+    window = desktop_native_widget->GetNativeWindow();
+    native_widget = desktop_native_widget;
+  } else {
+    views::NativeWidgetAura* native_widget_aura =
+        new views::NativeWidgetAura(delegate);
+    if (params->parent) {
+      Profile* parent_profile = reinterpret_cast<Profile*>(
+          params->parent->GetNativeWindowProperty(Profile::kProfileKey));
+      native_widget_aura->SetNativeWindowProperty(Profile::kProfileKey,
+                                                  parent_profile);
+    }
+    window = native_widget_aura->GetNativeWindow();
+    native_widget = native_widget_aura;
   }
   // Use the original profile because |window| may outlive the profile
   // of the context window.  This can happen with incognito profiles.
   // However, the original profile will stick around until shutdown.
-  if (profile)
-    profile = profile->GetOriginalProfile();
-  if (type == NativeWidgetType::DESKTOP_NATIVE_WIDGET_AURA ||
-      (!params->parent && !params->context && !params->child)) {
-    views::DesktopNativeWidgetAura* desktop_native_widget =
-        new views::DesktopNativeWidgetAura(delegate);
-    desktop_native_widget->SetNativeWindowProperty(Profile::kProfileKey,
-                                                   profile);
-    return desktop_native_widget;
-  }
-  views::NativeWidgetAura* native_widget_aura =
-      new views::NativeWidgetAura(delegate);
-  native_widget_aura->SetNativeWindowProperty(Profile::kProfileKey, profile);
-  return native_widget_aura;
+  SetThemeProfileForWindow(window,
+                           profile ? profile->GetOriginalProfile() : nullptr);
+  return native_widget;
 }
diff --git a/chrome/browser/ui/views/theme_profile_key.cc b/chrome/browser/ui/views/theme_profile_key.cc
new file mode 100644
index 0000000..e35d3da2
--- /dev/null
+++ b/chrome/browser/ui/views/theme_profile_key.cc
@@ -0,0 +1,24 @@
+// 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/ui/views/theme_profile_key.h"
+
+#include "ui/aura/window.h"
+#include "ui/base/class_property.h"
+
+DECLARE_UI_CLASS_PROPERTY_TYPE(Profile*);
+
+namespace {
+
+DEFINE_UI_CLASS_PROPERTY_KEY(Profile*, kThemeProfileKey, nullptr);
+
+}  // anonymous namespace
+
+void SetThemeProfileForWindow(aura::Window* window, Profile* profile) {
+  window->SetProperty(kThemeProfileKey, profile);
+}
+
+Profile* GetThemeProfileForWindow(aura::Window* window) {
+  return window->GetProperty(kThemeProfileKey);
+}
diff --git a/chrome/browser/ui/views/theme_profile_key.h b/chrome/browser/ui/views/theme_profile_key.h
new file mode 100644
index 0000000..ce16480
--- /dev/null
+++ b/chrome/browser/ui/views/theme_profile_key.h
@@ -0,0 +1,17 @@
+// 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_UI_VIEWS_THEME_PROFILE_KEY_H_
+#define CHROME_BROWSER_UI_VIEWS_THEME_PROFILE_KEY_H_
+
+namespace aura {
+class Window;
+}
+
+class Profile;
+
+void SetThemeProfileForWindow(aura::Window* window, Profile* profile);
+Profile* GetThemeProfileForWindow(aura::Window*);
+
+#endif  // CHROME_BROWSER_UI_VIEWS_THEME_PROFILE_KEY_H_
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index ef02641..42b7413 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -59,6 +59,7 @@
 #include "chrome/browser/ui/webui/chromeos/login/update_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/user_board_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.h"
+#include "chrome/browser/ui/webui/chromeos/login/voice_interaction_value_prop_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/wrong_hwid_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h"
 #include "chrome/browser/ui/webui/options/chromeos/user_image_source.h"
@@ -335,6 +336,8 @@
 
   AddScreenHandler(base::MakeUnique<EncryptionMigrationScreenHandler>());
 
+  AddScreenHandler(base::MakeUnique<VoiceInteractionValuePropScreenHandler>());
+
   // Initialize KioskAppMenuHandler. Note that it is NOT a screen handler.
   auto kiosk_app_menu_handler =
       base::MakeUnique<KioskAppMenuHandler>(network_state_informer_);
@@ -456,6 +459,11 @@
   return GetView<EncryptionMigrationScreenHandler>();
 }
 
+VoiceInteractionValuePropScreenView*
+OobeUI::GetVoiceInteractionValuePropScreenView() {
+  return GetView<VoiceInteractionValuePropScreenHandler>();
+}
+
 UserImageView* OobeUI::GetUserImageView() {
   return GetView<UserImageScreenHandler>();
 }
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
index a29a94c..288fefe4 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
@@ -32,6 +32,7 @@
 class AppLaunchSplashScreenView;
 class ArcKioskSplashScreenView;
 class ArcTermsOfServiceScreenView;
+class VoiceInteractionValuePropScreenView;
 class AutoEnrollmentCheckScreenView;
 class BaseScreenHandler;
 class ControllerPairingScreenView;
@@ -117,6 +118,7 @@
   HostPairingScreenView* GetHostPairingScreenView();
   DeviceDisabledScreenView* GetDeviceDisabledScreenView();
   EncryptionMigrationScreenView* GetEncryptionMigrationScreenView();
+  VoiceInteractionValuePropScreenView* GetVoiceInteractionValuePropScreenView();
   GaiaView* GetGaiaScreenView();
   UserBoardView* GetUserBoardView();
 
diff --git a/chrome/browser/ui/webui/chromeos/login/voice_interaction_value_prop_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/voice_interaction_value_prop_screen_handler.cc
new file mode 100644
index 0000000..6b683716
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/login/voice_interaction_value_prop_screen_handler.cc
@@ -0,0 +1,74 @@
+// 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/ui/webui/chromeos/login/voice_interaction_value_prop_screen_handler.h"
+
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/oobe_screen.h"
+#include "chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/login/localized_values_builder.h"
+
+namespace {
+
+const char kJsScreenPath[] = "login.VoiceInteractionValuePropScreen";
+
+}  // namespace
+
+namespace chromeos {
+
+VoiceInteractionValuePropScreenHandler::VoiceInteractionValuePropScreenHandler()
+    : BaseScreenHandler(kScreenId) {
+  set_call_js_prefix(kJsScreenPath);
+}
+
+VoiceInteractionValuePropScreenHandler::
+    ~VoiceInteractionValuePropScreenHandler() {
+  if (screen_) {
+    screen_->OnViewDestroyed(this);
+  }
+}
+
+void VoiceInteractionValuePropScreenHandler::DeclareLocalizedValues(
+    ::login::LocalizedValuesBuilder* builder) {
+  builder->Add("locale", g_browser_process->GetApplicationLocale());
+  builder->Add("voiceInteractionValuePropNoThanksButton",
+               IDS_VOICE_INTERACTION_VALUE_PROP_NO_THANKS_BUTTON);
+  builder->Add("voiceInteractionValuePropContinueButton",
+               IDS_VOICE_INTERACTION_VALUE_PROP_CONTINUE_BUTTION);
+}
+
+void VoiceInteractionValuePropScreenHandler::Bind(
+    VoiceInteractionValuePropScreen* screen) {
+  BaseScreenHandler::SetBaseScreen(screen);
+  screen_ = screen;
+  if (page_is_ready())
+    Initialize();
+}
+
+void VoiceInteractionValuePropScreenHandler::Unbind() {
+  screen_ = nullptr;
+  BaseScreenHandler::SetBaseScreen(nullptr);
+}
+
+void VoiceInteractionValuePropScreenHandler::Show() {
+  if (!page_is_ready() || !screen_) {
+    show_on_init_ = true;
+    return;
+  }
+
+  ShowScreen(kScreenId);
+}
+
+void VoiceInteractionValuePropScreenHandler::Hide() {}
+
+void VoiceInteractionValuePropScreenHandler::Initialize() {
+  if (!screen_ || !show_on_init_)
+    return;
+
+  Show();
+  show_on_init_ = false;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/ui/webui/chromeos/login/voice_interaction_value_prop_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/voice_interaction_value_prop_screen_handler.h
new file mode 100644
index 0000000..5b9d2fd
--- /dev/null
+++ b/chrome/browser/ui/webui/chromeos/login/voice_interaction_value_prop_screen_handler.h
@@ -0,0 +1,48 @@
+// 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_UI_WEBUI_CHROMEOS_LOGIN_VOICE_INTERACTION_VALUE_PROP_SCREEN_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_VOICE_INTERACTION_VALUE_PROP_SCREEN_HANDLER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/login/screens/voice_interaction_value_prop_screen_view.h"
+#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
+
+namespace chromeos {
+
+class VoiceInteractionValuePropScreenHandler
+    : public BaseScreenHandler,
+      public VoiceInteractionValuePropScreenView {
+ public:
+  VoiceInteractionValuePropScreenHandler();
+  ~VoiceInteractionValuePropScreenHandler() override;
+
+  // BaseScreenHandler:
+  void DeclareLocalizedValues(
+      ::login::LocalizedValuesBuilder* builder) override;
+
+  // VoiceInteractionValuePropScreenView:
+  void Bind(VoiceInteractionValuePropScreen* screen) override;
+  void Unbind() override;
+  void Show() override;
+  void Hide() override;
+
+ private:
+  // BaseScreenHandler:
+  void Initialize() override;
+
+  VoiceInteractionValuePropScreen* screen_ = nullptr;
+
+  // Whether the screen should be shown right after initialization.
+  bool show_on_init_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(VoiceInteractionValuePropScreenHandler);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_VOICE_INTERACTION_VALUE_PROP_SCREEN_HANDLER_H_
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 15adf88..cc4c055f 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -54,6 +54,9 @@
 // the user directory (i.e., the user finished required migration.)
 extern const char kArcCompatibleFilesystemChosen[] =
     "arc.compatible_filesystem.chosen";
+// A preference that indicates that user accepted Assistant Value Prop.
+const char kArcVoiceInteractionValuePropAccepted[] =
+    "arc.voice_interaction_value_prop.accepted";
 #endif
 
 // A bool pref that keeps whether the child status for this profile was already
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 8343198..41deff9 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -33,6 +33,7 @@
 extern const char kArcSetNotificationsEnabledDeferred[];
 extern const char kArcSignedIn[];
 extern const char kArcCompatibleFilesystemChosen[];
+extern const char kArcVoiceInteractionValuePropAccepted[];
 #endif
 extern const char kChildAccountStatusKnown[];
 extern const char kDefaultApps[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 99331b11..a4481454 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1958,6 +1958,7 @@
       sources += [
         "../browser/apps/app_browsertest_util.cc",
         "../browser/apps/app_browsertest_util.h",
+        "../browser/extensions/bookmark_app_helper_browsertest.cc",
         "../browser/extensions/browsertest_util.cc",
         "../browser/extensions/browsertest_util.h",
         "../browser/extensions/extension_apitest.cc",
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index 7701ac7..1cdd3d81 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -91,9 +91,6 @@
   bool IsToolbarShowing() const override;
   void ShowUpdateChromeDialog() override {}
   void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) override {}
-  void ShowBookmarkAppBubble(
-      const WebApplicationInfo& web_app_info,
-      const ShowBookmarkAppBubbleCallback& callback) override {}
   autofill::SaveCardBubbleView* ShowSaveCreditCardBubble(
       content::WebContents* contents,
       autofill::SaveCardBubbleController* controller,
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index c402224..0a3201e 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -1181,7 +1181,13 @@
   ]),
 };
 
-TEST_F('CrSettingsInternetPageTest', 'All', function() {
+// TODO(crbug.com/729607): deflake. Failing on Linux ChromiumOS Test (dbg)(1).
+GEN('#if defined(OS_CHROMEOS) && !defined(NDEBUG)');
+GEN('#define MAYBE_InternetPage DISABLED_InternetPage');
+GEN('#else');
+GEN('#define MAYBE_InternetPage InternetPage');
+GEN('#endif');
+TEST_F('CrSettingsInternetPageTest', 'MAYBE_InternetPage', function() {
   mocha.run();
 });
 
@@ -1421,11 +1427,11 @@
 
 // Failing on ChromiumOS dbg. https://crbug.com/709442
 GEN('#if (defined(OS_WIN) || defined(OS_CHROMEOS)) && !defined(NDEBUG)');
-GEN('#define MAYBE_All DISABLED_All');
+GEN('#define MAYBE_NonExistentRoute DISABLED_NonExistentRoute');
 GEN('#else');
-GEN('#define MAYBE_All All');
+GEN('#define MAYBE_NonExistentRoute NonExistentRoute');
 GEN('#endif');
-TEST_F('CrSettingsNonExistentRouteTest', 'MAYBE_All', function() {
+TEST_F('CrSettingsNonExistentRouteTest', 'MAYBE_NonExistentRoute', function() {
   suite('NonExistentRoutes', function() {
     test('redirect to basic', function() {
       assertEquals(settings.Route.BASIC, settings.getCurrentRoute());
@@ -1480,14 +1486,6 @@
   mocha.run();
 });
 
-// Times out on Windows Tests (dbg). See https://crbug.com/651296.
-// Times out / crashes on chromium.linux/Linux Tests (dbg) crbug.com/667882
-GEN('#if !defined(NDEBUG)')
-GEN('#define MAYBE_MainPage_All DISABLED_MainPage_All');
-GEN('#else');
-GEN('#define MAYBE_MainPage_All MainPage_All');
-GEN('#endif');
-
 /**
  * Test fixture for chrome/browser/resources/settings/settings_main/.
  * @constructor
@@ -1509,7 +1507,14 @@
   ]),
 };
 
-TEST_F('CrSettingsMainPageTest', 'MAYBE_MainPage_All', function() {
+// Times out on Windows Tests (dbg). See https://crbug.com/651296.
+// Times out / crashes on chromium.linux/Linux Tests (dbg) crbug.com/667882
+GEN('#if !defined(NDEBUG)')
+GEN('#define MAYBE_MainPage DISABLED_MainPage');
+GEN('#else');
+GEN('#define MAYBE_MainPage MainPage');
+GEN('#endif');
+TEST_F('CrSettingsMainPageTest', 'MAYBE_MainPage', function() {
   settings_main_page.registerTests();
   mocha.run();
 });
diff --git a/components/arc/common/voice_interaction_framework.mojom b/components/arc/common/voice_interaction_framework.mojom
index e723c2e6..05041a3 100644
--- a/components/arc/common/voice_interaction_framework.mojom
+++ b/components/arc/common/voice_interaction_framework.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 5
+// Next MinVersion: 6
 
 module arc.mojom;
 
@@ -28,7 +28,7 @@
 };
 
 // Connects with Android system server.
-// Next method ID: 4
+// Next method ID: 7
 interface VoiceInteractionFrameworkInstance {
   Init@0(VoiceInteractionFrameworkHost host_ptr);
 
@@ -43,10 +43,13 @@
   [MinVersion=1] SetMetalayerVisibility@3([MinVersion=2] bool visible);
 
   // Turns on / off voice interaction in container.
-  [MinVersion=4] SetVoiceInteractionEnabled(bool enable);
+  [MinVersion=4] SetVoiceInteractionEnabled@4(bool enable);
 
   // Turns on / off context for voice interaction in container. This function
   // controls whether screenshot and view hierarchy information should be sent
   // to container.
-  [MinVersion=4] SetVoiceInteractionContextEnabled(bool enable);
+  [MinVersion=4] SetVoiceInteractionContextEnabled@5(bool enable);
+
+  // Starts the voice interaction setup wizard in container.
+  [MinVersion=5] StartVoiceInteractionSetupWizard@6();
 };
diff --git a/content/browser/dom_storage/local_storage_context_mojo.cc b/content/browser/dom_storage/local_storage_context_mojo.cc
index 3d9c807..6c95724 100644
--- a/content/browser/dom_storage/local_storage_context_mojo.cc
+++ b/content/browser/dom_storage/local_storage_context_mojo.cc
@@ -465,9 +465,10 @@
         kStorageOpenHistogramName,
         static_cast<int>(LocalStorageOpenHistogram::DATABASE_OPEN_FAILED),
         static_cast<int>(LocalStorageOpenHistogram::MAX));
-    // If we failed to open the database, reset the service object so we pass
-    // null pointers to our wrappers.
-    database_.reset();
+    // If we failed to open the database, try to delete and recreate the
+    // database, or ultimately fallback to an in-memory database.
+    DeleteAndRecreateDatabase();
+    return;
   }
 
   // Verify DB schema version.
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 e3f7c76..2e4db58 100644
--- a/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
+++ b/content/browser/dom_storage/local_storage_context_mojo_unittest.cc
@@ -5,6 +5,7 @@
 #include "content/browser/dom_storage/local_storage_context_mojo.h"
 
 #include "base/files/file_enumerator.h"
+#include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
@@ -906,4 +907,52 @@
   context->ShutdownAndDelete();
 }
 
+TEST_F(LocalStorageContextMojoTestWithService, CorruptionOnDisk) {
+  base::FilePath test_path(FILE_PATH_LITERAL("test_path"));
+
+  // Create context and add some data to it.
+  auto* context = new LocalStorageContextMojo(
+      connector(), nullptr, base::FilePath(), test_path, nullptr);
+  auto key = StdStringToUint8Vector("key");
+  auto value = StdStringToUint8Vector("value");
+
+  DoTestPut(context, key, value);
+  std::vector<uint8_t> result;
+  EXPECT_TRUE(DoTestGet(context, key, &result));
+  EXPECT_EQ(value, result);
+
+  context->ShutdownAndDelete();
+  context = nullptr;
+  base::RunLoop().RunUntilIdle();
+
+  // Delete manifest files to mess up opening DB.
+  base::FilePath db_path =
+      temp_path().Append(test_path).Append(FILE_PATH_LITERAL("leveldb"));
+  base::FileEnumerator file_enum(db_path, true, base::FileEnumerator::FILES,
+                                 FILE_PATH_LITERAL("MANIFEST*"));
+  for (base::FilePath name = file_enum.Next(); !name.empty();
+       name = file_enum.Next()) {
+    base::DeleteFile(name, false);
+  }
+
+  // Make sure data is gone.
+  context = new LocalStorageContextMojo(connector(), nullptr, base::FilePath(),
+                                        test_path, nullptr);
+  EXPECT_FALSE(DoTestGet(context, key, &result));
+
+  // Write data again.
+  DoTestPut(context, key, value);
+
+  context->ShutdownAndDelete();
+  context = nullptr;
+  base::RunLoop().RunUntilIdle();
+
+  // Data should have been preserved now.
+  context = new LocalStorageContextMojo(connector(), nullptr, base::FilePath(),
+                                        test_path, nullptr);
+  EXPECT_TRUE(DoTestGet(context, key, &result));
+  EXPECT_EQ(value, result);
+  context->ShutdownAndDelete();
+}
+
 }  // namespace content
diff --git a/content/browser/web_contents/aura/gesture_nav_simple.cc b/content/browser/web_contents/aura/gesture_nav_simple.cc
index c67d8617..9976acc 100644
--- a/content/browser/web_contents/aura/gesture_nav_simple.cc
+++ b/content/browser/web_contents/aura/gesture_nav_simple.cc
@@ -34,6 +34,11 @@
 // Parameters defining the arrow icon inside the affordance.
 const int kArrowSize = 16;
 const SkColor kArrowColor = gfx::kGoogleBlue500;
+const uint8_t kArrowInitialOpacity = 0x4D;
+
+// The arrow opacity remains constant until progress reaches this threshold,
+// then increases quickly as the progress increases beyond the threshold
+const float kArrowOpacityProgressThreshold = .9f;
 
 // Parameters defining the background circle of the affordance.
 const int kBackgroundRadius = 18;
@@ -47,7 +52,7 @@
 // overscroll is successful, the ripple will burst by fading out and growing to
 // |kMaxRippleBurstRadius|.
 const int kMaxRippleRadius = 54;
-const SkColor kRippleColor = SkColorSetA(gfx::kGoogleBlue500, 0x33);
+const SkColor kRippleColor = SkColorSetA(gfx::kGoogleBlue500, 0x66);
 const int kMaxRippleBurstRadius = 72;
 const gfx::Tween::Type kBurstAnimationTweenType = gfx::Tween::EASE_IN;
 const int kRippleBurstAnimationDuration = 160;
@@ -77,10 +82,11 @@
 
 // This class is responsible for creating, painting, and positioning the layer
 // for the gesture nav affordance.
-class GestureNavSimple::Affordance : public ui::LayerDelegate,
-                                     public gfx::AnimationDelegate {
+class Affordance : public ui::LayerDelegate, public gfx::AnimationDelegate {
  public:
-  Affordance(OverscrollMode mode, const gfx::Rect& content_bounds);
+  Affordance(GestureNavSimple* owner,
+             OverscrollMode mode,
+             const gfx::Rect& content_bounds);
   ~Affordance() override;
 
   // Sets progress of affordance drag as a value between 0 and 1.
@@ -115,6 +121,8 @@
   void AnimationProgressed(const gfx::Animation* animation) override;
   void AnimationCanceled(const gfx::Animation* animation) override;
 
+  GestureNavSimple* const owner_;
+
   const OverscrollMode mode_;
 
   // Root layer of the affordance. This is used to clip the affordance to the
@@ -138,9 +146,11 @@
   DISALLOW_COPY_AND_ASSIGN(Affordance);
 };
 
-GestureNavSimple::Affordance::Affordance(OverscrollMode mode,
-                                         const gfx::Rect& content_bounds)
-    : mode_(mode),
+Affordance::Affordance(GestureNavSimple* owner,
+                       OverscrollMode mode,
+                       const gfx::Rect& content_bounds)
+    : owner_(owner),
+      mode_(mode),
       root_layer_(base::MakeUnique<ui::Layer>(ui::LAYER_NOT_DRAWN)),
       painted_layer_(base::MakeUnique<ui::Layer>(ui::LAYER_TEXTURED)),
       image_(gfx::CreateVectorIcon(
@@ -166,9 +176,9 @@
   root_layer_->Add(painted_layer_.get());
 }
 
-GestureNavSimple::Affordance::~Affordance() {}
+Affordance::~Affordance() {}
 
-void GestureNavSimple::Affordance::SetDragProgress(float progress) {
+void Affordance::SetDragProgress(float progress) {
   DCHECK_EQ(State::DRAGGING, state_);
   DCHECK_LE(0.f, progress);
   DCHECK_GE(1.f, progress);
@@ -181,7 +191,7 @@
   SchedulePaint();
 }
 
-void GestureNavSimple::Affordance::Abort() {
+void Affordance::Abort() {
   DCHECK_EQ(State::DRAGGING, state_);
 
   state_ = State::ABORTING;
@@ -192,7 +202,7 @@
   animation_->Start();
 }
 
-void GestureNavSimple::Affordance::Complete() {
+void Affordance::Complete() {
   DCHECK_EQ(State::DRAGGING, state_);
   DCHECK_EQ(1.f, drag_progress_);
 
@@ -204,18 +214,18 @@
   animation_->Start();
 }
 
-void GestureNavSimple::Affordance::UpdateTransform() {
+void Affordance::UpdateTransform() {
   float offset = (1 - abort_progress_) * drag_progress_ * kMaxAffordanceOffset;
   gfx::Transform transform;
   transform.Translate(mode_ == OVERSCROLL_EAST ? offset : -offset, 0);
   painted_layer_->SetTransform(transform);
 }
 
-void GestureNavSimple::Affordance::SchedulePaint() {
+void Affordance::SchedulePaint() {
   painted_layer_->SchedulePaint(gfx::Rect(painted_layer_->size()));
 }
 
-void GestureNavSimple::Affordance::SetAbortProgress(float progress) {
+void Affordance::SetAbortProgress(float progress) {
   DCHECK_EQ(State::ABORTING, state_);
   DCHECK_LE(0.f, progress);
   DCHECK_GE(1.f, progress);
@@ -228,7 +238,7 @@
   SchedulePaint();
 }
 
-void GestureNavSimple::Affordance::SetCompleteProgress(float progress) {
+void Affordance::SetCompleteProgress(float progress) {
   DCHECK_EQ(State::COMPLETING, state_);
   DCHECK_LE(0.f, progress);
   DCHECK_GE(1.f, progress);
@@ -241,8 +251,7 @@
   SchedulePaint();
 }
 
-void GestureNavSimple::Affordance::OnPaintLayer(
-    const ui::PaintContext& context) {
+void Affordance::OnPaintLayer(const ui::PaintContext& context) {
   DCHECK(drag_progress_ == 1.f || state_ != State::COMPLETING);
   DCHECK(abort_progress_ == 0.f || state_ == State::ABORTING);
   DCHECK(complete_progress_ == 0.f || state_ == State::COMPLETING);
@@ -287,25 +296,31 @@
   float arrow_x_offset =
       (1 - progress) * (-kBackgroundRadius + kArrowSize / 2.f);
   arrow_x += mode_ == OVERSCROLL_EAST ? arrow_x_offset : -arrow_x_offset;
-  uint8_t arrow_alpha =
-      static_cast<uint8_t>(std::min(0xFF, static_cast<int>(progress * 0xFF)));
+  // Calculate arrow opacity. Opacity is fixed before progress reaches
+  // kArrowOpacityProgressThreshold and after that increases linearly to 1;
+  // essentially, making a quick bump at the end.
+  uint8_t arrow_opacity = kArrowInitialOpacity;
+  if (progress > kArrowOpacityProgressThreshold) {
+    const uint8_t max_opacity_bump = 0xFF - kArrowInitialOpacity;
+    const float opacity_bump_ratio =
+        std::min(1.f, (progress - kArrowOpacityProgressThreshold) /
+                          (1.f - kArrowOpacityProgressThreshold));
+    arrow_opacity +=
+        static_cast<uint8_t>(opacity_bump_ratio * max_opacity_bump);
+  }
   canvas->DrawImageInt(*image_.ToImageSkia(), static_cast<int>(arrow_x),
-                       static_cast<int>(arrow_y), arrow_alpha);
+                       static_cast<int>(arrow_y), arrow_opacity);
 }
 
-void GestureNavSimple::Affordance::OnDelegatedFrameDamage(
-    const gfx::Rect& damage_rect_in_dip) {}
+void Affordance::OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) {}
 
-void GestureNavSimple::Affordance::OnDeviceScaleFactorChanged(
-    float device_scale_factor) {}
+void Affordance::OnDeviceScaleFactorChanged(float device_scale_factor) {}
 
-void GestureNavSimple::Affordance::AnimationEnded(
-    const gfx::Animation* animation) {
-  delete this;
+void Affordance::AnimationEnded(const gfx::Animation* animation) {
+  owner_->OnAffordanceAnimationEnded();
 }
 
-void GestureNavSimple::Affordance::AnimationProgressed(
-    const gfx::Animation* animation) {
+void Affordance::AnimationProgressed(const gfx::Animation* animation) {
   switch (state_) {
     case State::DRAGGING:
       NOTREACHED();
@@ -321,12 +336,10 @@
   }
 }
 
-void GestureNavSimple::Affordance::AnimationCanceled(
-    const gfx::Animation* animation) {
+void Affordance::AnimationCanceled(const gfx::Animation* animation) {
   NOTREACHED();
 }
 
-
 GestureNavSimple::GestureNavSimple(WebContentsImpl* web_contents)
     : web_contents_(web_contents),
       completion_threshold_(0.f) {}
@@ -334,21 +347,17 @@
 GestureNavSimple::~GestureNavSimple() {}
 
 void GestureNavSimple::AbortGestureAnimation() {
-  if (!affordance_)
-    return;
-  // Release the unique pointer. The affordance will delete itself upon
-  // completion of animation.
-  Affordance* affordance = affordance_.release();
-  affordance->Abort();
+  if (affordance_)
+    affordance_->Abort();
 }
 
 void GestureNavSimple::CompleteGestureAnimation() {
-  if (!affordance_)
-    return;
-  // Release the unique pointer. The affordance will delete itself upon
-  // completion of animation.
-  Affordance* affordance = affordance_.release();
-  affordance->Complete();
+  if (affordance_)
+    affordance_->Complete();
+}
+
+void GestureNavSimple::OnAffordanceAnimationEnded() {
+  affordance_.reset();
 }
 
 gfx::Rect GestureNavSimple::GetVisibleBounds() const {
@@ -395,7 +404,7 @@
           GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE) -
       start_threshold;
 
-  affordance_.reset(new Affordance(new_mode, window_bounds));
+  affordance_.reset(new Affordance(this, new_mode, window_bounds));
 
   // Adding the affordance as a child of the content window is not sufficient,
   // because it is possible for a new layer to be parented on top of the
diff --git a/content/browser/web_contents/aura/gesture_nav_simple.h b/content/browser/web_contents/aura/gesture_nav_simple.h
index b7624da..f55ceb2 100644
--- a/content/browser/web_contents/aura/gesture_nav_simple.h
+++ b/content/browser/web_contents/aura/gesture_nav_simple.h
@@ -12,6 +12,7 @@
 
 namespace content {
 
+class Affordance;
 class WebContentsImpl;
 
 // A simple delegate for the overscroll controller that paints an arrow on top
@@ -21,9 +22,11 @@
   explicit GestureNavSimple(WebContentsImpl* web_contents);
   ~GestureNavSimple() override;
 
- private:
-  class Affordance;
+  // Called by the affordance when its complete/abort animation is finished so
+  // that the affordance instance can be destroyed.
+  void OnAffordanceAnimationEnded();
 
+ private:
   void AbortGestureAnimation();
   void CompleteGestureAnimation();
 
diff --git a/content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.cc b/content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.cc
index 79116a0..aad26f63 100644
--- a/content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.cc
+++ b/content/common/sandbox_linux/android/sandbox_bpf_base_policy_android.cc
@@ -80,8 +80,10 @@
 #endif
 #if defined(__x86_64__) || defined(__aarch64__)
     case __NR_newfstatat:
+    case __NR_fstatfs:
 #elif defined(__i386__) || defined(__arm__) || defined(__mips__)
     case __NR_fstatat64:
+    case __NR_fstatfs64:
     case __NR_getdents:
 #endif
     case __NR_getdents64:
diff --git a/content/test/gpu/gpu_tests/gpu_process_integration_test.py b/content/test/gpu/gpu_tests/gpu_process_integration_test.py
index 08cfa9b32f..fc29dfc3 100644
--- a/content/test/gpu/gpu_tests/gpu_process_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_process_integration_test.py
@@ -541,6 +541,31 @@
       device = gpu.devices[0]
       if not device:
         self.fail("System Info doesn't have a device")
+      # Validate extensions.
+      ext_list = [
+        'ANGLE_instanced_arrays',
+        'EXT_blend_minmax',
+        'EXT_texture_filter_anisotropic',
+        'WEBKIT_EXT_texture_filter_anisotropic',
+        'OES_element_index_uint',
+        'OES_standard_derivatives',
+        'OES_texture_float',
+        'OES_texture_float_linear',
+        'OES_texture_half_float',
+        'OES_texture_half_float_linear',
+        'OES_vertex_array_object',
+        'WEBGL_compressed_texture_etc1',
+        'WEBGL_debug_renderer_info',
+        'WEBGL_debug_shaders',
+        'WEBGL_depth_texture',
+        'WEBKIT_WEBGL_depth_texture',
+        'WEBGL_lose_context',
+        'WEBKIT_WEBGL_lose_context',
+      ]
+      tab = self.tab
+      for ext in ext_list:
+        if tab.EvaluateJavaScript('!gl_context.getExtension("' + ext + '")'):
+          self.fail("Expected " + ext + " support")
 
 def load_tests(loader, tests, pattern):
   del loader, tests, pattern  # Unused.
diff --git a/device/bluetooth/bluetooth_adapter_mac.h b/device/bluetooth/bluetooth_adapter_mac.h
index fb832c8..52b1634 100644
--- a/device/bluetooth/bluetooth_adapter_mac.h
+++ b/device/bluetooth/bluetooth_adapter_mac.h
@@ -29,6 +29,25 @@
 @class NSArray;
 @class NSDate;
 
+#if !defined(MAC_OS_X_VERSION_10_13)
+
+// The 10.13 SDK deprecates the CBCentralManagerState enum. When building
+// against older SDKs, define the new enum in terms of the deprecated one.
+using CBManagerState = CBCentralManagerState;
+constexpr CBManagerState CBManagerStateUnknown = CBCentralManagerStateUnknown;
+constexpr CBManagerState CBManagerStateResetting =
+    CBCentralManagerStateResetting;
+constexpr CBManagerState CBManagerStateUnsupported =
+    CBCentralManagerStateUnsupported;
+constexpr CBManagerState CBManagerStateUnauthorized =
+    CBCentralManagerStateUnauthorized;
+constexpr CBManagerState CBManagerStatePoweredOff =
+    CBCentralManagerStatePoweredOff;
+constexpr CBManagerState CBManagerStatePoweredOn =
+    CBCentralManagerStatePoweredOn;
+
+#endif  // MAC_OS_X_VERSION_10_13
+
 namespace base {
 
 class SequencedTaskRunner;
diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm
index 64dbb03..f02859d 100644
--- a/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -155,7 +155,7 @@
   bool is_present = !address_.empty();
   if (IsLowEnergyAvailable()) {
     is_present = is_present || ([low_energy_central_manager_ state] !=
-                                CBCentralManagerStateUnsupported);
+                                CBManagerStateUnsupported);
   }
   return is_present;
 }
@@ -164,7 +164,7 @@
   bool is_powered = classic_powered_;
   if (IsLowEnergyAvailable()) {
     is_powered = is_powered || ([low_energy_central_manager_ state] ==
-                                CBCentralManagerStatePoweredOn);
+                                CBManagerStatePoweredOn);
   }
   return is_powered;
 }
@@ -588,17 +588,17 @@
   }
 }
 
-// TODO(crbug.com/511025): Handle state < CBCentralManagerStatePoweredOff.
+// TODO(crbug.com/511025): Handle state < CBManagerStatePoweredOff.
 void BluetoothAdapterMac::LowEnergyCentralManagerUpdatedState() {
   VLOG(1) << "Central manager state updated: "
           << [low_energy_central_manager_ state];
-  // A state with a value lower than CBCentralManagerStatePoweredOn implies that
+  // A state with a value lower than CBManagerStatePoweredOn implies that
   // scanning has stopped and that any connected peripherals have been
   // disconnected. Call DidDisconnectPeripheral manually to update the devices'
   // states since macOS doesn't call it.
   // See
   // https://developer.apple.com/reference/corebluetooth/cbcentralmanagerdelegate/1518888-centralmanagerdidupdatestate?language=objc
-  if ([low_energy_central_manager_ state] < CBCentralManagerStatePoweredOn) {
+  if ([low_energy_central_manager_ state] < CBManagerStatePoweredOn) {
     VLOG(1)
         << "Central no longer powered on. Notifying of device disconnection.";
     for (BluetoothDevice* device : GetDevices()) {
diff --git a/device/bluetooth/bluetooth_adapter_mac_unittest.mm b/device/bluetooth/bluetooth_adapter_mac_unittest.mm
index 8dc4c00..47ca54a1 100644
--- a/device/bluetooth/bluetooth_adapter_mac_unittest.mm
+++ b/device/bluetooth/bluetooth_adapter_mac_unittest.mm
@@ -4,6 +4,8 @@
 
 #include "device/bluetooth/bluetooth_adapter_mac.h"
 
+#import <Foundation/Foundation.h>
+
 #include <memory>
 
 #include "base/bind.h"
@@ -15,11 +17,11 @@
 #include "device/bluetooth/bluetooth_common.h"
 #include "device/bluetooth/bluetooth_discovery_session.h"
 #include "device/bluetooth/bluetooth_discovery_session_outcome.h"
-#include "device/bluetooth/bluetooth_low_energy_device_mac.h"
-#include "device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h"
-#include "device/bluetooth/test/mock_bluetooth_central_manager_mac.h"
+#import "device/bluetooth/bluetooth_low_energy_device_mac.h"
+#import "device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h"
+#import "device/bluetooth/test/mock_bluetooth_central_manager_mac.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
 
 #if defined(OS_IOS)
 #import <CoreBluetooth/CoreBluetooth.h>
@@ -27,8 +29,6 @@
 #import <IOBluetooth/IOBluetooth.h>
 #endif  // defined(OS_IOS)
 
-#import <Foundation/Foundation.h>
-
 namespace {
 // |kTestHashAddress| is the hash corresponding to identifier |kTestNSUUID|.
 const char* const kTestNSUUID = "00000000-1111-2222-3333-444444444444";
@@ -92,7 +92,7 @@
     return (device != NULL);
   }
 
-  bool SetMockCentralManager(CBCentralManagerState desired_state) {
+  bool SetMockCentralManager(CBManagerState desired_state) {
     if (!BluetoothAdapterMac::IsLowEnergyAvailable()) {
       LOG(WARNING) << "Low Energy Bluetooth unavailable, skipping unit test.";
       return false;
@@ -148,7 +148,7 @@
 }
 
 TEST_F(BluetoothAdapterMacTest, AddDiscoverySessionWithLowEnergyFilter) {
-  if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
+  if (!SetMockCentralManager(CBManagerStatePoweredOn))
     return;
   EXPECT_EQ(0, [mock_central_manager_ scanForPeripheralsCallCount]);
   EXPECT_EQ(0, NumDiscoverySessions());
@@ -168,7 +168,7 @@
 // TODO(krstnmnlsn): Test changing the filter when adding the second discovery
 // session (once we have that ability).
 TEST_F(BluetoothAdapterMacTest, AddSecondDiscoverySessionWithLowEnergyFilter) {
-  if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
+  if (!SetMockCentralManager(CBManagerStatePoweredOn))
     return;
   std::unique_ptr<BluetoothDiscoveryFilter> discovery_filter(
       new BluetoothDiscoveryFilter(BLUETOOTH_TRANSPORT_LE));
@@ -189,7 +189,7 @@
 }
 
 TEST_F(BluetoothAdapterMacTest, RemoveDiscoverySessionWithLowEnergyFilter) {
-  if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
+  if (!SetMockCentralManager(CBManagerStatePoweredOn))
     return;
   EXPECT_EQ(0, [mock_central_manager_ scanForPeripheralsCallCount]);
 
@@ -212,7 +212,7 @@
 }
 
 TEST_F(BluetoothAdapterMacTest, RemoveDiscoverySessionWithLowEnergyFilterFail) {
-  if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
+  if (!SetMockCentralManager(CBManagerStatePoweredOn))
     return;
   EXPECT_EQ(0, [mock_central_manager_ scanForPeripheralsCallCount]);
   EXPECT_EQ(0, [mock_central_manager_ stopScanCallCount]);
@@ -230,7 +230,7 @@
 }
 
 TEST_F(BluetoothAdapterMacTest, CheckGetPeripheralHashAddress) {
-  if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
+  if (!SetMockCentralManager(CBManagerStatePoweredOn))
     return;
   base::scoped_nsobject<CBPeripheral> mock_peripheral(
       CreateMockPeripheral(kTestNSUUID));
@@ -240,7 +240,7 @@
 }
 
 TEST_F(BluetoothAdapterMacTest, LowEnergyDeviceUpdatedNewDevice) {
-  if (!SetMockCentralManager(CBCentralManagerStatePoweredOn))
+  if (!SetMockCentralManager(CBManagerStatePoweredOn))
     return;
   base::scoped_nsobject<CBPeripheral> mock_peripheral(
       CreateMockPeripheral(kTestNSUUID));
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.mm b/device/bluetooth/bluetooth_low_energy_device_mac.mm
index e80b9c5..1e25ff4 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.mm
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.mm
@@ -20,6 +20,18 @@
 #include "device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h"
 #include "device/bluetooth/bluetooth_remote_gatt_service_mac.h"
 
+// Remove when Chrome no longer supports 10.12.
+#if defined(MAC_OS_X_VERSION_10_13)
+
+// In the 10.13 SDK, CBPeripheral became a subclass of CBPeer, which defines
+// -[CBPeer identifier] as partially available. Pretend it still exists on
+// CBPeripheral. At runtime the implementation on CBPeer will be invoked.
+@interface CBPeripheral (HighSierraSDK)
+@property(readonly, nonatomic) NSUUID* identifier;
+@end
+
+#endif  // MAC_OS_X_VERSION_10_13
+
 namespace device {
 
 BluetoothLowEnergyDeviceMac::BluetoothLowEnergyDeviceMac(
diff --git a/device/bluetooth/bluetooth_low_energy_discovery_manager_mac.mm b/device/bluetooth/bluetooth_low_energy_discovery_manager_mac.mm
index 609186b..7cb766f8 100644
--- a/device/bluetooth/bluetooth_low_energy_discovery_manager_mac.mm
+++ b/device/bluetooth/bluetooth_low_energy_discovery_manager_mac.mm
@@ -46,8 +46,8 @@
     return;
   }
 
-  if ([central_manager_ state] != CBCentralManagerStatePoweredOn) {
-    VLOG(1) << "TryStartDiscovery != CBCentralManagerStatePoweredOn";
+  if ([central_manager_ state] != CBManagerStatePoweredOn) {
+    VLOG(1) << "TryStartDiscovery != CBManagerStatePoweredOn";
     return;
   }
 
diff --git a/device/bluetooth/test/bluetooth_test_mac.mm b/device/bluetooth/test/bluetooth_test_mac.mm
index 5f16078..eac0c6a 100644
--- a/device/bluetooth/test/bluetooth_test_mac.mm
+++ b/device/bluetooth/test/bluetooth_test_mac.mm
@@ -2,28 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "device/bluetooth/test/bluetooth_test_mac.h"
+#import "device/bluetooth/test/bluetooth_test_mac.h"
 
+#import <CoreBluetooth/CoreBluetooth.h>
 #include <stdint.h>
 
-#include "base/mac/foundation_util.h"
+#import "base/mac/foundation_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/sys_string_conversions.h"
 #include "build/build_config.h"
-#include "device/bluetooth/bluetooth_adapter_mac.h"
-#include "device/bluetooth/bluetooth_device_mac.h"
-#include "device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h"
-#include "device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h"
-#include "device/bluetooth/bluetooth_remote_gatt_service_mac.h"
-#include "device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h"
-#include "device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h"
-#include "device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h"
-#include "device/bluetooth/test/mock_bluetooth_cbservice_mac.h"
-#include "device/bluetooth/test/mock_bluetooth_central_manager_mac.h"
-#include "device/bluetooth/test/test_bluetooth_adapter_observer.h"
-#include "third_party/ocmock/OCMock/OCMock.h"
-
-#import <CoreBluetooth/CoreBluetooth.h>
+#import "device/bluetooth/bluetooth_adapter_mac.h"
+#import "device/bluetooth/bluetooth_device_mac.h"
+#import "device/bluetooth/bluetooth_remote_gatt_characteristic_mac.h"
+#import "device/bluetooth/bluetooth_remote_gatt_descriptor_mac.h"
+#import "device/bluetooth/bluetooth_remote_gatt_service_mac.h"
+#import "device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h"
+#import "device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h"
+#import "device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h"
+#import "device/bluetooth/test/mock_bluetooth_cbservice_mac.h"
+#import "device/bluetooth/test/mock_bluetooth_central_manager_mac.h"
+#import "device/bluetooth/test/test_bluetooth_adapter_observer.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
 
 using base::mac::ObjCCast;
 using base::scoped_nsobject;
@@ -114,7 +113,7 @@
     mock_central_manager_.reset(
         new ScopedMockCentralManager([[MockCentralManager alloc] init]));
     [mock_central_manager_->get() setBluetoothTestMac:this];
-    [mock_central_manager_->get() setState:CBCentralManagerStateUnsupported];
+    [mock_central_manager_->get() setState:CBManagerStateUnsupported];
     adapter_mac_->SetCentralManagerForTesting((id)mock_central_manager_->get());
   }
 }
@@ -130,7 +129,7 @@
     mock_central_manager_.reset(
         new ScopedMockCentralManager([[MockCentralManager alloc] init]));
     mock_central_manager_->get().bluetoothTestMac = this;
-    [mock_central_manager_->get() setState:CBCentralManagerStatePoweredOn];
+    [mock_central_manager_->get() setState:CBManagerStatePoweredOn];
     adapter_mac_->SetCentralManagerForTesting((id)mock_central_manager_->get());
   }
 }
@@ -141,7 +140,7 @@
 }
 
 void BluetoothTestMac::SimulateAdapterPoweredOff() {
-  [mock_central_manager_->get() setState:CBCentralManagerStatePoweredOff];
+  [mock_central_manager_->get() setState:CBManagerStatePoweredOff];
 
   for (BluetoothDevice* device : adapter_->GetDevices()) {
     MockCBPeripheral* peripheral_mock = GetMockCBPeripheral(device);
diff --git a/device/bluetooth/test/mock_bluetooth_central_manager_mac.h b/device/bluetooth/test/mock_bluetooth_central_manager_mac.h
index aee59e3..e20f967 100644
--- a/device/bluetooth/test/mock_bluetooth_central_manager_mac.h
+++ b/device/bluetooth/test/mock_bluetooth_central_manager_mac.h
@@ -5,12 +5,13 @@
 #ifndef DEVICE_BLUETOOTH_MOCK_BLUETOOTH_CENTRAL_MANAGER_MAC_H_
 #define DEVICE_BLUETOOTH_MOCK_BLUETOOTH_CENTRAL_MANAGER_MAC_H_
 
-#include "base/mac/sdk_forward_declarations.h"
-#include "build/build_config.h"
-#include "device/bluetooth/test/bluetooth_test_mac.h"
-
 #import <CoreBluetooth/CoreBluetooth.h>
 
+#import "base/mac/sdk_forward_declarations.h"
+#include "build/build_config.h"
+#import "device/bluetooth/bluetooth_adapter_mac.h"
+#import "device/bluetooth/test/bluetooth_test_mac.h"
+
 // Class to mock a CBCentralManager. Cannot use a OCMockObject because mocking
 // the 'state' property gives a compiler warning when mock_central_manager is of
 // type id (multiple methods named 'state' found), and a compiler warning when
@@ -21,7 +22,7 @@
 @property(nonatomic, assign) NSInteger scanForPeripheralsCallCount;
 @property(nonatomic, assign) NSInteger stopScanCallCount;
 @property(nonatomic, assign) id<CBCentralManagerDelegate> delegate;
-@property(nonatomic, assign) CBCentralManagerState state;
+@property(nonatomic, assign) CBManagerState state;
 @property(nonatomic, assign) device::BluetoothTestMac* bluetoothTestMac;
 @property(nonatomic, readonly) NSArray* retrieveConnectedPeripheralServiceUUIDs;
 
diff --git a/ios/build/bots/tests/common_tests.json b/ios/build/bots/tests/common_tests.json
index c7a6aef9..36a06e25 100644
--- a/ios/build/bots/tests/common_tests.json
+++ b/ios/build/bots/tests/common_tests.json
@@ -22,6 +22,9 @@
       "app": "google_apis_unittests"
     },
     {
+      "app": "ios_chrome_unittests"
+    },
+    {
       "app": "ios_net_unittests"
     },
     {
@@ -31,6 +34,9 @@
       "app": "ios_web_unittests"
     },
     {
+      "app": "net_unittests"
+    },
+    {
       "app": "skia_unittests"
     },
     {
diff --git a/ios/chrome/browser/ui/dialogs/BUILD.gn b/ios/chrome/browser/ui/dialogs/BUILD.gn
index 0cb7340..0f915b8 100644
--- a/ios/chrome/browser/ui/dialogs/BUILD.gn
+++ b/ios/chrome/browser/ui/dialogs/BUILD.gn
@@ -40,8 +40,6 @@
     "dialog_presenter.mm",
     "java_script_dialog_presenter_impl.h",
     "java_script_dialog_presenter_impl.mm",
-    "nsurl_protection_space_util.h",
-    "nsurl_protection_space_util.mm",
   ]
   deps = [
     ":dialogs",
@@ -51,6 +49,7 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/alert_coordinator",
+    "//ios/shared/chrome/browser/ui/dialogs",
     "//ios/web",
     "//ui/base",
     "//url",
@@ -63,7 +62,6 @@
   testonly = true
   sources = [
     "dialog_presenter_unittest.mm",
-    "nsurl_protection_space_util_unittest.mm",
   ]
   deps = [
     ":dialogs_internal",
@@ -71,6 +69,7 @@
     "//components/strings",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui/alert_coordinator",
+    "//ios/shared/chrome/browser/ui/dialogs:unit_tests",
     "//ios/web",
     "//ios/web/public/test/fakes",
     "//testing/gtest",
diff --git a/ios/chrome/browser/ui/dialogs/dialog_presenter.mm b/ios/chrome/browser/ui/dialogs/dialog_presenter.mm
index 5be1336d..1d7094f 100644
--- a/ios/chrome/browser/ui/dialogs/dialog_presenter.mm
+++ b/ios/chrome/browser/ui/dialogs/dialog_presenter.mm
@@ -16,9 +16,9 @@
 #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
 #import "ios/chrome/browser/ui/alert_coordinator/input_alert_coordinator.h"
 #import "ios/chrome/browser/ui/dialogs/javascript_dialog_blocking_util.h"
-#import "ios/chrome/browser/ui/dialogs/nsurl_protection_space_util.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
+#import "ios/shared/chrome/browser/ui/dialogs/nsurl_protection_space_util.h"
 #include "ios/web/public/web_state/web_state.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
diff --git a/ios/shared/chrome/browser/ui/dialogs/BUILD.gn b/ios/shared/chrome/browser/ui/dialogs/BUILD.gn
new file mode 100644
index 0000000..a04eaea
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/dialogs/BUILD.gn
@@ -0,0 +1,40 @@
+# 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.
+
+source_set("dialogs") {
+  deps = [
+    "//base",
+    "//components/strings",
+    "//components/url_formatter",
+    "//ios/web",
+    "//ui/base",
+  ]
+
+  sources = [
+    "nsurl_protection_space_util.h",
+    "nsurl_protection_space_util.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [
+    "nsurl_protection_space_util_unittest.mm",
+  ]
+
+  deps = [
+    ":dialogs",
+    "//base",
+    "//components/strings",
+    "//ios/web",
+    "//ios/web/public/test/fakes",
+    "//testing/gtest",
+    "//ui/base",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/shared/chrome/browser/ui/dialogs/DEPS b/ios/shared/chrome/browser/ui/dialogs/DEPS
new file mode 100644
index 0000000..c9cf83b
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/dialogs/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+components/strings/grit",
+  "+components/url_formatter",
+]
diff --git a/ios/shared/chrome/browser/ui/dialogs/OWNERS b/ios/shared/chrome/browser/ui/dialogs/OWNERS
new file mode 100644
index 0000000..48efb49e
--- /dev/null
+++ b/ios/shared/chrome/browser/ui/dialogs/OWNERS
@@ -0,0 +1 @@
+kkhorimoto@chromium.org
diff --git a/ios/chrome/browser/ui/dialogs/nsurl_protection_space_util.h b/ios/shared/chrome/browser/ui/dialogs/nsurl_protection_space_util.h
similarity index 78%
rename from ios/chrome/browser/ui/dialogs/nsurl_protection_space_util.h
rename to ios/shared/chrome/browser/ui/dialogs/nsurl_protection_space_util.h
index 77bba8dd..c46efc8 100644
--- a/ios/chrome/browser/ui/dialogs/nsurl_protection_space_util.h
+++ b/ios/shared/chrome/browser/ui/dialogs/nsurl_protection_space_util.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 IOS_CHROME_BROWSER_UI_DIALOGS_NSURL_PROTECTION_SPACE_UTIL_H_
-#define IOS_CHROME_BROWSER_UI_DIALOGS_NSURL_PROTECTION_SPACE_UTIL_H_
+#ifndef IOS_SHARED_CHROME_BROWSER_UI_DIALOGS_NSURL_PROTECTION_SPACE_UTIL_H_
+#define IOS_SHARED_CHROME_BROWSER_UI_DIALOGS_NSURL_PROTECTION_SPACE_UTIL_H_
 
 #import <Foundation/Foundation.h>
 
@@ -27,4 +27,4 @@
 }  // namespace nsurlprotectionspace_util
 }  // namespace ios_internal
 
-#endif  // IOS_CHROME_BROWSER_UI_DIALOGS_NSURL_PROTECTION_SPACE_UTIL_H_
+#endif  // IOS_SHARED_CHROME_BROWSER_UI_DIALOGS_NSURL_PROTECTION_SPACE_UTIL_H_
diff --git a/ios/chrome/browser/ui/dialogs/nsurl_protection_space_util.mm b/ios/shared/chrome/browser/ui/dialogs/nsurl_protection_space_util.mm
similarity index 96%
rename from ios/chrome/browser/ui/dialogs/nsurl_protection_space_util.mm
rename to ios/shared/chrome/browser/ui/dialogs/nsurl_protection_space_util.mm
index 166b892..0b04896 100644
--- a/ios/chrome/browser/ui/dialogs/nsurl_protection_space_util.mm
+++ b/ios/shared/chrome/browser/ui/dialogs/nsurl_protection_space_util.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/dialogs/nsurl_protection_space_util.h"
+#import "ios/shared/chrome/browser/ui/dialogs/nsurl_protection_space_util.h"
 
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/sys_string_conversions.h"
diff --git a/ios/chrome/browser/ui/dialogs/nsurl_protection_space_util_unittest.mm b/ios/shared/chrome/browser/ui/dialogs/nsurl_protection_space_util_unittest.mm
similarity index 98%
rename from ios/chrome/browser/ui/dialogs/nsurl_protection_space_util_unittest.mm
rename to ios/shared/chrome/browser/ui/dialogs/nsurl_protection_space_util_unittest.mm
index e565270..b709732 100644
--- a/ios/chrome/browser/ui/dialogs/nsurl_protection_space_util_unittest.mm
+++ b/ios/shared/chrome/browser/ui/dialogs/nsurl_protection_space_util_unittest.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/dialogs/nsurl_protection_space_util.h"
+#import "ios/shared/chrome/browser/ui/dialogs/nsurl_protection_space_util.h"
 
 #include "base/ios/ios_util.h"
 #include "base/strings/sys_string_conversions.h"
diff --git a/services/preferences/BUILD.gn b/services/preferences/BUILD.gn
index d60d44d..f2e856a6 100644
--- a/services/preferences/BUILD.gn
+++ b/services/preferences/BUILD.gn
@@ -55,6 +55,7 @@
   ]
   sources = [
     "persistent_pref_store_impl_unittest.cc",
+    "pref_store_consistency_unittest.cc",
   ]
   if (!is_ios) {
     sources += [ "pref_service_factory_unittest.cc" ]
diff --git a/services/preferences/persistent_pref_store_impl.cc b/services/preferences/persistent_pref_store_impl.cc
index f5b4ae1..3054526 100644
--- a/services/preferences/persistent_pref_store_impl.cc
+++ b/services/preferences/persistent_pref_store_impl.cc
@@ -16,6 +16,65 @@
 #include "services/preferences/public/cpp/lib/util.h"
 
 namespace prefs {
+namespace {
+
+// Creates a PrefUpdateValuePtr representing |value| at |path|.
+mojom::PrefUpdateValuePtr CreatePrefUpdate(const std::vector<std::string>& path,
+                                           const base::Value* value) {
+  if (path.empty()) {
+    return mojom::PrefUpdateValue::NewAtomicUpdate(
+        value ? value->CreateDeepCopy() : nullptr);
+  }
+  std::vector<mojom::SubPrefUpdatePtr> pref_updates;
+  pref_updates.emplace_back(base::in_place, path,
+                            value ? value->CreateDeepCopy() : nullptr);
+  return mojom::PrefUpdateValue::NewSplitUpdates(std::move(pref_updates));
+}
+
+// Returns a mojom::PrefUpdateValuePtr for |path| relative to |value|. If the
+// full path does not exist, a PrefUpdateValue containing the closest value is
+// returned.
+//
+// For example, for a |path| of {"foo", "bar"}:
+//  - with a |value| of
+//     {
+//       "foo": 1
+//     }
+//   returns a path {"foo"} and value 1.
+//
+// - with a |value| of
+//     {}
+//   returns a path {"foo"} and null value.
+//
+// - with a |value| of
+//     {
+//       "foo": {}
+//     }
+//   returns a path {"foo", "bar"} and null value.
+//
+// - with a |value| of
+//     {
+//       "foo": {
+//         "bar": "baz"
+//       }
+//     }
+//   returns a path {"foo", "bar"} and value "baz".
+mojom::PrefUpdateValuePtr LookupPrefUpdate(const std::vector<std::string>& path,
+                                           const base::Value* value) {
+  if (!value)
+    return CreatePrefUpdate(std::vector<std::string>(), value);
+
+  for (size_t i = 0; i < path.size(); ++i) {
+    const base::DictionaryValue* dictionary_value = nullptr;
+    if (!value->GetAsDictionary(&dictionary_value) ||
+        !dictionary_value->Get(path[i], &value)) {
+      return CreatePrefUpdate({path.begin(), path.begin() + i}, value);
+    }
+  }
+  return CreatePrefUpdate(path, value);
+}
+
+}  // namespace
 
 class PersistentPrefStoreImpl::Connection : public mojom::PersistentPrefStore {
  public:
@@ -55,6 +114,19 @@
   void SetValues(std::vector<mojom::PrefUpdatePtr> updates) override {
     base::AutoReset<bool> scoped_call_in_progress(&write_in_progress_, true);
     pref_store_->SetValues(std::move(updates));
+    observer_->OnPrefChangeAck();
+  }
+
+  void RequestValue(const std::string& key,
+                    const std::vector<std::string>& path) override {
+    if (!base::ContainsKey(observed_keys_, key))
+      return;
+
+    const base::Value* value = nullptr;
+    pref_store_->GetValue(key, &value);
+    std::vector<mojom::PrefUpdatePtr> updates;
+    updates.emplace_back(base::in_place, key, LookupPrefUpdate(path, value), 0);
+    observer_->OnPrefsChanged(std::move(updates));
   }
 
   void CommitPendingWrite() override { pref_store_->CommitPendingWrite(); }
@@ -171,6 +243,11 @@
   }
 }
 
+bool PersistentPrefStoreImpl::GetValue(const std::string& key,
+                                       const base::Value** value) const {
+  return backing_pref_store_->GetValue(key, value);
+}
+
 void PersistentPrefStoreImpl::CommitPendingWrite() {
   backing_pref_store_->CommitPendingWrite();
 }
diff --git a/services/preferences/persistent_pref_store_impl.h b/services/preferences/persistent_pref_store_impl.h
index 5111ed7..f2e73d2 100644
--- a/services/preferences/persistent_pref_store_impl.h
+++ b/services/preferences/persistent_pref_store_impl.h
@@ -37,6 +37,7 @@
   class Connection;
 
   void SetValues(std::vector<mojom::PrefUpdatePtr> updates);
+  bool GetValue(const std::string& key, const base::Value** value) const;
 
   void CommitPendingWrite();
   void SchedulePendingLossyWrites();
diff --git a/services/preferences/persistent_pref_store_impl_unittest.cc b/services/preferences/persistent_pref_store_impl_unittest.cc
index 7a32627..c8fac3a 100644
--- a/services/preferences/persistent_pref_store_impl_unittest.cc
+++ b/services/preferences/persistent_pref_store_impl_unittest.cc
@@ -200,7 +200,7 @@
   EXPECT_TRUE(other_pref_store->IsInitializationComplete());
 
   const base::Value value("value");
-  pref_store()->SetValueSilently(kKey, value.CreateDeepCopy(), 0);
+  pref_store()->SetValue(kKey, value.CreateDeepCopy(), 0);
 
   ExpectPrefChange(other_pref_store.get(), kKey);
   const base::Value* output = nullptr;
diff --git a/services/preferences/pref_service_factory_unittest.cc b/services/preferences/pref_service_factory_unittest.cc
index 02752863..ce23803 100644
--- a/services/preferences/pref_service_factory_unittest.cc
+++ b/services/preferences/pref_service_factory_unittest.cc
@@ -496,7 +496,12 @@
 
   {
     ScopedDictionaryPrefUpdate update(pref_service.get(), kDictionaryKey);
-    update.Get();
+    update->SetInteger(kKey, kInitialValue);
+  }
+  WaitForPrefChange(pref_service2.get(), kDictionaryKey);
+  {
+    ScopedDictionaryPrefUpdate update(pref_service.get(), kDictionaryKey);
+    update->Remove(kKey, nullptr);
   }
   WaitForPrefChange(pref_service2.get(), kDictionaryKey);
   EXPECT_TRUE(pref_service2->GetDictionary(kDictionaryKey)->empty());
diff --git a/services/preferences/pref_store_consistency_unittest.cc b/services/preferences/pref_store_consistency_unittest.cc
new file mode 100644
index 0000000..ef53f4c0
--- /dev/null
+++ b/services/preferences/pref_store_consistency_unittest.cc
@@ -0,0 +1,916 @@
+// 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 <utility>
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/values.h"
+#include "components/prefs/in_memory_pref_store.h"
+#include "components/prefs/pref_notifier_impl.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "services/preferences/persistent_pref_store_impl.h"
+#include "services/preferences/public/cpp/dictionary_value_update.h"
+#include "services/preferences/public/cpp/persistent_pref_store_client.h"
+#include "services/preferences/public/cpp/scoped_pref_update.h"
+#include "services/preferences/public/interfaces/preferences.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace prefs {
+namespace {
+
+constexpr int kInitialValue = 1;
+constexpr char kKey[] = "key";
+constexpr char kNestedKey[] = "key.child";
+constexpr char kChildKey[] = "child";
+constexpr char kOtherKey[] = "other_key";
+constexpr char kNestedOtherKey[] = "key.other_key";
+constexpr char kDictionaryKey[] = "a.dictionary.pref";
+
+void DoNothingHandleReadError(PersistentPrefStore::PrefReadError error) {}
+
+struct UpdateOrAck {
+  std::vector<mojom::PrefUpdatePtr> updates;
+  bool is_ack;
+};
+
+struct Request {
+  std::string key;
+  std::vector<std::string> path;
+};
+
+struct UpdateOrRequest {
+  std::vector<mojom::PrefUpdatePtr> updates;
+  Request request;
+};
+
+// A client connection to a between a PersistentPrefStoreImpl and a
+// PersistentPrefStoreClient that provides control over when messages between
+// the two are delivered.
+class PrefServiceConnection : public mojom::PrefStoreObserver,
+                              public mojom::PersistentPrefStore {
+ public:
+  explicit PrefServiceConnection(PersistentPrefStoreImpl* pref_store)
+      : observer_binding_(this), pref_store_binding_(this) {
+    auto connection = pref_store->CreateConnection({
+        kKey, kOtherKey, kDictionaryKey,
+    });
+    observer_binding_.Bind(
+        std::move(connection->pref_store_connection->observer));
+    connection->pref_store_connection->observer = mojo::MakeRequest(&observer_);
+
+    pref_store_ = std::move(connection->pref_store);
+    pref_store_binding_.Bind(mojo::MakeRequest(&connection->pref_store));
+
+    pref_store_client_ =
+        base::MakeRefCounted<PersistentPrefStoreClient>(std::move(connection));
+    PrefNotifierImpl* pref_notifier = new PrefNotifierImpl();
+    auto pref_registry = base::MakeRefCounted<PrefRegistrySimple>();
+    pref_registry->RegisterIntegerPref(kKey, kInitialValue);
+    pref_registry->RegisterIntegerPref(kOtherKey, kInitialValue);
+    pref_registry->RegisterDictionaryPref(kDictionaryKey);
+    auto* pref_value_store = new PrefValueStore(
+        nullptr, nullptr, nullptr, nullptr, pref_store_client_.get(), nullptr,
+        pref_registry->defaults().get(), pref_notifier);
+    pref_service_ = base::MakeUnique<::PrefService>(
+        pref_notifier, pref_value_store, pref_store_client_.get(),
+        pref_registry.get(), base::Bind(&DoNothingHandleReadError), true);
+  }
+
+  ~PrefServiceConnection() override {
+    observer_binding_.FlushForTesting();
+    pref_store_binding_.FlushForTesting();
+    EXPECT_TRUE(writes_.empty());
+    EXPECT_TRUE(updates_.empty());
+  }
+
+  PrefService& pref_service() { return *pref_service_; }
+
+  void WaitForWrites(size_t num_writes) {
+    if (writes_.size() >= num_writes)
+      return;
+
+    expected_writes_ = num_writes;
+    base::RunLoop run_loop;
+    stop_ = run_loop.QuitClosure();
+    run_loop.Run();
+  }
+  void ForwardWrites(size_t num_writes) {
+    WaitForWrites(num_writes);
+    for (size_t i = 0; i < num_writes; ++i) {
+      auto& write = writes_.front();
+      if (write.updates.empty()) {
+        pref_store_->RequestValue(write.request.key, write.request.path);
+      } else {
+        pref_store_->SetValues(std::move(write.updates));
+      }
+      writes_.pop_front();
+    }
+    pref_store_.FlushForTesting();
+  }
+  void WaitForUpdates(size_t num_updates) {
+    if (updates_.size() >= num_updates)
+      return;
+
+    expected_updates_ = num_updates;
+    base::RunLoop run_loop;
+    stop_ = run_loop.QuitClosure();
+    run_loop.Run();
+  }
+
+  void ForwardUpdates(size_t num_updates) {
+    WaitForUpdates(num_updates);
+    for (size_t i = 0; i < num_updates; ++i) {
+      auto& update = updates_.front();
+      if (update.is_ack) {
+        observer_->OnPrefChangeAck();
+      } else {
+        observer_->OnPrefsChanged(std::move(update.updates));
+      }
+      updates_.pop_front();
+    }
+    observer_.FlushForTesting();
+  }
+
+  // mojom::PersistentPrefStore
+  void SetValues(std::vector<mojom::PrefUpdatePtr> updates) override {
+    writes_.push_back({std::move(updates)});
+    if (writes_.size() == expected_writes_) {
+      expected_writes_ = 0;
+      std::move(stop_).Run();
+    }
+  }
+  void RequestValue(const std::string& key,
+                    const std::vector<std::string>& path) override {
+    writes_.push_back({std::vector<mojom::PrefUpdatePtr>(), {key, path}});
+    if (writes_.size() == expected_writes_) {
+      expected_writes_ = 0;
+      std::move(stop_).Run();
+    }
+  }
+  void CommitPendingWrite() override {}
+  void SchedulePendingLossyWrites() override {}
+  void ClearMutableValues() override {}
+
+  // mojom::PrefStoreObserver
+  void OnPrefsChanged(std::vector<mojom::PrefUpdatePtr> updates) override {
+    updates_.push_back({std::move(updates), false});
+    if (updates_.size() == expected_updates_) {
+      expected_updates_ = 0;
+      std::move(stop_).Run();
+    }
+  }
+  void OnInitializationCompleted(bool succeeded) override { NOTREACHED(); }
+  void OnPrefChangeAck() override {
+    updates_.push_back({std::vector<mojom::PrefUpdatePtr>(), true});
+    acks_.push_back(updates_.size());
+  }
+
+ private:
+  scoped_refptr<PersistentPrefStoreClient> pref_store_client_;
+  std::unique_ptr<PrefService> pref_service_;
+  mojom::PrefStoreObserverPtr observer_;
+  mojom::PersistentPrefStorePtr pref_store_;
+  mojo::Binding<mojom::PrefStoreObserver> observer_binding_;
+  mojo::Binding<mojom::PersistentPrefStore> pref_store_binding_;
+
+  base::OnceClosure stop_;
+  size_t expected_writes_ = 0;
+  size_t expected_updates_ = 0;
+
+  std::deque<UpdateOrRequest> writes_;
+  std::deque<UpdateOrAck> updates_;
+  std::deque<size_t> acks_;
+};
+
+class PersistentPrefStoreConsistencyTest : public testing::Test {
+ public:
+  void SetUp() override {
+    pref_store_ = base::MakeRefCounted<InMemoryPrefStore>();
+    pref_store_impl_ = base::MakeUnique<PersistentPrefStoreImpl>(
+        pref_store_, base::BindOnce(&base::DoNothing));
+  }
+
+  PersistentPrefStore* pref_store() { return pref_store_.get(); }
+
+  std::unique_ptr<PrefServiceConnection> CreateConnection() {
+    return base::MakeUnique<PrefServiceConnection>(pref_store_impl_.get());
+  }
+
+ private:
+  scoped_refptr<PersistentPrefStore> pref_store_;
+  std::unique_ptr<PersistentPrefStoreImpl> pref_store_impl_;
+  base::MessageLoop message_loop_;
+};
+
+TEST_F(PersistentPrefStoreConsistencyTest, TwoPrefs) {
+  pref_store()->SetValue(kKey, base::MakeUnique<base::Value>(kInitialValue), 0);
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+
+  EXPECT_EQ(kInitialValue, pref_service.GetInteger(kKey));
+  EXPECT_EQ(kInitialValue, pref_service2.GetInteger(kKey));
+
+  pref_service.SetInteger(kKey, 2);
+  pref_service2.SetInteger(kKey, 3);
+
+  // The writes arrive in the order: 2, 3.
+  connection->ForwardWrites(1);
+  connection2->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(2, pref_service.GetInteger(kKey));
+
+  // This connection already has a later value. It should not move backwards in
+  // time.
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(3, pref_service2.GetInteger(kKey));
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(3, pref_service.GetInteger(kKey));
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(3, pref_service2.GetInteger(kKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest, TwoSubPrefs) {
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey);
+    update->SetInteger(kKey, 2);
+  }
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kKey, 3);
+  }
+
+  // The writes arrive in the order: 2, 3.
+  connection->ForwardWrites(1);
+  connection2->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue two_dict;
+  two_dict.SetInteger(kKey, 2);
+  base::DictionaryValue three_dict;
+  three_dict.SetInteger(kKey, 3);
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(two_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  // This connection already has a later value. It should not move backwards in
+  // time.
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(three_dict, *pref_service.GetDictionary(kDictionaryKey));
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest, DifferentSubPrefs) {
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey);
+    update->SetInteger(kKey, 2);
+  }
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kOtherKey, 3);
+  }
+
+  connection->ForwardWrites(1);
+  connection2->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue two_dict;
+  two_dict.SetInteger(kKey, 2);
+  base::DictionaryValue expected_dict;
+  expected_dict.SetInteger(kKey, 2);
+  expected_dict.SetInteger(kOtherKey, 3);
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(two_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  // This connection already has a later value. It should not move backwards in
+  // time.
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest, WriteParentThenChild) {
+  auto initial_value = base::MakeUnique<base::DictionaryValue>();
+  initial_value->SetDictionary(kDictionaryKey,
+                               base::MakeUnique<base::DictionaryValue>());
+  pref_store()->SetValue(kDictionaryKey, std::move(initial_value), 0);
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey);
+    update->SetInteger(kKey, 4);
+    update->Clear();
+    update->SetInteger(kKey, 2);
+    update->SetInteger(kOtherKey, 4);
+  }
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kKey, 3);
+  }
+
+  connection->ForwardWrites(1);
+  connection2->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue two_and_four_dict;
+  two_and_four_dict.SetInteger(kKey, 2);
+  two_and_four_dict.SetInteger(kOtherKey, 4);
+  base::DictionaryValue three_dict;
+  three_dict.SetInteger(kKey, 3);
+  three_dict.SetDictionary(kDictionaryKey,
+                           base::MakeUnique<base::DictionaryValue>());
+  base::DictionaryValue five_dict = three_dict;
+  five_dict.SetInteger(kKey, 5);
+  base::DictionaryValue expected_dict;
+  expected_dict.SetInteger(kKey, 3);
+  expected_dict.SetInteger(kOtherKey, 4);
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(two_and_four_dict, *pref_service.GetDictionary(kDictionaryKey));
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  // Applying the other client's write would lose this client's write so is
+  // ignored. Instead, this client requests an updated value from the service.
+  EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey));
+  connection2->ForwardWrites(1);
+
+  // Perform another update while waiting for the updated value from the
+  // service.
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kKey, 5);
+  }
+  connection2->ForwardWrites(1);
+
+  expected_dict.SetInteger(kKey, 5);
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  // Ack for the original write.
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(five_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  // New value that is ignored due to the second in-flight write.
+  connection2->ForwardUpdates(1);
+  // Sending another request for the value from the service.
+  connection2->ForwardWrites(1);
+  EXPECT_EQ(five_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  // Ack for the second write.
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(five_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest, WriteChildThenParent) {
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey);
+    update->SetInteger(kKey, 4);
+    update->Clear();
+    update->SetInteger(kKey, 2);
+  }
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kKey, 3);
+  }
+
+  connection2->ForwardWrites(1);
+  connection->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue expected_dict;
+  expected_dict.SetInteger(kKey, 2);
+  base::DictionaryValue three_dict;
+  three_dict.SetInteger(kKey, 3);
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest, WriteChildThenDeleteParent) {
+  pref_store()->SetValue(kDictionaryKey,
+                         base::MakeUnique<base::DictionaryValue>(), 0);
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  pref_service.ClearPref(kDictionaryKey);
+  EXPECT_FALSE(pref_service.HasPrefPath(kDictionaryKey));
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kKey, 3);
+  }
+
+  connection2->ForwardWrites(1);
+  connection->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue expected_dict;
+  expected_dict.SetInteger(kKey, 2);
+  base::DictionaryValue three_dict;
+  three_dict.SetInteger(kKey, 3);
+
+  connection->ForwardUpdates(1);
+  EXPECT_FALSE(pref_service.HasPrefPath(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection->ForwardUpdates(1);
+  EXPECT_FALSE(pref_service.HasPrefPath(kDictionaryKey));
+  connection2->ForwardUpdates(1);
+  EXPECT_FALSE(pref_service2.HasPrefPath(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest, DeleteParentThenWriteChild) {
+  auto initial_value = base::MakeUnique<base::DictionaryValue>();
+  initial_value->SetInteger(kOtherKey, 5);
+  pref_store()->SetValue(kDictionaryKey, std::move(initial_value), 0);
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  pref_service.ClearPref(kDictionaryKey);
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kKey, 3);
+  }
+
+  connection->ForwardWrites(1);
+  connection2->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue intermediate_dict;
+  intermediate_dict.SetInteger(kKey, 3);
+  intermediate_dict.SetInteger(kOtherKey, 5);
+
+  base::DictionaryValue expected_dict;
+  expected_dict.SetInteger(kKey, 3);
+
+  connection->ForwardUpdates(1);
+  EXPECT_FALSE(pref_service.HasPrefPath(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(intermediate_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardWrites(1);
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(intermediate_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest, WriteParentThenDeleteChild) {
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey);
+    update->SetInteger(kKey, 1);
+    update->Clear();
+    update->SetInteger(kKey, 2);
+    update->SetInteger(kOtherKey, 4);
+  }
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kKey, 3);
+    update->RemovePath(kKey, nullptr);
+  }
+
+  connection->ForwardWrites(1);
+  connection2->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue two_and_four_dict;
+  two_and_four_dict.SetInteger(kKey, 2);
+  two_and_four_dict.SetInteger(kOtherKey, 4);
+  base::DictionaryValue empty_dict;
+  base::DictionaryValue expected_dict;
+  expected_dict.SetInteger(kOtherKey, 4);
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(two_and_four_dict, *pref_service.GetDictionary(kDictionaryKey));
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(empty_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  // Applying the other client's write would lose this client's write so is
+  // ignored. Instead, this client requests an updated value from the service.
+  EXPECT_EQ(empty_dict, *pref_service2.GetDictionary(kDictionaryKey));
+  connection2->ForwardWrites(1);
+
+  // Ack for the original write.
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(empty_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest, DeleteChildThenWriteParent) {
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey);
+    update->SetInteger(kKey, 4);
+    update->Clear();
+    update->SetInteger(kKey, 2);
+    update->SetInteger(kOtherKey, 4);
+  }
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kKey, 3);
+    update->RemovePath(kKey, nullptr);
+  }
+
+  connection2->ForwardWrites(1);
+  connection->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue empty_dict;
+  base::DictionaryValue expected_dict;
+  expected_dict.SetInteger(kKey, 2);
+  expected_dict.SetInteger(kOtherKey, 4);
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(empty_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest, ReplaceParentThenWriteChild) {
+  auto initial_value = base::MakeUnique<base::DictionaryValue>();
+  initial_value->SetInteger(kNestedOtherKey, 5);
+  pref_store()->SetValue(kDictionaryKey, std::move(initial_value), 0);
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey);
+    update->SetInteger(kKey, 1);
+  }
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kNestedKey, 2);
+  }
+
+  connection->ForwardWrites(1);
+  connection2->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue simple_dict;
+  simple_dict.SetInteger(kKey, 1);
+  base::DictionaryValue nested_dict;
+  nested_dict.SetInteger(kNestedKey, 2);
+  nested_dict.SetInteger(kNestedOtherKey, 5);
+  base::DictionaryValue expected_dict;
+  expected_dict.SetInteger(kNestedKey, 2);
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(simple_dict, *pref_service.GetDictionary(kDictionaryKey));
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(nested_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardWrites(1);
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(nested_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest, WriteChildThenReplaceParent) {
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey);
+    update->SetInteger(kKey, 1);
+  }
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kNestedKey, 2);
+  }
+
+  connection2->ForwardWrites(1);
+  connection->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue simple_dict;
+  simple_dict.SetInteger(kKey, 1);
+  base::DictionaryValue nested_dict;
+  nested_dict.SetInteger(kNestedKey, 2);
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(simple_dict, *pref_service.GetDictionary(kDictionaryKey));
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(simple_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(nested_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(simple_dict, *pref_service2.GetDictionary(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest, NestedWriteParentThenChild) {
+  pref_store()->SetValue(kKey, base::MakeUnique<base::DictionaryValue>(), 0);
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey);
+    auto nested_dict =
+        update->SetDictionary(kKey, base::MakeUnique<base::DictionaryValue>());
+    nested_dict->SetInteger(kChildKey, 2);
+    nested_dict->SetInteger(kOtherKey, 4);
+  }
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kNestedKey, 3);
+  }
+
+  connection->ForwardWrites(1);
+  connection2->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue two_and_four_dict;
+  two_and_four_dict.SetInteger(kNestedKey, 2);
+  two_and_four_dict.SetInteger(kNestedOtherKey, 4);
+  base::DictionaryValue three_dict;
+  three_dict.SetInteger(kNestedKey, 3);
+  base::DictionaryValue expected_dict = two_and_four_dict;
+  expected_dict.SetInteger(kNestedKey, 3);
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(two_and_four_dict, *pref_service.GetDictionary(kDictionaryKey));
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  // Applying the other client's write would lose this client's write so is
+  // ignored. Instead, this client requests an updated value from the service.
+  EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey));
+  connection2->ForwardWrites(1);
+
+  // Ack for the original write.
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  // Updated value from the service.
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest, NestedWriteChildThenParent) {
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey);
+    auto nested_dict =
+        update->SetDictionary(kKey, base::MakeUnique<base::DictionaryValue>());
+    nested_dict->SetInteger(kChildKey, 2);
+    nested_dict->SetInteger(kOtherKey, 4);
+  }
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kNestedKey, 3);
+  }
+
+  connection2->ForwardWrites(1);
+  connection->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue expected_dict;
+  expected_dict.SetInteger(kNestedKey, 2);
+  expected_dict.SetInteger(kNestedOtherKey, 4);
+  base::DictionaryValue three_dict;
+  three_dict.SetInteger(kNestedKey, 3);
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(three_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service2.GetDictionary(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest,
+       DeleteParentThenWriteChildThenDeleteParent) {
+  auto initial_value = base::MakeUnique<base::DictionaryValue>();
+  initial_value->SetInteger(kOtherKey, 5);
+  pref_store()->SetValue(kDictionaryKey, std::move(initial_value), 0);
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  pref_service.ClearPref(kDictionaryKey);
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kKey, 3);
+  }
+
+  connection->ForwardWrites(1);
+  connection2->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue intermediate_dict;
+  intermediate_dict.SetInteger(kKey, 3);
+  intermediate_dict.SetInteger(kOtherKey, 5);
+
+  base::DictionaryValue expected_dict;
+  expected_dict.SetInteger(kKey, 3);
+
+  connection->ForwardUpdates(1);
+  EXPECT_FALSE(pref_service.HasPrefPath(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(intermediate_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  pref_service.ClearPref(kDictionaryKey);
+  connection->ForwardWrites(1);
+  connection->ForwardUpdates(1);
+  EXPECT_FALSE(pref_service.HasPrefPath(kDictionaryKey));
+
+  connection2->ForwardWrites(1);
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(intermediate_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_FALSE(pref_service2.HasPrefPath(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_FALSE(pref_service2.HasPrefPath(kDictionaryKey));
+}
+
+TEST_F(PersistentPrefStoreConsistencyTest,
+       NestedDeleteParentThenWriteChildThenDeleteChild) {
+  auto initial_value = base::MakeUnique<base::DictionaryValue>();
+  initial_value->SetInteger(kNestedOtherKey, 5);
+  pref_store()->SetValue(kDictionaryKey, std::move(initial_value), 0);
+  auto connection = CreateConnection();
+  auto connection2 = CreateConnection();
+  auto& pref_service = connection->pref_service();
+  auto& pref_service2 = connection2->pref_service();
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey);
+    update->RemovePath(kKey, nullptr);
+  }
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service2, kDictionaryKey);
+    update->SetInteger(kNestedKey, 3);
+  }
+
+  connection->ForwardWrites(1);
+  connection2->ForwardWrites(1);
+
+  connection->WaitForUpdates(2);
+  connection2->WaitForUpdates(2);
+
+  base::DictionaryValue intermediate_dict;
+  intermediate_dict.SetInteger(kNestedKey, 3);
+  intermediate_dict.SetInteger(kNestedOtherKey, 5);
+
+  base::DictionaryValue expected_dict;
+  expected_dict.SetInteger(kNestedKey, 3);
+
+  base::DictionaryValue empty_dict;
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(empty_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(intermediate_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection->ForwardUpdates(1);
+  EXPECT_EQ(expected_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  {
+    ScopedDictionaryPrefUpdate update(&pref_service, kDictionaryKey);
+    update->RemovePath(kKey, nullptr);
+  }
+  connection->ForwardWrites(1);
+  connection->ForwardUpdates(1);
+  EXPECT_TRUE(pref_service.HasPrefPath(kDictionaryKey));
+  EXPECT_EQ(empty_dict, *pref_service.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardWrites(1);
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(intermediate_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(empty_dict, *pref_service2.GetDictionary(kDictionaryKey));
+
+  connection2->ForwardUpdates(1);
+  EXPECT_EQ(empty_dict, *pref_service2.GetDictionary(kDictionaryKey));
+}
+
+}  // namespace
+}  // namespace prefs
diff --git a/services/preferences/public/cpp/persistent_pref_store_client.cc b/services/preferences/public/cpp/persistent_pref_store_client.cc
index ef9a633..b63e5aa 100644
--- a/services/preferences/public/cpp/persistent_pref_store_client.cc
+++ b/services/preferences/public/cpp/persistent_pref_store_client.cc
@@ -59,6 +59,92 @@
 
 }  // namespace
 
+// A trie of writes that have been applied locally and sent to the service
+// backend, but have not been acked.
+class PersistentPrefStoreClient::InFlightWriteTrie {
+ public:
+  // Decision on what to do with writes incoming from the service.
+  enum class Decision {
+    // The write should be allowed.
+    kAllow,
+
+    // The write has already been superceded locally and should be ignored.
+    kIgnore,
+
+    // The write may have been partially superceded locally and should be
+    // ignored but an updated value is needed from the service.
+    kResolve
+  };
+
+  InFlightWriteTrie() = default;
+
+  void Add() {
+    std::vector<std::string> v;
+    Add(v.begin(), v.end());
+  }
+
+  template <typename It, typename Jt>
+  void Add(It path_start, Jt path_end) {
+    if (path_start == path_end) {
+      ++writes_in_flight_;
+      return;
+    }
+    children_[*path_start].Add(path_start + 1, path_end);
+  }
+
+  bool Remove() {
+    std::vector<std::string> v;
+    return Remove(v.begin(), v.end());
+  }
+
+  template <typename It, typename Jt>
+  bool Remove(It path_start, Jt path_end) {
+    if (path_start == path_end) {
+      DCHECK_GT(writes_in_flight_, 0);
+      return --writes_in_flight_ == 0 && children_.empty();
+    }
+    auto it = children_.find(*path_start);
+    DCHECK(it != children_.end()) << *path_start;
+    auto removed = it->second.Remove(path_start + 1, path_end);
+    if (removed)
+      children_.erase(*path_start);
+
+    return children_.empty() && writes_in_flight_ == 0;
+  }
+
+  template <typename It, typename Jt>
+  Decision Lookup(It path_start, Jt path_end) {
+    if (path_start == path_end) {
+      if (children_.empty()) {
+        DCHECK_GT(writes_in_flight_, 0);
+        return Decision::kIgnore;
+      }
+      return Decision::kResolve;
+    }
+
+    if (writes_in_flight_ != 0) {
+      return Decision::kIgnore;
+    }
+    auto it = children_.find(*path_start);
+    if (it == children_.end()) {
+      return Decision::kAllow;
+    }
+
+    return it->second.Lookup(path_start + 1, path_end);
+  }
+
+ private:
+  std::map<std::string, InFlightWriteTrie> children_;
+  int writes_in_flight_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(InFlightWriteTrie);
+};
+
+struct PersistentPrefStoreClient::InFlightWrite {
+  std::string key;
+  std::vector<std::vector<std::string>> sub_pref_paths;
+};
+
 PersistentPrefStoreClient::PersistentPrefStoreClient(
     mojom::PrefStoreConnectorPtr connector,
     scoped_refptr<PrefRegistry> pref_registry,
@@ -123,9 +209,6 @@
     uint32_t flags) {
   DCHECK(pref_store_);
   GetMutableValues().Set(key, std::move(value));
-  QueueWrite(key,
-             std::set<std::vector<std::string>>{std::vector<std::string>{}},
-             flags);
 }
 
 bool PersistentPrefStoreClient::ReadOnly() const {
@@ -232,16 +315,24 @@
 }
 
 void PersistentPrefStoreClient::FlushPendingWrites() {
+  weak_factory_.InvalidateWeakPtrs();
+  if (pending_writes_.empty())
+    return;
+
   std::vector<mojom::PrefUpdatePtr> updates;
+  std::vector<InFlightWrite> writes;
+
   for (auto& pref : pending_writes_) {
     auto update_value = mojom::PrefUpdateValue::New();
     const base::Value* value = nullptr;
     if (GetValue(pref.first, &value)) {
       std::vector<mojom::SubPrefUpdatePtr> pref_updates;
+      std::vector<std::vector<std::string>> sub_pref_writes;
       RemoveRedundantPaths(&pref.second.first);
       for (const auto& path : pref.second.first) {
         if (path.empty()) {
           pref_updates.clear();
+          sub_pref_writes.clear();
           break;
         }
         const base::Value* nested_value = LookupPath(value, path);
@@ -251,20 +342,78 @@
         } else {
           pref_updates.emplace_back(base::in_place, path, nullptr);
         }
+        sub_pref_writes.push_back(path);
       }
       if (pref_updates.empty()) {
         update_value->set_atomic_update(value->CreateDeepCopy());
+        writes.push_back({pref.first});
       } else {
         update_value->set_split_updates(std::move(pref_updates));
+        writes.push_back({pref.first, std::move(sub_pref_writes)});
       }
     } else {
       update_value->set_atomic_update(nullptr);
+      writes.push_back({pref.first});
     }
     updates.emplace_back(base::in_place, pref.first, std::move(update_value),
                          pref.second.second);
   }
   pref_store_->SetValues(std::move(updates));
   pending_writes_.clear();
+
+  for (const auto& write : writes) {
+    auto& trie = in_flight_writes_tries_[write.key];
+    if (write.sub_pref_paths.empty()) {
+      trie.Add();
+    } else {
+      for (const auto& subpref_update : write.sub_pref_paths) {
+        trie.Add(subpref_update.begin(), subpref_update.end());
+      }
+    }
+  }
+  in_flight_writes_queue_.push(std::move(writes));
+}
+
+void PersistentPrefStoreClient::OnPrefChangeAck() {
+  const auto& writes = in_flight_writes_queue_.front();
+  for (const auto& write : writes) {
+    auto it = in_flight_writes_tries_.find(write.key);
+    DCHECK(it != in_flight_writes_tries_.end()) << write.key;
+    bool remove = false;
+    if (write.sub_pref_paths.empty()) {
+      remove = it->second.Remove();
+    } else {
+      for (const auto& subpref_update : write.sub_pref_paths) {
+        remove =
+            it->second.Remove(subpref_update.begin(), subpref_update.end());
+      }
+    }
+    if (remove) {
+      in_flight_writes_tries_.erase(it);
+    }
+  }
+  in_flight_writes_queue_.pop();
+}
+
+bool PersistentPrefStoreClient::ShouldSkipWrite(
+    const std::string& key,
+    const std::vector<std::string>& path,
+    const base::Value* new_value) {
+  if (!pending_writes_.empty()) {
+    FlushPendingWrites();
+  }
+  auto it = in_flight_writes_tries_.find(key);
+  if (it == in_flight_writes_tries_.end()) {
+    return false;
+  }
+
+  auto decision = it->second.Lookup(path.begin(), path.end());
+  if (decision == InFlightWriteTrie::Decision::kAllow) {
+    return false;
+  }
+  if (decision == InFlightWriteTrie::Decision::kResolve)
+    pref_store_->RequestValue(key, path);
+  return true;
 }
 
 }  // namespace prefs
diff --git a/services/preferences/public/cpp/persistent_pref_store_client.h b/services/preferences/public/cpp/persistent_pref_store_client.h
index c6f9de86..6183a8d 100644
--- a/services/preferences/public/cpp/persistent_pref_store_client.h
+++ b/services/preferences/public/cpp/persistent_pref_store_client.h
@@ -69,6 +69,9 @@
   ~PersistentPrefStoreClient() override;
 
  private:
+  class InFlightWriteTrie;
+  struct InFlightWrite;
+
   void OnConnect(mojom::PersistentPrefStoreConnectionPtr connection,
                  mojom::PersistentPrefStoreConnectionPtr incognito_connection,
                  std::unordered_map<PrefValueStore::PrefStoreType,
@@ -80,6 +83,13 @@
                   uint32_t flags);
   void FlushPendingWrites();
 
+  // prefs::mojom::PreferenceObserver:
+  void OnPrefChangeAck() override;
+
+  bool ShouldSkipWrite(const std::string& key,
+                       const std::vector<std::string>& path,
+                       const base::Value* new_value) override;
+
   mojom::PrefStoreConnectorPtr connector_;
   scoped_refptr<PrefRegistry> pref_registry_;
   bool read_only_ = false;
@@ -91,6 +101,9 @@
   std::unique_ptr<ReadErrorDelegate> error_delegate_;
   std::vector<PrefValueStore::PrefStoreType> already_connected_types_;
 
+  std::queue<std::vector<InFlightWrite>> in_flight_writes_queue_;
+  std::map<std::string, InFlightWriteTrie> in_flight_writes_tries_;
+
   base::WeakPtrFactory<PersistentPrefStoreClient> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PersistentPrefStoreClient);
diff --git a/services/preferences/public/cpp/pref_store_client_mixin.cc b/services/preferences/public/cpp/pref_store_client_mixin.cc
index f78cf76..842e533 100644
--- a/services/preferences/public/cpp/pref_store_client_mixin.cc
+++ b/services/preferences/public/cpp/pref_store_client_mixin.cc
@@ -101,12 +101,19 @@
 }
 
 template <typename BasePrefStore>
+void PrefStoreClientMixin<BasePrefStore>::OnPrefChangeAck() {}
+
+template <typename BasePrefStore>
 void PrefStoreClientMixin<BasePrefStore>::OnPrefChanged(
     const std::string& key,
     mojom::PrefUpdateValuePtr update_value) {
   DCHECK(cached_prefs_);
   bool changed = false;
   if (update_value->is_atomic_update()) {
+    if (ShouldSkipWrite(key, std::vector<std::string>(),
+                        update_value->get_atomic_update().get())) {
+      return;
+    }
     auto& value = update_value->get_atomic_update();
     if (!value) {  // Delete
       if (cached_prefs_->RemovePath(key, nullptr))
@@ -129,9 +136,10 @@
       changed = true;
     for (auto& update : updates) {
       // Clients shouldn't send empty paths.
-      if (update->path.empty())
+      if (update->path.empty() ||
+          ShouldSkipWrite(key, update->path, update->value.get())) {
         continue;
-
+      }
       std::vector<base::StringPiece> full_path = base::SplitStringPiece(
           key, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
       full_path.insert(full_path.end(), update->path.begin(),
@@ -143,6 +151,14 @@
     ReportPrefValueChanged(key);
 }
 
+template <typename BasePrefStore>
+bool PrefStoreClientMixin<BasePrefStore>::ShouldSkipWrite(
+    const std::string& key,
+    const std::vector<std::string>& path,
+    const base::Value* new_value) {
+  return false;
+}
+
 template class PrefStoreClientMixin<::PrefStore>;
 template class PrefStoreClientMixin<::PersistentPrefStore>;
 
diff --git a/services/preferences/public/cpp/pref_store_client_mixin.h b/services/preferences/public/cpp/pref_store_client_mixin.h
index 68286ec4..29cb6041 100644
--- a/services/preferences/public/cpp/pref_store_client_mixin.h
+++ b/services/preferences/public/cpp/pref_store_client_mixin.h
@@ -57,10 +57,17 @@
   // prefs::mojom::PreferenceObserver:
   void OnPrefsChanged(std::vector<mojom::PrefUpdatePtr> updates) override;
   void OnInitializationCompleted(bool succeeded) override;
+  void OnPrefChangeAck() override;
 
   void OnPrefChanged(const std::string& key,
                      mojom::PrefUpdateValuePtr update_value);
 
+  // Should this client ignore a write received from the service? The default
+  // implementation never skips writes.
+  virtual bool ShouldSkipWrite(const std::string& key,
+                               const std::vector<std::string>& path,
+                               const base::Value* new_value);
+
   // Cached preferences.
   // If null, indicates that initialization failed.
   std::unique_ptr<base::DictionaryValue> cached_prefs_;
diff --git a/services/preferences/public/cpp/tests/persistent_pref_store_client_unittest.cc b/services/preferences/public/cpp/tests/persistent_pref_store_client_unittest.cc
index ac59083..0ff2499b 100644
--- a/services/preferences/public/cpp/tests/persistent_pref_store_client_unittest.cc
+++ b/services/preferences/public/cpp/tests/persistent_pref_store_client_unittest.cc
@@ -54,11 +54,6 @@
                            nullptr, pref_notifier),
         persistent_pref_store_client.get(), pref_registry.get(),
         base::Bind(&DoNothingWithReadError), false);
-    // The first update to a pref will write the entire dictionary as it would
-    // previously be missing. Do this here to avoid individual tests needing to
-    // deal with those updates.
-    ScopedDictionaryPrefUpdate(pref_service(), kDictionaryKey).Get();
-    auto update = WaitForUpdate();
   }
 
   void TearDown() override {
@@ -81,6 +76,7 @@
   }
 
   void ExpectNoUpdate() {
+    pref_service()->CommitPendingWrite();
     binding_.FlushForTesting();
     EXPECT_TRUE(last_updates_.empty());
   }
@@ -92,6 +88,8 @@
       std::move(on_update_).Run();
   }
 
+  void RequestValue(const std::string& key,
+                    const std::vector<std::string>& path) override {}
   void CommitPendingWrite() override {}
   void SchedulePendingLossyWrites() override {}
   void ClearMutableValues() override {}
@@ -487,18 +485,5 @@
   EXPECT_EQ((std::vector<std::string>{"path"}), split_updates[0]->path);
 }
 
-TEST_F(PersistentPrefStoreClientTest, SubPrefUpdates_Uninitialized) {
-  {
-    ScopedDictionaryPrefUpdate update(pref_service(),
-                                      kUninitializedDictionaryKey);
-    update->SetInteger("path.to.integer", 1);
-  }
-  auto update = WaitForUpdate();
-  ASSERT_TRUE(update->is_atomic_update());
-  base::DictionaryValue expected_value;
-  expected_value.SetInteger("path.to.integer", 1);
-  EXPECT_EQ(expected_value, *update->get_atomic_update());
-}
-
 }  // namespace
 }  // namespace prefs
diff --git a/services/preferences/public/interfaces/preferences.mojom b/services/preferences/public/interfaces/preferences.mojom
index d195e240..790b56c1 100644
--- a/services/preferences/public/interfaces/preferences.mojom
+++ b/services/preferences/public/interfaces/preferences.mojom
@@ -32,6 +32,13 @@
 
   // The PrefStore has been initialized (asynchronously).
   OnInitializationCompleted(bool succeeded);
+
+  // A preference write by this client has been applied. If this
+  // PrefStoreObserver is associated with a PersistentPrefStore, one
+  // OnPrefChangeAck() message is sent in response to each SetValues() message.
+  // This exists to ensure acks are ordered with respect to OnPrefsChanged
+  // messages.
+  OnPrefChangeAck();
 };
 
 // Captures the connections to a PrefStore by supplying the initial state of the
@@ -145,6 +152,12 @@
   // Sets the values for prefs.
   SetValues(array<PrefUpdate> updates);
 
+  // Requests that the pref service transmits its value for a pref (or sub-pref
+  // if |sub_pref_path| is non-empty). The value will be transmitted over the
+  // corresponding PrefStoreObserver interface previous returned by
+  // PrefStoreConnector.Connect().
+  RequestValue(string key, array<string> sub_pref_path);
+
   // These mirror the C++ PersistentPrefStore methods.
   CommitPendingWrite();
   SchedulePendingLossyWrites();
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index ab79ff6..09e4a84 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -225,6 +225,11 @@
       }
     ]
   },
+  "Android Builder Goma Canary (dbg)": {
+    "additional_compile_targets": [
+      "all"
+    ]
+  },
   "Android VR Tests": {
     "gtest_tests": [
       {
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index 50dfb029..e1edb76 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -2057,23 +2057,20 @@
 crbug.com/702805 compositing/images/direct-image-dynamic-border-radius.html [ Crash ]
 crbug.com/702805 compositing/visibility/overlays-persist-on-navigation.html [ Crash ]
 
-# Check failed: DisplayItem::IsDrawingType(display_item.GetType())
-crbug.com/726041 images/color-profile-border-radius.html [ Failure Crash ]
-crbug.com/726041 images/color-profile-image-shape.html [ Failure Crash ]
-crbug.com/726041 images/color-profile-border-fade.html [ Failure Crash ]
-crbug.com/726041 fast/borders/border-radius-mask-canvas-all.html [ Failure Crash ]
-crbug.com/726041 fast/borders/border-radius-mask-canvas-border.html [ Failure Crash ]
-crbug.com/726041 fast/borders/border-radius-mask-canvas-padding.html [ Failure Crash ]
-crbug.com/726041 fast/borders/border-radius-mask-canvas-with-mask.html [ Failure Crash ]
-crbug.com/726041 fast/borders/border-radius-mask-canvas-with-shadow.html [ Failure Crash ]
-crbug.com/726041 fast/borders/border-radius-mask-canvas.html [ Failure Crash ]
-crbug.com/726041 fast/borders/border-radius-mask-video-ratio.html [ Failure Crash ]
-crbug.com/726041 fast/borders/border-radius-mask-video-shadow.html [ Failure Crash ]
-crbug.com/726041 fast/borders/border-radius-mask-video.html [ Failure Crash ]
-crbug.com/726041 fast/borders/border-radius-mask-video-crash.html [ Crash ]
-crbug.com/726041 fast/replaced/border-radius-clip-content-edge.html [ Failure Crash ]
-crbug.com/726041 fast/replaced/border-radius-clip.html [ Failure Crash ]
-crbug.com/726041 svg/custom/visibility-enable-on-svg-element-contained-border-radius.html [ Crash ]
+crbug.com/730284 images/color-profile-border-radius.html [ Failure ]
+crbug.com/730284 images/color-profile-image-shape.html [ Failure ]
+crbug.com/730284 images/color-profile-border-fade.html [ Failure ]
+crbug.com/730284 fast/borders/border-radius-mask-canvas-all.html [ Failure ]
+crbug.com/730284 fast/borders/border-radius-mask-canvas-border.html [ Failure ]
+crbug.com/730284 fast/borders/border-radius-mask-canvas-padding.html [ Failure ]
+crbug.com/730284 fast/borders/border-radius-mask-canvas-with-mask.html [ Failure ]
+crbug.com/730284 fast/borders/border-radius-mask-canvas-with-shadow.html [ Failure ]
+crbug.com/730284 fast/borders/border-radius-mask-canvas.html [ Failure ]
+crbug.com/730284 fast/borders/border-radius-mask-video-ratio.html [ Failure ]
+crbug.com/730284 fast/borders/border-radius-mask-video-shadow.html [ Failure ]
+crbug.com/730284 fast/borders/border-radius-mask-video.html [ Failure ]
+crbug.com/730284 fast/replaced/border-radius-clip-content-edge.html [ Failure ]
+crbug.com/730284 fast/replaced/border-radius-clip.html [ Failure ]
 
 # Paint property under-invalidation
 crbug.com/728913 paint/invalidation/svg/mask-clip-target-transform.svg [ Crash ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 850d5c4..b1ff717 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -970,22 +970,14 @@
 crbug.com/659456 http/tests/inspector/persistence/automapping-sourcemap.html [ Pass Failure ]
 crbug.com/659456 virtual/mojo-loading/http/tests/inspector/persistence/automapping-sourcemap.html [ Pass Failure ]
 
-crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphen-min-preferred-width-mock.html [ Skip ]
-crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphens-align.html [ Skip ]
-crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphens-auto.html [ Skip ]
-crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphens-auto-mock.html [ Skip ]
-crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphens-auto-nowrap.html [ Skip ]
-crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphens-locale.html [ Skip ]
-crbug.com/605840 [ Linux Win ] fast/text/hyphens/hyphens-orphaned-word.html [ Skip ]
-crbug.com/605840 [ Linux Win ] fast/text/hyphens/midword-break-priority.html [ Skip ]
-crbug.com/605840 [ Android ] fast/text/hyphens/hyphen-min-preferred-width-mock.html [ Pass ]
-crbug.com/605840 [ Android ] fast/text/hyphens/hyphens-align.html [ Pass ]
-crbug.com/605840 [ Android ] fast/text/hyphens/hyphens-auto.html [ Pass ]
-crbug.com/605840 [ Android ] fast/text/hyphens/hyphens-auto-mock.html [ Pass ]
-crbug.com/605840 [ Android ] fast/text/hyphens/hyphens-auto-nowrap.html [ Pass ]
-crbug.com/605840 [ Android ] fast/text/hyphens/hyphens-locale.html [ Pass ]
-crbug.com/605840 [ Android ] fast/text/hyphens/hyphens-orphaned-word.html [ Pass ]
-crbug.com/605840 [ Android ] fast/text/hyphens/midword-break-priority.html [ Pass ]
+crbug.com/652964 [ Linux Win ] fast/text/hyphens/hyphen-min-preferred-width-mock.html [ Skip ]
+crbug.com/652964 [ Linux Win ] fast/text/hyphens/hyphens-align.html [ Skip ]
+crbug.com/652964 [ Linux Win ] fast/text/hyphens/hyphens-auto.html [ Skip ]
+crbug.com/652964 [ Linux Win ] fast/text/hyphens/hyphens-auto-mock.html [ Skip ]
+crbug.com/652964 [ Linux Win ] fast/text/hyphens/hyphens-auto-nowrap.html [ Skip ]
+crbug.com/652964 [ Linux Win ] fast/text/hyphens/hyphens-locale.html [ Skip ]
+crbug.com/652964 [ Linux Win ] fast/text/hyphens/hyphens-orphaned-word.html [ Skip ]
+crbug.com/652964 [ Linux Win ] fast/text/hyphens/midword-break-priority.html [ Skip ]
 
 crbug.com/405389 external/wpt/css/css-shapes-1/shape-outside/supported-shapes/polygon/shape-outside-polygon-017.html [ Failure ]
 crbug.com/424365 external/wpt/css/css-shapes-1/shape-outside/shape-image/shape-image-010.html [ Failure ]
@@ -2512,11 +2504,11 @@
 crbug.com/576815 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/selectors4/dir-style-03b.html [ Failure ]
 crbug.com/576815 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/selectors4/dir-style-04.html [ Failure ]
 
-# Flakily timing out.
+# Flakily timing out or failing.
 # TODO(ccameron): Investigate this.
-crbug.com/730267 virtual/gpu-rasterization/images/color-profile-group.html [ Pass Timeout ]
-crbug.com/730267 virtual/gpu-rasterization/images/color-profile-layer.html [ Pass Timeout  ]
-crbug.com/730267 virtual/gpu-rasterization/images/color-profile-layer-filter.html [ Pass Timeout ]
+crbug.com/730267 virtual/gpu-rasterization/images/color-profile-group.html [ Pass Timeout Failure ]
+crbug.com/730267 virtual/gpu-rasterization/images/color-profile-layer.html [ Pass Timeout Failure ]
+crbug.com/730267 virtual/gpu-rasterization/images/color-profile-layer-filter.html [ Pass Timeout Failure ]
 
 # TODO(chrishall): this is a temporary mediation step as part of the P0 issue crbug.com/657646
 # this is not meant to be here for more than a few days (from 2016-11-03 SYD)
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/sources-test.js b/third_party/WebKit/LayoutTests/http/tests/inspector/sources-test.js
index 880db90..eded0cb0 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/sources-test.js
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/sources-test.js
@@ -6,9 +6,9 @@
 {
     var originalPosition = text1.indexOf(testToken);
     InspectorTest.assertTrue(originalPosition !== -1);
-    var originalLocation = Sources.Formatter.positionToLocation(text1.computeLineEndings(), originalPosition);
+    var originalLocation = Formatter.Formatter.positionToLocation(text1.computeLineEndings(), originalPosition);
     var formattedLocation = mapping.originalToFormatted(originalLocation[0], originalLocation[1]);
-    var formattedPosition = Sources.Formatter.locationToPosition(text2.computeLineEndings(), formattedLocation[0], formattedLocation[1]);
+    var formattedPosition = Formatter.Formatter.locationToPosition(text2.computeLineEndings(), formattedLocation[0], formattedLocation[1]);
     var expectedFormattedPosition = text2.indexOf(testToken);
     if (expectedFormattedPosition === formattedPosition)
         InspectorTest.addResult(String.sprintf("Correct mapping for <%s>", testToken));
@@ -18,7 +18,7 @@
 
 InspectorTest.testPrettyPrint = function(mimeType, text, mappingQueries, next)
 {
-    new Sources.ScriptFormatter(mimeType, text, didFormatContent);
+    new Formatter.ScriptFormatter(mimeType, text, didFormatContent);
 
     function didFormatContent(formattedSource, mapping)
     {
@@ -35,7 +35,7 @@
 InspectorTest.testJavascriptOutline = function(text) {
     var fulfill;
     var promise = new Promise(x => fulfill = x);
-    Common.formatterWorkerPool.javaScriptOutline(text, onChunk);
+    Formatter.formatterWorkerPool().javaScriptOutline(text, onChunk);
     var items = [];
     return promise;
 
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/hide-shortcut.html b/third_party/WebKit/LayoutTests/inspector/elements/hide-shortcut.html
index cabdc0a..1b124c6c5c6 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/hide-shortcut.html
+++ b/third_party/WebKit/LayoutTests/inspector/elements/hide-shortcut.html
@@ -51,7 +51,7 @@
 
         function testToggleHideShortcutOn(next)
         {
-            treeOutline.toggleHideElement(parentNode, callback);
+            treeOutline.toggleHideElement(parentNode).then(callback);
 
             function callback()
             {
@@ -76,7 +76,7 @@
 
         function testToggleHideShortcutOff(next)
         {
-            treeOutline.toggleHideElement(parentNode, callback);
+            treeOutline.toggleHideElement(parentNode).then(callback);
 
             function callback()
             {
@@ -121,7 +121,7 @@
 
         function testToggleHideShortcutOnInFrame(next)
         {
-            treeOutline.toggleHideElement(frameNode, callback);
+            treeOutline.toggleHideElement(frameNode).then(callback);
 
             function callback()
             {
@@ -142,7 +142,7 @@
 
     function testPseudoToggle(pseudoNode, next)
     {
-        treeOutline.toggleHideElement(pseudoNode, callback);
+        treeOutline.toggleHideElement(pseudoNode).then(callback);
         function callback()
         {
             var pseudoNodeTypeArg = pseudoNode.pseudoType() ? ("\"" + pseudoNode.pseudoType() + "\"") : "undefined";
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/resolve-alien-node.html b/third_party/WebKit/LayoutTests/inspector/elements/resolve-alien-node.html
index bd4d27bd..16d1366f 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/resolve-alien-node.html
+++ b/third_party/WebKit/LayoutTests/inspector/elements/resolve-alien-node.html
@@ -9,10 +9,8 @@
 
     var spanWrapper = InspectorTest.runtimeModel.createRemoteObject(result);
     var node = await InspectorTest.domModel.pushObjectAsNodeToFrontend(spanWrapper);
-
     InspectorTest.assertTrue(node, "Node object should be resovled");
-    var remoteObject = await node.resolveToObjectPromise();
-
+    var remoteObject = await node.resolveToObject();
     InspectorTest.addResult("Alien node should resolve to null: " + remoteObject);
     InspectorTest.completeTest();
 }
diff --git a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-outline.html b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-outline.html
index 08048cf3..8332620 100644
--- a/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-outline.html
+++ b/third_party/WebKit/LayoutTests/inspector/elements/styles-1/css-outline.html
@@ -53,6 +53,10 @@
 </style>
 <script>
 
+function initialize_Formatter() {
+    InspectorTest.preloadModule('formatter');
+}
+
 function getCSS()
 {
     return document.querySelector("#styler").textContent;
@@ -70,7 +74,7 @@
 
     function onStyleFetched(result)
     {
-        Common.formatterWorkerPool.parseCSS(result.value, onRulesParsed);
+        Formatter.formatterWorkerPool().parseCSS(result.value, onRulesParsed);
     }
 
     InspectorTest.evaluateInPage("getCSS()", onStyleFetched);
diff --git a/third_party/WebKit/LayoutTests/inspector/initial-modules-load-expected.txt b/third_party/WebKit/LayoutTests/inspector/initial-modules-load-expected.txt
index 944214e..6fc93be9 100644
--- a/third_party/WebKit/LayoutTests/inspector/initial-modules-load-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/initial-modules-load-expected.txt
@@ -71,6 +71,7 @@
     emulation
     event_listeners
     extensions
+    formatter
     help
     host
     inline_editor
@@ -111,6 +112,7 @@
     emulation
     event_listeners
     extensions
+    formatter
     help
     host
     inline_editor
@@ -154,6 +156,7 @@
     emulation
     event_listeners
     extensions
+    formatter
     help
     host
     inline_editor
diff --git a/third_party/WebKit/LayoutTests/inspector/network/network-json-parser.html b/third_party/WebKit/LayoutTests/inspector/network/network-json-parser.html
index 06cdf38..ad5ef82 100644
--- a/third_party/WebKit/LayoutTests/inspector/network/network-json-parser.html
+++ b/third_party/WebKit/LayoutTests/inspector/network/network-json-parser.html
@@ -12,7 +12,7 @@
             return;
         }
 
-        Common.formatterWorkerPool.parseJSONRelaxed(resultData.data).then(success);
+        Formatter.formatterWorkerPool().parseJSONRelaxed(resultData.data).then(success);
 
         function success(data)
         {
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger/extract-javascript-identifiers.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger/extract-javascript-identifiers.html
index e110c67f..e4b77ef 100644
--- a/third_party/WebKit/LayoutTests/inspector/sources/debugger/extract-javascript-identifiers.html
+++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger/extract-javascript-identifiers.html
@@ -56,7 +56,7 @@
     {
         InspectorTest.addResult("Text:");
         InspectorTest.addResult("    " + text + "\n");
-        Common.formatterWorkerPool.javaScriptIdentifiers(text)
+        Formatter.formatterWorkerPool().javaScriptIdentifiers(text)
             .then(onIdentifiers)
             .then(next);
     }
diff --git a/third_party/WebKit/LayoutTests/platform/android/tables/mozilla/bugs/bug2479-4-expected.png b/third_party/WebKit/LayoutTests/platform/android/tables/mozilla/bugs/bug2479-4-expected.png
index 7f538da..e00f263 100644
--- a/third_party/WebKit/LayoutTests/platform/android/tables/mozilla/bugs/bug2479-4-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/android/tables/mozilla/bugs/bug2479-4-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug109043-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug109043-expected.png
index 7e6cb5a2e..046bbd2 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug109043-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug109043-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug2479-4-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug2479-4-expected.png
index 5eef4c5..cdc9b7b1 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug2479-4-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug2479-4-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug7121-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug7121-1-expected.png
index 945cb5c..fe3e3d1 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug7121-1-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug7121-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug80762-1-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug80762-1-expected.png
index e98fd53..2f27d8d 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug80762-1-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla/bugs/bug80762-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png
index ac16988..752a287 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/tables/mozilla/bugs/bug7121-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/tables/mozilla/bugs/bug7121-1-expected.png
index 1e43a275..5169a337 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/tables/mozilla/bugs/bug7121-1-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/tables/mozilla/bugs/bug7121-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/tables/mozilla/bugs/bug2479-4-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/tables/mozilla/bugs/bug2479-4-expected.png
index b2598fd2..6545916 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/tables/mozilla/bugs/bug2479-4-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/tables/mozilla/bugs/bug2479-4-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/tables/mozilla/bugs/bug2479-4-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/tables/mozilla/bugs/bug2479-4-expected.png
index 55dd20d7..44f8599 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/tables/mozilla/bugs/bug2479-4-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/tables/mozilla/bugs/bug2479-4-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png
index e39c381..c410e34a 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/tables/mozilla/bugs/bug2479-4-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/tables/mozilla/bugs/bug2479-4-expected.png
index b2598fd2..6545916 100644
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/tables/mozilla/bugs/bug2479-4-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/tables/mozilla/bugs/bug2479-4-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug109043-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug109043-expected.png
index 903de45..2f2340f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug109043-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug109043-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug2479-4-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug2479-4-expected.png
index f785dcd8..d864ef3 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug2479-4-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug2479-4-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug7121-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug7121-1-expected.png
index c764389..34f26089 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug7121-1-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug7121-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug80762-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug80762-1-expected.png
index d7215df..795245c 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug80762-1-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla/bugs/bug80762-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png
index 3475a42c..a4d88b4 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug109043-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug109043-expected.png
index 54ebf959..e07c7637 100644
--- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug109043-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug109043-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug2479-4-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug2479-4-expected.png
index 6a0ba2b..197d76a1 100644
--- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug2479-4-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug2479-4-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug7121-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug7121-1-expected.png
index 69a6a6c6..8e270573 100644
--- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug7121-1-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug7121-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug80762-1-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug80762-1-expected.png
index 1c0f1222..9ddb999 100644
--- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug80762-1-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla/bugs/bug80762-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png
index c8f4d97..1a173ac9 100644
--- a/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/tables/mozilla_expected_failures/bugs/bug14007-2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug109043.html b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug109043.html
index 5690593..d2e1f36 100644
--- a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug109043.html
+++ b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug109043.html
@@ -4,11 +4,11 @@
 </head>
 
 <body>
-<table cellspacing=0 cellpading=10 border=1 width=100% bgcolor=yellow>
+<table cellspacing=0 cellpadding=10 border=1 width=100% bgcolor=yellow>
  <tr>
   <td width=70 bgcolor=red align=center>xxx</td>
   <td>
-      <table cellspacing=0 cellpading=0 border=1 width=100% height=100% bgcolor=lime>
+      <table cellspacing=0 cellpadding=0 border=1 width=100% height=100% bgcolor=lime>
          <tr>
            <th>IceWM, The Window Manager Cool As Ice</th>
          </tr>
diff --git a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug2479-4.html b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug2479-4.html
index 532c3c9..b61cc50 100644
--- a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug2479-4.html
+++ b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug2479-4.html
@@ -43,7 +43,7 @@
 
 <P>There are some minor exceptions, however. The x.1 tests should fill
 the viewport width, the other tests need not. Also, browsers
-attempting to have backwards compatability with the buggy behaviour of
+attempting to have backwards compatibility with the buggy behaviour of
 legacy browsers may omit to inherit the color and font-size in 1.1,
 which will make 1.1 (but only 1.1) unreadable. That is the problem
 with not following the specs.</P>
diff --git a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug7121-1.html b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug7121-1.html
index e262b56a..8f5e4364 100644
--- a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug7121-1.html
+++ b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug7121-1.html
@@ -1,7 +1,7 @@
 <HTML>
 <BODY>
 
-<p>In this table (modeled after mozilla.org template) the cells in
+<p>In this table (modelled after mozilla.org template) the cells in
 column two are requesting colspans of (2,3,2,6) respectively. Notice
 that they all want to span more than one column (which means, in the case of
 this table, all rows are requesting to span more columns than the
diff --git a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug80762-1.html b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug80762-1.html
index 3eb51c857..5d4921b 100644
--- a/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug80762-1.html
+++ b/third_party/WebKit/LayoutTests/tables/mozilla/bugs/bug80762-1.html
@@ -9,7 +9,7 @@
 
       <table width="100%">
         <tr>
-          <td>text below cant break out of this table</td>
+          <td>text below can't break out of this table</td>
         </tr>
       </table>
 
diff --git a/third_party/WebKit/LayoutTests/tables/mozilla_expected_failures/bugs/bug14007-2.html b/third_party/WebKit/LayoutTests/tables/mozilla_expected_failures/bugs/bug14007-2.html
index 6f1eddb..984f1f06 100644
--- a/third_party/WebKit/LayoutTests/tables/mozilla_expected_failures/bugs/bug14007-2.html
+++ b/third_party/WebKit/LayoutTests/tables/mozilla_expected_failures/bugs/bug14007-2.html
@@ -27,7 +27,7 @@
         </td></tr></tbody></table></td></tr></table><br></td>
 </tr><tr>
         <td><table><tr><td class=d>2.</td><td><table><tbody class=a><tr><td>
-                <p>Look for Y2K survivers.
+                <p>Look for Y2K survivors.
                 <p>Build outhouse
                 <p>Eat reserve food
         </td></tr></tbody></table></td></tr></table><br></td>
diff --git a/third_party/WebKit/Source/core/dom/ContainerNode.cpp b/third_party/WebKit/Source/core/dom/ContainerNode.cpp
index ac671097..fc395f19 100644
--- a/third_party/WebKit/Source/core/dom/ContainerNode.cpp
+++ b/third_party/WebKit/Source/core/dom/ContainerNode.cpp
@@ -63,9 +63,49 @@
 static void DispatchChildInsertionEvents(Node&);
 static void DispatchChildRemovalEvents(Node&);
 
+namespace {
+
+// This class is helpful to detect necessity of
+// RecheckNodeInsertionStructuralPrereq() after removeChild*() inside
+// InsertBefore(), AppendChild(), and ReplaceChild().
+//
+// After removeChild*(), we can detect necessity of
+// RecheckNodeInsertionStructuralPrereq() by
+//  - DOM tree version of |node_document_| was increased by at most one.
+//  - If |node| and |parent| are in different documents, Document for
+//    |parent| must not be changed.
+class DOMTreeMutationDetector {
+  STACK_ALLOCATED();
+
+ public:
+  DOMTreeMutationDetector(const Node& node, const Node& parent)
+      : node_document_(node.GetDocument()),
+        parent_document_(parent.GetDocument()),
+        original_node_document_version_(node_document_->DomTreeVersion()),
+        original_parent_document_version_(parent_document_->DomTreeVersion()) {}
+
+  bool HadAtMostOneDOMMutation() {
+    if (node_document_->DomTreeVersion() > original_node_document_version_ + 1)
+      return false;
+    if (node_document_ == parent_document_)
+      return true;
+    return parent_document_->DomTreeVersion() ==
+           original_parent_document_version_;
+  }
+
+ private:
+  const Member<Document> node_document_;
+  const Member<Document> parent_document_;
+  const uint64_t original_node_document_version_;
+  const uint64_t original_parent_document_version_;
+};
+
+}  // namespace
+
 // This dispatches various events; DOM mutation events, blur events, IFRAME
 // unload events, etc.
-static inline void CollectChildrenAndRemoveFromOldParent(
+// Returns true if DOM mutation should be proceeded.
+static inline bool CollectChildrenAndRemoveFromOldParent(
     Node& node,
     NodeVector& nodes,
     ExceptionState& exception_state) {
@@ -73,11 +113,12 @@
     DocumentFragment& fragment = ToDocumentFragment(node);
     GetChildNodes(fragment, nodes);
     fragment.RemoveChildren();
-    return;
+    return !nodes.IsEmpty();
   }
   nodes.push_back(&node);
   if (ContainerNode* old_parent = node.parentNode())
     old_parent->RemoveChild(&node, exception_state);
+  return !exception_state.HadException() && !nodes.IsEmpty();
 }
 
 void ContainerNode::ParserTakeAllChildrenFrom(ContainerNode& old_parent) {
@@ -171,6 +212,8 @@
         kHierarchyRequestError, "The new child element contains the parent.");
     return false;
   }
+  // TODO(tkent): IsChildTypeAllowed() is unnecessary for
+  // RecheckNodeInsertionStructuralPrereq().
   if (!IsChildTypeAllowed(new_child)) {
     exception_state.ThrowDOMException(
         kHierarchyRequestError,
@@ -181,41 +224,19 @@
   return true;
 }
 
-bool ContainerNode::CollectChildrenAndRemoveFromOldParentWithOptionalRecheck(
+// We need this extra structural check because prior DOM mutation operations
+// dispatched synchronous events, and their handlers might modified DOM trees.
+bool ContainerNode::RecheckNodeInsertionStructuralPrereq(
+    const NodeVector& new_children,
     const Node* next,
-    const Node* old_child,
-    Node& new_child,
-    NodeVector& new_children,
-    ExceptionState& exception_state) const {
-  Document& new_doc = new_child.GetDocument();
-  Document& this_doc = GetDocument();
-  uint64_t original_version_for_new_node = new_doc.DomTreeVersion();
-  uint64_t original_version_for_this = this_doc.DomTreeVersion();
-  CollectChildrenAndRemoveFromOldParent(new_child, new_children,
-                                        exception_state);
-  if (exception_state.HadException() || new_children.IsEmpty())
-    return false;
-  // We can skip the following hierarchy check if we have no DOM mutations other
-  // than one in CollectChildrenAndRemoveFromOldParent(). We can detect it by
-  //  - DOM tree version of |new_doc| was increased by at most one.
-  //  - If |this| and |new_child| are in different documents, Document for
-  //    |this| must not be changed.
-  if (new_doc.DomTreeVersion() <= original_version_for_new_node + 1) {
-    if (&new_doc == &this_doc)
-      return true;
-    if (this_doc.DomTreeVersion() == original_version_for_this)
-      return true;
-  }
-
-  // We need this extra check because synchronous event handlers triggered
-  // while CollectChildrenAndRemoveFromOldParent() modified DOM trees.
+    ExceptionState& exception_state) {
   for (const auto& child : new_children) {
     if (child->parentNode()) {
       // A new child was added to another parent before adding to this
       // node.  Firefox and Edge don't throw in this case.
       return false;
     }
-    if (!CheckAcceptChildGuaranteedNodeTypes(*child, next, old_child,
+    if (!CheckAcceptChildGuaranteedNodeTypes(*child, next, nullptr,
                                              exception_state))
       return false;
   }
@@ -328,9 +349,15 @@
   DCHECK(new_child);
 
   NodeVector targets;
-  if (!CollectChildrenAndRemoveFromOldParentWithOptionalRecheck(
-          ref_child, nullptr, *new_child, targets, exception_state))
+  DOMTreeMutationDetector detector(*new_child, *this);
+  if (!CollectChildrenAndRemoveFromOldParent(*new_child, targets,
+                                             exception_state))
     return new_child;
+  if (!detector.HadAtMostOneDOMMutation()) {
+    if (!RecheckNodeInsertionStructuralPrereq(targets, ref_child,
+                                              exception_state))
+      return new_child;
+  }
 
   NodeVector post_insertion_notification_targets;
   {
@@ -469,18 +496,21 @@
   if (next == new_child)
     next = new_child->nextSibling();
 
+  bool needs_recheck = false;
   // 10. Adopt node into parent’s node document.
   // TODO(tkent): Actually we do only RemoveChild() as a part of 'adopt'
   // operation.
   //
-  // Though the following
-  // CollectChildrenAndRemoveFromOldParentWithOptionalRecheck() also calls
+  // Though the following CollectChildrenAndRemoveFromOldParent() also calls
   // RemoveChild(), we'd like to call RemoveChild() here to make a separated
   // MutationRecord.
   if (ContainerNode* new_child_parent = new_child->parentNode()) {
+    DOMTreeMutationDetector detector(*new_child, *this);
     new_child_parent->RemoveChild(new_child, exception_state);
     if (exception_state.HadException())
       return nullptr;
+    if (!detector.HadAtMostOneDOMMutation())
+      needs_recheck = true;
   }
 
   NodeVector targets;
@@ -497,29 +527,24 @@
     //    1. Set removedNodes to a list solely containing child.
     //    2. Remove child from its parent with the suppress observers flag set.
     if (ContainerNode* old_child_parent = old_child->parentNode()) {
+      DOMTreeMutationDetector detector(*old_child, *this);
       old_child_parent->RemoveChild(old_child, exception_state);
       if (exception_state.HadException())
         return nullptr;
-    }
-
-    // Check DOM structure one more time because removeChild() above fires
-    // synchronous events.
-    if (!CheckAcceptChild(new_child, nullptr, old_child, exception_state))
-      return old_child;
-    if (next && next->parentNode() != this) {
-      exception_state.ThrowDOMException(
-          kNotFoundError,
-          "The node before which the new node is to "
-          "be inserted is not a child of this "
-          "node.");
-      return old_child;
+      if (!detector.HadAtMostOneDOMMutation())
+        needs_recheck = true;
     }
 
     // 13. Let nodes be node’s children if node is a DocumentFragment node, and
     // a list containing solely node otherwise.
-    if (!CollectChildrenAndRemoveFromOldParentWithOptionalRecheck(
-            next, old_child, *new_child, targets, exception_state))
+    DOMTreeMutationDetector detector(*new_child, *this);
+    if (!CollectChildrenAndRemoveFromOldParent(*new_child, targets,
+                                               exception_state))
       return old_child;
+    if (!detector.HadAtMostOneDOMMutation() || needs_recheck) {
+      if (!RecheckNodeInsertionStructuralPrereq(targets, next, exception_state))
+        return old_child;
+    }
 
     // 10. Adopt node into parent’s node document.
     // 14. Insert node into parent before reference child with the suppress
@@ -751,9 +776,15 @@
   DCHECK(new_child);
 
   NodeVector targets;
-  if (!CollectChildrenAndRemoveFromOldParentWithOptionalRecheck(
-          nullptr, nullptr, *new_child, targets, exception_state))
+  DOMTreeMutationDetector detector(*new_child, *this);
+  if (!CollectChildrenAndRemoveFromOldParent(*new_child, targets,
+                                             exception_state))
     return new_child;
+  if (!detector.HadAtMostOneDOMMutation()) {
+    if (!RecheckNodeInsertionStructuralPrereq(targets, nullptr,
+                                              exception_state))
+      return new_child;
+  }
 
   NodeVector post_insertion_notification_targets;
   {
diff --git a/third_party/WebKit/Source/core/dom/ContainerNode.h b/third_party/WebKit/Source/core/dom/ContainerNode.h
index 72db2f3..dcf2c9b 100644
--- a/third_party/WebKit/Source/core/dom/ContainerNode.h
+++ b/third_party/WebKit/Source/core/dom/ContainerNode.h
@@ -412,12 +412,9 @@
   bool HasRestyleFlagInternal(DynamicRestyleFlags) const;
   bool HasRestyleFlagsInternal() const;
 
-  bool CollectChildrenAndRemoveFromOldParentWithOptionalRecheck(
-      const Node* next,
-      const Node* old_child,
-      Node& new_child,
-      NodeVector&,
-      ExceptionState&) const;
+  bool RecheckNodeInsertionStructuralPrereq(const NodeVector&,
+                                            const Node* next,
+                                            ExceptionState&);
   inline bool CheckAcceptChildGuaranteedNodeTypes(const Node& new_child,
                                                   const Node* next,
                                                   const Node* old_child,
diff --git a/third_party/WebKit/Source/core/editing/SelectionController.cpp b/third_party/WebKit/Source/core/editing/SelectionController.cpp
index 3a8f03e..64774b9a 100644
--- a/third_party/WebKit/Source/core/editing/SelectionController.cpp
+++ b/third_party/WebKit/Source/core/editing/SelectionController.cpp
@@ -619,56 +619,78 @@
       kWordGranularity, HandleVisibility::kNotVisible);
 }
 
-// TODO(xiaochengh): We should not use reference to return value.
-static void AdjustEndpointsAtBidiBoundary(
-    VisiblePositionInFlatTree& visible_base,
-    VisiblePositionInFlatTree& visible_extent) {
+static SelectionInFlatTree AdjustEndpointsAtBidiBoundary(
+    const VisiblePositionInFlatTree& visible_base,
+    const VisiblePositionInFlatTree& visible_extent) {
   DCHECK(visible_base.IsValid());
   DCHECK(visible_extent.IsValid());
 
   RenderedPosition base(visible_base);
   RenderedPosition extent(visible_extent);
 
+  const SelectionInFlatTree& unchanged_selection =
+      SelectionInFlatTree::Builder()
+          .SetBaseAndExtent(visible_base.DeepEquivalent(),
+                            visible_extent.DeepEquivalent())
+          .Build();
+
   if (base.IsNull() || extent.IsNull() || base.IsEquivalent(extent))
-    return;
+    return unchanged_selection;
 
   if (base.AtLeftBoundaryOfBidiRun()) {
     if (!extent.AtRightBoundaryOfBidiRun(base.BidiLevelOnRight()) &&
         base.IsEquivalent(
             extent.LeftBoundaryOfBidiRun(base.BidiLevelOnRight()))) {
-      visible_base = CreateVisiblePosition(
-          ToPositionInFlatTree(base.PositionAtLeftBoundaryOfBiDiRun()));
-      return;
+      return SelectionInFlatTree::Builder()
+          .SetBaseAndExtent(
+              CreateVisiblePosition(
+                  ToPositionInFlatTree(base.PositionAtLeftBoundaryOfBiDiRun()))
+                  .DeepEquivalent(),
+              visible_extent.DeepEquivalent())
+          .Build();
     }
-    return;
+    return unchanged_selection;
   }
 
   if (base.AtRightBoundaryOfBidiRun()) {
     if (!extent.AtLeftBoundaryOfBidiRun(base.BidiLevelOnLeft()) &&
         base.IsEquivalent(
             extent.RightBoundaryOfBidiRun(base.BidiLevelOnLeft()))) {
-      visible_base = CreateVisiblePosition(
-          ToPositionInFlatTree(base.PositionAtRightBoundaryOfBiDiRun()));
-      return;
+      return SelectionInFlatTree::Builder()
+          .SetBaseAndExtent(
+              CreateVisiblePosition(
+                  ToPositionInFlatTree(base.PositionAtRightBoundaryOfBiDiRun()))
+                  .DeepEquivalent(),
+              visible_extent.DeepEquivalent())
+          .Build();
     }
-    return;
+    return unchanged_selection;
   }
 
   if (extent.AtLeftBoundaryOfBidiRun() &&
       extent.IsEquivalent(
           base.LeftBoundaryOfBidiRun(extent.BidiLevelOnRight()))) {
-    visible_extent = CreateVisiblePosition(
-        ToPositionInFlatTree(extent.PositionAtLeftBoundaryOfBiDiRun()));
-    return;
+    return SelectionInFlatTree::Builder()
+        .SetBaseAndExtent(
+            visible_base.DeepEquivalent(),
+            CreateVisiblePosition(
+                ToPositionInFlatTree(extent.PositionAtLeftBoundaryOfBiDiRun()))
+                .DeepEquivalent())
+        .Build();
   }
 
   if (extent.AtRightBoundaryOfBidiRun() &&
       extent.IsEquivalent(
           base.RightBoundaryOfBidiRun(extent.BidiLevelOnLeft()))) {
-    visible_extent = CreateVisiblePosition(
-        ToPositionInFlatTree(extent.PositionAtRightBoundaryOfBiDiRun()));
-    return;
+    return SelectionInFlatTree::Builder()
+        .SetBaseAndExtent(
+            visible_base.DeepEquivalent(),
+            CreateVisiblePosition(
+                ToPositionInFlatTree(extent.PositionAtRightBoundaryOfBiDiRun()))
+                .DeepEquivalent())
+        .Build();
   }
+  return unchanged_selection;
 }
 
 // TODO(yosin): We should take |granularity| and |handleVisibility| from
@@ -692,20 +714,23 @@
   const VisiblePositionInFlatTree& base =
       original_base.IsNotNull() ? original_base
                                 : CreateVisiblePosition(new_selection.Base());
-  VisiblePositionInFlatTree new_base = base;
   const VisiblePositionInFlatTree& extent =
       CreateVisiblePosition(new_selection.Extent());
-  VisiblePositionInFlatTree new_extent = extent;
-  if (endpoints_adjustment_mode == kAdjustEndpointsAtBidiBoundary)
-    AdjustEndpointsAtBidiBoundary(new_base, new_extent);
+  const SelectionInFlatTree& adjusted_selection =
+      endpoints_adjustment_mode == kAdjustEndpointsAtBidiBoundary
+          ? AdjustEndpointsAtBidiBoundary(base, extent)
+          : SelectionInFlatTree::Builder()
+                .SetBaseAndExtent(base.DeepEquivalent(),
+                                  extent.DeepEquivalent())
+                .Build();
 
   SelectionInFlatTree::Builder builder(new_selection.AsSelection());
-  if (new_base.DeepEquivalent() != base.DeepEquivalent() ||
-      new_extent.DeepEquivalent() != extent.DeepEquivalent()) {
+  if (adjusted_selection.Base() != base.DeepEquivalent() ||
+      adjusted_selection.Extent() != extent.DeepEquivalent()) {
     original_base_in_flat_tree_ = base;
     SetContext(&GetDocument());
-    builder.SetBaseAndExtent(new_base.DeepEquivalent(),
-                             new_extent.DeepEquivalent());
+    builder.SetBaseAndExtent(adjusted_selection.Base(),
+                             adjusted_selection.Extent());
   } else if (original_base.IsNotNull()) {
     if (Selection().ComputeVisibleSelectionInFlatTree().Base() ==
         new_selection.Base()) {
diff --git a/third_party/WebKit/Source/core/editing/SelectionModifierWord.cpp b/third_party/WebKit/Source/core/editing/SelectionModifierWord.cpp
index 522a3ef5..9ed2c28 100644
--- a/third_party/WebKit/Source/core/editing/SelectionModifierWord.cpp
+++ b/third_party/WebKit/Source/core/editing/SelectionModifierWord.cpp
@@ -378,8 +378,10 @@
                                          logical_end_in_layout_object);
     }
 
-    if (is_word_break)
-      return adjacent_character_position;
+    if (is_word_break) {
+      return HonorEditingBoundaryAtOrBefore(adjacent_character_position,
+                                            visible_position.DeepEquivalent());
+    }
 
     current = adjacent_character_position;
   }
@@ -392,42 +394,36 @@
     const VisiblePosition& visible_position,
     bool skips_space_when_moving_right) {
   DCHECK(visible_position.IsValid()) << visible_position;
-  VisiblePosition left_word_break = VisualWordPosition(
+  const VisiblePosition& left_word_break = VisualWordPosition(
       visible_position, kMoveLeft, skips_space_when_moving_right);
-  left_word_break = HonorEditingBoundaryAtOrBefore(
-      left_word_break, visible_position.DeepEquivalent());
-
+  if (left_word_break.IsNotNull())
+    return left_word_break;
   // TODO(editing-dev) How should we handle a non-editable position?
-  if (left_word_break.IsNull() &&
-      IsEditablePosition(visible_position.DeepEquivalent())) {
-    TextDirection block_direction =
-        DirectionOfEnclosingBlockOf(visible_position.DeepEquivalent());
-    left_word_break = block_direction == TextDirection::kLtr
-                          ? StartOfEditableContent(visible_position)
-                          : EndOfEditableContent(visible_position);
-  }
-  return left_word_break;
+  if (!IsEditablePosition(visible_position.DeepEquivalent()))
+    return left_word_break;
+  const TextDirection block_direction =
+      DirectionOfEnclosingBlockOf(visible_position.DeepEquivalent());
+  return block_direction == TextDirection::kLtr
+             ? StartOfEditableContent(visible_position)
+             : EndOfEditableContent(visible_position);
 }
 
 VisiblePosition SelectionModifier::RightWordPosition(
     const VisiblePosition& visible_position,
     bool skips_space_when_moving_right) {
   DCHECK(visible_position.IsValid()) << visible_position;
-  VisiblePosition right_word_break = VisualWordPosition(
+  const VisiblePosition& right_word_break = VisualWordPosition(
       visible_position, kMoveRight, skips_space_when_moving_right);
-  right_word_break = HonorEditingBoundaryAtOrBefore(
-      right_word_break, visible_position.DeepEquivalent());
-
+  if (right_word_break.IsNotNull())
+    return right_word_break;
   // TODO(editing-dev) How should we handle a non-editable position?
-  if (right_word_break.IsNull() &&
-      IsEditablePosition(visible_position.DeepEquivalent())) {
-    TextDirection block_direction =
-        blink::DirectionOfEnclosingBlockOf(visible_position.DeepEquivalent());
-    right_word_break = block_direction == TextDirection::kLtr
-                           ? EndOfEditableContent(visible_position)
-                           : StartOfEditableContent(visible_position);
-  }
-  return right_word_break;
+  if (!IsEditablePosition(visible_position.DeepEquivalent()))
+    return right_word_break;
+  const TextDirection block_direction =
+      blink::DirectionOfEnclosingBlockOf(visible_position.DeepEquivalent());
+  return block_direction == TextDirection::kLtr
+             ? EndOfEditableContent(visible_position)
+             : StartOfEditableContent(visible_position);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
index 9c1e4f9..516fc922 100644
--- a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
+++ b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
@@ -1587,26 +1587,26 @@
   DCHECK(!NeedsLayoutTreeUpdate(position)) << position;
   TRACE_EVENT0("input", "VisibleUnits::mostBackwardCaretPosition");
 
-  Node* start_node = position.AnchorNode();
+  Node* const start_node = position.AnchorNode();
   if (!start_node)
     return PositionTemplate<Strategy>();
 
   // iterate backward from there, looking for a qualified position
-  Node* boundary = EnclosingVisualBoundary<Strategy>(start_node);
+  Node* const boundary = EnclosingVisualBoundary<Strategy>(start_node);
   // FIXME: PositionIterator should respect Before and After positions.
   PositionIteratorAlgorithm<Strategy> last_visible(
       AdjustPositionForBackwardIteration<Strategy>(position));
-  PositionIteratorAlgorithm<Strategy> current_pos = last_visible;
-  bool start_editable = HasEditableStyle(*start_node);
+  const bool start_editable = HasEditableStyle(*start_node);
   Node* last_node = start_node;
   bool boundary_crossed = false;
-  for (; !current_pos.AtStart(); current_pos.Decrement()) {
+  for (PositionIteratorAlgorithm<Strategy> current_pos = last_visible;
+       !current_pos.AtStart(); current_pos.Decrement()) {
     Node* current_node = current_pos.GetNode();
     // Don't check for an editability change if we haven't moved to a different
     // node, to avoid the expense of computing hasEditableStyle().
     if (current_node != last_node) {
       // Don't change editability.
-      bool current_editable = HasEditableStyle(*current_node);
+      const bool current_editable = HasEditableStyle(*current_node);
       if (start_editable != current_editable) {
         if (rule == kCannotCrossEditingBoundary)
           break;
@@ -1623,7 +1623,7 @@
       return last_visible.DeprecatedComputePosition();
 
     // skip position in non-laid out or invisible node
-    LayoutObject* layout_object =
+    LayoutObject* const layout_object =
         AssociatedLayoutObjectOf(*current_node, current_pos.OffsetInLeafNode());
     if (!layout_object ||
         layout_object->Style()->Visibility() != EVisibility::kVisible)
@@ -1655,25 +1655,26 @@
     }
 
     // return current position if it is in laid out text
-    if (layout_object->IsText() &&
-        ToLayoutText(layout_object)->FirstTextBox()) {
-      LayoutText* const text_layout_object = ToLayoutText(layout_object);
-      const unsigned text_start_offset = text_layout_object->TextStartOffset();
-      if (current_node != start_node) {
-        // This assertion fires in layout tests in the case-transform.html test
-        // because of a mix-up between offsets in the text in the DOM tree with
-        // text in the layout tree which can have a different length due to case
-        // transformation.
-        // Until we resolve that, disable this so we can run the layout tests!
-        // DCHECK_GE(currentOffset, layoutObject->caretMaxOffset());
-        return PositionTemplate<Strategy>(
-            current_node, layout_object->CaretMaxOffset() + text_start_offset);
-      }
+    if (!layout_object->IsText())
+      continue;
+    LayoutText* const text_layout_object = ToLayoutText(layout_object);
+    if (!text_layout_object->FirstTextBox())
+      continue;
+    const unsigned text_start_offset = text_layout_object->TextStartOffset();
+    if (current_node != start_node) {
+      // This assertion fires in layout tests in the case-transform.html test
+      // because of a mix-up between offsets in the text in the DOM tree with
+      // text in the layout tree which can have a different length due to case
+      // transformation.
+      // Until we resolve that, disable this so we can run the layout tests!
+      // DCHECK_GE(currentOffset, layoutObject->caretMaxOffset());
+      return PositionTemplate<Strategy>(
+          current_node, layout_object->CaretMaxOffset() + text_start_offset);
+    }
 
-      if (CanBeBackwardCaretPosition(text_layout_object,
-                                     current_pos.OffsetInLeafNode())) {
-        return current_pos.ComputePosition();
-      }
+    if (CanBeBackwardCaretPosition(text_layout_object,
+                                   current_pos.OffsetInLeafNode())) {
+      return current_pos.ComputePosition();
     }
   }
   return last_visible.DeprecatedComputePosition();
@@ -1771,12 +1772,12 @@
   DCHECK(!NeedsLayoutTreeUpdate(position)) << position;
   TRACE_EVENT0("input", "VisibleUnits::mostForwardCaretPosition");
 
-  Node* start_node = position.AnchorNode();
+  Node* const start_node = position.AnchorNode();
   if (!start_node)
     return PositionTemplate<Strategy>();
 
   // iterate forward from there, looking for a qualified position
-  Node* boundary = EnclosingVisualBoundary<Strategy>(start_node);
+  Node* const boundary = EnclosingVisualBoundary<Strategy>(start_node);
   // FIXME: PositionIterator should respect Before and After positions.
   PositionIteratorAlgorithm<Strategy> last_visible(
       position.IsAfterAnchor()
@@ -1784,17 +1785,17 @@
                 position.AnchorNode(),
                 Strategy::CaretMaxOffset(*position.AnchorNode()))
           : position);
-  PositionIteratorAlgorithm<Strategy> current_pos = last_visible;
-  bool start_editable = HasEditableStyle(*start_node);
+  const bool start_editable = HasEditableStyle(*start_node);
   Node* last_node = start_node;
   bool boundary_crossed = false;
-  for (; !current_pos.AtEnd(); current_pos.Increment()) {
+  for (PositionIteratorAlgorithm<Strategy> current_pos = last_visible;
+       !current_pos.AtEnd(); current_pos.Increment()) {
     Node* current_node = current_pos.GetNode();
     // Don't check for an editability change if we haven't moved to a different
     // node, to avoid the expense of computing hasEditableStyle().
     if (current_node != last_node) {
       // Don't change editability.
-      bool current_editable = HasEditableStyle(*current_node);
+      const bool current_editable = HasEditableStyle(*current_node);
       if (start_editable != current_editable) {
         if (rule == kCannotCrossEditingBoundary)
           break;
@@ -1821,16 +1822,14 @@
       return last_visible.DeprecatedComputePosition();
 
     // skip position in non-laid out or invisible node
-    LayoutObject* layout_object =
+    LayoutObject* const layout_object =
         AssociatedLayoutObjectOf(*current_node, current_pos.OffsetInLeafNode());
     if (!layout_object ||
         layout_object->Style()->Visibility() != EVisibility::kVisible)
       continue;
 
-    if (rule == kCanCrossEditingBoundary && boundary_crossed) {
-      last_visible = current_pos;
-      break;
-    }
+    if (rule == kCanCrossEditingBoundary && boundary_crossed)
+      return current_pos.DeprecatedComputePosition();
 
     // track last visible streamer position
     if (IsStreamer<Strategy>(current_pos))
@@ -1847,20 +1846,21 @@
     }
 
     // return current position if it is in laid out text
-    if (layout_object->IsText() &&
-        ToLayoutText(layout_object)->FirstTextBox()) {
-      LayoutText* const text_layout_object = ToLayoutText(layout_object);
-      const unsigned text_start_offset = text_layout_object->TextStartOffset();
-      if (current_node != start_node) {
-        DCHECK(current_pos.AtStartOfNode());
-        return PositionTemplate<Strategy>(
-            current_node, layout_object->CaretMinOffset() + text_start_offset);
-      }
+    if (!layout_object->IsText())
+      continue;
+    LayoutText* const text_layout_object = ToLayoutText(layout_object);
+    if (!text_layout_object->FirstTextBox())
+      continue;
+    const unsigned text_start_offset = text_layout_object->TextStartOffset();
+    if (current_node != start_node) {
+      DCHECK(current_pos.AtStartOfNode());
+      return PositionTemplate<Strategy>(
+          current_node, layout_object->CaretMinOffset() + text_start_offset);
+    }
 
-      if (CanBeForwardCaretPosition(text_layout_object,
-                                    current_pos.OffsetInLeafNode())) {
-        return current_pos.ComputePosition();
-      }
+    if (CanBeForwardCaretPosition(text_layout_object,
+                                  current_pos.OffsetInLeafNode())) {
+      return current_pos.ComputePosition();
     }
   }
   return last_visible.DeprecatedComputePosition();
diff --git a/third_party/WebKit/Source/core/paint/RoundedInnerRectClipper.cpp b/third_party/WebKit/Source/core/paint/RoundedInnerRectClipper.cpp
index 36243f7..cd0628b 100644
--- a/third_party/WebKit/Source/core/paint/RoundedInnerRectClipper.cpp
+++ b/third_party/WebKit/Source/core/paint/RoundedInnerRectClipper.cpp
@@ -23,6 +23,9 @@
       clip_type_(use_paint_controller_
                      ? paint_info_.DisplayItemTypeForClipping()
                      : DisplayItem::kClipBoxPaintPhaseFirst) {
+  if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled() && use_paint_controller_)
+    return;
+
   Vector<FloatRoundedRect> rounded_rect_clips;
   if (clip_rect.IsRenderable()) {
     rounded_rect_clips.push_back(clip_rect);
@@ -81,6 +84,9 @@
 }
 
 RoundedInnerRectClipper::~RoundedInnerRectClipper() {
+  if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled() && use_paint_controller_)
+    return;
+
   DisplayItem::Type end_type = DisplayItem::ClipTypeToEndClipType(clip_type_);
   if (use_paint_controller_) {
     paint_info_.context.GetPaintController().EndItem<EndClipDisplayItem>(
diff --git a/third_party/WebKit/Source/devtools/BUILD.gn b/third_party/WebKit/Source/devtools/BUILD.gn
index 22f3c89..10cdd93 100644
--- a/third_party/WebKit/Source/devtools/BUILD.gn
+++ b/third_party/WebKit/Source/devtools/BUILD.gn
@@ -109,7 +109,6 @@
   "front_end/common/Color.js",
   "front_end/common/Console.js",
   "front_end/common/ContentProvider.js",
-  "front_end/common/FormatterWorkerPool.js",
   "front_end/common/module.json",
   "front_end/common/ModuleExtensionInterfaces.js",
   "front_end/common/Object.js",
@@ -226,6 +225,9 @@
   "front_end/extensions/ExtensionServer.js",
   "front_end/extensions/ExtensionView.js",
   "front_end/extensions/module.json",
+  "front_end/formatter/FormatterWorkerPool.js",
+  "front_end/formatter/module.json",
+  "front_end/formatter/ScriptFormatter.js",
   "front_end/formatter_worker.js",
   "front_end/formatter_worker.json",
   "front_end/formatter_worker/AcornTokenizer.js",
@@ -574,7 +576,6 @@
   "front_end/sources/RevisionHistoryView.js",
   "front_end/sources/scopeChainSidebarPane.css",
   "front_end/sources/ScopeChainSidebarPane.js",
-  "front_end/sources/ScriptFormatter.js",
   "front_end/sources/ScriptFormatterEditorAction.js",
   "front_end/sources/serviceWorkersSidebar.css",
   "front_end/sources/SimpleHistoryManager.js",
@@ -887,6 +888,7 @@
   "$resources_out_dir/diff/diff_module.js",
   "$resources_out_dir/elements/elements_module.js",
   "$resources_out_dir/event_listeners/event_listeners_module.js",
+  "$resources_out_dir/formatter/formatter_module.js",
   "$resources_out_dir/heap_snapshot_model/heap_snapshot_model_module.js",
   "$resources_out_dir/inline_editor/inline_editor_module.js",
   "$resources_out_dir/layer_viewer/layer_viewer_module.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/audits/AuditRules.js b/third_party/WebKit/Source/devtools/front_end/audits/AuditRules.js
index 9c2dae4..3c41ff8 100644
--- a/third_party/WebKit/Source/devtools/front_end/audits/AuditRules.js
+++ b/third_party/WebKit/Source/devtools/front_end/audits/AuditRules.js
@@ -484,7 +484,7 @@
 };
 
 /**
- * @typedef {!{sourceURL: string, rules: !Array.<!Common.FormatterWorkerPool.CSSStyleRule>}}
+ * @typedef {!{sourceURL: string, rules: !Array.<!Formatter.FormatterWorkerPool.CSSStyleRule>}}
  */
 Audits.AuditRules.ParsedStyleSheet;
 
@@ -517,11 +517,11 @@
 
     var allRules = [];
     this._currentStyleSheetHeader.requestContent().then(
-        content => Common.formatterWorkerPool.parseCSS(content || '', onRulesParsed.bind(this)));
+        content => Formatter.formatterWorkerPool().parseCSS(content || '', onRulesParsed.bind(this)));
 
     /**
      * @param {boolean} isLastChunk
-     * @param {!Array<!Common.FormatterWorkerPool.CSSRule>} rules
+     * @param {!Array<!Formatter.FormatterWorkerPool.CSSRule>} rules
      * @this {Audits.AuditRules.StyleSheetProcessor}
      */
     function onRulesParsed(isLastChunk, rules) {
@@ -532,7 +532,7 @@
   }
 
   /**
-   * @param {!Array.<!Common.FormatterWorkerPool.CSSRule>} rules
+   * @param {!Array.<!Formatter.FormatterWorkerPool.CSSRule>} rules
    */
   _onStyleSheetParsed(rules) {
     if (this._progress.isCanceled()) {
@@ -1205,7 +1205,7 @@
 
   /**
    * @param {!Audits.AuditRules.ParsedStyleSheet} styleSheet
-   * @param {!Common.FormatterWorkerPool.CSSStyleRule} rule
+   * @param {!Formatter.FormatterWorkerPool.CSSStyleRule} rule
    * @param {!Audits.AuditRuleResult} result
    */
   _visitRule(styleSheet, rule, result) {
@@ -1234,7 +1234,7 @@
 
   /**
    * @param {!Audits.AuditRules.ParsedStyleSheet} styleSheet
-   * @param {!Common.FormatterWorkerPool.CSSStyleRule} rule
+   * @param {!Formatter.FormatterWorkerPool.CSSStyleRule} rule
    * @param {!Audits.AuditRuleResult} result
    */
   visitRule(styleSheet, rule, result) {
@@ -1243,7 +1243,7 @@
 
   /**
    * @param {!Audits.AuditRules.ParsedStyleSheet} styleSheet
-   * @param {!Common.FormatterWorkerPool.CSSStyleRule} rule
+   * @param {!Formatter.FormatterWorkerPool.CSSStyleRule} rule
    * @param {!Audits.AuditRuleResult} result
    */
   didVisitRule(styleSheet, rule, result) {
@@ -1252,8 +1252,8 @@
 
   /**
    * @param {!Audits.AuditRules.ParsedStyleSheet} styleSheet
-   * @param {!Common.FormatterWorkerPool.CSSStyleRule} rule
-   * @param {!Common.FormatterWorkerPool.CSSProperty} property
+   * @param {!Formatter.FormatterWorkerPool.CSSStyleRule} rule
+   * @param {!Formatter.FormatterWorkerPool.CSSProperty} property
    * @param {!Audits.AuditRuleResult} result
    */
   visitProperty(styleSheet, rule, property, result) {
diff --git a/third_party/WebKit/Source/devtools/front_end/audits/module.json b/third_party/WebKit/Source/devtools/front_end/audits/module.json
index 8459091..1d48d5e7 100644
--- a/third_party/WebKit/Source/devtools/front_end/audits/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/audits/module.json
@@ -24,7 +24,8 @@
         "components",
         "extensions",
         "network_log",
-        "object_ui"
+        "object_ui",
+        "formatter"
     ],
     "scripts": [
         "AuditsPanel.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/common/module.json b/third_party/WebKit/Source/devtools/front_end/common/module.json
index 0ad0a41..99b5427a 100644
--- a/third_party/WebKit/Source/devtools/front_end/common/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/common/module.json
@@ -21,7 +21,6 @@
         "Trie.js",
         "UIString.js",
         "ModuleExtensionInterfaces.js",
-        "FormatterWorkerPool.js",
         "CharacterIdMap.js"
     ]
 }
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/ElementsPanel.js b/third_party/WebKit/Source/devtools/front_end/elements/ElementsPanel.js
index d5afb49a..2833117 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/ElementsPanel.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/ElementsPanel.js
@@ -552,6 +552,7 @@
     }
 
     var treeElement = this._treeElementForNode(searchResult.node);
+    searchResult.node.scrollIntoView();
     if (treeElement) {
       treeElement.highlightSearchResults(this._searchConfig.query);
       treeElement.reveal();
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js b/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js
index 32f18f1..9b8180d4 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeElement.js
@@ -460,15 +460,13 @@
   populateTagContextMenu(contextMenu, event) {
     // Add attribute-related actions.
     var treeElement = this._elementCloseTag ? this.treeOutline.findTreeElement(this._node) : this;
-    contextMenu.appendItem(
-        Common.UIString.capitalize('Add ^attribute'), treeElement._addNewAttribute.bind(treeElement));
+    contextMenu.appendItem(Common.UIString('Add attribute'), treeElement._addNewAttribute.bind(treeElement));
 
     var attribute = event.target.enclosingNodeOrSelfWithClass('webkit-html-attribute');
     var newAttribute = event.target.enclosingNodeOrSelfWithClass('add-attribute');
     if (attribute && !newAttribute) {
       contextMenu.appendItem(
-          Common.UIString.capitalize('Edit ^attribute'),
-          this._startEditingAttribute.bind(this, attribute, event.target));
+          Common.UIString('Edit attribute'), this._startEditingAttribute.bind(this, attribute, event.target));
     }
     this.populateNodeContextMenu(contextMenu);
     Elements.ElementsTreeElement.populateForcedPseudoStateItems(contextMenu, treeElement.node());
@@ -480,12 +478,12 @@
    * @param {!UI.ContextMenu} contextMenu
    */
   populateScrollIntoView(contextMenu) {
-    contextMenu.appendItem(Common.UIString.capitalize('Scroll into ^view'), this._scrollIntoView.bind(this));
+    contextMenu.appendItem(Common.UIString('Scroll into view'), () => this._node.scrollIntoView());
   }
 
   populateTextContextMenu(contextMenu, textNode) {
     if (!this._editing)
-      contextMenu.appendItem(Common.UIString.capitalize('Edit ^text'), this._startEditingTextNode.bind(this, textNode));
+      contextMenu.appendItem(Common.UIString('Edit text'), this._startEditingTextNode.bind(this, textNode));
     this.populateNodeContextMenu(contextMenu);
   }
 
@@ -508,7 +506,7 @@
       menuItem.setShortcut(createShortcut('V', modifier));
     }
     if (this._node.nodeType() === Node.ELEMENT_NODE)
-      copyMenu.appendItem(Common.UIString.capitalize('Copy selector'), this._copyCSSPath.bind(this));
+      copyMenu.appendItem(Common.UIString('Copy selector'), this._copyCSSPath.bind(this));
     if (!isShadowRoot)
       copyMenu.appendItem(Common.UIString('Copy XPath'), this._copyXPath.bind(this));
     if (!isShadowRoot) {
@@ -1629,23 +1627,6 @@
     UI.highlightSearchResults(this.listItemElement, matchRanges, this._highlightResult);
   }
 
-  _scrollIntoView() {
-    function scrollIntoViewCallback(object) {
-      /**
-       * @suppressReceiverCheck
-       * @this {!Element}
-       */
-      function scrollIntoView() {
-        this.scrollIntoViewIfNeeded(true);
-      }
-
-      if (object)
-        object.callFunction(scrollIntoView);
-    }
-
-    this._node.resolveToObject('', scrollIntoViewCallback);
-  }
-
   _editAsHTML() {
     var promise = Common.Revealer.revealPromise(this.node());
     promise.then(() => UI.actionRegistry.action('elements.edit-as-html').execute());
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeOutline.js b/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeOutline.js
index 322fadc..decdc8a 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeOutline.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/ElementsTreeOutline.js
@@ -550,38 +550,32 @@
    * @param {!SDK.DOMNode} node
    * @return {!Promise<!Object|undefined>}
    */
-  _loadDimensionsForNode(node) {
+  async _loadDimensionsForNode(node) {
     if (!node.nodeName() || node.nodeName().toLowerCase() !== 'img')
-      return Promise.resolve();
+      return;
 
-    var fulfill;
-    var promise = new Promise(x => fulfill = x);
-    node.resolveToObject('', resolvedNode);
+    var object = await node.resolveToObject('');
+
+    if (!object)
+      return;
+
+    var promise = object.callFunctionJSONPromise(features, undefined);
+    object.release();
     return promise;
 
-    function resolvedNode(object) {
-      if (!object) {
-        fulfill();
-        return;
-      }
-
-      object.callFunctionJSON(features, undefined, fulfill);
-      object.release();
-
-      /**
-       * @return {!{offsetWidth: number, offsetHeight: number, naturalWidth: number, naturalHeight: number, currentSrc: (string|undefined)}}
-       * @suppressReceiverCheck
-       * @this {!Element}
-       */
-      function features() {
-        return {
-          offsetWidth: this.offsetWidth,
-          offsetHeight: this.offsetHeight,
-          naturalWidth: this.naturalWidth,
-          naturalHeight: this.naturalHeight,
-          currentSrc: this.currentSrc
-        };
-      }
+    /**
+     * @return {!{offsetWidth: number, offsetHeight: number, naturalWidth: number, naturalHeight: number, currentSrc: (string|undefined)}}
+     * @suppressReceiverCheck
+     * @this {!Element}
+     */
+    function features() {
+      return {
+        offsetWidth: this.offsetWidth,
+        offsetHeight: this.offsetHeight,
+        naturalWidth: this.naturalWidth,
+        naturalHeight: this.naturalHeight,
+        currentSrc: this.currentSrc
+      };
     }
   }
 
@@ -896,66 +890,63 @@
    * ancestors.
    *
    * @param {!SDK.DOMNode} node
-   * @param {function(?SDK.RemoteObject, boolean=)=} userCallback
    */
-  toggleHideElement(node, userCallback) {
+  async toggleHideElement(node) {
     var pseudoType = node.pseudoType();
     var effectiveNode = pseudoType ? node.parentNode : node;
     if (!effectiveNode)
       return;
 
     var hidden = node.marker('hidden-marker');
+    var object = await effectiveNode.resolveToObject('');
 
-    function resolvedNode(object) {
-      if (!object)
+    if (!object)
+      return;
+
+    var result = object.callFunction(toggleClassAndInjectStyleRule, [{value: pseudoType}, {value: !hidden}]);
+    object.release();
+    node.setMarker('hidden-marker', hidden ? null : true);
+    return result;
+
+    /**
+     * @param {?string} pseudoType
+     * @param {boolean} hidden
+     * @suppressGlobalPropertiesCheck
+     * @suppressReceiverCheck
+     * @this {!Element}
+     */
+    function toggleClassAndInjectStyleRule(pseudoType, hidden) {
+      const classNamePrefix = '__web-inspector-hide';
+      const classNameSuffix = '-shortcut__';
+      const styleTagId = '__web-inspector-hide-shortcut-style__';
+      var selectors = [];
+      selectors.push('.__web-inspector-hide-shortcut__');
+      selectors.push('.__web-inspector-hide-shortcut__ *');
+      selectors.push('.__web-inspector-hidebefore-shortcut__::before');
+      selectors.push('.__web-inspector-hideafter-shortcut__::after');
+      var selector = selectors.join(', ');
+      var ruleBody = '    visibility: hidden !important;';
+      var rule = '\n' + selector + '\n{\n' + ruleBody + '\n}\n';
+      var className = classNamePrefix + (pseudoType || '') + classNameSuffix;
+      this.classList.toggle(className, hidden);
+
+      var localRoot = this;
+      while (localRoot.parentNode)
+        localRoot = localRoot.parentNode;
+      if (localRoot.nodeType === Node.DOCUMENT_NODE)
+        localRoot = document.head;
+
+      var style = localRoot.querySelector('style#' + styleTagId);
+      if (style)
         return;
 
-      /**
-       * @param {?string} pseudoType
-       * @param {boolean} hidden
-       * @suppressGlobalPropertiesCheck
-       * @suppressReceiverCheck
-       * @this {!Element}
-       */
-      function toggleClassAndInjectStyleRule(pseudoType, hidden) {
-        const classNamePrefix = '__web-inspector-hide';
-        const classNameSuffix = '-shortcut__';
-        const styleTagId = '__web-inspector-hide-shortcut-style__';
-        var selectors = [];
-        selectors.push('.__web-inspector-hide-shortcut__');
-        selectors.push('.__web-inspector-hide-shortcut__ *');
-        selectors.push('.__web-inspector-hidebefore-shortcut__::before');
-        selectors.push('.__web-inspector-hideafter-shortcut__::after');
-        var selector = selectors.join(', ');
-        var ruleBody = '    visibility: hidden !important;';
-        var rule = '\n' + selector + '\n{\n' + ruleBody + '\n}\n';
-        var className = classNamePrefix + (pseudoType || '') + classNameSuffix;
-        this.classList.toggle(className, hidden);
+      style = document.createElement('style');
+      style.id = styleTagId;
+      style.type = 'text/css';
+      style.textContent = rule;
 
-        var localRoot = this;
-        while (localRoot.parentNode)
-          localRoot = localRoot.parentNode;
-        if (localRoot.nodeType === Node.DOCUMENT_NODE)
-          localRoot = document.head;
-
-        var style = localRoot.querySelector('style#' + styleTagId);
-        if (style)
-          return;
-
-        style = document.createElement('style');
-        style.id = styleTagId;
-        style.type = 'text/css';
-        style.textContent = rule;
-
-        localRoot.appendChild(style);
-      }
-
-      object.callFunction(toggleClassAndInjectStyleRule, [{value: pseudoType}, {value: !hidden}], userCallback);
-      object.release();
-      node.setMarker('hidden-marker', hidden ? null : true);
+      localRoot.appendChild(style);
     }
-
-    effectiveNode.resolveToObject('', resolvedNode);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/EventListenersWidget.js b/third_party/WebKit/Source/devtools/front_end/elements/EventListenersWidget.js
index b59b574a..fa2081e7 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/EventListenersWidget.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/EventListenersWidget.js
@@ -101,11 +101,11 @@
     this._lastRequestedNode = node;
     var selectedNodeOnly = !this._showForAncestorsSetting.get();
     var promises = [];
-    promises.push(node.resolveToObjectPromise(Elements.EventListenersWidget._objectGroupName));
+    promises.push(node.resolveToObject(Elements.EventListenersWidget._objectGroupName));
     if (!selectedNodeOnly) {
       var currentNode = node.parentNode;
       while (currentNode) {
-        promises.push(currentNode.resolveToObjectPromise(Elements.EventListenersWidget._objectGroupName));
+        promises.push(currentNode.resolveToObject(Elements.EventListenersWidget._objectGroupName));
         currentNode = currentNode.parentNode;
       }
       promises.push(this._windowObjectInNodeContext(node));
diff --git a/third_party/WebKit/Source/devtools/front_end/elements/PropertiesWidget.js b/third_party/WebKit/Source/devtools/front_end/elements/PropertiesWidget.js
index f923fb3..f20b517 100644
--- a/third_party/WebKit/Source/devtools/front_end/elements/PropertiesWidget.js
+++ b/third_party/WebKit/Source/devtools/front_end/elements/PropertiesWidget.js
@@ -71,7 +71,7 @@
     }
 
     this._lastRequestedNode = this._node;
-    return this._node.resolveToObjectPromise(Elements.PropertiesWidget._objectGroupName).then(nodeResolved.bind(this));
+    return this._node.resolveToObject(Elements.PropertiesWidget._objectGroupName).then(nodeResolved.bind(this));
 
     /**
      * @param {?SDK.RemoteObject} object
diff --git a/third_party/WebKit/Source/devtools/front_end/common/FormatterWorkerPool.js b/third_party/WebKit/Source/devtools/front_end/formatter/FormatterWorkerPool.js
similarity index 69%
rename from third_party/WebKit/Source/devtools/front_end/common/FormatterWorkerPool.js
rename to third_party/WebKit/Source/devtools/front_end/formatter/FormatterWorkerPool.js
index 18988570..3e80f73c 100644
--- a/third_party/WebKit/Source/devtools/front_end/common/FormatterWorkerPool.js
+++ b/third_party/WebKit/Source/devtools/front_end/formatter/FormatterWorkerPool.js
@@ -4,10 +4,10 @@
 /**
  * @unrestricted
  */
-Common.FormatterWorkerPool = class {
+Formatter.FormatterWorkerPool = class {
   constructor() {
     this._taskQueue = [];
-    /** @type {!Map<!Common.Worker, ?Common.FormatterWorkerPool.Task>} */
+    /** @type {!Map<!Common.Worker, ?Formatter.FormatterWorkerPool.Task>} */
     this._workerTasks = new Map();
   }
 
@@ -26,7 +26,7 @@
       return;
 
     var freeWorker = this._workerTasks.keysArray().find(worker => !this._workerTasks.get(worker));
-    if (!freeWorker && this._workerTasks.size < Common.FormatterWorkerPool.MaxWorkers)
+    if (!freeWorker && this._workerTasks.size < Formatter.FormatterWorkerPool.MaxWorkers)
       freeWorker = this._createWorker();
     if (!freeWorker)
       return;
@@ -74,7 +74,7 @@
    * @param {function(boolean, *)} callback
    */
   _runChunkedTask(methodName, params, callback) {
-    var task = new Common.FormatterWorkerPool.Task(methodName, params, onData, true);
+    var task = new Formatter.FormatterWorkerPool.Task(methodName, params, onData, true);
     this._taskQueue.push(task);
     this._processNextTask();
 
@@ -100,7 +100,7 @@
   _runTask(methodName, params) {
     var callback;
     var promise = new Promise(fulfill => callback = fulfill);
-    var task = new Common.FormatterWorkerPool.Task(methodName, params, callback, false);
+    var task = new Formatter.FormatterWorkerPool.Task(methodName, params, callback, false);
     this._taskQueue.push(task);
     this._processNextTask();
     return promise;
@@ -116,7 +116,7 @@
 
   /**
    * @param {string} content
-   * @return {!Promise<!Array<!Common.FormatterWorkerPool.SCSSRule>>}
+   * @return {!Promise<!Array<!Formatter.FormatterWorkerPool.SCSSRule>>}
    */
   parseSCSS(content) {
     return this._runTask('parseSCSS', {content: content}).then(rules => rules || []);
@@ -126,11 +126,11 @@
    * @param {string} mimeType
    * @param {string} content
    * @param {string} indentString
-   * @return {!Promise<!Common.FormatterWorkerPool.FormatResult>}
+   * @return {!Promise<!Formatter.FormatterWorkerPool.FormatResult>}
    */
   format(mimeType, content, indentString) {
     var parameters = {mimeType: mimeType, content: content, indentString: indentString};
-    return /** @type {!Promise<!Common.FormatterWorkerPool.FormatResult>} */ (this._runTask('format', parameters));
+    return /** @type {!Promise<!Formatter.FormatterWorkerPool.FormatResult>} */ (this._runTask('format', parameters));
   }
 
   /**
@@ -151,7 +151,7 @@
 
   /**
    * @param {string} content
-   * @param {function(boolean, !Array<!Common.FormatterWorkerPool.CSSRule>)} callback
+   * @param {function(boolean, !Array<!Formatter.FormatterWorkerPool.CSSRule>)} callback
    */
   parseCSS(content, callback) {
     this._runChunkedTask('parseCSS', {content: content}, onDataChunk);
@@ -161,14 +161,14 @@
      * @param {*} data
      */
     function onDataChunk(isLastChunk, data) {
-      var rules = /** @type {!Array<!Common.FormatterWorkerPool.CSSRule>} */ (data || []);
+      var rules = /** @type {!Array<!Formatter.FormatterWorkerPool.CSSRule>} */ (data || []);
       callback(isLastChunk, rules);
     }
   }
 
   /**
    * @param {string} content
-   * @param {function(boolean, !Array<!Common.FormatterWorkerPool.JSOutlineItem>)} callback
+   * @param {function(boolean, !Array<!Formatter.FormatterWorkerPool.JSOutlineItem>)} callback
    */
   javaScriptOutline(content, callback) {
     this._runChunkedTask('javaScriptOutline', {content: content}, onDataChunk);
@@ -178,7 +178,7 @@
      * @param {*} data
      */
     function onDataChunk(isLastChunk, data) {
-      var items = /** @type {!Array.<!Common.FormatterWorkerPool.JSOutlineItem>} */ (data || []);
+      var items = /** @type {!Array.<!Formatter.FormatterWorkerPool.JSOutlineItem>} */ (data || []);
       callback(isLastChunk, items);
     }
   }
@@ -186,7 +186,7 @@
   /**
    * @param {string} content
    * @param {string} mimeType
-   * @param {function(boolean, !Array<!Common.FormatterWorkerPool.OutlineItem>)} callback
+   * @param {function(boolean, !Array<!Formatter.FormatterWorkerPool.OutlineItem>)} callback
    * @return {boolean}
    */
   outlineForMimetype(content, mimeType, callback) {
@@ -203,7 +203,7 @@
 
     /**
      * @param {boolean} isLastChunk
-     * @param {!Array<!Common.FormatterWorkerPool.JSOutlineItem>} items
+     * @param {!Array<!Formatter.FormatterWorkerPool.JSOutlineItem>} items
      */
     function javaScriptCallback(isLastChunk, items) {
       callback(
@@ -213,7 +213,7 @@
 
     /**
      * @param {boolean} isLastChunk
-     * @param {!Array<!Common.FormatterWorkerPool.CSSRule>} rules
+     * @param {!Array<!Formatter.FormatterWorkerPool.CSSRule>} rules
      */
     function cssCallback(isLastChunk, rules) {
       callback(
@@ -224,12 +224,12 @@
   }
 };
 
-Common.FormatterWorkerPool.MaxWorkers = 2;
+Formatter.FormatterWorkerPool.MaxWorkers = 2;
 
 /**
  * @unrestricted
  */
-Common.FormatterWorkerPool.Task = class {
+Formatter.FormatterWorkerPool.Task = class {
   /**
    * @param {string} method
    * @param {!Object<string, string>} params
@@ -244,22 +244,22 @@
   }
 };
 
-Common.FormatterWorkerPool.FormatResult = class {
+Formatter.FormatterWorkerPool.FormatResult = class {
   constructor() {
     /** @type {string} */
     this.content;
-    /** @type {!Common.FormatterWorkerPool.FormatMapping} */
+    /** @type {!Formatter.FormatterWorkerPool.FormatMapping} */
     this.mapping;
   }
 };
 
 /** @typedef {{original: !Array<number>, formatted: !Array<number>}} */
-Common.FormatterWorkerPool.FormatMapping;
+Formatter.FormatterWorkerPool.FormatMapping;
 
 /** @typedef {{line: number, column: number, title: string, subtitle: (string|undefined) }} */
-Common.FormatterWorkerPool.OutlineItem;
+Formatter.FormatterWorkerPool.OutlineItem;
 
-Common.FormatterWorkerPool.JSOutlineItem = class {
+Formatter.FormatterWorkerPool.JSOutlineItem = class {
   constructor() {
     /** @type {string} */
     this.name;
@@ -275,36 +275,36 @@
 /**
  * @typedef {{startLine: number, startColumn: number, endLine: number, endColumn: number}}
  */
-Common.FormatterWorkerPool.TextRange;
+Formatter.FormatterWorkerPool.TextRange;
 
-Common.FormatterWorkerPool.CSSProperty = class {
+Formatter.FormatterWorkerPool.CSSProperty = class {
   constructor() {
     /** @type {string} */
     this.name;
-    /** @type {!Common.FormatterWorkerPool.TextRange} */
+    /** @type {!Formatter.FormatterWorkerPool.TextRange} */
     this.nameRange;
     /** @type {string} */
     this.value;
-    /** @type {!Common.FormatterWorkerPool.TextRange} */
+    /** @type {!Formatter.FormatterWorkerPool.TextRange} */
     this.valueRange;
-    /** @type {!Common.FormatterWorkerPool.TextRange} */
+    /** @type {!Formatter.FormatterWorkerPool.TextRange} */
     this.range;
     /** @type {(boolean|undefined)} */
     this.disabled;
   }
 };
 
-Common.FormatterWorkerPool.CSSStyleRule = class {
+Formatter.FormatterWorkerPool.CSSStyleRule = class {
   constructor() {
     /** @type {string} */
     this.selectorText;
-    /** @type {!Common.FormatterWorkerPool.TextRange} */
+    /** @type {!Formatter.FormatterWorkerPool.TextRange} */
     this.styleRange;
     /** @type {number} */
     this.lineNumber;
     /** @type {number} */
     this.columnNumber;
-    /** @type {!Array.<!Common.FormatterWorkerPool.CSSProperty>} */
+    /** @type {!Array.<!Formatter.FormatterWorkerPool.CSSProperty>} */
     this.properties;
   }
 };
@@ -312,36 +312,42 @@
 /**
  * @typedef {{atRule: string, lineNumber: number, columnNumber: number}}
  */
-Common.FormatterWorkerPool.CSSAtRule;
+Formatter.FormatterWorkerPool.CSSAtRule;
 
 /**
- * @typedef {(Common.FormatterWorkerPool.CSSStyleRule|Common.FormatterWorkerPool.CSSAtRule)}
+ * @typedef {(Formatter.FormatterWorkerPool.CSSStyleRule|Formatter.FormatterWorkerPool.CSSAtRule)}
  */
-Common.FormatterWorkerPool.CSSRule;
+Formatter.FormatterWorkerPool.CSSRule;
 
-Common.FormatterWorkerPool.SCSSProperty = class {
+Formatter.FormatterWorkerPool.SCSSProperty = class {
   constructor() {
-    /** @type {!Common.FormatterWorkerPool.TextRange} */
+    /** @type {!Formatter.FormatterWorkerPool.TextRange} */
     this.range;
-    /** @type {!Common.FormatterWorkerPool.TextRange} */
+    /** @type {!Formatter.FormatterWorkerPool.TextRange} */
     this.name;
-    /** @type {!Common.FormatterWorkerPool.TextRange} */
+    /** @type {!Formatter.FormatterWorkerPool.TextRange} */
     this.value;
     /** @type {boolean} */
     this.disabled;
   }
 };
 
-Common.FormatterWorkerPool.SCSSRule = class {
+Formatter.FormatterWorkerPool.SCSSRule = class {
   constructor() {
-    /** @type {!Array<!Common.FormatterWorkerPool.TextRange>} */
+    /** @type {!Array<!Formatter.FormatterWorkerPool.TextRange>} */
     this.selectors;
-    /** @type {!Array<!Common.FormatterWorkerPool.SCSSProperty>} */
+    /** @type {!Array<!Formatter.FormatterWorkerPool.SCSSProperty>} */
     this.properties;
-    /** @type {!Common.FormatterWorkerPool.TextRange} */
+    /** @type {!Formatter.FormatterWorkerPool.TextRange} */
     this.styleRange;
   }
 };
 
-/** @type {!Common.FormatterWorkerPool} */
-Common.formatterWorkerPool;
+/**
+ * @return {!Formatter.FormatterWorkerPool}
+ */
+Formatter.formatterWorkerPool = function() {
+  if (!Formatter._formatterWorkerPool)
+    Formatter._formatterWorkerPool = new Formatter.FormatterWorkerPool();
+  return Formatter._formatterWorkerPool;
+};
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/ScriptFormatter.js b/third_party/WebKit/Source/devtools/front_end/formatter/ScriptFormatter.js
similarity index 74%
rename from third_party/WebKit/Source/devtools/front_end/sources/ScriptFormatter.js
rename to third_party/WebKit/Source/devtools/front_end/formatter/ScriptFormatter.js
index 2a43735..1a237f5 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/ScriptFormatter.js
+++ b/third_party/WebKit/Source/devtools/front_end/formatter/ScriptFormatter.js
@@ -30,19 +30,19 @@
 /**
  * @interface
  */
-Sources.Formatter = function() {};
+Formatter.Formatter = function() {};
 
 /**
  * @param {!Common.ResourceType} contentType
  * @param {string} mimeType
  * @param {string} content
- * @param {function(string, !Sources.FormatterSourceMapping)} callback
+ * @param {function(string, !Formatter.FormatterSourceMapping)} callback
  */
-Sources.Formatter.format = function(contentType, mimeType, content, callback) {
+Formatter.Formatter.format = function(contentType, mimeType, content, callback) {
   if (contentType.isDocumentOrScriptOrStyleSheet())
-    new Sources.ScriptFormatter(mimeType, content, callback);
+    new Formatter.ScriptFormatter(mimeType, content, callback);
   else
-    new Sources.ScriptIdentityFormatter(mimeType, content, callback);
+    new Formatter.ScriptIdentityFormatter(mimeType, content, callback);
 };
 
 /**
@@ -51,7 +51,7 @@
  * @param {number} columnNumber
  * @return {number}
  */
-Sources.Formatter.locationToPosition = function(lineEndings, lineNumber, columnNumber) {
+Formatter.Formatter.locationToPosition = function(lineEndings, lineNumber, columnNumber) {
   var position = lineNumber ? lineEndings[lineNumber - 1] + 1 : 0;
   return position + columnNumber;
 };
@@ -61,7 +61,7 @@
  * @param {number} position
  * @return {!Array<number>}
  */
-Sources.Formatter.positionToLocation = function(lineEndings, position) {
+Formatter.Formatter.positionToLocation = function(lineEndings, position) {
   var lineNumber = lineEndings.upperBound(position - 1);
   if (!lineNumber)
     var columnNumber = position;
@@ -71,55 +71,56 @@
 };
 
 /**
- * @implements {Sources.Formatter}
+ * @implements {Formatter.Formatter}
  * @unrestricted
  */
-Sources.ScriptFormatter = class {
+Formatter.ScriptFormatter = class {
   /**
    * @param {string} mimeType
    * @param {string} content
-   * @param {function(string, !Sources.FormatterSourceMapping)} callback
+   * @param {function(string, !Formatter.FormatterSourceMapping)} callback
    */
   constructor(mimeType, content, callback) {
     content = content.replace(/\r\n?|[\n\u2028\u2029]/g, '\n').replace(/^\uFEFF/, '');
     this._callback = callback;
     this._originalContent = content;
 
-    Common.formatterWorkerPool.format(mimeType, content, Common.moduleSetting('textEditorIndent').get())
+    Formatter.formatterWorkerPool()
+        .format(mimeType, content, Common.moduleSetting('textEditorIndent').get())
         .then(this._didFormatContent.bind(this));
   }
 
   /**
-   * @param {!Common.FormatterWorkerPool.FormatResult} formatResult
+   * @param {!Formatter.FormatterWorkerPool.FormatResult} formatResult
    */
   _didFormatContent(formatResult) {
-    var sourceMapping = new Sources.FormatterSourceMappingImpl(
+    var sourceMapping = new Formatter.FormatterSourceMappingImpl(
         this._originalContent.computeLineEndings(), formatResult.content.computeLineEndings(), formatResult.mapping);
     this._callback(formatResult.content, sourceMapping);
   }
 };
 
 /**
- * @implements {Sources.Formatter}
+ * @implements {Formatter.Formatter}
  * @unrestricted
  */
-Sources.ScriptIdentityFormatter = class {
+Formatter.ScriptIdentityFormatter = class {
   /**
    * @param {string} mimeType
    * @param {string} content
-   * @param {function(string, !Sources.FormatterSourceMapping)} callback
+   * @param {function(string, !Formatter.FormatterSourceMapping)} callback
    */
   constructor(mimeType, content, callback) {
-    callback(content, new Sources.IdentityFormatterSourceMapping());
+    callback(content, new Formatter.IdentityFormatterSourceMapping());
   }
 };
 
 /**
  * @interface
  */
-Sources.FormatterSourceMapping = function() {};
+Formatter.FormatterSourceMapping = function() {};
 
-Sources.FormatterSourceMapping.prototype = {
+Formatter.FormatterSourceMapping.prototype = {
   /**
    * @param {number} lineNumber
    * @param {number=} columnNumber
@@ -136,10 +137,10 @@
 };
 
 /**
- * @implements {Sources.FormatterSourceMapping}
+ * @implements {Formatter.FormatterSourceMapping}
  * @unrestricted
  */
-Sources.IdentityFormatterSourceMapping = class {
+Formatter.IdentityFormatterSourceMapping = class {
   /**
    * @override
    * @param {number} lineNumber
@@ -162,14 +163,14 @@
 };
 
 /**
- * @implements {Sources.FormatterSourceMapping}
+ * @implements {Formatter.FormatterSourceMapping}
  * @unrestricted
  */
-Sources.FormatterSourceMappingImpl = class {
+Formatter.FormatterSourceMappingImpl = class {
   /**
    * @param {!Array.<number>} originalLineEndings
    * @param {!Array.<number>} formattedLineEndings
-   * @param {!Common.FormatterWorkerPool.FormatMapping} mapping
+   * @param {!Formatter.FormatterWorkerPool.FormatMapping} mapping
    */
   constructor(originalLineEndings, formattedLineEndings, mapping) {
     this._originalLineEndings = originalLineEndings;
@@ -185,10 +186,10 @@
    */
   originalToFormatted(lineNumber, columnNumber) {
     var originalPosition =
-        Sources.Formatter.locationToPosition(this._originalLineEndings, lineNumber, columnNumber || 0);
+        Formatter.Formatter.locationToPosition(this._originalLineEndings, lineNumber, columnNumber || 0);
     var formattedPosition =
         this._convertPosition(this._mapping.original, this._mapping.formatted, originalPosition || 0);
-    return Sources.Formatter.positionToLocation(this._formattedLineEndings, formattedPosition);
+    return Formatter.Formatter.positionToLocation(this._formattedLineEndings, formattedPosition);
   }
 
   /**
@@ -199,9 +200,9 @@
    */
   formattedToOriginal(lineNumber, columnNumber) {
     var formattedPosition =
-        Sources.Formatter.locationToPosition(this._formattedLineEndings, lineNumber, columnNumber || 0);
+        Formatter.Formatter.locationToPosition(this._formattedLineEndings, lineNumber, columnNumber || 0);
     var originalPosition = this._convertPosition(this._mapping.formatted, this._mapping.original, formattedPosition);
-    return Sources.Formatter.positionToLocation(this._originalLineEndings, originalPosition || 0);
+    return Formatter.Formatter.positionToLocation(this._originalLineEndings, originalPosition || 0);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/formatter/module.json b/third_party/WebKit/Source/devtools/front_end/formatter/module.json
new file mode 100644
index 0000000..fb490c5
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/formatter/module.json
@@ -0,0 +1,9 @@
+{
+    "dependencies": [
+        "common"
+    ],
+    "scripts": [
+        "FormatterWorkerPool.js",
+        "ScriptFormatter.js"
+    ]
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/inspector.json b/third_party/WebKit/Source/devtools/front_end/inspector.json
index 956b443e..ad011bc4 100644
--- a/third_party/WebKit/Source/devtools/front_end/inspector.json
+++ b/third_party/WebKit/Source/devtools/front_end/inspector.json
@@ -61,7 +61,8 @@
         { "name": "text_utils", "type": "autostart"},
         { "name": "changes"},
         { "name": "mobile_throttling", "type": "autostart"},
-        { "name": "network_priorities"}
+        { "name": "network_priorities"},
+        { "name": "formatter" }
     ],
 
     "has_html": true
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js
index 770a33f..29add33 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/Main.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -187,7 +187,6 @@
 
     Workspace.fileManager = new Workspace.FileManager();
     Workspace.workspace = new Workspace.Workspace();
-    Common.formatterWorkerPool = new Common.FormatterWorkerPool();
     Persistence.fileSystemMapping = new Persistence.FileSystemMapping(Persistence.isolatedFileSystemManager);
 
     Bindings.networkProjectManager = new Bindings.NetworkProjectManager(SDK.targetManager, Workspace.workspace);
diff --git a/third_party/WebKit/Source/devtools/front_end/network/JSONView.js b/third_party/WebKit/Source/devtools/front_end/network/JSONView.js
index 2b973783..10f7bbb 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/JSONView.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/JSONView.js
@@ -76,7 +76,7 @@
       returnObj = Network.JSONView._extractJSON(/** @type {string} */ (text));
     if (!returnObj)
       return Promise.resolve(/** @type {?Network.ParsedJSON} */ (null));
-    return Common.formatterWorkerPool.parseJSONRelaxed(returnObj.data).then(handleReturnedJSON);
+    return Formatter.formatterWorkerPool().parseJSONRelaxed(returnObj.data).then(handleReturnedJSON);
 
     /**
      * @param {*} data
diff --git a/third_party/WebKit/Source/devtools/front_end/network/module.json b/third_party/WebKit/Source/devtools/front_end/network/module.json
index 711d4c4f..a53b112 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/network/module.json
@@ -143,7 +143,8 @@
         "network_log",
         "product_registry",
         "mobile_throttling",
-        "network_priorities"
+        "network_priorities",
+        "formatter"
     ],
     "scripts": [
         "BlockedURLsPane.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/sass/SASSSupport.js b/third_party/WebKit/Source/devtools/front_end/sass/SASSSupport.js
index 4ca67a7c..9be57c8 100644
--- a/third_party/WebKit/Source/devtools/front_end/sass/SASSSupport.js
+++ b/third_party/WebKit/Source/devtools/front_end/sass/SASSSupport.js
@@ -12,10 +12,10 @@
   var text = new TextUtils.Text(content);
   var document = new Sass.SASSSupport.ASTDocument(url, text);
 
-  return Common.formatterWorkerPool.parseSCSS(content).then(onParsed);
+  return Formatter.formatterWorkerPool().parseSCSS(content).then(onParsed);
 
   /**
-   * @param {!Array<!Common.FormatterWorkerPool.SCSSRule>} rulePayloads
+   * @param {!Array<!Formatter.FormatterWorkerPool.SCSSRule>} rulePayloads
    * @return {!Sass.SASSSupport.AST}
    */
   function onParsed(rulePayloads) {
diff --git a/third_party/WebKit/Source/devtools/front_end/sass/module.json b/third_party/WebKit/Source/devtools/front_end/sass/module.json
index 6777bc4..2fd1e16 100644
--- a/third_party/WebKit/Source/devtools/front_end/sass/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/sass/module.json
@@ -1,5 +1,11 @@
 {
-    "dependencies": ["platform", "common", "diff", "sdk"],
+    "dependencies": [
+        "platform",
+        "common",
+        "diff",
+        "sdk",
+        "formatter"
+    ],
     "scripts": [
         "SASSSupport.js",
         "ASTService.js",
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js
index d3a4668..7f2675a 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/DOMModel.js
@@ -863,17 +863,9 @@
 
   /**
    * @param {string=} objectGroup
-   * @param {function(?SDK.RemoteObject)=} callback
-   */
-  resolveToObject(objectGroup, callback) {
-    this.resolveToObjectPromise(objectGroup).then(object => callback && callback(object));
-  }
-
-  /**
-   * @param {string=} objectGroup
    * @return {!Promise<?SDK.RemoteObject>}
    */
-  async resolveToObjectPromise(objectGroup) {
+  async resolveToObject(objectGroup) {
     var object = await this._agent.resolveNode(this.id, objectGroup);
     return object && this._domModel._runtimeModel.createRemoteObject(object);
   }
@@ -912,6 +904,23 @@
       node = null;
     return node;
   }
+
+  async scrollIntoView() {
+    var node = this.enclosingElementOrSelf();
+    var object = await node.resolveToObject('');
+    if (object)
+      object.callFunction(scrollIntoView);
+    object.release();
+    node.highlightForTwoSeconds();
+
+    /**
+     * @suppressReceiverCheck
+     * @this {!Element}
+     */
+    function scrollIntoView() {
+      this.scrollIntoViewIfNeeded(true);
+    }
+  }
 };
 
 /**
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/InplaceFormatterEditorAction.js b/third_party/WebKit/Source/devtools/front_end/sources/InplaceFormatterEditorAction.js
index d4421357..03b450e1 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/InplaceFormatterEditorAction.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/InplaceFormatterEditorAction.js
@@ -83,13 +83,13 @@
      */
     function contentLoaded(content) {
       var highlighterType = uiSourceCode.mimeType();
-      Sources.Formatter.format(uiSourceCode.contentType(), highlighterType, content || '', innerCallback.bind(this));
+      Formatter.Formatter.format(uiSourceCode.contentType(), highlighterType, content || '', innerCallback.bind(this));
     }
 
     /**
      * @this {Sources.InplaceFormatterEditorAction}
      * @param {string} formattedContent
-     * @param {!Sources.FormatterSourceMapping} formatterMapping
+     * @param {!Formatter.FormatterSourceMapping} formatterMapping
      */
     function innerCallback(formattedContent, formatterMapping) {
       if (uiSourceCode.workingCopy() === formattedContent)
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/OutlineQuickOpen.js b/third_party/WebKit/Source/devtools/front_end/sources/OutlineQuickOpen.js
index 90688271..07b4887 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/OutlineQuickOpen.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/OutlineQuickOpen.js
@@ -21,7 +21,7 @@
 
     var uiSourceCode = this._currentUISourceCode();
     if (uiSourceCode) {
-      this._active = Common.formatterWorkerPool.outlineForMimetype(
+      this._active = Formatter.formatterWorkerPool().outlineForMimetype(
           uiSourceCode.workingCopy(), uiSourceCode.contentType().canonicalMimeType(),
           this._didBuildOutlineChunk.bind(this));
     }
@@ -29,7 +29,7 @@
 
   /**
    * @param {boolean} isLastChunk
-   * @param {!Array<!Common.FormatterWorkerPool.OutlineItem>} items
+   * @param {!Array<!Formatter.FormatterWorkerPool.OutlineItem>} items
    */
   _didBuildOutlineChunk(isLastChunk, items) {
     this._items.push(...items);
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/SourceFormatter.js b/third_party/WebKit/Source/devtools/front_end/sources/SourceFormatter.js
index 1faab4b..7912234 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/SourceFormatter.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/SourceFormatter.js
@@ -6,7 +6,7 @@
   /**
    * @param {!Workspace.UISourceCode} originalSourceCode
    * @param {!Workspace.UISourceCode} formattedSourceCode
-   * @param {!Sources.FormatterSourceMapping} mapping
+   * @param {!Formatter.FormatterSourceMapping} mapping
    */
   constructor(originalSourceCode, formattedSourceCode, mapping) {
     this.originalSourceCode = originalSourceCode;
@@ -102,13 +102,14 @@
     this._formattedSourceCodes.set(uiSourceCode, {promise: resultPromise, formatData: null});
     var content = await uiSourceCode.requestContent();
     // ------------ ASYNC ------------
-    Sources.Formatter.format(uiSourceCode.contentType(), uiSourceCode.mimeType(), content || '', formatDone.bind(this));
+    Formatter.Formatter.format(
+        uiSourceCode.contentType(), uiSourceCode.mimeType(), content || '', formatDone.bind(this));
     return resultPromise;
 
     /**
      * @this Sources.SourceFormatter
      * @param {string} formattedContent
-     * @param {!Sources.FormatterSourceMapping} formatterMapping
+     * @param {!Formatter.FormatterSourceMapping} formatterMapping
      */
     function formatDone(formattedContent, formatterMapping) {
       var cacheEntry = this._formattedSourceCodes.get(uiSourceCode);
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js b/third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js
index 4c00b54c..de48d7d4 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/SourceMapNamesResolver.js
@@ -52,7 +52,8 @@
     var scopeText = text.extract(scopeRange);
     var scopeStart = text.toSourceRange(scopeRange).offset;
     var prefix = 'function fui';
-    return Common.formatterWorkerPool.javaScriptIdentifiers(prefix + scopeText)
+    return Formatter.formatterWorkerPool()
+        .javaScriptIdentifiers(prefix + scopeText)
         .then(onIdentifiers.bind(null, text, scopeStart, prefix));
   }
 
@@ -279,7 +280,7 @@
     var originalText = text.extract(textRange);
     if (!originalText)
       return Promise.resolve('');
-    return Common.formatterWorkerPool.evaluatableJavaScriptSubstring(originalText);
+    return Formatter.formatterWorkerPool().evaluatableJavaScriptSubstring(originalText);
   }
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/module.json b/third_party/WebKit/Source/devtools/front_end/sources/module.json
index e7abff6..03cee9f 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/module.json
+++ b/third_party/WebKit/Source/devtools/front_end/sources/module.json
@@ -617,7 +617,8 @@
         "inline_editor",
         "color_picker",
         "event_listeners",
-        "object_ui"
+        "object_ui",
+        "formatter"
     ],
     "scripts": [
         "AddSourceMapURLDialog.js",
@@ -643,7 +644,6 @@
         "ThreadsSidebarPane.js",
         "ScriptFormatterEditorAction.js",
         "InplaceFormatterEditorAction.js",
-        "ScriptFormatter.js",
         "SourceFormatter.js",
         "OpenFileQuickOpen.js",
         "SourcesView.js",
diff --git a/third_party/WebKit/Source/devtools/scripts/extract_module/extract_module.js b/third_party/WebKit/Source/devtools/scripts/extract_module/extract_module.js
index f63f40f..9cb78c37 100644
--- a/third_party/WebKit/Source/devtools/scripts/extract_module/extract_module.js
+++ b/third_party/WebKit/Source/devtools/scripts/extract_module/extract_module.js
@@ -39,9 +39,8 @@
  * If moving to an existing module:
  * {file: 'ui/SomeFile.js', existing: 'common'}
  */
-const JS_FILES_MAPPING = [
-  {file: 'mobile_throttling/NetworkPriorities.js', new: 'network_priorities'},
-];
+const JS_FILES_MAPPING =
+    [{file: 'common/FormatterWorkerPool.js', new: 'formatter'}, {file: 'sources/ScriptFormatter.js', new: 'formatter'}];
 
 /**
  * List all new modules here:
@@ -53,9 +52,9 @@
  * }
  */
 const MODULE_MAPPING = {
-  network_priorities: {
-    dependencies: ['protocol', 'common'],
-    dependents: ['network', 'timeline'],
+  formatter: {
+    dependencies: ['common'],
+    dependents: ['sources', 'audits', 'network', 'sass'],
     applications: ['inspector.json'],
     autostart: false,
   },
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
index 09c44ee..0bc292f 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -400,12 +400,6 @@
   return content_window_;
 }
 
-void DesktopNativeWidgetAura::SetNativeWindowProperty(const char* name,
-                                                      void* value) {
-  if (content_window_)
-    content_window_->SetNativeWindowProperty(name, value);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // DesktopNativeWidgetAura, internal::NativeWidgetPrivate implementation:
 
@@ -610,6 +604,12 @@
   drop_helper_->ResetTargetViewIfEquals(view);
 }
 
+void DesktopNativeWidgetAura::SetNativeWindowProperty(const char* name,
+                                                      void* value) {
+  if (content_window_)
+    content_window_->SetNativeWindowProperty(name, value);
+}
+
 void* DesktopNativeWidgetAura::GetNativeWindowProperty(const char* name) const {
   return content_window_ ?
       content_window_->GetNativeWindowProperty(name) : NULL;
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
index 6d57e5e..01faa59 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -99,7 +99,6 @@
 
   // Overridden from internal::NativeWidgetPrivate:
   gfx::NativeWindow GetNativeWindow() const override;
-  void SetNativeWindowProperty(const char* name, void* value) override;
 
  protected:
   // Overridden from internal::NativeWidgetPrivate:
@@ -117,6 +116,7 @@
   const ui::Layer* GetLayer() const override;
   void ReorderNativeViews() override;
   void ViewRemoved(View* view) override;
+  void SetNativeWindowProperty(const char* name, void* value) override;
   void* GetNativeWindowProperty(const char* name) const override;
   TooltipManager* GetTooltipManager() const override;
   void SetCapture() override;