diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index f958dc8e..446acfb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -423,8 +423,7 @@
         intent.setData(contentUri);
         intent.putExtra(CustomTabIntentDataProvider.EXTRA_IS_MEDIA_VIEWER, true);
         intent.putExtra(CustomTabIntentDataProvider.EXTRA_MEDIA_VIEWER_URL, fileUri.toString());
-        intent.putExtra(CustomTabIntentDataProvider.EXTRA_ENABLE_EMBEDDED_MEDIA_EXPERIENCE,
-                isMimeTypeVideo(mimeType));
+        intent.putExtra(CustomTabIntentDataProvider.EXTRA_ENABLE_EMBEDDED_MEDIA_EXPERIENCE, true);
         intent.putExtra(
                 CustomTabIntentDataProvider.EXTRA_INITIAL_BACKGROUND_COLOR, mediaColor);
         intent.putExtra(
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
index fa072b9..f06c6169 100644
--- a/content/browser/media/media_internals.cc
+++ b/content/browser/media/media_internals.cc
@@ -297,6 +297,7 @@
     base::TimeDelta src_watch_time = media::kNoTimestamp;
     base::TimeDelta ac_watch_time = media::kNoTimestamp;
     base::TimeDelta battery_watch_time = media::kNoTimestamp;
+    base::TimeDelta embedded_experience_watch_time = media::kNoTimestamp;
   };
 
   struct PipelineInfo {
@@ -358,6 +359,8 @@
         MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioVideoMse, mse_watch_time);
         MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioVideoEme, eme_watch_time);
         MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioVideoSrc, src_watch_time);
+        MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioVideoEmbeddedExperience,
+                                embedded_experience_watch_time);
       } else {
         DCHECK_EQ(finalize_type, FinalizeType::POWER_ONLY);
       }
@@ -369,6 +372,8 @@
         MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioMse, mse_watch_time);
         MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioEme, eme_watch_time);
         MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioSrc, src_watch_time);
+        MAYBE_RECORD_WATCH_TIME(kWatchTimeAudioEmbeddedExperience,
+                                embedded_experience_watch_time);
       } else {
         DCHECK_EQ(finalize_type, FinalizeType::POWER_ONLY);
       }
@@ -467,6 +472,9 @@
                          &wti.battery_watch_time);
       MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioAc,
                          &wti.ac_watch_time);
+      MaybeSaveWatchTime(event,
+                         media::MediaLog::kWatchTimeAudioEmbeddedExperience,
+                         &wti.embedded_experience_watch_time);
 
       // Save audio+video watch time information.
       MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioVideoAll,
@@ -481,6 +489,9 @@
                          &wti.battery_watch_time);
       MaybeSaveWatchTime(event, media::MediaLog::kWatchTimeAudioVideoAc,
                          &wti.ac_watch_time);
+      MaybeSaveWatchTime(
+          event, media::MediaLog::kWatchTimeAudioVideoEmbeddedExperience,
+          &wti.embedded_experience_watch_time);
 
       if (event.params.HasKey(media::MediaLog::kWatchTimeFinalize)) {
         bool should_finalize;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 5a7b201..aa76a1ed 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2810,9 +2810,12 @@
   scoped_refptr<media::MediaLog> media_log(
       new RenderMediaLog(url::Origin(frame_->getSecurityOrigin()).GetURL()));
 
+  bool embedded_media_experience_enabled = false;
 #if defined(OS_ANDROID)
   if (!UseMediaPlayerRenderer(url) && !media_surface_manager_)
     media_surface_manager_ = new RendererSurfaceViewManager(this);
+  embedded_media_experience_enabled =
+      GetWebkitPreferences().embedded_media_experience_enabled;
 #endif  // defined(OS_ANDROID)
 
 #if BUILDFLAG(ENABLE_MEDIA_REMOTING)
@@ -2846,7 +2849,8 @@
       // in the renderer process. See https://crbug.com/681160.
       GetWebkitPreferences().max_keyframe_distance_to_disable_background_video,
       GetWebkitPreferences().enable_instant_source_buffer_gc,
-      GetContentClient()->renderer()->AllowMediaSuspend());
+      GetContentClient()->renderer()->AllowMediaSuspend(),
+      embedded_media_experience_enabled);
 
   bool use_fallback_path = false;
 #if defined(OS_ANDROID)
diff --git a/media/base/media_log.cc b/media/base/media_log.cc
index 52bf031..8aa0db34 100644
--- a/media/base/media_log.cc
+++ b/media/base/media_log.cc
@@ -28,6 +28,8 @@
 const char MediaLog::kWatchTimeAudioVideoBattery[] =
     "Media.WatchTime.AudioVideo.Battery";
 const char MediaLog::kWatchTimeAudioVideoAc[] = "Media.WatchTime.AudioVideo.AC";
+const char MediaLog::kWatchTimeAudioVideoEmbeddedExperience[] =
+    "Media.WatchTime.AudioVideo.EmbeddedExperience";
 
 // Audio only "watch time" metrics.
 const char MediaLog::kWatchTimeAudioAll[] = "Media.WatchTime.Audio.All";
@@ -36,6 +38,8 @@
 const char MediaLog::kWatchTimeAudioSrc[] = "Media.WatchTime.Audio.SRC";
 const char MediaLog::kWatchTimeAudioBattery[] = "Media.WatchTime.Audio.Battery";
 const char MediaLog::kWatchTimeAudioAc[] = "Media.WatchTime.Audio.AC";
+const char MediaLog::kWatchTimeAudioEmbeddedExperience[] =
+    "Media.WatchTime.Audio.EmbeddedExperience";
 
 const char MediaLog::kWatchTimeFinalize[] = "FinalizeWatchTime";
 const char MediaLog::kWatchTimeFinalizePower[] = "FinalizePowerWatchTime";
diff --git a/media/base/media_log.h b/media/base/media_log.h
index 6ef04e1..eba57bc 100644
--- a/media/base/media_log.h
+++ b/media/base/media_log.h
@@ -96,12 +96,14 @@
   static const char kWatchTimeAudioSrc[];
   static const char kWatchTimeAudioBattery[];
   static const char kWatchTimeAudioAc[];
+  static const char kWatchTimeAudioEmbeddedExperience[];
   static const char kWatchTimeAudioVideoAll[];
   static const char kWatchTimeAudioVideoMse[];
   static const char kWatchTimeAudioVideoEme[];
   static const char kWatchTimeAudioVideoSrc[];
   static const char kWatchTimeAudioVideoBattery[];
   static const char kWatchTimeAudioVideoAc[];
+  static const char kWatchTimeAudioVideoEmbeddedExperience[];
 
   // Markers which signify the watch time should be finalized immediately.
   static const char kWatchTimeFinalize[];
diff --git a/media/blink/watch_time_reporter.cc b/media/blink/watch_time_reporter.cc
index e9872cb..06e9764 100644
--- a/media/blink/watch_time_reporter.cc
+++ b/media/blink/watch_time_reporter.cc
@@ -26,6 +26,7 @@
                                      bool has_video,
                                      bool is_mse,
                                      bool is_encrypted,
+                                     bool is_embedded_media_experience_enabled,
                                      scoped_refptr<MediaLog> media_log,
                                      const gfx::Size& initial_video_size,
                                      const GetMediaTimeCB& get_media_time_cb)
@@ -33,6 +34,8 @@
       has_video_(has_video),
       is_mse_(is_mse),
       is_encrypted_(is_encrypted),
+      is_embedded_media_experience_enabled_(
+          is_embedded_media_experience_enabled),
       media_log_(std::move(media_log)),
       initial_video_size_(initial_video_size),
       get_media_time_cb_(get_media_time_cb) {
@@ -227,6 +230,13 @@
           elapsed.InSecondsF());
     }
 
+    if (is_embedded_media_experience_enabled_) {
+      log_event->params.SetDoubleWithoutPathExpansion(
+          has_video_ ? MediaLog::kWatchTimeAudioVideoEmbeddedExperience
+                     : MediaLog::kWatchTimeAudioEmbeddedExperience,
+          elapsed.InSecondsF());
+    }
+
     // Record watch time using the last known value for |is_on_battery_power_|;
     // if there's a |pending_power_change_| use that to accurately finalize the
     // last bits of time in the previous bucket.
diff --git a/media/blink/watch_time_reporter.h b/media/blink/watch_time_reporter.h
index d0ded92..81ea7bd 100644
--- a/media/blink/watch_time_reporter.h
+++ b/media/blink/watch_time_reporter.h
@@ -60,6 +60,7 @@
                     bool has_video,
                     bool is_mse,
                     bool is_encrypted,
+                    bool is_embedded_media_experience_enabled,
                     scoped_refptr<MediaLog> media_log,
                     const gfx::Size& initial_video_size,
                     const GetMediaTimeCB& get_media_time_cb);
@@ -116,6 +117,7 @@
   const bool has_video_;
   const bool is_mse_;
   const bool is_encrypted_;
+  const bool is_embedded_media_experience_enabled_;
   scoped_refptr<MediaLog> media_log_;
   const gfx::Size initial_video_size_;
   const GetMediaTimeCB get_media_time_cb_;
diff --git a/media/blink/watch_time_reporter_unittest.cc b/media/blink/watch_time_reporter_unittest.cc
index 380ccf6..85e9b709 100644
--- a/media/blink/watch_time_reporter_unittest.cc
+++ b/media/blink/watch_time_reporter_unittest.cc
@@ -81,7 +81,7 @@
                   bool is_encrypted,
                   const gfx::Size& initial_video_size) {
     wtr_.reset(new WatchTimeReporter(
-        has_audio, has_video_, is_mse, is_encrypted, media_log_,
+        has_audio, has_video_, is_mse, is_encrypted, false, media_log_,
         initial_video_size,
         base::Bind(&WatchTimeReporterTest::GetCurrentMediaTime,
                    base::Unretained(this))));
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index a81ad7c..813a643f 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -239,7 +239,9 @@
       max_keyframe_distance_to_disable_background_video_(
           params.max_keyframe_distance_to_disable_background_video()),
       enable_instant_source_buffer_gc_(
-          params.enable_instant_source_buffer_gc()) {
+          params.enable_instant_source_buffer_gc()),
+      embedded_media_experience_enabled_(
+          params.embedded_media_experience_enabled()) {
   DVLOG(1) << __func__;
   DCHECK(!adjust_allocated_memory_cb_.is_null());
   DCHECK(renderer_factory_);
@@ -2124,10 +2126,11 @@
 
 void WebMediaPlayerImpl::CreateWatchTimeReporter() {
   // Create the watch time reporter and synchronize its initial state.
-  watch_time_reporter_.reset(new WatchTimeReporter(
-      hasAudio(), hasVideo(), !!chunk_demuxer_, is_encrypted_, media_log_,
-      pipeline_metadata_.natural_size,
-      base::Bind(&GetCurrentTimeInternal, this)));
+  watch_time_reporter_.reset(
+      new WatchTimeReporter(hasAudio(), hasVideo(), !!chunk_demuxer_,
+                            is_encrypted_, embedded_media_experience_enabled_,
+                            media_log_, pipeline_metadata_.natural_size,
+                            base::Bind(&GetCurrentTimeInternal, this)));
   watch_time_reporter_->OnVolumeChange(volume_);
   if (delegate_->IsFrameHidden())
     watch_time_reporter_->OnHidden();
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index e29603d..308da41 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -707,6 +707,9 @@
   // the background. Affects the value of ShouldPauseVideoWhenHidden().
   bool video_locked_when_paused_when_hidden_ = false;
 
+  // Whether embedded media experience is currently enabled.
+  bool embedded_media_experience_enabled_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImpl);
 };
 
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index 5eb1a8d7..a040b02 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -213,7 +213,7 @@
             media_thread_.task_runner(), message_loop_.task_runner(),
             message_loop_.task_runner(), WebMediaPlayerParams::Context3DCB(),
             base::Bind(&OnAdjustAllocatedMemory), nullptr, nullptr, nullptr,
-            base::TimeDelta::FromSeconds(10), false, allow_suspend));
+            base::TimeDelta::FromSeconds(10), false, allow_suspend, false));
   }
 
   ~WebMediaPlayerImplTest() override {
diff --git a/media/blink/webmediaplayer_params.cc b/media/blink/webmediaplayer_params.cc
index ebace61..a89f693 100644
--- a/media/blink/webmediaplayer_params.cc
+++ b/media/blink/webmediaplayer_params.cc
@@ -25,7 +25,8 @@
     base::WeakPtr<MediaObserver> media_observer,
     base::TimeDelta max_keyframe_distance_to_disable_background_video,
     bool enable_instant_source_buffer_gc,
-    bool allow_suspend)
+    bool allow_suspend,
+    bool embedded_media_experience_enabled)
     : defer_load_cb_(defer_load_cb),
       audio_renderer_sink_(audio_renderer_sink),
       media_log_(media_log),
@@ -40,7 +41,8 @@
       max_keyframe_distance_to_disable_background_video_(
           max_keyframe_distance_to_disable_background_video),
       enable_instant_source_buffer_gc_(enable_instant_source_buffer_gc),
-      allow_suspend_(allow_suspend) {}
+      allow_suspend_(allow_suspend),
+      embedded_media_experience_enabled_(embedded_media_experience_enabled) {}
 
 WebMediaPlayerParams::~WebMediaPlayerParams() {}
 
diff --git a/media/blink/webmediaplayer_params.h b/media/blink/webmediaplayer_params.h
index 00aa774..b16ee3a 100644
--- a/media/blink/webmediaplayer_params.h
+++ b/media/blink/webmediaplayer_params.h
@@ -61,7 +61,8 @@
       base::WeakPtr<MediaObserver> media_observer,
       base::TimeDelta max_keyframe_distance_to_disable_background_video,
       bool enable_instant_source_buffer_gc,
-      bool allow_suspend);
+      bool allow_suspend,
+      bool embedded_media_experience_enabled);
 
   ~WebMediaPlayerParams();
 
@@ -115,6 +116,10 @@
 
   bool allow_suspend() const { return allow_suspend_; }
 
+  bool embedded_media_experience_enabled() const {
+    return embedded_media_experience_enabled_;
+  }
+
  private:
   DeferLoadCB defer_load_cb_;
   scoped_refptr<SwitchableAudioRendererSink> audio_renderer_sink_;
@@ -131,6 +136,7 @@
   base::TimeDelta max_keyframe_distance_to_disable_background_video_;
   bool enable_instant_source_buffer_gc_;
   const bool allow_suspend_;
+  const bool embedded_media_experience_enabled_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(WebMediaPlayerParams);
 };
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn
index 8bb2e24..71bcc321 100644
--- a/services/ui/ws/BUILD.gn
+++ b/services/ui/ws/BUILD.gn
@@ -29,6 +29,8 @@
     "display.h",
     "display_binding.cc",
     "display_binding.h",
+    "display_client_compositor_frame_sink.cc",
+    "display_client_compositor_frame_sink.h",
     "display_manager.cc",
     "display_manager.h",
     "drag_controller.cc",
diff --git a/services/ui/ws/display_client_compositor_frame_sink.cc b/services/ui/ws/display_client_compositor_frame_sink.cc
new file mode 100644
index 0000000..5d719fa
--- /dev/null
+++ b/services/ui/ws/display_client_compositor_frame_sink.cc
@@ -0,0 +1,106 @@
+// 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 "services/ui/ws/display_client_compositor_frame_sink.h"
+
+#include "base/threading/thread_checker.h"
+#include "cc/output/compositor_frame_sink_client.h"
+
+namespace ui {
+namespace ws {
+
+DisplayClientCompositorFrameSink::DisplayClientCompositorFrameSink(
+    const cc::FrameSinkId& frame_sink_id,
+    cc::mojom::MojoCompositorFrameSinkAssociatedPtr compositor_frame_sink,
+    cc::mojom::DisplayPrivateAssociatedPtr display_private,
+    cc::mojom::MojoCompositorFrameSinkClientRequest client_request)
+    : cc::CompositorFrameSink(nullptr, nullptr, nullptr, nullptr),
+      compositor_frame_sink_(std::move(compositor_frame_sink)),
+      client_binding_(this, std::move(client_request)),
+      display_private_(std::move(display_private)),
+      frame_sink_id_(frame_sink_id) {}
+
+DisplayClientCompositorFrameSink::~DisplayClientCompositorFrameSink() {}
+
+bool DisplayClientCompositorFrameSink::BindToClient(
+    cc::CompositorFrameSinkClient* client) {
+  if (!cc::CompositorFrameSink::BindToClient(client))
+    return false;
+  DCHECK(!thread_checker_);
+  thread_checker_ = base::MakeUnique<base::ThreadChecker>();
+
+  begin_frame_source_ = base::MakeUnique<cc::ExternalBeginFrameSource>(this);
+
+  client->SetBeginFrameSource(begin_frame_source_.get());
+  return true;
+}
+
+void DisplayClientCompositorFrameSink::DetachFromClient() {
+  client_->SetBeginFrameSource(nullptr);
+  begin_frame_source_.reset();
+  cc::CompositorFrameSink::DetachFromClient();
+}
+
+void DisplayClientCompositorFrameSink::SubmitCompositorFrame(
+    cc::CompositorFrame frame) {
+  DCHECK(thread_checker_->CalledOnValidThread());
+  if (!compositor_frame_sink_)
+    return;
+
+  gfx::Size frame_size = last_submitted_frame_size_;
+  if (!frame.render_pass_list.empty())
+    frame_size = frame.render_pass_list.back()->output_rect.size();
+
+  if (!local_surface_id_.is_valid() ||
+      frame_size != last_submitted_frame_size_) {
+    local_surface_id_ = id_allocator_.GenerateId();
+    display_private_->ResizeDisplay(frame_size);
+  }
+  display_private_->SetLocalSurfaceId(local_surface_id_,
+                                      frame.metadata.device_scale_factor);
+  compositor_frame_sink_->SubmitCompositorFrame(local_surface_id_,
+                                                std::move(frame));
+  last_submitted_frame_size_ = frame_size;
+}
+
+void DisplayClientCompositorFrameSink::DidReceiveCompositorFrameAck() {
+  DCHECK(thread_checker_->CalledOnValidThread());
+  if (!client_)
+    return;
+  client_->DidReceiveCompositorFrameAck();
+}
+
+void DisplayClientCompositorFrameSink::OnBeginFrame(
+    const cc::BeginFrameArgs& begin_frame_args) {
+  DCHECK(thread_checker_->CalledOnValidThread());
+  begin_frame_source_->OnBeginFrame(begin_frame_args);
+}
+
+void DisplayClientCompositorFrameSink::ReclaimResources(
+    const cc::ReturnedResourceArray& resources) {
+  DCHECK(thread_checker_->CalledOnValidThread());
+  if (!client_)
+    return;
+  client_->ReclaimResources(resources);
+}
+
+void DisplayClientCompositorFrameSink::WillDrawSurface(
+    const cc::LocalSurfaceId& local_surface_id,
+    const gfx::Rect& damage_rect) {
+  // TODO(fsamuel, staraz): Implement this.
+}
+
+void DisplayClientCompositorFrameSink::OnNeedsBeginFrames(
+    bool needs_begin_frames) {
+  DCHECK(thread_checker_->CalledOnValidThread());
+  compositor_frame_sink_->SetNeedsBeginFrame(needs_begin_frames);
+}
+
+void DisplayClientCompositorFrameSink::OnDidFinishFrame(
+    const cc::BeginFrameAck& ack) {
+  // TODO(eseckler): Pass on the ack to compositor_frame_sink_.
+}
+
+}  // namespace ws
+}  // namespace ui
diff --git a/services/ui/ws/display_client_compositor_frame_sink.h b/services/ui/ws/display_client_compositor_frame_sink.h
new file mode 100644
index 0000000..90846896
--- /dev/null
+++ b/services/ui/ws/display_client_compositor_frame_sink.h
@@ -0,0 +1,71 @@
+// 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 SERVICES_UI_PUBLIC_CPP_DISPLAY_CLIENT_COMPOSITOR_FRAME_SINK_H_
+#define SERVICES_UI_PUBLIC_CPP_DISPLAY_CLIENT_COMPOSITOR_FRAME_SINK_H_
+
+#include "cc/ipc/display_compositor.mojom.h"
+#include "cc/ipc/mojo_compositor_frame_sink.mojom.h"
+#include "cc/output/compositor_frame_sink.h"
+#include "cc/scheduler/begin_frame_source.h"
+#include "cc/surfaces/local_surface_id.h"
+#include "cc/surfaces/local_surface_id_allocator.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace base {
+class ThreadChecker;
+}  // namespace base
+
+namespace ui {
+namespace ws {
+
+class DisplayClientCompositorFrameSink
+    : public cc::CompositorFrameSink,
+      public cc::mojom::MojoCompositorFrameSinkClient,
+      public cc::ExternalBeginFrameSourceClient {
+ public:
+  DisplayClientCompositorFrameSink(
+      const cc::FrameSinkId& frame_sink_id,
+      cc::mojom::MojoCompositorFrameSinkAssociatedPtr compositor_frame_sink,
+      cc::mojom::DisplayPrivateAssociatedPtr display_private,
+      cc::mojom::MojoCompositorFrameSinkClientRequest client_request);
+
+  ~DisplayClientCompositorFrameSink() override;
+
+  // cc::CompositorFrameSink implementation:
+  bool BindToClient(cc::CompositorFrameSinkClient* client) override;
+  void DetachFromClient() override;
+  void SubmitCompositorFrame(cc::CompositorFrame frame) override;
+
+ private:
+  // cc::mojom::MojoCompositorFrameSinkClient implementation:
+  void DidReceiveCompositorFrameAck() override;
+  void OnBeginFrame(const cc::BeginFrameArgs& begin_frame_args) override;
+  void ReclaimResources(const cc::ReturnedResourceArray& resources) override;
+  void WillDrawSurface(const cc::LocalSurfaceId& local_surface_id,
+                       const gfx::Rect& damage_rect) override;
+
+  // cc::ExternalBeginFrameSourceClient implementation:
+  void OnNeedsBeginFrames(bool needs_begin_frame) override;
+  void OnDidFinishFrame(const cc::BeginFrameAck& ack) override;
+
+  gfx::Size last_submitted_frame_size_;
+  cc::LocalSurfaceId local_surface_id_;
+  cc::LocalSurfaceIdAllocator id_allocator_;
+  cc::mojom::MojoCompositorFrameSinkClientRequest client_request_;
+  cc::mojom::MojoCompositorFrameSinkAssociatedPtr compositor_frame_sink_;
+  mojo::Binding<cc::mojom::MojoCompositorFrameSinkClient> client_binding_;
+  cc::mojom::DisplayPrivateAssociatedPtr display_private_;
+  std::unique_ptr<base::ThreadChecker> thread_checker_;
+  std::unique_ptr<cc::ExternalBeginFrameSource> begin_frame_source_;
+  const cc::FrameSinkId frame_sink_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplayClientCompositorFrameSink);
+};
+
+}  // namspace ws
+}  // namespace ui
+
+#endif  // SERVICES_UI_PUBLIC_CPP_DISPLAY_CLIENT_COMPOSITOR_FRAME_SINK_H_
diff --git a/services/ui/ws/frame_generator.cc b/services/ui/ws/frame_generator.cc
index b1574c4..30b17f3 100644
--- a/services/ui/ws/frame_generator.cc
+++ b/services/ui/ws/frame_generator.cc
@@ -7,44 +7,40 @@
 #include <utility>
 #include <vector>
 
-#include "base/containers/adapters.h"
 #include "cc/output/compositor_frame.h"
+#include "cc/output/compositor_frame_sink.h"
 #include "cc/quads/render_pass.h"
 #include "cc/quads/render_pass_draw_quad.h"
 #include "cc/quads/shared_quad_state.h"
 #include "cc/quads/surface_draw_quad.h"
 #include "services/ui/ws/frame_generator_delegate.h"
 #include "services/ui/ws/server_window.h"
-#include "services/ui/ws/server_window_compositor_frame_sink_manager.h"
-#include "services/ui/ws/server_window_delegate.h"
 
 namespace ui {
 
 namespace ws {
 
-FrameGenerator::FrameGenerator(FrameGeneratorDelegate* delegate,
-                               ServerWindow* root_window,
-                               gfx::AcceleratedWidget widget)
-    : delegate_(delegate), root_window_(root_window), binding_(this) {
+FrameGenerator::FrameGenerator(
+    FrameGeneratorDelegate* delegate,
+    ServerWindow* root_window,
+    std::unique_ptr<cc::CompositorFrameSink> compositor_frame_sink)
+    : delegate_(delegate),
+      root_window_(root_window),
+      compositor_frame_sink_(std::move(compositor_frame_sink)) {
   DCHECK(delegate_);
-  DCHECK_NE(gfx::kNullAcceleratedWidget, widget);
-  cc::mojom::MojoCompositorFrameSinkAssociatedRequest sink_request =
-      mojo::MakeRequest(&compositor_frame_sink_);
-  cc::mojom::DisplayPrivateAssociatedRequest display_request =
-      mojo::MakeRequest(&display_private_);
-  root_window_->CreateRootCompositorFrameSink(
-      widget, std::move(sink_request), binding_.CreateInterfacePtrAndBind(),
-      std::move(display_request));
+  compositor_frame_sink_->BindToClient(this);
 }
 
-FrameGenerator::~FrameGenerator() = default;
+FrameGenerator::~FrameGenerator() {
+  compositor_frame_sink_->DetachFromClient();
+}
 
 void FrameGenerator::SetDeviceScaleFactor(float device_scale_factor) {
   if (device_scale_factor_ == device_scale_factor)
     return;
   device_scale_factor_ = device_scale_factor;
   if (window_manager_surface_info_.is_valid())
-    compositor_frame_sink_->SetNeedsBeginFrame(true);
+    SetNeedsBeginFrame(true);
 }
 
 void FrameGenerator::OnSurfaceCreated(const cc::SurfaceInfo& surface_info) {
@@ -54,39 +50,23 @@
   // changing is handled immediately after the CompositorFrame is submitted.
   if (surface_info != window_manager_surface_info_) {
     window_manager_surface_info_ = surface_info;
-    compositor_frame_sink_->SetNeedsBeginFrame(true);
+    SetNeedsBeginFrame(true);
   }
 }
 
 void FrameGenerator::OnWindowDamaged() {
   if (window_manager_surface_info_.is_valid())
-    compositor_frame_sink_->SetNeedsBeginFrame(true);
+    SetNeedsBeginFrame(true);
 }
 
-void FrameGenerator::DidReceiveCompositorFrameAck() {}
+void FrameGenerator::SetBeginFrameSource(cc::BeginFrameSource* source) {
+  if (begin_frame_source_ && observing_begin_frames_)
+    begin_frame_source_->RemoveObserver(this);
 
-void FrameGenerator::OnBeginFrame(const cc::BeginFrameArgs& begin_frame_arags) {
-  if (!root_window_->visible())
-    return;
+  begin_frame_source_ = source;
 
-  // TODO(fsamuel): We should add a trace for generating a top level frame.
-  cc::CompositorFrame frame(GenerateCompositorFrame(root_window_->bounds()));
-
-  gfx::Size frame_size = last_submitted_frame_size_;
-  if (!frame.render_pass_list.empty())
-    frame_size = frame.render_pass_list.back()->output_rect.size();
-
-  if (!local_surface_id_.is_valid() ||
-      frame_size != last_submitted_frame_size_) {
-    local_surface_id_ = id_allocator_.GenerateId();
-    display_private_->ResizeDisplay(frame_size);
-  }
-
-  display_private_->SetLocalSurfaceId(local_surface_id_, device_scale_factor_);
-  compositor_frame_sink_->SubmitCompositorFrame(local_surface_id_,
-                                                std::move(frame));
-  compositor_frame_sink_->SetNeedsBeginFrame(false);
-  last_submitted_frame_size_ = frame_size;
+  if (begin_frame_source_ && observing_begin_frames_)
+    begin_frame_source_->AddObserver(this);
 }
 
 void FrameGenerator::ReclaimResources(
@@ -96,11 +76,40 @@
   DCHECK(resources.empty());
 }
 
-void FrameGenerator::WillDrawSurface(const cc::LocalSurfaceId& local_surface_id,
-                                     const gfx::Rect& damage_rect) {
-  // TODO(fsamuel, staraz): Implement this.
+void FrameGenerator::SetTreeActivationCallback(const base::Closure& callback) {}
+
+void FrameGenerator::DidReceiveCompositorFrameAck() {}
+
+void FrameGenerator::DidLoseCompositorFrameSink() {}
+
+void FrameGenerator::OnDraw(const gfx::Transform& transform,
+                            const gfx::Rect& viewport,
+                            bool resourceless_software_draw) {}
+
+void FrameGenerator::SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) {}
+
+void FrameGenerator::SetExternalTilePriorityConstraints(
+    const gfx::Rect& viewport_rect,
+    const gfx::Transform& transform) {}
+
+void FrameGenerator::OnBeginFrame(const cc::BeginFrameArgs& begin_frame_args) {
+  if (!root_window_->visible())
+    return;
+
+  // TODO(fsamuel): We should add a trace for generating a top level frame.
+  cc::CompositorFrame frame(GenerateCompositorFrame(root_window_->bounds()));
+
+  compositor_frame_sink_->SubmitCompositorFrame(std::move(frame));
+  SetNeedsBeginFrame(false);
+  last_begin_frame_args_ = begin_frame_args;
 }
 
+const cc::BeginFrameArgs& FrameGenerator::LastUsedBeginFrameArgs() const {
+  return last_begin_frame_args_;
+}
+
+void FrameGenerator::OnBeginFrameSourcePausedChanged(bool paused) {}
+
 cc::CompositorFrame FrameGenerator::GenerateCompositorFrame(
     const gfx::Rect& output_rect) {
   const int render_pass_id = 1;
@@ -173,6 +182,20 @@
                cc::SurfaceDrawQuadType::PRIMARY, nullptr);
 }
 
+void FrameGenerator::SetNeedsBeginFrame(bool needs_begin_frame) {
+  if (needs_begin_frame == observing_begin_frames_)
+    return;
+
+  if (needs_begin_frame) {
+    begin_frame_source_->AddObserver(this);
+    observing_begin_frames_ = true;
+    return;
+  }
+
+  begin_frame_source_->RemoveObserver(this);
+  observing_begin_frames_ = false;
+}
+
 }  // namespace ws
 
 }  // namespace ui
diff --git a/services/ui/ws/frame_generator.h b/services/ui/ws/frame_generator.h
index 4372581..baf0479 100644
--- a/services/ui/ws/frame_generator.h
+++ b/services/ui/ws/frame_generator.h
@@ -10,38 +10,36 @@
 #include "base/macros.h"
 #include "base/timer/timer.h"
 #include "cc/ipc/display_compositor.mojom.h"
+#include "cc/output/compositor_frame_sink_client.h"
+#include "cc/scheduler/begin_frame_source.h"
 #include "cc/surfaces/frame_sink_id.h"
 #include "cc/surfaces/local_surface_id_allocator.h"
 #include "cc/surfaces/surface_id.h"
 #include "cc/surfaces/surface_reference.h"
 #include "services/ui/public/interfaces/window_tree_constants.mojom.h"
-#include "services/ui/ws/ids.h"
-#include "services/ui/ws/server_window_delegate.h"
-#include "services/ui/ws/server_window_tracker.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace cc {
+class CompositorFrameSink;
 class RenderPass;
 }
 
 namespace ui {
 namespace ws {
 
-namespace test {
-class FrameGeneratorTest;
-}
-
 class FrameGeneratorDelegate;
 class ServerWindow;
 
 // Responsible for redrawing the display in response to the redraw requests by
 // submitting CompositorFrames to the owned CompositorFrameSink.
-class FrameGenerator : public cc::mojom::MojoCompositorFrameSinkClient {
+class FrameGenerator : public cc::CompositorFrameSinkClient,
+                       public cc::BeginFrameObserver {
  public:
-  FrameGenerator(FrameGeneratorDelegate* delegate,
-                 ServerWindow* root_window,
-                 gfx::AcceleratedWidget widget);
+  FrameGenerator(
+      FrameGeneratorDelegate* delegate,
+      ServerWindow* root_window,
+      std::unique_ptr<cc::CompositorFrameSink> compositor_frame_sink);
   ~FrameGenerator() override;
 
   void SetDeviceScaleFactor(float device_scale_factor);
@@ -52,14 +50,24 @@
   void OnWindowDamaged();
 
  private:
-  friend class ui::ws::test::FrameGeneratorTest;
-
-  // cc::mojom::MojoCompositorFrameSinkClient implementation:
-  void DidReceiveCompositorFrameAck() override;
-  void OnBeginFrame(const cc::BeginFrameArgs& begin_frame_arags) override;
+  // cc::CompositorFrameSinkClient implementation:
+  void SetBeginFrameSource(cc::BeginFrameSource* source) override;
   void ReclaimResources(const cc::ReturnedResourceArray& resources) override;
-  void WillDrawSurface(const cc::LocalSurfaceId& local_surface_id,
-                       const gfx::Rect& damage_rect) override;
+  void SetTreeActivationCallback(const base::Closure& callback) override;
+  void DidReceiveCompositorFrameAck() override;
+  void DidLoseCompositorFrameSink() override;
+  void OnDraw(const gfx::Transform& transform,
+              const gfx::Rect& viewport,
+              bool resourceless_software_draw) override;
+  void SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) override;
+  void SetExternalTilePriorityConstraints(
+      const gfx::Rect& viewport_rect,
+      const gfx::Transform& transform) override;
+
+  // cc::BeginFrameObserver implementation:
+  void OnBeginFrame(const cc::BeginFrameArgs& args) override;
+  const cc::BeginFrameArgs& LastUsedBeginFrameArgs() const override;
+  void OnBeginFrameSourcePausedChanged(bool paused) override;
 
   // Generates the CompositorFrame.
   cc::CompositorFrame GenerateCompositorFrame(const gfx::Rect& output_rect);
@@ -68,20 +76,21 @@
   // the provided cc::RenderPass.
   void DrawWindow(cc::RenderPass* pass);
 
+  // SetNeedsBeginFrame sets observing_begin_frames_ and add/remove
+  // FrameGenerator as an observer to/from begin_frame_source_ accordingly.
+  void SetNeedsBeginFrame(bool needs_begin_frame);
+
   FrameGeneratorDelegate* delegate_;
   ServerWindow* const root_window_;
   float device_scale_factor_ = 1.f;
 
-  gfx::Size last_submitted_frame_size_;
-  cc::LocalSurfaceId local_surface_id_;
-  cc::LocalSurfaceIdAllocator id_allocator_;
-  cc::mojom::MojoCompositorFrameSinkAssociatedPtr compositor_frame_sink_;
-  cc::mojom::DisplayPrivateAssociatedPtr display_private_;
+  std::unique_ptr<cc::CompositorFrameSink> compositor_frame_sink_;
+  cc::BeginFrameArgs last_begin_frame_args_;
+  cc::BeginFrameSource* begin_frame_source_ = nullptr;
+  bool observing_begin_frames_ = false;
 
   cc::SurfaceInfo window_manager_surface_info_;
 
-  mojo::Binding<cc::mojom::MojoCompositorFrameSinkClient> binding_;
-
   DISALLOW_COPY_AND_ASSIGN(FrameGenerator);
 };
 
diff --git a/services/ui/ws/platform_display_default.cc b/services/ui/ws/platform_display_default.cc
index 6d32827f0..fcf12c8 100644
--- a/services/ui/ws/platform_display_default.cc
+++ b/services/ui/ws/platform_display_default.cc
@@ -7,6 +7,7 @@
 #include "base/memory/ptr_util.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "services/ui/display/screen_manager.h"
+#include "services/ui/ws/display_client_compositor_frame_sink.h"
 #include "services/ui/ws/platform_display_init_params.h"
 #include "services/ui/ws/server_window.h"
 #include "ui/base/cursor/image_cursors.h"
@@ -248,8 +249,26 @@
   DCHECK_EQ(gfx::kNullAcceleratedWidget, widget_);
   widget_ = widget;
   delegate_->OnAcceleratedWidgetAvailable();
-  frame_generator_ =
-      base::MakeUnique<FrameGenerator>(this, root_window_, widget_);
+
+  cc::mojom::MojoCompositorFrameSinkAssociatedPtr compositor_frame_sink;
+  cc::mojom::DisplayPrivateAssociatedPtr display_private;
+  cc::mojom::MojoCompositorFrameSinkClientPtr compositor_frame_sink_client;
+  cc::mojom::MojoCompositorFrameSinkClientRequest
+      compositor_frame_sink_client_request =
+          mojo::MakeRequest(&compositor_frame_sink_client);
+
+  root_window_->CreateRootCompositorFrameSink(
+      widget_, mojo::MakeRequest(&compositor_frame_sink),
+      std::move(compositor_frame_sink_client),
+      mojo::MakeRequest(&display_private));
+
+  auto display_client_compositor_frame_sink =
+      base::MakeUnique<DisplayClientCompositorFrameSink>(
+          root_window_->frame_sink_id(), std::move(compositor_frame_sink),
+          std::move(display_private),
+          std::move(compositor_frame_sink_client_request));
+  frame_generator_ = base::MakeUnique<FrameGenerator>(
+      this, root_window_, std::move(display_client_compositor_frame_sink));
   frame_generator_->SetDeviceScaleFactor(init_device_scale_factor_);
 }
 
diff --git a/services/ui/ws/platform_display_default.h b/services/ui/ws/platform_display_default.h
index 54d36d8..04252ee 100644
--- a/services/ui/ws/platform_display_default.h
+++ b/services/ui/ws/platform_display_default.h
@@ -18,6 +18,7 @@
 namespace ui {
 
 class ImageCursors;
+class LocatedEvent;
 class PlatformWindow;
 
 namespace ws {
diff --git a/third_party/WebKit/LayoutTests/virtual/layout_ng/fast/block/margin-collapse/self-collapsing-block-discards-margin-expected.txt b/third_party/WebKit/LayoutTests/virtual/layout_ng/fast/block/margin-collapse/self-collapsing-block-discards-margin-expected.txt
new file mode 100644
index 0000000..9229098
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/layout_ng/fast/block/margin-collapse/self-collapsing-block-discards-margin-expected.txt
@@ -0,0 +1,2 @@
+crbug.com/479275: Don't ASSERT when a self-collapsing block discards its margin 
+
diff --git a/third_party/WebKit/Source/core/frame/DOMTimer.cpp b/third_party/WebKit/Source/core/frame/DOMTimer.cpp
index 331376e..fb011686 100644
--- a/third_party/WebKit/Source/core/frame/DOMTimer.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMTimer.cpp
@@ -59,10 +59,6 @@
                       bool singleShot) {
   int timeoutID = context->timers()->installNewTimeout(context, action, timeout,
                                                        singleShot);
-  TRACE_EVENT_INSTANT1("devtools.timeline", "TimerInstall",
-                       TRACE_EVENT_SCOPE_THREAD, "data",
-                       InspectorTimerInstallEvent::data(context, timeoutID,
-                                                        timeout, singleShot));
   return timeoutID;
 }
 
@@ -103,6 +99,10 @@
     startRepeating(intervalMilliseconds, BLINK_FROM_HERE);
 
   suspendIfNeeded();
+  TRACE_EVENT_INSTANT1("devtools.timeline", "TimerInstall",
+                       TRACE_EVENT_SCOPE_THREAD, "data",
+                       InspectorTimerInstallEvent::data(context, timeoutID,
+                                                        interval, singleShot));
   probe::asyncTaskScheduledBreakable(
       context, singleShot ? "setTimeout" : "setInterval", this, !singleShot);
 }
diff --git a/third_party/WebKit/Source/core/html/MediaDocument.cpp b/third_party/WebKit/Source/core/html/MediaDocument.cpp
index 2e953ef4..3d9202b 100644
--- a/third_party/WebKit/Source/core/html/MediaDocument.cpp
+++ b/third_party/WebKit/Source/core/html/MediaDocument.cpp
@@ -193,7 +193,8 @@
   div->appendChild(content);
 
   if (document()->settings() &&
-      document()->settings()->getEmbeddedMediaExperienceEnabled()) {
+      document()->settings()->getEmbeddedMediaExperienceEnabled() &&
+      source->type().startsWith("video/", TextCaseASCIIInsensitive)) {
     EventListener* listener = MediaLoadedEventListener::create();
     AddEventListenerOptions options;
     options.setOnce(true);
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
index 5eb7dca..778de8c3 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControlElements.cpp
@@ -936,13 +936,24 @@
 
 void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event) {
   if (event->type() == EventTypeNames::click) {
+    bool isEmbeddedExperienceEnabled =
+        document().settings() &&
+        document().settings()->getEmbeddedMediaExperienceEnabled();
     if (mediaElement().isFullscreen()) {
       Platform::current()->recordAction(
           UserMetricsAction("Media.Controls.ExitFullscreen"));
+      if (isEmbeddedExperienceEnabled) {
+        Platform::current()->recordAction(UserMetricsAction(
+            "Media.Controls.ExitFullscreen.EmbeddedExperience"));
+      }
       mediaControls().exitFullscreen();
     } else {
       Platform::current()->recordAction(
           UserMetricsAction("Media.Controls.EnterFullscreen"));
+      if (isEmbeddedExperienceEnabled) {
+        Platform::current()->recordAction(UserMetricsAction(
+            "Media.Controls.EnterFullscreen.EmbeddedExperience"));
+      }
       mediaControls().enterFullscreen();
     }
     event->setDefaultHandled();
diff --git a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp
index 4c483b2..cb3d3139 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp
@@ -39,6 +39,7 @@
 #include "core/inspector/InspectorNetworkAgent.h"
 #include "core/inspector/InspectorPageAgent.h"
 #include "core/inspector/InspectorSession.h"
+#include "core/inspector/InspectorTraceEvents.h"
 #include "core/inspector/MainThreadDebugger.h"
 #include "core/inspector/ThreadDebugger.h"
 #include "core/inspector/WorkerInspectorController.h"
@@ -46,6 +47,7 @@
 #include "core/workers/MainThreadWorkletGlobalScope.h"
 #include "core/workers/WorkerGlobalScope.h"
 #include "core/workers/WorkerThread.h"
+#include "platform/instrumentation/tracing/TraceEvent.h"
 #include "platform/loader/fetch/FetchInitiatorInfo.h"
 
 namespace blink {
@@ -75,6 +77,7 @@
 AsyncTask::AsyncTask(ExecutionContext* context, void* task, bool enabled)
     : m_debugger(enabled ? ThreadDebugger::from(toIsolate(context)) : nullptr),
       m_task(task) {
+  TRACE_EVENT_FLOW_END0("devtools.timeline.async", "AsyncTask", task);
   if (m_debugger)
     m_debugger->asyncTaskStarted(m_task);
 }
@@ -88,6 +91,8 @@
                         const String& name,
                         void* task,
                         bool recurring) {
+  TRACE_EVENT_FLOW_BEGIN1("devtools.timeline.async", "AsyncTask", task, "data",
+                          InspectorAsyncTask::data(name));
   if (ThreadDebugger* debugger = ThreadDebugger::from(toIsolate(context)))
     debugger->asyncTaskScheduled(name, task, recurring);
 }
diff --git a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
index 0de5ae0..41c59523 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.cpp
@@ -1192,4 +1192,10 @@
   return value;
 }
 
+std::unique_ptr<TracedValue> InspectorAsyncTask::data(const String& name) {
+  std::unique_ptr<TracedValue> value = TracedValue::create();
+  value->setString("name", name);
+  return value;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.h b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.h
index 23734812..5fe45d7 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorTraceEvents.h
@@ -444,6 +444,10 @@
                                      const HitTestResult&);
 }
 
+namespace InspectorAsyncTask {
+std::unique_ptr<TracedValue> data(const String&);
+}
+
 CORE_EXPORT String toHexString(const void* p);
 CORE_EXPORT void setCallStack(TracedValue*);
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
index 07ed841b..2d85a29 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -2401,7 +2401,7 @@
     containerOffset.move(layer()->offsetForInFlowPosition());
   }
 
-  bool preserve3D = container->style()->preserves3D() || style()->preserves3D();
+  bool preserve3D = container->style()->preserves3D();
 
   TransformState::TransformAccumulation accumulation =
       preserve3D ? TransformState::AccumulateTransform
diff --git a/third_party/WebKit/Source/core/layout/LayoutInline.cpp b/third_party/WebKit/Source/core/layout/LayoutInline.cpp
index 6992af87..4a8cf22 100644
--- a/third_party/WebKit/Source/core/layout/LayoutInline.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutInline.cpp
@@ -1211,7 +1211,7 @@
   if (!container)
     return true;
 
-  bool preserve3D = container->style()->preserves3D() || style()->preserves3D();
+  bool preserve3D = container->style()->preserves3D();
 
   TransformState::TransformAccumulation accumulation =
       preserve3D ? TransformState::AccumulateTransform
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
index baecf0e..077c6726 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -1306,8 +1306,7 @@
         transformState.setQuad(FloatQuad(FloatRect(rect)));
       }
 
-      bool preserve3D = (parent->style()->preserves3D() && !parent->isText()) ||
-                        (style()->preserves3D() && !isText());
+      bool preserve3D = parent->style()->preserves3D() && !parent->isText();
 
       TransformState::TransformAccumulation accumulation =
           preserve3D ? TransformState::AccumulateTransform
diff --git a/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp b/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
index 4020dfa..a4b9c5d 100644
--- a/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
+++ b/third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
@@ -6,6 +6,7 @@
 #include "core/layout/LayoutView.h"
 #include "core/layout/PaintInvalidationState.h"
 #include "core/paint/PaintLayer.h"
+#include "core/paint/PaintPropertyTreePrinter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace blink {
@@ -24,16 +25,47 @@
       toLayoutBox(object).flipForWritingMode(rect);
     const LayoutBoxModelObject& paintInvalidationContainer =
         object.containerForPaintInvalidation();
-    object.mapToVisualRectInAncestorSpace(&paintInvalidationContainer, rect);
-    if (rect.isEmpty() && object.visualRect().isEmpty())
+
+    checkVisualRect(object, paintInvalidationContainer, rect,
+                    object.visualRect(), true);
+  }
+
+  void checkVisualRect(const LayoutObject& object,
+                       const LayoutBoxModelObject& ancestor,
+                       const LayoutRect& localRect,
+                       const LayoutRect& expectedVisualRect,
+                       bool adjustForBacking = false) {
+    LayoutRect slowMapRect = localRect;
+    object.mapToVisualRectInAncestorSpace(&ancestor, slowMapRect);
+    if (slowMapRect.isEmpty() && object.visualRect().isEmpty())
       return;
+
+    FloatRect geometryMapperRect(localRect);
+    if (object.paintProperties()) {
+      geometryMapperRect.moveBy(FloatPoint(object.paintOffset()));
+      document().view()->geometryMapper().sourceToDestinationVisualRect(
+          *object.paintProperties()->localBorderBoxProperties(),
+          *ancestor.paintProperties()->contentsProperties(),
+          geometryMapperRect);
+      geometryMapperRect.moveBy(-FloatPoint(ancestor.paintOffset()));
+    }
+
     // The following condition can be false if paintInvalidationContainer is
     // a LayoutView and compositing is not enabled.
-    if (paintInvalidationContainer.isPaintInvalidationContainer()) {
-      PaintLayer::mapRectInPaintInvalidationContainerToBacking(
-          paintInvalidationContainer, rect);
+    if (adjustForBacking && ancestor.isPaintInvalidationContainer()) {
+      PaintLayer::mapRectInPaintInvalidationContainerToBacking(ancestor,
+                                                               slowMapRect);
+      LayoutRect temp(geometryMapperRect);
+      PaintLayer::mapRectInPaintInvalidationContainerToBacking(ancestor, temp);
+      geometryMapperRect = FloatRect(temp);
     }
-    EXPECT_EQ(enclosingIntRect(rect), enclosingIntRect(object.visualRect()));
+    EXPECT_TRUE(enclosingIntRect(slowMapRect)
+                    .contains(enclosingIntRect(expectedVisualRect)));
+
+    if (object.paintProperties()) {
+      EXPECT_EQ(enclosingIntRect(expectedVisualRect),
+                enclosingIntRect(geometryMapperRect));
+    }
   }
 };
 
@@ -241,11 +273,8 @@
   // This rect is in physical coordinates of target.
   EXPECT_EQ(LayoutRect(0, 0, 140, 70), rect);
 
-  rect = localVisualRect;
-  target->flipForWritingMode(rect);
-  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(&layoutView(), rect));
-  EXPECT_EQ(LayoutRect(222, 111, 140, 70), rect);
-  EXPECT_EQ(rect, target->visualRect());
+  checkPaintInvalidationVisualRect(*target);
+  EXPECT_EQ(LayoutRect(222, 111, 140, 70), target->visualRect());
 }
 
 TEST_F(VisualRectMappingTest, ContainerFlippedWritingMode) {
@@ -278,11 +307,11 @@
   EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(container, rect));
   // 100 is the physical x location of target in container.
   EXPECT_EQ(LayoutRect(100, 0, 140, 110), rect);
+
   rect = targetLocalVisualRect;
   target->flipForWritingMode(rect);
-  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(&layoutView(), rect));
-  EXPECT_EQ(LayoutRect(322, 111, 140, 110), rect);
-  EXPECT_EQ(rect, target->visualRect());
+  checkPaintInvalidationVisualRect(*target);
+  EXPECT_EQ(LayoutRect(322, 111, 140, 110), target->visualRect());
 
   LayoutRect containerLocalVisualRect = container->localVisualRect();
   EXPECT_EQ(LayoutRect(0, 0, 200, 100), containerLocalVisualRect);
@@ -332,13 +361,11 @@
   // overflow:scroll.
   EXPECT_EQ(LayoutRect(2, 3, 140, 110), rect);
 
-  rect = targetLocalVisualRect;
-  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(&layoutView(), rect));
+  checkPaintInvalidationVisualRect(*target);
   // (2, 3, 140, 100) is first clipped by container's overflow clip, to
   // (10, 10, 50, 80), then is by added container's offset in LayoutView
   // (111, 222).
-  EXPECT_EQ(LayoutRect(232, 121, 50, 80), rect);
-  EXPECT_EQ(rect, target->visualRect());
+  EXPECT_EQ(LayoutRect(232, 121, 50, 80), target->visualRect());
 
   LayoutRect containerLocalVisualRect = container->localVisualRect();
   // Because container has overflow clip, its visual overflow doesn't include
@@ -351,10 +378,8 @@
   // Container should not apply overflow clip on its own overflow rect.
   EXPECT_EQ(LayoutRect(0, 0, 70, 100), rect);
 
-  rect = containerLocalVisualRect;
-  EXPECT_TRUE(container->mapToVisualRectInAncestorSpace(&layoutView(), rect));
-  EXPECT_EQ(LayoutRect(222, 111, 70, 100), rect);
-  EXPECT_EQ(rect, container->visualRect());
+  checkPaintInvalidationVisualRect(*container);
+  EXPECT_EQ(LayoutRect(222, 111, 70, 100), container->visualRect());
 }
 
 TEST_F(VisualRectMappingTest, ContainerFlippedWritingModeAndOverflowScroll) {
@@ -403,17 +428,14 @@
   // Rect is clipped by container's overflow clip because of overflow:scroll.
   EXPECT_EQ(LayoutRect(-2, 3, 140, 110), rect);
 
-  rect = targetLocalVisualRect;
-  target->flipForWritingMode(rect);
-  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(&layoutView(), rect));
+  checkPaintInvalidationVisualRect(*target);
   // (-2, 3, 140, 100) is first clipped by container's overflow clip, to
   // (40, 10, 50, 80), then is added by container's offset in LayoutView
   // (111, 222).
   // TODO(crbug.com/600039): rect.x() should be 262 (left + border-left), but is
   // offset
   // by extra horizontal border-widths because of layout error.
-  EXPECT_EQ(LayoutRect(322, 121, 50, 80), rect);
-  EXPECT_EQ(rect, target->visualRect());
+  EXPECT_EQ(LayoutRect(322, 121, 50, 80), target->visualRect());
 
   LayoutRect containerLocalVisualRect = container->localVisualRect();
   // Because container has overflow clip, its visual overflow doesn't include
@@ -427,14 +449,11 @@
   EXPECT_TRUE(container->mapToVisualRectInAncestorSpace(container, rect));
   EXPECT_EQ(LayoutRect(0, 0, 110, 120), rect);
 
-  rect = containerLocalVisualRect;
-  container->flipForWritingMode(rect);
-  EXPECT_TRUE(container->mapToVisualRectInAncestorSpace(&layoutView(), rect));
   // TODO(crbug.com/600039): rect.x() should be 222 (left), but is offset by
   // extra horizontal
   // border-widths because of layout error.
-  EXPECT_EQ(LayoutRect(282, 111, 110, 120), rect);
-  EXPECT_EQ(rect, container->visualRect());
+  checkPaintInvalidationVisualRect(*container);
+  EXPECT_EQ(LayoutRect(282, 111, 110, 120), container->visualRect());
 }
 
 TEST_F(VisualRectMappingTest, ContainerOverflowHidden) {
@@ -464,9 +483,8 @@
   EXPECT_EQ(LayoutRect(0, 0, 140, 110), rect);
 
   rect = targetLocalVisualRect;
-  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(container, rect));
   // Rect is not clipped by container's overflow clip.
-  EXPECT_EQ(LayoutRect(10, 10, 140, 110), rect);
+  checkVisualRect(*target, *container, rect, LayoutRect(10, 10, 140, 110));
 }
 
 TEST_F(VisualRectMappingTest, ContainerFlippedWritingModeAndOverflowHidden) {
@@ -507,9 +525,9 @@
 
   rect = targetLocalVisualRect;
   target->flipForWritingMode(rect);
-  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(container, rect));
   // 58 = target_physical_x(100) + container_border_left(40) - scroll_left(58)
-  EXPECT_EQ(LayoutRect(-10, 10, 140, 110), rect);
+  checkVisualRect(*target, *container, rect, LayoutRect(-10, 10, 140, 110));
+  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(container, rect));
 }
 
 TEST_F(VisualRectMappingTest, ContainerAndTargetDifferentFlippedWritingMode) {
@@ -595,12 +613,9 @@
   EXPECT_EQ(stackingContext, &absolute->containerForPaintInvalidation());
   EXPECT_EQ(stackingContext, absolute->container());
 
-  LayoutRect absoluteVisualRect = absolute->localVisualRect();
-  EXPECT_EQ(LayoutRect(0, 0, 50, 50), absoluteVisualRect);
-  rect = absoluteVisualRect;
-  EXPECT_TRUE(absolute->mapToVisualRectInAncestorSpace(stackingContext, rect));
-  EXPECT_EQ(LayoutRect(222, 111, 50, 50), rect);
-  EXPECT_EQ(rect, absolute->visualRect());
+  EXPECT_EQ(LayoutRect(0, 0, 50, 50), absolute->localVisualRect());
+  checkPaintInvalidationVisualRect(*absolute);
+  EXPECT_EQ(LayoutRect(222, 111, 50, 50), absolute->visualRect());
 }
 
 TEST_F(VisualRectMappingTest,
@@ -649,12 +664,9 @@
 
   LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
 
-  LayoutRect targetLocalVisualRect = target->localVisualRect();
-  EXPECT_EQ(LayoutRect(0, 0, 400, 400), targetLocalVisualRect);
-  LayoutRect rect = targetLocalVisualRect;
-  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(&layoutView(), rect));
-  EXPECT_EQ(LayoutRect(0, 0, 200, 200), rect);
-  EXPECT_EQ(rect, target->visualRect());
+  EXPECT_EQ(LayoutRect(0, 0, 400, 400), target->localVisualRect());
+  checkPaintInvalidationVisualRect(*target);
+  EXPECT_EQ(LayoutRect(0, 0, 200, 200), target->visualRect());
 }
 
 TEST_F(VisualRectMappingTest, ContainPaint) {
@@ -666,12 +678,9 @@
 
   LayoutBox* target = toLayoutBox(getLayoutObjectByElementId("target"));
 
-  LayoutRect targetLocalVisualRect = target->localVisualRect();
-  EXPECT_EQ(LayoutRect(0, 0, 400, 400), targetLocalVisualRect);
-  LayoutRect rect = targetLocalVisualRect;
-  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(&layoutView(), rect));
-  EXPECT_EQ(LayoutRect(0, 0, 200, 200), rect);
-  EXPECT_EQ(rect, target->visualRect());
+  EXPECT_EQ(LayoutRect(0, 0, 400, 400), target->localVisualRect());
+  checkPaintInvalidationVisualRect(*target);
+  EXPECT_EQ(LayoutRect(0, 0, 200, 200), target->visualRect());
 }
 
 TEST_F(VisualRectMappingTest, FloatUnderInline) {
@@ -696,8 +705,8 @@
   EXPECT_EQ(rect, target->visualRect());
 
   rect = targetVisualRect;
-  EXPECT_TRUE(target->mapToVisualRectInAncestorSpace(span, rect));
-  EXPECT_EQ(LayoutRect(-200, -100, 33, 44), rect);
+
+  checkVisualRect(*target, *span, rect, LayoutRect(-200, -100, 33, 44));
 }
 
 TEST_F(VisualRectMappingTest, ShouldAccountForPreserve3d) {
@@ -722,13 +731,40 @@
   LayoutRect originalRect(0, 0, 100, 100);
   // Multiply both matrices together before flattening.
   TransformationMatrix matrix = container->layer()->currentTransform();
-  matrix *= target->layer()->currentTransform();
   matrix.flattenTo2d();
-  FloatRect output = matrix.mapRect(FloatRect(originalRect));
+  matrix *= target->layer()->currentTransform();
+  LayoutRect output(matrix.mapRect(FloatRect(originalRect)));
 
-  EXPECT_TRUE(
-      target->mapToVisualRectInAncestorSpace(target->view(), originalRect));
-  EXPECT_EQ(LayoutRect(enclosingIntRect(output)), originalRect);
+  checkVisualRect(*target, *target->view(), originalRect, output);
+}
+
+TEST_F(VisualRectMappingTest, ShouldAccountForPreserve3dNested) {
+  enableCompositing();
+  setBodyInnerHTML(
+      "<style>"
+      "* { margin: 0; }"
+      "#container {"
+      "  transform-style: preserve-3d;"
+      "  transform: rotateX(-45deg);"
+      "  width: 100px; height: 100px;"
+      "}"
+      "#target {"
+      "  transform-style: preserve-3d; transform: rotateX(45deg);"
+      "  background: lightblue;"
+      "  width: 100px; height: 100px;"
+      "}"
+      "</style>"
+      "<div id='container'><div id='target'></div></div>");
+  LayoutBlock* container =
+      toLayoutBlock(getLayoutObjectByElementId("container"));
+  LayoutBlock* target = toLayoutBlock(getLayoutObjectByElementId("target"));
+  LayoutRect originalRect(0, 0, 100, 100);
+  // Multiply both matrices together before flattening.
+  TransformationMatrix matrix = container->layer()->currentTransform();
+  matrix *= target->layer()->currentTransform();
+  LayoutRect output(matrix.mapRect(FloatRect(originalRect)));
+
+  checkVisualRect(*target, *target->view(), originalRect, output);
 }
 
 TEST_F(VisualRectMappingTest, ShouldAccountForPerspective) {
@@ -752,17 +788,47 @@
   LayoutBlock* target = toLayoutBlock(getLayoutObjectByElementId("target"));
   LayoutRect originalRect(0, 0, 100, 100);
   TransformationMatrix matrix = container->layer()->currentTransform();
+  matrix.flattenTo2d();
   TransformationMatrix targetMatrix;
   // getTransformfromContainter includes transform and perspective matrix
   // of the container.
   target->getTransformFromContainer(container, LayoutSize(), targetMatrix);
   matrix *= targetMatrix;
-  matrix.flattenTo2d();
-  FloatRect output = matrix.mapRect(FloatRect(originalRect));
+  LayoutRect output(matrix.mapRect(FloatRect(originalRect)));
 
-  EXPECT_TRUE(
-      target->mapToVisualRectInAncestorSpace(target->view(), originalRect));
-  EXPECT_EQ(LayoutRect(enclosingIntRect(output)), originalRect);
+  checkVisualRect(*target, *target->view(), originalRect, output);
+}
+
+TEST_F(VisualRectMappingTest, ShouldAccountForPerspectiveNested) {
+  enableCompositing();
+  setBodyInnerHTML(
+      "<style>"
+      "* { margin: 0; }"
+      "#container {"
+      "  transform-style: preserve-3d;"
+      "  transform: rotateX(-45deg); perspective: 100px;"
+      "  width: 100px; height: 100px;"
+      "}"
+      "#target {"
+      "  transform-style: preserve-3d; transform: rotateX(45deg);"
+      "  background: lightblue;"
+      "  width: 100px; height: 100px;"
+      "}"
+      "</style>"
+      "<div id='container'><div id='target'></div></div>");
+  LayoutBlock* container =
+      toLayoutBlock(getLayoutObjectByElementId("container"));
+  LayoutBlock* target = toLayoutBlock(getLayoutObjectByElementId("target"));
+  LayoutRect originalRect(0, 0, 100, 100);
+  TransformationMatrix matrix = container->layer()->currentTransform();
+  TransformationMatrix targetMatrix;
+  // getTransformfromContainter includes transform and perspective matrix
+  // of the container.
+  target->getTransformFromContainer(container, LayoutSize(), targetMatrix);
+  matrix *= targetMatrix;
+  LayoutRect output(matrix.mapRect(FloatRect(originalRect)));
+
+  checkVisualRect(*target, *target->view(), originalRect, output);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
index 75f6bc60..1211480 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
@@ -458,7 +458,10 @@
         CreateConstraintSpaceForChild(child);
 
     if (child->Type() == NGLayoutInputNode::kLegacyInline) {
-      LayoutInlineChildren(toNGInlineNode(child), child_space.get());
+      LayoutInlineChild(toNGInlineNode(child), child_space.get());
+      // TODO(kojii): We need to get NGInlineNode::NextSibiling() and continue,
+      // but currently it always return nullptr. This needs to change when it
+      // supports inline/block mixed children.
       break;
     }
 
@@ -519,23 +522,17 @@
   return builder_.ToBoxFragment();
 }
 
-void NGBlockLayoutAlgorithm::LayoutInlineChildren(
-    NGInlineNode* inline_child,
-    NGConstraintSpace* child_space) {
+void NGBlockLayoutAlgorithm::LayoutInlineChild(NGInlineNode* inline_child,
+                                               NGConstraintSpace* child_space) {
   // TODO(kojii): This logic does not handle when children are mix of
   // inline/block. We need to detect the case and setup appropriately; e.g.,
   // constraint space, margin collapsing, next siblings, etc.
-  NGLineBuilder line_builder(inline_child, child_space);
+  NGLineBuilder line_builder(inline_child, child_space, &builder_);
   // TODO(kojii): Need to determine when to invalidate PrepareLayout() more
   // efficiently than "everytime".
   inline_child->InvalidatePrepareLayout();
   inline_child->LayoutInline(child_space, &line_builder);
-  // TODO(kojii): The wrapper fragment should not be needed.
-  NGFragmentBuilder wrapper_fragment_builder(NGPhysicalFragment::kFragmentBox,
-                                             inline_child);
-  line_builder.CreateFragments(&wrapper_fragment_builder);
-  RefPtr<NGLayoutResult> child_result =
-      wrapper_fragment_builder.ToBoxFragment();
+  RefPtr<NGLayoutResult> child_result = line_builder.CreateFragments();
   line_builder.CopyFragmentDataToLayoutBlockFlow();
   FinishChildLayout(inline_child, child_space, child_result);
 }
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
index 904777d..a14a042 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
@@ -47,8 +47,8 @@
                          NGConstraintSpace*,
                          RefPtr<NGLayoutResult>);
 
-  // Layout inline children.
-  void LayoutInlineChildren(NGInlineNode*, NGConstraintSpace*);
+  // Layout inline child.
+  void LayoutInlineChild(NGInlineNode*, NGConstraintSpace*);
 
   // Final adjustments before fragment creation. We need to prevent the
   // fragment from crossing fragmentainer boundaries, and rather create a break
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_inline_node.cc b/third_party/WebKit/Source/core/layout/ng/ng_inline_node.cc
index a4ba2f3..3e7717d 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_inline_node.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_inline_node.cc
@@ -92,7 +92,10 @@
       builder->SetIsSVGText(node->isSVGInlineText());
       builder->Append(toLayoutText(node)->text(), node->style(), node);
     } else if (node->isFloating() || node->isOutOfFlowPositioned()) {
-      // Skip positioned objects.
+      // Add floats and positioned objects in the same way as atomic inlines.
+      // Because these objects need positions, they will be handled in
+      // NGLineBuilder.
+      builder->Append(objectReplacementCharacter, nullptr, node);
     } else if (!node->isInline()) {
       // TODO(kojii): Implement when inline has block children.
     } else {
@@ -273,7 +276,7 @@
           .SetTextDirection(BlockStyle()->direction())
           .SetAvailableSize({LayoutUnit(), NGSizeIndefinite})
           .ToConstraintSpace(writing_mode);
-  NGLineBuilder line_builder(this, constraint_space.get());
+  NGLineBuilder line_builder(this, constraint_space.get(), nullptr);
   LayoutInline(constraint_space.get(), &line_builder);
   MinMaxContentSize sizes;
   sizes.min_content = line_builder.MaxInlineSize();
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_inline_node_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_inline_node_test.cc
index 4a11633..99c6a75 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_inline_node_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_inline_node_test.cc
@@ -82,14 +82,12 @@
     RefPtr<NGConstraintSpace> constraint_space =
         NGConstraintSpaceBuilder(kHorizontalTopBottom)
             .ToConstraintSpace(kHorizontalTopBottom);
-    NGLineBuilder line_builder(node, constraint_space.get());
+    NGLineBuilder line_builder(node, constraint_space.get(), nullptr);
 
     NGTextLayoutAlgorithm algorithm(node, constraint_space.get());
     algorithm.LayoutInline(&line_builder);
 
-    NGFragmentBuilder fragment_builder(NGPhysicalFragment::kFragmentBox, node);
-    line_builder.CreateFragments(&fragment_builder);
-    RefPtr<NGLayoutResult> result = fragment_builder.ToBoxFragment();
+    RefPtr<NGLayoutResult> result = line_builder.CreateFragments();
     for (const auto& child :
          toNGPhysicalBoxFragment(result->PhysicalFragment().get())
              ->Children()) {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_line_builder.cc b/third_party/WebKit/Source/core/layout/ng/ng_line_builder.cc
index de7113fd..198796b 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_line_builder.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_line_builder.cc
@@ -20,9 +20,11 @@
 namespace blink {
 
 NGLineBuilder::NGLineBuilder(NGInlineNode* inline_box,
-                             NGConstraintSpace* constraint_space)
+                             NGConstraintSpace* constraint_space,
+                             NGFragmentBuilder* containing_block_builder)
     : inline_box_(inline_box),
       constraint_space_(constraint_space),
+      containing_block_builder_(containing_block_builder),
       baseline_type_(constraint_space->WritingMode() ==
                              NGWritingMode::kHorizontalTopBottom
                          ? FontBaseline::AlphabeticBaseline
@@ -214,11 +216,7 @@
 
     LayoutUnit top, height;
     const ComputedStyle* style = item.Style();
-    if (!style) {
-      // TODO(kojii): Implement atomic inline.
-      style = item.GetLayoutObject()->style();
-      top = content_size_;
-    } else {
+    if (style) {
       // |InlineTextBoxPainter| sets the baseline at |top +
       // ascent-of-primary-font|. Compute |top| to match.
       InlineItemMetrics metrics(*style, baseline_type_);
@@ -229,6 +227,25 @@
       // Take all used fonts into account if 'line-height: normal'.
       if (style->lineHeight().isNegative())
         AccumulateUsedFonts(item, line_item_chunk, &line_box_data);
+    } else {
+      LayoutObject* layout_object = item.GetLayoutObject();
+      if (layout_object->isOutOfFlowPositioned()) {
+        if (containing_block_builder_) {
+          // Absolute positioning blockifies the box's display type.
+          // https://drafts.csswg.org/css-display/#transformations
+          containing_block_builder_->AddOutOfFlowChildCandidate(
+              new NGBlockNode(layout_object),
+              NGLogicalOffset(line_box_data.inline_size, content_size_));
+        }
+        continue;
+      } else if (layout_object->isFloating()) {
+        // TODO(kojii): Implement float.
+        continue;
+      }
+      DCHECK(layout_object->isAtomicInlineLevel());
+      // TODO(kojii): Implement atomic inline.
+      style = layout_object->style();
+      top = content_size_;
     }
 
     // The direction of a fragment is the CSS direction to resolve logical
@@ -337,21 +354,27 @@
     current_opportunity_ = opportunity;
 }
 
-void NGLineBuilder::CreateFragments(NGFragmentBuilder* container_builder) {
+RefPtr<NGLayoutResult> NGLineBuilder::CreateFragments() {
   DCHECK(!HasItems()) << "Must call CreateLine()";
   DCHECK_EQ(fragments_.size(), offsets_.size());
 
+  NGFragmentBuilder container_builder(NGPhysicalFragment::kFragmentBox,
+                                      inline_box_);
+
   for (unsigned i = 0; i < fragments_.size(); i++) {
     // TODO(layout-dev): This should really be a std::move but
     // CopyFragmentDataToLayoutBlockFlow also uses the fragments.
-    container_builder->AddChild(fragments_[i].get(), offsets_[i]);
+    container_builder.AddChild(fragments_[i].get(), offsets_[i]);
   }
 
   // TODO(kojii): Check if the line box width should be content or available.
-  container_builder->SetInlineSize(max_inline_size_)
+  // TODO(kojii): Need to take constraint_space into account.
+  container_builder.SetInlineSize(max_inline_size_)
       .SetInlineOverflow(max_inline_size_)
       .SetBlockSize(content_size_)
       .SetBlockOverflow(content_size_);
+
+  return container_builder.ToBoxFragment();
 }
 
 void NGLineBuilder::CopyFragmentDataToLayoutBlockFlow() {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_line_builder.h b/third_party/WebKit/Source/core/layout/ng/ng_line_builder.h
index 6dcb0ef..4e9fea39 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_line_builder.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_line_builder.h
@@ -31,7 +31,7 @@
   STACK_ALLOCATED();
 
  public:
-  NGLineBuilder(NGInlineNode*, NGConstraintSpace*);
+  NGLineBuilder(NGInlineNode*, NGConstraintSpace*, NGFragmentBuilder*);
 
   const NGConstraintSpace& ConstraintSpace() const {
     return *constraint_space_;
@@ -81,7 +81,7 @@
   void SetStartOfHangables(unsigned offset);
 
   // Create fragments for all lines created so far.
-  void CreateFragments(NGFragmentBuilder*);
+  RefPtr<NGLayoutResult> CreateFragments();
 
   // Copy fragment data of all lines created by this NGLineBuilder to
   // LayoutBlockFlow.
@@ -145,6 +145,7 @@
 
   Persistent<NGInlineNode> inline_box_;
   NGConstraintSpace* constraint_space_;  // Not owned as STACK_ALLOCATED.
+  NGFragmentBuilder* containing_block_builder_;
   Vector<RefPtr<NGPhysicalFragment>, 32> fragments_;
   Vector<NGLogicalOffset, 32> offsets_;
   Vector<LineBoxData, 32> line_box_data_list_;
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 f2e18b8..10ca4c15 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/Main.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -113,6 +113,7 @@
     Runtime.experiments.register('timelineShowAllProcesses', 'Show all processes on Timeline', true);
     Runtime.experiments.register('timelinePaintTimingMarkers', 'Show paint timing markers on Timeline', true);
     Runtime.experiments.register('sourceDiff', 'Source diff');
+    Runtime.experiments.register('timelineFlowEvents', 'Timeline flow events', true);
     Runtime.experiments.register('terminalInDrawer', 'Terminal in drawer', true);
     Runtime.experiments.register('timelineInvalidationTracking', 'Timeline invalidation tracking', true);
     Runtime.experiments.register('timelineMultipleMainViews', 'Tabbed views on Performance panel');
diff --git a/third_party/WebKit/Source/devtools/front_end/perf_ui/FlameChart.js b/third_party/WebKit/Source/devtools/front_end/perf_ui/FlameChart.js
index 49249c4..7c1c6e9 100644
--- a/third_party/WebKit/Source/devtools/front_end/perf_ui/FlameChart.js
+++ b/third_party/WebKit/Source/devtools/front_end/perf_ui/FlameChart.js
@@ -699,9 +699,11 @@
       context.fillStyle = this._dataProvider.textColor(entryIndex);
       context.fillText(text, barX + textPadding, barY + textBaseHeight);
     }
+
     context.restore();
 
     this._drawGroupHeaders(width, height);
+    this._drawFlowEvents(context, width, height);
     this._drawMarkers();
     var headerHeight = this._rulerEnabled ? PerfUI.FlameChart.HeaderHeight : 0;
     PerfUI.TimelineGrid.drawCanvasGrid(context, this._calculator, 3, headerHeight);
@@ -940,6 +942,57 @@
     }
   }
 
+  /**
+   * @param {!CanvasRenderingContext2D} context
+   * @param {number} height
+   * @param {number} width
+   */
+  _drawFlowEvents(context, width, height) {
+    context.save();
+    var ratio = window.devicePixelRatio;
+    var top = this.getScrollOffset();
+    var arrowWidth = 6;
+    context.scale(ratio, ratio);
+    context.translate(0, -top);
+
+    context.fillStyle = '#7f0000';
+    context.strokeStyle = '#7f0000';
+    var td = this._timelineData();
+    var endIndex = td.flowStartTimes.lowerBound(this._timeWindowRight);
+
+    context.lineWidth = 0.5;
+    for (var i = 0; i < endIndex; ++i) {
+      if (td.flowEndTimes[i] < this._timeWindowLeft)
+        continue;
+      var startX = this._timeToPosition(td.flowStartTimes[i]);
+      var endX = this._timeToPosition(td.flowEndTimes[i]);
+      var startY = this._levelToHeight(td.flowStartLevels[i]) + this._barHeight / 2;
+      var endY = this._levelToHeight(td.flowEndLevels[i]) + this._barHeight / 2;
+      var distance = (endY - startY) / 10;
+      var spread = 30;
+      var lineY = spread + Math.max(0, startY + distance * (i % spread));
+
+      var segment = Math.min((endX - startX) / 4, 40);
+      context.beginPath();
+      context.moveTo(startX, startY);
+      context.bezierCurveTo(startX + segment, startY, startX + segment, lineY, startX + segment * 2, lineY);
+      context.lineTo(endX - segment * 2, lineY);
+      context.bezierCurveTo(endX - segment, lineY, endX - segment, endY, endX - arrowWidth, endY);
+      context.stroke();
+
+      context.beginPath();
+      context.arc(startX, startY, 2, -Math.PI / 2, Math.PI / 2, false);
+      context.fill();
+
+      context.beginPath();
+      context.moveTo(endX, endY);
+      context.lineTo(endX - arrowWidth, endY - 3);
+      context.lineTo(endX - arrowWidth, endY + 3);
+      context.fill();
+    }
+    context.restore();
+  }
+
   _drawMarkers() {
     var markers = this._timelineData().markers;
     var left = this._markerIndexBeforeTime(this._calculator.minimumBoundary());
@@ -1265,6 +1318,10 @@
     this.groups = groups;
     /** @type {!Array.<!PerfUI.FlameChartMarker>} */
     this.markers = [];
+    this.flowStartTimes = [];
+    this.flowStartLevels = [];
+    this.flowEndTimes = [];
+    this.flowEndLevels = [];
   }
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/text_editor/TextEditorAutocompleteController.js b/third_party/WebKit/Source/devtools/front_end/text_editor/TextEditorAutocompleteController.js
index f0f76cf6..0364441a 100644
--- a/third_party/WebKit/Source/devtools/front_end/text_editor/TextEditorAutocompleteController.js
+++ b/third_party/WebKit/Source/devtools/front_end/text_editor/TextEditorAutocompleteController.js
@@ -250,8 +250,10 @@
         this._onSuggestionsShownForTest([]);
         return;
       }
-      if (!this._suggestBox)
+      if (!this._suggestBox) {
         this._suggestBox = new UI.SuggestBox(this, 20, this._config.captureEnter);
+        this._suggestBox.setDefaultSelectionIsDimmed(!!this._config.captureEnter);
+      }
 
       var oldQueryRange = this._queryRange;
       this._queryRange = queryRange;
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineController.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineController.js
index dbfd06d..2b5862c 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineController.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineController.js
@@ -50,6 +50,9 @@
     ];
     categoriesArray.push(TimelineModel.TimelineModel.Category.LatencyInfo);
 
+    if (Runtime.experiments.isEnabled('timelineFlowEvents'))
+      categoriesArray.push('devtools.timeline.async');
+
     if (Runtime.experiments.isEnabled('timelineV8RuntimeCallStats') && options.enableJSSampling)
       categoriesArray.push(disabledByDefault('v8.runtime_stats_sampling'));
     if (Runtime.experiments.isEnabled('timelineTracingJSProfile') && options.enableJSSampling) {
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js
index 356dd8e..e85439f 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChart.js
@@ -74,6 +74,9 @@
         (Object.assign({useFirstLineForOverview: true}, defaultGroupStyle));
     this._interactionsHeaderLevel2 = /** @type {!PerfUI.FlameChart.GroupStyle} */
         (Object.assign({}, defaultGroupStyle, {padding: 2, nestingLevel: 1}));
+
+    /** @type {!Map<string, number>} */
+    this._flowEventIndexById = new Map();
   }
 
   /**
@@ -175,6 +178,8 @@
     if (!this._model)
       return this._timelineData;
 
+    this._flowEventIndexById.clear();
+
     this._minimumBoundary = this._model.minimumRecordTime();
     this._timeSpan = this._model.isEmpty() ? 1000 : this._model.maximumRecordTime() - this._minimumBoundary;
     this._currentLevel = 0;
@@ -240,6 +245,7 @@
 
     this._markers.sort(compareStartTime);
     this._timelineData.markers = this._markers;
+    this._flowEventIndexById.clear();
 
     return this._timelineData;
   }
@@ -296,6 +302,7 @@
   _appendSyncEvents(events, title, style, entryType, forceExpanded) {
     var isExtension = entryType === Timeline.TimelineFlameChartEntryType.ExtensionEvent;
     var openEvents = [];
+    var flowEventsEnabled = Runtime.experiments.isEnabled('timelineFlowEvents');
     var blackboxingEnabled = !isExtension && Runtime.experiments.isEnabled('blackboxJSFramesOnTimeline');
     var maxStackDepth = 0;
     for (var i = 0; i < events.length; ++i) {
@@ -328,6 +335,8 @@
       }
 
       var level = this._currentLevel + openEvents.length;
+      if (flowEventsEnabled)
+        this._appendFlowEvent(e, level);
       this._appendEvent(e, level);
       if (!isExtension && TimelineModel.TimelineModel.isMarkerEvent(e))
         this._timelineData.entryTotalTimes[this._entryData.length] = undefined;
@@ -592,7 +601,7 @@
 
     if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord) {
       var color = Timeline.TimelineUIUtils.interactionPhaseColor(
-          /** @type {!TimelineModel.TimelineIRModel.Phases} */ (this._entryData[entryIndex]));
+          /** @type {!TimelineModel.TimelineIRModel.Phases} */ (data));
       context.fillStyle = color;
       context.fillRect(barX, barY, barWidth - 1, 2);
       context.fillRect(barX, barY - 3, 2, 3);
@@ -601,7 +610,7 @@
     }
 
     if (type === Timeline.TimelineFlameChartEntryType.Event) {
-      var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]);
+      var event = /** @type {!SDK.TracingModel.Event} */ (data);
       if (event.hasCategory(TimelineModel.TimelineModel.Category.LatencyInfo)) {
         var timeWaitingForMainThread = TimelineModel.TimelineData.forEvent(event).timeWaitingForMainThread;
         if (timeWaitingForMainThread) {
@@ -643,10 +652,14 @@
    */
   forceDecoration(entryIndex) {
     var type = this._entryType(entryIndex);
-    return type === Timeline.TimelineFlameChartEntryType.Frame ||
-        type === Timeline.TimelineFlameChartEntryType.Event &&
-        !!TimelineModel.TimelineData.forEvent(/** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]))
-              .warning;
+    if (type === Timeline.TimelineFlameChartEntryType.Frame)
+      return true;
+
+    if (type === Timeline.TimelineFlameChartEntryType.Event) {
+      var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]);
+      return !!TimelineModel.TimelineData.forEvent(event).warning;
+    }
+    return false;
   }
 
   /**
@@ -721,6 +734,47 @@
   }
 
   /**
+   * @param {!SDK.TracingModel.Event} event
+   * @param {number} level
+   */
+  _appendFlowEvent(event, level) {
+    var timelineData = this._timelineData;
+    /**
+     * @param {!SDK.TracingModel.Event} event
+     * @return {number}
+     */
+    function pushStartFlow(event) {
+      var flowIndex = timelineData.flowStartTimes.length;
+      timelineData.flowStartTimes.push(event.startTime);
+      timelineData.flowStartLevels.push(level);
+      return flowIndex;
+    }
+
+    /**
+     * @param {!SDK.TracingModel.Event} event
+     * @param {number} flowIndex
+     */
+    function pushEndFlow(event, flowIndex) {
+      timelineData.flowEndTimes[flowIndex] = event.startTime;
+      timelineData.flowEndLevels[flowIndex] = level;
+    }
+
+    switch (event.phase) {
+      case SDK.TracingModel.Phase.FlowBegin:
+        this._flowEventIndexById.set(event.id, pushStartFlow(event));
+        break;
+      case SDK.TracingModel.Phase.FlowStep:
+        pushEndFlow(event, this._flowEventIndexById.get(event.id));
+        this._flowEventIndexById.set(event.id, pushStartFlow(event));
+        break;
+      case SDK.TracingModel.Phase.FlowEnd:
+        pushEndFlow(event, this._flowEventIndexById.get(event.id));
+        this._flowEventIndexById.delete(event.id);
+        break;
+    }
+  }
+
+  /**
    * @param {!TimelineModel.TimelineFrame} frame
    */
   _appendFrame(frame) {
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
index 1dbcfe13..ff89a85 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js
@@ -174,6 +174,9 @@
     eventStyles[recordTypes.GCCollectGarbage] =
         new Timeline.TimelineRecordStyle(Common.UIString('DOM GC'), categories['scripting']);
 
+    eventStyles[recordTypes.AsyncTask] =
+        new Timeline.TimelineRecordStyle(Common.UIString('Async Task'), categories['async']);
+
     Timeline.TimelineUIUtils._eventStylesMap = eventStyles;
     return eventStyles;
   }
@@ -527,6 +530,10 @@
         detailsText = Common.UIString('collect');
         break;
 
+      case recordType.AsyncTask:
+        detailsText = eventData ? eventData['name'] : null;
+        break;
+
       default:
         if (event.hasCategory(TimelineModel.TimelineModel.Category.Console))
           detailsText = null;
@@ -1473,6 +1480,8 @@
           'painting', Common.UIString('Painting'), true, 'hsl(109, 33%, 64%)', 'hsl(109, 33%, 55%)'),
       gpu: new Timeline.TimelineCategory(
           'gpu', Common.UIString('GPU'), false, 'hsl(109, 33%, 64%)', 'hsl(109, 33%, 55%)'),
+      async: new Timeline.TimelineCategory(
+          'async', Common.UIString('Async'), false, 'hsl(0, 100%, 50%)', 'hsl(0, 100%, 40%)'),
       other:
           new Timeline.TimelineCategory('other', Common.UIString('Other'), false, 'hsl(0, 0%, 87%)', 'hsl(0, 0%, 79%)'),
       idle: new Timeline.TimelineCategory('idle', Common.UIString('Idle'), false, 'hsl(0, 0%, 98%)', 'hsl(0, 0%, 98%)')
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js
index 63ba41e5..d9e3925e 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline_model/TimelineModel.js
@@ -1163,7 +1163,9 @@
   // CpuProfile is a virtual event created on frontend to support
   // serialization of CPU Profiles within tracing timeline data.
   CpuProfile: 'CpuProfile',
-  Profile: 'Profile'
+  Profile: 'Profile',
+
+  AsyncTask: 'AsyncTask',
 };
 
 TimelineModel.TimelineModel.Category = {
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js b/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
index 592de32a..06d7a9c6 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/SuggestBox.js
@@ -78,6 +78,21 @@
   }
 
   /**
+   * @param {boolean} value
+   */
+  setDefaultSelectionIsDimmed(value) {
+    this._element.classList.toggle('default-selection-is-dimmed', value);
+  }
+
+  /**
+   * @param {boolean} value
+   */
+  _setUserInteracted(value) {
+    this._userInteracted = value;
+    this._element.classList.toggle('user-has-interacted', value);
+  }
+
+  /**
    * @return {boolean}
    */
   visible() {
@@ -137,7 +152,7 @@
   hide() {
     if (!this.visible())
       return;
-    this._userInteracted = false;
+    this._setUserInteracted(false);
     this._glassPane.hide();
   }
 
@@ -209,7 +224,7 @@
 
     element.addEventListener('click', event => {
       this._list.selectItem(item);
-      this._userInteracted = true;
+      this._setUserInteracted(true);
       event.consume(true);
       this.acceptSuggestion();
     });
@@ -335,7 +350,7 @@
         return false;
     }
     if (selected) {
-      this._userInteracted = true;
+      this._setUserInteracted(true);
       return true;
     }
     return false;
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/suggestBox.css b/third_party/WebKit/Source/devtools/front_end/ui/suggestBox.css
index 816bc72..0a0ec31 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/suggestBox.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/suggestBox.css
@@ -90,6 +90,22 @@
 }
 
 .suggest-box-content-item.selected > span {
+    color: #fff;
+}
+
+.default-selection-is-dimmed .suggest-box-content-item.selected {
+    background-color: whitesmoke;
+}
+
+.default-selection-is-dimmed .suggest-box-content-item.selected > span {
+    color: inherit;
+}
+
+.user-has-interacted .suggest-box-content-item.selected {
+    background-color: rgb(56, 121, 217);
+}
+
+.user-has-interacted .suggest-box-content-item.selected > span {
     color: #FFF;
 }
 
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index a894063..ba5ff4b 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -861,6 +861,8 @@
     "graphics/WebGraphicsContext3DProviderWrapper.h",
     "graphics/compositing/PaintArtifactCompositor.cpp",
     "graphics/compositing/PaintArtifactCompositor.h",
+    "graphics/compositing/PaintChunksToCcLayer.cpp",
+    "graphics/compositing/PaintChunksToCcLayer.h",
     "graphics/compositing/PropertyTreeManager.cpp",
     "graphics/compositing/PropertyTreeManager.h",
     "graphics/cpu/arm/WebGLImageConversionNEON.h",
diff --git a/third_party/WebKit/Source/platform/ScriptForbiddenScope.h b/third_party/WebKit/Source/platform/ScriptForbiddenScope.h
index b8ddadf..4aa19b5 100644
--- a/third_party/WebKit/Source/platform/ScriptForbiddenScope.h
+++ b/third_party/WebKit/Source/platform/ScriptForbiddenScope.h
@@ -44,11 +44,15 @@
     ++s_scriptForbiddenCount;
   }
   static void exit() {
+    DCHECK(isMainThread());
     DCHECK(s_scriptForbiddenCount);
     --s_scriptForbiddenCount;
   }
   static bool isScriptForbidden() {
-    return isMainThread() && s_scriptForbiddenCount;
+    // Check s_scriptForbiddenCount first to avoid any calls to isMainThread()
+    // since under normal situations where we check this (ex. inside
+    // V8ScriptRunner) the value should always be zero.
+    return s_scriptForbiddenCount && isMainThread();
   }
 
  private:
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
index b0927fb..a3588a3 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
@@ -19,6 +19,7 @@
 #include "cc/trees/layer_tree_host.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/graphics/GraphicsContext.h"
+#include "platform/graphics/compositing/PaintChunksToCcLayer.h"
 #include "platform/graphics/compositing/PropertyTreeManager.h"
 #include "platform/graphics/paint/ClipPaintPropertyNode.h"
 #include "platform/graphics/paint/DisplayItem.h"
@@ -222,29 +223,7 @@
   return json;
 }
 
-namespace {
-
-static gfx::Rect largeRect(-200000, -200000, 400000, 400000);
-
-static void appendDisplayItemToCcDisplayItemList(const DisplayItem& displayItem,
-                                                 cc::DisplayItemList* list) {
-  if (DisplayItem::isDrawingType(displayItem.getType())) {
-    const PaintRecord* record =
-        static_cast<const DrawingDisplayItem&>(displayItem).GetPaintRecord();
-    if (!record)
-      return;
-    // In theory we would pass the bounds of the record, previously done as:
-    // gfx::Rect bounds = gfx::SkIRectToRect(record->cullRect().roundOut());
-    // or use the visual rect directly. However, clip content layers attempt
-    // to raster in a different space than that of the visual rects. We'll be
-    // reworking visual rects further for SPv2, so for now we just pass a
-    // visual rect large enough to make sure items raster.
-    list->CreateAndAppendDrawingItem<cc::DrawingDisplayItem>(largeRect,
-                                                             sk_ref_sp(record));
-  }
-}
-
-scoped_refptr<cc::Layer> foreignLayerForPaintChunk(
+static scoped_refptr<cc::Layer> foreignLayerForPaintChunk(
     const PaintArtifact& paintArtifact,
     const PaintChunk& paintChunk,
     gfx::Vector2dF& layerOffset) {
@@ -266,210 +245,6 @@
   return layer;
 }
 
-enum EndDisplayItemType { EndTransform, EndClip, EndEffect };
-
-// Applies the clips between |localState| and |ancestorState| into a single
-// combined cc::FloatClipDisplayItem on |ccList|.
-static void applyClipsBetweenStates(const PropertyTreeState& localState,
-                                    const PropertyTreeState& ancestorState,
-                                    cc::DisplayItemList& ccList,
-                                    Vector<EndDisplayItemType>& endDisplayItems,
-                                    GeometryMapper& geometryMapper) {
-  DCHECK(localState.transform() == ancestorState.transform());
-#if DCHECK_IS_ON()
-  const TransformPaintPropertyNode* transformNode =
-      localState.clip()->localTransformSpace();
-  if (transformNode != ancestorState.transform()) {
-    const TransformationMatrix& localToAncestorMatrix =
-        geometryMapper.localToAncestorMatrix(transformNode,
-                                             ancestorState.transform());
-    // Clips are only in descendant spaces that are transformed by one
-    // or more scrolls.
-    DCHECK(localToAncestorMatrix.isIdentityOrTranslation());
-  }
-#endif
-
-  const FloatClipRect& combinedClip =
-      geometryMapper.localToAncestorClipRect(localState, ancestorState);
-
-  ccList.CreateAndAppendPairedBeginItem<cc::FloatClipDisplayItem>(
-      gfx::RectF(combinedClip.rect()));
-  endDisplayItems.push_back(EndClip);
-}
-
-static void recordPairedBeginDisplayItems(
-    const Vector<PropertyTreeState>& pairedStates,
-    const PropertyTreeState& pendingLayerState,
-    cc::DisplayItemList& ccList,
-    Vector<EndDisplayItemType>& endDisplayItems,
-    GeometryMapper& geometryMapper) {
-  PropertyTreeState mappedClipDestinationSpace = pendingLayerState;
-  PropertyTreeState clipSpace = pendingLayerState;
-  bool hasClip = false;
-
-  for (Vector<PropertyTreeState>::const_reverse_iterator pairedState =
-           pairedStates.rbegin();
-       pairedState != pairedStates.rend(); ++pairedState) {
-    switch (pairedState->innermostNode()) {
-      case PropertyTreeState::Transform: {
-        if (hasClip) {
-          applyClipsBetweenStates(clipSpace, mappedClipDestinationSpace, ccList,
-                                  endDisplayItems, geometryMapper);
-          hasClip = false;
-        }
-        mappedClipDestinationSpace = *pairedState;
-        clipSpace = *pairedState;
-
-        TransformationMatrix matrix = pairedState->transform()->matrix();
-        matrix.applyTransformOrigin(pairedState->transform()->origin());
-
-        gfx::Transform transform(gfx::Transform::kSkipInitialization);
-        transform.matrix() = TransformationMatrix::toSkMatrix44(matrix);
-
-        ccList.CreateAndAppendPairedBeginItem<cc::TransformDisplayItem>(
-            transform);
-        endDisplayItems.push_back(EndTransform);
-        break;
-      }
-      case PropertyTreeState::Clip: {
-        // Clips are handled in |applyClips| when ending the iterator, or
-        // transitioning between transform spaces. Here we store off the
-        // PropertyTreeState of the first found clip, under the transform of
-        // pairedState->transform(). All subsequent clips before applying the
-        // transform will be applied in applyClips.
-        clipSpace = *pairedState;
-        hasClip = true;
-#if DCHECK_IS_ON()
-        if (pairedState->clip()->localTransformSpace() !=
-            pairedState->transform()) {
-          const TransformationMatrix& localTransformMatrix =
-              pairedState->effect()->localTransformSpace()->matrix();
-          // Clips are only in descendant spaces that are transformed by scroll.
-          DCHECK(localTransformMatrix.isIdentityOrTranslation());
-        }
-#endif
-        break;
-      }
-      case PropertyTreeState::Effect: {
-        // TODO(chrishtr): skip effect and/or compositing display items if
-        // not necessary.
-
-        FloatRect clipRect =
-            pairedState->effect()->outputClip()->clipRect().rect();
-        // TODO(chrishtr): specify origin of the filter.
-        FloatPoint filterOrigin;
-        if (pairedState->effect()->localTransformSpace() !=
-            pairedState->transform()) {
-          const TransformPaintPropertyNode* transformNode =
-              pairedState->effect()->localTransformSpace();
-          const TransformationMatrix& localToAncestorMatrix =
-              geometryMapper.localToAncestorMatrix(transformNode,
-                                                   pairedState->transform());
-          // Effects are only in descendant spaces that are transformed by one
-          // or more scrolls.
-          DCHECK(localToAncestorMatrix.isIdentityOrTranslation());
-
-          clipRect = localToAncestorMatrix.mapRect(clipRect);
-          filterOrigin = localToAncestorMatrix.mapPoint(filterOrigin);
-        }
-
-        const bool kLcdTextRequiresOpaqueLayer = true;
-        ccList.CreateAndAppendPairedBeginItem<cc::CompositingDisplayItem>(
-            static_cast<uint8_t>(
-                gfx::ToFlooredInt(255 * pairedState->effect()->opacity())),
-            pairedState->effect()->blendMode(),
-            // TODO(chrishtr): compute bounds as necessary.
-            nullptr, GraphicsContext::WebCoreColorFilterToSkiaColorFilter(
-                         pairedState->effect()->colorFilter()),
-            kLcdTextRequiresOpaqueLayer);
-
-        ccList.CreateAndAppendPairedBeginItem<cc::FilterDisplayItem>(
-            pairedState->effect()->filter().asCcFilterOperations(), clipRect,
-            gfx::PointF(filterOrigin.x(), filterOrigin.y()));
-
-        endDisplayItems.push_back(EndEffect);
-        break;
-      }
-      case PropertyTreeState::None:
-        break;
-    }
-  }
-
-  if (hasClip) {
-    applyClipsBetweenStates(clipSpace, mappedClipDestinationSpace, ccList,
-                            endDisplayItems, geometryMapper);
-  }
-}
-
-static void recordPairedEndDisplayItems(
-    const Vector<EndDisplayItemType>& endDisplayItemTypes,
-    cc::DisplayItemList* ccList) {
-  for (Vector<EndDisplayItemType>::const_reverse_iterator endType =
-           endDisplayItemTypes.rbegin();
-       endType != endDisplayItemTypes.rend(); ++endType) {
-    switch (*endType) {
-      case EndTransform:
-        ccList->CreateAndAppendPairedEndItem<cc::EndTransformDisplayItem>();
-        break;
-      case EndClip:
-        ccList->CreateAndAppendPairedEndItem<cc::EndFloatClipDisplayItem>();
-        break;
-      case EndEffect:
-        ccList->CreateAndAppendPairedEndItem<cc::EndFilterDisplayItem>();
-        ccList->CreateAndAppendPairedEndItem<cc::EndCompositingDisplayItem>();
-        break;
-    }
-  }
-}
-
-}  // namespace
-
-scoped_refptr<cc::DisplayItemList> PaintArtifactCompositor::recordPendingLayer(
-    const PaintArtifact& artifact,
-    const PendingLayer& pendingLayer,
-    const gfx::Rect& combinedBounds,
-    GeometryMapper& geometryMapper) {
-  auto ccList = make_scoped_refptr(new cc::DisplayItemList);
-
-  gfx::Transform translation;
-  translation.Translate(-combinedBounds.x(), -combinedBounds.y());
-  // Passing combinedBounds as the visual rect for the begin/end transform item
-  // would normally be the sensible thing to do, but see comment above re:
-  // visual rects for drawing items and further rework in flight.
-  ccList->CreateAndAppendPairedBeginItem<cc::TransformDisplayItem>(translation);
-
-  const DisplayItemList& displayItems = artifact.getDisplayItemList();
-  for (const auto& paintChunk : pendingLayer.paintChunks) {
-    const PropertyTreeState* state = &paintChunk->properties.propertyTreeState;
-    PropertyTreeStateIterator iterator(*state);
-    Vector<PropertyTreeState> pairedStates;
-    for (; state && *state != pendingLayer.propertyTreeState;
-         state = iterator.next()) {
-      if (state->innermostNode() != PropertyTreeState::None)
-        pairedStates.push_back(*state);
-    }
-
-    // TODO(chrishtr): we can avoid some extra paired display items if
-    // multiple PaintChunks share them. We can also collapse clips between
-    // transforms into single clips in the same way that PaintLayerClipper does.
-    Vector<EndDisplayItemType> endDisplayItems;
-
-    recordPairedBeginDisplayItems(pairedStates, pendingLayer.propertyTreeState,
-                                  *ccList.get(), endDisplayItems,
-                                  geometryMapper);
-
-    for (const auto& displayItem : displayItems.itemsInPaintChunk(*paintChunk))
-      appendDisplayItemToCcDisplayItemList(displayItem, ccList.get());
-
-    recordPairedEndDisplayItems(endDisplayItems, ccList.get());
-  }
-
-  ccList->CreateAndAppendPairedEndItem<cc::EndTransformDisplayItem>();
-
-  ccList->Finalize();
-  return ccList;
-}
-
 std::unique_ptr<PaintArtifactCompositor::ContentLayerClientImpl>
 PaintArtifactCompositor::clientForPaintChunk(
     const PaintChunk& paintChunk,
@@ -514,13 +289,14 @@
 
   gfx::Rect ccCombinedBounds(enclosingIntRect(pendingLayer.bounds));
 
-  scoped_refptr<cc::DisplayItemList> displayList = recordPendingLayer(
-      paintArtifact, pendingLayer, ccCombinedBounds, geometryMapper);
+  layerOffset = ccCombinedBounds.OffsetFromOrigin();
+  scoped_refptr<cc::DisplayItemList> displayList =
+      PaintChunksToCcLayer::convert(
+          pendingLayer.paintChunks, pendingLayer.propertyTreeState, layerOffset,
+          paintArtifact.getDisplayItemList(), geometryMapper);
   contentLayerClient->SetDisplayList(std::move(displayList));
   contentLayerClient->SetPaintableRegion(gfx::Rect(ccCombinedBounds.size()));
 
-  layerOffset = ccCombinedBounds.OffsetFromOrigin();
-
   scoped_refptr<cc::PictureLayer> ccPictureLayer =
       contentLayerClient->ccPictureLayer();
   ccPictureLayer->SetBounds(ccCombinedBounds.size());
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.h b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.h
index 50aed96..c5464c8 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.h
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.h
@@ -16,7 +16,6 @@
 #include <memory>
 
 namespace cc {
-class DisplayItemList;
 class Layer;
 }
 
@@ -135,13 +134,6 @@
       const PaintChunk&,
       const PaintArtifact&);
 
-  // This method is an implementation of Algorithm step 4 from goo.gl/6xP8Oe.
-  static scoped_refptr<cc::DisplayItemList> recordPendingLayer(
-      const PaintArtifact&,
-      const PendingLayer&,
-      const gfx::Rect& combinedBounds,
-      GeometryMapper&);
-
   static bool canMergeInto(const PaintArtifact&,
                            const PaintChunk& newChunk,
                            const PendingLayer& candidatePendingLayer);
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
new file mode 100644
index 0000000..0abb746
--- /dev/null
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.cpp
@@ -0,0 +1,244 @@
+// 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 "platform/graphics/compositing/PaintChunksToCcLayer.h"
+
+#include "cc/playback/compositing_display_item.h"
+#include "cc/playback/display_item_list.h"
+#include "cc/playback/drawing_display_item.h"
+#include "cc/playback/filter_display_item.h"
+#include "cc/playback/float_clip_display_item.h"
+#include "cc/playback/transform_display_item.h"
+#include "platform/graphics/GraphicsContext.h"
+#include "platform/graphics/paint/DisplayItemList.h"
+#include "platform/graphics/paint/DrawingDisplayItem.h"
+#include "platform/graphics/paint/GeometryMapper.h"
+#include "platform/graphics/paint/PaintChunk.h"
+#include "platform/graphics/paint/PropertyTreeState.h"
+
+namespace blink {
+
+namespace {
+
+enum EndDisplayItemType { EndTransform, EndClip, EndEffect };
+
+// Applies the clips between |localState| and |ancestorState| into a single
+// combined cc::FloatClipDisplayItem on |ccList|.
+static void applyClipsBetweenStates(const PropertyTreeState& localState,
+                                    const PropertyTreeState& ancestorState,
+                                    cc::DisplayItemList& ccList,
+                                    Vector<EndDisplayItemType>& endDisplayItems,
+                                    GeometryMapper& geometryMapper) {
+  DCHECK(localState.transform() == ancestorState.transform());
+#if DCHECK_IS_ON()
+  const TransformPaintPropertyNode* transformNode =
+      localState.clip()->localTransformSpace();
+  if (transformNode != ancestorState.transform()) {
+    const TransformationMatrix& localToAncestorMatrix =
+        geometryMapper.localToAncestorMatrix(transformNode,
+                                             ancestorState.transform());
+    // Clips are only in descendant spaces that are transformed by one
+    // or more scrolls.
+    DCHECK(localToAncestorMatrix.isIdentityOrTranslation());
+  }
+#endif
+
+  const FloatClipRect& combinedClip =
+      geometryMapper.localToAncestorClipRect(localState, ancestorState);
+
+  ccList.CreateAndAppendPairedBeginItem<cc::FloatClipDisplayItem>(
+      gfx::RectF(combinedClip.rect()));
+  endDisplayItems.push_back(EndClip);
+}
+
+static void recordPairedBeginDisplayItems(
+    const Vector<PropertyTreeState>& pairedStates,
+    const PropertyTreeState& pendingLayerState,
+    cc::DisplayItemList& ccList,
+    Vector<EndDisplayItemType>& endDisplayItems,
+    GeometryMapper& geometryMapper) {
+  PropertyTreeState mappedClipDestinationSpace = pendingLayerState;
+  PropertyTreeState clipSpace = pendingLayerState;
+  bool hasClip = false;
+
+  for (Vector<PropertyTreeState>::const_reverse_iterator pairedState =
+           pairedStates.rbegin();
+       pairedState != pairedStates.rend(); ++pairedState) {
+    switch (pairedState->innermostNode()) {
+      case PropertyTreeState::Transform: {
+        if (hasClip) {
+          applyClipsBetweenStates(clipSpace, mappedClipDestinationSpace, ccList,
+                                  endDisplayItems, geometryMapper);
+          hasClip = false;
+        }
+        mappedClipDestinationSpace = *pairedState;
+        clipSpace = *pairedState;
+
+        TransformationMatrix matrix = pairedState->transform()->matrix();
+        matrix.applyTransformOrigin(pairedState->transform()->origin());
+
+        gfx::Transform transform(gfx::Transform::kSkipInitialization);
+        transform.matrix() = TransformationMatrix::toSkMatrix44(matrix);
+
+        ccList.CreateAndAppendPairedBeginItem<cc::TransformDisplayItem>(
+            transform);
+        endDisplayItems.push_back(EndTransform);
+        break;
+      }
+      case PropertyTreeState::Clip: {
+        // Clips are handled in |applyClips| when ending the iterator, or
+        // transitioning between transform spaces. Here we store off the
+        // PropertyTreeState of the first found clip, under the transform of
+        // pairedState->transform(). All subsequent clips before applying the
+        // transform will be applied in applyClips.
+        clipSpace = *pairedState;
+        hasClip = true;
+#if DCHECK_IS_ON()
+        if (pairedState->clip()->localTransformSpace() !=
+            pairedState->transform()) {
+          const TransformationMatrix& localTransformMatrix =
+              pairedState->effect()->localTransformSpace()->matrix();
+          // Clips are only in descendant spaces that are transformed by scroll.
+          DCHECK(localTransformMatrix.isIdentityOrTranslation());
+        }
+#endif
+        break;
+      }
+      case PropertyTreeState::Effect: {
+        // TODO(chrishtr): skip effect and/or compositing display items if
+        // not necessary.
+
+        FloatRect clipRect =
+            pairedState->effect()->outputClip()->clipRect().rect();
+        // TODO(chrishtr): specify origin of the filter.
+        FloatPoint filterOrigin;
+        if (pairedState->effect()->localTransformSpace() !=
+            pairedState->transform()) {
+          const TransformPaintPropertyNode* transformNode =
+              pairedState->effect()->localTransformSpace();
+          const TransformationMatrix& localToAncestorMatrix =
+              geometryMapper.localToAncestorMatrix(transformNode,
+                                                   pairedState->transform());
+          // Effects are only in descendant spaces that are transformed by one
+          // or more scrolls.
+          DCHECK(localToAncestorMatrix.isIdentityOrTranslation());
+
+          clipRect = localToAncestorMatrix.mapRect(clipRect);
+          filterOrigin = localToAncestorMatrix.mapPoint(filterOrigin);
+        }
+
+        const bool kLcdTextRequiresOpaqueLayer = true;
+        ccList.CreateAndAppendPairedBeginItem<cc::CompositingDisplayItem>(
+            static_cast<uint8_t>(
+                gfx::ToFlooredInt(255 * pairedState->effect()->opacity())),
+            pairedState->effect()->blendMode(),
+            // TODO(chrishtr): compute bounds as necessary.
+            nullptr,
+            GraphicsContext::WebCoreColorFilterToSkiaColorFilter(
+                pairedState->effect()->colorFilter()),
+            kLcdTextRequiresOpaqueLayer);
+
+        ccList.CreateAndAppendPairedBeginItem<cc::FilterDisplayItem>(
+            pairedState->effect()->filter().asCcFilterOperations(), clipRect,
+            gfx::PointF(filterOrigin.x(), filterOrigin.y()));
+
+        endDisplayItems.push_back(EndEffect);
+        break;
+      }
+      case PropertyTreeState::None:
+        break;
+    }
+  }
+
+  if (hasClip) {
+    applyClipsBetweenStates(clipSpace, mappedClipDestinationSpace, ccList,
+                            endDisplayItems, geometryMapper);
+  }
+}
+
+static void recordPairedEndDisplayItems(
+    const Vector<EndDisplayItemType>& endDisplayItemTypes,
+    cc::DisplayItemList* ccList) {
+  for (Vector<EndDisplayItemType>::const_reverse_iterator endType =
+           endDisplayItemTypes.rbegin();
+       endType != endDisplayItemTypes.rend(); ++endType) {
+    switch (*endType) {
+      case EndTransform:
+        ccList->CreateAndAppendPairedEndItem<cc::EndTransformDisplayItem>();
+        break;
+      case EndClip:
+        ccList->CreateAndAppendPairedEndItem<cc::EndFloatClipDisplayItem>();
+        break;
+      case EndEffect:
+        ccList->CreateAndAppendPairedEndItem<cc::EndFilterDisplayItem>();
+        ccList->CreateAndAppendPairedEndItem<cc::EndCompositingDisplayItem>();
+        break;
+    }
+  }
+}
+
+static gfx::Rect largeRect(-200000, -200000, 400000, 400000);
+static void appendDisplayItemToCcDisplayItemList(const DisplayItem& displayItem,
+                                                 cc::DisplayItemList* list) {
+  if (DisplayItem::isDrawingType(displayItem.getType())) {
+    const PaintRecord* record =
+        static_cast<const DrawingDisplayItem&>(displayItem).GetPaintRecord();
+    if (!record)
+      return;
+    // In theory we would pass the bounds of the record, previously done as:
+    // gfx::Rect bounds = gfx::SkIRectToRect(record->cullRect().roundOut());
+    // or use the visual rect directly. However, clip content layers attempt
+    // to raster in a different space than that of the visual rects. We'll be
+    // reworking visual rects further for SPv2, so for now we just pass a
+    // visual rect large enough to make sure items raster.
+    list->CreateAndAppendDrawingItem<cc::DrawingDisplayItem>(largeRect,
+                                                             sk_ref_sp(record));
+  }
+}
+
+}  // unnamed namespace
+
+scoped_refptr<cc::DisplayItemList> PaintChunksToCcLayer::convert(
+    const Vector<const PaintChunk*>& paintChunks,
+    const PropertyTreeState& layerState,
+    const gfx::Vector2dF& layerOffset,
+    const DisplayItemList& displayItems,
+    GeometryMapper& geometryMapper) {
+  auto ccList = make_scoped_refptr(new cc::DisplayItemList);
+
+  gfx::Transform counterOffset;
+  counterOffset.Translate(-layerOffset.x(), -layerOffset.y());
+  ccList->CreateAndAppendPairedBeginItem<cc::TransformDisplayItem>(
+      counterOffset);
+
+  for (const auto& paintChunk : paintChunks) {
+    const PropertyTreeState* state = &paintChunk->properties.propertyTreeState;
+    PropertyTreeStateIterator iterator(*state);
+    Vector<PropertyTreeState> pairedStates;
+    for (; state && *state != layerState; state = iterator.next()) {
+      if (state->innermostNode() != PropertyTreeState::None)
+        pairedStates.push_back(*state);
+    }
+
+    // TODO(chrishtr): we can avoid some extra paired display items if
+    // multiple PaintChunks share them. We can also collapse clips between
+    // transforms into single clips in the same way that PaintLayerClipper does.
+    Vector<EndDisplayItemType> endDisplayItems;
+
+    recordPairedBeginDisplayItems(pairedStates, layerState, *ccList.get(),
+                                  endDisplayItems, geometryMapper);
+
+    for (const auto& displayItem : displayItems.itemsInPaintChunk(*paintChunk))
+      appendDisplayItemToCcDisplayItemList(displayItem, ccList.get());
+
+    recordPairedEndDisplayItems(endDisplayItems, ccList.get());
+  }
+
+  ccList->CreateAndAppendPairedEndItem<cc::EndTransformDisplayItem>();
+
+  ccList->Finalize();
+  return ccList;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.h b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.h
new file mode 100644
index 0000000..3c9a095
--- /dev/null
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintChunksToCcLayer.h
@@ -0,0 +1,39 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PaintChunksToCcLayer_h
+#define PaintChunksToCcLayer_h
+
+#include "base/memory/ref_counted.h"
+#include "platform/PlatformExport.h"
+#include "wtf/Vector.h"
+
+namespace cc {
+class DisplayItemList;
+}  // namespace cc
+
+namespace gfx {
+class Vector2dF;
+}  // namespace gfx
+
+namespace blink {
+
+class DisplayItemList;
+class GeometryMapper;
+struct PaintChunk;
+class PropertyTreeState;
+
+class PLATFORM_EXPORT PaintChunksToCcLayer {
+ public:
+  static scoped_refptr<cc::DisplayItemList> convert(
+      const Vector<const PaintChunk*>&,
+      const PropertyTreeState& layerState,
+      const gfx::Vector2dF& layerOffset,
+      const DisplayItemList&,
+      GeometryMapper&);
+};
+
+}  // namespace blink
+
+#endif  // PaintArtifactCompositor_h
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.cpp b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.cpp
index 6258d642..55f018c 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.cpp
@@ -373,6 +373,14 @@
        it++) {
     TransformationMatrix localTransformMatrix = (*it)->matrix();
     localTransformMatrix.applyTransformOrigin((*it)->origin());
+
+    // Flattening Lemma: flatten(A * flatten(B)) = flatten(flatten(A) * B).
+    // goo.gl/DNKyOc. Thus we can flatten transformMatrix rather than
+    // localTransformMatrix, because GeometryMapper only supports transforms
+    // into a flattened destination space.
+    if ((*it)->flattensInheritedTransform())
+      transformMatrix.flattenTo2d();
+
     transformMatrix = transformMatrix * localTransformMatrix;
     (*it)->getTransformCache().setCachedTransform(ancestorTransformNode,
                                                   transformMatrix);
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h
index c5a8ec36..0b414c5e 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapper.h
@@ -113,6 +113,10 @@
   // Returns the matrix used in |LocalToAncestorRect|. DCHECK fails iff
   // |localTransformNode| is not equal to or a descendant of
   // |ancestorTransformNode|.
+  // This matrix may not be flattened. Since GeometryMapper only supports
+  // flattened ancestor spaces, the returned matrix must be flattened to have
+  // the correct semantics (calling mapRect() on it implicitly applies
+  // flattening to the input; flattenTo2d() does it explicitly to tme matrix).
   const TransformationMatrix& localToAncestorMatrix(
       const TransformPaintPropertyNode* localTransformNode,
       const TransformPaintPropertyNode* ancestorTransformNode);
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp
index 7c44f7b1..cdda820d 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp
@@ -276,6 +276,32 @@
       *getTransform(transform1.get(), rootPropertyTreeState().transform()));
 }
 
+TEST_F(GeometryMapperTest, NestedTransformsFlattening) {
+  TransformationMatrix rotateTransform;
+  rotateTransform.rotate3d(45, 0, 0);
+  RefPtr<TransformPaintPropertyNode> transform1 =
+      TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
+                                         rotateTransform, FloatPoint3D());
+
+  TransformationMatrix inverseRotateTransform;
+  inverseRotateTransform.rotate3d(-45, 0, 0);
+  RefPtr<TransformPaintPropertyNode> transform2 =
+      TransformPaintPropertyNode::create(transform1, inverseRotateTransform,
+                                         FloatPoint3D(),
+                                         true);  // Flattens
+
+  PropertyTreeState localState = rootPropertyTreeState();
+  localState.setTransform(transform2.get());
+
+  FloatRect input(0, 0, 100, 100);
+  rotateTransform.flattenTo2d();
+  TransformationMatrix final = rotateTransform * inverseRotateTransform;
+  FloatRect output = final.mapRect(input);
+  bool hasRadius = false;
+  CHECK_MAPPINGS(input, output, output, final, FloatClipRect(), localState,
+                 rootPropertyTreeState(), hasRadius);
+}
+
 TEST_F(GeometryMapperTest, NestedTransformsScaleAndTranslation) {
   TransformationMatrix scaleTransform;
   scaleTransform.scale(2);
@@ -819,11 +845,11 @@
   EXPECT_EQ(FloatRect(-150, -150, 450, 450), output);
 
   bool hasRadius = false;
-  CHECK_MAPPINGS(
-      input, output, FloatRect(0, 0, 300, 300),
-      transformAboveEffect->matrix() * transformBelowEffect->matrix(),
-      FloatClipRect(FloatRect(30, 30, 270, 270)), localState,
-      rootPropertyTreeState(), hasRadius);
+  TransformationMatrix combinedTransform =
+      transformAboveEffect->matrix() * transformBelowEffect->matrix();
+  CHECK_MAPPINGS(input, output, FloatRect(0, 0, 300, 300), combinedTransform,
+                 FloatClipRect(FloatRect(30, 30, 270, 270)), localState,
+                 rootPropertyTreeState(), hasRadius);
 }
 
 TEST_F(GeometryMapperTest, ReflectionWithPaintOffset) {
diff --git a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTransformCache.h b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTransformCache.h
index 9d08f72..7b82beca 100644
--- a/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTransformCache.h
+++ b/third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTransformCache.h
@@ -24,6 +24,8 @@
   // Returns the transformed rect (see GeometryMapper.h) of |this| in the
   // space of |ancestorTransform|, if there is one cached. Otherwise returns
   // null.
+  //
+  // These transforms are not flattened to 2d.
   const TransformationMatrix* getCachedTransform(
       const TransformPaintPropertyNode* ancestorTransform);
 
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index aedcb02..0ed10fe 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -8994,11 +8994,29 @@
   <description>The user entered fullscreen mode from the controls.</description>
 </action>
 
+<action name="Media.Controls.EnterFullscreen.EmbeddedExperience">
+  <owner>shaktisahu@chromium.org</owner>
+  <description>
+    The user has entered fullscreen mode from the controls. Recorded only for
+    downloaded media on Android and is a subset of
+    Media.Controls.EnterFullscreen.
+  </description>
+</action>
+
 <action name="Media.Controls.ExitFullscreen">
   <owner>mlamouri@chromium.org</owner>
   <description>The user left fullscreen mode from the controls.</description>
 </action>
 
+<action name="Media.Controls.ExitFullscreen.EmbeddedExperience">
+  <owner>shaktisahu@chromium.org</owner>
+  <description>
+    The user has left fullscreen mode from the controls. Recorded only for
+    downloaded media on Android and is a subset of
+    Media.Controls.ExitFullscreen.
+  </description>
+</action>
+
 <action name="Media.Controls.Mute">
   <owner>mlamouri@chromium.org</owner>
   <description>The user muted a media element from the controls.</description>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index a6958aa8..35bf3ee 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -117404,6 +117404,9 @@
       label="Watch time for SRC media with only an audio track."/>
   <suffix name="Audio.EME"
       label="Watch time for EME media with only an audio track."/>
+  <suffix name="Audio.EmbeddedExperience."
+      label="Watch time for downloaded media on Android with only an audio
+             track."/>
   <suffix name="AudioVideo.All"
       label="Watch time for all media with both an audio and video track."/>
   <suffix name="AudioVideo.AC"
@@ -117418,6 +117421,9 @@
       label="Watch time for SRC media with both an audio and video track."/>
   <suffix name="AudioVideo.EME"
       label="Watch time for EME media with both an audio and video track."/>
+  <suffix name="AudioVideo.EmbeddedExperience."
+      label="Watch time for downloaded media on Android with both an audio
+             and video track."/>
   <affected-histogram name="Media.WatchTime"/>
 </histogram_suffixes>
 
diff --git a/tools/vim/PRESUBMIT.py b/tools/vim/PRESUBMIT.py
index 33cdd6d..1cd25f9 100644
--- a/tools/vim/PRESUBMIT.py
+++ b/tools/vim/PRESUBMIT.py
@@ -1,12 +1,12 @@
 # Copyright 2015 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """Presubmit tests for /tools/vim.
 
 Runs Python unit tests in /tools/vim/tests on upload.
 """
 
+
 def CheckChangeOnUpload(input_api, output_api):
   results = []
 
@@ -14,7 +14,8 @@
   # relative to the directory containing PRESUBMIT.py.
   affected_files = [
       input_api.os_path.relpath(f, input_api.PresubmitLocalPath())
-      for f in input_api.AbsoluteLocalPaths()]
+      for f in input_api.AbsoluteLocalPaths()
+  ]
 
   # Run the chromium.ycm_extra_conf_unittest test if the YCM config file is
   # changed or if any change is affecting the tests directory. This specific
@@ -24,8 +25,8 @@
       'ninja_output.py' in affected_files or \
       any([input_api.re.match(r'tests(/|\\)',f) for f in affected_files]):
     results += input_api.RunTests(
-        input_api.canned_checks.GetUnitTests(
-            input_api, output_api,
-            ['tests/chromium.ycm_extra_conf_unittest.py']))
+        input_api.canned_checks.GetUnitTests(input_api, output_api, [
+            'tests/chromium.ycm_extra_conf_unittest.py'
+        ]))
 
   return results
diff --git a/tools/vim/chromium.ycm_extra_conf.py b/tools/vim/chromium.ycm_extra_conf.py
index 9456394..2669573 100644
--- a/tools/vim/chromium.ycm_extra_conf.py
+++ b/tools/vim/chromium.ycm_extra_conf.py
@@ -47,7 +47,6 @@
 #
 #   * This has only been tested on gPrecise.
 
-
 import os
 import os.path
 import re
@@ -57,19 +56,20 @@
 
 # Flags from YCM's default config.
 _default_flags = [
-  '-DUSE_CLANG_COMPLETER',
-  '-std=c++11',
-  '-x',
-  'c++',
+    '-DUSE_CLANG_COMPLETER',
+    '-std=c++11',
+    '-x',
+    'c++',
 ]
 
 _header_alternates = ('.cc', '.cpp', '.c', '.mm', '.m')
 
 _extension_flags = {
-  '.m': ['-x', 'objective-c'],
-  '.mm': ['-x', 'objective-c++'],
+    '.m': ['-x', 'objective-c'],
+    '.mm': ['-x', 'objective-c++'],
 }
 
+
 def PathExists(*args):
   return os.path.exists(os.path.join(*args))
 
@@ -86,10 +86,9 @@
     (String) Path of 'src/', or None if unable to find.
   """
   curdir = os.path.normpath(os.path.dirname(filename))
-  while not (os.path.basename(curdir) == 'src'
-             and PathExists(curdir, 'DEPS')
-             and (PathExists(curdir, '..', '.gclient')
-                  or PathExists(curdir, '.git'))):
+  while not (
+      os.path.basename(curdir) == 'src' and PathExists(curdir, 'DEPS') and
+      (PathExists(curdir, '..', '.gclient') or PathExists(curdir, '.git'))):
     nextdir = os.path.normpath(os.path.join(curdir, '..'))
     if nextdir == curdir:
       return None
@@ -138,9 +137,11 @@
   # directory.
   rel_filename = os.path.relpath(filename, out_dir)
 
-  p = subprocess.Popen(['ninja', '-C', out_dir, '-t', 'query', rel_filename],
-                       stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
-                       universal_newlines=True)
+  p = subprocess.Popen(
+      ['ninja', '-C', out_dir, '-t', 'query', rel_filename],
+      stdout=subprocess.PIPE,
+      stderr=subprocess.STDOUT,
+      universal_newlines=True)
   stdout, _ = p.communicate()
   if p.returncode != 0:
     return []
@@ -154,8 +155,10 @@
   #
   outputs_text = stdout.partition('\n  outputs:\n')[2]
   output_lines = [line.strip() for line in outputs_text.split('\n')]
-  return [target for target in output_lines
-          if target and (target.endswith('.o') or target.endswith('.obj'))]
+  return [
+      target for target in output_lines
+      if target and (target.endswith('.o') or target.endswith('.obj'))
+  ]
 
 
 def GetClangCommandLineForNinjaOutput(out_dir, build_target):
@@ -172,9 +175,10 @@
     (String or None) Clang command line or None if a Clang command line couldn't
         be determined.
   """
-  p = subprocess.Popen(['ninja', '-v', '-C', out_dir,
-                        '-t', 'commands', build_target],
-                       stdout=subprocess.PIPE, universal_newlines=True)
+  p = subprocess.Popen(
+      ['ninja', '-v', '-C', out_dir, '-t', 'commands', build_target],
+      stdout=subprocess.PIPE,
+      universal_newlines=True)
   stdout, stderr = p.communicate()
   if p.returncode != 0:
     return None
@@ -319,8 +323,10 @@
     # If ninja didn't know about filename or it's companion files, then try a
     # default build target. It is possible that the file is new, or build.ninja
     # is stale.
-    clang_line = GetClangCommandLineFromNinjaForSource(
-        out_dir, GetDefaultSourceFile(chrome_root, filename))
+    clang_line = GetClangCommandLineFromNinjaForSource(out_dir,
+                                                       GetDefaultSourceFile(
+                                                           chrome_root,
+                                                           filename))
 
   if not clang_line:
     return additional_flags
@@ -350,7 +356,4 @@
 
   final_flags = _default_flags + clang_flags
 
-  return {
-    'flags': final_flags,
-    'do_cache': should_cache_flags_for_file
-  }
+  return {'flags': final_flags, 'do_cache': should_cache_flags_for_file}
diff --git a/tools/vim/ninja_output.py b/tools/vim/ninja_output.py
index f959dc2..6695fcfa 100644
--- a/tools/vim/ninja_output.py
+++ b/tools/vim/ninja_output.py
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-
 import sys
 import os
 import itertools
@@ -13,6 +12,7 @@
 except ImportError:
   pass
 
+
 def GetNinjaOutputDirectory(chrome_root):
   """Returns <chrome_root>/<output_dir>/(Release|Debug|<other>).
 
@@ -65,8 +65,8 @@
   try:
     return max(generate_paths(), key=approx_directory_mtime)
   except ValueError:
-    raise RuntimeError(
-      'Unable to find a valid ninja output directory.')
+    raise RuntimeError('Unable to find a valid ninja output directory.')
+
 
 if __name__ == '__main__':
   if len(sys.argv) != 2:
diff --git a/tools/vim/tests/chromium.ycm_extra_conf_unittest.py b/tools/vim/tests/chromium.ycm_extra_conf_unittest.py
index 3443fa3..5ed29e91 100755
--- a/tools/vim/tests/chromium.ycm_extra_conf_unittest.py
+++ b/tools/vim/tests/chromium.ycm_extra_conf_unittest.py
@@ -3,7 +3,6 @@
 # Copyright 2015 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """Tests for chromium.ycm_extra_conf.
 
 These tests should be getting picked up by the PRESUBMIT.py in /tools/vim.
@@ -21,10 +20,8 @@
 import tempfile
 import unittest
 
-def CreateFile(path,
-               copy_from = None,
-               format_with = None,
-               make_executable = False):
+
+def CreateFile(path, copy_from=None, format_with=None, make_executable=False):
   """Creates a file.
 
   If a file already exists at |path|, it will be overwritten.
@@ -53,21 +50,26 @@
     statinfo = os.stat(path)
     os.chmod(path, statinfo.st_mode | stat.S_IXUSR)
 
+
 def GetLastLangFlag(flags):
   lastLang = None
   for i, flag in enumerate(flags):
-      if flag =='-x':
-        lastLang = flags[i+1]
+    if flag == '-x':
+      lastLang = flags[i + 1]
   return lastLang
 
+
 def TestLanguage(test_file, language):
+
   def test(self):
     result = self.ycm_extra_conf.FlagsForFile(
         os.path.join(self.chrome_root, test_file))
     self.assertTrue(result)
     self.assertEqual(GetLastLangFlag(result['flags']), language)
+
   return test
 
+
 class Chromium_ycmExtraConfTest(unittest.TestCase):
 
   def SetUpFakeChromeTreeBelowPath(self):
@@ -89,8 +91,8 @@
           +-- gn
                 build.ninja
     """
-    self.chrome_root = os.path.abspath(os.path.normpath(
-        os.path.join(self.test_root, 'src')))
+    self.chrome_root = os.path.abspath(
+        os.path.normpath(os.path.join(self.test_root, 'src')))
     self.out_dir = os.path.join(self.chrome_root, 'out', 'gn')
 
     os.makedirs(self.chrome_root)
@@ -104,9 +106,9 @@
     # Fake ninja build file. Applications of 'cxx' rule are tagged by which
     # source file was used as input so that the test can verify that the correct
     # build dependency was used.
-    CreateFile(os.path.join(self.out_dir, 'build.ninja'),
-               copy_from=os.path.join(self.test_data_path,
-                                      'fake_build_ninja.txt'))
+    CreateFile(
+        os.path.join(self.out_dir, 'build.ninja'),
+        copy_from=os.path.join(self.test_data_path, 'fake_build_ninja.txt'))
 
   def NormalizeString(self, string):
     return string.replace(self.out_dir, '[OUT]').\
@@ -147,8 +149,8 @@
   def testCommandLineForKnownCppFile(self):
     command_line = self.ycm_extra_conf.GetClangCommandLineFromNinjaForSource(
         self.out_dir, os.path.join(self.chrome_root, 'one.cpp'))
-    self.assertEquals(
-        command_line, ('../../fake-clang++ -Ia -isysroot /mac.sdk -Itag-one '
+    self.assertEquals(command_line,
+                      ('../../fake-clang++ -Ia -isysroot /mac.sdk -Itag-one '
                        '../../one.cpp -o obj/one.o'))
 
   def testCommandLineForUnknownCppFile(self):
@@ -160,28 +162,24 @@
     clang_options = \
         self.ycm_extra_conf.GetClangOptionsFromNinjaForFilename(
             self.chrome_root, os.path.join(self.chrome_root, 'one.cpp'))
-    self.assertEquals(self.NormalizeStringsInList(clang_options), [
-        '-I[SRC]',
-        '-Wno-unknown-warning-option',
-        '-I[OUT]/a',
-        '-isysroot',
-        '/mac.sdk',
-        '-I[OUT]/tag-one'
+    self.assertEquals(
+        self.NormalizeStringsInList(clang_options), [
+            '-I[SRC]', '-Wno-unknown-warning-option', '-I[OUT]/a', '-isysroot',
+            '/mac.sdk', '-I[OUT]/tag-one'
         ])
 
   def testOutDirNames(self):
     out_root = os.path.join(self.chrome_root, 'out_with_underscore')
     out_dir = os.path.join(out_root, 'gn')
-    shutil.move(os.path.join(self.chrome_root, 'out'),
-        out_root)
+    shutil.move(os.path.join(self.chrome_root, 'out'), out_root)
 
     clang_options = \
         self.ycm_extra_conf.GetClangOptionsFromNinjaForFilename(
             self.chrome_root, os.path.join(self.chrome_root, 'one.cpp'))
     self.assertIn('-I%s/a' % self.NormalizeString(out_dir),
-        self.NormalizeStringsInList(clang_options))
+                  self.NormalizeStringsInList(clang_options))
     self.assertIn('-I%s/tag-one' % self.NormalizeString(out_dir),
-        self.NormalizeStringsInList(clang_options))
+                  self.NormalizeStringsInList(clang_options))
 
   def testGetFlagsForFileForKnownCppFile(self):
     result = self.ycm_extra_conf.FlagsForFile(
@@ -190,16 +188,11 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(self.NormalizeStringsInList(result['flags']), [
-        '-DUSE_CLANG_COMPLETER',
-        '-std=c++11',
-        '-x', 'c++',
-        '-I[SRC]',
-        '-Wno-unknown-warning-option',
-        '-I[OUT]/a',
-        '-isysroot',
-        '/mac.sdk',
-        '-I[OUT]/tag-one'
+    self.assertEquals(
+        self.NormalizeStringsInList(result['flags']), [
+            '-DUSE_CLANG_COMPLETER', '-std=c++11', '-x', 'c++', '-I[SRC]',
+            '-Wno-unknown-warning-option', '-I[OUT]/a', '-isysroot', '/mac.sdk',
+            '-I[OUT]/tag-one'
         ])
 
   def testGetFlagsForFileForUnknownCppFile(self):
@@ -209,16 +202,11 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(self.NormalizeStringsInList(result['flags']), [
-        '-DUSE_CLANG_COMPLETER',
-        '-std=c++11',
-        '-x', 'c++',
-        '-I[SRC]',
-        '-Wno-unknown-warning-option',
-        '-I[OUT]/a',
-        '-isysroot',
-        '/mac.sdk',
-        '-I[OUT]/tag-default'
+    self.assertEquals(
+        self.NormalizeStringsInList(result['flags']), [
+            '-DUSE_CLANG_COMPLETER', '-std=c++11', '-x', 'c++', '-I[SRC]',
+            '-Wno-unknown-warning-option', '-I[OUT]/a', '-isysroot', '/mac.sdk',
+            '-I[OUT]/tag-default'
         ])
 
   def testGetFlagsForFileForUnknownCppNotTestFile(self):
@@ -228,26 +216,20 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(self.NormalizeStringsInList(result['flags']), [
-        '-DUSE_CLANG_COMPLETER',
-        '-std=c++11',
-        '-x', 'c++',
-        '-I[SRC]',
-        '-Wno-unknown-warning-option',
-        '-I[OUT]/a',
-        '-isysroot',
-        '/mac.sdk',
-        '-I[OUT]/tag-default'
+    self.assertEquals(
+        self.NormalizeStringsInList(result['flags']), [
+            '-DUSE_CLANG_COMPLETER', '-std=c++11', '-x', 'c++', '-I[SRC]',
+            '-Wno-unknown-warning-option', '-I[OUT]/a', '-isysroot', '/mac.sdk',
+            '-I[OUT]/tag-default'
         ])
 
-  testGetFlagsForFileForKnownObjcFile = TestLanguage(
-      'eight.m', 'objective-c')
+  testGetFlagsForFileForKnownObjcFile = TestLanguage('eight.m', 'objective-c')
   testGetFlagsForFileForKnownObjcHeaderFile = TestLanguage(
       'eight.h', 'objective-c')
-  testGetFlagsForFileForUnknownObjcFile = TestLanguage(
-      'nonexistent.m', 'objective-c')
-  testGetFlagsForFileForKnownObjcppFile = TestLanguage(
-      'nine.mm', 'objective-c++')
+  testGetFlagsForFileForUnknownObjcFile = TestLanguage('nonexistent.m',
+                                                       'objective-c')
+  testGetFlagsForFileForKnownObjcppFile = TestLanguage('nine.mm',
+                                                       'objective-c++')
   testGetFlagsForFileForKnownObjcppHeaderFile = TestLanguage(
       'nine.h', 'objective-c++')
   testGetFlagsForFileForUnknownObjcppFile = TestLanguage(
@@ -260,16 +242,11 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(self.NormalizeStringsInList(result['flags']), [
-        '-DUSE_CLANG_COMPLETER',
-        '-std=c++11',
-        '-x', 'c++',
-        '-I[SRC]',
-        '-Wno-unknown-warning-option',
-        '-I[OUT]/a',
-        '-isysroot',
-        '/mac.sdk',
-        '-I[OUT]/tag-default'
+    self.assertEquals(
+        self.NormalizeStringsInList(result['flags']), [
+            '-DUSE_CLANG_COMPLETER', '-std=c++11', '-x', 'c++', '-I[SRC]',
+            '-Wno-unknown-warning-option', '-I[OUT]/a', '-isysroot', '/mac.sdk',
+            '-I[OUT]/tag-default'
         ])
 
   def testGetFlagsForFileForUnknownUnittestFile(self):
@@ -279,16 +256,11 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(self.NormalizeStringsInList(result['flags']), [
-        '-DUSE_CLANG_COMPLETER',
-        '-std=c++11',
-        '-x', 'c++',
-        '-I[SRC]',
-        '-Wno-unknown-warning-option',
-        '-I[OUT]/a',
-        '-isysroot',
-        '/mac.sdk',
-        '-I[OUT]/tag-default-test'
+    self.assertEquals(
+        self.NormalizeStringsInList(result['flags']), [
+            '-DUSE_CLANG_COMPLETER', '-std=c++11', '-x', 'c++', '-I[SRC]',
+            '-Wno-unknown-warning-option', '-I[OUT]/a', '-isysroot', '/mac.sdk',
+            '-I[OUT]/tag-default-test'
         ])
 
   def testGetFlagsForFileForUnknownBrowsertestFile2(self):
@@ -298,16 +270,11 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(self.NormalizeStringsInList(result['flags']), [
-        '-DUSE_CLANG_COMPLETER',
-        '-std=c++11',
-        '-x', 'c++',
-        '-I[SRC]',
-        '-Wno-unknown-warning-option',
-        '-I[OUT]/a',
-        '-isysroot',
-        '/mac.sdk',
-        '-I[OUT]/tag-default-test'
+    self.assertEquals(
+        self.NormalizeStringsInList(result['flags']), [
+            '-DUSE_CLANG_COMPLETER', '-std=c++11', '-x', 'c++', '-I[SRC]',
+            '-Wno-unknown-warning-option', '-I[OUT]/a', '-isysroot', '/mac.sdk',
+            '-I[OUT]/tag-default-test'
         ])
 
   def testGetFlagsForFileForKnownHeaderFileWithAssociatedCppFile(self):
@@ -317,29 +284,24 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(self.NormalizeStringsInList(result['flags']), [
-        '-DUSE_CLANG_COMPLETER',
-        '-std=c++11',
-        '-x', 'c++',
-        '-I[SRC]',
-        '-Wno-unknown-warning-option',
-        '-I[OUT]/a',
-        '-isysroot',
-        '/mac.sdk',
-        '-I[OUT]/tag-three'
+    self.assertEquals(
+        self.NormalizeStringsInList(result['flags']), [
+            '-DUSE_CLANG_COMPLETER', '-std=c++11', '-x', 'c++', '-I[SRC]',
+            '-Wno-unknown-warning-option', '-I[OUT]/a', '-isysroot', '/mac.sdk',
+            '-I[OUT]/tag-three'
         ])
 
   def testSourceFileWithNonClangOutputs(self):
     # Verify assumption that four.cc has non-compiler-output listed as the first
     # output.
-    p = subprocess.Popen(['ninja', '-C', self.out_dir, '-t',
-                          'query', '../../four.cc'],
-                         stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
-                         universal_newlines=True)
+    p = subprocess.Popen(
+        ['ninja', '-C', self.out_dir, '-t', 'query', '../../four.cc'],
+        stdout=subprocess.PIPE,
+        stderr=subprocess.STDOUT,
+        universal_newlines=True)
     stdout, _ = p.communicate()
     self.assertFalse(p.returncode)
-    self.assertEquals(stdout,
-                      '../../four.cc:\n'
+    self.assertEquals(stdout, '../../four.cc:\n'
                       '  outputs:\n'
                       '    obj/linker-output.o\n'
                       '    obj/four.o\n')
@@ -350,16 +312,11 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(self.NormalizeStringsInList(result['flags']), [
-        '-DUSE_CLANG_COMPLETER',
-        '-std=c++11',
-        '-x', 'c++',
-        '-I[SRC]',
-        '-Wno-unknown-warning-option',
-        '-I[OUT]/a',
-        '-isysroot',
-        '/mac.sdk',
-        '-I[OUT]/tag-four'
+    self.assertEquals(
+        self.NormalizeStringsInList(result['flags']), [
+            '-DUSE_CLANG_COMPLETER', '-std=c++11', '-x', 'c++', '-I[SRC]',
+            '-Wno-unknown-warning-option', '-I[OUT]/a', '-isysroot', '/mac.sdk',
+            '-I[OUT]/tag-four'
         ])
 
   def testSourceFileWithOnlyNonClangOutputs(self):
@@ -369,16 +326,11 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(self.NormalizeStringsInList(result['flags']), [
-        '-DUSE_CLANG_COMPLETER',
-        '-std=c++11',
-        '-x', 'c++',
-        '-I[SRC]',
-        '-Wno-unknown-warning-option',
-        '-I[OUT]/a',
-        '-isysroot',
-        '/mac.sdk',
-        '-I[OUT]/tag-default'
+    self.assertEquals(
+        self.NormalizeStringsInList(result['flags']), [
+            '-DUSE_CLANG_COMPLETER', '-std=c++11', '-x', 'c++', '-I[SRC]',
+            '-Wno-unknown-warning-option', '-I[OUT]/a', '-isysroot', '/mac.sdk',
+            '-I[OUT]/tag-default'
         ])
 
   def testGetFlagsForSysrootAbsPath(self):
@@ -388,14 +340,16 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(self.NormalizeStringsInList(result['flags']), [
-        '-DUSE_CLANG_COMPLETER',
-        '-std=c++11',
-        '-x', 'c++',
-        '-I[SRC]',
-        '-Wno-unknown-warning-option',
-        '-I[OUT]/a',
-        '--sysroot=/usr/lib/sysroot-image',
+    self.assertEquals(
+        self.NormalizeStringsInList(result['flags']), [
+            '-DUSE_CLANG_COMPLETER',
+            '-std=c++11',
+            '-x',
+            'c++',
+            '-I[SRC]',
+            '-Wno-unknown-warning-option',
+            '-I[OUT]/a',
+            '--sysroot=/usr/lib/sysroot-image',
         ])
 
   def testGetFlagsForSysrootRelPath(self):
@@ -405,16 +359,19 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(self.NormalizeStringsInList(result['flags']), [
-        '-DUSE_CLANG_COMPLETER',
-        '-std=c++11',
-        '-x', 'c++',
-        '-I[SRC]',
-        '-Wno-unknown-warning-option',
-        '-I[OUT]/a',
-        '--sysroot=[SRC]/build/sysroot-image',
+    self.assertEquals(
+        self.NormalizeStringsInList(result['flags']), [
+            '-DUSE_CLANG_COMPLETER',
+            '-std=c++11',
+            '-x',
+            'c++',
+            '-I[SRC]',
+            '-Wno-unknown-warning-option',
+            '-I[OUT]/a',
+            '--sysroot=[SRC]/build/sysroot-image',
         ])
 
+
 if __name__ == '__main__':
   if not os.path.isfile('chromium.ycm_extra_conf.py'):
     print('The test must be run from src/tools/vim/ directory')