diff --git a/DEPS b/DEPS
index cc8f18d..be4a6af2 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'b30d0b12cfea2dd6235e37fb3be301da34098b4d',
+  'v8_revision': '7fb6b9426d6970cde5c338a2bf2189800db0b775',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
diff --git a/base/task_scheduler/task_scheduler_impl.cc b/base/task_scheduler/task_scheduler_impl.cc
index 7f56f48..b4fcc62 100644
--- a/base/task_scheduler/task_scheduler_impl.cc
+++ b/base/task_scheduler/task_scheduler_impl.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "base/memory/ptr_util.h"
 #include "base/task_scheduler/delayed_task_manager.h"
 #include "base/task_scheduler/environment_config.h"
 #include "base/task_scheduler/scheduler_worker_pool_params.h"
@@ -18,10 +17,13 @@
 namespace base {
 namespace internal {
 
-TaskSchedulerImpl::TaskSchedulerImpl(StringPiece name)
+TaskSchedulerImpl::TaskSchedulerImpl(
+    StringPiece name,
+    std::unique_ptr<TaskTrackerImpl> task_tracker)
     : name_(name),
       service_thread_("TaskSchedulerServiceThread"),
-      single_thread_task_runner_manager_(&task_tracker_,
+      task_tracker_(std::move(task_tracker)),
+      single_thread_task_runner_manager_(task_tracker_.get(),
                                          &delayed_task_manager_) {
   static_assert(arraysize(worker_pools_) == ENVIRONMENT_COUNT,
                 "The size of |worker_pools_| must match ENVIRONMENT_COUNT.");
@@ -33,7 +35,7 @@
        ++environment_type) {
     worker_pools_[environment_type] = MakeUnique<SchedulerWorkerPoolImpl>(
         name_ + kEnvironmentParams[environment_type].name_suffix,
-        kEnvironmentParams[environment_type].priority_hint, &task_tracker_,
+        kEnvironmentParams[environment_type].priority_hint, task_tracker_.get(),
         &delayed_task_manager_);
   }
 }
@@ -61,11 +63,11 @@
 #if defined(OS_POSIX) && !defined(OS_NACL_SFI)
   // Needs to happen after starting the service thread to get its
   // message_loop().
-  task_tracker_.set_watch_file_descriptor_message_loop(
+  task_tracker_->set_watch_file_descriptor_message_loop(
       static_cast<MessageLoopForIO*>(service_thread_.message_loop()));
 
 #if DCHECK_IS_ON()
-  task_tracker_.set_service_thread_handle(service_thread_.GetThreadHandle());
+  task_tracker_->set_service_thread_handle(service_thread_.GetThreadHandle());
 #endif  // DCHECK_IS_ON()
 #endif  // defined(OS_POSIX) && !defined(OS_NACL_SFI)
 
@@ -138,11 +140,11 @@
 
 void TaskSchedulerImpl::Shutdown() {
   // TODO(fdoray): Increase the priority of BACKGROUND tasks blocking shutdown.
-  task_tracker_.Shutdown();
+  task_tracker_->Shutdown();
 }
 
 void TaskSchedulerImpl::FlushForTesting() {
-  task_tracker_.Flush();
+  task_tracker_->Flush();
 }
 
 void TaskSchedulerImpl::JoinForTesting() {
diff --git a/base/task_scheduler/task_scheduler_impl.h b/base/task_scheduler/task_scheduler_impl.h
index 4907ce2..1e35639 100644
--- a/base/task_scheduler/task_scheduler_impl.h
+++ b/base/task_scheduler/task_scheduler_impl.h
@@ -12,6 +12,7 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/string_piece.h"
 #include "base/synchronization/atomic_flag.h"
@@ -38,8 +39,19 @@
 // Default TaskScheduler implementation. This class is thread-safe.
 class BASE_EXPORT TaskSchedulerImpl : public TaskScheduler {
  public:
-  // |name| is used to label threads and histograms.
-  explicit TaskSchedulerImpl(StringPiece name);
+  using TaskTrackerImpl =
+#if defined(OS_POSIX) && !defined(OS_NACL_SFI)
+      TaskTrackerPosix;
+#else
+      TaskTracker;
+#endif
+
+  // |name| is used to label threads and histograms. |task_tracker| can be used
+  // for tests that need more execution control. By default, the production
+  // TaskTracker is used.
+  explicit TaskSchedulerImpl(StringPiece name,
+                             std::unique_ptr<TaskTrackerImpl> task_tracker =
+                                 MakeUnique<TaskTrackerImpl>());
   ~TaskSchedulerImpl() override;
 
   // TaskScheduler:
@@ -74,11 +86,7 @@
 
   const std::string name_;
   Thread service_thread_;
-#if defined(OS_POSIX) && !defined(OS_NACL_SFI)
-  TaskTrackerPosix task_tracker_;
-#else
-  TaskTracker task_tracker_;
-#endif
+  const std::unique_ptr<TaskTrackerImpl> task_tracker_;
   DelayedTaskManager delayed_task_manager_;
   SchedulerSingleThreadTaskRunnerManager single_thread_task_runner_manager_;
 
diff --git a/base/task_scheduler/task_tracker.cc b/base/task_scheduler/task_tracker.cc
index 55230aa9..ebf43152 100644
--- a/base/task_scheduler/task_tracker.cc
+++ b/base/task_scheduler/task_tracker.cc
@@ -388,6 +388,10 @@
 }
 #endif
 
+int TaskTracker::GetNumPendingUndelayedTasksForTesting() const {
+  return subtle::NoBarrier_Load(&num_pending_undelayed_tasks_);
+}
+
 bool TaskTracker::BeforePostTask(TaskShutdownBehavior shutdown_behavior) {
   if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN) {
     // BLOCK_SHUTDOWN tasks block shutdown between the moment they are posted
diff --git a/base/task_scheduler/task_tracker.h b/base/task_scheduler/task_tracker.h
index 1a1977d..b80e661c 100644
--- a/base/task_scheduler/task_tracker.h
+++ b/base/task_scheduler/task_tracker.h
@@ -90,6 +90,10 @@
   virtual bool IsPostingBlockShutdownTaskAfterShutdownAllowed();
 #endif
 
+  // Returns the number of undelayed tasks that haven't completed their
+  // execution.
+  int GetNumPendingUndelayedTasksForTesting() const;
+
  private:
   class State;
 
diff --git a/base/task_scheduler/task_tracker_posix.h b/base/task_scheduler/task_tracker_posix.h
index a98b464c..7a17f75 100644
--- a/base/task_scheduler/task_tracker_posix.h
+++ b/base/task_scheduler/task_tracker_posix.h
@@ -50,11 +50,12 @@
   }
 #endif
 
- private:
+ protected:
   // TaskTracker:
   void PerformRunTask(std::unique_ptr<Task> task,
                       const SequenceToken& sequence_token) override;
 
+ private:
 #if DCHECK_IS_ON()
   bool IsPostingBlockShutdownTaskAfterShutdownAllowed() override;
 #endif
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc
index 672ffb05..8129162 100644
--- a/base/test/scoped_task_environment.cc
+++ b/base/test/scoped_task_environment.cc
@@ -4,9 +4,15 @@
 
 #include "base/test/scoped_task_environment.h"
 
+#include "base/bind_helpers.h"
+#include "base/logging.h"
 #include "base/run_loop.h"
-#include "base/task_scheduler/scheduler_worker_pool_params.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/task_scheduler/task_scheduler.h"
+#include "base/task_scheduler/task_scheduler_impl.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 
 namespace base {
@@ -21,25 +27,69 @@
   // MessageLoop::TaskObserver:
   void WillProcessTask(const PendingTask& pending_task) override {}
   void DidProcessTask(const PendingTask& pending_task) override {
-    ran_task_ = true;
+    ++task_count_;
   }
 
-  bool ran_task() const { return ran_task_; }
+  int task_count() const { return task_count_; }
 
  private:
-  bool ran_task_ = false;
+  int task_count_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(TaskObserver);
 };
 
 }  // namespace
 
-ScopedTaskEnvironment::ScopedTaskEnvironment(MainThreadType main_thread_type)
-    : message_loop_(main_thread_type == MainThreadType::DEFAULT
+class ScopedTaskEnvironment::TestTaskTracker
+    : public internal::TaskSchedulerImpl::TaskTrackerImpl {
+ public:
+  TestTaskTracker();
+
+  void RegisterOnQueueEmptyClosure(OnceClosure queue_empty_closure);
+  void AssertOnQueueEmptyClosureIsNull();
+
+  // Allow running tasks.
+  void AllowRunRask();
+
+  // Disallow running tasks. No-ops and returns false if a task is running.
+  bool DisallowRunTasks();
+
+ private:
+  friend class ScopedTaskEnvironment;
+
+  // internal::TaskSchedulerImpl::TaskTrackerImpl:
+  void PerformRunTask(std::unique_ptr<internal::Task> task,
+                      const SequenceToken& sequence_token) override;
+
+  // Synchronizes accesses to members below.
+  Lock lock_;
+
+  // Closure posted to the main thread when the task queue becomes empty.
+  OnceClosure queue_empty_closure_;
+
+  // True if running tasks is allowed.
+  bool can_run_tasks_ = true;
+
+  // Signaled when |can_run_tasks_| becomes true.
+  ConditionVariable can_run_tasks_cv_;
+
+  // Number of tasks that are currently running.
+  int num_tasks_running_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(TestTaskTracker);
+};
+
+ScopedTaskEnvironment::ScopedTaskEnvironment(
+    MainThreadType main_thread_type,
+    ExecutionMode execution_control_mode)
+    : execution_control_mode_(execution_control_mode),
+      message_loop_(main_thread_type == MainThreadType::DEFAULT
                         ? MessageLoop::TYPE_DEFAULT
                         : (main_thread_type == MainThreadType::UI
                                ? MessageLoop::TYPE_UI
-                               : MessageLoop::TYPE_IO)) {
-  DCHECK(!TaskScheduler::GetInstance());
+                               : MessageLoop::TYPE_IO)),
+      task_tracker_(new TestTaskTracker()) {
+  CHECK(!TaskScheduler::GetInstance());
 
   // Instantiate a TaskScheduler with 1 thread in each of its 4 pools. Threads
   // stay alive even when they don't have work.
@@ -48,10 +98,14 @@
   const SchedulerWorkerPoolParams worker_pool_params(
       SchedulerWorkerPoolParams::StandbyThreadPolicy::ONE, kMaxThreads,
       kSuggestedReclaimTime);
-  TaskScheduler::Create("ScopedTaskEnvironment");
+  TaskScheduler::SetInstance(MakeUnique<internal::TaskSchedulerImpl>(
+      "ScopedTaskEnvironment", WrapUnique(task_tracker_)));
   task_scheduler_ = TaskScheduler::GetInstance();
   TaskScheduler::GetInstance()->Start({worker_pool_params, worker_pool_params,
                                        worker_pool_params, worker_pool_params});
+
+  if (execution_control_mode_ == ExecutionMode::QUEUED)
+    CHECK(task_tracker_->DisallowRunTasks());
 }
 
 ScopedTaskEnvironment::~ScopedTaskEnvironment() {
@@ -59,10 +113,10 @@
   // infinite post loop in the remaining work but this isn't possible right now
   // because base::~MessageLoop() didn't use to do this and adding it here would
   // make the migration away from MessageLoop that much harder.
-
-  DCHECK_EQ(TaskScheduler::GetInstance(), task_scheduler_);
+  CHECK_EQ(TaskScheduler::GetInstance(), task_scheduler_);
   // Without FlushForTesting(), DeleteSoon() and ReleaseSoon() tasks could be
   // skipped, resulting in memory leaks.
+  task_tracker_->AllowRunRask();
   TaskScheduler::GetInstance()->FlushForTesting();
   TaskScheduler::GetInstance()->Shutdown();
   TaskScheduler::GetInstance()->JoinForTesting();
@@ -76,15 +130,108 @@
 
 void ScopedTaskEnvironment::RunUntilIdle() {
   for (;;) {
-    TaskScheduler::GetInstance()->FlushForTesting();
+    RunLoop run_loop;
 
+    // Register a closure to stop running tasks on the main thread when the
+    // TaskScheduler queue becomes empty.
+    task_tracker_->RegisterOnQueueEmptyClosure(run_loop.QuitWhenIdleClosure());
+
+    // The closure registered above may never run if the TaskScheduler queue
+    // starts empty. Post a TaskScheduler tasks to make sure that the queue
+    // doesn't start empty.
+    PostTask(FROM_HERE, BindOnce(&DoNothing));
+
+    // Run main thread and TaskScheduler tasks.
+    task_tracker_->AllowRunRask();
     TaskObserver task_observer;
     MessageLoop::current()->AddTaskObserver(&task_observer);
-    RunLoop().RunUntilIdle();
+    run_loop.Run();
     MessageLoop::current()->RemoveTaskObserver(&task_observer);
 
-    if (!task_observer.ran_task())
-      return;
+    task_tracker_->AssertOnQueueEmptyClosureIsNull();
+
+    // If tasks other than the QuitWhenIdle closure ran on the main thread, they
+    // may have posted TaskScheduler tasks that didn't run yet. Another
+    // iteration is required to run them.
+    //
+    // If the ExecutionMode is QUEUED and DisallowRunTasks() fails,
+    // another iteration is required to make sure that RunUntilIdle() doesn't
+    // return while TaskScheduler tasks are still allowed to run.
+    //
+    // Note: DisallowRunTasks() fails when a TaskScheduler task is running. A
+    // TaskScheduler task may be running after the TaskScheduler queue became
+    // empty even if no tasks ran on the main thread in these cases:
+    // - An undelayed task became ripe for execution.
+    // - A task was posted from an external thread.
+    if (task_observer.task_count() == 1 &&
+        (execution_control_mode_ != ExecutionMode::QUEUED ||
+         task_tracker_->DisallowRunTasks())) {
+      break;
+    }
+  }
+}
+
+ScopedTaskEnvironment::TestTaskTracker::TestTaskTracker()
+    : can_run_tasks_cv_(&lock_) {}
+
+void ScopedTaskEnvironment::TestTaskTracker::RegisterOnQueueEmptyClosure(
+    OnceClosure queue_empty_closure) {
+  AutoLock auto_lock(lock_);
+  CHECK(!queue_empty_closure_);
+  queue_empty_closure_ = std::move(queue_empty_closure);
+}
+
+void ScopedTaskEnvironment::TestTaskTracker::AssertOnQueueEmptyClosureIsNull() {
+  AutoLock auto_lock(lock_);
+  CHECK(!queue_empty_closure_);
+}
+
+void ScopedTaskEnvironment::TestTaskTracker::AllowRunRask() {
+  AutoLock auto_lock(lock_);
+  can_run_tasks_ = true;
+  can_run_tasks_cv_.Broadcast();
+}
+
+bool ScopedTaskEnvironment::TestTaskTracker::DisallowRunTasks() {
+  AutoLock auto_lock(lock_);
+
+  // Can't disallow run task if there are tasks running.
+  if (num_tasks_running_ > 0)
+    return false;
+
+  can_run_tasks_ = false;
+  return true;
+}
+
+void ScopedTaskEnvironment::TestTaskTracker::PerformRunTask(
+    std::unique_ptr<internal::Task> task,
+    const SequenceToken& sequence_token) {
+  {
+    AutoLock auto_lock(lock_);
+
+    while (!can_run_tasks_)
+      can_run_tasks_cv_.Wait();
+
+    ++num_tasks_running_;
+  }
+
+  internal::TaskSchedulerImpl::TaskTrackerImpl::PerformRunTask(std::move(task),
+                                                               sequence_token);
+
+  {
+    AutoLock auto_lock(lock_);
+
+    CHECK_GT(num_tasks_running_, 0);
+    CHECK(can_run_tasks_);
+
+    // Notify the main thread when no task other than the current one is running
+    // or queued.
+    if (num_tasks_running_ == 1 &&
+        GetNumPendingUndelayedTasksForTesting() == 1 && queue_empty_closure_) {
+      std::move(queue_empty_closure_).Run();
+    }
+
+    --num_tasks_running_;
   }
 }
 
diff --git a/base/test/scoped_task_environment.h b/base/test/scoped_task_environment.h
index f46b516..5166a5e7 100644
--- a/base/test/scoped_task_environment.h
+++ b/base/test/scoped_task_environment.h
@@ -26,7 +26,9 @@
 // RunLoop::Run(UntilIdle) or ScopedTaskEnvironment::RunUntilIdle is called on
 // the thread where the ScopedTaskEnvironment lives.
 //
-// Tasks posted through base/task_scheduler/post_task.h run on dedicated threads
+// Tasks posted through base/task_scheduler/post_task.h run on dedicated
+// threads. If ExecutionMode is QUEUED, they run when RunUntilIdle() or
+// ~ScopedTaskEnvironment is called. If ExecutionMode is ASYNC, they run
 // as they are posted.
 //
 // All methods of ScopedTaskEnvironment must be called from the same thread.
@@ -61,8 +63,18 @@
     IO,
   };
 
+  enum class ExecutionMode {
+    // Tasks are queued and only executed when RunUntilIdle() is explicitly
+    // called.
+    QUEUED,
+    // Tasks run as they are posted. RunUntilIdle() can still be used to block
+    // until done.
+    ASYNC,
+  };
+
   ScopedTaskEnvironment(
-      MainThreadType main_thread_type = MainThreadType::DEFAULT);
+      MainThreadType main_thread_type = MainThreadType::DEFAULT,
+      ExecutionMode execution_control_mode = ExecutionMode::ASYNC);
 
   // Waits until no undelayed TaskScheduler tasks remain. Then, unregisters the
   // TaskScheduler and the (Thread|Sequenced)TaskRunnerHandle.
@@ -71,11 +83,15 @@
   // Returns a TaskRunner that schedules tasks on the main thread.
   scoped_refptr<base::SingleThreadTaskRunner> GetMainThreadTaskRunner();
 
-  // Synchronously runs (Thread|Sequenced)TaskRunnerHandle tasks until no
-  // undelayed (Thread|Sequenced)TaskRunnerHandle or TaskScheduler tasks remain.
+  // Runs tasks until both the (Thread|Sequenced)TaskRunnerHandle and the
+  // TaskScheduler queues are empty.
   void RunUntilIdle();
 
  private:
+  class TestTaskTracker;
+
+  const ExecutionMode execution_control_mode_;
+
   // Note: |message_loop_| is an implementation detail and will be replaced in
   // the future, do NOT rely on the presence of a MessageLoop beyond
   // (Thread|Sequenced)TaskRunnerHandle and RunLoop.
@@ -83,6 +99,9 @@
 
   const TaskScheduler* task_scheduler_ = nullptr;
 
+  // Owned by |task_scheduler_|.
+  TestTaskTracker* const task_tracker_;
+
   DISALLOW_COPY_AND_ASSIGN(ScopedTaskEnvironment);
 };
 
diff --git a/base/test/scoped_task_environment_unittest.cc b/base/test/scoped_task_environment_unittest.cc
index 245cda28..93a980a 100644
--- a/base/test/scoped_task_environment_unittest.cc
+++ b/base/test/scoped_task_environment_unittest.cc
@@ -6,7 +6,10 @@
 
 #include "base/bind.h"
 #include "base/synchronization/atomic_flag.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/task_scheduler/post_task.h"
+#include "base/test/test_timeouts.h"
+#include "base/threading/platform_thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -22,11 +25,11 @@
   task_ran->Set();
 }
 
-}  // namespace
-
-TEST(ScopedTaskEnvironmentTest, RunUntilIdle) {
+void RunUntilIdleTest(
+    ScopedTaskEnvironment::ExecutionMode execution_control_mode) {
   AtomicFlag run_until_idle_returned;
-  ScopedTaskEnvironment scoped_task_environment;
+  ScopedTaskEnvironment scoped_task_environment(
+      ScopedTaskEnvironment::MainThreadType::DEFAULT, execution_control_mode);
 
   AtomicFlag first_main_thread_task_ran;
   ThreadTaskRunnerHandle::Get()->PostTask(
@@ -58,5 +61,76 @@
   EXPECT_TRUE(second_main_thread_task_ran.IsSet());
 }
 
+}  // namespace
+
+TEST(ScopedTaskEnvironmentTest, QueuedRunUntilIdle) {
+  RunUntilIdleTest(ScopedTaskEnvironment::ExecutionMode::QUEUED);
+}
+
+TEST(ScopedTaskEnvironmentTest, AsyncRunUntilIdle) {
+  RunUntilIdleTest(ScopedTaskEnvironment::ExecutionMode::ASYNC);
+}
+
+// Verify that tasks posted to an ExecutionMode::QUEUED ScopedTaskEnvironment do
+// not run outside of RunUntilIdle().
+TEST(ScopedTaskEnvironmentTest, QueuedTasksDoNotRunOutsideOfRunUntilIdle) {
+  ScopedTaskEnvironment scoped_task_environment(
+      ScopedTaskEnvironment::MainThreadType::DEFAULT,
+      ScopedTaskEnvironment::ExecutionMode::QUEUED);
+
+  AtomicFlag run_until_idle_called;
+  PostTask(FROM_HERE, BindOnce(
+                          [](AtomicFlag* run_until_idle_called) {
+                            EXPECT_TRUE(run_until_idle_called->IsSet());
+                          },
+                          Unretained(&run_until_idle_called)));
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  run_until_idle_called.Set();
+  scoped_task_environment.RunUntilIdle();
+
+  AtomicFlag other_run_until_idle_called;
+  PostTask(FROM_HERE, BindOnce(
+                          [](AtomicFlag* other_run_until_idle_called) {
+                            EXPECT_TRUE(other_run_until_idle_called->IsSet());
+                          },
+                          Unretained(&other_run_until_idle_called)));
+  PlatformThread::Sleep(TestTimeouts::tiny_timeout());
+  other_run_until_idle_called.Set();
+  scoped_task_environment.RunUntilIdle();
+}
+
+// Verify that a task posted to an ExecutionMode::ASYNC ScopedTaskEnvironment
+// can run without a call to RunUntilIdle().
+TEST(ScopedTaskEnvironmentTest, AsyncTasksRunAsTheyArePosted) {
+  ScopedTaskEnvironment scoped_task_environment(
+      ScopedTaskEnvironment::MainThreadType::DEFAULT,
+      ScopedTaskEnvironment::ExecutionMode::ASYNC);
+
+  WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL,
+                         WaitableEvent::InitialState::NOT_SIGNALED);
+  PostTask(FROM_HERE,
+           BindOnce([](WaitableEvent* task_ran) { task_ran->Signal(); },
+                    Unretained(&task_ran)));
+  task_ran.Wait();
+}
+
+// Verify that a task posted to an ExecutionMode::ASYNC ScopedTaskEnvironment
+// after a call to RunUntilIdle() can run without another call to
+// RunUntilIdle().
+TEST(ScopedTaskEnvironmentTest, AsyncTasksRunAsTheyArePostedAfterRunUntilIdle) {
+  ScopedTaskEnvironment scoped_task_environment(
+      ScopedTaskEnvironment::MainThreadType::DEFAULT,
+      ScopedTaskEnvironment::ExecutionMode::ASYNC);
+
+  scoped_task_environment.RunUntilIdle();
+
+  WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL,
+                         WaitableEvent::InitialState::NOT_SIGNALED);
+  PostTask(FROM_HERE,
+           BindOnce([](WaitableEvent* task_ran) { task_ran->Signal(); },
+                    Unretained(&task_ran)));
+  task_ran.Wait();
+}
+
 }  // namespace test
 }  // namespace base
diff --git a/build/config/fuchsia/rules.gni b/build/config/fuchsia/rules.gni
new file mode 100644
index 0000000..b3e7f3b
--- /dev/null
+++ b/build/config/fuchsia/rules.gni
@@ -0,0 +1,78 @@
+# 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.
+
+assert(is_fuchsia)
+
+# This template is used to generate a runner script for test binaries into the
+# build dir for Fuchsia. It's generally used from the "test" template.
+template("test_runner_script") {
+  testonly = true
+  _test_name = invoker.test_name
+
+  # This runtime_deps file is used at runtime and thus cannot go in
+  # target_gen_dir.
+  _target_dir_name = get_label_info(":$target_name", "dir")
+  _runtime_deps_file =
+      "$root_out_dir/gen.runtime/$_target_dir_name/$target_name.runtime_deps"
+  _runtime_deps_target = "${target_name}__write_deps"
+  group(_runtime_deps_target) {
+    forward_variables_from(invoker,
+                           [
+                             "data",
+                             "data_deps",
+                             "deps",
+                             "public_deps",
+                           ])
+    write_runtime_deps = _runtime_deps_file
+  }
+
+  action(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "data_deps",
+                             "deps",
+                           ])
+    if (!defined(deps)) {
+      deps = []
+    }
+    if (!defined(data_deps)) {
+      data_deps = []
+    }
+
+    script = "//build/fuchsia/create_test_runner_script.py"
+    depfile = "$target_gen_dir/$target_name.d"
+
+    data = []
+    test_runner_args = []
+
+    generated_script = "$root_build_dir/bin/run_${_test_name}"
+    outputs = [
+      generated_script,
+    ]
+    data += [ generated_script ]
+
+    test_runner_args += [
+      "--output-directory",
+      rebase_path(root_build_dir, root_build_dir),
+    ]
+
+    deps += [ ":$_runtime_deps_target" ]
+    data += [ _runtime_deps_file ]
+    test_runner_args += [
+      "--runtime-deps-path",
+      rebase_path(_runtime_deps_file, root_build_dir),
+    ]
+
+    args = [
+      "--depfile",
+      rebase_path(depfile, root_build_dir),
+      "--script-output-path",
+      rebase_path(generated_script, root_build_dir),
+      "--test-name",
+      _test_name,
+    ]
+
+    args += test_runner_args
+  }
+}
diff --git a/build/fuchsia/create_test_runner_script.py b/build/fuchsia/create_test_runner_script.py
new file mode 100755
index 0000000..d6e067b
--- /dev/null
+++ b/build/fuchsia/create_test_runner_script.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+#
+# 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.
+
+"""Creates a script to run a Fushsia test (typically on QEMU) by delegating to
+build/fuchsia/test_runner.py.
+"""
+
+import argparse
+import os
+import pipes
+import re
+import sys
+
+
+SCRIPT_TEMPLATE = """\
+#!/bin/sh
+exec {test_runner_path} {test_runner_args} "$@"
+"""
+
+
+def MakeDirectory(dir_path):
+  try:
+    os.makedirs(dir_path)
+  except OSError:
+    pass
+
+
+def WriteDepfile(depfile_path, first_gn_output, inputs=None):
+  assert depfile_path != first_gn_output
+  inputs = inputs or []
+  MakeDirectory(os.path.dirname(depfile_path))
+  # Ninja does not support multiple outputs in depfiles.
+  with open(depfile_path, 'w') as depfile:
+    depfile.write(first_gn_output.replace(' ', '\\ '))
+    depfile.write(': ')
+    depfile.write(' '.join(i.replace(' ', '\\ ') for i in inputs))
+    depfile.write('\n')
+
+
+def main(args):
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--script-output-path',
+                      help='Output path for executable script.')
+  parser.add_argument('--depfile',
+                      help='Path to the depfile. This must be specified as '
+                           'the action\'s first output.')
+  parser.add_argument('--test-runner-path',
+                      help='Path to test_runner.py (optional).')
+  group = parser.add_argument_group('Test runner path arguments.')
+  group.add_argument('--output-directory')
+  group.add_argument('--runtime-deps-path')
+  group.add_argument('--test-name')
+  args, test_runner_args = parser.parse_known_args(args)
+
+  def ResolvePath(path):
+    return os.path.abspath(os.path.join(
+        os.path.dirname(args.script_output_path), '..', path))
+
+  test_runner_path = args.test_runner_path or os.path.join(
+      os.path.dirname(__file__), 'test_runner.py')
+  test_runner_path = ResolvePath(test_runner_path)
+
+  if args.output_directory:
+    test_runner_args.extend(
+        ['--output-directory', ResolvePath(args.output_directory)])
+  if args.runtime_deps_path:
+    test_runner_args.extend(
+        ['--runtime-deps-path', ResolvePath(args.runtime_deps_path)])
+  if args.test_name:
+    test_runner_args.extend(['--test-name', args.test_name])
+
+  with open(args.script_output_path, 'w') as script:
+    script.write(SCRIPT_TEMPLATE.format(
+        test_runner_path=test_runner_path,
+        test_runner_args=' '.join(pipes.quote(x) for x in test_runner_args)))
+
+  os.chmod(args.script_output_path, 0750)
+
+  if args.depfile:
+    WriteDepfile(args.depfile, args.script_output_path,
+                 [__file__])
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/build/fuchsia/test_runner.py b/build/fuchsia/test_runner.py
new file mode 100755
index 0000000..d79fdae
--- /dev/null
+++ b/build/fuchsia/test_runner.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+#
+# 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.
+
+"""Stub for script that will run a Fuchsia binary on QEMU or hardware."""
+
+import sys
+
+
+def main():
+  print sys.argv
+  print 'TODO(fuchsia): Implement test runner. https://crbug.com/706592.'
+  return 1
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b259642..58f30f86 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2025,14 +2025,14 @@
   }
   if (enable_spellcheck) {
     sources += [
+      "spellchecker/spell_check_host_impl.cc",
+      "spellchecker/spell_check_host_impl.h",
       "spellchecker/spellcheck_custom_dictionary.cc",
       "spellchecker/spellcheck_custom_dictionary.h",
       "spellchecker/spellcheck_factory.cc",
       "spellchecker/spellcheck_factory.h",
       "spellchecker/spellcheck_hunspell_dictionary.cc",
       "spellchecker/spellcheck_hunspell_dictionary.h",
-      "spellchecker/spellcheck_message_filter.cc",
-      "spellchecker/spellcheck_message_filter.h",
       "spellchecker/spellcheck_message_filter_platform_mac.cc",
       "spellchecker/spellcheck_service.cc",
       "spellchecker/spellcheck_service.h",
@@ -4414,6 +4414,7 @@
 
   if (use_vulcanize) {
     deps += [
+      "//chrome/browser/resources/md_bookmarks:build",
       "//chrome/browser/resources/md_downloads:build",
       "//chrome/browser/resources/md_history:build",
     ]
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 77382fa2..a5d48f14 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -266,42 +266,50 @@
 
       <if expr="not is_android and not is_ios">
         <!-- MD Bookmarks. -->
-        <include name="IDR_MD_BOOKMARKS_ACTIONS_HTML" file="resources\md_bookmarks\actions.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_ACTIONS_JS" file="resources\md_bookmarks\actions.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_API_LISTENER_HTML" file="resources\md_bookmarks\api_listener.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_API_LISTENER_JS" file="resources\md_bookmarks\api_listener.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_APP_HTML" file="resources\md_bookmarks\app.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_APP_JS" file="resources\md_bookmarks\app.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_BOOKMARKS_HTML" file="resources\md_bookmarks\bookmarks.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_COMMAND_MANAGER_HTML" file="resources\md_bookmarks\command_manager.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_COMMAND_MANAGER_JS" file="resources\md_bookmarks\command_manager.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_CONSTANTS_HTML" file="resources\md_bookmarks\constants.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_CONSTANTS_JS" file="resources\md_bookmarks\constants.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_DND_MANAGER_HTML" file="resources\md_bookmarks\dnd_manager.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_DND_MANAGER_JS" file="resources\md_bookmarks\dnd_manager.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_EDIT_DIALOG_HTML" file="resources\md_bookmarks\edit_dialog.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_EDIT_DIALOG_JS" file="resources\md_bookmarks\edit_dialog.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_FOLDER_NODE_HTML" file="resources\md_bookmarks\folder_node.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_FOLDER_NODE_JS" file="resources\md_bookmarks\folder_node.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_ICONS_HTML" file="resources\md_bookmarks\icons.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_ITEM_HTML" file="resources\md_bookmarks\item.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_ITEM_JS" file="resources\md_bookmarks\item.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_LIST_HTML" file="resources\md_bookmarks\list.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_LIST_JS" file="resources\md_bookmarks\list.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_REDUCERS_HTML" file="resources\md_bookmarks\reducers.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_REDUCERS_JS" file="resources\md_bookmarks\reducers.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_ROUTER_HTML" file="resources\md_bookmarks\router.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_ROUTER_JS" file="resources\md_bookmarks\router.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_SHARED_STYLE_HTML" file="resources\md_bookmarks\shared_style.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_SHARED_VARS_HTML" file="resources\md_bookmarks\shared_vars.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_STORE_CLIENT_HTML" file="resources\md_bookmarks\store_client.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_STORE_CLIENT_JS" file="resources\md_bookmarks\store_client.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_STORE_HTML" file="resources\md_bookmarks\store.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_STORE_JS" file="resources\md_bookmarks\store.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_TOOLBAR_HTML" file="resources\md_bookmarks\toolbar.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_TOOLBAR_JS" file="resources\md_bookmarks\toolbar.js" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_UTIL_HTML" file="resources\md_bookmarks\util.html" type="BINDATA" />
-        <include name="IDR_MD_BOOKMARKS_UTIL_JS" file="resources\md_bookmarks\util.js" type="BINDATA" />
+        <if expr="use_vulcanize">
+          <then>
+            <include name="IDR_MD_BOOKMARKS_VULCANIZED_HTML" file="${root_gen_dir}\chrome\browser\resources\md_bookmarks\vulcanized.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
+            <include name="IDR_MD_BOOKMARKS_CRISPER_JS" file="${root_gen_dir}\chrome\browser\resources\md_bookmarks\crisper.js" use_base_dir="false" flattenhtml="true" type="BINDATA" compress="gzip" />
+          </then>
+          <else>
+            <include name="IDR_MD_BOOKMARKS_ACTIONS_HTML" file="resources\md_bookmarks\actions.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_ACTIONS_JS" file="resources\md_bookmarks\actions.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_API_LISTENER_HTML" file="resources\md_bookmarks\api_listener.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_API_LISTENER_JS" file="resources\md_bookmarks\api_listener.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_APP_HTML" file="resources\md_bookmarks\app.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_APP_JS" file="resources\md_bookmarks\app.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_BOOKMARKS_HTML" file="resources\md_bookmarks\bookmarks.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_COMMAND_MANAGER_HTML" file="resources\md_bookmarks\command_manager.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_COMMAND_MANAGER_JS" file="resources\md_bookmarks\command_manager.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_CONSTANTS_HTML" file="resources\md_bookmarks\constants.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_CONSTANTS_JS" file="resources\md_bookmarks\constants.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_DND_MANAGER_HTML" file="resources\md_bookmarks\dnd_manager.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_DND_MANAGER_JS" file="resources\md_bookmarks\dnd_manager.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_EDIT_DIALOG_HTML" file="resources\md_bookmarks\edit_dialog.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_EDIT_DIALOG_JS" file="resources\md_bookmarks\edit_dialog.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_FOLDER_NODE_HTML" file="resources\md_bookmarks\folder_node.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_FOLDER_NODE_JS" file="resources\md_bookmarks\folder_node.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_ICONS_HTML" file="resources\md_bookmarks\icons.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_ITEM_HTML" file="resources\md_bookmarks\item.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_ITEM_JS" file="resources\md_bookmarks\item.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_LIST_HTML" file="resources\md_bookmarks\list.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_LIST_JS" file="resources\md_bookmarks\list.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_REDUCERS_HTML" file="resources\md_bookmarks\reducers.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_REDUCERS_JS" file="resources\md_bookmarks\reducers.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_ROUTER_HTML" file="resources\md_bookmarks\router.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_ROUTER_JS" file="resources\md_bookmarks\router.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_SHARED_STYLE_HTML" file="resources\md_bookmarks\shared_style.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_SHARED_VARS_HTML" file="resources\md_bookmarks\shared_vars.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_STORE_CLIENT_HTML" file="resources\md_bookmarks\store_client.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_STORE_CLIENT_JS" file="resources\md_bookmarks\store_client.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_STORE_HTML" file="resources\md_bookmarks\store.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_STORE_JS" file="resources\md_bookmarks\store.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_TOOLBAR_HTML" file="resources\md_bookmarks\toolbar.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_TOOLBAR_JS" file="resources\md_bookmarks\toolbar.js" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_UTIL_HTML" file="resources\md_bookmarks\util.html" type="BINDATA" />
+            <include name="IDR_MD_BOOKMARKS_UTIL_JS" file="resources\md_bookmarks\util.js" type="BINDATA" />
+          </else>
+        </if>
 
         <!-- MD History. -->
         <include name="IDR_MD_HISTORY_CONSTANTS_HTML" file="resources\md_history\constants.html" type="BINDATA" />
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index e90776d..840d9a1 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -359,7 +359,7 @@
 #endif
 
 #if BUILDFLAG(ENABLE_SPELLCHECK)
-#include "chrome/browser/spellchecker/spellcheck_message_filter.h"
+#include "chrome/browser/spellchecker/spell_check_host_impl.h"
 #endif
 
 #if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
@@ -1205,9 +1205,6 @@
 #if BUILDFLAG(ENABLE_PRINTING)
   host->AddFilter(new printing::PrintingMessageFilter(id, profile));
 #endif
-#if BUILDFLAG(ENABLE_SPELLCHECK)
-  host->AddFilter(new SpellCheckMessageFilter(id));
-#endif
 #if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
   host->AddFilter(new SpellCheckMessageFilterPlatform(id));
 #endif
@@ -3071,6 +3068,11 @@
   registry->AddInterface(
       base::Bind(&BudgetServiceImpl::Create, render_process_host->GetID()),
       ui_task_runner);
+#if BUILDFLAG(ENABLE_SPELLCHECK)
+  registry->AddInterface(
+      base::Bind(&SpellCheckHostImpl::Create, render_process_host->GetID()),
+      ui_task_runner);
+#endif
   registry->AddInterface(
       base::Bind(&rappor::RapporRecorderImpl::Create,
                  g_browser_process->rappor_service()),
diff --git a/chrome/browser/chrome_content_browser_manifest_overlay.json b/chrome/browser/chrome_content_browser_manifest_overlay.json
index 887404b..8f8676b 100644
--- a/chrome/browser/chrome_content_browser_manifest_overlay.json
+++ b/chrome/browser/chrome_content_browser_manifest_overlay.json
@@ -13,6 +13,7 @@
           "metrics::mojom::LeakDetector",
           "mojom::ModuleEventSink",
           "rappor::mojom::RapporRecorder",
+          "spellcheck::mojom::SpellCheckHost",
           "startup_metric_utils::mojom::StartupMetricHost",
           "translate::mojom::ContentTranslateDriver"
         ],
diff --git a/chrome/browser/chrome_site_per_process_browsertest.cc b/chrome/browser/chrome_site_per_process_browsertest.cc
index 1c6047f..27e813c 100644
--- a/chrome/browser/chrome_site_per_process_browsertest.cc
+++ b/chrome/browser/chrome_site_per_process_browsertest.cc
@@ -38,7 +38,9 @@
 #include "url/gurl.h"
 
 #if BUILDFLAG(ENABLE_SPELLCHECK)
+#include "components/spellcheck/common/spellcheck.mojom.h"
 #include "components/spellcheck/common/spellcheck_messages.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
 #endif
 
 class ChromeSitePerProcessTest : public InProcessBrowserTest {
@@ -560,58 +562,87 @@
 }
 
 #if BUILDFLAG(ENABLE_SPELLCHECK)
-// Class to sniff incoming IPCs for spell check messages.
-class TestSpellCheckMessageFilter : public content::BrowserMessageFilter {
+// Class to sniff incoming spellcheck IPC / Mojo SpellCheckHost messages.
+class TestSpellCheckMessageFilter : public content::BrowserMessageFilter,
+                                    spellcheck::mojom::SpellCheckHost {
  public:
   explicit TestSpellCheckMessageFilter(content::RenderProcessHost* process_host)
       : content::BrowserMessageFilter(SpellCheckMsgStart),
         process_host_(process_host),
         text_received_(false),
         message_loop_runner_(
-            base::MakeRefCounted<content::MessageLoopRunner>()) {}
+            base::MakeRefCounted<content::MessageLoopRunner>()),
+        binding_(this) {}
 
-  bool OnMessageReceived(const IPC::Message& message) override {
-    IPC_BEGIN_MESSAGE_MAP(TestSpellCheckMessageFilter, message)
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-      IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CallSpellingService, HandleMessage)
-#else
-      IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestTextCheck, HandleMessage)
-#endif
-    IPC_END_MESSAGE_MAP()
-    return false;
-  }
+  content::RenderProcessHost* process() const { return process_host_; }
 
-  base::string16 last_text() const { return last_text_; }
+  const base::string16& text() const { return text_; }
 
   void Wait() {
     if (!text_received_)
       message_loop_runner_->Run();
   }
 
-  content::RenderProcessHost* process() const { return process_host_; }
+  bool OnMessageReceived(const IPC::Message& message) override {
+#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+    IPC_BEGIN_MESSAGE_MAP(TestSpellCheckMessageFilter, message)
+      // TODO(crbug.com/714480): convert the RequestTextCheck IPC to mojo.
+      IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestTextCheck, HandleMessage)
+    IPC_END_MESSAGE_MAP()
+#endif
+    return false;
+  }
+
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  void ShellCheckHostRequest(const service_manager::BindSourceInfo& source_info,
+                             spellcheck::mojom::SpellCheckHostRequest request) {
+    EXPECT_FALSE(binding_.is_bound());
+    binding_.Bind(std::move(request));
+  }
+#endif
 
  private:
   ~TestSpellCheckMessageFilter() override {}
 
+#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
   void HandleMessage(int, int, const base::string16& text) {
     content::BrowserThread::PostTask(
         content::BrowserThread::UI, FROM_HERE,
-        base::BindOnce(&TestSpellCheckMessageFilter::HandleMessageOnUI, this,
-                       text));
+        base::BindOnce(&TestSpellCheckMessageFilter::HandleMessageOnUIThread,
+                       this, text));
   }
+#endif
 
-  void HandleMessageOnUI(const base::string16& text) {
-    last_text_ = text;
+  void HandleMessageOnUIThread(const base::string16& text) {
     if (!text_received_) {
       text_received_ = true;
+      text_ = text;
       message_loop_runner_->Quit();
+    } else {
+      NOTREACHED();
     }
   }
 
+  // spellcheck::mojom::SpellCheckHost:
+  void RequestDictionary() override {}
+
+  void NotifyChecked(const base::string16& word, bool misspelled) override {}
+
+  void CallSpellingService(const base::string16& text,
+                           CallSpellingServiceCallback callback) override {
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+    std::move(callback).Run(true, std::vector<SpellCheckResult>());
+    binding_.Close();
+    HandleMessageOnUIThread(text);
+#endif
+  }
+
   content::RenderProcessHost* process_host_;
   bool text_received_;
-  base::string16 last_text_;
+  base::string16 text_;
   scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+  mojo::Binding<spellcheck::mojom::SpellCheckHost> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(TestSpellCheckMessageFilter);
 };
@@ -629,6 +660,28 @@
     ChromeContentBrowserClient::RenderProcessWillLaunch(process_host);
   }
 
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  void ExposeInterfacesToRenderer(
+      service_manager::BinderRegistry* registry,
+      content::AssociatedInterfaceRegistry* associated_registry,
+      content::RenderProcessHost* render_process_host) override {
+    // Expose the default interfaces.
+    ChromeContentBrowserClient::ExposeInterfacesToRenderer(
+        registry, associated_registry, render_process_host);
+
+    scoped_refptr<TestSpellCheckMessageFilter> filter =
+        GetSpellCheckMessageFilterForProcess(render_process_host);
+    CHECK(filter);
+
+    // Override the default SpellCheckHost interface.
+    auto ui_task_runner = content::BrowserThread::GetTaskRunnerForThread(
+        content::BrowserThread::UI);
+    registry->AddInterface(
+        base::Bind(&TestSpellCheckMessageFilter::ShellCheckHostRequest, filter),
+        ui_task_runner);
+  }
+#endif  // !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
   // Retrieves the registered filter for the given RenderProcessHost. It will
   // return nullptr if the RenderProcessHost was initialized while a different
   // instance of ContentBrowserClient was in action.
@@ -661,14 +714,15 @@
 
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  content::RenderFrameHost* subframe =
+  content::RenderFrameHost* cross_site_subframe =
       ChildFrameAt(web_contents->GetMainFrame(), 0);
+
   scoped_refptr<TestSpellCheckMessageFilter> filter =
       browser_client.GetSpellCheckMessageFilterForProcess(
-          subframe->GetProcess());
+          cross_site_subframe->GetProcess());
   filter->Wait();
 
-  EXPECT_EQ(base::ASCIIToUTF16("zz."), filter->last_text());
+  EXPECT_EQ(base::ASCIIToUTF16("zz."), filter->text());
 
   content::SetBrowserClientForTesting(old_browser_client);
 }
diff --git a/chrome/browser/resources/md_bookmarks/BUILD.gn b/chrome/browser/resources/md_bookmarks/BUILD.gn
new file mode 100644
index 0000000..eea759d7
--- /dev/null
+++ b/chrome/browser/resources/md_bookmarks/BUILD.gn
@@ -0,0 +1,22 @@
+import("../vulcanize.gni")
+
+vulcanized_unbuilt = "vulcanized.unbuilt.html"
+
+vulcanize("vulcanize") {
+  host = "bookmarks"
+  html_in_file = "bookmarks.html"
+  html_out_file = vulcanized_unbuilt
+
+  input = rebase_path(".", root_build_dir)
+  js_out_file = "crisper.js"
+
+  deps = []
+}
+
+polymer_css_build("build") {
+  input_files = [ vulcanized_unbuilt ]
+  output_files = [ "vulcanized.html" ]
+  deps = [
+    ":vulcanize",
+  ]
+}
diff --git a/chrome/browser/resources/md_bookmarks/bookmarks.html b/chrome/browser/resources/md_bookmarks/bookmarks.html
index 955f7fb..685fb57 100644
--- a/chrome/browser/resources/md_bookmarks/bookmarks.html
+++ b/chrome/browser/resources/md_bookmarks/bookmarks.html
@@ -5,9 +5,6 @@
   <title>$i18n{title}</title>
   <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
   <link rel="stylesheet" href="chrome://resources/css/md_colors.css">
-  <link rel="import" href="chrome://resources/html/load_time_data.html">
-  <script src="chrome://bookmarks/strings.js"></script>
-  <link rel="import" href="chrome://bookmarks/app.html">
   <style>
     html,
     body {
@@ -19,5 +16,9 @@
 </head>
 <body>
   <bookmarks-app></bookmarks-app>
+
+  <link rel="import" href="chrome://resources/html/load_time_data.html">
+  <script src="chrome://bookmarks/strings.js"></script>
+  <link rel="import" href="chrome://bookmarks/app.html">
 </body>
 </html>
diff --git a/chrome/browser/spellchecker/spell_check_host_impl.cc b/chrome/browser/spellchecker/spell_check_host_impl.cc
new file mode 100644
index 0000000..788a19c
--- /dev/null
+++ b/chrome/browser/spellchecker/spell_check_host_impl.cc
@@ -0,0 +1,133 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/spellchecker/spell_check_host_impl.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/spellchecker/spellcheck_custom_dictionary.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
+#include "components/spellcheck/browser/spellcheck_host_metrics.h"
+#include "components/spellcheck/common/spellcheck_result.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+
+SpellCheckHostImpl::SpellCheckHostImpl(int render_process_id)
+    : render_process_id_(render_process_id) {}
+
+SpellCheckHostImpl::~SpellCheckHostImpl() = default;
+
+// static
+void SpellCheckHostImpl::Create(
+    int render_process_id,
+    const service_manager::BindSourceInfo& source_info,
+    spellcheck::mojom::SpellCheckHostRequest request) {
+  mojo::MakeStrongBinding(
+      base::MakeUnique<SpellCheckHostImpl>(render_process_id),
+      std::move(request));
+}
+
+void SpellCheckHostImpl::RequestDictionary() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // The renderer has requested that we initialize its spellchecker. This
+  // generally should only be called once per session, as after the first
+  // call, future renderers will be passed the initialization information
+  // on startup (or when the dictionary changes in some way).
+  SpellcheckService* spellcheck = GetSpellcheckService();
+  if (!spellcheck)
+    return;  // Teardown.
+
+  // The spellchecker initialization already started and finished; just
+  // send it to the renderer.
+  spellcheck->InitForRenderer(
+      content::RenderProcessHost::FromID(render_process_id_));
+
+  // TODO(rlp): Ensure that we do not initialize the hunspell dictionary
+  // more than once if we get requests from different renderers.
+}
+
+void SpellCheckHostImpl::NotifyChecked(const base::string16& word,
+                                       bool misspelled) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  SpellcheckService* spellcheck = GetSpellcheckService();
+  if (!spellcheck)
+    return;  // Teardown.
+  if (spellcheck->GetMetrics())
+    spellcheck->GetMetrics()->RecordCheckedWordStats(word, misspelled);
+}
+
+void SpellCheckHostImpl::CallSpellingService(
+    const base::string16& text,
+    CallSpellingServiceCallback callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (text.empty()) {
+    std::move(callback).Run(false, std::vector<SpellCheckResult>());
+    mojo::ReportBadMessage(__FUNCTION__);
+    return;
+  }
+
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  content::RenderProcessHost* host =
+      content::RenderProcessHost::FromID(render_process_id_);
+
+  // Checks the user profile and sends a JSON-RPC request to the Spelling
+  // service if a user enables the "Ask Google for suggestions" option. When
+  // a response is received (including an error) from the remote Spelling
+  // service, calls CallSpellingServiceDone.
+  client_.RequestTextCheck(
+      host ? host->GetBrowserContext() : nullptr,
+      SpellingServiceClient::SPELLCHECK, text,
+      base::Bind(&SpellCheckHostImpl::CallSpellingServiceDone,
+                 base::Unretained(this), base::Passed(&callback)));
+#else
+  std::move(callback).Run(false, std::vector<SpellCheckResult>());
+#endif
+}
+
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+void SpellCheckHostImpl::CallSpellingServiceDone(
+    CallSpellingServiceCallback callback,
+    bool success,
+    const base::string16& text,
+    const std::vector<SpellCheckResult>& service_results) const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  SpellcheckService* spellcheck = GetSpellcheckService();
+  if (!spellcheck) {  // Teardown.
+    std::move(callback).Run(false, std::vector<SpellCheckResult>());
+    return;
+  }
+
+  std::vector<SpellCheckResult> results = FilterCustomWordResults(
+      base::UTF16ToUTF8(text), *spellcheck->GetCustomDictionary(),
+      service_results);
+
+  std::move(callback).Run(success, results);
+}
+
+// static
+std::vector<SpellCheckResult> SpellCheckHostImpl::FilterCustomWordResults(
+    const std::string& text,
+    const SpellcheckCustomDictionary& custom_dictionary,
+    const std::vector<SpellCheckResult>& service_results) {
+  std::vector<SpellCheckResult> results;
+  for (const auto& result : service_results) {
+    const std::string word = text.substr(result.location, result.length);
+    if (!custom_dictionary.HasWord(word))
+      results.push_back(result);
+  }
+
+  return results;
+}
+#endif  // !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
+SpellcheckService* SpellCheckHostImpl::GetSpellcheckService() const {
+  return SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
+}
diff --git a/chrome/browser/spellchecker/spell_check_host_impl.h b/chrome/browser/spellchecker/spell_check_host_impl.h
new file mode 100644
index 0000000..9349fa1
--- /dev/null
+++ b/chrome/browser/spellchecker/spell_check_host_impl.h
@@ -0,0 +1,74 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SPELLCHECKER_SPELL_CHECK_HOST_IMPL_H_
+#define CHROME_BROWSER_SPELLCHECKER_SPELL_CHECK_HOST_IMPL_H_
+
+#include "base/macros.h"
+#include "components/spellcheck/browser/spelling_service_client.h"
+#include "components/spellcheck/common/spellcheck.mojom.h"
+#include "components/spellcheck/spellcheck_build_features.h"
+
+#if !BUILDFLAG(ENABLE_SPELLCHECK)
+#error "Spellcheck should be enabled."
+#endif
+
+class SpellcheckCustomDictionary;
+class SpellcheckService;
+
+struct SpellCheckResult;
+
+namespace service_manager {
+struct BindSourceInfo;
+}
+
+class SpellCheckHostImpl : public spellcheck::mojom::SpellCheckHost {
+ public:
+  explicit SpellCheckHostImpl(int render_process_id);
+  ~SpellCheckHostImpl() override;
+
+  static void Create(int render_process_id,
+                     const service_manager::BindSourceInfo& source_info,
+                     spellcheck::mojom::SpellCheckHostRequest request);
+
+ private:
+  friend class TestSpellCheckHostImpl;
+
+  // spellcheck::mojom::SpellCheckHost:
+  void RequestDictionary() override;
+  void NotifyChecked(const base::string16& word, bool misspelled) override;
+  void CallSpellingService(const base::string16& text,
+                           CallSpellingServiceCallback callback) override;
+
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  // Invoked when the remote Spelling service has finished checking the
+  // text of a CallSpellingService request.
+  void CallSpellingServiceDone(
+      CallSpellingServiceCallback callback,
+      bool success,
+      const base::string16& text,
+      const std::vector<SpellCheckResult>& service_results) const;
+
+  // Filter out spelling corrections of custom dictionary words from the
+  // Spelling service results.
+  static std::vector<SpellCheckResult> FilterCustomWordResults(
+      const std::string& text,
+      const SpellcheckCustomDictionary& custom_dictionary,
+      const std::vector<SpellCheckResult>& service_results);
+#endif
+
+  // Returns the SpellcheckService of our |render_process_id_|. The return
+  // is null if the render process is being shut down.
+  virtual SpellcheckService* GetSpellcheckService() const;
+
+  // The process ID of our render process host.
+  const int render_process_id_;
+
+  // A JSON-RPC client that calls the remote Spelling service.
+  SpellingServiceClient client_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpellCheckHostImpl);
+};
+
+#endif  // CHROME_BROWSER_SPELLCHECKER_SPELL_CHECK_HOST_IMPL_H_
diff --git a/chrome/browser/spellchecker/spell_check_host_impl_unittest.cc b/chrome/browser/spellchecker/spell_check_host_impl_unittest.cc
new file mode 100644
index 0000000..22cadb6
--- /dev/null
+++ b/chrome/browser/spellchecker/spell_check_host_impl_unittest.cc
@@ -0,0 +1,89 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/spellchecker/spell_check_host_impl.h"
+
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/spellcheck/common/spellcheck_result.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+#error !BUILDFLAG(USE_BROWSER_SPELLCHECKER) is required for these tests.
+#endif
+
+class TestSpellCheckHostImpl {
+ public:
+  TestSpellCheckHostImpl()
+      : spellcheck_(base::MakeUnique<SpellcheckService>(&testing_profile_)) {}
+
+  SpellcheckCustomDictionary& GetCustomDictionary() const {
+    EXPECT_NE(nullptr, spellcheck_.get());
+    SpellcheckCustomDictionary* custom_dictionary =
+        spellcheck_->GetCustomDictionary();
+    return *custom_dictionary;
+  }
+
+  std::vector<SpellCheckResult> FilterCustomWordResults(
+      const std::string& text,
+      const std::vector<SpellCheckResult>& service_results) const {
+    return SpellCheckHostImpl::FilterCustomWordResults(
+        text, GetCustomDictionary(), service_results);
+  }
+
+ private:
+  content::TestBrowserThreadBundle thread_bundle_;
+  TestingProfile testing_profile_;
+  std::unique_ptr<SpellcheckService> spellcheck_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSpellCheckHostImpl);
+};
+
+// Spelling corrections of custom dictionary words should be removed from the
+// results returned by the remote Spelling service.
+TEST(SpellCheckHostImplTest, CustomSpellingResults) {
+  std::vector<SpellCheckResult> service_results;
+  service_results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, 0, 6,
+                                             base::ASCIIToUTF16("Hello")));
+  service_results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, 7, 5,
+                                             base::ASCIIToUTF16("World")));
+  TestSpellCheckHostImpl host_impl;
+  host_impl.GetCustomDictionary().AddWord("Helllo");
+  std::vector<SpellCheckResult> results =
+      host_impl.FilterCustomWordResults("Helllo Warld", service_results);
+  ASSERT_EQ(1u, results.size());
+
+  EXPECT_EQ(service_results[1].decoration, results[0].decoration);
+  EXPECT_EQ(service_results[1].location, results[0].location);
+  EXPECT_EQ(service_results[1].length, results[0].length);
+  EXPECT_EQ(service_results[1].replacement, results[0].replacement);
+}
+
+// Spelling corrections of words that are not in the custom dictionary should
+// be retained in the results returned by the remote Spelling service.
+TEST(SpellCheckHostImplTest, SpellingServiceResults) {
+  std::vector<SpellCheckResult> service_results;
+  service_results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, 0, 6,
+                                             base::ASCIIToUTF16("Hello")));
+  service_results.push_back(SpellCheckResult(SpellCheckResult::SPELLING, 7, 5,
+                                             base::ASCIIToUTF16("World")));
+  TestSpellCheckHostImpl host_impl;
+  host_impl.GetCustomDictionary().AddWord("Hulo");
+  std::vector<SpellCheckResult> results =
+      host_impl.FilterCustomWordResults("Helllo Warld", service_results);
+  ASSERT_EQ(service_results.size(), results.size());
+
+  for (size_t i = 0; i < results.size(); ++i) {
+    EXPECT_EQ(service_results[i].decoration, results[i].decoration);
+    EXPECT_EQ(service_results[i].location, results[i].location);
+    EXPECT_EQ(service_results[i].length, results[i].length);
+    EXPECT_EQ(service_results[i].replacement, results[i].replacement);
+  }
+}
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter.cc b/chrome/browser/spellchecker/spellcheck_message_filter.cc
deleted file mode 100644
index 7f69a55..0000000
--- a/chrome/browser/spellchecker/spellcheck_message_filter.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/spellchecker/spellcheck_message_filter.h"
-
-#include <algorithm>
-#include <functional>
-
-#include "base/bind.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/spellchecker/spellcheck_factory.h"
-#include "chrome/browser/spellchecker/spellcheck_service.h"
-#include "components/prefs/pref_service.h"
-#include "components/spellcheck/browser/spellcheck_host_metrics.h"
-#include "components/spellcheck/browser/spelling_service_client.h"
-#include "components/spellcheck/common/spellcheck_messages.h"
-#include "components/spellcheck/spellcheck_build_features.h"
-#include "content/public/browser/render_process_host.h"
-#include "net/url_request/url_fetcher.h"
-
-using content::BrowserThread;
-
-SpellCheckMessageFilter::SpellCheckMessageFilter(int render_process_id)
-    : BrowserMessageFilter(SpellCheckMsgStart),
-      render_process_id_(render_process_id),
-      client_(new SpellingServiceClient) {
-}
-
-void SpellCheckMessageFilter::OverrideThreadForMessage(
-    const IPC::Message& message, BrowserThread::ID* thread) {
-  // IPC messages arrive on IO thread, but spellcheck data lives on UI thread.
-  // The message filter overrides the thread for these messages because they
-  // access spellcheck data.
-  if (message.type() == SpellCheckHostMsg_RequestDictionary::ID ||
-      message.type() == SpellCheckHostMsg_NotifyChecked::ID)
-    *thread = BrowserThread::UI;
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-  if (message.type() == SpellCheckHostMsg_CallSpellingService::ID)
-    *thread = BrowserThread::UI;
-#endif
-}
-
-bool SpellCheckMessageFilter::OnMessageReceived(const IPC::Message& message) {
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(SpellCheckMessageFilter, message)
-    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_RequestDictionary,
-                        OnSpellCheckerRequestDictionary)
-    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_NotifyChecked,
-                        OnNotifyChecked)
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CallSpellingService,
-                        OnCallSpellingService)
-#endif
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-  return handled;
-}
-
-SpellCheckMessageFilter::~SpellCheckMessageFilter() {}
-
-void SpellCheckMessageFilter::OnSpellCheckerRequestDictionary() {
-  content::RenderProcessHost* host =
-      content::RenderProcessHost::FromID(render_process_id_);
-  if (!host)
-    return;  // Teardown.
-  // The renderer has requested that we initialize its spellchecker. This should
-  // generally only be called once per session, as after the first call, all
-  // future renderers will be passed the initialization information on startup
-  // (or when the dictionary changes in some way).
-  SpellcheckService* spellcheck_service =
-      SpellcheckServiceFactory::GetForContext(host->GetBrowserContext());
-
-  DCHECK(spellcheck_service);
-  // The spellchecker initialization already started and finished; just send
-  // it to the renderer.
-  spellcheck_service->InitForRenderer(host);
-
-  // TODO(rlp): Ensure that we do not initialize the hunspell dictionary more
-  // than once if we get requests from different renderers.
-}
-
-void SpellCheckMessageFilter::OnNotifyChecked(const base::string16& word,
-                                              bool misspelled) {
-  SpellcheckService* spellcheck = GetSpellcheckService();
-  // Spellcheck service may not be available for a renderer process that is
-  // shutting down.
-  if (!spellcheck)
-    return;
-  if (spellcheck->GetMetrics())
-    spellcheck->GetMetrics()->RecordCheckedWordStats(word, misspelled);
-}
-
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-void SpellCheckMessageFilter::OnCallSpellingService(
-    int route_id,
-    int identifier,
-    const base::string16& text) {
-  DCHECK(!text.empty());
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  CallSpellingService(text, route_id, identifier);
-}
-
-void SpellCheckMessageFilter::OnTextCheckComplete(
-    int route_id,
-    int identifier,
-    bool success,
-    const base::string16& text,
-    const std::vector<SpellCheckResult>& results) {
-  SpellcheckService* spellcheck = GetSpellcheckService();
-  // Spellcheck service may not be available for a renderer process that is
-  // shutting down.
-  if (!spellcheck)
-    return;
-  std::vector<SpellCheckResult> results_copy = results;
-
-  // Erase custom dictionary words from the spellcheck results and record
-  // in-dictionary feedback.
-  std::vector<SpellCheckResult>::iterator write_iter;
-  std::vector<SpellCheckResult>::iterator iter;
-  std::string text_copy = base::UTF16ToUTF8(text);
-  for (iter = write_iter = results_copy.begin();
-       iter != results_copy.end();
-       ++iter) {
-    if (!spellcheck->GetCustomDictionary()->HasWord(
-            text_copy.substr(iter->location, iter->length))) {
-      if (write_iter != iter)
-        *write_iter = *iter;
-      ++write_iter;
-    }
-  }
-  results_copy.erase(write_iter, results_copy.end());
-
-  Send(new SpellCheckMsg_RespondSpellingService(
-      route_id, identifier, success, text, results_copy));
-}
-
-// CallSpellingService always executes the callback OnTextCheckComplete.
-// (Which, in turn, sends a SpellCheckMsg_RespondSpellingService)
-void SpellCheckMessageFilter::CallSpellingService(const base::string16& text,
-                                                  int route_id,
-                                                  int identifier) {
-  content::RenderProcessHost* host =
-      content::RenderProcessHost::FromID(render_process_id_);
-
-  client_->RequestTextCheck(
-      host ? host->GetBrowserContext() : NULL,
-      SpellingServiceClient::SPELLCHECK, text,
-      base::Bind(&SpellCheckMessageFilter::OnTextCheckComplete,
-                 base::Unretained(this), route_id, identifier));
-}
-#endif
-
-SpellcheckService* SpellCheckMessageFilter::GetSpellcheckService() const {
-  return SpellcheckServiceFactory::GetForRenderProcessId(render_process_id_);
-}
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter.h b/chrome/browser/spellchecker/spellcheck_message_filter.h
deleted file mode 100644
index 8f62b1e5..0000000
--- a/chrome/browser/spellchecker/spellcheck_message_filter.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_MESSAGE_FILTER_H_
-#define CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_MESSAGE_FILTER_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "components/spellcheck/browser/spelling_service_client.h"
-#include "components/spellcheck/spellcheck_build_features.h"
-#include "content/public/browser/browser_message_filter.h"
-
-class SpellcheckService;
-struct SpellCheckResult;
-
-// A message filter implementation that receives spell checker requests from
-// SpellCheckProvider.
-class SpellCheckMessageFilter : public content::BrowserMessageFilter {
- public:
-  explicit SpellCheckMessageFilter(int render_process_id);
-
-  // content::BrowserMessageFilter implementation.
-  void OverrideThreadForMessage(const IPC::Message& message,
-                                content::BrowserThread::ID* thread) override;
-  bool OnMessageReceived(const IPC::Message& message) override;
-
- private:
-  friend class TestingSpellCheckMessageFilter;
-
-  ~SpellCheckMessageFilter() override;
-
-  void OnSpellCheckerRequestDictionary();
-  void OnNotifyChecked(const base::string16& word, bool misspelled);
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-  void OnCallSpellingService(int route_id,
-                             int identifier,
-                             const base::string16& text);
-
-  // A callback function called when the Spelling service finishes checking
-  // text. Sends the given results to a renderer.
-  void OnTextCheckComplete(
-      int route_id,
-      int identifier,
-      bool success,
-      const base::string16& text,
-      const std::vector<SpellCheckResult>& results);
-
-  // Checks the user profile and sends a JSON-RPC request to the Spelling
-  // service if a user enables the "Ask Google for suggestions" option. When we
-  // receive a response (including an error) from the service, it calls
-  // OnTextCheckComplete. When this function is called before we receive a
-  // response for the previous request, this function cancels the previous
-  // request and sends a new one.
-  void CallSpellingService(const base::string16& text,
-                           int route_id,
-                           int identifier);
-#endif
-
-  // Can be overridden for testing.
-  virtual SpellcheckService* GetSpellcheckService() const;
-
-  int render_process_id_;
-
-  // A JSON-RPC client that calls the Spelling service in the background.
-  std::unique_ptr<SpellingServiceClient> client_;
-};
-
-#endif  // CHROME_BROWSER_SPELLCHECKER_SPELLCHECK_MESSAGE_FILTER_H_
diff --git a/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc b/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
deleted file mode 100644
index 62d445f9..0000000
--- a/chrome/browser/spellchecker/spellcheck_message_filter_unittest.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <memory>
-#include <tuple>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/spellchecker/spellcheck_factory.h"
-#include "chrome/browser/spellchecker/spellcheck_message_filter.h"
-#include "chrome/browser/spellchecker/spellcheck_service.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/spellcheck/common/spellcheck_messages.h"
-#include "components/spellcheck/spellcheck_build_features.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "ipc/ipc_message.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-class TestingSpellCheckMessageFilter : public SpellCheckMessageFilter {
- public:
-  TestingSpellCheckMessageFilter()
-      : SpellCheckMessageFilter(0),
-        spellcheck_(new SpellcheckService(&profile_)) {}
-
-  bool Send(IPC::Message* message) override {
-    sent_messages.push_back(base::WrapUnique(message));
-    return true;
-  }
-
-  SpellcheckService* GetSpellcheckService() const override {
-    return spellcheck_.get();
-  }
-
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-  void OnTextCheckComplete(int route_id,
-                           int identifier,
-                           bool success,
-                           const base::string16& text,
-                           const std::vector<SpellCheckResult>& results) {
-    SpellCheckMessageFilter::OnTextCheckComplete(route_id, identifier, success,
-                                                 text, results);
-  }
-#endif
-
-  std::vector<std::unique_ptr<IPC::Message>> sent_messages;
-
- private:
-  ~TestingSpellCheckMessageFilter() override {}
-
-  content::TestBrowserThreadBundle thread_bundle_;
-  TestingProfile profile_;
-  std::unique_ptr<SpellcheckService> spellcheck_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestingSpellCheckMessageFilter);
-};
-
-TEST(SpellCheckMessageFilterTest, TestOverrideThread) {
-  static const uint32_t kSpellcheckMessages[] = {
-    SpellCheckHostMsg_RequestDictionary::ID,
-    SpellCheckHostMsg_NotifyChecked::ID,
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-    SpellCheckHostMsg_CallSpellingService::ID,
-#endif
-  };
-  content::BrowserThread::ID thread;
-  IPC::Message message;
-  scoped_refptr<TestingSpellCheckMessageFilter> filter(
-      new TestingSpellCheckMessageFilter);
-  for (size_t i = 0; i < arraysize(kSpellcheckMessages); ++i) {
-    message.SetHeaderValues(
-        0, kSpellcheckMessages[i], IPC::Message::PRIORITY_NORMAL);
-    thread = content::BrowserThread::IO;
-    filter->OverrideThreadForMessage(message, &thread);
-    EXPECT_EQ(content::BrowserThread::UI, thread);
-  }
-}
-
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-TEST(SpellCheckMessageFilterTest, OnTextCheckCompleteTestCustomDictionary) {
-  static const std::string kCustomWord = "Helllo";
-  static const int kRouteId = 0;
-  static const int kCallbackId = 0;
-  static const base::string16 kText = base::ASCIIToUTF16("Helllo warld.");
-  static const bool kSuccess = true;
-  static const SpellCheckResult::Decoration kDecoration =
-      SpellCheckResult::SPELLING;
-  static const int kLocation = 7;
-  static const int kLength = 5;
-  static const base::string16 kReplacement = base::ASCIIToUTF16("world");
-
-  std::vector<SpellCheckResult> results;
-  results.push_back(SpellCheckResult(
-      SpellCheckResult::SPELLING, 0, 6, base::ASCIIToUTF16("Hello")));
-  results.push_back(
-      SpellCheckResult(kDecoration, kLocation, kLength, kReplacement));
-
-  scoped_refptr<TestingSpellCheckMessageFilter> filter(
-      new TestingSpellCheckMessageFilter);
-  filter->GetSpellcheckService()->GetCustomDictionary()->AddWord(kCustomWord);
-  filter->OnTextCheckComplete(kRouteId, kCallbackId, kSuccess, kText, results);
-  ASSERT_EQ(1U, filter->sent_messages.size());
-
-  SpellCheckMsg_RespondSpellingService::Param params;
-  bool ok = SpellCheckMsg_RespondSpellingService::Read(
-      filter->sent_messages[0].get(), &params);
-  int sent_identifier = std::get<0>(params);
-  bool sent_success = std::get<1>(params);
-  base::string16 sent_text = std::get<2>(params);
-  std::vector<SpellCheckResult> sent_results = std::get<3>(params);
-  EXPECT_TRUE(ok);
-  EXPECT_EQ(kCallbackId, sent_identifier);
-  EXPECT_EQ(kSuccess, sent_success);
-  EXPECT_EQ(kText, sent_text);
-  ASSERT_EQ(1U, sent_results.size());
-  EXPECT_EQ(kDecoration, sent_results[0].decoration);
-  EXPECT_EQ(kLocation, sent_results[0].location);
-  EXPECT_EQ(kLength, sent_results[0].length);
-  EXPECT_EQ(kReplacement, sent_results[0].replacement);
-}
-
-TEST(SpellCheckMessageFilterTest, OnTextCheckCompleteTest) {
-  std::vector<SpellCheckResult> results;
-  results.push_back(SpellCheckResult(
-      SpellCheckResult::SPELLING, 0, 6, base::ASCIIToUTF16("Hello")));
-  results.push_back(SpellCheckResult(
-      SpellCheckResult::SPELLING, 7, 7, base::ASCIIToUTF16("world")));
-
-  scoped_refptr<TestingSpellCheckMessageFilter> filter(
-      new TestingSpellCheckMessageFilter);
-  filter->OnTextCheckComplete(1, 1, true, base::ASCIIToUTF16("Helllo walrd"),
-                              results);
-  ASSERT_EQ(1U, filter->sent_messages.size());
-
-  SpellCheckMsg_RespondSpellingService::Param params;
-  bool ok = SpellCheckMsg_RespondSpellingService::Read(
-      filter->sent_messages[0].get(), &params);
-  EXPECT_TRUE(ok);
-
-  std::vector<SpellCheckResult> sent_results = std::get<3>(params);
-  EXPECT_EQ(2U, sent_results.size());
-}
-#endif
diff --git a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
index 5abe4b7..2afbf90 100644
--- a/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
+++ b/chrome/browser/spellchecker/spellcheck_service_browsertest.cc
@@ -4,54 +4,40 @@
 
 #include "chrome/browser/spellchecker/spellcheck_service.h"
 
-#include <stddef.h>
-#include <stdint.h>
 #include <string>
-#include <tuple>
 #include <vector>
 
-#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/test/histogram_tester.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/values.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/spellchecker/spell_check_host_impl.h"
 #include "chrome/browser/spellchecker/spellcheck_factory.h"
+#include "chrome/browser/spellchecker/spellcheck_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/common/chrome_paths.h"
-#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/prefs/pref_service.h"
 #include "components/spellcheck/browser/pref_names.h"
 #include "components/spellcheck/common/spellcheck.mojom.h"
 #include "components/spellcheck/common/spellcheck_common.h"
+#include "components/spellcheck/common/spellcheck_result.h"
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/test/mock_render_process_host.h"
 #include "content/public/test/test_utils.h"
-#include "url/gurl.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
 
 using content::BrowserContext;
-
-namespace {
-
-// A corrupted BDICT data used in DeleteCorruptedBDICT. Please do not use this
-// BDICT data for other tests.
-const uint8_t kCorruptedBDICT[] = {
-    0x42, 0x44, 0x69, 0x63, 0x02, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
-    0x3b, 0x00, 0x00, 0x00, 0x65, 0x72, 0xe0, 0xac, 0x27, 0xc7, 0xda, 0x66,
-    0x6d, 0x1e, 0xa6, 0x35, 0xd1, 0xf6, 0xb7, 0x35, 0x32, 0x00, 0x00, 0x00,
-    0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
-    0x0a, 0x0a, 0x41, 0x46, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6,
-    0x49, 0x00, 0x68, 0x02, 0x73, 0x06, 0x74, 0x0b, 0x77, 0x11, 0x79, 0x15,
-};
-
-}  // namespace
+using content::RenderProcessHost;
 
 class SpellcheckServiceBrowserTest : public InProcessBrowserTest,
                                      public spellcheck::mojom::SpellChecker {
@@ -69,13 +55,13 @@
     renderer_.reset();
   }
 
-  BrowserContext* GetContext() {
+  RenderProcessHost* GetRenderer() const { return renderer_.get(); }
+
+  BrowserContext* GetContext() const {
     return static_cast<BrowserContext*>(browser()->profile());
   }
 
-  PrefService* GetPrefs() {
-    return prefs_;
-  }
+  PrefService* GetPrefs() const { return prefs_; }
 
   void InitSpellcheck(bool enable_spellcheck,
                       const std::string& single_dictionary,
@@ -208,6 +194,11 @@
     EXPECT_EQ(2u, words_added.size());
   }
 
+ protected:
+  // Quits the RunLoop on Mojo request flow completion.
+  base::OnceClosure quit_;
+
+ private:
   // Mocked RenderProcessHost.
   std::unique_ptr<content::MockRenderProcessHost> renderer_;
 
@@ -217,9 +208,6 @@
   // Binding to receive the SpellChecker request flow.
   mojo::Binding<spellcheck::mojom::SpellChecker> binding_;
 
-  // Quits the RunLoop on SpellChecker request flow completion.
-  base::OnceClosure quit_;
-
   // Used to verify the SpellChecker request flow.
   bool bound_connection_closed_;
   bool custom_dictionary_changed_called_;
@@ -229,6 +217,64 @@
   DISALLOW_COPY_AND_ASSIGN(SpellcheckServiceBrowserTest);
 };
 
+class SpellcheckServiceHostBrowserTest : public SpellcheckServiceBrowserTest {
+ public:
+  SpellcheckServiceHostBrowserTest() = default;
+
+  void RequestDictionary() {
+    spellcheck::mojom::SpellCheckHostPtr interface;
+    RequestSpellCheckHost(&interface);
+
+    interface->RequestDictionary();
+  }
+
+  void NotifyChecked() {
+    spellcheck::mojom::SpellCheckHostPtr interface;
+    RequestSpellCheckHost(&interface);
+
+    const bool misspelt = true;
+    base::UTF8ToUTF16("hallo", 5, &word_);
+    interface->NotifyChecked(word_, misspelt);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void CallSpellingService() {
+    spellcheck::mojom::SpellCheckHostPtr interface;
+    RequestSpellCheckHost(&interface);
+
+    base::UTF8ToUTF16("hello", 5, &word_);
+    interface->CallSpellingService(
+        word_,
+        base::Bind(&SpellcheckServiceHostBrowserTest::SpellingServiceDone,
+                   base::Unretained(this)));
+
+    base::RunLoop run_loop;
+    quit_ = run_loop.QuitClosure();
+    run_loop.Run();
+
+    EXPECT_TRUE(spelling_service_done_called_);
+  }
+
+ private:
+  void RequestSpellCheckHost(spellcheck::mojom::SpellCheckHostPtr* interface) {
+    SpellCheckHostImpl::Create(GetRenderer()->GetID(),
+                               service_manager::BindSourceInfo(),
+                               mojo::MakeRequest(interface));
+  }
+
+  void SpellingServiceDone(bool success,
+                           const std::vector<::SpellCheckResult>& results) {
+    spelling_service_done_called_ = true;
+    if (quit_)
+      std::move(quit_).Run();
+  }
+
+  bool spelling_service_done_called_ = false;
+  base::string16 word_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpellcheckServiceHostBrowserTest);
+};
+
 // Removing a spellcheck language from accept languages should remove it from
 // spellcheck languages list as well.
 IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest,
@@ -334,9 +380,49 @@
   EXPECT_TRUE(GetCustomDictionaryChangedState());
 }
 
+// Starting with only a single-language spellcheck setting, the host should
+// initialize the renderer's spellcheck system, and the same if the renderer
+// explicity requests the spellcheck dictionaries.
+IN_PROC_BROWSER_TEST_F(SpellcheckServiceHostBrowserTest, RequestDictionary) {
+  InitSpellcheck(true, "en-US", "");
+  EXPECT_TRUE(GetEnableSpellcheckState());
+
+  RequestDictionary();
+  EXPECT_TRUE(GetEnableSpellcheckState());
+}
+
+// When the renderer notifies that it corrected a word, the render process
+// host should record UMA stats about the correction.
+IN_PROC_BROWSER_TEST_F(SpellcheckServiceHostBrowserTest, NotifyChecked) {
+  const char kMisspellRatio[] = "SpellCheck.MisspellRatio";
+
+  base::HistogramTester tester;
+  tester.ExpectTotalCount(kMisspellRatio, 0);
+  NotifyChecked();
+  tester.ExpectTotalCount(kMisspellRatio, 1);
+}
+
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+// When the renderer requests the spelling service for correcting text, the
+// render process host should call the remote spelling service.
+IN_PROC_BROWSER_TEST_F(SpellcheckServiceHostBrowserTest, CallSpellingService) {
+  CallSpellingService();
+}
+#endif  // !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+
 // Tests that we can delete a corrupted BDICT file used by hunspell. We do not
 // run this test on Mac because Mac does not use hunspell by default.
 IN_PROC_BROWSER_TEST_F(SpellcheckServiceBrowserTest, DeleteCorruptedBDICT) {
+  // Corrupted BDICT data: please do not use this BDICT data for other tests.
+  const uint8_t kCorruptedBDICT[] = {
+      0x42, 0x44, 0x69, 0x63, 0x02, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
+      0x3b, 0x00, 0x00, 0x00, 0x65, 0x72, 0xe0, 0xac, 0x27, 0xc7, 0xda, 0x66,
+      0x6d, 0x1e, 0xa6, 0x35, 0xd1, 0xf6, 0xb7, 0x35, 0x32, 0x00, 0x00, 0x00,
+      0x38, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+      0x0a, 0x0a, 0x41, 0x46, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6,
+      0x49, 0x00, 0x68, 0x02, 0x73, 0x06, 0x74, 0x0b, 0x77, 0x11, 0x79, 0x15,
+  };
+
   // Write the corrupted BDICT data to create a corrupted BDICT file.
   base::FilePath dict_dir;
   ASSERT_TRUE(PathService::Get(chrome::DIR_APP_DICTIONARIES, &dict_dir));
diff --git a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
index 521277b8b..a7e62da5 100644
--- a/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
+++ b/chrome/browser/ui/webui/md_bookmarks/md_bookmarks_ui.cc
@@ -86,6 +86,11 @@
   AddLocalizedString(source, "title", IDS_MD_BOOKMARK_MANAGER_TITLE);
 
   // Resources.
+#if BUILDFLAG(USE_VULCANIZE)
+  source->AddResourcePath("crisper.js", IDR_MD_BOOKMARKS_CRISPER_JS);
+  source->SetDefaultResource(IDR_MD_BOOKMARKS_VULCANIZED_HTML);
+  source->UseGzip(std::unordered_set<std::string>());
+#else
   source->AddResourcePath("actions.html", IDR_MD_BOOKMARKS_ACTIONS_HTML);
   source->AddResourcePath("actions.js", IDR_MD_BOOKMARKS_ACTIONS_JS);
   source->AddResourcePath("api_listener.html",
@@ -131,7 +136,10 @@
   source->AddResourcePath("toolbar.js", IDR_MD_BOOKMARKS_TOOLBAR_JS);
   source->AddResourcePath("util.html", IDR_MD_BOOKMARKS_UTIL_HTML);
   source->AddResourcePath("util.js", IDR_MD_BOOKMARKS_UTIL_JS);
+
   source->SetDefaultResource(IDR_MD_BOOKMARKS_BOOKMARKS_HTML);
+#endif
+
   source->SetJsonPath("strings.js");
 
   return source;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index e0cb84f5..692da7f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3786,11 +3786,14 @@
     sources += [
       "../browser/spellchecker/spellcheck_custom_dictionary_unittest.cc",
       "../browser/spellchecker/spellcheck_message_filter_platform_mac_unittest.cc",
-      "../browser/spellchecker/spellcheck_message_filter_unittest.cc",
       "../browser/spellchecker/spellcheck_service_unittest.cc",
       "../browser/spellchecker/spelling_service_client_unittest.cc",
       "../tools/convert_dict/convert_dict_unittest.cc",
     ]
+
+    if (!use_browser_spellchecker) {
+      sources += [ "../browser/spellchecker/spell_check_host_impl_unittest.cc" ]
+    }
   }
 
   if (enable_one_click_signin) {
diff --git a/components/spellcheck/common/OWNERS b/components/spellcheck/common/OWNERS
index ef88cc7..ed454dad 100644
--- a/components/spellcheck/common/OWNERS
+++ b/components/spellcheck/common/OWNERS
@@ -2,3 +2,5 @@
 per-file *_messages*.h=file://ipc/SECURITY_OWNERS
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
+per-file *.typemap=set noparent
+per-file *.typemap=file://ipc/SECURITY_OWNERS
diff --git a/components/spellcheck/common/spellcheck.mojom b/components/spellcheck/common/spellcheck.mojom
index 9195261..33b818f 100644
--- a/components/spellcheck/common/spellcheck.mojom
+++ b/components/spellcheck/common/spellcheck.mojom
@@ -5,13 +5,15 @@
 module spellcheck.mojom;
 
 import "mojo/common/file.mojom";
+import "mojo/common/string16.mojom";
 
 // Render process interface exposed to the browser for receiving process-
 // wide spellcheck control and updates from the browser process.
 //
 interface SpellChecker {
   // Initialize the render process spellchecker. Called after startup and
-  // also in response to a render process RequestDictionary request.
+  // also in response to a renderer's spellcheck::mojom::SpellCheckHost
+  // RequestDictionary request.
   Initialize(array<SpellCheckBDictLanguage> dictionaries,
              array<string> custom_words,
              bool enable);
@@ -26,3 +28,27 @@
   mojo.common.mojom.File? file;
   string language;
 };
+
+// Browser process interface exposed to the renderer for requesting spell-
+// check host services.
+//
+interface SpellCheckHost {
+  // Asks the browser to initialize the renderer's spellcheck system. The
+  // initialize call arrives on interface spellcheck::mojom::SpellChecker
+  // in async response to this request.
+  RequestDictionary();
+
+  // Tracks spell checking occurrences to collect histograms, where |word|
+  // was checked, and |misspelled| is true if |word| was misspelt.
+  NotifyChecked(mojo.common.mojom.String16 word, bool misspelled);
+
+  // Asks the host to spellcheck the |text| using a remote Spelling server
+  // to do the spellchecking. If the remote Spelling server is available,
+  // returns |success| true, and the spellchecked |results|. Note this API
+  // requires a !BUILDFLAG(USE_BROWSER_SPELLCHECKER) build.
+  CallSpellingService(mojo.common.mojom.String16 text) =>
+      (bool success, array<SpellCheckResult> results);
+};
+
+[Native]
+struct SpellCheckResult;
diff --git a/components/spellcheck/common/spellcheck.typemap b/components/spellcheck/common/spellcheck.typemap
new file mode 100644
index 0000000..468241c
--- /dev/null
+++ b/components/spellcheck/common/spellcheck.typemap
@@ -0,0 +1,15 @@
+# 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.
+
+mojom = "//components/spellcheck/common/spellcheck.mojom"
+
+public_headers = [ "//components/spellcheck/common/spellcheck_result.h" ]
+
+traits_headers = [ "//components/spellcheck/common/spellcheck_messages.h" ]
+
+deps = [
+  "//components/spellcheck:build_features",
+]
+
+type_mappings = [ "spellcheck.mojom.SpellCheckResult=::SpellCheckResult" ]
diff --git a/components/spellcheck/common/spellcheck_messages.h b/components/spellcheck/common/spellcheck_messages.h
index e8e83a8..fbb69e4 100644
--- a/components/spellcheck/common/spellcheck_messages.h
+++ b/components/spellcheck/common/spellcheck_messages.h
@@ -12,7 +12,7 @@
 #include "ipc/ipc_message_macros.h"
 
 #if !BUILDFLAG(ENABLE_SPELLCHECK)
-#error "Spellcheck should be enabled"
+#error "Spellcheck should be enabled."
 #endif
 
 #define IPC_MESSAGE_START SpellCheckMsgStart
@@ -28,18 +28,6 @@
 
 // Messages sent from the browser to the renderer.
 
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-// Sends text-check results from the Spelling service when the service finishes
-// checking text received by a SpellCheckHostMsg_CallSpellingService message.
-// If the service is not available, the 4th parameter should be false and the
-// 5th parameter should contain the requested sentence.
-IPC_MESSAGE_ROUTED4(SpellCheckMsg_RespondSpellingService,
-                    int /* request identifier given by WebKit */,
-                    bool /* succeeded calling service */,
-                    base::string16 /* sentence */,
-                    std::vector<SpellCheckResult>)
-#endif
-
 #if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 // Sends when NSSpellChecker finishes checking text received by a preceding
 // SpellCheckHostMsg_RequestTextCheck message.
@@ -59,26 +47,6 @@
 
 // Messages sent from the renderer to the browser.
 
-// The renderer has tried to spell check a word, but couldn't because no
-// dictionary was available to load. Request that the browser find an
-// appropriate dictionary and return it.
-IPC_MESSAGE_CONTROL0(SpellCheckHostMsg_RequestDictionary)
-
-// Tracks spell checking occurrence to collect histogram.
-IPC_MESSAGE_ROUTED2(SpellCheckHostMsg_NotifyChecked,
-                    base::string16 /* word */,
-                    bool /* true if checked word is misspelled */)
-
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-// Asks the Spelling service to check text. When the service finishes checking
-// the input text, it sends a SpellingCheckMsg_RespondSpellingService with
-// text-check results.
-IPC_MESSAGE_CONTROL3(SpellCheckHostMsg_CallSpellingService,
-                     int /* route_id for response */,
-                     int /* request identifier given by WebKit */,
-                     base::string16 /* sentence */)
-#endif
-
 #if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 // TODO(groby): This needs to originate from SpellcheckProvider.
 IPC_SYNC_MESSAGE_CONTROL2_1(SpellCheckHostMsg_CheckSpelling,
diff --git a/components/spellcheck/renderer/hunspell_engine.cc b/components/spellcheck/renderer/hunspell_engine.cc
index c385348..4ef0267 100644
--- a/components/spellcheck/renderer/hunspell_engine.cc
+++ b/components/spellcheck/renderer/hunspell_engine.cc
@@ -11,10 +11,12 @@
 
 #include "base/files/memory_mapped_file.h"
 #include "base/time/time.h"
+#include "components/spellcheck/common/spellcheck.mojom.h"
 #include "components/spellcheck/common/spellcheck_common.h"
-#include "components/spellcheck/common/spellcheck_messages.h"
 #include "components/spellcheck/spellcheck_build_features.h"
+#include "content/public/common/service_names.mojom.h"
 #include "content/public/renderer/render_thread.h"
+#include "services/service_manager/public/cpp/connector.h"
 #include "third_party/hunspell/src/hunspell/hunspell.hxx"
 
 using content::RenderThread;
@@ -119,8 +121,12 @@
 bool HunspellEngine::InitializeIfNeeded() {
   if (!initialized_ && !dictionary_requested_) {
     // RenderThread will not exist in test.
-    if (RenderThread::Get())
-      RenderThread::Get()->Send(new SpellCheckHostMsg_RequestDictionary);
+    if (RenderThread::Get()) {
+      spellcheck::mojom::SpellCheckHostPtr spell_check_host;
+      RenderThread::Get()->GetConnector()->BindInterface(
+          content::mojom::kBrowserServiceName, &spell_check_host);
+      spell_check_host->RequestDictionary();
+    }
     dictionary_requested_ = true;
     return true;
   }
diff --git a/components/spellcheck/renderer/spellcheck.cc b/components/spellcheck/renderer/spellcheck.cc
index 4ef53a06..c34a181f 100644
--- a/components/spellcheck/renderer/spellcheck.cc
+++ b/components/spellcheck/renderer/spellcheck.cc
@@ -203,7 +203,7 @@
 void SpellCheck::SpellCheckerRequest(
     const service_manager::BindSourceInfo& source_info,
     spellcheck::mojom::SpellCheckerRequest request) {
-  spellchecker_bindings_.AddBinding(this, std::move(request));
+  bindings_.AddBinding(this, std::move(request));
 }
 
 void SpellCheck::Initialize(
diff --git a/components/spellcheck/renderer/spellcheck.h b/components/spellcheck/renderer/spellcheck.h
index c5a839f7..4dd5c714 100644
--- a/components/spellcheck/renderer/spellcheck.h
+++ b/components/spellcheck/renderer/spellcheck.h
@@ -153,7 +153,7 @@
 #endif
 
   // Bindings for SpellChecker clients.
-  mojo::BindingSet<spellcheck::mojom::SpellChecker> spellchecker_bindings_;
+  mojo::BindingSet<spellcheck::mojom::SpellChecker> bindings_;
 
   // A vector of objects used to actually check spelling, one for each enabled
   // language.
diff --git a/components/spellcheck/renderer/spellcheck_provider.cc b/components/spellcheck/renderer/spellcheck_provider.cc
index abd2cd5..1807349e 100644
--- a/components/spellcheck/renderer/spellcheck_provider.cc
+++ b/components/spellcheck/renderer/spellcheck_provider.cc
@@ -4,13 +4,18 @@
 
 #include "components/spellcheck/renderer/spellcheck_provider.h"
 
+#include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
+#include "components/spellcheck/common/spellcheck.mojom.h"
 #include "components/spellcheck/common/spellcheck_messages.h"
 #include "components/spellcheck/common/spellcheck_result.h"
 #include "components/spellcheck/renderer/spellcheck.h"
 #include "components/spellcheck/renderer/spellcheck_language.h"
 #include "components/spellcheck/spellcheck_build_features.h"
+#include "content/public/common/service_names.mojom.h"
 #include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_thread.h"
+#include "services/service_manager/public/cpp/connector.h"
 #include "third_party/WebKit/public/platform/WebVector.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
 #include "third_party/WebKit/public/web/WebElement.h"
@@ -47,6 +52,16 @@
 SpellCheckProvider::~SpellCheckProvider() {
 }
 
+spellcheck::mojom::SpellCheckHost& SpellCheckProvider::GetSpellCheckHost() {
+  if (spell_check_host_)
+    return *spell_check_host_;
+
+  DCHECK(content::RenderThread::Get());
+  content::RenderThread::Get()->GetConnector()->BindInterface(
+      content::mojom::kBrowserServiceName, &spell_check_host_);
+  return *spell_check_host_;
+}
+
 void SpellCheckProvider::RequestTextChecking(
     const base::string16& text,
     WebTextCheckingCompletion* completion) {
@@ -64,34 +79,36 @@
   // this text to the Spelling service only if a user enables this feature.
   last_request_.clear();
   last_results_.Assign(blink::WebVector<blink::WebTextCheckingResult>());
+  last_identifier_ = text_check_completions_.Add(completion);
 
 #if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  // TODO(crbug.com/714480): convert the RequestTextCheck IPC to mojo.
   // Text check (unified request for grammar and spell check) is only
   // available for browser process, so we ask the system spellchecker
-  // over IPC or return an empty result if the checker is not
-  // available.
-  Send(new SpellCheckHostMsg_RequestTextCheck(
-      routing_id(), text_check_completions_.Add(completion), text));
+  // over IPC or return an empty result if the checker is not available.
+  Send(new SpellCheckHostMsg_RequestTextCheck(routing_id(), last_identifier_,
+                                              text));
 #else
-  Send(new SpellCheckHostMsg_CallSpellingService(
-      routing_id(), text_check_completions_.Add(completion),
-      base::string16(text)));
+  if (!spell_check_host_ && !content::RenderThread::Get())
+    return;  // NULL in tests that do not provide a spell_check_host_.
+  GetSpellCheckHost().CallSpellingService(
+      text, base::Bind(&SpellCheckProvider::OnRespondSpellingService,
+                       base::Unretained(this), last_identifier_, text));
 #endif  // !USE_BROWSER_SPELLCHECKER
 }
 
 bool SpellCheckProvider::OnMessageReceived(const IPC::Message& message) {
+#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(SpellCheckProvider, message)
-#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-    IPC_MESSAGE_HANDLER(SpellCheckMsg_RespondSpellingService,
-                        OnRespondSpellingService)
-#endif
-#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+    // TODO(crbug.com/714480): convert the RequestTextCheck IPC to mojo.
     IPC_MESSAGE_HANDLER(SpellCheckMsg_RespondTextCheck, OnRespondTextCheck)
-#endif
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
   return handled;
+#else
+  return false;
+#endif
 }
 
 void SpellCheckProvider::FocusedNodeChanged(const blink::WebNode& unused) {
@@ -103,6 +120,7 @@
   bool enabled = !element.IsNull() && element.IsEditable();
   bool checked = enabled && frame->IsSpellCheckingEnabled();
 
+  // TODO(crbug.com/714480): convert the ToggleSpellCheck IPC to mojo.
   Send(new SpellCheckHostMsg_ToggleSpellCheck(routing_id(), enabled, checked));
 #endif  // USE_BROWSER_SPELLCHECKER
 }
@@ -129,7 +147,9 @@
     UMA_HISTOGRAM_COUNTS("SpellCheck.api.check", word.size());
     // If optional_suggestions is not requested, the API is called
     // for marking.  So we use this for counting markable words.
-    Send(new SpellCheckHostMsg_NotifyChecked(routing_id(), word, 0 < length));
+    if (!spell_check_host_ && !content::RenderThread::Get())
+      return;  // NULL in tests that do not provide a spell_check_host_.
+    GetSpellCheckHost().NotifyChecked(word, 0 < length);
   }
 }
 
@@ -151,8 +171,8 @@
 #if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
 void SpellCheckProvider::OnRespondSpellingService(
     int identifier,
-    bool succeeded,
     const base::string16& line,
+    bool success,
     const std::vector<SpellCheckResult>& results) {
   WebTextCheckingCompletion* completion =
       text_check_completions_.Lookup(identifier);
@@ -160,8 +180,8 @@
     return;
   text_check_completions_.Remove(identifier);
 
-  // If |succeeded| is false, we use local spellcheck as a fallback.
-  if (!succeeded) {
+  // If |success| is false, we use local spellcheck as a fallback.
+  if (!success) {
     spellcheck_->RequestTextChecking(line, completion);
     return;
   }
diff --git a/components/spellcheck/renderer/spellcheck_provider.h b/components/spellcheck/renderer/spellcheck_provider.h
index cead107..d31e110 100644
--- a/components/spellcheck/renderer/spellcheck_provider.h
+++ b/components/spellcheck/renderer/spellcheck_provider.h
@@ -5,13 +5,11 @@
 #ifndef COMPONENTS_SPELLCHECK_RENDERER_SPELLCHECK_PROVIDER_H_
 #define COMPONENTS_SPELLCHECK_RENDERER_SPELLCHECK_PROVIDER_H_
 
-#include <stddef.h>
-#include <stdint.h>
-
 #include <vector>
 
 #include "base/id_map.h"
 #include "base/macros.h"
+#include "components/spellcheck/common/spellcheck.mojom.h"
 #include "components/spellcheck/spellcheck_build_features.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "content/public/renderer/render_frame_observer_tracker.h"
@@ -25,8 +23,8 @@
 struct WebTextCheckingResult;
 }
 
-// This class deals with invoking browser-side spellcheck mechanism
-// which is done asynchronously.
+// This class deals with asynchronously invoking text spelling and grammar
+// checking services provided by the browser process (host).
 class SpellCheckProvider
     : public content::RenderFrameObserver,
       public content::RenderFrameObserverTracker<SpellCheckProvider>,
@@ -38,14 +36,14 @@
                      SpellCheck* spellcheck);
   ~SpellCheckProvider() override;
 
-  // Requests async spell and grammar checker to the platform text
-  // checker, which is available on the browser process. The function does not
-  // have special handling for partial words, as Blink guarantees that no
-  // request is made when typing in the middle of a word.
+  // Requests async spell and grammar checks from the platform text checker
+  // available in the browser process. The function does not have special
+  // handling for partial words, as Blink guarantees that no request is made
+  // when typing in the middle of a word.
   void RequestTextChecking(const base::string16& text,
                            blink::WebTextCheckingCompletion* completion);
 
-  // The number of ongoing IPC requests.
+  // The number of ongoing spell check host requests.
   size_t pending_text_request_size() const {
     return text_check_completions_.size();
   }
@@ -56,23 +54,31 @@
   // Enables document-wide spellchecking.
   void EnableSpellcheck(bool enabled);
 
-  // RenderFrameObserver implementation.
+  // content::RenderFrameObserver:
   bool OnMessageReceived(const IPC::Message& message) override;
   void FocusedNodeChanged(const blink::WebNode& node) override;
 
  private:
   friend class TestingSpellCheckProvider;
 
-  // Tries to satisfy a spell check request from the cache in |last_request_|.
+  // Sets the SpellCheckHost (for unit tests).
+  void SetSpellCheckHostForTesting(spellcheck::mojom::SpellCheckHostPtr host) {
+    spell_check_host_ = std::move(host);
+  }
+
+  // Returns the SpellCheckHost.
+  spellcheck::mojom::SpellCheckHost& GetSpellCheckHost();
+
+  // Tries to satisfy a spellcheck request from the cache in |last_request_|.
   // Returns true (and cancels/finishes the completion) if it can, false
   // if the provider should forward the query on.
   bool SatisfyRequestFromCache(const base::string16& text,
                                blink::WebTextCheckingCompletion* completion);
 
-  // RenderFrameObserver implementation.
+  // content::RenderFrameObserver:
   void OnDestruct() override;
 
-  // blink::WebTextCheckClient implementation.
+  // blink::WebTextCheckClient:
   void CheckSpelling(
       const blink::WebString& text,
       int& offset,
@@ -84,11 +90,10 @@
   void CancelAllPendingRequests() override;
 
 #if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-  void OnRespondSpellingService(
-      int identifier,
-      bool succeeded,
-      const base::string16& text,
-      const std::vector<SpellCheckResult>& results);
+  void OnRespondSpellingService(int identifier,
+                                const base::string16& text,
+                                bool success,
+                                const std::vector<SpellCheckResult>& results);
 #endif
 
   // Returns whether |text| has word characters, i.e. whether a spellchecker
@@ -102,17 +107,21 @@
       const std::vector<SpellCheckResult>& results);
 #endif
 
-  // Holds ongoing spellchecking operations, assigns IDs for the IPC routing.
+  // Holds ongoing spellchecking operations.
   WebTextCheckCompletions text_check_completions_;
 
-  // The last text sent to the browser process to spellcheck it and its
-  // spellchecking results.
+  // The last text sent to the browser process for spellchecking, and its
+  // spellcheck results and WebTextCheckCompletions identifier.
   base::string16 last_request_;
   blink::WebVector<blink::WebTextCheckingResult> last_results_;
+  int last_identifier_;
 
-  // Weak pointer to shared (per RenderView) spellcheck data.
+  // Weak pointer to shared (per renderer) spellcheck data.
   SpellCheck* spellcheck_;
 
+  // Interface to the SpellCheckHost.
+  spellcheck::mojom::SpellCheckHostPtr spell_check_host_;
+
   DISALLOW_COPY_AND_ASSIGN(SpellCheckProvider);
 };
 
diff --git a/components/spellcheck/renderer/spellcheck_provider_hunspell_unittest.cc b/components/spellcheck/renderer/spellcheck_provider_hunspell_unittest.cc
index 34ab0a6..40269b8 100644
--- a/components/spellcheck/renderer/spellcheck_provider_hunspell_unittest.cc
+++ b/components/spellcheck/renderer/spellcheck_provider_hunspell_unittest.cc
@@ -2,13 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <vector>
-
-#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/spellcheck/renderer/spellcheck_provider_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/WebKit/public/platform/WebString.h"
 
 // Tests for Hunspell functionality in SpellcheckingProvider
 
@@ -17,14 +13,6 @@
 
 namespace {
 
-TEST_F(SpellCheckProviderTest, UsingHunspell) {
-  FakeTextCheckingCompletion completion;
-  provider_.RequestTextChecking(ASCIIToUTF16("hello"), &completion);
-  EXPECT_EQ(completion.completion_count_, 1U);
-  EXPECT_EQ(provider_.messages_.size(), 0U);
-  EXPECT_EQ(provider_.pending_text_request_size(), 0U);
-}
-
 // Tests that the SpellCheckProvider object sends a spellcheck request when a
 // user finishes typing a word. Also this test verifies that this object checks
 // only a line being edited by the user.
@@ -35,18 +23,21 @@
   provider_.ResetResult();
   provider_.RequestTextChecking(base::string16(), &completion);
   EXPECT_TRUE(provider_.text_.empty());
+  EXPECT_EQ(provider_.spelling_service_call_count_, 0U);
 
   // Verify that the SpellCheckProvider class spellcheck the first word when we
   // stop typing after finishing the first word.
   provider_.ResetResult();
   provider_.RequestTextChecking(ASCIIToUTF16("First"), &completion);
   EXPECT_EQ(ASCIIToUTF16("First"), provider_.text_);
+  EXPECT_EQ(provider_.spelling_service_call_count_, 1U);
 
   // Verify that the SpellCheckProvider class spellcheck the first line when we
   // type a return key, i.e. when we finish typing a line.
   provider_.ResetResult();
   provider_.RequestTextChecking(ASCIIToUTF16("First Second\n"), &completion);
   EXPECT_EQ(ASCIIToUTF16("First Second\n"), provider_.text_);
+  EXPECT_EQ(provider_.spelling_service_call_count_, 2U);
 
   // Verify that the SpellCheckProvider class spellcheck the lines when we
   // finish typing a word "Third" to the second line.
@@ -54,6 +45,7 @@
   provider_.RequestTextChecking(ASCIIToUTF16("First Second\nThird "),
                                 &completion);
   EXPECT_EQ(ASCIIToUTF16("First Second\nThird "), provider_.text_);
+  EXPECT_EQ(provider_.spelling_service_call_count_, 3U);
 
   // Verify that the SpellCheckProvider class does not send a spellcheck request
   // when a user inserts whitespace characters.
@@ -61,6 +53,7 @@
   provider_.RequestTextChecking(ASCIIToUTF16("First Second\nThird   "),
                                 &completion);
   EXPECT_TRUE(provider_.text_.empty());
+  EXPECT_EQ(provider_.spelling_service_call_count_, 3U);
 
   // Verify that the SpellCheckProvider class spellcheck the lines when we type
   // a period.
@@ -68,6 +61,7 @@
   provider_.RequestTextChecking(ASCIIToUTF16("First Second\nThird   Fourth."),
                                 &completion);
   EXPECT_EQ(ASCIIToUTF16("First Second\nThird   Fourth."), provider_.text_);
+  EXPECT_EQ(provider_.spelling_service_call_count_, 4U);
 }
 
 // Tests that the SpellCheckProvider class does not send requests to the
diff --git a/components/spellcheck/renderer/spellcheck_provider_test.cc b/components/spellcheck/renderer/spellcheck_provider_test.cc
index ffba9d1..595ecd0 100644
--- a/components/spellcheck/renderer/spellcheck_provider_test.cc
+++ b/components/spellcheck/renderer/spellcheck_provider_test.cc
@@ -5,19 +5,16 @@
 #include "components/spellcheck/renderer/spellcheck_provider_test.h"
 
 #include "base/memory/ptr_util.h"
-#include "base/stl_util.h"
-#include "components/spellcheck/common/spellcheck_messages.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "components/spellcheck/common/spellcheck.mojom.h"
+#include "components/spellcheck/common/spellcheck_result.h"
 #include "components/spellcheck/renderer/spellcheck.h"
 #include "components/spellcheck/spellcheck_build_features.h"
-#include "ipc/ipc_message_macros.h"
-
-class MockSpellcheck: public SpellCheck {
-};
+#include "ipc/ipc_message.h"
 
 FakeTextCheckingCompletion::FakeTextCheckingCompletion()
-: completion_count_(0),
-  cancellation_count_(0) {
-}
+    : completion_count_(0), cancellation_count_(0) {}
 
 FakeTextCheckingCompletion::~FakeTextCheckingCompletion() {}
 
@@ -32,62 +29,76 @@
 }
 
 TestingSpellCheckProvider::TestingSpellCheckProvider()
-      : SpellCheckProvider(NULL, new MockSpellcheck),
-        spelling_service_call_count_(0) {
-}
+    : SpellCheckProvider(nullptr, new SpellCheck),
+      spelling_service_call_count_(0),
+      binding_(this) {}
 
-TestingSpellCheckProvider::TestingSpellCheckProvider(
-    SpellCheck* spellcheck)
+TestingSpellCheckProvider::TestingSpellCheckProvider(SpellCheck* spellcheck)
     : SpellCheckProvider(nullptr, spellcheck),
-      spelling_service_call_count_(0) {
-}
+      spelling_service_call_count_(0),
+      binding_(this) {}
 
 TestingSpellCheckProvider::~TestingSpellCheckProvider() {
+  binding_.Close();
   delete spellcheck_;
 }
 
-bool TestingSpellCheckProvider::Send(IPC::Message* message)  {
+void TestingSpellCheckProvider::RequestTextChecking(
+    const base::string16& text,
+    blink::WebTextCheckingCompletion* completion) {
 #if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-  // Call our mock message handlers.
-  bool handled = true;
-  IPC_BEGIN_MESSAGE_MAP(TestingSpellCheckProvider, *message)
-    IPC_MESSAGE_HANDLER(SpellCheckHostMsg_CallSpellingService,
-                        OnCallSpellingService)
-    IPC_MESSAGE_UNHANDLED(handled = false)
-  IPC_END_MESSAGE_MAP()
-
-  if (handled) {
-    delete message;
-    return true;
-  }
+  if (!loop_ && !base::MessageLoop::current())
+    loop_ = base::MakeUnique<base::MessageLoop>();
+  if (!binding_.is_bound())
+    SetSpellCheckHostForTesting(binding_.CreateInterfacePtrAndBind());
+  SpellCheckProvider::RequestTextChecking(text, completion);
+  base::RunLoop().RunUntilIdle();
+#else
+  SpellCheckProvider::RequestTextChecking(text, completion);
 #endif
+}
 
+bool TestingSpellCheckProvider::Send(IPC::Message* message) {
   messages_.push_back(base::WrapUnique<IPC::Message>(message));
   return true;
 }
 
-void TestingSpellCheckProvider::OnCallSpellingService(
-    int route_id,
-    int identifier,
-    const base::string16& text) {
-#if BUILDFLAG(USE_BROWSER_SPELLCHECKER)
-  NOTREACHED();
+void TestingSpellCheckProvider::RequestDictionary() {}
+
+void TestingSpellCheckProvider::NotifyChecked(const base::string16& word,
+                                              bool misspelled) {}
+
+void TestingSpellCheckProvider::CallSpellingService(
+    const base::string16& text,
+    CallSpellingServiceCallback callback) {
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
+  OnCallSpellingService(text);
+  std::move(callback).Run(true, std::vector<SpellCheckResult>());
 #else
+  NOTREACHED();
+#endif
+}
+
+void TestingSpellCheckProvider::OnCallSpellingService(
+    const base::string16& text) {
+#if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
   ++spelling_service_call_count_;
   blink::WebTextCheckingCompletion* completion =
-      text_check_completions_.Lookup(identifier);
+      text_check_completions_.Lookup(last_identifier_);
   if (!completion) {
     ResetResult();
     return;
   }
   text_.assign(text);
-  text_check_completions_.Remove(identifier);
+  text_check_completions_.Remove(last_identifier_);
   std::vector<blink::WebTextCheckingResult> results;
   results.push_back(blink::WebTextCheckingResult(
       blink::kWebTextDecorationTypeSpelling, 0, 5, blink::WebString("hello")));
   completion->DidFinishCheckingText(results);
   last_request_ = text;
   last_results_ = results;
+#else
+  NOTREACHED();
 #endif
 }
 
diff --git a/components/spellcheck/renderer/spellcheck_provider_test.h b/components/spellcheck/renderer/spellcheck_provider_test.h
index 89a354e16..aa2cb77 100644
--- a/components/spellcheck/renderer/spellcheck_provider_test.h
+++ b/components/spellcheck/renderer/spellcheck_provider_test.h
@@ -5,19 +5,23 @@
 #ifndef COMPONENTS_SPELLCHECK_RENDERER_SPELLCHECK_PROVIDER_TEST_H_
 #define COMPONENTS_SPELLCHECK_RENDERER_SPELLCHECK_PROVIDER_TEST_H_
 
-#include <stddef.h>
-
+#include <memory>
 #include <vector>
 
 #include "base/strings/string16.h"
 #include "components/spellcheck/renderer/spellcheck_provider.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/WebKit/public/platform/WebVector.h"
 #include "third_party/WebKit/public/web/WebTextCheckingCompletion.h"
 #include "third_party/WebKit/public/web/WebTextCheckingResult.h"
 
+namespace base {
+class MessageLoop;
+}
+
 namespace IPC {
-  class Message;
+class Message;
 }
 
 // A fake completion object for verification.
@@ -35,17 +39,20 @@
 };
 
 // Faked test target, which stores sent message for verification.
-class TestingSpellCheckProvider : public SpellCheckProvider {
+class TestingSpellCheckProvider : public SpellCheckProvider,
+                                  public spellcheck::mojom::SpellCheckHost {
  public:
   TestingSpellCheckProvider();
   // Takes ownership of |spellcheck|.
   explicit TestingSpellCheckProvider(SpellCheck* spellcheck);
 
   ~TestingSpellCheckProvider() override;
+
+  void RequestTextChecking(const base::string16& text,
+                           blink::WebTextCheckingCompletion* completion);
+
   bool Send(IPC::Message* message) override;
-  void OnCallSpellingService(int route_id,
-                             int identifier,
-                             const base::string16& text);
+  void OnCallSpellingService(const base::string16& text);
   void ResetResult();
 
   void SetLastResults(
@@ -57,6 +64,19 @@
   base::string16 text_;
   std::vector<std::unique_ptr<IPC::Message>> messages_;
   size_t spelling_service_call_count_;
+
+ private:
+  // spellcheck::mojom::SpellCheckerHost:
+  void RequestDictionary() override;
+  void NotifyChecked(const base::string16& word, bool misspelled) override;
+  void CallSpellingService(const base::string16& text,
+                           CallSpellingServiceCallback callback) override;
+
+  // Message loop (if needed) to deliver the SpellCheckHost request flow.
+  std::unique_ptr<base::MessageLoop> loop_;
+
+  // Binding to receive the SpellCheckHost request flow.
+  mojo::Binding<spellcheck::mojom::SpellCheckHost> binding_;
 };
 
 // SpellCheckProvider test fixture.
diff --git a/components/typemaps.gni b/components/typemaps.gni
index 57cb1505..94d94bac 100644
--- a/components/typemaps.gni
+++ b/components/typemaps.gni
@@ -8,5 +8,6 @@
   "//components/nacl/common/nacl.typemap",
   "//components/password_manager/content/common/credential_manager.typemap",
   "//components/signin/public/interfaces/account_id.typemap",
+  "//components/spellcheck/common/spellcheck.typemap",
   "//components/translate/content/common/translate.typemap",
 ]
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
index ea367d34..2d72f2f 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java
@@ -460,9 +460,9 @@
         mRule.waitForKeyboardStates(3, 1, 4,
                 new Integer[] {TextInputType.NUMBER, TextInputType.NUMBER, TextInputType.TEXT});
 
-        mRule.resetUpdateSelectionList();
         mRule.setComposingText("a", 1);
-        mRule.waitAndVerifyUpdateSelection(0, 1, 1, 0, 1);
+        mRule.waitAndVerifyUpdateSelection(0, 0, 0, -1, -1);
+        mRule.waitAndVerifyUpdateSelection(1, 1, 1, 0, 1);
         mRule.resetUpdateSelectionList();
 
         // JavaScript changes focus.
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.h b/device/bluetooth/bluetooth_low_energy_device_mac.h
index f559e60..cabab11 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.h
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.h
@@ -129,11 +129,11 @@
   CBPeripheral* GetPeripheral();
 
   // Returns BluetoothRemoteGattServiceMac based on the CBService.
-  BluetoothRemoteGattServiceMac* GetBluetoothRemoteGattService(
+  BluetoothRemoteGattServiceMac* GetBluetoothRemoteGattServiceMac(
       CBService* service) const;
 
   // Returns BluetoothRemoteGattDescriptorMac based on the CBDescriptor.
-  BluetoothRemoteGattDescriptorMac* GetBluetoothRemoteGattDescriptor(
+  BluetoothRemoteGattDescriptorMac* GetBluetoothRemoteGattDescriptorMac(
       CBDescriptor* cb_descriptor) const;
 
   // Callback used when the CoreBluetooth Peripheral is disconnected.
diff --git a/device/bluetooth/bluetooth_low_energy_device_mac.mm b/device/bluetooth/bluetooth_low_energy_device_mac.mm
index 2f2cfa9b..8e730550 100644
--- a/device/bluetooth/bluetooth_low_energy_device_mac.mm
+++ b/device/bluetooth/bluetooth_low_energy_device_mac.mm
@@ -230,7 +230,7 @@
 
   for (CBService* cb_service in GetPeripheral().services) {
     BluetoothRemoteGattServiceMac* gatt_service =
-        GetBluetoothRemoteGattService(cb_service);
+        GetBluetoothRemoteGattServiceMac(cb_service);
     if (!gatt_service) {
       gatt_service = new BluetoothRemoteGattServiceMac(this, cb_service,
                                                        true /* is_primary */);
@@ -279,7 +279,7 @@
   }
 
   BluetoothRemoteGattServiceMac* gatt_service =
-      GetBluetoothRemoteGattService(cb_service);
+      GetBluetoothRemoteGattServiceMac(cb_service);
   DCHECK(gatt_service);
   gatt_service->DidDiscoverCharacteristics();
   SendNotificationIfDiscoveryComplete();
@@ -292,7 +292,7 @@
           << base::SysNSStringToUTF8([invalidatedServices description]);
   for (CBService* cb_service in invalidatedServices) {
     BluetoothRemoteGattServiceMac* gatt_service =
-        GetBluetoothRemoteGattService(cb_service);
+        GetBluetoothRemoteGattServiceMac(cb_service);
     DCHECK(gatt_service);
     VLOG(1) << gatt_service->GetUUID().canonical_value();
     std::unique_ptr<BluetoothRemoteGattService> scoped_service =
@@ -310,7 +310,7 @@
     CBCharacteristic* characteristic,
     NSError* error) {
   BluetoothRemoteGattServiceMac* gatt_service =
-      GetBluetoothRemoteGattService(characteristic.service);
+      GetBluetoothRemoteGattServiceMac(characteristic.service);
   DCHECK(gatt_service);
   gatt_service->DidUpdateValue(characteristic, error);
 }
@@ -319,7 +319,7 @@
     CBCharacteristic* characteristic,
     NSError* error) {
   BluetoothRemoteGattServiceMac* gatt_service =
-      GetBluetoothRemoteGattService(characteristic.service);
+      GetBluetoothRemoteGattServiceMac(characteristic.service);
   DCHECK(gatt_service);
   gatt_service->DidWriteValue(characteristic, error);
 }
@@ -328,7 +328,7 @@
     CBCharacteristic* characteristic,
     NSError* error) {
   BluetoothRemoteGattServiceMac* gatt_service =
-      GetBluetoothRemoteGattService(characteristic.service);
+      GetBluetoothRemoteGattServiceMac(characteristic.service);
   DCHECK(gatt_service);
   gatt_service->DidUpdateNotificationState(characteristic, error);
 }
@@ -356,7 +356,7 @@
     return;
   }
   BluetoothRemoteGattServiceMac* gatt_service =
-      GetBluetoothRemoteGattService(cb_characteristic.service);
+      GetBluetoothRemoteGattServiceMac(cb_characteristic.service);
   DCHECK(gatt_service);
   gatt_service->DidDiscoverDescriptors(cb_characteristic);
   SendNotificationIfDiscoveryComplete();
@@ -366,7 +366,7 @@
     CBDescriptor* cb_descriptor,
     NSError* error) {
   BluetoothRemoteGattDescriptorMac* gatt_descriptor =
-      GetBluetoothRemoteGattDescriptor(cb_descriptor);
+      GetBluetoothRemoteGattDescriptorMac(cb_descriptor);
   DCHECK(gatt_descriptor);
   gatt_descriptor->DidUpdateValueForDescriptor(error);
 }
@@ -375,7 +375,7 @@
     CBDescriptor* cb_descriptor,
     NSError* error) {
   BluetoothRemoteGattDescriptorMac* gatt_descriptor =
-      GetBluetoothRemoteGattDescriptor(cb_descriptor);
+      GetBluetoothRemoteGattDescriptorMac(cb_descriptor);
   DCHECK(gatt_descriptor);
   gatt_descriptor->DidWriteValueForDescriptor(error);
 }
@@ -451,7 +451,7 @@
 }
 
 device::BluetoothRemoteGattServiceMac*
-BluetoothLowEnergyDeviceMac::GetBluetoothRemoteGattService(
+BluetoothLowEnergyDeviceMac::GetBluetoothRemoteGattServiceMac(
     CBService* cb_service) const {
   for (auto it = gatt_services_.begin(); it != gatt_services_.end(); ++it) {
     device::BluetoothRemoteGattService* gatt_service = it->second.get();
@@ -464,11 +464,11 @@
 }
 
 device::BluetoothRemoteGattDescriptorMac*
-BluetoothLowEnergyDeviceMac::GetBluetoothRemoteGattDescriptor(
+BluetoothLowEnergyDeviceMac::GetBluetoothRemoteGattDescriptorMac(
     CBDescriptor* cb_descriptor) const {
   CBCharacteristic* cb_characteristic = cb_descriptor.characteristic;
   device::BluetoothRemoteGattServiceMac* gatt_service =
-      GetBluetoothRemoteGattService(cb_characteristic.service);
+      GetBluetoothRemoteGattServiceMac(cb_characteristic.service);
   DCHECK(gatt_service);
   device::BluetoothRemoteGattCharacteristicMac* gatt_characteristic =
       gatt_service->GetBluetoothRemoteGattCharacteristicMac(cb_characteristic);
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 2dddf9bf..4f083257 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -135,6 +135,14 @@
   return base::FeatureList::IsEnabled(kBackgroundVideoPauseOptimization);
 }
 
+#if defined(OS_ANDROID)
+
+bool IsNewRemotePlaybackPipelineEnabled() {
+  return base::FeatureList::IsEnabled(kNewRemotePlaybackPipeline);
+}
+
+#endif
+
 bool IsNetworkStateError(blink::WebMediaPlayer::NetworkState state) {
   bool result = state == blink::WebMediaPlayer::kNetworkStateFormatError ||
                 state == blink::WebMediaPlayer::kNetworkStateNetworkError ||
@@ -1164,7 +1172,7 @@
 
 void WebMediaPlayerImpl::OnPipelineSuspended() {
 #if defined(OS_ANDROID)
-  if (IsRemote()) {
+  if (IsRemote() && !IsNewRemotePlaybackPipelineEnabled()) {
     scoped_refptr<VideoFrame> frame = cast_impl_.GetCastingBanner();
     if (frame)
       compositor_->PaintSingleFrame(frame);
@@ -1645,7 +1653,8 @@
 }
 
 void WebMediaPlayerImpl::SuspendForRemote() {
-  if (pipeline_controller_.IsPipelineSuspended()) {
+  if (pipeline_controller_.IsPipelineSuspended() &&
+      !IsNewRemotePlaybackPipelineEnabled()) {
     scoped_refptr<VideoFrame> frame = cast_impl_.GetCastingBanner();
     if (frame)
       compositor_->PaintSingleFrame(frame);
diff --git a/rlz/chromeos/lib/rlz_value_store_chromeos.cc b/rlz/chromeos/lib/rlz_value_store_chromeos.cc
index 347f7e5b..65b5634 100644
--- a/rlz/chromeos/lib/rlz_value_store_chromeos.cc
+++ b/rlz/chromeos/lib/rlz_value_store_chromeos.cc
@@ -89,37 +89,38 @@
 }
 
 RlzValueStoreChromeOS::~RlzValueStoreChromeOS() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   WriteStore();
 }
 
 bool RlzValueStoreChromeOS::HasAccess(AccessType type) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return type == kReadAccess || !read_only_;
 }
 
 bool RlzValueStoreChromeOS::WritePingTime(Product product, int64_t time) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   rlz_store_->SetString(GetKeyName(kPingTimeKey, product),
                         base::Int64ToString(time));
   return true;
 }
 
 bool RlzValueStoreChromeOS::ReadPingTime(Product product, int64_t* time) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::string ping_time;
   return rlz_store_->GetString(GetKeyName(kPingTimeKey, product), &ping_time) &&
       base::StringToInt64(ping_time, time);
 }
 
 bool RlzValueStoreChromeOS::ClearPingTime(Product product) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   rlz_store_->Remove(GetKeyName(kPingTimeKey, product), NULL);
   return true;
 }
 
 bool RlzValueStoreChromeOS::WriteAccessPointRlz(AccessPoint access_point,
                                                 const char* new_rlz) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   rlz_store_->SetString(
       GetKeyName(kAccessPointKey, access_point), new_rlz);
   return true;
@@ -128,7 +129,7 @@
 bool RlzValueStoreChromeOS::ReadAccessPointRlz(AccessPoint access_point,
                                                char* rlz,
                                                size_t rlz_size) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   std::string rlz_value;
   rlz_store_->GetString(GetKeyName(kAccessPointKey, access_point), &rlz_value);
   if (rlz_value.size() < rlz_size) {
@@ -141,14 +142,14 @@
 }
 
 bool RlzValueStoreChromeOS::ClearAccessPointRlz(AccessPoint access_point) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   rlz_store_->Remove(GetKeyName(kAccessPointKey, access_point), NULL);
   return true;
 }
 
 bool RlzValueStoreChromeOS::AddProductEvent(Product product,
                                             const char* event_rlz) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return AddValueToList(GetKeyName(kProductEventKey, product),
                         base::MakeUnique<base::Value>(event_rlz));
 }
@@ -156,7 +157,7 @@
 bool RlzValueStoreChromeOS::ReadProductEvents(
     Product product,
     std::vector<std::string>* events) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::ListValue* events_list = nullptr;
   if (!rlz_store_->GetList(GetKeyName(kProductEventKey, product), &events_list))
     return false;
@@ -171,28 +172,28 @@
 
 bool RlzValueStoreChromeOS::ClearProductEvent(Product product,
                                               const char* event_rlz) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Value event_value(event_rlz);
   return RemoveValueFromList(GetKeyName(kProductEventKey, product),
                              event_value);
 }
 
 bool RlzValueStoreChromeOS::ClearAllProductEvents(Product product) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   rlz_store_->Remove(GetKeyName(kProductEventKey, product), NULL);
   return true;
 }
 
 bool RlzValueStoreChromeOS::AddStatefulEvent(Product product,
                                              const char* event_rlz) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return AddValueToList(GetKeyName(kStatefulEventKey, product),
                         base::MakeUnique<base::Value>(event_rlz));
 }
 
 bool RlzValueStoreChromeOS::IsStatefulEvent(Product product,
                                             const char* event_rlz) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::Value event_value(event_rlz);
   base::ListValue* events_list = NULL;
   return rlz_store_->GetList(GetKeyName(kStatefulEventKey, product),
@@ -201,13 +202,13 @@
 }
 
 bool RlzValueStoreChromeOS::ClearAllStatefulEvents(Product product) {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   rlz_store_->Remove(GetKeyName(kStatefulEventKey, product), NULL);
   return true;
 }
 
 void RlzValueStoreChromeOS::CollectGarbage() {
-  DCHECK(CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   NOTIMPLEMENTED();
 }
 
diff --git a/rlz/chromeos/lib/rlz_value_store_chromeos.h b/rlz/chromeos/lib/rlz_value_store_chromeos.h
index cae01ba..7222a78 100644
--- a/rlz/chromeos/lib/rlz_value_store_chromeos.h
+++ b/rlz/chromeos/lib/rlz_value_store_chromeos.h
@@ -12,7 +12,7 @@
 
 #include "base/files/file_path.h"
 #include "base/macros.h"
-#include "base/threading/non_thread_safe.h"
+#include "base/sequence_checker.h"
 #include "rlz/lib/rlz_value_store.h"
 
 namespace base {
@@ -23,8 +23,7 @@
 namespace rlz_lib {
 
 // An implementation of RlzValueStore for ChromeOS.
-class RlzValueStoreChromeOS : public RlzValueStore,
-                              public base::NonThreadSafe {
+class RlzValueStoreChromeOS : public RlzValueStore {
  public:
   // Creates new instance and synchronously reads data from file.
   explicit RlzValueStoreChromeOS(const base::FilePath& store_path);
@@ -77,6 +76,8 @@
 
   bool read_only_;
 
+  SEQUENCE_CHECKER(sequence_checker_);
+
   DISALLOW_COPY_AND_ASSIGN(RlzValueStoreChromeOS);
 };
 
diff --git a/testing/test.gni b/testing/test.gni
index 7039e17..36e0d91 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -12,6 +12,10 @@
   import("//build/config/sanitizers/sanitizers.gni")
 }
 
+if (is_fuchsia) {
+  import("//build/config/fuchsia/rules.gni")
+}
+
 # Define a test as an executable (or apk on Android) with the "testonly" flag
 # set.
 # Variable:
@@ -220,6 +224,29 @@
       test_suite = _output_name
       test_type = "gtest"
     }
+  } else if (is_fuchsia) {
+    _output_name = invoker.target_name
+    _test_runner_target = "${_output_name}__test_runner_script"
+
+    test_runner_script(_test_runner_target) {
+      forward_variables_from(invoker,
+                             [
+                               "data",
+                               "data_deps",
+                               "deps",
+                               "public_deps",
+                             ])
+      test_name = _output_name
+    }
+
+    executable(target_name) {
+      testonly = true
+      forward_variables_from(invoker, "*")
+      if (!defined(data_deps)) {
+        data_deps = []
+      }
+      data_deps += [ ":$_test_runner_target" ]
+    }
   } else if (is_ios) {
     import("//build/config/ios/ios_sdk.gni")
     import("//build/config/ios/rules.gni")
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index f2491d8..e84b8c78 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -2693,8 +2693,6 @@
 # [css-grid]
 crbug.com/659610 fast/css-grid-layout/grid-baseline.html [ Failure ]
 crbug.com/659610 fast/css-grid-layout/grid-baseline-margins.html [ Failure ]
-crbug.com/725903 external/wpt/css/css-grid-1/abspos/orthogonal-positioned-grid-items-013.html [ Failure ]
-crbug.com/725903 external/wpt/css/css-grid-1/abspos/orthogonal-positioned-grid-items-017.html [ Failure ]
 crbug.com/511177 external/wpt/css/css-grid-1/grid-layout-properties.html [ Failure ]
 crbug.com/707359 [ Mac ] fast/css-grid-layout/grid-align-baseline-vertical.html [ Failure ]
 crbug.com/707359 [ Mac ] fast/css-grid-layout/grid-self-baseline-03.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-001.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-001.html
new file mode 100644
index 0000000..b25b4713
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-001.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="Grid items with 'stretch' value for align-self and/or justify-self are stretched along the column and/or row axis respectively; they will be sized as fit-content otherwise.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="100" data-expected-height="10"  class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="0"   data-expected-width="60"  data-expected-height="150" class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="0"   data-offset-y="150" data-expected-width="60"  data-expected-height="10"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="150" data-expected-width="150" data-expected-height="100" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-002.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-002.html
new file mode 100644
index 0000000..b9a354e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-002.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  margin-right: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  margin-bottom: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  margin-right: 20px;
+  margin-bottom: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="90"  data-expected-height="10"  class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="0"   data-expected-width="60"  data-expected-height="130" class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="0"   data-offset-y="150" data-expected-width="60"  data-expected-height="10"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="150" data-expected-width="130" data-expected-height="90"  class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-003.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-003.html
new file mode 100644
index 0000000..0af4081
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-003.html
@@ -0,0 +1,62 @@
+ <!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis include their defined border-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+}
+.grid > div { border-style: solid; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  border-width: 0px 10px 0px 0px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  border-width: 0px 0px 20px 0px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+  border-width: 0px 0px 0px 0px;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  border-width: 0px 20px 10px 0px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="100" data-expected-height="10"  class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="0"   data-expected-width="60"  data-expected-height="150" class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="0"   data-offset-y="150" data-expected-width="60"  data-expected-height="10"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="150" data-expected-width="150" data-expected-height="100" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-004.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-004.html
new file mode 100644
index 0000000..ad76f52
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-004.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis include their defined padding-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  padding-right: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  padding-bottom: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  padding-right: 20px;
+  padding-bottom: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="100" data-expected-height="10"  class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="0"   data-expected-width="60"  data-expected-height="150" class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="0"   data-offset-y="150" data-expected-width="60"  data-expected-height="10"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="150" data-expected-width="150" data-expected-height="100" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-005.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-005.html
new file mode 100644
index 0000000..68e794bd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-005.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="Orthogonal grid items with 'stretch' value for align-self and/or justify-self are stretched along the column and/or row axis respectively; they will be sized as fit-content otherwise.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+}
+.grid > div { writing-mode: vertical-lr; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="100" data-expected-height="60"  class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="0"   data-expected-width="40"  data-expected-height="150" class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="0"   data-offset-y="150" data-expected-width="10"  data-expected-height="60"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="150" data-expected-width="150" data-expected-height="100" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-006.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-006.html
new file mode 100644
index 0000000..94c65d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-006.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+}
+.grid > div { writing-mode: vertical-lr; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  margin-right: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  margin-bottom: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  margin-right: 20px;
+  margin-bottom: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="90"  data-expected-height="60"  class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="0"   data-expected-width="40"  data-expected-height="130" class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="0"   data-offset-y="150" data-expected-width="10"  data-expected-height="60"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="150" data-expected-width="130" data-expected-height="90"  class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-007.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-007.html
new file mode 100644
index 0000000..8cb459ed9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-007.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis include their defined border-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+}
+.grid > div { writing-mode: vertical-lr; }
+.grid > div { border-style: solid; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  border-width: 0px 10px 0px 0px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  border-width: 0px 0px 20px 0px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+  border-width: 0px 0px 0px 0px;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  border-width: 0px 20px 10px 0px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="100" data-expected-height="60"  class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="0"   data-expected-width="40"  data-expected-height="150" class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="0"   data-offset-y="150" data-expected-width="10"  data-expected-height="60"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="150" data-expected-width="150" data-expected-height="100" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-008.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-008.html
new file mode 100644
index 0000000..52337ee
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-008.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis include their defined padding-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+}
+.grid > div { writing-mode: vertical-lr; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  padding-right: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  padding-bottom: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  padding-right: 20px;
+  padding-bottom: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="100" data-expected-height="60"  class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="0"   data-expected-width="40"  data-expected-height="150" class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="0"   data-offset-y="150" data-expected-width="10"  data-expected-height="60"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="150" data-expected-width="150" data-expected-height="100" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-009.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-009.html
new file mode 100644
index 0000000..22bcc707
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-009.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="Grid items with 'stretch' value for align-self and/or justify-self are stretched along the column and/or row axis respectively; they will be sized as fit-content otherwise.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="125" data-expected-height="20"  class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="125" data-offset-y="0"   data-expected-width="80"  data-expected-height="125" class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="125" data-expected-width="80"  data-expected-height="20"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="125" data-offset-y="125" data-expected-width="125" data-expected-height="125" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-010.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-010.html
new file mode 100644
index 0000000..02e138e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-010.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  margin-right: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  margin-bottom: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  margin-right: 20px;
+  margin-bottom: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="110" data-expected-height="20"  class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="0"   data-expected-width="80"  data-expected-height="110" class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="130" data-expected-width="80"  data-expected-height="20"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="130" data-expected-width="110" data-expected-height="110" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-011.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-011.html
new file mode 100644
index 0000000..060e025
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-011.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+}
+.grid > div { border-style: solid; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  border-width: 0px 10px 0px 0px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  border-width: 0px 0px 20px 0px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+  border-width: 0px 0px 0px 0px;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  border-width: 0px 20px 10px 0px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="120" data-expected-height="20"  class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="0"   data-expected-width="80"  data-expected-height="130" class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="130" data-expected-width="80"  data-expected-height="20"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="130" data-expected-width="130" data-expected-height="120" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-012.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-012.html
new file mode 100644
index 0000000..422d2eb0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-012.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis include their defined padding-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  padding-right: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  padding-bottom: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  padding-right: 20px;
+  padding-bottom: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="120" data-expected-height="20"  class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="0"   data-expected-width="80"  data-expected-height="130" class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="130" data-expected-width="80"  data-expected-height="20"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="130" data-expected-width="130" data-expected-height="120" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-013.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-013.html
new file mode 100644
index 0000000..ba88de0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-013.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="Orthogonal grid items with 'stretch' value for align-self and/or justify-self are stretched along the column and/or row axis respectively; they will be sized as fit-content otherwise.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+}
+.grid > div { writing-mode: vertical-lr; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="125" data-expected-height="80"  class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="125" data-offset-y="0"   data-expected-width="20"  data-expected-height="125" class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="125" data-expected-width="20"  data-expected-height="80"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="125" data-offset-y="125" data-expected-width="125" data-expected-height="125" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-014.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-014.html
new file mode 100644
index 0000000..9b3d64d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-014.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+}
+.grid > div { writing-mode: vertical-lr; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  margin-right: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  margin-bottom: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  margin-right: 20px;
+  margin-bottom: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="110" data-expected-height="80"  class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="0"   data-expected-width="20"  data-expected-height="110" class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="130" data-expected-width="20"  data-expected-height="80"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="130" data-expected-width="110" data-expected-height="110" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-015.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-015.html
new file mode 100644
index 0000000..c7290f7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-015.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis include their defined border-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+}
+.grid > div { writing-mode: vertical-lr; }
+.grid > div { border-style: solid; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  border-width: 0px 10px 0px 0px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  border-width: 0px 0px 20px 0px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+  border-width: 0px 0px 0px 0px;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  border-width: 0px 20px 10px 0px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="120" data-expected-height="80"  class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="0"   data-expected-width="20"  data-expected-height="130" class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="130" data-expected-width="20"  data-expected-height="80"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="130" data-expected-width="130" data-expected-height="120" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-016.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-016.html
new file mode 100644
index 0000000..1365b709
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-016.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis include their defined padding-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+}
+.grid > div { writing-mode: vertical-lr; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  padding-right: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  padding-bottom: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  padding-right: 20px;
+  padding-bottom: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="120" data-expected-height="80"  class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="0"   data-expected-width="20"  data-expected-height="130" class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="130" data-expected-width="20"  data-expected-height="80"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="130" data-expected-width="130" data-expected-height="120" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-001.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-001.html
new file mode 100644
index 0000000..ac300a5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-001.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="Grid items with 'stretch' value for align-self and/or justify-self are stretched along the column and/or row axis respectively; they will be sized as fit-content otherwise.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-lr;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="10"  data-expected-height="100" class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="150" data-expected-height="60"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="150" data-offset-y="0"   data-expected-width="10"  data-expected-height="60"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="150" data-offset-y="100" data-expected-width="100" data-expected-height="150" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-002.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-002.html
new file mode 100644
index 0000000..00e4ca7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-002.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-lr;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  margin-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  margin-right: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  margin-bottom: 20px;
+  margin-right: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="10"  data-expected-height="90"  class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="130" data-expected-height="60"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="150" data-offset-y="0"   data-expected-width="10"  data-expected-height="60"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="150" data-offset-y="100" data-expected-width="90"  data-expected-height="130" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-003.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-003.html
new file mode 100644
index 0000000..dd96194
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-003.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis include their defined border-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-lr;
+}
+.grid > div { border-style: solid; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  border-width: 0px 0px 10px 0px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  border-width: 0px 20px 0px 0px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+  border-width: 0px 0px 0px 0px;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  border-width: 0px 10px 20px 0px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="10"  data-expected-height="100" class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="150" data-expected-height="60"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="150" data-offset-y="0"   data-expected-width="10"  data-expected-height="60"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="150" data-offset-y="100" data-expected-width="100" data-expected-height="150" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-004.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-004.html
new file mode 100644
index 0000000..8b5882c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-004.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis include their defined padding-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-lr;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  padding-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  padding-right: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  padding-bottom: 20px;
+  padding-right: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="10"  data-expected-height="100" class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="150" data-expected-height="60"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="150" data-offset-y="0"   data-expected-width="10"  data-expected-height="60"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="150" data-offset-y="100" data-expected-width="100" data-expected-height="150" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-005.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-005.html
new file mode 100644
index 0000000..4b29aa7c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-005.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="Orthogonal grid items with 'stretch' value for align-self and/or justify-self are stretched along the column and/or row axis respectively; they will be sized as fit-content otherwise.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-lr;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="60"  data-expected-height="100" class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="150" data-expected-height="40"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="150" data-offset-y="0"   data-expected-width="60"  data-expected-height="10"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="150" data-offset-y="100" data-expected-width="100" data-expected-height="150" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-006.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-006.html
new file mode 100644
index 0000000..a72fbd13
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-006.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-lr;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  margin-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  margin-right: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  margin-bottom: 20px;
+  margin-right: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="60"  data-expected-height="90"  class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="130" data-expected-height="40"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="150" data-offset-y="0"   data-expected-width="60"  data-expected-height="10"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="150" data-offset-y="100" data-expected-width="90"  data-expected-height="130" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-007.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-007.html
new file mode 100644
index 0000000..ab190d39
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-007.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis include their defined border-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-lr;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.grid > div { border-style: solid; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  border-width: 0px 0px 10px 0px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  border-width: 0px 20px 0px 0px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+  border-width: 0px 0px 0px 0px;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  border-width: 0px 10px 20px 0px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="60"  data-expected-height="100" class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="150" data-expected-height="40"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="150" data-offset-y="0"   data-expected-width="60"  data-expected-height="10"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="150" data-offset-y="100" data-expected-width="100" data-expected-height="150" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-008.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-008.html
new file mode 100644
index 0000000..5be8653
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-008.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis include their defined padding-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-lr;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  padding-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  padding-right: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  padding-bottom: 20px;
+  padding-right: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="60"  data-expected-height="100" class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="150" data-expected-height="40"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="150" data-offset-y="0"   data-expected-width="60"  data-expected-height="10"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="150" data-offset-y="100" data-expected-width="100" data-expected-height="150" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-009.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-009.html
new file mode 100644
index 0000000..f38ceb6a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-009.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="Grid items with 'stretch' value for align-self and/or justify-self are stretched along the column and/or row axis respectively; they will be sized as fit-content otherwise.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-lr;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="20"  data-expected-height="125" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="125" data-expected-width="125" data-expected-height="80"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="125" data-offset-y="0"   data-expected-width="20"  data-expected-height="80"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="125" data-offset-y="125" data-expected-width="125" data-expected-height="125" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-010.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-010.html
new file mode 100644
index 0000000..6744c05
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-010.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-lr;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  margin-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  margin-right: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  margin-bottom: 20px;
+  margin-right: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="20"  data-expected-height="110" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="120" data-expected-width="110" data-expected-height="80"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="130" data-offset-y="00"  data-expected-width="20"  data-expected-height="80"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="130" data-offset-y="120" data-expected-width="110" data-expected-height="110" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-011.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-011.html
new file mode 100644
index 0000000..fa50dff
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-011.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-lr;
+}
+.grid > div { border-style: solid; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  border-width: 0px 0px 10px 0px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  border-width: 0px 20px 0px 0px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+  border-width: 0px 0px 0px 0px;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  border-width: 0px 10px 20px 0px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="20"  data-expected-height="120" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="120" data-expected-width="130" data-expected-height="80"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="130" data-offset-y="0"   data-expected-width="20"  data-expected-height="80"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="130" data-offset-y="120" data-expected-width="120" data-expected-height="130" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-012.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-012.html
new file mode 100644
index 0000000..94773382
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-012.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis include their defined padding-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-lr;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  padding-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  padding-right: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  padding-bottom: 20px;
+  padding-right: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="20"  data-expected-height="120" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="120" data-expected-width="130" data-expected-height="80"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="130" data-offset-y="0"   data-expected-width="20"  data-expected-height="80"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="130" data-offset-y="120" data-expected-width="120" data-expected-height="130" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-013.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-013.html
new file mode 100644
index 0000000..25baada
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-013.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="Orthogonal grid items with 'stretch' value for align-self and/or justify-self are stretched along the column and/or row axis respectively; they will be sized as fit-content otherwise.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-lr;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="80"  data-expected-height="125" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="125" data-expected-width="125" data-expected-height="20"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="125" data-offset-y="0"   data-expected-width="80"  data-expected-height="20"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="125" data-offset-y="125" data-expected-width="125" data-expected-height="125" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-014.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-014.html
new file mode 100644
index 0000000..19a8ede
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-014.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-lr;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  margin-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  margin-right: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  margin-bottom: 20px;
+  margin-right: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="80"  data-expected-height="110" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="120" data-expected-width="110" data-expected-height="20"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="130" data-offset-y="0"   data-expected-width="80"  data-expected-height="20"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="130" data-offset-y="120" data-expected-width="110" data-expected-height="110" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-015.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-015.html
new file mode 100644
index 0000000..45c8b75
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-015.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis include their defined border-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-lr;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.grid > div { border-style: solid; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  border-width: 0px 0px 10px 0px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  border-width: 0px 20px 0px 0px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+  border-width: 0px 0px 0px 0px;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  border-width: 0px 10px 20px 0px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="80"  data-expected-height="120" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="120" data-expected-width="130" data-expected-height="20"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="130" data-offset-y="0"   data-expected-width="80"  data-expected-height="20"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="130" data-offset-y="120" data-expected-width="120" data-expected-height="130" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-016.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-016.html
new file mode 100644
index 0000000..2a47e02
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-lr-016.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis include their defined padding-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-lr;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  padding-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  padding-right: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  padding-bottom: 20px;
+  padding-right: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="0"   data-offset-y="0"   data-expected-width="80"  data-expected-height="120" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="120" data-expected-width="130" data-expected-height="20"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="130" data-offset-y="0"   data-expected-width="80"  data-expected-height="20"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="130" data-offset-y="120" data-expected-width="120" data-expected-height="130" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-001.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-001.html
new file mode 100644
index 0000000..1afde93
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-001.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="Grid items with 'stretch' value for align-self and/or justify-self are stretched along the column and/or row axis respectively; they will be sized as fit-content otherwise.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-rl;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="240" data-offset-y="0"   data-expected-width="10"  data-expected-height="100" class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="100" data-expected-width="150" data-expected-height="60"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="90"  data-offset-y="0"   data-expected-width="10"  data-expected-height="60"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="100" data-expected-height="150" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-002.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-002.html
new file mode 100644
index 0000000..a7a1c97
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-002.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-rl;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  margin-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  margin-left: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  margin-bottom: 20px;
+  margin-left: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="240" data-offset-y="0"   data-expected-width="10"  data-expected-height="90"  class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="120" data-offset-y="100" data-expected-width="130" data-expected-height="60"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="90"  data-offset-y="0"   data-expected-width="10"  data-expected-height="60"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="10"  data-offset-y="100" data-expected-width="90"  data-expected-height="130" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-003.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-003.html
new file mode 100644
index 0000000..96cbad3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-003.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis include their defined border-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-rl;
+}
+.grid > div { border-style: solid; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  border-width: 0px 0px 10px 0px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  border-width: 0px 0px 0px 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+  border-width: 0px 0px 0px 0px;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  border-width: 0px 0px 20px 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="240" data-offset-y="0"   data-expected-width="10"  data-expected-height="100" class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="100" data-expected-width="150" data-expected-height="60"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="90"  data-offset-y="0"   data-expected-width="10"  data-expected-height="60"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="100" data-expected-height="150" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-004.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-004.html
new file mode 100644
index 0000000..c93b7b6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-004.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis include their defined padding-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-rl;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  padding-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  padding-left: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  padding-bottom: 20px;
+  padding-left: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="240" data-offset-y="0"   data-expected-width="10"  data-expected-height="100" class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="100" data-expected-width="150" data-expected-height="60"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="90"  data-offset-y="0"   data-expected-width="10"  data-expected-height="60"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="100" data-expected-height="150" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-005.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-005.html
new file mode 100644
index 0000000..73f7a6f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-005.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="Orthogonal grid items with 'stretch' value for align-self and/or justify-self are stretched along the column and/or row axis respectively; they will be sized as fit-content otherwise.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-rl;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="190" data-offset-y="0"   data-expected-width="60"  data-expected-height="100" class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="100" data-expected-width="150" data-expected-height="40"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="40"  data-offset-y="0"   data-expected-width="60"  data-expected-height="10"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="100" data-expected-height="150" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-006.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-006.html
new file mode 100644
index 0000000..0a6d003f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-006.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-rl;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  margin-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  margin-right: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  margin-bottom: 20px;
+  margin-right: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="190" data-offset-y="0"   data-expected-width="60"  data-expected-height="90"  class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="100" data-expected-width="130" data-expected-height="40"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="40"  data-offset-y="0"   data-expected-width="60"  data-expected-height="10"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="90"  data-expected-height="130" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-007.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-007.html
new file mode 100644
index 0000000..ac92314
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-007.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis include their defined border-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-rl;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.grid > div { border-style: solid; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  border-width: 0px 0px 10px 0px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  border-width: 0px 20px 0px 0px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+  border-width: 0px 0px 0px 0px;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  border-width: 0px 10px 20px 0px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="190" data-offset-y="0"   data-expected-width="60"  data-expected-height="100" class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="100" data-expected-width="150" data-expected-height="40"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="40"  data-offset-y="0"   data-expected-width="60"  data-expected-height="10"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="100" data-expected-height="150" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-008.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-008.html
new file mode 100644
index 0000000..4756ebad
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-008.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on fixed-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis include their defined padding-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  grid-template-columns: 100px 150px;
+  grid-template-rows: 150px 100px;
+  font: 10px/1 Ahem;
+  background: grey;
+  writing-mode: vertical-rl;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  padding-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  padding-right: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  padding-bottom: 20px;
+  padding-right: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="190" data-offset-y="0"   data-expected-width="60"  data-expected-height="100" class="firstRowFirstColumn">X XX X</div>
+  <div data-offset-x="100" data-offset-y="100" data-expected-width="150" data-expected-height="40"  class="firstRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+  <div data-offset-x="40"  data-offset-y="0"   data-expected-width="60"  data-expected-height="10"  class="secondRowFirstColumn">X XX X</div>
+  <div data-offset-x="0"   data-offset-y="100" data-expected-width="100" data-expected-height="150" class="secondRowSecondColumn">XX X<br>X XXX<br>X<br>XX XXX</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-009.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-009.html
new file mode 100644
index 0000000..4d05c9f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-009.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="Grid items with 'stretch' value for align-self and/or justify-self are stretched along the column and/or row axis respectively; they will be sized as fit-content otherwise.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-rl;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="230" data-offset-y="0"   data-expected-width="20"  data-expected-height="125" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="125" data-offset-y="125" data-expected-width="125" data-expected-height="80"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="105" data-offset-y="0"   data-expected-width="20"  data-expected-height="80"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="125" data-expected-width="125" data-expected-height="125" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-010.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-010.html
new file mode 100644
index 0000000..1cb5b8dc7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-010.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-rl;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  margin-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  margin-left: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  margin-bottom: 20px;
+  margin-left: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="230" data-offset-y="0"   data-expected-width="20"  data-expected-height="110" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="140" data-offset-y="120" data-expected-width="110" data-expected-height="80"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="100" data-offset-y="00"  data-expected-width="20"  data-expected-height="80"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="10"  data-offset-y="120" data-expected-width="110" data-expected-height="110" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-011.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-011.html
new file mode 100644
index 0000000..535d57c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-011.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-rl;
+}
+.grid > div { border-style: solid; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  border-width: 0px 0px 10px 0px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  border-width: 0px 0px 00px 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+  border-width: 0px 0px 0px 0px;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  border-width: 0px 0px 20px 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="230" data-offset-y="0"   data-expected-width="20"  data-expected-height="120" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="120" data-expected-width="130" data-expected-height="80"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="100" data-offset-y="0"   data-expected-width="20"  data-expected-height="80"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="120" data-expected-width="120" data-expected-height="130" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-012.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-012.html
new file mode 100644
index 0000000..8ed59c7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-012.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched grid items along the column and/or row axis include their defined padding-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-rl;
+}
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  padding-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  padding-left: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  padding-bottom: 20px;
+  padding-left: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="230" data-offset-y="0"   data-expected-width="20"  data-expected-height="120" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="120" data-expected-width="130" data-expected-height="80"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="100" data-offset-y="0"   data-expected-width="20"  data-expected-height="80"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="120" data-expected-width="120" data-expected-height="130" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-013.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-013.html
new file mode 100644
index 0000000..4070c2e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-013.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="Orthogonal grid items with 'stretch' value for align-self and/or justify-self are stretched along the column and/or row axis respectively; they will be sized as fit-content otherwise.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-rl;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="170" data-offset-y="0"   data-expected-width="80"  data-expected-height="125" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="125" data-offset-y="125" data-expected-width="125" data-expected-height="20"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="45"  data-offset-y="0"   data-expected-width="80"  data-expected-height="20"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="125" data-expected-width="125" data-expected-height="125" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-014.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-014.html
new file mode 100644
index 0000000..d6a6b30
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-014.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis respect their defined margin-box's boundaries.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-rl;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  margin-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  margin-right: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  margin-bottom: 20px;
+  margin-right: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="170" data-offset-y="0"   data-expected-width="80"  data-expected-height="110" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="120" data-expected-width="110" data-expected-height="20"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="40"  data-offset-y="0"   data-expected-width="80"  data-expected-height="20"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="120" data-expected-width="110" data-expected-height="110" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-015.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-015.html
new file mode 100644
index 0000000..e6ef67df
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-015.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-align-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis include their defined border-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-rl;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.grid > div { border-style: solid; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  border-width: 0px 0px 10px 0px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  border-width: 0px 20px 0px 0px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+  border-width: 0px 0px 0px 0px;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  border-width: 0px 10px 20px 0px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="170" data-offset-y="0"   data-expected-width="80"  data-expected-height="120" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="120" data-expected-width="130" data-expected-height="20"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="40"  data-offset-y="0"   data-expected-width="80"  data-expected-height="20"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="120" data-expected-width="120" data-expected-height="130" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-016.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-016.html
new file mode 100644
index 0000000..5e26d140
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/alignment/grid-self-alignment-stretch-vertical-rl-016.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Self-Alignment and stretch on auto-sized tracks</title>
+<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-align-self">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-justify-self">
+<link rel="help" href="https://drafts.csswg.org/css-align/#valdef-justify-self-stretch">
+<meta name="assert" content="The stretched orthogonal grid items along the column and/or row axis include their defined padding-box.">
+<style>
+.grid {
+  position: relative;
+  display: inline-grid;
+  font: 20px/1 Ahem;
+  background: grey;
+  width: 250px;
+  height: 250px;
+  writing-mode: vertical-rl;
+}
+.grid > div { writing-mode: horizontal-tb; }
+.firstRowFirstColumn {
+  grid-row: 1;
+  grid-column: 1;
+  background: green;
+  justify-self: stretch;
+  align-self: start;
+  padding-bottom: 10px;
+}
+.firstRowSecondColumn {
+  grid-row: 1;
+  grid-column: 2;
+  background: blue;
+  justify-self: start;
+  align-self: stretch;
+  padding-right: 20px;
+}
+.secondRowFirstColumn {
+  grid-row: 2;
+  grid-column: 1;
+  background: yellow;
+  justify-self: start;
+  align-self: start;
+}
+.secondRowSecondColumn {
+  grid-row: 2;
+  grid-column: 2;
+  background: red;
+  justify-self: stretch;
+  align-self: stretch;
+  padding-bottom: 20px;
+  padding-right: 10px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/check-layout-th.js"></script>
+<body onload="checkLayout('.grid')">
+<div class="grid">
+  <div data-offset-x="170" data-offset-y="0"   data-expected-width="80"  data-expected-height="120" class="firstRowFirstColumn">XX X</div>
+  <div data-offset-x="120" data-offset-y="120" data-expected-width="130" data-expected-height="20"  class="firstRowSecondColumn">XX X</div>
+  <div data-offset-x="40"  data-offset-y="0"   data-expected-width="80"  data-expected-height="20"  class="secondRowFirstColumn">XX X</div>
+  <div data-offset-x="0"   data-offset-y="120" data-expected-width="120" data-expected-height="130" class="secondRowSecondColumn">XX X</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/support/check-layout-th.js b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/support/check-layout-th.js
new file mode 100644
index 0000000..3f257d4a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid-1/support/check-layout-th.js
@@ -0,0 +1,195 @@
+(function() {
+// Test is initiated from body.onload, so explicit done() call is required.
+setup({ explicit_done: true });
+
+function checkSubtreeExpectedValues(t, parent, prefix)
+{
+    var checkedLayout = checkExpectedValues(t, parent, prefix);
+    Array.prototype.forEach.call(parent.childNodes, function(node) {
+        checkedLayout |= checkSubtreeExpectedValues(t, node, prefix);
+    });
+    return checkedLayout;
+}
+
+function checkAttribute(output, node, attribute)
+{
+    var result = node.getAttribute && node.getAttribute(attribute);
+    output.checked |= !!result;
+    return result;
+}
+
+function assert_tolerance(actual, expected, message)
+{
+    if (isNaN(expected) || Math.abs(actual - expected) >= 1) {
+        assert_equals(actual, Number(expected), message);
+    }
+}
+
+function checkExpectedValues(t, node, prefix)
+{
+    var output = { checked: false };
+
+    var expectedWidth = checkAttribute(output, node, "data-expected-width");
+    if (expectedWidth) {
+        assert_tolerance(node.offsetWidth, expectedWidth, prefix + "width");
+    }
+
+    var expectedHeight = checkAttribute(output, node, "data-expected-height");
+    if (expectedHeight) {
+        assert_tolerance(node.offsetHeight, expectedHeight, prefix + "height");
+    }
+
+    var expectedOffset = checkAttribute(output, node, "data-offset-x");
+    if (expectedOffset) {
+        assert_tolerance(node.offsetLeft, expectedOffset, prefix + "offsetLeft");
+    }
+
+    var expectedOffset = checkAttribute(output, node, "data-offset-y");
+    if (expectedOffset) {
+        assert_tolerance(node.offsetTop, expectedOffset, prefix + "offsetTop");
+    }
+
+    var expectedWidth = checkAttribute(output, node, "data-expected-client-width");
+    if (expectedWidth) {
+        assert_tolerance(node.clientWidth, expectedWidth, prefix + "clientWidth");
+    }
+
+    var expectedHeight = checkAttribute(output, node, "data-expected-client-height");
+    if (expectedHeight) {
+        assert_tolerance(node.clientHeight, expectedHeight, prefix + "clientHeight");
+    }
+
+    var expectedWidth = checkAttribute(output, node, "data-expected-scroll-width");
+    if (expectedWidth) {
+        assert_tolerance(node.scrollWidth, expectedWidth, prefix + "scrollWidth");
+    }
+
+    var expectedHeight = checkAttribute(output, node, "data-expected-scroll-height");
+    if (expectedHeight) {
+        assert_tolerance(node.scrollHeight, expectedHeight, prefix + "scrollHeight");
+    }
+
+    var expectedWidth = checkAttribute(output, node, "data-expected-bounding-client-rect-width");
+    if (expectedWidth) {
+        assert_tolerance(node.getBoundingClientRect().width, expectedWidth, prefix + "getBoundingClientRect().width");
+    }
+
+    var expectedOffset = checkAttribute(output, node, "data-total-x");
+    if (expectedOffset) {
+        var totalLeft = node.clientLeft + node.offsetLeft;
+        assert_tolerance(totalLeft, expectedOffset, prefix +
+                         "clientLeft+offsetLeft (" + node.clientLeft + " + " + node.offsetLeft + ")");
+    }
+
+    var expectedOffset = checkAttribute(output, node, "data-total-y");
+    if (expectedOffset) {
+        var totalTop = node.clientTop + node.offsetTop;
+        assert_tolerance(totalTop, expectedOffset, prefix +
+                         "clientTop+offsetTop (" + node.clientTop + " + " + node.offsetTop + ")");
+    }
+
+    var expectedDisplay = checkAttribute(output, node, "data-expected-display");
+    if (expectedDisplay) {
+        var actualDisplay = getComputedStyle(node).display;
+        assert_equals(actualDisplay, expectedDisplay, prefix + "display");
+    }
+
+    var expectedPaddingTop = checkAttribute(output, node, "data-expected-padding-top");
+    if (expectedPaddingTop) {
+        var actualPaddingTop = getComputedStyle(node).paddingTop;
+        // Trim the unit "px" from the output.
+        actualPaddingTop = actualPaddingTop.slice(0, -2);
+        assert_equals(actualPaddingTop, expectedPaddingTop, prefix + "padding-top");
+    }
+
+    var expectedPaddingBottom = checkAttribute(output, node, "data-expected-padding-bottom");
+    if (expectedPaddingBottom) {
+        var actualPaddingBottom = getComputedStyle(node).paddingBottom;
+        // Trim the unit "px" from the output.
+        actualPaddingBottom = actualPaddingBottom.slice(0, -2);
+        assert_equals(actualPaddingBottom, expectedPaddingBottom, prefix + "padding-bottom");
+    }
+
+    var expectedPaddingLeft = checkAttribute(output, node, "data-expected-padding-left");
+    if (expectedPaddingLeft) {
+        var actualPaddingLeft = getComputedStyle(node).paddingLeft;
+        // Trim the unit "px" from the output.
+        actualPaddingLeft = actualPaddingLeft.slice(0, -2);
+        assert_equals(actualPaddingLeft, expectedPaddingLeft, prefix + "padding-left");
+    }
+
+    var expectedPaddingRight = checkAttribute(output, node, "data-expected-padding-right");
+    if (expectedPaddingRight) {
+        var actualPaddingRight = getComputedStyle(node).paddingRight;
+        // Trim the unit "px" from the output.
+        actualPaddingRight = actualPaddingRight.slice(0, -2);
+        assert_equals(actualPaddingRight, expectedPaddingRight, prefix + "padding-right");
+    }
+
+    var expectedMarginTop = checkAttribute(output, node, "data-expected-margin-top");
+    if (expectedMarginTop) {
+        var actualMarginTop = getComputedStyle(node).marginTop;
+        // Trim the unit "px" from the output.
+        actualMarginTop = actualMarginTop.slice(0, -2);
+        assert_equals(actualMarginTop, expectedMarginTop, prefix + "margin-top");
+    }
+
+    var expectedMarginBottom = checkAttribute(output, node, "data-expected-margin-bottom");
+    if (expectedMarginBottom) {
+        var actualMarginBottom = getComputedStyle(node).marginBottom;
+        // Trim the unit "px" from the output.
+        actualMarginBottom = actualMarginBottom.slice(0, -2);
+        assert_equals(actualMarginBottom, expectedMarginBottom, prefix + "margin-bottom");
+    }
+
+    var expectedMarginLeft = checkAttribute(output, node, "data-expected-margin-left");
+    if (expectedMarginLeft) {
+        var actualMarginLeft = getComputedStyle(node).marginLeft;
+        // Trim the unit "px" from the output.
+        actualMarginLeft = actualMarginLeft.slice(0, -2);
+        assert_equals(actualMarginLeft, expectedMarginLeft, prefix + "margin-left");
+    }
+
+    var expectedMarginRight = checkAttribute(output, node, "data-expected-margin-right");
+    if (expectedMarginRight) {
+        var actualMarginRight = getComputedStyle(node).marginRight;
+        // Trim the unit "px" from the output.
+        actualMarginRight = actualMarginRight.slice(0, -2);
+        assert_equals(actualMarginRight, expectedMarginRight, prefix + "margin-right");
+    }
+
+    return output.checked;
+}
+
+window.checkLayout = function(selectorList, outputContainer)
+{
+    if (!selectorList) {
+        console.error("You must provide a CSS selector of nodes to check.");
+        return;
+    }
+    var nodes = document.querySelectorAll(selectorList);
+    var testNumber = 0;
+    nodes = Array.prototype.slice.call(nodes);
+    nodes.reverse();
+    var checkedLayout = false;
+    Array.prototype.forEach.call(nodes, function(node) {
+        test(function(t) {
+            var container = node.parentNode.className == 'container' ? node.parentNode : node;
+            var prefix = "\n" + container.outerHTML + "\n";
+            var passed = false;
+            try {
+                checkedLayout |= checkExpectedValues(t, node.parentNode, prefix);
+                checkedLayout |= checkSubtreeExpectedValues(t, node, prefix);
+                passed = true;
+            } finally {
+                checkedLayout |= !passed;
+            }
+        }, selectorList + ' ' + String(++testNumber));
+    });
+    if (!checkedLayout) {
+        console.error("No valid data-* attributes found in selector list : " + selectorList);
+    }
+    done();
+};
+
+})();
diff --git a/third_party/WebKit/LayoutTests/external/wpt/scroll-into-view/check-scroll-position.html b/third_party/WebKit/LayoutTests/external/wpt/scroll-into-view/check-scroll-position.html
new file mode 100644
index 0000000..200491a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/scroll-into-view/check-scroll-position.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<title> Check End Position of ScrollIntoView</title>
+<div id='container' style='height: 2500px; width: 2500px;'>
+  <div id='content' style='height: 500px; width: 500px;margin-left: 1000px; margin-right: 1000px; margin-top: 1000px;margin-bottom: 1000px'>
+  </div>
+</div>
+<script>
+
+var frames = 0;
+var content_height = 500;
+var content_width = 500;
+var window_height = document.documentElement.clientHeight;
+var window_width = document.documentElement.clientWidth;
+var content = document.getElementById('content');
+
+function animate (funct, x, y, next) {
+  if (frames < 500) {
+    ++frames;
+    requestAnimationFrame(animate.bind(null, funct, x, y, next));
+  } else {
+    funct.step(function() {
+      assert_approx_equals(window.scrollX, x, 1);
+      assert_approx_equals(window.scrollY, y, 1);
+      funct.done();
+      if (next)
+        next();
+    });
+  }
+}
+
+var checkNearest = async_test("Smooth ScrollIntoView should scroll the element to the 'nearest' position");
+checkNearest.step(function() {
+  content.scrollIntoView(
+    {behavior: 'smooth', block: 'nearest', inlinePosition: 'nearest'});
+  frames = 0;
+  var x = content.offsetLeft + content_width - window_width;
+  var y = content.offsetTop + content_height - window_height;
+  animate(checkNearest, x, y, test2);
+});
+
+var checkStart = async_test("Smooth ScrollIntoView should scroll the element to the 'start' position");
+function test2() {
+  checkStart.step(function() {
+    content.scrollIntoView(
+      {behavior: 'smooth', block: 'start', inlinePosition: 'start'});
+    frames = 0;
+    animate(checkStart, content.offsetLeft, content.offsetTop, test3);
+  });
+}
+
+var checkCenter = async_test("Smooth ScrollIntoView should scroll the element to the 'center' position");
+function test3() {
+  checkCenter.step(function() {
+    content.scrollIntoView(
+      {behavior: 'smooth', block: 'center', inlinePosition: 'center'});
+    frames = 0;
+    var x = content.offsetLeft + (content_width - window_width) / 2;
+    var y = content.offsetTop + (content_height - window_height) / 2;
+    animate(checkCenter, x, y, test4);
+  });
+}
+
+var checkEnd = async_test("Smooth ScrollIntoView should scroll the element to the 'end' position");
+function test4() {
+  checkEnd.step(function() {
+    content.scrollIntoView(
+      {behavior: 'smooth', block: 'end', inlinePosition: 'end'});
+    frames = 0;
+    var x = content.offsetLeft + content_width - window_width;
+    var y = content.offsetTop + content_height - window_height;
+    animate(checkEnd, x, y, null);
+  });
+}
+
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-stretching-must-not-depend-on-previous-layouts-expected.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-stretching-must-not-depend-on-previous-layouts-expected.html
new file mode 100644
index 0000000..fbbab3b9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-stretching-must-not-depend-on-previous-layouts-expected.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<style>
+body { overflow: hidden; } /* Ensure there wont be scrollbar to avoid the extra layout. */
+.grid {
+  display: inline-grid;
+  border: solid thick;
+  grid: 100px / 100px;
+  align-items: start;
+}
+.item {
+  background: magenta;
+  writing-mode: vertical-lr;
+  margin-right: 25px;
+}
+</style>
+<p>Check whether stretching logic is not affected by multiple layouts becuase of the grid item's override height.<br>The test passes if there is a margin-right of 25px.</p>
+<div class="grid">
+  <div class="item">item</div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-stretching-must-not-depend-on-previous-layouts.html b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-stretching-must-not-depend-on-previous-layouts.html
new file mode 100644
index 0000000..16ec9a85
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css-grid-layout/grid-item-stretching-must-not-depend-on-previous-layouts.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="match" href="grid-item-stretching-must-not-depend-on-previous-layouts-expected.html">
+<style>
+.grid {
+  display: inline-grid;
+  border: solid thick;
+  grid: 100px / 100px;
+  align-items: start;
+}
+.item {
+  background: magenta;
+  writing-mode: vertical-lr;
+  margin-right: 25px;
+}
+</style>
+<p>Check whether stretching logic is not affected by multiple layouts becuase of the grid item's override height.<br>The test passes if there is a margin-right of 25px.</p>
+<div class="grid">
+  <div class="item">item</div>
+</div>
diff --git a/third_party/WebKit/Source/bindings/core/v8/BUILD.gn b/third_party/WebKit/Source/bindings/core/v8/BUILD.gn
index b723540..894fa7d 100644
--- a/third_party/WebKit/Source/bindings/core/v8/BUILD.gn
+++ b/third_party/WebKit/Source/bindings/core/v8/BUILD.gn
@@ -109,6 +109,8 @@
   "$blink_core_output_dir/fileapi/BlobPropertyBag.h",
   "$blink_core_output_dir/fileapi/FilePropertyBag.cpp",
   "$blink_core_output_dir/fileapi/FilePropertyBag.h",
+  "$blink_core_output_dir/frame/ScrollIntoViewOptions.cpp",
+  "$blink_core_output_dir/frame/ScrollIntoViewOptions.h",
   "$blink_core_output_dir/frame/ScrollOptions.cpp",
   "$blink_core_output_dir/frame/ScrollOptions.h",
   "$blink_core_output_dir/frame/ScrollToOptions.cpp",
@@ -220,6 +222,8 @@
   "$bindings_core_v8_output_dir/NodeOrString.h",
   "$bindings_core_v8_output_dir/RadioNodeListOrElement.cpp",
   "$bindings_core_v8_output_dir/RadioNodeListOrElement.h",
+  "$bindings_core_v8_output_dir/ScrollIntoViewOptionsOrBoolean.cpp",
+  "$bindings_core_v8_output_dir/ScrollIntoViewOptionsOrBoolean.h",
   "$bindings_core_v8_output_dir/StringOrArrayBuffer.cpp",
   "$bindings_core_v8_output_dir/StringOrArrayBuffer.h",
   "$bindings_core_v8_output_dir/StringOrArrayBufferOrArrayBufferView.cpp",
diff --git a/third_party/WebKit/Source/core/core_idl_files.gni b/third_party/WebKit/Source/core/core_idl_files.gni
index 9dad1a5..e997194 100644
--- a/third_party/WebKit/Source/core/core_idl_files.gni
+++ b/third_party/WebKit/Source/core/core_idl_files.gni
@@ -559,6 +559,7 @@
                     "events/WheelEventInit.idl",
                     "fileapi/BlobPropertyBag.idl",
                     "fileapi/FilePropertyBag.idl",
+                    "frame/ScrollIntoViewOptions.idl",
                     "frame/ScrollOptions.idl",
                     "frame/ScrollToOptions.idl",
                     "html/AssignedNodesOptions.idl",
diff --git a/third_party/WebKit/Source/core/dom/Element.cpp b/third_party/WebKit/Source/core/dom/Element.cpp
index 3f81b6d..18ec4fe 100644
--- a/third_party/WebKit/Source/core/dom/Element.cpp
+++ b/third_party/WebKit/Source/core/dom/Element.cpp
@@ -30,6 +30,7 @@
 #include "bindings/core/v8/Dictionary.h"
 #include "bindings/core/v8/ExceptionMessages.h"
 #include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/ScrollIntoViewOptionsOrBoolean.h"
 #include "bindings/core/v8/V8DOMActivityLogger.h"
 #include "core/CSSValueKeywords.h"
 #include "core/SVGNames.h"
@@ -95,6 +96,7 @@
 #include "core/frame/LocalDOMWindow.h"
 #include "core/frame/LocalFrame.h"
 #include "core/frame/LocalFrameView.h"
+#include "core/frame/ScrollIntoViewOptions.h"
 #include "core/frame/ScrollToOptions.h"
 #include "core/frame/Settings.h"
 #include "core/frame/UseCounter.h"
@@ -141,6 +143,7 @@
 #include "platform/graphics/CompositorMutableProperties.h"
 #include "platform/graphics/CompositorMutation.h"
 #include "platform/scroll/ScrollableArea.h"
+#include "platform/scroll/SmoothScrollSequencer.h"
 #include "platform/wtf/BitVector.h"
 #include "platform/wtf/HashFunctions.h"
 #include "platform/wtf/text/CString.h"
@@ -438,27 +441,88 @@
   EnsureElementRareData().SetNonce(nonce);
 }
 
+void Element::scrollIntoView(ScrollIntoViewOptionsOrBoolean arg) {
+  ScrollIntoViewOptions options;
+  if (arg.isBoolean()) {
+    if (arg.getAsBoolean())
+      options.setBlock("start");
+    else
+      options.setBlock("end");
+    options.setInlinePosition("nearest");
+  } else if (arg.isScrollIntoViewOptions()) {
+    options = arg.getAsScrollIntoViewOptions();
+    if (!RuntimeEnabledFeatures::cssomSmoothScrollEnabled() &&
+        options.behavior() == "smooth") {
+      options.setBehavior("instant");
+    }
+  }
+  scrollIntoViewWithOptions(options);
+}
+
 void Element::scrollIntoView(bool align_to_top) {
+  ScrollIntoViewOptionsOrBoolean arg;
+  arg.setBoolean(align_to_top);
+  scrollIntoView(arg);
+}
+
+static ScrollAlignment ToPhysicalAlignment(const ScrollIntoViewOptions& options,
+                                           ScrollOrientation axis,
+                                           bool is_horizontal_writing_mode) {
+  String alignment =
+      ((axis == kHorizontalScroll && is_horizontal_writing_mode) ||
+       (axis == kVerticalScroll && !is_horizontal_writing_mode))
+          ? options.inlinePosition()
+          : options.block();
+
+  if (alignment == "center")
+    return ScrollAlignment::kAlignCenterAlways;
+  if (alignment == "nearest")
+    return ScrollAlignment::kAlignToEdgeIfNeeded;
+  if (alignment == "start") {
+    return (axis == kHorizontalScroll) ? ScrollAlignment::kAlignLeftAlways
+                                       : ScrollAlignment::kAlignTopAlways;
+  }
+  if (alignment == "end") {
+    return (axis == kHorizontalScroll) ? ScrollAlignment::kAlignRightAlways
+                                       : ScrollAlignment::kAlignBottomAlways;
+  }
+
+  // Default values
+  if (is_horizontal_writing_mode) {
+    return (axis == kHorizontalScroll) ? ScrollAlignment::kAlignToEdgeIfNeeded
+                                       : ScrollAlignment::kAlignTopAlways;
+  }
+  return (axis == kHorizontalScroll) ? ScrollAlignment::kAlignLeftAlways
+                                     : ScrollAlignment::kAlignToEdgeIfNeeded;
+}
+
+void Element::scrollIntoViewWithOptions(const ScrollIntoViewOptions& options) {
   GetDocument().EnsurePaintLocationDataValidForNode(this);
 
-  if (!GetLayoutObject())
+  if (!GetLayoutObject() || !GetDocument().GetPage())
     return;
 
   bool make_visible_in_visual_viewport =
       !GetDocument().GetPage()->GetSettings().GetInertVisualViewport();
 
+  ScrollBehavior behavior = (options.behavior() == "smooth")
+                                ? kScrollBehaviorSmooth
+                                : kScrollBehaviorAuto;
+
+  bool is_horizontal_writing_mode =
+      GetComputedStyle()->IsHorizontalWritingMode();
+  ScrollAlignment align_x = ToPhysicalAlignment(options, kHorizontalScroll,
+                                                is_horizontal_writing_mode);
+  ScrollAlignment align_y =
+      ToPhysicalAlignment(options, kVerticalScroll, is_horizontal_writing_mode);
+
+  GetDocument().GetPage()->GetSmoothScrollSequencer()->AbortAnimations();
   LayoutRect bounds = BoundingBox();
-  // Align to the top / bottom and to the closest edge.
-  if (align_to_top)
-    GetLayoutObject()->ScrollRectToVisible(
-        bounds, ScrollAlignment::kAlignToEdgeIfNeeded,
-        ScrollAlignment::kAlignTopAlways, kProgrammaticScroll,
-        make_visible_in_visual_viewport);
-  else
-    GetLayoutObject()->ScrollRectToVisible(
-        bounds, ScrollAlignment::kAlignToEdgeIfNeeded,
-        ScrollAlignment::kAlignBottomAlways, kProgrammaticScroll,
-        make_visible_in_visual_viewport);
+  GetLayoutObject()->ScrollRectToVisible(
+      bounds, align_x, align_y, kProgrammaticScroll,
+      make_visible_in_visual_viewport, behavior);
+
+  GetDocument().GetPage()->GetSmoothScrollSequencer()->RunQueuedAnimations();
 
   GetDocument().SetSequentialFocusNavigationStartingPoint(this);
 }
diff --git a/third_party/WebKit/Source/core/dom/Element.h b/third_party/WebKit/Source/core/dom/Element.h
index 692a381..5323a536 100644
--- a/third_party/WebKit/Source/core/dom/Element.h
+++ b/third_party/WebKit/Source/core/dom/Element.h
@@ -65,6 +65,8 @@
 class PseudoStyleRequest;
 class ResizeObservation;
 class ResizeObserver;
+class ScrollIntoViewOptions;
+class ScrollIntoViewOptionsOrBoolean;
 class ScrollState;
 class ScrollStateCallback;
 class ScrollToOptions;
@@ -227,7 +229,9 @@
   // attributes.
   AttributeCollection AttributesWithoutUpdate() const;
 
+  void scrollIntoView(ScrollIntoViewOptionsOrBoolean);
   void scrollIntoView(bool align_to_top = true);
+  void scrollIntoViewWithOptions(const ScrollIntoViewOptions&);
   void scrollIntoViewIfNeeded(bool center_if_needed = true);
 
   int OffsetLeft();
diff --git a/third_party/WebKit/Source/core/dom/Element.idl b/third_party/WebKit/Source/core/dom/Element.idl
index 47867a1..b9ea6a7 100644
--- a/third_party/WebKit/Source/core/dom/Element.idl
+++ b/third_party/WebKit/Source/core/dom/Element.idl
@@ -93,8 +93,8 @@
     // return DOMRectList and DOMRect respectively.
     ClientRectList getClientRects();
     ClientRect getBoundingClientRect();
-    // FIXME: scrollIntoView() should have a ScrollIntoViewOptions dictionary argument.
-    void scrollIntoView(optional boolean alignWithTop);
+
+    void scrollIntoView(optional (ScrollIntoViewOptions or boolean)? arg);
     [RuntimeEnabled=CSSOMSmoothScroll, ImplementedAs=scrollTo] void scroll(optional ScrollToOptions options);
     [RuntimeEnabled=CSSOMSmoothScroll, ImplementedAs=scrollTo] void scroll(unrestricted double x, unrestricted double y);
     [RuntimeEnabled=CSSOMSmoothScroll] void scrollTo(optional ScrollToOptions options);
diff --git a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
index 2e8f1551..c0f23e6 100644
--- a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
@@ -1180,6 +1180,21 @@
   return GetFrame()->DevicePixelRatio();
 }
 
+void LocalDOMWindow::scrollViewportTo(ScrollableArea* viewport,
+                                      const ScrollOffset& offset,
+                                      ScrollBehavior scroll_behavior) const {
+  if (SmoothScrollSequencer* sequencer =
+          GetFrame()->GetPage()->GetSmoothScrollSequencer()) {
+    sequencer->AbortAnimations();
+    if (scroll_behavior == kScrollBehaviorSmooth) {
+      sequencer->QueueAnimation(viewport, offset);
+      sequencer->RunQueuedAnimations();
+    } else {
+      viewport->SetScrollOffset(offset, kProgrammaticScroll, scroll_behavior);
+    }
+  }
+}
+
 void LocalDOMWindow::scrollBy(double x,
                               double y,
                               ScrollBehavior scroll_behavior) const {
@@ -1207,8 +1222,7 @@
   ScrollOffset scaled_delta(x * GetFrame()->PageZoomFactor(),
                             y * GetFrame()->PageZoomFactor());
 
-  viewport->SetScrollOffset(current_offset + scaled_delta, kProgrammaticScroll,
-                            scroll_behavior);
+  scrollViewportTo(viewport, current_offset + scaled_delta, scroll_behavior);
 }
 
 void LocalDOMWindow::scrollBy(const ScrollToOptions& scroll_to_options) const {
@@ -1249,8 +1263,7 @@
   ScrollableArea* viewport = page->GetSettings().GetInertVisualViewport()
                                  ? view->LayoutViewportScrollableArea()
                                  : view->GetScrollableArea();
-  viewport->SetScrollOffset(layout_offset, kProgrammaticScroll,
-                            kScrollBehaviorAuto);
+  scrollViewportTo(viewport, layout_offset, kScrollBehaviorAuto);
 }
 
 void LocalDOMWindow::scrollTo(const ScrollToOptions& scroll_to_options) const {
@@ -1297,8 +1310,7 @@
   ScrollableArea::ScrollBehaviorFromString(scroll_to_options.behavior(),
                                            scroll_behavior);
 
-  viewport->SetScrollOffset(ScrollOffset(scaled_x, scaled_y),
-                            kProgrammaticScroll, scroll_behavior);
+  scrollViewportTo(viewport, ScrollOffset(scaled_x, scaled_y), scroll_behavior);
 }
 
 void LocalDOMWindow::moveBy(int x, int y) const {
diff --git a/third_party/WebKit/Source/core/frame/LocalDOMWindow.h b/third_party/WebKit/Source/core/frame/LocalDOMWindow.h
index 2ad0b6a..987f1cad 100644
--- a/third_party/WebKit/Source/core/frame/LocalDOMWindow.h
+++ b/third_party/WebKit/Source/core/frame/LocalDOMWindow.h
@@ -195,6 +195,9 @@
 
   // FIXME: ScrollBehaviorSmooth is currently unsupported in VisualViewport.
   // crbug.com/434497
+  void scrollViewportTo(ScrollableArea*,
+                        const ScrollOffset&,
+                        ScrollBehavior) const;
   void scrollBy(double x, double y, ScrollBehavior = kScrollBehaviorAuto) const;
   void scrollBy(const ScrollToOptions&) const;
   void scrollTo(double x, double y) const;
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
index eb769944..0e838fd5 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.cpp
@@ -2127,6 +2127,13 @@
   return &page->GetChromeClient();
 }
 
+SmoothScrollSequencer* LocalFrameView::GetSmoothScrollSequencer() const {
+  Page* page = GetFrame().GetPage();
+  if (!page)
+    return nullptr;
+  return page->GetSmoothScrollSequencer();
+}
+
 void LocalFrameView::ContentsResized() {
   if (frame_->IsMainFrame() && frame_->GetDocument()) {
     if (TextAutosizer* text_autosizer =
@@ -4610,14 +4617,20 @@
 LayoutRect LocalFrameView::ScrollIntoView(const LayoutRect& rect_in_content,
                                           const ScrollAlignment& align_x,
                                           const ScrollAlignment& align_y,
+                                          bool is_smooth,
                                           ScrollType scroll_type) {
   LayoutRect view_rect(VisibleContentRect());
   LayoutRect expose_rect = ScrollAlignment::GetRectToExpose(
       view_rect, rect_in_content, align_x, align_y);
   if (expose_rect != view_rect) {
-    SetScrollOffset(
-        ScrollOffset(expose_rect.X().ToFloat(), expose_rect.Y().ToFloat()),
-        scroll_type);
+    ScrollOffset target_offset(expose_rect.X().ToFloat(),
+                               expose_rect.Y().ToFloat());
+    if (is_smooth) {
+      DCHECK(scroll_type == kProgrammaticScroll);
+      GetSmoothScrollSequencer()->QueueAnimation(this, target_offset);
+    } else {
+      SetScrollOffset(target_offset, scroll_type);
+    }
   }
 
   // Scrolling the LocalFrameView cannot change the input rect's location
diff --git a/third_party/WebKit/Source/core/frame/LocalFrameView.h b/third_party/WebKit/Source/core/frame/LocalFrameView.h
index 7c6e9386..d4dfe865 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrameView.h
+++ b/third_party/WebKit/Source/core/frame/LocalFrameView.h
@@ -51,6 +51,7 @@
 #include "platform/graphics/GraphicsLayerClient.h"
 #include "platform/scroll/ScrollTypes.h"
 #include "platform/scroll/Scrollbar.h"
+#include "platform/scroll/SmoothScrollSequencer.h"
 #include "platform/wtf/Allocator.h"
 #include "platform/wtf/AutoReset.h"
 #include "platform/wtf/Forward.h"
@@ -470,6 +471,7 @@
   LayoutRect ScrollIntoView(const LayoutRect& rect_in_content,
                             const ScrollAlignment& align_x,
                             const ScrollAlignment& align_y,
+                            bool is_smooth,
                             ScrollType = kProgrammaticScroll) override;
 
   // The window that hosts the LocalFrameView. The LocalFrameView will
@@ -477,6 +479,8 @@
   // coordinate space.
   PlatformChromeClient* GetChromeClient() const;
 
+  SmoothScrollSequencer* GetSmoothScrollSequencer() const override;
+
   // Functions for child manipulation and inspection.
   bool IsSelfVisible() const {
     return self_visible_;
diff --git a/third_party/WebKit/Source/core/frame/RootFrameViewport.cpp b/third_party/WebKit/Source/core/frame/RootFrameViewport.cpp
index cc76a0e..d89b8b0 100644
--- a/third_party/WebKit/Source/core/frame/RootFrameViewport.cpp
+++ b/third_party/WebKit/Source/core/frame/RootFrameViewport.cpp
@@ -209,6 +209,7 @@
 LayoutRect RootFrameViewport::ScrollIntoView(const LayoutRect& rect_in_content,
                                              const ScrollAlignment& align_x,
                                              const ScrollAlignment& align_y,
+                                             bool is_smooth,
                                              ScrollType scroll_type) {
   // We want to move the rect into the viewport that excludes the scrollbars so
   // we intersect the visual viewport with the scrollbar-excluded frameView
@@ -229,8 +230,13 @@
   LayoutRect target_viewport = ScrollAlignment::GetRectToExpose(
       view_rect_in_content, rect_in_content, align_x, align_y);
   if (target_viewport != view_rect_in_content) {
-    SetScrollOffset(ScrollOffset(target_viewport.X(), target_viewport.Y()),
-                    scroll_type);
+    ScrollOffset target_offset(target_viewport.X(), target_viewport.Y());
+    if (is_smooth) {
+      DCHECK(scroll_type == kProgrammaticScroll);
+      GetSmoothScrollSequencer()->QueueAnimation(this, target_offset);
+    } else {
+      SetScrollOffset(target_offset, scroll_type);
+    }
   }
 
   // RootFrameViewport only changes the viewport relative to the document so we
@@ -431,6 +437,10 @@
   return LayoutViewport().GetChromeClient();
 }
 
+SmoothScrollSequencer* RootFrameViewport::GetSmoothScrollSequencer() const {
+  return LayoutViewport().GetSmoothScrollSequencer();
+}
+
 void RootFrameViewport::ServiceScrollAnimations(double monotonic_time) {
   ScrollableArea::ServiceScrollAnimations(monotonic_time);
   LayoutViewport().ServiceScrollAnimations(monotonic_time);
diff --git a/third_party/WebKit/Source/core/frame/RootFrameViewport.h b/third_party/WebKit/Source/core/frame/RootFrameViewport.h
index 2c276dad..473b4d2 100644
--- a/third_party/WebKit/Source/core/frame/RootFrameViewport.h
+++ b/third_party/WebKit/Source/core/frame/RootFrameViewport.h
@@ -61,6 +61,7 @@
   LayoutRect ScrollIntoView(const LayoutRect& rect_in_content,
                             const ScrollAlignment& align_x,
                             const ScrollAlignment& align_y,
+                            bool is_smooth,
                             ScrollType = kProgrammaticScroll) override;
   IntRect VisibleContentRect(
       IncludeScrollbarsInRect = kExcludeScrollbars) const override;
@@ -95,6 +96,7 @@
   ScrollResult UserScroll(ScrollGranularity, const FloatSize&) override;
   bool ScrollAnimatorEnabled() const override;
   PlatformChromeClient* GetChromeClient() const override;
+  SmoothScrollSequencer* GetSmoothScrollSequencer() const override;
   void ServiceScrollAnimations(double) override;
   void UpdateCompositorScrollAnimations() override;
   void CancelProgrammaticScrollAnimation() override;
diff --git a/third_party/WebKit/Source/core/frame/RootFrameViewportTest.cpp b/third_party/WebKit/Source/core/frame/RootFrameViewportTest.cpp
index 54d335f..b22aace 100644
--- a/third_party/WebKit/Source/core/frame/RootFrameViewportTest.cpp
+++ b/third_party/WebKit/Source/core/frame/RootFrameViewportTest.cpp
@@ -307,15 +307,15 @@
   // resized (as is the case when the ChromeOS keyboard comes up) but not
   // scaled.
   visual_viewport->SetViewportSize(IntSize(100, 100));
-  root_frame_viewport->ScrollIntoView(LayoutRect(100, 250, 50, 50),
-                                      ScrollAlignment::kAlignToEdgeIfNeeded,
-                                      ScrollAlignment::kAlignToEdgeIfNeeded);
+  root_frame_viewport->ScrollIntoView(
+      LayoutRect(100, 250, 50, 50), ScrollAlignment::kAlignToEdgeIfNeeded,
+      ScrollAlignment::kAlignToEdgeIfNeeded, false);
   EXPECT_SIZE_EQ(ScrollOffset(50, 150), layout_viewport->GetScrollOffset());
   EXPECT_SIZE_EQ(ScrollOffset(0, 50), visual_viewport->GetScrollOffset());
 
-  root_frame_viewport->ScrollIntoView(LayoutRect(25, 75, 50, 50),
-                                      ScrollAlignment::kAlignToEdgeIfNeeded,
-                                      ScrollAlignment::kAlignToEdgeIfNeeded);
+  root_frame_viewport->ScrollIntoView(
+      LayoutRect(25, 75, 50, 50), ScrollAlignment::kAlignToEdgeIfNeeded,
+      ScrollAlignment::kAlignToEdgeIfNeeded, false);
   EXPECT_SIZE_EQ(ScrollOffset(25, 75), layout_viewport->GetScrollOffset());
   EXPECT_SIZE_EQ(ScrollOffset(0, 0), visual_viewport->GetScrollOffset());
 
@@ -324,15 +324,15 @@
   visual_viewport->SetScale(2);
   root_frame_viewport->SetScrollOffset(ScrollOffset(), kProgrammaticScroll);
 
-  root_frame_viewport->ScrollIntoView(LayoutRect(50, 75, 50, 75),
-                                      ScrollAlignment::kAlignToEdgeIfNeeded,
-                                      ScrollAlignment::kAlignToEdgeIfNeeded);
+  root_frame_viewport->ScrollIntoView(
+      LayoutRect(50, 75, 50, 75), ScrollAlignment::kAlignToEdgeIfNeeded,
+      ScrollAlignment::kAlignToEdgeIfNeeded, false);
   EXPECT_SIZE_EQ(ScrollOffset(0, 0), layout_viewport->GetScrollOffset());
   EXPECT_SIZE_EQ(ScrollOffset(50, 75), visual_viewport->GetScrollOffset());
 
-  root_frame_viewport->ScrollIntoView(LayoutRect(190, 290, 10, 10),
-                                      ScrollAlignment::kAlignToEdgeIfNeeded,
-                                      ScrollAlignment::kAlignToEdgeIfNeeded);
+  root_frame_viewport->ScrollIntoView(
+      LayoutRect(190, 290, 10, 10), ScrollAlignment::kAlignToEdgeIfNeeded,
+      ScrollAlignment::kAlignToEdgeIfNeeded, false);
   EXPECT_SIZE_EQ(ScrollOffset(100, 150), layout_viewport->GetScrollOffset());
   EXPECT_SIZE_EQ(ScrollOffset(50, 75), visual_viewport->GetScrollOffset());
 
@@ -347,19 +347,21 @@
   root_frame_viewport->ScrollIntoView(
       LayoutRect(root_frame_viewport->VisibleContentRect(kExcludeScrollbars)),
       ScrollAlignment::kAlignToEdgeIfNeeded,
-      ScrollAlignment::kAlignToEdgeIfNeeded);
+      ScrollAlignment::kAlignToEdgeIfNeeded, false);
   EXPECT_SIZE_EQ(ScrollOffset(50, 50), layout_viewport->GetScrollOffset());
   EXPECT_SIZE_EQ(ScrollOffset(0, 10), visual_viewport->GetScrollOffset());
 
   root_frame_viewport->ScrollIntoView(
       LayoutRect(root_frame_viewport->VisibleContentRect(kExcludeScrollbars)),
-      ScrollAlignment::kAlignCenterAlways, ScrollAlignment::kAlignCenterAlways);
+      ScrollAlignment::kAlignCenterAlways, ScrollAlignment::kAlignCenterAlways,
+      false);
   EXPECT_SIZE_EQ(ScrollOffset(50, 50), layout_viewport->GetScrollOffset());
   EXPECT_SIZE_EQ(ScrollOffset(0, 10), visual_viewport->GetScrollOffset());
 
   root_frame_viewport->ScrollIntoView(
       LayoutRect(root_frame_viewport->VisibleContentRect(kExcludeScrollbars)),
-      ScrollAlignment::kAlignTopAlways, ScrollAlignment::kAlignTopAlways);
+      ScrollAlignment::kAlignTopAlways, ScrollAlignment::kAlignTopAlways,
+      false);
   EXPECT_SIZE_EQ(ScrollOffset(50, 50), layout_viewport->GetScrollOffset());
   EXPECT_SIZE_EQ(ScrollOffset(0, 10), visual_viewport->GetScrollOffset());
 }
diff --git a/third_party/WebKit/Source/core/frame/ScrollIntoViewOptions.idl b/third_party/WebKit/Source/core/frame/ScrollIntoViewOptions.idl
new file mode 100644
index 0000000..49a2c831
--- /dev/null
+++ b/third_party/WebKit/Source/core/frame/ScrollIntoViewOptions.idl
@@ -0,0 +1,6 @@
+enum ScrollLogicalPosition { "start", "center", "end", "nearest" };
+
+dictionary ScrollIntoViewOptions : ScrollOptions {
+    ScrollLogicalPosition block = "center";
+    ScrollLogicalPosition inlinePosition = "center";
+};
\ No newline at end of file
diff --git a/third_party/WebKit/Source/core/html/HTMLVideoElement.h b/third_party/WebKit/Source/core/html/HTMLVideoElement.h
index 6c9a1f55..f8dbebc 100644
--- a/third_party/WebKit/Source/core/html/HTMLVideoElement.h
+++ b/third_party/WebKit/Source/core/html/HTMLVideoElement.h
@@ -143,6 +143,9 @@
   }
   void DisableMediaRemoting();
 
+  void MediaRemotingStarted() final;
+  void MediaRemotingStopped() final;
+
  private:
   friend class MediaCustomControlsFullscreenDetectorTest;
   friend class HTMLMediaElementEventListenersTest;
@@ -167,8 +170,6 @@
   void UpdateDisplayState() override;
   void DidMoveToNewDocument(Document& old_document) override;
   void SetDisplayMode(DisplayMode) override;
-  void MediaRemotingStarted() final;
-  void MediaRemotingStopped() final;
 
   Member<HTMLImageLoader> image_loader_;
   Member<MediaCustomControlsFullscreenDetector>
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
index 8cbc40d..382ef45 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -662,7 +662,8 @@
                                     const ScrollAlignment& align_x,
                                     const ScrollAlignment& align_y,
                                     ScrollType scroll_type,
-                                    bool make_visible_in_visual_viewport) {
+                                    bool make_visible_in_visual_viewport,
+                                    ScrollBehavior scroll_behavior) {
   DCHECK(scroll_type == kProgrammaticScroll || scroll_type == kUserScroll);
   // Presumably the same issue as in setScrollTop. See crbug.com/343132.
   DisableCompositingQueryAsserts disabler;
@@ -683,14 +684,18 @@
         !ContainingBlock()->Style()->LineClamp().IsNone();
   }
 
+  bool is_smooth = scroll_behavior == kScrollBehaviorSmooth ||
+                   (scroll_behavior == kScrollBehaviorAuto &&
+                    Style()->GetScrollBehavior() == kScrollBehaviorSmooth);
+
   if (HasOverflowClip() && !restricted_by_line_clamp) {
     // Don't scroll to reveal an overflow layer that is restricted by the
     // -webkit-line-clamp property. This will prevent us from revealing text
     // hidden by the slider in Safari RSS.
     // TODO(eae): We probably don't need this any more as we don't share any
     //            code with the Safari RSS reeder.
-    new_rect = GetScrollableArea()->ScrollIntoView(rect_to_scroll, align_x,
-                                                   align_y, scroll_type);
+    new_rect = GetScrollableArea()->ScrollIntoView(
+        rect_to_scroll, align_x, align_y, is_smooth, scroll_type);
     if (new_rect.IsEmpty())
       return;
   } else if (!parent_box && CanBeProgramaticallyScrolled()) {
@@ -699,10 +704,10 @@
       if (!IsDisallowedAutoscroll(owner_element, frame_view)) {
         if (make_visible_in_visual_viewport) {
           frame_view->GetScrollableArea()->ScrollIntoView(
-              rect_to_scroll, align_x, align_y, scroll_type);
+              rect_to_scroll, align_x, align_y, is_smooth, scroll_type);
         } else {
           frame_view->LayoutViewportScrollableArea()->ScrollIntoView(
-              rect_to_scroll, align_x, align_y, scroll_type);
+              rect_to_scroll, align_x, align_y, is_smooth, scroll_type);
         }
         if (owner_element && owner_element->GetLayoutObject()) {
           if (frame_view->SafeToPropagateScrollToParent()) {
@@ -732,9 +737,11 @@
   if (GetFrame()->GetPage()->GetAutoscrollController().AutoscrollInProgress())
     parent_box = EnclosingScrollableBox();
 
-  if (parent_box)
+  if (parent_box) {
     parent_box->ScrollRectToVisible(new_rect, align_x, align_y, scroll_type,
-                                    make_visible_in_visual_viewport);
+                                    make_visible_in_visual_viewport,
+                                    scroll_behavior);
+  }
 }
 
 void LayoutBox::AbsoluteRects(Vector<IntRect>& rects,
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.h b/third_party/WebKit/Source/core/layout/LayoutBox.h
index d2b3f86..464f77a 100644
--- a/third_party/WebKit/Source/core/layout/LayoutBox.h
+++ b/third_party/WebKit/Source/core/layout/LayoutBox.h
@@ -588,7 +588,8 @@
                            const ScrollAlignment& align_x,
                            const ScrollAlignment& align_y,
                            ScrollType = kProgrammaticScroll,
-                           bool make_visible_in_visual_viewport = true);
+                           bool make_visible_in_visual_viewport = true,
+                           ScrollBehavior = kScrollBehaviorAuto);
 
   LayoutRectOutsets MarginBoxOutsets() const override {
     return margin_box_outsets_;
diff --git a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp
index a3d2251..7fc73fa 100644
--- a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp
@@ -1483,11 +1483,12 @@
   return LayoutUnit();
 }
 
-// FIXME: This logic is shared by LayoutFlexibleBox, so it should be moved to
-// LayoutBox.
-LayoutUnit LayoutGrid::MarginLogicalHeightForChild(
+LayoutUnit LayoutGrid::MarginLogicalSizeForChild(
+    GridTrackSizingDirection direction,
     const LayoutBox& child) const {
-  return IsHorizontalWritingMode() ? child.MarginHeight() : child.MarginWidth();
+  return FlowAwareDirectionForChild(child, direction) == kForColumns
+             ? child.MarginLogicalWidth()
+             : child.MarginLogicalHeight();
 }
 
 LayoutUnit LayoutGrid::ComputeMarginLogicalSizeForChild(
@@ -1520,10 +1521,12 @@
   // performed before children are laid out, so we can't use the child cached
   // values. Hence, we need to compute margins in order to determine the
   // available height before stretching.
+  GridTrackSizingDirection child_block_flow_direction =
+      FlowAwareDirectionForChild(child, kForRows);
   return grid_area_breadth_for_child -
          (child.NeedsLayout()
               ? ComputeMarginLogicalSizeForChild(kBlockDirection, child)
-              : MarginLogicalHeightForChild(child));
+              : MarginLogicalSizeForChild(child_block_flow_direction, child));
 }
 
 StyleSelfAlignmentData LayoutGrid::AlignSelfForChild(
diff --git a/third_party/WebKit/Source/core/layout/LayoutGrid.h b/third_party/WebKit/Source/core/layout/LayoutGrid.h
index 34b5414..cf5354f 100644
--- a/third_party/WebKit/Source/core/layout/LayoutGrid.h
+++ b/third_party/WebKit/Source/core/layout/LayoutGrid.h
@@ -205,7 +205,8 @@
 
   void PaintChildren(const PaintInfo&, const LayoutPoint&) const override;
 
-  LayoutUnit MarginLogicalHeightForChild(const LayoutBox&) const;
+  LayoutUnit MarginLogicalSizeForChild(GridTrackSizingDirection,
+                                       const LayoutBox&) const;
   LayoutUnit ComputeMarginLogicalSizeForChild(MarginDirection,
                                               const LayoutBox&) const;
   LayoutUnit AvailableAlignmentSpaceForChildBeforeStretching(
diff --git a/third_party/WebKit/Source/core/layout/LayoutListBox.cpp b/third_party/WebKit/Source/core/layout/LayoutListBox.cpp
index d3a45d6..fb7cbd9 100644
--- a/third_party/WebKit/Source/core/layout/LayoutListBox.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutListBox.cpp
@@ -133,7 +133,7 @@
     DCHECK(Layer()->GetScrollableArea());
     Layer()->GetScrollableArea()->ScrollIntoView(
         rect, ScrollAlignment::kAlignToEdgeIfNeeded,
-        ScrollAlignment::kAlignToEdgeIfNeeded);
+        ScrollAlignment::kAlignToEdgeIfNeeded, false);
   }
 }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
index 1f6b5b50..1639492 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -631,13 +631,15 @@
                                        const ScrollAlignment& align_x,
                                        const ScrollAlignment& align_y,
                                        ScrollType scroll_type,
-                                       bool make_visible_in_visual_viewport) {
+                                       bool make_visible_in_visual_viewport,
+                                       ScrollBehavior scroll_behavior) {
   LayoutBox* enclosing_box = this->EnclosingBox();
   if (!enclosing_box)
     return false;
 
   enclosing_box->ScrollRectToVisible(rect, align_x, align_y, scroll_type,
-                                     make_visible_in_visual_viewport);
+                                     make_visible_in_visual_viewport,
+                                     scroll_behavior);
   return true;
 }
 
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.h b/third_party/WebKit/Source/core/layout/LayoutObject.h
index 34ccc620..ed845f2 100644
--- a/third_party/WebKit/Source/core/layout/LayoutObject.h
+++ b/third_party/WebKit/Source/core/layout/LayoutObject.h
@@ -280,7 +280,8 @@
       const ScrollAlignment& align_x = ScrollAlignment::kAlignCenterIfNeeded,
       const ScrollAlignment& align_y = ScrollAlignment::kAlignCenterIfNeeded,
       ScrollType = kProgrammaticScroll,
-      bool make_visible_in_visual_viewport = true);
+      bool make_visible_in_visual_viewport = true,
+      ScrollBehavior = kScrollBehaviorAuto);
 
   // Convenience function for getting to the nearest enclosing box of a
   // LayoutObject.
diff --git a/third_party/WebKit/Source/core/layout/ScrollAlignment.cpp b/third_party/WebKit/Source/core/layout/ScrollAlignment.cpp
index b91cd6b..f9756ff 100644
--- a/third_party/WebKit/Source/core/layout/ScrollAlignment.cpp
+++ b/third_party/WebKit/Source/core/layout/ScrollAlignment.cpp
@@ -59,6 +59,10 @@
     kScrollAlignmentTop, kScrollAlignmentTop, kScrollAlignmentTop};
 const ScrollAlignment ScrollAlignment::kAlignBottomAlways = {
     kScrollAlignmentBottom, kScrollAlignmentBottom, kScrollAlignmentBottom};
+const ScrollAlignment ScrollAlignment::kAlignLeftAlways = {
+    kScrollAlignmentLeft, kScrollAlignmentLeft, kScrollAlignmentLeft};
+const ScrollAlignment ScrollAlignment::kAlignRightAlways = {
+    kScrollAlignmentRight, kScrollAlignmentRight, kScrollAlignmentRight};
 
 #define MIN_INTERSECT_FOR_REVEAL 32
 
diff --git a/third_party/WebKit/Source/core/layout/ScrollAlignment.h b/third_party/WebKit/Source/core/layout/ScrollAlignment.h
index 4d162cc7..cfa070b 100644
--- a/third_party/WebKit/Source/core/layout/ScrollAlignment.h
+++ b/third_party/WebKit/Source/core/layout/ScrollAlignment.h
@@ -84,6 +84,8 @@
   static const ScrollAlignment kAlignCenterAlways;
   static const ScrollAlignment kAlignTopAlways;
   static const ScrollAlignment kAlignBottomAlways;
+  static const ScrollAlignment kAlignLeftAlways;
+  static const ScrollAlignment kAlignRightAlways;
 
   ScrollAlignmentBehavior rect_visible_;
   ScrollAlignmentBehavior rect_hidden_;
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp
index 25b883e..a8096785 100644
--- a/third_party/WebKit/Source/core/page/Page.cpp
+++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -61,6 +61,7 @@
 #include "platform/graphics/GraphicsLayer.h"
 #include "platform/loader/fetch/ResourceFetcher.h"
 #include "platform/plugins/PluginData.h"
+#include "platform/scroll/SmoothScrollSequencer.h"
 #include "public/platform/Platform.h"
 
 namespace blink {
@@ -172,6 +173,13 @@
   return *page_scale_constraints_set_;
 }
 
+SmoothScrollSequencer* Page::GetSmoothScrollSequencer() {
+  if (!smooth_scroll_sequencer_)
+    smooth_scroll_sequencer_ = new SmoothScrollSequencer();
+
+  return smooth_scroll_sequencer_.Get();
+}
+
 const PageScaleConstraintsSet& Page::GetPageScaleConstraintsSet() const {
   return *page_scale_constraints_set_;
 }
@@ -626,6 +634,7 @@
   visitor->Trace(context_menu_controller_);
   visitor->Trace(pointer_lock_controller_);
   visitor->Trace(scrolling_coordinator_);
+  visitor->Trace(smooth_scroll_sequencer_);
   visitor->Trace(browser_controls_);
   visitor->Trace(console_message_storage_);
   visitor->Trace(event_handler_registry_);
diff --git a/third_party/WebKit/Source/core/page/Page.h b/third_party/WebKit/Source/core/page/Page.h
index f1ef9635..9e0b4de 100644
--- a/third_party/WebKit/Source/core/page/Page.h
+++ b/third_party/WebKit/Source/core/page/Page.h
@@ -66,6 +66,7 @@
 class PointerLockController;
 class ScopedPageSuspender;
 class ScrollingCoordinator;
+class SmoothScrollSequencer;
 class Settings;
 class ConsoleMessageStorage;
 class SpellCheckerClient;
@@ -177,6 +178,8 @@
 
   ScrollingCoordinator* GetScrollingCoordinator();
 
+  SmoothScrollSequencer* GetSmoothScrollSequencer();
+
   ClientRectList* NonFastScrollableRects(const LocalFrame*);
 
   Settings& GetSettings() const { return *settings_; }
@@ -302,6 +305,7 @@
   const std::unique_ptr<PageScaleConstraintsSet> page_scale_constraints_set_;
   const Member<PointerLockController> pointer_lock_controller_;
   Member<ScrollingCoordinator> scrolling_coordinator_;
+  Member<SmoothScrollSequencer> smooth_scroll_sequencer_;
   const Member<BrowserControls> browser_controls_;
   const Member<ConsoleMessageStorage> console_message_storage_;
   const Member<EventHandlerRegistry> event_handler_registry_;
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
index a268a74f..948b6c2 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.cpp
@@ -209,6 +209,13 @@
   return nullptr;
 }
 
+SmoothScrollSequencer* PaintLayerScrollableArea::GetSmoothScrollSequencer()
+    const {
+  if (Page* page = Box().GetFrame()->GetPage())
+    return page->GetSmoothScrollSequencer();
+  return nullptr;
+}
+
 GraphicsLayer* PaintLayerScrollableArea::LayerForScrolling() const {
   return Layer()->HasCompositedLayerMapping()
              ? Layer()->GetCompositedLayerMapping()->ScrollingContentsLayer()
@@ -1731,6 +1738,7 @@
     const LayoutRect& rect,
     const ScrollAlignment& align_x,
     const ScrollAlignment& align_y,
+    bool is_smooth,
     ScrollType scroll_type) {
   LayoutRect local_expose_rect(
       Box()
@@ -1744,8 +1752,14 @@
   ScrollOffset old_scroll_offset = GetScrollOffset();
   ScrollOffset new_scroll_offset(ClampScrollOffset(RoundedIntSize(
       ToScrollOffset(FloatPoint(r.Location()) + old_scroll_offset))));
-  SetScrollOffset(new_scroll_offset, scroll_type, kScrollBehaviorInstant);
-  ScrollOffset scroll_offset_difference = GetScrollOffset() - old_scroll_offset;
+  if (is_smooth) {
+    DCHECK(scroll_type == kProgrammaticScroll);
+    GetSmoothScrollSequencer()->QueueAnimation(this, new_scroll_offset);
+  } else {
+    SetScrollOffset(new_scroll_offset, scroll_type, kScrollBehaviorInstant);
+  }
+  ScrollOffset scroll_offset_difference =
+      ClampScrollOffset(new_scroll_offset) - old_scroll_offset;
   local_expose_rect.Move(-LayoutSize(scroll_offset_difference));
 
   LayoutRect intersect =
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
index c396827..ff607f8f 100644
--- a/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
+++ b/third_party/WebKit/Source/core/paint/PaintLayerScrollableArea.h
@@ -240,6 +240,8 @@
 
   PlatformChromeClient* GetChromeClient() const override;
 
+  SmoothScrollSequencer* GetSmoothScrollSequencer() const override;
+
   // For composited scrolling, we allocate an extra GraphicsLayer to hold
   // onto the scrolling content. The layer can be shifted on the GPU and
   // composited at little cost.
@@ -392,6 +394,7 @@
   LayoutRect ScrollIntoView(const LayoutRect&,
                             const ScrollAlignment& align_x,
                             const ScrollAlignment& align_y,
+                            bool is_smooth,
                             ScrollType = kProgrammaticScroll) override;
 
   // Returns true if scrollable area is in the FrameView's collection of
diff --git a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
index aaa72f77..90fafd8 100644
--- a/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
+++ b/third_party/WebKit/Source/modules/remoteplayback/RemotePlayback.cpp
@@ -13,6 +13,7 @@
 #include "core/dom/UserGestureIndicator.h"
 #include "core/events/Event.h"
 #include "core/html/HTMLMediaElement.h"
+#include "core/html/HTMLVideoElement.h"
 #include "core/probe/CoreProbes.h"
 #include "modules/EventTargetModules.h"
 #include "modules/remoteplayback/AvailabilityCallbackWrapper.h"
@@ -269,12 +270,20 @@
   switch (state_) {
     case WebRemotePlaybackState::kConnecting:
       DispatchEvent(Event::Create(EventTypeNames::connecting));
+      if (RuntimeEnabledFeatures::newRemotePlaybackPipelineEnabled() &&
+          media_element_->IsHTMLVideoElement()) {
+        toHTMLVideoElement(media_element_)->MediaRemotingStarted();
+      }
       break;
     case WebRemotePlaybackState::kConnected:
       DispatchEvent(Event::Create(EventTypeNames::connect));
       break;
     case WebRemotePlaybackState::kDisconnected:
       DispatchEvent(Event::Create(EventTypeNames::disconnect));
+      if (RuntimeEnabledFeatures::newRemotePlaybackPipelineEnabled() &&
+          media_element_->IsHTMLVideoElement()) {
+        toHTMLVideoElement(media_element_)->MediaRemotingStopped();
+      }
       break;
   }
 }
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index e3caa04..6dcc0b1b 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -1386,6 +1386,8 @@
     "scroll/ScrollbarThemeOverlay.cpp",
     "scroll/ScrollbarThemeOverlay.h",
     "scroll/ScrollbarThemeOverlayMock.h",
+    "scroll/SmoothScrollSequencer.cpp",
+    "scroll/SmoothScrollSequencer.h",
     "speech/PlatformSpeechSynthesisUtterance.cpp",
     "speech/PlatformSpeechSynthesisUtterance.h",
     "speech/PlatformSpeechSynthesisVoice.cpp",
diff --git a/third_party/WebKit/Source/platform/geometry/FloatSize.h b/third_party/WebKit/Source/platform/geometry/FloatSize.h
index 138c452..0dbbafe 100644
--- a/third_party/WebKit/Source/platform/geometry/FloatSize.h
+++ b/third_party/WebKit/Source/platform/geometry/FloatSize.h
@@ -199,4 +199,7 @@
 
 }  // namespace blink
 
+// Allows this class to be stored in a HeapVector.
+WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::FloatSize);
+
 #endif  // FloatSize_h
diff --git a/third_party/WebKit/Source/platform/scroll/ProgrammaticScrollAnimator.cpp b/third_party/WebKit/Source/platform/scroll/ProgrammaticScrollAnimator.cpp
index b717e08..1ced7257 100644
--- a/third_party/WebKit/Source/platform/scroll/ProgrammaticScrollAnimator.cpp
+++ b/third_party/WebKit/Source/platform/scroll/ProgrammaticScrollAnimator.cpp
@@ -10,6 +10,7 @@
 #include "platform/geometry/IntSize.h"
 #include "platform/graphics/GraphicsLayer.h"
 #include "platform/scroll/ScrollableArea.h"
+#include "platform/scroll/SmoothScrollSequencer.h"
 #include "platform/wtf/PtrUtil.h"
 #include "public/platform/Platform.h"
 #include "public/platform/WebCompositorSupport.h"
@@ -39,12 +40,15 @@
   NotifyOffsetChanged(offset);
 }
 
-void ProgrammaticScrollAnimator::AnimateToOffset(const ScrollOffset& offset) {
+void ProgrammaticScrollAnimator::AnimateToOffset(
+    const ScrollOffset& offset,
+    bool sequenced_for_smooth_scroll) {
   if (run_state_ == RunState::kPostAnimationCleanup)
     ResetAnimationState();
 
   start_time_ = 0.0;
   target_offset_ = offset;
+  sequenced_for_smooth_scroll_ = sequenced_for_smooth_scroll;
   animation_curve_ = CompositorScrollOffsetAnimationCurve::Create(
       CompositorOffsetFromBlinkOffset(target_offset_),
       CompositorScrollOffsetAnimationCurve::kScrollDurationDeltaBased);
@@ -76,6 +80,7 @@
 
   if (is_finished) {
     run_state_ = RunState::kPostAnimationCleanup;
+    AnimationFinished();
   } else if (!scrollable_area_->ScheduleAnimation()) {
     NotifyOffsetChanged(offset);
     ResetAnimationState();
@@ -169,6 +174,16 @@
     int group_id) {
   DCHECK_NE(run_state_, RunState::kRunningOnCompositorButNeedsUpdate);
   ScrollAnimatorCompositorCoordinator::CompositorAnimationFinished(group_id);
+  AnimationFinished();
+}
+
+void ProgrammaticScrollAnimator::AnimationFinished() {
+  if (sequenced_for_smooth_scroll_) {
+    sequenced_for_smooth_scroll_ = false;
+    if (SmoothScrollSequencer* sequencer =
+            GetScrollableArea()->GetSmoothScrollSequencer())
+      sequencer->RunQueuedAnimations();
+  }
 }
 
 DEFINE_TRACE(ProgrammaticScrollAnimator) {
diff --git a/third_party/WebKit/Source/platform/scroll/ProgrammaticScrollAnimator.h b/third_party/WebKit/Source/platform/scroll/ProgrammaticScrollAnimator.h
index f8a365c..8f1240fa 100644
--- a/third_party/WebKit/Source/platform/scroll/ProgrammaticScrollAnimator.h
+++ b/third_party/WebKit/Source/platform/scroll/ProgrammaticScrollAnimator.h
@@ -30,7 +30,8 @@
   virtual ~ProgrammaticScrollAnimator();
 
   void ScrollToOffsetWithoutAnimation(const ScrollOffset&);
-  void AnimateToOffset(const ScrollOffset&);
+  void AnimateToOffset(const ScrollOffset&,
+                       bool sequenced_for_smooth_scroll = false);
 
   // ScrollAnimatorCompositorCoordinator implementation.
   void ResetAnimationState() override;
@@ -52,11 +53,13 @@
   explicit ProgrammaticScrollAnimator(ScrollableArea*);
 
   void NotifyOffsetChanged(const ScrollOffset&);
+  void AnimationFinished();
 
   Member<ScrollableArea> scrollable_area_;
   std::unique_ptr<CompositorScrollOffsetAnimationCurve> animation_curve_;
   ScrollOffset target_offset_;
   double start_time_;
+  bool sequenced_for_smooth_scroll_;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollTypes.h b/third_party/WebKit/Source/platform/scroll/ScrollTypes.h
index 63a564a..7be2896 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollTypes.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollTypes.h
@@ -181,6 +181,8 @@
 
 enum ScrollbarOrientation { kHorizontalScrollbar, kVerticalScrollbar };
 
+enum ScrollOrientation { kHorizontalScroll, kVerticalScroll };
+
 enum ScrollbarMode { kScrollbarAuto, kScrollbarAlwaysOff, kScrollbarAlwaysOn };
 
 enum ScrollbarControlSize { kRegularScrollbar, kSmallScrollbar };
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollableArea.cpp b/third_party/WebKit/Source/platform/scroll/ScrollableArea.cpp
index 769acc3..09a44edf 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollableArea.cpp
+++ b/third_party/WebKit/Source/platform/scroll/ScrollableArea.cpp
@@ -37,6 +37,7 @@
 #include "platform/scroll/MainThreadScrollingReason.h"
 #include "platform/scroll/ProgrammaticScrollAnimator.h"
 #include "platform/scroll/ScrollbarTheme.h"
+#include "platform/scroll/SmoothScrollSequencer.h"
 
 static const int kPixelsPerLineStep = 40;
 static const float kMinFractionToStepWhenPaging = 0.875f;
@@ -227,7 +228,7 @@
   CancelScrollAnimation();
 
   if (scroll_behavior == kScrollBehaviorSmooth)
-    GetProgrammaticScrollAnimator().AnimateToOffset(offset);
+    GetProgrammaticScrollAnimator().AnimateToOffset(offset, true);
   else
     GetProgrammaticScrollAnimator().ScrollToOffsetWithoutAnimation(offset);
 }
@@ -255,6 +256,7 @@
 LayoutRect ScrollableArea::ScrollIntoView(const LayoutRect& rect_in_content,
                                           const ScrollAlignment& align_x,
                                           const ScrollAlignment& align_y,
+                                          bool is_smooth,
                                           ScrollType) {
   // TODO(bokan): This should really be implemented here but ScrollAlignment is
   // in Core which is a dependency violation.
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollableArea.h b/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
index a84fe5c..728ac9b5 100644
--- a/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
+++ b/third_party/WebKit/Source/platform/scroll/ScrollableArea.h
@@ -53,6 +53,7 @@
 struct ScrollAlignment;
 class ScrollAnchor;
 class ScrollAnimatorBase;
+class SmoothScrollSequencer;
 class CompositorAnimationTimeline;
 
 enum IncludeScrollbarsInRect {
@@ -77,6 +78,10 @@
 
   virtual PlatformChromeClient* GetChromeClient() const { return 0; }
 
+  virtual SmoothScrollSequencer* GetSmoothScrollSequencer() const {
+    return nullptr;
+  }
+
   virtual ScrollResult UserScroll(ScrollGranularity, const ScrollOffset&);
 
   virtual void SetScrollOffset(const ScrollOffset&,
@@ -99,6 +104,7 @@
   virtual LayoutRect ScrollIntoView(const LayoutRect& rect_in_content,
                                     const ScrollAlignment& align_x,
                                     const ScrollAlignment& align_y,
+                                    bool is_smooth,
                                     ScrollType = kProgrammaticScroll);
 
   static bool ScrollBehaviorFromString(const String&, ScrollBehavior&);
diff --git a/third_party/WebKit/Source/platform/scroll/SmoothScrollSequencer.cpp b/third_party/WebKit/Source/platform/scroll/SmoothScrollSequencer.cpp
new file mode 100644
index 0000000..f4ba71b
--- /dev/null
+++ b/third_party/WebKit/Source/platform/scroll/SmoothScrollSequencer.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) 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/scroll/SmoothScrollSequencer.h"
+
+#include "platform/scroll/ProgrammaticScrollAnimator.h"
+#include "platform/scroll/ScrollableArea.h"
+
+namespace blink {
+
+SmoothScrollSequencer::SmoothScrollSequencer() {}
+
+void SmoothScrollSequencer::QueueAnimation(ScrollableArea* scrollable,
+                                           ScrollOffset offset) {
+  ScrollerAndOffsetPair scroller_offset(scrollable, offset);
+  queue_.push_back(scroller_offset);
+}
+
+void SmoothScrollSequencer::RunQueuedAnimations() {
+  if (queue_.IsEmpty()) {
+    current_scrollable_ = nullptr;
+    return;
+  }
+  ScrollerAndOffsetPair scroller_offset = queue_.back();
+  queue_.pop_back();
+  ScrollableArea* scrollable = scroller_offset.first;
+  current_scrollable_ = scrollable;
+  ScrollOffset offset = scroller_offset.second;
+  scrollable->SetScrollOffset(offset, kProgrammaticScroll,
+                              kScrollBehaviorSmooth);
+}
+
+void SmoothScrollSequencer::AbortAnimations() {
+  if (current_scrollable_) {
+    current_scrollable_->GetProgrammaticScrollAnimator().CancelAnimation();
+    current_scrollable_ = nullptr;
+  }
+  queue_.clear();
+}
+
+DEFINE_TRACE(SmoothScrollSequencer) {
+  visitor->Trace(queue_);
+  visitor->Trace(current_scrollable_);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/scroll/SmoothScrollSequencer.h b/third_party/WebKit/Source/platform/scroll/SmoothScrollSequencer.h
new file mode 100644
index 0000000..3c72499
--- /dev/null
+++ b/third_party/WebKit/Source/platform/scroll/SmoothScrollSequencer.h
@@ -0,0 +1,41 @@
+// Copyright (c) 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 SmoothScrollSequencer_h
+#define SmoothScrollSequencer_h
+
+#include <utility>
+#include "platform/heap/Handle.h"
+#include "platform/scroll/ScrollTypes.h"
+
+namespace blink {
+
+class ScrollableArea;
+
+// A sequencer that queues the nested scrollers from inside to outside,
+// so that they can be animated from outside to inside when smooth scroll
+// is called.
+class PLATFORM_EXPORT SmoothScrollSequencer final
+    : public GarbageCollected<SmoothScrollSequencer> {
+ public:
+  SmoothScrollSequencer();
+  // Add a scroll offset animation to the back of a queue.
+  void QueueAnimation(ScrollableArea*, ScrollOffset);
+
+  // Run the animation at the back of the queue.
+  void RunQueuedAnimations();
+
+  // Abort the currently running animation and all the animations in the queue.
+  void AbortAnimations();
+
+  DECLARE_TRACE();
+
+ private:
+  typedef std::pair<Member<ScrollableArea>, ScrollOffset> ScrollerAndOffsetPair;
+  HeapVector<ScrollerAndOffsetPair> queue_;
+  Member<ScrollableArea> current_scrollable_;
+};
+
+}  // namespace blink
+
+#endif  // SmoothScrollSequencer_h
diff --git a/third_party/WebKit/Source/platform/wtf/ThreadingPthreads.cpp b/third_party/WebKit/Source/platform/wtf/ThreadingPthreads.cpp
index e813f3c..763081e 100644
--- a/third_party/WebKit/Source/platform/wtf/ThreadingPthreads.cpp
+++ b/third_party/WebKit/Source/platform/wtf/ThreadingPthreads.cpp
@@ -249,11 +249,18 @@
 #if DCHECK_IS_ON()
 static bool g_thread_created = false;
 
+Mutex& GetThreadCreatedMutex() {
+  static Mutex g_thread_created_mutex;
+  return g_thread_created_mutex;
+}
+
 bool IsBeforeThreadCreated() {
+  MutexLocker locker(GetThreadCreatedMutex());
   return !g_thread_created;
 }
 
 void WillCreateThread() {
+  MutexLocker locker(GetThreadCreatedMutex());
   g_thread_created = true;
 }
 #endif
diff --git a/third_party/WebKit/Source/platform/wtf/ThreadingWin.cpp b/third_party/WebKit/Source/platform/wtf/ThreadingWin.cpp
index 5610e95..2fd8fa3 100644
--- a/third_party/WebKit/Source/platform/wtf/ThreadingWin.cpp
+++ b/third_party/WebKit/Source/platform/wtf/ThreadingWin.cpp
@@ -412,11 +412,18 @@
 #if DCHECK_IS_ON()
 static bool g_thread_created = false;
 
+Mutex& GetThreadCreatedMutex() {
+  static Mutex g_thread_created_mutex;
+  return g_thread_created_mutex;
+}
+
 bool IsBeforeThreadCreated() {
+  MutexLocker locker(GetThreadCreatedMutex());
   return !g_thread_created;
 }
 
 void WillCreateThread() {
+  MutexLocker locker(GetThreadCreatedMutex());
   g_thread_created = true;
 }
 #endif
diff --git a/third_party/WebKit/Source/web/BUILD.gn b/third_party/WebKit/Source/web/BUILD.gn
index eb4a756..9dce615 100644
--- a/third_party/WebKit/Source/web/BUILD.gn
+++ b/third_party/WebKit/Source/web/BUILD.gn
@@ -296,6 +296,7 @@
     "tests/ScrollbarsTest.cpp",
     "tests/ScrollingCoordinatorTest.cpp",
     "tests/ShadowDOMV0Test.cpp",
+    "tests/SmoothScrollTest.cpp",
     "tests/SpinLockTest.cpp",
     "tests/TextFinderTest.cpp",
     "tests/TextSelectionRepaintTest.cpp",
diff --git a/third_party/WebKit/Source/web/tests/SmoothScrollTest.cpp b/third_party/WebKit/Source/web/tests/SmoothScrollTest.cpp
new file mode 100644
index 0000000..8f74a32
--- /dev/null
+++ b/third_party/WebKit/Source/web/tests/SmoothScrollTest.cpp
@@ -0,0 +1,288 @@
+// 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 "bindings/core/v8/ScrollIntoViewOptionsOrBoolean.h"
+#include "core/frame/ScrollIntoViewOptions.h"
+#include "core/frame/ScrollToOptions.h"
+#include "public/web/WebScriptSource.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "web/WebLocalFrameImpl.h"
+#include "web/tests/sim/SimCompositor.h"
+#include "web/tests/sim/SimDisplayItemList.h"
+#include "web/tests/sim/SimRequest.h"
+#include "web/tests/sim/SimTest.h"
+
+namespace blink {
+
+namespace {
+
+class SmoothScrollTest : public SimTest {};
+
+TEST_F(SmoothScrollTest, InstantScroll) {
+  v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
+  WebView().Resize(WebSize(800, 600));
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(
+      "<div id='space' style='height: 1000px'></div>"
+      "<div id='content' style='height: 1000px'></div>");
+
+  Compositor().BeginFrame();
+  ASSERT_EQ(Window().scrollY(), 0);
+  Element* content = GetDocument().getElementById("content");
+  ScrollIntoViewOptionsOrBoolean arg;
+  ScrollIntoViewOptions options;
+  options.setBlock("start");
+  arg.setScrollIntoViewOptions(options);
+  content->scrollIntoView(arg);
+
+  ASSERT_EQ(Window().scrollY(), content->OffsetTop());
+}
+
+TEST_F(SmoothScrollTest, SmoothScroll) {
+  v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
+  WebView().Resize(WebSize(800, 600));
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(
+      "<div id='space' style='height: 1000px'></div>"
+      "<div id='content' style='height: 1000px'></div>");
+
+  Element* content = GetDocument().getElementById("content");
+  ScrollIntoViewOptionsOrBoolean arg;
+  ScrollIntoViewOptions options;
+  options.setBlock("start");
+  options.setBehavior("smooth");
+  arg.setScrollIntoViewOptions(options);
+  Compositor().BeginFrame();
+  ASSERT_EQ(Window().scrollY(), 0);
+
+  content->scrollIntoView(arg);
+  // Scrolling the container
+  Compositor().BeginFrame();  // update run_state_.
+  Compositor().BeginFrame();  // Set start_time = now.
+  Compositor().BeginFrame(0.2);
+  ASSERT_EQ(Window().scrollY(), 299);
+
+  // Finish scrolling the container
+  Compositor().BeginFrame(1);
+  ASSERT_EQ(Window().scrollY(), content->OffsetTop());
+}
+
+TEST_F(SmoothScrollTest, NestedContainer) {
+  v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
+  WebView().Resize(WebSize(800, 600));
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(
+      "<div id='space' style='height: 1000px'></div>"
+      "<div id='container' style='height: 600px; overflow: scroll'>"
+      "  <div id='space1' style='height: 1000px'></div>"
+      "  <div id='content' style='height: 1000px'></div>"
+      "</div>");
+
+  Element* container = GetDocument().getElementById("container");
+  Element* content = GetDocument().getElementById("content");
+  ScrollIntoViewOptionsOrBoolean arg;
+  ScrollIntoViewOptions options;
+  options.setBlock("start");
+  options.setBehavior("smooth");
+  arg.setScrollIntoViewOptions(options);
+  Compositor().BeginFrame();
+  ASSERT_EQ(Window().scrollY(), 0);
+  ASSERT_EQ(container->scrollTop(), 0);
+
+  content->scrollIntoView(arg);
+  // Scrolling the outer container
+  Compositor().BeginFrame();  // update run_state_.
+  Compositor().BeginFrame();  // Set start_time = now.
+  Compositor().BeginFrame(0.2);
+  ASSERT_EQ(Window().scrollY(), 299);
+  ASSERT_EQ(container->scrollTop(), 0);
+
+  // Finish scrolling the outer container
+  Compositor().BeginFrame(1);
+  ASSERT_EQ(Window().scrollY(), container->OffsetTop());
+  ASSERT_EQ(container->scrollTop(), 0);
+
+  // Scrolling the inner container
+  Compositor().BeginFrame();  // Set start_time = now.
+  Compositor().BeginFrame(0.2);
+  ASSERT_EQ(container->scrollTop(), 299);
+
+  // Finish scrolling the inner container
+  Compositor().BeginFrame(1);
+  ASSERT_EQ(container->scrollTop(),
+            content->OffsetTop() - container->OffsetTop());
+}
+
+TEST_F(SmoothScrollTest, NewScrollIntoViewAbortsCurrentAnimation) {
+  v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
+  WebView().Resize(WebSize(800, 600));
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(
+      "<div id='container2' style='height: 1000px; overflow: scroll'>"
+      "  <div id='space2' style='height: 1200px'></div>"
+      "  <div id='content2' style='height: 1000px'></div>"
+      "</div>"
+      "<div id='container1' style='height: 600px; overflow: scroll'>"
+      "  <div id='space1' style='height: 1000px'></div>"
+      "  <div id='content1' style='height: 1000px'></div>"
+      "</div>");
+
+  Element* container1 = GetDocument().getElementById("container1");
+  Element* container2 = GetDocument().getElementById("container2");
+  Element* content1 = GetDocument().getElementById("content1");
+  Element* content2 = GetDocument().getElementById("content2");
+  ScrollIntoViewOptionsOrBoolean arg;
+  ScrollIntoViewOptions options;
+  options.setBlock("start");
+  options.setBehavior("smooth");
+  arg.setScrollIntoViewOptions(options);
+
+  Compositor().BeginFrame();
+  ASSERT_EQ(Window().scrollY(), 0);
+  ASSERT_EQ(container1->scrollTop(), 0);
+  ASSERT_EQ(container2->scrollTop(), 0);
+
+  content1->scrollIntoView(arg);
+  Compositor().BeginFrame();  // update run_state_.
+  Compositor().BeginFrame();  // Set start_time = now.
+  Compositor().BeginFrame(0.2);
+  ASSERT_EQ(Window().scrollY(), 299);
+  ASSERT_EQ(container1->scrollTop(), 0);
+
+  content2->scrollIntoView(arg);
+  Compositor().BeginFrame();  // update run_state_.
+  Compositor().BeginFrame();  // Set start_time = now.
+  Compositor().BeginFrame(0.2);
+  ASSERT_EQ(Window().scrollY(), 61);
+  ASSERT_EQ(container1->scrollTop(), 0);  // container1 should not scroll.
+
+  Compositor().BeginFrame(1);
+  ASSERT_EQ(Window().scrollY(), container2->OffsetTop());
+  ASSERT_EQ(container2->scrollTop(), 0);
+
+  // Scrolling content2 in container2
+  Compositor().BeginFrame();  // Set start_time = now.
+  Compositor().BeginFrame(0.2);
+  ASSERT_EQ(container2->scrollTop(), 300);
+
+  // Finish all the animation to make sure there is no another animation queued
+  // on container1.
+  while (Compositor().NeedsBeginFrame()) {
+    Compositor().BeginFrame();
+  }
+  ASSERT_EQ(Window().scrollY(), container2->OffsetTop());
+  ASSERT_EQ(container2->scrollTop(),
+            content2->OffsetTop() - container2->OffsetTop());
+  ASSERT_EQ(container1->scrollTop(), 0);
+}
+
+TEST_F(SmoothScrollTest, ScrollWindowAbortsCurrentAnimation) {
+  v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
+  WebView().Resize(WebSize(800, 600));
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(
+      "<div id='space' style='height: 1000px'></div>"
+      "<div id='container' style='height: 600px; overflow: scroll'>"
+      "  <div id='space1' style='height: 1000px'></div>"
+      "  <div id='content' style='height: 1000px'></div>"
+      "</div>");
+
+  Element* container = GetDocument().getElementById("container");
+  Element* content = GetDocument().getElementById("content");
+  ScrollIntoViewOptionsOrBoolean arg;
+  ScrollIntoViewOptions options;
+  options.setBlock("start");
+  options.setBehavior("smooth");
+  arg.setScrollIntoViewOptions(options);
+  Compositor().BeginFrame();
+  ASSERT_EQ(Window().scrollY(), 0);
+  ASSERT_EQ(container->scrollTop(), 0);
+
+  content->scrollIntoView(arg);
+  // Scrolling the outer container
+  Compositor().BeginFrame();  // update run_state_.
+  Compositor().BeginFrame();  // Set start_time = now.
+  Compositor().BeginFrame(0.2);
+  ASSERT_EQ(Window().scrollY(), 299);
+  ASSERT_EQ(container->scrollTop(), 0);
+
+  ScrollToOptions window_option;
+  window_option.setLeft(0);
+  window_option.setTop(0);
+  window_option.setBehavior("smooth");
+  Window().scrollTo(window_option);
+  Compositor().BeginFrame();  // update run_state_.
+  Compositor().BeginFrame();  // Set start_time = now.
+  Compositor().BeginFrame(0.2);
+  ASSERT_EQ(Window().scrollY(), 58);
+
+  Compositor().BeginFrame(1);
+  ASSERT_EQ(Window().scrollY(), 0);
+  ASSERT_EQ(container->scrollTop(), 0);
+}
+
+TEST_F(SmoothScrollTest, BlockAndInlineSettings) {
+  v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
+  WebView().Resize(WebSize(800, 600));
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(
+      "<div id='container' style='height: 2500px; width: 2500px;'>"
+      "<div id='content' style='height: 500px; width: 500px;"
+      "margin-left: 1000px; margin-right: 1000px; margin-top: 1000px;"
+      "margin-bottom: 1000px'></div></div>");
+
+  int content_height = 500;
+  int content_width = 500;
+  int window_height = 600;
+  int window_width = 800;
+
+  Element* content = GetDocument().getElementById("content");
+  ScrollIntoViewOptionsOrBoolean arg1, arg2, arg3, arg4;
+  ScrollIntoViewOptions options;
+  ASSERT_EQ(Window().scrollY(), 0);
+
+  options.setBlock("nearest");
+  options.setInlinePosition("nearest");
+  arg1.setScrollIntoViewOptions(options);
+  content->scrollIntoView(arg1);
+  ASSERT_EQ(Window().scrollX(),
+            content->OffsetLeft() + content_width - window_width);
+  ASSERT_EQ(Window().scrollY(),
+            content->OffsetTop() + content_height - window_height);
+
+  options.setBlock("start");
+  options.setInlinePosition("start");
+  arg2.setScrollIntoViewOptions(options);
+  content->scrollIntoView(arg2);
+  ASSERT_EQ(Window().scrollX(), content->OffsetLeft());
+  ASSERT_EQ(Window().scrollY(), content->OffsetTop());
+
+  options.setBlock("center");
+  options.setInlinePosition("center");
+  arg3.setScrollIntoViewOptions(options);
+  content->scrollIntoView(arg3);
+  ASSERT_EQ(Window().scrollX(),
+            content->OffsetLeft() + (content_width - window_width) / 2);
+  ASSERT_EQ(Window().scrollY(),
+            content->OffsetTop() + (content_height - window_height) / 2);
+
+  options.setBlock("end");
+  options.setInlinePosition("end");
+  arg4.setScrollIntoViewOptions(options);
+  content->scrollIntoView(arg4);
+  ASSERT_EQ(Window().scrollX(),
+            content->OffsetLeft() + content_width - window_width);
+  ASSERT_EQ(Window().scrollY(),
+            content->OffsetTop() + content_height - window_height);
+}
+
+}  // namespace
+
+}  // namespace blink
diff --git a/third_party/WebKit/Tools/Scripts/merge-layout-test-results b/third_party/WebKit/Tools/Scripts/merge-layout-test-results
index 42778a3..e4980d73 100755
--- a/third_party/WebKit/Tools/Scripts/merge-layout-test-results
+++ b/third_party/WebKit/Tools/Scripts/merge-layout-test-results
@@ -8,45 +8,39 @@
 import json
 import logging
 import os
+import stat
 import shutil
 import sys
 import tempfile
 import time
 
+from webkitpy.common.system.filesystem import FileSystem
 from webkitpy.common.system.log_utils import configure_logging
 from webkitpy.layout_tests import merge_results
 
 # ------------------------------------------------------------------------
+def ensure_empty_dir(fs, directory, allow_existing, remove_existing):
+    """Ensure an empty directory exists.
 
-def rmtree(dirname):
-    # Attempt to remove a directory tree. We try multiple times as on Windows a
-    # process which is currently closing could still have a file open in the
-    # directory.
-    logging.info('Removing %s', dirname)
-    errors = []
-    def onerror(func, path, exc_info):
-        errors.append(path)
-        logging.exception('Failed at %s %s: %r', func, path, exc_info)
-
-    attempts = 0
-    while attempts < 5:
-        del errors[:]
-        shutil.rmtree(dirname, onerror=onerror)
-        if not errors:
-            break
-        attempts += 1
-        time.sleep(1)
-
-    # Check the path is gone.
-    if not os.path.exists(dirname):
+    Args:
+        allow_existing (bool): Allow the empty directory to already exist.
+        remove_existing (bool): Remove the contents if the directory
+            already exists.
+    """
+    if not fs.exists(directory):
+        fs.maybe_make_directory(directory)
         return
 
-    logging.warning('Unable to remove %s', dirname)
-    for dirpath, dirnames, filenames in os.walk(dirname, onerror=onerror, topdown=False):
-        for fname in filenames:
-            logging.warning('File %s still in output dir.', os.path.join(dirpath, fname))
-        for dname in dirnames:
-            logging.warning('Dir %s still in output dir.', os.path.join(dirpath, dname))
+    logging.warning('Output directory exists %r', directory)
+    if not allow_existing:
+        raise IOError(
+            ('Output directory %s exists!\n'
+             'Use --allow-existing-output-directory to continue') % directory)
+
+    if remove_existing and not fs.remove_contents(directory):
+        raise IOError(
+            ('Unable to remove output directory %s contents!\n'
+             'See log output for errors.') % directory)
 
 
 def main(argv):
@@ -156,9 +150,10 @@
             for result_key, build_prop_key in args.results_json_override_with_build_property:
                 results_json_value_overrides[result_key] = build_properties[build_prop_key]
 
-        assert not args.output_directory
-        args.output_directory = os.getcwd()
-        args.remove_existing_output_directory = True
+        if not args.output_directory:
+            args.output_directory = os.getcwd()
+            args.allow_existing_output_directory = True
+            args.remove_existing_output_directory = True
 
         assert not args.input_directories
         args.input_directories = [os.path.dirname(f) for f in args.positional]
@@ -186,14 +181,12 @@
     merger = merge_results.LayoutTestDirMerger(
         results_json_value_overrides=results_json_value_overrides,
         results_json_allow_unknown_if_matching=args.results_json_allow_unknown_if_matching)
-    if os.path.exists(args.output_directory):
-        logging.warning('Output directory exists %r', args.output_directory)
-        if args.remove_existing_output_directory:
-            rmtree(args.output_directory)
-        elif not args.allow_existing_output_directory:
-            raise IOError(
-                ('Output directory %s exists!\n'
-                 'Use --allow-existing-output-directory to continue') % args.output_directory)
+
+    ensure_empty_dir(
+        FileSystem(),
+        args.output_directory,
+        allow_existing=args.allow_existing_output_directory,
+        remove_existing=args.remove_existing_output_directory)
 
     merger.merge(args.output_directory, args.input_directories)
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem.py
index c06c190..f55d249 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem.py
@@ -28,19 +28,23 @@
 
 """Wrapper object for the file system / source tree."""
 
-import stat
 import codecs
 import errno
 import exceptions
 import glob
 import hashlib
+import logging
 import os
 import shutil
+import stat
 import sys
 import tempfile
 import time
 
 
+_log = logging.getLogger(__name__)
+
+
 class FileSystem(object):
     """FileSystem interface for webkitpy.
 
@@ -136,8 +140,8 @@
     def listdir(self, path):
         return os.listdir(path)
 
-    def walk(self, top):
-        return os.walk(top)
+    def walk(self, top, topdown=True, onerror=None, followlinks=False):
+        return os.walk(top, topdown=topdown, onerror=onerror, followlinks=followlinks)
 
     def mkdtemp(self, **kwargs):
         """Create and return a uniquely named directory.
@@ -246,7 +250,7 @@
     class _WindowsError(exceptions.OSError):
         """Fake exception for Linux and Mac."""
 
-    def remove(self, path, osremove=os.remove):
+    def remove(self, path, osremove=os.remove, retry=True):
         """On Windows, if a process was recently killed and it held on to a
         file, the OS will hold on to the file for a short while.  This makes
         attempts to delete the file fail.  To work around that, this method
@@ -266,12 +270,22 @@
             except exceptions.WindowsError:
                 time.sleep(sleep_interval)
                 retry_timeout_sec -= sleep_interval
-                if retry_timeout_sec < 0:
+                if retry_timeout_sec < 0 and not retry:
                     raise
 
-    def rmtree(self, path):
+    def rmtree(self, path, ignore_errors=True, onerror=None):
         """Delete the directory rooted at path, whether empty or not."""
-        shutil.rmtree(path, ignore_errors=True)
+        shutil.rmtree(path, ignore_errors=ignore_errors, onerror=onerror)
+
+    def remove_contents(self, dirname):
+        """Attempt to remove the contents of a directory tree.
+        Args:
+            dirname (string): Directory to remove the contents of.
+
+        Returns:
+            bool: True if the directory is now empty.
+        """
+        return _remove_contents(self, dirname)
 
     def copytree(self, source, destination):
         shutil.copytree(source, destination)
@@ -286,3 +300,66 @@
 
     def make_executable(self, file_path):
         os.chmod(file_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP)
+
+
+# _remove_contents is implemented in terms of other FileSystem functions. To
+# allow it to be reused on the MockFileSystem object, we define it here and
+# then call it in both FileSystem and MockFileSystem classes.
+def _remove_contents(fs, dirname, sleep=time.sleep):
+    # We try multiple times, because on Windows a process which is
+    # currently closing could still have a file open in the directory.
+    _log.info('Removing contents of %s', dirname)
+    errors = []
+
+    def onerror(func, path, exc_info):
+        errors.append(path)
+        _log.exception('Failed at %s %s: %r', func, path, exc_info)
+
+    attempts = 0
+    while attempts < 5:
+        del errors[:]
+
+        for name in fs.listdir(dirname):
+            fullname = fs.join(dirname, name)
+
+            isdir = True
+            try:
+                isdir = fs.isdir(fullname)
+            except os.error:
+                onerror(fs.isdir, fullname, sys.exc_info())
+                continue
+
+            if isdir:
+                try:
+                    _log.debug('Removing directory %s', fullname)
+                    fs.rmtree(fullname, ignore_errors=False, onerror=onerror)
+                except os.error:
+                    onerror(fs.rmtree, fullname, sys.exc_info())
+                    continue
+            else:
+                try:
+                    _log.debug('Removing file %s', fullname)
+                    fs.remove(fullname, retry=False)
+                except os.error:
+                    onerror(fs.remove, fullname, sys.exc_info())
+                    continue
+
+        if not errors:
+            break
+
+        _log.warning('Contents removal failed, retrying in 1 second.')
+        attempts += 1
+        sleep(1)
+
+    # Check the path is gone.
+    if not fs.listdir(dirname):
+        return True
+
+    _log.warning('Unable to remove %s', dirname)
+    for dirpath, dirnames, filenames in fs.walk(dirname, onerror=onerror, topdown=False):
+        for fname in filenames:
+            _log.warning('File %s still in output dir.', fs.join(dirpath, fname))
+        for dname in dirnames:
+            _log.warning('Dir %s still in output dir.', fs.join(dirpath, dname))
+
+    return False
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_mock.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
index e4d94a1..fd570c9 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
@@ -33,8 +33,12 @@
 import StringIO
 import unittest
 
+from webkitpy.common.system.filesystem import _remove_contents
+
 
 class MockFileSystem(object):
+    # pylint: disable=unused-argument
+
     sep = '/'
     pardir = '..'
 
@@ -386,13 +390,13 @@
 
         return dot_dot + rel_path
 
-    def remove(self, path):
+    def remove(self, path, retry=True):
         if self.files[path] is None:
             self._raise_not_found(path)
         self.files[path] = None
         self.written_files[path] = None
 
-    def rmtree(self, path_to_remove):
+    def rmtree(self, path_to_remove, ignore_errors=True, onerror=None):
         path_to_remove = self.normpath(path_to_remove)
 
         for file_path in self.files:
@@ -406,6 +410,9 @@
 
         self.dirs = {d for d in self.dirs if not should_remove(d)}
 
+    def remove_contents(self, dirname):
+        return _remove_contents(self, dirname, sleep=lambda *args, **kw: None)
+
     def copytree(self, source, destination):
         source = self.normpath(source)
         destination = self.normpath(destination)
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
index 9318ca031..858c47c8 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
@@ -119,6 +119,15 @@
         self.assertFalse(self.fs.exists('foodir'))
         self.assertFalse(self.fs.exists(self.fs.join('foodir', 'baz')))
 
+    def test_remove_contents(self):
+        self.fs.chdir(self.generic_test_dir)
+
+        self.assertTrue(self.fs.exists('foodir'))
+        self.assertTrue(self.fs.exists(self.fs.join('foodir', 'baz')))
+        self.assertTrue(self.fs.remove_contents('foodir'))
+        self.assertTrue(self.fs.exists('foodir'))
+        self.assertFalse(self.fs.exists(self.fs.join('foodir', 'baz')))
+
     def test_copytree(self):
         self.fs.chdir(self.generic_test_dir)
         self.fs.copytree('foodir/', 'bardir/')