diff --git a/.gitignore b/.gitignore
index 2bdbac3..09560d0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -271,6 +271,7 @@
 /tools/metrics/histograms/histograms.before.pretty-print.xml
 /tools/metrics/histograms/enums.before.pretty-print.xml
 /tools/page_cycler/acid3
+/tools/skia_goldctl/
 /tools/swarming_client
 /tools/tryserver
 /tools/win/link_limiter/build
diff --git a/BUILD.gn b/BUILD.gn
index 5d692b4b..59d65aa 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -115,7 +115,6 @@
       "//ppapi/examples/2d",
       "//ppapi/examples/audio",
       "//ppapi/examples/audio_input",
-      "//ppapi/examples/compositor",
       "//ppapi/examples/crxfs",
       "//ppapi/examples/enumerate_devices",
       "//ppapi/examples/file_chooser",
diff --git a/DEPS b/DEPS
index 50fceddc4b..7f01766 100644
--- a/DEPS
+++ b/DEPS
@@ -121,11 +121,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '1400f599f5ddbd2abfb45b412637edbc482bebdf',
+  'skia_revision': '14e9177492e631a5e55132a0580ece0f45d48e79',
   # 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': '62870e413e844b75279f358f5da0fd8c7e83ddfb',
+  'v8_revision': '6ce46c2e95c6fecbbc812ff58181791d3f03877e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -133,7 +133,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '80766cfa26566de48142708e1d158ecbf4129cc2',
+  'angle_revision': 'e4a52cb53f877637ffb17677396d02d9f8a907a4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -145,7 +145,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '084a842135de0294b487584ffd2430633ee3214f',
+  'pdfium_revision': 'dbb285793fc293ff826da2459078b5ad33acd32e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -181,7 +181,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '775ce3b01f6bdc38937284a9cccc9dadd1e3fa10',
+  'catapult_revision': '0cc582388fa2041558980460a1450e3fa02b1819',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -197,7 +197,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'feed_revision': '4da2d86623e7cb85126f5ef2c6894a76941e8c69',
+  'feed_revision': '45d963c29e87258796293589f724ac08dab37cd0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling android_sdk_build-tools_version
   # and whatever else without interference from each other.
@@ -679,7 +679,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '3c5925f8a155af9b280670a95caeac0c35a9ad54',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '6e6bd76fca9aab6c7d02410cb9ae0ffe74701021',
       'condition': 'checkout_linux',
   },
 
@@ -1240,7 +1240,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@5d0d8017656d90ff48950081cca638304c567b90',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a9c8cbac5faa28d2d60ada190db01f815b0d171d',
     'condition': 'checkout_src_internal',
   },
 
@@ -2625,18 +2625,6 @@
     ],
   },
   {
-    'name': 'vr_assets',
-    'pattern': '.',
-    'condition': 'checkout_src_internal and checkout_android',
-    'action': ['python',
-               'src/third_party/depot_tools/download_from_google_storage.py',
-               '--bucket', 'chrome-vr-assets',
-               '--recursive',
-               '--directory',
-               'src/chrome/browser/resources/vr/assets/google_chrome',
-    ],
-  },
-  {
     'name': 'vr_controller_test_api',
     'pattern': '\\.sha1',
     'condition': 'checkout_android',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 0fe8608..89967c3c 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -566,15 +566,6 @@
       ),
     ),
     (
-      r'/\barraysize\b',
-      (
-          "arraysize is deprecated, please use base::size(array) instead ",
-          "(https://crbug.com/837308). ",
-      ),
-      False,
-      (),
-    ),
-    (
       r'std::random_shuffle',
       (
         'std::random_shuffle is deprecated in C++14, and removed in C++17. Use',
diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS
index ca1bb07f..65957ae 100644
--- a/android_webview/browser/DEPS
+++ b/android_webview/browser/DEPS
@@ -58,6 +58,7 @@
   # network service related unit tests
   "+services/network/test",
   "+mojo/core/embedder/embedder.h",
+  "+mojo/public/cpp/system",
 
   "+storage/browser/quota",
   "+storage/common/quota",
diff --git a/android_webview/browser/net_network_service/android_stream_reader_url_loader.cc b/android_webview/browser/net_network_service/android_stream_reader_url_loader.cc
index 88f5765..5e36ed0ce 100644
--- a/android_webview/browser/net_network_service/android_stream_reader_url_loader.cc
+++ b/android_webview/browser/net_network_service/android_stream_reader_url_loader.cc
@@ -86,11 +86,14 @@
       client_(std::move(client)),
       traffic_annotation_(traffic_annotation),
       response_delegate_(std::move(response_delegate)),
+      writable_handle_watcher_(FROM_HERE,
+                               mojo::SimpleWatcher::ArmingPolicy::MANUAL,
+                               base::SequencedTaskRunnerHandle::Get()),
       weak_factory_(this) {
   // If there is a client error, clean up the request.
-  client_.set_connection_error_handler(base::BindOnce(
-      &AndroidStreamReaderURLLoader::OnRequestError, weak_factory_.GetWeakPtr(),
-      network::URLLoaderCompletionStatus(net::ERR_ABORTED)));
+  client_.set_connection_error_handler(
+      base::BindOnce(&AndroidStreamReaderURLLoader::RequestComplete,
+                     weak_factory_.GetWeakPtr(), net::ERR_ABORTED));
 }
 
 AndroidStreamReaderURLLoader::~AndroidStreamReaderURLLoader() {}
@@ -107,8 +110,7 @@
 
 void AndroidStreamReaderURLLoader::Start() {
   if (!ParseRange(resource_request_.headers)) {
-    OnRequestError(network::URLLoaderCompletionStatus(
-        net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
+    RequestComplete(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
     return;
   }
 
@@ -152,7 +154,7 @@
     // we've got the expected content size here
     HeadersComplete(net::HTTP_OK, kHTTPOkText);
   } else {
-    OnRequestError(network::URLLoaderCompletionStatus(net::ERR_FAILED));
+    RequestComplete(net::ERR_FAILED);
   }
 }
 
@@ -183,7 +185,7 @@
   client_->OnReceiveResponse(head);
 
   if (status_code != net::HTTP_OK) {
-    OnRequestError(network::URLLoaderCompletionStatus(net::ERR_FAILED));
+    RequestComplete(net::ERR_FAILED);
     return;
   }
 
@@ -191,29 +193,96 @@
 }
 
 void AndroidStreamReaderURLLoader::SendBody() {
-  // TODO(timvolodine): implement IOBuffer stream -> mojo pipes here
-  mojo::ScopedDataPipeConsumerHandle body_to_send;
-  client_->OnStartLoadingResponseBody(std::move(body_to_send));
-  RequestComplete(network::URLLoaderCompletionStatus(net::OK));
-}
-
-void AndroidStreamReaderURLLoader::RequestComplete(
-    const network::URLLoaderCompletionStatus& status) {
-  if (status.error_code != net::OK) {
-    OnRequestError(status);
+  mojo::ScopedDataPipeConsumerHandle consumer_handle;
+  if (CreateDataPipe(nullptr /*options*/, &producer_handle_,
+                     &consumer_handle) != MOJO_RESULT_OK) {
+    RequestComplete(net::ERR_FAILED);
     return;
   }
-  client_->OnComplete(status);
+  writable_handle_watcher_.Watch(
+      producer_handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+      base::BindRepeating(&AndroidStreamReaderURLLoader::OnDataPipeWritable,
+                          base::Unretained(this)));
+  client_->OnStartLoadingResponseBody(std::move(consumer_handle));
 
-  // Manages its own lifetime
-  delete this;
+  ReadMore();
 }
 
-void AndroidStreamReaderURLLoader::OnRequestError(
-    const network::URLLoaderCompletionStatus& status) {
-  client_->OnComplete(status);
+void AndroidStreamReaderURLLoader::ReadMore() {
+  DCHECK(!pending_buffer_.get());
+  uint32_t num_bytes;
+  MojoResult mojo_result = network::NetToMojoPendingBuffer::BeginWrite(
+      &producer_handle_, &pending_buffer_, &num_bytes);
+  if (mojo_result == MOJO_RESULT_SHOULD_WAIT) {
+    // The pipe is full. We need to wait for it to have more space.
+    writable_handle_watcher_.ArmOrNotify();
+    return;
+  } else if (mojo_result == MOJO_RESULT_FAILED_PRECONDITION) {
+    // The data pipe consumer handle has been closed.
+    RequestComplete(net::ERR_ABORTED);
+    return;
+  } else if (mojo_result != MOJO_RESULT_OK) {
+    // The body stream is in a bad state. Bail out.
+    RequestComplete(net::ERR_UNEXPECTED);
+    return;
+  }
+  scoped_refptr<net::IOBuffer> buffer(
+      new network::NetToMojoIOBuffer(pending_buffer_.get()));
 
-  // Manages it's own lifetime
+  // TODO(timvolodine): consider using a sequenced task runner.
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(
+          &InputStreamReaderWrapper::ReadRawData, input_stream_reader_wrapper_,
+          base::RetainedRef(buffer.get()), base::checked_cast<int>(num_bytes)),
+      base::BindOnce(&AndroidStreamReaderURLLoader::DidRead,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void AndroidStreamReaderURLLoader::DidRead(int result) {
+  DCHECK(pending_buffer_);
+  if (result < 0) {
+    // error case
+    RequestComplete(result);
+    return;
+  }
+  if (result == 0) {
+    // eof, read completed
+    pending_buffer_->Complete(0);
+    RequestComplete(net::OK);
+    return;
+  }
+  producer_handle_ = pending_buffer_->Complete(result);
+  pending_buffer_ = nullptr;
+
+  // TODO(timvolodine): consider using a sequenced task runner.
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&AndroidStreamReaderURLLoader::ReadMore,
+                                weak_factory_.GetWeakPtr()));
+}
+
+void AndroidStreamReaderURLLoader::OnDataPipeWritable(MojoResult result) {
+  if (result == MOJO_RESULT_FAILED_PRECONDITION) {
+    RequestComplete(net::ERR_ABORTED);
+    return;
+  }
+  DCHECK_EQ(result, MOJO_RESULT_OK) << result;
+
+  ReadMore();
+}
+
+void AndroidStreamReaderURLLoader::RequestComplete(int status_code) {
+  client_->OnComplete(network::URLLoaderCompletionStatus(status_code));
+  CleanUp();
+}
+
+void AndroidStreamReaderURLLoader::CleanUp() {
+  // Resets the watchers and pipes, so that we will never be called back.
+  writable_handle_watcher_.Cancel();
+  pending_buffer_ = nullptr;
+  producer_handle_.reset();
+
+  // Manages its own lifetime
   delete this;
 }
 
diff --git a/android_webview/browser/net_network_service/android_stream_reader_url_loader.h b/android_webview/browser/net_network_service/android_stream_reader_url_loader.h
index 053bdb2..1498dc65 100644
--- a/android_webview/browser/net_network_service/android_stream_reader_url_loader.h
+++ b/android_webview/browser/net_network_service/android_stream_reader_url_loader.h
@@ -6,7 +6,9 @@
 #define ANDROID_WEBVIEW_BROWSER_NET_NETWORK_SERVICE_ANDROID_STREAM_READER_URL_LOADER_H_
 
 #include "android_webview/browser/net/aw_web_resource_response.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
 #include "net/http/http_byte_range.h"
+#include "services/network/public/cpp/net_adapters.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 
 namespace android_webview {
@@ -53,9 +55,13 @@
       std::unique_ptr<android_webview::InputStream> input_stream);
   void OnReaderSeekCompleted(int result);
   void HeadersComplete(int status_code, const std::string& status_text);
-  void RequestComplete(const network::URLLoaderCompletionStatus& status);
+  void RequestComplete(int status_code);
   void SendBody();
-  void OnRequestError(const network::URLLoaderCompletionStatus& status);
+
+  void OnDataPipeWritable(MojoResult result);
+  void CleanUp();
+  void DidRead(int result);
+  void ReadMore();
 
   net::HttpByteRange byte_range_;
   network::ResourceRequest resource_request_;
@@ -64,6 +70,10 @@
   std::unique_ptr<ResponseDelegate> response_delegate_;
   scoped_refptr<InputStreamReaderWrapper> input_stream_reader_wrapper_;
 
+  mojo::ScopedDataPipeProducerHandle producer_handle_;
+  scoped_refptr<network::NetToMojoPendingBuffer> pending_buffer_;
+  mojo::SimpleWatcher writable_handle_watcher_;
+
   base::WeakPtrFactory<AndroidStreamReaderURLLoader> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AndroidStreamReaderURLLoader);
diff --git a/android_webview/browser/net_network_service/android_stream_reader_url_loader_unittest.cc b/android_webview/browser/net_network_service/android_stream_reader_url_loader_unittest.cc
index bb7826e..e55f223 100644
--- a/android_webview/browser/net_network_service/android_stream_reader_url_loader_unittest.cc
+++ b/android_webview/browser/net_network_service/android_stream_reader_url_loader_unittest.cc
@@ -5,6 +5,7 @@
 #include "android_webview/browser/net_network_service/android_stream_reader_url_loader.h"
 
 #include "android_webview/browser/input_stream.h"
+#include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "content/public/common/resource_type.h"
@@ -17,19 +18,47 @@
 
 namespace android_webview {
 
-// Always succeeds
+namespace {
+
+const GURL kTestURL = GURL("https://www.example.com/");
+
+}  // namespace
+
+// Always succeeds, depending on constructor uses the given string as contents
+// for the input stream and puts it in the IOBuffer |nb_reads| times.
 class FakeInputStream : public InputStream {
  public:
-  FakeInputStream() {}
+  explicit FakeInputStream() : contents_(""), nb_reads_(0) {}
+  explicit FakeInputStream(std::string contents)
+      : contents_(contents), nb_reads_(1) {}
+  explicit FakeInputStream(std::string contents, int nb_reads)
+      : contents_(contents), nb_reads_(nb_reads) {}
   ~FakeInputStream() override {}
+
   bool BytesAvailable(int* bytes_available) const override { return true; }
+
   bool Skip(int64_t n, int64_t* bytes_skipped) override {
     *bytes_skipped = n;
     return true;
   }
-  bool Read(net::IOBuffer* dest, int length, int* bytes_read) override {
+
+  bool Read(net::IOBuffer* buf, int length, int* bytes_read) override {
+    if (nb_reads_ <= 0) {
+      *bytes_read = 0;
+      return true;
+    }
+    CHECK_GE(length, static_cast<int>(contents_.length()));
+    memcpy(buf->data(), contents_.c_str(), contents_.length());
+    *bytes_read = contents_.length();
+    buffer_written_ = true;
+    nb_reads_--;
     return true;
   }
+
+ private:
+  std::string contents_;
+  bool buffer_written_;
+  int nb_reads_;
 };
 
 // Stream that always fails
@@ -89,6 +118,28 @@
         std::make_unique<TestResponseDelegate>(std::move(input_stream)));
   }
 
+  // Extracts the body data that is present in the consumer pipe
+  // and returns it as a string.
+  std::string ReadAvailableBody(network::TestURLLoaderClient* client) {
+    MojoHandle consumer = client->response_body().value();
+
+    uint32_t num_bytes = 0;
+    MojoReadDataOptions options;
+    options.struct_size = sizeof(options);
+    options.flags = MOJO_READ_DATA_FLAG_QUERY;
+    MojoResult result = MojoReadData(consumer, &options, nullptr, &num_bytes);
+    CHECK_EQ(MOJO_RESULT_OK, result);
+    if (num_bytes == 0)
+      return std::string();
+
+    std::vector<char> buffer(num_bytes);
+    result = MojoReadData(consumer, nullptr, buffer.data(), &num_bytes);
+    CHECK_EQ(MOJO_RESULT_OK, result);
+    CHECK_EQ(num_bytes, buffer.size());
+
+    return std::string(buffer.data(), buffer.size());
+  }
+
   base::test::ScopedFeatureList feature_list_;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 
@@ -97,8 +148,7 @@
 
 // crbug.com/919929
 TEST_F(AndroidStreamReaderURLLoaderTest, DISABLED_ReadFakeStream) {
-  network::ResourceRequest request =
-      CreateRequest(GURL("https://www.example.com/"));
+  network::ResourceRequest request = CreateRequest(kTestURL);
   std::unique_ptr<network::TestURLLoaderClient> client =
       std::make_unique<network::TestURLLoaderClient>();
   AndroidStreamReaderURLLoader* loader =
@@ -111,8 +161,7 @@
 }
 
 TEST_F(AndroidStreamReaderURLLoaderTest, ReadFailingStream) {
-  network::ResourceRequest request =
-      CreateRequest(GURL("https://www.example.com/"));
+  network::ResourceRequest request = CreateRequest(kTestURL);
   std::unique_ptr<network::TestURLLoaderClient> client =
       std::make_unique<network::TestURLLoaderClient>();
   AndroidStreamReaderURLLoader* loader = CreateLoader(
@@ -124,8 +173,7 @@
 
 // crbug.com/919929
 TEST_F(AndroidStreamReaderURLLoaderTest, DISABLED_ValidRangeRequest) {
-  network::ResourceRequest request =
-      CreateRequest(GURL("https://www.example.com/"));
+  network::ResourceRequest request = CreateRequest(kTestURL);
   request.headers.SetHeader(net::HttpRequestHeaders::kRange, "bytes=10-200");
 
   std::unique_ptr<network::TestURLLoaderClient> client =
@@ -140,8 +188,7 @@
 }
 
 TEST_F(AndroidStreamReaderURLLoaderTest, InvalidRangeRequest) {
-  network::ResourceRequest request =
-      CreateRequest(GURL("https://www.example.com/"));
+  network::ResourceRequest request = CreateRequest(kTestURL);
   request.headers.SetHeader(net::HttpRequestHeaders::kRange, "bytes=10-0");
 
   std::unique_ptr<network::TestURLLoaderClient> client =
@@ -155,8 +202,7 @@
 }
 
 TEST_F(AndroidStreamReaderURLLoaderTest, NullInputStream) {
-  network::ResourceRequest request =
-      CreateRequest(GURL("https://www.example.com/"));
+  network::ResourceRequest request = CreateRequest(kTestURL);
 
   std::unique_ptr<network::TestURLLoaderClient> client =
       std::make_unique<network::TestURLLoaderClient>();
@@ -169,4 +215,60 @@
             client->response_head().headers->GetStatusLine());
 }
 
+TEST_F(AndroidStreamReaderURLLoaderTest, ReadFakeStreamWithBody) {
+  network::ResourceRequest request = CreateRequest(kTestURL);
+
+  std::string expected_body("test");
+  std::unique_ptr<network::TestURLLoaderClient> client =
+      std::make_unique<network::TestURLLoaderClient>();
+  AndroidStreamReaderURLLoader* loader = CreateLoader(
+      request, client.get(), std::make_unique<FakeInputStream>(expected_body));
+  loader->Start();
+  client->RunUntilComplete();
+  EXPECT_EQ(net::OK, client->completion_status().error_code);
+  EXPECT_EQ("HTTP/1.1 200 OK",
+            client->response_head().headers->GetStatusLine());
+  std::string body = ReadAvailableBody(client.get());
+  EXPECT_EQ(expected_body, body);
+}
+
+TEST_F(AndroidStreamReaderURLLoaderTest, ReadFakeStreamWithBodyMultipleReads) {
+  network::ResourceRequest request = CreateRequest(kTestURL);
+
+  std::string expected_body("test");
+  std::unique_ptr<network::TestURLLoaderClient> client =
+      std::make_unique<network::TestURLLoaderClient>();
+  AndroidStreamReaderURLLoader* loader =
+      CreateLoader(request, client.get(),
+                   std::make_unique<FakeInputStream>(expected_body, 2));
+  loader->Start();
+  client->RunUntilComplete();
+  EXPECT_EQ(net::OK, client->completion_status().error_code);
+  EXPECT_EQ("HTTP/1.1 200 OK",
+            client->response_head().headers->GetStatusLine());
+  std::string body = ReadAvailableBody(client.get());
+  EXPECT_EQ(expected_body + expected_body, body);
+}
+
+TEST_F(AndroidStreamReaderURLLoaderTest,
+       ReadFakeStreamCloseConsumerPipeDuringResponse) {
+  network::ResourceRequest request = CreateRequest(kTestURL);
+
+  std::string expected_body("test");
+  std::unique_ptr<network::TestURLLoaderClient> client =
+      std::make_unique<network::TestURLLoaderClient>();
+  AndroidStreamReaderURLLoader* loader = CreateLoader(
+      request, client.get(), std::make_unique<FakeInputStream>(expected_body));
+  loader->Start();
+  client->RunUntilResponseBodyArrived();
+  EXPECT_TRUE(client->has_received_response());
+  EXPECT_FALSE(client->has_received_completion());
+  EXPECT_EQ("HTTP/1.1 200 OK",
+            client->response_head().headers->GetStatusLine());
+  auto response_body = client->response_body_release();
+  response_body.reset();
+  client->RunUntilComplete();
+  EXPECT_EQ(net::ERR_ABORTED, client->completion_status().error_code);
+}
+
 }  // namespace android_webview
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
index 7b871f7..b8927082 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/SafeBrowsingTest.java
@@ -143,6 +143,11 @@
         private static final long CHECK_DELTA_US = 10;
 
         @Override
+        public String getSafetyNetId() {
+            return "";
+        }
+
+        @Override
         public boolean init(Observer result) {
             return init(result, false);
         }
diff --git a/ash/frame/default_frame_header_unittest.cc b/ash/frame/default_frame_header_unittest.cc
index 63995577..ef9b652 100644
--- a/ash/frame/default_frame_header_unittest.cc
+++ b/ash/frame/default_frame_header_unittest.cc
@@ -9,9 +9,11 @@
 #include "ash/public/cpp/caption_buttons/frame_back_button.h"
 #include "ash/public/cpp/caption_buttons/frame_caption_button_container_view.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/public/cpp/window_properties.h"
 #include "ash/test/ash_test_base.h"
 #include "base/i18n/rtl.h"
 #include "base/test/icu_test_util.h"
+#include "ui/aura/window.h"
 #include "ui/gfx/animation/animation_test_api.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/views/test/test_views.h"
@@ -92,7 +94,9 @@
   // Check frame color is sensitive to mode.
   SkColor active = SkColorSetRGB(70, 70, 70);
   SkColor inactive = SkColorSetRGB(200, 200, 200);
-  frame_header.SetFrameColors(active, inactive);
+  widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, active);
+  widget->GetNativeWindow()->SetProperty(kFrameInactiveColorKey, inactive);
+  frame_header.UpdateFrameColors();
   frame_header.mode_ = FrameHeader::MODE_ACTIVE;
   EXPECT_EQ(active, frame_header.GetCurrentFrameColor());
   frame_header.mode_ = FrameHeader::MODE_INACTIVE;
@@ -102,7 +106,8 @@
   // Update to the new value which has no blue, which should animate.
   frame_header.mode_ = FrameHeader::MODE_ACTIVE;
   SkColor new_active = SkColorSetRGB(70, 70, 0);
-  frame_header.SetFrameColors(new_active, SK_ColorBLACK);
+  widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, new_active);
+  frame_header.UpdateFrameColors();
 
   gfx::SlideAnimation* animation =
       frame_header.GetAnimationForActiveFrameColorForTest();
@@ -124,7 +129,8 @@
 
   // Now update to the new value which is full blue.
   SkColor new_new_active = SkColorSetRGB(70, 70, 255);
-  frame_header.SetFrameColors(new_new_active, SK_ColorBLACK);
+  widget->GetNativeWindow()->SetProperty(kFrameActiveColorKey, new_new_active);
+  frame_header.UpdateFrameColors();
 
   now = base::TimeTicks::Now();
   test_api.SetStartTime(now);
diff --git a/ash/frame/header_view.cc b/ash/frame/header_view.cc
index 10eaf90..1a60975 100644
--- a/ash/frame/header_view.cc
+++ b/ash/frame/header_view.cc
@@ -72,8 +72,7 @@
 
   UpdateBackButton();
 
-  frame_header_->SetFrameColors(window->GetProperty(kFrameActiveColorKey),
-                                window->GetProperty(kFrameInactiveColorKey));
+  frame_header_->UpdateFrameColors();
   window_observer_.Add(window);
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
 }
@@ -197,8 +196,7 @@
         window->GetProperty(aura::client::kAvatarIconKey);
     SetAvatarIcon(avatar_icon ? *avatar_icon : gfx::ImageSkia());
   } else if (key == kFrameActiveColorKey || key == kFrameInactiveColorKey) {
-    frame_header_->SetFrameColors(window->GetProperty(kFrameActiveColorKey),
-                                  window->GetProperty(kFrameInactiveColorKey));
+    frame_header_->UpdateFrameColors();
   } else if (key == aura::client::kShowStateKey) {
     frame_header_->OnShowStateChanged(
         window->GetProperty(aura::client::kShowStateKey));
diff --git a/ash/keyboard/ash_keyboard_controller.cc b/ash/keyboard/ash_keyboard_controller.cc
index c694fb64..244ba5c 100644
--- a/ash/keyboard/ash_keyboard_controller.cc
+++ b/ash/keyboard/ash_keyboard_controller.cc
@@ -47,11 +47,9 @@
   if (!keyboard_controller_->IsKeyboardEnableRequested())
     return;
 
-  // De-activate the keyboard, as some callers expect the keyboard to be
-  // reloaded. TODO(https://crbug.com/731537): Add a separate function for
+  // KeyboardController::EnableKeyboard will reload the keyboard if it's already
+  // enabled. TODO(https://crbug.com/731537): Add a separate function for
   // reloading the keyboard.
-  DeactivateKeyboard();
-
   keyboard_controller_->EnableKeyboard(
       keyboard_ui_factory_ ? keyboard_ui_factory_->CreateKeyboardUI()
                            : std::make_unique<AshKeyboardUI>(this),
@@ -60,7 +58,6 @@
 }
 
 void AshKeyboardController::DisableKeyboard() {
-  DeactivateKeyboard();
   keyboard_controller_->DisableKeyboard();
 }
 
@@ -274,6 +271,11 @@
   keyboard_controller_->DeactivateKeyboard();
 }
 
+void AshKeyboardController::OnRootWindowClosing(aura::Window* root_window) {
+  if (keyboard_controller_->GetRootWindow() == root_window)
+    DeactivateKeyboard();
+}
+
 void AshKeyboardController::UpdateEnableFlag(bool was_enabled) {
   bool is_enabled = keyboard_controller_->IsKeyboardEnableRequested();
   if (is_enabled && !was_enabled) {
diff --git a/ash/keyboard/ash_keyboard_controller.h b/ash/keyboard/ash_keyboard_controller.h
index 3336ef0b..39e6da9 100644
--- a/ash/keyboard/ash_keyboard_controller.h
+++ b/ash/keyboard/ash_keyboard_controller.h
@@ -110,6 +110,11 @@
   // Deactivates the keyboard controller.
   void DeactivateKeyboard();
 
+  // Called whenever a root window is closing.
+  // If the root window contains the virtual keyboard window, deactivates
+  // the keyboard so that its window doesn't get destroyed as well.
+  void OnRootWindowClosing(aura::Window* root_window);
+
  private:
   // Called whenever the enable flags may have changed the enabled state from
   // |was_enabled|. If changed, enables or disables the keyboard.
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 30fc788..c3b34725 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -42,7 +42,6 @@
 #include "components/user_manager/user_type.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/display/display.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/managed_display_info.h"
@@ -389,7 +388,7 @@
   system_info_ = new views::View();
   auto* system_info_layout =
       system_info_->SetLayoutManager(std::make_unique<views::BoxLayout>(
-          views::BoxLayout::kVertical, gfx::Insets(6, 8)));
+          views::BoxLayout::kVertical, gfx::Insets(5, 8)));
   system_info_layout->set_cross_axis_alignment(
       views::BoxLayout::CROSS_AXIS_ALIGNMENT_END);
   system_info_->SetVisible(false);
@@ -880,8 +879,6 @@
   if (system_info_->child_count() == 0) {
     for (int i = 0; i < 3; ++i)
       system_info_->AddChildView(create_info_label());
-    if (::features::IsSingleProcessMash())
-      system_info_->AddChildView(create_info_label());
   }
 
   if (show)
@@ -896,8 +893,6 @@
   update_label(0, os_version_label_text);
   update_label(1, enterprise_info_text);
   update_label(2, bluetooth_name);
-  if (::features::IsSingleProcessMash())
-    update_label(3, "SingleProcessMash enabled");
 
   LayoutTopHeader();
 }
@@ -1524,6 +1519,8 @@
     supervised_user_deprecation_bubble_->SetAnchorView(
         CurrentBigUserView()->auth_user()->password_view());
     supervised_user_deprecation_bubble_->Show();
+  } else if (supervised_user_deprecation_bubble_->IsVisible()) {
+    supervised_user_deprecation_bubble_->Hide();
   }
 
   // The new auth user might have different last used detachable base - make
diff --git a/ash/login/ui/login_keyboard_test_base.cc b/ash/login/ui/login_keyboard_test_base.cc
index fed9442..2acd764 100644
--- a/ash/login/ui/login_keyboard_test_base.cc
+++ b/ash/login/ui/login_keyboard_test_base.cc
@@ -33,12 +33,6 @@
   Shell::Get()->ash_keyboard_controller()->ActivateKeyboard();
 }
 
-void LoginKeyboardTestBase::TearDown() {
-  Shell::Get()->ash_keyboard_controller()->DeactivateKeyboard();
-
-  LoginTestBase::TearDown();
-}
-
 void LoginKeyboardTestBase::ShowKeyboard() {
   auto* keyboard_controller = keyboard::KeyboardController::Get();
   keyboard_controller->ShowKeyboard(false);
diff --git a/ash/login/ui/login_keyboard_test_base.h b/ash/login/ui/login_keyboard_test_base.h
index 75a31e1..94374e6 100644
--- a/ash/login/ui/login_keyboard_test_base.h
+++ b/ash/login/ui/login_keyboard_test_base.h
@@ -34,7 +34,6 @@
 
   // AshTestBase:
   void SetUp() override;
-  void TearDown() override;
 
  private:
   std::vector<mojom::LoginUserInfoPtr> users_;
diff --git a/ash/public/cpp/default_frame_header.cc b/ash/public/cpp/default_frame_header.cc
index 3260d67c..c97ffb39 100644
--- a/ash/public/cpp/default_frame_header.cc
+++ b/ash/public/cpp/default_frame_header.cc
@@ -7,6 +7,7 @@
 #include "ash/public/cpp/ash_constants.h"
 #include "ash/public/cpp/caption_buttons/caption_button_model.h"
 #include "ash/public/cpp/caption_buttons/frame_caption_button_container_view.h"
+#include "ash/public/cpp/window_properties.h"
 #include "base/logging.h"  // DCHECK
 #include "third_party/skia/include/core/SkPath.h"
 #include "ui/gfx/canvas.h"
@@ -103,6 +104,28 @@
   SchedulePaintForTitle();
 }
 
+void DefaultFrameHeader::UpdateFrameColors() {
+  const SkColor active_frame_color =
+      target_widget()->GetNativeWindow()->GetProperty(kFrameActiveColorKey);
+  const SkColor inactive_frame_color =
+      target_widget()->GetNativeWindow()->GetProperty(kFrameInactiveColorKey);
+
+  bool updated = false;
+  if (active_frame_color_.target_color() != active_frame_color) {
+    active_frame_color_.SetTargetColor(active_frame_color);
+    updated = true;
+  }
+  if (inactive_frame_color_.target_color() != inactive_frame_color) {
+    inactive_frame_color_.SetTargetColor(inactive_frame_color);
+    updated = true;
+  }
+
+  if (updated) {
+    UpdateCaptionButtonColors();
+    view()->SchedulePaint();
+  }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // DefaultFrameHeader, protected:
 
@@ -135,24 +158,6 @@
   PaintTitleBar(canvas);
 }
 
-void DefaultFrameHeader::DoSetFrameColors(SkColor active_frame_color,
-                                          SkColor inactive_frame_color) {
-  bool updated = false;
-  if (active_frame_color_.target_color() != active_frame_color) {
-    active_frame_color_.SetTargetColor(active_frame_color);
-    updated = true;
-  }
-  if (inactive_frame_color_.target_color() != inactive_frame_color) {
-    inactive_frame_color_.SetTargetColor(inactive_frame_color);
-    updated = true;
-  }
-
-  if (updated) {
-    UpdateCaptionButtonColors();
-    view()->SchedulePaint();
-  }
-}
-
 views::CaptionButtonLayoutSize DefaultFrameHeader::GetButtonLayoutSize() const {
   return views::CaptionButtonLayoutSize::kNonBrowserCaption;
 }
diff --git a/ash/public/cpp/default_frame_header.h b/ash/public/cpp/default_frame_header.h
index 176212a..3472e7f 100644
--- a/ash/public/cpp/default_frame_header.h
+++ b/ash/public/cpp/default_frame_header.h
@@ -35,11 +35,12 @@
 
   void SetWidthInPixels(int width_in_pixels);
 
+  // FrameHeader:
+  void UpdateFrameColors() override;
+
  protected:
   // FrameHeader:
   void DoPaintHeader(gfx::Canvas* canvas) override;
-  void DoSetFrameColors(SkColor active_frame_color,
-                        SkColor inactive_frame_color) override;
   views::CaptionButtonLayoutSize GetButtonLayoutSize() const override;
   SkColor GetTitleColor() const override;
   SkColor GetCurrentFrameColor() const override;
diff --git a/ash/public/cpp/frame_header.cc b/ash/public/cpp/frame_header.cc
index 89e6e8c..a21f6589 100644
--- a/ash/public/cpp/frame_header.cc
+++ b/ash/public/cpp/frame_header.cc
@@ -177,11 +177,6 @@
   return back_button_;
 }
 
-void FrameHeader::SetFrameColors(SkColor active_frame_color,
-                                 SkColor inactive_frame_color) {
-  DoSetFrameColors(active_frame_color, inactive_frame_color);
-}
-
 void FrameHeader::SetFrameTextOverride(
     const base::string16& frame_text_override) {
   frame_text_override_ = frame_text_override;
diff --git a/ash/public/cpp/frame_header.h b/ash/public/cpp/frame_header.h
index 343aff8..c113e26 100644
--- a/ash/public/cpp/frame_header.h
+++ b/ash/public/cpp/frame_header.h
@@ -66,9 +66,8 @@
   void SetBackButton(views::FrameCaptionButton* view);
   views::FrameCaptionButton* GetBackButton() const;
 
-  // Sets the active and inactive frame colors. Note the inactive frame color
-  // will have some transparency added when the frame is drawn.
-  void SetFrameColors(SkColor active_frame_color, SkColor inactive_frame_color);
+  // Updates the frame header painting to reflect a change in frame colors.
+  virtual void UpdateFrameColors() = 0;
 
   // Sets text to display in place of the window's title. This will be shown
   // regardless of what WidgetDelegate::ShouldShowWindowTitle() returns.
@@ -113,11 +112,6 @@
   }
 
   virtual void DoPaintHeader(gfx::Canvas* canvas) = 0;
-  // Updates the frame colors. The parameters may or may not be ignored.
-  // TODO(estade): remove these parameters and instead always set them via Aura
-  // window properties, as is done with CustomFrameHeader.
-  virtual void DoSetFrameColors(SkColor active_frame_color,
-                                SkColor inactive_frame_color) = 0;
   virtual views::CaptionButtonLayoutSize GetButtonLayoutSize() const = 0;
   virtual SkColor GetTitleColor() const = 0;
   virtual SkColor GetCurrentFrameColor() const = 0;
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index e39ae83..faedb65 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -554,13 +554,9 @@
     return;
   did_close_child_windows_ = true;
 
-  // Deactivate keyboard container before closing child windows and shutting
+  // Notify the keyboard controller before closing child windows and shutting
   // down associated layout managers.
-  auto* ash_keyboard_controller = Shell::Get()->ash_keyboard_controller();
-  if (ash_keyboard_controller->keyboard_controller()->GetRootWindow() ==
-      GetRootWindow()) {
-    ash_keyboard_controller->DeactivateKeyboard();
-  }
+  Shell::Get()->ash_keyboard_controller()->OnRootWindowClosing(GetRootWindow());
 
   shelf_->ShutdownShelfWidget();
 
diff --git a/ash/wm/always_on_top_controller_unittest.cc b/ash/wm/always_on_top_controller_unittest.cc
index 3af781c..5832032 100644
--- a/ash/wm/always_on_top_controller_unittest.cc
+++ b/ash/wm/always_on_top_controller_unittest.cc
@@ -63,13 +63,9 @@
   // Install test layout manager.
   TestLayoutManager* manager = new TestLayoutManager(always_on_top_container);
   RootWindowController* controller = Shell::GetPrimaryRootWindowController();
-  // Deactivates keyboard to unregister existing listeners.
-  Shell::Get()->ash_keyboard_controller()->DeactivateKeyboard();
   AlwaysOnTopController* always_on_top_controller =
       controller->always_on_top_controller();
   always_on_top_controller->SetLayoutManagerForTest(base::WrapUnique(manager));
-  // Activate keyboard. This triggers keyboard listeners to be registered.
-  Shell::Get()->ash_keyboard_controller()->ActivateKeyboard();
 
   // Show the keyboard.
   auto* keyboard_controller = keyboard::KeyboardController::Get();
diff --git a/ash/wm/lock_action_handler_layout_manager_unittest.cc b/ash/wm/lock_action_handler_layout_manager_unittest.cc
index 507a32c6..30723d7 100644
--- a/ash/wm/lock_action_handler_layout_manager_unittest.cc
+++ b/ash/wm/lock_action_handler_layout_manager_unittest.cc
@@ -98,7 +98,6 @@
   }
 
   void TearDown() override {
-    Shell::Get()->ash_keyboard_controller()->DeactivateKeyboard();
     lock_window_.reset();
     AshTestBase::TearDown();
     LockScreenActionBackgroundController::SetFactoryCallbackForTesting(nullptr);
diff --git a/ash/wm/system_modal_container_layout_manager_unittest.cc b/ash/wm/system_modal_container_layout_manager_unittest.cc
index 1e25b8d..8d468507 100644
--- a/ash/wm/system_modal_container_layout_manager_unittest.cc
+++ b/ash/wm/system_modal_container_layout_manager_unittest.cc
@@ -157,10 +157,6 @@
     Shell::Get()->ash_keyboard_controller()->ActivateKeyboard();
   }
 
-  void DeactivateKeyboard() {
-    Shell::Get()->ash_keyboard_controller()->DeactivateKeyboard();
-  }
-
   aura::Window* OpenToplevelTestWindow(bool modal) {
     views::Widget* widget = views::Widget::CreateWindowWithContext(
         new TestWindow(modal), CurrentContext());
@@ -715,7 +711,6 @@
   EXPECT_NE(modal_bounds.ToString(), modal_window->bounds().ToString());
   EXPECT_EQ(modal_size.ToString(), modal_window->bounds().size().ToString());
   EXPECT_EQ(modal_origin.x(), modal_window->bounds().x());
-  DeactivateKeyboard();
 }
 
 // Test that windows will not get cropped through the visible virtual keyboard -
@@ -750,7 +745,6 @@
   EXPECT_EQ(0, modal_window->bounds().y());
 
   ShowKeyboard(false);
-  DeactivateKeyboard();
 }
 
 // Test that windows will not get cropped through the visible virtual keyboard -
@@ -782,7 +776,6 @@
   EXPECT_EQ(0, modal_window->bounds().y());
 
   ShowKeyboard(false);
-  DeactivateKeyboard();
 }
 
 TEST_F(SystemModalContainerLayoutManagerTest, UpdateModalType) {
diff --git a/base/debug/stack_trace.cc b/base/debug/stack_trace.cc
index 3a9cbd5..a577bd22 100644
--- a/base/debug/stack_trace.cc
+++ b/base/debug/stack_trace.cc
@@ -10,7 +10,7 @@
 #include <sstream>
 
 #include "base/logging.h"
-#include "base/macros.h"
+#include "base/stl_util.h"
 
 #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
 
@@ -200,10 +200,10 @@
 }
 #endif  // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
 
-StackTrace::StackTrace() : StackTrace(arraysize(trace_)) {}
+StackTrace::StackTrace() : StackTrace(base::size(trace_)) {}
 
 StackTrace::StackTrace(const void* const* trace, size_t count) {
-  count = std::min(count, arraysize(trace_));
+  count = std::min(count, base::size(trace_));
   if (count)
     memcpy(trace_, trace, count * sizeof(trace_[0]));
   count_ = count;
diff --git a/base/debug/stack_trace_android.cc b/base/debug/stack_trace_android.cc
index 0190ecc..10435b1 100644
--- a/base/debug/stack_trace_android.cc
+++ b/base/debug/stack_trace_android.cc
@@ -12,6 +12,7 @@
 #include <ostream>
 
 #include "base/debug/proc_maps_linux.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_restrictions.h"
 
@@ -70,7 +71,7 @@
 }
 
 StackTrace::StackTrace(size_t count) {
-  count = std::min(arraysize(trace_), count);
+  count = std::min(base::size(trace_), count);
 
   StackCrawlState state(reinterpret_cast<uintptr_t*>(trace_), count);
   _Unwind_Backtrace(&TraceStackFrame, &state);
diff --git a/base/debug/stack_trace_fuchsia.cc b/base/debug/stack_trace_fuchsia.cc
index 124e344..d7d25954 100644
--- a/base/debug/stack_trace_fuchsia.cc
+++ b/base/debug/stack_trace_fuchsia.cc
@@ -19,8 +19,10 @@
 #include <array>
 #include <iomanip>
 #include <iostream>
+#include <type_traits>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 
 namespace base {
 namespace debug {
@@ -28,7 +30,7 @@
 namespace {
 
 const char kProcessNamePrefix[] = "app:";
-const size_t kProcessNamePrefixLen = arraysize(kProcessNamePrefix) - 1;
+const size_t kProcessNamePrefixLen = base::size(kProcessNamePrefix) - 1;
 
 struct BacktraceData {
   void** trace_array;
@@ -105,7 +107,7 @@
   // TODO(wez): Object names can only have up to ZX_MAX_NAME_LEN characters, so
   // if we keep hitting problems with truncation, find a way to plumb argv[0]
   // through to here instead, e.g. using CommandLine::GetProgramName().
-  char app_name[arraysize(SymbolMap::Entry::name)];
+  char app_name[std::extent<decltype(SymbolMap::Entry::name)>()];
   strcpy(app_name, kProcessNamePrefix);
   zx_status_t status = zx_object_get_property(
       process, ZX_PROP_NAME, app_name + kProcessNamePrefixLen,
diff --git a/base/debug/stack_trace_posix.cc b/base/debug/stack_trace_posix.cc
index 8e4d54c1..2ae6a5b 100644
--- a/base/debug/stack_trace_posix.cc
+++ b/base/debug/stack_trace_posix.cc
@@ -43,11 +43,11 @@
 #include "base/debug/debugger.h"
 #include "base/files/scoped_file.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/memory/free_deleter.h"
 #include "base/memory/singleton.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/posix/eintr_wrapper.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
@@ -400,7 +400,7 @@
   const int kRegisterPadding = 16;
 #endif
 
-  for (size_t i = 0; i < arraysize(registers); i++) {
+  for (size_t i = 0; i < base::size(registers); i++) {
     PrintToStderr(registers[i].label);
     internal::itoa_r(registers[i].value, buf, sizeof(buf),
                      16, kRegisterPadding);
@@ -813,11 +813,11 @@
 // stack dumping signal handler). NO malloc or stdio is allowed here.
 
 #if !defined(__UCLIBC__) && !defined(_AIX)
-  count = std::min(arraysize(trace_), count);
+count = std::min(base::size(trace_), count);
 
-  // Though the backtrace API man page does not list any possible negative
-  // return values, we take no chance.
-  count_ = base::saturated_cast<size_t>(backtrace(trace_, count));
+// Though the backtrace API man page does not list any possible negative
+// return values, we take no chance.
+count_ = base::saturated_cast<size_t>(backtrace(trace_, count));
 #else
   count_ = 0;
 #endif
diff --git a/base/debug/stack_trace_win.cc b/base/debug/stack_trace_win.cc
index c88e71ac7..f6134c2 100644
--- a/base/debug/stack_trace_win.cc
+++ b/base/debug/stack_trace_win.cc
@@ -14,8 +14,8 @@
 
 #include "base/files/file_path.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/memory/singleton.h"
+#include "base/stl_util.h"
 #include "base/synchronization/lock.h"
 #include "build/build_config.h"
 
@@ -275,7 +275,7 @@
 }
 
 NOINLINE StackTrace::StackTrace(size_t count) {
-  count = std::min(arraysize(trace_), count);
+  count = std::min(base::size(trace_), count);
 
   // When walking our own stack, use CaptureStackBackTrace().
   count_ = CaptureStackBackTrace(0, count, trace_, NULL);
@@ -324,20 +324,14 @@
   stack_frame.AddrPC.Mode = AddrModeFlat;
   stack_frame.AddrFrame.Mode = AddrModeFlat;
   stack_frame.AddrStack.Mode = AddrModeFlat;
-  while (StackWalk64(machine_type,
-                     GetCurrentProcess(),
-                     GetCurrentThread(),
-                     &stack_frame,
-                     &context_copy,
-                     NULL,
-                     &SymFunctionTableAccess64,
-                     &SymGetModuleBase64,
-                     NULL) &&
-         count_ < arraysize(trace_)) {
+  while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(),
+                     &stack_frame, &context_copy, NULL,
+                     &SymFunctionTableAccess64, &SymGetModuleBase64, NULL) &&
+         count_ < base::size(trace_)) {
     trace_[count_++] = reinterpret_cast<void*>(stack_frame.AddrPC.Offset);
   }
 
-  for (size_t i = count_; i < arraysize(trace_); ++i)
+  for (size_t i = count_; i < base::size(trace_); ++i)
     trace_[i] = NULL;
 }
 
diff --git a/base/i18n/utf8_validator_tables.cc b/base/i18n/utf8_validator_tables.cc
index 913afc7..31310b2 100644
--- a/base/i18n/utf8_validator_tables.cc
+++ b/base/i18n/utf8_validator_tables.cc
@@ -6,6 +6,7 @@
 // DO NOT EDIT.
 
 #include "base/i18n/utf8_validator_tables.h"
+#include "base/stl_util.h"
 
 namespace base {
 namespace internal {
@@ -49,7 +50,7 @@
     0x81,                                            // 0xa8
 };
 
-const size_t kUtf8ValidatorTablesSize = arraysize(kUtf8ValidatorTables);
+const size_t kUtf8ValidatorTablesSize = base::size(kUtf8ValidatorTables);
 
 }  // namespace internal
 }  // namespace base
diff --git a/base/macros.h b/base/macros.h
index 95b76915..cda8e3a 100644
--- a/base/macros.h
+++ b/base/macros.h
@@ -10,8 +10,6 @@
 #ifndef BASE_MACROS_H_
 #define BASE_MACROS_H_
 
-#include <stddef.h>  // For size_t.
-
 // Put this in the declarations for a class to be uncopyable.
 #define DISALLOW_COPY(TypeName) \
   TypeName(const TypeName&) = delete
@@ -31,21 +29,6 @@
   TypeName() = delete;                           \
   DISALLOW_COPY_AND_ASSIGN(TypeName)
 
-// The arraysize(arr) macro returns the # of elements in an array arr.  The
-// expression is a compile-time constant, and therefore can be used in defining
-// new arrays, for example.  If you use arraysize on a pointer by mistake, you
-// will get a compile-time error.  For the technical details, refer to
-// http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx.
-
-// This template function declaration is used in defining arraysize.
-// Note that the function doesn't need an implementation, as we only
-// use its type.
-//
-// DEPRECATED, please use base::size(array) instead.
-// TODO(https://crbug.com/837308): Replace existing arraysize usages.
-template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N];
-#define arraysize(array) (sizeof(ArraySizeHelper(array)))
-
 // Used to explicitly mark the return value of a function as unused. If you are
 // really sure you don't want to do anything with the return value of a function
 // that has been marked WARN_UNUSED_RESULT, wrap it with this. Example:
diff --git a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.cc b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.cc
index 9d19197..67b4325 100644
--- a/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.cc
+++ b/base/task/task_scheduler/scheduler_single_thread_task_runner_manager.cc
@@ -13,6 +13,7 @@
 #include "base/callback.h"
 #include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/atomic_flag.h"
 #include "base/task/task_scheduler/delayed_task_manager.h"
@@ -371,14 +372,17 @@
   DCHECK(task_tracker_);
   DCHECK(delayed_task_manager_);
 #if defined(OS_WIN)
-  static_assert(arraysize(shared_com_scheduler_workers_) ==
-                    arraysize(shared_scheduler_workers_),
+  static_assert(std::extent<decltype(shared_com_scheduler_workers_)>() ==
+                    std::extent<decltype(shared_scheduler_workers_)>(),
                 "The size of |shared_com_scheduler_workers_| must match "
                 "|shared_scheduler_workers_|");
-  static_assert(arraysize(shared_com_scheduler_workers_[0]) ==
-                    arraysize(shared_scheduler_workers_[0]),
-                "The size of |shared_com_scheduler_workers_| must match "
-                "|shared_scheduler_workers_|");
+  static_assert(
+      std::extent<std::remove_reference<decltype(
+              shared_com_scheduler_workers_[0])>>() ==
+          std::extent<
+              std::remove_reference<decltype(shared_scheduler_workers_[0])>>(),
+      "The size of |shared_com_scheduler_workers_| must match "
+      "|shared_scheduler_workers_|");
 #endif  // defined(OS_WIN)
   DCHECK(!g_manager_is_alive);
   g_manager_is_alive = true;
@@ -610,8 +614,8 @@
 #endif
   {
     AutoSchedulerLock auto_lock(lock_);
-    for (size_t i = 0; i < arraysize(shared_scheduler_workers_); ++i) {
-      for (size_t j = 0; j < arraysize(shared_scheduler_workers_[i]); ++j) {
+    for (size_t i = 0; i < base::size(shared_scheduler_workers_); ++i) {
+      for (size_t j = 0; j < base::size(shared_scheduler_workers_[i]); ++j) {
         local_shared_scheduler_workers[i][j] = shared_scheduler_workers_[i][j];
         shared_scheduler_workers_[i][j] = nullptr;
 #if defined(OS_WIN)
@@ -623,8 +627,8 @@
     }
   }
 
-  for (size_t i = 0; i < arraysize(local_shared_scheduler_workers); ++i) {
-    for (size_t j = 0; j < arraysize(local_shared_scheduler_workers[i]); ++j) {
+  for (size_t i = 0; i < base::size(local_shared_scheduler_workers); ++i) {
+    for (size_t j = 0; j < base::size(local_shared_scheduler_workers[i]); ++j) {
       if (local_shared_scheduler_workers[i][j])
         UnregisterSchedulerWorker(local_shared_scheduler_workers[i][j]);
 #if defined(OS_WIN)
diff --git a/base/task/task_scheduler/task_scheduler_impl.cc b/base/task/task_scheduler/task_scheduler_impl.cc
index 0e1c275..d940c41 100644
--- a/base/task/task_scheduler/task_scheduler_impl.cc
+++ b/base/task/task_scheduler/task_scheduler_impl.cc
@@ -65,9 +65,10 @@
       tracked_ref_factory_(this) {
   DCHECK(!histogram_label.empty());
 
-  static_assert(arraysize(environment_to_worker_pool_) == ENVIRONMENT_COUNT,
-                "The size of |environment_to_worker_pool_| must match "
-                "ENVIRONMENT_COUNT.");
+  static_assert(
+      std::extent<decltype(environment_to_worker_pool_)>() == ENVIRONMENT_COUNT,
+      "The size of |environment_to_worker_pool_| must match "
+      "ENVIRONMENT_COUNT.");
   static_assert(
       size(kEnvironmentParams) == ENVIRONMENT_COUNT,
       "The size of |kEnvironmentParams| must match ENVIRONMENT_COUNT.");
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc
index 6d59dc3..099ac51b 100644
--- a/base/test/scoped_task_environment.cc
+++ b/base/test/scoped_task_environment.cc
@@ -311,7 +311,7 @@
 #if defined(OS_POSIX) || defined(OS_FUCHSIA)
       file_descriptor_watcher_(main_thread_type == MainThreadType::IO
                                    ? std::make_unique<FileDescriptorWatcher>(
-                                         task_queue_->task_runner())
+                                         GetMainThreadTaskRunner())
                                    : nullptr),
 #endif  // defined(OS_POSIX) || defined(OS_FUCHSIA)
       task_tracker_(new TestTaskTracker()),
@@ -323,7 +323,6 @@
           BindRepeating([]() { LOG(FATAL) << "Run() timed out."; })) {
   CHECK(now_source == NowSource::REAL_TIME || mock_time_domain_)
       << "NowSource must be REAL_TIME unless we're using mock time";
-  CHECK(!base::ThreadTaskRunnerHandle::IsSet());
   CHECK(!TaskScheduler::GetInstance())
       << "Someone has already initialized TaskScheduler. If nothing in your "
          "test does so, then a test that ran earlier may have initialized one, "
@@ -331,7 +330,10 @@
          "someone has explicitly disabled it with "
          "DisableCheckForLeakedGlobals().";
 
-  sequence_manager_->SetDefaultTaskRunner(task_queue_->task_runner());
+  CHECK(!base::ThreadTaskRunnerHandle::IsSet());
+  sequence_manager_->SetDefaultTaskRunner(GetMainThreadTaskRunner());
+  CHECK(base::ThreadTaskRunnerHandle::IsSet())
+      << "ThreadTaskRunnerHandle should've been set now.";
 
   // Instantiate a TaskScheduler with 2 threads in each of its 4 pools. Threads
   // stay alive even when they don't have work.
diff --git a/build/config/posix/BUILD.gn b/build/config/posix/BUILD.gn
index a143421..91236f3 100644
--- a/build/config/posix/BUILD.gn
+++ b/build/config/posix/BUILD.gn
@@ -10,8 +10,7 @@
 import("//build/toolchain/toolchain.gni")
 import("//buildtools/deps_revisions.gni")
 
-# TODO(crbug.com/830987): Come up with a better name for is POSIX + Fuchsia
-# configuration.
+# This build configuration is used by both Fuchsia and POSIX systems.
 assert(is_posix || is_fuchsia)
 
 group("posix") {
diff --git a/build/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc b/build/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc
index 0f0a948..3cc8c64e 100644
--- a/build/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc
+++ b/build/fuchsia/fidlgen_js/test/fidlgen_js_unittest.cc
@@ -5,7 +5,7 @@
 #include <lib/fidl/cpp/binding.h>
 #include <lib/fidl/cpp/internal/pending_response.h>
 #include <lib/fidl/cpp/internal/weak_stub_controller.h>
-#include <lib/zx/log.h>
+#include <lib/zx/debuglog.h>
 #include <zircon/syscalls/log.h>
 
 #include "base/bind.h"
@@ -293,10 +293,10 @@
 
   void PassHandles(zx::job job, PassHandlesCallback callback) override {
     EXPECT_EQ(GetKoidForHandle(job), GetKoidForHandle(*zx::job::default_job()));
-    zx::log log;
-    EXPECT_EQ(zx::log::create(ZX_LOG_FLAG_READABLE, &log), ZX_OK);
-    unowned_log_handle_ = log.get();
-    callback(std::move(log));
+    zx::process process;
+    ASSERT_EQ(zx::process::self()->duplicate(ZX_RIGHT_SAME_RIGHTS, &process),
+              ZX_OK);
+    callback(std::move(process));
   }
 
   void ReceiveUnions(fidljstest::StructOfMultipleUnions somu) override {
@@ -495,8 +495,6 @@
   const std::string& various_msg() const { return various_msg_; }
   const std::vector<uint32_t>& various_stuff() const { return various_stuff_; }
 
-  zx_handle_t unowned_log_handle() const { return unowned_log_handle_; }
-
   fidljstest::BasicStruct GetReceivedStruct() const { return basic_struct_; }
 
   bool did_receive_union() const { return did_receive_union_; }
@@ -525,7 +523,6 @@
   std::vector<uint32_t> various_stuff_;
   fidljstest::BasicStruct basic_struct_;
   std::vector<base::OnceClosure> response_callbacks_;
-  zx_handle_t unowned_log_handle_;
   bool did_receive_union_ = false;
   bool did_get_vectors_of_string_ = false;
   std::unique_ptr<AnotherInterfaceImpl> another_interface_impl_;
@@ -871,7 +868,7 @@
     var proxy = new TestolaProxy();
     proxy.$bind(testHandle);
     proxy.PassHandles(testJobHandle).then(h => {
-      this.debuglogHandle = h;
+      this.processHandle = h;
     }).catch((e) => log('FAILED: ' + e));
   )";
   helper.runner().Run(source, "test.js");
@@ -879,17 +876,19 @@
   // Run the message loop to send the request and receive a response.
   base::RunLoop().RunUntilIdle();
 
-  zx_handle_t debug_handle_back_from_js =
-      helper.Get<uint32_t>("debuglogHandle");
-  EXPECT_EQ(debug_handle_back_from_js, testola_impl.unowned_log_handle());
+  zx_handle_t process_handle_back_from_js =
+      helper.Get<uint32_t>("processHandle");
+  EXPECT_EQ(GetKoidForHandle(process_handle_back_from_js),
+            GetKoidForHandle(*zx::process::self()));
 
   // Make sure we received the valid handle back correctly, and close it. Not
-  // stored into a zx::log in case it isn't valid, and to check the return value
-  // from closing it.
-  EXPECT_EQ(zx_handle_close(debug_handle_back_from_js), ZX_OK);
+  // stored into a zx::process in case it isn't valid, and to check the return
+  // value from closing it.
+  EXPECT_EQ(zx_handle_close(process_handle_back_from_js), ZX_OK);
 
-  // Ensure we didn't pass away our default job.
+  // Ensure we didn't pass away our default job, or process self.
   EXPECT_NE(GetKoidForHandle(*zx::job::default_job()), ZX_KOID_INVALID);
+  EXPECT_NE(GetKoidForHandle(*zx::process::self()), ZX_KOID_INVALID);
 }
 
 TEST_F(FidlGenJsTest, UnionSend) {
diff --git a/build/fuchsia/fidlgen_js/test/simple.fidl b/build/fuchsia/fidlgen_js/test/simple.fidl
index 1d1c6be..ffa8e4f 100644
--- a/build/fuchsia/fidlgen_js/test/simple.fidl
+++ b/build/fuchsia/fidlgen_js/test/simple.fidl
@@ -119,7 +119,7 @@
 
   NestedStructsWithResponse(BasicStruct basic) -> (StuffAndThings resp);
 
-  PassHandles(handle<job> job) -> (handle<debuglog> debuglog);
+  PassHandles(handle<job> job) -> (handle<process> process);
 
   ReceiveUnions(StructOfMultipleUnions somu);
 
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index b4fa2bcc..72199993 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-5f05d47ce53c778be8f268a4648719c279c17ecf
\ No newline at end of file
+07d378eba1ad10797afa486fb15d95e22472ff32
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index e4836ab..397db99 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-a78ee76400e8b3a23af2f93efb25a160285c145a
\ No newline at end of file
+2d731e977453b01979c76b1bbcc245c40ab8036a
\ No newline at end of file
diff --git a/build/fuchsia/remote_cmd.py b/build/fuchsia/remote_cmd.py
index 70f6ef1..5f54747 100644
--- a/build/fuchsia/remote_cmd.py
+++ b/build/fuchsia/remote_cmd.py
@@ -18,6 +18,12 @@
 def _IsLinkLocalIPv6(hostname):
   return hostname.startswith('fe80::')
 
+# Adds ""
+def _EscapeIfIPv6Address(address):
+  if ':' in address:
+    return '[' + address + ']'
+  else:
+    return address
 
 class CommandRunner(object):
   """Helper class used to execute commands on a remote host over SSH."""
@@ -34,9 +40,6 @@
     self._host = host
     self._port = port
 
-    if ':' in self._host:
-      self._host = '[' + self._host + ']'
-
   def _GetSshCommandLinePrefix(self):
     return _SSH + ['-F', self._config_path, self._host, '-p', str(self._port)]
 
@@ -98,10 +101,12 @@
     if recursive:
       scp_command.append('-r')
 
+    host = _EscapeIfIPv6Address(self._host)
+
     if direction == COPY_TO_TARGET:
-      dest = "%s:%s" % (self._host, dest)
+      dest = "%s:%s" % (host, dest)
     else:
-      sources = ["%s:%s" % (self._host, source) for source in sources]
+      sources = ["%s:%s" % (host, source) for source in sources]
 
     scp_command += ['-F', self._config_path, '-P', str(self._port)]
     scp_command += sources
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 09559ad7..f234c7d 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -496,6 +496,8 @@
     "test/test_options_provider.h",
     "test/test_paint_worklet_input.cc",
     "test/test_paint_worklet_input.h",
+    "test/test_paint_worklet_layer_painter.cc",
+    "test/test_paint_worklet_layer_painter.h",
     "test/test_skcanvas.cc",
     "test/test_skcanvas.h",
     "test/test_task_graph_runner.cc",
diff --git a/cc/paint/paint_flags.cc b/cc/paint/paint_flags.cc
index a86da2a4..3103b7a 100644
--- a/cc/paint/paint_flags.cc
+++ b/cc/paint/paint_flags.cc
@@ -126,7 +126,6 @@
 
 SkPaint PaintFlags::ToSkPaint() const {
   SkPaint paint;
-  paint.setTypeface(typeface_);
   paint.setPathEffect(path_effect_);
   if (shader_)
     paint.setShader(shader_->GetSkShader());
@@ -135,7 +134,6 @@
   paint.setDrawLooper(draw_looper_);
   if (image_filter_)
     paint.setImageFilter(image_filter_->cached_sk_filter_);
-  paint.setTextSize(text_size_);
   paint.setColor(color_);
   paint.setStrokeWidth(width_);
   paint.setStrokeMiter(miter_limit_);
@@ -144,7 +142,6 @@
   paint.setStrokeCap(static_cast<SkPaint::Cap>(getStrokeCap()));
   paint.setStrokeJoin(static_cast<SkPaint::Join>(getStrokeJoin()));
   paint.setStyle(static_cast<SkPaint::Style>(getStyle()));
-  paint.setHinting(static_cast<SkFontHinting>(getHinting()));
   paint.setFilterQuality(getFilterQuality());
   return paint;
 }
diff --git a/cc/paint/paint_image.h b/cc/paint/paint_image.h
index d5969aa..c71fe72 100644
--- a/cc/paint/paint_image.h
+++ b/cc/paint/paint_image.h
@@ -219,7 +219,7 @@
   sk_sp<SkImage> GetSkImageForFrame(size_t index,
                                     GeneratorClientId client_id) const;
 
-  PaintWorkletInput* paint_worklet_input() {
+  PaintWorkletInput* paint_worklet_input() const {
     return paint_worklet_input_.get();
   }
 
diff --git a/cc/paint/paint_op_buffer_fuzzer.cc b/cc/paint/paint_op_buffer_fuzzer.cc
index bd3681485..d7fcd2ed 100644
--- a/cc/paint/paint_op_buffer_fuzzer.cc
+++ b/cc/paint/paint_op_buffer_fuzzer.cc
@@ -112,7 +112,7 @@
   base::CommandLine::Init(0, nullptr);
 
   // Partition the data to use some bytes for populating the font cache.
-  size_t bytes_for_fonts = data[0];
+  uint32_t bytes_for_fonts = data[0];
   if (bytes_for_fonts > size)
     bytes_for_fonts = size / 2;
 
diff --git a/cc/paint/paint_worklet_layer_painter.h b/cc/paint/paint_worklet_layer_painter.h
index 586bd3b7..c1a8e09 100644
--- a/cc/paint/paint_worklet_layer_painter.h
+++ b/cc/paint/paint_worklet_layer_painter.h
@@ -6,6 +6,7 @@
 #define CC_PAINT_PAINT_WORKLET_LAYER_PAINTER_H_
 
 #include "cc/cc_export.h"
+#include "cc/paint/paint_record.h"
 
 namespace cc {
 
@@ -13,7 +14,7 @@
  public:
   virtual ~PaintWorkletLayerPainter() {}
 
-  // TODO(xidachen) add a PaintWorkletPaint function.
+  virtual sk_sp<PaintRecord> Paint() = 0;
 };
 
 }  // namespace cc
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index 089066fb..aa69d70 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -451,6 +451,13 @@
   void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel,
                                          bool has_scrolled_by_touch) override {}
 
+  void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      ElementId scroll_latched_element_id) override {}
+
+  void SendScrollEndEventFromImplSide(
+      ElementId scroll_latched_element_id) override {}
+
   void RequestNewLayerTreeFrameSink() override {
     test_hooks_->RequestNewLayerTreeFrameSink();
   }
diff --git a/cc/test/stub_layer_tree_host_client.h b/cc/test/stub_layer_tree_host_client.h
index 27317aa5..e150e251 100644
--- a/cc/test/stub_layer_tree_host_client.h
+++ b/cc/test/stub_layer_tree_host_client.h
@@ -5,6 +5,7 @@
 #ifndef CC_TEST_STUB_LAYER_TREE_HOST_CLIENT_H_
 #define CC_TEST_STUB_LAYER_TREE_HOST_CLIENT_H_
 
+#include "cc/trees/element_id.h"
 #include "cc/trees/layer_tree_host_client.h"
 
 namespace cc {
@@ -24,6 +25,11 @@
   void ApplyViewportChanges(const ApplyViewportChangesArgs&) override {}
   void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel,
                                          bool has_scrolled_by_touch) override {}
+  void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      ElementId scroll_latched_element_id) override {}
+  void SendScrollEndEventFromImplSide(
+      ElementId scroll_latched_element_id) override {}
   void RequestNewLayerTreeFrameSink() override {}
   void DidInitializeLayerTreeFrameSink() override {}
   void DidFailToInitializeLayerTreeFrameSink() override {}
diff --git a/cc/test/test_paint_worklet_layer_painter.cc b/cc/test/test_paint_worklet_layer_painter.cc
new file mode 100644
index 0000000..08bd271
--- /dev/null
+++ b/cc/test/test_paint_worklet_layer_painter.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/test/test_paint_worklet_layer_painter.h"
+
+#include "cc/test/skia_common.h"
+#include "cc/test/test_paint_worklet_input.h"
+
+namespace cc {
+
+TestPaintWorkletLayerPainter::TestPaintWorkletLayerPainter() = default;
+
+TestPaintWorkletLayerPainter::~TestPaintWorkletLayerPainter() = default;
+
+sk_sp<PaintRecord> TestPaintWorkletLayerPainter::Paint() {
+  auto manual_record = sk_make_sp<PaintOpBuffer>();
+  scoped_refptr<TestPaintWorkletInput> input =
+      base::MakeRefCounted<TestPaintWorkletInput>(gfx::SizeF(100, 100));
+  PaintImage image = CreatePaintWorkletPaintImage(input);
+  manual_record->push<DrawImageOp>(image, 0.f, 0.f, nullptr);
+  return manual_record;
+}
+
+}  // namespace cc
diff --git a/cc/test/test_paint_worklet_layer_painter.h b/cc/test/test_paint_worklet_layer_painter.h
new file mode 100644
index 0000000..6662ac5f
--- /dev/null
+++ b/cc/test/test_paint_worklet_layer_painter.h
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_TEST_TEST_PAINT_WORKLET_LAYER_PAINTER_H_
+#define CC_TEST_TEST_PAINT_WORKLET_LAYER_PAINTER_H_
+
+#include "cc/paint/paint_worklet_layer_painter.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace cc {
+
+class TestPaintWorkletLayerPainter : public PaintWorkletLayerPainter {
+ public:
+  TestPaintWorkletLayerPainter();
+  ~TestPaintWorkletLayerPainter() override;
+
+  sk_sp<PaintRecord> Paint() override;
+};
+
+}  // namespace cc
+
+#endif  // CC_TEST_TEST_PAINT_WORKLET_LAYER_PAINTER_H_
diff --git a/cc/tiles/image_controller.cc b/cc/tiles/image_controller.cc
index ea5989f..eb975d5 100644
--- a/cc/tiles/image_controller.cc
+++ b/cc/tiles/image_controller.cc
@@ -129,6 +129,11 @@
   image_decode_queue_.clear();
 }
 
+void ImageController::SetPaintWorkletLayerPainter(
+    std::unique_ptr<PaintWorkletLayerPainter> painter) {
+  paint_worklet_image_cache_.SetPaintWorkletLayerPainter(std::move(painter));
+}
+
 void ImageController::SetImageDecodeCache(ImageDecodeCache* cache) {
   DCHECK(!cache_ || !cache);
 
@@ -157,7 +162,7 @@
       continue;
     }
     scoped_refptr<TileTask> result =
-        paint_worklet_image_cache_->GetTaskForPaintWorkletImage(*it);
+        paint_worklet_image_cache_.GetTaskForPaintWorkletImage(*it);
     DCHECK(result);
     tasks->push_back(std::move(result));
     // Remove it so that there is no need to check whether an image is
diff --git a/cc/tiles/image_controller.h b/cc/tiles/image_controller.h
index 3f4d0b63..6f500d1 100644
--- a/cc/tiles/image_controller.h
+++ b/cc/tiles/image_controller.h
@@ -37,6 +37,8 @@
   virtual ~ImageController();
 
   void SetImageDecodeCache(ImageDecodeCache* cache);
+  void SetPaintWorkletLayerPainter(
+      std::unique_ptr<PaintWorkletLayerPainter> painter);
   // The name "Data images" are the images that are not generated by
   // PaintWorklet.
   // Build tile tasks for synchronously decoded images that are not generated by
@@ -124,7 +126,7 @@
   base::WeakPtr<ImageController> weak_ptr_;
 
   ImageDecodeCache* cache_ = nullptr;
-  PaintWorkletImageCache* paint_worklet_image_cache_ = nullptr;
+  PaintWorkletImageCache paint_worklet_image_cache_;
   std::vector<DrawImage> predecode_locked_images_;
 
   static ImageDecodeRequestId s_next_image_decode_queue_id_;
diff --git a/cc/tiles/paint_worklet_image_cache.cc b/cc/tiles/paint_worklet_image_cache.cc
index 3be065a..47f7773 100644
--- a/cc/tiles/paint_worklet_image_cache.cc
+++ b/cc/tiles/paint_worklet_image_cache.cc
@@ -3,9 +3,11 @@
 // found in the LICENSE file.
 
 #include "cc/tiles/paint_worklet_image_cache.h"
+#include "cc/paint/paint_worklet_layer_painter.h"
 
 namespace cc {
 
+// TODO(xidachen): Rename this to PaintWorkletTaskImpl.
 class PaintWorkletImageCacheImpl : public TileTask {
  public:
   PaintWorkletImageCacheImpl(PaintWorkletImageCache* cache,
@@ -32,6 +34,11 @@
 
 PaintWorkletImageCache::~PaintWorkletImageCache() {}
 
+void PaintWorkletImageCache::SetPaintWorkletLayerPainter(
+    std::unique_ptr<PaintWorkletLayerPainter> painter) {
+  painter_ = std::move(painter);
+}
+
 scoped_refptr<TileTask> PaintWorkletImageCache::GetTaskForPaintWorkletImage(
     const DrawImage& image) {
   return base::MakeRefCounted<PaintWorkletImageCacheImpl>(this,
@@ -39,6 +46,16 @@
 }
 
 // TODO(xidachen): dispatch the work to a worklet thread, invoke JS callback.
-void PaintWorkletImageCache::PaintImageInTask(const PaintImage& paint_image) {}
+// Do check the cache first. If there is already a cache entry for this input,
+// then there is no need to call the Paint() function.
+void PaintWorkletImageCache::PaintImageInTask(const PaintImage& paint_image) {
+  sk_sp<PaintRecord> record = painter_->Paint();
+  records_[paint_image.paint_worklet_input()] = record;
+}
+
+PaintRecord* PaintWorkletImageCache::GetPaintRecordForTest(
+    PaintWorkletInput* input) {
+  return records_[input].get();
+}
 
 }  // namespace cc
diff --git a/cc/tiles/paint_worklet_image_cache.h b/cc/tiles/paint_worklet_image_cache.h
index 9efc35e..d857dc3 100644
--- a/cc/tiles/paint_worklet_image_cache.h
+++ b/cc/tiles/paint_worklet_image_cache.h
@@ -5,8 +5,11 @@
 #ifndef CC_TILES_PAINT_WORKLET_IMAGE_CACHE_H_
 #define CC_TILES_PAINT_WORKLET_IMAGE_CACHE_H_
 
+#include "base/containers/flat_map.h"
 #include "cc/cc_export.h"
 #include "cc/paint/draw_image.h"
+#include "cc/paint/paint_record.h"
+#include "cc/paint/paint_worklet_layer_painter.h"
 #include "cc/raster/tile_task.h"
 #include "cc/tiles/image_decode_cache.h"
 
@@ -21,9 +24,27 @@
 
   ~PaintWorkletImageCache();
 
+  void SetPaintWorkletLayerPainter(
+      std::unique_ptr<PaintWorkletLayerPainter> painter);
+
   scoped_refptr<TileTask> GetTaskForPaintWorkletImage(const DrawImage& image);
 
   void PaintImageInTask(const PaintImage& paint_image);
+
+  PaintRecord* GetPaintRecordForTest(PaintWorkletInput* input);
+  const base::flat_map<PaintWorkletInput*, sk_sp<PaintRecord>>&
+  GetRecordsForTest() {
+    return records_;
+  }
+
+ private:
+  // The PaintRecord is produced by PaintWorkletLayerPainter::Paint(), and used
+  // for raster.
+  base::flat_map<PaintWorkletInput*, sk_sp<PaintRecord>> records_;
+  // The PaintWorkletImageCache is owned by ImageController, which has the same
+  // life time as the LayerTreeHostImpl, that guarantees that the painter will
+  // live as long as the LayerTreeHostImpl.
+  std::unique_ptr<PaintWorkletLayerPainter> painter_;
 };
 
 }  // namespace cc
diff --git a/cc/tiles/paint_worklet_image_cache_unittest.cc b/cc/tiles/paint_worklet_image_cache_unittest.cc
index 2677229d..18355d4 100644
--- a/cc/tiles/paint_worklet_image_cache_unittest.cc
+++ b/cc/tiles/paint_worklet_image_cache_unittest.cc
@@ -7,6 +7,7 @@
 #include "cc/paint/draw_image.h"
 #include "cc/test/skia_common.h"
 #include "cc/test/test_paint_worklet_input.h"
+#include "cc/test/test_paint_worklet_layer_painter.h"
 #include "cc/test/test_tile_task_runner.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -36,17 +37,67 @@
   return CreatePaintWorkletPaintImage(input);
 }
 
-TEST(PaintWorkletImageCacheTest, GetTaskForImage) {
-  TestPaintWorkletImageCache cache;
-  PaintImage paint_image = CreatePaintImage(100, 100);
+scoped_refptr<TileTask> GetTaskForPaintWorkletImage(
+    const PaintImage& paint_image,
+    TestPaintWorkletImageCache* cache) {
   DrawImage draw_image(
       paint_image, SkIRect::MakeWH(paint_image.width(), paint_image.height()),
       kNone_SkFilterQuality, CreateMatrix(SkSize::Make(1.f, 1.f), true),
       PaintImage::kDefaultFrameIndex);
-  scoped_refptr<TileTask> task = cache.GetTaskForPaintWorkletImage(draw_image);
+  std::unique_ptr<TestPaintWorkletLayerPainter> painter =
+      std::make_unique<TestPaintWorkletLayerPainter>();
+  cache->SetPaintWorkletLayerPainter(std::move(painter));
+  return cache->GetTaskForPaintWorkletImage(draw_image);
+}
+
+void TestPaintRecord(PaintRecord* record) {
+  EXPECT_EQ(record->total_op_count(), 1u);
+
+  // GetOpAtForTesting check whether the type is the same as DrawImageOp or not.
+  // If not, it returns a nullptr.
+  auto* paint_op = record->GetOpAtForTesting<DrawImageOp>(0);
+  EXPECT_TRUE(paint_op);
+}
+
+TEST(PaintWorkletImageCacheTest, GetTaskForImage) {
+  TestPaintWorkletImageCache cache;
+  PaintImage paint_image = CreatePaintImage(100, 100);
+  scoped_refptr<TileTask> task =
+      GetTaskForPaintWorkletImage(paint_image, &cache);
   EXPECT_TRUE(task);
 
   TestTileTaskRunner::ProcessTask(task.get());
+
+  PaintRecord* record =
+      cache.GetPaintRecordForTest(paint_image.paint_worklet_input());
+  TestPaintRecord(record);
+}
+
+TEST(PaintWorkletImageCacheTest, MultipleRecordsInCache) {
+  TestPaintWorkletImageCache cache;
+  PaintImage paint_image1 = CreatePaintImage(100, 100);
+  scoped_refptr<TileTask> task1 =
+      GetTaskForPaintWorkletImage(paint_image1, &cache);
+  EXPECT_TRUE(task1);
+  PaintImage paint_image2 = CreatePaintImage(200, 200);
+  scoped_refptr<TileTask> task2 =
+      GetTaskForPaintWorkletImage(paint_image2, &cache);
+  EXPECT_TRUE(task2);
+
+  TestTileTaskRunner::ProcessTask(task1.get());
+  TestTileTaskRunner::ProcessTask(task2.get());
+
+  base::flat_map<PaintWorkletInput*, sk_sp<PaintRecord>> records =
+      cache.GetRecordsForTest();
+  EXPECT_EQ(records.size(), 2u);
+
+  PaintRecord* record1 = records[paint_image1.paint_worklet_input()].get();
+  EXPECT_TRUE(record1);
+  TestPaintRecord(record1);
+
+  PaintRecord* record2 = records[paint_image2.paint_worklet_input()].get();
+  EXPECT_TRUE(record2);
+  TestPaintRecord(record2);
 }
 
 }  // namespace
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index 8abddd4..aafd1e18 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -1694,6 +1694,11 @@
   return std::move(state);
 }
 
+void TileManager::SetPaintWorkletLayerPainter(
+    std::unique_ptr<PaintWorkletLayerPainter> painter) {
+  image_controller_.SetPaintWorkletLayerPainter(std::move(painter));
+}
+
 void TileManager::ActivationStateAsValueInto(
     base::trace_event::TracedValue* state) {
   state->SetString("tree_priority",
diff --git a/cc/tiles/tile_manager.h b/cc/tiles/tile_manager.h
index ac58ccf..e211e64 100644
--- a/cc/tiles/tile_manager.h
+++ b/cc/tiles/tile_manager.h
@@ -292,6 +292,9 @@
 
   void set_active_url(const GURL& url) { active_url_ = url; }
 
+  void SetPaintWorkletLayerPainter(
+      std::unique_ptr<PaintWorkletLayerPainter> painter);
+
  protected:
   friend class Tile;
   // Must be called by tile during destruction.
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 605b3723..0197be96 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -881,6 +881,19 @@
   }
 }
 
+void LayerTreeHost::SendOverscrollAndScrollEndEventsFromImplSide(
+    const ScrollAndScaleSet& info) {
+  if (info.scroll_latched_element_id == ElementId())
+    return;
+
+  if (!info.overscroll_delta.IsZero()) {
+    client_->SendOverscrollEventFromImplSide(info.overscroll_delta,
+                                             info.scroll_latched_element_id);
+  }
+  if (info.scroll_gesture_did_end)
+    client_->SendScrollEndEventFromImplSide(info.scroll_latched_element_id);
+}
+
 void LayerTreeHost::ApplyScrollAndScale(ScrollAndScaleSet* info) {
   DCHECK(info);
   for (auto& swap_promise : info->swap_promises) {
@@ -908,6 +921,8 @@
     }
   }
 
+  SendOverscrollAndScrollEndEventsFromImplSide(*info);
+
   // This needs to happen after scroll deltas have been sent to prevent top
   // controls from clamping the layout viewport both on the compositor and
   // on the main thread.
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 59563f3..8b0ae028 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -702,6 +702,8 @@
 
   void ApplyViewportChanges(const ScrollAndScaleSet& info);
   void RecordWheelAndTouchScrollingCount(const ScrollAndScaleSet& info);
+  void SendOverscrollAndScrollEndEventsFromImplSide(
+      const ScrollAndScaleSet& info);
   void ApplyPageScaleDeltaFromImplSide(float page_scale_delta);
   void InitializeProxy(std::unique_ptr<Proxy> proxy);
 
diff --git a/cc/trees/layer_tree_host_client.h b/cc/trees/layer_tree_host_client.h
index 2973940a..03b5ade5 100644
--- a/cc/trees/layer_tree_host_client.h
+++ b/cc/trees/layer_tree_host_client.h
@@ -22,6 +22,7 @@
 }
 
 namespace cc {
+struct ElementId;
 
 struct ApplyViewportChangesArgs {
   // Scroll offset delta of the inner (visual) viewport.
@@ -103,6 +104,15 @@
   virtual void RecordWheelAndTouchScrollingCount(
       bool has_scrolled_by_wheel,
       bool has_scrolled_by_touch) = 0;
+
+  // Notifies the client when an overscroll has happened.
+  virtual void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      ElementId scroll_latched_element_id) = 0;
+  // Notifies the client when a gesture scroll has ended.
+  virtual void SendScrollEndEventFromImplSide(
+      ElementId scroll_latched_element_id) = 0;
+
   // Request a LayerTreeFrameSink from the client. When the client has one it
   // should call LayerTreeHost::SetLayerTreeFrameSink. This will result in
   // either DidFailToInitializeLayerTreeFrameSink or
diff --git a/cc/trees/layer_tree_host_common.h b/cc/trees/layer_tree_host_common.h
index 42de6d2..235910b 100644
--- a/cc/trees/layer_tree_host_common.h
+++ b/cc/trees/layer_tree_host_common.h
@@ -168,7 +168,19 @@
 
   std::vector<LayerTreeHostCommon::ScrollUpdateInfo> scrolls;
   float page_scale_delta;
+
+  // Elastic overscroll effect offset delta. This is used only on Mac and shows
+  // the pixels that the page is rubber-banned/stretched by.
   gfx::Vector2dF elastic_overscroll_delta;
+
+  // Unconsumed scroll delta used to send overscroll events to the latched
+  // element on the main thread;
+  gfx::Vector2dF overscroll_delta;
+
+  // The element id of the node to which scrolling is latched. This is used to
+  // send overscroll/scrollend DOM events to proper targets whenever needed.
+  ElementId scroll_latched_element_id;
+
   float top_controls_delta;
   std::vector<LayerTreeHostCommon::ScrollbarsUpdateInfo> scrollbars;
   std::vector<std::unique_ptr<SwapPromise>> swap_promises;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 318b967..91a7d3d 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -3142,7 +3142,7 @@
 
 void LayerTreeHostImpl::SetPaintWorkletLayerPainter(
     std::unique_ptr<PaintWorkletLayerPainter> painter) {
-  painter_ = std::move(painter);
+  tile_manager_.SetPaintWorkletLayerPainter(std::move(painter));
 }
 
 LayerImpl* LayerTreeHostImpl::ViewportMainScrollLayer() {
@@ -4413,6 +4413,10 @@
     client_->SetNeedsCommitOnImplThread();
     SetNeedsRedraw();
     client_->RenewTreePriority();
+  } else {
+    overscroll_delta_for_main_thread_ +=
+        gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y());
+    client_->SetNeedsCommitOnImplThread();
   }
 
   // Scrolling along an axis resets accumulated root overscroll for that axis.
@@ -4600,6 +4604,10 @@
 
 void LayerTreeHostImpl::ScrollEnd(ScrollState* scroll_state, bool should_snap) {
   scroll_gesture_did_end_ = true;
+  last_scroller_element_id_ = CurrentlyScrollingNode()
+                                  ? CurrentlyScrollingNode()->element_id
+                                  : ElementId();
+  client_->SetNeedsCommitOnImplThread();
 
   if (should_snap && SnapAtScrollEnd())
     return;
@@ -4783,6 +4791,23 @@
   scroll_info->scroll_gesture_did_end = scroll_gesture_did_end_;
   has_scrolled_by_wheel_ = has_scrolled_by_touch_ = false;
 
+  // Record and reset overscroll delta.
+  scroll_info->overscroll_delta = overscroll_delta_for_main_thread_;
+  overscroll_delta_for_main_thread_ = gfx::Vector2dF();
+
+  if (scroll_gesture_did_end_) {
+    // When the scrolling has finished send the element id of the last node that
+    // has scrolled.
+    scroll_info->scroll_latched_element_id = last_scroller_element_id_;
+    last_scroller_element_id_ = ElementId();
+  } else {
+    // Send the element id of the currently scrolling node.
+    auto* node =
+        active_tree_->property_trees()->scroll_tree.CurrentlyScrollingNode();
+    scroll_info->scroll_latched_element_id =
+        node ? node->element_id : ElementId();
+  }
+
   if (browser_controls_manager()) {
     scroll_info->browser_controls_constraint =
         browser_controls_manager()->PullConstraintForMainThread(
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 11489c6..b8b2af7 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -966,6 +966,10 @@
 
   gfx::Vector2dF accumulated_root_overscroll_;
 
+  // Unconsumed scroll delta sent to the main thread for firing overscroll DOM
+  // events. Resets after each commit.
+  gfx::Vector2dF overscroll_delta_for_main_thread_;
+
   // True iff some of the delta has been consumed for the current scroll
   // sequence on the specific axis.
   bool did_scroll_x_for_scroll_gesture_;
@@ -1106,14 +1110,16 @@
   int last_color_space_id_ = -1;
   bool is_animating_for_snap_;
 
-  std::unique_ptr<PaintWorkletLayerPainter> painter_;
-
   const PaintImage::GeneratorClientId paint_image_generator_client_id_;
 
   // Set to true when a scroll gesture being handled on the compositor has
   // ended.
   bool scroll_gesture_did_end_;
 
+  // Set in ScrollEnd before clearing the currently scrolling node. This is
+  // used to send the scrollend DOM event when scrolling has happened on CC.
+  ElementId last_scroller_element_id_;
+
   DISALLOW_COPY_AND_ASSIGN(LayerTreeHostImpl);
 };
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 8ca47ac..cb4a82f 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -5816,27 +5816,6 @@
   ASSERT_EQ(0, host_impl_->active_tree()->CurrentBrowserControlsShownRatio());
 }
 
-// Test that if only the browser controls are scrolled, we shouldn't request a
-// commit.
-TEST_F(LayerTreeHostImplBrowserControlsTest, BrowserControlsDontTriggerCommit) {
-  SetupBrowserControlsAndScrollLayerWithVirtualViewport(
-      gfx::Size(100, 50), gfx::Size(100, 100), gfx::Size(100, 100));
-  DrawFrame();
-
-  // Show browser controls
-  EXPECT_EQ(1.f, host_impl_->active_tree()->CurrentBrowserControlsShownRatio());
-
-  // Scroll 25px to hide browser controls
-  gfx::Vector2dF scroll_delta(0.f, 25.f);
-  EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
-            host_impl_
-                ->ScrollBegin(BeginState(gfx::Point()).get(),
-                              InputHandler::TOUCHSCREEN)
-                .thread);
-  host_impl_->ScrollBy(UpdateState(gfx::Point(), scroll_delta).get());
-  EXPECT_FALSE(did_request_commit_);
-}
-
 // Test that if a scrollable sublayer doesn't consume the scroll,
 // browser controls should hide when scrolling down.
 TEST_F(LayerTreeHostImplBrowserControlsTest,
@@ -10833,7 +10812,7 @@
   ASSERT_TRUE(host_impl_->browser_controls_manager()->has_animation());
   EXPECT_TRUE(did_request_next_frame_);
   EXPECT_TRUE(did_request_redraw_);
-  EXPECT_FALSE(did_request_commit_);
+  EXPECT_TRUE(did_request_commit_);
 
   // The browser controls should properly animate until finished, despite the
   // scroll offset being at the origin.
@@ -10919,7 +10898,7 @@
   ASSERT_TRUE(host_impl_->browser_controls_manager()->has_animation());
   EXPECT_TRUE(did_request_next_frame_);
   EXPECT_TRUE(did_request_redraw_);
-  EXPECT_FALSE(did_request_commit_);
+  EXPECT_TRUE(did_request_commit_);
 
   // Animate the browser controls to the limit.
   viz::BeginFrameArgs begin_frame_args = viz::CreateBeginFrameArgsForTesting(
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 7cc260fd..d7aca3b1 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -294,8 +294,8 @@
     "//components/dom_distiller/core/android:dom_distiller_core_java",
     "//components/download/internal/background_service:internal_java",
     "//components/download/network:network_java",
-    "//components/download/public/background_service:public_java",
     "//components/download/public/common:public_java",
+    "//components/download/public/task:public_java",
     "//components/embedder_support/android:content_view_java",
     "//components/embedder_support/android:web_contents_delegate_java",
     "//components/feature_engagement:feature_engagement_java",
@@ -1355,6 +1355,7 @@
 
 generate_jni("test_support_jni_headers") {
   sources = [
+    "javatests/src/org/chromium/chrome/browser/offlinepages/OfflineTestUtil.java",
     "javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchTestBridge.java",
     "javatests/src/org/chromium/chrome/browser/test/MockCertVerifierRuleAndroid.java",
   ]
@@ -1366,12 +1367,15 @@
   testonly = true
   java_files = [
     "javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchTestBridge.java",
+    "javatests/src/org/chromium/chrome/browser/offlinepages/OfflineTestUtil.java",
     "javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java",
     "javatests/src/org/chromium/chrome/browser/test/MockCertVerifierRuleAndroid.java",
   ]
   deps = [
     ":chrome_java",
     "//base:base_java",
+    "//base:base_java_test_support",
+    "//components/offline_items_collection/core:core_java",
     "//components/sync:test_support_proto_java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_deps:com_google_protobuf_protobuf_lite_java",
@@ -1384,6 +1388,7 @@
   sources = [
     "../browser/android/ssl/mock_cert_verifier_rule_android.cc",
     "../browser/android/ssl/mock_cert_verifier_rule_android.h",
+    "../browser/offline_pages/android/offline_test_util_jni.cc",
     "../browser/offline_pages/android/prefetch_test_bridge.cc",
     "../browser/ssl/chrome_mock_cert_verifier.cc",
     "../browser/ssl/chrome_mock_cert_verifier.h",
@@ -1391,6 +1396,7 @@
   deps = [
     ":test_support_jni_headers",
     "//chrome/browser",
+    "//components/offline_pages/core/background:test_support",
   ]
 }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/MockSafeBrowsingApiHandler.java b/chrome/android/javatests/src/org/chromium/chrome/browser/MockSafeBrowsingApiHandler.java
index 15c43251..74b29c9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/MockSafeBrowsingApiHandler.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/MockSafeBrowsingApiHandler.java
@@ -35,6 +35,11 @@
     private HashMap<String, String> mResponseMap;
 
     @Override
+    public String getSafetyNetId() {
+        return "";
+    }
+
+    @Override
     public boolean init(Observer observer) {
         return init(observer, false);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
index b12588b..2aa2ad4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridgeTest.java
@@ -50,6 +50,7 @@
 import java.util.Set;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
@@ -130,7 +131,7 @@
     @SmallTest
     @RetryOnFailure
     public void testLoadOfflinePagesWhenEmpty() throws Exception {
-        List<OfflinePageItem> offlinePages = getAllPages();
+        List<OfflinePageItem> offlinePages = OfflineTestUtil.getAllPages();
         Assert.assertEquals("Offline pages count incorrect.", 0, offlinePages.size());
     }
 
@@ -142,7 +143,7 @@
     public void testAddOfflinePageAndLoad() throws Exception {
         mActivityTestRule.loadUrl(mTestPage);
         savePage(SavePageResult.SUCCESS, mTestPage);
-        List<OfflinePageItem> allPages = getAllPages();
+        List<OfflinePageItem> allPages = OfflineTestUtil.getAllPages();
         OfflinePageItem offlinePage = allPages.get(0);
         Assert.assertEquals("Offline pages count incorrect.", 1, allPages.size());
         Assert.assertEquals("Offline page item url incorrect.", mTestPage, offlinePage.getUrl());
@@ -154,10 +155,11 @@
     public void testGetPageByBookmarkId() throws Exception {
         mActivityTestRule.loadUrl(mTestPage);
         savePage(SavePageResult.SUCCESS, mTestPage);
-        OfflinePageItem offlinePage = getPageByClientId(TEST_CLIENT_ID);
+        OfflinePageItem offlinePage = OfflineTestUtil.getPageByClientId(TEST_CLIENT_ID);
         Assert.assertEquals("Offline page item url incorrect.", mTestPage, offlinePage.getUrl());
         Assert.assertNull("Offline page is not supposed to exist",
-                getPageByClientId(new ClientId(OfflinePageBridge.BOOKMARK_NAMESPACE, "-42")));
+                OfflineTestUtil.getPageByClientId(
+                        new ClientId(OfflinePageBridge.BOOKMARK_NAMESPACE, "-42")));
     }
 
     @Test
@@ -170,10 +172,10 @@
         mActivityTestRule.loadUrl(mTestPage);
         savePage(SavePageResult.SUCCESS, mTestPage);
         Assert.assertNotNull("Offline page should be available, but it is not.",
-                getPageByClientId(TEST_CLIENT_ID));
+                OfflineTestUtil.getPageByClientId(TEST_CLIENT_ID));
         deletePage(TEST_CLIENT_ID, DeletePageResult.SUCCESS);
         Assert.assertNull("Offline page should be gone, but it is available.",
-                getPageByClientId(TEST_CLIENT_ID));
+                OfflineTestUtil.getPageByClientId(TEST_CLIENT_ID));
     }
 
     @Test
@@ -198,7 +200,7 @@
         String url = "https://www.google.com/";
         String namespace = "custom_tabs";
         savePageLater(url, namespace);
-        SavePageRequest[] requests = getRequestsInQueue();
+        SavePageRequest[] requests = OfflineTestUtil.getRequestsInQueue();
         Assert.assertEquals(1, requests.length);
         Assert.assertEquals(namespace, requests[0].getClientId().getNamespace());
         Assert.assertEquals(url, requests[0].getUrl());
@@ -206,7 +208,7 @@
         String url2 = "https://mail.google.com/";
         String namespace2 = "last_n";
         savePageLater(url2, namespace2);
-        requests = getRequestsInQueue();
+        requests = OfflineTestUtil.getRequestsInQueue();
         Assert.assertEquals(2, requests.length);
 
         HashSet<String> expectedUrls = new HashSet<>();
@@ -245,7 +247,7 @@
         String namespace2 = "last_n";
         savePageLater(url2, namespace2);
 
-        SavePageRequest[] requests = getRequestsInQueue();
+        SavePageRequest[] requests = OfflineTestUtil.getRequestsInQueue();
         Assert.assertEquals(2, requests.length);
 
         List<Long> requestsToRemove = new ArrayList<>();
@@ -255,7 +257,7 @@
                 removeRequestsFromQueue(requestsToRemove);
         Assert.assertEquals(requests[1].getRequestId(), removed.get(0).getRequestId());
         Assert.assertEquals(UpdateRequestResult.SUCCESS, removed.get(0).getUpdateRequestResult());
-        SavePageRequest[] remaining = getRequestsInQueue();
+        SavePageRequest[] remaining = OfflineTestUtil.getRequestsInQueue();
         Assert.assertEquals(1, remaining.length);
 
         Assert.assertEquals(requests[0].getRequestId(), remaining[0].getRequestId());
@@ -371,7 +373,7 @@
         Assert.assertTrue("Semaphore acquire failed. Timed out.",
                 semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
-        List<OfflinePageItem> pages = getAllPages();
+        List<OfflinePageItem> pages = OfflineTestUtil.getAllPages();
         Assert.assertEquals(originString, pages.get(0).getRequestOrigin());
     }
 
@@ -404,7 +406,7 @@
 
         Assert.assertTrue("Semaphore acquire failed. Timed out.",
                 semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        List<OfflinePageItem> pages = getAllPages();
+        List<OfflinePageItem> pages = OfflineTestUtil.getAllPages();
         Assert.assertEquals(originString, pages.get(0).getRequestOrigin());
     }
 
@@ -414,7 +416,7 @@
     public void testSavePageNoOrigin() throws Exception {
         mActivityTestRule.loadUrl(mTestPage);
         savePage(SavePageResult.SUCCESS, mTestPage);
-        List<OfflinePageItem> pages = getAllPages();
+        List<OfflinePageItem> pages = OfflineTestUtil.getAllPages();
         Assert.assertEquals("", pages.get(0).getRequestOrigin());
     }
 
@@ -426,7 +428,7 @@
     public void testGetLoadUrlParamsForOpeningMhtmlFileUrl() throws Exception {
         mActivityTestRule.loadUrl(mTestPage);
         savePage(SavePageResult.SUCCESS, mTestPage);
-        List<OfflinePageItem> allPages = getAllPages();
+        List<OfflinePageItem> allPages = OfflineTestUtil.getAllPages();
         Assert.assertEquals(1, allPages.size());
         OfflinePageItem offlinePage = allPages.get(0);
         File archiveFile = new File(offlinePage.getFilePath());
@@ -556,25 +558,6 @@
         Assert.assertEquals("Delete result incorrect.", expectedResult, deletePageResultRef.get());
     }
 
-    private List<OfflinePageItem> getAllPages() throws InterruptedException {
-        final List<OfflinePageItem> result = new ArrayList<OfflinePageItem>();
-        final Semaphore semaphore = new Semaphore(0);
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mOfflinePageBridge.getAllPages(new Callback<List<OfflinePageItem>>() {
-                    @Override
-                    public void onResult(List<OfflinePageItem> pages) {
-                        result.addAll(pages);
-                        semaphore.release();
-                    }
-                });
-            }
-        });
-        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        return result;
-    }
-
     private List<OfflinePageItem> getPagesByNamespace(final String namespace)
             throws InterruptedException {
         final List<OfflinePageItem> result = new ArrayList<OfflinePageItem>();
@@ -605,49 +588,10 @@
         });
     }
 
-    private OfflinePageItem getPageByClientId(ClientId clientId) throws InterruptedException {
-        final OfflinePageItem[] result = {null};
-        final Semaphore semaphore = new Semaphore(0);
-        final List<ClientId> clientIdList = new ArrayList<>();
-        clientIdList.add(clientId);
-
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mOfflinePageBridge.getPagesByClientIds(
-                        clientIdList, new Callback<List<OfflinePageItem>>() {
-                            @Override
-                            public void onResult(List<OfflinePageItem> items) {
-                                if (!items.isEmpty()) {
-                                    result[0] = items.get(0);
-                                }
-                                semaphore.release();
-                            }
-                        });
-            }
-        });
-        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        return result[0];
-    }
-
     private Set<String> getUrlsExistOfflineFromSet(final Set<String> query)
-            throws InterruptedException {
+            throws InterruptedException, TimeoutException {
         final Set<String> result = new HashSet<>();
-        final List<OfflinePageItem> pages = new ArrayList<>();
-        final Semaphore semaphore = new Semaphore(0);
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mOfflinePageBridge.getAllPages(new Callback<List<OfflinePageItem>>() {
-                    @Override
-                    public void onResult(List<OfflinePageItem> offlinePages) {
-                        pages.addAll(offlinePages);
-                        semaphore.release();
-                    }
-                });
-            }
-        });
-        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        final List<OfflinePageItem> pages = OfflineTestUtil.getAllPages();
         for (String url : query) {
             for (OfflinePageItem page : pages) {
                 if (url.equals(page.getUrl())) {
@@ -679,25 +623,6 @@
         Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
     }
 
-    private SavePageRequest[] getRequestsInQueue() throws InterruptedException {
-        final AtomicReference<SavePageRequest[]> ref = new AtomicReference<>();
-        final Semaphore semaphore = new Semaphore(0);
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mOfflinePageBridge.getRequestsInQueue(new Callback<SavePageRequest[]>() {
-                    @Override
-                    public void onResult(SavePageRequest[] requestsInQueue) {
-                        ref.set(requestsInQueue);
-                        semaphore.release();
-                    }
-                });
-            }
-        });
-        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        return ref.get();
-    }
-
     private List<OfflinePageBridge.RequestRemovedResult> removeRequestsFromQueue(
             final List<Long> requestsToRemove) throws InterruptedException {
         final AtomicReference<List<OfflinePageBridge.RequestRemovedResult>> ref =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java
index 4d517b2..49985c9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageRequestTest.java
@@ -18,9 +18,7 @@
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.offlinepages.OfflinePageBridge.OfflinePageModelObserver;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge.SavePageCallback;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -50,7 +48,6 @@
     @Before
     public void setUp() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
-        final Semaphore semaphore = new Semaphore(0);
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
@@ -58,23 +55,9 @@
                     NetworkChangeNotifier.init();
                 }
                 NetworkChangeNotifier.forceConnectivityState(true);
-
-                Profile profile = Profile.getLastUsedProfile();
-                mOfflinePageBridge = OfflinePageBridge.getForProfile(profile);
-                if (mOfflinePageBridge.isOfflinePageModelLoaded()) {
-                    semaphore.release();
-                } else {
-                    mOfflinePageBridge.addObserver(new OfflinePageModelObserver() {
-                        @Override
-                        public void offlinePageModelLoaded() {
-                            semaphore.release();
-                            mOfflinePageBridge.removeObserver(this);
-                        }
-                    });
-                }
             }
         });
-        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        mOfflinePageBridge = OfflineTestUtil.getOfflinePageBridge();
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java
index ed0c917c..1aa75406 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageSavePageLaterEvaluationTest.java
@@ -405,24 +405,10 @@
     /**
      * Get saved offline pages and align them with the metadata we got from testing.
      */
-    private void loadSavedPages() throws InterruptedException {
-        final Semaphore semaphore = new Semaphore(0);
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mBridge.getAllPages(new Callback<List<OfflinePageItem>>() {
-                    @Override
-                    public void onResult(List<OfflinePageItem> pages) {
-                        for (OfflinePageItem page : pages) {
-                            mRequestMetadata.get(page.getOfflineId()).mPage = page;
-                        }
-                        semaphore.release();
-                    }
-                });
-            }
-        });
-        checkTrue(semaphore.tryAcquire(GET_PAGES_TIMEOUT_MS, TimeUnit.MILLISECONDS),
-                "Timed out when getting all offline pages");
+    private void loadSavedPages() throws TimeoutException, InterruptedException {
+        for (OfflinePageItem page : OfflineTestUtil.getAllPages()) {
+            mRequestMetadata.get(page.getOfflineId()).mPage = page;
+        }
     }
 
     private boolean copyToShareableLocation(File src, File dst) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflineTestUtil.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflineTestUtil.java
new file mode 100644
index 0000000..8e949e6
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflineTestUtil.java
@@ -0,0 +1,119 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.offlinepages;
+
+import android.support.annotation.Nullable;
+
+import org.junit.Assert;
+
+import org.chromium.base.Callback;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.chrome.browser.download.items.OfflineContentAggregatorFactory;
+import org.chromium.chrome.browser.offlinepages.OfflinePageBridge.OfflinePageModelObserver;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.offline_items_collection.OfflineItem;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/** Test utility functions for OfflinePages. */
+@JNINamespace("offline_pages")
+public class OfflineTestUtil {
+    // Gets all the URLs in the request queue.
+    public static SavePageRequest[] getRequestsInQueue()
+            throws TimeoutException, InterruptedException {
+        final AtomicReference<SavePageRequest[]> result = new AtomicReference<>();
+        final CallbackHelper callbackHelper = new CallbackHelper();
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            nativeGetRequestsInQueue((SavePageRequest[] requests) -> {
+                result.set(requests);
+                callbackHelper.notifyCalled();
+            });
+        });
+        callbackHelper.waitForCallback(0);
+        return result.get();
+    }
+
+    // Gets all available offline pages.
+    public static List<OfflinePageItem> getAllPages()
+            throws TimeoutException, InterruptedException {
+        final AtomicReference<List<OfflinePageItem>> result =
+                new AtomicReference<List<OfflinePageItem>>();
+        final CallbackHelper callbackHelper = new CallbackHelper();
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            nativeGetAllPages(new ArrayList<OfflinePageItem>(), (List<OfflinePageItem> items) -> {
+                result.set(items);
+                callbackHelper.notifyCalled();
+            });
+        });
+        callbackHelper.waitForCallback(0);
+        return result.get();
+    }
+
+    // Returns the OfflinePageItem with the given clientId, or null if one doesn't exist.
+    public static @Nullable OfflinePageItem getPageByClientId(ClientId clientId)
+            throws TimeoutException, InterruptedException {
+        ArrayList<OfflinePageItem> result = new ArrayList<OfflinePageItem>();
+        for (OfflinePageItem item : getAllPages()) {
+            if (item.getClientId().equals(clientId)) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    // Returns all OfflineItems provided by the OfflineContentProvider.
+    public static List<OfflineItem> getOfflineItems()
+            throws TimeoutException, InterruptedException {
+        CallbackHelper finished = new CallbackHelper();
+        final AtomicReference<ArrayList<OfflineItem>> result =
+                new AtomicReference<ArrayList<OfflineItem>>();
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            OfflineContentAggregatorFactory
+                    .forProfile(Profile.getLastUsedProfile().getOriginalProfile())
+                    .getAllItems(items -> {
+                        result.set(items);
+                        finished.notifyCalled();
+                    });
+        });
+        finished.waitForCallback(0);
+        return result.get();
+    }
+
+    // Waits for the offline model to initialize and returns an OfflinePageBridge.
+    public static OfflinePageBridge getOfflinePageBridge()
+            throws TimeoutException, InterruptedException {
+        final CallbackHelper ready = new CallbackHelper();
+        final AtomicReference<OfflinePageBridge> result = new AtomicReference<OfflinePageBridge>();
+        ThreadUtils.runOnUiThread(() -> {
+            OfflinePageBridge bridge =
+                    OfflinePageBridge.getForProfile(Profile.getLastUsedProfile());
+            if (bridge == null || bridge.isOfflinePageModelLoaded()) {
+                result.set(bridge);
+                ready.notifyCalled();
+                return;
+            }
+            bridge.addObserver(new OfflinePageModelObserver() {
+                @Override
+                public void offlinePageModelLoaded() {
+                    result.set(bridge);
+                    ready.notifyCalled();
+                    bridge.removeObserver(this);
+                }
+            });
+        });
+        ready.waitForCallback(0);
+        Assert.assertTrue(result.get() != null);
+        return result.get();
+    }
+
+    private static native void nativeGetRequestsInQueue(Callback<SavePageRequest[]> callback);
+    private static native void nativeGetAllPages(
+            List<OfflinePageItem> offlinePages, final Callback<List<OfflinePageItem>> callback);
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java
index 61cf5ffb..313f225 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/RecentTabsTest.java
@@ -14,26 +14,21 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.Callback;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.offlinepages.OfflinePageBridge.OfflinePageModelObserver;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.net.NetworkChangeNotifier;
 import org.chromium.net.test.EmbeddedTestServer;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.Callable;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /** Integration tests for the Last 1 feature of Offline Pages. */
 @RunWith(ChromeJUnit4ClassRunner.class)
@@ -49,34 +44,6 @@
     private EmbeddedTestServer mTestServer;
     private String mTestPage;
 
-    private void initializeBridgeForProfile(final boolean incognitoProfile)
-            throws InterruptedException {
-        final Semaphore semaphore = new Semaphore(0);
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                Profile profile = Profile.getLastUsedProfile();
-                if (incognitoProfile) {
-                    profile = profile.getOffTheRecordProfile();
-                }
-                // Ensure we start in an offline state.
-                mOfflinePageBridge = OfflinePageBridge.getForProfile(profile);
-                if (mOfflinePageBridge == null || mOfflinePageBridge.isOfflinePageModelLoaded()) {
-                    semaphore.release();
-                    return;
-                }
-                mOfflinePageBridge.addObserver(new OfflinePageModelObserver() {
-                    @Override
-                    public void offlinePageModelLoaded() {
-                        semaphore.release();
-                        mOfflinePageBridge.removeObserver(this);
-                    }
-                });
-            }
-        });
-        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-    }
-
     @Before
     public void setUp() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
@@ -91,8 +58,7 @@
             }
         });
 
-        initializeBridgeForProfile(false);
-
+        mOfflinePageBridge = OfflineTestUtil.getOfflinePageBridge();
         mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
         mTestPage = mTestServer.getURL(TEST_PAGE);
     }
@@ -113,7 +79,7 @@
                 new ClientId(OfflinePageBridge.LAST_N_NAMESPACE, Integer.toString(tab.getId()));
 
         // The tab should be foreground and so no snapshot should exist.
-        Assert.assertNull(getPageByClientId(firstTabClientId));
+        Assert.assertNull(OfflineTestUtil.getPageByClientId(firstTabClientId));
 
         // Note, that switching to a new tab must occur after the SnapshotController believes the
         // page quality is good enough.  With the debug flag, the delay after DomContentLoaded is 0
@@ -144,7 +110,7 @@
         TabModelSelector tabModelSelector = tab.getTabModelSelector();
         Assert.assertEquals(tabModelSelector.getCurrentTab(), tab);
         Assert.assertFalse(tab.isHidden());
-        Assert.assertNull(getPageByClientId(firstTabClientId));
+        Assert.assertNull(OfflineTestUtil.getPageByClientId(firstTabClientId));
 
         // The tab model is expected to support pending closures.
         final TabModel tabModel = tabModelSelector.getModelForTabId(tab.getId());
@@ -163,7 +129,7 @@
 
         // Wait a bit and checks that no snapshot was created.
         Thread.sleep(100); // Note: Flakiness potential here.
-        Assert.assertNull(getPageByClientId(firstTabClientId));
+        Assert.assertNull(OfflineTestUtil.getPageByClientId(firstTabClientId));
 
         // Undo the closure and make sure the tab is again the current one on foreground.
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@@ -185,49 +151,9 @@
         waitForPageWithClientId(firstTabClientId);
     }
 
-    private void waitForPageWithClientId(final ClientId clientId) throws InterruptedException {
-        if (getPageByClientId(clientId) != null) return;
-
-        final Semaphore semaphore = new Semaphore(0);
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mOfflinePageBridge.addObserver(new OfflinePageModelObserver() {
-                    @Override
-                    public void offlinePageAdded(OfflinePageItem newPage) {
-                        if (newPage.getClientId().equals(clientId)) {
-                            mOfflinePageBridge.removeObserver(this);
-                            semaphore.release();
-                        }
-                    }
-                });
-            }
-        });
-        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-    }
-
-    private OfflinePageItem getPageByClientId(ClientId clientId) throws InterruptedException {
-        final OfflinePageItem[] result = {null};
-        final Semaphore semaphore = new Semaphore(0);
-        final List<ClientId> clientIdList = new ArrayList<>();
-        clientIdList.add(clientId);
-
-        ThreadUtils.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mOfflinePageBridge.getPagesByClientIds(
-                        clientIdList, new Callback<List<OfflinePageItem>>() {
-                            @Override
-                            public void onResult(List<OfflinePageItem> items) {
-                                if (!items.isEmpty()) {
-                                    result[0] = items.get(0);
-                                }
-                                semaphore.release();
-                            }
-                        });
-            }
-        });
-        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-        return result[0];
+    private void waitForPageWithClientId(final ClientId clientId)
+            throws TimeoutException, InterruptedException {
+        CriteriaHelper.pollInstrumentationThread(
+                () -> { return OfflineTestUtil.getPageByClientId(clientId) != null; });
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFeedFlowTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFeedFlowTest.java
index 4432e51f..4f2097ef 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFeedFlowTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/prefetch/PrefetchFeedFlowTest.java
@@ -34,6 +34,7 @@
 import org.chromium.chrome.browser.feed.TestNetworkClient;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
 import org.chromium.chrome.browser.offlinepages.OfflinePageItem;
+import org.chromium.chrome.browser.offlinepages.OfflineTestUtil;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.ChromeActivityTestRule;
@@ -202,20 +203,12 @@
     }
 
     private OfflineItem findItemByUrl(String url) throws InterruptedException, TimeoutException {
-        CallbackHelper finished = new CallbackHelper();
-        final AtomicReference<OfflineItem> result = new AtomicReference<OfflineItem>();
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            offlineContentProvider().getAllItems(items -> {
-                for (OfflineItem item : items) {
-                    if (item.pageUrl.equals(URL1)) {
-                        result.set(item);
-                    }
-                }
-                finished.notifyCalled();
-            });
-        });
-        finished.waitForCallback(0);
-        return result.get();
+        for (OfflineItem item : OfflineTestUtil.getOfflineItems()) {
+            if (item.pageUrl.equals(URL1)) {
+                return item;
+            }
+        }
+        return null;
     }
 
     private Bitmap findVisuals(ContentId id) throws InterruptedException, TimeoutException {
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index ecd71a4..50cbe68 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1152,13 +1152,19 @@
   <message name="IDS_SUPERVISED_USER_IMPORT_BUBBLE_TEXT" desc="Text shown in notification bubble when user enters name of user that can be imported.">
     Looks like you're already managing a user by that name. Did you want to <ph name="LINK_START">$1<ex>&gt;a&lt;</ex></ph>import <ph name="USER_DISPLAY_NAME">$2<ex>John</ex></ph> to this device<ph name="LINK_END">$3<ex>&gt;/a&lt;</ex></ph>?
   </message>
-  <!-- Supervised users deprecation notification -->
+  <!-- Supervised users deprecation notifications -->
   <message name="IDS_SUPERVISED_USER_EXPIRING_NOTIFICATION_TITLE" desc="Title of notification displayed on supervised user login indicating that the feature is expiring soon.">
-     Your supervised user account is expiring soon.
+    Your supervised user account is expiring soon.
   </message>
   <message name="IDS_SUPERVISED_USER_EXPIRING_NOTIFICATION_BODY" desc="Body of notification displayed on supervised user login indicating that the feature is expiring soon.">
     Please save your local files and set up a new account.
   </message>
+  <message name="IDS_MANAGER_SUPERVISED_USER_EXPIRING_NOTIFICATION_TITLE" desc="Title of notification displayed on manager login indicating that the supervised user feature is expiring soon.">
+    Your supervised user account(s) are expiring soon.
+  </message>
+  <message name="IDS_MANAGER_SUPERVISED_USER_EXPIRING_NOTIFICATION_BODY" desc="Body of notification displayed on manager user login indicating that the supervised user feature is expiring soon.">
+    Supervised Users will not be available in an upcoming update.  Please back up any data from your Supervised User accounts on this device.
+  </message>
   <message name="IDS_SUPERVISED_USER_EXPIRING_NOTIFICATION_LEARN_MORE" desc="Learn more text for button on Supervised User expiration notification.">
     Learn more
   </message>
diff --git a/chrome/app/nibs/generate_localizer.py b/chrome/app/nibs/generate_localizer.py
index c93f9d6..6892968 100755
--- a/chrome/app/nibs/generate_localizer.py
+++ b/chrome/app/nibs/generate_localizer.py
@@ -36,7 +36,7 @@
 
 static const UILocalizerResourceMap kUIResources[] = {
 %(resource_map_list)s  };
-static const size_t kUIResourcesSize = arraysize(kUIResources);
+static const size_t kUIResourcesSize = base::size(kUIResources);
 
 #endif  // CHROME_APP_NIBS_LOCALIZER_TABLE_H_
 '''
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 2314ec4a..c0f23ce 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2560,6 +2560,7 @@
       ":delta_file_proto",
       ":explore_sites_proto",
       ":jni_headers",
+      ":usage_stats_proto",
       "//chrome/browser/android/webapk:proto",
       "//chrome/services/media_gallery_util:manifest",  # TODO(xingliu): Tries to remove this.
       "//chrome/services/media_gallery_util/public/cpp",
@@ -4984,6 +4985,12 @@
       "android/explore_sites/catalog.proto",
     ]
   }
+
+  proto_library("usage_stats_proto") {
+    sources = [
+      "android/usage_stats/website_event.proto",
+    ]
+  }
 }
 
 if (is_win) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 585f50d3..79e7168 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3577,6 +3577,12 @@
      FEATURE_VALUE_TYPE(features::kEnableVizHitTestDrawQuad)},
 
 #if BUILDFLAG(ENABLE_PDF)
+#if defined(OS_CHROMEOS)
+    {"pdf-annotations", flag_descriptions::kPdfAnnotations,
+     flag_descriptions::kPdfAnnotationsDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chrome_pdf::features::kPDFAnnotations)},
+#endif  // defined(OS_CHROMEOS)
+
     {"pdf-form-save", flag_descriptions::kPdfFormSaveName,
      flag_descriptions::kPdfFormSaveDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(chrome_pdf::features::kSaveEditedPDFForm)},
@@ -4269,6 +4275,10 @@
      flag_descriptions::kEnableMediaSessionServiceName,
      flag_descriptions::kEnableMediaSessionServiceDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(media_session::features::kMediaSessionService)},
+    {"enable-safe-browsing-ap-download-verdicts",
+     flag_descriptions::kSafeBrowsingUseAPDownloadVerdictsName,
+     flag_descriptions::kSafeBrowsingUseAPDownloadVerdictsDescription,
+     kOsDesktop, FEATURE_VALUE_TYPE(safe_browsing::kUseAPDownloadProtection)},
 };
 
 class FlagsStateSingleton {
diff --git a/chrome/browser/android/download/service/download_task_scheduler.h b/chrome/browser/android/download/service/download_task_scheduler.h
index ec4624cc..cfa9005 100644
--- a/chrome/browser/android/download/service/download_task_scheduler.h
+++ b/chrome/browser/android/download/service/download_task_scheduler.h
@@ -10,8 +10,7 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "components/download/public/background_service/download_task_types.h"
-#include "components/download/public/background_service/task_scheduler.h"
+#include "components/download/public/task/task_scheduler.h"
 
 namespace download {
 namespace android {
diff --git a/chrome/browser/android/usage_stats/usage_stats_bridge.cc b/chrome/browser/android/usage_stats/usage_stats_bridge.cc
index 221b990..977c569 100644
--- a/chrome/browser/android/usage_stats/usage_stats_bridge.cc
+++ b/chrome/browser/android/usage_stats/usage_stats_bridge.cc
@@ -13,19 +13,13 @@
 
 namespace usage_stats {
 
-using base::android::JavaParamRef;
-using base::android::JavaRef;
-
-const char kUsageStatsFolder[] = "usage_stats";
-
 static jlong JNI_UsageStatsBridge_Init(JNIEnv* env,
                                        const JavaParamRef<jobject>& j_this,
                                        const JavaParamRef<jobject>& j_profile) {
   Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile);
-  base::FilePath usage_stats_dir(profile->GetPath().Append(kUsageStatsFolder));
 
   std::unique_ptr<UsageStatsDatabase> usage_stats_database =
-      std::make_unique<UsageStatsDatabase>(usage_stats_dir);
+      std::make_unique<UsageStatsDatabase>(profile);
 
   UsageStatsBridge* native_usage_stats_bridge =
       new UsageStatsBridge(std::move(usage_stats_database));
@@ -44,62 +38,54 @@
   delete this;
 }
 
-void UsageStatsBridge::GetAllEvents(
-    JNIEnv* j_env,
-    const base::android::JavaRef<jobject>& j_this,
-    const base::android::JavaRef<jobject>& j_callback) {}
+void UsageStatsBridge::GetAllEvents(JNIEnv* j_env,
+                                    const JavaRef<jobject>& j_this,
+                                    const JavaRef<jobject>& j_callback) {}
 
-void UsageStatsBridge::QueryEventsInRange(
-    JNIEnv* j_env,
-    const base::android::JavaRef<jobject>& j_this,
-    const jlong j_start,
-    const jlong j_end,
-    const base::android::JavaRef<jobject>& j_callback) {}
+void UsageStatsBridge::QueryEventsInRange(JNIEnv* j_env,
+                                          const JavaRef<jobject>& j_this,
+                                          const jlong j_start,
+                                          const jlong j_end,
+                                          const JavaRef<jobject>& j_callback) {}
 
-void UsageStatsBridge::AddEvents(
-    JNIEnv* j_env,
-    const base::android::JavaRef<jobject>& j_this,
-    const base::android::JavaRef<jobjectArray>& j_events,
-    const base::android::JavaRef<jobject>& j_callback) {}
+void UsageStatsBridge::AddEvents(JNIEnv* j_env,
+                                 const JavaRef<jobject>& j_this,
+                                 const JavaRef<jobjectArray>& j_events,
+                                 const JavaRef<jobject>& j_callback) {}
 
-void UsageStatsBridge::DeleteAllEvents(
-    JNIEnv* j_env,
-    const base::android::JavaRef<jobject>& j_this,
-    const base::android::JavaRef<jobject>& j_callback) {}
+void UsageStatsBridge::DeleteAllEvents(JNIEnv* j_env,
+                                       const JavaRef<jobject>& j_this,
+                                       const JavaRef<jobject>& j_callback) {}
 
-void UsageStatsBridge::DeleteEventsInRange(
-    JNIEnv* j_env,
-    const base::android::JavaRef<jobject>& j_this,
-    const jlong j_start,
-    const jlong j_end,
-    const base::android::JavaRef<jobject>& j_callback) {}
+void UsageStatsBridge::DeleteEventsInRange(JNIEnv* j_env,
+                                           const JavaRef<jobject>& j_this,
+                                           const jlong j_start,
+                                           const jlong j_end,
+                                           const JavaRef<jobject>& j_callback) {
+}
 
 void UsageStatsBridge::DeleteEventsWithMatchingDomains(
     JNIEnv* j_env,
-    const base::android::JavaRef<jobject>& j_this,
-    const base::android::JavaRef<jobjectArray>& j_domains,
-    const base::android::JavaRef<jobject>& j_callback) {}
+    const JavaRef<jobject>& j_this,
+    const JavaRef<jobjectArray>& j_domains,
+    const JavaRef<jobject>& j_callback) {}
 
-void UsageStatsBridge::GetAllSuspensions(
-    JNIEnv* j_env,
-    const base::android::JavaRef<jobject>& j_this,
-    const base::android::JavaRef<jobject>& j_callback) {}
+void UsageStatsBridge::GetAllSuspensions(JNIEnv* j_env,
+                                         const JavaRef<jobject>& j_this,
+                                         const JavaRef<jobject>& j_callback) {}
 
-void UsageStatsBridge::SetSuspensions(
-    JNIEnv* j_env,
-    const base::android::JavaRef<jobject>& j_this,
-    const base::android::JavaRef<jobjectArray>& j_domains,
-    const base::android::JavaRef<jobject>& j_callback) {}
+void UsageStatsBridge::SetSuspensions(JNIEnv* j_env,
+                                      const JavaRef<jobject>& j_this,
+                                      const JavaRef<jobjectArray>& j_domains,
+                                      const JavaRef<jobject>& j_callback) {}
 
-void UsageStatsBridge::GetAllTokenMappings(
-    JNIEnv* j_env,
-    const base::android::JavaRef<jobject>& j_this,
-    const base::android::JavaRef<jobject>& j_callback) {}
+void UsageStatsBridge::GetAllTokenMappings(JNIEnv* j_env,
+                                           const JavaRef<jobject>& j_this,
+                                           const JavaRef<jobject>& j_callback) {
+}
 
-void UsageStatsBridge::SetTokenMappings(
-    JNIEnv* j_env,
-    const base::android::JavaRef<jobject>& j_this,
-    const base::android::JavaRef<jobject>& j_mappings,
-    const base::android::JavaRef<jobject>& j_callback) {}
-
+void UsageStatsBridge::SetTokenMappings(JNIEnv* j_env,
+                                        const JavaRef<jobject>& j_this,
+                                        const JavaRef<jobject>& j_mappings,
+                                        const JavaRef<jobject>& j_callback) {}
 }  // namespace usage_stats
diff --git a/chrome/browser/android/usage_stats/usage_stats_bridge.h b/chrome/browser/android/usage_stats/usage_stats_bridge.h
index 1da0706..5fa0417 100644
--- a/chrome/browser/android/usage_stats/usage_stats_bridge.h
+++ b/chrome/browser/android/usage_stats/usage_stats_bridge.h
@@ -12,6 +12,9 @@
 
 namespace usage_stats {
 
+using base::android::JavaParamRef;
+using base::android::JavaRef;
+
 class UsageStatsDatabase;
 
 /* Native counterpart of UsageStatsBridge.java. Holds non-owning pointers to
@@ -24,56 +27,55 @@
       std::unique_ptr<UsageStatsDatabase> usage_stats_database);
   ~UsageStatsBridge();
 
-  void Destroy(JNIEnv* j_env, const base::android::JavaRef<jobject>& j_this);
+  void Destroy(JNIEnv* j_env, const JavaRef<jobject>& j_this);
 
   void GetAllEvents(JNIEnv* j_env,
-                    const base::android::JavaRef<jobject>& j_this,
-                    const base::android::JavaRef<jobject>& j_callback);
+                    const JavaRef<jobject>& j_this,
+                    const JavaRef<jobject>& j_callback);
 
   void QueryEventsInRange(JNIEnv* j_env,
-                          const base::android::JavaRef<jobject>& j_this,
+                          const JavaRef<jobject>& j_this,
                           const jlong j_start,
                           const jlong j_end,
-                          const base::android::JavaRef<jobject>& j_callback);
+                          const JavaRef<jobject>& j_callback);
 
   void AddEvents(JNIEnv* j_env,
-                 const base::android::JavaRef<jobject>& j_this,
-                 const base::android::JavaRef<jobjectArray>& j_events,
-                 const base::android::JavaRef<jobject>& j_callback);
+                 const JavaRef<jobject>& j_this,
+                 const JavaRef<jobjectArray>& j_events,
+                 const JavaRef<jobject>& j_callback);
 
   void DeleteAllEvents(JNIEnv* j_env,
-                       const base::android::JavaRef<jobject>& j_this,
-                       const base::android::JavaRef<jobject>& j_callback);
+                       const JavaRef<jobject>& j_this,
+                       const JavaRef<jobject>& j_callback);
 
   void DeleteEventsInRange(JNIEnv* j_env,
-                           const base::android::JavaRef<jobject>& j_this,
+                           const JavaRef<jobject>& j_this,
                            const jlong j_start,
                            const jlong j_end,
-                           const base::android::JavaRef<jobject>& j_callback);
+                           const JavaRef<jobject>& j_callback);
 
-  void DeleteEventsWithMatchingDomains(
-      JNIEnv* j_env,
-      const base::android::JavaRef<jobject>& j_this,
-      const base::android::JavaRef<jobjectArray>& j_domains,
-      const base::android::JavaRef<jobject>& j_callback);
+  void DeleteEventsWithMatchingDomains(JNIEnv* j_env,
+                                       const JavaRef<jobject>& j_this,
+                                       const JavaRef<jobjectArray>& j_domains,
+                                       const JavaRef<jobject>& j_callback);
 
   void GetAllSuspensions(JNIEnv* j_env,
-                         const base::android::JavaRef<jobject>& j_this,
-                         const base::android::JavaRef<jobject>& j_callback);
+                         const JavaRef<jobject>& j_this,
+                         const JavaRef<jobject>& j_callback);
 
   void SetSuspensions(JNIEnv* j_env,
-                      const base::android::JavaRef<jobject>& j_this,
-                      const base::android::JavaRef<jobjectArray>& j_domains,
-                      const base::android::JavaRef<jobject>& j_callback);
+                      const JavaRef<jobject>& j_this,
+                      const JavaRef<jobjectArray>& j_domains,
+                      const JavaRef<jobject>& j_callback);
 
   void GetAllTokenMappings(JNIEnv* j_env,
-                           const base::android::JavaRef<jobject>& j_this,
-                           const base::android::JavaRef<jobject>& j_callback);
+                           const JavaRef<jobject>& j_this,
+                           const JavaRef<jobject>& j_callback);
 
   void SetTokenMappings(JNIEnv* j_env,
-                        const base::android::JavaRef<jobject>& j_this,
-                        const base::android::JavaRef<jobject>& j_mappings,
-                        const base::android::JavaRef<jobject>& j_callback);
+                        const JavaRef<jobject>& j_this,
+                        const JavaRef<jobject>& j_mappings,
+                        const JavaRef<jobject>& j_callback);
 
  private:
   std::unique_ptr<UsageStatsDatabase> usage_stats_database_;
diff --git a/chrome/browser/android/usage_stats/usage_stats_database.cc b/chrome/browser/android/usage_stats/usage_stats_database.cc
index 7288a94..f0593a1 100644
--- a/chrome/browser/android/usage_stats/usage_stats_database.cc
+++ b/chrome/browser/android/usage_stats/usage_stats_database.cc
@@ -4,9 +4,38 @@
 
 #include "chrome/browser/android/usage_stats/usage_stats_database.h"
 
+#include "base/task/post_task.h"
+#include "components/leveldb_proto/content/proto_database_provider_factory.h"
+#include "components/leveldb_proto/proto_database_provider.h"
+
 namespace usage_stats {
 
-UsageStatsDatabase::UsageStatsDatabase(const base::FilePath& database_folder) {}
+const char kNamespace[] = "usage_stats";
+const char kWebsiteEventPrefix[] = "website_event";
+const char kSuspensionPrefix[] = "suspension";
+const char kTokenMappingPrefix[] = "token_mapping";
+
+UsageStatsDatabase::UsageStatsDatabase(Profile* profile)
+    : weak_ptr_factory_(this) {
+  leveldb_proto::ProtoDatabaseProvider* db_provider =
+      leveldb_proto::ProtoDatabaseProviderFactory::GetInstance()
+          ->GetForBrowserContext(profile);
+
+  base::FilePath usage_stats_dir = profile->GetPath().Append(kNamespace);
+
+  scoped_refptr<base::SequencedTaskRunner> db_task_runner =
+      base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
+
+  website_event_db_ = db_provider->GetDB<WebsiteEvent>(
+      kNamespace, kWebsiteEventPrefix, usage_stats_dir, db_task_runner);
+
+  suspension_db_ = db_provider->GetDB<Suspension>(
+      kNamespace, kSuspensionPrefix, usage_stats_dir, db_task_runner);
+
+  token_mapping_db_ = db_provider->GetDB<TokenMapping>(
+      kNamespace, kTokenMappingPrefix, usage_stats_dir, db_task_runner);
+}
 
 UsageStatsDatabase::~UsageStatsDatabase() = default;
 
diff --git a/chrome/browser/android/usage_stats/usage_stats_database.h b/chrome/browser/android/usage_stats/usage_stats_database.h
index 2bd24133b..e074faac 100644
--- a/chrome/browser/android/usage_stats/usage_stats_database.h
+++ b/chrome/browser/android/usage_stats/usage_stats_database.h
@@ -5,20 +5,80 @@
 #ifndef CHROME_BROWSER_ANDROID_USAGE_STATS_USAGE_STATS_DATABASE_H_
 #define CHROME_BROWSER_ANDROID_USAGE_STATS_USAGE_STATS_DATABASE_H_
 
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_set.h"
 #include "base/files/file_path.h"
 #include "base/macros.h"
+#include "chrome/browser/android/usage_stats/website_event.pb.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/leveldb_proto/proto_database.h"
 
 namespace usage_stats {
 
-// UsageStatsDatabase will be used to store website events, suspensions and
-// token to fdqn mappings in LevelDB
+// Stores website events, suspensions and token to fully-qualified domain name
+// (FQDN) mappings in LevelDB.
 class UsageStatsDatabase {
  public:
-  // Initializes the database with |database_folder|
-  explicit UsageStatsDatabase(const base::FilePath& database_folder);
+  enum class Error { kNoError, kUnknownError };
+
+  using EventsCallback =
+      base::OnceCallback<void(Error, std::vector<WebsiteEvent>)>;
+
+  using SuspensionCallback =
+      base::OnceCallback<void(Error, std::vector<std::string>)>;
+
+  using TokenMappingsCallback =
+      base::OnceCallback<void(Error, std::map<std::string, std::string>)>;
+
+  using StatusCallback = base::OnceCallback<void(Error)>;
+
+  // Initializes the database with user |profile|.
+  explicit UsageStatsDatabase(Profile* profile);
 
   ~UsageStatsDatabase();
 
+  void GetAllEvents(EventsCallback callback);
+
+  // |start| and |end| are timestamps representing milliseconds since the
+  // beginning of the Unix Epoch.
+  void QueryEventsInRange(int64_t start, int64_t end, EventsCallback callback);
+
+  void AddEvents(std::vector<WebsiteEvent> events, StatusCallback callback);
+
+  void DeleteAllEvents(StatusCallback callback);
+
+  // |start| and |end| are timestamps representing milliseconds since the
+  // beginning of the Unix Epoch.
+  void DeleteEventsInRange(int64_t start, int64_t end, StatusCallback callback);
+
+  void DeleteEventsWithMatchingDomains(base::flat_set<std::string> domains,
+                                       StatusCallback callback);
+
+  void GetAllSuspensions(SuspensionCallback callback);
+
+  // Persists all the suspensions in |domains| and deletes any suspensions *not*
+  // in |domains|.
+  void SetSuspensions(base::flat_set<std::string> domains,
+                      StatusCallback callback);
+
+  void GetAllTokenMappings(TokenMappingsCallback callback);
+
+  // Persists all the mappings in |mappings| and deletes any mappings *not* in
+  // |mappings|. The map's key is the token, and its value is the FQDN.
+  void SetTokenMappings(std::map<std::string, std::string> mappings,
+                        StatusCallback callback);
+
+ private:
+  std::unique_ptr<leveldb_proto::ProtoDatabase<WebsiteEvent>> website_event_db_;
+  std::unique_ptr<leveldb_proto::ProtoDatabase<Suspension>> suspension_db_;
+  std::unique_ptr<leveldb_proto::ProtoDatabase<TokenMapping>> token_mapping_db_;
+
+  base::WeakPtrFactory<UsageStatsDatabase> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(UsageStatsDatabase);
 };
 
diff --git a/chrome/browser/android/usage_stats/website_event.proto b/chrome/browser/android/usage_stats/website_event.proto
new file mode 100644
index 0000000..4ae0326
--- /dev/null
+++ b/chrome/browser/android/usage_stats/website_event.proto
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package usage_stats;
+
+message WebsiteEvent {
+  // Fully-qualified domain name of the website. Example: "docs.google.com".
+  optional string fqdn = 1;
+
+  // Timestamp when the event occurred.
+  optional Timestamp timestamp = 2;
+
+  // Type of the event.
+  enum EventType {
+    UNKNOWN = 0;
+    // The user has started (or resumed) browsing the website.
+    START_BROWSING = 1;
+    // The user has stopped browsing the website.
+    STOP_BROWSING = 2;
+  }
+  optional EventType type = 3;
+}
+
+message Timestamp {
+  optional int64 seconds = 1;
+  optional int32 nanos = 2;
+}
+
+message TokenMapping {
+  optional string token = 1;
+  optional string fqdn = 2;
+}
+
+message Suspension {
+  optional string fqdn = 1;
+}
diff --git a/chrome/browser/badging/badge_service_delegate.h b/chrome/browser/badging/badge_service_delegate.h
index 83962d07f..4c01c82 100644
--- a/chrome/browser/badging/badge_service_delegate.h
+++ b/chrome/browser/badging/badge_service_delegate.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_BADGING_BADGE_SERVICE_DELEGATE_H_
 #define CHROME_BROWSER_BADGING_BADGE_SERVICE_DELEGATE_H_
 
+#include "base/callback.h"
 #include "base/optional.h"
 
 namespace content {
@@ -15,12 +16,29 @@
 // UI-layer, allowing for a platform specific implementation of badging.
 class BadgeServiceDelegate {
  public:
+  using SetBadgeCallback =
+      base::RepeatingCallback<void(content::WebContents*,
+                                   base::Optional<uint64_t>)>;
+  using ClearBadgeCallback =
+      base::RepeatingCallback<void(content::WebContents*)>;
+
+  BadgeServiceDelegate();
+  ~BadgeServiceDelegate();
+
   // Sets the Badge for |web_contents|.
   void SetBadge(content::WebContents* web_contents,
                 base::Optional<uint64_t> badge_content);
 
   // Clears the badge for |web_contents|.
   void ClearBadge(content::WebContents* web_contents);
+
+  // Swaps out the implementation of |SetBadge| and |ClearBadge| for testing.
+  void SetImplForTesting(SetBadgeCallback on_set_badge,
+                         ClearBadgeCallback on_clear_badge);
+
+ private:
+  SetBadgeCallback on_set_badge_;
+  ClearBadgeCallback on_clear_badge_;
 };
 
 #endif  // CHROME_BROWSER_BADGING_BADGE_SERVICE_DELEGATE_H_
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl.cc b/chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl.cc
index 01f1abc1..f8d0e80d 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl.cc
@@ -13,11 +13,9 @@
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/web_applications/components/pending_app_manager.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
@@ -30,13 +28,6 @@
 const char kDefaultToPersistCookieName[] = "default_to_persist";
 const char kDefaultToPersistCookieValue[] = "true";
 
-network::mojom::CookieManager* GetCookieManager(Profile* profile) {
-  content::StoragePartition* partition =
-      content::BrowserContext::GetStoragePartitionForSite(
-          profile, chromeos::android_sms::GetAndroidMessagesURL());
-  return partition->GetCookieManagerForBrowserProcess();
-}
-
 void OnAppUninstallResult(const GURL& app_url, bool succeeded) {
   UMA_HISTOGRAM_BOOLEAN("AndroidSms.PWAUninstallationResult", succeeded);
 
@@ -52,37 +43,37 @@
 
 namespace android_sms {
 
-AndroidSmsAppHelperDelegateImpl::PwaFetcherDelegate::PwaFetcherDelegate() =
-    default;
+AndroidSmsAppHelperDelegateImpl::PwaDelegate::PwaDelegate() = default;
 
-AndroidSmsAppHelperDelegateImpl::PwaFetcherDelegate::~PwaFetcherDelegate() =
-    default;
+AndroidSmsAppHelperDelegateImpl::PwaDelegate::~PwaDelegate() = default;
 
 const extensions::Extension*
-AndroidSmsAppHelperDelegateImpl::PwaFetcherDelegate::GetPwaForUrl(
-    Profile* profile,
-    GURL gurl) {
+AndroidSmsAppHelperDelegateImpl::PwaDelegate::GetPwaForUrl(Profile* profile,
+                                                           GURL gurl) {
   return extensions::util::GetInstalledPwaForUrl(profile, gurl);
 }
 
-AndroidSmsAppHelperDelegateImpl::AndroidSmsAppHelperDelegateImpl(
-    Profile* profile)
-    : pending_app_manager_(
-          &web_app::WebAppProvider::Get(profile)->pending_app_manager()),
-      profile_(profile),
-      host_content_settings_map_(
-          HostContentSettingsMapFactory::GetForProfile(profile)),
-      cookie_manager_(GetCookieManager(profile)),
-      pwa_fetcher_delegate_(std::make_unique<PwaFetcherDelegate>()),
-      weak_ptr_factory_(this) {}
+content::WebContents* AndroidSmsAppHelperDelegateImpl::PwaDelegate::OpenApp(
+    const AppLaunchParams& params) {
+  // Note: OpenApplications() is not namespaced and is defined in
+  // application_launch.h.
+  return OpenApplication(params);
+}
+
+network::mojom::CookieManager*
+AndroidSmsAppHelperDelegateImpl::PwaDelegate::GetCookieManager(
+    Profile* profile) {
+  return GetCookieManagerForAndroidMessagesURL(profile);
+}
 
 AndroidSmsAppHelperDelegateImpl::AndroidSmsAppHelperDelegateImpl(
+    Profile* profile,
     web_app::PendingAppManager* pending_app_manager,
-    HostContentSettingsMap* host_content_settings_map,
-    network::mojom::CookieManager* cookie_manager)
-    : pending_app_manager_(pending_app_manager),
+    HostContentSettingsMap* host_content_settings_map)
+    : profile_(profile),
+      pending_app_manager_(pending_app_manager),
       host_content_settings_map_(host_content_settings_map),
-      cookie_manager_(cookie_manager),
+      pwa_delegate_(std::make_unique<PwaDelegate>()),
       weak_ptr_factory_(this) {}
 
 AndroidSmsAppHelperDelegateImpl::~AndroidSmsAppHelperDelegateImpl() = default;
@@ -95,7 +86,7 @@
   const GURL old_messages_url =
       chromeos::android_sms::GetAndroidMessagesURLOld();
   const extensions::Extension* old_android_sms_pwa =
-      pwa_fetcher_delegate_->GetPwaForUrl(profile_, old_messages_url);
+      pwa_delegate_->GetPwaForUrl(profile_, old_messages_url);
   if (old_android_sms_pwa) {
     PA_LOG(INFO) << "Messages PWA exists for old URL (" << old_messages_url
                  << "); uninstalling before continuing.";
@@ -112,7 +103,7 @@
 void AndroidSmsAppHelperDelegateImpl::SetUpAndroidSmsAppWithNoOldApp(
     bool launch_on_install) {
   PA_LOG(INFO) << "Setting DefaultToPersist Cookie";
-  cookie_manager_->SetCanonicalCookie(
+  pwa_delegate_->GetCookieManager(profile_)->SetCanonicalCookie(
       *net::CanonicalCookie::CreateSanitizedCookie(
           chromeos::android_sms::GetAndroidMessagesURL(),
           kDefaultToPersistCookieName, kDefaultToPersistCookieValue,
@@ -155,9 +146,8 @@
 }
 
 void AndroidSmsAppHelperDelegateImpl::SetUpAndLaunchAndroidSmsApp() {
-  const extensions::Extension* android_sms_pwa =
-      pwa_fetcher_delegate_->GetPwaForUrl(profile_,
-                                          android_sms::GetAndroidMessagesURL());
+  const extensions::Extension* android_sms_pwa = pwa_delegate_->GetPwaForUrl(
+      profile_, android_sms::GetAndroidMessagesURL());
   if (!android_sms_pwa) {
     PA_LOG(VERBOSE) << "No Messages app found. Installing it.";
     SetUpAndroidSmsApp(true /* launch_on_install */);
@@ -168,17 +158,14 @@
 }
 
 void AndroidSmsAppHelperDelegateImpl::LaunchAndroidSmsApp() {
-  const extensions::Extension* android_sms_pwa =
-      pwa_fetcher_delegate_->GetPwaForUrl(profile_,
-                                          android_sms::GetAndroidMessagesURL());
+  const extensions::Extension* android_sms_pwa = pwa_delegate_->GetPwaForUrl(
+      profile_, android_sms::GetAndroidMessagesURL());
   DCHECK(android_sms_pwa);
 
   PA_LOG(VERBOSE) << "Messages app Launching...";
-  AppLaunchParams params(
+  pwa_delegate_->OpenApp(AppLaunchParams(
       profile_, android_sms_pwa, extensions::LAUNCH_CONTAINER_WINDOW,
-      WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_CHROME_INTERNAL);
-  // OpenApplications() is defined in application_launch.h.
-  OpenApplication(params);
+      WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_CHROME_INTERNAL));
 }
 
 void AndroidSmsAppHelperDelegateImpl::OnAppInstalled(
@@ -215,12 +202,13 @@
       network::mojom::CookieDeletionFilter::New());
   filter->url = pwa_url;
   filter->cookie_name = kDefaultToPersistCookieName;
-  cookie_manager_->DeleteCookies(std::move(filter), base::DoNothing());
+  pwa_delegate_->GetCookieManager(profile_)->DeleteCookies(std::move(filter),
+                                                           base::DoNothing());
 }
 
-void AndroidSmsAppHelperDelegateImpl::SetPwaFetcherDelegateForTesting(
-    std::unique_ptr<PwaFetcherDelegate> test_pwa_fetcher_delegate) {
-  pwa_fetcher_delegate_ = std::move(test_pwa_fetcher_delegate);
+void AndroidSmsAppHelperDelegateImpl::SetPwaDelegateForTesting(
+    std::unique_ptr<PwaDelegate> test_pwa_delegate) {
+  pwa_delegate_ = std::move(test_pwa_delegate);
 }
 
 }  // namespace android_sms
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl.h b/chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl.h
index 15e6dfd..94391ca 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl.h
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chromeos/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "extensions/common/extension.h"
@@ -17,6 +18,10 @@
 
 class Profile;
 
+namespace content {
+class WebContents;
+}  // namespace content
+
 namespace web_app {
 enum class InstallResultCode;
 class PendingAppManager;
@@ -29,33 +34,29 @@
 class AndroidSmsAppHelperDelegateImpl
     : public multidevice_setup::AndroidSmsAppHelperDelegate {
  public:
-  explicit AndroidSmsAppHelperDelegateImpl(Profile* profile);
+  AndroidSmsAppHelperDelegateImpl(
+      Profile* profile,
+      web_app::PendingAppManager* pending_app_manager,
+      HostContentSettingsMap* host_content_settings_map);
   ~AndroidSmsAppHelperDelegateImpl() override;
 
  private:
   friend class AndroidSmsAppHelperDelegateImplTest;
 
-  // Fetches the Extension* associated with the PWA for |gurl|. This class is
-  // a thin wrapper around extensions::util::GetInstalledPwaForUrl() which is
-  // stubbed out for tests.
-  class PwaFetcherDelegate {
+  // Thin wrapper around static PWA functions which is stubbed out for tests.
+  // Specifically, this class wraps extensions::util::GetInstalledPwaForUrl()
+  // and OpenApplication().
+  class PwaDelegate {
    public:
-    PwaFetcherDelegate();
-    virtual ~PwaFetcherDelegate();
+    PwaDelegate();
+    virtual ~PwaDelegate();
 
     virtual const extensions::Extension* GetPwaForUrl(Profile* profile,
                                                       GURL gurl);
+    virtual content::WebContents* OpenApp(const AppLaunchParams& params);
+    virtual network::mojom::CookieManager* GetCookieManager(Profile* profile);
   };
 
-  // Note: This constructor should only be used in testing. Right now, objects
-  // built using this constructor will segfault on profile_ if
-  // LaunchAndroidSmsApp is called. We'll need to fix this once tests for that
-  // function are added. See https://crbug.com/876972.
-  AndroidSmsAppHelperDelegateImpl(
-      web_app::PendingAppManager* pending_app_manager,
-      HostContentSettingsMap* host_content_settings_map,
-      network::mojom::CookieManager* cookie_manager);
-
   // AndroidSmsAppHelperDelegate:
   void SetUpAndroidSmsApp() override;
   void SetUpAndLaunchAndroidSmsApp() override;
@@ -71,15 +72,15 @@
                                              bool set_cookie_success);
   void TearDownAndroidSmsAppAtUrl(GURL pwa_url);
 
-  void SetPwaFetcherDelegateForTesting(
-      std::unique_ptr<PwaFetcherDelegate> test_pwa_fetcher_delegate);
+  void SetPwaDelegateForTesting(std::unique_ptr<PwaDelegate> test_pwa_delegate);
 
   static const char kMessagesWebAppUrl[];
-  web_app::PendingAppManager* pending_app_manager_;
+
   Profile* profile_;
+  web_app::PendingAppManager* pending_app_manager_;
   HostContentSettingsMap* host_content_settings_map_;
-  network::mojom::CookieManager* cookie_manager_;
-  std::unique_ptr<PwaFetcherDelegate> pwa_fetcher_delegate_;
+
+  std::unique_ptr<PwaDelegate> pwa_delegate_;
   base::WeakPtrFactory<AndroidSmsAppHelperDelegateImpl> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(AndroidSmsAppHelperDelegateImpl);
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl_unittest.cc b/chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl_unittest.cc
index f8b4b39..b0b030e 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl_unittest.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl_unittest.cc
@@ -88,11 +88,11 @@
 
 class AndroidSmsAppHelperDelegateImplTest : public testing::Test {
  protected:
-  class TestPwaFetcherDelegate
-      : public AndroidSmsAppHelperDelegateImpl::PwaFetcherDelegate {
+  class TestPwaDelegate : public AndroidSmsAppHelperDelegateImpl::PwaDelegate {
    public:
-    TestPwaFetcherDelegate() = default;
-    ~TestPwaFetcherDelegate() override = default;
+    explicit TestPwaDelegate(FakeCookieManager* fake_cookie_manager)
+        : fake_cookie_manager_(fake_cookie_manager) {}
+    ~TestPwaDelegate() override = default;
 
     void SetHasPwa(GURL gurl, bool has_pwa) {
       // If no PWA should exist, erase any existing entry and return.
@@ -113,8 +113,11 @@
                                    .Build();
     }
 
-   private:
-    // AndroidSmsAppHelperDelegateImpl::PwaFetcherDelegate:
+    const std::vector<AppLaunchParams>& opened_app_launch_params() const {
+      return opened_app_launch_params_;
+    }
+
+    // AndroidSmsAppHelperDelegateImpl::PwaDelegate:
     const extensions::Extension* GetPwaForUrl(Profile* profile,
                                               GURL gurl) override {
       if (!base::ContainsKey(gurl_to_pwa_map_, gurl))
@@ -123,8 +126,42 @@
       return gurl_to_pwa_map_[gurl].get();
     }
 
+    content::WebContents* OpenApp(const AppLaunchParams& params) override {
+      opened_app_launch_params_.push_back(params);
+      return nullptr;
+    }
+
+    network::mojom::CookieManager* GetCookieManager(Profile* profile) override {
+      return fake_cookie_manager_;
+    }
+
+   private:
+    FakeCookieManager* fake_cookie_manager_;
+
     base::flat_map<GURL, scoped_refptr<const extensions::Extension>>
         gurl_to_pwa_map_;
+    std::vector<AppLaunchParams> opened_app_launch_params_;
+  };
+
+  class TestPendingAppManager : public web_app::TestPendingAppManager {
+   public:
+    explicit TestPendingAppManager(TestPwaDelegate* test_pwa_delegate)
+        : web_app::TestPendingAppManager(),
+          test_pwa_delegate_(test_pwa_delegate) {}
+    ~TestPendingAppManager() override = default;
+
+    // PendingAppManager:
+    void Install(AppInfo app_to_install,
+                 OnceInstallCallback callback) override {
+      test_pwa_delegate_->SetHasPwa(app_to_install.url, true);
+      web_app::TestPendingAppManager::Install(app_to_install,
+                                              std::move(callback));
+    }
+
+   private:
+    TestPwaDelegate* test_pwa_delegate_;
+
+    DISALLOW_COPY_AND_ASSIGN(TestPendingAppManager);
   };
 
   AndroidSmsAppHelperDelegateImplTest()
@@ -137,25 +174,26 @@
   void SetUp() override {
     host_content_settings_map_->ClearSettingsForOneType(
         ContentSettingsType::CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
-    test_pending_app_manager_ =
-        std::make_unique<web_app::TestPendingAppManager>();
     fake_cookie_manager_ = std::make_unique<FakeCookieManager>();
+    auto test_pwa_delegate =
+        std::make_unique<TestPwaDelegate>(fake_cookie_manager_.get());
+    test_pwa_delegate_ = test_pwa_delegate.get();
+    test_pending_app_manager_ =
+        std::make_unique<TestPendingAppManager>(test_pwa_delegate_);
     android_sms_app_helper_delegate_ =
         base::WrapUnique(new AndroidSmsAppHelperDelegateImpl(
-            test_pending_app_manager_.get(), host_content_settings_map_,
-            fake_cookie_manager_.get()));
+            &profile_, test_pending_app_manager_.get(),
+            host_content_settings_map_));
 
-    auto test_pwa_fetcher_delegate = std::make_unique<TestPwaFetcherDelegate>();
-    test_pwa_fetcher_delegate_ = test_pwa_fetcher_delegate.get();
-    std::unique_ptr<AndroidSmsAppHelperDelegateImpl::PwaFetcherDelegate>
-        base_delegate(test_pwa_fetcher_delegate.release());
+    std::unique_ptr<AndroidSmsAppHelperDelegateImpl::PwaDelegate> base_delegate(
+        test_pwa_delegate.release());
 
     static_cast<AndroidSmsAppHelperDelegateImpl*>(
         android_sms_app_helper_delegate_.get())
-        ->SetPwaFetcherDelegateForTesting(std::move(base_delegate));
+        ->SetPwaDelegateForTesting(std::move(base_delegate));
   }
 
-  web_app::TestPendingAppManager* test_pending_app_manager() {
+  TestPendingAppManager* test_pending_app_manager() {
     return test_pending_app_manager_.get();
   }
 
@@ -163,12 +201,6 @@
     return fake_cookie_manager_.get();
   }
 
-  void SetUpApp() { android_sms_app_helper_delegate_->SetUpAndroidSmsApp(); }
-
-  void SetUpAndLaunchApp() {
-    android_sms_app_helper_delegate_->SetUpAndLaunchAndroidSmsApp();
-  }
-
   void TearDownApp() {
     android_sms_app_helper_delegate_->TearDownAndroidSmsApp();
   }
@@ -176,36 +208,37 @@
   ContentSetting GetNotificationSetting() {
     std::unique_ptr<base::Value> notification_settings_value =
         host_content_settings_map_->GetWebsiteSetting(
-            chromeos::android_sms::GetAndroidMessagesURL(),
-            GURL() /* top_level_url */,
+            GetAndroidMessagesURL(), GURL() /* top_level_url */,
             ContentSettingsType::CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
             content_settings::ResourceIdentifier(), nullptr);
     return static_cast<ContentSetting>(notification_settings_value->GetInt());
   }
 
-  void TestInstallMessagesApp() {
+  void TestSetUpMessagesApp(bool also_launch_app) {
     base::HistogramTester histogram_tester;
     EXPECT_NE(ContentSetting::CONTENT_SETTING_ALLOW, GetNotificationSetting());
-    SetUpApp();
 
-    std::vector<web_app::PendingAppManager::AppInfo> expected_apps_to_install;
+    if (also_launch_app)
+      android_sms_app_helper_delegate_->SetUpAndLaunchAndroidSmsApp();
+    else
+      android_sms_app_helper_delegate_->SetUpAndroidSmsApp();
 
-    web_app::PendingAppManager::AppInfo info(
-        chromeos::android_sms::GetAndroidMessagesURL(),
-        web_app::LaunchContainer::kWindow, web_app::InstallSource::kInternal);
+    // Construct AppInfo which is expected to be requested for install.
+    web_app::PendingAppManager::AppInfo info(GetAndroidMessagesURL(),
+                                             web_app::LaunchContainer::kWindow,
+                                             web_app::InstallSource::kInternal);
     info.override_previous_user_uninstall = true;
     info.bypass_service_worker_check = true;
     info.require_manifest = true;
 
-    expected_apps_to_install.push_back(std::move(info));
-
-    EXPECT_EQ(expected_apps_to_install,
+    // Verify that the installation was requested.
+    EXPECT_EQ(std::vector<web_app::PendingAppManager::AppInfo>{info},
               test_pending_app_manager()->install_requests());
     EXPECT_EQ(ContentSetting::CONTENT_SETTING_ALLOW, GetNotificationSetting());
     histogram_tester.ExpectBucketCount("AndroidSms.PWAInstallationResult",
                                        web_app::InstallResultCode::kSuccess, 1);
 
-    // Check if the default_to_persist cookie is set.
+    // Verify that the default_to_persist cookie is set.
     ASSERT_EQ(1u, fake_cookie_manager()->set_canonical_cookie_calls().size());
     std::tuple<net::CanonicalCookie, bool, bool> set_cookie_call =
         fake_cookie_manager()->set_canonical_cookie_calls()[0];
@@ -213,6 +246,13 @@
     EXPECT_EQ("true", std::get<0>(set_cookie_call).Value());
     EXPECT_TRUE(std::get<1>(set_cookie_call));
     EXPECT_FALSE(std::get<2>(set_cookie_call));
+
+    if (also_launch_app) {
+      EXPECT_EQ(
+          test_pwa_delegate_->GetPwaForUrl(&profile_, GetAndroidMessagesURL())
+              ->id(),
+          test_pwa_delegate_->opened_app_launch_params().back().extension_id);
+    }
   }
 
   void VerifyCookieDeletedForUrl(GURL gurl) {
@@ -223,59 +263,56 @@
     EXPECT_EQ("default_to_persist", delete_filter->cookie_name);
   }
 
-  TestPwaFetcherDelegate* test_pwa_fetcher_delegate() {
-    return test_pwa_fetcher_delegate_;
-  }
+  TestPwaDelegate* test_pwa_delegate() { return test_pwa_delegate_; }
 
  private:
   content::TestBrowserThreadBundle thread_bundle_;
   TestingProfile profile_;
   HostContentSettingsMap* host_content_settings_map_;
   std::unique_ptr<FakeCookieManager> fake_cookie_manager_;
-  std::unique_ptr<web_app::TestPendingAppManager> test_pending_app_manager_;
-  TestPwaFetcherDelegate* test_pwa_fetcher_delegate_;
+  std::unique_ptr<TestPendingAppManager> test_pending_app_manager_;
+  TestPwaDelegate* test_pwa_delegate_;
   std::unique_ptr<multidevice_setup::AndroidSmsAppHelperDelegate>
       android_sms_app_helper_delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(AndroidSmsAppHelperDelegateImplTest);
 };
 
-TEST_F(AndroidSmsAppHelperDelegateImplTest, TestInstallMessagesApp_NoOldApp) {
-  TestInstallMessagesApp();
+TEST_F(AndroidSmsAppHelperDelegateImplTest, TestSetUpMessagesApp_NoOldApp) {
+  TestSetUpMessagesApp(false /* also_launch_app */);
 
   // No app should have been uninstalled in this process.
   EXPECT_EQ(0u, test_pending_app_manager()->uninstall_requests().size());
 }
 
 TEST_F(AndroidSmsAppHelperDelegateImplTest,
-       TestInstallMessagesApp_UninstallsOldApp) {
+       TestSetUpMessagesApp_UninstallsOldApp) {
   base::HistogramTester histogram_tester;
 
   // Simulate a PWA having already been installed at the old URL.
-  test_pwa_fetcher_delegate()->SetHasPwa(
-      android_sms::GetAndroidMessagesURLOld(), true /* has_pwa */);
+  test_pwa_delegate()->SetHasPwa(GetAndroidMessagesURLOld(),
+                                 true /* has_pwa */);
 
-  TestInstallMessagesApp();
+  TestSetUpMessagesApp(false /* also_launch_app */);
 
   // The old app should have been uninstalled.
   ASSERT_EQ(1u, test_pending_app_manager()->uninstall_requests().size());
-  EXPECT_EQ(android_sms::GetAndroidMessagesURLOld(),
+  EXPECT_EQ(GetAndroidMessagesURLOld(),
             test_pending_app_manager()->uninstall_requests()[0]);
   histogram_tester.ExpectBucketCount("AndroidSms.PWAUninstallationResult",
                                      true /* success */, 1);
 
   // The old app's cookie should have been deleted.
-  VerifyCookieDeletedForUrl(android_sms::GetAndroidMessagesURLOld());
+  VerifyCookieDeletedForUrl(GetAndroidMessagesURLOld());
 }
 
 TEST_F(AndroidSmsAppHelperDelegateImplTest, TestTearDownMessagesApp) {
   TearDownApp();
-  VerifyCookieDeletedForUrl(android_sms::GetAndroidMessagesURL());
+  VerifyCookieDeletedForUrl(GetAndroidMessagesURL());
 }
 
 TEST_F(AndroidSmsAppHelperDelegateImplTest, TestInstallAndLaunchMessagesApp) {
-  // TODO(crbug/876972): Figure out how to actually test the launching of the
-  // app here.
+  TestSetUpMessagesApp(true /* also_launch_app */);
 }
 
 }  // namespace android_sms
diff --git a/chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.cc b/chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.cc
index 4849f987..1b9d2dad 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chromeos/android_sms/android_sms_urls.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "content/public/browser/browser_context.h"
@@ -25,18 +26,15 @@
 namespace android_sms {
 
 AndroidSmsPairingStateTrackerImpl::AndroidSmsPairingStateTrackerImpl(
-    content::BrowserContext* browser_context)
-    : browser_context_(browser_context),
+    Profile* profile)
+    : profile_(profile),
       cookie_listener_binding_(this),
       weak_ptr_factory_(this) {
-  // Trigger the first fetch of the sms cookie and start listening for changes.
-  FetchMessagesPairingState();
-  network::mojom::CookieChangeListenerPtr listener_ptr;
-  cookie_listener_binding_.Bind(mojo::MakeRequest(&listener_ptr));
-
-  GetCookieManager()->AddCookieChangeListener(
-      chromeos::android_sms::GetAndroidMessagesURL(),
-      kMessagesPairStateCookieName, std::move(listener_ptr));
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &AndroidSmsPairingStateTrackerImpl::AddCookieChangeListener,
+          weak_ptr_factory_.GetWeakPtr()));
 }
 
 AndroidSmsPairingStateTrackerImpl::~AndroidSmsPairingStateTrackerImpl() =
@@ -47,7 +45,7 @@
 }
 
 void AndroidSmsPairingStateTrackerImpl::FetchMessagesPairingState() {
-  GetCookieManager()->GetCookieList(
+  GetCookieManagerForAndroidMessagesURL(profile_)->GetCookieList(
       chromeos::android_sms::GetAndroidMessagesURL(), net::CookieOptions(),
       base::BindOnce(&AndroidSmsPairingStateTrackerImpl::OnCookiesRetrieved,
                      base::Unretained(this)));
@@ -84,12 +82,15 @@
   FetchMessagesPairingState();
 }
 
-network::mojom::CookieManager*
-AndroidSmsPairingStateTrackerImpl::GetCookieManager() {
-  content::StoragePartition* partition =
-      content::BrowserContext::GetStoragePartitionForSite(
-          browser_context_, chromeos::android_sms::GetAndroidMessagesURL());
-  return partition->GetCookieManagerForBrowserProcess();
+void AndroidSmsPairingStateTrackerImpl::AddCookieChangeListener() {
+  // Trigger the first fetch of the sms cookie and start listening for changes.
+  FetchMessagesPairingState();
+  network::mojom::CookieChangeListenerPtr listener_ptr;
+  cookie_listener_binding_.Bind(mojo::MakeRequest(&listener_ptr));
+
+  GetCookieManagerForAndroidMessagesURL(profile_)->AddCookieChangeListener(
+      chromeos::android_sms::GetAndroidMessagesURL(),
+      kMessagesPairStateCookieName, std::move(listener_ptr));
 }
 
 }  // namespace android_sms
diff --git a/chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.h b/chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.h
index 156ec13..24b68b6 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.h
+++ b/chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.h
@@ -10,13 +10,7 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 
-namespace content {
-class BrowserContext;
-}  // namespace content
-
-namespace net {
-class CanonicalCookie;
-}  // namespace net
+class Profile;
 
 namespace chromeos {
 
@@ -27,8 +21,7 @@
     : public multidevice_setup::AndroidSmsPairingStateTracker,
       public network::mojom::CookieChangeListener {
  public:
-  explicit AndroidSmsPairingStateTrackerImpl(
-      content::BrowserContext* browser_context);
+  explicit AndroidSmsPairingStateTrackerImpl(Profile* profile);
   ~AndroidSmsPairingStateTrackerImpl() override;
 
   // AndroidSmsPairingStateTracker:
@@ -41,9 +34,11 @@
 
   void FetchMessagesPairingState();
   void OnCookiesRetrieved(const std::vector<net::CanonicalCookie>& cookies);
-  network::mojom::CookieManager* GetCookieManager();
 
-  content::BrowserContext* browser_context_;
+  void AddCookieChangeListener();
+
+  Profile* profile_;
+
   mojo::Binding<network::mojom::CookieChangeListener> cookie_listener_binding_;
   bool was_paired_on_last_update_ = false;
   base::WeakPtrFactory<AndroidSmsPairingStateTrackerImpl> weak_ptr_factory_;
diff --git a/chrome/browser/chromeos/android_sms/android_sms_service.cc b/chrome/browser/chromeos/android_sms/android_sms_service.cc
index df9ed3c..2458b3de 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_service.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_service.cc
@@ -3,24 +3,36 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/chromeos/android_sms/android_sms_service.h"
+
 #include "base/time/default_clock.h"
 #include "chrome/browser/chromeos/android_sms/android_sms_urls.h"
 #include "chrome/browser/chromeos/android_sms/connection_establisher_impl.h"
+#include "chrome/browser/chromeos/android_sms/connection_manager.h"
 #include "chrome/browser/chromeos/multidevice_setup/multidevice_setup_client_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chromeos/services/multidevice_setup/public/cpp/prefs.h"
 #include "components/session_manager/core/session_manager.h"
 #include "content/public/browser/storage_partition.h"
 
-using chromeos::multidevice_setup::MultiDeviceSetupClient;
-using chromeos::multidevice_setup::MultiDeviceSetupClientFactory;
-
 namespace chromeos {
 
 namespace android_sms {
 
-AndroidSmsService::AndroidSmsService(content::BrowserContext* browser_context)
-    : browser_context_(browser_context) {
+AndroidSmsService::AndroidSmsService(
+    Profile* profile,
+    HostContentSettingsMap* host_content_settings_map,
+    multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
+    web_app::WebAppProvider* web_app_provider)
+    : profile_(profile),
+      multidevice_setup_client_(multidevice_setup_client),
+      android_sms_app_helper_delegate_(
+          std::make_unique<AndroidSmsAppHelperDelegateImpl>(
+              profile_,
+              &web_app_provider->pending_app_manager(),
+              host_content_settings_map)),
+      android_sms_pairing_state_tracker_(
+          std::make_unique<AndroidSmsPairingStateTrackerImpl>(profile_)) {
   session_manager::SessionManager::Get()->AddObserver(this);
 }
 
@@ -28,6 +40,8 @@
 
 void AndroidSmsService::Shutdown() {
   connection_manager_.reset();
+  android_sms_app_helper_delegate_.reset();
+  android_sms_pairing_state_tracker_.reset();
   session_manager::SessionManager::Get()->RemoveObserver(this);
 }
 
@@ -40,22 +54,12 @@
   if (session_manager::SessionManager::Get()->IsUserSessionBlocked())
     return;
 
-  content::StoragePartition* storage_partition =
-      content::BrowserContext::GetStoragePartitionForSite(
-          browser_context_, GetAndroidMessagesURL());
-  content::ServiceWorkerContext* service_worker_context =
-      storage_partition->GetServiceWorkerContext();
-
-  MultiDeviceSetupClient* multidevice_setup_client =
-      MultiDeviceSetupClientFactory::GetForProfile(
-          Profile::FromBrowserContext(browser_context_));
-  DCHECK(multidevice_setup_client);
-
   connection_manager_ = std::make_unique<ConnectionManager>(
-      service_worker_context,
+      GetStoragePartitionForAndroidMessagesURL(profile_)
+          ->GetServiceWorkerContext(),
       std::make_unique<ConnectionEstablisherImpl>(
           base::DefaultClock::GetInstance()),
-      multidevice_setup_client);
+      multidevice_setup_client_);
 }
 
 }  // namespace android_sms
diff --git a/chrome/browser/chromeos/android_sms/android_sms_service.h b/chrome/browser/chromeos/android_sms/android_sms_service.h
index d7a900d..a842d39e 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_service.h
+++ b/chrome/browser/chromeos/android_sms/android_sms_service.h
@@ -6,23 +6,55 @@
 #define CHROME_BROWSER_CHROMEOS_ANDROID_SMS_ANDROID_SMS_SERVICE_H_
 
 #include <memory>
-#include "chrome/browser/chromeos/android_sms/connection_manager.h"
+#include "chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl.h"
+#include "chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/session_manager/core/session_manager_observer.h"
-#include "content/public/browser/browser_context.h"
+
+class HostContentSettingsMap;
+class Profile;
+
+namespace web_app {
+class WebAppProvider;
+}  // namespace web_app
 
 namespace chromeos {
 
+namespace multidevice_setup {
+class AndroidSmsAppHelperDelegate;
+class AndroidSmsPairingStateTracker;
+class MultiDeviceSetupClient;
+}  // namespace multidevice_setup
+
 namespace android_sms {
 
-// Profile Keyed Service responsible for maintaining connection with
-// android web messages service worker and initiating pairing.
+class ConnectionManager;
+
+// KeyedService which manages Android Messages integration. This service
+// has three main responsibilities:
+//   (1) Maintaining a connection with the Messages ServiceWorker,
+//   (2) Managing installation/launching of the Messages PWA, and
+//   (3) Tracking the pairing state of the PWA.
 class AndroidSmsService : public KeyedService,
                           public session_manager::SessionManagerObserver {
  public:
-  explicit AndroidSmsService(content::BrowserContext* context);
+  AndroidSmsService(
+      Profile* profile,
+      HostContentSettingsMap* host_content_settings_map,
+      multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
+      web_app::WebAppProvider* web_app_provider);
   ~AndroidSmsService() override;
 
+  multidevice_setup::AndroidSmsAppHelperDelegate*
+  android_sms_app_helper_delegate() {
+    return android_sms_app_helper_delegate_.get();
+  }
+
+  multidevice_setup::AndroidSmsPairingStateTracker*
+  android_sms_pairing_state_tracker() {
+    return android_sms_pairing_state_tracker_.get();
+  }
+
  private:
   // KeyedService:
   void Shutdown() override;
@@ -30,7 +62,13 @@
   // session_manager::SessionManagerObserver
   void OnSessionStateChanged() override;
 
-  content::BrowserContext* browser_context_;
+  Profile* profile_;
+  multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_;
+
+  std::unique_ptr<AndroidSmsAppHelperDelegateImpl>
+      android_sms_app_helper_delegate_;
+  std::unique_ptr<AndroidSmsPairingStateTrackerImpl>
+      android_sms_pairing_state_tracker_;
   std::unique_ptr<ConnectionManager> connection_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(AndroidSmsService);
diff --git a/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc b/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc
index a927360e..8e17a51c 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_service_factory.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/chromeos/android_sms/android_sms_service_factory.h"
 #include "chrome/browser/chromeos/multidevice_setup/multidevice_setup_client_factory.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/web_applications/web_app_provider_factory.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/multidevice_setup/public/cpp/prefs.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
@@ -42,8 +44,10 @@
     : BrowserContextKeyedServiceFactory(
           "AndroidSmsService",
           BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(HostContentSettingsMapFactory::GetInstance());
   DependsOn(chromeos::multidevice_setup::MultiDeviceSetupClientFactory::
                 GetInstance());
+  DependsOn(web_app::WebAppProviderFactory::GetInstance());
 }
 
 AndroidSmsServiceFactory::~AndroidSmsServiceFactory() = default;
@@ -60,7 +64,11 @@
   if (ProfileHelper::Get()->GetUserByProfile(profile) == nullptr)
     return nullptr;
 
-  return new AndroidSmsService(context);
+  return new AndroidSmsService(
+      profile, HostContentSettingsMapFactory::GetForProfile(profile),
+      chromeos::multidevice_setup::MultiDeviceSetupClientFactory::GetForProfile(
+          profile),
+      web_app::WebAppProviderFactory::GetForProfile(profile));
 }
 
 bool AndroidSmsServiceFactory::ServiceIsCreatedWithBrowserContext() const {
diff --git a/chrome/browser/chromeos/android_sms/android_sms_urls.cc b/chrome/browser/chromeos/android_sms/android_sms_urls.cc
index 4cbf228..778278a 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_urls.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_urls.cc
@@ -9,7 +9,10 @@
 #include "base/command_line.h"
 #include "base/optional.h"
 #include "chrome/browser/chromeos/android_sms/android_sms_switches.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
 #include "url/gurl.h"
 
 namespace chromeos {
@@ -45,6 +48,18 @@
       !base::FeatureList::IsEnabled(features::kUseMessagesGoogleComDomain));
 }
 
+content::StoragePartition* GetStoragePartitionForAndroidMessagesURL(
+    Profile* profile) {
+  return content::BrowserContext::GetStoragePartitionForSite(
+      profile, GetAndroidMessagesURL());
+}
+
+network::mojom::CookieManager* GetCookieManagerForAndroidMessagesURL(
+    Profile* profile) {
+  return GetStoragePartitionForAndroidMessagesURL(profile)
+      ->GetCookieManagerForBrowserProcess();
+}
+
 }  // namespace android_sms
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/android_sms/android_sms_urls.h b/chrome/browser/chromeos/android_sms/android_sms_urls.h
index b1292fe6..a6924fa 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_urls.h
+++ b/chrome/browser/chromeos/android_sms/android_sms_urls.h
@@ -7,6 +7,18 @@
 
 #include "url/gurl.h"
 
+class Profile;
+
+namespace content {
+class StoragePartition;
+}  // namespace content
+
+namespace network {
+namespace mojom {
+class CookieManager;
+}  // namespace mojom
+}  // namespace network
+
 namespace chromeos {
 
 namespace android_sms {
@@ -21,6 +33,12 @@
 // complete.
 GURL GetAndroidMessagesURLOld();
 
+content::StoragePartition* GetStoragePartitionForAndroidMessagesURL(
+    Profile* profile);
+
+network::mojom::CookieManager* GetCookieManagerForAndroidMessagesURL(
+    Profile* profile);
+
 }  // namespace android_sms
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc
index f8458be7..2d882b9 100644
--- a/chrome/browser/chromeos/login/session/user_session_manager.cc
+++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -1194,11 +1194,22 @@
 }
 
 // http://crbug/866790: After Supervised Users are deprecated, remove this.
-void ShowSupervisedUserDeprecationNotification(Profile* profile) {
-  base::string16 title = l10n_util::GetStringUTF16(
-      IDS_SUPERVISED_USER_EXPIRING_NOTIFICATION_TITLE);
-  base::string16 message =
-      l10n_util::GetStringUTF16(IDS_SUPERVISED_USER_EXPIRING_NOTIFICATION_BODY);
+void ShowSupervisedUserDeprecationNotification(Profile* profile,
+                                               bool is_manager) {
+  base::string16 title;
+  base::string16 message;
+
+  if (is_manager) {
+    title = l10n_util::GetStringUTF16(
+        IDS_MANAGER_SUPERVISED_USER_EXPIRING_NOTIFICATION_TITLE);
+    message = l10n_util::GetStringUTF16(
+        IDS_MANAGER_SUPERVISED_USER_EXPIRING_NOTIFICATION_BODY);
+  } else {
+    title = l10n_util::GetStringUTF16(
+        IDS_SUPERVISED_USER_EXPIRING_NOTIFICATION_TITLE);
+    message = l10n_util::GetStringUTF16(
+        IDS_SUPERVISED_USER_EXPIRING_NOTIFICATION_BODY);
+  }
 
   auto delegate =
       base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
@@ -1213,7 +1224,7 @@
                                     GURL("https://support.google.com/chrome/"
                                          "?p=ui_supervised_user"),
                                     ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
-              params.disposition = WindowOpenDisposition::SINGLETON_TAB;
+              params.disposition = WindowOpenDisposition::NEW_WINDOW;
               Navigate(&params);
             }
           }));
@@ -1344,9 +1355,15 @@
                                                 bool is_incognito_profile,
                                                 const AccountId& account_id) {
   // http://crbug/866790: After Supervised Users are deprecated, remove this.
-  if (ash::features::IsSupervisedUserDeprecationNoticeEnabled() &&
-      user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser())
-    ShowSupervisedUserDeprecationNotification(profile);
+  if (ash::features::IsSupervisedUserDeprecationNoticeEnabled()) {
+    bool is_supervised_user =
+        user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser();
+    bool is_manager = ChromeUserManager::Get()
+                          ->GetSupervisedUserManager()
+                          ->HasSupervisedUsers(account_id.GetUserEmail());
+    if (is_manager || is_supervised_user)
+      ShowSupervisedUserDeprecationNotification(profile, is_manager);
+  }
 
   // Demo user signed in.
   if (is_incognito_profile) {
diff --git a/chrome/browser/chromeos/mobile/mobile_activator.cc b/chrome/browser/chromeos/mobile/mobile_activator.cc
index d295a02..bb6ed6f0 100644
--- a/chrome/browser/chromeos/mobile/mobile_activator.cc
+++ b/chrome/browser/chromeos/mobile/mobile_activator.cc
@@ -39,16 +39,10 @@
 
 using content::BrowserThread;
 
+namespace chromeos {
+
 namespace {
 
-// Cellular configuration file path.
-const char kCellularConfigPath[] =
-    "/usr/share/chromeos-assets/mobile/mobile_config.json";
-
-// Cellular config file field names.
-const char kVersionField[] = "version";
-const char kErrorsField[] = "errors";
-
 // Number of times we'll try an OTASP before failing the activation process.
 const int kMaxOTASPTries = 3;
 // Number of times we will retry to reconnect and reload payment portal page.
@@ -60,20 +54,6 @@
 // Maximum amount of time we'll wait for a service to reconnect.
 const int kMaxReconnectTime = 30000;
 
-// Error codes matching codes defined in the cellular config file.
-const char kErrorDefault[] = "default";
-const char kErrorBadConnectionPartial[] = "bad_connection_partial";
-const char kErrorBadConnectionActivated[] = "bad_connection_activated";
-const char kErrorRoamingOnConnection[] = "roaming_connection";
-const char kErrorNoEVDO[] = "no_evdo";
-const char kErrorRoamingActivation[] = "roaming_activation";
-const char kErrorRoamingPartiallyActivated[] = "roaming_partially_activated";
-const char kErrorNoService[] = "no_service";
-const char kErrorDisabled[] = "disabled";
-const char kErrorNoDevice[] = "no_device";
-const char kFailedPaymentError[] = "failed_payment";
-const char kFailedConnectivity[] = "connectivity";
-
 // Returns true if the device follows the simple activation flow.
 bool IsSimpleActivationFlow(const chromeos::NetworkState* network) {
   return (network->activation_type() == shill::kActivationTypeNonCellular ||
@@ -82,91 +62,13 @@
 
 }  // namespace
 
-namespace chromeos {
-
-////////////////////////////////////////////////////////////////////////////////
-//
-// CellularConfigDocument
-//
-////////////////////////////////////////////////////////////////////////////////
-CellularConfigDocument::CellularConfigDocument() {}
-
-std::string CellularConfigDocument::GetErrorMessage(const std::string& code) {
-  base::AutoLock create(config_lock_);
-  ErrorMap::iterator iter = error_map_.find(code);
-  if (iter == error_map_.end())
-    return code;
-  return iter->second;
-}
-
-void CellularConfigDocument::LoadCellularConfigFile() {
-  base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
-
-  // Load partner customization startup manifest if it is available.
-  base::FilePath config_path(kCellularConfigPath);
-  if (!base::PathExists(config_path))
-    return;
-
-  if (LoadFromFile(config_path))
-    DVLOG(1) << "Cellular config file loaded: " << kCellularConfigPath;
-  else
-    LOG(ERROR) << "Error loading cellular config file: " << kCellularConfigPath;
-}
-
-CellularConfigDocument::~CellularConfigDocument() {}
-
-void CellularConfigDocument::SetErrorMap(
-    const ErrorMap& map) {
-  base::AutoLock create(config_lock_);
-  error_map_.clear();
-  error_map_.insert(map.begin(), map.end());
-}
-
-bool CellularConfigDocument::LoadFromFile(const base::FilePath& config_path) {
-  std::string config;
-  if (!base::ReadFileToString(config_path, &config))
-    return false;
-
-  std::unique_ptr<base::Value> root =
-      base::JSONReader::Read(config, base::JSON_ALLOW_TRAILING_COMMAS);
-  DCHECK(root.get() != NULL);
-  if (!root.get() || !root->is_dict()) {
-    LOG(WARNING) << "Bad cellular config file";
-    return false;
-  }
-
-  base::DictionaryValue* root_dict =
-      static_cast<base::DictionaryValue*>(root.get());
-  if (!root_dict->GetString(kVersionField, &version_)) {
-    LOG(WARNING) << "Cellular config file missing version";
-    return false;
-  }
-  ErrorMap error_map;
-  base::DictionaryValue* errors = NULL;
-  if (!root_dict->GetDictionary(kErrorsField, &errors))
-    return false;
-  for (base::DictionaryValue::Iterator it(*errors);
-      !it.IsAtEnd(); it.Advance()) {
-    std::string value;
-    if (!it.value().GetAsString(&value)) {
-      LOG(WARNING) << "Bad cellular config error value";
-      return false;
-    }
-    error_map.insert(ErrorMap::value_type(it.key(), value));
-  }
-  SetErrorMap(error_map);
-  return true;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 //
 // MobileActivator
 //
 ////////////////////////////////////////////////////////////////////////////////
 MobileActivator::MobileActivator()
-    : cellular_config_(new CellularConfigDocument()),
-      state_(PLAN_ACTIVATION_PAGE_LOADING),
-      reenable_cert_check_(false),
+    : state_(PLAN_ACTIVATION_PAGE_LOADING),
       terminated_(true),
       pending_activation_request_(false),
       connection_retry_count_(0),
@@ -174,8 +76,7 @@
       trying_OTASP_attempts_(0),
       final_OTASP_attempts_(0),
       payment_reconnect_count_(0),
-      weak_ptr_factory_(this) {
-}
+      weak_ptr_factory_(this) {}
 
 MobileActivator::~MobileActivator() {
   TerminateActivation();
@@ -199,10 +100,7 @@
   service_path_.clear();
   device_path_.clear();
   state_ = PLAN_ACTIVATION_PAGE_LOADING;
-  reenable_cert_check_ = false;
   terminated_ = true;
-  // Release the previous cellular config and setup a new empty one.
-  cellular_config_ = new CellularConfigDocument();
 }
 
 void MobileActivator::DefaultNetworkChanged(const NetworkState* network) {
@@ -261,52 +159,15 @@
     return;
   }
 
+  DCHECK(network->Matches(NetworkTypePattern::Cellular()));
+
   terminated_ = false;
   meid_ = device->meid();
   iccid_ = device->iccid();
   service_path_ = service_path;
   device_path_ = network->device_path();
 
-  ChangeState(network, PLAN_ACTIVATION_PAGE_LOADING, "");
-
-  base::PostTaskWithTraitsAndReply(
-      FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
-      base::BindOnce(&CellularConfigDocument::LoadCellularConfigFile,
-                     cellular_config_.get()),
-      base::BindOnce(&MobileActivator::ContinueActivation, AsWeakPtr()));
-}
-
-void MobileActivator::ContinueActivation() {
-  NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
-      service_path_,
-      base::Bind(&MobileActivator::GetPropertiesAndContinueActivation,
-                 weak_ptr_factory_.GetWeakPtr()),
-      base::Bind(&MobileActivator::GetPropertiesFailure,
-                 weak_ptr_factory_.GetWeakPtr()));
-}
-
-void MobileActivator::GetPropertiesAndContinueActivation(
-    const std::string& service_path,
-    const base::DictionaryValue& properties) {
-  if (service_path != service_path_) {
-    NET_LOG_EVENT("MobileActivator::GetProperties received for stale network",
-                  service_path);
-    return;  // Edge case; abort.
-  }
-  const base::DictionaryValue* payment_dict;
-  std::string usage_url, payment_url;
-  if (!properties.GetStringWithoutPathExpansion(
-          shill::kUsageURLProperty, &usage_url) ||
-      !properties.GetDictionaryWithoutPathExpansion(
-          shill::kPaymentPortalProperty, &payment_dict) ||
-      !payment_dict->GetStringWithoutPathExpansion(
-          shill::kPaymentPortalURL, &payment_url)) {
-    NET_LOG_ERROR("MobileActivator missing properties", service_path_);
-    return;
-  }
-
-  if (payment_url.empty() && usage_url.empty())
-    return;
+  ChangeState(network, PLAN_ACTIVATION_PAGE_LOADING, ActivationError::kNone);
 
   // We want shill to connect us after activations, so enable autoconnect.
   base::DictionaryValue auto_connect_property;
@@ -314,6 +175,7 @@
   NetworkHandler::Get()->network_configuration_handler()->SetShillProperties(
       service_path_, auto_connect_property, base::DoNothing(),
       network_handler::ErrorCallback());
+
   StartActivation();
 }
 
@@ -327,8 +189,8 @@
 void MobileActivator::OnSetTransactionStatus(bool success) {
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
-      base::Bind(&MobileActivator::HandleSetTransactionStatus, AsWeakPtr(),
-                 success));
+      base::BindOnce(&MobileActivator::HandleSetTransactionStatus,
+                     weak_ptr_factory_.GetWeakPtr(), success));
 }
 
 void MobileActivator::HandleSetTransactionStatus(bool success) {
@@ -353,21 +215,23 @@
 void MobileActivator::OnPortalLoaded(bool success) {
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
-      base::Bind(&MobileActivator::HandlePortalLoaded, AsWeakPtr(), success));
+      base::BindOnce(&MobileActivator::HandlePortalLoaded,
+                     weak_ptr_factory_.GetWeakPtr(), success));
 }
 
 void MobileActivator::HandlePortalLoaded(bool success) {
   const NetworkState* network = GetNetworkState(service_path_);
   if (!network) {
     ChangeState(NULL, PLAN_ACTIVATION_ERROR,
-                GetErrorMessage(kErrorNoService));
+                ActivationError::kNoCellularService);
     return;
   }
   if (state_ == PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING ||
       state_ == PLAN_ACTIVATION_SHOWING_PAYMENT) {
     if (success) {
       payment_reconnect_count_ = 0;
-      ChangeState(network, PLAN_ACTIVATION_SHOWING_PAYMENT, std::string());
+      ChangeState(network, PLAN_ACTIVATION_SHOWING_PAYMENT,
+                  ActivationError::kNone);
     } else {
       // There is no point in forcing reconnecting the cellular network if the
       // activation should not be done over it.
@@ -377,14 +241,13 @@
       payment_reconnect_count_++;
       if (payment_reconnect_count_ > kMaxPortalReconnectCount) {
         ChangeState(NULL, PLAN_ACTIVATION_ERROR,
-                    GetErrorMessage(kErrorNoService));
+                    ActivationError::kNoCellularService);
         return;
       }
 
       // Reconnect and try and load the frame again.
-      ChangeState(network,
-                  PLAN_ACTIVATION_RECONNECTING,
-                  GetErrorMessage(kFailedPaymentError));
+      ChangeState(network, PLAN_ACTIVATION_RECONNECTING,
+                  ActivationError::kNone);
     }
   } else {
     NOTREACHED() << "Called paymentPortalLoad while in unexpected state: "
@@ -408,27 +271,28 @@
     NetworkStateHandler::TechnologyState technology_state =
         NetworkHandler::Get()->network_state_handler()->GetTechnologyState(
             NetworkTypePattern::Cellular());
-    std::string error;
+    ActivationError error = ActivationError::kActivationFailed;
     if (technology_state == NetworkStateHandler::TECHNOLOGY_UNAVAILABLE) {
-      error = kErrorNoDevice;
+      error = ActivationError::kNoCellularDevice;
     } else if (technology_state != NetworkStateHandler::TECHNOLOGY_ENABLED) {
-      error = kErrorDisabled;
+      error = ActivationError::kCellularDisabled;
     } else {
-      error = kErrorNoService;
+      error = ActivationError::kNoCellularService;
     }
-    ChangeState(NULL, PLAN_ACTIVATION_ERROR, GetErrorMessage(error));
+    ChangeState(NULL, PLAN_ACTIVATION_ERROR, error);
     return;
   }
 
   // Start monitoring network property changes.
   NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE);
 
-  if (network->activation_type() == shill::kActivationTypeNonCellular)
-      StartActivationOverNonCellularNetwork();
-  else if (network->activation_type() == shill::kActivationTypeOTA)
-      StartActivationOTA();
-  else if (network->activation_type() == shill::kActivationTypeOTASP)
-      StartActivationOTASP();
+  if (network->activation_type() == shill::kActivationTypeNonCellular) {
+    StartActivationOverNonCellularNetwork();
+  } else if (network->activation_type() == shill::kActivationTypeOTA) {
+    StartActivationOTA();
+  } else if (network->activation_type() == shill::kActivationTypeOTASP) {
+    StartActivationOTASP();
+  }
 }
 
 void MobileActivator::StartActivationOverNonCellularNetwork() {
@@ -439,12 +303,11 @@
     return;
   }
 
-  ChangeState(
-      network,
-      (network->activation_state() == shill::kActivationStateActivated) ?
-      PLAN_ACTIVATION_DONE :
-      PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING,
-      "" /* error_description */);
+  ChangeState(network,
+              (network->activation_state() == shill::kActivationStateActivated)
+                  ? PLAN_ACTIVATION_DONE
+                  : PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING,
+              ActivationError::kNone);
 
   RefreshCellularNetworks();
 }
@@ -465,7 +328,7 @@
     ConnectNetwork(network);
 
   ChangeState(network, PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING,
-              "" /* error_description */);
+              ActivationError::kNone);
   RefreshCellularNetworks();
 }
 
@@ -500,7 +363,7 @@
     return;
   }
 
-  ChangeState(network, PLAN_ACTIVATION_START_OTASP, std::string());
+  ChangeState(network, PLAN_ACTIVATION_START_OTASP, ActivationError::kNone);
   EvaluateCellularNetwork(network);
 }
 
@@ -517,35 +380,31 @@
   if (state_ == PLAN_ACTIVATION_INITIATING_ACTIVATION) {
     ++initial_OTASP_attempts_;
     if (initial_OTASP_attempts_ <= kMaxOTASPTries) {
-      ChangeState(network,
-                  PLAN_ACTIVATION_RECONNECTING,
-                  GetErrorMessage(kErrorDefault));
+      ChangeState(network, PLAN_ACTIVATION_RECONNECTING,
+                  ActivationError::kActivationFailed);
       return;
     }
   } else if (state_ == PLAN_ACTIVATION_TRYING_OTASP) {
     ++trying_OTASP_attempts_;
     if (trying_OTASP_attempts_ <= kMaxOTASPTries) {
-      ChangeState(network,
-                  PLAN_ACTIVATION_RECONNECTING,
-                  GetErrorMessage(kErrorDefault));
+      ChangeState(network, PLAN_ACTIVATION_RECONNECTING,
+                  ActivationError::kActivationFailed);
       return;
     }
   } else if (state_ == PLAN_ACTIVATION_OTASP) {
     ++final_OTASP_attempts_;
     if (final_OTASP_attempts_ <= kMaxOTASPTries) {
       // Give the portal time to propagate all those magic bits.
-      ChangeState(network,
-                  PLAN_ACTIVATION_DELAY_OTASP,
-                  GetErrorMessage(kErrorDefault));
+      ChangeState(network, PLAN_ACTIVATION_DELAY_OTASP,
+                  ActivationError::kActivationFailed);
       return;
     }
   } else {
     LOG(ERROR) << "OTASP timed out from a non-OTASP wait state?";
   }
   LOG(ERROR) << "OTASP failed too many times; aborting.";
-  ChangeState(network,
-              PLAN_ACTIVATION_ERROR,
-              GetErrorMessage(kErrorDefault));
+  ChangeState(network, PLAN_ACTIVATION_ERROR,
+              ActivationError::kActivationFailed);
 }
 
 void MobileActivator::ConnectNetwork(const NetworkState* network) {
@@ -589,9 +448,8 @@
     return;
   }
 
-  ChangeState(network,
-              PLAN_ACTIVATION_ERROR,
-              GetErrorMessage(kFailedConnectivity));
+  ChangeState(network, PLAN_ACTIVATION_ERROR,
+              ActivationError::kActivationFailed);
 }
 
 void MobileActivator::ContinueConnecting() {
@@ -644,9 +502,10 @@
          (default_network->type() == shill::kTypeCellular &&
           default_network->connection_state() == shill::kStatePortal));
     if (waiting && is_online_or_portal) {
-      ChangeState(network, post_reconnect_state_, "");
+      ChangeState(network, post_reconnect_state_, ActivationError::kNone);
     } else if (!waiting && !is_online_or_portal) {
-      ChangeState(network, PLAN_ACTIVATION_WAITING_FOR_CONNECTION, "");
+      ChangeState(network, PLAN_ACTIVATION_WAITING_FOR_CONNECTION,
+                  ActivationError::kNone);
     }
   }
 
@@ -687,21 +546,23 @@
   if (IsSimpleActivationFlow(network))
     return;
 
-  std::string error_description;
-  PlanActivationState new_state = PickNextState(network, &error_description);
-
-  ChangeState(network, new_state, error_description);
+  PlanActivationState new_state = PickNextState(network);
+  ActivationError error = new_state == PLAN_ACTIVATION_ERROR
+                              ? ActivationError::kActivationFailed
+                              : ActivationError::kNone;
+  ChangeState(network, new_state, error);
 }
 
 MobileActivator::PlanActivationState MobileActivator::PickNextState(
-    const NetworkState* network, std::string* error_description) const {
+    const NetworkState* network) const {
   PlanActivationState new_state = state_;
   if (!network->IsConnectedState())
     new_state = PickNextOfflineState(network);
   else
     new_state = PickNextOnlineState(network);
+
   if (new_state != PLAN_ACTIVATION_ERROR &&
-      GotActivationError(network, error_description)) {
+      network->connection_state() == shill::kStateActivationFailure) {
     // Check for this special case when we try to do activate partially
     // activated device. If that attempt failed, try to disconnect to clear the
     // state and reconnect again.
@@ -709,8 +570,7 @@
     if ((activation == shill::kActivationStatePartiallyActivated ||
          activation == shill::kActivationStateActivating) &&
         (network->error().empty() ||
-         network->error() == shill::kErrorOtaspFailed) &&
-        network->connection_state() == shill::kStateActivationFailure) {
+         network->error() == shill::kErrorOtaspFailed)) {
       NET_LOG_EVENT("Activation failure detected ", network->path());
       switch (state_) {
         case PLAN_ACTIVATION_OTASP:
@@ -738,8 +598,6 @@
     }
   }
 
-  if (new_state == PLAN_ACTIVATION_ERROR && !error_description->length())
-    *error_description = GetErrorMessage(kErrorDefault);
   return new_state;
 }
 
@@ -922,11 +780,10 @@
   UMA_HISTOGRAM_COUNTS_1M("Cellular.ActivationFailure", 1);
   NET_LOG_ERROR("Failed to call Activate() on service", service_path);
   if (new_state == PLAN_ACTIVATION_OTASP) {
-    ChangeState(network, PLAN_ACTIVATION_DELAY_OTASP, std::string());
+    ChangeState(network, PLAN_ACTIVATION_DELAY_OTASP, ActivationError::kNone);
   } else {
-    ChangeState(network,
-                PLAN_ACTIVATION_ERROR,
-                GetErrorMessage(kFailedConnectivity));
+    ChangeState(network, PLAN_ACTIVATION_ERROR,
+                ActivationError::kActivationFailed);
   }
 }
 
@@ -947,7 +804,7 @@
 
 void MobileActivator::ChangeState(const NetworkState* network,
                                   PlanActivationState new_state,
-                                  std::string error_description) {
+                                  ActivationError error) {
   // Report an error, by transitioning into a PLAN_ACTIVATION_ERROR state with
   // a "no service" error instead, if no network state is available (e.g. the
   // cellular service no longer exists) when we are transitioning into certain
@@ -959,7 +816,7 @@
       case PLAN_ACTIVATION_OTASP:
       case PLAN_ACTIVATION_DONE:
         new_state = PLAN_ACTIVATION_ERROR;
-        error_description = GetErrorMessage(kErrorNoService);
+        error = ActivationError::kNoCellularService;
         break;
       default:
         break;
@@ -984,7 +841,7 @@
 
   // Signal to observers layer that the state is changing.
   for (auto& observer : observers_)
-    observer.OnActivationStateChanged(network, state_, error_description);
+    observer.OnActivationStateChanged(network, state_, error);
 
   // Pick action that should happen on entering the new state.
   switch (new_state) {
@@ -994,7 +851,8 @@
       UMA_HISTOGRAM_COUNTS_1M("Cellular.RetryOTASP", 1);
       base::PostDelayedTaskWithTraits(
           FROM_HERE, {BrowserThread::UI},
-          base::Bind(&MobileActivator::RetryOTASP, AsWeakPtr()),
+          base::BindOnce(&MobileActivator::RetryOTASP,
+                         weak_ptr_factory_.GetWeakPtr()),
           base::TimeDelta::FromMilliseconds(kOTASPRetryDelay));
       break;
     }
@@ -1004,13 +862,13 @@
     case PLAN_ACTIVATION_TRYING_OTASP:
     case PLAN_ACTIVATION_OTASP: {
       DCHECK(network);
-      network_handler::ErrorCallback on_activation_error =
-          base::Bind(&MobileActivator::HandleActivationFailure, AsWeakPtr(),
-                     network->path(),
-                     new_state);
+      network_handler::ErrorCallback on_activation_error = base::BindRepeating(
+          &MobileActivator::HandleActivationFailure,
+          weak_ptr_factory_.GetWeakPtr(), network->path(), new_state);
       RequestCellularActivation(
           network,
-          base::Bind(&MobileActivator::StartOTASPTimer, AsWeakPtr()),
+          base::BindRepeating(&MobileActivator::StartOTASPTimer,
+                              weak_ptr_factory_.GetWeakPtr()),
           on_activation_error);
       }
       break;
@@ -1073,49 +931,6 @@
   }
 }
 
-bool MobileActivator::GotActivationError(
-    const NetworkState* network, std::string* error) const {
-  DCHECK(network);
-  bool got_error = false;
-  const char* error_code = kErrorDefault;
-  const std::string& activation = network->activation_state();
-
-  // This is the magic for detection of errors in during activation process.
-  if (network->connection_state() == shill::kStateFailure &&
-      network->error() == shill::kErrorAaaFailed) {
-    if (activation == shill::kActivationStatePartiallyActivated) {
-      error_code = kErrorBadConnectionPartial;
-    } else if (activation == shill::kActivationStateActivated) {
-      if (network->roaming() == shill::kRoamingStateHome)
-        error_code = kErrorBadConnectionActivated;
-      else if (network->roaming() == shill::kRoamingStateRoaming)
-        error_code = kErrorRoamingOnConnection;
-    }
-    got_error = true;
-  } else if (network->connection_state() == shill::kStateActivationFailure) {
-    if (network->error() == shill::kErrorNeedEvdo) {
-      if (activation == shill::kActivationStatePartiallyActivated)
-        error_code = kErrorNoEVDO;
-    } else if (network->error() == shill::kErrorNeedHomeNetwork) {
-      if (activation == shill::kActivationStateNotActivated) {
-        error_code = kErrorRoamingActivation;
-      } else if (activation == shill::kActivationStatePartiallyActivated) {
-        error_code = kErrorRoamingPartiallyActivated;
-      }
-    }
-    got_error = true;
-  }
-
-  if (got_error)
-    *error = GetErrorMessage(error_code);
-
-  return got_error;
-}
-
-std::string MobileActivator::GetErrorMessage(const std::string& code) const {
-  return cellular_config_->GetErrorMessage(code);
-}
-
 void MobileActivator::SignalCellularPlanPayment() {
   DCHECK(!HasRecentCellularPlanPayment());
   cellular_plan_payment_time_ = base::Time::Now();
diff --git a/chrome/browser/chromeos/mobile/mobile_activator.h b/chrome/browser/chromeos/mobile/mobile_activator.h
index c584393..75887402 100644
--- a/chrome/browser/chromeos/mobile/mobile_activator.h
+++ b/chrome/browser/chromeos/mobile/mobile_activator.h
@@ -27,33 +27,6 @@
 class NetworkState;
 class TestMobileActivator;
 
-// Cellular plan config document.
-class CellularConfigDocument
-    : public base::RefCountedThreadSafe<CellularConfigDocument> {
- public:
-  CellularConfigDocument();
-
-  // Return error message for a given code.
-  std::string GetErrorMessage(const std::string& code);
-  void LoadCellularConfigFile();
-  const std::string& version() { return version_; }
-
- private:
-  friend class base::RefCountedThreadSafe<CellularConfigDocument>;
-  typedef std::map<std::string, std::string> ErrorMap;
-
-  virtual ~CellularConfigDocument();
-
-  void SetErrorMap(const ErrorMap& map);
-  bool LoadFromFile(const base::FilePath& config_path);
-
-  std::string version_;
-  ErrorMap error_map_;
-  base::Lock config_lock_;
-
-  DISALLOW_COPY_AND_ASSIGN(CellularConfigDocument);
-};
-
 // This class performs mobile plan activation process.
 //
 // There are two types of activation flow:
@@ -73,9 +46,7 @@
 //      a. Ensure there's a network connection.
 //      a. Navigate to payment portal.
 //      b. Activate the modem via shill CompletetActivation().
-class MobileActivator
-    : public base::SupportsWeakPtr<MobileActivator>,
-      public NetworkStateHandlerObserver {
+class MobileActivator : public NetworkStateHandlerObserver {
  public:
   // Activation state.
   enum PlanActivationState {
@@ -111,14 +82,31 @@
     PLAN_ACTIVATION_ERROR                   = 0xFF,
   };
 
+  // Set of errors encountered during activation.
+  enum class ActivationError {
+    // No error.
+    kNone,
+
+    // Activation encountered an error.
+    kActivationFailed,
+
+    // Mobile data was disabled.
+    kCellularDisabled,
+
+    // Cellular device not present.
+    kNoCellularDevice,
+
+    // Cellular service not present.
+    kNoCellularService,
+  };
+
   // Activation process observer.
   class Observer {
    public:
     // Signals activation |state| change for given |network|.
-    virtual void OnActivationStateChanged(
-        const NetworkState* network,
-        PlanActivationState state,
-        const std::string& error_description) = 0;
+    virtual void OnActivationStateChanged(const NetworkState* network,
+                                          PlanActivationState state,
+                                          ActivationError error) = 0;
 
    protected:
     Observer() {}
@@ -164,12 +152,6 @@
   void DefaultNetworkChanged(const NetworkState* network) override;
   void NetworkPropertiesUpdated(const NetworkState* network) override;
 
-  // Continue activation after inital setup (config load). Makes an
-  // asynchronous call to NetworkConfigurationHandler::GetProperties.
-  void ContinueActivation();
-  void GetPropertiesAndContinueActivation(
-      const std::string& service_path,
-      const base::DictionaryValue& properties);
   void GetPropertiesFailure(const std::string& error_name,
                             std::unique_ptr<base::DictionaryValue> error_data);
   // Handles the signal that the payment portal has finished loading.
@@ -210,15 +192,11 @@
   virtual void EvaluateCellularNetwork(const NetworkState* network);
   // PickNextState selects the desired state based on the current state of the
   // modem and the activator.  It does not transition to this state however.
-  PlanActivationState PickNextState(const NetworkState* network,
-                                    std::string* error_description) const;
+  PlanActivationState PickNextState(const NetworkState* network) const;
   // One of PickNext*State are called in PickNextState based on whether the
   // modem is online or not.
   PlanActivationState PickNextOnlineState(const NetworkState* network) const;
   PlanActivationState PickNextOfflineState(const NetworkState* network) const;
-  // Check the current cellular network for error conditions.
-  bool GotActivationError(const NetworkState* network,
-                          std::string* error) const;
 
   // Callback used to handle an activation error.
   void HandleActivationFailure(
@@ -238,11 +216,9 @@
   // Changes internal state.
   virtual void ChangeState(const NetworkState* network,
                            PlanActivationState new_state,
-                           std::string error_description);
+                           ActivationError error);
   // Resets network devices after cellular activation process.
   void CompleteActivation();
-  // Return error message for a given code.
-  std::string GetErrorMessage(const std::string& code) const;
 
   // Starts the OTASP timeout timer.  If the timer fires, we'll force a
   // disconnect/reconnect cycle on this network.
@@ -256,7 +232,6 @@
 
   static const char* GetStateDescription(PlanActivationState state);
 
-  scoped_refptr<CellularConfigDocument> cellular_config_;
   // Internal handler state.
   PlanActivationState state_;
   // MEID of cellular device to activate.
@@ -270,9 +245,6 @@
   // can change during activation due to modem resets, the device path stays
   // the same.
   std::string device_path_;
-  // Flags that controls if cert_checks needs to be restored
-  // after the activation of cellular network.
-  bool reenable_cert_check_;
   // True if activation process has been terminated.
   bool terminated_;
   // True if an asynchronous activation request was dispatched to Shill
diff --git a/chrome/browser/chromeos/mobile/mobile_activator_unittest.cc b/chrome/browser/chromeos/mobile/mobile_activator_unittest.cc
index d13941f..df7f43d 100644
--- a/chrome/browser/chromeos/mobile/mobile_activator_unittest.cc
+++ b/chrome/browser/chromeos/mobile/mobile_activator_unittest.cc
@@ -60,9 +60,10 @@
                void(const NetworkState*,
                     const base::Closure&,
                     const network_handler::ErrorCallback&));
-  MOCK_METHOD3(ChangeState, void(const NetworkState*,
-                                 MobileActivator::PlanActivationState,
-                                 std::string));
+  MOCK_METHOD3(ChangeState,
+               void(const NetworkState*,
+                    MobileActivator::PlanActivationState,
+                    MobileActivator::ActivationError));
   MOCK_METHOD0(GetDefaultNetwork, const NetworkState*());
   MOCK_METHOD1(EvaluateCellularNetwork, void(const NetworkState*));
   MOCK_METHOD0(SignalCellularPlanPayment, void(void));
@@ -86,16 +87,14 @@
     HandleSetTransactionStatus(success);
   }
 
-  PlanActivationState InvokePickNextState(
-      const NetworkState* network,
-      std::string* error_description) const {
-    return PickNextState(network, error_description);
+  PlanActivationState InvokePickNextState(const NetworkState* network) const {
+    return PickNextState(network);
   }
 
   void InvokeChangeState(const NetworkState* network,
-                         MobileActivator::PlanActivationState new_state,
-                         std::string error_description) {
-    MobileActivator::ChangeState(network, new_state, error_description);
+                         MobileActivator::PlanActivationState new_state) {
+    MobileActivator::ChangeState(network, new_state,
+                                 MobileActivator::ActivationError::kNone);
   }
 
  private:
@@ -204,48 +203,40 @@
   // In a new device, we aren't connected to Verizon, we start at START
   // because we haven't paid Verizon (ever), and the modem isn't even partially
   // activated.
-  std::string error_description;
   set_activator_state(MobileActivator::PLAN_ACTIVATION_START);
   set_connection_state(shill::kStateIdle);
   set_network_activation_type(shill::kActivationTypeOTASP);
   set_network_activation_state(shill::kActivationStateNotActivated);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION,
-            mobile_activator_.InvokePickNextState(&cellular_network_,
-                                                  &error_description));
+            mobile_activator_.InvokePickNextState(&cellular_network_));
   // Now behave as if ChangeState() has initiated an activation.
   set_activator_state(MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION);
   set_network_activation_state(shill::kActivationStateActivating);
   // We'll sit in this state while we wait for the OTASP to finish.
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION,
-            mobile_activator_.InvokePickNextState(&cellular_network_,
-                                                  &error_description));
+            mobile_activator_.InvokePickNextState(&cellular_network_));
   set_network_activation_state(shill::kActivationStatePartiallyActivated);
   // We'll sit in this state until we go online as well.
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_INITIATING_ACTIVATION,
-            mobile_activator_.InvokePickNextState(&cellular_network_,
-                                                  &error_description));
+            mobile_activator_.InvokePickNextState(&cellular_network_));
   set_connection_state(shill::kStatePortal);
   // After we go online, we go back to START, which acts as a jumping off
   // point for the two types of initial OTASP.
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_START,
-            mobile_activator_.InvokePickNextState(&cellular_network_,
-                                                  &error_description));
+            mobile_activator_.InvokePickNextState(&cellular_network_));
   set_activator_state(MobileActivator::PLAN_ACTIVATION_START);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_TRYING_OTASP,
-            mobile_activator_.InvokePickNextState(&cellular_network_,
-                                                  &error_description));
+            mobile_activator_.InvokePickNextState(&cellular_network_));
   // Very similar things happen while we're trying OTASP.
   set_activator_state(MobileActivator::PLAN_ACTIVATION_TRYING_OTASP);
   set_network_activation_state(shill::kActivationStateActivating);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_TRYING_OTASP,
-            mobile_activator_.InvokePickNextState(&cellular_network_,
-                                                  &error_description));
+            mobile_activator_.InvokePickNextState(&cellular_network_));
   set_network_activation_state(shill::kActivationStatePartiallyActivated);
   set_connection_state(shill::kStatePortal);
   // And when we come back online again and aren't activating, load the portal.
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING,
-            mobile_activator_.InvokePickNextState(&cellular_network_,
-                                                  &error_description));
+            mobile_activator_.InvokePickNextState(&cellular_network_));
   // The JS drives us through the payment portal.
   set_activator_state(MobileActivator::PLAN_ACTIVATION_SHOWING_PAYMENT);
   // The JS also calls us to signal that the portal is done.  This triggers us
@@ -263,18 +254,15 @@
   // OTASP.
   set_activator_state(MobileActivator::PLAN_ACTIVATION_START_OTASP);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_OTASP,
-            mobile_activator_.InvokePickNextState(&cellular_network_,
-                                                  &error_description));
+            mobile_activator_.InvokePickNextState(&cellular_network_));
   // Similarly to TRYING_OTASP and INITIATING_OTASP above...
   set_activator_state(MobileActivator::PLAN_ACTIVATION_OTASP);
   set_network_activation_state(shill::kActivationStateActivating);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_OTASP,
-            mobile_activator_.InvokePickNextState(&cellular_network_,
-                                                  &error_description));
+            mobile_activator_.InvokePickNextState(&cellular_network_));
   set_network_activation_state(shill::kActivationStateActivated);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_DONE,
-            mobile_activator_.InvokePickNextState(&cellular_network_,
-                                                  &error_description));
+            mobile_activator_.InvokePickNextState(&cellular_network_));
 }
 
 // A fake for MobileActivator::RequestCellularActivation that always succeeds.
@@ -295,7 +283,6 @@
 }
 
 TEST_F(MobileActivatorTest, OTASPScheduling) {
-  const std::string error;
   for (size_t i = 0; i < kNumOTASPStates; ++i) {
     // When activation works, we start a timer to watch for success.
     EXPECT_CALL(mobile_activator_, RequestCellularActivation(_, _, _))
@@ -304,9 +291,7 @@
     EXPECT_CALL(mobile_activator_, StartOTASPTimer())
          .Times(1);
     set_activator_state(MobileActivator::PLAN_ACTIVATION_START);
-    mobile_activator_.InvokeChangeState(&cellular_network_,
-                                        kOTASPStates[i],
-                                        error);
+    mobile_activator_.InvokeChangeState(&cellular_network_, kOTASPStates[i]);
 
     // When activation fails, it's an error, unless we're trying for the final
     // OTASP, in which case we try again via DELAY_OTASP.
@@ -325,9 +310,7 @@
           _));
     }
     set_activator_state(MobileActivator::PLAN_ACTIVATION_START);
-    mobile_activator_.InvokeChangeState(&cellular_network_,
-                                        kOTASPStates[i],
-                                        error);
+    mobile_activator_.InvokeChangeState(&cellular_network_, kOTASPStates[i]);
   }
 }
 
@@ -349,17 +332,14 @@
   // the modem (offline or online) to work correctly.  A few places however,
   // like when we're displaying the portal care quite a bit about going
   // offline.  Lets test for those cases.
-  std::string error_description;
   set_connection_state(shill::kStateFailure);
   set_network_activation_state(shill::kActivationStatePartiallyActivated);
   set_activator_state(MobileActivator::PLAN_ACTIVATION_PAYMENT_PORTAL_LOADING);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_RECONNECTING,
-            mobile_activator_.InvokePickNextState(&cellular_network_,
-                                                  &error_description));
+            mobile_activator_.InvokePickNextState(&cellular_network_));
   set_activator_state(MobileActivator::PLAN_ACTIVATION_SHOWING_PAYMENT);
   EXPECT_EQ(MobileActivator::PLAN_ACTIVATION_RECONNECTING,
-            mobile_activator_.InvokePickNextState(&cellular_network_,
-                                                  &error_description));
+            mobile_activator_.InvokePickNextState(&cellular_network_));
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_measurement.cc b/chrome/browser/data_use_measurement/chrome_data_use_measurement.cc
index 2d1bd8c..941dfcd0 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_measurement.cc
+++ b/chrome/browser/data_use_measurement/chrome_data_use_measurement.cc
@@ -30,6 +30,36 @@
 // be deleted. When network service is disabled, this should always be null.
 ChromeDataUseMeasurement* g_chrome_data_use_measurement = nullptr;
 
+DataUseUserData::DataUseContentType GetContentType(const std::string& mime_type,
+                                                   bool is_main_frame_resource,
+                                                   bool is_app_background,
+                                                   bool is_tab_visible) {
+  if (mime_type == "text/html")
+    return is_main_frame_resource ? DataUseUserData::MAIN_FRAME_HTML
+                                  : DataUseUserData::NON_MAIN_FRAME_HTML;
+  if (mime_type == "text/css")
+    return DataUseUserData::CSS;
+  if (base::StartsWith(mime_type, "image/", base::CompareCase::SENSITIVE))
+    return DataUseUserData::IMAGE;
+  if (base::EndsWith(mime_type, "javascript", base::CompareCase::SENSITIVE) ||
+      base::EndsWith(mime_type, "ecmascript", base::CompareCase::SENSITIVE)) {
+    return DataUseUserData::JAVASCRIPT;
+  }
+  if (mime_type.find("font") != std::string::npos)
+    return DataUseUserData::FONT;
+  if (base::StartsWith(mime_type, "audio/", base::CompareCase::SENSITIVE))
+    return is_app_background
+               ? DataUseUserData::AUDIO_APPBACKGROUND
+               : (!is_tab_visible ? DataUseUserData::AUDIO_TABBACKGROUND
+                                  : DataUseUserData::AUDIO);
+  if (base::StartsWith(mime_type, "video/", base::CompareCase::SENSITIVE))
+    return is_app_background
+               ? DataUseUserData::VIDEO_APPBACKGROUND
+               : (!is_tab_visible ? DataUseUserData::VIDEO_TABBACKGROUND
+                                  : DataUseUserData::VIDEO);
+  return DataUseUserData::OTHER;
+}
+
 }  // namespace
 
 // static
@@ -118,8 +148,21 @@
 #endif
 }
 
-void ChromeDataUseMeasurement::ReportUserTrafficDataUse(int64_t recv_bytes) {
-  RecordTrafficSizeMetric(true, true, recv_bytes);
+void ChromeDataUseMeasurement::ReportUserTrafficDataUse(bool is_tab_visible,
+                                                        int64_t recv_bytes) {
+  RecordTrafficSizeMetric(true, true, is_tab_visible, recv_bytes);
+}
+
+void ChromeDataUseMeasurement::RecordContentTypeMetric(
+    const std::string& mime_type,
+    bool is_main_frame_resource,
+    bool is_tab_visible,
+    int64_t recv_bytes) {
+  DataUseUserData::DataUseContentType content_type = GetContentType(
+      mime_type, is_main_frame_resource,
+      CurrentAppState() == DataUseUserData::BACKGROUND, is_tab_visible);
+  UMA_HISTOGRAM_SCALED_ENUMERATION("DataUse.ContentType.UserTrafficKB",
+                                   content_type, recv_bytes, 1024);
 }
 
 void ChromeDataUseMeasurement::UpdateMetricsUsagePrefs(
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_measurement.h b/chrome/browser/data_use_measurement/chrome_data_use_measurement.h
index ec5c447f..de8c0a22 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_measurement.h
+++ b/chrome/browser/data_use_measurement/chrome_data_use_measurement.h
@@ -36,7 +36,12 @@
   void ReportNetworkServiceDataUse(int32_t network_traffic_annotation_id_hash,
                                    int64_t recv_bytes,
                                    int64_t sent_bytes);
-  void ReportUserTrafficDataUse(int64_t recv_bytes);
+  void ReportUserTrafficDataUse(bool is_tab_visible, int64_t recv_bytes);
+
+  void RecordContentTypeMetric(const std::string& mime_type,
+                               bool is_main_frame_resource,
+                               bool is_tab_visible,
+                               int64_t recv_bytes);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ChromeDataUseMeasurement);
diff --git a/chrome/browser/download/download_service_factory.cc b/chrome/browser/download/download_service_factory.cc
index adc476f..42998a7 100644
--- a/chrome/browser/download/download_service_factory.cc
+++ b/chrome/browser/download/download_service_factory.cc
@@ -23,7 +23,7 @@
 #include "components/download/public/background_service/clients.h"
 #include "components/download/public/background_service/download_service.h"
 #include "components/download/public/background_service/features.h"
-#include "components/download/public/background_service/task_scheduler.h"
+#include "components/download/public/task/task_scheduler.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/offline_pages/buildflags/buildflags.h"
 #include "content/public/browser/browser_context.h"
diff --git a/chrome/browser/download/download_task_scheduler_impl.h b/chrome/browser/download/download_task_scheduler_impl.h
index 7f35d67..5385040e 100644
--- a/chrome/browser/download/download_task_scheduler_impl.h
+++ b/chrome/browser/download/download_task_scheduler_impl.h
@@ -11,7 +11,7 @@
 #include "base/cancelable_callback.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "components/download/public/background_service/task_scheduler.h"
+#include "components/download/public/task/task_scheduler.h"
 
 namespace content {
 class BrowserContext;
diff --git a/chrome/browser/extensions/api/cast_streaming/performance_test.cc b/chrome/browser/extensions/api/cast_streaming/performance_test.cc
index b0ed943..2d218e5 100644
--- a/chrome/browser/extensions/api/cast_streaming/performance_test.cc
+++ b/chrome/browser/extensions/api/cast_streaming/performance_test.cc
@@ -152,6 +152,8 @@
     }
   }
 
+  void SetMeanAsAbsoluteValue() { mean_ = std::abs(mean_); }
+
   std::string AsString() const {
     return base::StringPrintf("%f,%f", mean_, std_dev_);
   }
@@ -255,8 +257,11 @@
     }
     EXPECT_GE(deltas.size(), kMinDataPoints);
 
-    // Close to zero is better. (can be negative)
-    MeanAndError(deltas).Print(name, modifier, "av_sync", "ms");
+    MeanAndError av_sync(deltas);
+    av_sync.Print(name, modifier, "av_sync", "ms");
+    // Close to zero is better (av_sync can be negative).
+    av_sync.SetMeanAsAbsoluteValue();
+    av_sync.Print(name, modifier, "abs_av_sync", "ms");
     // lower is better.
     AnalyzeJitter(audio_events_).Print(name, modifier, "audio_jitter", "ms");
     // lower is better.
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc
index 9d89cdc..4a5d0c4 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -319,9 +319,12 @@
   if (!EventRouter::Get(profile_))
     return;
 
-  std::unique_ptr<base::Value> result = base::JSONReader::Read(message);
-  if (!result || !result->is_dict())
+  std::unique_ptr<base::Value> result =
+      base::JSONReader::Read(message, base::JSON_REPLACE_INVALID_CHARACTERS);
+  if (!result || !result->is_dict()) {
+    LOG(ERROR) << "Tried to send invalid message to extension: " << message;
     return;
+  }
   base::DictionaryValue* dictionary =
       static_cast<base::DictionaryValue*>(result.get());
 
diff --git a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
index 5da7b5d..6a9ab04 100644
--- a/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
+++ b/chrome/browser/extensions/api/management/chrome_management_api_delegate.cc
@@ -91,9 +91,8 @@
       bool show_programmatic_uninstall_ui)
       : function_(function) {
     ChromeExtensionFunctionDetails details(function);
-    extension_uninstall_dialog_.reset(
-        extensions::ExtensionUninstallDialog::Create(
-            details.GetProfile(), details.GetNativeWindowForUI(), this));
+    extension_uninstall_dialog_ = extensions::ExtensionUninstallDialog::Create(
+        details.GetProfile(), details.GetNativeWindowForUI(), this);
     bool uninstall_from_webstore =
         function->extension() &&
         function->extension()->id() == extensions::kWebStoreAppId;
diff --git a/chrome/browser/extensions/extension_context_menu_model.cc b/chrome/browser/extensions/extension_context_menu_model.cc
index baaaa68..37a0e898 100644
--- a/chrome/browser/extensions/extension_context_menu_model.cc
+++ b/chrome/browser/extensions/extension_context_menu_model.cc
@@ -160,8 +160,8 @@
   ~UninstallDialogHelper() override {}
 
   void BeginUninstall(Browser* browser, const Extension* extension) {
-    uninstall_dialog_.reset(ExtensionUninstallDialog::Create(
-        browser->profile(), browser->window()->GetNativeWindow(), this));
+    uninstall_dialog_ = ExtensionUninstallDialog::Create(
+        browser->profile(), browser->window()->GetNativeWindow(), this);
     uninstall_dialog_->ConfirmUninstall(extension,
                                         UNINSTALL_REASON_USER_INITIATED,
                                         UNINSTALL_SOURCE_TOOLBAR_CONTEXT_MENU);
diff --git a/chrome/browser/extensions/extension_disabled_ui.cc b/chrome/browser/extensions/extension_disabled_ui.cc
index 108ac21..bc98590 100644
--- a/chrome/browser/extensions/extension_disabled_ui.cc
+++ b/chrome/browser/extensions/extension_disabled_ui.cc
@@ -292,8 +292,8 @@
   // there is only an "OK" button.
   // Supervised users may never remove custodian-installed extensions.
   DCHECK(!util::IsExtensionSupervised(extension_, service_->profile()));
-  uninstall_dialog_.reset(ExtensionUninstallDialog::Create(
-      service_->profile(), browser->window()->GetNativeWindow(), this));
+  uninstall_dialog_ = ExtensionUninstallDialog::Create(
+      service_->profile(), browser->window()->GetNativeWindow(), this);
   user_response_ = UNINSTALL;
   // Delay showing the uninstall dialog, so that this function returns
   // immediately, to close the bubble properly. See crbug.com/121544.
diff --git a/chrome/browser/extensions/extension_storage_monitor.cc b/chrome/browser/extensions/extension_storage_monitor.cc
index 66b3028..24fc97b 100644
--- a/chrome/browser/extensions/extension_storage_monitor.cc
+++ b/chrome/browser/extensions/extension_storage_monitor.cc
@@ -548,8 +548,7 @@
   if (!extension)
     return;
 
-  uninstall_dialog_.reset(
-      ExtensionUninstallDialog::Create(profile_, nullptr, this));
+  uninstall_dialog_ = ExtensionUninstallDialog::Create(profile_, nullptr, this);
 
   uninstall_extension_id_ = extension->id();
   uninstall_dialog_->ConfirmUninstall(
diff --git a/chrome/browser/extensions/extension_uninstall_dialog.h b/chrome/browser/extensions/extension_uninstall_dialog.h
index d5745ea..0612e33 100644
--- a/chrome/browser/extensions/extension_uninstall_dialog.h
+++ b/chrome/browser/extensions/extension_uninstall_dialog.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_UNINSTALL_DIALOG_H_
 #define CHROME_BROWSER_EXTENSIONS_EXTENSION_UNINSTALL_DIALOG_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
@@ -60,15 +62,13 @@
   // Creates a platform specific implementation of ExtensionUninstallDialog. The
   // dialog will be modal to |parent|, or a non-modal dialog if |parent| is
   // NULL.
-  static ExtensionUninstallDialog* Create(Profile* profile,
-                                          gfx::NativeWindow parent,
-                                          Delegate* delegate);
+  static std::unique_ptr<ExtensionUninstallDialog>
+  Create(Profile* profile, gfx::NativeWindow parent, Delegate* delegate);
 
   // Create the Views implementation of ExtensionUninstallDialog, for use on
   // platforms where that is not the native platform implementation.
-  static ExtensionUninstallDialog* CreateViews(Profile* profile,
-                                               gfx::NativeWindow parent,
-                                               Delegate* delegate);
+  static std::unique_ptr<ExtensionUninstallDialog>
+  CreateViews(Profile* profile, gfx::NativeWindow parent, Delegate* delegate);
 
   ~ExtensionUninstallDialog() override;
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 0fa05a01..19b7c1b 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1744,6 +1744,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "enable-safe-browsing-ap-download-verdicts",
+    "owners": [ "drubery" ],
+    "expiry_milestone": 76
+  },
+  {
     "name": "enable-scroll-anchor-serialization",
     // "owners": [ "your-team" ],
     "expiry_milestone": 76
@@ -2796,6 +2801,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "pdf-annotations",
+    "owners": [ "dstockwell@google.com" ],
+    "expiry_milestone": 76
+  },
+  {
     "name": "pdf-form-save",
     // "owners": [ "your-team" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index d320a6b..964d3ac 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1606,6 +1606,13 @@
     "Uses the resource load scheduler in blink to throttle resource load "
     "requests.";
 
+const char kSafeBrowsingUseAPDownloadVerdictsName[] =
+    "Request Advanced Protection verdicts when inspecting downloads";
+const char kSafeBrowsingUseAPDownloadVerdictsDescription[] =
+    "If enabled, download protection will request Advanced Protection "
+    "verdicts from Safe Browsing. These will provide stronger protections "
+    "from files Safe Browsing is unsure about.";
+
 const char kSafeSearchUrlReportingName[] = "SafeSearch URLs reporting.";
 const char kSafeSearchUrlReportingDescription[] =
     "If enabled, inappropriate URLs can be reported back to SafeSearch.";
@@ -3681,6 +3688,12 @@
 
 #if BUILDFLAG(ENABLE_PLUGINS)
 
+#if defined(OS_CHROMEOS)
+const char kPdfAnnotations[] = "PDF Annotations";
+const char kPdfAnnotationsDescription[] =
+    "Enable annotating PDF documents.";
+#endif  // defined(OS_CHROMEOS)
+
 const char kPdfFormSaveName[] = "Save PDF Forms";
 const char kPdfFormSaveDescription[] =
     "Enable saving PDFs with filled form data.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 278d4b4..220aebc 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -968,6 +968,9 @@
 extern const char kResourceLoadSchedulerName[];
 extern const char kResourceLoadSchedulerDescription[];
 
+extern const char kSafeBrowsingUseAPDownloadVerdictsName[];
+extern const char kSafeBrowsingUseAPDownloadVerdictsDescription[];
+
 extern const char kSafeSearchUrlReportingName[];
 extern const char kSafeSearchUrlReportingDescription[];
 
@@ -2232,6 +2235,13 @@
 
 #if BUILDFLAG(ENABLE_PLUGINS)
 
+#if defined(OS_CHROMEOS)
+
+extern const char kPdfAnnotations[];
+extern const char kPdfAnnotationsDescription[];
+
+#endif  // defined(OS_CHROMEOS)
+
 extern const char kPdfFormSaveName[];
 extern const char kPdfFormSaveDescription[];
 
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index 708fec7..6606d5a7 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -59,7 +59,6 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/installer/util/util_constants.h"
-#include "chromeos/assistant/buildflags.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/browser_watcher/stability_paths.h"
 #include "components/crash/core/common/crash_keys.h"
@@ -125,15 +124,17 @@
 #include "chrome/browser/metrics/plugin_metrics_provider.h"
 #endif
 
-#if BUILDFLAG(ENABLE_CROS_ASSISTANT)
-#include "chrome/browser/metrics/assistant_service_metrics_provider.h"
-#endif
-
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/printing/printer_metrics_provider.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/metrics/chromeos_metrics_provider.h"
 #include "chrome/browser/signin/signin_status_metrics_provider_chromeos.h"
+#include "chromeos/assistant/buildflags.h"
+
+#if BUILDFLAG(ENABLE_CROS_ASSISTANT)
+#include "chrome/browser/metrics/assistant_service_metrics_provider.h"
+#endif
+
 #endif
 
 #if defined(OS_WIN)
@@ -725,10 +726,12 @@
       std::make_unique<PowerMetricsProvider>());
 #endif
 
+#if defined(OS_CHROMEOS)
 #if BUILDFLAG(ENABLE_CROS_ASSISTANT)
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<AssistantServiceMetricsProvider>());
 #endif  // BUILDFLAG(ENABLE_CROS_ASSISTANT)
+#endif  // defined(OS_CHROMEOS)
 }
 
 void ChromeMetricsServiceClient::RegisterUKMProviders() {
diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.cc b/chrome/browser/offline_pages/android/offline_page_bridge.cc
index 3472068..0e710c2cf 100644
--- a/chrome/browser/offline_pages/android/offline_page_bridge.cc
+++ b/chrome/browser/offline_pages/android/offline_page_bridge.cc
@@ -65,25 +65,6 @@
 
 const char kOfflinePageBridgeKey[] = "offline-page-bridge";
 
-void JNI_SavePageRequest_ToJavaOfflinePageList(
-    JNIEnv* env,
-    const JavaRef<jobject>& j_result_obj,
-    const std::vector<OfflinePageItem>& offline_pages) {
-  for (const OfflinePageItem& offline_page : offline_pages) {
-    Java_OfflinePageBridge_createOfflinePageAndAddToList(
-        env, j_result_obj,
-        ConvertUTF8ToJavaString(env, offline_page.url.spec()),
-        offline_page.offline_id,
-        ConvertUTF8ToJavaString(env, offline_page.client_id.name_space),
-        ConvertUTF8ToJavaString(env, offline_page.client_id.id),
-        ConvertUTF16ToJavaString(env, offline_page.title),
-        ConvertUTF8ToJavaString(env, offline_page.file_path.value()),
-        offline_page.file_size, offline_page.creation_time.ToJavaTime(),
-        offline_page.access_count, offline_page.last_access_time.ToJavaTime(),
-        ConvertUTF8ToJavaString(env, offline_page.request_origin));
-  }
-}
-
 ScopedJavaLocalRef<jobject> JNI_SavePageRequest_ToJavaOfflinePageItem(
     JNIEnv* env,
     const OfflinePageItem& offline_page) {
@@ -114,7 +95,7 @@
     const ScopedJavaGlobalRef<jobject>& j_callback_obj,
     const OfflinePageModel::MultipleOfflinePageItemResult& result) {
   JNIEnv* env = base::android::AttachCurrentThread();
-  JNI_SavePageRequest_ToJavaOfflinePageList(env, j_result_obj, result);
+  OfflinePageBridge::AddOfflinePageItemsToJavaList(env, j_result_obj, result);
   base::android::RunObjectCallbackAndroid(j_callback_obj, j_result_obj);
 }
 
@@ -234,31 +215,8 @@
 ScopedJavaLocalRef<jobjectArray> JNI_SavePageRequest_CreateJavaSavePageRequests(
     JNIEnv* env,
     std::vector<std::unique_ptr<SavePageRequest>> requests) {
-  ScopedJavaLocalRef<jclass> save_page_request_clazz = base::android::GetClass(
-      env, "org/chromium/chrome/browser/offlinepages/SavePageRequest");
-  jobjectArray joa = env->NewObjectArray(
-      requests.size(), save_page_request_clazz.obj(), nullptr);
-  base::android::CheckException(env);
-
-  for (size_t i = 0; i < requests.size(); ++i) {
-    SavePageRequest request = *(requests[i]);
-    ScopedJavaLocalRef<jstring> name_space =
-        ConvertUTF8ToJavaString(env, request.client_id().name_space);
-    ScopedJavaLocalRef<jstring> id =
-        ConvertUTF8ToJavaString(env, request.client_id().id);
-    ScopedJavaLocalRef<jstring> url =
-        ConvertUTF8ToJavaString(env, request.url().spec());
-    ScopedJavaLocalRef<jstring> origin =
-        ConvertUTF8ToJavaString(env, request.request_origin());
-
-    ScopedJavaLocalRef<jobject> j_save_page_request =
-        Java_SavePageRequest_create(
-            env, static_cast<int>(request.request_state()),
-            request.request_id(), url, name_space, id, origin);
-    env->SetObjectArrayElement(joa, i, j_save_page_request.obj());
-  }
-
-  return ScopedJavaLocalRef<jobjectArray>(env, joa);
+  return OfflinePageBridge::CreateJavaSavePageRequests(env,
+                                                       std::move(requests));
 }
 
 void OnGetAllRequestsDone(
@@ -384,6 +342,57 @@
 }
 
 // static
+ScopedJavaLocalRef<jobjectArray> OfflinePageBridge::CreateJavaSavePageRequests(
+    JNIEnv* env,
+    std::vector<std::unique_ptr<SavePageRequest>> requests) {
+  ScopedJavaLocalRef<jclass> save_page_request_clazz = base::android::GetClass(
+      env, "org/chromium/chrome/browser/offlinepages/SavePageRequest");
+  jobjectArray joa = env->NewObjectArray(
+      requests.size(), save_page_request_clazz.obj(), nullptr);
+  base::android::CheckException(env);
+
+  for (size_t i = 0; i < requests.size(); ++i) {
+    SavePageRequest request = *(requests[i]);
+    ScopedJavaLocalRef<jstring> name_space =
+        ConvertUTF8ToJavaString(env, request.client_id().name_space);
+    ScopedJavaLocalRef<jstring> id =
+        ConvertUTF8ToJavaString(env, request.client_id().id);
+    ScopedJavaLocalRef<jstring> url =
+        ConvertUTF8ToJavaString(env, request.url().spec());
+    ScopedJavaLocalRef<jstring> origin =
+        ConvertUTF8ToJavaString(env, request.request_origin());
+
+    ScopedJavaLocalRef<jobject> j_save_page_request =
+        Java_SavePageRequest_create(
+            env, static_cast<int>(request.request_state()),
+            request.request_id(), url, name_space, id, origin);
+    env->SetObjectArrayElement(joa, i, j_save_page_request.obj());
+  }
+
+  return ScopedJavaLocalRef<jobjectArray>(env, joa);
+}
+
+// static
+void OfflinePageBridge::AddOfflinePageItemsToJavaList(
+    JNIEnv* env,
+    const JavaRef<jobject>& j_result_obj,
+    const std::vector<OfflinePageItem>& offline_pages) {
+  for (const OfflinePageItem& offline_page : offline_pages) {
+    Java_OfflinePageBridge_createOfflinePageAndAddToList(
+        env, j_result_obj,
+        ConvertUTF8ToJavaString(env, offline_page.url.spec()),
+        offline_page.offline_id,
+        ConvertUTF8ToJavaString(env, offline_page.client_id.name_space),
+        ConvertUTF8ToJavaString(env, offline_page.client_id.id),
+        ConvertUTF16ToJavaString(env, offline_page.title),
+        ConvertUTF8ToJavaString(env, offline_page.file_path.value()),
+        offline_page.file_size, offline_page.creation_time.ToJavaTime(),
+        offline_page.access_count, offline_page.last_access_time.ToJavaTime(),
+        ConvertUTF8ToJavaString(env, offline_page.request_origin));
+  }
+}
+
+// static
 std::string OfflinePageBridge::GetEncodedOriginApp(
     const content::WebContents* web_contents) {
   TabAndroid* tab = TabAndroid::FromWebContents(web_contents);
diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.h b/chrome/browser/offline_pages/android/offline_page_bridge.h
index 70c34f42..248decd 100644
--- a/chrome/browser/offline_pages/android/offline_page_bridge.h
+++ b/chrome/browser/offline_pages/android/offline_page_bridge.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/supports_user_data.h"
 #include "components/offline_items_collection/core/launch_location.h"
+#include "components/offline_pages/core/background/save_page_request.h"
 #include "components/offline_pages/core/offline_page_item.h"
 #include "components/offline_pages/core/offline_page_model.h"
 
@@ -39,6 +40,16 @@
       JNIEnv* env,
       const OfflinePageItem& offline_page);
 
+  static base::android::ScopedJavaLocalRef<jobjectArray>
+  CreateJavaSavePageRequests(
+      JNIEnv* env,
+      std::vector<std::unique_ptr<SavePageRequest>> requests);
+
+  static void AddOfflinePageItemsToJavaList(
+      JNIEnv* env,
+      const base::android::JavaRef<jobject>& j_result_obj,
+      const std::vector<OfflinePageItem>& offline_pages);
+
   static std::string GetEncodedOriginApp(
       const content::WebContents* web_contents);
 
diff --git a/chrome/browser/offline_pages/android/offline_test_util_jni.cc b/chrome/browser/offline_pages/android/offline_test_util_jni.cc
new file mode 100644
index 0000000..58914dab
--- /dev/null
+++ b/chrome/browser/offline_pages/android/offline_test_util_jni.cc
@@ -0,0 +1,115 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sstream>
+
+#include "base/android/callback_android.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/android/jni_utils.h"
+#include "base/json/json_writer.h"
+#include "chrome/browser/offline_pages/android/offline_page_bridge.h"
+#include "chrome/browser/offline_pages/offline_page_model_factory.h"
+#include "chrome/browser/offline_pages/prefetch/prefetch_service_factory.h"
+#include "chrome/browser/offline_pages/request_coordinator_factory.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "components/offline_pages/core/background/request_coordinator.h"
+#include "components/offline_pages/core/offline_page_model.h"
+#include "content/public/test/url_loader_interceptor.h"
+#include "jni/OfflineTestUtil_jni.h"
+
+// Below is the native implementation of OfflineTestUtil.java.
+
+namespace offline_pages {
+namespace {
+using ::base::android::JavaParamRef;
+using ::base::android::ScopedJavaGlobalRef;
+using ::base::android::ScopedJavaLocalRef;
+using ::offline_pages::android::OfflinePageBridge;
+
+Profile* GetProfile() {
+  Profile* profile = ProfileManager::GetLastUsedProfile();
+  DCHECK(profile);
+  return profile;
+}
+RequestCoordinator* GetRequestCoordinator() {
+  return RequestCoordinatorFactory::GetForBrowserContext(GetProfile());
+}
+OfflinePageModel* GetOfflinePageModel() {
+  return OfflinePageModelFactory::GetForBrowserContext(GetProfile());
+}
+
+void OnGetAllRequestsDone(
+    const ScopedJavaGlobalRef<jobject>& j_callback_obj,
+    std::vector<std::unique_ptr<SavePageRequest>> all_requests) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::RunObjectCallbackAndroid(
+      j_callback_obj,
+      offline_pages::android::OfflinePageBridge::CreateJavaSavePageRequests(
+          env, std::move(all_requests)));
+}
+
+void OnGetAllPagesDone(
+    const ScopedJavaGlobalRef<jobject>& j_result_obj,
+    const ScopedJavaGlobalRef<jobject>& j_callback_obj,
+    const OfflinePageModel::MultipleOfflinePageItemResult& result) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  OfflinePageBridge::AddOfflinePageItemsToJavaList(env, j_result_obj, result);
+  base::android::RunObjectCallbackAndroid(j_callback_obj, j_result_obj);
+}
+
+void OnDeletePageDone(const ScopedJavaGlobalRef<jobject>& j_callback_obj,
+                      OfflinePageModel::DeletePageResult result) {
+  base::android::RunIntCallbackAndroid(j_callback_obj,
+                                       static_cast<int>(result));
+}
+
+}  // namespace
+
+void JNI_OfflineTestUtil_GetRequestsInQueue(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& j_callback_obj) {
+  ScopedJavaGlobalRef<jobject> j_callback_ref(j_callback_obj);
+
+  RequestCoordinator* coordinator = GetRequestCoordinator();
+
+  if (!coordinator) {
+    // Callback with null to signal that results are unavailable.
+    const JavaParamRef<jobject> empty_result(nullptr);
+    base::android::RunObjectCallbackAndroid(j_callback_obj, empty_result);
+    return;
+  }
+
+  coordinator->GetAllRequests(
+      base::BindOnce(&OnGetAllRequestsDone, std::move(j_callback_ref)));
+}
+
+void JNI_OfflineTestUtil_GetAllPages(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& j_result_obj,
+    const JavaParamRef<jobject>& j_callback_obj) {
+  DCHECK(j_result_obj);
+  DCHECK(j_callback_obj);
+
+  ScopedJavaGlobalRef<jobject> j_result_ref(env, j_result_obj);
+  ScopedJavaGlobalRef<jobject> j_callback_ref(env, j_callback_obj);
+  GetOfflinePageModel()->GetAllPages(base::BindOnce(
+      &OnGetAllPagesDone, std::move(j_result_ref), std::move(j_callback_ref)));
+}
+
+void JNI_OfflineTestUtil_DeletePagesByOfflineId(
+    JNIEnv* env,
+    const JavaParamRef<jlongArray>& j_offline_ids_array,
+    const JavaParamRef<jobject>& j_callback_obj) {
+  ScopedJavaGlobalRef<jobject> j_callback_ref(env, j_callback_obj);
+  std::vector<int64_t> offline_ids;
+  base::android::JavaLongArrayToInt64Vector(env, j_offline_ids_array,
+                                            &offline_ids);
+  GetOfflinePageModel()->DeletePagesByOfflineId(
+      offline_ids,
+      base::BindOnce(&OnDeletePageDone, std::move(j_callback_ref)));
+}
+
+}  // namespace offline_pages
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
index 01b9b749..b4dfc8c 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
@@ -27,15 +27,8 @@
 
 namespace {
 
-#define ADS_HISTOGRAM(suffix, hist_macro, ad_type, value)                  \
-  switch (ad_type) {                                                       \
-    case AdsPageLoadMetricsObserver::AD_TYPE_GOOGLE:                       \
-      hist_macro("PageLoad.Clients.Ads.Google." suffix, value);            \
-      break;                                                               \
-    case AdsPageLoadMetricsObserver::AD_TYPE_SUBRESOURCE_FILTER:           \
-      hist_macro("PageLoad.Clients.Ads.SubresourceFilter." suffix, value); \
-      break;                                                               \
-  }
+#define ADS_HISTOGRAM(suffix, hist_macro, value) \
+  hist_macro("PageLoad.Clients.Ads.SubresourceFilter." suffix, value);
 
 #define RESOURCE_BYTES_HISTOGRAM(suffix, was_cached, value)                \
   if (was_cached) {                                                        \
@@ -55,37 +48,6 @@
                    handle->GetFrameTreeNodeId());
 }
 
-bool DetectGoogleAd(content::NavigationHandle* navigation_handle) {
-  // Because sub-resource filtering isn't always enabled, and doesn't work
-  // well in monitoring mode (no CSS enforcement), it's difficult to identify
-  // ads. Google ads are prevalent and easy to track, so we'll start by
-  // tracking those. Note that the frame name can be very large, so be careful
-  // to avoid full string searches if possible.
-  // TODO(jkarlin): Track other ad networks that are easy to identify.
-
-  // In case the navigation aborted, look up the RFH by the Frame Tree Node
-  // ID. It returns the committed frame host or the initial frame host for the
-  // frame if no committed host exists. Using a previous host is fine because
-  // once a frame has an ad we always consider it to have an ad.
-  // NOTE: Just used for measuring bytes, does not grant security privileges.
-  content::RenderFrameHost* current_frame_host =
-      FindFrameMaybeUnsafe(navigation_handle);
-  if (current_frame_host) {
-    const std::string& frame_name = current_frame_host->GetFrameName();
-    if (base::StartsWith(frame_name, "google_ads_iframe",
-                         base::CompareCase::SENSITIVE) ||
-        base::StartsWith(frame_name, "google_ads_frame",
-                         base::CompareCase::SENSITIVE)) {
-      return true;
-    }
-  }
-
-  const GURL& url = navigation_handle->GetURL();
-  return url.host_piece() == "tpc.googlesyndication.com" &&
-         base::StartsWith(url.path_piece(), "/safeframe",
-                          base::CompareCase::SENSITIVE);
-}
-
 bool IsSubframeSameOriginToMainFrame(content::RenderFrameHost* sub_host,
                                      bool use_parent_origin) {
   DCHECK(sub_host);
@@ -112,13 +74,11 @@
 
 AdsPageLoadMetricsObserver::AdFrameData::AdFrameData(
     FrameTreeNodeId frame_tree_node_id,
-    AdTypes ad_types,
     AdOriginStatus origin_status,
     bool frame_navigated)
     : frame_bytes(0u),
       frame_bytes_uncached(0u),
       frame_tree_node_id(frame_tree_node_id),
-      ad_types(ad_types),
       origin_status(origin_status),
       frame_navigated(frame_navigated) {}
 
@@ -168,7 +128,7 @@
 // and record it into the appropriate data structures.
 void AdsPageLoadMetricsObserver::RecordAdFrameData(
     FrameTreeNodeId ad_id,
-    AdTypes ad_types,
+    bool is_adframe,
     content::RenderFrameHost* ad_host,
     bool frame_navigated) {
   // If an existing subframe is navigating and it was an ad previously that
@@ -178,8 +138,6 @@
   if (id_and_data != ad_frames_data_.end() && id_and_data->second) {
     DCHECK(frame_navigated);
     if (id_and_data->second->frame_navigated) {
-      // We need to update the types with any new types that triggered it.
-      id_and_data->second->ad_types |= ad_types;
       ProcessOngoingNavigationResource(ad_id);
       return;
     }
@@ -200,7 +158,7 @@
 
   // This frame is not nested within an ad frame but is itself an ad.
   AdFrameData* ad_data = parent_id_and_data->second;
-  if (!ad_data && ad_types.any()) {
+  if (!ad_data && is_adframe) {
     AdOriginStatus origin_status = AdOriginStatus::kUnknown;
     if (ad_host) {
       // For ads triggered on render, their origin is their parent's origin.
@@ -210,13 +168,11 @@
     }
     // If data existed already, update it and exit, otherwise, add it.
     if (previous_data) {
-      previous_data->ad_types |= ad_types;
       previous_data->origin_status = origin_status;
       previous_data->frame_navigated = frame_navigated;
       return;
     }
-    ad_frames_data_storage_.emplace_back(ad_id, ad_types, origin_status,
-                                         frame_navigated);
+    ad_frames_data_storage_.emplace_back(ad_id, origin_status, frame_navigated);
     ad_data = &ad_frames_data_storage_.back();
   }
 
@@ -232,7 +188,7 @@
 void AdsPageLoadMetricsObserver::OnDidFinishSubFrameNavigation(
     content::NavigationHandle* navigation_handle) {
   FrameTreeNodeId frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
-  AdTypes ad_types = DetectAds(navigation_handle);
+  bool is_adframe = DetectAds(navigation_handle);
 
   // NOTE: Frame look-up only used for determining cross-origin status, not
   // granting security permissions.
@@ -243,7 +199,7 @@
     bool has_gesture = navigation_handle->HasUserGesture();
 
     std::vector<blink::mojom::WebFeature> web_features;
-    if (ad_types.any()) {
+    if (is_adframe) {
       // Note: Here it covers download due to navigations to non-web-renderable
       // content. These two features can also be logged from blink for download
       // originated from clicking on <a download> link that results in direct
@@ -267,7 +223,7 @@
     flags.has_sandbox = has_sandbox;
     flags.is_cross_origin =
         !IsSubframeSameOriginToMainFrame(ad_host, /*use_parent_origin=*/false);
-    flags.is_ad_frame = ad_types.any();
+    flags.is_ad_frame = is_adframe;
     flags.has_gesture = has_gesture;
     blink::DownloadStats::RecordSubframeDownloadFlags(
         flags,
@@ -276,7 +232,7 @@
         ukm::UkmRecorder::Get());
   }
 
-  RecordAdFrameData(frame_tree_node_id, ad_types, ad_host,
+  RecordAdFrameData(frame_tree_node_id, is_adframe, ad_host,
                     /*frame_navigated=*/true);
   ProcessOngoingNavigationResource(frame_tree_node_id);
 }
@@ -364,10 +320,9 @@
 
 void AdsPageLoadMetricsObserver::OnAdSubframeDetected(
     content::RenderFrameHost* render_frame_host) {
-  AdTypes ad_types;
-  ad_types.set(AD_TYPE_SUBRESOURCE_FILTER);
   FrameTreeNodeId frame_tree_node_id = render_frame_host->GetFrameTreeNodeId();
-  RecordAdFrameData(frame_tree_node_id, ad_types, render_frame_host,
+  RecordAdFrameData(frame_tree_node_id, true /* is_adframe */,
+                    render_frame_host,
                     /*frame_navigated=*/false);
 }
 
@@ -380,17 +335,9 @@
   return unfinished_subresource_ad_frames_.erase(frame_tree_node_id);
 }
 
-AdsPageLoadMetricsObserver::AdTypes AdsPageLoadMetricsObserver::DetectAds(
+bool AdsPageLoadMetricsObserver::DetectAds(
     content::NavigationHandle* navigation_handle) {
-  AdTypes ad_types;
-
-  if (DetectGoogleAd(navigation_handle))
-    ad_types.set(AD_TYPE_GOOGLE);
-
-  if (DetectSubresourceFilterAd(navigation_handle->GetFrameTreeNodeId()))
-    ad_types.set(AD_TYPE_SUBRESOURCE_FILTER);
-
-  return ad_types;
+  return DetectSubresourceFilterAd(navigation_handle->GetFrameTreeNodeId());
 }
 
 void AdsPageLoadMetricsObserver::ProcessResourceForFrame(
@@ -612,14 +559,13 @@
 }
 
 void AdsPageLoadMetricsObserver::RecordHistograms(ukm::SourceId source_id) {
-  RecordHistogramsForType(AD_TYPE_GOOGLE);
-  RecordHistogramsForType(AD_TYPE_SUBRESOURCE_FILTER);
+  RecordHistogramsForAdTagging();
   RecordPageResourceTotalHistograms(source_id);
   for (auto const& kv : page_resources_)
     RecordResourceHistograms(kv.second);
 }
 
-void AdsPageLoadMetricsObserver::RecordHistogramsForType(int ad_type) {
+void AdsPageLoadMetricsObserver::RecordHistogramsForAdTagging() {
   if (page_bytes_ == 0)
     return;
 
@@ -631,62 +577,56 @@
     if (ad_frame_data.frame_bytes == 0)
       continue;
 
-    // If this isn't the type of ad we're looking for, move on to the next.
-    if (!ad_frame_data.ad_types.test(ad_type))
-      continue;
-
     non_zero_ad_frames += 1;
     total_ad_frame_bytes += ad_frame_data.frame_bytes;
 
     uncached_ad_frame_bytes += ad_frame_data.frame_bytes_uncached;
     ADS_HISTOGRAM("Bytes.AdFrames.PerFrame.Total", PAGE_BYTES_HISTOGRAM,
-                  ad_type, ad_frame_data.frame_bytes);
+                  ad_frame_data.frame_bytes);
     ADS_HISTOGRAM("Bytes.AdFrames.PerFrame.Network", PAGE_BYTES_HISTOGRAM,
-                  ad_type, ad_frame_data.frame_bytes_uncached);
+                  ad_frame_data.frame_bytes_uncached);
     ADS_HISTOGRAM(
         "Bytes.AdFrames.PerFrame.PercentNetwork", UMA_HISTOGRAM_PERCENTAGE,
-        ad_type,
+
         ad_frame_data.frame_bytes_uncached * 100 / ad_frame_data.frame_bytes);
     ADS_HISTOGRAM("FrameCounts.AdFrames.PerFrame.OriginStatus",
-                  UMA_HISTOGRAM_ENUMERATION, ad_type,
-                  ad_frame_data.origin_status);
+                  UMA_HISTOGRAM_ENUMERATION, ad_frame_data.origin_status);
   }
 
   // TODO(ericrobinson): Consider renaming this to match
   //   'FrameCounts.AdFrames.PerFrame.OriginStatus'.
   ADS_HISTOGRAM("FrameCounts.AnyParentFrame.AdFrames",
-                UMA_HISTOGRAM_COUNTS_1000, ad_type, non_zero_ad_frames);
+                UMA_HISTOGRAM_COUNTS_1000, non_zero_ad_frames);
 
   // Don't post UMA for pages that don't have ads.
   if (non_zero_ad_frames == 0)
     return;
 
   ADS_HISTOGRAM("Bytes.NonAdFrames.Aggregate.Total", PAGE_BYTES_HISTOGRAM,
-                ad_type, page_bytes_ - total_ad_frame_bytes);
+                page_bytes_ - total_ad_frame_bytes);
 
-  ADS_HISTOGRAM("Bytes.FullPage.Total", PAGE_BYTES_HISTOGRAM, ad_type,
-                page_bytes_);
-  ADS_HISTOGRAM("Bytes.FullPage.Network", PAGE_BYTES_HISTOGRAM, ad_type,
+  ADS_HISTOGRAM("Bytes.FullPage.Total", PAGE_BYTES_HISTOGRAM, page_bytes_);
+  ADS_HISTOGRAM("Bytes.FullPage.Network", PAGE_BYTES_HISTOGRAM,
                 uncached_page_bytes_);
 
   if (page_bytes_) {
     ADS_HISTOGRAM("Bytes.FullPage.Total.PercentAds", UMA_HISTOGRAM_PERCENTAGE,
-                  ad_type, total_ad_frame_bytes * 100 / page_bytes_);
+                  total_ad_frame_bytes * 100 / page_bytes_);
   }
   if (uncached_page_bytes_ > 0) {
     ADS_HISTOGRAM("Bytes.FullPage.Network.PercentAds", UMA_HISTOGRAM_PERCENTAGE,
-                  ad_type,
+
                   uncached_ad_frame_bytes * 100 / uncached_page_bytes_);
   }
 
-  ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Total", PAGE_BYTES_HISTOGRAM, ad_type,
+  ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Total", PAGE_BYTES_HISTOGRAM,
                 total_ad_frame_bytes);
   ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Network", PAGE_BYTES_HISTOGRAM,
-                ad_type, uncached_ad_frame_bytes);
+                uncached_ad_frame_bytes);
 
   if (total_ad_frame_bytes) {
     ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.PercentNetwork",
-                  UMA_HISTOGRAM_PERCENTAGE, ad_type,
+                  UMA_HISTOGRAM_PERCENTAGE,
                   uncached_ad_frame_bytes * 100 / total_ad_frame_bytes);
   }
 }
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h
index 63359a8..0ba3e67 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h
@@ -26,13 +26,6 @@
     : public page_load_metrics::PageLoadMetricsObserver,
       public subresource_filter::SubresourceFilterObserver {
  public:
-  // The types of ads that one can filter on.
-  enum AdType {
-    AD_TYPE_GOOGLE = 0,
-    AD_TYPE_SUBRESOURCE_FILTER = 1,
-    AD_TYPE_MAX = AD_TYPE_SUBRESOURCE_FILTER
-  };
-
   // The origin of the ad relative to the main frame's origin.
   // Note: Logged to UMA, keep in sync with CrossOriginAdStatus in enums.xml.
   //   Add new entries to the end, and do not renumber.
@@ -54,8 +47,6 @@
     kMaxValue = kOther,
   };
 
-  using AdTypes = std::bitset<AD_TYPE_MAX + 1>;
-
   // Returns a new AdsPageLoadMetricObserver. If the feature is disabled it
   // returns nullptr.
   static std::unique_ptr<AdsPageLoadMetricsObserver> CreateIfNeeded();
@@ -70,7 +61,7 @@
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
   void RecordAdFrameData(FrameTreeNodeId ad_id,
-                         AdTypes ad_types,
+                         bool is_adframe,
                          content::RenderFrameHost* ad_host,
                          bool frame_navigated);
   void OnDidFinishSubFrameNavigation(
@@ -93,7 +84,6 @@
  private:
   struct AdFrameData {
     AdFrameData(FrameTreeNodeId frame_tree_node_id,
-                AdTypes ad_types,
                 AdOriginStatus origin_status,
                 bool frame_navigated);
 
@@ -101,7 +91,6 @@
     size_t frame_bytes;
     size_t frame_bytes_uncached;
     const FrameTreeNodeId frame_tree_node_id;
-    AdTypes ad_types;
     AdOriginStatus origin_status;
     bool frame_navigated;
   };
@@ -122,7 +111,7 @@
   // This should only be called once per frame navigation, as the
   // SubresourceFilter detector clears its state about detected frames after
   // each call in order to free up memory.
-  AdTypes DetectAds(content::NavigationHandle* navigation_handle);
+  bool DetectAds(content::NavigationHandle* navigation_handle);
 
   void ProcessResourceForFrame(
       FrameTreeNodeId frame_tree_node_id,
@@ -150,7 +139,7 @@
       const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
   void RecordPageResourceTotalHistograms(ukm::SourceId source_id);
   void RecordHistograms(ukm::SourceId source_id);
-  void RecordHistogramsForType(int ad_type);
+  void RecordHistogramsForAdTagging();
 
   // Checks to see if a resource is waiting for a navigation with the given
   // |frame_tree_node_id| to commit before it can be processed. If so, call
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
index 6f67009..89f842fa 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
@@ -43,7 +43,8 @@
 namespace {
 
 const char kCrossOriginHistogramId[] =
-    "PageLoad.Clients.Ads.Google.FrameCounts.AdFrames.PerFrame.OriginStatus";
+    "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AdFrames.PerFrame."
+    "OriginStatus";
 
 enum class Origin {
   kNavigation,
@@ -115,7 +116,7 @@
  public:
   AdsPageLoadMetricsObserverBrowserTest()
       : subresource_filter::SubresourceFilterBrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(features::kAdsFeature);
+    scoped_feature_list_.InitAndEnableFeature(subresource_filter::kAdTagging);
   }
   ~AdsPageLoadMetricsObserverBrowserTest() override {}
 
@@ -127,6 +128,12 @@
         web_contents);
   }
 
+  void SetUpOnMainThread() override {
+    SubresourceFilterBrowserTest::SetUpOnMainThread();
+    SetRulesetWithRules(
+        {subresource_filter::testing::CreateSuffixRule("ad_iframe_writer.js")});
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 
@@ -141,7 +148,7 @@
   ui_test_utils::NavigateToURL(
       browser(),
       embedded_test_server()->GetURL("/ads_observer/srcdoc_embedded_ad.html"));
-  waiter->AddMinimumCompleteResourcesExpectation(3);
+  waiter->AddMinimumCompleteResourcesExpectation(4);
   waiter->Wait();
   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
   histogram_tester.ExpectUniqueSample(
@@ -168,7 +175,7 @@
   ui_test_utils::NavigateToURL(
       browser(),
       embedded_test_server()->GetURL("/ads_observer/same_origin_ad.html"));
-  waiter->AddMinimumCompleteResourcesExpectation(3);
+  waiter->AddMinimumCompleteResourcesExpectation(4);
   waiter->Wait();
 
   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
@@ -236,14 +243,16 @@
   ui_test_utils::NavigateToURL(browser(),
                                embedded_test_server()->GetURL(
                                    "/ads_observer/docwrite_blank_frame.html"));
-  waiter->AddMinimumCompleteResourcesExpectation(4);
+  waiter->AddMinimumCompleteResourcesExpectation(5);
   waiter->Wait();
   // Navigate away to force the histogram recording.
   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
   histogram_tester.ExpectUniqueSample(
-      "PageLoad.Clients.Ads.Google.FrameCounts.AnyParentFrame.AdFrames", 1, 1);
+      "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AnyParentFrame."
+      "AdFrames",
+      1, 1);
   histogram_tester.ExpectUniqueSample(
-      "PageLoad.Clients.Ads.Google.Bytes.AdFrames.Aggregate.Total",
+      "PageLoad.Clients.Ads.SubresourceFilter.Bytes.AdFrames.Aggregate.Total",
       0 /* < 1 KB */, 1);
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
index a2c6e45..f805701 100644
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
@@ -55,17 +55,12 @@
   size_t uncached_kb;
 };
 
-enum class AdType { GOOGLE = 0, SUBRESOURCE_FILTER = 1 };
 enum class ResourceCached { NOT_CACHED = false, CACHED = true };
 enum class FrameType { AD = 0, NON_AD };
 
-const char kAdUrl[] = "https://tpc.googlesyndication.com/safeframe/1";
+const char kAdUrl[] = "https://ads.com/ad/disallowed.html";
 const char kNonAdUrl[] = "https://foo.com/";
-const char kNonAdUrl2[] = "https://bar.com/";
-const char kNonAdUrlSameOrigin[] = "https://tpc.googlesyndication.com/nonad";
-
-const char kAdName[] = "google_ads_iframe_1";
-const char kNonAdName[] = "foo";
+const char kNonAdUrlSameOrigin[] = "https://ads.com/foo";
 
 // Asynchronously cancels the navigation at WillProcessResponse. Before
 // cancelling, simulates loading a main frame resource.
@@ -119,30 +114,16 @@
   DISALLOW_COPY_AND_ASSIGN(ResourceLoadingCancellingThrottle);
 };
 
-std::string AdTypeToString(AdType ad_type) {
-  switch (ad_type) {
-    case AdType::GOOGLE:
-      return "Google";
-    case AdType::SUBRESOURCE_FILTER:
-      return "SubresourceFilter";
-  }
-  ADD_FAILURE();
-  return "";
+std::string SuffixedHistogram(const std::string& suffix) {
+  return base::StringPrintf("PageLoad.Clients.Ads.SubresourceFilter.%s",
+                            suffix.c_str());
 }
 
-std::string TypedHistogram(const std::string& suffix, AdType ad_type) {
-  return base::StringPrintf("PageLoad.Clients.Ads.%s.%s",
-                            AdTypeToString(ad_type).c_str(), suffix.c_str());
-}
-
-// Verifies that the histograms match what is expected given |google_ad_frames|
-// ad frame byte counts and non-ad counts (|other_cached_kb| and
-// |other_uncached_kb|).
+// Verifies that the histograms match what is expected.
 void TestHistograms(const base::HistogramTester& histograms,
-                    const std::vector<ExpectedFrameBytes>& google_ad_frames,
+                    const std::vector<ExpectedFrameBytes>& ad_frames,
                     size_t non_ad_cached_kb,
-                    size_t non_ad_uncached_kb,
-                    AdType ad_type) {
+                    size_t non_ad_uncached_kb) {
   size_t total_ad_cached_kb = 0;
   size_t total_ad_uncached_kb = 0;
   size_t total_ad_kb = 0;
@@ -154,7 +135,7 @@
 
   // Perform some initial calculations on the number of bytes, of each type,
   // in each ad frame.
-  for (const ExpectedFrameBytes& bytes : google_ad_frames) {
+  for (const ExpectedFrameBytes& bytes : ad_frames) {
     total_ad_cached_kb += bytes.cached_kb;
     total_ad_uncached_kb += bytes.uncached_kb;
     total_ad_kb += bytes.cached_kb + bytes.uncached_kb;
@@ -174,54 +155,52 @@
 
   // Test the histograms.
   histograms.ExpectUniqueSample(
-      TypedHistogram("FrameCounts.AnyParentFrame.AdFrames", ad_type),
-      ad_frame_count, 1);
+      SuffixedHistogram("FrameCounts.AnyParentFrame.AdFrames"), ad_frame_count,
+      1);
 
   if (ad_frame_count == 0)
     return;
 
   for (const auto& total_bytes_and_count : frames_with_total_byte_count) {
     histograms.ExpectBucketCount(
-        TypedHistogram("Bytes.AdFrames.PerFrame.Total", ad_type),
+        SuffixedHistogram("Bytes.AdFrames.PerFrame.Total"),
         total_bytes_and_count.first, total_bytes_and_count.second);
   }
   for (const auto& network_bytes_and_count : frames_with_network_byte_count) {
     histograms.ExpectBucketCount(
-        TypedHistogram("Bytes.AdFrames.PerFrame.Network", ad_type),
+        SuffixedHistogram("Bytes.AdFrames.PerFrame.Network"),
         network_bytes_and_count.first, network_bytes_and_count.second);
   }
   for (const auto& percent_network_and_count :
        frames_with_percent_network_count) {
     histograms.ExpectBucketCount(
-        TypedHistogram("Bytes.AdFrames.PerFrame.PercentNetwork", ad_type),
+        SuffixedHistogram("Bytes.AdFrames.PerFrame.PercentNetwork"),
         percent_network_and_count.first, percent_network_and_count.second);
   }
 
   histograms.ExpectUniqueSample(
-      TypedHistogram("Bytes.AdFrames.Aggregate.Total", ad_type), total_ad_kb,
-      1);
+      SuffixedHistogram("Bytes.AdFrames.Aggregate.Total"), total_ad_kb, 1);
   histograms.ExpectUniqueSample(
-      TypedHistogram("Bytes.AdFrames.Aggregate.Network", ad_type),
+      SuffixedHistogram("Bytes.AdFrames.Aggregate.Network"),
       total_ad_uncached_kb, 1);
   histograms.ExpectUniqueSample(
-      TypedHistogram("Bytes.FullPage.Total", ad_type),
+      SuffixedHistogram("Bytes.FullPage.Total"),
       non_ad_cached_kb + non_ad_uncached_kb + total_ad_kb, 1);
+  histograms.ExpectUniqueSample(SuffixedHistogram("Bytes.FullPage.Network"),
+                                non_ad_uncached_kb + total_ad_uncached_kb, 1);
   histograms.ExpectUniqueSample(
-      TypedHistogram("Bytes.FullPage.Network", ad_type),
-      non_ad_uncached_kb + total_ad_uncached_kb, 1);
-  histograms.ExpectUniqueSample(
-      TypedHistogram("Bytes.NonAdFrames.Aggregate.Total", ad_type),
+      SuffixedHistogram("Bytes.NonAdFrames.Aggregate.Total"),
       non_ad_cached_kb + non_ad_uncached_kb, 1);
   histograms.ExpectUniqueSample(
-      TypedHistogram("Bytes.FullPage.Total.PercentAds", ad_type),
+      SuffixedHistogram("Bytes.FullPage.Total.PercentAds"),
       (total_ad_kb * 100) /
           (total_ad_kb + non_ad_cached_kb + non_ad_uncached_kb),
       1);
   histograms.ExpectUniqueSample(
-      TypedHistogram("Bytes.AdFrames.Aggregate.PercentNetwork", ad_type),
+      SuffixedHistogram("Bytes.AdFrames.Aggregate.PercentNetwork"),
       ((total_ad_uncached_kb * 100) / total_ad_kb), 1);
   histograms.ExpectUniqueSample(
-      TypedHistogram("Bytes.FullPage.Network.PercentAds", ad_type),
+      SuffixedHistogram("Bytes.FullPage.Network.PercentAds"),
       (total_ad_uncached_kb * 100) /
           (total_ad_uncached_kb + non_ad_uncached_kb),
       1);
@@ -241,6 +220,7 @@
             base::BindRepeating(
                 &AdsPageLoadMetricsObserverTest::RegisterObservers,
                 base::Unretained(this)));
+    ConfigureAsSubresourceFilterOnlyURL(GURL(kAdUrl));
   }
 
   // Returns the final RenderFrameHost after navigation commits.
@@ -257,12 +237,19 @@
     return NavigateFrame(url, web_contents()->GetMainFrame());
   }
 
+  // Frame creation doesn't trigger a mojo call since unit tests have no render
+  // process. Just mock them for now.
+  void OnAdSubframeDetected(RenderFrameHost* render_frame_host) {
+    subresource_filter::SubresourceFilterObserverManager::FromWebContents(
+        web_contents())
+        ->NotifyAdSubframeDetected(render_frame_host);
+  }
+
   // Returns the final RenderFrameHost after navigation commits.
   RenderFrameHost* CreateAndNavigateSubFrame(const std::string& url,
-                                             const std::string& frame_name,
                                              content::RenderFrameHost* parent) {
     RenderFrameHost* subframe =
-        RenderFrameHostTester::For(parent)->AppendChild(frame_name);
+        RenderFrameHostTester::For(parent)->AppendChild("frame_name");
     auto navigation_simulator =
         NavigationSimulator::CreateRendererInitiated(GURL(url), subframe);
     navigation_simulator->Commit();
@@ -300,9 +287,13 @@
 
   base::HistogramTester& histogram_tester() { return histogram_tester_; }
 
+  AdsPageLoadMetricsObserver* ads_observer_ = nullptr;
+
  private:
   void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) {
-    tracker->AddObserver(std::make_unique<AdsPageLoadMetricsObserver>());
+    auto observer = std::make_unique<AdsPageLoadMetricsObserver>();
+    ads_observer_ = observer.get();
+    tracker->AddObserver(std::move(observer));
   }
 
   base::HistogramTester histogram_tester_;
@@ -313,10 +304,8 @@
 
 TEST_F(AdsPageLoadMetricsObserverTest, PageWithNoAds) {
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  RenderFrameHost* frame1 =
-      CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
-  RenderFrameHost* frame2 =
-      CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
+  RenderFrameHost* frame1 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
+  RenderFrameHost* frame2 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
   ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
   ResourceDataUpdate(frame1, ResourceCached::NOT_CACHED, 10);
   ResourceDataUpdate(frame2, ResourceCached::NOT_CACHED, 10);
@@ -325,12 +314,27 @@
   NavigateFrame(kNonAdUrl, main_frame);
 
   TestHistograms(histogram_tester(), std::vector<ExpectedFrameBytes>(),
-                 0 /* non_ad_cached_kb */, 30 /* non_ad_uncached_kb */,
-                 AdType::GOOGLE);
+                 0 /* non_ad_cached_kb */, 30 /* non_ad_uncached_kb */);
 
   // Verify that other UMA wasn't written.
   histogram_tester().ExpectTotalCount(
-      "PageLoad.Clients.Ads.Google.Bytes.AdFrames.Aggregate.Total", 0);
+      "PageLoad.Clients.Ads.SubresourceFilter.Bytes.AdFrames.Aggregate.Total",
+      0);
+}
+
+TEST_F(AdsPageLoadMetricsObserverTest, PageWithAds) {
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  RenderFrameHost* frame1 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
+  RenderFrameHost* frame2 = CreateAndNavigateSubFrame(kAdUrl, main_frame);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(frame1, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(frame2, ResourceCached::NOT_CACHED, 10);
+
+  // Navigate again to trigger histograms.
+  NavigateFrame(kNonAdUrl, main_frame);
+
+  TestHistograms(histogram_tester(), {{0, 10}}, 0 /* non_ad_cached_kb */,
+                 20 /* non_ad_uncached_kb */);
 }
 
 TEST_F(AdsPageLoadMetricsObserverTest, ResourceBeforeAdFrameCommits) {
@@ -340,9 +344,9 @@
 
   // Create subframe and load resource before commit.
   RenderFrameHost* subframe =
-      RenderFrameHostTester::For(main_frame)->AppendChild(kAdName);
+      RenderFrameHostTester::For(main_frame)->AppendChild("foo");
   auto navigation_simulator =
-      NavigationSimulator::CreateRendererInitiated(GURL(kNonAdUrl), subframe);
+      NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), subframe);
   ResourceDataUpdate(subframe, ResourceCached::NOT_CACHED, 10);
   navigation_simulator->Commit();
 
@@ -350,61 +354,7 @@
   NavigateFrame(kNonAdUrl, main_frame);
 
   TestHistograms(histogram_tester(), {{0, 10}}, 0 /* non_ad_cached_kb */,
-                 10 /*non_ad_uncached_kb*/, AdType::GOOGLE);
-}
-
-TEST_F(AdsPageLoadMetricsObserverTest, AllAdTypesInPage) {
-  // Make this page DRYRUN.
-  scoped_configuration().ResetConfiguration(subresource_filter::Configuration(
-      subresource_filter::mojom::ActivationLevel::kDryRun,
-      subresource_filter::ActivationScope::ALL_SITES));
-
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  RenderFrameHost* non_ad_frame =
-      CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
-  RenderFrameHost* non_ad_frame2 =
-      CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
-
-  // Create 5 ad frames with the 5th nested inside the 4th. Verify that the
-  // nested ad frame doesn't get counted separately (but that its bytes are
-  // still counted). Also verify that the various ad signals (urls and names)
-  // are properly detected.
-  RenderFrameHost* google_frame1 =
-      CreateAndNavigateSubFrame(kNonAdUrl, "google_ads_iframe_1", main_frame);
-  RenderFrameHost* google_frame2 =
-      CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame);
-
-  RenderFrameHost* srf_frame1 =
-      CreateAndNavigateSubFrame(kDefaultDisallowedUrl, kNonAdName, main_frame);
-  RenderFrameHost* srf_frame2 =
-      CreateAndNavigateSubFrame(kDefaultDisallowedUrl, kNonAdName, main_frame);
-  RenderFrameHost* nested_srf_frame3 =
-      CreateAndNavigateSubFrame(kDefaultDisallowedUrl, kNonAdName, srf_frame2);
-
-  // Create an addditional ad frame without content. It shouldn't be counted
-  // as an ad frame.
-  CreateAndNavigateSubFrame(kDefaultDisallowedUrl, kNonAdName, main_frame);
-
-  // 70KB total in page, 50 from ads, 40 from network, and 30 of those
-  // are from ads.
-  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(non_ad_frame, ResourceCached::CACHED, 10);
-  ResourceDataUpdate(non_ad_frame2, ResourceCached::CACHED, 10);
-  ResourceDataUpdate(google_frame1, ResourceCached::CACHED, 10);
-  ResourceDataUpdate(google_frame2, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(srf_frame1, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(srf_frame2, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(nested_srf_frame3, ResourceCached::CACHED, 10);
-
-  // Navigate again to trigger histograms.
-  NavigateFrame(kNonAdUrl, main_frame);
-
-  TestHistograms(histogram_tester(), {{10, 0}, {0, 10}},
-                 30 /* non_ad_cached_kb */, 30 /* non_ad_uncached_kb */,
-                 AdType::GOOGLE);
-  TestHistograms(histogram_tester(), {{0, 10}, {10, 10}},
-                 30 /* non_ad_cached_kb */, 20 /* non_ad_uncached_kb */,
-                 AdType::SUBRESOURCE_FILTER);
+                 10 /*non_ad_uncached_kb*/);
 }
 
 // Test that the cross-origin ad subframe navigation metric works as it's
@@ -412,7 +362,8 @@
 // origin as the main page, and a true when when the ad has a separate origin.
 TEST_F(AdsPageLoadMetricsObserverTest, AdsOriginStatusMetrics) {
   const char kCrossOriginHistogramId[] =
-      "PageLoad.Clients.Ads.Google.FrameCounts.AdFrames.PerFrame.OriginStatus";
+      "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AdFrames.PerFrame."
+      "OriginStatus";
 
   // Test that when the main frame origin is different from a direct ad
   // subframe it is correctly identified as cross-origin, but do not count
@@ -421,12 +372,11 @@
     base::HistogramTester histograms;
     RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
     RenderFrameHost* ad_sub_frame =
-        CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame);
+        CreateAndNavigateSubFrame(kAdUrl, main_frame);
     ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
     ResourceDataUpdate(ad_sub_frame, ResourceCached::NOT_CACHED, 10);
-    ResourceDataUpdate(
-        CreateAndNavigateSubFrame(kAdUrl, kNonAdName, ad_sub_frame),
-        ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(CreateAndNavigateSubFrame(kAdUrl, ad_sub_frame),
+                       ResourceCached::NOT_CACHED, 10);
     // Trigger histograms by navigating away, then test them.
     NavigateFrame(kAdUrl, main_frame);
     histograms.ExpectUniqueSample(
@@ -440,12 +390,10 @@
     base::HistogramTester histograms;
     RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
     ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-    ResourceDataUpdate(
-        CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame),
-        ResourceCached::NOT_CACHED, 10);
-    ResourceDataUpdate(
-        CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame),
-        ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(CreateAndNavigateSubFrame(kAdUrl, main_frame),
+                       ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(CreateAndNavigateSubFrame(kNonAdUrl, main_frame),
+                       ResourceCached::NOT_CACHED, 10);
     // Trigger histograms by navigating away, then test them.
     NavigateFrame(kAdUrl, main_frame);
     histograms.ExpectUniqueSample(
@@ -460,9 +408,8 @@
     base::HistogramTester histograms;
     RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrlSameOrigin);
     ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-    ResourceDataUpdate(
-        CreateAndNavigateSubFrame(kAdUrl, kNonAdName, main_frame),
-        ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(CreateAndNavigateSubFrame(kAdUrl, main_frame),
+                       ResourceCached::NOT_CACHED, 10);
     // Trigger histograms by navigating away, then test them.
     NavigateFrame(kAdUrl, main_frame);
     histograms.ExpectUniqueSample(
@@ -473,8 +420,7 @@
 
 TEST_F(AdsPageLoadMetricsObserverTest, PageWithAdFrameThatRenavigates) {
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  RenderFrameHost* ad_frame =
-      CreateAndNavigateSubFrame(kNonAdUrl, kAdName, main_frame);
+  RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
 
   ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
   ResourceDataUpdate(ad_frame, ResourceCached::NOT_CACHED, 10);
@@ -489,7 +435,7 @@
   NavigateFrame(kNonAdUrl, main_frame);
 
   TestHistograms(histogram_tester(), {{0, 20}}, 0 /* non_ad_cached_kb */,
-                 10 /* non_ad_uncached_kb */, AdType::GOOGLE);
+                 10 /* non_ad_uncached_kb */);
 }
 
 TEST_F(AdsPageLoadMetricsObserverTest, PageWithNonAdFrameThatRenavigatesToAd) {
@@ -497,12 +443,11 @@
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
 
   // Sub frame that is not an ad.
-  RenderFrameHost* sub_frame =
-      CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
+  RenderFrameHost* sub_frame = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
 
   // Child of the sub-frame that is an ad.
   RenderFrameHost* sub_frame_child_ad =
-      CreateAndNavigateSubFrame(kNonAdUrl2, kAdName, sub_frame);
+      CreateAndNavigateSubFrame(kAdUrl, sub_frame);
 
   ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
   ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
@@ -520,8 +465,7 @@
   NavigateFrame(kNonAdUrl, main_frame);
 
   TestHistograms(histogram_tester(), {{0, 10}, {0, 10}},
-                 0 /* non_ad_cached_kb */, 20 /* non_ad_uncached_kb */,
-                 AdType::GOOGLE);
+                 0 /* non_ad_cached_kb */, 20 /* non_ad_uncached_kb */);
 }
 
 TEST_F(AdsPageLoadMetricsObserverTest, CountAbortedNavigation) {
@@ -531,11 +475,12 @@
 
   // Create an ad subframe that aborts before committing.
   RenderFrameHost* subframe_ad =
-      RenderFrameHostTester::For(main_frame)->AppendChild(kAdName);
-  auto navigation_simulator = NavigationSimulator::CreateRendererInitiated(
-      GURL(kNonAdUrl), subframe_ad);
+      RenderFrameHostTester::For(main_frame)->AppendChild("foo");
+  auto navigation_simulator =
+      NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), subframe_ad);
   // The sub-frame renavigates before it commits.
   navigation_simulator->Start();
+  OnAdSubframeDetected(subframe_ad);
   navigation_simulator->Fail(net::ERR_ABORTED);
 
   // Load resources for the aborted frame (e.g., simulate the navigation
@@ -548,7 +493,7 @@
   NavigateFrame(kNonAdUrl, main_frame);
 
   TestHistograms(histogram_tester(), {{0, 20}}, 0 /* non_ad_cached_kb */,
-                 10 /* non_ad_uncached_kb */, AdType::GOOGLE);
+                 10 /* non_ad_uncached_kb */);
 }
 
 TEST_F(AdsPageLoadMetricsObserverTest, CountAbortedSecondNavigationForFrame) {
@@ -556,8 +501,7 @@
   ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
 
   // Sub frame that is not an ad.
-  RenderFrameHost* sub_frame =
-      CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
+  RenderFrameHost* sub_frame = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
   ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
 
   // Now navigate (and abort) the subframe to an ad.
@@ -565,6 +509,7 @@
       NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), sub_frame);
   // The sub-frame renavigates before it commits.
   navigation_simulator->Start();
+  OnAdSubframeDetected(sub_frame);
   navigation_simulator->Fail(net::ERR_ABORTED);
 
   // Load resources for the aborted frame (e.g., simulate the navigation
@@ -577,7 +522,7 @@
   NavigateFrame(kNonAdUrl, main_frame);
 
   TestHistograms(histogram_tester(), {{0, 20}}, 0 /* non_ad_cached_kb */,
-                 20 /* non_ad_uncached_kb */, AdType::GOOGLE);
+                 20 /* non_ad_uncached_kb */);
 }
 
 TEST_F(AdsPageLoadMetricsObserverTest, TwoResourceLoadsBeforeCommit) {
@@ -588,13 +533,14 @@
   // Now open a subframe and have its resource load before notification of
   // navigation finishing.
   RenderFrameHost* subframe_ad =
-      RenderFrameHostTester::For(main_frame)->AppendChild(kAdName);
-  auto navigation_simulator = NavigationSimulator::CreateRendererInitiated(
-      GURL(kNonAdUrl), subframe_ad);
+      RenderFrameHostTester::For(main_frame)->AppendChild("foo");
+  auto navigation_simulator =
+      NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), subframe_ad);
   ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
 
   // The sub-frame renavigates before it commits.
   navigation_simulator->Start();
+  OnAdSubframeDetected(subframe_ad);
   navigation_simulator->Fail(net::ERR_ABORTED);
 
   // Renavigate the subframe to a successful commit. But again, the resource
@@ -606,7 +552,7 @@
   NavigateFrame(kNonAdUrl, main_frame);
 
   TestHistograms(histogram_tester(), {{0, 20}}, 0 /* non_ad_cached_kb */,
-                 10 /* non_ad_uncached_kb */, AdType::GOOGLE);
+                 10 /* non_ad_uncached_kb */);
 }
 
 // This tests an issue that is believed to be the cause of
@@ -615,14 +561,13 @@
 // know about the frame's parent (because it doesn't exist in the page).
 TEST_F(AdsPageLoadMetricsObserverTest, FrameWithNoParent) {
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  RenderFrameHost* sub_frame =
-      CreateAndNavigateSubFrame(kNonAdUrl, kNonAdName, main_frame);
+  RenderFrameHost* sub_frame = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
 
   // Renavigate the child, but, while navigating, the main frame renavigates.
   RenderFrameHost* child_of_subframe =
-      RenderFrameHostTester::For(sub_frame)->AppendChild(kAdName);
+      RenderFrameHostTester::For(sub_frame)->AppendChild("foo");
   auto navigation_simulator = NavigationSimulator::CreateRendererInitiated(
-      GURL(kNonAdUrl2), child_of_subframe);
+      GURL(kAdUrl), child_of_subframe);
   navigation_simulator->Start();
 
   // Main frame renavigates.
@@ -652,11 +597,14 @@
   // We only log histograms if we observed bytes for the page. Verify that the
   // main frame resource was properly tracked and attributed.
   histogram_tester().ExpectUniqueSample(
-      "PageLoad.Clients.Ads.Google.FrameCounts.AnyParentFrame.AdFrames", 0, 1);
+      "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AnyParentFrame."
+      "AdFrames",
+      0, 1);
   // There shouldn't be any other histograms for a page with no ad
   // resources.
   EXPECT_EQ(1u, histogram_tester()
-                    .GetTotalCountsForPrefix("PageLoad.Clients.Ads.Google.")
+                    .GetTotalCountsForPrefix(
+                        "PageLoad.Clients.Ads.SubresourceFilter.")
                     .size());
 }
 
@@ -681,7 +629,8 @@
 
   // There shouldn't be any histograms for an aborted main frame.
   EXPECT_EQ(0u, histogram_tester()
-                    .GetTotalCountsForPrefix("PageLoad.Clients.Ads.Google.")
+                    .GetTotalCountsForPrefix(
+                        "PageLoad.Clients.Ads.SubresourceFilter.")
                     .size());
 }
 
@@ -694,7 +643,7 @@
   ResourceDataUpdate(main_rfh(), ResourceCached::NOT_CACHED, 10);
 
   RenderFrameHost* subframe =
-      RenderFrameHostTester::For(main_rfh())->AppendChild(kNonAdName);
+      RenderFrameHostTester::For(main_rfh())->AppendChild("foo");
   std::unique_ptr<NavigationSimulator> simulator =
       NavigationSimulator::CreateRendererInitiated(GURL(kDefaultDisallowedUrl),
                                                    subframe);
@@ -706,8 +655,7 @@
 
   NavigateMainFrame(kNonAdUrl);
   TestHistograms(histogram_tester(), std::vector<ExpectedFrameBytes>(),
-                 0u /* non_ad_cached_kb */, 0u /* non_ad_uncached_kb */,
-                 AdType::SUBRESOURCE_FILTER);
+                 0u /* non_ad_cached_kb */, 0u /* non_ad_uncached_kb */);
 }
 
 // UKM metrics for ad page load are recorded correctly.
diff --git a/chrome/browser/page_load_metrics/observers/data_use_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/data_use_metrics_observer.cc
index 9e7060d..4c2c176be 100644
--- a/chrome/browser/page_load_metrics/observers/data_use_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/data_use_metrics_observer.cc
@@ -23,19 +23,47 @@
   return CONTINUE_OBSERVING;
 }
 
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+DataUseMetricsObserver::OnStart(content::NavigationHandle* navigation_handle,
+                                const GURL& currently_committed_url,
+                                bool started_in_foreground) {
+  currently_in_foreground_ = started_in_foreground;
+  return CONTINUE_OBSERVING;
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+DataUseMetricsObserver::OnHidden(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+  currently_in_foreground_ = false;
+  return CONTINUE_OBSERVING;
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+DataUseMetricsObserver::OnShown() {
+  currently_in_foreground_ = true;
+  return CONTINUE_OBSERVING;
+}
+
 void DataUseMetricsObserver::OnResourceDataUseObserved(
     FrameTreeNodeId frame_tree_node_id,
     const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
         resources) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto* chrome_data_use_measurement =
+      data_use_measurement::ChromeDataUseMeasurement::GetInstance();
+  if (!chrome_data_use_measurement)
+    return;
+
   int64_t received_data_length = 0;
   for (auto const& resource : resources) {
     received_data_length += resource->delta_bytes;
+    chrome_data_use_measurement->RecordContentTypeMetric(
+        resource->mime_type, resource->is_main_frame_resource,
+        currently_in_foreground_, resource->delta_bytes);
   }
   if (!received_data_length)
     return;
-  if (auto* chrome_data_use_measurement =
-          data_use_measurement::ChromeDataUseMeasurement::GetInstance()) {
-    chrome_data_use_measurement->ReportUserTrafficDataUse(received_data_length);
-  }
+  chrome_data_use_measurement->ReportUserTrafficDataUse(
+      currently_in_foreground_, received_data_length);
 }
diff --git a/chrome/browser/page_load_metrics/observers/data_use_metrics_observer.h b/chrome/browser/page_load_metrics/observers/data_use_metrics_observer.h
index 90248ed..784f9df 100644
--- a/chrome/browser/page_load_metrics/observers/data_use_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/data_use_metrics_observer.h
@@ -8,6 +8,8 @@
 #include "base/macros.h"
 #include "base/sequence_checker.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+#include "chrome/common/page_load_metrics/page_load_metrics.mojom.h"
+#include "chrome/common/page_load_metrics/page_load_timing.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
 namespace content {
@@ -26,11 +28,20 @@
   // page_load_metrics::PageLoadMetricsObserver:
   ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
                          ukm::SourceId source_id) override;
+  ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
+                        const GURL& currently_committed_url,
+                        bool started_in_foreground) override;
+  ObservePolicy OnHidden(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  ObservePolicy OnShown() override;
   void OnResourceDataUseObserved(
       FrameTreeNodeId frame_tree_node_id,
       const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
           resources) override;
 
+  bool currently_in_foreground_ = false;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(DataUseMetricsObserver);
diff --git a/chrome/browser/page_load_metrics/observers/data_use_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/data_use_metrics_observer_browsertest.cc
new file mode 100644
index 0000000..9d12feca
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/data_use_metrics_observer_browsertest.cc
@@ -0,0 +1,91 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/data_use_metrics_observer.h"
+
+#include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/data_use_measurement/core/data_use_user_data.h"
+#include "content/public/test/browser_test_base.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/network/public/cpp/features.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class DataUseMetricsObserverBrowserTest : public InProcessBrowserTest {
+ protected:
+  void SetUp() override {
+    scoped_feature_list_.InitAndEnableFeature(
+        network::features::kNetworkService);
+    InProcessBrowserTest::SetUp();
+  }
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(DataUseMetricsObserverBrowserTest,
+                       NavigateToSimplePage) {
+  const struct {
+    std::string url;
+    size_t expected_min_page_size;
+    size_t expected_max_page_size;
+  } tests[] = {
+      // The range of the pages is calculated approximately from the html size
+      // and the size of the subresources it includes.
+      {"/google/google.html", 5000, 20000},
+      {"/simple.html", 100, 1000},
+      {"/media/youtube.html", 5000, 20000},
+  };
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  for (const auto& test : tests) {
+    base::HistogramTester histogram_tester;
+    ui_test_utils::NavigateToURL(browser(),
+                                 embedded_test_server()->GetURL(test.url));
+
+    base::RunLoop().RunUntilIdle();
+    // Navigate away to finish the histogram recording.
+    ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+
+    uint64_t total_usage = 0, total_apptabstate_usage = 0;
+    for (const auto& sample : histogram_tester.GetAllSamples(
+             "DataUse.TrafficSize.User.Downstream.Foreground.NotCellular")) {
+      total_usage += sample.min * sample.count;
+    }
+    for (const auto& sample : histogram_tester.GetAllSamples(
+             "DataUse.AppTabState.Downstream.AppForeground.TabForeground")) {
+      total_apptabstate_usage += sample.min * sample.count;
+    }
+
+    EXPECT_LE(test.expected_min_page_size, total_usage);
+    EXPECT_GE(test.expected_max_page_size, total_usage);
+    EXPECT_LE(test.expected_min_page_size, total_apptabstate_usage);
+    EXPECT_GE(test.expected_max_page_size, total_apptabstate_usage);
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(DataUseMetricsObserverBrowserTest, TestContentType) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  base::HistogramTester histogram_tester;
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("/google/google.html"));
+
+  base::RunLoop().RunUntilIdle();
+  // Navigate away to finish the histogram recording.
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+
+  base::HistogramBase::Count main_frame_html_data_use =
+      histogram_tester.GetBucketCount(
+          "DataUse.ContentType.UserTrafficKB",
+          data_use_measurement::DataUseUserData::MAIN_FRAME_HTML);
+  base::HistogramBase::Count image_data_use = histogram_tester.GetBucketCount(
+      "DataUse.ContentType.UserTrafficKB",
+      data_use_measurement::DataUseUserData::IMAGE);
+
+  // Verify that some bytes are recorded for the main frame html and image.
+  EXPECT_LE(1, main_frame_html_data_use);
+  EXPECT_LE(1, image_data_use);
+}
diff --git a/chrome/browser/predictors/loading_predictor_browsertest.cc b/chrome/browser/predictors/loading_predictor_browsertest.cc
index 9cbd307..5262a85 100644
--- a/chrome/browser/predictors/loading_predictor_browsertest.cc
+++ b/chrome/browser/predictors/loading_predictor_browsertest.cc
@@ -470,11 +470,13 @@
   // Checking GetActiveHintsSizeForTesting() is racy since the active hint
   // is removed after the preconnect finishes. Instead check for total
   // hints activated.
-  EXPECT_EQ(1u, loading_predictor()->GetTotalHintsActivatedForTesting());
+  EXPECT_LE(1u, loading_predictor()->GetTotalHintsActivatedForTesting());
+  EXPECT_GE(2u, loading_predictor()->GetTotalHintsActivatedForTesting());
   observer->WaitForNavigationFinished();
   EXPECT_EQ(0u, loading_predictor()->GetActiveNavigationsSizeForTesting());
   EXPECT_EQ(0u, loading_predictor()->GetActiveHintsSizeForTesting());
-  EXPECT_EQ(1u, loading_predictor()->GetTotalHintsActivatedForTesting());
+  EXPECT_LE(1u, loading_predictor()->GetTotalHintsActivatedForTesting());
+  EXPECT_GE(2u, loading_predictor()->GetTotalHintsActivatedForTesting());
 }
 
 // Tests that two concurrenct navigations are recorded correctly by the
@@ -490,12 +492,14 @@
   // Checking GetActiveHintsSizeForTesting() is racy since the active hint
   // is removed after the preconnect finishes. Instead check for total
   // hints activated.
-  EXPECT_EQ(2u, loading_predictor()->GetTotalHintsActivatedForTesting());
+  EXPECT_LE(2u, loading_predictor()->GetTotalHintsActivatedForTesting());
+  EXPECT_GE(4u, loading_predictor()->GetTotalHintsActivatedForTesting());
   observer1->WaitForNavigationFinished();
   observer2->WaitForNavigationFinished();
   EXPECT_EQ(0u, loading_predictor()->GetActiveNavigationsSizeForTesting());
   EXPECT_EQ(0u, loading_predictor()->GetActiveHintsSizeForTesting());
-  EXPECT_EQ(2u, loading_predictor()->GetTotalHintsActivatedForTesting());
+  EXPECT_LE(2u, loading_predictor()->GetTotalHintsActivatedForTesting());
+  EXPECT_GE(4u, loading_predictor()->GetTotalHintsActivatedForTesting());
 }
 
 // Tests that two navigations to the same URL are deduplicated.
@@ -513,13 +517,13 @@
   // navigation arrives before the first preconnect finishes. However, if the
   // second navigation arrives later, then two hints may get activated.
   EXPECT_LE(1u, loading_predictor()->GetTotalHintsActivatedForTesting());
-  EXPECT_GE(2u, loading_predictor()->GetTotalHintsActivatedForTesting());
+  EXPECT_GE(4u, loading_predictor()->GetTotalHintsActivatedForTesting());
   observer1->WaitForNavigationFinished();
   observer2->WaitForNavigationFinished();
   EXPECT_EQ(0u, loading_predictor()->GetActiveNavigationsSizeForTesting());
   EXPECT_EQ(0u, loading_predictor()->GetActiveHintsSizeForTesting());
   EXPECT_LE(1u, loading_predictor()->GetTotalHintsActivatedForTesting());
-  EXPECT_GE(2u, loading_predictor()->GetTotalHintsActivatedForTesting());
+  EXPECT_GE(4u, loading_predictor()->GetTotalHintsActivatedForTesting());
 }
 
 // Tests that the LoadingPredictor doesn't record non-http(s) navigations.
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index b31b220..74f24320 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -145,6 +145,7 @@
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl.h"
 #include "chrome/browser/chromeos/android_sms/android_sms_pairing_state_tracker_impl.h"
+#include "chrome/browser/chromeos/android_sms/android_sms_service_factory.h"
 #include "chrome/browser/chromeos/arc/arc_service_launcher.h"
 #include "chrome/browser/chromeos/authpolicy/auth_policy_credentials_manager.h"
 #include "chrome/browser/chromeos/cryptauth/gcm_device_info_provider_impl.h"
@@ -1187,6 +1188,9 @@
   }
 
   if (service_name == chromeos::multidevice_setup::mojom::kServiceName) {
+    chromeos::android_sms::AndroidSmsService* android_sms_service =
+        chromeos::android_sms::AndroidSmsServiceFactory::GetForBrowserContext(
+            this);
     return std::make_unique<
         chromeos::multidevice_setup::MultiDeviceSetupService>(
         std::move(request), GetPrefs(),
@@ -1195,10 +1199,12 @@
             this),
         chromeos::multidevice_setup::OobeCompletionTrackerFactory::
             GetForProfile(this),
-        std::make_unique<
-            chromeos::android_sms::AndroidSmsAppHelperDelegateImpl>(this),
-        std::make_unique<
-            chromeos::android_sms::AndroidSmsPairingStateTrackerImpl>(this),
+        android_sms_service
+            ? android_sms_service->android_sms_app_helper_delegate()
+            : nullptr,
+        android_sms_service
+            ? android_sms_service->android_sms_pairing_state_tracker()
+            : nullptr,
         chromeos::GcmDeviceInfoProviderImpl::GetInstance());
   }
 
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/braille_table_test.extjs b/chrome/browser/resources/chromeos/chromevox/braille/braille_table_test.extjs
index a4d363c..ed92cac1 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/braille_table_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/braille/braille_table_test.extjs
@@ -27,7 +27,7 @@
  */
 TEST_F('ChromeVoxBrailleTableTest', 'testGetAllAndValidate', function() {
   cvox.BrailleTable.getAll(this.newCallback(function(tables) {
-    expectEquals(59, tables.length);
+    expectEquals(166, tables.length);
     assertNotNullNorUndefined(
         cvox.BrailleTable.forId(tables, 'en-US-g1'),
         'Can\'t find US English grade 1 table');
diff --git a/chrome/browser/resources/chromeos/chromevox/common/key_util.js b/chrome/browser/resources/chromeos/chromevox/common/key_util.js
index 067af93..a0d46e2 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/key_util.js
+++ b/chrome/browser/resources/chromeos/chromevox/common/key_util.js
@@ -309,6 +309,8 @@
     return 'F11';
   } else if (keyCode == 123) {
     return 'F12';
+  } else if (keyCode == 153) {
+    return cros ? msg('assistant_key') : '';
   } else if (keyCode == 186) {
     return 'Semicolon';
   } else if (keyCode == 187) {
diff --git a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
index e87e8af..a169eee 100644
--- a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
+++ b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
@@ -2565,44 +2565,85 @@
       </message>
       <message desc="A dictionary mapping locale identifiers to their corresponding language names. The format is the following: { ..., 'en_US': 'English (United States)', ...}. Translation only needed for the language and country names (e.g. English (United States)). All other strings must be kept as is." name="IDS_CHROMEVOX_LOCALE_DICT">
         {"ar": "Arabic",
+        "as": "Assamese",
+        "aw": "Awadhi",
+        "be": "Bengali",
         "bg": "Bulgarian",
+        "bh": "Bihari",
+        "bo": "Tibetan",
+        "br": "Braj",
         "ca": "Catalan",
+        "ckb": "Sorani",
         "cs": "Czech",
+        "cy": "Welsh",
         "da": "Danish",
         "de": "German",
         "de_CH": "German (Switzerland)",
         "de_DE": "German (Germany)",
+        "dra": "Dravidian",
         "el": "Greek",
         "en": "English",
         "en_CA": "English (Canada)",
         "en_GB": "English (United Kingdom)",
         "en_US": "English (United States)",
+        "eo": "Esperanto",
         "es": "Spanish",
         "et": "Estonian",
+        "fa": "Persian",
         "fr": "French",
         "fr_CA": "French (Canada)",
         "fr_FR": "French (France)",
         "fi": "Finnish",
+        "ga": "Irish",
+        "gd": "Gaelic",
+        "gon": "Gondi",
+        "gu": "Gujarati",
         "he": "Hebrew",
         "hi": "Hindi",
         "hr": "Croatian",
         "hu": "Hungarian",
+        "hy": "Armenian",
         "is": "Icelandic",
         "it": "Italian",
+        "iu": "Inuktitut",
+        "ka": "Kannada",
+        "kh": "Khasi",
         "ko": "Korean",
+        "kok": "Konkani",
+        "kru": "Kurukh",
+        "ks": "Kashmiri",
         "lt": "Lithuanian",
         "lv": "Latvian",
+        "mao": "Maori",
+        "ml": "Malayalam",
+        "mt": "Maltese",
+        "mwr": "Marwari",
+        "mn": "Mongolian",
+        "mun": "Munda",
         "nb": "Norwegian Bokmål",
+        "ne": "Nepali",
         "nl": "Dutch",
+        "no": "Norwegian",
+        "or": "Oriya",
+        "pi": "Pali",
         "pl": "Polish",
         "pt": "Portuguese",
+        "pu": "Punjabi",
         "ro": "Romanian",
         "ru": "Russian",
+        "sa": "Sanskrit",
+        "si": "Sindhi",
+        "sin": "Sinhala",
         "sk": "Slovak",
         "sl": "Slovenian",
         "sr": "Serbian",
         "sv": "Swedish",
+        "ta": "Tamil",
+        "te": "Telugu",
         "tr": "Turkish",
+        "tsn": "Afrikaans",
+        "uk": "Ukrainian",
+        "ur": "Urdu",
         "vi": "Vietnamese",
         "zh": "Chinese",
         "zh_TW": "Chinese (Traditional Han)"}
@@ -2675,6 +2716,9 @@
       <message desc="Describes the volume up key in the ChromeVox keyboard explorer." name="IDS_CHROMEVOX_VOLUME_UP_KEY">
         volume up
       </message>
+      <message desc="Describes the Assistant key in the ChromeVox keyboard explorer." name="IDS_CHROMEVOX_ASSISTANT_KEY">
+        Google Assistant
+      </message>
 
       <!-- Panel -->
       <message desc="Title of the ChromeVox panel, a window that displays the text ChromeVox is speaking and contains controls to manipulate ChromeVox." name="IDS_CHROMEVOX_PANEL_TITLE">
diff --git a/chrome/browser/resources/chromeos/switch_access/context_menu_manager.js b/chrome/browser/resources/chromeos/switch_access/context_menu_manager.js
index 1cf79a0..0dca335 100644
--- a/chrome/browser/resources/chromeos/switch_access/context_menu_manager.js
+++ b/chrome/browser/resources/chromeos/switch_access/context_menu_manager.js
@@ -190,15 +190,29 @@
   /**
    * Determines which menu actions are relevant, given the current node.
    * @param {!chrome.automation.AutomationNode} node
+   * @return {!Array<ContextMenuManager.Action>}
    * @private
    */
   getActionsForNode_(node) {
-    // TODO(crbug/881080): determine relevant actions programmatically.
-    let actions = [
-      ContextMenuManager.Action.CLICK, ContextMenuManager.Action.DICTATION,
-      ContextMenuManager.Action.OPTIONS, ContextMenuManager.Action.SCROLL_UP,
-      ContextMenuManager.Action.SCROLL_DOWN
-    ];
+    let actions =
+        [ContextMenuManager.Action.CLICK, ContextMenuManager.Action.OPTIONS];
+
+    if (SwitchAccessPredicate.isTextInput(node))
+      actions.push(ContextMenuManager.Action.DICTATION);
+
+    if (node.scrollable) {
+      // TODO(crbug/920659) This does not work for ARC++. Implement scroll
+      // directions via standardActions.
+      if (node.scrollX > node.scrollXMin)
+        actions.push(ContextMenuManager.Action.SCROLL_LEFT);
+      if (node.scrollX < node.scrollXMax)
+        actions.push(ContextMenuManager.Action.SCROLL_RIGHT);
+      if (node.scrollY > node.scrollYMin)
+        actions.push(ContextMenuManager.Action.SCROLL_UP);
+      if (node.scrollY < node.scrollYMax)
+        actions.push(ContextMenuManager.Action.SCROLL_DOWN);
+    }
+
     return actions;
   }
 
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js b/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js
index 73aaa4a..44df51f 100644
--- a/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js
+++ b/chrome/browser/resources/chromeos/switch_access/switch_access_predicate.js
@@ -15,6 +15,7 @@
  *    - isGroup
  *    - isInteresting
  *    - isInterestingSubtree
+ *    - isTextInput
  *    - isNotContainer
  *    - isContextMenu
  *
@@ -62,7 +63,7 @@
     if (role === RoleType.BUTTON)
       return true;
 
-    if (node.inputType && node.inputType !== 'full-page')
+    if (SwitchAccessPredicate.isTextInput(node))
       return true;
 
     if (defaultActionVerb &&
@@ -146,6 +147,13 @@
       node.children.some(SwitchAccessPredicate.isInterestingSubtree),
 
   /**
+   * Returns true if |node| is an element that contains editable text.
+   * @param {!chrome.automation.AutomationNode} node
+   * @return {boolean}
+   */
+  isTextInput: (node) => !!node.state[StateType.EDITABLE],
+
+  /**
    * Returns true if |node| does not have a role of desktop, window, web view,
    * or root web area.
    * @param {!chrome.automation.AutomationNode} node
diff --git a/chrome/browser/resources/md_downloads/item.html b/chrome/browser/resources/md_downloads/item.html
index 7e14642..06f5f43 100644
--- a/chrome/browser/resources/md_downloads/item.html
+++ b/chrome/browser/resources/md_downloads/item.html
@@ -142,6 +142,11 @@
         color: var(--google-red-700);
       }
 
+      :host-context([dark]) #file-icon-wrapper iron-icon[icon='cr:warning'],
+      :host-context([dark]) .dangerous #description {
+        color: var(--google-red-refresh-300);
+      }
+
       #name,
       #file-link,
       #url {
diff --git a/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js b/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js
index ce57117..0eb9bd55 100644
--- a/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js
+++ b/chrome/browser/resources/pdf/elements/viewer-ink-host/viewer-ink-host.js
@@ -50,6 +50,7 @@
     pointermove: 'onPointerMove_',
     pointercancel: 'onPointerUpOrCancel_',
     pointerleave: 'onPointerLeave_',
+    touchstart: 'onTouchStart_',
   },
 
   /** @param {AnnotationTool} tool */
@@ -82,6 +83,13 @@
     });
   },
 
+  /** @param {TouchEvent} e */
+  onTouchStart_: function(e) {
+    // TODO(dstockwell): prevent this conditionally when in "pen mode"
+    e.preventDefault();
+  },
+
+  /** @param {PointerEvent} e */
   onPointerDown_: function(e) {
     if (e.pointerType == 'mouse' && e.buttons != 1 || this.pointerGesture_) {
       return;
diff --git a/chrome/browser/resources/print_preview/data/user_info.js b/chrome/browser/resources/print_preview/data/user_info.js
index 0e5a4f85..14d9d8cf 100644
--- a/chrome/browser/resources/print_preview/data/user_info.js
+++ b/chrome/browser/resources/print_preview/data/user_info.js
@@ -28,6 +28,10 @@
     activeUser: {
       type: String,
       notify: true,
+      // The initialization below is needed only in Polymer 1, to allow
+      // observers to fire.
+      // TODO (rbpotter): Remove when migration to Polymer 2 is complete.
+      value: '',
     },
 
     /** @type {?cloudprint.CloudPrintInterface} */
diff --git a/chrome/browser/resources/print_preview/new/app.js b/chrome/browser/resources/print_preview/new/app.js
index 6e391ce9..660e2f7 100644
--- a/chrome/browser/resources/print_preview/new/app.js
+++ b/chrome/browser/resources/print_preview/new/app.js
@@ -723,6 +723,10 @@
 
   /** @private */
   onActiveUserChanged_: function() {
+    if (!this.activeUser_) {
+      return;
+    }
+
     assert(this.cloudPrintState_ !== print_preview.CloudPrintState.DISABLED);
     this.cloudPrintState_ = print_preview.CloudPrintState.SIGNED_IN;
   },
diff --git a/chrome/browser/safe_browsing/android/services_delegate_android.cc b/chrome/browser/safe_browsing/android/services_delegate_android.cc
index 0dc7844..cc65b2a 100644
--- a/chrome/browser/safe_browsing/android/services_delegate_android.cc
+++ b/chrome/browser/safe_browsing/android/services_delegate_android.cc
@@ -130,4 +130,8 @@
   return telemetry_service_.get();
 }
 
+std::string ServicesDelegateAndroid::GetSafetyNetId() const {
+  return database_manager_->GetSafetyNetId();
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/android/services_delegate_android.h b/chrome/browser/safe_browsing/android/services_delegate_android.h
index 216fcce..f821aa1 100644
--- a/chrome/browser/safe_browsing/android/services_delegate_android.h
+++ b/chrome/browser/safe_browsing/android/services_delegate_android.h
@@ -53,6 +53,8 @@
   void RemoveTelemetryService() override;
   TelemetryService* GetTelemetryService() const override;
 
+  std::string GetSafetyNetId() const override;
+
   SafeBrowsingService* const safe_browsing_service_;
 
   // The telemetry service tied to the current profile.
diff --git a/chrome/browser/safe_browsing/services_delegate.h b/chrome/browser/safe_browsing/services_delegate.h
index b474840..e315923 100644
--- a/chrome/browser/safe_browsing/services_delegate.h
+++ b/chrome/browser/safe_browsing/services_delegate.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_SAFE_BROWSING_SERVICES_DELEGATE_H_
 
 #include <memory>
+#include <string>
 
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/safe_browsing/incident_reporting/delayed_analysis_callback.h"
@@ -122,6 +123,8 @@
   virtual void CreateTelemetryService(Profile* profile) = 0;
   virtual void RemoveTelemetryService() = 0;
   virtual TelemetryService* GetTelemetryService() const = 0;
+
+  virtual std::string GetSafetyNetId() const = 0;
 };
 
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/services_delegate_desktop.cc b/chrome/browser/safe_browsing/services_delegate_desktop.cc
index 1eb00c0..78b04163 100644
--- a/chrome/browser/safe_browsing/services_delegate_desktop.cc
+++ b/chrome/browser/safe_browsing/services_delegate_desktop.cc
@@ -236,4 +236,9 @@
   return nullptr;
 }
 
+std::string ServicesDelegateDesktop::GetSafetyNetId() const {
+  NOTREACHED() << "Only implemented on Android";
+  return "";
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/services_delegate_desktop.h b/chrome/browser/safe_browsing/services_delegate_desktop.h
index 62223a1..03bff57 100644
--- a/chrome/browser/safe_browsing/services_delegate_desktop.h
+++ b/chrome/browser/safe_browsing/services_delegate_desktop.h
@@ -76,6 +76,8 @@
   void RemoveTelemetryService() override;
   TelemetryService* GetTelemetryService() const override;
 
+  std::string GetSafetyNetId() const override;
+
   std::unique_ptr<ClientSideDetectionService> csd_service_;
   std::unique_ptr<DownloadProtectionService> download_service_;
   std::unique_ptr<IncidentReportingService> incident_service_;
diff --git a/chrome/browser/subresource_filter/ad_tagging_browsertest.cc b/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
index 02663330..c1b0b8a 100644
--- a/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
+++ b/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
@@ -212,8 +212,6 @@
 const char kSubresourceFilterOriginStatusHistogram[] =
     "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AdFrames.PerFrame."
     "OriginStatus";
-const char kGoogleOriginStatusHistogram[] =
-    "PageLoad.Clients.Ads.Google.FrameCounts.AdFrames.PerFrame.OriginStatus";
 const char kWindowOpenFromAdStateHistogram[] = "Blink.WindowOpen.FromAdState";
 
 IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest, VerifySameOriginWithoutNavigate) {
@@ -258,7 +256,6 @@
 
   auto waiter = CreatePageLoadMetricsTestWaiter();
   // Create the main frame and cross origin subframe from an ad script.
-  // This triggers both subresource_filter and Google ad detection.
   ui_test_utils::NavigateToURL(browser(), GetURL("frame_factory.html"));
   CreateSrcFrameFromAdScript(GetWebContents(),
                              embedded_test_server()->GetURL(
@@ -267,15 +264,12 @@
   // Waiting for the navigation to finish is not sufficient, as it blocks on the
   // main resource load finishing, not the iframe resource. Page loads 4
   // resources, a favicon, and 2 resources for the iframe.
-  waiter->AddMinimumCompleteResourcesExpectation(7);
+  waiter->AddMinimumCompleteResourcesExpectation(8);
   waiter->Wait();
 
   // Navigate away and ensure we report cross origin.
   ui_test_utils::NavigateToURL(browser(), GetURL(url::kAboutBlankURL));
   histogram_tester.ExpectUniqueSample(
-      kGoogleOriginStatusHistogram,
-      AdsPageLoadMetricsObserver::AdOriginStatus::kCross, 1);
-  histogram_tester.ExpectUniqueSample(
       kSubresourceFilterOriginStatusHistogram,
       AdsPageLoadMetricsObserver::AdOriginStatus::kCross, 1);
 }
@@ -298,7 +292,6 @@
 
   // Navigate away and ensure we report same origin.
   ui_test_utils::NavigateToURL(browser(), GetURL(url::kAboutBlankURL));
-  histogram_tester.ExpectTotalCount(kGoogleOriginStatusHistogram, 0);
   histogram_tester.ExpectUniqueSample(
       kSubresourceFilterOriginStatusHistogram,
       AdsPageLoadMetricsObserver::AdOriginStatus::kSame, 1);
diff --git a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
index 90a8a3a..76e36d8 100644
--- a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
@@ -59,20 +59,12 @@
   DISALLOW_COPY_AND_ASSIGN(SingleClientSecondaryAccountSyncTest);
 };
 
-class SingleClientSecondaryAccountWithoutStandaloneTransportSyncTest
-    : public SingleClientSecondaryAccountSyncTest {
- public:
-  SingleClientSecondaryAccountWithoutStandaloneTransportSyncTest() {
-    features_.InitAndDisableFeature(switches::kSyncStandaloneTransport);
-  }
+IN_PROC_BROWSER_TEST_F(SingleClientSecondaryAccountSyncTest,
+                       DoesNotStartSyncWithStandaloneTransportDisabled) {
+  base::test::ScopedFeatureList disable_standalone_transport;
+  disable_standalone_transport.InitAndDisableFeature(
+      switches::kSyncStandaloneTransport);
 
- private:
-  base::test::ScopedFeatureList features_;
-};
-
-IN_PROC_BROWSER_TEST_F(
-    SingleClientSecondaryAccountWithoutStandaloneTransportSyncTest,
-    DoesNotStartSyncWithStandaloneTransportDisabled) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
 
   // Since standalone transport is disabled, just signing in (without making the
@@ -82,20 +74,12 @@
             GetSyncService(0)->GetTransportState());
 }
 
-class SingleClientSecondaryAccountWithoutSecondaryAccountSupportSyncTest
-    : public SingleClientSecondaryAccountSyncTest {
- public:
-  SingleClientSecondaryAccountWithoutSecondaryAccountSupportSyncTest() {
-    features_.InitAndDisableFeature(switches::kSyncSupportSecondaryAccount);
-  }
+IN_PROC_BROWSER_TEST_F(SingleClientSecondaryAccountSyncTest,
+                       DoesNotStartSyncWithSecondaryAccountSupportDisabled) {
+  base::test::ScopedFeatureList disable_secondary_account_support;
+  disable_secondary_account_support.InitAndDisableFeature(
+      switches::kSyncSupportSecondaryAccount);
 
- private:
-  base::test::ScopedFeatureList features_;
-};
-
-IN_PROC_BROWSER_TEST_F(
-    SingleClientSecondaryAccountWithoutSecondaryAccountSupportSyncTest,
-    DoesNotStartSyncWithSecondaryAccountSupportDisabled) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
 
   // Since secondary account support is disabled, just signing in (without
diff --git a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
index b896e44..7497db2 100644
--- a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
@@ -321,20 +321,12 @@
           .Wait());
 }
 
-class SingleClientSessionsWithDeferRecyclingSyncTest
-    : public SingleClientSessionsSyncTest {
- public:
-  SingleClientSessionsWithDeferRecyclingSyncTest() {
-    features_.InitAndEnableFeature(
-        sync_sessions::kDeferRecyclingOfSyncTabNodesIfUnsynced);
-  }
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-
-IN_PROC_BROWSER_TEST_F(SingleClientSessionsWithDeferRecyclingSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest,
                        NavigateThenCloseTabThenOpenTab) {
+  base::test::ScopedFeatureList override_features;
+  override_features.InitAndEnableFeature(
+      sync_sessions::kDeferRecyclingOfSyncTabNodesIfUnsynced);
+
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   ASSERT_TRUE(CheckInitialState(0));
 
diff --git a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
index ab796265..f3c92dc 100644
--- a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
@@ -53,19 +53,12 @@
   DISALLOW_COPY_AND_ASSIGN(SingleClientStandaloneTransportSyncTest);
 };
 
-class SingleClientStandaloneTransportFeatureDisabledSyncTest : public SyncTest {
- public:
-  SingleClientStandaloneTransportFeatureDisabledSyncTest()
-      : SyncTest(SINGLE_CLIENT) {
-    features_.InitAndDisableFeature(switches::kSyncStandaloneTransport);
-  }
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-
-IN_PROC_BROWSER_TEST_F(SingleClientStandaloneTransportFeatureDisabledSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientStandaloneTransportSyncTest,
                        DoesNotStartSyncWithFeatureDisabled) {
+  base::test::ScopedFeatureList disable_standalone_transport;
+  disable_standalone_transport.InitAndDisableFeature(
+      switches::kSyncStandaloneTransport);
+
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
 
   // Since standalone transport is disabled, signing in should *not* start the
diff --git a/chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc b/chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc
index 45d86bd..accb0b6 100644
--- a/chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc
@@ -186,22 +186,14 @@
   EXPECT_TRUE(ExpectUserConsents({specifics1, specifics2}));
 }
 
-class SingleClientUserConsentsWithStandaloneTransportSyncTest
-    : public SingleClientUserConsentsSyncTest {
- public:
-  SingleClientUserConsentsWithStandaloneTransportSyncTest() {
-    features_.InitAndEnableFeature(switches::kSyncStandaloneTransport);
-  }
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-
 // ChromeOS does not support late signin after profile creation, so the test
 // below does not apply, at least in the current form.
 #if !defined(OS_CHROMEOS)
-IN_PROC_BROWSER_TEST_F(SingleClientUserConsentsWithStandaloneTransportSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientUserConsentsSyncTest,
                        ShouldSubmitIfSignedInAlthoughFullSyncNotEnabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(switches::kSyncStandaloneTransport);
+
   // We avoid calling SetupSync(), because we don't want to turn on full sync,
   // only sign in such that the standalone transport starts.
   ASSERT_TRUE(SetupClients());
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
index 2052a68f..4557714 100644
--- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -307,22 +307,6 @@
   ExpectNoHistogramsForAddressesDiff();
 }
 
-class SingleClientWalletWithAccountStorageSyncTest
-    : public SingleClientWalletSyncTest {
- public:
-  SingleClientWalletWithAccountStorageSyncTest() {
-    features_.InitWithFeatures(
-        /*enabled_features=*/{switches::kSyncStandaloneTransport,
-                              switches::kSyncUSSAutofillWalletData,
-                              autofill::features::
-                                  kAutofillEnableAccountWalletStorage},
-        /*disabled_features=*/{});
-  }
-
- private:
-  base::test::ScopedFeatureList features_;
-};
-
 // ChromeOS does not support late signin after profile creation, so the test
 // below does not apply, at least in the current form.
 #if !defined(OS_CHROMEOS)
@@ -334,8 +318,16 @@
 #endif
 // The account storage requires USS, so we only test the USS implementation
 // here.
-IN_PROC_BROWSER_TEST_F(SingleClientWalletWithAccountStorageSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest,
                        MAYBE_DownloadAccountStorage_Card) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures(
+      /*enabled_features=*/{switches::kSyncStandaloneTransport,
+                            switches::kSyncUSSAutofillWalletData,
+                            autofill::features::
+                                kAutofillEnableAccountWalletStorage},
+      /*disabled_features=*/{});
+
   ASSERT_TRUE(SetupClients());
   autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
   pdm->OnSyncServiceInitialized(GetSyncService(0));
@@ -1275,8 +1267,16 @@
 // 3. Enable Sync-the-feature again -> profile storage.
 // 4. StopAndClear() -> account storage.
 // 5. Enable Sync-the-feature again -> profile storage.
-IN_PROC_BROWSER_TEST_F(SingleClientWalletWithAccountStorageSyncTest,
+IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest,
                        SwitchesBetweenAccountAndProfileStorageOnTogglingSync) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures(
+      /*enabled_features=*/{switches::kSyncStandaloneTransport,
+                            switches::kSyncUSSAutofillWalletData,
+                            autofill::features::
+                                kAutofillEnableAccountWalletStorage},
+      /*disabled_features=*/{});
+
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   GetPersonalDataManager(0)->OnSyncServiceInitialized(GetSyncService(0));
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index e42a5f0..8f2a104 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1754,7 +1754,8 @@
       "//chromeos/services/assistant/public:feature_flags",
       "//chromeos/services/assistant/public/mojom",
       "//chromeos/services/assistant/public/proto:proto",
-      "//chromeos/services/multidevice_setup/public/cpp:cpp",
+      "//chromeos/services/multidevice_setup/public/cpp",
+      "//chromeos/services/multidevice_setup/public/cpp:android_sms_app_helper_delegate",
       "//chromeos/services/multidevice_setup/public/cpp:prefs",
       "//chromeos/services/multidevice_setup/public/cpp:url_provider",
       "//chromeos/services/multidevice_setup/public/mojom",
diff --git a/chrome/browser/ui/app_list/extension_uninstaller.cc b/chrome/browser/ui/app_list/extension_uninstaller.cc
index b0e46eb..3eedd55 100644
--- a/chrome/browser/ui/app_list/extension_uninstaller.cc
+++ b/chrome/browser/ui/app_list/extension_uninstaller.cc
@@ -26,8 +26,8 @@
     CleanUp();
     return;
   }
-  dialog_.reset(
-      extensions::ExtensionUninstallDialog::Create(profile_, nullptr, this));
+  dialog_ =
+      extensions::ExtensionUninstallDialog::Create(profile_, nullptr, this);
   dialog_->ConfirmUninstall(extension,
                             extensions::UNINSTALL_REASON_USER_INITIATED,
                             extensions::UNINSTALL_SOURCE_APP_LIST);
diff --git a/chrome/browser/ui/cocoa/ui_localizer.mm b/chrome/browser/ui/cocoa/ui_localizer.mm
index d64bef2..83bd4209 100644
--- a/chrome/browser/ui/cocoa/ui_localizer.mm
+++ b/chrome/browser/ui/cocoa/ui_localizer.mm
@@ -9,6 +9,7 @@
 #include <stdlib.h>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
index a25e163..4be247b 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
@@ -344,8 +344,8 @@
 
 void HostedAppBrowserController::Uninstall(UninstallReason reason,
                                            UninstallSource source) {
-  uninstall_dialog_.reset(ExtensionUninstallDialog::Create(
-      browser_->profile(), browser_->window()->GetNativeWindow(), this));
+  uninstall_dialog_ = ExtensionUninstallDialog::Create(
+      browser_->profile(), browser_->window()->GetNativeWindow(), this);
   uninstall_dialog_->ConfirmUninstall(GetExtension(), reason, source);
 }
 
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index da3e667..2e0adbf 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -19,6 +19,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/badging/badge_service_delegate.h"
 #include "chrome/browser/banners/app_banner_manager_desktop.h"
 #include "chrome/browser/engagement/site_engagement_service.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
@@ -34,6 +35,7 @@
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/extensions/hosted_app_browser_controller.h"
@@ -977,6 +979,113 @@
             app_browser->GetWindowTitleForCurrentTab(false));
 }
 
+#if !defined(OS_CHROMEOS)
+class HostedAppBadgingTest : public HostedAppTest {
+ public:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    HostedAppTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitchASCII("enable-blink-features", "Badging");
+  }
+
+  void OnBadgeSet(content::WebContents* web_contents,
+                  base::Optional<uint64_t> badge_content) {
+    if (badge_content.has_value())
+      last_badge_content_ = badge_content;
+    else
+      was_flagged_ = true;
+
+    awaiter_->Quit();
+  }
+
+  void OnBadgeCleared(content::WebContents* web_contents) {
+    was_cleared_ = true;
+    awaiter_->Quit();
+  }
+
+  void SetUpOnMainThread() override {
+    HostedAppTest::SetUpOnMainThread();
+
+    ASSERT_TRUE(https_server()->Start());
+    ASSERT_TRUE(embedded_test_server()->Start());
+
+    InstallSecurePWA();
+
+    awaiter_ = std::make_unique<base::RunLoop>();
+    badge_service_delegate_ = app_browser_->window()->GetBadgeServiceDelegate();
+    badge_service_delegate_->SetImplForTesting(
+        base::BindRepeating(&HostedAppBadgingTest::OnBadgeSet,
+                            base::Unretained(this)),
+        base::BindRepeating(&HostedAppBadgingTest::OnBadgeCleared,
+                            base::Unretained(this)));
+  }
+
+ protected:
+  void ExecuteScriptAndWaitForBadgeChange(std::string script) {
+    was_cleared_ = false;
+    was_flagged_ = false;
+    last_badge_content_ = base::nullopt;
+    awaiter_.reset(new base::RunLoop());
+
+    content::WebContents* web_contents =
+        app_browser_->tab_strip_model()->GetActiveWebContents();
+    ASSERT_TRUE(content::ExecuteScript(web_contents, script));
+
+    if (was_cleared_ || was_flagged_ || last_badge_content_ != base::nullopt)
+      return;
+
+    awaiter_->Run();
+  }
+
+  BadgeServiceDelegate* badge_service_delegate_;
+
+  bool was_cleared_ = false;
+  bool was_flagged_ = false;
+  base::Optional<uint64_t> last_badge_content_ = base::nullopt;
+
+ private:
+  std::unique_ptr<base::RunLoop> awaiter_;
+};
+
+// Tests that setting the badge to an integer will be propagated across
+// processes.
+IN_PROC_BROWSER_TEST_P(HostedAppBadgingTest, BadgeCanBeSetToAnInteger) {
+  ExecuteScriptAndWaitForBadgeChange("Badge.set(99)");
+  ASSERT_FALSE(was_cleared_);
+  ASSERT_FALSE(was_flagged_);
+  ASSERT_EQ(base::Optional<uint64_t>(99u), last_badge_content_);
+}
+
+// Tests that calls to |Badge.clear| are propagated across processes.
+IN_PROC_BROWSER_TEST_P(HostedAppBadgingTest, BadgeCanBeClearedWithClearMethod) {
+  ExecuteScriptAndWaitForBadgeChange("Badge.set(55)");
+  ASSERT_FALSE(was_cleared_);
+  ASSERT_FALSE(was_flagged_);
+  ASSERT_EQ(base::Optional<uint64_t>(55u), last_badge_content_);
+
+  ExecuteScriptAndWaitForBadgeChange("Badge.clear()");
+  ASSERT_TRUE(was_cleared_);
+  ASSERT_FALSE(was_flagged_);
+  ASSERT_EQ(base::nullopt, last_badge_content_);
+}
+
+// Tests that calling Badge.set(0) is equivalent to calling |Badge.clear| and
+// that it propagates across processes.
+IN_PROC_BROWSER_TEST_P(HostedAppBadgingTest, BadgeCanBeClearedWithZero) {
+  ExecuteScriptAndWaitForBadgeChange("Badge.set(0)");
+  ASSERT_TRUE(was_cleared_);
+  ASSERT_FALSE(was_flagged_);
+  ASSERT_EQ(base::nullopt, last_badge_content_);
+}
+
+// Tests that setting the badge without content is propagated across processes.
+IN_PROC_BROWSER_TEST_P(HostedAppBadgingTest, BadgeCanBeSetWithoutAValue) {
+  ExecuteScriptAndWaitForBadgeChange("Badge.set()");
+  ASSERT_FALSE(was_cleared_);
+  ASSERT_TRUE(was_flagged_);
+  ASSERT_EQ(base::nullopt, last_badge_content_);
+}
+#endif  // !defined(OS_CHROMEOS)
+
 using HostedAppPWAOnlyTest = HostedAppTest;
 
 // Tests that the command for popping a tab out to a PWA window is disabled in
@@ -2816,3 +2925,12 @@
     ::testing::Combine(::testing::Values(AppType::HOSTED_APP),
                        ::testing::Bool(),
                        ::testing::Bool()));
+
+#if !defined(OS_CHROMEOS)
+INSTANTIATE_TEST_CASE_P(
+    /* no prefix */,
+    HostedAppBadgingTest,
+    ::testing::Combine(::testing::Values(AppType::BOOKMARK_APP),
+                       ::testing::Values(true),
+                       ::testing::Bool()));
+#endif  // !defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
index 0e3562e..45a347db 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
@@ -177,9 +177,8 @@
 
 void AppInfoFooterPanel::UninstallApp() {
   DCHECK(CanUninstallApp());
-  extension_uninstall_dialog_.reset(
-      extensions::ExtensionUninstallDialog::Create(
-          profile_, GetWidget()->GetNativeWindow(), this));
+  extension_uninstall_dialog_ = extensions::ExtensionUninstallDialog::Create(
+      profile_, GetWidget()->GetNativeWindow(), this);
   extension_uninstall_dialog_->ConfirmUninstall(
       app_, extensions::UNINSTALL_REASON_USER_INITIATED,
       extensions::UNINSTALL_SOURCE_APP_INFO_DIALOG);
diff --git a/chrome/browser/ui/views/badge_service_delegate_impl.cc b/chrome/browser/ui/views/badge_service_delegate_impl.cc
index 5b790e9..a140507 100644
--- a/chrome/browser/ui/views/badge_service_delegate_impl.cc
+++ b/chrome/browser/ui/views/badge_service_delegate_impl.cc
@@ -58,10 +58,8 @@
 }
 #endif
 
-}  // namespace
-
-void BadgeServiceDelegate::SetBadge(content::WebContents* contents,
-                                    base::Optional<uint64_t> badge_content) {
+void SetBadgeImpl(content::WebContents* contents,
+                  base::Optional<uint64_t> badge_content) {
 #if defined(OS_WIN)
   Browser* browser = chrome::FindBrowserWithWebContents(contents);
   auto* window = browser->window()->GetNativeWindow();
@@ -71,7 +69,7 @@
 #endif
 }
 
-void BadgeServiceDelegate::ClearBadge(content::WebContents* contents) {
+void ClearBadgeImpl(content::WebContents* contents) {
 #if defined(OS_WIN)
   Browser* browser = chrome::FindBrowserWithWebContents(contents);
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
@@ -83,3 +81,27 @@
   SetAppShimBadgeLabel(contents, "");
 #endif
 }
+
+}  // namespace
+
+BadgeServiceDelegate::BadgeServiceDelegate()
+    : on_set_badge_(base::BindRepeating(&SetBadgeImpl)),
+      on_clear_badge_(base::BindRepeating(&ClearBadgeImpl)) {}
+
+BadgeServiceDelegate::~BadgeServiceDelegate() {}
+
+void BadgeServiceDelegate::SetBadge(content::WebContents* contents,
+                                    base::Optional<uint64_t> badge_content) {
+  on_set_badge_.Run(contents, badge_content);
+}
+
+void BadgeServiceDelegate::ClearBadge(content::WebContents* contents) {
+  on_clear_badge_.Run(contents);
+}
+
+void BadgeServiceDelegate::SetImplForTesting(
+    SetBadgeCallback on_set_badge,
+    ClearBadgeCallback on_clear_badge) {
+  on_set_badge_ = on_set_badge;
+  on_clear_badge_ = on_clear_badge;
+}
diff --git a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc
index cdceb581..884e8f4 100644
--- a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view.cc
@@ -285,7 +285,7 @@
 }  // namespace
 
 // static
-extensions::ExtensionUninstallDialog*
+std::unique_ptr<extensions::ExtensionUninstallDialog>
 extensions::ExtensionUninstallDialog::Create(Profile* profile,
                                              gfx::NativeWindow parent,
                                              Delegate* delegate) {
@@ -293,9 +293,10 @@
 }
 
 // static
-extensions::ExtensionUninstallDialog*
+std::unique_ptr<extensions::ExtensionUninstallDialog>
 extensions::ExtensionUninstallDialog::CreateViews(Profile* profile,
                                                   gfx::NativeWindow parent,
                                                   Delegate* delegate) {
-  return new ExtensionUninstallDialogViews(profile, parent, delegate);
+  return std::make_unique<ExtensionUninstallDialogViews>(profile, parent,
+                                                         delegate);
 }
diff --git a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc
index 616a9fcd..300eb315 100644
--- a/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extension_uninstall_dialog_view_browsertest.cc
@@ -158,9 +158,9 @@
   std::unique_ptr<extensions::ExtensionUninstallDialog> dialog;
   {
     base::RunLoop run_loop;
-    dialog.reset(extensions::ExtensionUninstallDialog::Create(
+    dialog = extensions::ExtensionUninstallDialog::Create(
         app_browser->profile(), app_browser->window()->GetNativeWindow(),
-        nullptr));
+        nullptr);
     run_loop.RunUntilIdle();
   }
 
@@ -313,9 +313,9 @@
         ->extension_service()
         ->AddExtension(extension_.get());
 
-    dialog_.reset(extensions::ExtensionUninstallDialog::Create(
+    dialog_ = extensions::ExtensionUninstallDialog::Create(
         browser()->profile(), browser()->window()->GetNativeWindow(),
-        &delegate_));
+        &delegate_);
     if (uninstall_method_ == UNINSTALL_BY_EXTENSION) {
       triggering_extension_ =
           extensions::ExtensionBuilder("TestExtensionRemover").Build();
diff --git a/chrome/browser/ui/views/frame/browser_frame_header_ash.cc b/chrome/browser/ui/views/frame/browser_frame_header_ash.cc
index a05d7b65..30eb6bc2 100644
--- a/chrome/browser/ui/views/frame/browser_frame_header_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_header_ash.cc
@@ -168,8 +168,7 @@
   return appearance_provider_->GetFrameHeaderColor(mode() == MODE_ACTIVE);
 }
 
-void BrowserFrameHeaderAsh::DoSetFrameColors(SkColor active_frame_color,
-                                             SkColor inactive_frame_color) {
+void BrowserFrameHeaderAsh::UpdateFrameColors() {
   UpdateCaptionButtonColors();
   view()->SchedulePaint();
 }
diff --git a/chrome/browser/ui/views/frame/browser_frame_header_ash.h b/chrome/browser/ui/views/frame/browser_frame_header_ash.h
index 96188d6..c1ea091c 100644
--- a/chrome/browser/ui/views/frame/browser_frame_header_ash.h
+++ b/chrome/browser/ui/views/frame/browser_frame_header_ash.h
@@ -38,11 +38,12 @@
   // the window.
   static int GetThemeBackgroundXInset();
 
+  // FrameHeader:
+  void UpdateFrameColors() override;
+
  protected:
   // FrameHeader:
   void DoPaintHeader(gfx::Canvas* canvas) override;
-  void DoSetFrameColors(SkColor active_frame_color,
-                        SkColor inactive_frame_color) override;
   views::CaptionButtonLayoutSize GetButtonLayoutSize() const override;
   SkColor GetTitleColor() const override;
   SkColor GetCurrentFrameColor() const override;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index f72c40d..af1906e6 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -204,6 +204,9 @@
 
   frame_header_ = CreateFrameHeader();
 
+  if (browser_view()->IsBrowserTypeHostedApp())
+    SetUpForHostedApp();
+
   browser_view()->immersive_mode_controller()->AddObserver(this);
 
   UpdateFrameColors();
@@ -798,15 +801,8 @@
     header = std::make_unique<BrowserFrameHeaderAsh>(frame(), this, this,
                                                      caption_button_container_);
   } else {
-    auto default_frame_header = std::make_unique<ash::DefaultFrameHeader>(
+    header = std::make_unique<ash::DefaultFrameHeader>(
         frame(), this, caption_button_container_);
-    if (browser_view()->IsBrowserTypeHostedApp()) {
-      SetUpForHostedApp(default_frame_header.get());
-    } else if (!browser->is_app()) {
-      default_frame_header->SetFrameColors(kMdWebUiFrameColor,
-                                           kMdWebUiFrameColor);
-    }
-    header = std::move(default_frame_header);
   }
 
   header->SetBackButton(back_button_);
@@ -814,18 +810,8 @@
   return header;
 }
 
-void BrowserNonClientFrameViewAsh::SetUpForHostedApp(
-    ash::DefaultFrameHeader* header) {
-  // Hosted apps apply a theme color if specified by the extension.
+void BrowserNonClientFrameViewAsh::SetUpForHostedApp() {
   Browser* browser = browser_view()->browser();
-  base::Optional<SkColor> theme_color =
-      browser->hosted_app_controller()->GetThemeColor();
-  if (theme_color) {
-    header->set_button_color_mode(
-        views::FrameCaptionButton::ColorMode::kThemed);
-    header->SetFrameColors(*theme_color, *theme_color);
-  }
-
   if (!browser->hosted_app_controller()->ShouldShowHostedAppButtonContainer())
     return;
 
@@ -857,13 +843,12 @@
     window->SetProperty(ash::kFrameActiveColorKey, *active_color);
     window->SetProperty(ash::kFrameInactiveColorKey,
                         inactive_color.value_or(*active_color));
-    frame_header_->SetFrameColors(
-        window->GetProperty(ash::kFrameActiveColorKey),
-        window->GetProperty(ash::kFrameInactiveColorKey));
   } else {
     window->ClearProperty(ash::kFrameActiveColorKey);
     window->ClearProperty(ash::kFrameInactiveColorKey);
   }
+
+  frame_header_->UpdateFrameColors();
 }
 
 void BrowserNonClientFrameViewAsh::UpdateTopViewInset() {
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
index 720786c..ce4ccaf4 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h
@@ -34,7 +34,6 @@
 
 namespace ash {
 class AshFrameCaptionController;
-class DefaultFrameHeader;
 class FrameCaptionButtonContainerView;
 }  // namespace ash
 
@@ -194,8 +193,7 @@
   std::unique_ptr<ash::FrameHeader> CreateFrameHeader();
 
   // Creates views and does other setup for a hosted app.
-  // TODO(estade): remove the parameter as it's unused in Mash.
-  void SetUpForHostedApp(ash::DefaultFrameHeader* header);
+  void SetUpForHostedApp();
 
   // Triggers the hosted app origin and icon animations, assumes the hosted
   // app UI elements exist.
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 850dcb8..07b33cc 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -71,6 +71,7 @@
 #include "ui/views/mouse_watcher_view_host.h"
 #include "ui/views/rect_based_targeting_utils.h"
 #include "ui/views/view_model_utils.h"
+#include "ui/views/view_observer.h"
 #include "ui/views/view_targeter.h"
 #include "ui/views/widget/root_view.h"
 #include "ui/views/widget/widget.h"
@@ -1151,14 +1152,13 @@
   if (!base::FeatureList::IsEnabled(features::kTabHoverCards))
     return;
 
-  if (!hover_card_ && !should_show)
-    return;
-
-  // The view tracker is used to prevent us from accessing the hover card after
-  // it has been deleted by the owning widget.
-  if (!hover_card_ || !hover_card_view_tracker_.view()) {
+  if (!hover_card_) {
+    // There is nothing to be done if the hover card doesn't exist and we are
+    // not trying to show it.
+    if (!should_show)
+      return;
     hover_card_ = new TabHoverCardBubbleView(tab, tab->data());
-    hover_card_view_tracker_.SetView(hover_card_);
+    hover_card_->views::View::AddObserver(this);
   }
 
   if (should_show) {
@@ -2849,6 +2849,13 @@
   return this;
 }
 
+void TabStrip::OnViewIsDeleting(views::View* observed_view) {
+  if (observed_view == hover_card_) {
+    hover_card_->views::View::RemoveObserver(this);
+    hover_card_ = nullptr;
+  }
+}
+
 void TabStrip::OnTouchUiChanged() {
   UpdateNewTabButtonBorder();
   new_tab_button_bounds_.set_size(new_tab_button_->GetPreferredSize());
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index 8d72b6a..d7ec646c 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -34,7 +34,6 @@
 #include "ui/views/view.h"
 #include "ui/views/view_model.h"
 #include "ui/views/view_targeter_delegate.h"
-#include "ui/views/view_tracker.h"
 
 class NewTabButton;
 class StackedTabStripLayout;
@@ -43,6 +42,7 @@
 class TabHoverCardBubbleView;
 class TabStripController;
 class TabStripObserver;
+class ViewObserver;
 
 namespace gfx {
 class Rect;
@@ -68,6 +68,7 @@
 class TabStrip : public views::AccessiblePaneView,
                  public views::ButtonListener,
                  public views::MouseWatcherListener,
+                 public views::ViewObserver,
                  public views::ViewTargeterDelegate,
                  public TabController,
                  public BrowserRootView::DropTarget,
@@ -629,6 +630,9 @@
   // views::ViewTargeterDelegate:
   views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override;
 
+  // views::ViewObserver:
+  void OnViewIsDeleting(views::View* observed_view) override;
+
   // ui::MaterialDesignControllerObserver:
   void OnTouchUiChanged() override;
 
@@ -649,7 +653,6 @@
   // The view tracker is used to keep track of if the hover card has been
   // destroyed by its widget.
   TabHoverCardBubbleView* hover_card_ = nullptr;
-  views::ViewTracker hover_card_view_tracker_;
 
   std::unique_ptr<TabStripController> controller_;
 
diff --git a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
index 22a3fb4..1695b2d 100644
--- a/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/mobile_setup_ui.cc
@@ -11,6 +11,7 @@
 #include <algorithm>
 #include <map>
 #include <string>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -20,6 +21,7 @@
 #include "base/memory/ref_counted_memory.h"
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/string16.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -48,6 +50,7 @@
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/base/webui/jstemplate_builder.h"
 #include "ui/base/webui/web_ui_util.h"
+#include "ui/chromeos/devicetype_utils.h"
 #include "url/gurl.h"
 
 namespace chromeos {
@@ -68,6 +71,54 @@
 const char kJsConnectivityChangedCallback[] =
     "mobile.MobileSetupPortal.onConnectivityChanged";
 
+// TODO(tbarzic): Localize these strings.
+const char kDefaultActivationError[] =
+    "$1 is unable to connect to $2 at this time. Please try again later.";
+const char kCellularDisabledError[] =
+    "Mobile network connections are not currently enabled on this device.";
+const char kNoCellularDeviceError[] = "Mobile network modem is not present.";
+const char kNoCellularServiceError[] =
+    "$1 is unable to connect at this time due to insufficient coverage.";
+
+bool ActivationErrorRequiresCarrier(MobileActivator::ActivationError error) {
+  return error == MobileActivator::ActivationError::kActivationFailed;
+}
+
+base::string16 GetActivationErrorMessage(MobileActivator::ActivationError error,
+                                         const std::string& carrier) {
+  // If the activation error message requires the carrier name, and none was
+  // provider, fallback to kNoCellularServiceError.
+  if (carrier.empty() && ActivationErrorRequiresCarrier(error)) {
+    CHECK(!ActivationErrorRequiresCarrier(
+        MobileActivator::ActivationError::kNoCellularService));
+    return GetActivationErrorMessage(
+        MobileActivator::ActivationError::kNoCellularService, carrier);
+  }
+
+  switch (error) {
+    case MobileActivator::ActivationError::kNone:
+      return base::string16();
+    case MobileActivator::ActivationError::kActivationFailed: {
+      return base::ReplaceStringPlaceholders(
+          base::UTF8ToUTF16(kDefaultActivationError),
+          std::vector<base::string16>(
+              {ui::GetChromeOSDeviceName(), base::UTF8ToUTF16(carrier)}),
+          nullptr);
+    }
+    case MobileActivator::ActivationError::kCellularDisabled:
+      return base::UTF8ToUTF16(kCellularDisabledError);
+    case MobileActivator::ActivationError::kNoCellularDevice:
+      return base::UTF8ToUTF16(kNoCellularDeviceError);
+    case MobileActivator::ActivationError::kNoCellularService:
+      return base::ReplaceStringPlaceholders(
+          base::UTF8ToUTF16(kNoCellularServiceError),
+          ui::GetChromeOSDeviceName(), nullptr);
+  }
+  NOTREACHED() << "Unexpected activation error";
+  return GetActivationErrorMessage(
+      MobileActivator::ActivationError::kActivationFailed, carrier);
+}
+
 void DataRequestFailed(
     const std::string& service_path,
     const content::URLDataSource::GotDataCallback& callback) {
@@ -98,11 +149,34 @@
   return !!result;
 }
 
+// Keys for the dictionary that is set to activation UI and that contains the
+// cellular network information.
+namespace keys {
+
+// The current activation state:
+constexpr char kActivationState[] = "state";
+constexpr char kActivationErrorMessage[] = "error";
+
+// The cellular service properties:
+constexpr char kCellularActivationType[] = "activation_type";
+constexpr char kCarrier[] = "carrier";
+constexpr char kPaymentPortalUrl[] = "payment_url";
+constexpr char kPaymentPortalPostData[] = "post_data";
+
+// Cellular device properties:
+constexpr char kMeid[] = "MEID";
+constexpr char kImei[] = "IMEI";
+constexpr char kMdn[] = "MDN";
+
+}  // namespace keys
+
+constexpr char kPaymentPortalPostMethod[] = "post";
+
 // Converts the network properties into a JS object.
 void GetDeviceInfo(const base::DictionaryValue& properties,
-                   base::DictionaryValue* value) {
-  std::string name;
-  properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
+                   base::DictionaryValue* value,
+                   std::string* carrier) {
+  properties.GetStringWithoutPathExpansion(shill::kNameProperty, carrier);
   std::string activation_type;
   properties.GetStringWithoutPathExpansion(shill::kActivationTypeProperty,
                                            &activation_type);
@@ -114,17 +188,19 @@
                                                 &payment_url);
     payment_dict->GetStringWithoutPathExpansion(shill::kPaymentPortalMethod,
                                                 &post_method);
-    payment_dict->GetStringWithoutPathExpansion(shill::kPaymentPortalPostData,
-                                                &post_data);
+    if (base::LowerCaseEqualsASCII(post_method, kPaymentPortalPostMethod)) {
+      payment_dict->GetStringWithoutPathExpansion(shill::kPaymentPortalPostData,
+                                                  &post_data);
+    }
   }
   NET_LOG(EVENT) << "MobileSetupUI: Payment URL = " << payment_url
                  << " Post Data = " << post_data;
 
-  value->SetString("activation_type", activation_type);
-  value->SetString("carrier", name);
-  value->SetString("payment_url", payment_url);
-  if (base::LowerCaseEqualsASCII(post_method, "post") && !post_data.empty())
-    value->SetString("post_data", post_data);
+  value->SetString(keys::kCarrier, *carrier);
+  value->SetString(keys::kCellularActivationType, activation_type);
+  value->SetString(keys::kPaymentPortalUrl, payment_url);
+  if (!post_data.empty())
+    value->SetString(keys::kPaymentPortalPostData, post_data);
 
   // Use the cached DeviceState properties.
   std::string device_path;
@@ -139,17 +215,18 @@
   if (!device)
     return;
 
-  value->SetString("MEID", device->meid());
-  value->SetString("IMEI", device->imei());
-  value->SetString("MDN", device->mdn());
+  value->SetString(keys::kMeid, device->meid());
+  value->SetString(keys::kImei, device->imei());
+  value->SetString(keys::kMdn, device->mdn());
 }
 
 void SetActivationStateAndError(MobileActivator::PlanActivationState state,
-                                const std::string& error_description,
+                                MobileActivator::ActivationError error,
+                                const std::string& carrier,
                                 base::DictionaryValue* value) {
-  value->SetInteger("state", state);
-  if (!error_description.empty())
-    value->SetString("error", error_description);
+  value->SetInteger(keys::kActivationState, state);
+  value->SetString(keys::kActivationErrorMessage,
+                   GetActivationErrorMessage(error, carrier));
 }
 
 }  // namespace
@@ -216,14 +293,15 @@
   };
 
   // MobileActivator::Observer.
-  void OnActivationStateChanged(const NetworkState* network,
-                                MobileActivator::PlanActivationState new_state,
-                                const std::string& error_description) override;
+  void OnActivationStateChanged(
+      const NetworkState* network,
+      MobileActivator::PlanActivationState new_state,
+      MobileActivator::ActivationError error) override;
 
   // Callbacks for NetworkConfigurationHandler::GetProperties.
   void GetPropertiesAndCallStatusChanged(
       MobileActivator::PlanActivationState state,
-      const std::string& error_description,
+      MobileActivator::ActivationError error,
       const std::string& service_path,
       const base::DictionaryValue& properties);
   void GetPropertiesAndCallGetDeviceInfo(
@@ -390,23 +468,25 @@
 void MobileSetupHandler::OnActivationStateChanged(
     const NetworkState* network,
     MobileActivator::PlanActivationState state,
-    const std::string& error_description) {
+    MobileActivator::ActivationError error) {
   DCHECK_EQ(TYPE_ACTIVATION, type_);
   if (!web_ui())
     return;
 
   if (!network) {
     base::DictionaryValue device_dict;
-    SetActivationStateAndError(state, error_description, &device_dict);
+    SetActivationStateAndError(state, error, "" /*carrier*/, &device_dict);
     web_ui()->CallJavascriptFunctionUnsafe(kJsDeviceStatusChangedCallback,
                                            device_dict);
     return;
   }
 
+  // TODO(tbarzic): Create the device info dict from info cached in NetworkState
+  // and DeviceState.
   NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
       network->path(),
       base::Bind(&MobileSetupHandler::GetPropertiesAndCallStatusChanged,
-                 weak_ptr_factory_.GetWeakPtr(), state, error_description),
+                 weak_ptr_factory_.GetWeakPtr(), state, error),
       base::Bind(&MobileSetupHandler::GetPropertiesFailure,
                  weak_ptr_factory_.GetWeakPtr(), network->path(),
                  kJsDeviceStatusChangedCallback));
@@ -414,12 +494,13 @@
 
 void MobileSetupHandler::GetPropertiesAndCallStatusChanged(
     MobileActivator::PlanActivationState state,
-    const std::string& error_description,
+    MobileActivator::ActivationError error,
     const std::string& service_path,
     const base::DictionaryValue& properties) {
   base::DictionaryValue device_dict;
-  GetDeviceInfo(properties, &device_dict);
-  SetActivationStateAndError(state, error_description, &device_dict);
+  std::string carrier;
+  GetDeviceInfo(properties, &device_dict, &carrier);
+  SetActivationStateAndError(state, error, carrier, &device_dict);
   web_ui()->CallJavascriptFunctionUnsafe(kJsDeviceStatusChangedCallback,
                                          device_dict);
 }
@@ -546,7 +627,8 @@
     const std::string& service_path,
     const base::DictionaryValue& properties) {
   base::DictionaryValue device_info;
-  GetDeviceInfo(properties, &device_info);
+  std::string carrier;
+  GetDeviceInfo(properties, &device_info, &carrier);
   web_ui()->CallJavascriptFunctionUnsafe(kJsGetDeviceInfoCallback, device_info);
 }
 
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index 0b764934..4077b32 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -852,10 +852,9 @@
 AppLauncherHandler::CreateExtensionUninstallDialog() {
   Browser* browser =
       chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
-  extension_uninstall_dialog_.reset(
-      extensions::ExtensionUninstallDialog::Create(
-          extension_service_->profile(), browser->window()->GetNativeWindow(),
-          this));
+  extension_uninstall_dialog_ = extensions::ExtensionUninstallDialog::Create(
+      extension_service_->profile(), browser->window()->GetNativeWindow(),
+      this);
   return extension_uninstall_dialog_.get();
 }
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
index e143e3fc..10f7cc69 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
@@ -8,7 +8,6 @@
 #include "base/bind_helpers.h"
 #include "base/logging.h"
 #include "base/values.h"
-#include "chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl.h"
 #include "chrome/browser/chromeos/android_sms/android_sms_urls.h"
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h"
 #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h"
@@ -16,6 +15,7 @@
 #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "chromeos/components/proximity_auth/proximity_auth_pref_names.h"
+#include "chromeos/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h"
 #include "chromeos/services/multidevice_setup/public/cpp/prefs.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/prefs/pref_service.h"
@@ -51,11 +51,10 @@
 MultideviceHandler::MultideviceHandler(
     PrefService* prefs,
     multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
-    std::unique_ptr<multidevice_setup::AndroidSmsAppHelperDelegate>
-        android_sms_app_helper)
+    multidevice_setup::AndroidSmsAppHelperDelegate* android_sms_app_helper)
     : prefs_(prefs),
       multidevice_setup_client_(multidevice_setup_client),
-      android_sms_app_helper_(std::move(android_sms_app_helper)),
+      android_sms_app_helper_(android_sms_app_helper),
       multidevice_setup_observer_(this),
       callback_weak_ptr_factory_(this) {
   RegisterPrefChangeListeners();
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
index 027f527..6bef9c4d 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
@@ -36,8 +36,7 @@
   MultideviceHandler(
       PrefService* prefs,
       multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
-      std::unique_ptr<multidevice_setup::AndroidSmsAppHelperDelegate>
-          android_sms_app_helper);
+      multidevice_setup::AndroidSmsAppHelperDelegate* android_sms_app_helper);
   ~MultideviceHandler() override;
 
  protected:
@@ -104,8 +103,7 @@
   GetFeatureStatesMap();
 
   multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_;
-  std::unique_ptr<multidevice_setup::AndroidSmsAppHelperDelegate>
-      android_sms_app_helper_;
+  multidevice_setup::AndroidSmsAppHelperDelegate* android_sms_app_helper_;
 
   ScopedObserver<multidevice_setup::MultiDeviceSetupClient,
                  multidevice_setup::MultiDeviceSetupClient::Observer>
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
index b6ba8bb..545cbcd5 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
@@ -27,8 +27,7 @@
   TestMultideviceHandler(
       PrefService* prefs,
       multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
-      std::unique_ptr<multidevice_setup::AndroidSmsAppHelperDelegate>
-          android_sms_app_helper)
+      multidevice_setup::AndroidSmsAppHelperDelegate* android_sms_app_helper)
       : MultideviceHandler(prefs,
                            multidevice_setup_client,
                            std::move(android_sms_app_helper)) {}
@@ -116,16 +115,14 @@
 
     fake_multidevice_setup_client_ =
         std::make_unique<multidevice_setup::FakeMultiDeviceSetupClient>();
-    auto fake_android_sms_app_helper_delegate =
-        std::make_unique<multidevice_setup::FakeAndroidSmsAppHelperDelegate>();
     fake_android_sms_app_helper_delegate_ =
-        fake_android_sms_app_helper_delegate.get();
+        std::make_unique<multidevice_setup::FakeAndroidSmsAppHelperDelegate>();
 
     prefs_.reset(new TestingPrefServiceSimple());
 
     handler_ = std::make_unique<TestMultideviceHandler>(
         prefs_.get(), fake_multidevice_setup_client_.get(),
-        std::move(fake_android_sms_app_helper_delegate));
+        fake_android_sms_app_helper_delegate_.get());
     handler_->set_web_ui(test_web_ui_.get());
     handler_->RegisterMessages();
     handler_->AllowJavascript();
@@ -269,7 +266,7 @@
 
   multidevice_setup::FakeAndroidSmsAppHelperDelegate*
   fake_android_sms_app_helper_delegate() {
-    return fake_android_sms_app_helper_delegate_;
+    return fake_android_sms_app_helper_delegate_.get();
   }
 
   const multidevice::RemoteDeviceRef test_device_;
@@ -292,7 +289,7 @@
       host_status_with_device_;
   multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap
       feature_states_map_;
-  multidevice_setup::FakeAndroidSmsAppHelperDelegate*
+  std::unique_ptr<multidevice_setup::FakeAndroidSmsAppHelperDelegate>
       fake_android_sms_app_helper_delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(MultideviceHandlerTest);
@@ -331,11 +328,11 @@
 }
 
 TEST_F(MultideviceHandlerTest, SetUpAndroidSms) {
-  EXPECT_FALSE(fake_android_sms_app_helper_delegate()->HasInstalledApp());
-  EXPECT_FALSE(fake_android_sms_app_helper_delegate()->HasLaunchedApp());
+  EXPECT_FALSE(fake_android_sms_app_helper_delegate()->has_installed_app());
+  EXPECT_FALSE(fake_android_sms_app_helper_delegate()->has_launched_app());
   CallSetUpAndroidSms();
-  EXPECT_TRUE(fake_android_sms_app_helper_delegate()->HasInstalledApp());
-  EXPECT_TRUE(fake_android_sms_app_helper_delegate()->HasLaunchedApp());
+  EXPECT_TRUE(fake_android_sms_app_helper_delegate()->has_installed_app());
+  EXPECT_TRUE(fake_android_sms_app_helper_delegate()->has_launched_app());
 }
 
 TEST_F(MultideviceHandlerTest, SetFeatureEnabledState) {
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc
index 903be4a..6c2f88c 100644
--- a/chrome/browser/ui/webui/settings/md_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -76,6 +76,7 @@
 #include "ash/public/cpp/stylus_utils.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/android_sms/android_sms_app_helper_delegate_impl.h"
+#include "chrome/browser/chromeos/android_sms/android_sms_service_factory.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
@@ -280,14 +281,17 @@
 
 #if defined(OS_CHROMEOS)
   if (!profile->IsGuestSession()) {
+    chromeos::android_sms::AndroidSmsService* android_sms_service =
+        chromeos::android_sms::AndroidSmsServiceFactory::GetForBrowserContext(
+            profile);
     AddSettingsPageUIHandler(
         std::make_unique<chromeos::settings::MultideviceHandler>(
             profile->GetPrefs(),
             chromeos::multidevice_setup::MultiDeviceSetupClientFactory::
                 GetForProfile(profile),
-            std::make_unique<
-                chromeos::android_sms::AndroidSmsAppHelperDelegateImpl>(
-                profile)));
+            android_sms_service
+                ? android_sms_service->android_sms_app_helper_delegate()
+                : nullptr));
   }
   html_source->AddBoolean(
       "multideviceAllowedByPolicy",
diff --git a/chrome/common/ppapi_utils.cc b/chrome/common/ppapi_utils.cc
index 968a768..e04512c 100644
--- a/chrome/common/ppapi_utils.cc
+++ b/chrome/common/ppapi_utils.cc
@@ -31,8 +31,6 @@
 #include "ppapi/c/ppb_audio_buffer.h"
 #include "ppapi/c/ppb_audio_config.h"
 #include "ppapi/c/ppb_audio_encoder.h"
-#include "ppapi/c/ppb_compositor.h"
-#include "ppapi/c/ppb_compositor_layer.h"
 #include "ppapi/c/ppb_console.h"
 #include "ppapi/c/ppb_core.h"
 #include "ppapi/c/ppb_file_io.h"
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index d79cf049..f5eb845 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -229,11 +229,6 @@
   "6EAED1924DB611B6EEF2A664BD077BE7EAD33B8F",
   "4EB74897CB187C7633357C2FE832E0AD6A44883A"
 };
-
-const char* const kPredefinedAllowedCompositorOrigins[] = {
-  "6EAED1924DB611B6EEF2A664BD077BE7EAD33B8F",
-  "4EB74897CB187C7633357C2FE832E0AD6A44883A"
-};
 #endif
 
 #if BUILDFLAG(ENABLE_PLUGINS)
@@ -350,8 +345,6 @@
 #if BUILDFLAG(ENABLE_PLUGINS)
   for (const char* origin : kPredefinedAllowedCameraDeviceOrigins)
     allowed_camera_device_origins_.insert(origin);
-  for (const char* origin : kPredefinedAllowedCompositorOrigins)
-    allowed_compositor_origins_.insert(origin);
 #endif
 }
 
@@ -1422,22 +1415,6 @@
   return false;
 }
 
-bool ChromeContentRendererClient::IsPluginAllowedToUseCompositorAPI(
-    const GURL& url) {
-#if BUILDFLAG(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_EXTENSIONS)
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnablePepperTesting))
-    return true;
-  if (IsExtensionOrSharedModuleWhitelisted(url, allowed_compositor_origins_))
-    return true;
-
-  version_info::Channel channel = chrome::GetChannel();
-  return channel <= version_info::Channel::DEV;
-#else
-  return false;
-#endif
-}
-
 content::BrowserPluginDelegate*
 ChromeContentRendererClient::CreateBrowserPluginDelegate(
     content::RenderFrame* render_frame,
diff --git a/chrome/renderer/chrome_content_renderer_client.h b/chrome/renderer/chrome_content_renderer_client.h
index a32c277..f07aab4 100644
--- a/chrome/renderer/chrome_content_renderer_client.h
+++ b/chrome/renderer/chrome_content_renderer_client.h
@@ -165,7 +165,6 @@
   bool IsKeySystemsUpdateNeeded() override;
   bool IsPluginAllowedToUseDevChannelAPIs() override;
   bool IsPluginAllowedToUseCameraDeviceAPI(const GURL& url) override;
-  bool IsPluginAllowedToUseCompositorAPI(const GURL& url) override;
   content::BrowserPluginDelegate* CreateBrowserPluginDelegate(
       content::RenderFrame* render_frame,
       const content::WebPluginInfo& info,
@@ -296,7 +295,6 @@
 #endif
 #if BUILDFLAG(ENABLE_PLUGINS)
   std::set<std::string> allowed_camera_device_origins_;
-  std::set<std::string> allowed_compositor_origins_;
 #endif
 
 #if defined(OS_WIN)
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index d8528be..ba14ca3 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -719,6 +719,7 @@
       "../browser/ntp_tiles/ntp_tiles_browsertest.cc",
       "../browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer_browsertest.cc",
+      "../browser/page_load_metrics/observers/data_use_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/https_engagement_metrics/https_engagement_page_load_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer_browsertest.cc",
diff --git a/chrome/test/data/ads_observer/ad_iframe_writer.js b/chrome/test/data/ads_observer/ad_iframe_writer.js
new file mode 100644
index 0000000..8bcbb61
--- /dev/null
+++ b/chrome/test/data/ads_observer/ad_iframe_writer.js
@@ -0,0 +1,7 @@
+// Creates and iframe and appends it to the body element. Make sure the caller
+// has a body element!
+function createAdIframe() {
+    let frame = document.createElement('iframe');
+    document.body.appendChild(frame);
+    return frame;
+}
\ No newline at end of file
diff --git a/chrome/test/data/ads_observer/docwrite_blank_frame.html b/chrome/test/data/ads_observer/docwrite_blank_frame.html
index e79fa3f..bf9cfd90 100644
--- a/chrome/test/data/ads_observer/docwrite_blank_frame.html
+++ b/chrome/test/data/ads_observer/docwrite_blank_frame.html
@@ -1,12 +1,14 @@
 <html>
-<iframe id="blank_frame" name="google_ads_iframe"></iframe>
+<body>
+<script src="ad_iframe_writer.js"></script>
 
 <script>
-  let iframe = document.getElementById("blank_frame");
-  let doc = iframe.contentWindow.document;
+  let adIframe = createAdIframe();
+  let doc = adIframe.contentWindow.document;
+
   doc.open();
   doc.write("<html>Rewritten. <img src=pixel.png> <img src=pixel2.png> <img src=pixel3.png></html>");
   doc.close();
 </script>
-
+</body>
 </html>
diff --git a/chrome/test/data/ads_observer/docwrite_provisional_frame.html b/chrome/test/data/ads_observer/docwrite_provisional_frame.html
index 7a347dbb..555305c 100644
--- a/chrome/test/data/ads_observer/docwrite_provisional_frame.html
+++ b/chrome/test/data/ads_observer/docwrite_provisional_frame.html
@@ -1,19 +1,22 @@
 <html>
-<iframe id="slow_frame" name="google_ads_iframe" src="/slow?100"></iframe>
+<body>
+<script src="ad_iframe_writer.js"></script>
 
 <script>
   window.addEventListener('message', function(e) {
     window.domAutomationController.send(e.data);
   });
 
+  adIframe = createAdIframe();
+  adIframe.id = "slow_frame";
+  adIframe.src = "/slow?100";
+
   // slow takes 100 seconds to load, plenty of time to overwrite the
   // provisional load.
-  let iframe = document.getElementById("slow_frame");
-  let doc = iframe.contentDocument;
-
+  let doc = adIframe.contentDocument;
   doc.open();
   doc.write("<html>Rewritten. <img src=pixel.png> <img src=pixel2.png> <img src=pixel3.png onload='parent.postMessage(&quot;loaded&quot;, &quot;*&quot;);'></html>");
   doc.close();
 </script>
-
+</body>
 </html>
diff --git a/chrome/test/data/ads_observer/same_origin_ad.html b/chrome/test/data/ads_observer/same_origin_ad.html
index 8015b46..db3ba1e 100644
--- a/chrome/test/data/ads_observer/same_origin_ad.html
+++ b/chrome/test/data/ads_observer/same_origin_ad.html
@@ -1,6 +1,13 @@
 <html>
+<body>
+<script src="ad_iframe_writer.js"></script>
+
 <!-- Should be counted as a normal same-origin ad iframe. -->
-<iframe id="src_frame_same_origin" name="google_ads_iframe"
-        src="pixel.png">
-</iframe>
+<script>
+    let adIframe = createAdIframe();
+    adIframe.id = "src_frame_same_origin";
+    adIframe.src = "pixel.png"
+</script>
+
+</body>
 </html>
diff --git a/chrome/test/data/ads_observer/srcdoc_embedded_ad.html b/chrome/test/data/ads_observer/srcdoc_embedded_ad.html
index 07af0d2..77f34ef3 100644
--- a/chrome/test/data/ads_observer/srcdoc_embedded_ad.html
+++ b/chrome/test/data/ads_observer/srcdoc_embedded_ad.html
@@ -1,6 +1,14 @@
 <html>
+<body>
+
+<script src="ad_iframe_writer.js"></script>
+
 <!-- Should be counted as same-origin even though it's about:contents. -->
-<iframe id="srcdoc_frame_content" name="google_ads_iframe"
-        srcdoc="<hmtl> <img src=pixel.png> </html>">
-</iframe>
+<script>
+    let adIframe = createAdIframe();
+    adIframe.id = "srcdoc_frame_content";
+    adIframe.srcdoc = "<html> <img src=pixel.png> </html>"
+</script>
+
+</body>
 </html>
diff --git a/chrome/test/data/ads_observer/srcdoc_embedded_ad_empty.html b/chrome/test/data/ads_observer/srcdoc_embedded_ad_empty.html
index a2af28d8..868701c 100644
--- a/chrome/test/data/ads_observer/srcdoc_embedded_ad_empty.html
+++ b/chrome/test/data/ads_observer/srcdoc_embedded_ad_empty.html
@@ -1,6 +1,14 @@
 <html>
+<body>
+
+<script src="ad_iframe_writer.js"></script>
+
 <!-- Shouldn't be counted at all, page has no bytes. -->
-<iframe id="srcdoc_frame_no_content" name="google_ads_iframe"
-        srcdoc="<hmtl></html>">
-</iframe>
+<script>
+    let adIframe = createAdIframe();
+    adIframe.id = "srcdoc_frame_no_content";
+    adIframe.srcdoc = "<html></html>"
+</script>
+
+</body>
 </html>
diff --git a/chrome/test/data/extensions/api_test/debugger/background.js b/chrome/test/data/extensions/api_test/debugger/background.js
index 44a2b9ee..7f0b181 100644
--- a/chrome/test/data/extensions/api_test/debugger/background.js
+++ b/chrome/test/data/extensions/api_test/debugger/background.js
@@ -325,6 +325,39 @@
     });
   },
 
+  // http://crbug.com/824174
+  function getResponseBodyInvalidChar() {
+    let requestId;
+
+    function onEvent(debuggeeId, message, params) {
+      if (message === 'Network.responseReceived' &&
+          params.response.url.endsWith('invalid_char.html')) {
+        requestId = params.requestId;
+      } else if (message === 'Network.loadingFinished' &&
+                 params.requestId === requestId) {
+        chrome.debugger.sendCommand(
+            debuggeeId, 'Network.getResponseBody',
+            {requestId: params.requestId}, function(responseBody) {
+              chrome.debugger.onEvent.removeListener(onEvent);
+              chrome.debugger.detach(debuggeeId);
+              chrome.test.succeed();
+            });
+      }
+    }
+
+    chrome.tabs.create({url: 'inspected.html'}, function(tab) {
+      const debuggee = {tabId: tab.id};
+      chrome.debugger.attach(debuggee, protocolVersion, function() {
+        chrome.debugger.onEvent.addListener(onEvent);
+        chrome.debugger.sendCommand(debuggee, 'Network.enable', function() {
+          chrome.debugger.sendCommand(
+              debuggee, 'Runtime.evaluate',
+              {expression: `fetch('/invalid_char.html');`});
+        });
+      });
+    });
+  },
+
   function offlineErrorPage() {
     const url = 'http://127.0.0.1//extensions/api_test/debugger/inspected.html';
     chrome.tabs.create({url: url}, function(tab) {
diff --git a/chrome/test/data/extensions/api_test/debugger/invalid_char.html b/chrome/test/data/extensions/api_test/debugger/invalid_char.html
new file mode 100644
index 0000000..5b25430
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/debugger/invalid_char.html
@@ -0,0 +1,3 @@
+<html>
+<p>￿</p>
+</html>
diff --git a/chrome/test/data/pdf/annotations_feature_enabled_test.js b/chrome/test/data/pdf/annotations_feature_enabled_test.js
index 5945a4e..65d7bf2 100644
--- a/chrome/test/data/pdf/annotations_feature_enabled_test.js
+++ b/chrome/test/data/pdf/annotations_feature_enabled_test.js
@@ -233,6 +233,19 @@
       ]);
     });
   },
+  function testPreventDefaultTouchStart() {
+    testAsync(async () => {
+      chrome.test.assertTrue(isAnnotationMode());
+      const inkHost = contentElement();
+      let called = false;
+      inkHost.onTouchStart_({
+        preventDefault() {
+          called = true;
+        }
+      });
+      chrome.test.assertTrue(called);
+    });
+  },
   function testExitAnnotationMode() {
     testAsync(async () => {
       chrome.test.assertTrue(isAnnotationMode());
diff --git a/chrome_elf/nt_registry/nt_registry_unittest.cc b/chrome_elf/nt_registry/nt_registry_unittest.cc
index 08728db..6921057 100644
--- a/chrome_elf/nt_registry/nt_registry_unittest.cc
+++ b/chrome_elf/nt_registry/nt_registry_unittest.cc
@@ -9,6 +9,7 @@
 #include <stddef.h>
 
 #include "base/callback_helpers.h"
+#include "base/stl_util.h"
 #include "base/test/test_reg_util_win.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -623,7 +624,7 @@
     ASSERT_TRUE(nt::QueryRegSubkey(key_handle, i, &subkey_name));
 
     bool found = false;
-    for (size_t index = 0; index < arraysize(check_names); index++) {
+    for (size_t index = 0; index < base::size(check_names); index++) {
       if (0 == subkey_name.compare(check_names[index])) {
         found = true;
         break;
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index f40e06f..122f243a 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -374,6 +374,7 @@
   sources = [
     "cast_content_window.cc",
     "cast_content_window.h",
+    "cast_web_contents.cc",
     "cast_web_contents.h",
     "cast_web_view.cc",
     "cast_web_view.h",
@@ -381,6 +382,7 @@
 
   # Need to expose this so that internal public_configs are propagated.
   public_deps = [
+    "//chromecast/common/mojom",
     "//content/public/browser",
   ]
 
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index 533d5ba..a84c674 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -400,12 +400,15 @@
   // the background (resources have not yet been granted to cast) since it
   // prevents the long delay the user would have seen on first rendering. Note
   // that future calls to FcInit() are safe no-ops per the FontConfig interface.
-  FcChar8 bundle_dir[] = "/chrome/fonts/";
+  base::FilePath dir_module;
+  base::PathService::Get(base::DIR_MODULE, &dir_module);
+  base::FilePath dir_font = dir_module.Append("fonts");
 
   FcInit();
 
-  if (FcConfigAppFontAddDir(nullptr, bundle_dir) == FcFalse) {
-    LOG(ERROR) << "Cannot load fonts from " << bundle_dir;
+  const FcChar8 *dir_font_char8 = reinterpret_cast<const FcChar8*>(dir_font.value().data());
+  if (FcConfigAppFontAddDir(nullptr, dir_font_char8) == FcFalse) {
+    LOG(ERROR) << "Cannot load fonts from " << dir_font_char8;
   }
 #endif
 }
diff --git a/chromecast/browser/cast_content_renderer_manifest_overlay.json b/chromecast/browser/cast_content_renderer_manifest_overlay.json
index 8194a7d..37ad598d 100644
--- a/chromecast/browser/cast_content_renderer_manifest_overlay.json
+++ b/chromecast/browser/cast_content_renderer_manifest_overlay.json
@@ -4,7 +4,10 @@
   "interface_provider_specs": {
     "navigation:frame": {
       "provides": {
-        "browser": ["chromecast.shell.mojom.MediaPlaybackOptions"]
+        "browser": [
+          "chromecast.shell.mojom.FeatureManager",
+          "chromecast.shell.mojom.MediaPlaybackOptions"
+        ]
       }
     }
   }
diff --git a/chromecast/browser/cast_extension_host.cc b/chromecast/browser/cast_extension_host.cc
index b19f2e4..c5a360a 100644
--- a/chromecast/browser/cast_extension_host.cc
+++ b/chromecast/browser/cast_extension_host.cc
@@ -83,8 +83,8 @@
     const base::string16& message,
     int32_t line_no,
     const base::string16& source_id) {
-  return delegate_->OnAddMessageToConsoleReceived(source, level, message,
-                                                  line_no, source_id);
+  return delegate_->OnAddMessageToConsoleReceived(level, message, line_no,
+                                                  source_id);
 }
 
 void CastExtensionHost::Observe(int type,
diff --git a/chromecast/browser/cast_web_contents.cc b/chromecast/browser/cast_web_contents.cc
new file mode 100644
index 0000000..3cd42c2
--- /dev/null
+++ b/chromecast/browser/cast_web_contents.cc
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/browser/cast_web_contents.h"
+
+namespace chromecast {
+
+std::vector<chromecast::shell::mojom::FeaturePtr>
+CastWebContents::Delegate::GetRendererFeatures() {
+  return std::vector<chromecast::shell::mojom::FeaturePtr>();
+}
+
+CastWebContents::Observer::Observer() : cast_web_contents_(nullptr) {}
+
+CastWebContents::Observer::~Observer() {
+  if (cast_web_contents_) {
+    cast_web_contents_->RemoveObserver(this);
+  }
+}
+
+void CastWebContents::Observer::Observe(CastWebContents* cast_web_contents) {
+  if (cast_web_contents == cast_web_contents_) {
+    // Early exit to avoid infinite loops if we're in the middle of a callback.
+    return;
+  }
+  if (cast_web_contents_) {
+    cast_web_contents_->RemoveObserver(this);
+  }
+  cast_web_contents_ = cast_web_contents;
+  if (cast_web_contents_) {
+    cast_web_contents_->AddObserver(this);
+  }
+}
+
+void CastWebContents::Observer::ResetCastWebContents() {
+  cast_web_contents_->RemoveObserver(this);
+  cast_web_contents_ = nullptr;
+}
+
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_web_contents.h b/chromecast/browser/cast_web_contents.h
index 71d946b..db1c817 100644
--- a/chromecast/browser/cast_web_contents.h
+++ b/chromecast/browser/cast_web_contents.h
@@ -5,6 +5,11 @@
 #ifndef CHROMECAST_BROWSER_CAST_WEB_CONTENTS_H_
 #define CHROMECAST_BROWSER_CAST_WEB_CONTENTS_H_
 
+#include <string>
+#include <vector>
+
+#include "base/observer_list.h"
+#include "chromecast/common/mojom/feature_manager.mojom.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -35,10 +40,42 @@
     virtual void OnPageStopped(CastWebContents* cast_web_contents,
                                int error_code) = 0;
 
+    // Collects the set of delegate-specific renderer features that needs to be
+    // configured when `CastWebContents::RenderFrameCreated` is invoked.
+    virtual std::vector<chromecast::shell::mojom::FeaturePtr>
+    GetRendererFeatures();
+
    protected:
     virtual ~Delegate() {}
   };
 
+  class Observer {
+   public:
+    Observer();
+
+    virtual void RenderFrameCreated(int render_process_id,
+                                    int render_frame_id) {}
+    virtual void OnInterfaceRequestFromFrame(
+        const std::string& interface_name,
+        mojo::ScopedMessagePipeHandle* interface_pipe) {}
+
+    // Adds |this| to the ObserverList in the implementation of
+    // |cast_web_contents|.
+    void Observe(CastWebContents* cast_web_contents);
+
+    // Removes |this| from the ObserverList in the implementation of
+    // |cast_web_contents_|. This is only invoked by CastWebContents and is used
+    // to ensure that once the observed CastWebContents object is destructed the
+    // CastWebContents::Observer does not invoke any additional function calls
+    // on it.
+    void ResetCastWebContents();
+
+   protected:
+    virtual ~Observer();
+
+    CastWebContents* cast_web_contents_;
+  };
+
   // Page state for the main frame.
   enum class PageState {
     IDLE,       // Main frame has not started yet.
@@ -72,6 +109,15 @@
   // Set the delegate. SetDelegate(nullptr) can be used to stop notifications.
   virtual void SetDelegate(Delegate* delegate) = 0;
 
+  virtual void AllowWebAndMojoWebUiBindings() = 0;
+  virtual void ClearRenderWidgetHostView() = 0;
+
+  // Used to add or remove |observer| to the ObserverList in the implementation.
+  // These functions should only be invoked by CastWebContents::Observer in a
+  // valid sequence, enforced via SequenceChecker.
+  virtual void AddObserver(Observer* observer) = 0;
+  virtual void RemoveObserver(Observer* observer) = 0;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(CastWebContents);
 };
diff --git a/chromecast/browser/cast_web_contents_impl.cc b/chromecast/browser/cast_web_contents_impl.cc
index 05a9a89..89436e13 100644
--- a/chromecast/browser/cast_web_contents_impl.cc
+++ b/chromecast/browser/cast_web_contents_impl.cc
@@ -10,7 +10,12 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/common/bindings_policy.h"
 #include "net/base/net_errors.h"
+#include "services/service_manager/public/cpp/interface_provider.h"
 #include "url/gurl.h"
 
 namespace chromecast {
@@ -47,6 +52,10 @@
 CastWebContentsImpl::~CastWebContentsImpl() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DisableDebugging();
+
+  for (auto& observer : observer_list_) {
+    observer.ResetCastWebContents();
+  }
 }
 
 content::WebContents* CastWebContentsImpl::web_contents() const {
@@ -119,6 +128,41 @@
   delegate_ = delegate;
 }
 
+void CastWebContentsImpl::AllowWebAndMojoWebUiBindings() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  content::RenderViewHost* rvh = web_contents_->GetRenderViewHost();
+  DCHECK(rvh);
+  rvh->GetMainFrame()->AllowBindings(content::BINDINGS_POLICY_WEB_UI |
+                                     content::BINDINGS_POLICY_MOJO_WEB_UI);
+}
+
+// Set background to transparent before making the view visible. This is in
+// case Chrome dev tools was opened and caused background color to be reset.
+// Note: we also have to set color to black first, because
+// RenderWidgetHostViewBase::SetBackgroundColor ignores setting color to
+// current color, and it isn't aware that dev tools has changed the color.
+void CastWebContentsImpl::ClearRenderWidgetHostView() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  content::RenderWidgetHostView* view =
+      web_contents_->GetRenderWidgetHostView();
+  if (view) {
+    view->SetBackgroundColor(SK_ColorBLACK);
+    view->SetBackgroundColor(SK_ColorTRANSPARENT);
+  }
+}
+
+void CastWebContentsImpl::AddObserver(CastWebContents::Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(observer);
+  observer_list_.AddObserver(observer);
+}
+
+void CastWebContentsImpl::RemoveObserver(CastWebContents::Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(observer);
+  observer_list_.RemoveObserver(observer);
+}
+
 void CastWebContentsImpl::OnClosePageTimeout() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!closing_ || stopped_) {
@@ -128,6 +172,43 @@
   Stop(net::OK);
 }
 
+void CastWebContentsImpl::RenderFrameCreated(
+    content::RenderFrameHost* render_frame_host) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(render_frame_host);
+
+  // New render frame has been created, we need to add it to the app
+  // whitelisting session so URL requests are handled correctly. This must be
+  // done before URL requests are executed within render frame.
+  auto* process = render_frame_host->GetProcess();
+  const int render_process_id = process->GetID();
+  const int render_frame_id = render_frame_host->GetRoutingID();
+
+  for (Observer& observer : observer_list_) {
+    observer.RenderFrameCreated(render_process_id, render_frame_id);
+  }
+
+  // Only the root frame should receive feature configuration messages.
+  if (render_frame_host->GetParent()) {
+    return;
+  }
+
+  chromecast::shell::mojom::FeatureManagerPtr feature_manager_ptr;
+  render_frame_host->GetRemoteInterfaces()->GetInterface(&feature_manager_ptr);
+  feature_manager_ptr->ConfigureFeatures(delegate_->GetRendererFeatures());
+}
+
+void CastWebContentsImpl::OnInterfaceRequestFromFrame(
+    content::RenderFrameHost* /* render_frame_host */,
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle* interface_pipe) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  for (Observer& observer : observer_list_) {
+    observer.OnInterfaceRequestFromFrame(interface_name, interface_pipe);
+  }
+}
+
 void CastWebContentsImpl::RenderProcessGone(base::TerminationStatus status) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   LOG(INFO) << "Render process for main frame exited unexpectedly.";
diff --git a/chromecast/browser/cast_web_contents_impl.h b/chromecast/browser/cast_web_contents_impl.h
index 8d5c6a44..ed9d27f 100644
--- a/chromecast/browser/cast_web_contents_impl.h
+++ b/chromecast/browser/cast_web_contents_impl.h
@@ -5,6 +5,8 @@
 #ifndef CHROMECAST_BROWSER_CAST_WEB_CONTENTS_IMPL_H_
 #define CHROMECAST_BROWSER_CAST_WEB_CONTENTS_IMPL_H_
 
+#include <string>
+
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
@@ -36,9 +38,20 @@
   void ClosePage() override;
   void Stop(int error_code) override;
   void SetDelegate(Delegate* delegate) override;
+  void AllowWebAndMojoWebUiBindings() override;
+  void ClearRenderWidgetHostView() override;
+
+  // Observer interface:
+  void AddObserver(Observer* observer) override;
+  void RemoveObserver(Observer* observer) override;
 
  private:
   // WebContentsObserver implementation:
+  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
+  void OnInterfaceRequestFromFrame(
+      content::RenderFrameHost* /* render_frame_host */,
+      const std::string& interface_name,
+      mojo::ScopedMessagePipeHandle* interface_pipe) override;
   void RenderProcessGone(base::TerminationStatus status) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
@@ -69,6 +82,8 @@
   bool notifying_;
   int last_error_;
 
+  base::ObserverList<Observer>::Unchecked observer_list_;
+
   const scoped_refptr<base::SequencedTaskRunner> task_runner_;
   SEQUENCE_CHECKER(sequence_checker_);
   base::WeakPtrFactory<CastWebContentsImpl> weak_factory_;
diff --git a/chromecast/browser/cast_web_contents_manager.cc b/chromecast/browser/cast_web_contents_manager.cc
index 8065f4b..6f29ba8 100644
--- a/chromecast/browser/cast_web_contents_manager.cc
+++ b/chromecast/browser/cast_web_contents_manager.cc
@@ -18,6 +18,7 @@
 #include "chromecast/browser/cast_web_view_factory.h"
 #include "chromecast/chromecast_buildflags.h"
 #include "content/public/browser/media_session.h"
+#include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
 
 #if BUILDFLAG(ENABLE_CHROMECAST_EXTENSIONS)
@@ -50,6 +51,17 @@
       params, this, std::move(site_instance), extension, initial_url);
 }
 
+std::unique_ptr<CastWebView> CastWebContentsManager::CreateWebView(
+    const CastWebView::CreateParams& params,
+    const extensions::Extension* extension,
+    const GURL& initial_url) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return web_view_factory_->CreateWebView(
+      params, this,
+      content::SiteInstance::CreateForURL(browser_context_, initial_url),
+      extension, initial_url);
+}
+
 void CastWebContentsManager::DelayWebContentsDeletion(
     std::unique_ptr<content::WebContents> web_contents,
     base::TimeDelta time_delta) {
diff --git a/chromecast/browser/cast_web_contents_manager.h b/chromecast/browser/cast_web_contents_manager.h
index 161b0a2..ec96dcf 100644
--- a/chromecast/browser/cast_web_contents_manager.h
+++ b/chromecast/browser/cast_web_contents_manager.h
@@ -48,6 +48,11 @@
       const extensions::Extension* extension,
       const GURL& initial_url);
 
+  std::unique_ptr<CastWebView> CreateWebView(
+      const CastWebView::CreateParams& params,
+      const extensions::Extension* extension,
+      const GURL& initial_url);
+
   // Take ownership of |web_contents| and delete after |time_delta|, or sooner
   // if necessary.
   void DelayWebContentsDeletion(
diff --git a/chromecast/browser/cast_web_view.h b/chromecast/browser/cast_web_view.h
index fc2b0c3..87f50a68 100644
--- a/chromecast/browser/cast_web_view.h
+++ b/chromecast/browser/cast_web_view.h
@@ -36,7 +36,6 @@
     // Returning true indicates that the delegate handled the message.
     // If false is returned the default logging mechanism will be used.
     virtual bool OnAddMessageToConsoleReceived(
-        content::WebContents* source,
         int32_t level,
         const base::string16& message,
         int32_t line_no,
@@ -102,6 +101,8 @@
 
   virtual content::WebContents* web_contents() const = 0;
 
+  virtual CastWebContents* cast_web_contents() = 0;
+
   // Navigates to |url|. The loaded page will be preloaded if MakeVisible has
   // not been called on the object.
   virtual void LoadUrl(GURL url) = 0;
diff --git a/chromecast/browser/cast_web_view_default.cc b/chromecast/browser/cast_web_view_default.cc
index 671b74c0..74f02d3 100644
--- a/chromecast/browser/cast_web_view_default.cc
+++ b/chromecast/browser/cast_web_view_default.cc
@@ -107,6 +107,10 @@
   return web_contents_.get();
 }
 
+CastWebContents* CastWebViewDefault::cast_web_contents() {
+  return &cast_web_contents_;
+}
+
 void CastWebViewDefault::LoadUrl(GURL url) {
   web_contents_->GetController().LoadURL(url, content::Referrer(),
                                          ui::PAGE_TRANSITION_TYPED, "");
@@ -191,8 +195,8 @@
     const base::string16& message,
     int32_t line_no,
     const base::string16& source_id) {
-  return delegate_->OnAddMessageToConsoleReceived(source, level, message,
-                                                  line_no, source_id);
+  return delegate_->OnAddMessageToConsoleReceived(level, message, line_no,
+                                                  source_id);
 }
 
 const content::MediaStreamDevice* GetRequestedDeviceOrDefault(
diff --git a/chromecast/browser/cast_web_view_default.h b/chromecast/browser/cast_web_view_default.h
index 23b19b72e..8dde75d 100644
--- a/chromecast/browser/cast_web_view_default.h
+++ b/chromecast/browser/cast_web_view_default.h
@@ -44,6 +44,7 @@
   // CastWebView implementation:
   shell::CastContentWindow* window() const override;
   content::WebContents* web_contents() const override;
+  CastWebContents* cast_web_contents() override;
   void LoadUrl(GURL url) override;
   void ClosePage(const base::TimeDelta& shutdown_delay) override;
   void InitializeWindow(CastWindowManager* window_manager,
diff --git a/chromecast/browser/cast_web_view_extension.cc b/chromecast/browser/cast_web_view_extension.cc
index 450144f..7fd423e 100644
--- a/chromecast/browser/cast_web_view_extension.cc
+++ b/chromecast/browser/cast_web_view_extension.cc
@@ -50,6 +50,10 @@
   return extension_host_->host_contents();
 }
 
+CastWebContents* CastWebViewExtension::cast_web_contents() {
+  return &cast_web_contents_;
+}
+
 void CastWebViewExtension::LoadUrl(GURL url) {
   extension_host_->CreateRenderViewSoon();
 }
diff --git a/chromecast/browser/cast_web_view_extension.h b/chromecast/browser/cast_web_view_extension.h
index f29fe14..9cbc879 100644
--- a/chromecast/browser/cast_web_view_extension.h
+++ b/chromecast/browser/cast_web_view_extension.h
@@ -44,6 +44,7 @@
   shell::CastContentWindow* window() const override;
 
   content::WebContents* web_contents() const override;
+  CastWebContents* cast_web_contents() override;
 
   // CastWebView implementation:
   void LoadUrl(GURL url) override;
diff --git a/chromecast/browser/service/cast_service_simple.cc b/chromecast/browser/service/cast_service_simple.cc
index 8be27c5e..04037b0 100644
--- a/chromecast/browser/service/cast_service_simple.cc
+++ b/chromecast/browser/service/cast_service_simple.cc
@@ -101,7 +101,6 @@
 void CastServiceSimple::OnKeyEvent(const ui::KeyEvent& key_event) {}
 
 bool CastServiceSimple::OnAddMessageToConsoleReceived(
-    content::WebContents* source,
     int32_t level,
     const base::string16& message,
     int32_t line_no,
diff --git a/chromecast/browser/service/cast_service_simple.h b/chromecast/browser/service/cast_service_simple.h
index 9729dd4..22d9b3e4 100644
--- a/chromecast/browser/service/cast_service_simple.h
+++ b/chromecast/browser/service/cast_service_simple.h
@@ -39,8 +39,7 @@
   void OnPageStopped(CastWebContents* cast_web_contents,
                      int error_code) override;
   void OnPageStateChanged(CastWebContents* cast_web_contents) override;
-  bool OnAddMessageToConsoleReceived(content::WebContents* source,
-                                     int32_t level,
+  bool OnAddMessageToConsoleReceived(int32_t level,
                                      const base::string16& message,
                                      int32_t line_no,
                                      const base::string16& source_id) override;
diff --git a/chromecast/browser/test/cast_browser_test.cc b/chromecast/browser/test/cast_browser_test.cc
index 8bd2989..164bda1 100644
--- a/chromecast/browser/test/cast_browser_test.cc
+++ b/chromecast/browser/test/cast_browser_test.cc
@@ -107,7 +107,6 @@
 }
 
 bool CastBrowserTest::OnAddMessageToConsoleReceived(
-    content::WebContents* source,
     int32_t level,
     const base::string16& message,
     int32_t line_no,
diff --git a/chromecast/browser/test/cast_browser_test.h b/chromecast/browser/test/cast_browser_test.h
index bfb9e018..9b0e181 100644
--- a/chromecast/browser/test/cast_browser_test.h
+++ b/chromecast/browser/test/cast_browser_test.h
@@ -49,8 +49,7 @@
                      int error_code) override;
   void OnWindowDestroyed() override;
   void OnKeyEvent(const ui::KeyEvent& key_event) override;
-  bool OnAddMessageToConsoleReceived(content::WebContents* source,
-                                     int32_t level,
+  bool OnAddMessageToConsoleReceived(int32_t level,
                                      const base::string16& message,
                                      int32_t line_no,
                                      const base::string16& source_id) override;
diff --git a/chromecast/common/mojom/BUILD.gn b/chromecast/common/mojom/BUILD.gn
index 8c1042f3..6997473 100644
--- a/chromecast/common/mojom/BUILD.gn
+++ b/chromecast/common/mojom/BUILD.gn
@@ -8,6 +8,7 @@
   sources = [
     "application_media_capabilities.mojom",
     "constants.mojom",
+    "feature_manager.mojom",
     "media_caps.mojom",
     "media_playback_options.mojom",
     "memory_pressure.mojom",
diff --git a/chromecast/common/mojom/feature_manager.mojom b/chromecast/common/mojom/feature_manager.mojom
new file mode 100644
index 0000000..3ced1f7
--- /dev/null
+++ b/chromecast/common/mojom/feature_manager.mojom
@@ -0,0 +1,24 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chromecast.shell.mojom;
+
+import "mojo/public/mojom/base/values.mojom";
+
+// Represents one feature and stores additional data in
+// DictionaryValue
+struct Feature {
+  string name;
+  mojo_base.mojom.DictionaryValue config;
+};
+
+// Receives messages from the browser process to enable/disable Cast
+// application-facing features. This interface is implemented by a
+// single renderer.
+interface FeatureManager {
+
+  ConfigureFeatures(array<Feature> features);
+
+};
+
diff --git a/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.cc b/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.cc
index 5ce76773c..f0a3a1a 100644
--- a/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.cc
+++ b/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.cc
@@ -41,8 +41,7 @@
 AndroidSmsAppInstallingStatusObserver::Factory::BuildInstance(
     HostStatusProvider* host_status_provider,
     FeatureStateManager* feature_state_manager,
-    std::unique_ptr<AndroidSmsAppHelperDelegate>
-        android_sms_app_helper_delegate) {
+    AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate) {
   return base::WrapUnique(new AndroidSmsAppInstallingStatusObserver(
       host_status_provider, feature_state_manager,
       std::move(android_sms_app_helper_delegate)));
@@ -57,12 +56,10 @@
 AndroidSmsAppInstallingStatusObserver::AndroidSmsAppInstallingStatusObserver(
     HostStatusProvider* host_status_provider,
     FeatureStateManager* feature_state_manager,
-    std::unique_ptr<AndroidSmsAppHelperDelegate>
-        android_sms_app_helper_delegate)
+    AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate)
     : host_status_provider_(host_status_provider),
       feature_state_manager_(feature_state_manager),
-      android_sms_app_helper_delegate_(
-          std::move(android_sms_app_helper_delegate)) {
+      android_sms_app_helper_delegate_(android_sms_app_helper_delegate) {
   host_status_provider_->AddObserver(this);
   feature_state_manager_->AddObserver(this);
   InstallPwaIfNeeded();
diff --git a/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.h b/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.h
index 36c2a3be..ff0500afc 100644
--- a/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.h
+++ b/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer.h
@@ -30,8 +30,7 @@
     virtual std::unique_ptr<AndroidSmsAppInstallingStatusObserver>
     BuildInstance(HostStatusProvider* host_status_provider,
                   FeatureStateManager* feature_state_manager,
-                  std::unique_ptr<AndroidSmsAppHelperDelegate>
-                      android_sms_app_helper_delegate);
+                  AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate);
 
    private:
     static Factory* test_factory_;
@@ -43,8 +42,7 @@
   AndroidSmsAppInstallingStatusObserver(
       HostStatusProvider* host_status_provider,
       FeatureStateManager* feature_state_manager,
-      std::unique_ptr<AndroidSmsAppHelperDelegate>
-          android_sms_app_helper_delegate);
+      AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate);
 
   // HostStatusProvider::Observer:
   void OnHostStatusChange(const HostStatusProvider::HostStatusWithDevice&
@@ -59,7 +57,7 @@
 
   HostStatusProvider* host_status_provider_;
   FeatureStateManager* feature_state_manager_;
-  std::unique_ptr<AndroidSmsAppHelperDelegate> android_sms_app_helper_delegate_;
+  AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(AndroidSmsAppInstallingStatusObserver);
 };
diff --git a/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc b/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
index 7d112600..caa9e26 100644
--- a/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
+++ b/chromeos/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
@@ -33,21 +33,19 @@
       default;
 
   void SetUp() override {
-    auto fake_android_sms_app_helper_delegate =
-        std::make_unique<FakeAndroidSmsAppHelperDelegate>();
     fake_android_sms_app_helper_delegate_ =
-        fake_android_sms_app_helper_delegate.get();
+        std::make_unique<FakeAndroidSmsAppHelperDelegate>();
     fake_host_status_provider_ = std::make_unique<FakeHostStatusProvider>();
     fake_feature_state_manager_ = std::make_unique<FakeFeatureStateManager>();
     android_sms_app_installing_status_observer_ =
         AndroidSmsAppInstallingStatusObserver::Factory::Get()->BuildInstance(
             fake_host_status_provider_.get(), fake_feature_state_manager_.get(),
-            std::move(fake_android_sms_app_helper_delegate));
+            fake_android_sms_app_helper_delegate_.get());
 
     SetMessagesFeatureState(mojom::FeatureState::kEnabledByUser);
     SetHostWithStatus(mojom::HostStatus::kHostVerified, GetFakePhone());
     fake_app_helper_delegate()->Reset();
-    EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+    EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
   }
 
   void SetHostWithStatus(
@@ -57,7 +55,7 @@
   }
 
   FakeAndroidSmsAppHelperDelegate* fake_app_helper_delegate() {
-    return fake_android_sms_app_helper_delegate_;
+    return fake_android_sms_app_helper_delegate_.get();
   }
 
   multidevice::RemoteDeviceRef GetFakePhone() {
@@ -75,7 +73,8 @@
  private:
   std::unique_ptr<FakeHostStatusProvider> fake_host_status_provider_;
   std::unique_ptr<FakeFeatureStateManager> fake_feature_state_manager_;
-  FakeAndroidSmsAppHelperDelegate* fake_android_sms_app_helper_delegate_;
+  std::unique_ptr<FakeAndroidSmsAppHelperDelegate>
+      fake_android_sms_app_helper_delegate_;
 
   std::unique_ptr<AndroidSmsAppInstallingStatusObserver>
       android_sms_app_installing_status_observer_;
@@ -86,112 +85,112 @@
 
 TEST_F(MultiDeviceSetupAndroidSmsAppInstallingStatusObserverTest,
        InstallsAfterHostPending) {
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 
   SetHostWithStatus(mojom::HostStatus::kEligibleHostExistsButNoHostSet,
                     base::nullopt /* host_device */);
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 
   SetHostWithStatus(
       mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
       GetFakePhone());
-  EXPECT_TRUE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_TRUE(fake_app_helper_delegate()->has_installed_app());
 }
 
 TEST_F(MultiDeviceSetupAndroidSmsAppInstallingStatusObserverTest,
        InstallsAfterHostVerified) {
   SetHostWithStatus(mojom::HostStatus::kNoEligibleHosts,
                     base::nullopt /* host_device */);
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 
   SetHostWithStatus(mojom::HostStatus::kHostVerified, GetFakePhone());
-  EXPECT_TRUE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_TRUE(fake_app_helper_delegate()->has_installed_app());
 }
 
 TEST_F(MultiDeviceSetupAndroidSmsAppInstallingStatusObserverTest,
        DoesNotInstallsAfterHostVerifiedIfNotAllowed) {
   SetMessagesFeatureState(mojom::FeatureState::kProhibitedByPolicy);
   fake_app_helper_delegate()->Reset();
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 
   SetHostWithStatus(mojom::HostStatus::kNoEligibleHosts,
                     base::nullopt /* host_device */);
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 
   SetHostWithStatus(mojom::HostStatus::kHostVerified, GetFakePhone());
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 }
 
 TEST_F(MultiDeviceSetupAndroidSmsAppInstallingStatusObserverTest,
        DoesNotInstallsAfterHostVerifiedIfNotSupportedByPhone) {
   SetMessagesFeatureState(mojom::FeatureState::kNotSupportedByPhone);
   fake_app_helper_delegate()->Reset();
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 
   SetHostWithStatus(mojom::HostStatus::kNoEligibleHosts,
                     base::nullopt /* host_device */);
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 
   SetHostWithStatus(mojom::HostStatus::kHostVerified, GetFakePhone());
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 }
 
 TEST_F(MultiDeviceSetupAndroidSmsAppInstallingStatusObserverTest,
        DoesNotInstallsAfterHostVerifiedIfNotSupportedByChromebook) {
   SetMessagesFeatureState(mojom::FeatureState::kNotSupportedByChromebook);
   fake_app_helper_delegate()->Reset();
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 
   SetHostWithStatus(mojom::HostStatus::kNoEligibleHosts,
                     base::nullopt /* host_device */);
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 
   SetHostWithStatus(mojom::HostStatus::kHostVerified, GetFakePhone());
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 }
 
 TEST_F(MultiDeviceSetupAndroidSmsAppInstallingStatusObserverTest,
        InstallsWhenFeatureBecomesEnabled) {
   SetMessagesFeatureState(mojom::FeatureState::kNotSupportedByChromebook);
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
   SetMessagesFeatureState(mojom::FeatureState::kEnabledByUser);
-  EXPECT_TRUE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_TRUE(fake_app_helper_delegate()->has_installed_app());
 }
 
 TEST_F(MultiDeviceSetupAndroidSmsAppInstallingStatusObserverTest,
        CleansUpPwaInstallationWhenDisabled) {
   SetMessagesFeatureState(mojom::FeatureState::kNotSupportedByChromebook);
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
   SetMessagesFeatureState(mojom::FeatureState::kEnabledByUser);
-  EXPECT_TRUE(fake_app_helper_delegate()->HasInstalledApp());
-  EXPECT_TRUE(fake_app_helper_delegate()->IsDefaultToPersistCookieSet());
+  EXPECT_TRUE(fake_app_helper_delegate()->has_installed_app());
+  EXPECT_TRUE(fake_app_helper_delegate()->is_default_to_persist_cookie_set());
 
   SetMessagesFeatureState(mojom::FeatureState::kDisabledByUser);
-  EXPECT_TRUE(fake_app_helper_delegate()->HasInstalledApp());
-  EXPECT_FALSE(fake_app_helper_delegate()->IsDefaultToPersistCookieSet());
+  EXPECT_TRUE(fake_app_helper_delegate()->has_installed_app());
+  EXPECT_FALSE(fake_app_helper_delegate()->is_default_to_persist_cookie_set());
 }
 
 TEST_F(MultiDeviceSetupAndroidSmsAppInstallingStatusObserverTest,
        DoesNotInstallWhenFeatureIsDisabledByUser) {
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
   SetMessagesFeatureState(mojom::FeatureState::kDisabledByUser);
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 }
 
 TEST_F(MultiDeviceSetupAndroidSmsAppInstallingStatusObserverTest,
        DoesNotInstallWhenSuiteIsDisabledByUser) {
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
   SetMessagesFeatureState(mojom::FeatureState::kUnavailableSuiteDisabled);
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 }
 
 TEST_F(MultiDeviceSetupAndroidSmsAppInstallingStatusObserverTest,
        DoesNotInstallIfNotVerified) {
   SetHostWithStatus(mojom::HostStatus::kNoEligibleHosts,
                     base::nullopt /* host_device */);
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
   SetMessagesFeatureState(mojom::FeatureState::kUnavailableNoVerifiedHost);
-  EXPECT_FALSE(fake_app_helper_delegate()->HasInstalledApp());
+  EXPECT_FALSE(fake_app_helper_delegate()->has_installed_app());
 }
 
 }  // namespace multidevice_setup
diff --git a/chromeos/services/multidevice_setup/feature_state_manager_impl.cc b/chromeos/services/multidevice_setup/feature_state_manager_impl.cc
index 0636b2db..231125d 100644
--- a/chromeos/services/multidevice_setup/feature_state_manager_impl.cc
+++ b/chromeos/services/multidevice_setup/feature_state_manager_impl.cc
@@ -166,30 +166,28 @@
     PrefService* pref_service,
     HostStatusProvider* host_status_provider,
     device_sync::DeviceSyncClient* device_sync_client,
-    std::unique_ptr<AndroidSmsPairingStateTracker>
-        android_sms_pairing_state_tracker) {
+    AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker) {
   return base::WrapUnique(new FeatureStateManagerImpl(
       pref_service, host_status_provider, device_sync_client,
-      std::move(android_sms_pairing_state_tracker)));
+      android_sms_pairing_state_tracker));
 }
 
 FeatureStateManagerImpl::FeatureStateManagerImpl(
     PrefService* pref_service,
     HostStatusProvider* host_status_provider,
     device_sync::DeviceSyncClient* device_sync_client,
-    std::unique_ptr<AndroidSmsPairingStateTracker>
-        android_sms_pairing_state_tracker)
+    AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker)
     : pref_service_(pref_service),
       host_status_provider_(host_status_provider),
       device_sync_client_(device_sync_client),
-      android_sms_pairing_state_tracker_(
-          std::move(android_sms_pairing_state_tracker)),
+      android_sms_pairing_state_tracker_(android_sms_pairing_state_tracker),
       feature_to_enabled_pref_name_map_(GenerateFeatureToEnabledPrefNameMap()),
       feature_to_allowed_pref_name_map_(GenerateFeatureToAllowedPrefNameMap()),
       cached_feature_state_map_(GenerateInitialDefaultCachedStateMap()) {
   host_status_provider_->AddObserver(this);
   device_sync_client_->AddObserver(this);
-  android_sms_pairing_state_tracker_->AddObserver(this);
+  if (android_sms_pairing_state_tracker_)
+    android_sms_pairing_state_tracker_->AddObserver(this);
 
   registrar_.Init(pref_service_);
 
@@ -221,7 +219,8 @@
 FeatureStateManagerImpl::~FeatureStateManagerImpl() {
   host_status_provider_->RemoveObserver(this);
   device_sync_client_->RemoveObserver(this);
-  android_sms_pairing_state_tracker_->RemoveObserver(this);
+  if (android_sms_pairing_state_tracker_)
+    android_sms_pairing_state_tracker_->RemoveObserver(this);
 }
 
 FeatureStateManager::FeatureStatesMap
diff --git a/chromeos/services/multidevice_setup/feature_state_manager_impl.h b/chromeos/services/multidevice_setup/feature_state_manager_impl.h
index 30552ce..9de92754 100644
--- a/chromeos/services/multidevice_setup/feature_state_manager_impl.h
+++ b/chromeos/services/multidevice_setup/feature_state_manager_impl.h
@@ -39,8 +39,7 @@
         PrefService* pref_service,
         HostStatusProvider* host_status_provider,
         device_sync::DeviceSyncClient* device_sync_client,
-        std::unique_ptr<AndroidSmsPairingStateTracker>
-            android_sms_pairing_state_tracker);
+        AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker);
 
    private:
     static Factory* test_factory_;
@@ -49,11 +48,11 @@
   ~FeatureStateManagerImpl() override;
 
  private:
-  FeatureStateManagerImpl(PrefService* pref_service,
-                          HostStatusProvider* host_status_provider,
-                          device_sync::DeviceSyncClient* device_sync_client,
-                          std::unique_ptr<AndroidSmsPairingStateTracker>
-                              android_sms_pairing_state_tracker);
+  FeatureStateManagerImpl(
+      PrefService* pref_service,
+      HostStatusProvider* host_status_provider,
+      device_sync::DeviceSyncClient* device_sync_client,
+      AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker);
 
   // FeatureStateManager:
   FeatureStatesMap GetFeatureStates() override;
@@ -85,8 +84,7 @@
   PrefService* pref_service_;
   HostStatusProvider* host_status_provider_;
   device_sync::DeviceSyncClient* device_sync_client_;
-  std::unique_ptr<AndroidSmsPairingStateTracker>
-      android_sms_pairing_state_tracker_;
+  AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker_;
 
   // Map from feature to the pref name which indicates the enabled/disabled
   // boolean state for the feature.
diff --git a/chromeos/services/multidevice_setup/feature_state_manager_impl_unittest.cc b/chromeos/services/multidevice_setup/feature_state_manager_impl_unittest.cc
index b553a2bb..cc756ec 100644
--- a/chromeos/services/multidevice_setup/feature_state_manager_impl_unittest.cc
+++ b/chromeos/services/multidevice_setup/feature_state_manager_impl_unittest.cc
@@ -70,16 +70,14 @@
                                          test_host_device_});
     fake_device_sync_client_->set_local_device_metadata(test_local_device_);
 
-    auto fake_android_sms_pairing_state_tracker =
-        std::make_unique<FakeAndroidSmsPairingStateTracker>();
-    fake_android_sms_pairing_state_tracker->SetPairingComplete(true);
     fake_android_sms_pairing_state_tracker_ =
-        fake_android_sms_pairing_state_tracker.get();
+        std::make_unique<FakeAndroidSmsPairingStateTracker>();
+    fake_android_sms_pairing_state_tracker_->SetPairingComplete(true);
 
     manager_ = FeatureStateManagerImpl::Factory::Get()->BuildInstance(
         test_pref_service_.get(), fake_host_status_provider_.get(),
         fake_device_sync_client_.get(),
-        std::move(fake_android_sms_pairing_state_tracker));
+        fake_android_sms_pairing_state_tracker_.get());
 
     fake_observer_ = std::make_unique<FakeFeatureStateManagerObserver>();
     manager_->AddObserver(fake_observer_.get());
@@ -198,7 +196,8 @@
       test_pref_service_;
   std::unique_ptr<FakeHostStatusProvider> fake_host_status_provider_;
   std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
-  FakeAndroidSmsPairingStateTracker* fake_android_sms_pairing_state_tracker_;
+  std::unique_ptr<FakeAndroidSmsPairingStateTracker>
+      fake_android_sms_pairing_state_tracker_;
 
   std::unique_ptr<FakeFeatureStateManagerObserver> fake_observer_;
 
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
index 95bbd09..1ba32fc 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
@@ -78,15 +78,13 @@
     device_sync::DeviceSyncClient* device_sync_client,
     AuthTokenValidator* auth_token_validator,
     OobeCompletionTracker* oobe_completion_tracker,
-    std::unique_ptr<AndroidSmsAppHelperDelegate>
-        android_sms_app_helper_delegate,
-    std::unique_ptr<AndroidSmsPairingStateTracker>
-        android_sms_pairing_state_tracker,
+    AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
+    AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
     const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider) {
   return base::WrapUnique(new MultiDeviceSetupImpl(
       pref_service, device_sync_client, auth_token_validator,
-      oobe_completion_tracker, std::move(android_sms_app_helper_delegate),
-      std::move(android_sms_pairing_state_tracker), gcm_device_info_provider));
+      oobe_completion_tracker, android_sms_app_helper_delegate,
+      android_sms_pairing_state_tracker, gcm_device_info_provider));
 }
 
 MultiDeviceSetupImpl::MultiDeviceSetupImpl(
@@ -94,10 +92,8 @@
     device_sync::DeviceSyncClient* device_sync_client,
     AuthTokenValidator* auth_token_validator,
     OobeCompletionTracker* oobe_completion_tracker,
-    std::unique_ptr<AndroidSmsAppHelperDelegate>
-        android_sms_app_helper_delegate,
-    std::unique_ptr<AndroidSmsPairingStateTracker>
-        android_sms_pairing_state_tracker,
+    AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
+    AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
     const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider)
     : eligible_host_devices_provider_(
           EligibleHostDevicesProviderImpl::Factory::Get()->BuildInstance(
@@ -127,7 +123,7 @@
               pref_service,
               host_status_provider_.get(),
               device_sync_client,
-              std::move(android_sms_pairing_state_tracker))),
+              android_sms_pairing_state_tracker)),
       host_device_timestamp_manager_(
           HostDeviceTimestampManagerImpl::Factory::Get()->BuildInstance(
               host_status_provider_.get(),
@@ -144,10 +140,12 @@
           device_sync_client,
           gcm_device_info_provider)),
       android_sms_app_installing_host_observer_(
-          AndroidSmsAppInstallingStatusObserver::Factory::Get()->BuildInstance(
-              host_status_provider_.get(),
-              feature_state_manager_.get(),
-              std::move(android_sms_app_helper_delegate))),
+          android_sms_app_helper_delegate
+              ? AndroidSmsAppInstallingStatusObserver::Factory::Get()
+                    ->BuildInstance(host_status_provider_.get(),
+                                    feature_state_manager_.get(),
+                                    android_sms_app_helper_delegate)
+              : nullptr),
       auth_token_validator_(auth_token_validator) {
   host_status_provider_->AddObserver(this);
   feature_state_manager_->AddObserver(this);
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl.h b/chromeos/services/multidevice_setup/multidevice_setup_impl.h
index 5576566..ce9662a 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_impl.h
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl.h
@@ -56,10 +56,8 @@
         device_sync::DeviceSyncClient* device_sync_client,
         AuthTokenValidator* auth_token_validator,
         OobeCompletionTracker* oobe_completion_tracker,
-        std::unique_ptr<AndroidSmsAppHelperDelegate>
-            android_sms_app_helper_delegate,
-        std::unique_ptr<AndroidSmsPairingStateTracker>
-            android_sms_pairing_state_tracker,
+        AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
+        AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
         const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider);
 
    private:
@@ -76,10 +74,8 @@
       device_sync::DeviceSyncClient* device_sync_client,
       AuthTokenValidator* auth_token_validator,
       OobeCompletionTracker* oobe_completion_tracker,
-      std::unique_ptr<AndroidSmsAppHelperDelegate>
-          android_sms_app_helper_delegate,
-      std::unique_ptr<AndroidSmsPairingStateTracker>
-          android_sms_pairing_state_tracker,
+      AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
+      AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
       const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider);
 
   // mojom::MultiDeviceSetup:
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc b/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
index d4cdeda..3efe5061 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
@@ -297,15 +297,15 @@
       PrefService* pref_service,
       HostStatusProvider* host_status_provider,
       device_sync::DeviceSyncClient* device_sync_client,
-      std::unique_ptr<AndroidSmsPairingStateTracker>
-          android_sms_pairing_state_tracker) override {
+      AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker)
+      override {
     EXPECT_FALSE(instance_);
     EXPECT_EQ(expected_testing_pref_service_, pref_service);
     EXPECT_EQ(fake_host_status_provider_factory_->instance(),
               host_status_provider);
     EXPECT_EQ(expected_device_sync_client_, device_sync_client);
     EXPECT_EQ(expected_android_sms_pairing_state_tracker_,
-              android_sms_pairing_state_tracker.get());
+              android_sms_pairing_state_tracker);
 
     auto instance = std::make_unique<FakeFeatureStateManager>();
     instance_ = instance.get();
@@ -462,14 +462,13 @@
   std::unique_ptr<AndroidSmsAppInstallingStatusObserver> BuildInstance(
       HostStatusProvider* host_status_provider,
       FeatureStateManager* feature_state_manager,
-      std::unique_ptr<AndroidSmsAppHelperDelegate>
-          android_sms_app_helper_delegate) override {
+      AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate) override {
     EXPECT_EQ(fake_host_status_provider_factory_->instance(),
               host_status_provider);
     EXPECT_EQ(fake_feature_state_manager_factory_->instance(),
               feature_state_manager);
     EXPECT_EQ(expected_android_sms_app_helper_delegate_,
-              android_sms_app_helper_delegate.get());
+              android_sms_app_helper_delegate);
     // Only check inputs and return nullptr. We do not want to trigger the
     // AndroidSmsAppInstallingStatusObserver logic in these unit tests.
     return nullptr;
@@ -502,13 +501,11 @@
 
     fake_oobe_completion_tracker_ = std::make_unique<OobeCompletionTracker>();
 
-    auto fake_android_sms_app_helper_delegate =
+    fake_android_sms_app_helper_delegate_ =
         std::make_unique<FakeAndroidSmsAppHelperDelegate>();
 
-    auto fake_android_sms_pairing_state_tracker =
-        std::make_unique<FakeAndroidSmsPairingStateTracker>();
     fake_android_sms_pairing_state_tracker_ =
-        fake_android_sms_pairing_state_tracker.get();
+        std::make_unique<FakeAndroidSmsPairingStateTracker>();
 
     fake_gcm_device_info_provider_ =
         std::make_unique<device_sync::FakeGcmDeviceInfoProvider>(
@@ -552,7 +549,7 @@
         std::make_unique<FakeFeatureStateManagerFactory>(
             test_pref_service_.get(), fake_host_status_provider_factory_.get(),
             fake_device_sync_client_.get(),
-            fake_android_sms_pairing_state_tracker_);
+            fake_android_sms_pairing_state_tracker_.get());
     FeatureStateManagerImpl::Factory::SetFactoryForTesting(
         fake_feature_state_manager_factory_.get());
 
@@ -581,15 +578,15 @@
         std::make_unique<FakeAndroidSmsAppInstallingStatusObserverFactory>(
             fake_host_status_provider_factory_.get(),
             fake_feature_state_manager_factory_.get(),
-            fake_android_sms_app_helper_delegate.get());
+            fake_android_sms_app_helper_delegate_.get());
     AndroidSmsAppInstallingStatusObserver::Factory::SetFactoryForTesting(
         fake_android_sms_app_installing_status_observer_factory_.get());
 
     multidevice_setup_ = MultiDeviceSetupImpl::Factory::Get()->BuildInstance(
         test_pref_service_.get(), fake_device_sync_client_.get(),
         fake_auth_token_validator_.get(), fake_oobe_completion_tracker_.get(),
-        std::move(fake_android_sms_app_helper_delegate),
-        std::move(fake_android_sms_pairing_state_tracker),
+        fake_android_sms_app_helper_delegate_.get(),
+        fake_android_sms_pairing_state_tracker_.get(),
         fake_gcm_device_info_provider_.get());
   }
 
@@ -890,7 +887,10 @@
   std::unique_ptr<FakeDeviceReenrollerFactory> fake_device_reenroller_factory_;
   std::unique_ptr<FakeAndroidSmsAppInstallingStatusObserverFactory>
       fake_android_sms_app_installing_status_observer_factory_;
-  FakeAndroidSmsPairingStateTracker* fake_android_sms_pairing_state_tracker_;
+  std::unique_ptr<FakeAndroidSmsAppHelperDelegate>
+      fake_android_sms_app_helper_delegate_;
+  std::unique_ptr<FakeAndroidSmsPairingStateTracker>
+      fake_android_sms_pairing_state_tracker_;
 
   std::unique_ptr<FakeAccountStatusChangeDelegate>
       fake_account_status_change_delegate_;
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc b/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
index d348f6c..86e581dc 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_initializer.cc
@@ -44,15 +44,13 @@
     device_sync::DeviceSyncClient* device_sync_client,
     AuthTokenValidator* auth_token_validator,
     OobeCompletionTracker* oobe_completion_tracker,
-    std::unique_ptr<AndroidSmsAppHelperDelegate>
-        android_sms_app_helper_delegate,
-    std::unique_ptr<AndroidSmsPairingStateTracker>
-        android_sms_pairing_state_tracker,
+    AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
+    AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
     const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider) {
   return base::WrapUnique(new MultiDeviceSetupInitializer(
       pref_service, device_sync_client, auth_token_validator,
-      oobe_completion_tracker, std::move(android_sms_app_helper_delegate),
-      std::move(android_sms_pairing_state_tracker), gcm_device_info_provider));
+      oobe_completion_tracker, android_sms_app_helper_delegate,
+      android_sms_pairing_state_tracker, gcm_device_info_provider));
 }
 
 MultiDeviceSetupInitializer::SetHostDeviceArgs::SetHostDeviceArgs(
@@ -75,19 +73,15 @@
     device_sync::DeviceSyncClient* device_sync_client,
     AuthTokenValidator* auth_token_validator,
     OobeCompletionTracker* oobe_completion_tracker,
-    std::unique_ptr<AndroidSmsAppHelperDelegate>
-        android_sms_app_helper_delegate,
-    std::unique_ptr<AndroidSmsPairingStateTracker>
-        android_sms_pairing_state_tracker,
+    AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
+    AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
     const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider)
     : pref_service_(pref_service),
       device_sync_client_(device_sync_client),
       auth_token_validator_(auth_token_validator),
       oobe_completion_tracker_(oobe_completion_tracker),
-      android_sms_app_helper_delegate_(
-          std::move(android_sms_app_helper_delegate)),
-      android_sms_pairing_state_tracker_(
-          std::move(android_sms_pairing_state_tracker)),
+      android_sms_app_helper_delegate_(android_sms_app_helper_delegate),
+      android_sms_pairing_state_tracker_(android_sms_pairing_state_tracker),
       gcm_device_info_provider_(gcm_device_info_provider) {
   // If |device_sync_client_| is null, this interface cannot perform its tasks.
   if (!device_sync_client_)
@@ -272,8 +266,8 @@
 
   multidevice_setup_impl_ = MultiDeviceSetupImpl::Factory::Get()->BuildInstance(
       pref_service_, device_sync_client_, auth_token_validator_,
-      oobe_completion_tracker_, std::move(android_sms_app_helper_delegate_),
-      std::move(android_sms_pairing_state_tracker_), gcm_device_info_provider_);
+      oobe_completion_tracker_, android_sms_app_helper_delegate_,
+      android_sms_pairing_state_tracker_, gcm_device_info_provider_);
 
   if (pending_delegate_) {
     multidevice_setup_impl_->SetAccountStatusChangeDelegate(
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_initializer.h b/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
index a0fd550..92834cf6 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
+++ b/chromeos/services/multidevice_setup/multidevice_setup_initializer.h
@@ -46,10 +46,8 @@
         device_sync::DeviceSyncClient* device_sync_client,
         AuthTokenValidator* auth_token_validator,
         OobeCompletionTracker* oobe_completion_tracker,
-        std::unique_ptr<AndroidSmsAppHelperDelegate>
-            android_sms_app_helper_delegate,
-        std::unique_ptr<AndroidSmsPairingStateTracker>
-            android_sms_pairing_state_tracker,
+        AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
+        AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
         const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider);
 
    private:
@@ -84,10 +82,8 @@
       device_sync::DeviceSyncClient* device_sync_client,
       AuthTokenValidator* auth_token_validator,
       OobeCompletionTracker* oobe_completion_tracker,
-      std::unique_ptr<AndroidSmsAppHelperDelegate>
-          android_sms_app_helper_delegate,
-      std::unique_ptr<AndroidSmsPairingStateTracker>
-          android_sms_pairing_state_tracker,
+      AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
+      AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
       const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider);
 
   // mojom::MultiDeviceSetup:
@@ -127,9 +123,8 @@
   device_sync::DeviceSyncClient* device_sync_client_;
   AuthTokenValidator* auth_token_validator_;
   OobeCompletionTracker* oobe_completion_tracker_;
-  std::unique_ptr<AndroidSmsAppHelperDelegate> android_sms_app_helper_delegate_;
-  std::unique_ptr<AndroidSmsPairingStateTracker>
-      android_sms_pairing_state_tracker_;
+  AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate_;
+  AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker_;
   const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider_;
 
   std::unique_ptr<MultiDeviceSetupBase> multidevice_setup_impl_;
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service.cc b/chromeos/services/multidevice_setup/multidevice_setup_service.cc
index b800d409..91544de 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_service.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_service.cc
@@ -39,10 +39,8 @@
     device_sync::DeviceSyncClient* device_sync_client,
     AuthTokenValidator* auth_token_validator,
     OobeCompletionTracker* oobe_completion_tracker,
-    std::unique_ptr<AndroidSmsAppHelperDelegate>
-        android_sms_app_helper_delegate,
-    std::unique_ptr<AndroidSmsPairingStateTracker>
-        android_sms_pairing_state_tracker,
+    AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
+    AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
     const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider)
     : service_binding_(this, std::move(request)),
       multidevice_setup_(
@@ -51,8 +49,8 @@
               device_sync_client,
               auth_token_validator,
               oobe_completion_tracker,
-              std::move(android_sms_app_helper_delegate),
-              std::move(android_sms_pairing_state_tracker),
+              android_sms_app_helper_delegate,
+              android_sms_pairing_state_tracker,
               gcm_device_info_provider)),
       privileged_host_device_setter_(
           PrivilegedHostDeviceSetterImpl::Factory::Get()->BuildInstance(
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service.h b/chromeos/services/multidevice_setup/multidevice_setup_service.h
index 06f6749..1fdefc0 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_service.h
+++ b/chromeos/services/multidevice_setup/multidevice_setup_service.h
@@ -43,10 +43,8 @@
       device_sync::DeviceSyncClient* device_sync_client,
       AuthTokenValidator* auth_token_validator,
       OobeCompletionTracker* oobe_completion_tracker,
-      std::unique_ptr<AndroidSmsAppHelperDelegate>
-          android_sms_app_helper_delegate,
-      std::unique_ptr<AndroidSmsPairingStateTracker>
-          android_sms_pairing_state_tracker,
+      AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
+      AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
       const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider);
   ~MultiDeviceSetupService() override;
 
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc b/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
index 01cec71e..58ba6a5 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
@@ -66,10 +66,8 @@
       device_sync::DeviceSyncClient* device_sync_client,
       AuthTokenValidator* auth_token_validator,
       OobeCompletionTracker* oobe_completion_tracker,
-      std::unique_ptr<AndroidSmsAppHelperDelegate>
-          android_sms_app_helper_delegate,
-      std::unique_ptr<AndroidSmsPairingStateTracker>
-          android_sms_pairing_state_tracker,
+      AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
+      AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
       const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider)
       override {
     EXPECT_FALSE(instance_);
@@ -78,9 +76,9 @@
     EXPECT_EQ(expected_auth_token_validator_, auth_token_validator);
     EXPECT_EQ(expected_oobe_completion_tracker_, oobe_completion_tracker);
     EXPECT_EQ(expected_android_sms_app_helper_delegate_,
-              android_sms_app_helper_delegate.get());
+              android_sms_app_helper_delegate);
     EXPECT_EQ(expected_android_sms_pairing_state_tracker_,
-              android_sms_pairing_state_tracker.get());
+              android_sms_pairing_state_tracker);
     EXPECT_EQ(expected_gcm_device_info_provider_, gcm_device_info_provider);
 
     auto instance = std::make_unique<FakeMultiDeviceSetup>();
@@ -120,14 +118,10 @@
         std::make_unique<device_sync::FakeDeviceSyncClient>();
     fake_auth_token_validator_ = std::make_unique<FakeAuthTokenValidator>();
     fake_oobe_completion_tracker_ = std::make_unique<OobeCompletionTracker>();
-    auto fake_android_sms_app_helper_delegate =
-        std::make_unique<FakeAndroidSmsAppHelperDelegate>();
     fake_android_sms_app_helper_delegate_ =
-        fake_android_sms_app_helper_delegate.get();
-    auto fake_android_sms_pairing_state_tracker =
-        std::make_unique<FakeAndroidSmsPairingStateTracker>();
+        std::make_unique<FakeAndroidSmsAppHelperDelegate>();
     fake_android_sms_pairing_state_tracker_ =
-        fake_android_sms_pairing_state_tracker.get();
+        std::make_unique<FakeAndroidSmsPairingStateTracker>();
     fake_gcm_device_info_provider_ =
         std::make_unique<device_sync::FakeGcmDeviceInfoProvider>(
             cryptauth::GcmDeviceInfo());
@@ -137,8 +131,8 @@
             test_pref_service_.get(), fake_device_sync_client_.get(),
             fake_auth_token_validator_.get(),
             fake_oobe_completion_tracker_.get(),
-            fake_android_sms_app_helper_delegate_,
-            fake_android_sms_pairing_state_tracker_,
+            fake_android_sms_app_helper_delegate_.get(),
+            fake_android_sms_pairing_state_tracker_.get(),
             fake_gcm_device_info_provider_.get());
     MultiDeviceSetupImpl::Factory::SetFactoryForTesting(
         fake_multidevice_setup_factory_.get());
@@ -147,8 +141,8 @@
         connector_factory_.RegisterInstance(mojom::kServiceName),
         test_pref_service_.get(), fake_device_sync_client_.get(),
         fake_auth_token_validator_.get(), fake_oobe_completion_tracker_.get(),
-        std::move(fake_android_sms_app_helper_delegate),
-        std::move(fake_android_sms_pairing_state_tracker),
+        fake_android_sms_app_helper_delegate_.get(),
+        fake_android_sms_pairing_state_tracker_.get(),
         fake_gcm_device_info_provider_.get());
 
     auto* connector = connector_factory_.GetDefaultConnector();
@@ -213,8 +207,10 @@
   std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
   std::unique_ptr<FakeAuthTokenValidator> fake_auth_token_validator_;
   std::unique_ptr<OobeCompletionTracker> fake_oobe_completion_tracker_;
-  FakeAndroidSmsAppHelperDelegate* fake_android_sms_app_helper_delegate_;
-  FakeAndroidSmsPairingStateTracker* fake_android_sms_pairing_state_tracker_;
+  std::unique_ptr<FakeAndroidSmsAppHelperDelegate>
+      fake_android_sms_app_helper_delegate_;
+  std::unique_ptr<FakeAndroidSmsPairingStateTracker>
+      fake_android_sms_pairing_state_tracker_;
   std::unique_ptr<device_sync::FakeGcmDeviceInfoProvider>
       fake_gcm_device_info_provider_;
 
diff --git a/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.cc b/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.cc
index e67cf97..d32d0ec 100644
--- a/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.cc
+++ b/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.cc
@@ -16,30 +16,18 @@
 
 void FakeAndroidSmsAppHelperDelegate::SetUpAndroidSmsApp() {
   // TODO(jlklein): Add error simulation when error codes are added to the API.
-  has_installed_ = true;
+  has_installed_app_ = true;
   is_default_to_persist_cookie_set_ = true;
 }
 
 void FakeAndroidSmsAppHelperDelegate::SetUpAndLaunchAndroidSmsApp() {
   SetUpAndroidSmsApp();
-  has_launched_ = true;
-}
-
-bool FakeAndroidSmsAppHelperDelegate::HasInstalledApp() {
-  return has_installed_;
-}
-
-bool FakeAndroidSmsAppHelperDelegate::HasLaunchedApp() {
-  return has_launched_;
-}
-
-bool FakeAndroidSmsAppHelperDelegate::IsDefaultToPersistCookieSet() {
-  return is_default_to_persist_cookie_set_;
+  has_launched_app_ = true;
 }
 
 void FakeAndroidSmsAppHelperDelegate::Reset() {
-  has_installed_ = false;
-  has_launched_ = false;
+  has_installed_app_ = false;
+  has_launched_app_ = false;
   is_default_to_persist_cookie_set_ = false;
 }
 
diff --git a/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.h b/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.h
index d36e822..f3cea8a 100644
--- a/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.h
+++ b/chromeos/services/multidevice_setup/public/cpp/fake_android_sms_app_helper_delegate.h
@@ -9,31 +9,38 @@
 #include "chromeos/services/multidevice_setup/public/cpp/android_sms_app_helper_delegate.h"
 
 namespace chromeos {
+
 namespace multidevice_setup {
 
 class FakeAndroidSmsAppHelperDelegate : public AndroidSmsAppHelperDelegate {
  public:
   FakeAndroidSmsAppHelperDelegate();
   ~FakeAndroidSmsAppHelperDelegate() override;
-  bool HasInstalledApp();
-  bool HasLaunchedApp();
-  bool IsDefaultToPersistCookieSet();
+
+  bool has_installed_app() const { return has_installed_app_; }
+  bool has_launched_app() const { return has_launched_app_; }
+  bool is_default_to_persist_cookie_set() const {
+    return is_default_to_persist_cookie_set_;
+  }
+
+  // Sets all booleans representing recorded actions to false.
   void Reset();
 
+ private:
   // AndroidSmsAppHelperDelegate:
   void SetUpAndroidSmsApp() override;
   void SetUpAndLaunchAndroidSmsApp() override;
   void TearDownAndroidSmsApp() override;
 
- private:
-  bool has_installed_ = false;
-  bool has_launched_ = false;
+  bool has_installed_app_ = false;
+  bool has_launched_app_ = false;
   bool is_default_to_persist_cookie_set_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(FakeAndroidSmsAppHelperDelegate);
 };
 
 }  // namespace multidevice_setup
+
 }  // namespace chromeos
 
 #endif  // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_PUBLIC_CPP_FAKE_ANDROID_SMS_APP_HELPER_DELEGATE_H_
diff --git a/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc b/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
index e403cc9c..027b3920 100644
--- a/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
+++ b/chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
@@ -47,10 +47,8 @@
       device_sync::DeviceSyncClient* device_sync_client,
       AuthTokenValidator* auth_token_validator,
       OobeCompletionTracker* oobe_completion_tracker,
-      std::unique_ptr<AndroidSmsAppHelperDelegate>
-          android_sms_app_helper_delegate,
-      std::unique_ptr<AndroidSmsPairingStateTracker>
-          android_sms_pairing_state_tracker,
+      AndroidSmsAppHelperDelegate* android_sms_app_helper_delegate,
+      AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
       const device_sync::GcmDeviceInfoProvider* gcm_device_info_provider)
       override {
     EXPECT_TRUE(fake_multidevice_setup_);
diff --git a/components/data_use_measurement/core/data_use_measurement.cc b/components/data_use_measurement/core/data_use_measurement.cc
index 89b85e8..89e2b90e0 100644
--- a/components/data_use_measurement/core/data_use_measurement.cc
+++ b/components/data_use_measurement/core/data_use_measurement.cc
@@ -210,6 +210,7 @@
 
 void DataUseMeasurement::RecordTrafficSizeMetric(bool is_user_traffic,
                                                  bool is_downstream,
+                                                 bool is_tab_visible,
                                                  int64_t bytes) {
   RecordUMAHistogramCount(
       GetHistogramName(is_user_traffic ? "DataUse.TrafficSize.User"
@@ -217,6 +218,9 @@
                        is_downstream ? DOWNSTREAM : UPSTREAM, CurrentAppState(),
                        IsCurrentNetworkCellular()),
       bytes);
+  if (is_user_traffic)
+    RecordTabStateHistogram(is_downstream ? DOWNSTREAM : UPSTREAM,
+                            CurrentAppState(), is_tab_visible, bytes);
 }
 
 void DataUseMeasurement::ReportDataUseUMA(const net::URLRequest& request,
@@ -239,8 +243,6 @@
   if (attached_service_data && old_app_state != new_app_state)
     attached_service_data->set_app_state(CurrentAppState());
 
-  RecordTrafficSizeMetric(is_user_traffic, dir == DOWNSTREAM, bytes);
-
 #if defined(OS_ANDROID)
   if (dir == DOWNSTREAM && CurrentAppState() == DataUseUserData::BACKGROUND) {
     DCHECK(!last_app_background_time_.is_null());
@@ -264,13 +266,12 @@
   bool is_tab_visible = false;
 
   if (is_user_traffic) {
-    const DataUseRecorder* recorder = ascriber_->GetDataUseRecorder(request);
-    if (recorder) {
+    if (const auto* recorder = ascriber_->GetDataUseRecorder(request))
       is_tab_visible = recorder->is_visible();
-      RecordTabStateHistogram(dir, new_app_state, recorder->is_visible(),
-                              bytes);
-    }
   }
+  RecordTrafficSizeMetric(is_user_traffic, dir == DOWNSTREAM, is_tab_visible,
+                          bytes);
+
   if (attached_service_data && dir == DOWNSTREAM &&
       new_app_state != DataUseUserData::UNKNOWN) {
     RecordContentTypeHistogram(attached_service_data->content_type(),
diff --git a/components/data_use_measurement/core/data_use_measurement.h b/components/data_use_measurement/core/data_use_measurement.h
index 4db7bcd..2e9daf1 100644
--- a/components/data_use_measurement/core/data_use_measurement.h
+++ b/components/data_use_measurement/core/data_use_measurement.h
@@ -112,6 +112,7 @@
 
   void RecordTrafficSizeMetric(bool is_user_traffic,
                                bool is_downstream,
+                               bool is_tab_visible,
                                int64_t bytes);
 
  protected:
diff --git a/components/download/BUILD.gn b/components/download/BUILD.gn
index d022618..a2a5342a 100644
--- a/components/download/BUILD.gn
+++ b/components/download/BUILD.gn
@@ -12,7 +12,7 @@
       "//components/download/database:unit_tests",
       "//components/download/internal/background_service:unit_tests",
       "//components/download/internal/common:unit_tests",
-      "//components/download/public/background_service:unit_tests",
+      "//components/download/public/task:unit_tests",
       "//components/download/quarantine:unit_tests",
     ]
   }
diff --git a/components/download/content/factory/download_service_factory.cc b/components/download/content/factory/download_service_factory.cc
index 5dbbeceaf..d691b40 100644
--- a/components/download/content/factory/download_service_factory.cc
+++ b/components/download/content/factory/download_service_factory.cc
@@ -14,7 +14,6 @@
 #include "components/download/internal/background_service/download_service_impl.h"
 #include "components/download/internal/background_service/download_store.h"
 #include "components/download/internal/background_service/empty_file_monitor.h"
-#include "components/download/internal/background_service/empty_task_scheduler.h"
 #include "components/download/internal/background_service/file_monitor_impl.h"
 #include "components/download/internal/background_service/in_memory_download_driver.h"
 #include "components/download/internal/background_service/logger_impl.h"
@@ -22,6 +21,7 @@
 #include "components/download/internal/background_service/noop_store.h"
 #include "components/download/internal/background_service/proto/entry.pb.h"
 #include "components/download/internal/background_service/scheduler/scheduler_impl.h"
+#include "components/download/public/task/empty_task_scheduler.h"
 #include "components/leveldb_proto/proto_database_impl.h"
 #include "content/public/browser/storage_partition.h"
 
diff --git a/components/download/internal/background_service/BUILD.gn b/components/download/internal/background_service/BUILD.gn
index 6034df5e..83b9120 100644
--- a/components/download/internal/background_service/BUILD.gn
+++ b/components/download/internal/background_service/BUILD.gn
@@ -38,8 +38,6 @@
     "driver_entry.h",
     "empty_file_monitor.cc",
     "empty_file_monitor.h",
-    "empty_task_scheduler.cc",
-    "empty_task_scheduler.h",
     "entry.cc",
     "entry.h",
     "entry_utils.cc",
diff --git a/components/download/internal/background_service/controller.h b/components/download/internal/background_service/controller.h
index 6c35850..9b7f53f 100644
--- a/components/download/internal/background_service/controller.h
+++ b/components/download/internal/background_service/controller.h
@@ -10,7 +10,7 @@
 #include "base/macros.h"
 #include "components/download/public/background_service/clients.h"
 #include "components/download/public/background_service/download_service.h"
-#include "components/download/public/background_service/download_task_types.h"
+#include "components/download/public/task/download_task_types.h"
 
 namespace download {
 
diff --git a/components/download/internal/background_service/controller_impl.h b/components/download/internal/background_service/controller_impl.h
index d2476d5..7105d00 100644
--- a/components/download/internal/background_service/controller_impl.h
+++ b/components/download/internal/background_service/controller_impl.h
@@ -28,7 +28,7 @@
 #include "components/download/public/background_service/client.h"
 #include "components/download/public/background_service/download_params.h"
 #include "components/download/public/background_service/navigation_monitor.h"
-#include "components/download/public/background_service/task_scheduler.h"
+#include "components/download/public/task/task_scheduler.h"
 
 namespace download {
 
diff --git a/components/download/internal/background_service/scheduler/scheduler_impl.cc b/components/download/internal/background_service/scheduler/scheduler_impl.cc
index a47fe638..4976f10e 100644
--- a/components/download/internal/background_service/scheduler/scheduler_impl.cc
+++ b/components/download/internal/background_service/scheduler/scheduler_impl.cc
@@ -9,7 +9,7 @@
 #include "components/download/internal/background_service/entry_utils.h"
 #include "components/download/internal/background_service/scheduler/device_status.h"
 #include "components/download/public/background_service/download_params.h"
-#include "components/download/public/background_service/task_scheduler.h"
+#include "components/download/public/task/task_scheduler.h"
 
 namespace download {
 
diff --git a/components/download/internal/background_service/scheduler/scheduler_impl_unittest.cc b/components/download/internal/background_service/scheduler/scheduler_impl_unittest.cc
index 10b5fafe..35eb57b 100644
--- a/components/download/internal/background_service/scheduler/scheduler_impl_unittest.cc
+++ b/components/download/internal/background_service/scheduler/scheduler_impl_unittest.cc
@@ -11,7 +11,7 @@
 #include "components/download/internal/background_service/config.h"
 #include "components/download/internal/background_service/entry.h"
 #include "components/download/internal/background_service/scheduler/device_status.h"
-#include "components/download/public/background_service/task_scheduler.h"
+#include "components/download/public/task/task_scheduler.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/components/download/internal/background_service/stats.h b/components/download/internal/background_service/stats.h
index 7a0e40f..11100e1 100644
--- a/components/download/internal/background_service/stats.h
+++ b/components/download/internal/background_service/stats.h
@@ -13,7 +13,7 @@
 #include "components/download/internal/background_service/entry.h"
 #include "components/download/public/background_service/clients.h"
 #include "components/download/public/background_service/download_params.h"
-#include "components/download/public/background_service/download_task_types.h"
+#include "components/download/public/task/download_task_types.h"
 
 namespace download {
 
diff --git a/components/download/public/background_service/BUILD.gn b/components/download/public/background_service/BUILD.gn
index 4a91c71..424ed0b 100644
--- a/components/download/public/background_service/BUILD.gn
+++ b/components/download/public/background_service/BUILD.gn
@@ -16,15 +16,11 @@
     "download_params.cc",
     "download_params.h",
     "download_service.h",
-    "download_task_types.h",
     "features.cc",
     "features.h",
     "logger.h",
     "navigation_monitor.h",
     "service_config.h",
-    "task_manager.cc",
-    "task_manager.h",
-    "task_scheduler.h",
   ]
 
   deps = [
@@ -39,41 +35,8 @@
 
   public_deps = [
     "//base",
+    "//components/download/public/task:public",
     "//net",
     "//url",
   ]
 }
-
-if (is_android) {
-  android_library("public_java") {
-    srcjar_deps = [ ":jni_enums" ]
-
-    deps = [
-      "//base:base_java",
-      "//third_party/android_deps:android_support_annotations_java",
-    ]
-  }
-
-  java_cpp_enum("jni_enums") {
-    visibility = [ "*" ]
-
-    sources = [
-      "download_task_types.h",
-    ]
-  }
-}
-
-source_set("unit_tests") {
-  testonly = true
-
-  sources = [
-    "task_manager_unittest.cc",
-  ]
-
-  deps = [
-    "//base/test:test_support",
-    "//components/download/public/background_service:public",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-}
diff --git a/components/download/public/background_service/download_service.h b/components/download/public/background_service/download_service.h
index 736666e0..01168f7 100644
--- a/components/download/public/background_service/download_service.h
+++ b/components/download/public/background_service/download_service.h
@@ -13,7 +13,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/sequenced_task_runner.h"
 #include "components/download/public/background_service/clients.h"
-#include "components/download/public/background_service/download_task_types.h"
+#include "components/download/public/task/download_task_types.h"
 #include "components/keyed_service/core/keyed_service.h"
 
 namespace download {
diff --git a/components/download/public/common/auto_resumption_handler.cc b/components/download/public/common/auto_resumption_handler.cc
index 3e6dfa5..3e01c85 100644
--- a/components/download/public/common/auto_resumption_handler.cc
+++ b/components/download/public/common/auto_resumption_handler.cc
@@ -10,7 +10,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "components/download/public/background_service/task_scheduler.h"
+#include "components/download/public/task/task_scheduler.h"
 #include "url/gurl.h"
 
 namespace {
diff --git a/components/download/public/common/auto_resumption_handler.h b/components/download/public/common/auto_resumption_handler.h
index c0bb504..0f36597 100644
--- a/components/download/public/common/auto_resumption_handler.h
+++ b/components/download/public/common/auto_resumption_handler.h
@@ -13,9 +13,9 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/download/network/network_status_listener.h"
-#include "components/download/public/background_service/task_manager.h"
 #include "components/download/public/common/download_export.h"
 #include "components/download/public/common/download_item.h"
+#include "components/download/public/task/task_manager.h"
 
 namespace download {
 
diff --git a/components/download/public/task/BUILD.gn b/components/download/public/task/BUILD.gn
new file mode 100644
index 0000000..33ccb4c
--- /dev/null
+++ b/components/download/public/task/BUILD.gn
@@ -0,0 +1,57 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+if (is_android) {
+  import("//build/config/android/config.gni")
+  import("//build/config/android/rules.gni")
+}
+
+source_set("public") {
+  sources = [
+    "download_task_types.h",
+    "empty_task_scheduler.cc",
+    "empty_task_scheduler.h",
+    "task_manager.cc",
+    "task_manager.h",
+    "task_scheduler.h",
+  ]
+
+  public_deps = [
+    "//base",
+  ]
+}
+
+if (is_android) {
+  android_library("public_java") {
+    srcjar_deps = [ ":jni_enums" ]
+
+    deps = [
+      "//base:base_java",
+      "//third_party/android_deps:android_support_annotations_java",
+    ]
+  }
+
+  java_cpp_enum("jni_enums") {
+    visibility = [ "*" ]
+
+    sources = [
+      "download_task_types.h",
+    ]
+  }
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [
+    "task_manager_unittest.cc",
+  ]
+
+  deps = [
+    ":public",
+    "//base/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/download/public/background_service/download_task_types.h b/components/download/public/task/download_task_types.h
similarity index 72%
rename from components/download/public/background_service/download_task_types.h
rename to components/download/public/task/download_task_types.h
index 5140f520..559ca79 100644
--- a/components/download/public/background_service/download_task_types.h
+++ b/components/download/public/task/download_task_types.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_DOWNLOAD_TASK_TYPES_H_
-#define COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_DOWNLOAD_TASK_TYPES_H_
+#ifndef COMPONENTS_DOWNLOAD_PUBLIC_TASK_DOWNLOAD_TASK_TYPES_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_TASK_DOWNLOAD_TASK_TYPES_H_
 
 namespace download {
 
@@ -22,4 +22,4 @@
 
 }  // namespace download
 
-#endif  // COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_DOWNLOAD_TASK_TYPES_H_
+#endif  // COMPONENTS_DOWNLOAD_PUBLIC_TASK_DOWNLOAD_TASK_TYPES_H_
diff --git a/components/download/internal/background_service/empty_task_scheduler.cc b/components/download/public/task/empty_task_scheduler.cc
similarity index 90%
rename from components/download/internal/background_service/empty_task_scheduler.cc
rename to components/download/public/task/empty_task_scheduler.cc
index 445d622..ae59a0d 100644
--- a/components/download/internal/background_service/empty_task_scheduler.cc
+++ b/components/download/public/task/empty_task_scheduler.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/download/internal/background_service/empty_task_scheduler.h"
+#include "components/download/public/task/empty_task_scheduler.h"
 
 namespace download {
 
diff --git a/components/download/internal/background_service/empty_task_scheduler.h b/components/download/public/task/empty_task_scheduler.h
similarity index 73%
rename from components/download/internal/background_service/empty_task_scheduler.h
rename to components/download/public/task/empty_task_scheduler.h
index 814ade7..4b452cc3 100644
--- a/components/download/internal/background_service/empty_task_scheduler.h
+++ b/components/download/public/task/empty_task_scheduler.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_DOWNLOAD_INTERNAL_BACKGROUND_SERVICE_EMPTY_TASK_SCHEDULER_H_
-#define COMPONENTS_DOWNLOAD_INTERNAL_BACKGROUND_SERVICE_EMPTY_TASK_SCHEDULER_H_
+#ifndef COMPONENTS_DOWNLOAD_PUBLIC_TASK_EMPTY_TASK_SCHEDULER_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_TASK_EMPTY_TASK_SCHEDULER_H_
 
 #include <stdint.h>
 
 #include "base/macros.h"
-#include "components/download/public/background_service/task_scheduler.h"
+#include "components/download/public/task/task_scheduler.h"
 
 namespace download {
 
@@ -33,4 +33,4 @@
 
 }  // namespace download
 
-#endif  // COMPONENTS_DOWNLOAD_INTERNAL_BACKGROUND_SERVICE_EMPTY_TASK_SCHEDULER_H_
+#endif  // COMPONENTS_DOWNLOAD_PUBLIC_TASK_EMPTY_TASK_SCHEDULER_H_
diff --git a/components/download/public/background_service/task_manager.cc b/components/download/public/task/task_manager.cc
similarity index 97%
rename from components/download/public/background_service/task_manager.cc
rename to components/download/public/task/task_manager.cc
index 085b77f..0ef808b 100644
--- a/components/download/public/background_service/task_manager.cc
+++ b/components/download/public/task/task_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/download/public/background_service/task_manager.h"
+#include "components/download/public/task/task_manager.h"
 
 namespace download {
 namespace {
diff --git a/components/download/public/background_service/task_manager.h b/components/download/public/task/task_manager.h
similarity index 92%
rename from components/download/public/background_service/task_manager.h
rename to components/download/public/task/task_manager.h
index c0bf734..0808541 100644
--- a/components/download/public/background_service/task_manager.h
+++ b/components/download/public/task/task_manager.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_TASK_MANAGER_H_
-#define COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_TASK_MANAGER_H_
+#ifndef COMPONENTS_DOWNLOAD_PUBLIC_TASK_TASK_MANAGER_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_TASK_TASK_MANAGER_H_
 
 #include <stdint.h>
 #include <map>
 
 #include "base/callback.h"
-#include "components/download/public/background_service/task_scheduler.h"
+#include "components/download/public/task/task_scheduler.h"
 
 namespace download {
 
@@ -89,4 +89,4 @@
 
 }  // namespace download
 
-#endif  // COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_TASK_MANAGER_H_
+#endif  // COMPONENTS_DOWNLOAD_PUBLIC_TASK_TASK_MANAGER_H_
diff --git a/components/download/public/background_service/task_manager_unittest.cc b/components/download/public/task/task_manager_unittest.cc
similarity index 98%
rename from components/download/public/background_service/task_manager_unittest.cc
rename to components/download/public/task/task_manager_unittest.cc
index d2d34e8..c4c9daa 100644
--- a/components/download/public/background_service/task_manager_unittest.cc
+++ b/components/download/public/task/task_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/download/public/background_service/task_manager.h"
+#include "components/download/public/task/task_manager.h"
 
 #include <stdint.h>
 #include <memory>
diff --git a/components/download/public/background_service/task_scheduler.h b/components/download/public/task/task_scheduler.h
similarity index 81%
rename from components/download/public/background_service/task_scheduler.h
rename to components/download/public/task/task_scheduler.h
index 409af4b..5d4af64 100644
--- a/components/download/public/background_service/task_scheduler.h
+++ b/components/download/public/task/task_scheduler.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_TASK_SCHEDULER_H_
-#define COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_TASK_SCHEDULER_H_
+#ifndef COMPONENTS_DOWNLOAD_PUBLIC_TASK_TASK_SCHEDULER_H_
+#define COMPONENTS_DOWNLOAD_PUBLIC_TASK_TASK_SCHEDULER_H_
 
 #include <stdint.h>
 
-#include "components/download/public/background_service/download_task_types.h"
+#include "components/download/public/task/download_task_types.h"
 
 namespace download {
 
@@ -36,4 +36,4 @@
 
 }  // namespace download
 
-#endif  // COMPONENTS_DOWNLOAD_PUBLIC_BACKGROUND_SERVICE_TASK_SCHEDULER_H_
+#endif  // COMPONENTS_DOWNLOAD_PUBLIC_TASK_TASK_SCHEDULER_H_
diff --git a/components/feed/core/feed_scheduler_host.cc b/components/feed/core/feed_scheduler_host.cc
index 1ebb755..68120727 100644
--- a/components/feed/core/feed_scheduler_host.cc
+++ b/components/feed/core/feed_scheduler_host.cc
@@ -328,6 +328,10 @@
   // Since there are no stored articles, a refresh will be needed soon.
   profile_prefs_->ClearPref(prefs::kLastFetchAttemptTime);
 
+  // The Feed will try to drop any outstanding refresh request, so we should
+  // stop tracking one as well.
+  tracking_oustanding_request_ = false;
+
   if (suppress_refreshes) {
     // Due to privacy, we should not fetch for a while (unless the user
     // explicitly asks for new suggestions) to give sync the time to propagate
diff --git a/components/feed/core/feed_scheduler_host_unittest.cc b/components/feed/core/feed_scheduler_host_unittest.cc
index dee2cb5..e753572 100644
--- a/components/feed/core/feed_scheduler_host_unittest.cc
+++ b/components/feed/core/feed_scheduler_host_unittest.cc
@@ -834,7 +834,7 @@
   EXPECT_EQ(1, refresh_call_count());
 }
 
-TEST_F(FeedSchedulerHostTest, OnArticlesClearedNoSupress) {
+TEST_F(FeedSchedulerHostTest, OnArticlesClearedNoSuppress) {
   EXPECT_EQ(0, refresh_call_count());
 
   scheduler()->OnArticlesCleared(/*suppress_refreshes*/ false);
@@ -852,6 +852,19 @@
   EXPECT_EQ(2, refresh_call_count());
 }
 
+TEST_F(FeedSchedulerHostTest, OnArticlesClearedIgnoresOutstanding) {
+  scheduler()->OnForegrounded();
+  EXPECT_EQ(1, refresh_call_count());
+
+  // Now that there's an outstanding request, new triggers are not acted upon.
+  scheduler()->OnForegrounded();
+  EXPECT_EQ(1, refresh_call_count());
+
+  // Clearing articles should disregard the outstanding request logic.
+  scheduler()->OnArticlesCleared(/*suppress_refreshes*/ false);
+  EXPECT_EQ(2, refresh_call_count());
+}
+
 TEST_F(FeedSchedulerHostTest, OustandingRequest) {
   scheduler()->OnForegrounded();
   EXPECT_EQ(1, refresh_call_count());
diff --git a/components/gwp_asan/client/sampling_allocator_shims_win.h b/components/gwp_asan/client/sampling_allocator_shims_win.h
index 710856d70..a67664fd 100644
--- a/components/gwp_asan/client/sampling_allocator_shims_win.h
+++ b/components/gwp_asan/client/sampling_allocator_shims_win.h
@@ -27,7 +27,13 @@
 }
 
 size_t TLSGetValue(const TLSKey& key) {
-  return reinterpret_cast<size_t>(::TlsGetValue(key));
+  // TlsGetValue() sets GetLastError() to ERROR_SUCCESS to differentiate between
+  // success and failure since any return value is a possible valid result.
+  // Preserve it in case it was already set.
+  int last_error = ::GetLastError();
+  size_t retval = reinterpret_cast<size_t>(::TlsGetValue(key));
+  ::SetLastError(last_error);
+  return retval;
 }
 
 void TLSSetValue(const TLSKey& key, size_t value) {
diff --git a/components/gwp_asan/crash_handler/crash_handler_unittest.cc b/components/gwp_asan/crash_handler/crash_handler_unittest.cc
index b7d657e..cf7b0bf3 100644
--- a/components/gwp_asan/crash_handler/crash_handler_unittest.cc
+++ b/components/gwp_asan/crash_handler/crash_handler_unittest.cc
@@ -80,8 +80,8 @@
   base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
   base::FilePath directory = cmd_line->GetSwitchValuePath("directory");
   CHECK(!directory.empty());
-  std::string crash_type = cmd_line->GetSwitchValueASCII("crash-type");
-  CHECK(!crash_type.empty());
+  std::string test_name = cmd_line->GetSwitchValueASCII("test-name");
+  CHECK(!test_name.empty());
 
   static char gpa_addr_buf[32];
   snprintf(gpa_addr_buf, sizeof(gpa_addr_buf), "%zx",
@@ -109,26 +109,26 @@
     return kSuccess;
   }
 
-  if (crash_type == "use-after-free") {
+  if (test_name == "UseAfterFree") {
     void* ptr = gpa->Allocate(kAllocationSize);
     gpa->Deallocate(ptr);
     *(int*)ptr = 0;
-  } else if (crash_type == "double-free") {
+  } else if (test_name == "DoubleFree") {
     void* ptr = gpa->Allocate(kAllocationSize);
     gpa->Deallocate(ptr);
     gpa->Deallocate(ptr);
-  } else if (crash_type == "underflow") {
+  } else if (test_name == "Underflow") {
     void* ptr = gpa->Allocate(kAllocationSize);
     for (size_t i = 0; i < base::GetPageSize(); i++)
       ((unsigned char*)ptr)[-i] = 0;
-  } else if (crash_type == "overflow") {
+  } else if (test_name == "Overflow") {
     void* ptr = gpa->Allocate(kAllocationSize);
     for (size_t i = 0; i <= base::GetPageSize(); i++)
       ((unsigned char*)ptr)[i] = 0;
-  } else if (crash_type == "trap") {
+  } else if (test_name == "UnrelatedException") {
     __builtin_trap();
   } else {
-    LOG(ERROR) << "Unknown crash type " << crash_type;
+    LOG(ERROR) << "Unknown test name " << test_name;
   }
 
   LOG(ERROR) << "This return should never be reached.";
@@ -136,16 +136,31 @@
 }
 
 class CrashHandlerTest : public base::MultiProcessTest {
- public:
+ protected:
+  // Launch a child process and wait for it to crash. Set |gwp_asan_found_| if a
+  // GWP-ASan data was found and if so, read it into |proto_|.
+  void SetUp() final {
+    base::ScopedTempDir database_dir;
+    ASSERT_TRUE(database_dir.CreateUniqueTempDir());
+    ASSERT_TRUE(runTestProcess(
+        database_dir.GetPath(),
+        ::testing::UnitTest::GetInstance()->current_test_info()->name()));
+
+    bool minidump_found;
+    readGwpAsanStreamFromCrash(database_dir.GetPath(), &minidump_found,
+                               &gwp_asan_found_, &proto_);
+    ASSERT_TRUE(minidump_found);
+  }
+
   // Launch a second process that installs a crashpad handler and causes an
-  // exception of type crash_type, then validate that it exited successfully.
+  // exception of type test_name, then validate that it exited successfully.
   // crashpad is initialized to write to the given database directory.
   bool runTestProcess(const base::FilePath& database_dir,
-                      const char* crash_type) {
+                      const char* test_name) {
     base::CommandLine cmd_line =
         base::GetMultiProcessTestChildBaseCommandLine();
     cmd_line.AppendSwitchPath("directory", database_dir);
-    cmd_line.AppendSwitchASCII("crash-type", crash_type);
+    cmd_line.AppendSwitchASCII("test-name", test_name);
 
     base::LaunchOptions options;
 #if defined(OS_WIN)
@@ -198,6 +213,33 @@
       }
     }
   }
+
+  void checkProto(Crash_ErrorType error_type, bool has_deallocation) {
+    EXPECT_TRUE(proto_.has_error_type());
+    EXPECT_EQ(proto_.error_type(), error_type);
+
+    EXPECT_TRUE(proto_.has_allocation_address());
+
+    EXPECT_TRUE(proto_.has_allocation_size());
+    EXPECT_EQ(proto_.allocation_size(), kAllocationSize);
+
+    EXPECT_TRUE(proto_.has_allocation());
+    EXPECT_TRUE(proto_.allocation().has_thread_id());
+    EXPECT_NE(proto_.allocation().thread_id(), base::kInvalidThreadId);
+    EXPECT_GT(proto_.allocation().stack_trace_size(), 0);
+
+    EXPECT_EQ(proto_.has_deallocation(), has_deallocation);
+    if (has_deallocation) {
+      EXPECT_TRUE(proto_.deallocation().has_thread_id());
+      EXPECT_NE(proto_.deallocation().thread_id(), base::kInvalidThreadId);
+      EXPECT_EQ(proto_.allocation().thread_id(),
+                proto_.deallocation().thread_id());
+      EXPECT_GT(proto_.deallocation().stack_trace_size(), 0);
+    }
+  }
+
+  gwp_asan::Crash proto_;
+  bool gwp_asan_found_;
 };
 
 #if defined(ADDRESS_SANITIZER) && defined(OS_WIN)
@@ -208,145 +250,27 @@
 #endif  // defined(ADDRESS_SANITIZER) && defined(OS_WIN)
 
 TEST_F(CrashHandlerTest, MAYBE_DISABLED(UseAfterFree)) {
-  base::ScopedTempDir database_dir;
-  ASSERT_TRUE(database_dir.CreateUniqueTempDir());
-
-  ASSERT_TRUE(runTestProcess(database_dir.GetPath(), "use-after-free"));
-
-  bool minidump_found, gwp_asan_found;
-  gwp_asan::Crash proto;
-  readGwpAsanStreamFromCrash(database_dir.GetPath(), &minidump_found,
-                             &gwp_asan_found, &proto);
-  ASSERT_TRUE(minidump_found);
-  ASSERT_TRUE(gwp_asan_found);
-
-  EXPECT_TRUE(proto.has_error_type());
-  EXPECT_EQ(proto.error_type(), Crash_ErrorType_USE_AFTER_FREE);
-
-  EXPECT_TRUE(proto.has_allocation_address());
-
-  EXPECT_TRUE(proto.has_allocation_size());
-  EXPECT_EQ(proto.allocation_size(), kAllocationSize);
-
-  EXPECT_TRUE(proto.has_allocation());
-  EXPECT_TRUE(proto.allocation().has_thread_id());
-  EXPECT_NE(proto.allocation().thread_id(), base::kInvalidThreadId);
-
-  EXPECT_TRUE(proto.has_deallocation());
-  EXPECT_TRUE(proto.deallocation().has_thread_id());
-  EXPECT_NE(proto.deallocation().thread_id(), base::kInvalidThreadId);
-  EXPECT_EQ(proto.allocation().thread_id(), proto.deallocation().thread_id());
-
-  EXPECT_GT(proto.allocation().stack_trace_size(), 0);
-  EXPECT_GT(proto.deallocation().stack_trace_size(), 0);
+  ASSERT_TRUE(gwp_asan_found_);
+  checkProto(Crash_ErrorType_USE_AFTER_FREE, true);
 }
 
 TEST_F(CrashHandlerTest, MAYBE_DISABLED(DoubleFree)) {
-  base::ScopedTempDir database_dir;
-  ASSERT_TRUE(database_dir.CreateUniqueTempDir());
-
-  ASSERT_TRUE(runTestProcess(database_dir.GetPath(), "double-free"));
-
-  bool minidump_found, gwp_asan_found;
-  gwp_asan::Crash proto;
-  readGwpAsanStreamFromCrash(database_dir.GetPath(), &minidump_found,
-                             &gwp_asan_found, &proto);
-  ASSERT_TRUE(minidump_found);
-  ASSERT_TRUE(gwp_asan_found);
-
-  EXPECT_TRUE(proto.has_error_type());
-  EXPECT_EQ(proto.error_type(), Crash_ErrorType_DOUBLE_FREE);
-
-  EXPECT_TRUE(proto.has_allocation_address());
-
-  EXPECT_TRUE(proto.has_allocation_size());
-  EXPECT_EQ(proto.allocation_size(), kAllocationSize);
-
-  EXPECT_TRUE(proto.has_allocation());
-  EXPECT_TRUE(proto.allocation().has_thread_id());
-  EXPECT_NE(proto.allocation().thread_id(), base::kInvalidThreadId);
-
-  EXPECT_TRUE(proto.has_deallocation());
-  EXPECT_TRUE(proto.deallocation().has_thread_id());
-  EXPECT_NE(proto.deallocation().thread_id(), base::kInvalidThreadId);
-  EXPECT_EQ(proto.allocation().thread_id(), proto.deallocation().thread_id());
-
-  EXPECT_GT(proto.allocation().stack_trace_size(), 0);
-  EXPECT_GT(proto.deallocation().stack_trace_size(), 0);
+  ASSERT_TRUE(gwp_asan_found_);
+  checkProto(Crash_ErrorType_DOUBLE_FREE, true);
 }
 
 TEST_F(CrashHandlerTest, MAYBE_DISABLED(Underflow)) {
-  base::ScopedTempDir database_dir;
-  ASSERT_TRUE(database_dir.CreateUniqueTempDir());
-
-  ASSERT_TRUE(runTestProcess(database_dir.GetPath(), "underflow"));
-
-  bool minidump_found, gwp_asan_found;
-  gwp_asan::Crash proto;
-  readGwpAsanStreamFromCrash(database_dir.GetPath(), &minidump_found,
-                             &gwp_asan_found, &proto);
-  ASSERT_TRUE(minidump_found);
-  ASSERT_TRUE(gwp_asan_found);
-
-  EXPECT_TRUE(proto.has_error_type());
-  EXPECT_EQ(proto.error_type(), Crash_ErrorType_BUFFER_UNDERFLOW);
-
-  EXPECT_TRUE(proto.has_allocation_address());
-
-  EXPECT_TRUE(proto.has_allocation_size());
-  EXPECT_EQ(proto.allocation_size(), kAllocationSize);
-
-  EXPECT_TRUE(proto.has_allocation());
-  EXPECT_TRUE(proto.allocation().has_thread_id());
-  EXPECT_NE(proto.allocation().thread_id(), base::kInvalidThreadId);
-
-  EXPECT_FALSE(proto.has_deallocation());
-
-  EXPECT_GT(proto.allocation().stack_trace_size(), 0);
+  ASSERT_TRUE(gwp_asan_found_);
+  checkProto(Crash_ErrorType_BUFFER_UNDERFLOW, false);
 }
 
 TEST_F(CrashHandlerTest, MAYBE_DISABLED(Overflow)) {
-  base::ScopedTempDir database_dir;
-  ASSERT_TRUE(database_dir.CreateUniqueTempDir());
-
-  ASSERT_TRUE(runTestProcess(database_dir.GetPath(), "overflow"));
-
-  bool minidump_found, gwp_asan_found;
-  gwp_asan::Crash proto;
-  readGwpAsanStreamFromCrash(database_dir.GetPath(), &minidump_found,
-                             &gwp_asan_found, &proto);
-  ASSERT_TRUE(minidump_found);
-  ASSERT_TRUE(gwp_asan_found);
-
-  EXPECT_TRUE(proto.has_error_type());
-  EXPECT_EQ(proto.error_type(), Crash_ErrorType_BUFFER_OVERFLOW);
-
-  EXPECT_TRUE(proto.has_allocation_address());
-
-  EXPECT_TRUE(proto.has_allocation_size());
-  EXPECT_EQ(proto.allocation_size(), kAllocationSize);
-
-  EXPECT_TRUE(proto.has_allocation());
-  EXPECT_TRUE(proto.allocation().has_thread_id());
-  EXPECT_NE(proto.allocation().thread_id(), base::kInvalidThreadId);
-
-  EXPECT_FALSE(proto.has_deallocation());
-
-  EXPECT_GT(proto.allocation().stack_trace_size(), 0);
+  ASSERT_TRUE(gwp_asan_found_);
+  checkProto(Crash_ErrorType_BUFFER_OVERFLOW, false);
 }
 
 TEST_F(CrashHandlerTest, MAYBE_DISABLED(UnrelatedException)) {
-  base::ScopedTempDir database_dir;
-  ASSERT_TRUE(database_dir.CreateUniqueTempDir());
-
-  ASSERT_TRUE(runTestProcess(database_dir.GetPath(), "trap"));
-
-  bool minidump_found, gwp_asan_found;
-  gwp_asan::Crash proto;
-  readGwpAsanStreamFromCrash(database_dir.GetPath(), &minidump_found,
-                             &gwp_asan_found, &proto);
-  ASSERT_TRUE(minidump_found);
-  ASSERT_FALSE(gwp_asan_found);
+  ASSERT_FALSE(gwp_asan_found_);
 }
 
 }  // namespace
diff --git a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiBridge.java b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiBridge.java
index 0cf35a8..2d11612 100644
--- a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiBridge.java
+++ b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiBridge.java
@@ -15,7 +15,8 @@
  */
 @JNINamespace("safe_browsing")
 public final class SafeBrowsingApiBridge {
-    private static final String TAG = "ApiBridge";
+    private static final String TAG = "SBApiBridge";
+    private static final boolean DEBUG = false;
 
     private static Class<? extends SafeBrowsingApiHandler> sHandler;
 
@@ -58,13 +59,26 @@
     }
 
     /**
+     * Get the SafetyNet ID of the device.
+     */
+    @CalledByNative
+    private static String getSafetyNetId(SafeBrowsingApiHandler handler) {
+        return handler.getSafetyNetId();
+    }
+
+    /**
      * Starts a Safe Browsing check. Must be called on the same sequence as |create|.
      */
     @CalledByNative
     private static void startUriLookup(
             SafeBrowsingApiHandler handler, long callbackId, String uri, int[] threatsOfInterest) {
+        if (DEBUG) {
+            Log.i(TAG, "Starting request: %s", uri);
+        }
         handler.startUriLookup(callbackId, uri, threatsOfInterest);
-        Log.d(TAG, "Done starting request");
+        if (DEBUG) {
+            Log.i(TAG, "Done starting request: %s", uri);
+        }
     }
 
     private static native boolean nativeAreLocalBlacklistsEnabled();
diff --git a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java
index 1e2aba6..3eb1b245 100644
--- a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java
+++ b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java
@@ -57,6 +57,14 @@
     public boolean init(Observer result, boolean enableLocalBlacklists);
 
     /**
+     * Returns the Safety Net ID of the device. Checks to make sure that the feature to report
+     * telemetry for APK downloads is enabled. This should not be called for AW.
+     *
+     * @return the Safety Net ID of the device.
+     */
+    public String getSafetyNetId();
+
+    /**
      * Start a URI-lookup to determine if it matches one of the specified threats.
      * This is called on every URL resource Chrome loads, on the same sequence as |init|.
      */
diff --git a/components/safe_browsing/android/remote_database_manager.cc b/components/safe_browsing/android/remote_database_manager.cc
index c3da901..111dd42 100644
--- a/components/safe_browsing/android/remote_database_manager.cc
+++ b/components/safe_browsing/android/remote_database_manager.cc
@@ -293,6 +293,12 @@
   return safe_browsing::ThreatSource::REMOTE;
 }
 
+std::string RemoteSafeBrowsingDatabaseManager::GetSafetyNetId() const {
+  SafeBrowsingApiHandler* api_handler = SafeBrowsingApiHandler::GetInstance();
+  DCHECK(api_handler) << "SafeBrowsingApiHandler was never constructed";
+  return api_handler->GetSafetyNetId();
+}
+
 bool RemoteSafeBrowsingDatabaseManager::IsDownloadProtectionEnabled() const {
   return false;
 }
diff --git a/components/safe_browsing/android/remote_database_manager.h b/components/safe_browsing/android/remote_database_manager.h
index 3a81e5b3..ce9948b 100644
--- a/components/safe_browsing/android/remote_database_manager.h
+++ b/components/safe_browsing/android/remote_database_manager.h
@@ -52,6 +52,7 @@
   bool MatchDownloadWhitelistString(const std::string& str) override;
   bool MatchDownloadWhitelistUrl(const GURL& url) override;
   bool MatchMalwareIP(const std::string& ip_address) override;
+  std::string GetSafetyNetId() const override;
   safe_browsing::ThreatSource GetThreatSource() const override;
   bool IsDownloadProtectionEnabled() const override;
   bool IsSupported() const override;
diff --git a/components/safe_browsing/android/remote_database_manager_unittest.cc b/components/safe_browsing/android/remote_database_manager_unittest.cc
index c73ae08..95857de 100644
--- a/components/safe_browsing/android/remote_database_manager_unittest.cc
+++ b/components/safe_browsing/android/remote_database_manager_unittest.cc
@@ -23,6 +23,7 @@
 
 class TestSafeBrowsingApiHandler : public SafeBrowsingApiHandler {
  public:
+  std::string GetSafetyNetId() const override { return ""; }
   void StartURLCheck(std::unique_ptr<URLCheckCallbackMeta> callback,
                      const GURL& url,
                      const SBThreatTypeSet& threat_types) override {}
diff --git a/components/safe_browsing/android/safe_browsing_api_handler.h b/components/safe_browsing/android/safe_browsing_api_handler.h
index b608627..04385c2 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler.h
+++ b/components/safe_browsing/android/safe_browsing_api_handler.h
@@ -29,6 +29,8 @@
                                   const ThreatMetadata& metadata)>
       URLCheckCallbackMeta;
 
+  // Returns the Safety Net ID of the device.
+  virtual std::string GetSafetyNetId() const = 0;
   // Makes Native->Java call and invokes callback when check is done.
   virtual void StartURLCheck(std::unique_ptr<URLCheckCallbackMeta> callback,
                              const GURL& url,
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc b/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
index 33a71a6e..40d85d39 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
+++ b/components/safe_browsing/android/safe_browsing_api_handler_bridge.cc
@@ -223,6 +223,28 @@
   return j_api_handler_.obj() != nullptr;
 }
 
+std::string SafeBrowsingApiHandlerBridge::GetSafetyNetId() const {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  bool feature_enabled =
+      base::FeatureList::IsEnabled(kTelemetryForApkDownloads);
+  DCHECK(feature_enabled);
+
+  if (!feature_enabled)
+    return "";
+
+  static std::string safety_net_id;
+  if (safety_net_id.empty()) {
+    JNIEnv* env = AttachCurrentThread();
+    ScopedJavaLocalRef<jstring> jsafety_net_id =
+        Java_SafeBrowsingApiBridge_getSafetyNetId(env, j_api_handler_);
+    safety_net_id =
+        jsafety_net_id ? ConvertJavaStringToUTF8(env, jsafety_net_id) : "";
+    DVLOG(1) << __FUNCTION__ << ": safety_net_id: " << safety_net_id;
+  }
+
+  return safety_net_id;
+}
+
 void SafeBrowsingApiHandlerBridge::StartURLCheck(
     std::unique_ptr<SafeBrowsingApiHandler::URLCheckCallbackMeta> callback,
     const GURL& url,
diff --git a/components/safe_browsing/android/safe_browsing_api_handler_bridge.h b/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
index 0eb2fb2..99262aa 100644
--- a/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
+++ b/components/safe_browsing/android/safe_browsing_api_handler_bridge.h
@@ -25,6 +25,8 @@
   SafeBrowsingApiHandlerBridge();
   ~SafeBrowsingApiHandlerBridge() override;
 
+  std::string GetSafetyNetId() const override;
+
   // Makes Native->Java call to check the URL against Safe Browsing lists.
   void StartURLCheck(std::unique_ptr<URLCheckCallbackMeta> callback,
                      const GURL& url,
diff --git a/components/safe_browsing/db/database_manager.cc b/components/safe_browsing/db/database_manager.cc
index 53ec1b8c..e9ddc2e 100644
--- a/components/safe_browsing/db/database_manager.cc
+++ b/components/safe_browsing/db/database_manager.cc
@@ -152,6 +152,11 @@
   update_complete_callback_list_.Notify();
 }
 
+std::string SafeBrowsingDatabaseManager::GetSafetyNetId() const {
+  NOTREACHED() << "Only implemented on Android";
+  return "";
+}
+
 SafeBrowsingDatabaseManager::SafeBrowsingApiCheck::SafeBrowsingApiCheck(
     const GURL& url,
     Client* client)
diff --git a/components/safe_browsing/db/database_manager.h b/components/safe_browsing/db/database_manager.h
index 3850b84..49f7a77 100644
--- a/components/safe_browsing/db/database_manager.h
+++ b/components/safe_browsing/db/database_manager.h
@@ -196,6 +196,9 @@
   // syncs.
   virtual std::unique_ptr<StoreStateMap> GetStoreStateMap();
 
+  // Returns the Safety Net ID of the device.
+  virtual std::string GetSafetyNetId() const;
+
   // Returns the ThreatSource for this implementation.
   virtual ThreatSource GetThreatSource() const = 0;
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index fdb9272..53ebbf0 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -627,6 +627,7 @@
     sk_surface = SkSurface::MakeFromBackendRenderTarget(
         gr_context(), render_target, kTopLeft_GrSurfaceOrigin,
         kBGRA_8888_SkColorType, nullptr, &surface_props);
+    DCHECK(sk_surface);
   } else {
     auto backend = sk_surface->getBackendRenderTarget(
         SkSurface::kFlushRead_BackendHandleAccess);
diff --git a/content/browser/cache_storage/OWNERS b/content/browser/cache_storage/OWNERS
index fd09fa5d..f4041a3 100644
--- a/content/browser/cache_storage/OWNERS
+++ b/content/browser/cache_storage/OWNERS
@@ -1,6 +1,12 @@
-nhiroki@chromium.org
+# Primary
+wanderview@chromium.org
+
+# Secondary
+falken@chromium.org
 jkarlin@chromium.org
 jsbell@chromium.org
+horo@chromium.org
+nhiroki@chromium.org
 pwnall@chromium.org
 
 # TEAM: storage-dev@chromium.org
diff --git a/content/browser/dom_storage/dom_storage_context_impl_unittest.cc b/content/browser/dom_storage/dom_storage_context_impl_unittest.cc
index 0d70eb7..1b7099e 100644
--- a/content/browser/dom_storage/dom_storage_context_impl_unittest.cc
+++ b/content/browser/dom_storage/dom_storage_context_impl_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "content/browser/dom_storage/dom_storage_context_impl.h"
+
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.cc b/content/browser/dom_storage/dom_storage_context_wrapper.cc
index 603d6b2d..1c04288 100644
--- a/content/browser/dom_storage/dom_storage_context_wrapper.cc
+++ b/content/browser/dom_storage/dom_storage_context_wrapper.cc
@@ -111,7 +111,7 @@
 
 }  // namespace
 
-DOMStorageContextWrapper::DOMStorageContextWrapper(
+scoped_refptr<DOMStorageContextWrapper> DOMStorageContextWrapper::Create(
     service_manager::Connector* connector,
     const base::FilePath& profile_path,
     const base::FilePath& local_partition_path,
@@ -129,32 +129,30 @@
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
            base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
 
-  legacy_localstorage_path_ =
+  scoped_refptr<DOMStorageContextImpl> old_context_impl =
+      base::MakeRefCounted<DOMStorageContextImpl>(
+          data_path.empty() ? data_path
+                            : data_path.AppendASCII(kSessionStorageDirectory),
+          special_storage_policy,
+          new DOMStorageWorkerPoolTaskRunner(std::move(primary_sequence),
+                                             std::move(commit_sequence)));
+  auto mojo_task_runner =
+      base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO});
+
+  base::FilePath legacy_localstorage_path =
       data_path.empty() ? data_path
                         : data_path.AppendASCII(kLocalStorageDirectory);
-
-  context_ = new DOMStorageContextImpl(
-      data_path.empty() ? data_path
-                        : data_path.AppendASCII(kSessionStorageDirectory),
-      special_storage_policy,
-      new DOMStorageWorkerPoolTaskRunner(std::move(primary_sequence),
-                                         std::move(commit_sequence)));
-
-  base::FilePath storage_dir;
-  if (!profile_path.empty())
-    storage_dir = local_partition_path.AppendASCII(kLocalStorageDirectory);
-  // TODO(dmurph): Change this to a sequenced task runner after
-  // https://crbug.com/809255 is fixed.
-  mojo_task_runner_ =
-      base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO});
-  mojo_state_ = new LocalStorageContextMojo(
-      mojo_task_runner_, connector, context_->task_runner(),
-      legacy_localstorage_path_, storage_dir, special_storage_policy);
-
+  base::FilePath new_localstorage_path =
+      profile_path.empty()
+          ? base::FilePath()
+          : local_partition_path.AppendASCII(kLocalStorageDirectory);
+  LocalStorageContextMojo* mojo_local_state = new LocalStorageContextMojo(
+      mojo_task_runner, connector, old_context_impl->task_runner(),
+      legacy_localstorage_path, new_localstorage_path, special_storage_policy);
+  SessionStorageContextMojo* mojo_session_state = nullptr;
   if (base::FeatureList::IsEnabled(blink::features::kOnionSoupDOMStorage)) {
-    mojo_session_state_ = new SessionStorageContextMojo(
-        mojo_task_runner_, connector,
-
+    mojo_session_state = new SessionStorageContextMojo(
+        mojo_task_runner, connector,
 #if defined(OS_ANDROID)
         // On Android there is no support for session storage restoring, and
         // since the restoring code is responsible for database cleanup, we must
@@ -168,6 +166,22 @@
         local_partition_path, std::string(kSessionStorageDirectory));
   }
 
+  return base::WrapRefCounted(new DOMStorageContextWrapper(
+      std::move(legacy_localstorage_path), std::move(old_context_impl),
+      mojo_task_runner, mojo_local_state, mojo_session_state));
+}
+
+DOMStorageContextWrapper::DOMStorageContextWrapper(
+    base::FilePath legacy_local_storage_path,
+    scoped_refptr<DOMStorageContextImpl> context_impl,
+    scoped_refptr<base::SequencedTaskRunner> mojo_task_runner,
+    LocalStorageContextMojo* mojo_local_storage_context,
+    SessionStorageContextMojo* mojo_session_storage_context)
+    : mojo_state_(mojo_local_storage_context),
+      mojo_session_state_(mojo_session_storage_context),
+      mojo_task_runner_(std::move(mojo_task_runner)),
+      legacy_localstorage_path_(std::move(legacy_local_storage_path)),
+      context_(std::move(context_impl)) {
   memory_pressure_listener_.reset(new base::MemoryPressureListener(
       base::BindRepeating(&DOMStorageContextWrapper::OnMemoryPressure,
                           base::Unretained(this))));
@@ -365,11 +379,12 @@
 }
 
 void DOMStorageContextWrapper::Shutdown() {
-  DCHECK(context_.get());
-  mojo_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&LocalStorageContextMojo::ShutdownAndDelete,
-                                base::Unretained(mojo_state_)));
-  mojo_state_ = nullptr;
+  if (mojo_state_) {
+    mojo_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&LocalStorageContextMojo::ShutdownAndDelete,
+                                  base::Unretained(mojo_state_)));
+    mojo_state_ = nullptr;
+  }
   if (mojo_session_state_) {
     mojo_task_runner_->PostTask(
         FROM_HERE, base::BindOnce(&SessionStorageContextMojo::ShutdownAndDelete,
@@ -377,9 +392,11 @@
     mojo_session_state_ = nullptr;
   }
   memory_pressure_listener_.reset();
-  context_->task_runner()->PostShutdownBlockingTask(
-      FROM_HERE, DOMStorageTaskRunner::PRIMARY_SEQUENCE,
-      base::BindOnce(&DOMStorageContextImpl::Shutdown, context_));
+  if (context_) {
+    context_->task_runner()->PostShutdownBlockingTask(
+        FROM_HERE, DOMStorageTaskRunner::PRIMARY_SEQUENCE,
+        base::BindOnce(&DOMStorageContextImpl::Shutdown, context_));
+  }
 }
 
 void DOMStorageContextWrapper::Flush() {
@@ -428,6 +445,16 @@
     blink::mojom::SessionStorageNamespaceRequest request) {
   if (!mojo_session_state_)
     return;
+  // The bad message callback must be called on the same sequenced task runner
+  // as the binding set. It cannot be called from our own mojo task runner.
+  auto wrapped_bad_message_callback = base::BindOnce(
+      [](mojo::ReportBadMessageCallback bad_message_callback,
+         scoped_refptr<base::SequencedTaskRunner> bindings_runner,
+         const std::string& error) {
+        bindings_runner->PostTask(
+            FROM_HERE, base::BindOnce(std::move(bad_message_callback), error));
+      },
+      std::move(bad_message_callback), base::SequencedTaskRunnerHandle::Get());
   // base::Unretained is safe here, because the mojo_state_ won't be deleted
   // until a ShutdownAndDelete task has been ran on the mojo_task_runner_, and
   // as soon as that task is posted, mojo_state_ is set to null, preventing
@@ -436,7 +463,7 @@
       FROM_HERE,
       base::BindOnce(&SessionStorageContextMojo::OpenSessionStorage,
                      base::Unretained(mojo_session_state_), process_id,
-                     namespace_id, std::move(bad_message_callback),
+                     namespace_id, std::move(wrapped_bad_message_callback),
                      std::move(request)));
 }
 
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.h b/content/browser/dom_storage/dom_storage_context_wrapper.h
index dfaec1e4..ded7b75a 100644
--- a/content/browser/dom_storage/dom_storage_context_wrapper.h
+++ b/content/browser/dom_storage/dom_storage_context_wrapper.h
@@ -48,12 +48,19 @@
       public base::RefCountedThreadSafe<DOMStorageContextWrapper> {
  public:
   // If |data_path| is empty, nothing will be saved to disk.
-  DOMStorageContextWrapper(
+  static scoped_refptr<DOMStorageContextWrapper> Create(
       service_manager::Connector* connector,
-      const base::FilePath& data_path,
+      const base::FilePath& profile_path,
       const base::FilePath& local_partition_path,
       storage::SpecialStoragePolicy* special_storage_policy);
 
+  DOMStorageContextWrapper(
+      base::FilePath legacy_local_storage_path,
+      scoped_refptr<DOMStorageContextImpl> context_impl,
+      scoped_refptr<base::SequencedTaskRunner> mojo_task_runner,
+      LocalStorageContextMojo* mojo_local_storage_context,
+      SessionStorageContextMojo* mojo_session_storage_context);
+
   // DOMStorageContext implementation.
   void GetLocalStorageUsage(GetLocalStorageUsageCallback callback) override;
   void GetSessionStorageUsage(GetSessionStorageUsageCallback callback) override;
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper_unittest.cc b/content/browser/dom_storage/dom_storage_context_wrapper_unittest.cc
new file mode 100644
index 0000000..6398811
--- /dev/null
+++ b/content/browser/dom_storage/dom_storage_context_wrapper_unittest.cc
@@ -0,0 +1,85 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/dom_storage/dom_storage_context_wrapper.h"
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "content/browser/dom_storage/local_storage_context_mojo.h"
+#include "content/browser/dom_storage/session_storage_context_mojo.h"
+#include "storage/browser/test/mock_special_storage_policy.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/dom_storage/storage_area.mojom.h"
+
+namespace content {
+
+class DOMStorageContextWrapperTest : public testing::Test {
+ public:
+  DOMStorageContextWrapperTest() = default;
+
+  void SetUp() override {
+    storage_policy_ = new MockSpecialStoragePolicy();
+    fake_mojo_task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+    context_ = new DOMStorageContextWrapper(
+        /*legacy_local_storage_path=*/base::FilePath(),
+        /*context_impl=*/nullptr, fake_mojo_task_runner_,
+        /*mojo_local_storage_context=*/nullptr,
+        new SessionStorageContextMojo(
+            fake_mojo_task_runner_, /*connector=*/nullptr,
+            SessionStorageContextMojo::BackingMode::kNoDisk,
+            /*local_partition_directory=*/base::FilePath(),
+            /*leveldb_name=*/""));
+  }
+
+  void TearDown() override {
+    context_->Shutdown();
+    context_.reset();
+    fake_mojo_task_runner_->RunUntilIdle();
+    base::RunLoop().RunUntilIdle();
+  }
+
+ protected:
+  base::test::ScopedTaskEnvironment task_environment_;
+  scoped_refptr<base::TestSimpleTaskRunner> fake_mojo_task_runner_;
+  scoped_refptr<MockSpecialStoragePolicy> storage_policy_;
+  scoped_refptr<DOMStorageContextWrapper> context_;
+
+  DISALLOW_COPY_AND_ASSIGN(DOMStorageContextWrapperTest);
+};
+
+TEST_F(DOMStorageContextWrapperTest, BadMessageScheduling) {
+  blink::mojom::SessionStorageNamespacePtr ss_namespace_ptr;
+  auto request = mojo::MakeRequest(&ss_namespace_ptr);
+  bool called = false;
+  // This call is invalid because |CreateSessionNamespace| was never called on
+  // the SessionStorage context.
+  context_->OpenSessionStorage(
+      0, "nonexistant-namespace",
+      base::BindLambdaForTesting(
+          [&called](const std::string& message) { called = true; }),
+      std::move(request));
+  EXPECT_FALSE(called);
+  fake_mojo_task_runner_->RunPendingTasks();
+
+  // There should not be an error yet, as the callback has to run on
+  // 'this' task runner and not the fake one.
+  EXPECT_FALSE(called);
+
+  // Cycle the current task runner.
+  base::RunLoop loop;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                   loop.QuitClosure());
+  loop.Run();
+
+  // The callback should have run now.
+  EXPECT_TRUE(called);
+}
+
+}  // namespace content
diff --git a/content/browser/frame_host/frame_navigation_entry.cc b/content/browser/frame_host/frame_navigation_entry.cc
index 95b8e2de..d50e29d1 100644
--- a/content/browser/frame_host/frame_navigation_entry.cc
+++ b/content/browser/frame_host/frame_navigation_entry.cc
@@ -42,7 +42,7 @@
       post_id_(post_id),
       blob_url_loader_factory_(std::move(blob_url_loader_factory)) {
   if (origin)
-    origin_ = *origin;
+    committed_origin_ = *origin;
 }
 
 FrameNavigationEntry::~FrameNavigationEntry() {
@@ -52,11 +52,11 @@
   FrameNavigationEntry* copy = new FrameNavigationEntry();
 
   // Omit any fields cleared at commit time.
-  copy->UpdateEntryInternal(frame_unique_name_, item_sequence_number_,
-                            document_sequence_number_, site_instance_.get(),
-                            nullptr, url_, base::OptionalOrNullptr(origin_),
-                            referrer_, redirect_chain_, page_state_, method_,
-                            post_id_, nullptr /* blob_url_loader_factory */);
+  copy->UpdateEntry(frame_unique_name_, item_sequence_number_,
+                    document_sequence_number_, site_instance_.get(), nullptr,
+                    url_, committed_origin_, referrer_, redirect_chain_,
+                    page_state_, method_, post_id_,
+                    nullptr /* blob_url_loader_factory */);
   return copy;
 }
 
@@ -67,28 +67,7 @@
     SiteInstanceImpl* site_instance,
     scoped_refptr<SiteInstanceImpl> source_site_instance,
     const GURL& url,
-    const url::Origin& origin,
-    const Referrer& referrer,
-    const std::vector<GURL>& redirect_chain,
-    const PageState& page_state,
-    const std::string& method,
-    int64_t post_id,
-    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
-  UpdateEntryInternal(frame_unique_name, item_sequence_number,
-                      document_sequence_number, site_instance,
-                      std::move(source_site_instance), url, &origin, referrer,
-                      redirect_chain, page_state, method, post_id,
-                      std::move(blob_url_loader_factory));
-}
-
-void FrameNavigationEntry::UpdateEntryInternal(
-    const std::string& frame_unique_name,
-    int64_t item_sequence_number,
-    int64_t document_sequence_number,
-    SiteInstanceImpl* site_instance,
-    scoped_refptr<SiteInstanceImpl> source_site_instance,
-    const GURL& url,
-    const url::Origin* origin,
+    const base::Optional<url::Origin>& origin,
     const Referrer& referrer,
     const std::vector<GURL>& redirect_chain,
     const PageState& page_state,
@@ -102,16 +81,12 @@
   source_site_instance_ = std::move(source_site_instance);
   redirect_chain_ = redirect_chain;
   url_ = url;
+  committed_origin_ = origin;
   referrer_ = referrer;
   page_state_ = page_state;
   method_ = method;
   post_id_ = post_id;
   blob_url_loader_factory_ = std::move(blob_url_loader_factory);
-
-  if (origin)
-    origin_ = *origin;
-  else
-    origin_.reset();
 }
 
 void FrameNavigationEntry::set_item_sequence_number(
diff --git a/content/browser/frame_host/frame_navigation_entry.h b/content/browser/frame_host/frame_navigation_entry.h
index 5ca67d2..a65c6b2 100644
--- a/content/browser/frame_host/frame_navigation_entry.h
+++ b/content/browser/frame_host/frame_navigation_entry.h
@@ -59,7 +59,7 @@
       SiteInstanceImpl* site_instance,
       scoped_refptr<SiteInstanceImpl> source_site_instance,
       const GURL& url,
-      const url::Origin& origin,
+      const base::Optional<url::Origin>& origin,
       const Referrer& referrer,
       const std::vector<GURL>& redirect_chain,
       const PageState& page_state,
@@ -127,8 +127,12 @@
   // The origin of the document the frame has committed. It is optional, since
   // pending entries do not have an origin associated with them and the real
   // origin is set at commit time.
-  void set_origin(const url::Origin& origin) { origin_ = origin; }
-  const base::Optional<url::Origin>& origin() const { return origin_; }
+  void set_committed_origin(const url::Origin& origin) {
+    committed_origin_ = origin;
+  }
+  const base::Optional<url::Origin>& committed_origin() const {
+    return committed_origin_;
+  }
 
   // The redirect chain traversed during this frame navigation, from the initial
   // redirecting URL to the final non-redirecting current URL.
@@ -168,25 +172,6 @@
   friend class base::RefCounted<FrameNavigationEntry>;
   virtual ~FrameNavigationEntry();
 
-  // Internal version of UpdateEntry, which differs in accepting the |origin|
-  // parameter as a pointer instead of const ref. It is used internally by
-  // the Clone method, which can be called without the origin being properly
-  // initialized.
-  void UpdateEntryInternal(
-      const std::string& frame_unique_name,
-      int64_t item_sequence_number,
-      int64_t document_sequence_number,
-      SiteInstanceImpl* site_instance,
-      scoped_refptr<SiteInstanceImpl> source_site_instance,
-      const GURL& url,
-      const url::Origin* origin,
-      const Referrer& referrer,
-      const std::vector<GURL>& redirect_chain,
-      const PageState& page_state,
-      const std::string& method,
-      int64_t post_id,
-      scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory);
-
   // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
   // Add all new fields to |UpdateEntry|.
   // TODO(creis): These fields have implications for session restore.  This is
@@ -201,10 +186,10 @@
   // This member is cleared at commit time and is not persisted.
   scoped_refptr<SiteInstanceImpl> source_site_instance_;
   GURL url_;
-  // For a committed navigation, holds the origin that ultimately committed.
+  // For a committed navigation, holds the origin of the resulting document.
   // TODO(nasko): This should be possible to calculate at ReadyToCommit time
   // and verified when receiving the DidCommit IPC.
-  base::Optional<url::Origin> origin_;
+  base::Optional<url::Origin> committed_origin_;
   Referrer referrer_;
   // This is used when transferring a pending entry from one process to another.
   // We also send the main frame's redirect chain through session sync for
diff --git a/content/browser/frame_host/frame_tree_browsertest.cc b/content/browser/frame_host/frame_tree_browsertest.cc
index 3ca0d55..2f0ae26 100644
--- a/content/browser/frame_host/frame_tree_browsertest.cc
+++ b/content/browser/frame_host/frame_tree_browsertest.cc
@@ -13,6 +13,7 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/origin_util.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
@@ -435,6 +436,64 @@
   EXPECT_EQ("Hi from a.com", EvalJs(target, "document.body.innerHTML"));
 }
 
+// Tests a cross-origin navigation to a data: URL. The main frame initiates this
+// navigation on its grandchild. It should wind up in the main frame's process
+// and have precursor origin of the main frame origin.
+IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, NavigateGrandchildToDataUrl) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  WebContentsImpl* contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+
+  // The leaf node (c.com) will be navigated. Its grandparent node (a.com) will
+  // initiate the navigation.
+  FrameTreeNode* target =
+      contents->GetFrameTree()->root()->child_at(0)->child_at(0);
+  FrameTreeNode* initiator = target->parent()->parent();
+
+  // Give the target a name.
+  EXPECT_TRUE(ExecJs(target, "window.name = 'target';"));
+
+  // Navigate the target frame through the initiator frame.
+  {
+    TestFrameNavigationObserver observer(target);
+    EXPECT_TRUE(
+        ExecJs(initiator, "window.open('data:text/html,content', 'target');"));
+    observer.Wait();
+  }
+
+  url::Origin original_target_origin =
+      target->current_frame_host()->GetLastCommittedOrigin();
+  EXPECT_TRUE(original_target_origin.opaque());
+  EXPECT_EQ(original_target_origin.GetTupleOrPrecursorTupleIfOpaque(),
+            url::SchemeHostPort(main_url));
+
+  // Navigate the grandchild frame again cross-process to foo.com, then
+  // go back in session history. The origin for the data: URL must be preserved.
+  {
+    TestFrameNavigationObserver observer(target);
+    EXPECT_TRUE(ExecJs(target, JsReplace("window.location = $1",
+                                         embedded_test_server()->GetURL(
+                                             "foo.com", "/title2.html"))));
+    observer.Wait();
+  }
+  EXPECT_NE(original_target_origin,
+            target->current_frame_host()->GetLastCommittedOrigin());
+  {
+    TestFrameNavigationObserver observer(target);
+    contents->GetController().GoBack();
+    observer.Wait();
+  }
+
+  url::Origin target_origin =
+      target->current_frame_host()->GetLastCommittedOrigin();
+  EXPECT_TRUE(target_origin.opaque());
+  EXPECT_EQ(target_origin.GetTupleOrPrecursorTupleIfOpaque(),
+            url::SchemeHostPort(main_url));
+  EXPECT_EQ(target_origin, original_target_origin);
+}
+
 // Ensures that iframe with srcdoc is always put in the same origin as its
 // parent frame.
 IN_PROC_BROWSER_TEST_F(FrameTreeBrowserTest, ChildFrameWithSrcdoc) {
@@ -791,7 +850,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
-                       OriginSetOnCrossProcessNavigations) {
+                       OriginSetOnNavigations) {
   GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
 
@@ -824,12 +883,259 @@
   EXPECT_EQ(root->current_origin().Serialize() + '/',
             main_url.GetOrigin().spec());
 
-  GURL data_url("data:text/html,foo");
-  NavigateFrameToURL(root->child_at(1), data_url);
+  {
+    GURL data_url("data:text/html,foo");
+    TestNavigationObserver observer(shell()->web_contents());
+    EXPECT_TRUE(
+        ExecJs(root->child_at(1), JsReplace("window.location = $1", data_url)));
+    observer.Wait();
+  }
 
   // Navigating to a data URL should set a unique origin.  This is represented
-  // as "null" per RFC 6454.
+  // as "null" per RFC 6454.  A frame navigating itself to a data: URL does not
+  // require a process transfer, but should retain the original origin
+  // as its precursor.
   EXPECT_EQ(root->child_at(1)->current_origin().Serialize(), "null");
+  EXPECT_TRUE(root->child_at(1)->current_origin().opaque());
+  ASSERT_EQ(
+      url::SchemeHostPort(main_url),
+      root->child_at(1)->current_origin().GetTupleOrPrecursorTupleIfOpaque())
+      << "Expected the precursor origin to be preserved; should be the "
+         "initiator of a data: navigation.";
+
+  // Adding an <iframe sandbox srcdoc=> frame should result in a unique origin
+  // that is different-origin from its data: URL parent.
+  {
+    TestNavigationObserver observer(shell()->web_contents());
+
+    ASSERT_EQ(0U, root->child_at(1)->child_count());
+    EXPECT_TRUE(
+        ExecJs(root->child_at(1), JsReplace(
+                                      R"(
+                var iframe = document.createElement('iframe');
+                iframe.setAttribute('sandbox', 'allow-scripts');
+                iframe.srcdoc = $1;
+                document.body.appendChild(iframe);
+            )",
+                                      "<html><body>This sandboxed doc should "
+                                      "be different-origin.</body></html>")));
+    observer.Wait();
+    ASSERT_EQ(1U, root->child_at(1)->child_count());
+  }
+
+  url::Origin root_origin = root->current_origin();
+  url::Origin child_1 = root->child_at(1)->current_origin();
+  url::Origin child_1_0 = root->child_at(1)->child_at(0)->current_origin();
+  EXPECT_FALSE(root_origin.opaque());
+  EXPECT_TRUE(child_1.opaque());
+  EXPECT_TRUE(child_1_0.opaque());
+  EXPECT_NE(child_1, child_1_0);
+  EXPECT_EQ(url::SchemeHostPort(main_url),
+            root_origin.GetTupleOrPrecursorTupleIfOpaque());
+  EXPECT_EQ(url::SchemeHostPort(main_url),
+            child_1.GetTupleOrPrecursorTupleIfOpaque());
+  EXPECT_EQ(url::SchemeHostPort(main_url),
+            child_1_0.GetTupleOrPrecursorTupleIfOpaque());
+
+  {
+    TestNavigationObserver observer(shell()->web_contents());
+
+    ASSERT_EQ(1U, root->child_at(1)->child_count());
+    EXPECT_TRUE(
+        ExecJs(root->child_at(1), JsReplace(
+                                      R"(
+                var iframe = document.createElement('iframe');
+                iframe.srcdoc = $1;
+                document.body.appendChild(iframe);
+            )",
+                                      "<html><body>This srcdoc document should "
+                                      "be same-origin.</body></html>")));
+    observer.Wait();
+    ASSERT_EQ(2U, root->child_at(1)->child_count());
+  }
+  EXPECT_EQ(root_origin, root->current_origin());
+  EXPECT_EQ(child_1, root->child_at(1)->current_origin());
+  EXPECT_EQ(child_1_0, root->child_at(1)->child_at(0)->current_origin());
+  url::Origin child_1_1 = root->child_at(1)->child_at(1)->current_origin();
+  EXPECT_EQ(child_1, child_1_1);
+  EXPECT_NE(child_1_0, child_1_1);
+
+  {
+    TestNavigationObserver observer(shell()->web_contents());
+
+    ASSERT_EQ(2U, root->child_at(1)->child_count());
+    EXPECT_TRUE(
+        ExecJs(root->child_at(1), JsReplace(
+                                      R"(
+                var iframe = document.createElement('iframe');
+                iframe.src = 'data:text/html;base64,' + btoa($1);
+                document.body.appendChild(iframe);
+            )",
+                                      "<html><body>This data: doc should be "
+                                      "different-origin.</body></html>")));
+    observer.Wait();
+    ASSERT_EQ(3U, root->child_at(1)->child_count());
+  }
+  EXPECT_EQ(root_origin, root->current_origin());
+  EXPECT_EQ(child_1, root->child_at(1)->current_origin());
+  EXPECT_EQ(child_1_0, root->child_at(1)->child_at(0)->current_origin());
+  EXPECT_EQ(child_1_1, root->child_at(1)->child_at(1)->current_origin());
+  url::Origin child_1_2 = root->child_at(1)->child_at(2)->current_origin();
+  EXPECT_NE(child_1, child_1_2);
+  EXPECT_NE(child_1_0, child_1_2);
+  EXPECT_NE(child_1_1, child_1_2);
+  EXPECT_EQ(url::SchemeHostPort(main_url),
+            child_1_2.GetTupleOrPrecursorTupleIfOpaque());
+
+  // If the parent navigates its child to a data URL, it should transfer
+  // to the parent's process, and the precursor origin should track the
+  // parent's origin.
+  {
+    GURL data_url("data:text/html,foo2");
+    TestNavigationObserver observer(shell()->web_contents());
+    EXPECT_TRUE(ExecJs(root, JsReplace("frames[0].location = $1", data_url)));
+    observer.Wait();
+    EXPECT_EQ(data_url, root->child_at(0)->current_url());
+  }
+
+  EXPECT_EQ(root->child_at(0)->current_origin().Serialize(), "null");
+  EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
+  EXPECT_EQ(
+      url::SchemeHostPort(main_url),
+      root->child_at(0)->current_origin().GetTupleOrPrecursorTupleIfOpaque());
+  EXPECT_EQ(root->current_frame_host()->GetProcess(),
+            root->child_at(0)->current_frame_host()->GetProcess());
+}
+
+// Test to verify that a blob: URL that is created by a unique opaque origin
+// will correctly set the origin_to_commit on a session history navigation.
+IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
+                       OriginForBlobUrlsFromUniqueOpaqueOrigin) {
+  // Start off with a navigation to data: URL in the main frame. It should
+  // result in a unique opaque origin without any precursor information.
+  GURL data_url("data:text/html,foo<iframe id='child' src='" +
+                embedded_test_server()->GetURL("/title1.html").spec() +
+                "'></iframe>");
+  EXPECT_TRUE(NavigateToURL(shell(), data_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  EXPECT_TRUE(root->current_origin().opaque());
+  EXPECT_TRUE(
+      root->current_origin().GetTupleOrPrecursorTupleIfOpaque().IsInvalid());
+  EXPECT_EQ(1UL, root->child_count());
+  FrameTreeNode* child = root->child_at(0);
+
+  // Create a blob: URL and navigate the child frame to it.
+  std::string html = "<html><body>This is blob content.</body></html>";
+  std::string script = JsReplace(
+      "var blob = new Blob([$1], {type: 'text/html'});"
+      "var blob_url = URL.createObjectURL(blob);"
+      "document.getElementById('child').src = blob_url;"
+      "blob_url;",
+      html);
+  GURL blob_url;
+  {
+    TestFrameNavigationObserver observer(child);
+    blob_url = GURL(EvalJs(root, script).ExtractString());
+    observer.Wait();
+    EXPECT_EQ(blob_url, child->current_frame_host()->GetLastCommittedURL());
+  }
+
+  // We expect the frame to have committed in an opaque origin which contains
+  // the same precursor information - none.
+  url::Origin blob_origin = child->current_origin();
+  EXPECT_TRUE(blob_origin.opaque());
+  EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
+            blob_origin.GetTupleOrPrecursorTupleIfOpaque());
+
+  // Navigate the frame away to any web URL.
+  {
+    GURL url(embedded_test_server()->GetURL("/title2.html"));
+    TestFrameNavigationObserver observer(child);
+    EXPECT_TRUE(ExecJs(child, JsReplace("window.location = $1", url)));
+    observer.Wait();
+    EXPECT_EQ(url, child->current_frame_host()->GetLastCommittedURL());
+  }
+  EXPECT_FALSE(child->current_origin().opaque());
+  EXPECT_TRUE(shell()->web_contents()->GetController().CanGoBack());
+  EXPECT_EQ(3, shell()->web_contents()->GetController().GetEntryCount());
+  EXPECT_EQ(
+      2, shell()->web_contents()->GetController().GetLastCommittedEntryIndex());
+
+  // Verify the blob URL still exists in the main frame, which keeps it alive
+  // allowing a session history navigation back to succeed.
+  EXPECT_EQ(blob_url, GURL(EvalJs(root, "blob_url;").ExtractString()));
+
+  // Now navigate back in session history. It should successfully go back to
+  // the blob: URL.
+  {
+    TestFrameNavigationObserver observer(child);
+    shell()->web_contents()->GetController().GoBack();
+    observer.Wait();
+  }
+  EXPECT_EQ(blob_url, child->current_frame_host()->GetLastCommittedURL());
+  EXPECT_TRUE(child->current_origin().opaque());
+  EXPECT_EQ(blob_origin, child->current_origin());
+  EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
+            child->current_origin().GetTupleOrPrecursorTupleIfOpaque());
+}
+
+// Test to verify that about:blank iframe, which is a child of a sandboxed
+// iframe is not considered same origin, but precursor information is preserved
+// in its origin.
+IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
+                       AboutBlankSubframeInSandboxedFrame) {
+  // Start off by navigating to a page with sandboxed iframe, which allows
+  // script execution.
+  GURL main_url(
+      embedded_test_server()->GetURL("/sandboxed_main_frame_script.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  EXPECT_EQ(1UL, root->child_count());
+  FrameTreeNode* child = root->child_at(0);
+
+  // Navigate the frame to data: URL to cause it to have an opaque origin that
+  // is derived from the |main_url| origin.
+  GURL data_url("data:text/html,<html><body>foo</body></html>");
+  {
+    TestFrameNavigationObserver observer(child);
+    EXPECT_TRUE(ExecJs(root, JsReplace("frames[0].location = $1", data_url)));
+    observer.Wait();
+    EXPECT_EQ(data_url, child->current_frame_host()->GetLastCommittedURL());
+  }
+
+  // Add an about:blank iframe to the data: frame, which should not inherit the
+  // origin, but should preserve the precursor information.
+  {
+    EXPECT_TRUE(ExecJs(child,
+                       "var f = document.createElement('iframe');"
+                       "document.body.appendChild(f);"));
+  }
+  EXPECT_EQ(1UL, child->child_count());
+  FrameTreeNode* grandchild = child->child_at(0);
+
+  EXPECT_TRUE(grandchild->current_origin().opaque());
+  EXPECT_EQ(GURL(url::kAboutBlankURL),
+            grandchild->current_frame_host()->GetLastCommittedURL());
+
+  // The origin of the data: document should have precursor information matching
+  // the main frame origin.
+  EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
+            child->current_origin().GetTupleOrPrecursorTupleIfOpaque());
+
+  // The same should hold also for the about:blank subframe of the data: frame.
+  EXPECT_EQ(root->current_origin().GetTupleOrPrecursorTupleIfOpaque(),
+            grandchild->current_origin().GetTupleOrPrecursorTupleIfOpaque());
+
+  // The about:blank document should not be able to access its parent, as they
+  // are considered cross origin due to the sandbox flags on the parent.
+  EXPECT_FALSE(ExecJs(grandchild, "window.parent.foo = 'bar';"));
+  EXPECT_NE(child->current_origin(), grandchild->current_origin());
 }
 
 // Ensure that a popup opened from a sandboxed main frame inherits sandbox flags
@@ -965,73 +1271,4 @@
       FrameTreeVisualizer().DepictFrameTree(root));
 }
 
-// Test to verify that a blob: URL that is created by an unique opaque origin
-// will correctly navigate back in session history.
-IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest,
-                       OriginForBlobUrlsFromUniqueOpaqueOrigin) {
-  // Start off with a navigation to data: URL in the main frame. It should
-  // result in an unique opaque origin without any precursor information.
-  GURL data_url("data:text/html,foo<iframe id='child' src='" +
-                embedded_test_server()->GetURL("/title1.html").spec() +
-                "'></iframe>");
-  EXPECT_TRUE(NavigateToURL(shell(), data_url));
-
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetFrameTree()
-                            ->root();
-  EXPECT_TRUE(root->current_origin().opaque());
-  EXPECT_EQ(1UL, root->child_count());
-
-  // Create a blob: URL and navigate the child frame to it.
-  std::string html = "<html><body>This is blob content.</body></html>";
-  std::string script = JsReplace(
-      "var blob = new Blob([$1], {type: 'text/html'});"
-      "var blob_url = URL.createObjectURL(blob);"
-      "document.getElementById('child').src = blob_url;"
-      "blob_url",
-      html);
-  GURL blob_url;
-  {
-    TestFrameNavigationObserver observer(root->child_at(0));
-    blob_url = GURL(EvalJs(root, script).ExtractString());
-    observer.WaitForCommit();
-    EXPECT_EQ(blob_url,
-              root->child_at(0)->current_frame_host()->GetLastCommittedURL());
-  }
-
-  // We expect the frame to have committed in an opaque origin which contains
-  // the same precursor information - aka none :).
-  EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
-  url::Origin blob_origin = root->child_at(0)->current_origin();
-
-  // Navigate the frame away to any web URL.
-  {
-    GURL url(embedded_test_server()->GetURL("/title2.html"));
-    TestFrameNavigationObserver observer(root->child_at(0));
-    EXPECT_TRUE(
-        ExecJs(root->child_at(0), JsReplace("window.location = $1", url)));
-    observer.WaitForCommit();
-    EXPECT_EQ(url,
-              root->child_at(0)->current_frame_host()->GetLastCommittedURL());
-  }
-  EXPECT_FALSE(root->child_at(0)->current_origin().opaque());
-  EXPECT_TRUE(shell()->web_contents()->GetController().CanGoBack());
-  EXPECT_EQ(
-      2, shell()->web_contents()->GetController().GetLastCommittedEntryIndex());
-
-  EXPECT_EQ(blob_url, GURL(EvalJs(root, "blob_url;").ExtractString()));
-
-  // Now navigate back in session history. It should successfully go back to
-  // the blob: URL.
-  {
-    TestFrameNavigationObserver observer(root->child_at(0));
-    shell()->web_contents()->GetController().GoBack();
-    observer.WaitForCommit();
-    EXPECT_EQ(
-        1,
-        shell()->web_contents()->GetController().GetLastCommittedEntryIndex());
-  }
-  EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
-}
-
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 4010bfb..b2ca35d19 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -233,6 +233,29 @@
   return true;
 }
 
+bool DoesURLMatchOriginForNavigation(
+    const GURL& url,
+    const base::Optional<url::Origin>& origin) {
+  // If there is no origin supplied there is nothing to match. This can happen
+  // for navigations to a pending entry and therefore it should be allowed.
+  if (!origin)
+    return true;
+
+  return origin->CanBeDerivedFrom(url);
+}
+
+base::Optional<url::Origin> GetCommittedOriginForFrameEntry(
+    const FrameHostMsg_DidCommitProvisionalLoad_Params& params) {
+  // Error pages commit in an opaque origin, yet have the real URL that resulted
+  // in an error as the |params.url|. Since successful reload of an error page
+  // should commit in the correct origin, setting the opaque origin on the
+  // FrameNavigationEntry will be incorrect.
+  if (params.url_is_unreachable)
+    return base::nullopt;
+
+  return base::make_optional(params.origin);
+}
+
 bool IsValidURLForNavigation(bool is_main_frame,
                              const GURL& virtual_url,
                              const GURL& dest_url) {
@@ -1256,9 +1279,9 @@
     FrameNavigationEntry* frame_entry = new FrameNavigationEntry(
         rfh->frame_tree_node()->unique_name(), params.item_sequence_number,
         params.document_sequence_number, rfh->GetSiteInstance(), nullptr,
-        params.url, &params.origin, params.referrer, params.redirects,
-        params.page_state, params.method, params.post_id,
-        nullptr /* blob_url_loader_factory */);
+        params.url, (params.url_is_unreachable) ? nullptr : &params.origin,
+        params.referrer, params.redirects, params.page_state, params.method,
+        params.post_id, nullptr /* blob_url_loader_factory */);
 
     new_entry = GetLastCommittedEntry()->CloneAndReplace(
         frame_entry, true, rfh->frame_tree_node(),
@@ -1369,7 +1392,8 @@
   frame_entry->SetPageState(params.page_state);
   frame_entry->set_method(params.method);
   frame_entry->set_post_id(params.post_id);
-  frame_entry->set_origin(params.origin);
+  if (!params.url_is_unreachable)
+    frame_entry->set_committed_origin(params.origin);
 
   // history.pushState() is classified as a navigation to a new page, but sets
   // is_same_document to true. In this case, we already have the title and
@@ -1562,8 +1586,8 @@
   entry->AddOrUpdateFrameEntry(
       rfh->frame_tree_node(), params.item_sequence_number,
       params.document_sequence_number, rfh->GetSiteInstance(), nullptr,
-      params.url, params.origin, params.referrer, params.redirects,
-      params.page_state, params.method, params.post_id,
+      params.url, GetCommittedOriginForFrameEntry(params), params.referrer,
+      params.redirects, params.page_state, params.method, params.post_id,
       nullptr /* blob_url_loader_factory */);
 
   // The redirected to page should not inherit the favicon from the previous
@@ -1636,8 +1660,8 @@
   existing_entry->AddOrUpdateFrameEntry(
       rfh->frame_tree_node(), params.item_sequence_number,
       params.document_sequence_number, rfh->GetSiteInstance(), nullptr,
-      params.url, params.origin, params.referrer, params.redirects,
-      params.page_state, params.method, params.post_id,
+      params.url, GetCommittedOriginForFrameEntry(params), params.referrer,
+      params.redirects, params.page_state, params.method, params.post_id,
       nullptr /* blob_url_loader_factory */);
 
   DiscardNonCommittedEntries();
@@ -1668,9 +1692,9 @@
   scoped_refptr<FrameNavigationEntry> frame_entry(new FrameNavigationEntry(
       rfh->frame_tree_node()->unique_name(), params.item_sequence_number,
       params.document_sequence_number, rfh->GetSiteInstance(), nullptr,
-      params.url, &params.origin, params.referrer, params.redirects,
-      params.page_state, params.method, params.post_id,
-      nullptr /* blob_url_loader_factory */));
+      params.url, (params.url_is_unreachable) ? nullptr : &params.origin,
+      params.referrer, params.redirects, params.page_state, params.method,
+      params.post_id, nullptr /* blob_url_loader_factory */));
 
   std::unique_ptr<NavigationEntryImpl> new_entry =
       GetLastCommittedEntry()->CloneAndReplace(
@@ -1739,8 +1763,8 @@
   last_committed->AddOrUpdateFrameEntry(
       rfh->frame_tree_node(), params.item_sequence_number,
       params.document_sequence_number, rfh->GetSiteInstance(), nullptr,
-      params.url, params.origin, params.referrer, params.redirects,
-      params.page_state, params.method, params.post_id,
+      params.url, GetCommittedOriginForFrameEntry(params), params.referrer,
+      params.redirects, params.page_state, params.method, params.post_id,
       nullptr /* blob_url_loader_factory */);
 
   return send_commit_notification;
@@ -2069,10 +2093,6 @@
     scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
   FrameTreeNode* node = render_frame_host->frame_tree_node();
 
-  // TODO(nasko): Plumb through the real initiator origin and use it to
-  // compute the origin to use.
-  url::Origin origin_to_use;
-
   // Create a NavigationEntry for the transfer, without making it the pending
   // entry. Subframe transfers should have a clone of the last committed entry
   // with a FrameNavigationEntry for the target frame. Main frame transfers
@@ -2102,8 +2122,8 @@
     entry->AddOrUpdateFrameEntry(
         node, -1, -1, nullptr,
         static_cast<SiteInstanceImpl*>(source_site_instance), url,
-        origin_to_use, referrer, std::vector<GURL>(), PageState(), method, -1,
-        blob_url_loader_factory);
+        base::nullopt /* commit_origin */, referrer, std::vector<GURL>(),
+        PageState(), method, -1, blob_url_loader_factory);
   } else {
     // Main frame case.
     entry = NavigationEntryImpl::FromNavigationEntry(CreateNavigationEntry(
@@ -2137,8 +2157,8 @@
     frame_entry = new FrameNavigationEntry(
         node->unique_name(), -1, -1, nullptr,
         static_cast<SiteInstanceImpl*>(source_site_instance), url,
-        &origin_to_use, referrer, std::vector<GURL>(), PageState(), method, -1,
-        blob_url_loader_factory);
+        nullptr /* origin */, referrer, std::vector<GURL>(), PageState(),
+        method, -1, blob_url_loader_factory);
   }
 
   LoadURLParams params(url);
@@ -2727,15 +2747,11 @@
     // the target subframe.
     entry = GetLastCommittedEntry()->Clone();
 
-    // TODO(nasko): Investigate what is the proper origin to supply here
-    // or whether a valid one is required.
-    url::Origin origin;
-
     entry->AddOrUpdateFrameEntry(
         node, -1, -1, nullptr,
         static_cast<SiteInstanceImpl*>(params.source_site_instance.get()),
-        params.url, origin, params.referrer, params.redirect_chain, PageState(),
-        "GET", -1, blob_url_loader_factory);
+        params.url, base::nullopt, params.referrer, params.redirect_chain,
+        PageState(), "GET", -1, blob_url_loader_factory);
   } else {
     // Otherwise, create a pending entry for the main frame.
 
@@ -2805,6 +2821,9 @@
 
   GURL url_to_load;
   GURL virtual_url;
+  base::Optional<url::Origin> origin_to_commit =
+      frame_entry ? frame_entry->committed_origin() : base::nullopt;
+
   // For main frames, rewrite the URL if necessary and compute the virtual URL
   // that should be shown in the address bar.
   if (node->IsMainFrame()) {
@@ -2849,6 +2868,12 @@
   if (!IsValidURLForNavigation(node->IsMainFrame(), virtual_url, url_to_load))
     return nullptr;
 
+  if (!DoesURLMatchOriginForNavigation(url_to_load, origin_to_commit)) {
+    DCHECK(false) << " url:" << url_to_load
+                  << " origin:" << origin_to_commit.value();
+    return nullptr;
+  }
+
   // Determine if Previews should be used for the navigation.
   PreviewsState previews_state = PREVIEWS_UNSPECIFIED;
   if (!node->IsMainFrame()) {
@@ -2897,10 +2922,10 @@
       params.href_translate, params.input_start);
 
   CommitNavigationParams commit_params(
-      override_user_agent, params.redirect_chain, common_params.url,
-      common_params.method, params.can_load_local_resources,
-      frame_entry->page_state(), entry->GetUniqueID(),
-      false /* is_history_navigation_in_new_child */,
+      frame_entry->committed_origin(), override_user_agent,
+      params.redirect_chain, common_params.url, common_params.method,
+      params.can_load_local_resources, frame_entry->page_state(),
+      entry->GetUniqueID(), false /* is_history_navigation_in_new_child */,
       entry->GetSubframeUniqueNames(node), true /* intended_as_new_entry */,
       -1 /* pending_history_list_offset */,
       params.should_clear_history_list ? -1 : GetLastCommittedEntryIndex(),
@@ -2936,6 +2961,9 @@
     bool is_same_document_history_load,
     bool is_history_navigation_in_new_child) {
   GURL dest_url = frame_entry->url();
+  base::Optional<url::Origin> origin_to_commit =
+      frame_entry->committed_origin();
+
   Referrer dest_referrer = frame_entry->referrer();
   if (reload_type == ReloadType::ORIGINAL_REQUEST_URL &&
       entry->GetOriginalRequestURL().is_valid() && !entry->GetHasPostData()) {
@@ -2945,6 +2973,7 @@
     // case avoids issues with sending data to the wrong page.
     dest_url = entry->GetOriginalRequestURL();
     dest_referrer = Referrer();
+    origin_to_commit.reset();
   }
 
   if (auto* rfh = frame_tree_node->current_frame_host()) {
@@ -2960,6 +2989,12 @@
     return nullptr;
   }
 
+  if (!DoesURLMatchOriginForNavigation(dest_url, origin_to_commit)) {
+    DCHECK(false) << " url:" << dest_url
+                  << " origin:" << origin_to_commit.value();
+    return nullptr;
+  }
+
   // Determine if Previews should be used for the navigation.
   PreviewsState previews_state = PREVIEWS_UNSPECIFIED;
   if (!frame_tree_node->IsMainFrame()) {
@@ -3006,7 +3041,7 @@
   // Reload no longer leads to this being called for a pending NavigationEntry
   // of index -1.
   CommitNavigationParams commit_params = entry->ConstructCommitNavigationParams(
-      *frame_entry, common_params.url, common_params.method,
+      *frame_entry, common_params.url, origin_to_commit, common_params.method,
       is_history_navigation_in_new_child,
       entry->GetSubframeUniqueNames(frame_tree_node),
       GetPendingEntryIndex() == -1 /* intended_as_new_entry */,
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index 88b2490..6ae0f9df3 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -224,6 +224,9 @@
   EXPECT_EQ(history_url, entry->GetVirtualURL());
   EXPECT_EQ(history_url, entry->GetHistoryURLForDataURL());
   EXPECT_EQ(data_url1, entry->GetURL());
+  EXPECT_EQ("http://baseurl", EvalJs(shell(), "self.origin"));
+  EXPECT_EQ(url::Origin::Create(base_url),
+            shell()->web_contents()->GetMainFrame()->GetLastCommittedOrigin());
 
   // Navigate again to a different data URL.
   const std::string data2 = "<html><title>Two</title><body>bar</body></html>";
@@ -234,6 +237,11 @@
     EXPECT_TRUE(NavigateToURL(shell(), data_url2));
     same_tab_observer.Wait();
   }
+  url::Origin data_origin =
+      shell()->web_contents()->GetMainFrame()->GetLastCommittedOrigin();
+  EXPECT_TRUE(data_origin.opaque());
+  EXPECT_EQ(url::SchemeHostPort(),
+            data_origin.GetTupleOrPrecursorTupleIfOpaque());
 
   // Go back.
   TestNavigationObserver back_load_observer(shell()->web_contents());
@@ -253,6 +261,8 @@
 
   EXPECT_EQ(data_url1,
             shell()->web_contents()->GetMainFrame()->GetLastCommittedURL());
+  EXPECT_EQ(url::Origin::Create(base_url),
+            shell()->web_contents()->GetMainFrame()->GetLastCommittedOrigin());
 }
 
 IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
@@ -1860,7 +1870,7 @@
   EXPECT_EQ(main_url, entry->GetURL());
   FrameNavigationEntry* root_entry = entry->root_node()->frame_entry.get();
   EXPECT_EQ(main_url, root_entry->url());
-  EXPECT_EQ(main_origin, root_entry->origin());
+  EXPECT_EQ(main_origin, root_entry->committed_origin());
 
   // Verify subframe entries.  The entry should now have one blank subframe
   // FrameNavigationEntry, but this does not count as committing a real load.
@@ -1868,7 +1878,7 @@
   FrameNavigationEntry* frame_entry =
       entry->root_node()->children[0]->frame_entry.get();
   EXPECT_EQ(about_blank_url, frame_entry->url());
-  EXPECT_EQ(main_origin, frame_entry->origin());
+  EXPECT_EQ(main_origin, frame_entry->committed_origin());
   EXPECT_FALSE(root->child_at(0)->has_committed_real_load());
 
   // 1a. A nested iframe with no URL should also create a subframe entry but not
@@ -1888,7 +1898,7 @@
   ASSERT_EQ(1U, entry->root_node()->children[0]->children.size());
   frame_entry = entry->root_node()->children[0]->children[0]->frame_entry.get();
   EXPECT_EQ(about_blank_url, frame_entry->url());
-  EXPECT_EQ(main_origin, frame_entry->origin());
+  EXPECT_EQ(main_origin, frame_entry->committed_origin());
   EXPECT_FALSE(root->child_at(0)->child_at(0)->has_committed_real_load());
 
   // 2. Create another iframe with an explicit about:blank URL.
@@ -1912,7 +1922,7 @@
   ASSERT_EQ(2U, entry->root_node()->children.size());
   frame_entry = entry->root_node()->children[1]->frame_entry.get();
   EXPECT_EQ(about_blank_url, frame_entry->url());
-  EXPECT_EQ(main_origin, frame_entry->origin());
+  EXPECT_EQ(main_origin, frame_entry->committed_origin());
   EXPECT_FALSE(root->child_at(1)->has_committed_real_load());
 
   // 3. A real same-site navigation in the nested iframe should be AUTO.
@@ -1939,7 +1949,7 @@
   ASSERT_EQ(1U, entry->root_node()->children[0]->children.size());
   frame_entry = entry->root_node()->children[0]->children[0]->frame_entry.get();
   EXPECT_EQ(frame_url, frame_entry->url());
-  EXPECT_EQ(url::Origin::Create(frame_url), frame_entry->origin());
+  EXPECT_EQ(url::Origin::Create(frame_url), frame_entry->committed_origin());
   EXPECT_FALSE(root->child_at(0)->has_committed_real_load());
   EXPECT_TRUE(root->child_at(0)->child_at(0)->has_committed_real_load());
   EXPECT_FALSE(root->child_at(1)->has_committed_real_load());
@@ -1967,11 +1977,11 @@
   ASSERT_EQ(2U, entry->root_node()->children.size());
   frame_entry = entry->root_node()->children[1]->frame_entry.get();
   EXPECT_EQ(foo_url, frame_entry->url());
-  EXPECT_EQ(url::Origin::Create(foo_url), frame_entry->origin());
+  EXPECT_EQ(url::Origin::Create(foo_url), frame_entry->committed_origin());
   EXPECT_FALSE(root->child_at(0)->has_committed_real_load());
   EXPECT_TRUE(root->child_at(0)->child_at(0)->has_committed_real_load());
   EXPECT_TRUE(root->child_at(1)->has_committed_real_load());
-  EXPECT_EQ(frame_entry->origin(),
+  EXPECT_EQ(frame_entry->committed_origin(),
             root->child_at(1)->current_frame_host()->GetLastCommittedOrigin());
 
   // 5. A new navigation to about:blank in the nested frame should count as a
@@ -1997,7 +2007,7 @@
   frame_entry =
       entry2->root_node()->children[0]->children[0]->frame_entry.get();
   EXPECT_EQ(about_blank_url, frame_entry->url());
-  EXPECT_EQ(main_origin, frame_entry->origin());
+  EXPECT_EQ(main_origin, frame_entry->committed_origin());
   EXPECT_FALSE(root->child_at(0)->has_committed_real_load());
   EXPECT_TRUE(root->child_at(0)->child_at(0)->has_committed_real_load());
   EXPECT_TRUE(root->child_at(1)->has_committed_real_load());
@@ -2744,6 +2754,13 @@
   // The entry should have a FrameNavigationEntry for the data subframe.
   ASSERT_EQ(1U, entry2->root_node()->children.size());
   EXPECT_EQ(data_url, entry2->root_node()->children[0]->frame_entry->url());
+  EXPECT_EQ(entry2->root_node()
+                ->frame_entry->committed_origin()
+                ->GetTupleOrPrecursorTupleIfOpaque(),
+            entry2->root_node()
+                ->children[0]
+                ->frame_entry->committed_origin()
+                ->GetTupleOrPrecursorTupleIfOpaque());
 
   // 3. Navigate the iframe cross-site to a page with a nested iframe.
   GURL frame_url_b(embedded_test_server()->GetURL(
@@ -2768,6 +2785,15 @@
   EXPECT_EQ(frame_url_b, entry3->root_node()->children[0]->frame_entry->url());
   EXPECT_EQ(data_url,
             entry3->root_node()->children[0]->children[0]->frame_entry->url());
+  EXPECT_EQ(entry3->root_node()
+                ->children[0]
+                ->frame_entry->committed_origin()
+                ->GetTupleOrPrecursorTupleIfOpaque(),
+            entry3->root_node()
+                ->children[0]
+                ->children[0]
+                ->frame_entry->committed_origin()
+                ->GetTupleOrPrecursorTupleIfOpaque());
 
   // 4. Navigate the nested iframe cross-site.
   GURL frame_url_c(embedded_test_server()->GetURL(
@@ -2876,6 +2902,15 @@
   EXPECT_EQ(frame_url_b, entry3->root_node()->children[0]->frame_entry->url());
   EXPECT_EQ(data_url,
             entry3->root_node()->children[0]->children[0]->frame_entry->url());
+  EXPECT_EQ(entry3->root_node()
+                ->children[0]
+                ->frame_entry->committed_origin()
+                ->GetTupleOrPrecursorTupleIfOpaque(),
+            entry3->root_node()
+                ->children[0]
+                ->children[0]
+                ->frame_entry->committed_origin()
+                ->GetTupleOrPrecursorTupleIfOpaque());
 
   // Verify that we did not reload the main frame. See https://crbug.com/586234.
   EXPECT_EQ(3, EvalJs(root, "foo"));
@@ -2898,6 +2933,13 @@
   // The entry should have a FrameNavigationEntry for the subframe.
   ASSERT_EQ(1U, entry2->root_node()->children.size());
   EXPECT_EQ(data_url, entry2->root_node()->children[0]->frame_entry->url());
+  EXPECT_EQ(entry2->root_node()
+                ->frame_entry->committed_origin()
+                ->GetTupleOrPrecursorTupleIfOpaque(),
+            entry2->root_node()
+                ->children[0]
+                ->frame_entry->committed_origin()
+                ->GetTupleOrPrecursorTupleIfOpaque());
 
   // 9. Go back again, to the initial main frame page.
   {
@@ -2933,6 +2975,15 @@
   EXPECT_EQ(frame_url_b, entry3->root_node()->children[0]->frame_entry->url());
   EXPECT_EQ(data_url,
             entry3->root_node()->children[0]->children[0]->frame_entry->url());
+  EXPECT_EQ(entry3->root_node()
+                ->children[0]
+                ->frame_entry->committed_origin()
+                ->GetTupleOrPrecursorTupleIfOpaque(),
+            entry3->root_node()
+                ->children[0]
+                ->children[0]
+                ->frame_entry->committed_origin()
+                ->GetTupleOrPrecursorTupleIfOpaque());
 }
 
 // Verify that we navigate to the fallback (original) URL if a subframe's
@@ -5867,6 +5918,10 @@
 
   EXPECT_EQ(1, controller.GetEntryCount());
   EXPECT_EQ(1, EvalJs(shell(), "history.length"));
+  const url::Origin opaque_origin = root->current_origin();
+  EXPECT_TRUE(opaque_origin.opaque());
+  EXPECT_EQ(url::SchemeHostPort(),
+            opaque_origin.GetTupleOrPrecursorTupleIfOpaque());
 
   // Add an iframe with no 'src'.
   GURL blank_url(url::kAboutBlankURL);
@@ -5874,15 +5929,17 @@
       "var iframe = document.createElement('iframe');"
       "iframe.id = 'frame';"
       "document.body.appendChild(iframe);";
-  EXPECT_TRUE(ExecJs(root->current_frame_host(), script));
+  EXPECT_TRUE(ExecJs(root, script));
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
   EXPECT_EQ(1, controller.GetEntryCount());
-  EXPECT_EQ(1, EvalJs(shell(), "history.length"));
+  EXPECT_EQ(1, EvalJs(root, "history.length"));
   EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
   ASSERT_EQ(1U, root->child_count());
   FrameTreeNode* frame = root->child_at(0);
   ASSERT_NE(nullptr, frame);
   EXPECT_EQ(blank_url, frame->current_url());
+  EXPECT_EQ(opaque_origin, root->current_origin());
+  EXPECT_EQ(opaque_origin, frame->current_origin());
 
   // Do a document.write in the subframe to create a link to click.
   std::string html = "<a id='fraglink' href='#frag'>fragment link</a>";
@@ -5891,16 +5948,26 @@
       "iframe.contentWindow.document.write($1);"
       "iframe.contentWindow.document.close();",
       html);
-  EXPECT_TRUE(ExecJs(root->current_frame_host(), document_write_script));
+  EXPECT_TRUE(ExecJs(root, document_write_script));
+  EXPECT_EQ(opaque_origin, root->current_origin());
+  EXPECT_EQ(opaque_origin, frame->current_origin());
 
   // Click the link to do a same document navigation.  Due to the
-  // document.write, the new URL matches the parent frame's URL.
+  // document.write, the new URL matches the parent frame's URL, but the
+  // opaque origin is preserved.
   GURL frame_url_2("data:text/html,Top level page#frag");
   std::string link_script = "document.getElementById('fraglink').click()";
-  EXPECT_TRUE(ExecJs(frame->current_frame_host(), link_script));
+  EXPECT_TRUE(ExecJs(frame, link_script));
   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+  EXPECT_EQ(opaque_origin, root->current_origin());
+  EXPECT_EQ(opaque_origin, frame->current_origin());
+  EXPECT_EQ(ListValueOf("Top level page", "fragment link"),
+            EvalJs(frame,
+                   "[window.parent.document.body.textContent,"
+                   " document.body.textContent]"))
+      << "Frames should be same-origin and able to script each other.";
   EXPECT_EQ(2, controller.GetEntryCount());
-  EXPECT_EQ(2, EvalJs(shell(), "history.length"));
+  EXPECT_EQ(2, EvalJs(root, "history.length"));
   EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
   EXPECT_EQ(frame_url_2, frame->current_url());
 
@@ -5914,10 +5981,11 @@
   // Verify the process is still alive by running script.  We can't just call
   // IsRenderFrameLive after the navigation since it might not have disconnected
   // yet.
-  EXPECT_TRUE(ExecJs(root->current_frame_host(), "true;"));
+  EXPECT_EQ("ping", EvalJs(root, "'ping'"));
   EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
 
   EXPECT_EQ(blank_url, frame->current_url());
+  EXPECT_EQ(opaque_origin, frame->current_origin());
 }
 
 // Ensure that we do not corrupt a NavigationEntry's PageState if a subframe
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc
index 531ecb9..680d2c9 100644
--- a/content/browser/frame_host/navigation_entry_impl.cc
+++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -699,6 +699,7 @@
 CommitNavigationParams NavigationEntryImpl::ConstructCommitNavigationParams(
     const FrameNavigationEntry& frame_entry,
     const GURL& original_url,
+    const base::Optional<url::Origin>& origin_to_commit,
     const std::string& original_method,
     bool is_history_navigation_in_new_child,
     const std::map<std::string, bool>& subframe_unique_names,
@@ -726,9 +727,9 @@
   }
 
   CommitNavigationParams commit_params(
-      GetIsOverridingUserAgent(), redirects, original_url, original_method,
-      GetCanLoadLocalResources(), frame_entry.page_state(), GetUniqueID(),
-      is_history_navigation_in_new_child, subframe_unique_names,
+      origin_to_commit, GetIsOverridingUserAgent(), redirects, original_url,
+      original_method, GetCanLoadLocalResources(), frame_entry.page_state(),
+      GetUniqueID(), is_history_navigation_in_new_child, subframe_unique_names,
       intended_as_new_entry, pending_offset_to_send, current_offset_to_send,
       current_length_to_send, IsViewSourceMode(), should_clear_history_list());
 #if defined(OS_ANDROID)
@@ -783,7 +784,7 @@
     SiteInstanceImpl* site_instance,
     scoped_refptr<SiteInstanceImpl> source_site_instance,
     const GURL& url,
-    const url::Origin& origin,
+    const base::Optional<url::Origin>& origin,
     const Referrer& referrer,
     const std::vector<GURL>& redirect_chain,
     const PageState& page_state,
@@ -842,9 +843,9 @@
   // or unique name.
   FrameNavigationEntry* frame_entry = new FrameNavigationEntry(
       unique_name, item_sequence_number, document_sequence_number,
-      site_instance, std::move(source_site_instance), url, &origin, referrer,
-      redirect_chain, page_state, method, post_id,
-      std::move(blob_url_loader_factory));
+      site_instance, std::move(source_site_instance), url,
+      base::OptionalOrNullptr(origin), referrer, redirect_chain, page_state,
+      method, post_id, std::move(blob_url_loader_factory));
   parent_node->children.push_back(
       std::make_unique<NavigationEntryImpl::TreeNode>(parent_node,
                                                       frame_entry));
diff --git a/content/browser/frame_host/navigation_entry_impl.h b/content/browser/frame_host/navigation_entry_impl.h
index f86d97a3f..83d9493 100644
--- a/content/browser/frame_host/navigation_entry_impl.h
+++ b/content/browser/frame_host/navigation_entry_impl.h
@@ -189,6 +189,7 @@
   CommitNavigationParams ConstructCommitNavigationParams(
       const FrameNavigationEntry& frame_entry,
       const GURL& original_url,
+      const base::Optional<url::Origin>& origin_to_commit,
       const std::string& original_method,
       bool is_history_navigation_in_new_child,
       const std::map<std::string, bool>& subframe_unique_names,
@@ -229,7 +230,7 @@
       SiteInstanceImpl* site_instance,
       scoped_refptr<SiteInstanceImpl> source_site_instance,
       const GURL& url,
-      const url::Origin& origin,
+      const base::Optional<url::Origin>& origin,
       const Referrer& referrer,
       const std::vector<GURL>& redirect_chain,
       const PageState& page_state,
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 7a55d91..bfb3e02 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -368,7 +368,7 @@
   // TODO(clamy): See if the navigation start time should be measured in the
   // renderer and sent to the browser instead of being measured here.
   CommitNavigationParams commit_params(
-      override_user_agent,
+      base::nullopt, override_user_agent,
       std::vector<GURL>(),  // redirects
       common_params.url, common_params.method,
       false,                          // can_load_local_resources
@@ -417,7 +417,7 @@
       params.gesture == NavigationGestureUser, InitiatorCSPInfo(),
       std::string() /* href_translate */, base::TimeTicks::Now());
   CommitNavigationParams commit_params(
-      params.is_overriding_user_agent, params.redirects,
+      params.origin, params.is_overriding_user_agent, params.redirects,
       params.original_request_url, params.method,
       false /* can_load_local_resources */, params.page_state,
       params.nav_entry_id, false /* is_history_navigation_in_new_child */,
@@ -931,6 +931,11 @@
   commit_params_.redirect_response.push_back(response->head);
   commit_params_.redirect_infos.push_back(redirect_info);
 
+  // On redirects, the initial origin_to_commit is no longer correct, so it
+  // must be cleared to avoid sending incorrect value to the renderer process.
+  if (commit_params_.origin_to_commit)
+    commit_params_.origin_to_commit.reset();
+
   commit_params_.redirects.push_back(common_params_.url);
   common_params_.url = redirect_info.new_url;
   common_params_.method = redirect_info.new_method;
@@ -1753,6 +1758,13 @@
     const base::Optional<std::string>& error_page_content) {
   UpdateCommitNavigationParamsHistory();
   frame_tree_node_->TransferNavigationRequestOwnership(render_frame_host);
+  // Error pages commit in an opaque origin in the renderer process. If this
+  // NavigationRequest resulted in committing an error page, just clear the
+  // |origin_to_commit| and let the renderer process calculate the origin.
+  // TODO(nasko): Create an opque origin here and pass it for the renderer
+  // to commit into it. Potentially also make it an opaque origin derived from
+  // the error page URL, so it can be checked at DidCommit processing.
+  commit_params_.origin_to_commit.reset();
   if (IsPerNavigationMojoInterfaceEnabled() && request_navigation_client_ &&
       request_navigation_client_.is_bound()) {
     if (associated_site_instance_id_ ==
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index 12b5a58d..b9d7f8b 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -4342,6 +4342,10 @@
 
   scoped_refptr<SiteInstance> success_site_instance =
       shell()->web_contents()->GetMainFrame()->GetSiteInstance();
+  url::Origin expected_origin =
+      shell()->web_contents()->GetMainFrame()->GetLastCommittedOrigin();
+
+  EXPECT_EQ(url::Origin::Create(error_url), expected_origin);
 
   // Install an interceptor which will cause network failure for |error_url|,
   // reload the existing entry and verify.
@@ -4403,6 +4407,8 @@
       GURL(kUnreachableWebDataURL),
       policy->GetOriginLock(
           shell()->web_contents()->GetSiteInstance()->GetProcess()->GetID()));
+  EXPECT_EQ(expected_origin,
+            shell()->web_contents()->GetMainFrame()->GetLastCommittedOrigin());
 
   // Test the same scenario as above, but this time initiated by the
   // renderer process.
@@ -4448,6 +4454,8 @@
       GURL(kUnreachableWebDataURL),
       policy->GetOriginLock(
           shell()->web_contents()->GetSiteInstance()->GetProcess()->GetID()));
+  EXPECT_EQ(expected_origin,
+            shell()->web_contents()->GetMainFrame()->GetLastCommittedOrigin());
 }
 
 // Make sure that reload works properly if it redirects to a different site than
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index e4d40186..279dd0c 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -445,7 +445,8 @@
             base::TimeTicks::Now(), base::TimeTicks::Now());
     CommitNavigationParams commit_params =
         entry->ConstructCommitNavigationParams(
-            *frame_entry, common_params.url, common_params.method, false,
+            *frame_entry, common_params.url, frame_entry->committed_origin(),
+            common_params.method, false,
             entry->GetSubframeUniqueNames(frame_tree_node),
             controller->GetPendingEntryIndex() ==
                 -1 /* intended_as_new_entry */,
@@ -2869,7 +2870,8 @@
       FrameMsg_Navigate_Type::DIFFERENT_DOCUMENT, PREVIEWS_UNSPECIFIED,
       base::TimeTicks::Now(), base::TimeTicks::Now());
   CommitNavigationParams commit_params = entry.ConstructCommitNavigationParams(
-      *frame_entry, common_params.url, common_params.method, false,
+      *frame_entry, common_params.url, frame_entry->committed_origin(),
+      common_params.method, false,
       entry.GetSubframeUniqueNames(frame_tree_node),
       controller().GetPendingEntryIndex() == -1 /* intended_as_new_entry */,
       static_cast<NavigationControllerImpl&>(controller())
@@ -2943,7 +2945,8 @@
       FrameMsg_Navigate_Type::DIFFERENT_DOCUMENT, PREVIEWS_UNSPECIFIED,
       base::TimeTicks::Now(), base::TimeTicks::Now());
   CommitNavigationParams commit_params = entry.ConstructCommitNavigationParams(
-      *frame_entry, common_params.url, common_params.method, false,
+      *frame_entry, common_params.url, frame_entry->committed_origin(),
+      common_params.method, false,
       entry.GetSubframeUniqueNames(frame_tree_node),
       controller().GetPendingEntryIndex() == -1 /* intended_as_new_entry */,
       static_cast<NavigationControllerImpl&>(controller())
@@ -3014,7 +3017,8 @@
       FrameMsg_Navigate_Type::DIFFERENT_DOCUMENT, PREVIEWS_UNSPECIFIED,
       base::TimeTicks::Now(), base::TimeTicks::Now());
   CommitNavigationParams commit_params = entry.ConstructCommitNavigationParams(
-      *frame_entry, common_params.url, common_params.method, false,
+      *frame_entry, common_params.url, frame_entry->committed_origin(),
+      common_params.method, false,
       entry.GetSubframeUniqueNames(frame_tree_node),
       controller().GetPendingEntryIndex() == -1 /* intended_as_new_entry */,
       static_cast<NavigationControllerImpl&>(controller())
diff --git a/content/browser/locks/lock_manager.cc b/content/browser/locks/lock_manager.cc
index dc5384db..1751c88 100644
--- a/content/browser/locks/lock_manager.cc
+++ b/content/browser/locks/lock_manager.cc
@@ -98,9 +98,6 @@
     DCHECK(request_);
     DCHECK(!handle_);
 
-    // Get a new ID when granted, to maintain map in grant order.
-    lock_id_ = context->NextLockId();
-
     blink::mojom::LockHandlePtr ptr;
     handle_ =
         LockHandleImpl::Create(std::move(context), origin, lock_id_, &ptr);
@@ -126,18 +123,13 @@
   LockMode mode() const { return mode_; }
   int64_t lock_id() const { return lock_id_; }
   const std::string& client_id() const { return client_id_; }
+  bool is_granted() const { return !!handle_; }
 
  private:
   const std::string name_;
   const LockMode mode_;
   const std::string client_id_;
-
-  // |lock_id_| is assigned when the request is arrives, and serves as the key
-  // in an ordered request map. If it is a PREEMPT request, it is given the
-  // special kPreemptiveLockId value which precedes all others, and is
-  // processed immediately. When the lock is granted, a new id is generated to
-  // be the key in the ordered map of held locks.
-  int64_t lock_id_;
+  const int64_t lock_id_;
 
   // Exactly one of the following is non-null at any given time.
 
@@ -153,120 +145,164 @@
 
 LockManager::~LockManager() = default;
 
+// The OriginState class manages and exposes the state of lock requests
+// for a given origin.
 class LockManager::OriginState {
  public:
-  OriginState() = default;
+  OriginState(LockManager* lock_manager) : lock_manager_(lock_manager) {}
   ~OriginState() = default;
 
-  const Lock* AddRequest(int64_t lock_id,
-                         const std::string& name,
-                         LockMode mode,
-                         const std::string& client_id,
-                         blink::mojom::LockRequestPtr request) {
-    auto it = requested_.emplace(
-        lock_id, std::make_unique<Lock>(name, mode, lock_id, client_id,
-                                        std::move(request)));
-
-    DCHECK(it.second) << "Insertion should have taken place";
-    return it.first->second.get();
+  // Helper function for breaking the lock at the front of a given request
+  // queue.
+  void BreakFront(std::list<Lock>& request_queue) {
+    Lock& broken_lock = request_queue.front();
+    lock_id_to_iterator_.erase(broken_lock.lock_id());
+    broken_lock.Break();
+    request_queue.pop_front();
   }
 
-  bool EraseLock(int64_t lock_id) {
-    return requested_.erase(lock_id) || held_.erase(lock_id);
+  // Steals a lock for a given resource.
+  //
+  // Breaks any currently held locks and inserts a new lock at the front of the
+  // request queue and grants it.
+  void PreemptLock(int64_t lock_id,
+                   const std::string& name,
+                   LockMode mode,
+                   const std::string& client_id,
+                   blink::mojom::LockRequestPtr request,
+                   const url::Origin origin) {
+    // Preempting shared locks is not supported.
+    DCHECK_EQ(mode, LockMode::EXCLUSIVE);
+    std::list<Lock>& request_queue = resource_names_to_requests_[name];
+    while (!request_queue.empty() && request_queue.front().is_granted())
+      BreakFront(request_queue);
+    request_queue.emplace_front(name, mode, lock_id, client_id,
+                                std::move(request));
+    auto it = request_queue.begin();
+    lock_id_to_iterator_.emplace(it->lock_id(), it);
+    it->Grant(lock_manager_->weak_ptr_factory_.GetWeakPtr(), origin);
   }
 
-  bool IsEmpty() const { return requested_.empty() && held_.empty(); }
+  void AddRequest(int64_t lock_id,
+                  const std::string& name,
+                  LockMode mode,
+                  const std::string& client_id,
+                  blink::mojom::LockRequestPtr request,
+                  WaitMode wait,
+                  const url::Origin origin) {
+    DCHECK(wait != WaitMode::PREEMPT);
+    std::list<Lock>& request_queue = resource_names_to_requests_[name];
+    bool can_grant = request_queue.empty() ||
+                     (request_queue.back().is_granted() &&
+                      request_queue.back().mode() == LockMode::SHARED &&
+                      mode == LockMode::SHARED);
 
+    if (!can_grant && wait == WaitMode::NO_WAIT) {
+      request->Failed();
+      return;
+    }
+
+    request_queue.emplace_back(name, mode, lock_id, client_id,
+                               std::move(request));
+    auto it = --(request_queue.end());
+    lock_id_to_iterator_.emplace(it->lock_id(), it);
+    if (can_grant)
+      it->Grant(lock_manager_->weak_ptr_factory_.GetWeakPtr(), origin);
+  }
+
+  void EraseLock(int64_t lock_id, const url::Origin& origin) {
+    // Note - the two lookups here could be replaced with one if the
+    // lock_id_to_iterator_ map also stored a reference to the request queue.
+    auto iterator_it = lock_id_to_iterator_.find(lock_id);
+    if (iterator_it == lock_id_to_iterator_.end())
+      return;
+
+    auto lock_it = iterator_it->second;
+    lock_id_to_iterator_.erase(iterator_it);
+
+    auto request_it = resource_names_to_requests_.find(lock_it->name());
+    if (request_it == resource_names_to_requests_.end())
+      return;
+
+    std::list<Lock>& request_queue = request_it->second;
 #if DCHECK_IS_ON()
-  bool IsHeld(int64_t lock_id) { return held_.find(lock_id) != held_.end(); }
+    auto check_it = request_queue.begin();
+    bool found = false;
+    for (; check_it != request_queue.end(); ++check_it) {
+      found = check_it == lock_it;
+      if (found)
+        break;
+    }
+    DCHECK(found);
 #endif
 
-  bool IsGrantable(const std::string& name, LockMode mode) const {
-    if (mode == LockMode::EXCLUSIVE) {
-      return !shared_.count(name) && !exclusive_.count(name);
+    request_queue.erase(lock_it);
+    if (request_queue.empty()) {
+      resource_names_to_requests_.erase(request_it);
+      return;
+    }
+
+    // If, after erasing the lock from the request queue, the front of the
+    // queue is ungranted, then we have just erased the only granted lock. In
+    // this situation it will be necessary then to grant the next lock or locks
+    // (locks in the case that there is more than one SHARED lock at the front
+    // of the request queue now).
+    if (request_queue.front().is_granted())
+      return;
+
+    if (request_queue.front().mode() == LockMode::EXCLUSIVE) {
+      request_queue.front().Grant(lock_manager_->weak_ptr_factory_.GetWeakPtr(),
+                                  origin);
     } else {
-      return !exclusive_.count(name);
-    }
-  }
-
-  // Break any held locks by that name.
-  void Break(const std::string& name) {
-    for (auto it = held_.begin(); it != held_.end();) {
-      if (it->second->name() == name) {
-        std::unique_ptr<Lock> lock = std::move(it->second);
-        it = held_.erase(it);
-
-        // Deleting the LockHandleImpl will signal an error on the other end
-        // of the pipe, which will notify script that the lock was broken.
-        lock->Break();
-      } else {
-        ++it;
+      DCHECK(request_queue.front().mode() == LockMode::SHARED);
+      for (auto grantee = request_queue.begin();
+           grantee != request_queue.end() &&
+           grantee->mode() == LockMode::SHARED;
+           ++grantee) {
+        DCHECK(!grantee->is_granted());
+        grantee->Grant(lock_manager_->weak_ptr_factory_.GetWeakPtr(), origin);
       }
     }
   }
 
-  void ProcessRequests(LockManager* lock_manager, const url::Origin& origin) {
-    shared_.clear();
-    exclusive_.clear();
-    for (const auto& id_lock_pair : held_) {
-      const auto& lock = id_lock_pair.second;
-      MergeLockState(lock->name(), lock->mode());
-    }
+  bool IsEmpty() { return lock_id_to_iterator_.empty(); }
 
-    for (auto it = requested_.begin(); it != requested_.end();) {
-      auto& lock = it->second;
-
-      bool granted = IsGrantable(lock->name(), lock->mode());
-
-      MergeLockState(lock->name(), lock->mode());
-
-      if (granted) {
-        std::unique_ptr<Lock> grantee = std::move(lock);
-        it = requested_.erase(it);
-        grantee->Grant(lock_manager->weak_ptr_factory_.GetWeakPtr(), origin);
-        held_.insert(std::make_pair(grantee->lock_id(), std::move(grantee)));
-      } else {
-        ++it;
+  std::pair<std::vector<blink::mojom::LockInfoPtr>,
+            std::vector<blink::mojom::LockInfoPtr>>
+  Snapshot() const {
+    std::vector<blink::mojom::LockInfoPtr> requests;
+    std::vector<blink::mojom::LockInfoPtr> held;
+    for (const auto& name_queue_pair : resource_names_to_requests_) {
+      auto& request_queue = name_queue_pair.second;
+      if (request_queue.empty())
+        continue;
+      for (const auto& lock : request_queue) {
+        std::vector<blink::mojom::LockInfoPtr>& target =
+            lock.is_granted() ? held : requests;
+        target.emplace_back(base::in_place, lock.name(), lock.mode(),
+                            lock.client_id());
       }
     }
-  }
-
-  std::vector<blink::mojom::LockInfoPtr> SnapshotRequested() const {
-    std::vector<blink::mojom::LockInfoPtr> out;
-    out.reserve(requested_.size());
-    for (const auto& id_lock_pair : requested_) {
-      const auto& lock = id_lock_pair.second;
-      out.emplace_back(base::in_place, lock->name(), lock->mode(),
-                       lock->client_id());
-    }
-    return out;
-  }
-
-  std::vector<blink::mojom::LockInfoPtr> SnapshotHeld() const {
-    std::vector<blink::mojom::LockInfoPtr> out;
-    out.reserve(held_.size());
-    for (const auto& id_lock_pair : held_) {
-      const auto& lock = id_lock_pair.second;
-      out.emplace_back(base::in_place, lock->name(), lock->mode(),
-                       lock->client_id());
-    }
-    return out;
+    return std::make_pair(std::move(requests), std::move(held));
   }
 
  private:
-  void MergeLockState(const std::string& name, LockMode mode) {
-    (mode == LockMode::SHARED ? shared_ : exclusive_).insert(name);
-  }
+  // OriginState::resource_names_to_requests_ maps a resource name to
+  // that resource's associated request queue for a given origin.
+  //
+  // A resource's request queue is a list of Lock objects representing lock
+  // requests against that resource. All the granted locks for a resource reside
+  // at the front of the resource's
+  // request queue.
+  std::unordered_map<std::string, std::list<Lock>> resource_names_to_requests_;
 
-  // These represent the actual state of locks in the origin.
-  std::map<int64_t, std::unique_ptr<Lock>> requested_;
-  std::map<int64_t, std::unique_ptr<Lock>> held_;
+  // OriginState::lock_id_to_iterator_ maps a lock's id to the
+  // iterator pointing to its location in its associated request queue.
+  std::unordered_map<int64_t, std::list<Lock>::iterator> lock_id_to_iterator_;
 
-  // These sets represent what is held or requested, so that "IsGrantable"
-  // tests are simple. These are cleared/rebuilt when the request queue
-  // is processed.
-  std::unordered_set<std::string> shared_;
-  std::unordered_set<std::string> exclusive_;
+  // Any OriginState is owned by a LockManager so a raw pointer back to an
+  // OriginState's owning LockManager is safe.
+  LockManager* lock_manager_;
 };
 
 void LockManager::CreateService(blink::mojom::LockManagerRequest request,
@@ -297,71 +333,52 @@
   }
 
   const auto& context = bindings_.dispatch_context();
-  int64_t lock_id =
-      (wait == WaitMode::PREEMPT) ? kPreemptiveLockId : NextLockId();
 
-  if (wait == WaitMode::PREEMPT) {
-    Break(context.origin, name);
-  } else if (wait == WaitMode::NO_WAIT &&
-             !IsGrantable(context.origin, name, mode)) {
-    request->Failed();
-    return;
-  }
+  if (!base::ContainsKey(origins_, context.origin))
+    origins_.emplace(context.origin, this);
 
+  int64_t lock_id = NextLockId();
   request.set_connection_error_handler(base::BindOnce(&LockManager::ReleaseLock,
                                                       base::Unretained(this),
                                                       context.origin, lock_id));
-  const Lock* lock = origins_[context.origin].AddRequest(
-      lock_id, name, mode, context.client_id, std::move(request));
-  ProcessRequests(context.origin);
 
-  DCHECK_GT(lock->lock_id(), kPreemptiveLockId)
-      << "Preemptive lock should be assigned a new id";
-
-#if DCHECK_IS_ON()
-  DCHECK(wait != WaitMode::PREEMPT ||
-         origins_[context.origin].IsHeld(lock->lock_id()))
-      << "Preemptive lock should be granted immediately";
-#endif
+  OriginState& origin_state = origins_.find(context.origin)->second;
+  if (wait == WaitMode::PREEMPT) {
+    origin_state.PreemptLock(lock_id, name, mode, context.client_id,
+                             std::move(request), context.origin);
+  } else
+    origin_state.AddRequest(lock_id, name, mode, context.client_id,
+                            std::move(request), wait, context.origin);
 }
 
 void LockManager::ReleaseLock(const url::Origin& origin, int64_t lock_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!base::ContainsKey(origins_, origin))
+  auto origin_it = origins_.find(origin);
+  if (origin_it == origins_.end())
     return;
-  OriginState& state = origins_[origin];
 
-  bool dirty = state.EraseLock(lock_id);
-
+  OriginState& state = origin_it->second;
+  state.EraseLock(lock_id, origin);
   if (state.IsEmpty())
     origins_.erase(origin);
-  else if (dirty)
-    ProcessRequests(origin);
 }
 
 void LockManager::QueryState(QueryStateCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   const url::Origin& origin = bindings_.dispatch_context().origin;
-  if (!base::ContainsKey(origins_, origin)) {
+  auto origin_it = origins_.find(origin);
+  if (origin_it == origins_.end()) {
     std::move(callback).Run(std::vector<blink::mojom::LockInfoPtr>(),
                             std::vector<blink::mojom::LockInfoPtr>());
     return;
   }
 
-  OriginState& state = origins_[origin];
-  std::move(callback).Run(state.SnapshotRequested(), state.SnapshotHeld());
-}
-
-bool LockManager::IsGrantable(const url::Origin& origin,
-                              const std::string& name,
-                              LockMode mode) const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = origins_.find(origin);
-  if (it == origins_.end())
-    return true;
-  return it->second.IsGrantable(name, mode);
+  OriginState& state = origin_it->second;
+  auto requested_held_pair = state.Snapshot();
+  std::move(callback).Run(std::move(requested_held_pair.first),
+                          std::move(requested_held_pair.second));
 }
 
 int64_t LockManager::NextLockId() {
@@ -370,22 +387,4 @@
   return lock_id;
 }
 
-void LockManager::Break(const url::Origin& origin, const std::string& name) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = origins_.find(origin);
-  if (it != origins_.end())
-    it->second.Break(name);
-}
-
-void LockManager::ProcessRequests(const url::Origin& origin) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!base::ContainsKey(origins_, origin))
-    return;
-
-  OriginState& state = origins_[origin];
-  DCHECK(!state.IsEmpty());
-  state.ProcessRequests(this, origin);
-  DCHECK(!state.IsEmpty());
-}
-
 }  // namespace content
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index 995e4c4..e552c2cc 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -15,6 +15,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/time/time.h"
+#include "cc/trees/element_id.h"
 #include "cc/trees/layer_tree_host_client.h"
 #include "cc/trees/layer_tree_host_single_thread_client.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
@@ -117,6 +118,11 @@
   }
   void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel,
                                          bool has_scrolled_by_touch) override {}
+  void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      cc::ElementId scroll_latched_element_id) override {}
+  void SendScrollEndEventFromImplSide(
+      cc::ElementId scroll_latched_element_id) override {}
   void RequestNewLayerTreeFrameSink() override;
   void DidInitializeLayerTreeFrameSink() override;
   void DidFailToInitializeLayerTreeFrameSink() override;
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 2385237..b2654d4 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -6537,9 +6537,9 @@
   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
 
   // Set sandbox flags for child frame.
-  EXPECT_TRUE(ExecuteScript(root,
-                            "document.querySelector('iframe').sandbox = "
-                            "    'allow-scripts allow-popups';"));
+  EXPECT_TRUE(ExecJs(root,
+                     "document.querySelector('iframe').sandbox = "
+                     "    'allow-scripts allow-popups';"));
 
   // Calculate expected flags.  Note that "allow-scripts" resets both
   // WebSandboxFlags::Scripts and WebSandboxFlags::AutomaticFeatures bits per
@@ -6560,8 +6560,12 @@
             root->child_at(0)->effective_frame_policy().sandbox_flags);
 
   // Verify that they've also taken effect on the renderer side.  The sandboxed
-  // frame's origin should be unique.
+  // frame's origin should be opaque.
   EXPECT_EQ("null", GetOriginFromRenderer(root->child_at(0)));
+  const url::SchemeHostPort tuple_b(frame_url);
+  const url::Origin sandbox_origin_b = root->child_at(0)->current_origin();
+  EXPECT_TRUE(sandbox_origin_b.opaque());
+  EXPECT_EQ(tuple_b, sandbox_origin_b.GetTupleOrPrecursorTupleIfOpaque());
 
   // Open a popup named "foo" from the sandboxed child frame.
   Shell* foo_shell =
@@ -6577,16 +6581,21 @@
   // process.
   EXPECT_EQ(expected_flags, foo_root->effective_frame_policy().sandbox_flags);
 
-  // The popup's origin should be unique, since it's sandboxed.
+  // The popup's origin should be opaque, since it's sandboxed, but cross-origin
+  // from its opener.
   EXPECT_EQ("null", GetOriginFromRenderer(foo_root));
+  url::Origin sandbox_origin_b2 = foo_root->current_origin();
+  EXPECT_NE(sandbox_origin_b2, sandbox_origin_b);
+  EXPECT_TRUE(sandbox_origin_b2.opaque());
+  EXPECT_EQ(tuple_b, sandbox_origin_b2.GetTupleOrPrecursorTupleIfOpaque());
 
-  // Navigate the popup cross-site.  This should keep the unique origin and the
-  // inherited sandbox flags.
+  // Navigate the popup cross-site.  This should be placed in an opaque origin
+  // derived from c.com, and retain the inherited sandbox flags.
   GURL c_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
+  const url::SchemeHostPort tuple_c(c_url);
   {
     TestFrameNavigationObserver popup_observer(foo_root);
-    EXPECT_TRUE(
-        ExecuteScript(foo_root, "location.href = '" + c_url.spec() + "';"));
+    EXPECT_TRUE(ExecJs(foo_root, JsReplace("location.href = $1", c_url)));
     popup_observer.Wait();
     EXPECT_EQ(c_url, foo_shell->web_contents()->GetLastCommittedURL());
   }
@@ -6595,22 +6604,31 @@
   // sides.
   EXPECT_EQ(expected_flags, foo_root->effective_frame_policy().sandbox_flags);
   EXPECT_EQ("null", GetOriginFromRenderer(foo_root));
+  const url::Origin sandbox_origin_c = foo_root->current_origin();
+  EXPECT_NE(sandbox_origin_b, sandbox_origin_c);
+  EXPECT_TRUE(sandbox_origin_c.opaque());
+  EXPECT_EQ(tuple_c, sandbox_origin_c.GetTupleOrPrecursorTupleIfOpaque());
 
   // Navigate the popup back to b.com.  The popup should perform a
-  // remote-to-local navigation in the b.com process, and keep the unique
+  // remote-to-local navigation in the b.com process, and keep an opaque
   // origin and the inherited sandbox flags.
   {
     TestFrameNavigationObserver popup_observer(foo_root);
-    EXPECT_TRUE(
-        ExecuteScript(foo_root, "location.href = '" + frame_url.spec() + "';"));
+    EXPECT_TRUE(ExecJs(foo_root, JsReplace("location.href = $1", frame_url)));
     popup_observer.Wait();
     EXPECT_EQ(frame_url, foo_shell->web_contents()->GetLastCommittedURL());
   }
 
   // Confirm that the popup is still sandboxed, both on browser and renderer
-  // sides.
+  // sides. This navigation should result in a new opaque origin derived
+  // from b.com.
   EXPECT_EQ(expected_flags, foo_root->effective_frame_policy().sandbox_flags);
   EXPECT_EQ("null", GetOriginFromRenderer(foo_root));
+  url::Origin sandbox_origin_b3 = foo_root->current_origin();
+  EXPECT_TRUE(sandbox_origin_b3.opaque());
+  EXPECT_EQ(tuple_b, sandbox_origin_b3.GetTupleOrPrecursorTupleIfOpaque());
+  EXPECT_NE(sandbox_origin_b, sandbox_origin_b3);
+  EXPECT_NE(sandbox_origin_b2, sandbox_origin_b3);
 }
 
 // Verify that popups opened from frames sandboxed with the
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 63dd3a8..bba837f 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -649,7 +649,7 @@
       partition_path, in_memory, context->GetSpecialStoragePolicy(),
       quota_manager_proxy.get());
 
-  partition->dom_storage_context_ = new DOMStorageContextWrapper(
+  partition->dom_storage_context_ = DOMStorageContextWrapper::Create(
       BrowserContext::GetConnectorFor(context),
       in_memory ? base::FilePath() : context->GetPath(),
       relative_partition_path, context->GetSpecialStoragePolicy());
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 391aca4..d48e8196 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -494,6 +494,7 @@
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(content::CommitNavigationParams)
+  IPC_STRUCT_TRAITS_MEMBER(origin_to_commit)
   IPC_STRUCT_TRAITS_MEMBER(is_overriding_user_agent)
   IPC_STRUCT_TRAITS_MEMBER(redirects)
   IPC_STRUCT_TRAITS_MEMBER(redirect_response)
diff --git a/content/common/navigation_params.cc b/content/common/navigation_params.cc
index 8d8ffee..33f9a63 100644
--- a/content/common/navigation_params.cc
+++ b/content/common/navigation_params.cc
@@ -106,6 +106,7 @@
 CommitNavigationParams::CommitNavigationParams() = default;
 
 CommitNavigationParams::CommitNavigationParams(
+    const base::Optional<url::Origin>& origin_to_commit,
     bool is_overriding_user_agent,
     const std::vector<GURL>& redirects,
     const GURL& original_url,
@@ -121,7 +122,8 @@
     int current_history_list_length,
     bool is_view_source,
     bool should_clear_history_list)
-    : is_overriding_user_agent(is_overriding_user_agent),
+    : origin_to_commit(origin_to_commit),
+      is_overriding_user_agent(is_overriding_user_agent),
       redirects(redirects),
       original_url(original_url),
       original_method(original_method),
diff --git a/content/common/navigation_params.h b/content/common/navigation_params.h
index 6d3d932..8cc6de6 100644
--- a/content/common/navigation_params.h
+++ b/content/common/navigation_params.h
@@ -249,7 +249,8 @@
 // commit a navigation besides those in CommonNavigationParams.
 struct CONTENT_EXPORT CommitNavigationParams {
   CommitNavigationParams();
-  CommitNavigationParams(bool is_overriding_user_agent,
+  CommitNavigationParams(const base::Optional<url::Origin>& origin_to_commit,
+                         bool is_overriding_user_agent,
                          const std::vector<GURL>& redirects,
                          const GURL& original_url,
                          const std::string& original_method,
@@ -267,6 +268,14 @@
   CommitNavigationParams(const CommitNavigationParams& other);
   ~CommitNavigationParams();
 
+  // The origin to be used for committing the navigation, if specified.
+  // This will be an origin that's compatible with the |url| in the
+  // CommonNavigationParams; if |url| is data: or about:blank, or the frame has
+  // sandbox attributes, this determines the origin of the resulting document.
+  // It is specified for session history navigations, for which the origin is
+  // known and saved in the FrameNavigationEntry.
+  base::Optional<url::Origin> origin_to_commit;
+
   // Whether or not the user agent override string should be used.
   bool is_overriding_user_agent = false;
 
diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc
index 078b3f9..d31e0954 100644
--- a/content/public/renderer/content_renderer_client.cc
+++ b/content/public/renderer/content_renderer_client.cc
@@ -201,10 +201,6 @@
   return false;
 }
 
-bool ContentRendererClient::IsPluginAllowedToUseCompositorAPI(const GURL& url) {
-  return false;
-}
-
 bool ContentRendererClient::IsPluginAllowedToUseDevChannelAPIs() {
   return false;
 }
diff --git a/content/public/renderer/content_renderer_client.h b/content/public/renderer/content_renderer_client.h
index ab52332..cfe87b6 100644
--- a/content/public/renderer/content_renderer_client.h
+++ b/content/public/renderer/content_renderer_client.h
@@ -295,9 +295,6 @@
   // Returns true if the page at |url| can use Pepper CameraDevice APIs.
   virtual bool IsPluginAllowedToUseCameraDeviceAPI(const GURL& url);
 
-  // Returns true if the page at |url| can use Pepper Compositor APIs.
-  virtual bool IsPluginAllowedToUseCompositorAPI(const GURL& url);
-
   // Returns true if dev channel APIs are available for plugins.
   virtual bool IsPluginAllowedToUseDevChannelAPIs();
 
diff --git a/content/public/test/navigation_simulator.cc b/content/public/test/navigation_simulator.cc
index 4f257d7..21e5ed6 100644
--- a/content/public/test/navigation_simulator.cc
+++ b/content/public/test/navigation_simulator.cc
@@ -926,6 +926,7 @@
           base::nullopt /* detools_initiator_info */);
   CommonNavigationParams common_params;
   common_params.url = navigation_url_;
+  common_params.initiator_origin = url::Origin();
   common_params.method = initial_method_;
   common_params.referrer = referrer_;
   common_params.transition = transition_;
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index e61d93a2..d86ac48 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -856,8 +856,6 @@
       "pepper/pepper_browser_connection.h",
       "pepper/pepper_camera_device_host.cc",
       "pepper/pepper_camera_device_host.h",
-      "pepper/pepper_compositor_host.cc",
-      "pepper/pepper_compositor_host.h",
       "pepper/pepper_device_enumeration_host_helper.cc",
       "pepper/pepper_device_enumeration_host_helper.h",
       "pepper/pepper_file_chooser_host.cc",
diff --git a/content/renderer/compositor/layer_tree_view.cc b/content/renderer/compositor/layer_tree_view.cc
index cbe23e1..e6be9e7b 100644
--- a/content/renderer/compositor/layer_tree_view.cc
+++ b/content/renderer/compositor/layer_tree_view.cc
@@ -647,6 +647,18 @@
                                                has_scrolled_by_touch);
 }
 
+void LayerTreeView::SendOverscrollEventFromImplSide(
+    const gfx::Vector2dF& overscroll_delta,
+    cc::ElementId scroll_latched_element_id) {
+  delegate_->SendOverscrollEventFromImplSide(overscroll_delta,
+                                             scroll_latched_element_id);
+}
+
+void LayerTreeView::SendScrollEndEventFromImplSide(
+    cc::ElementId scroll_latched_element_id) {
+  delegate_->SendScrollEndEventFromImplSide(scroll_latched_element_id);
+}
+
 void LayerTreeView::RequestNewLayerTreeFrameSink() {
   // When the compositor is not visible it would not request a
   // LayerTreeFrameSink so this is a race where it requested one then became
diff --git a/content/renderer/compositor/layer_tree_view.h b/content/renderer/compositor/layer_tree_view.h
index 3e01903..17f56d7 100644
--- a/content/renderer/compositor/layer_tree_view.h
+++ b/content/renderer/compositor/layer_tree_view.h
@@ -199,6 +199,11 @@
   void ApplyViewportChanges(const cc::ApplyViewportChangesArgs& args) override;
   void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel,
                                          bool has_scrolled_by_touch) override;
+  void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      cc::ElementId scroll_latched_element_id) override;
+  void SendScrollEndEventFromImplSide(
+      cc::ElementId scroll_latched_element_id) override;
   void RequestNewLayerTreeFrameSink() override;
   void DidInitializeLayerTreeFrameSink() override;
   void DidFailToInitializeLayerTreeFrameSink() override;
diff --git a/content/renderer/compositor/layer_tree_view_delegate.h b/content/renderer/compositor/layer_tree_view_delegate.h
index faad249f..f3ef7a52 100644
--- a/content/renderer/compositor/layer_tree_view_delegate.h
+++ b/content/renderer/compositor/layer_tree_view_delegate.h
@@ -15,6 +15,7 @@
 namespace cc {
 class LayerTreeFrameSink;
 class SwapPromise;
+struct ElementId;
 }  // namespace cc
 
 namespace viz {
@@ -41,6 +42,17 @@
       bool has_scrolled_by_wheel,
       bool has_scrolled_by_touch) = 0;
 
+  // Send overscroll DOM event when overscrolling has happened on the compositor
+  // thread.
+  virtual void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      cc::ElementId scroll_latched_element_id) = 0;
+
+  // Send scrollend DOM event when gesture scrolling on the compositor thread
+  // has finished.
+  virtual void SendScrollEndEventFromImplSide(
+      cc::ElementId scroll_latched_element_id) = 0;
+
   // Notifies that the compositor has issed a BeginMainFrame.
   virtual void BeginMainFrame(base::TimeTicks frame_time) = 0;
 
diff --git a/content/renderer/pepper/content_renderer_pepper_host_factory.cc b/content/renderer/pepper/content_renderer_pepper_host_factory.cc
index 0bd85ec..a7f102f 100644
--- a/content/renderer/pepper/content_renderer_pepper_host_factory.cc
+++ b/content/renderer/pepper/content_renderer_pepper_host_factory.cc
@@ -17,7 +17,6 @@
 #include "content/renderer/pepper/pepper_audio_input_host.h"
 #include "content/renderer/pepper/pepper_audio_output_host.h"
 #include "content/renderer/pepper/pepper_camera_device_host.h"
-#include "content/renderer/pepper/pepper_compositor_host.h"
 #include "content/renderer/pepper/pepper_file_chooser_host.h"
 #include "content/renderer/pepper/pepper_file_ref_renderer_host.h"
 #include "content/renderer/pepper/pepper_file_system_host.h"
@@ -67,19 +66,6 @@
       document_url);
 }
 
-bool CanUseCompositorAPI(RendererPpapiHost* host, PP_Instance instance) {
-  blink::WebPluginContainer* container =
-      host->GetContainerForInstance(instance);
-  if (!container)
-    return false;
-
-  GURL document_url = container->GetDocument().Url();
-  ContentRendererClient* content_renderer_client =
-      GetContentClient()->renderer();
-  return content_renderer_client->IsPluginAllowedToUseCompositorAPI(
-      document_url);
-}
-
 }  // namespace
 
 ContentRendererPepperHostFactory::ContentRendererPepperHostFactory(
@@ -107,11 +93,6 @@
 
   // Public interfaces.
   switch (message.type()) {
-    case PpapiHostMsg_Compositor_Create::ID: {
-      if (!CanUseCompositorAPI(host_, instance))
-        return nullptr;
-      return std::make_unique<PepperCompositorHost>(host_, instance, resource);
-    }
     case PpapiHostMsg_FileRef_CreateForFileAPI::ID: {
       PP_Resource file_system;
       std::string internal_path;
diff --git a/content/renderer/pepper/pepper_compositor_host.cc b/content/renderer/pepper/pepper_compositor_host.cc
deleted file mode 100644
index 74389fe..0000000
--- a/content/renderer/pepper/pepper_compositor_host.cc
+++ /dev/null
@@ -1,425 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/renderer/pepper/pepper_compositor_host.h"
-
-#include <stddef.h>
-#include <limits>
-#include <utility>
-
-#include "base/logging.h"
-#include "base/memory/shared_memory.h"
-#include "cc/layers/layer.h"
-#include "cc/layers/solid_color_layer.h"
-#include "cc/layers/texture_layer.h"
-#include "cc/trees/layer_tree_host.h"
-#include "content/public/renderer/renderer_ppapi_host.h"
-#include "content/renderer/pepper/gfx_conversion.h"
-#include "content/renderer/pepper/host_globals.h"
-#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
-#include "content/renderer/pepper/ppb_image_data_impl.h"
-#include "content/renderer/render_thread_impl.h"
-#include "gpu/command_buffer/common/mailbox.h"
-#include "gpu/command_buffer/common/sync_token.h"
-#include "ppapi/c/pp_errors.h"
-#include "ppapi/host/dispatch_host_message.h"
-#include "ppapi/host/ppapi_host.h"
-#include "ppapi/proxy/ppapi_messages.h"
-#include "ppapi/thunk/enter.h"
-#include "ppapi/thunk/ppb_image_data_api.h"
-#include "third_party/khronos/GLES2/gl2.h"
-#include "ui/gfx/geometry/size_conversions.h"
-#include "ui/gfx/transform.h"
-
-using ppapi::host::HostMessageContext;
-using ppapi::thunk::EnterResourceNoLock;
-using ppapi::thunk::PPB_ImageData_API;
-
-namespace content {
-
-namespace {
-
-bool CheckPPFloatRect(const PP_FloatRect& rect, float width, float height) {
-    const float kEpsilon = std::numeric_limits<float>::epsilon();
-    return (rect.point.x >= -kEpsilon &&
-            rect.point.y >= -kEpsilon &&
-            rect.point.x + rect.size.width <= width + kEpsilon &&
-            rect.point.y + rect.size.height <= height + kEpsilon);
-}
-
-int32_t VerifyCommittedLayer(const ppapi::CompositorLayerData* old_layer,
-                             const ppapi::CompositorLayerData* new_layer,
-                             std::unique_ptr<base::SharedMemory>* image_shm) {
-  if (!new_layer->is_valid())
-    return PP_ERROR_BADARGUMENT;
-
-  if (new_layer->color) {
-    // Make sure the old layer is a color layer too.
-    if (old_layer && !old_layer->color)
-      return PP_ERROR_BADARGUMENT;
-    return PP_OK;
-  }
-
-  if (new_layer->texture) {
-    if (old_layer) {
-      // Make sure the old layer is a texture layer too.
-      if (!old_layer->texture)
-        return PP_ERROR_BADARGUMENT;
-      // The mailbox should be same, if the resource_id is not changed.
-      if (new_layer->common.resource_id == old_layer->common.resource_id) {
-        if (new_layer->texture->mailbox != old_layer->texture->mailbox)
-          return PP_ERROR_BADARGUMENT;
-        return PP_OK;
-      }
-    }
-    if (!new_layer->texture->mailbox.Verify())
-      return PP_ERROR_BADARGUMENT;
-
-    // Make sure the source rect is not beyond the dimensions of the
-    // texture.
-    if (!CheckPPFloatRect(new_layer->texture->source_rect, 1.0f, 1.0f))
-      return PP_ERROR_BADARGUMENT;
-    return PP_OK;
-  }
-
-  if (new_layer->image) {
-    if (old_layer) {
-      // Make sure the old layer is an image layer too.
-      if (!old_layer->image)
-        return PP_ERROR_BADARGUMENT;
-      // The image data resource should be same, if the resource_id is not
-      // changed.
-      if (new_layer->common.resource_id == old_layer->common.resource_id) {
-        if (new_layer->image->resource != old_layer->image->resource)
-          return PP_ERROR_BADARGUMENT;
-        return PP_OK;
-      }
-    }
-
-    EnterResourceNoLock<PPB_ImageData_API> enter(new_layer->image->resource,
-                                                 true);
-    if (enter.failed())
-      return PP_ERROR_BADRESOURCE;
-
-    // TODO(penghuang): support all kinds of image.
-    PP_ImageDataDesc desc;
-    if (enter.object()->Describe(&desc) != PP_TRUE ||
-        desc.stride != desc.size.width * 4 ||
-        desc.format != PP_IMAGEDATAFORMAT_RGBA_PREMUL) {
-      return PP_ERROR_BADARGUMENT;
-    }
-
-    // Make sure the source rect is not beyond the dimensions of the
-    // image.
-    if (!CheckPPFloatRect(new_layer->image->source_rect,
-                          desc.size.width, desc.size.height)) {
-      return PP_ERROR_BADARGUMENT;
-    }
-
-    base::SharedMemory* shm;
-    uint32_t byte_count;
-    if (enter.object()->GetSharedMemory(&shm, &byte_count) != PP_OK)
-      return PP_ERROR_FAILED;
-
-    base::SharedMemoryHandle shm_handle =
-        base::SharedMemory::DuplicateHandle(shm->handle());
-    if (!base::SharedMemory::IsHandleValid(shm_handle))
-      return PP_ERROR_FAILED;
-
-    image_shm->reset(new base::SharedMemory(shm_handle, true));
-    if (!(*image_shm)->Map(desc.stride * desc.size.height)) {
-      image_shm->reset();
-      return PP_ERROR_NOMEMORY;
-    }
-    return PP_OK;
-  }
-
-  return PP_ERROR_BADARGUMENT;
-}
-
-}  // namespace
-
-PepperCompositorHost::LayerData::LayerData(
-    const scoped_refptr<cc::Layer>& cc,
-    const ppapi::CompositorLayerData& pp) : cc_layer(cc), pp_layer(pp) {}
-
-PepperCompositorHost::LayerData::LayerData(const LayerData& other) = default;
-
-PepperCompositorHost::LayerData::~LayerData() {}
-
-PepperCompositorHost::PepperCompositorHost(RendererPpapiHost* host,
-                                           PP_Instance instance,
-                                           PP_Resource resource)
-    : ResourceHost(host->GetPpapiHost(), instance, resource),
-      bound_instance_(nullptr),
-      weak_factory_(this) {
-  layer_ = cc::Layer::Create();
-  // TODO(penghuang): SetMasksToBounds() can be expensive if the layer is
-  // transformed. Possibly better could be to explicitly clip the child layers
-  // (by modifying their bounds).
-  layer_->SetMasksToBounds(true);
-  layer_->SetIsDrawable(true);
-}
-
-PepperCompositorHost::~PepperCompositorHost() {
-  // Unbind from the instance when destroyed if we're still bound.
-  if (bound_instance_)
-    bound_instance_->BindGraphics(bound_instance_->pp_instance(), 0);
-}
-
-bool PepperCompositorHost::BindToInstance(
-    PepperPluginInstanceImpl* new_instance) {
-  if (new_instance && new_instance->pp_instance() != pp_instance())
-    return false;  // Can't bind other instance's contexts.
-  if (bound_instance_ == new_instance)
-    return true;  // Rebinding the same device, nothing to do.
-  if (bound_instance_ && new_instance)
-    return false;  // Can't change a bound device.
-  bound_instance_ = new_instance;
-  if (!bound_instance_)
-    SendCommitLayersReplyIfNecessary();
-
-  return true;
-}
-
-void PepperCompositorHost::ViewInitiatedPaint() {
-  SendCommitLayersReplyIfNecessary();
-}
-
-void PepperCompositorHost::ImageReleased(
-    int32_t id,
-    scoped_refptr<cc::CrossThreadSharedBitmap> shared_bitmap,
-    cc::SharedBitmapIdRegistration registration,
-    const gpu::SyncToken& sync_token,
-    bool is_lost) {
-  ResourceReleased(id, sync_token, is_lost);
-}
-
-void PepperCompositorHost::ResourceReleased(int32_t id,
-                                            const gpu::SyncToken& sync_token,
-                                            bool is_lost) {
-  host()->SendUnsolicitedReply(
-      pp_resource(),
-      PpapiPluginMsg_Compositor_ReleaseResource(id, sync_token, is_lost));
-}
-
-void PepperCompositorHost::SendCommitLayersReplyIfNecessary() {
-  if (!commit_layers_reply_context_.is_valid())
-    return;
-  host()->SendReply(commit_layers_reply_context_,
-                    PpapiPluginMsg_Compositor_CommitLayersReply());
-  commit_layers_reply_context_ = ppapi::host::ReplyMessageContext();
-}
-
-void PepperCompositorHost::UpdateLayer(
-    const scoped_refptr<cc::Layer>& layer,
-    const ppapi::CompositorLayerData* old_layer,
-    const ppapi::CompositorLayerData* new_layer,
-    std::unique_ptr<base::SharedMemory> image_shm) {
-  // Always update properties on cc::Layer, because cc::Layer
-  // will ignore any setting with unchanged value.
-  gfx::SizeF size(PP_ToGfxSize(new_layer->common.size));
-  gfx::RectF clip_rect(PP_ToGfxRect(new_layer->common.clip_rect));
-
-  // Pepper API uses DIP, so we must scale the layer's coordinates to
-  // viewport in use-zoom-for-dsf.
-  float dip_to_viewport_scale = 1 / viewport_to_dip_scale_;
-  size.Scale(dip_to_viewport_scale);
-  clip_rect.Scale(dip_to_viewport_scale);
-
-  layer->SetIsDrawable(true);
-  layer->SetBlendMode(SkBlendMode::kSrcOver);
-  layer->SetOpacity(new_layer->common.opacity);
-
-  layer->SetBounds(gfx::ToRoundedSize(size));
-  layer->SetTransformOrigin(
-      gfx::Point3F(size.width() / 2, size.height() / 2, 0.0f));
-  gfx::Transform transform(gfx::Transform::kSkipInitialization);
-  transform.matrix().setColMajorf(new_layer->common.transform.matrix);
-  layer->SetTransform(transform);
-
-  // Consider a (0,0,0,0) rect as no clip rect.
-  if (new_layer->common.clip_rect.point.x != 0 ||
-      new_layer->common.clip_rect.point.y != 0 ||
-      new_layer->common.clip_rect.size.width != 0 ||
-      new_layer->common.clip_rect.size.height != 0) {
-    scoped_refptr<cc::Layer> clip_parent = layer->parent();
-    if (clip_parent.get() == layer_.get()) {
-      // Create a clip parent layer, if it does not exist.
-      clip_parent = cc::Layer::Create();
-      clip_parent->SetMasksToBounds(true);
-      clip_parent->SetIsDrawable(true);
-      layer_->ReplaceChild(layer.get(), clip_parent);
-      clip_parent->AddChild(layer);
-    }
-    auto position = clip_rect.origin();
-    clip_parent->SetPosition(position);
-    clip_parent->SetBounds(gfx::ToRoundedSize(clip_rect.size()));
-    layer->SetPosition(gfx::PointF(-position.x(), -position.y()));
-  } else if (layer->parent() != layer_.get()) {
-    // Remove the clip parent layer.
-    layer_->ReplaceChild(layer->parent(), layer);
-    layer->SetPosition(gfx::PointF());
-  }
-
-  if (new_layer->color) {
-    layer->SetBackgroundColor(SkColorSetARGB(
-        new_layer->color->alpha * 255, new_layer->color->red * 255,
-        new_layer->color->green * 255, new_layer->color->blue * 255));
-    return;
-  }
-
-  if (new_layer->texture) {
-    scoped_refptr<cc::TextureLayer> texture_layer(
-        static_cast<cc::TextureLayer*>(layer.get()));
-    if (!old_layer ||
-        new_layer->common.resource_id != old_layer->common.resource_id) {
-      auto resource = viz::TransferableResource::MakeGL(
-          new_layer->texture->mailbox, GL_LINEAR, new_layer->texture->target,
-          new_layer->texture->sync_token);
-      texture_layer->SetTransferableResource(
-          resource,
-          viz::SingleReleaseCallback::Create(base::BindOnce(
-              &PepperCompositorHost::ResourceReleased,
-              weak_factory_.GetWeakPtr(), new_layer->common.resource_id)));
-      // TODO(penghuang): get a damage region from the application and
-      // pass it to SetNeedsDisplayRect().
-      texture_layer->SetNeedsDisplay();
-    }
-    texture_layer->SetPremultipliedAlpha(new_layer->texture->premult_alpha);
-    gfx::RectF rect = PP_ToGfxRectF(new_layer->texture->source_rect);
-    texture_layer->SetUV(rect.origin(), rect.bottom_right());
-    return;
-  }
-
-  if (new_layer->image) {
-    if (!old_layer ||
-        new_layer->common.resource_id != old_layer->common.resource_id) {
-      scoped_refptr<cc::TextureLayer> image_layer(
-          static_cast<cc::TextureLayer*>(layer.get()));
-      EnterResourceNoLock<PPB_ImageData_API> enter(new_layer->image->resource,
-                                                   true);
-      DCHECK(enter.succeeded());
-
-      // TODO(penghuang): support all kinds of image.
-      PP_ImageDataDesc desc;
-      PP_Bool rv = enter.object()->Describe(&desc);
-      DCHECK_EQ(rv, PP_TRUE);
-      DCHECK_EQ(desc.stride, desc.size.width * 4);
-      DCHECK_EQ(desc.format, PP_IMAGEDATAFORMAT_RGBA_PREMUL);
-
-      viz::SharedBitmapId shared_bitmap_id = viz::SharedBitmap::GenerateId();
-      // TODO(danakj): These bitmaps could be reused for future frames instead
-      // of malloc/free for each frame.
-      auto shared_bitmap = base::MakeRefCounted<cc::CrossThreadSharedBitmap>(
-          shared_bitmap_id, std::move(image_shm), PP_ToGfxSize(desc.size),
-          viz::RGBA_8888);
-
-      cc::SharedBitmapIdRegistration registration =
-          image_layer->RegisterSharedBitmapId(shared_bitmap_id, shared_bitmap);
-
-      auto resource = viz::TransferableResource::MakeSoftware(
-          shared_bitmap_id, PP_ToGfxSize(desc.size), viz::RGBA_8888);
-      image_layer->SetTransferableResource(
-          resource,
-          viz::SingleReleaseCallback::Create(base::BindOnce(
-              &PepperCompositorHost::ImageReleased, weak_factory_.GetWeakPtr(),
-              new_layer->common.resource_id, std::move(shared_bitmap),
-              std::move(registration))));
-      // TODO(penghuang): get a damage region from the application and
-      // pass it to SetNeedsDisplayRect().
-      image_layer->SetNeedsDisplay();
-
-      // ImageData is always premultiplied alpha.
-      image_layer->SetPremultipliedAlpha(true);
-    }
-    return;
-  }
-  // Should not be reached.
-  NOTREACHED();
-}
-
-int32_t PepperCompositorHost::OnResourceMessageReceived(
-    const IPC::Message& msg,
-    HostMessageContext* context) {
-  PPAPI_BEGIN_MESSAGE_MAP(PepperCompositorHost, msg)
-  PPAPI_DISPATCH_HOST_RESOURCE_CALL(
-      PpapiHostMsg_Compositor_CommitLayers, OnHostMsgCommitLayers)
-  PPAPI_END_MESSAGE_MAP()
-  return ppapi::host::ResourceHost::OnResourceMessageReceived(msg, context);
-}
-
-bool PepperCompositorHost::IsCompositorHost() {
-  return true;
-}
-
-int32_t PepperCompositorHost::OnHostMsgCommitLayers(
-    HostMessageContext* context,
-    const std::vector<ppapi::CompositorLayerData>& layers,
-    bool reset) {
-  if (commit_layers_reply_context_.is_valid())
-    return PP_ERROR_INPROGRESS;
-
-  std::unique_ptr<std::unique_ptr<base::SharedMemory>[]> image_shms;
-  if (layers.size() > 0) {
-    image_shms.reset(new std::unique_ptr<base::SharedMemory>[layers.size()]);
-    if (!image_shms)
-      return PP_ERROR_NOMEMORY;
-    // Verfiy the layers first, if an error happens, we will return the error to
-    // plugin and keep current layers set by the previous CommitLayers()
-    // unchanged.
-    for (size_t i = 0; i < layers.size(); ++i) {
-      const ppapi::CompositorLayerData* old_layer = nullptr;
-      if (!reset && i < layers_.size())
-        old_layer = &layers_[i].pp_layer;
-      int32_t rv = VerifyCommittedLayer(old_layer, &layers[i], &image_shms[i]);
-      if (rv != PP_OK)
-        return rv;
-    }
-  }
-
-  // ResetLayers() has been called, we need rebuild layer stack.
-  if (reset) {
-    layer_->RemoveAllChildren();
-    layers_.clear();
-  }
-
-  for (size_t i = 0; i < layers.size(); ++i) {
-    const ppapi::CompositorLayerData* pp_layer = &layers[i];
-    LayerData* data = i >= layers_.size() ? nullptr : &layers_[i];
-    DCHECK(!data || data->cc_layer.get());
-    scoped_refptr<cc::Layer> cc_layer = data ? data->cc_layer : nullptr;
-    ppapi::CompositorLayerData* old_layer = data ? &data->pp_layer : nullptr;
-
-    if (!cc_layer.get()) {
-      if (pp_layer->color)
-        cc_layer = cc::SolidColorLayer::Create();
-      else if (pp_layer->texture || pp_layer->image)
-        cc_layer = cc::TextureLayer::CreateForMailbox(nullptr);
-      layer_->AddChild(cc_layer);
-    }
-
-    UpdateLayer(cc_layer, old_layer, pp_layer, std::move(image_shms[i]));
-
-    if (old_layer)
-      *old_layer = *pp_layer;
-    else
-      layers_.push_back(LayerData(cc_layer, *pp_layer));
-  }
-
-  // We need to force a commit for each CommitLayers() call, even if no layers
-  // changed since the last call to CommitLayers(). This is so
-  // WiewInitiatedPaint() will always be called.
-  if (layer_->layer_tree_host())
-    layer_->layer_tree_host()->SetNeedsCommit();
-
-  // If the host is not bound to the instance, return PP_OK immediately.
-  if (!bound_instance_)
-    return PP_OK;
-
-  commit_layers_reply_context_ = context->MakeReplyMessageContext();
-  return PP_OK_COMPLETIONPENDING;
-}
-
-}  // namespace content
diff --git a/content/renderer/pepper/pepper_compositor_host.h b/content/renderer/pepper/pepper_compositor_host.h
deleted file mode 100644
index 4824410..0000000
--- a/content/renderer/pepper/pepper_compositor_host.h
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_PEPPER_PEPPER_COMPOSITOR_HOST_H_
-#define CONTENT_RENDERER_PEPPER_PEPPER_COMPOSITOR_HOST_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "cc/resources/shared_bitmap_id_registrar.h"
-#include "ppapi/host/host_message_context.h"
-#include "ppapi/host/resource_host.h"
-#include "ppapi/shared_impl/compositor_layer_data.h"
-
-namespace base {
-class SharedMemory;
-}  // namespace
-
-namespace cc {
-class CrossThreadSharedBitmap;
-class Layer;
-}  // namespace cc
-
-namespace gpu {
-struct SyncToken;
-}  // namespace gpu
-
-namespace content {
-
-class PepperPluginInstanceImpl;
-class RendererPpapiHost;
-
-class PepperCompositorHost : public ppapi::host::ResourceHost {
- public:
-  PepperCompositorHost(RendererPpapiHost* host,
-                       PP_Instance instance,
-                       PP_Resource resource);
-  ~PepperCompositorHost() override;
-
-  // Associates this device with the given plugin instance. You can pass NULL
-  // to clear the existing device. Returns true on success. In this case, a
-  // repaint of the page will also be scheduled. Failure means that the device
-  // is already bound to a different instance, and nothing will happen.
-  bool BindToInstance(PepperPluginInstanceImpl* new_instance);
-
-  const scoped_refptr<cc::Layer> layer() { return layer_; };
-
-  void ViewInitiatedPaint();
-
-  void set_viewport_to_dip_scale(float viewport_to_dip_scale) {
-    DCHECK_LT(0, viewport_to_dip_scale_);
-    viewport_to_dip_scale_ = viewport_to_dip_scale;
-  }
-
- private:
-  void ImageReleased(int32_t id,
-                     scoped_refptr<cc::CrossThreadSharedBitmap> shared_bitmap,
-                     cc::SharedBitmapIdRegistration registration,
-                     const gpu::SyncToken& sync_token,
-                     bool is_lost);
-  void ResourceReleased(int32_t id,
-                        const gpu::SyncToken& sync_token,
-                        bool is_lost);
-  void SendCommitLayersReplyIfNecessary();
-  void UpdateLayer(const scoped_refptr<cc::Layer>& layer,
-                   const ppapi::CompositorLayerData* old_layer,
-                   const ppapi::CompositorLayerData* new_layer,
-                   std::unique_ptr<base::SharedMemory> image_shm);
-
-  // ResourceMessageHandler overrides:
-  int32_t OnResourceMessageReceived(
-      const IPC::Message& msg,
-      ppapi::host::HostMessageContext* context) override;
-
-  // ppapi::host::ResourceHost overrides:
-  bool IsCompositorHost() override;
-
-  // Message handlers:
-  int32_t OnHostMsgCommitLayers(
-      ppapi::host::HostMessageContext* context,
-      const std::vector<ppapi::CompositorLayerData>& layers,
-      bool reset);
-
-  // Non-owning pointer to the plugin instance this context is currently bound
-  // to, if any. If the context is currently unbound, this will be NULL.
-  PepperPluginInstanceImpl* bound_instance_;
-
-  // The toplevel cc::Layer. It is the parent of other cc::Layers.
-  scoped_refptr<cc::Layer> layer_;
-
-  // A list of layers. It is used for updating layers' properties in
-  // subsequent CommitLayers() calls.
-  struct LayerData {
-    LayerData(const scoped_refptr<cc::Layer>& cc,
-              const ppapi::CompositorLayerData& pp);
-    LayerData(const LayerData& other);
-    ~LayerData();
-
-    scoped_refptr<cc::Layer> cc_layer;
-    ppapi::CompositorLayerData pp_layer;
-  };
-  std::vector<LayerData> layers_;
-
-  ppapi::host::ReplyMessageContext commit_layers_reply_context_;
-
-  // The scale between the viewport and dip. This differs in
-  // use-zoom-for-dsf mode where the content is scaled by zooming.
-  float viewport_to_dip_scale_ = 1.0f;
-
-  base::WeakPtrFactory<PepperCompositorHost> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(PepperCompositorHost);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_PEPPER_PEPPER_COMPOSITOR_HOST_H_
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index d2f5b3ca..cbcb0ed 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -36,7 +36,6 @@
 #include "content/renderer/pepper/message_channel.h"
 #include "content/renderer/pepper/pepper_audio_controller.h"
 #include "content/renderer/pepper/pepper_browser_connection.h"
-#include "content/renderer/pepper/pepper_compositor_host.h"
 #include "content/renderer/pepper/pepper_file_ref_renderer_host.h"
 #include "content/renderer/pepper/pepper_graphics_2d_host.h"
 #include "content/renderer/pepper/pepper_in_process_router.h"
@@ -516,7 +515,6 @@
       viewport_to_dip_scale_(1.0f),
       sent_initial_did_change_view_(false),
       bound_graphics_2d_platform_(nullptr),
-      bound_compositor_(nullptr),
       has_webkit_focus_(false),
       has_content_area_focus_(false),
       find_identifier_(-1),
@@ -747,13 +745,11 @@
       container_->InvalidateRect(rect);
   }
 
-  cc::Layer* layer =
-      texture_layer_ ? texture_layer_.get() : compositor_layer_.get();
-  if (layer) {
+  if (texture_layer_) {
     if (rect.IsEmpty()) {
-      layer->SetNeedsDisplay();
+      texture_layer_->SetNeedsDisplay();
     } else {
-      layer->SetNeedsDisplayRect(rect);
+      texture_layer_->SetNeedsDisplayRect(rect);
     }
   }
 }
@@ -761,9 +757,7 @@
 void PepperPluginInstanceImpl::ScrollRect(int dx,
                                           int dy,
                                           const gfx::Rect& rect) {
-  cc::Layer* layer =
-      texture_layer_ ? texture_layer_.get() : compositor_layer_.get();
-  if (layer) {
+  if (texture_layer_) {
     InvalidateRect(rect);
   } else if (fullscreen_container_) {
     fullscreen_container_->ScrollRect(dx, dy, rect);
@@ -1398,8 +1392,6 @@
     bound_graphics_2d_platform_->ViewInitiatedPaint();
   else if (bound_graphics_3d_.get())
     bound_graphics_3d_->ViewInitiatedPaint();
-  else if (bound_compositor_)
-    bound_compositor_->ViewInitiatedPaint();
 }
 
 void PepperPluginInstanceImpl::SetSelectedText(
@@ -1854,9 +1846,6 @@
   if (module()->is_crashed())
     return;
 
-  if (bound_compositor_)
-    bound_compositor_->set_viewport_to_dip_scale(viewport_to_dip_scale_);
-
   if (bound_graphics_2d_platform_)
     bound_graphics_2d_platform_->set_viewport_to_dip_scale(
         viewport_to_dip_scale_);
@@ -2204,36 +2193,29 @@
   bool want_3d_layer = !!bound_graphics_3d_.get();
   bool want_2d_layer = !!bound_graphics_2d_platform_;
   bool want_texture_layer = want_3d_layer || want_2d_layer;
-  bool want_compositor_layer = !!bound_compositor_;
 
   if (throttler_ && throttler_->IsHiddenForPlaceholder()) {
     want_3d_layer = false;
     want_2d_layer = false;
     want_texture_layer = false;
-    want_compositor_layer = false;
   }
 
   if (!force_creation && (want_texture_layer == !!texture_layer_) &&
       (want_3d_layer == layer_is_hardware_) &&
-      (want_compositor_layer == !!compositor_layer_.get()) &&
       layer_bound_to_fullscreen_ == !!fullscreen_container_) {
     UpdateLayerTransform();
     return;
   }
 
-  if (texture_layer_ || compositor_layer_) {
+  if (texture_layer_) {
     if (!layer_bound_to_fullscreen_)
       container_->SetCcLayer(nullptr, false);
     else if (fullscreen_container_)
       fullscreen_container_->SetLayer(nullptr);
-    if (texture_layer_) {
-      texture_layer_->ClearClient();
-      texture_layer_ = nullptr;
-    }
-    compositor_layer_ = nullptr;
+    texture_layer_->ClearClient();
+    texture_layer_ = nullptr;
   }
 
-  cc::Layer* either_layer = nullptr;
   if (want_texture_layer) {
     bool opaque = false;
     if (want_3d_layer) {
@@ -2255,19 +2237,15 @@
     // wmode=transparent was specified.
     opaque = opaque || fullscreen_container_;
     texture_layer_->SetContentsOpaque(opaque);
-    either_layer = texture_layer_.get();
-  } else if (want_compositor_layer) {
-    compositor_layer_ = bound_compositor_->layer();
-    either_layer = compositor_layer_.get();
   }
 
-  if (either_layer) {
+  if (texture_layer_) {
     if (fullscreen_container_)
-      fullscreen_container_->SetLayer(either_layer);
+      fullscreen_container_->SetLayer(texture_layer_.get());
     else
-      container_->SetCcLayer(either_layer, true);
+      container_->SetCcLayer(texture_layer_.get(), true);
     if (is_flash_plugin_)
-      either_layer->SetMayContainVideo(true);
+      texture_layer_->SetMayContainVideo(true);
   }
 
   layer_bound_to_fullscreen_ = !!fullscreen_container_;
@@ -2448,10 +2426,6 @@
     bound_graphics_2d_platform_->BindToInstance(nullptr);
     bound_graphics_2d_platform_ = nullptr;
   }
-  if (bound_compositor_) {
-    bound_compositor_->BindToInstance(nullptr);
-    bound_compositor_ = nullptr;
-  }
 
   // Special-case clearing the current device.
   if (!device) {
@@ -2470,12 +2444,9 @@
       RendererPpapiHost::GetForPPInstance(instance)->GetPpapiHost();
   ppapi::host::ResourceHost* host = ppapi_host->GetResourceHost(device);
   PepperGraphics2DHost* graphics_2d = nullptr;
-  PepperCompositorHost* compositor = nullptr;
   if (host) {
     if (host->IsGraphics2DHost()) {
       graphics_2d = static_cast<PepperGraphics2DHost*>(host);
-    } else if (host->IsCompositorHost()) {
-      compositor = static_cast<PepperCompositorHost*>(host);
     } else {
       DLOG(ERROR) <<
           "Resource is not PepperCompositorHost or PepperGraphics2DHost.";
@@ -2488,14 +2459,7 @@
           ? static_cast<PPB_Graphics3D_Impl*>(enter_3d.object())
           : nullptr;
 
-  if (compositor) {
-    if (compositor->BindToInstance(this)) {
-      bound_compositor_ = compositor;
-      bound_compositor_->set_viewport_to_dip_scale(viewport_to_dip_scale_);
-      UpdateLayer(true);
-      return PP_TRUE;
-    }
-  } else if (graphics_2d) {
+  if (graphics_2d) {
     if (graphics_2d->BindToInstance(this)) {
       bound_graphics_2d_platform_ = graphics_2d;
       bound_graphics_2d_platform_->set_viewport_to_dip_scale(
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.h b/content/renderer/pepper/pepper_plugin_instance_impl.h
index 25d6804e..275e3c1 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.h
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.h
@@ -107,7 +107,6 @@
 class FullscreenContainer;
 class MessageChannel;
 class PepperAudioController;
-class PepperCompositorHost;
 class PepperGraphics2DHost;
 class PluginInstanceThrottlerImpl;
 class PluginModule;
@@ -708,7 +707,6 @@
 
   // NULL until we have been initialized.
   blink::WebPluginContainer* container_;
-  scoped_refptr<cc::Layer> compositor_layer_;
   scoped_refptr<cc::TextureLayer> texture_layer_;
   bool layer_bound_to_fullscreen_;
   bool layer_is_hardware_;
@@ -746,10 +744,9 @@
   // same as the default values.
   bool sent_initial_did_change_view_;
 
-  // The current device context for painting in 2D, 3D or compositor.
+  // The current device context for painting in 2D or 3D.
   scoped_refptr<PPB_Graphics3D_Impl> bound_graphics_3d_;
   PepperGraphics2DHost* bound_graphics_2d_platform_;
-  PepperCompositorHost* bound_compositor_;
 
   // We track two types of focus, one from WebKit, which is the focus among
   // all elements of the page, one one from the browser, which is whether the
diff --git a/content/renderer/pepper/plugin_module.cc b/content/renderer/pepper/plugin_module.cc
index 79c84a7..b03756a 100644
--- a/content/renderer/pepper/plugin_module.cc
+++ b/content/renderer/pepper/plugin_module.cc
@@ -63,8 +63,6 @@
 #include "ppapi/c/ppb_audio_buffer.h"
 #include "ppapi/c/ppb_audio_config.h"
 #include "ppapi/c/ppb_audio_encoder.h"
-#include "ppapi/c/ppb_compositor.h"
-#include "ppapi/c/ppb_compositor_layer.h"
 #include "ppapi/c/ppb_console.h"
 #include "ppapi/c/ppb_core.h"
 #include "ppapi/c/ppb_file_io.h"
diff --git a/content/renderer/pepper/resource_creation_impl.cc b/content/renderer/pepper/resource_creation_impl.cc
index 5f0db4a..27dfee54 100644
--- a/content/renderer/pepper/resource_creation_impl.cc
+++ b/content/renderer/pepper/resource_creation_impl.cc
@@ -78,10 +78,6 @@
   return 0;  // Not supported in-process.
 }
 
-PP_Resource ResourceCreationImpl::CreateCompositor(PP_Instance instance) {
-  return 0;  // Not supported in-process.
-}
-
 PP_Resource ResourceCreationImpl::CreateBroker(PP_Instance instance) {
   return (new PPB_Broker_Impl(instance))->GetReference();
 }
diff --git a/content/renderer/pepper/resource_creation_impl.h b/content/renderer/pepper/resource_creation_impl.h
index 13b5c04..1f80cc2 100644
--- a/content/renderer/pepper/resource_creation_impl.h
+++ b/content/renderer/pepper/resource_creation_impl.h
@@ -40,7 +40,6 @@
                                 uint32_t sample_frame_count) override;
   PP_Resource CreateAudioInput(PP_Instance instance) override;
   PP_Resource CreateAudioOutput(PP_Instance instance) override;
-  PP_Resource CreateCompositor(PP_Instance instance) override;
   PP_Resource CreateBroker(PP_Instance instance) override;
   PP_Resource CreateBuffer(PP_Instance instance, uint32_t size) override;
   PP_Resource CreateCameraDevicePrivate(PP_Instance instance) override;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 1837715..02933bf 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -984,6 +984,11 @@
 
   navigation_params->is_user_activated =
       commit_params.was_activated == WasActivatedOption::kYes;
+
+  if (commit_params.origin_to_commit) {
+    navigation_params->origin_to_commit =
+        commit_params.origin_to_commit.value();
+  }
 }
 
 }  // namespace
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 99357db..c71e60f 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -932,6 +932,21 @@
                                                     has_scrolled_by_touch);
 }
 
+void RenderWidget::SendOverscrollEventFromImplSide(
+    const gfx::Vector2dF& overscroll_delta,
+    cc::ElementId scroll_latched_element_id) {
+  if (!GetWebWidget())
+    return;
+  GetWebWidget()->SendOverscrollEventFromImplSide(overscroll_delta,
+                                                  scroll_latched_element_id);
+}
+void RenderWidget::SendScrollEndEventFromImplSide(
+    cc::ElementId scroll_latched_element_id) {
+  if (!GetWebWidget())
+    return;
+  GetWebWidget()->SendScrollEndEventFromImplSide(scroll_latched_element_id);
+}
+
 void RenderWidget::BeginMainFrame(base::TimeTicks frame_time) {
   if (!GetWebWidget())
     return;
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index d35401d..cece012 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -302,6 +302,11 @@
   void ApplyViewportChanges(const cc::ApplyViewportChangesArgs& args) override;
   void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel,
                                          bool has_scrolled_by_touch) override;
+  void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      cc::ElementId scroll_latched_element_id) override;
+  void SendScrollEndEventFromImplSide(
+      cc::ElementId scroll_latched_element_id) override;
   void BeginMainFrame(base::TimeTicks frame_time) override;
   void RequestNewLayerTreeFrameSink(
       LayerTreeFrameSinkCallback callback) override;
diff --git a/content/shell/renderer/shell_content_renderer_client.cc b/content/shell/renderer/shell_content_renderer_client.cc
index deab3f9..c3aa914f 100644
--- a/content/shell/renderer/shell_content_renderer_client.cc
+++ b/content/shell/renderer/shell_content_renderer_client.cc
@@ -164,16 +164,6 @@
   }
 }
 
-bool ShellContentRendererClient::IsPluginAllowedToUseCompositorAPI(
-    const GURL& url) {
-#if BUILDFLAG(ENABLE_PLUGINS)
-  return base::CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnablePepperTesting);
-#else
-  return false;
-#endif
-}
-
 bool ShellContentRendererClient::IsPluginAllowedToUseDevChannelAPIs() {
 #if BUILDFLAG(ENABLE_PLUGINS)
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/content/shell/renderer/shell_content_renderer_client.h b/content/shell/renderer/shell_content_renderer_client.h
index 5a30271..f0a2f39 100644
--- a/content/shell/renderer/shell_content_renderer_client.h
+++ b/content/shell/renderer/shell_content_renderer_client.h
@@ -42,7 +42,6 @@
 
   // TODO(mkwst): These toggle based on the kEnablePepperTesting flag. Do we
   // need that outside of web tests?
-  bool IsPluginAllowedToUseCompositorAPI(const GURL& url) override;
   bool IsPluginAllowedToUseDevChannelAPIs() override;
 
   void DidInitializeWorkerContextOnWorkerThread(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 1963f0f..fbf64b53 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1381,6 +1381,7 @@
     "../browser/devtools/protocol_unittest.cc",
     "../browser/dom_storage/dom_storage_area_unittest.cc",
     "../browser/dom_storage/dom_storage_context_impl_unittest.cc",
+    "../browser/dom_storage/dom_storage_context_wrapper_unittest.cc",
     "../browser/dom_storage/dom_storage_database_unittest.cc",
     "../browser/dom_storage/local_storage_context_mojo_unittest.cc",
     "../browser/dom_storage/session_storage_area_impl_unittest.cc",
diff --git a/content/test/stub_layer_tree_view_delegate.h b/content/test/stub_layer_tree_view_delegate.h
index 31ddc36f..1e98a9b9c 100644
--- a/content/test/stub_layer_tree_view_delegate.h
+++ b/content/test/stub_layer_tree_view_delegate.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_TEST_STUB_LAYER_TREE_VIEW_DELEGATE_H_
 #define CONTENT_TEST_STUB_LAYER_TREE_VIEW_DELEGATE_H_
 
+#include "cc/trees/element_id.h"
 #include "content/renderer/compositor/layer_tree_view_delegate.h"
 
 namespace cc {
@@ -19,6 +20,11 @@
   void ApplyViewportChanges(const cc::ApplyViewportChangesArgs&) override {}
   void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel,
                                          bool has_scrolled_by_touch) override {}
+  void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      cc::ElementId scroll_latched_element_id) override {}
+  void SendScrollEndEventFromImplSide(
+      cc::ElementId scroll_latched_element_id) override {}
   void BeginMainFrame(base::TimeTicks frame_time) override {}
   void RecordEndOfFrameMetrics(base::TimeTicks) override {}
   void RequestNewLayerTreeFrameSink(
diff --git a/docs/infra/cq_builders.md b/docs/infra/cq_builders.md
index 9251e21..b1826b9 100644
--- a/docs/infra/cq_builders.md
+++ b/docs/infra/cq_builders.md
@@ -115,12 +115,6 @@
   Path regular expressions:
     * [`//ios/.+`](https://cs.chromium.org/chromium/src/ios/)
 
-* [linux-blink-gen-property-trees](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-blink-gen-property-trees) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux-blink-gen-property-trees)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-blink-gen-property-trees))
-
-  Path regular expressions:
-    * [`//third_party/blink/web_tests/FlagExpectations/enable-blink-features=BlinkGenPropertyTrees`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/FlagExpectations/enable-blink-features=BlinkGenPropertyTrees)
-    * [`//third_party/blink/web_tests/flag-specific/enable-blink-features=BlinkGenPropertyTrees`](https://cs.chromium.org/search/?q=package:%5Echromium$+file:third_party/blink/web_tests/flag-specific/enable-blink-features=BlinkGenPropertyTrees)
-
 * [linux-blink-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-blink-rel) ([`cq.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:cq.cfg+linux-blink-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-blink-rel))
 
   Path regular expressions:
diff --git a/docs/security/url_display_guidelines/blank_spoofing.png b/docs/security/url_display_guidelines/blank_spoofing.png
new file mode 100644
index 0000000..5280f12
--- /dev/null
+++ b/docs/security/url_display_guidelines/blank_spoofing.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/decimal_ip.png b/docs/security/url_display_guidelines/decimal_ip.png
new file mode 100644
index 0000000..dc0bdfc
--- /dev/null
+++ b/docs/security/url_display_guidelines/decimal_ip.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/elision.jpg b/docs/security/url_display_guidelines/elision.jpg
new file mode 100644
index 0000000..f73d7af
--- /dev/null
+++ b/docs/security/url_display_guidelines/elision.jpg
Binary files differ
diff --git a/docs/security/url_display_guidelines/filesystem_uri.png b/docs/security/url_display_guidelines/filesystem_uri.png
new file mode 100644
index 0000000..72af9e2
--- /dev/null
+++ b/docs/security/url_display_guidelines/filesystem_uri.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/hex_ip.png b/docs/security/url_display_guidelines/hex_ip.png
new file mode 100644
index 0000000..a019a08
--- /dev/null
+++ b/docs/security/url_display_guidelines/hex_ip.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/highlighting.png b/docs/security/url_display_guidelines/highlighting.png
new file mode 100644
index 0000000..32925ca
--- /dev/null
+++ b/docs/security/url_display_guidelines/highlighting.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/ip_example.png b/docs/security/url_display_guidelines/ip_example.png
new file mode 100644
index 0000000..41ed5ab
--- /dev/null
+++ b/docs/security/url_display_guidelines/ip_example.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/ipv6.png b/docs/security/url_display_guidelines/ipv6.png
new file mode 100644
index 0000000..27ab0cc9
--- /dev/null
+++ b/docs/security/url_display_guidelines/ipv6.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/noncanonical_ipv6.png b/docs/security/url_display_guidelines/noncanonical_ipv6.png
new file mode 100644
index 0000000..c84a809
--- /dev/null
+++ b/docs/security/url_display_guidelines/noncanonical_ipv6.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/not_secure.png b/docs/security/url_display_guidelines/not_secure.png
new file mode 100644
index 0000000..038f770
--- /dev/null
+++ b/docs/security/url_display_guidelines/not_secure.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/notsecure_blob_uri.png b/docs/security/url_display_guidelines/notsecure_blob_uri.png
new file mode 100644
index 0000000..2cee318
--- /dev/null
+++ b/docs/security/url_display_guidelines/notsecure_blob_uri.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/notsecure_data_uri.png b/docs/security/url_display_guidelines/notsecure_data_uri.png
new file mode 100644
index 0000000..0b11877c
--- /dev/null
+++ b/docs/security/url_display_guidelines/notsecure_data_uri.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/notsecure_filesystem_uri.png b/docs/security/url_display_guidelines/notsecure_filesystem_uri.png
new file mode 100644
index 0000000..f63000c0
--- /dev/null
+++ b/docs/security/url_display_guidelines/notsecure_filesystem_uri.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/url_components.png b/docs/security/url_display_guidelines/url_components.png
new file mode 100644
index 0000000..35c1adb
--- /dev/null
+++ b/docs/security/url_display_guidelines/url_components.png
Binary files differ
diff --git a/docs/security/url_display_guidelines/url_display_guidelines.md b/docs/security/url_display_guidelines/url_display_guidelines.md
new file mode 100644
index 0000000..135f679
--- /dev/null
+++ b/docs/security/url_display_guidelines/url_display_guidelines.md
@@ -0,0 +1,333 @@
+# Guidelines for URL Display
+
+*This document covers the best practices and pitfalls for building UI to display URLs in browsers and other apps. It covers the main categories of problems and challenges that we’ve seen in building Chrome. The guidance is intended to be generally applicable, but includes some Chrome-specific notes throughout.*
+
+[TOC]
+
+## Background
+
+The [URL](https://url.spec.whatwg.org) displayed in a user agent's address bar is often the only security context directly exposed to users, and therefore in many cases it is the only signal users can reasonably rely upon to determine whether or not they should trust a particular website.
+
+### Components of URLs
+
+A URL is made up of a number of components, the majority of which should not have any impact on security decisions:
+
+![Components of a URL](url_components.png)
+
+Usually, the security context that the user cares about is the **[registrable domain](#registrabledomain)** of the top-level page’s URL's origin, even when a given page is made up of components from many different origins. The registrable domain typically consists of a subdomain of an entry on the [Public Suffix list](https://publicsuffix.org/). For instance, bbc.co.uk is a registrable domain under the co.uk public suffix.  The **fully-qualified hostname** consists of a registrable domain, and optionally one or more **subdomain** labels.
+
+The **scheme** of the URL determines the protocol by which the content from the URL is delivered. If the scheme refers to a non-secure protocol like HTTP, and especially if the protocol traverses an untrusted network, the **registrable domain** information may not accurately describe the true source of the content because the content may have been modified by a **man-in-the-middle** on the network. The **port** number only needs to be specified if it is not the default for the scheme (e.g., 80 for HTTP, 443 for HTTPS).
+
+Other components of the URL (**subdomain**, **userinfo**, **path**, **query**, and **fragment**) are completely under the control of the website and may be crafted in an attempt to spoof the user by misrepresenting the registrable domain.
+
+### Challenges and Threats
+
+Malicious websites are motivated to misrepresent their provenance in order to trick visitors into performing an unsafe action (e.g., phishing, malware install) or to otherwise grant unwarranted trust in the information provided by the site (e.g., "fake news").
+
+As the web platform becomes more capable (introducing new features like device access, etc.), the importance of evaluating the source of web content grows more important and the desire to misrepresent its origins by bad actors increases.
+
+## Best Practices
+
+### TL;DR: Advice on What to Display
+
+**Where possible, avoid displaying URLs**, especially when the user is likely to be making a trust decision. Instead, display only the **[origin](#simplify)**. Additionally, if the connection is **not secure**, add an indicator to that effect:
+
+![image alt text](not_secure.png)
+
+If the URL display only applies to secure URLs (for example, a permission prompt that can only be requested by HTTPS pages), omit the scheme and non-default ports (443 for HTTPS).
+
+More detailed guidance follows.
+
+### Show Origins At All Times
+
+To avoid spoofing, the current best practice is to always show the
+origin. Whenever the user is interacting with web content, they might be making
+a security decision ("Should I enter my password?", “Should I trust the
+information on this site?”, etc.) and the origin is what we currently rely on to
+help them make that decision.
+
+It’s also important to show an origin when Chrome is explicitly asking the user to make a security decision.
+
+Currently, in Chrome, origins are hidden in the following special cases:
+
+* Chrome for Android, when a user scrolls down
+
+* Fullscreen mode across platforms
+
+* Installed PWAs on Android
+
+    * The origin is shown in the install dialog
+
+* Installed PWAs on Desktop after animation
+
+    * The origin is shown in the install dialog
+
+### Use Only Security-Reviewed Libraries to Canonicalize and Parse Strings into URLs
+
+Parsing and canonicalization of URLs is extremely [error-prone](https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf), and mistakes can introduce both spoofing vulnerabilities and lower-level problems that can lead to vulnerabilities as severe as remote code execution.
+
+Rather than attempting to parse components from a string directly, you should always obtain components of the URL via a trusted object (e.g., GURL/KURL).
+
+### Display URLs in Canonical Form
+
+In virtually all cases, you should display URLs in **canonical** form, and you should rely on trusted libraries to perform such canonicalization. Canonicalization includes a number of steps that help ensure that URLs are in their simplest form and the one a user is most likely to be able to understand.
+
+Canonicalization includes:
+
+1. Normalizing the hostname (e.g., convert to lowercase, perform IDN-related normalizations)
+
+2. Stripping default ports
+
+3. Path simplification
+
+4. Unnecessarily-encoded octets are decoded
+
+For example, canonicalization converts `https://ExAmPle.com:443/one/%2e./Tw%2fo/` to the canonical form, `https://example.com/Tw%2fo/`.
+
+Do not attempt to write your own canonicalizer.
+
+<a name="simplify"></a>
+### Simplify URLs Whenever Possible
+
+* [Do NOT display the username and password components](https://url.spec.whatwg.org/#url-rendering) of URLs (e.g., `https://user:password@example.com/`) anywhere the user is making a security decision.
+
+    * As these credentials may be sensitive, consider omitting them wherever possible (e.g., do not include username and password when generating printouts).
+
+* When the primary purpose of displaying a URL is to have the user make a security decision, display the **origin**, or if scheme is always HTTPS, just the **domain**. Omit the path, query string, fragment, and any other components of the URL because they provide opportunities for spoofing.
+
+    * Do not display the scheme if it will always be https://. If the scheme is not https://, prefer to show a security indicator icon (dangerous triangle icon + "Not Secure" string on http://) rather than the scheme itself.
+
+    * In Chrome, we often remove subdomains "www" and “m” as a special case to simplify the origin, except when they are part of the registrable domain. You can use the [`kFormatUrlOmitTrivialSubdomains`](https://cs.chromium.org/chromium/src/components/url_formatter/url_formatter.h?q=kFormatUrlOmitTrivial&sq=package:chromium&g=0&l=63) flag for `url_formatter::FormatURL` ([example usage](https://cs.chromium.org/chromium/src/components/history/core/browser/history_backend.cc?type=cs&q=OmitTrivialSubdomains&sq=package:chromium&g=0&l=127)).
+
+    * Omit default ports (80 for http, 443 for https).
+
+    * If in a space-constrained environment, it's acceptable to use registrable domain instead of the full origin. (e.g., [AMP for CCT](https://docs.google.com/presentation/d/1SNAvoQdXvQsYJAdMJbdCaRvzP3fUYKS87dknrHDV4xU/edit#slide=id.p))
+
+### Eliding URLs
+
+* When the full hostname cannot be displayed, elide labels starting from the front. (Right-to-Left character support means that the *front* of the string may not appear at the *left*). (Note that Chrome's omnibox behavior on desktop is currently [buggy](https://bugs.chromium.org/p/chromium/issues/detail?id=527638) in this respect.)
+
+* Ensure that at least the registrable domain can be shown, to avoid showing **...paypal.com** when loading `https://not-really-paypal.com`.
+
+ ![URL elision examples](elision.jpg)
+
+### Highlighting
+
+If showing the full URL is deemed necessary, consider highlighting to help the user focus on the most security relevant information in the URL.
+
+* Ideally, consider highlighting only the registrable domain component, to increase awareness of situations where a malicious site uses a misleading subdomain in an attempt to fool the user:
+
+![URL highlighting example](highlighting.png)
+
+(Note that Chrome's omnibox does [not](https://bugs.chromium.org/p/chromium/issues/detail?id=527638#c6) currently do this style of highlighting.)
+
+### URL Length
+
+In general, the *web platform* does not have limits on the length of URLs (although 2^31 is a common limit). *Chrome* limits URLs to a maximum length of **2MB** for practical reasons and to avoid causing denial-of-service problems in inter-process communication.
+
+On most platforms, Chrome’s omnibox limits URL display to **32kB** (`kMaxURLDisplayChars`) although a **1kB** limit is used on VR platforms.
+
+Ensure that the client behaves reasonably if the length of the URL exceeds any limits:
+
+* Origin information appears at the start of the URL, so truncating the end is typically harmless.
+
+* Rendering a URL as an empty string in edge cases is not ideal, but truncating poorly (or crashing unexpectedly and insecurely) could be worse.
+
+* Attackers may use long URLs to abuse other parts of the system. [DNS syntax](https://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax) limits fully-qualified hostnames to **253 characters** and each [label](#label) in the hostname to **63 characters**, but Chromium's GURL class does not enforce this limit.
+
+### Display Font
+
+Selection of fonts can have a meaningful impact on the security of a URL display. Relevant factors include:
+
+* Visual distinctiveness for visually-similar characters and sequences (**0** vs **o**, **1** vs **l**, **w** vs. **vv**, etc.), especially in the ASCII character set:
+
+![Visually similar characters in a URL](visually_similar.png)
+
+* Glyphs for all necessary characters, including those used in International Domain Names
+
+If a font is missing a glyph for a character, it may render as a blank, leading to the possibility of a spoofing vulnerability like [714196](https://crbug.com/714196).
+
+![Spoofing vulnerability from a missing glyph](blank_spoofing.png)
+
+Depending on the platform, font fallback may be used.
+
+### Display of International Characters
+
+Non-English characters present a huge attack surface for spoofing attacks but are today *relatively* uncommon in the host component of URLs worldwide. As global use of the web continues to grow, use of International Domain Names is expected to grow and URL displays should not be overly English-centric.
+
+The primary threat of non-ASCII text in URLs is a [homoglyph attack](https://en.wikipedia.org/wiki/Homoglyph) in which ASCII characters are swapped with visually-identical (or near-identical) characters.
+
+#### IDN Display Restrictions in Chrome
+
+To mitigate URL spoofing attacks, Chrome follows a set of [rules](https://www.chromium.org/developers/design-documents/idn-in-google-chrome) that determine whether a given International Domain Name is shown in Unicode characters or degrades to show the ASCII [Punycode](https://en.wikipedia.org/wiki/Punycode) string (starting with "xn--"). The Punycode string is not intended to be human-readable: display of Punycode is instead only intended to defeat homoglyph attacks.
+
+Chrome’s [`FormatURLForSecurityDisplay`](https://cs.chromium.org/chromium/src/components/url_formatter/elide_url.h?q=FormatURLForS&sq=package:chromium&l=108) function encapsulates this and other behaviors.
+
+* Follow Chrome’s rules for [IDN Display](https://www.chromium.org/developers/design-documents/idn-in-google-chrome) to avoid homograph attacks.
+
+* For the benefit of technical users, *consider* displaying an indicator when a URL contains non-ASCII characters. To date, Chrome has not implemented such an indicator. By way of comparison, Internet Explorer shows an icon in the address bar which opens a bubble that provides more information. An international URL indicator generally *should not* be positioned as a warning, but should allow the user to get more information about the content of the URL. Security-savvy users may find this UI useful in detecting spoofs and escalating reports to more effective security mechanisms (e.g., SafeBrowsing).
+
+#### RTL
+
+* Ensure that URLs are sanitized to prevent abuse by [Right-to-Left override characters](https://unicode.org/reports/tr9/#Explicit_Directional_Embeddings).
+
+    * In Chrome Views code, a view that solely contains a URL should use `DIRECTIONALITY_AS_URL`, which handles everything correctly and will be updated if the spec changes.
+
+    * Follow the advice in [Sneaky Unicode Characters](#sneakyunicode) below to remove all explicit overrides.
+
+    * Wrap the URL in U+202A ... U+202C, so that it is forced to appear in a LTR paragraph. The URL standard (both [RFC 3987](https://www.ietf.org/rfc/rfc3987.txt) and https://url.spec.whatwg.org) mandate that URLs are displayed in LTR paragraphs, no matter what they contain.
+
+<a id="sneakyunicode"></a>
+#### Sneaky Unicode Characters
+
+An attacker may abuse whitespace and line-wrapping characters in order to push the display of their true origin out of view. Such characters should be banned or displayed in %-escaped form.
+
+* Non-breaking spaces (U+00A0) and other invisible characters (e.g., U+2000 to U+200A inclusive).
+
+* Unicode line terminators (e.g., U+2028, U+2029, and U+0085)
+
+* Unicode explicit directional formatting commands (e.g., U+200E--U+200F, U+202A--U+202E, see the full [list](http://unicode.org/reports/tr9/#Directional_Formatting_Characters) and [`ShouldUnescapeCodePoint`](https://cs.chromium.org/chromium/src/net/base/escape.cc?l=172&rcl=dc22553340d5c4dda162f17a07d706748be44042)).
+
+* Characters that look like security UI (e.g., U+1F512 🔒).
+    - Emoji may be confusing because users are not accustomed to seeing graphics in URL displays and may be misled into believing that they represent claims on the browser’s part (e.g., the Lock emoji). See issue [746350](https://bugs.chromium.org/p/chromium/issues/detail?id=746350).
+
+* Use of [Combining characters](https://blog.emojipedia.org/fun-emoji-hacks/) to create look-alikes
+
+In Chromium, this is handled by routines in `net/base/escape.h`. Outside of Chromium, consult `ShouldUnescapeCodePoint` in `net/base/escape.cc` for guidance.
+
+### Literal IP Addresses
+
+A URL may contain an IPv4 or IPv6 literal in the host component instead of a fully-qualified hostname:
+
+![URL with an IP literal hostname](ip_example.png)
+
+#### IPv4
+
+The canonical representation of an IPv4 literal is [dotted-decimal](https://en.wikipedia.org/wiki/Dot-decimal_notation) (aka "dotted quad") notation, but many surfaces will accept a 32-bit decimal integer (or even [octal](https://bugs.chromium.org/p/chromium/issues/detail?id=787361) or hex) IP representation:
+
+![URL with a decimal IP literal hostname](decimal_ip.png)
+![URL with a hex IP literal hostname](hex_ip.png)
+
+IPv4 literals should be converted to canonical form for display. If your code makes any sort of formatting or security decision based on the [absence of dots within a hostname](https://blogs.msdn.microsoft.com/ieinternals/2014/03/06/browser-arcana-ip-literals-in-urls/), be sure that it isn’t fooled by undotted IP literals.
+
+##### IPv6
+
+IPv6 literals use colons as a delimiter and the literal is wrapped in square brackets (this allows disambiguating the colons in the literal from the colon which begins the port component of the URL).
+
+![URL with an IPv6 literal hostname](ipv6.png)
+
+If an IPv6 Literal contains a Zone component, the % delimiter must be [escaped to %25](https://en.wikipedia.org/wiki/IPv6_address#Use_of_zone_indices_in_URIs).
+
+http://[fe80::1ff:fe23:4567:890a%25eth0]/
+
+Most surfaces will accept non-canonical forms of IPv6 literals:
+
+![URL with a non-canonical IPv6 literal hostname](noncanonical_ipv6.png)
+
+....but these should be converted to canonical form for display.
+
+### Uncommon Schemes and Virtual URLs
+
+While many users have at least *some* familiarity with HTTP and HTTPS URLs, the browser offers a number of more exotic URL schemes with which users have no experience.
+
+Where possible, limit acceptance of URLs outside of the most common schemes (e.g., HTTP/HTTPS) to reduce risk.
+
+[data:// URIs](https://en.wikipedia.org/wiki/Data_URI_scheme) carry the response data directly within the URL, meaning that they do not need to hit the network. Attackers have tried to spoof users by putting what *looks like* an origin at the front of the URL string, so Chrome no longer allows websites to navigate directly to Data URIs in the top frame and also labels Data URIs as "Not secure" in the event that a user loads one via other means. filesystem URIs (a Chrome-specific scheme) receive the same treatment.
+
+![data:// URI with a Not Secure label](notsecure_data_uri.png)
+
+filesystem URIs and blob URIs embed the origin from which they originated as the first component after the URL scheme.
+
+![blob URI with a Not Secure label](notsecure_blob_uri.png)
+
+![filesystem URI with a Not Secure label](notsecure_filesystem_uri.png)
+
+![filesystem URI](filesystem_uri.png)
+
+In Chrome, file:// schemed URIs do not contain a host component; be sure that your UI accounts for this possibility
+
+**view-source:** is a special URL scheme which wraps another scheme (e.g., `view-source:https://example.com`) and displays the document in a special Blink "view-source" mode. For security reasons, web content cannot navigate directly to view-source URLs.
+
+In Chrome, a **virtual URL** is roughly the URL displayed in the omnibox even though the real url is something different. An example is `chrome://newtab/` -- under the hood it is either a local version or a remote one but that shouldn’t matter to the user who sees it as the New Tab Page. URL spoofs can easily result if the URL display surface fails to update the URL upon a navigation to a resource which is not intended to be rendered under the virtual URL (e.g., [750298](https://bugs.chromium.org/p/chromium/issues/detail?id=750298)).
+
+### %-Escaping
+
+The [URL Standard](https://url.spec.whatwg.org/#url-rendering) suggests that *the path, query, and fragment components of the URL should have their sequences of percent-encoded bytes replaced with code points resulting from percent decoding those sequences converted to bytes, unless that renders those sequences invisible.*
+
+This is generally a user-experience feature (some sites strive to use human-readable URLs and %-escaped characters are not human readable) but could lead to spoofing attacks if performed incorrectly.
+
+Chrome’s [`FormatUrl`](https://cs.chromium.org/chromium/src/components/url_formatter/url_formatter.h?l=100&rcl=1deab0dd75a1659e44b8159d60de9cf26dc3dbf0) function takes an [`UnescapeRule`](https://cs.chromium.org/chromium/src/net/base/escape.h?l=64&rcl=ecba19472b9290092745e9846edd0d6fd8dcc48b) parameter that determines what components should be decoded for display. As of Chrome 65, we unescape path, query, and fragment components for display.
+
+Space and other invisible characters should be displayed in encoded form.
+
+Example URL with space and emoji:
+
+     https://bayden.com/test/🍌/fragment#space    🍌
+
+## Glossary
+
+A **homograph** (or **homoglyph**) **attack** occurs when an attacker uses lookalike characters to make one URL resemble another. This can occur purely in ASCII (e.g., **1** looks like **l**, **vv** looks like **w**), or when using Unicode characters via the **International Domain Names** mechanism.
+
+**International Domain Names** is a mechanism for using Unicode characters for hostnames. Under the covers, the Unicode labels are encoded using **[punycode](https://tools.ietf.org/html/rfc3492)** and prefixed with **xn--**. The browser may display the label in Unicode, or in the underlying punycode form. Users are not expected to be able to decode the punycode form: display in this form is intended to foil spoofing attempts.
+
+<a id="label"></a>A **[label](http://en.wikipedia.org/wiki/DNS_label#Parts_of_a_domain_name)** is a single component of a [domain name](http://en.wikipedia.org/wiki/Domain_name) string, delimited by periods. For instance, "*www*", “*microsoft*”, and “*com*” are the three labels in the domain name “*www.microsoft.com*”.
+
+A **plain hostname** is an unqualified, single label hostname like "*Payroll*", which typically refers to a server on a local intranet.
+
+A **[Public Suffix](http://publicsuffix.org/)** is the suffix portion of a FQDN under which independent entities may register subdomains. For example, *ltd.co.im* is a Public Suffix. A Public Suffix contains one or more labels. Sometimes the term "[effective TLD](https://wiki.mozilla.org/Gecko:Effective_TLD_List)" is used as a synonym.
+
+<a id="registrabledomain"></a>The **registrable domain** is the public suffix plus one additional label. Sometimes eTLD+1 is used as a synonym.
+
+## A Caveat on Security Sensitive Surfaces
+
+It should be noted that *many* displays of URLs in the web platform occur on surfaces [not deemed securable](https://docs.google.com/document/d/11-SXwzCGBlk8q1cNtb7peZjb2UjRPrKSFhOfZhTOz24/edit#).
+
+Any URL displayed below the browser’s "[line of death](https://textslashplain.com/2017/01/14/the-line-of-death/)" is usually *inherently* spoofable (insofar as web content can usually fake the entire UI). In particular, this means that UI helpers like the [Status Bubble](https://dev.chromium.org/user-experience/status-bubble#TOC-Lack-of-Security) are inherently untrustworthy.
+
+## Out-of-Scope for this Document
+
+### Dynamic Displays
+
+This document focuses on best practices for "static" display of URLs: that is, the display of a single URL.
+
+URL spoofing vulnerabilities also include "dynamic" spoofing attacks that rely upon the browser failing to update a URL display (or failing to do so in a timely manner) based on an asynchronous operation.
+
+If your UI allows for asynchronous operations (e.g., user can interact with one context while another loads), care must be taken to avoid mismatch between the "current" and the “loading” context.
+
+The most common dynamic attacks rely upon the fact that navigation and script execution are typically asynchronous operations.
+
+* An attacker starts on a page with a trusted URL and then navigates that context to malicious content. The user sees the outdated trusted URL and interacts with the malicious content, believing that it is under the control of the trusted site.
+
+* Alternatively, the attack starts on a malicious page which begins a navigation to a trusted URL. The user sees the pending trusted URL and interacts with the malicious content believing it is under the control of the trusted site.
+
+Dynamic attacks are particularly prevalent and challenging to mitigate in UI surfaces used both for URL input *and* display, such as the omnibox.
+
+An additional complexity arises from the fact that some forms of navigation (those which result in a HTTP/204 response or a file download) are not expected to update the URL shown in the omnibox. As a consequence, the precise timing of updates to the URL shown in the omnibox is a critical factor in the security of the system and a common source of vulnerability. Chrome has been discussing possible UI treatments to more clearly differentiate pending and committed URLs in [crbug.com/719856](https://crbug.com/719856).
+
+However, because dynamic attacks have little in common with static attacks and are generally very tightly tied to the behavior of the URL display surface, they are out of scope for this document.
+
+### Misleading Domain Names
+
+The web security model expects the user to recognize which hosts are backed by legitimate organizations (`google.com`) and which are not (`google-update.com`).
+
+For the purposes of this document, we do not concern ourselves with the ability of a website to obtain a misleading domain name that is visually distinctive from a legitimate domain name.
+
+There exist features, like Safe Browsing and EV certificate display, which may act as a supplement to origin display, but these features are not available in all situations and are often "best effort." Replacing or augmenting human analysis of origins remains an active topic of research and [brainstorming](https://medium.com/@owencm/rethinking-url-bars-as-primary-browser-ui-e2118339d2c0).
+
+### "Proxy" Domain Names
+
+In some cases, a domain owner is willing to supply content from a third-party within their own address space, leading to potential confusion about the ownership and source of the content displayed.
+
+This is, generally, outside of the client threat-model, although in some cases (e.g., AMP), the client platform may attempt to introduce new UI to clarify the situation.
+
+## Further Reading
+
+* [The Trouble with URLs](https://docs.google.com/presentation/d/1Nr47m1qlLjV8xZfw03jVKU3GQpSwbC0PWLHhXcyMcQM/) (LocoMoco Sec Conference talk)
+
+* [Chris Palmer’s Problems of URLs](https://noncombatant.org/2017/11/07/problems-of-urls/)
+
+* [Rethinking URL bars](https://medium.com/@owencm/rethinking-url-bars-as-primary-browser-ui-e2118339d2c0)
+
+* [Chromium’s URL Formatter Component (C++)](https://cs.chromium.org/chromium/src/components/url_formatter/url_formatter.cc)
+
+* [URL interop issues across specs](https://github.com/bagder/docs/blob/master/URL-interop.md)
\ No newline at end of file
diff --git a/docs/security/url_display_guidelines/visually_similar.png b/docs/security/url_display_guidelines/visually_similar.png
new file mode 100644
index 0000000..6ad027ac
--- /dev/null
+++ b/docs/security/url_display_guidelines/visually_similar.png
Binary files differ
diff --git a/gpu/command_buffer/client/client_font_manager.cc b/gpu/command_buffer/client/client_font_manager.cc
index b0985c5..6dc6f26 100644
--- a/gpu/command_buffer/client/client_font_manager.cc
+++ b/gpu/command_buffer/client/client_font_manager.cc
@@ -11,7 +11,7 @@
 
 class Serializer {
  public:
-  Serializer(char* memory, size_t memory_size)
+  Serializer(char* memory, uint32_t memory_size)
       : memory_(memory), memory_size_(memory_size) {}
   ~Serializer() = default;
 
@@ -21,7 +21,7 @@
     WriteData(val, sizeof(T), alignof(T));
   }
 
-  void WriteData(const void* input, size_t bytes, size_t alignment) {
+  void WriteData(const void* input, uint32_t bytes, size_t alignment) {
     AlignMemory(bytes, alignment);
     if (bytes == 0)
       return;
@@ -32,7 +32,7 @@
   }
 
  private:
-  void AlignMemory(size_t size, size_t alignment) {
+  void AlignMemory(uint32_t size, size_t alignment) {
     // Due to the math below, alignment must be a power of two.
     DCHECK_GT(alignment, 0u);
     DCHECK_EQ(alignment & (alignment - 1), 0u);
@@ -46,8 +46,8 @@
   }
 
   char* memory_ = nullptr;
-  size_t memory_size_ = 0u;
-  size_t bytes_written_ = 0u;
+  uint32_t memory_size_ = 0u;
+  uint32_t bytes_written_ = 0u;
 };
 
 }  // namespace
@@ -109,7 +109,7 @@
   std::vector<uint8_t> strike_data;
   strike_server_.writeStrikeData(&strike_data);
 
-  const uint64_t num_handles_created =
+  const uint32_t num_handles_created =
       last_allocated_handle_id_ - last_serialized_handle_id_;
   if (strike_data.size() == 0u && num_handles_created == 0u &&
       locked_handles_.size() == 0u) {
@@ -117,19 +117,29 @@
     return;
   }
 
-  // Size requires for serialization.
-  size_t bytes_required =
-      // Skia data size.
-      +sizeof(uint64_t) + alignof(uint64_t) + strike_data.size() +
-      16
-      // num of handles created + SerializableHandles.
-      + sizeof(uint64_t) + alignof(uint64_t) +
-      num_handles_created * sizeof(SerializableSkiaHandle) +
-      alignof(SerializableSkiaHandle) +
-      // num of handles locked + DiscardableHandleIds.
-      +sizeof(uint64_t) + alignof(uint64_t) +
-      locked_handles_.size() * sizeof(SkDiscardableHandleId) +
-      alignof(SkDiscardableHandleId);
+  // Size required for serialization.
+  base::CheckedNumeric<uint32_t> checked_bytes_required = 0;
+  // Skia data size.
+  checked_bytes_required += sizeof(uint32_t) + alignof(uint32_t) + 16;
+  checked_bytes_required += strike_data.size();
+
+  // num of handles created + SerializableHandles.
+  checked_bytes_required +=
+      sizeof(uint32_t) + alignof(uint32_t) + alignof(SerializableSkiaHandle);
+  checked_bytes_required +=
+      base::CheckMul(num_handles_created, sizeof(SerializableSkiaHandle));
+
+  // num of handles locked + DiscardableHandleIds.
+  checked_bytes_required +=
+      sizeof(uint32_t) + alignof(uint32_t) + alignof(SkDiscardableHandleId);
+  checked_bytes_required +=
+      base::CheckMul(locked_handles_.size(), sizeof(SkDiscardableHandleId));
+
+  uint32_t bytes_required = 0;
+  if (!checked_bytes_required.AssignIfValid(&bytes_required)) {
+    DLOG(FATAL) << "ClientFontManager::Serialize: font buffer overflow";
+    return;
+  }
 
   // Allocate memory.
   void* memory = client_->MapFontBuffer(bytes_required);
@@ -141,7 +151,7 @@
   Serializer serializer(reinterpret_cast<char*>(memory), bytes_required);
 
   // Serialize all new handles.
-  serializer.Write<uint64_t>(&num_handles_created);
+  serializer.Write<uint32_t>(&num_handles_created);
   for (SkDiscardableHandleId handle_id = last_serialized_handle_id_ + 1;
        handle_id <= last_allocated_handle_id_; handle_id++) {
     auto it = discardable_handle_map_.find(handle_id);
@@ -157,14 +167,16 @@
   }
 
   // Serialize all locked handle ids, so the raster unlocks them when done.
-  const uint64_t num_locked_handles = locked_handles_.size();
-  serializer.Write<uint64_t>(&num_locked_handles);
+  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(locked_handles_.size()));
+  const uint32_t num_locked_handles = locked_handles_.size();
+  serializer.Write<uint32_t>(&num_locked_handles);
   for (auto handle_id : locked_handles_)
     serializer.Write<SkDiscardableHandleId>(&handle_id);
 
   // Serialize skia data.
-  const uint64_t skia_data_size = strike_data.size();
-  serializer.Write<uint64_t>(&skia_data_size);
+  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(strike_data.size()));
+  const uint32_t skia_data_size = strike_data.size();
+  serializer.Write<uint32_t>(&skia_data_size);
   serializer.WriteData(strike_data.data(), strike_data.size(), 16);
 
   // Reset all state for what has been serialized.
diff --git a/gpu/command_buffer/client/client_font_manager.h b/gpu/command_buffer/client/client_font_manager.h
index 0b1a697..5b90623 100644
--- a/gpu/command_buffer/client/client_font_manager.h
+++ b/gpu/command_buffer/client/client_font_manager.h
@@ -22,7 +22,7 @@
    public:
     virtual ~Client() {}
 
-    virtual void* MapFontBuffer(size_t size) = 0;
+    virtual void* MapFontBuffer(uint32_t size) = 0;
   };
 
   ClientFontManager(Client* client, CommandBuffer* command_buffer);
diff --git a/gpu/command_buffer/client/raster_implementation.cc b/gpu/command_buffer/client/raster_implementation.cc
index 7af4b02b..799c436 100644
--- a/gpu/command_buffer/client/raster_implementation.cc
+++ b/gpu/command_buffer/client/raster_implementation.cc
@@ -989,7 +989,7 @@
   return raster_mapped_buffer_->address();
 }
 
-void* RasterImplementation::MapFontBuffer(size_t size) {
+void* RasterImplementation::MapFontBuffer(uint32_t size) {
   if (size < 0) {
     SetGLError(GL_INVALID_VALUE, "glMapFontBufferCHROMIUM", "negative size");
     return nullptr;
@@ -1004,11 +1004,6 @@
                "mapped font buffer with no raster buffer");
     return nullptr;
   }
-  if (size > std::numeric_limits<uint32_t>::max()) {
-    SetGLError(GL_INVALID_OPERATION, "glMapFontBufferCHROMIUM",
-               "trying to map too large font buffer");
-    return nullptr;
-  }
 
   font_mapped_buffer_.emplace(size, helper_, mapped_memory_.get());
   if (!font_mapped_buffer_->valid()) {
diff --git a/gpu/command_buffer/client/raster_implementation.h b/gpu/command_buffer/client/raster_implementation.h
index f4c3a19..224ee4f 100644
--- a/gpu/command_buffer/client/raster_implementation.h
+++ b/gpu/command_buffer/client/raster_implementation.h
@@ -177,7 +177,7 @@
                                  GLuint64* params);
 
   // ClientFontManager::Client implementation.
-  void* MapFontBuffer(size_t size) override;
+  void* MapFontBuffer(uint32_t size) override;
 
   void set_max_inlined_entry_size_for_testing(uint32_t max_size) {
     max_inlined_entry_size_ = max_size;
diff --git a/gpu/command_buffer/raster_cmd_buffer_functions.txt b/gpu/command_buffer/raster_cmd_buffer_functions.txt
index c423b6d..abad34d 100644
--- a/gpu/command_buffer/raster_cmd_buffer_functions.txt
+++ b/gpu/command_buffer/raster_cmd_buffer_functions.txt
@@ -29,7 +29,7 @@
 
 // Extension CHROMIUM_raster_transport
 GL_APICALL void         GL_APIENTRY glBeginRasterCHROMIUM (GLuint sk_color, GLuint msaa_sample_count, GLboolean can_use_lcd_text, GLuint color_space_transfer_cache_id, const GLbyte* mailbox);
-GL_APICALL void         GL_APIENTRY glRasterCHROMIUM (GLuint raster_shm_id, GLuint raster_shm_offset, GLsizeiptr raster_shm_size, GLuint font_shm_id, GLuint font_shm_offset, GLsizeiptr font_shm_size);
+GL_APICALL void         GL_APIENTRY glRasterCHROMIUM (GLuint raster_shm_id, GLuint raster_shm_offset, GLuint raster_shm_size, GLuint font_shm_id, GLuint font_shm_offset, GLuint font_shm_size);
 GL_APICALL void         GL_APIENTRY glEndRasterCHROMIUM (void);
 GL_APICALL void         GL_APIENTRY glCreateTransferCacheEntryINTERNAL (GLuint entry_type, GLuint entry_id, GLuint handle_shm_id, GLuint handle_shm_offset, GLuint data_shm_id, GLuint data_shm_offset, GLuint data_size);
 GL_APICALL void         GL_APIENTRY glDeleteTransferCacheEntryINTERNAL (GLuint entry_type, GLuint entry_id);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index f0d5d2a5..929e032 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -236,7 +236,7 @@
 #define LOCAL_PEEK_GL_ERROR(function_name) \
   ERRORSTATE_PEEK_GL_ERROR(error_state_.get(), function_name)
 #define LOCAL_CLEAR_REAL_GL_ERRORS(function_name) \
-  ERRORSTATE_CLEAR_REAL_GL_ERRORS(error_state_.get()) function_name)
+  ERRORSTATE_CLEAR_REAL_GL_ERRORS(error_state_.get(), function_name)
 #define LOCAL_PERFORMANCE_WARNING(msg) \
     PerformanceWarning(__FILE__, __LINE__, msg)
 #define LOCAL_RENDER_WARNING(msg) \
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index 7036cc8f..2788be4 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -1094,10 +1094,6 @@
   surface_ = nullptr;
 
   if (group_) {
-    if (group_->has_program_cache()) {
-      group_->get_program_cache()->ResetCacheProgramCallback();
-    }
-
     group_->Destroy(this, have_context);
     group_ = nullptr;
   }
@@ -1262,13 +1258,6 @@
     return false;
   }
 
-  // Establish the program binary caching callback.
-  if (group_->has_program_cache()) {
-    auto program_callback = base::BindRepeating(&DecoderClient::CacheShader,
-                                                base::Unretained(client_));
-    group_->get_program_cache()->SetCacheProgramCallback(program_callback);
-  }
-
   ProcessReadPixels(false);
   ProcessQueries(false);
 
diff --git a/gpu/command_buffer/service/memory_program_cache.h b/gpu/command_buffer/service/memory_program_cache.h
index 9df1e55..06b2e64f 100644
--- a/gpu/command_buffer/service/memory_program_cache.h
+++ b/gpu/command_buffer/service/memory_program_cache.h
@@ -16,6 +16,7 @@
 #include "base/memory/ref_counted.h"
 #include "gpu/command_buffer/service/decoder_client.h"
 #include "gpu/command_buffer/service/program_cache.h"
+#include "gpu/command_buffer/service/shader_translator.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/service/mocks.h b/gpu/command_buffer/service/mocks.h
index 1af73c8..b55ae6c 100644
--- a/gpu/command_buffer/service/mocks.h
+++ b/gpu/command_buffer/service/mocks.h
@@ -17,6 +17,7 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "gpu/command_buffer/common/cmd_buffer_common.h"
 #include "gpu/command_buffer/service/async_api_interface.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/program_cache.h"
diff --git a/gpu/command_buffer/service/passthrough_program_cache.h b/gpu/command_buffer/service/passthrough_program_cache.h
index 471125b..cb2d05d5 100644
--- a/gpu/command_buffer/service/passthrough_program_cache.h
+++ b/gpu/command_buffer/service/passthrough_program_cache.h
@@ -10,6 +10,7 @@
 #include "base/macros.h"
 #include "gpu/command_buffer/service/decoder_context.h"
 #include "gpu/command_buffer/service/program_cache.h"
+#include "ui/gl/gl_bindings.h"
 
 namespace gpu {
 
diff --git a/gpu/command_buffer/service/program_cache.cc b/gpu/command_buffer/service/program_cache.cc
index b8b4eb3..31bcca0e 100644
--- a/gpu/command_buffer/service/program_cache.cc
+++ b/gpu/command_buffer/service/program_cache.cc
@@ -16,6 +16,16 @@
 namespace gpu {
 namespace gles2 {
 
+ProgramCache::ScopedCacheUse::ScopedCacheUse(ProgramCache* cache,
+                                             CacheProgramCallback callback)
+    : cache_(cache) {
+  cache_->cache_program_callback_ = callback;
+}
+
+ProgramCache::ScopedCacheUse::~ScopedCacheUse() {
+  cache_->cache_program_callback_.Reset();
+}
+
 ProgramCache::ProgramCache(size_t max_cache_size_bytes)
     : max_size_bytes_(max_cache_size_bytes) {}
 ProgramCache::~ProgramCache() = default;
@@ -184,13 +194,5 @@
   }
 }
 
-void ProgramCache::SetCacheProgramCallback(CacheProgramCallback callback) {
-  cache_program_callback_ = callback;
-}  // namespace gles2
-
-void ProgramCache::ResetCacheProgramCallback() {
-  cache_program_callback_.Reset();
-}
-
 }  // namespace gles2
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/program_cache.h b/gpu/command_buffer/service/program_cache.h
index 810af3c..d5b0a7dc 100644
--- a/gpu/command_buffer/service/program_cache.h
+++ b/gpu/command_buffer/service/program_cache.h
@@ -14,11 +14,13 @@
 #include "base/macros.h"
 #include "base/memory/memory_pressure_listener.h"
 #include "base/sha1.h"
-#include "gpu/command_buffer/common/gles2_cmd_format.h"
-#include "gpu/command_buffer/service/program_manager.h"
-#include "gpu/command_buffer/service/shader_manager.h"
+#include "gpu/command_buffer/common/gl2_types.h"
+#include "gpu/gpu_gles2_export.h"
 
 namespace gpu {
+
+class DecoderClient;
+
 namespace gles2 {
 
 class Shader;
@@ -42,6 +44,18 @@
     PROGRAM_LOAD_SUCCESS
   };
 
+  class GPU_GLES2_EXPORT ScopedCacheUse {
+   public:
+    ScopedCacheUse(ProgramCache* cache, CacheProgramCallback callback);
+    ~ScopedCacheUse();
+
+    ScopedCacheUse(ScopedCacheUse&&) = default;
+    ScopedCacheUse& operator=(ScopedCacheUse&& other) = default;
+
+   private:
+    ProgramCache* cache_;
+  };
+
   explicit ProgramCache(size_t max_cache_size_bytes);
   virtual ~ProgramCache();
 
@@ -95,9 +109,6 @@
   void HandleMemoryPressure(
       base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
 
-  void SetCacheProgramCallback(CacheProgramCallback callback);
-  void ResetCacheProgramCallback();
-
  protected:
   size_t max_size_bytes() const { return max_size_bytes_; }
 
diff --git a/gpu/command_buffer/service/program_cache_unittest.cc b/gpu/command_buffer/service/program_cache_unittest.cc
index 27bee7d5..e97f5ef5 100644
--- a/gpu/command_buffer/service/program_cache_unittest.cc
+++ b/gpu/command_buffer/service/program_cache_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "gpu/command_buffer/service/mocks.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_bindings.h"
 
 using ::testing::Return;
 
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index b57fcca..74a3470 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -516,10 +516,10 @@
                              const volatile GLbyte* key);
   void DoRasterCHROMIUM(GLuint raster_shm_id,
                         GLuint raster_shm_offset,
-                        GLsizeiptr raster_shm_size,
+                        GLuint raster_shm_size,
                         GLuint font_shm_id,
                         GLuint font_shm_offset,
-                        GLsizeiptr font_shm_size);
+                        GLuint font_shm_size);
   void DoEndRasterCHROMIUM();
   void DoCreateTransferCacheEntryINTERNAL(GLuint entry_type,
                                           GLuint entry_id,
@@ -2242,10 +2242,10 @@
 
 void RasterDecoderImpl::DoRasterCHROMIUM(GLuint raster_shm_id,
                                          GLuint raster_shm_offset,
-                                         GLsizeiptr raster_shm_size,
+                                         GLuint raster_shm_size,
                                          GLuint font_shm_id,
                                          GLuint font_shm_offset,
-                                         GLsizeiptr font_shm_size) {
+                                         GLuint font_shm_size) {
   TRACE_EVENT1("gpu", "RasterDecoderImpl::DoRasterCHROMIUM", "raster_id",
                ++raster_chromium_id_);
 
diff --git a/gpu/command_buffer/service/raster_decoder_autogen.h b/gpu/command_buffer/service/raster_decoder_autogen.h
index ff48ba2b..a3fa291 100644
--- a/gpu/command_buffer/service/raster_decoder_autogen.h
+++ b/gpu/command_buffer/service/raster_decoder_autogen.h
@@ -166,20 +166,10 @@
 
   GLuint raster_shm_id = static_cast<GLuint>(c.raster_shm_id);
   GLuint raster_shm_offset = static_cast<GLuint>(c.raster_shm_offset);
-  GLsizeiptr raster_shm_size = static_cast<GLsizeiptr>(c.raster_shm_size);
+  GLuint raster_shm_size = static_cast<GLuint>(c.raster_shm_size);
   GLuint font_shm_id = static_cast<GLuint>(c.font_shm_id);
   GLuint font_shm_offset = static_cast<GLuint>(c.font_shm_offset);
-  GLsizeiptr font_shm_size = static_cast<GLsizeiptr>(c.font_shm_size);
-  if (raster_shm_size < 0) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glRasterCHROMIUM",
-                       "raster_shm_size < 0");
-    return error::kNoError;
-  }
-  if (font_shm_size < 0) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glRasterCHROMIUM",
-                       "font_shm_size < 0");
-    return error::kNoError;
-  }
+  GLuint font_shm_size = static_cast<GLuint>(c.font_shm_size);
   DoRasterCHROMIUM(raster_shm_id, raster_shm_offset, raster_shm_size,
                    font_shm_id, font_shm_offset, font_shm_size);
   return error::kNoError;
diff --git a/gpu/command_buffer/service/service_font_manager.cc b/gpu/command_buffer/service/service_font_manager.cc
index f4f40f9..53bcf21 100644
--- a/gpu/command_buffer/service/service_font_manager.cc
+++ b/gpu/command_buffer/service/service_font_manager.cc
@@ -14,7 +14,7 @@
 namespace {
 class Deserializer {
  public:
-  Deserializer(const volatile char* memory, size_t memory_size)
+  Deserializer(const volatile char* memory, uint32_t memory_size)
       : memory_(memory), memory_size_(memory_size) {}
   ~Deserializer() = default;
 
@@ -32,7 +32,7 @@
     return true;
   }
 
-  bool ReadStrikeData(SkStrikeClient* strike_client, size_t size) {
+  bool ReadStrikeData(SkStrikeClient* strike_client, uint32_t size) {
     if (size == 0u)
       return true;
 
@@ -48,14 +48,20 @@
   }
 
  private:
-  bool AlignMemory(size_t size, size_t alignment) {
+  bool AlignMemory(uint32_t size, size_t alignment) {
     // Due to the math below, alignment must be a power of two.
     DCHECK_GT(alignment, 0u);
     DCHECK_EQ(alignment & (alignment - 1), 0u);
 
     uintptr_t memory = reinterpret_cast<uintptr_t>(memory_);
     size_t padding = ((memory + alignment - 1) & ~(alignment - 1)) - memory;
-    if (bytes_read_ + size + padding > memory_size_)
+
+    base::CheckedNumeric<uint32_t> checked_padded_size = bytes_read_;
+    checked_padded_size += padding;
+    checked_padded_size += size;
+    uint32_t padded_size = 0;
+    if (!checked_padded_size.AssignIfValid(&padded_size) ||
+        padded_size > memory_size_)
       return false;
 
     memory_ += padding;
@@ -64,8 +70,8 @@
   }
 
   const volatile char* memory_;
-  size_t memory_size_;
-  size_t bytes_read_ = 0u;
+  uint32_t memory_size_;
+  uint32_t bytes_read_ = 0u;
 };
 }  // namespace
 
@@ -125,7 +131,7 @@
 
 bool ServiceFontManager::Deserialize(
     const volatile char* memory,
-    size_t memory_size,
+    uint32_t memory_size,
     std::vector<SkDiscardableHandleId>* locked_handles) {
   base::AutoLock hold(lock_);
 
@@ -134,11 +140,11 @@
 
   // All new handles.
   Deserializer deserializer(memory, memory_size);
-  uint64_t new_handles_created;
-  if (!deserializer.Read<uint64_t>(&new_handles_created))
+  uint32_t new_handles_created;
+  if (!deserializer.Read<uint32_t>(&new_handles_created))
     return false;
 
-  for (size_t i = 0; i < new_handles_created; ++i) {
+  for (uint32_t i = 0; i < new_handles_created; ++i) {
     SerializableSkiaHandle handle;
     if (!deserializer.Read<SerializableSkiaHandle>(&handle))
       return false;
@@ -156,19 +162,19 @@
   }
 
   // All locked handles
-  uint64_t num_locked_handles;
-  if (!deserializer.Read<uint64_t>(&num_locked_handles))
+  uint32_t num_locked_handles;
+  if (!deserializer.Read<uint32_t>(&num_locked_handles))
     return false;
 
   locked_handles->resize(num_locked_handles);
-  for (size_t i = 0; i < num_locked_handles; ++i) {
+  for (uint32_t i = 0; i < num_locked_handles; ++i) {
     if (!deserializer.Read<SkDiscardableHandleId>(&locked_handles->at(i)))
       return false;
   }
 
   // Skia font data.
-  uint64_t skia_data_size = 0u;
-  if (!deserializer.Read<uint64_t>(&skia_data_size))
+  uint32_t skia_data_size = 0u;
+  if (!deserializer.Read<uint32_t>(&skia_data_size))
     return false;
 
   {
diff --git a/gpu/command_buffer/service/service_font_manager.h b/gpu/command_buffer/service/service_font_manager.h
index 5bc77d6..198c54f 100644
--- a/gpu/command_buffer/service/service_font_manager.h
+++ b/gpu/command_buffer/service/service_font_manager.h
@@ -28,7 +28,7 @@
   void Destroy();
 
   bool Deserialize(const volatile char* memory,
-                   size_t memory_size,
+                   uint32_t memory_size,
                    std::vector<SkDiscardableHandleId>* locked_handles);
   bool Unlock(const std::vector<SkDiscardableHandleId>& handles);
   SkStrikeClient* strike_client() { return strike_client_.get(); }
diff --git a/gpu/ipc/command_buffer_task_executor.cc b/gpu/ipc/command_buffer_task_executor.cc
index 56567e90..c7a7a109 100644
--- a/gpu/ipc/command_buffer_task_executor.cc
+++ b/gpu/ipc/command_buffer_task_executor.cc
@@ -8,6 +8,7 @@
 #include "gpu/command_buffer/service/mailbox_manager_factory.h"
 #include "gpu/command_buffer/service/memory_program_cache.h"
 #include "gpu/command_buffer/service/program_cache.h"
+#include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_share_group.h"
 
 namespace gpu {
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index 9f378ab..19f1bf93 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -308,6 +308,17 @@
   return true;
 }
 
+base::Optional<gles2::ProgramCache::ScopedCacheUse>
+InProcessCommandBuffer::CreateCacheUse() {
+  base::Optional<gles2::ProgramCache::ScopedCacheUse> cache_use;
+  if (context_group_->has_program_cache()) {
+    cache_use.emplace(context_group_->get_program_cache(),
+                      base::BindRepeating(&DecoderClient::CacheShader,
+                                          base::Unretained(this)));
+  }
+  return cache_use;
+}
+
 gpu::ContextResult InProcessCommandBuffer::Initialize(
     scoped_refptr<gl::GLSurface> surface,
     bool is_offscreen,
@@ -689,6 +700,9 @@
   bool have_context = context_.get() && context_->MakeCurrent(surface_.get());
   if (shared_image_factory_)
     shared_image_factory_->DestroyAllSharedImages(have_context);
+  base::Optional<gles2::ProgramCache::ScopedCacheUse> cache_use;
+  if (have_context)
+    cache_use = CreateCacheUse();
 
   // Prepare to destroy the surface while the context is still current, because
   // some surface destructors make GL calls.
@@ -817,6 +831,7 @@
 
   if (!MakeCurrent())
     return;
+  auto cache_use = CreateCacheUse();
 
   MailboxManager* mailbox_manager = context_group_->mailbox_manager();
   if (mailbox_manager->UsesSync()) {
@@ -825,9 +840,9 @@
   }
 
   {
-    base::Optional<raster::GrShaderCache::ScopedCacheUse> cache_use;
+    base::Optional<raster::GrShaderCache::ScopedCacheUse> gr_cache_use;
     if (gr_shader_cache_)
-      cache_use.emplace(gr_shader_cache_, kInProcessCommandBufferClientId);
+      gr_cache_use.emplace(gr_shader_cache_, kInProcessCommandBufferClientId);
     command_buffer_->Flush(put_offset, decoder_.get());
   }
   // Update state before signaling the flush event.
@@ -856,6 +871,7 @@
   crash_keys::gpu_gl_context_is_virtual.Set(use_virtualized_gl_context_ ? "1"
                                                                         : "0");
   if (MakeCurrent()) {
+    auto cache_use = CreateCacheUse();
     decoder_->PerformIdleWork();
     decoder_->ProcessPendingQueries(false);
     if (decoder_->HasMoreIdleWork() || decoder_->HasPendingQueries()) {
diff --git a/gpu/ipc/in_process_command_buffer.h b/gpu/ipc/in_process_command_buffer.h
index bb4d41b..a489938 100644
--- a/gpu/ipc/in_process_command_buffer.h
+++ b/gpu/ipc/in_process_command_buffer.h
@@ -19,6 +19,7 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
@@ -31,6 +32,7 @@
 #include "gpu/command_buffer/service/decoder_client.h"
 #include "gpu/command_buffer/service/decoder_context.h"
 #include "gpu/command_buffer/service/gr_cache_controller.h"
+#include "gpu/command_buffer/service/program_cache.h"
 #include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/service_transfer_cache.h"
 #include "gpu/config/gpu_feature_info.h"
@@ -236,6 +238,8 @@
 
   bool MakeCurrent();
 
+  base::Optional<gles2::ProgramCache::ScopedCacheUse> CreateCacheUse();
+
   // Client callbacks are posted back to |origin_task_runner_|, or run
   // synchronously if there's no task runner or message loop.
   void PostOrRunClientCallback(base::OnceClosure callback);
diff --git a/gpu/ipc/service/command_buffer_stub.cc b/gpu/ipc/service/command_buffer_stub.cc
index 6ff0dc2..08aa42b5 100644
--- a/gpu/ipc/service/command_buffer_stub.cc
+++ b/gpu/ipc/service/command_buffer_stub.cc
@@ -165,6 +165,7 @@
   crash_keys::gpu_gl_context_is_virtual.Set(use_virtualized_gl_context_ ? "1"
                                                                         : "0");
   bool have_context = false;
+  base::Optional<gles2::ProgramCache::ScopedCacheUse> cache_use;
   // Ensure the appropriate GL context is current before handling any IPC
   // messages directed at the command buffer. This ensures that the message
   // handler can assume that the context is current (not necessary for
@@ -179,6 +180,7 @@
       message.type() != GpuCommandBufferMsg_SignalQuery::ID) {
     if (!MakeCurrent())
       return false;
+    cache_use.emplace(CreateCacheUse());
     have_context = true;
   }
 
@@ -257,6 +259,7 @@
                                                                         : "0");
   if (decoder_context_.get() && !MakeCurrent())
     return;
+  auto cache_use = CreateCacheUse();
 
   if (decoder_context_) {
     uint32_t current_unprocessed_num =
@@ -345,6 +348,12 @@
   return false;
 }
 
+gles2::ProgramCache::ScopedCacheUse CommandBufferStub::CreateCacheUse() {
+  return gles2::ProgramCache::ScopedCacheUse(
+      channel_->gpu_channel_manager()->program_cache(),
+      base::BindRepeating(&DecoderClient::CacheShader, base::Unretained(this)));
+}
+
 void CommandBufferStub::Destroy() {
   FastSetActiveURL(active_url_, active_url_hash_, channel_);
   // TODO(sunnyps): Should this use ScopedCrashKey instead?
@@ -384,6 +393,11 @@
     have_context =
         decoder_context_->GetGLContext()->MakeCurrent(surface_.get());
   }
+
+  base::Optional<gles2::ProgramCache::ScopedCacheUse> cache_use;
+  if (have_context)
+    cache_use.emplace(CreateCacheUse());
+
   for (auto& observer : destruction_observers_)
     observer.OnWillDestroyStub(have_context);
 
diff --git a/gpu/ipc/service/command_buffer_stub.h b/gpu/ipc/service/command_buffer_stub.h
index 6a561de..a1fb4bed 100644
--- a/gpu/ipc/service/command_buffer_stub.h
+++ b/gpu/ipc/service/command_buffer_stub.h
@@ -23,6 +23,7 @@
 #include "gpu/command_buffer/service/command_buffer_service.h"
 #include "gpu/command_buffer/service/context_group.h"
 #include "gpu/command_buffer/service/decoder_client.h"
+#include "gpu/command_buffer/service/program_cache.h"
 #include "gpu/command_buffer/service/sequence_id.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "gpu/ipc/service/gpu_ipc_service_export.h"
@@ -176,6 +177,8 @@
 
   bool MakeCurrent();
 
+  gles2::ProgramCache::ScopedCacheUse CreateCacheUse();
+
   // Message handlers:
   void OnSetGetBuffer(int32_t shm_id);
   virtual void OnTakeFrontBuffer(const Mailbox& mailbox) = 0;
diff --git a/gpu/vulkan/demo/vulkan_demo.cc b/gpu/vulkan/demo/vulkan_demo.cc
index 2c0f2a08..ed67c365 100644
--- a/gpu/vulkan/demo/vulkan_demo.cc
+++ b/gpu/vulkan/demo/vulkan_demo.cc
@@ -12,6 +12,7 @@
 #include "gpu/vulkan/vulkan_implementation.h"
 #include "gpu/vulkan/vulkan_surface.h"
 #include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkFont.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/effects/SkGradientShader.h"
 #include "third_party/skia/include/gpu/GrBackendSurface.h"
@@ -161,9 +162,11 @@
   }
 
   // Draw a message with a nice black paint
-  paint.setSubpixelText(true);
   paint.setColor(SK_ColorBLACK);
-  paint.setTextSize(32);
+
+  SkFont font;
+  font.setSize(32);
+  font.setSubpixel(true);
 
   static const char message[] = "Hello Vulkan";
 
@@ -176,7 +179,7 @@
   canvas->rotate(rotation_angle_);
 
   // Draw the text
-  canvas->drawText(message, strlen(message), 0, 0, paint);
+  canvas->drawString(message, 0, 0, font, paint);
 
   canvas->restore();
   canvas->flush();
diff --git a/infra/config/branch/cq.cfg b/infra/config/branch/cq.cfg
index fdc9310..ed219ad 100644
--- a/infra/config/branch/cq.cfg
+++ b/infra/config/branch/cq.cfg
@@ -126,11 +126,6 @@
         path_regexp: "ios/.+"
       }
       builders {
-        name: "linux-blink-gen-property-trees"
-        path_regexp: "third_party/blink/web_tests/FlagExpectations/enable-blink-features=BlinkGenPropertyTrees"
-        path_regexp: "third_party/blink/web_tests/flag-specific/enable-blink-features=BlinkGenPropertyTrees"
-      }
-      builders {
         name: "linux-blink-rel"
         path_regexp: "cc/.+"
         path_regexp: "third_party/blink/renderer/core/(svg|paint)/.+"
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 5623225..a05f871 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -3164,7 +3164,6 @@
     builders { mixins: "linux-try" name: "fuchsia_x64" }
     builders { mixins: "linux-try" name: "fuchsia-x64-cast" }
     builders { mixins: "linux-try" name: "leak_detection_linux" }
-    builders { mixins: "linux-try" name: "linux-blink-gen-property-trees" }
     builders { mixins: "linux-try" name: "linux-blink-heap-incremental-marking" }
     builders { mixins: "linux-try" name: "linux-blink-heap-verification-try" }
     builders { mixins: "linux-try" name: "linux-dcheck-off-rel" }
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 681ebf4..28efe4f 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -4462,9 +4462,6 @@
     name: "buildbucket/luci.chromium.try/gpu_manual_try_win7_nvidia_rel"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux-blink-gen-property-trees"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux-blink-heap-incremental-marking"
   }
   builders {
diff --git a/ios/chrome/browser/infobars/infobar_manager_impl.mm b/ios/chrome/browser/infobars/infobar_manager_impl.mm
index 597c4f3..6edcc7d6 100644
--- a/ios/chrome/browser/infobars/infobar_manager_impl.mm
+++ b/ios/chrome/browser/infobars/infobar_manager_impl.mm
@@ -75,9 +75,11 @@
     web::WebState* web_state,
     web::NavigationContext* navigation_context) {
   DCHECK_EQ(web_state_, web_state);
-  OnNavigation(CreateNavigationDetails(
-      web_state->GetNavigationManager()->GetLastCommittedItem(),
-      navigation_context->IsSameDocument()));
+  if (navigation_context->HasCommitted()) {
+    OnNavigation(CreateNavigationDetails(
+        web_state->GetNavigationManager()->GetLastCommittedItem(),
+        navigation_context->IsSameDocument()));
+  }
 }
 
 void InfoBarManagerImpl::WebStateDestroyed(web::WebState* web_state) {
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index db88974..dfb0cd1 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -319,6 +319,13 @@
 }
 
 - (void)webState:(web::WebState*)webState
+    didFinishNavigation:(web::NavigationContext*)navigation {
+  if (navigation->HasCommitted() && !navigation->IsSameDocument()) {
+    [self.dialogDelegate cancelDialogForTab:self];
+  }
+}
+
+- (void)webState:(web::WebState*)webState
     didLoadPageWithSuccess:(BOOL)loadSuccess {
   if (loadSuccess) {
     scoped_refptr<net::HttpResponseHeaders> headers =
diff --git a/ios/chrome/browser/ui/webui/inspect/inspect_ui.mm b/ios/chrome/browser/ui/webui/inspect/inspect_ui.mm
index faad6f0..466958f 100644
--- a/ios/chrome/browser/ui/webui/inspect/inspect_ui.mm
+++ b/ios/chrome/browser/ui/webui/inspect/inspect_ui.mm
@@ -4,6 +4,9 @@
 
 #include "ios/chrome/browser/ui/webui/inspect/inspect_ui.h"
 
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #include "ios/chrome/browser/tabs/tab_model.h"
@@ -29,6 +32,23 @@
 
 namespace {
 
+// Used to record when the user loads the inspect page.
+const char kInspectPageVisited[] = "IOSInspectPageVisited";
+
+// The histogram used to record user actions performed on the inspect page.
+const char kInspectConsoleHistogram[] = "IOS.Inspect.Console";
+
+// Actions performed by the user logged to |kInspectConsoleHistogram|.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class InspectConsoleAction {
+  // Recorded when a user pressed the "Start Logging" button to collect logs.
+  kStartLogging = 0,
+  // Recorded when a user pressed the "Stop Logging" button.
+  kStopLogging = 1,
+  kMaxValue = kStopLogging,
+};
+
 web::WebUIIOSDataSource* CreateInspectUIHTMLSource() {
   web::WebUIIOSDataSource* source =
       web::WebUIIOSDataSource::Create(kChromeUIInspectHost);
@@ -112,6 +132,10 @@
     NOTREACHED();
   }
 
+  UMA_HISTOGRAM_ENUMERATION(kInspectConsoleHistogram,
+                            enabled ? InspectConsoleAction::kStartLogging
+                                    : InspectConsoleAction::kStopLogging);
+
   SetLoggingEnabled(enabled);
 }
 
@@ -207,6 +231,8 @@
 }  // namespace
 
 InspectUI::InspectUI(web::WebUIIOS* web_ui) : web::WebUIIOSController(web_ui) {
+  base::RecordAction(base::UserMetricsAction(kInspectPageVisited));
+
   web_ui->AddMessageHandler(std::make_unique<InspectDOMHandler>());
 
   web::WebUIIOSDataSource::Add(ios::ChromeBrowserState::FromWebUIIOS(web_ui),
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index bdb60dc..6854001d 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -486,8 +486,6 @@
 - (GURL)webURLWithTrustLevel:(web::URLVerificationTrustLevel*)trustLevel;
 // Returns |YES| if |url| should be loaded in a native view.
 - (BOOL)shouldLoadURLInNativeView:(const GURL&)url;
-// Loads the request into the |webView|.
-- (WKNavigation*)loadRequest:(NSMutableURLRequest*)request;
 // Loads POST request with body in |_wkWebView| by constructing an HTML page
 // that executes the request through JavaScript and replaces document with the
 // result.
@@ -4129,13 +4127,6 @@
   return web::WKWebViewConfigurationProvider::FromBrowserState(browserState);
 }
 
-- (WKNavigation*)loadRequest:(NSMutableURLRequest*)request {
-  WKNavigation* navigation = [_webView loadRequest:request];
-  [_navigationStates setState:web::WKNavigationState::REQUESTED
-                forNavigation:navigation];
-  return navigation;
-}
-
 - (WKNavigation*)loadPOSTRequest:(NSMutableURLRequest*)request {
   if (!_POSTRequestLoader) {
     _POSTRequestLoader = [[CRWJSPOSTRequestLoader alloc] init];
@@ -5796,7 +5787,21 @@
                          hasUserGesture:YES
                   placeholderNavigation:IsPlaceholderUrl(navigationURL)];
     navigationContext->SetIsRendererInitiated(false);
-    WKNavigation* navigation = [self loadRequest:request];
+
+    WKNavigation* navigation = nil;
+    GURL virtualURL = item ? item->GetVirtualURL() : GURL::EmptyGURL();
+    if (navigationURL.SchemeIsFile() &&
+        web::GetWebClient()->IsAppSpecificURL(virtualURL)) {
+      // file:// URL navigations are allowed for app-specific URLs, which
+      // already have elevated privileges.
+      NSURL* navigationNSURL = net::NSURLWithGURL(navigationURL);
+      navigation = [_webView loadFileURL:navigationNSURL
+                 allowingReadAccessToURL:navigationNSURL];
+    } else {
+      navigation = [_webView loadRequest:request];
+    }
+    [_navigationStates setState:web::WKNavigationState::REQUESTED
+                  forNavigation:navigation];
     [_navigationStates setContext:std::move(navigationContext)
                     forNavigation:navigation];
     [self reportBackForwardNavigationTypeForFastNavigation:NO];
diff --git a/ios/web/web_state/web_state_observer_inttest.mm b/ios/web/web_state/web_state_observer_inttest.mm
index 16198da..ef2defb 100644
--- a/ios/web/web_state/web_state_observer_inttest.mm
+++ b/ios/web/web_state/web_state_observer_inttest.mm
@@ -5,7 +5,10 @@
 #include <memory>
 #include <string>
 
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
 #include "base/ios/ios_util.h"
+#include "base/path_service.h"
 #include "base/scoped_observer.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -182,6 +185,36 @@
   EXPECT_EQ(url, item->GetURL());
 }
 
+// Verifies correctness of |NavigationContext| (|arg1|) for file:// URL
+// navigation passed to |DidFinishNavigation|. Asserts that |NavigationContext|
+// the same as |context|.
+ACTION_P4(VerifyPdfFileUrlFinishedContext, web_state, url, context, nav_id) {
+  ASSERT_EQ(*context, arg1);
+  EXPECT_EQ(web_state, arg0);
+  ASSERT_TRUE((*context));
+  EXPECT_EQ(web_state, (*context)->GetWebState());
+  EXPECT_EQ(*nav_id, (*context)->GetNavigationId());
+  EXPECT_EQ(url, (*context)->GetUrl());
+  EXPECT_TRUE((*context)->HasUserGesture());
+  EXPECT_TRUE(
+      PageTransitionCoreTypeIs(ui::PageTransition::PAGE_TRANSITION_TYPED,
+                               (*context)->GetPageTransition()));
+  EXPECT_FALSE((*context)->IsSameDocument());
+  EXPECT_TRUE((*context)->HasCommitted());
+  EXPECT_FALSE((*context)->IsDownload());
+  EXPECT_FALSE((*context)->IsPost());
+  EXPECT_FALSE((*context)->GetError());
+  EXPECT_FALSE((*context)->IsRendererInitiated());
+  ASSERT_FALSE((*context)->GetResponseHeaders());
+  ASSERT_TRUE(web_state->IsLoading());
+  ASSERT_FALSE(web_state->ContentIsHTML());
+  ASSERT_EQ("application/pdf", web_state->GetContentsMimeType());
+  NavigationManager* navigation_manager = web_state->GetNavigationManager();
+  NavigationItem* item = navigation_manager->GetLastCommittedItem();
+  EXPECT_TRUE(!item->GetTimestamp().is_null());
+  EXPECT_EQ(url, item->GetURL());
+}
+
 // Verifies correctness of |NavigationContext| (|arg1|) for failed navigation
 // passed to |DidFinishNavigation|. Asserts that |NavigationContext| the same as
 // |context|.
@@ -2328,6 +2361,52 @@
   ASSERT_FALSE(navigation_manager()->CanGoBack());
 }
 
+// Tests successful navigation to a PDF file:// URL.
+TEST_P(WebStateObserverTest, PdfFileUrlNavigation) {
+  // Construct a valid file:// URL.
+  base::FilePath path;
+  base::PathService::Get(base::DIR_MODULE, &path);
+  path = path.Append(
+      FILE_PATH_LITERAL("ios/testing/data/http_server_files/testpage.pdf"));
+
+  GURL url(url::SchemeHostPort(url::kFileScheme, std::string(), 0).Serialize());
+  GURL::Replacements replacements;
+  replacements.SetPathStr(path.value());
+  url = url.ReplaceComponents(replacements);
+  ASSERT_TRUE(url.is_valid());
+  ASSERT_FALSE(url.is_empty());
+
+  // Perform file:// URL navigation.
+  NavigationContext* context = nullptr;
+  int32_t nav_id = 0;
+  EXPECT_CALL(observer_, DidStartLoading(web_state()));
+  WebStatePolicyDecider::RequestInfo expected_request_info(
+      ui::PageTransition::PAGE_TRANSITION_TYPED,
+      /*target_main_frame=*/true, /*has_user_gesture=*/false);
+  EXPECT_CALL(*decider_,
+              ShouldAllowRequest(_, RequestInfoMatch(expected_request_info)))
+      .WillOnce(Return(true));
+  EXPECT_CALL(observer_, DidStartNavigation(web_state(), _))
+      .WillOnce(VerifyPageStartedContext(
+          web_state(), url, ui::PageTransition::PAGE_TRANSITION_TYPED, &context,
+          &nav_id));
+  EXPECT_CALL(*decider_, ShouldAllowResponse(_, /*for_main_frame=*/true))
+      .WillOnce(Return(true));
+  EXPECT_CALL(observer_, DidFinishNavigation(web_state(), _))
+      .WillOnce(
+          VerifyPdfFileUrlFinishedContext(web_state(), url, &context, &nav_id));
+  EXPECT_CALL(observer_, TitleWasSet(web_state()))
+      .WillOnce(VerifyTitle("testpage.pdf"));
+  EXPECT_CALL(observer_, DidStopLoading(web_state()));
+  EXPECT_CALL(observer_,
+              PageLoaded(web_state(), PageLoadCompletionStatus::SUCCESS));
+  web::NavigationManager::WebLoadParams params(url);
+  params.transition_type = ui::PageTransition::PAGE_TRANSITION_TYPED;
+  params.virtual_url =
+      GURL(url::SchemeHostPort(kTestAppSpecificScheme, "foo", 0).Serialize());
+  ASSERT_TRUE(LoadWithParams(params));
+}
+
 INSTANTIATE_TEST_CASE_P(
     ProgrammaticWebStateObserverTest,
     WebStateObserverTest,
diff --git a/jingle/notifier/base/gaia_token_pre_xmpp_auth.cc b/jingle/notifier/base/gaia_token_pre_xmpp_auth.cc
index 44192bf5..611746a 100644
--- a/jingle/notifier/base/gaia_token_pre_xmpp_auth.cc
+++ b/jingle/notifier/base/gaia_token_pre_xmpp_auth.cc
@@ -65,7 +65,6 @@
 
 void GaiaTokenPreXmppAuth::StartPreXmppAuth(
     const buzz::Jid& jid,
-    const rtc::SocketAddress& server,
     const std::string& pass,
     const std::string& auth_mechanism,
     const std::string& auth_token) {
diff --git a/jingle/notifier/base/gaia_token_pre_xmpp_auth.h b/jingle/notifier/base/gaia_token_pre_xmpp_auth.h
index a2c3b34..722c95c 100644
--- a/jingle/notifier/base/gaia_token_pre_xmpp_auth.h
+++ b/jingle/notifier/base/gaia_token_pre_xmpp_auth.h
@@ -28,7 +28,6 @@
   // all the methods out as we don't actually do any authentication at
   // this point.
   void StartPreXmppAuth(const buzz::Jid& jid,
-                        const rtc::SocketAddress& server,
                         const std::string& pass,
                         const std::string& auth_mechanism,
                         const std::string& auth_token) override;
diff --git a/jingle/notifier/base/xmpp_connection_unittest.cc b/jingle/notifier/base/xmpp_connection_unittest.cc
index 606ff3e..46ba78ba 100644
--- a/jingle/notifier/base/xmpp_connection_unittest.cc
+++ b/jingle/notifier/base/xmpp_connection_unittest.cc
@@ -50,9 +50,8 @@
                std::string(const std::vector<std::string>&, bool));
   MOCK_METHOD1(CreateSaslMechanism,
                buzz::SaslMechanism*(const std::string&));
-  MOCK_METHOD5(StartPreXmppAuth,
+  MOCK_METHOD4(StartPreXmppAuth,
                void(const buzz::Jid&,
-                    const rtc::SocketAddress&,
                     const std::string&,
                     const std::string&,
                     const std::string&));
@@ -127,7 +126,7 @@
 }
 
 TEST_F(XmppConnectionTest, PreAuthFailure) {
-  EXPECT_CALL(*mock_pre_xmpp_auth_, StartPreXmppAuth(_, _, _, _,_));
+  EXPECT_CALL(*mock_pre_xmpp_auth_, StartPreXmppAuth(_, _, _,_));
   EXPECT_CALL(*mock_pre_xmpp_auth_, IsAuthDone()).WillOnce(Return(true));
   EXPECT_CALL(*mock_pre_xmpp_auth_, IsAuthorized()).WillOnce(Return(false));
   EXPECT_CALL(*mock_pre_xmpp_auth_, HadError()).WillOnce(Return(true));
@@ -148,7 +147,7 @@
 }
 
 TEST_F(XmppConnectionTest, FailureAfterPreAuth) {
-  EXPECT_CALL(*mock_pre_xmpp_auth_, StartPreXmppAuth(_, _, _, _,_));
+  EXPECT_CALL(*mock_pre_xmpp_auth_, StartPreXmppAuth(_, _, _,_));
   EXPECT_CALL(*mock_pre_xmpp_auth_, IsAuthDone()).WillOnce(Return(true));
   EXPECT_CALL(*mock_pre_xmpp_auth_, IsAuthorized()).WillOnce(Return(true));
   EXPECT_CALL(*mock_pre_xmpp_auth_, GetAuthMechanism()).WillOnce(Return(""));
diff --git a/mojo/core/ports/user_message.h b/mojo/core/ports/user_message.h
index 34d2688..e30e316a 100644
--- a/mojo/core/ports/user_message.h
+++ b/mojo/core/ports/user_message.h
@@ -5,6 +5,8 @@
 #ifndef MOJO_CORE_PORTS_USER_MESSAGE_H_
 #define MOJO_CORE_PORTS_USER_MESSAGE_H_
 
+#include <stddef.h>
+
 #include "base/component_export.h"
 #include "base/macros.h"
 
diff --git a/native_client_sdk/src/libraries/ppapi/library.dsc b/native_client_sdk/src/libraries/ppapi/library.dsc
index a1e1b1b..2da974d 100644
--- a/native_client_sdk/src/libraries/ppapi/library.dsc
+++ b/native_client_sdk/src/libraries/ppapi/library.dsc
@@ -23,8 +23,6 @@
         'ppb_audio_config.h',
         'ppb_audio_encoder.h',
         'ppb_audio.h',
-        'ppb_compositor.h',
-        'ppb_compositor_layer.h',
         'ppb_console.h',
         'ppb_core.h',
         'ppb_file_io.h',
diff --git a/native_client_sdk/src/libraries/ppapi_cpp/library.dsc b/native_client_sdk/src/libraries/ppapi_cpp/library.dsc
index 5c0c95668..62eb2cf 100644
--- a/native_client_sdk/src/libraries/ppapi_cpp/library.dsc
+++ b/native_client_sdk/src/libraries/ppapi_cpp/library.dsc
@@ -18,8 +18,6 @@
         'audio_buffer.cc',
         'audio_config.cc',
         'audio_encoder.cc',
-        'compositor.cc',
-        'compositor_layer.cc',
         'core.cc',
         'directory_entry.cc',
         'file_io.cc',
@@ -95,8 +93,6 @@
         'audio_encoder.h',
         'audio.h',
         'completion_callback.h',
-        'compositor.h',
-        'compositor_layer.h',
         'core.h',
         'directory_entry.h',
         'file_io.h',
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 2675a1f..a45e894 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1063,6 +1063,8 @@
       "socket/transport_client_socket.h",
       "socket/transport_client_socket_pool.cc",
       "socket/transport_client_socket_pool.h",
+      "socket/transport_connect_job.cc",
+      "socket/transport_connect_job.h",
       "socket/udp_client_socket.cc",
       "socket/udp_client_socket.h",
       "socket/udp_net_log_parameters.cc",
@@ -5004,6 +5006,8 @@
     "quic/quic_test_packet_maker.h",
     "quic/quic_utils_chromium_test.cc",
     "socket/client_socket_pool_base_unittest.cc",
+    "socket/connect_job_test_util.cc",
+    "socket/connect_job_test_util.h",
     "socket/connect_job_unittest.cc",
     "socket/mock_client_socket_pool_manager.cc",
     "socket/mock_client_socket_pool_manager.h",
@@ -5023,6 +5027,7 @@
     "socket/transport_client_socket_pool_test_util.h",
     "socket/transport_client_socket_pool_unittest.cc",
     "socket/transport_client_socket_unittest.cc",
+    "socket/transport_connect_job_unittest.cc",
     "socket/udp_socket_unittest.cc",
     "socket/websocket_endpoint_lock_manager_unittest.cc",
     "socket/websocket_transport_client_socket_pool_unittest.cc",
diff --git a/net/dns/mdns_client_unittest.cc b/net/dns/mdns_client_unittest.cc
index 3519a1d..bd4f748a 100644
--- a/net/dns/mdns_client_unittest.cc
+++ b/net/dns/mdns_client_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
 #include "base/timer/mock_timer.h"
+#include "net/base/address_family.h"
 #include "net/base/completion_repeating_callback.h"
 #include "net/base/ip_address.h"
 #include "net/base/rand_callback.h"
@@ -22,6 +23,7 @@
 #include "net/dns/mdns_client_impl.h"
 #include "net/dns/mock_mdns_socket_factory.h"
 #include "net/dns/record_rdata.h"
+#include "net/log/net_log.h"
 #include "net/socket/udp_client_socket.h"
 #include "net/test/test_with_scoped_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -1261,4 +1263,13 @@
   callback.Run(OK);
 }
 
+TEST(MDnsSocketTest, CreateSocket) {
+  // Verifies that socket creation hasn't been broken.
+  NetLog net_log;
+  auto socket =
+      CreateAndBindMDnsSocket(AddressFamily::ADDRESS_FAMILY_IPV4, 1, &net_log);
+  EXPECT_TRUE(socket);
+  socket->Close();
+}
+
 }  // namespace net
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
index 0196926..761fe8c 100644
--- a/net/http/http_proxy_client_socket_pool.cc
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -32,6 +32,7 @@
 #include "net/socket/ssl_client_socket.h"
 #include "net/socket/ssl_client_socket_pool.h"
 #include "net/socket/transport_client_socket_pool.h"
+#include "net/socket/transport_connect_job.h"
 #include "net/spdy/spdy_proxy_client_socket.h"
 #include "net/spdy/spdy_session.h"
 #include "net/spdy/spdy_session_pool.h"
diff --git a/net/socket/connect_job_test_util.cc b/net/socket/connect_job_test_util.cc
new file mode 100644
index 0000000..dbb1c65
--- /dev/null
+++ b/net/socket/connect_job_test_util.cc
@@ -0,0 +1,51 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/socket/connect_job_test_util.h"
+
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "net/socket/stream_socket.h"
+#include "net/test/gtest_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+TestConnectJobDelegate::TestConnectJobDelegate() = default;
+TestConnectJobDelegate::~TestConnectJobDelegate() = default;
+
+void TestConnectJobDelegate::OnConnectJobComplete(int result, ConnectJob* job) {
+  EXPECT_FALSE(has_result_);
+  result_ = result;
+  socket_ = job->PassSocket();
+  // socket_.get() should be nullptr iff result != OK.
+  EXPECT_EQ(socket_.get() == nullptr, result != OK);
+  has_result_ = true;
+  run_loop_.Quit();
+}
+
+int TestConnectJobDelegate::WaitForResult() {
+  run_loop_.Run();
+  DCHECK(has_result_);
+  return result_;
+}
+
+void TestConnectJobDelegate::StartJobExpectingResult(ConnectJob* connect_job,
+                                                     net::Error expected_result,
+                                                     bool expect_sync_result) {
+  int rv = connect_job->Connect();
+  if (expect_sync_result) {
+    EXPECT_THAT(rv, test::IsError(expected_result));
+    // The callback should not be invoked when a synchronous result is returned.
+    base::RunLoop().RunUntilIdle();
+    EXPECT_FALSE(has_result_);
+  } else {
+    EXPECT_FALSE(has_result_);
+    EXPECT_THAT(WaitForResult(), test::IsError(expected_result));
+    // Make sure the callback isn't invoked again.
+    base::RunLoop().RunUntilIdle();
+  }
+}
+
+}  // namespace net
diff --git a/net/socket/connect_job_test_util.h b/net/socket/connect_job_test_util.h
new file mode 100644
index 0000000..df677b5
--- /dev/null
+++ b/net/socket/connect_job_test_util.h
@@ -0,0 +1,52 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_SOCKET_CONNECT_JOB_TEST_UTIL_H_
+#define NET_SOCKET_CONNECT_JOB_TEST_UTIL_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "net/base/net_errors.h"
+#include "net/socket/connect_job.h"
+
+namespace net {
+
+class StreamSocket;
+
+class TestConnectJobDelegate : public ConnectJob::Delegate {
+ public:
+  TestConnectJobDelegate();
+  ~TestConnectJobDelegate() override;
+
+  // ConnectJob::Delegate implementation.
+  void OnConnectJobComplete(int result, ConnectJob* job) override;
+
+  // Waits for the ConnectJob to complete if it hasn't already and returns the
+  // resulting network error code.
+  int WaitForResult();
+
+  // Returns true if the ConnectJob has a result.
+  bool has_result() const { return has_result_; }
+
+  void StartJobExpectingResult(ConnectJob* connect_job,
+                               net::Error expected_result,
+                               bool expect_sync_result);
+
+  StreamSocket* socket() { return socket_.get(); }
+
+ private:
+  bool has_result_ = false;
+  int result_ = ERR_IO_PENDING;
+  std::unique_ptr<StreamSocket> socket_;
+
+  base::RunLoop run_loop_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestConnectJobDelegate);
+};
+
+}  // namespace net
+
+#endif  // NET_SOCKET_CONNECT_JOB_TEST_UTIL_H_
diff --git a/net/socket/connect_job_unittest.cc b/net/socket/connect_job_unittest.cc
index 0413ab0..5071860 100644
--- a/net/socket/connect_job_unittest.cc
+++ b/net/socket/connect_job_unittest.cc
@@ -14,6 +14,7 @@
 #include "net/base/request_priority.h"
 #include "net/log/test_net_log.h"
 #include "net/log/test_net_log_util.h"
+#include "net/socket/connect_job_test_util.h"
 #include "net/socket/socket_tag.h"
 #include "net/socket/socket_test_util.h"
 #include "net/test/gtest_util.h"
@@ -22,36 +23,6 @@
 namespace net {
 namespace {
 
-class TestConnectJobDelegate : public ConnectJob::Delegate {
- public:
-  TestConnectJobDelegate() = default;
-  ~TestConnectJobDelegate() override = default;
-
-  void OnConnectJobComplete(int result, ConnectJob* job) override {
-    result_ = result;
-    std::unique_ptr<StreamSocket> socket = job->PassSocket();
-    // socket.get() should be NULL iff result != OK
-    EXPECT_EQ(socket == nullptr, result != OK);
-    has_result_ = true;
-    run_loop_.Quit();
-  }
-
-  int WaitForResult() {
-    run_loop_.Run();
-    DCHECK(has_result_);
-    return result_;
-  }
-
-  bool has_result() const { return has_result_; }
-
- private:
-  bool has_result_ = false;
-  int result_ = ERR_IO_PENDING;
-  base::RunLoop run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestConnectJobDelegate);
-};
-
 class TestConnectJob : public ConnectJob {
  public:
   enum class JobType {
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index 24a6c39..c0d067b 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -27,6 +27,7 @@
 #include "net/socket/socks_client_socket_pool.h"
 #include "net/socket/ssl_client_socket.h"
 #include "net/socket/transport_client_socket_pool.h"
+#include "net/socket/transport_connect_job.h"
 #include "net/ssl/ssl_cert_request_info.h"
 #include "net/ssl/ssl_connection_status_flags.h"
 #include "net/ssl/ssl_info.h"
diff --git a/net/socket/transport_client_socket_pool.cc b/net/socket/transport_client_socket_pool.cc
index 0940fa7..7ad5afc 100644
--- a/net/socket/transport_client_socket_pool.cc
+++ b/net/socket/transport_client_socket_pool.cc
@@ -4,19 +4,12 @@
 
 #include "net/socket/transport_client_socket_pool.h"
 
-#include <algorithm>
-#include <utility>
-
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_util.h"
-#include "base/synchronization/lock.h"
-#include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "base/values.h"
-#include "net/base/ip_endpoint.h"
-#include "net/base/net_errors.h"
 #include "net/base/trace_constants.h"
 #include "net/log/net_log.h"
 #include "net/log/net_log_event_type.h"
@@ -26,439 +19,19 @@
 #include "net/socket/client_socket_handle.h"
 #include "net/socket/client_socket_pool_base.h"
 #include "net/socket/socket_net_log_params.h"
-#include "net/socket/socket_performance_watcher.h"
-#include "net/socket/socket_performance_watcher_factory.h"
-#include "net/socket/tcp_client_socket.h"
-
-using base::TimeDelta;
 
 namespace net {
 
-namespace {
-
-// Returns true iff all addresses in |list| are in the IPv6 family.
-bool AddressListOnlyContainsIPv6(const AddressList& list) {
-  DCHECK(!list.empty());
-  for (auto iter = list.begin(); iter != list.end(); ++iter) {
-    if (iter->GetFamily() != ADDRESS_FAMILY_IPV6)
-      return false;
-  }
-  return true;
-}
-
-// TODO(eroman): The use of this constant needs to be re-evaluated. The time
-// needed for TCPClientSocketXXX::Connect() can be arbitrarily long, since
-// the address list may contain many alternatives, and most of those may
-// timeout. Even worse, the per-connect timeout threshold varies greatly
-// between systems (anywhere from 20 seconds to 190 seconds).
-// See comment #12 at http://crbug.com/23364 for specifics.
-const int kTransportConnectJobTimeoutInSeconds = 240;  // 4 minutes.
-
-}  // namespace
-
-TransportSocketParams::TransportSocketParams(
-    const HostPortPair& host_port_pair,
-    bool disable_resolver_cache,
-    const OnHostResolutionCallback& host_resolution_callback)
-    : destination_(host_port_pair),
-      host_resolution_callback_(host_resolution_callback) {
-  if (disable_resolver_cache)
-    destination_.set_allow_cached_response(false);
-}
-
-TransportSocketParams::~TransportSocketParams() = default;
-
-// TODO(willchan): Base this off RTT instead of statically setting it. Note we
-// choose a timeout that is different from the backup connect job timer so they
-// don't synchronize.
-const int TransportConnectJob::kIPv6FallbackTimerInMs = 300;
-
-TransportConnectJob::TransportConnectJob(
-    const std::string& group_name,
-    RequestPriority priority,
-    const SocketTag& socket_tag,
-    ClientSocketPool::RespectLimits respect_limits,
-    const scoped_refptr<TransportSocketParams>& params,
-    ClientSocketFactory* client_socket_factory,
-    SocketPerformanceWatcherFactory* socket_performance_watcher_factory,
-    HostResolver* host_resolver,
-    Delegate* delegate,
-    NetLog* net_log)
-    : ConnectJob(
-          group_name,
-          ConnectionTimeout(),
-          priority,
-          socket_tag,
-          respect_limits == ClientSocketPool::RespectLimits::ENABLED,
-          delegate,
-          NetLogWithSource::Make(net_log,
-                                 NetLogSourceType::TRANSPORT_CONNECT_JOB)),
-      params_(params),
-      resolver_(host_resolver),
-      client_socket_factory_(client_socket_factory),
-      next_state_(STATE_NONE),
-      socket_performance_watcher_factory_(socket_performance_watcher_factory),
-      resolve_result_(OK) {}
-
-TransportConnectJob::~TransportConnectJob() {
-  // We don't worry about cancelling the host resolution and TCP connect, since
-  // ~HostResolver::Request and ~StreamSocket will take care of it.
-}
-
-LoadState TransportConnectJob::GetLoadState() const {
-  switch (next_state_) {
-    case STATE_RESOLVE_HOST:
-    case STATE_RESOLVE_HOST_COMPLETE:
-      return LOAD_STATE_RESOLVING_HOST;
-    case STATE_TRANSPORT_CONNECT:
-    case STATE_TRANSPORT_CONNECT_COMPLETE:
-      return LOAD_STATE_CONNECTING;
-    case STATE_NONE:
-      return LOAD_STATE_IDLE;
-  }
-  NOTREACHED();
-  return LOAD_STATE_IDLE;
-}
-
-void TransportConnectJob::GetAdditionalErrorState(ClientSocketHandle* handle) {
-  // If hostname resolution failed, record an empty endpoint and the result.
-  // Also record any attempts made on either of the sockets.
-  ConnectionAttempts attempts;
-  if (resolve_result_ != OK) {
-    DCHECK_EQ(0u, addresses_.size());
-    attempts.push_back(ConnectionAttempt(IPEndPoint(), resolve_result_));
-  }
-  attempts.insert(attempts.begin(), connection_attempts_.begin(),
-                  connection_attempts_.end());
-  attempts.insert(attempts.begin(), fallback_connection_attempts_.begin(),
-                  fallback_connection_attempts_.end());
-  handle->set_connection_attempts(attempts);
-}
-
-// static
-void TransportConnectJob::MakeAddressListStartWithIPv4(AddressList* list) {
-  for (auto i = list->begin(); i != list->end(); ++i) {
-    if (i->GetFamily() == ADDRESS_FAMILY_IPV4) {
-      std::rotate(list->begin(), i, list->end());
-      break;
-    }
-  }
-}
-
-// static
-void TransportConnectJob::HistogramDuration(
-    const LoadTimingInfo::ConnectTiming& connect_timing,
-    RaceResult race_result) {
-  DCHECK(!connect_timing.connect_start.is_null());
-  DCHECK(!connect_timing.dns_start.is_null());
-  base::TimeTicks now = base::TimeTicks::Now();
-  base::TimeDelta total_duration = now - connect_timing.dns_start;
-  UMA_HISTOGRAM_CUSTOM_TIMES("Net.DNS_Resolution_And_TCP_Connection_Latency2",
-                             total_duration,
-                             base::TimeDelta::FromMilliseconds(1),
-                             base::TimeDelta::FromMinutes(10),
-                             100);
-
-  base::TimeDelta connect_duration = now - connect_timing.connect_start;
-  UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency",
-                             connect_duration,
-                             base::TimeDelta::FromMilliseconds(1),
-                             base::TimeDelta::FromMinutes(10),
-                             100);
-
-  switch (race_result) {
-    case RACE_IPV4_WINS:
-      UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_Wins_Race",
-                                 connect_duration,
-                                 base::TimeDelta::FromMilliseconds(1),
-                                 base::TimeDelta::FromMinutes(10),
-                                 100);
-      break;
-
-    case RACE_IPV4_SOLO:
-      UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_No_Race",
-                                 connect_duration,
-                                 base::TimeDelta::FromMilliseconds(1),
-                                 base::TimeDelta::FromMinutes(10),
-                                 100);
-      break;
-
-    case RACE_IPV6_WINS:
-      UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Raceable",
-                                 connect_duration,
-                                 base::TimeDelta::FromMilliseconds(1),
-                                 base::TimeDelta::FromMinutes(10),
-                                 100);
-      break;
-
-    case RACE_IPV6_SOLO:
-      UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Solo",
-                                 connect_duration,
-                                 base::TimeDelta::FromMilliseconds(1),
-                                 base::TimeDelta::FromMinutes(10),
-                                 100);
-      break;
-
-    default:
-      NOTREACHED();
-      break;
-  }
-}
-
-// static
-base::TimeDelta TransportConnectJob::ConnectionTimeout() {
-  return base::TimeDelta::FromSeconds(kTransportConnectJobTimeoutInSeconds);
-}
-
-void TransportConnectJob::OnIOComplete(int result) {
-  result = DoLoop(result);
-  if (result != ERR_IO_PENDING)
-    NotifyDelegateOfCompletion(result);  // Deletes |this|
-}
-
-int TransportConnectJob::DoLoop(int result) {
-  DCHECK_NE(next_state_, STATE_NONE);
-
-  int rv = result;
-  do {
-    State state = next_state_;
-    next_state_ = STATE_NONE;
-    switch (state) {
-      case STATE_RESOLVE_HOST:
-        DCHECK_EQ(OK, rv);
-        rv = DoResolveHost();
-        break;
-      case STATE_RESOLVE_HOST_COMPLETE:
-        rv = DoResolveHostComplete(rv);
-        break;
-      case STATE_TRANSPORT_CONNECT:
-        DCHECK_EQ(OK, rv);
-        rv = DoTransportConnect();
-        break;
-      case STATE_TRANSPORT_CONNECT_COMPLETE:
-        rv = DoTransportConnectComplete(rv);
-        break;
-      default:
-        NOTREACHED();
-        rv = ERR_FAILED;
-        break;
-    }
-  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
-
-  return rv;
-}
-
-int TransportConnectJob::DoResolveHost() {
-  next_state_ = STATE_RESOLVE_HOST_COMPLETE;
-  connect_timing_.dns_start = base::TimeTicks::Now();
-
-  return resolver_->Resolve(
-      params_->destination(), priority(), &addresses_,
-      base::Bind(&TransportConnectJob::OnIOComplete, base::Unretained(this)),
-      &request_, net_log());
-}
-
-int TransportConnectJob::DoResolveHostComplete(int result) {
-  TRACE_EVENT0(NetTracingCategory(),
-               "TransportConnectJob::DoResolveHostComplete");
-  connect_timing_.dns_end = base::TimeTicks::Now();
-  // Overwrite connection start time, since for connections that do not go
-  // through proxies, |connect_start| should not include dns lookup time.
-  connect_timing_.connect_start = connect_timing_.dns_end;
-  resolve_result_ = result;
-
-  if (result != OK)
-    return result;
-
-  // Invoke callback, and abort if it fails.
-  if (!params_->host_resolution_callback().is_null()) {
-    result = params_->host_resolution_callback().Run(addresses_, net_log());
-    if (result != OK)
-      return result;
-  }
-
-  next_state_ = STATE_TRANSPORT_CONNECT;
-  return result;
-}
-
-int TransportConnectJob::DoTransportConnect() {
-  next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
-  // Create a |SocketPerformanceWatcher|, and pass the ownership.
-  std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher;
-  if (socket_performance_watcher_factory_) {
-    socket_performance_watcher =
-        socket_performance_watcher_factory_->CreateSocketPerformanceWatcher(
-            SocketPerformanceWatcherFactory::PROTOCOL_TCP, addresses_);
-  }
-  transport_socket_ = client_socket_factory_->CreateTransportClientSocket(
-      addresses_, std::move(socket_performance_watcher), net_log().net_log(),
-      net_log().source());
-
-  // If the list contains IPv6 and IPv4 addresses, and the first address
-  // is IPv6, the IPv4 addresses will be tried as fallback addresses, per
-  // "Happy Eyeballs" (RFC 6555).
-  bool try_ipv6_connect_with_ipv4_fallback =
-      addresses_.front().GetFamily() == ADDRESS_FAMILY_IPV6 &&
-      !AddressListOnlyContainsIPv6(addresses_);
-
-  transport_socket_->ApplySocketTag(socket_tag());
-
-  int rv = transport_socket_->Connect(base::BindOnce(
-      &TransportConnectJob::OnIOComplete, base::Unretained(this)));
-  if (rv == ERR_IO_PENDING && try_ipv6_connect_with_ipv4_fallback) {
-    fallback_timer_.Start(
-        FROM_HERE, base::TimeDelta::FromMilliseconds(kIPv6FallbackTimerInMs),
-        this, &TransportConnectJob::DoIPv6FallbackTransportConnect);
-  }
-  return rv;
-}
-
-int TransportConnectJob::DoTransportConnectComplete(int result) {
-  if (result == OK) {
-    // Success will be returned via the main socket, so also include connection
-    // attempts made on the fallback socket up to this point. (Unfortunately,
-    // the only simple way to return information in the success case is through
-    // the successfully-connected socket.)
-    if (fallback_transport_socket_) {
-      ConnectionAttempts fallback_attempts;
-      fallback_transport_socket_->GetConnectionAttempts(&fallback_attempts);
-      transport_socket_->AddConnectionAttempts(fallback_attempts);
-    }
-
-    bool is_ipv4 = addresses_.front().GetFamily() == ADDRESS_FAMILY_IPV4;
-    RaceResult race_result = RACE_UNKNOWN;
-    if (is_ipv4)
-      race_result = RACE_IPV4_SOLO;
-    else if (AddressListOnlyContainsIPv6(addresses_))
-      race_result = RACE_IPV6_SOLO;
-    else
-      race_result = RACE_IPV6_WINS;
-    HistogramDuration(connect_timing_, race_result);
-
-    SetSocket(std::move(transport_socket_));
-  } else {
-    // Failure will be returned via |GetAdditionalErrorState|, so save
-    // connection attempts from both sockets for use there.
-    CopyConnectionAttemptsFromSockets();
-
-    transport_socket_.reset();
-  }
-
-  fallback_timer_.Stop();
-  fallback_transport_socket_.reset();
-  fallback_addresses_.reset();
-
-  return result;
-}
-
-void TransportConnectJob::DoIPv6FallbackTransportConnect() {
-  // The timer should only fire while we're waiting for the main connect to
-  // succeed.
-  if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) {
-    NOTREACHED();
-    return;
-  }
-
-  DCHECK(!fallback_transport_socket_.get());
-  DCHECK(!fallback_addresses_.get());
-
-  fallback_addresses_.reset(new AddressList(addresses_));
-  MakeAddressListStartWithIPv4(fallback_addresses_.get());
-
-  // Create a |SocketPerformanceWatcher|, and pass the ownership.
-  std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher;
-  if (socket_performance_watcher_factory_) {
-    socket_performance_watcher =
-        socket_performance_watcher_factory_->CreateSocketPerformanceWatcher(
-            SocketPerformanceWatcherFactory::PROTOCOL_TCP,
-            *fallback_addresses_);
-  }
-
-  fallback_transport_socket_ =
-      client_socket_factory_->CreateTransportClientSocket(
-          *fallback_addresses_, std::move(socket_performance_watcher),
-          net_log().net_log(), net_log().source());
-  fallback_connect_start_time_ = base::TimeTicks::Now();
-  int rv = fallback_transport_socket_->Connect(base::BindOnce(
-      &TransportConnectJob::DoIPv6FallbackTransportConnectComplete,
-      base::Unretained(this)));
-  if (rv != ERR_IO_PENDING)
-    DoIPv6FallbackTransportConnectComplete(rv);
-}
-
-void TransportConnectJob::DoIPv6FallbackTransportConnectComplete(int result) {
-  // This should only happen when we're waiting for the main connect to succeed.
-  if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) {
-    NOTREACHED();
-    return;
-  }
-
-  DCHECK_NE(ERR_IO_PENDING, result);
-  DCHECK(fallback_transport_socket_.get());
-  DCHECK(fallback_addresses_.get());
-
-  if (result == OK) {
-    DCHECK(!fallback_connect_start_time_.is_null());
-
-    // Success will be returned via the fallback socket, so also include
-    // connection attempts made on the main socket up to this point.
-    // (Unfortunately, the only simple way to return information in the success
-    // case is through the successfully-connected socket.)
-    if (transport_socket_) {
-      ConnectionAttempts attempts;
-      transport_socket_->GetConnectionAttempts(&attempts);
-      fallback_transport_socket_->AddConnectionAttempts(attempts);
-    }
-
-    connect_timing_.connect_start = fallback_connect_start_time_;
-    HistogramDuration(connect_timing_, RACE_IPV4_WINS);
-    SetSocket(std::move(fallback_transport_socket_));
-    next_state_ = STATE_NONE;
-  } else {
-    // Failure will be returned via |GetAdditionalErrorState|, so save
-    // connection attempts from both sockets for use there.
-    CopyConnectionAttemptsFromSockets();
-
-    fallback_transport_socket_.reset();
-    fallback_addresses_.reset();
-  }
-
-  transport_socket_.reset();
-
-  NotifyDelegateOfCompletion(result);  // Deletes |this|
-}
-
-int TransportConnectJob::ConnectInternal() {
-  next_state_ = STATE_RESOLVE_HOST;
-  return DoLoop(OK);
-}
-
-void TransportConnectJob::ChangePriorityInternal(RequestPriority priority) {
-  if (next_state_ == STATE_RESOLVE_HOST_COMPLETE) {
-    DCHECK(request_);
-    // Change the request priority in the host resolver.
-    request_->ChangeRequestPriority(priority);
-  }
-}
-
-void TransportConnectJob::CopyConnectionAttemptsFromSockets() {
-  if (transport_socket_)
-    transport_socket_->GetConnectionAttempts(&connection_attempts_);
-  if (fallback_transport_socket_) {
-    fallback_transport_socket_->GetConnectionAttempts(
-        &fallback_connection_attempts_);
-  }
-}
-
 std::unique_ptr<ConnectJob>
 TransportClientSocketPool::TransportConnectJobFactory::NewConnectJob(
     const std::string& group_name,
     const PoolBase::Request& request,
     ConnectJob::Delegate* delegate) const {
-  return std::unique_ptr<ConnectJob>(new TransportConnectJob(
+  return std::make_unique<TransportConnectJob>(
       group_name, request.priority(), request.socket_tag(),
-      request.respect_limits(), request.params(), client_socket_factory_,
-      socket_performance_watcher_factory_, host_resolver_, delegate, net_log_));
+      request.respect_limits() == ClientSocketPool::RespectLimits::ENABLED,
+      request.params(), client_socket_factory_,
+      socket_performance_watcher_factory_, host_resolver_, delegate, net_log_);
 }
 
 TransportClientSocketPool::TransportClientSocketPool(
diff --git a/net/socket/transport_client_socket_pool.h b/net/socket/transport_client_socket_pool.h
index b88b378..c010a5c82 100644
--- a/net/socket/transport_client_socket_pool.h
+++ b/net/socket/transport_client_socket_pool.h
@@ -11,169 +11,24 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "net/base/host_port_pair.h"
 #include "net/base/net_export.h"
-#include "net/dns/host_resolver.h"
 #include "net/socket/client_socket_pool.h"
 #include "net/socket/client_socket_pool_base.h"
 #include "net/socket/connection_attempts.h"
 #include "net/socket/socket_tag.h"
+#include "net/socket/transport_connect_job.h"
 
 namespace net {
 
 class ClientSocketFactory;
-class SocketPerformanceWatcherFactory;
+class HostResolver;
 class NetLog;
 class NetLogWithSource;
-
-typedef base::Callback<int(const AddressList&, const NetLogWithSource& net_log)>
-    OnHostResolutionCallback;
-
-class NET_EXPORT_PRIVATE TransportSocketParams
-    : public base::RefCounted<TransportSocketParams> {
- public:
-  // |host_resolution_callback| will be invoked after the the hostname is
-  // resolved.  If |host_resolution_callback| does not return OK, then the
-  // connection will be aborted with that value.
-  TransportSocketParams(
-      const HostPortPair& host_port_pair,
-      bool disable_resolver_cache,
-      const OnHostResolutionCallback& host_resolution_callback);
-
-  const HostResolver::RequestInfo& destination() const { return destination_; }
-  const OnHostResolutionCallback& host_resolution_callback() const {
-    return host_resolution_callback_;
-  }
-
- private:
-  friend class base::RefCounted<TransportSocketParams>;
-  ~TransportSocketParams();
-
-  HostResolver::RequestInfo destination_;
-  const OnHostResolutionCallback host_resolution_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(TransportSocketParams);
-};
-
-// TransportConnectJob handles the host resolution necessary for socket creation
-// and the transport (likely TCP) connect. TransportConnectJob also has fallback
-// logic for IPv6 connect() timeouts (which may happen due to networks / routers
-// with broken IPv6 support). Those timeouts take 20s, so rather than make the
-// user wait 20s for the timeout to fire, we use a fallback timer
-// (kIPv6FallbackTimerInMs) and start a connect() to a IPv4 address if the timer
-// fires. Then we race the IPv4 connect() against the IPv6 connect() (which has
-// a headstart) and return the one that completes first to the socket pool.
-class NET_EXPORT_PRIVATE TransportConnectJob : public ConnectJob {
- public:
-  // For recording the connection time in the appropriate bucket.
-  enum RaceResult {
-    RACE_UNKNOWN,
-    RACE_IPV4_WINS,
-    RACE_IPV4_SOLO,
-    RACE_IPV6_WINS,
-    RACE_IPV6_SOLO,
-  };
-
-  // In cases where both IPv6 and IPv4 addresses were returned from DNS,
-  // TransportConnectJobs will start a second connection attempt to just the
-  // IPv4 addresses after this many milliseconds. (This is "Happy Eyeballs".)
-  static const int kIPv6FallbackTimerInMs;
-
-  TransportConnectJob(
-      const std::string& group_name,
-      RequestPriority priority,
-      const SocketTag& socket_tag,
-      ClientSocketPool::RespectLimits respect_limits,
-      const scoped_refptr<TransportSocketParams>& params,
-      ClientSocketFactory* client_socket_factory,
-      SocketPerformanceWatcherFactory* socket_performance_watcher_factory,
-      HostResolver* host_resolver,
-      Delegate* delegate,
-      NetLog* net_log);
-  ~TransportConnectJob() override;
-
-  // ConnectJob methods.
-  LoadState GetLoadState() const override;
-  void GetAdditionalErrorState(ClientSocketHandle* handle) override;
-
-  // Rolls |addrlist| forward until the first IPv4 address, if any.
-  // WARNING: this method should only be used to implement the prefer-IPv4 hack.
-  static void MakeAddressListStartWithIPv4(AddressList* addrlist);
-
-  // Record the histograms Net.DNS_Resolution_And_TCP_Connection_Latency2 and
-  // Net.TCP_Connection_Latency and return the connect duration.
-  static void HistogramDuration(
-      const LoadTimingInfo::ConnectTiming& connect_timing,
-      RaceResult race_result);
-
-  // Returns the connection timeout used by TransportConnectJobs.
-  static base::TimeDelta ConnectionTimeout();
-
- private:
-  enum State {
-    STATE_RESOLVE_HOST,
-    STATE_RESOLVE_HOST_COMPLETE,
-    STATE_TRANSPORT_CONNECT,
-    STATE_TRANSPORT_CONNECT_COMPLETE,
-    STATE_NONE,
-  };
-
-  void OnIOComplete(int result);
-  int DoLoop(int result);
-
-  int DoResolveHost();
-  int DoResolveHostComplete(int result);
-  int DoTransportConnect();
-  int DoTransportConnectComplete(int result);
-
-  // Not part of the state machine.
-  void DoIPv6FallbackTransportConnect();
-  void DoIPv6FallbackTransportConnectComplete(int result);
-
-  // Begins the host resolution and the TCP connect.  Returns OK on success
-  // and ERR_IO_PENDING if it cannot immediately service the request.
-  // Otherwise, it returns a net error code.
-  int ConnectInternal() override;
-
-  // If host resolution is currently underway, change the priority of the host
-  // resolver request.
-  void ChangePriorityInternal(RequestPriority priority) override;
-
-  void CopyConnectionAttemptsFromSockets();
-
-  scoped_refptr<TransportSocketParams> params_;
-  HostResolver* resolver_;
-  std::unique_ptr<HostResolver::Request> request_;
-  ClientSocketFactory* const client_socket_factory_;
-
-  State next_state_;
-
-  std::unique_ptr<StreamSocket> transport_socket_;
-  AddressList addresses_;
-
-  std::unique_ptr<StreamSocket> fallback_transport_socket_;
-  std::unique_ptr<AddressList> fallback_addresses_;
-  base::TimeTicks fallback_connect_start_time_;
-  base::OneShotTimer fallback_timer_;
-  SocketPerformanceWatcherFactory* socket_performance_watcher_factory_;
-
-  int resolve_result_;
-
-  // Used in the failure case to save connection attempts made on the main and
-  // fallback sockets and pass them on in |GetAdditionalErrorState|. (In the
-  // success case, connection attempts are passed through the returned socket;
-  // attempts are copied from the other socket, if one exists, into it before
-  // it is returned.)
-  ConnectionAttempts connection_attempts_;
-  ConnectionAttempts fallback_connection_attempts_;
-
-  DISALLOW_COPY_AND_ASSIGN(TransportConnectJob);
-};
+class SocketPerformanceWatcherFactory;
 
 class NET_EXPORT_PRIVATE TransportClientSocketPool : public ClientSocketPool {
  public:
-  typedef TransportSocketParams SocketParams;
+  using SocketParams = TransportSocketParams;
 
   TransportClientSocketPool(
       int max_sockets,
diff --git a/net/socket/transport_client_socket_pool_unittest.cc b/net/socket/transport_client_socket_pool_unittest.cc
index b0bf833..846362f5 100644
--- a/net/socket/transport_client_socket_pool_unittest.cc
+++ b/net/socket/transport_client_socket_pool_unittest.cc
@@ -12,7 +12,6 @@
 #include "base/threading/platform_thread.h"
 #include "build/build_config.h"
 #include "net/base/completion_once_callback.h"
-#include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/load_timing_info.h"
 #include "net/base/load_timing_info_test_util.h"
@@ -105,75 +104,6 @@
   DISALLOW_COPY_AND_ASSIGN(TransportClientSocketPoolTest);
 };
 
-TEST(TransportConnectJobTest, MakeAddrListStartWithIPv4) {
-  IPEndPoint addrlist_v4_1(IPAddress(192, 168, 1, 1), 80);
-  IPEndPoint addrlist_v4_2(IPAddress(192, 168, 1, 2), 80);
-  IPAddress ip_address;
-  ASSERT_TRUE(ip_address.AssignFromIPLiteral("2001:4860:b006::64"));
-  IPEndPoint addrlist_v6_1(ip_address, 80);
-  ASSERT_TRUE(ip_address.AssignFromIPLiteral("2001:4860:b006::66"));
-  IPEndPoint addrlist_v6_2(ip_address, 80);
-
-  AddressList addrlist;
-
-  // Test 1: IPv4 only.  Expect no change.
-  addrlist.clear();
-  addrlist.push_back(addrlist_v4_1);
-  addrlist.push_back(addrlist_v4_2);
-  TransportConnectJob::MakeAddressListStartWithIPv4(&addrlist);
-  ASSERT_EQ(2u, addrlist.size());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[0].GetFamily());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[1].GetFamily());
-
-  // Test 2: IPv6 only.  Expect no change.
-  addrlist.clear();
-  addrlist.push_back(addrlist_v6_1);
-  addrlist.push_back(addrlist_v6_2);
-  TransportConnectJob::MakeAddressListStartWithIPv4(&addrlist);
-  ASSERT_EQ(2u, addrlist.size());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[0].GetFamily());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[1].GetFamily());
-
-  // Test 3: IPv4 then IPv6.  Expect no change.
-  addrlist.clear();
-  addrlist.push_back(addrlist_v4_1);
-  addrlist.push_back(addrlist_v4_2);
-  addrlist.push_back(addrlist_v6_1);
-  addrlist.push_back(addrlist_v6_2);
-  TransportConnectJob::MakeAddressListStartWithIPv4(&addrlist);
-  ASSERT_EQ(4u, addrlist.size());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[0].GetFamily());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[1].GetFamily());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[2].GetFamily());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[3].GetFamily());
-
-  // Test 4: IPv6, IPv4, IPv6, IPv4.  Expect first IPv6 moved to the end.
-  addrlist.clear();
-  addrlist.push_back(addrlist_v6_1);
-  addrlist.push_back(addrlist_v4_1);
-  addrlist.push_back(addrlist_v6_2);
-  addrlist.push_back(addrlist_v4_2);
-  TransportConnectJob::MakeAddressListStartWithIPv4(&addrlist);
-  ASSERT_EQ(4u, addrlist.size());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[0].GetFamily());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[1].GetFamily());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[2].GetFamily());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[3].GetFamily());
-
-  // Test 5: IPv6, IPv6, IPv4, IPv4.  Expect first two IPv6's moved to the end.
-  addrlist.clear();
-  addrlist.push_back(addrlist_v6_1);
-  addrlist.push_back(addrlist_v6_2);
-  addrlist.push_back(addrlist_v4_1);
-  addrlist.push_back(addrlist_v4_2);
-  TransportConnectJob::MakeAddressListStartWithIPv4(&addrlist);
-  ASSERT_EQ(4u, addrlist.size());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[0].GetFamily());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[1].GetFamily());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[2].GetFamily());
-  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[3].GetFamily());
-}
-
 TEST_F(TransportClientSocketPoolTest, Basic) {
   TestCompletionCallback callback;
   ClientSocketHandle handle;
@@ -1096,171 +1026,6 @@
   handle.Reset();
 }
 
-// Test the case of the IPv6 address stalling, and falling back to the IPv4
-// socket which finishes first.
-TEST_F(TransportClientSocketPoolTest, IPv6FallbackSocketIPv4FinishesFirst) {
-  // Create a pool without backup jobs.
-  ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
-  TransportClientSocketPool pool(kMaxSockets, kMaxSocketsPerGroup,
-                                 host_resolver_.get(), &client_socket_factory_,
-                                 NULL, NULL);
-
-  MockTransportClientSocketFactory::ClientSocketType case_types[] = {
-      // This is the IPv6 socket. It stalls, but presents one failed connection
-      // attempt on GetConnectionAttempts.
-      MockTransportClientSocketFactory::MOCK_STALLED_FAILING_CLIENT_SOCKET,
-      // This is the IPv4 socket.
-      MockTransportClientSocketFactory::MOCK_PENDING_CLIENT_SOCKET};
-
-  client_socket_factory_.set_client_socket_types(case_types, 2);
-
-  // Resolve an AddressList with a IPv6 address first and then a IPv4 address.
-  host_resolver_->rules()
-      ->AddIPLiteralRule("*", "2:abcd::3:4:ff,2.2.2.2", std::string());
-
-  TestCompletionCallback callback;
-  ClientSocketHandle handle;
-  int rv = handle.Init("a", params_, LOW, SocketTag(),
-                       ClientSocketPool::RespectLimits::ENABLED,
-                       callback.callback(), &pool, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  EXPECT_FALSE(handle.is_initialized());
-  EXPECT_FALSE(handle.socket());
-
-  EXPECT_THAT(callback.WaitForResult(), IsOk());
-  EXPECT_TRUE(handle.is_initialized());
-  EXPECT_TRUE(handle.socket());
-  IPEndPoint endpoint;
-  handle.socket()->GetLocalAddress(&endpoint);
-  EXPECT_TRUE(endpoint.address().IsIPv4());
-
-  // Check that the failed connection attempt on the main socket is collected.
-  ConnectionAttempts attempts;
-  handle.socket()->GetConnectionAttempts(&attempts);
-  ASSERT_EQ(1u, attempts.size());
-  EXPECT_THAT(attempts[0].result, IsError(ERR_CONNECTION_FAILED));
-  EXPECT_TRUE(attempts[0].endpoint.address().IsIPv6());
-
-  EXPECT_EQ(2, client_socket_factory_.allocation_count());
-}
-
-// Test the case of the IPv6 address being slow, thus falling back to trying to
-// connect to the IPv4 address, but having the connect to the IPv6 address
-// finish first.
-TEST_F(TransportClientSocketPoolTest, IPv6FallbackSocketIPv6FinishesFirst) {
-  // Create a pool without backup jobs.
-  ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
-  TransportClientSocketPool pool(kMaxSockets, kMaxSocketsPerGroup,
-                                 host_resolver_.get(), &client_socket_factory_,
-                                 NULL, NULL);
-
-  MockTransportClientSocketFactory::ClientSocketType case_types[] = {
-      // This is the IPv6 socket.
-      MockTransportClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET,
-      // This is the IPv4 socket. It stalls, but presents one failed connection
-      // attempt on GetConnectionATtempts.
-      MockTransportClientSocketFactory::MOCK_STALLED_FAILING_CLIENT_SOCKET};
-
-  client_socket_factory_.set_client_socket_types(case_types, 2);
-  client_socket_factory_.set_delay(base::TimeDelta::FromMilliseconds(
-      TransportConnectJob::kIPv6FallbackTimerInMs + 50));
-
-  // Resolve an AddressList with a IPv6 address first and then a IPv4 address.
-  host_resolver_->rules()
-      ->AddIPLiteralRule("*", "2:abcd::3:4:ff,2.2.2.2", std::string());
-
-  TestCompletionCallback callback;
-  ClientSocketHandle handle;
-  int rv = handle.Init("a", params_, LOW, SocketTag(),
-                       ClientSocketPool::RespectLimits::ENABLED,
-                       callback.callback(), &pool, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  EXPECT_FALSE(handle.is_initialized());
-  EXPECT_FALSE(handle.socket());
-
-  EXPECT_THAT(callback.WaitForResult(), IsOk());
-  EXPECT_TRUE(handle.is_initialized());
-  EXPECT_TRUE(handle.socket());
-  IPEndPoint endpoint;
-  handle.socket()->GetLocalAddress(&endpoint);
-  EXPECT_TRUE(endpoint.address().IsIPv6());
-
-  // Check that the failed connection attempt on the fallback socket is
-  // collected.
-  ConnectionAttempts attempts;
-  handle.socket()->GetConnectionAttempts(&attempts);
-  ASSERT_EQ(1u, attempts.size());
-  EXPECT_THAT(attempts[0].result, IsError(ERR_CONNECTION_FAILED));
-  EXPECT_TRUE(attempts[0].endpoint.address().IsIPv4());
-
-  EXPECT_EQ(2, client_socket_factory_.allocation_count());
-}
-
-TEST_F(TransportClientSocketPoolTest, IPv6NoIPv4AddressesToFallbackTo) {
-  // Create a pool without backup jobs.
-  ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
-  TransportClientSocketPool pool(kMaxSockets, kMaxSocketsPerGroup,
-                                 host_resolver_.get(), &client_socket_factory_,
-                                 NULL, NULL);
-
-  client_socket_factory_.set_default_client_socket_type(
-      MockTransportClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET);
-
-  // Resolve an AddressList with only IPv6 addresses.
-  host_resolver_->rules()
-      ->AddIPLiteralRule("*", "2:abcd::3:4:ff,3:abcd::3:4:ff", std::string());
-
-  TestCompletionCallback callback;
-  ClientSocketHandle handle;
-  int rv = handle.Init("a", params_, LOW, SocketTag(),
-                       ClientSocketPool::RespectLimits::ENABLED,
-                       callback.callback(), &pool, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  EXPECT_FALSE(handle.is_initialized());
-  EXPECT_FALSE(handle.socket());
-
-  EXPECT_THAT(callback.WaitForResult(), IsOk());
-  EXPECT_TRUE(handle.is_initialized());
-  EXPECT_TRUE(handle.socket());
-  IPEndPoint endpoint;
-  handle.socket()->GetLocalAddress(&endpoint);
-  EXPECT_TRUE(endpoint.address().IsIPv6());
-  EXPECT_EQ(0u, handle.connection_attempts().size());
-  EXPECT_EQ(1, client_socket_factory_.allocation_count());
-}
-
-TEST_F(TransportClientSocketPoolTest, IPv4HasNoFallback) {
-  // Create a pool without backup jobs.
-  ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
-  TransportClientSocketPool pool(kMaxSockets, kMaxSocketsPerGroup,
-                                 host_resolver_.get(), &client_socket_factory_,
-                                 NULL, NULL);
-
-  client_socket_factory_.set_default_client_socket_type(
-      MockTransportClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET);
-
-  // Resolve an AddressList with only IPv4 addresses.
-  host_resolver_->rules()->AddIPLiteralRule("*", "1.1.1.1", std::string());
-
-  TestCompletionCallback callback;
-  ClientSocketHandle handle;
-  int rv = handle.Init("a", params_, LOW, SocketTag(),
-                       ClientSocketPool::RespectLimits::ENABLED,
-                       callback.callback(), &pool, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  EXPECT_FALSE(handle.is_initialized());
-  EXPECT_FALSE(handle.socket());
-
-  EXPECT_THAT(callback.WaitForResult(), IsOk());
-  EXPECT_TRUE(handle.is_initialized());
-  EXPECT_TRUE(handle.socket());
-  IPEndPoint endpoint;
-  handle.socket()->GetLocalAddress(&endpoint);
-  EXPECT_TRUE(endpoint.address().IsIPv4());
-  EXPECT_EQ(0u, handle.connection_attempts().size());
-  EXPECT_EQ(1, client_socket_factory_.allocation_count());
-}
-
 // Test that SocketTag passed into TransportClientSocketPool is applied to
 // returned sockets.
 #if defined(OS_ANDROID)
diff --git a/net/socket/transport_connect_job.cc b/net/socket/transport_connect_job.cc
new file mode 100644
index 0000000..9e617b0
--- /dev/null
+++ b/net/socket/transport_connect_job.cc
@@ -0,0 +1,441 @@
+// 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 "net/socket/transport_connect_job.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_util.h"
+#include "base/trace_event/trace_event.h"
+#include "base/values.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/base/trace_constants.h"
+#include "net/log/net_log.h"
+#include "net/log/net_log_event_type.h"
+#include "net/log/net_log_source_type.h"
+#include "net/log/net_log_with_source.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/socket_performance_watcher.h"
+#include "net/socket/socket_performance_watcher_factory.h"
+#include "net/socket/tcp_client_socket.h"
+
+namespace net {
+
+namespace {
+
+// Returns true iff all addresses in |list| are in the IPv6 family.
+bool AddressListOnlyContainsIPv6(const AddressList& list) {
+  DCHECK(!list.empty());
+  for (auto iter = list.begin(); iter != list.end(); ++iter) {
+    if (iter->GetFamily() != ADDRESS_FAMILY_IPV6)
+      return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+TransportSocketParams::TransportSocketParams(
+    const HostPortPair& host_port_pair,
+    bool disable_resolver_cache,
+    const OnHostResolutionCallback& host_resolution_callback)
+    : destination_(host_port_pair),
+      host_resolution_callback_(host_resolution_callback) {
+  if (disable_resolver_cache)
+    destination_.set_allow_cached_response(false);
+}
+
+TransportSocketParams::~TransportSocketParams() = default;
+
+// TODO(eroman): The use of this constant needs to be re-evaluated. The time
+// needed for TCPClientSocketXXX::Connect() can be arbitrarily long, since
+// the address list may contain many alternatives, and most of those may
+// timeout. Even worse, the per-connect timeout threshold varies greatly
+// between systems (anywhere from 20 seconds to 190 seconds).
+// See comment #12 at http://crbug.com/23364 for specifics.
+const int TransportConnectJob::kTimeoutInSeconds = 240;  // 4 minutes.
+
+// TODO(willchan): Base this off RTT instead of statically setting it. Note we
+// choose a timeout that is different from the backup connect job timer so they
+// don't synchronize.
+const int TransportConnectJob::kIPv6FallbackTimerInMs = 300;
+
+TransportConnectJob::TransportConnectJob(
+    const std::string& group_name,
+    RequestPriority priority,
+    const SocketTag& socket_tag,
+    bool respect_limits,
+    const scoped_refptr<TransportSocketParams>& params,
+    ClientSocketFactory* client_socket_factory,
+    SocketPerformanceWatcherFactory* socket_performance_watcher_factory,
+    HostResolver* host_resolver,
+    Delegate* delegate,
+    NetLog* net_log)
+    : ConnectJob(
+          group_name,
+          ConnectionTimeout(),
+          priority,
+          socket_tag,
+          respect_limits,
+          delegate,
+          NetLogWithSource::Make(net_log,
+                                 NetLogSourceType::TRANSPORT_CONNECT_JOB)),
+      params_(params),
+      resolver_(host_resolver),
+      client_socket_factory_(client_socket_factory),
+      next_state_(STATE_NONE),
+      socket_performance_watcher_factory_(socket_performance_watcher_factory),
+      resolve_result_(OK) {}
+
+TransportConnectJob::~TransportConnectJob() {
+  // We don't worry about cancelling the host resolution and TCP connect, since
+  // ~HostResolver::Request and ~StreamSocket will take care of it.
+}
+
+LoadState TransportConnectJob::GetLoadState() const {
+  switch (next_state_) {
+    case STATE_RESOLVE_HOST:
+    case STATE_RESOLVE_HOST_COMPLETE:
+      return LOAD_STATE_RESOLVING_HOST;
+    case STATE_TRANSPORT_CONNECT:
+    case STATE_TRANSPORT_CONNECT_COMPLETE:
+      return LOAD_STATE_CONNECTING;
+    case STATE_NONE:
+      return LOAD_STATE_IDLE;
+  }
+  NOTREACHED();
+  return LOAD_STATE_IDLE;
+}
+
+void TransportConnectJob::GetAdditionalErrorState(ClientSocketHandle* handle) {
+  // If hostname resolution failed, record an empty endpoint and the result.
+  // Also record any attempts made on either of the sockets.
+  ConnectionAttempts attempts;
+  if (resolve_result_ != OK) {
+    DCHECK_EQ(0u, addresses_.size());
+    attempts.push_back(ConnectionAttempt(IPEndPoint(), resolve_result_));
+  }
+  attempts.insert(attempts.begin(), connection_attempts_.begin(),
+                  connection_attempts_.end());
+  attempts.insert(attempts.begin(), fallback_connection_attempts_.begin(),
+                  fallback_connection_attempts_.end());
+  handle->set_connection_attempts(attempts);
+}
+
+// static
+void TransportConnectJob::MakeAddressListStartWithIPv4(AddressList* list) {
+  for (auto i = list->begin(); i != list->end(); ++i) {
+    if (i->GetFamily() == ADDRESS_FAMILY_IPV4) {
+      std::rotate(list->begin(), i, list->end());
+      break;
+    }
+  }
+}
+
+// static
+void TransportConnectJob::HistogramDuration(
+    const LoadTimingInfo::ConnectTiming& connect_timing,
+    RaceResult race_result) {
+  DCHECK(!connect_timing.connect_start.is_null());
+  DCHECK(!connect_timing.dns_start.is_null());
+  base::TimeTicks now = base::TimeTicks::Now();
+  base::TimeDelta total_duration = now - connect_timing.dns_start;
+  UMA_HISTOGRAM_CUSTOM_TIMES("Net.DNS_Resolution_And_TCP_Connection_Latency2",
+                             total_duration,
+                             base::TimeDelta::FromMilliseconds(1),
+                             base::TimeDelta::FromMinutes(10), 100);
+
+  base::TimeDelta connect_duration = now - connect_timing.connect_start;
+  UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency", connect_duration,
+                             base::TimeDelta::FromMilliseconds(1),
+                             base::TimeDelta::FromMinutes(10), 100);
+
+  switch (race_result) {
+    case RACE_IPV4_WINS:
+      UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_Wins_Race",
+                                 connect_duration,
+                                 base::TimeDelta::FromMilliseconds(1),
+                                 base::TimeDelta::FromMinutes(10), 100);
+      break;
+
+    case RACE_IPV4_SOLO:
+      UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_No_Race",
+                                 connect_duration,
+                                 base::TimeDelta::FromMilliseconds(1),
+                                 base::TimeDelta::FromMinutes(10), 100);
+      break;
+
+    case RACE_IPV6_WINS:
+      UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Raceable",
+                                 connect_duration,
+                                 base::TimeDelta::FromMilliseconds(1),
+                                 base::TimeDelta::FromMinutes(10), 100);
+      break;
+
+    case RACE_IPV6_SOLO:
+      UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Solo",
+                                 connect_duration,
+                                 base::TimeDelta::FromMilliseconds(1),
+                                 base::TimeDelta::FromMinutes(10), 100);
+      break;
+
+    default:
+      NOTREACHED();
+      break;
+  }
+}
+
+// static
+base::TimeDelta TransportConnectJob::ConnectionTimeout() {
+  return base::TimeDelta::FromSeconds(TransportConnectJob::kTimeoutInSeconds);
+}
+
+void TransportConnectJob::OnIOComplete(int result) {
+  result = DoLoop(result);
+  if (result != ERR_IO_PENDING)
+    NotifyDelegateOfCompletion(result);  // Deletes |this|
+}
+
+int TransportConnectJob::DoLoop(int result) {
+  DCHECK_NE(next_state_, STATE_NONE);
+
+  int rv = result;
+  do {
+    State state = next_state_;
+    next_state_ = STATE_NONE;
+    switch (state) {
+      case STATE_RESOLVE_HOST:
+        DCHECK_EQ(OK, rv);
+        rv = DoResolveHost();
+        break;
+      case STATE_RESOLVE_HOST_COMPLETE:
+        rv = DoResolveHostComplete(rv);
+        break;
+      case STATE_TRANSPORT_CONNECT:
+        DCHECK_EQ(OK, rv);
+        rv = DoTransportConnect();
+        break;
+      case STATE_TRANSPORT_CONNECT_COMPLETE:
+        rv = DoTransportConnectComplete(rv);
+        break;
+      default:
+        NOTREACHED();
+        rv = ERR_FAILED;
+        break;
+    }
+  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+
+  return rv;
+}
+
+int TransportConnectJob::DoResolveHost() {
+  next_state_ = STATE_RESOLVE_HOST_COMPLETE;
+  connect_timing_.dns_start = base::TimeTicks::Now();
+
+  return resolver_->Resolve(params_->destination(), priority(), &addresses_,
+                            base::BindOnce(&TransportConnectJob::OnIOComplete,
+                                           base::Unretained(this)),
+                            &request_, net_log());
+}
+
+int TransportConnectJob::DoResolveHostComplete(int result) {
+  TRACE_EVENT0(NetTracingCategory(),
+               "TransportConnectJob::DoResolveHostComplete");
+  connect_timing_.dns_end = base::TimeTicks::Now();
+  // Overwrite connection start time, since for connections that do not go
+  // through proxies, |connect_start| should not include dns lookup time.
+  connect_timing_.connect_start = connect_timing_.dns_end;
+  resolve_result_ = result;
+
+  if (result != OK)
+    return result;
+
+  // Invoke callback, and abort if it fails.
+  if (!params_->host_resolution_callback().is_null()) {
+    result = params_->host_resolution_callback().Run(addresses_, net_log());
+    if (result != OK)
+      return result;
+  }
+
+  next_state_ = STATE_TRANSPORT_CONNECT;
+  return result;
+}
+
+int TransportConnectJob::DoTransportConnect() {
+  next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
+  // Create a |SocketPerformanceWatcher|, and pass the ownership.
+  std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher;
+  if (socket_performance_watcher_factory_) {
+    socket_performance_watcher =
+        socket_performance_watcher_factory_->CreateSocketPerformanceWatcher(
+            SocketPerformanceWatcherFactory::PROTOCOL_TCP, addresses_);
+  }
+  transport_socket_ = client_socket_factory_->CreateTransportClientSocket(
+      addresses_, std::move(socket_performance_watcher), net_log().net_log(),
+      net_log().source());
+
+  // If the list contains IPv6 and IPv4 addresses, and the first address
+  // is IPv6, the IPv4 addresses will be tried as fallback addresses, per
+  // "Happy Eyeballs" (RFC 6555).
+  bool try_ipv6_connect_with_ipv4_fallback =
+      addresses_.front().GetFamily() == ADDRESS_FAMILY_IPV6 &&
+      !AddressListOnlyContainsIPv6(addresses_);
+
+  transport_socket_->ApplySocketTag(socket_tag());
+
+  int rv = transport_socket_->Connect(base::BindOnce(
+      &TransportConnectJob::OnIOComplete, base::Unretained(this)));
+  if (rv == ERR_IO_PENDING && try_ipv6_connect_with_ipv4_fallback) {
+    fallback_timer_.Start(
+        FROM_HERE, base::TimeDelta::FromMilliseconds(kIPv6FallbackTimerInMs),
+        this, &TransportConnectJob::DoIPv6FallbackTransportConnect);
+  }
+  return rv;
+}
+
+int TransportConnectJob::DoTransportConnectComplete(int result) {
+  if (result == OK) {
+    // Success will be returned via the main socket, so also include connection
+    // attempts made on the fallback socket up to this point. (Unfortunately,
+    // the only simple way to return information in the success case is through
+    // the successfully-connected socket.)
+    if (fallback_transport_socket_) {
+      ConnectionAttempts fallback_attempts;
+      fallback_transport_socket_->GetConnectionAttempts(&fallback_attempts);
+      transport_socket_->AddConnectionAttempts(fallback_attempts);
+    }
+
+    bool is_ipv4 = addresses_.front().GetFamily() == ADDRESS_FAMILY_IPV4;
+    RaceResult race_result = RACE_UNKNOWN;
+    if (is_ipv4)
+      race_result = RACE_IPV4_SOLO;
+    else if (AddressListOnlyContainsIPv6(addresses_))
+      race_result = RACE_IPV6_SOLO;
+    else
+      race_result = RACE_IPV6_WINS;
+    HistogramDuration(connect_timing_, race_result);
+
+    SetSocket(std::move(transport_socket_));
+  } else {
+    // Failure will be returned via |GetAdditionalErrorState|, so save
+    // connection attempts from both sockets for use there.
+    CopyConnectionAttemptsFromSockets();
+
+    transport_socket_.reset();
+  }
+
+  fallback_timer_.Stop();
+  fallback_transport_socket_.reset();
+  fallback_addresses_.reset();
+
+  return result;
+}
+
+void TransportConnectJob::DoIPv6FallbackTransportConnect() {
+  // The timer should only fire while we're waiting for the main connect to
+  // succeed.
+  if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) {
+    NOTREACHED();
+    return;
+  }
+
+  DCHECK(!fallback_transport_socket_.get());
+  DCHECK(!fallback_addresses_.get());
+
+  fallback_addresses_.reset(new AddressList(addresses_));
+  MakeAddressListStartWithIPv4(fallback_addresses_.get());
+
+  // Create a |SocketPerformanceWatcher|, and pass the ownership.
+  std::unique_ptr<SocketPerformanceWatcher> socket_performance_watcher;
+  if (socket_performance_watcher_factory_) {
+    socket_performance_watcher =
+        socket_performance_watcher_factory_->CreateSocketPerformanceWatcher(
+            SocketPerformanceWatcherFactory::PROTOCOL_TCP,
+            *fallback_addresses_);
+  }
+
+  fallback_transport_socket_ =
+      client_socket_factory_->CreateTransportClientSocket(
+          *fallback_addresses_, std::move(socket_performance_watcher),
+          net_log().net_log(), net_log().source());
+  fallback_connect_start_time_ = base::TimeTicks::Now();
+  int rv = fallback_transport_socket_->Connect(base::BindOnce(
+      &TransportConnectJob::DoIPv6FallbackTransportConnectComplete,
+      base::Unretained(this)));
+  if (rv != ERR_IO_PENDING)
+    DoIPv6FallbackTransportConnectComplete(rv);
+}
+
+void TransportConnectJob::DoIPv6FallbackTransportConnectComplete(int result) {
+  // This should only happen when we're waiting for the main connect to succeed.
+  if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) {
+    NOTREACHED();
+    return;
+  }
+
+  DCHECK_NE(ERR_IO_PENDING, result);
+  DCHECK(fallback_transport_socket_.get());
+  DCHECK(fallback_addresses_.get());
+
+  if (result == OK) {
+    DCHECK(!fallback_connect_start_time_.is_null());
+
+    // Success will be returned via the fallback socket, so also include
+    // connection attempts made on the main socket up to this point.
+    // (Unfortunately, the only simple way to return information in the success
+    // case is through the successfully-connected socket.)
+    if (transport_socket_) {
+      ConnectionAttempts attempts;
+      transport_socket_->GetConnectionAttempts(&attempts);
+      fallback_transport_socket_->AddConnectionAttempts(attempts);
+    }
+
+    connect_timing_.connect_start = fallback_connect_start_time_;
+    HistogramDuration(connect_timing_, RACE_IPV4_WINS);
+    SetSocket(std::move(fallback_transport_socket_));
+    next_state_ = STATE_NONE;
+  } else {
+    // Failure will be returned via |GetAdditionalErrorState|, so save
+    // connection attempts from both sockets for use there.
+    CopyConnectionAttemptsFromSockets();
+
+    fallback_transport_socket_.reset();
+    fallback_addresses_.reset();
+  }
+
+  transport_socket_.reset();
+
+  NotifyDelegateOfCompletion(result);  // Deletes |this|
+}
+
+int TransportConnectJob::ConnectInternal() {
+  next_state_ = STATE_RESOLVE_HOST;
+  return DoLoop(OK);
+}
+
+void TransportConnectJob::ChangePriorityInternal(RequestPriority priority) {
+  if (next_state_ == STATE_RESOLVE_HOST_COMPLETE) {
+    DCHECK(request_);
+    // Change the request priority in the host resolver.
+    request_->ChangeRequestPriority(priority);
+  }
+}
+
+void TransportConnectJob::CopyConnectionAttemptsFromSockets() {
+  if (transport_socket_)
+    transport_socket_->GetConnectionAttempts(&connection_attempts_);
+  if (fallback_transport_socket_) {
+    fallback_transport_socket_->GetConnectionAttempts(
+        &fallback_connection_attempts_);
+  }
+}
+
+}  // namespace net
diff --git a/net/socket/transport_connect_job.h b/net/socket/transport_connect_job.h
new file mode 100644
index 0000000..b8b48a4
--- /dev/null
+++ b/net/socket/transport_connect_job.h
@@ -0,0 +1,179 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_SOCKET_TRANSPORT_CONNECT_JOB_H_
+#define NET_SOCKET_TRANSPORT_CONNECT_JOB_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "net/base/net_export.h"
+#include "net/dns/host_resolver.h"
+#include "net/socket/connect_job.h"
+#include "net/socket/connection_attempts.h"
+#include "net/socket/socket_tag.h"
+
+namespace net {
+
+class ClientSocketFactory;
+class HostPortPair;
+class NetLog;
+class NetLogWithSource;
+class SocketPerformanceWatcherFactory;
+
+typedef base::RepeatingCallback<int(const AddressList&,
+                                    const NetLogWithSource& net_log)>
+    OnHostResolutionCallback;
+
+class NET_EXPORT_PRIVATE TransportSocketParams
+    : public base::RefCounted<TransportSocketParams> {
+ public:
+  // |host_resolution_callback| will be invoked after the the hostname is
+  // resolved.  If |host_resolution_callback| does not return OK, then the
+  // connection will be aborted with that value.
+  TransportSocketParams(
+      const HostPortPair& host_port_pair,
+      bool disable_resolver_cache,
+      const OnHostResolutionCallback& host_resolution_callback);
+
+  const HostResolver::RequestInfo& destination() const { return destination_; }
+  const OnHostResolutionCallback& host_resolution_callback() const {
+    return host_resolution_callback_;
+  }
+
+ private:
+  friend class base::RefCounted<TransportSocketParams>;
+  ~TransportSocketParams();
+
+  HostResolver::RequestInfo destination_;
+  const OnHostResolutionCallback host_resolution_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(TransportSocketParams);
+};
+
+// TransportConnectJob handles the host resolution necessary for socket creation
+// and the transport (likely TCP) connect. TransportConnectJob also has fallback
+// logic for IPv6 connect() timeouts (which may happen due to networks / routers
+// with broken IPv6 support). Those timeouts take 20s, so rather than make the
+// user wait 20s for the timeout to fire, we use a fallback timer
+// (kIPv6FallbackTimerInMs) and start a connect() to a IPv4 address if the timer
+// fires. Then we race the IPv4 connect() against the IPv6 connect() (which has
+// a headstart) and return the one that completes first to the socket pool.
+class NET_EXPORT_PRIVATE TransportConnectJob : public ConnectJob {
+ public:
+  // For recording the connection time in the appropriate bucket.
+  enum RaceResult {
+    RACE_UNKNOWN,
+    RACE_IPV4_WINS,
+    RACE_IPV4_SOLO,
+    RACE_IPV6_WINS,
+    RACE_IPV6_SOLO,
+  };
+
+  // TransportConnectJobs will time out after this many seconds.  Note this is
+  // the total time, including both host resolution and TCP connect() times.
+  static const int kTimeoutInSeconds;
+
+  // In cases where both IPv6 and IPv4 addresses were returned from DNS,
+  // TransportConnectJobs will start a second connection attempt to just the
+  // IPv4 addresses after this many milliseconds. (This is "Happy Eyeballs".)
+  static const int kIPv6FallbackTimerInMs;
+
+  TransportConnectJob(
+      const std::string& group_name,
+      RequestPriority priority,
+      const SocketTag& socket_tag,
+      bool respect_limits,
+      const scoped_refptr<TransportSocketParams>& params,
+      ClientSocketFactory* client_socket_factory,
+      SocketPerformanceWatcherFactory* socket_performance_watcher_factory,
+      HostResolver* host_resolver,
+      Delegate* delegate,
+      NetLog* net_log);
+  ~TransportConnectJob() override;
+
+  // ConnectJob methods.
+  LoadState GetLoadState() const override;
+  void GetAdditionalErrorState(ClientSocketHandle* handle) override;
+
+  // Rolls |addrlist| forward until the first IPv4 address, if any.
+  // WARNING: this method should only be used to implement the prefer-IPv4 hack.
+  static void MakeAddressListStartWithIPv4(AddressList* addrlist);
+
+  // Record the histograms Net.DNS_Resolution_And_TCP_Connection_Latency2 and
+  // Net.TCP_Connection_Latency and return the connect duration.
+  static void HistogramDuration(
+      const LoadTimingInfo::ConnectTiming& connect_timing,
+      RaceResult race_result);
+
+  static base::TimeDelta ConnectionTimeout();
+
+ private:
+  enum State {
+    STATE_RESOLVE_HOST,
+    STATE_RESOLVE_HOST_COMPLETE,
+    STATE_TRANSPORT_CONNECT,
+    STATE_TRANSPORT_CONNECT_COMPLETE,
+    STATE_NONE,
+  };
+
+  void OnIOComplete(int result);
+  int DoLoop(int result);
+
+  int DoResolveHost();
+  int DoResolveHostComplete(int result);
+  int DoTransportConnect();
+  int DoTransportConnectComplete(int result);
+
+  // Not part of the state machine.
+  void DoIPv6FallbackTransportConnect();
+  void DoIPv6FallbackTransportConnectComplete(int result);
+
+  // Begins the host resolution and the TCP connect.  Returns OK on success
+  // and ERR_IO_PENDING if it cannot immediately service the request.
+  // Otherwise, it returns a net error code.
+  int ConnectInternal() override;
+
+  // If host resolution is currently underway, change the priority of the host
+  // resolver request.
+  void ChangePriorityInternal(RequestPriority priority) override;
+
+  void CopyConnectionAttemptsFromSockets();
+
+  scoped_refptr<TransportSocketParams> params_;
+  HostResolver* resolver_;
+  std::unique_ptr<HostResolver::Request> request_;
+  ClientSocketFactory* const client_socket_factory_;
+
+  State next_state_;
+
+  std::unique_ptr<StreamSocket> transport_socket_;
+  AddressList addresses_;
+
+  std::unique_ptr<StreamSocket> fallback_transport_socket_;
+  std::unique_ptr<AddressList> fallback_addresses_;
+  base::TimeTicks fallback_connect_start_time_;
+  base::OneShotTimer fallback_timer_;
+  SocketPerformanceWatcherFactory* socket_performance_watcher_factory_;
+
+  int resolve_result_;
+
+  // Used in the failure case to save connection attempts made on the main and
+  // fallback sockets and pass them on in |GetAdditionalErrorState|. (In the
+  // success case, connection attempts are passed through the returned socket;
+  // attempts are copied from the other socket, if one exists, into it before
+  // it is returned.)
+  ConnectionAttempts connection_attempts_;
+  ConnectionAttempts fallback_connection_attempts_;
+
+  DISALLOW_COPY_AND_ASSIGN(TransportConnectJob);
+};
+
+}  // namespace net
+
+#endif  // NET_SOCKET_TRANSPORT_CONNECT_JOB_H_
diff --git a/net/socket/transport_connect_job_unittest.cc b/net/socket/transport_connect_job_unittest.cc
new file mode 100644
index 0000000..77b0545
--- /dev/null
+++ b/net/socket/transport_connect_job_unittest.cc
@@ -0,0 +1,315 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/socket/transport_connect_job.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "net/base/address_family.h"
+#include "net/base/address_list.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/log/test_net_log.h"
+#include "net/socket/connect_job_test_util.h"
+#include "net/socket/connection_attempts.h"
+#include "net/socket/stream_socket.h"
+#include "net/socket/transport_client_socket_pool_test_util.h"
+#include "net/test/gtest_util.h"
+#include "net/test/test_with_scoped_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace {
+
+const char kHostName[] = "unresolvable.host.name";
+
+class TransportConnectJobTest : public TestWithScopedTaskEnvironment {
+ public:
+  TransportConnectJobTest() : client_socket_factory_(&net_log_) {}
+
+  ~TransportConnectJobTest() override {}
+
+  static scoped_refptr<TransportSocketParams> DefaultParams() {
+    return base::MakeRefCounted<TransportSocketParams>(
+        HostPortPair(kHostName, 80), false, OnHostResolutionCallback());
+  }
+
+ protected:
+  TestNetLog net_log_;
+  MockHostResolver host_resolver_;
+  MockTransportClientSocketFactory client_socket_factory_;
+};
+
+TEST_F(TransportConnectJobTest, MakeAddrListStartWithIPv4) {
+  IPEndPoint addrlist_v4_1(IPAddress(192, 168, 1, 1), 80);
+  IPEndPoint addrlist_v4_2(IPAddress(192, 168, 1, 2), 80);
+  IPAddress ip_address;
+  ASSERT_TRUE(ip_address.AssignFromIPLiteral("2001:4860:b006::64"));
+  IPEndPoint addrlist_v6_1(ip_address, 80);
+  ASSERT_TRUE(ip_address.AssignFromIPLiteral("2001:4860:b006::66"));
+  IPEndPoint addrlist_v6_2(ip_address, 80);
+
+  AddressList addrlist;
+
+  // Test 1: IPv4 only.  Expect no change.
+  addrlist.clear();
+  addrlist.push_back(addrlist_v4_1);
+  addrlist.push_back(addrlist_v4_2);
+  TransportConnectJob::MakeAddressListStartWithIPv4(&addrlist);
+  ASSERT_EQ(2u, addrlist.size());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[0].GetFamily());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[1].GetFamily());
+
+  // Test 2: IPv6 only.  Expect no change.
+  addrlist.clear();
+  addrlist.push_back(addrlist_v6_1);
+  addrlist.push_back(addrlist_v6_2);
+  TransportConnectJob::MakeAddressListStartWithIPv4(&addrlist);
+  ASSERT_EQ(2u, addrlist.size());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[0].GetFamily());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[1].GetFamily());
+
+  // Test 3: IPv4 then IPv6.  Expect no change.
+  addrlist.clear();
+  addrlist.push_back(addrlist_v4_1);
+  addrlist.push_back(addrlist_v4_2);
+  addrlist.push_back(addrlist_v6_1);
+  addrlist.push_back(addrlist_v6_2);
+  TransportConnectJob::MakeAddressListStartWithIPv4(&addrlist);
+  ASSERT_EQ(4u, addrlist.size());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[0].GetFamily());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[1].GetFamily());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[2].GetFamily());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[3].GetFamily());
+
+  // Test 4: IPv6, IPv4, IPv6, IPv4.  Expect first IPv6 moved to the end.
+  addrlist.clear();
+  addrlist.push_back(addrlist_v6_1);
+  addrlist.push_back(addrlist_v4_1);
+  addrlist.push_back(addrlist_v6_2);
+  addrlist.push_back(addrlist_v4_2);
+  TransportConnectJob::MakeAddressListStartWithIPv4(&addrlist);
+  ASSERT_EQ(4u, addrlist.size());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[0].GetFamily());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[1].GetFamily());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[2].GetFamily());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[3].GetFamily());
+
+  // Test 5: IPv6, IPv6, IPv4, IPv4.  Expect first two IPv6's moved to the end.
+  addrlist.clear();
+  addrlist.push_back(addrlist_v6_1);
+  addrlist.push_back(addrlist_v6_2);
+  addrlist.push_back(addrlist_v4_1);
+  addrlist.push_back(addrlist_v4_2);
+  TransportConnectJob::MakeAddressListStartWithIPv4(&addrlist);
+  ASSERT_EQ(4u, addrlist.size());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[0].GetFamily());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV4, addrlist[1].GetFamily());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[2].GetFamily());
+  EXPECT_EQ(ADDRESS_FAMILY_IPV6, addrlist[3].GetFamily());
+}
+
+TEST_F(TransportConnectJobTest, HostResolutionFailure) {
+  host_resolver_.rules()->AddSimulatedFailure(kHostName);
+
+  //  Check sync and async failures.
+  for (bool host_resolution_synchronous : {false, true}) {
+    host_resolver_.set_synchronous_mode(host_resolution_synchronous);
+    TestConnectJobDelegate test_delegate;
+    TransportConnectJob transport_conect_job(
+        kHostName /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+        true /* respect_limits */, DefaultParams(), &client_socket_factory_,
+        nullptr /* socket_performance_watcher_factory */, &host_resolver_,
+        &test_delegate, &net_log_);
+    test_delegate.StartJobExpectingResult(&transport_conect_job,
+                                          ERR_NAME_NOT_RESOLVED,
+                                          host_resolution_synchronous);
+  }
+}
+
+TEST_F(TransportConnectJobTest, ConnectionFailure) {
+  for (bool host_resolution_synchronous : {false, true}) {
+    for (bool connection_synchronous : {false, true}) {
+      host_resolver_.set_synchronous_mode(host_resolution_synchronous);
+      client_socket_factory_.set_default_client_socket_type(
+          connection_synchronous
+              ? MockTransportClientSocketFactory::MOCK_FAILING_CLIENT_SOCKET
+              : MockTransportClientSocketFactory::
+                    MOCK_PENDING_FAILING_CLIENT_SOCKET);
+      ClientSocketHandle handle;
+      TestConnectJobDelegate test_delegate;
+      TransportConnectJob transport_conect_job(
+          kHostName /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+          true /* respect_limits */, DefaultParams(), &client_socket_factory_,
+          nullptr /* socket_performance_watcher_factory */, &host_resolver_,
+          &test_delegate, &net_log_);
+      test_delegate.StartJobExpectingResult(
+          &transport_conect_job, ERR_CONNECTION_FAILED,
+          host_resolution_synchronous && connection_synchronous);
+    }
+  }
+}
+
+TEST_F(TransportConnectJobTest, ConnectionSuccess) {
+  for (bool host_resolution_synchronous : {false, true}) {
+    for (bool connection_synchronous : {false, true}) {
+      host_resolver_.set_synchronous_mode(host_resolution_synchronous);
+      client_socket_factory_.set_default_client_socket_type(
+          connection_synchronous
+              ? MockTransportClientSocketFactory::MOCK_CLIENT_SOCKET
+              : MockTransportClientSocketFactory::MOCK_PENDING_CLIENT_SOCKET);
+      ClientSocketHandle handle;
+      TestConnectJobDelegate test_delegate;
+      TransportConnectJob transport_conect_job(
+          kHostName /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+          true /* respect_limits */, DefaultParams(), &client_socket_factory_,
+          nullptr /* socket_performance_watcher_factory */, &host_resolver_,
+          &test_delegate, &net_log_);
+      test_delegate.StartJobExpectingResult(
+          &transport_conect_job, OK,
+          host_resolution_synchronous && connection_synchronous);
+    }
+  }
+}
+
+// Test the case of the IPv6 address stalling, and falling back to the IPv4
+// socket which finishes first.
+TEST_F(TransportConnectJobTest, IPv6FallbackSocketIPv4FinishesFirst) {
+  MockTransportClientSocketFactory::ClientSocketType case_types[] = {
+      // This is the IPv6 socket. It stalls, but presents one failed connection
+      // attempt on GetConnectionAttempts.
+      MockTransportClientSocketFactory::MOCK_STALLED_FAILING_CLIENT_SOCKET,
+      // This is the IPv4 socket.
+      MockTransportClientSocketFactory::MOCK_PENDING_CLIENT_SOCKET};
+
+  client_socket_factory_.set_client_socket_types(case_types, 2);
+
+  // Resolve an AddressList with a IPv6 address first and then a IPv4 address.
+  host_resolver_.rules()->AddIPLiteralRule(kHostName, "2:abcd::3:4:ff,2.2.2.2",
+                                           std::string());
+
+  TestConnectJobDelegate test_delegate;
+  TransportConnectJob transport_conect_job(
+      kHostName /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+      true /* respect_limits */, DefaultParams(), &client_socket_factory_,
+      nullptr /* socket_performance_watcher_factory */, &host_resolver_,
+      &test_delegate, &net_log_);
+  test_delegate.StartJobExpectingResult(&transport_conect_job, OK,
+                                        false /* expect_sync_result */);
+
+  IPEndPoint endpoint;
+  test_delegate.socket()->GetLocalAddress(&endpoint);
+  EXPECT_TRUE(endpoint.address().IsIPv4());
+
+  // Check that the failed connection attempt on the main socket is collected.
+  ConnectionAttempts attempts;
+  test_delegate.socket()->GetConnectionAttempts(&attempts);
+  ASSERT_EQ(1u, attempts.size());
+  EXPECT_THAT(attempts[0].result, test::IsError(ERR_CONNECTION_FAILED));
+  EXPECT_TRUE(attempts[0].endpoint.address().IsIPv6());
+
+  EXPECT_EQ(2, client_socket_factory_.allocation_count());
+}
+
+// Test the case of the IPv6 address being slow, thus falling back to trying to
+// connect to the IPv4 address, but having the connect to the IPv6 address
+// finish first.
+TEST_F(TransportConnectJobTest, IPv6FallbackSocketIPv6FinishesFirst) {
+  MockTransportClientSocketFactory::ClientSocketType case_types[] = {
+      // This is the IPv6 socket.
+      MockTransportClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET,
+      // This is the IPv4 socket. It stalls, but presents one failed connection
+      // attempt on GetConnectionAttempts.
+      MockTransportClientSocketFactory::MOCK_STALLED_FAILING_CLIENT_SOCKET};
+
+  client_socket_factory_.set_client_socket_types(case_types, 2);
+  client_socket_factory_.set_delay(base::TimeDelta::FromMilliseconds(
+      TransportConnectJob::kIPv6FallbackTimerInMs + 50));
+
+  // Resolve an AddressList with a IPv6 address first and then a IPv4 address.
+  host_resolver_.rules()->AddIPLiteralRule(kHostName, "2:abcd::3:4:ff,2.2.2.2",
+                                           std::string());
+
+  TestConnectJobDelegate test_delegate;
+  TransportConnectJob transport_conect_job(
+      kHostName /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+      true /* respect_limits */, DefaultParams(), &client_socket_factory_,
+      nullptr /* socket_performance_watcher_factory */, &host_resolver_,
+      &test_delegate, &net_log_);
+  test_delegate.StartJobExpectingResult(&transport_conect_job, OK,
+                                        false /* expect_sync_result */);
+
+  IPEndPoint endpoint;
+  test_delegate.socket()->GetLocalAddress(&endpoint);
+  EXPECT_TRUE(endpoint.address().IsIPv6());
+
+  // Check that the failed connection attempt on the fallback socket is
+  // collected.
+  ConnectionAttempts attempts;
+  test_delegate.socket()->GetConnectionAttempts(&attempts);
+  ASSERT_EQ(1u, attempts.size());
+  EXPECT_THAT(attempts[0].result, test::IsError(ERR_CONNECTION_FAILED));
+  EXPECT_TRUE(attempts[0].endpoint.address().IsIPv4());
+
+  EXPECT_EQ(2, client_socket_factory_.allocation_count());
+}
+
+TEST_F(TransportConnectJobTest, IPv6NoIPv4AddressesToFallbackTo) {
+  client_socket_factory_.set_default_client_socket_type(
+      MockTransportClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET);
+
+  // Resolve an AddressList with only IPv6 addresses.
+  host_resolver_.rules()->AddIPLiteralRule(
+      kHostName, "2:abcd::3:4:ff,3:abcd::3:4:ff", std::string());
+
+  TestConnectJobDelegate test_delegate;
+  TransportConnectJob transport_conect_job(
+      kHostName /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+      true /* respect_limits */, DefaultParams(), &client_socket_factory_,
+      nullptr /* socket_performance_watcher_factory */, &host_resolver_,
+      &test_delegate, &net_log_);
+  test_delegate.StartJobExpectingResult(&transport_conect_job, OK,
+                                        false /* expect_sync_result */);
+
+  IPEndPoint endpoint;
+  test_delegate.socket()->GetLocalAddress(&endpoint);
+  EXPECT_TRUE(endpoint.address().IsIPv6());
+  ConnectionAttempts attempts;
+  test_delegate.socket()->GetConnectionAttempts(&attempts);
+  EXPECT_EQ(0u, attempts.size());
+  EXPECT_EQ(1, client_socket_factory_.allocation_count());
+}
+
+TEST_F(TransportConnectJobTest, IPv4HasNoFallback) {
+  client_socket_factory_.set_default_client_socket_type(
+      MockTransportClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET);
+
+  // Resolve an AddressList with only IPv4 addresses.
+  host_resolver_.rules()->AddIPLiteralRule(kHostName, "1.1.1.1", std::string());
+
+  TestConnectJobDelegate test_delegate;
+  TransportConnectJob transport_conect_job(
+      kHostName /* group_name */, DEFAULT_PRIORITY, SocketTag(),
+      true /* respect_limits */, DefaultParams(), &client_socket_factory_,
+      nullptr /* socket_performance_watcher_factory */, &host_resolver_,
+      &test_delegate, &net_log_);
+  test_delegate.StartJobExpectingResult(&transport_conect_job, OK,
+                                        false /* expect_sync_result */);
+
+  IPEndPoint endpoint;
+  test_delegate.socket()->GetLocalAddress(&endpoint);
+  EXPECT_TRUE(endpoint.address().IsIPv4());
+  ConnectionAttempts attempts;
+  test_delegate.socket()->GetConnectionAttempts(&attempts);
+  EXPECT_EQ(0u, attempts.size());
+  EXPECT_EQ(1, client_socket_factory_.allocation_count());
+}
+
+}  // namespace
+}  // namespace net
diff --git a/net/socket/udp_socket_posix.cc b/net/socket/udp_socket_posix.cc
index 1d237a8..6e9b664 100644
--- a/net/socket/udp_socket_posix.cc
+++ b/net/socket/udp_socket_posix.cc
@@ -695,7 +695,7 @@
   int value = 1;
   rv = setsockopt(socket_, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value));
   // Ignore errors that the option does not exist.
-  if (rv != 0 && rv != ENOPROTOOPT)
+  if (rv != 0 && errno != ENOPROTOOPT)
     return MapSystemError(errno);
 #endif  // SO_REUSEPORT
 
diff --git a/ppapi/BUILD.gn b/ppapi/BUILD.gn
index cc4695c..61f1bb8 100644
--- a/ppapi/BUILD.gn
+++ b/ppapi/BUILD.gn
@@ -36,8 +36,6 @@
   "tests/test_audio_encoder.h",
   "tests/test_case.cc",
   "tests/test_case.h",
-  "tests/test_compositor.cc",
-  "tests/test_compositor.h",
   "tests/test_console.cc",
   "tests/test_console.h",
   "tests/test_core.cc",
diff --git a/ppapi/api/ppb_compositor.idl b/ppapi/api/ppb_compositor.idl
deleted file mode 100644
index d9b4e64..0000000
--- a/ppapi/api/ppb_compositor.idl
+++ /dev/null
@@ -1,131 +0,0 @@
-/* Copyright 2014 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-[generate_thunk]
-
-label Chrome {
-  [channel=dev] M37 = 0.1
-};
-
-/**
- * Defines the <code>PPB_Compositor</code> interface. Used for setting
- * <code>PPB_CompositorLayer</code> layers to the Chromium compositor for
- * compositing. This allows a plugin to combine different sources of visual
- * data efficiently, such as <code>PPB_ImageData</code> images and
- * OpenGL textures. See also <code>PPB_CompositorLayer</code> for more
- * information.
- * This interface is still in development (Dev API status) and may change,
- * so is only supported on Dev channel and Canary currently.
- *
- * <strong>Example usage from plugin code:</strong>
- *
- * <strong>Setup:</strong>
- * @code
- * PP_Resource compositor;
- * compositor = compositor_if->Create(instance);
- * instance_if->BindGraphics(instance, compositor);
- * @endcode
- *
- * <strong>Setup layer stack:</strong>
- * @code
- * PP_Resource color_layer = compositor_if->AddLayer(compositor);
- * PP_Resource texture_layer = compositor_if->AddLayer(compositor);
- * @endcode
- *
- * <strong> Present one frame:</strong>
- * layer_if->SetColor(color_layer, 255, 255, 0, 255, PP_MakeSize(400, 400));
- * PP_CompletionCallback release_callback = {
- *   TextureReleasedCallback, 0, PP_COMPLETIONCALLBACK_FLAG_NONE,
- * };
- * layer_if->SetTexture(texture_layer, graphics3d, texture_id,
- *                      PP_MakeSize(300, 300), release_callback);
- *
- * PP_CompletionCallback callback = {
- *   DidFinishCommitLayersCallback,
- *   (void*) texture_id,
- *   PP_COMPLETIONCALLBACK_FLAG_NONE,
- * };
- * compositor_if->CommitLayers(compositor, callback);
- * @endcode
- *
- * <strong>release callback</strong>
- * void ReleaseCallback(int32_t result, void* user_data) {
- *   if (result == PP_OK) {
- *     uint32_t texture_id = (uint32_t) user_data;
- *     // reuse the texture or delete it.
- *   }
- * }
- *
- * <strong>Shutdown:</strong>
- * @code
- * core->ReleaseResource(color_layer);
- * core->ReleaseResource(texture_layer);
- * core->ReleaseResource(compositor);
- * @endcode
- */
-
-interface PPB_Compositor {
-  /**
-   * Determines if a resource is a compositor resource.
-   *
-   * @param[in] resource The <code>PP_Resource</code> to test.
-   *
-   * @return A <code>PP_Bool</code> with <code>PP_TRUE</code> if the given
-   * resource is a compositor resource or <code>PP_FALSE</code> otherwise.
-   */
-  PP_Bool IsCompositor([in] PP_Resource resource);
-
-  /**
-   * Creates a Compositor resource.
-   *
-   * @param[in] instance A <code>PP_Instance</code> identifying one instance
-   * of a module.
-   *
-   * @return A <code>PP_Resource</code> containing the compositor resource if
-   * successful or 0 otherwise.
-   */
-  PP_Resource Create([in] PP_Instance instance);
-
-  /**
-   * Creates a new <code>PPB_CompositorLayer</code> and adds it to the end
-   * of the layer stack. A <code>PP_Resource</code> containing the layer is
-   * returned. It is uninitialized, <code>SetColor()</code>,
-   * <code>SetTexture</code> or <code>SetImage</code> should be used to
-   * initialize it. The layer will appear above other pre-existing layers.
-   * If <code>ResetLayers</code> is called or the <code>PPB_Compositor</code> is
-   * released, the returned layer will be invalidated, and any further calls on
-   * the layer will return <code>PP_ERROR_BADRESOURCE</code>.
-   *
-   * param[in] compositor A <code>PP_Resource</code> corresponding to
-   * a compositor layer resource.
-   *
-   * @return A <code>PP_Resource</code> containing the compositor layer
-   * resource if successful or 0 otherwise.
-   */
-  PP_Resource AddLayer([in] PP_Resource compositor);
-
-  /**
-   * Commits layers added by <code>AddLayer()</code> to the chromium compositor.
-   *
-   * param[in] compositor A <code>PP_Resource</code> corresponding to
-   * a compositor layer resource.
-   * @param[in] cc A <code>PP_CompletionCallback</code> to be called when
-   * layers have been represented on screen.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t CommitLayers([in] PP_Resource compositor,
-                       [in] PP_CompletionCallback cc);
-
-  /**
-   * Resets layers added by <code>AddLayer()</code>.
-   *
-   * param[in] compositor A <code>PP_Resource</code> corresponding to
-   * a compositor layer resource.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t ResetLayers([in] PP_Resource compositor);
-};
diff --git a/ppapi/api/ppb_compositor_layer.idl b/ppapi/api/ppb_compositor_layer.idl
deleted file mode 100644
index ed0967d..0000000
--- a/ppapi/api/ppb_compositor_layer.idl
+++ /dev/null
@@ -1,251 +0,0 @@
-/* Copyright 2014 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-[generate_thunk]
-
-label Chrome {
-  [channel=dev] M37 = 0.1,
-  [channel=dev] M38 = 0.2
-};
-
-/**
- * This enumeration contains blend modes used for computing the result pixels
- * based on the source RGBA values in layers with the RGBA values that are
- * already in the destination framebuffer.
- * alpha_src, color_src: source alpha and color.
- * alpha_dst, color_dst: destination alpha and color (before compositing).
- * Below descriptions of the blend modes assume the colors are pre-multiplied.
- * This interface is still in development (Dev API status) and may change,
- * so is only supported on Dev channel and Canary currently.
- */
-enum PP_BlendMode {
-  /**
-   * No blending, copy source to the destination directly.
-   */
-  PP_BLENDMODE_NONE,
-
-  /**
-   * Source is placed over the destination.
-   * Resulting alpha = alpha_src + alpha_dst - alpha_src * alpha_dst
-   * Resulting color = color_src + color_dst * (1 - alpha_src)
-   */
-  PP_BLENDMODE_SRC_OVER,
-
-  /**
-   * The last blend mode.
-   */
-  PP_BLENDMODE_LAST = PP_BLENDMODE_SRC_OVER
-};
-
-/**
- * Defines the <code>PPB_CompositorLayer</code> interface. It is used by
- * <code>PPB_Compositor</code>.
- */
-interface PPB_CompositorLayer {
-  /**
-   * Determines if a resource is a compositor layer resource.
-   *
-   * @param[in] resource The <code>PP_Resource</code> to test.
-   *
-   * @return A <code>PP_Bool</code> with <code>PP_TRUE</code> if the given
-   * resource is a compositor layer resource or <code>PP_FALSE</code>
-   * otherwise.
-   */
-  PP_Bool IsCompositorLayer([in] PP_Resource resource);
-
-  /**
-   * Sets the color of a solid color layer. If the layer is uninitialized,
-   * it will initialize the layer first, and then set its color.
-   * If the layer has been initialized to another kind of layer, the layer will
-   * not be changed, and <code>PP_ERROR_BADARGUMENT</code> will be returned.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] red A <code>float</code> for the red color component. It will be
-   * clamped to [0, 1].
-   * param[in] green A <code>float</code> for the green color component. It will
-   * be clamped to [0, 1].
-   * param[in] blue A <code>float</code> for the blue color component. It will
-   * be clamped to [0, 1].
-   * param[in] alpha A <code>float</code> for the alpha color component. It will
-   * be clamped to [0, 1].
-   * param[in] size A <code>PP_Size</code> for the size of the layer before
-   * transform.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t SetColor([in] PP_Resource layer,
-                   [in] float_t red,
-                   [in] float_t green,
-                   [in] float_t blue,
-                   [in] float_t alpha,
-                   [in] PP_Size size);
-
-  /**
-   * Sets the texture of a texture layer. If the layer is uninitialized,
-   * it will initialize the layer first, and then set its texture.
-   * The source rect will be set to ((0, 0), (1, 1)). If the layer has been
-   * initialized to another kind of layer, the layer will not be changed,
-   * and <code>PP_ERROR_BADARGUMENT</code> will be returned.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] context A <code>PP_Resource</code> corresponding to a graphics
-   * 3d resource which owns the GL texture.
-   * param[in] texture A GL texture object id.
-   * param[in] size A <code>PP_Size</code> for the size of the layer before
-   * transform.
-   * param[in] cc A <code>PP_CompletionCallback</code> to be called when
-   * the texture is released by Chromium compositor.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t SetTexture([in] PP_Resource layer,
-                     [in] PP_Resource context,
-                     [in] uint32_t texture,
-                     [in] PP_Size size,
-                     [in] PP_CompletionCallback cc);
-
-  /**
-   * Sets the texture of a texture layer. If the layer is uninitialized,
-   * it will initialize the layer first, and then set its texture.
-   * The source rect will be set to ((0, 0), (1, 1)). If the layer has been
-   * initialized to another kind of layer, the layer will not be changed,
-   * and <code>PP_ERROR_BADARGUMENT</code> will be returned.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] context A <code>PP_Resource</code> corresponding to a graphics
-   * 3d resource which owns the GL texture.
-   * param[in] target GL texture target (GL_TEXTURE_2D, etc).
-   * param[in] texture A GL texture object id.
-   * param[in] size A <code>PP_Size</code> for the size of the layer before
-   * transform.
-   * param[in] cc A <code>PP_CompletionCallback</code> to be called when
-   * the texture is released by Chromium compositor.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  [version = 0.2]
-  int32_t SetTexture([in] PP_Resource layer,
-                     [in] PP_Resource context,
-                     [in] uint32_t target,
-                     [in] uint32_t texture,
-                     [in] PP_Size size,
-                     [in] PP_CompletionCallback cc);
-
-  /**
-   * Sets the image of an image layer. If the layer is uninitialized,
-   * it will initialize the layer first, and then set its image.
-   * The layer size will be set to the image's size. The source rect will be set
-   * to the full image. If the layer has been initialized to another kind of
-   * layer, the layer will not be changed, and <code>PP_ERROR_BADARGUMENT</code>
-   * will be returned.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] image_data A <code>PP_Resource</code> corresponding to
-   * an image data resource.
-   * param[in] size A <code>PP_Size</code> for the size of the layer before
-   * transform. If NULL, the image's size will be used.
-   * param[in] cc A <code>PP_CompletionCallback</code> to be called when
-   * the image data is released by Chromium compositor.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t SetImage([in] PP_Resource layer,
-                   [in] PP_Resource image_data,
-                   [in] PP_Size size,
-                   [in] PP_CompletionCallback cc);
-
-  /**
-   * Sets a clip rectangle for a compositor layer. The Chromium compositor
-   * applies a transform matrix on the layer first, and then clips the layer
-   * with the rectangle.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] rect The clip rectangle. The origin is top-left corner of
-   * the plugin.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t SetClipRect([in] PP_Resource layer,
-                      [in] PP_Rect rect);
-
-  /**
-   * Sets a transform matrix which is used to composite the layer.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] matrix A float array with 16 elements. The matrix is
-   * column major. The default transform matrix is an identity matrix.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t SetTransform([in] PP_Resource layer,
-                       [in] float_t[16] matrix);
-
-  /**
-   * Sets the opacity value which will be applied to the layer. The effective
-   * value of each pixel is computed as:
-   *
-   *   if (premult_alpha)
-   *     pixel.rgb = pixel.rgb * opacity;
-   *   pixel.a = pixel.a * opactiy;
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] opacity A <code>float</code> for the opacity value, The default
-   * value is 1.0f.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t SetOpacity([in] PP_Resource layer,
-                     [in] float_t opacity);
-
-  /**
-   * Sets the blend mode which is used to composite the layer.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] mode A <code>PP_BlendMode</code>. The default mode is
-   * <code>PP_BLENDMODE_SRC_OVER</code>.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t SetBlendMode([in] PP_Resource layer,
-                       [in] PP_BlendMode mode);
-
-  /**
-   * Sets a source rectangle for a texture layer or an image layer.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] rect A <code>PP_FloatRect</code> for an area of the source to
-   * consider. For a texture layer, rect is in uv coordinates. For an image
-   * layer, rect is in pixels. If the rect is beyond the dimensions of the
-   * texture or image, <code>PP_ERROR_BADARGUMENT</code> will be returned.
-   * If the layer size does not match the source rect size, bilinear scaling
-   * will be used.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t SetSourceRect([in] PP_Resource layer,
-                        [in] PP_FloatRect rect);
-
-  /**
-   * Sets the premultiplied alpha for an texture layer.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] premult A <code>PP_Bool</code> with <code>PP_TRUE</code> if
-   * pre-multiplied alpha is used.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t SetPremultipliedAlpha([in] PP_Resource layer,
-                                [in] PP_Bool premult);
-};
diff --git a/ppapi/c/BUILD.gn b/ppapi/c/BUILD.gn
index 8c23e16..e1b01aec 100644
--- a/ppapi/c/BUILD.gn
+++ b/ppapi/c/BUILD.gn
@@ -30,8 +30,6 @@
     "ppb_audio_buffer.h",
     "ppb_audio_config.h",
     "ppb_audio_encoder.h",
-    "ppb_compositor.h",
-    "ppb_compositor_layer.h",
     "ppb_console.h",
     "ppb_core.h",
     "ppb_file_io.h",
diff --git a/ppapi/c/ppb_compositor.h b/ppapi/c/ppb_compositor.h
deleted file mode 100644
index ce58ab4..0000000
--- a/ppapi/c/ppb_compositor.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/* Copyright 2014 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/* From ppb_compositor.idl modified Tue Jun  3 12:44:44 2014. */
-
-#ifndef PPAPI_C_PPB_COMPOSITOR_H_
-#define PPAPI_C_PPB_COMPOSITOR_H_
-
-#include "ppapi/c/pp_bool.h"
-#include "ppapi/c/pp_completion_callback.h"
-#include "ppapi/c/pp_instance.h"
-#include "ppapi/c/pp_macros.h"
-#include "ppapi/c/pp_resource.h"
-#include "ppapi/c/pp_stdint.h"
-
-#define PPB_COMPOSITOR_INTERFACE_0_1 "PPB_Compositor;0.1" /* dev */
-/**
- * @file
- */
-
-
-/**
- * @addtogroup Interfaces
- * @{
- */
-/**
- * Defines the <code>PPB_Compositor</code> interface. Used for setting
- * <code>PPB_CompositorLayer</code> layers to the Chromium compositor for
- * compositing. This allows a plugin to combine different sources of visual
- * data efficiently, such as <code>PPB_ImageData</code> images and
- * OpenGL textures. See also <code>PPB_CompositorLayer</code> for more
- * information.
- * This interface is still in development (Dev API status) and may change,
- * so is only supported on Dev channel and Canary currently.
- *
- * <strong>Example usage from plugin code:</strong>
- *
- * <strong>Setup:</strong>
- * @code
- * PP_Resource compositor;
- * compositor = compositor_if->Create(instance);
- * instance_if->BindGraphics(instance, compositor);
- * @endcode
- *
- * <strong>Setup layer stack:</strong>
- * @code
- * PP_Resource color_layer = compositor_if->AddLayer(compositor);
- * PP_Resource texture_layer = compositor_if->AddLayer(compositor);
- * @endcode
- *
- * <strong> Present one frame:</strong>
- * layer_if->SetColor(color_layer, 255, 255, 0, 255, PP_MakeSize(400, 400));
- * PP_CompletionCallback release_callback = {
- *   TextureReleasedCallback, 0, PP_COMPLETIONCALLBACK_FLAG_NONE,
- * };
- * layer_if->SetTexture(texture_layer, graphics3d, texture_id,
- *                      PP_MakeSize(300, 300), release_callback);
- *
- * PP_CompletionCallback callback = {
- *   DidFinishCommitLayersCallback,
- *   (void*) texture_id,
- *   PP_COMPLETIONCALLBACK_FLAG_NONE,
- * };
- * compositor_if->CommitLayers(compositor, callback);
- * @endcode
- *
- * <strong>release callback</strong>
- * void ReleaseCallback(int32_t result, void* user_data) {
- *   if (result == PP_OK) {
- *     uint32_t texture_id = (uint32_t) user_data;
- *     // reuse the texture or delete it.
- *   }
- * }
- *
- * <strong>Shutdown:</strong>
- * @code
- * core->ReleaseResource(color_layer);
- * core->ReleaseResource(texture_layer);
- * core->ReleaseResource(compositor);
- * @endcode
- */
-struct PPB_Compositor_0_1 { /* dev */
-  /**
-   * Determines if a resource is a compositor resource.
-   *
-   * @param[in] resource The <code>PP_Resource</code> to test.
-   *
-   * @return A <code>PP_Bool</code> with <code>PP_TRUE</code> if the given
-   * resource is a compositor resource or <code>PP_FALSE</code> otherwise.
-   */
-  PP_Bool (*IsCompositor)(PP_Resource resource);
-  /**
-   * Creates a Compositor resource.
-   *
-   * @param[in] instance A <code>PP_Instance</code> identifying one instance
-   * of a module.
-   *
-   * @return A <code>PP_Resource</code> containing the compositor resource if
-   * successful or 0 otherwise.
-   */
-  PP_Resource (*Create)(PP_Instance instance);
-  /**
-   * Creates a new <code>PPB_CompositorLayer</code> and adds it to the end
-   * of the layer stack. A <code>PP_Resource</code> containing the layer is
-   * returned. It is uninitialized, <code>SetColor()</code>,
-   * <code>SetTexture</code> or <code>SetImage</code> should be used to
-   * initialize it. The layer will appear above other pre-existing layers.
-   * If <code>ResetLayers</code> is called or the <code>PPB_Compositor</code> is
-   * released, the returned layer will be invalidated, and any further calls on
-   * the layer will return <code>PP_ERROR_BADRESOURCE</code>.
-   *
-   * param[in] compositor A <code>PP_Resource</code> corresponding to
-   * a compositor layer resource.
-   *
-   * @return A <code>PP_Resource</code> containing the compositor layer
-   * resource if successful or 0 otherwise.
-   */
-  PP_Resource (*AddLayer)(PP_Resource compositor);
-  /**
-   * Commits layers added by <code>AddLayer()</code> to the chromium compositor.
-   *
-   * param[in] compositor A <code>PP_Resource</code> corresponding to
-   * a compositor layer resource.
-   * @param[in] cc A <code>PP_CompletionCallback</code> to be called when
-   * layers have been represented on screen.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t (*CommitLayers)(PP_Resource compositor,
-                          struct PP_CompletionCallback cc);
-  /**
-   * Resets layers added by <code>AddLayer()</code>.
-   *
-   * param[in] compositor A <code>PP_Resource</code> corresponding to
-   * a compositor layer resource.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t (*ResetLayers)(PP_Resource compositor);
-};
-/**
- * @}
- */
-
-#endif  /* PPAPI_C_PPB_COMPOSITOR_H_ */
-
diff --git a/ppapi/c/ppb_compositor_layer.h b/ppapi/c/ppb_compositor_layer.h
deleted file mode 100644
index 25d2ebc3..0000000
--- a/ppapi/c/ppb_compositor_layer.h
+++ /dev/null
@@ -1,264 +0,0 @@
-/* Copyright 2014 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/* From ppb_compositor_layer.idl modified Thu Aug 14 18:06:33 2014. */
-
-#ifndef PPAPI_C_PPB_COMPOSITOR_LAYER_H_
-#define PPAPI_C_PPB_COMPOSITOR_LAYER_H_
-
-#include "ppapi/c/pp_bool.h"
-#include "ppapi/c/pp_completion_callback.h"
-#include "ppapi/c/pp_macros.h"
-#include "ppapi/c/pp_point.h"
-#include "ppapi/c/pp_rect.h"
-#include "ppapi/c/pp_resource.h"
-#include "ppapi/c/pp_size.h"
-#include "ppapi/c/pp_stdint.h"
-
-#define PPB_COMPOSITORLAYER_INTERFACE_0_1 "PPB_CompositorLayer;0.1" /* dev */
-#define PPB_COMPOSITORLAYER_INTERFACE_0_2 "PPB_CompositorLayer;0.2" /* dev */
-/**
- * @file
- */
-
-
-/**
- * @addtogroup Enums
- * @{
- */
-/**
- * This enumeration contains blend modes used for computing the result pixels
- * based on the source RGBA values in layers with the RGBA values that are
- * already in the destination framebuffer.
- * alpha_src, color_src: source alpha and color.
- * alpha_dst, color_dst: destination alpha and color (before compositing).
- * Below descriptions of the blend modes assume the colors are pre-multiplied.
- * This interface is still in development (Dev API status) and may change,
- * so is only supported on Dev channel and Canary currently.
- */
-typedef enum {
-  /**
-   * No blending, copy source to the destination directly.
-   */
-  PP_BLENDMODE_NONE,
-  /**
-   * Source is placed over the destination.
-   * Resulting alpha = alpha_src + alpha_dst - alpha_src * alpha_dst
-   * Resulting color = color_src + color_dst * (1 - alpha_src)
-   */
-  PP_BLENDMODE_SRC_OVER,
-  /**
-   * The last blend mode.
-   */
-  PP_BLENDMODE_LAST = PP_BLENDMODE_SRC_OVER
-} PP_BlendMode;
-/**
- * @}
- */
-
-/**
- * @addtogroup Interfaces
- * @{
- */
-/**
- * Defines the <code>PPB_CompositorLayer</code> interface. It is used by
- * <code>PPB_Compositor</code>.
- */
-struct PPB_CompositorLayer_0_2 { /* dev */
-  /**
-   * Determines if a resource is a compositor layer resource.
-   *
-   * @param[in] resource The <code>PP_Resource</code> to test.
-   *
-   * @return A <code>PP_Bool</code> with <code>PP_TRUE</code> if the given
-   * resource is a compositor layer resource or <code>PP_FALSE</code>
-   * otherwise.
-   */
-  PP_Bool (*IsCompositorLayer)(PP_Resource resource);
-  /**
-   * Sets the color of a solid color layer. If the layer is uninitialized,
-   * it will initialize the layer first, and then set its color.
-   * If the layer has been initialized to another kind of layer, the layer will
-   * not be changed, and <code>PP_ERROR_BADARGUMENT</code> will be returned.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] red A <code>float</code> for the red color component. It will be
-   * clamped to [0, 1].
-   * param[in] green A <code>float</code> for the green color component. It will
-   * be clamped to [0, 1].
-   * param[in] blue A <code>float</code> for the blue color component. It will
-   * be clamped to [0, 1].
-   * param[in] alpha A <code>float</code> for the alpha color component. It will
-   * be clamped to [0, 1].
-   * param[in] size A <code>PP_Size</code> for the size of the layer before
-   * transform.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t (*SetColor)(PP_Resource layer,
-                      float red,
-                      float green,
-                      float blue,
-                      float alpha,
-                      const struct PP_Size* size);
-  /**
-   * Sets the texture of a texture layer. If the layer is uninitialized,
-   * it will initialize the layer first, and then set its texture.
-   * The source rect will be set to ((0, 0), (1, 1)). If the layer has been
-   * initialized to another kind of layer, the layer will not be changed,
-   * and <code>PP_ERROR_BADARGUMENT</code> will be returned.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] context A <code>PP_Resource</code> corresponding to a graphics
-   * 3d resource which owns the GL texture.
-   * param[in] target GL texture target (GL_TEXTURE_2D, etc).
-   * param[in] texture A GL texture object id.
-   * param[in] size A <code>PP_Size</code> for the size of the layer before
-   * transform.
-   * param[in] cc A <code>PP_CompletionCallback</code> to be called when
-   * the texture is released by Chromium compositor.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t (*SetTexture)(PP_Resource layer,
-                        PP_Resource context,
-                        uint32_t target,
-                        uint32_t texture,
-                        const struct PP_Size* size,
-                        struct PP_CompletionCallback cc);
-  /**
-   * Sets the image of an image layer. If the layer is uninitialized,
-   * it will initialize the layer first, and then set its image.
-   * The layer size will be set to the image's size. The source rect will be set
-   * to the full image. If the layer has been initialized to another kind of
-   * layer, the layer will not be changed, and <code>PP_ERROR_BADARGUMENT</code>
-   * will be returned.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] image_data A <code>PP_Resource</code> corresponding to
-   * an image data resource.
-   * param[in] size A <code>PP_Size</code> for the size of the layer before
-   * transform. If NULL, the image's size will be used.
-   * param[in] cc A <code>PP_CompletionCallback</code> to be called when
-   * the image data is released by Chromium compositor.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t (*SetImage)(PP_Resource layer,
-                      PP_Resource image_data,
-                      const struct PP_Size* size,
-                      struct PP_CompletionCallback cc);
-  /**
-   * Sets a clip rectangle for a compositor layer. The Chromium compositor
-   * applies a transform matrix on the layer first, and then clips the layer
-   * with the rectangle.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] rect The clip rectangle. The origin is top-left corner of
-   * the plugin.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t (*SetClipRect)(PP_Resource layer, const struct PP_Rect* rect);
-  /**
-   * Sets a transform matrix which is used to composite the layer.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] matrix A float array with 16 elements. The matrix is
-   * column major. The default transform matrix is an identity matrix.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t (*SetTransform)(PP_Resource layer, const float matrix[16]);
-  /**
-   * Sets the opacity value which will be applied to the layer. The effective
-   * value of each pixel is computed as:
-   *
-   *   if (premult_alpha)
-   *     pixel.rgb = pixel.rgb * opacity;
-   *   pixel.a = pixel.a * opactiy;
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] opacity A <code>float</code> for the opacity value, The default
-   * value is 1.0f.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t (*SetOpacity)(PP_Resource layer, float opacity);
-  /**
-   * Sets the blend mode which is used to composite the layer.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] mode A <code>PP_BlendMode</code>. The default mode is
-   * <code>PP_BLENDMODE_SRC_OVER</code>.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t (*SetBlendMode)(PP_Resource layer, PP_BlendMode mode);
-  /**
-   * Sets a source rectangle for a texture layer or an image layer.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] rect A <code>PP_FloatRect</code> for an area of the source to
-   * consider. For a texture layer, rect is in uv coordinates. For an image
-   * layer, rect is in pixels. If the rect is beyond the dimensions of the
-   * texture or image, <code>PP_ERROR_BADARGUMENT</code> will be returned.
-   * If the layer size does not match the source rect size, bilinear scaling
-   * will be used.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t (*SetSourceRect)(PP_Resource layer, const struct PP_FloatRect* rect);
-  /**
-   * Sets the premultiplied alpha for an texture layer.
-   *
-   * param[in] layer A <code>PP_Resource</code> corresponding to a compositor
-   * layer resource.
-   * param[in] premult A <code>PP_Bool</code> with <code>PP_TRUE</code> if
-   * pre-multiplied alpha is used.
-   *
-   * @return An int32_t containing a result code from <code>pp_errors.h</code>.
-   */
-  int32_t (*SetPremultipliedAlpha)(PP_Resource layer, PP_Bool premult);
-};
-
-struct PPB_CompositorLayer_0_1 { /* dev */
-  PP_Bool (*IsCompositorLayer)(PP_Resource resource);
-  int32_t (*SetColor)(PP_Resource layer,
-                      float red,
-                      float green,
-                      float blue,
-                      float alpha,
-                      const struct PP_Size* size);
-  int32_t (*SetTexture)(PP_Resource layer,
-                        PP_Resource context,
-                        uint32_t texture,
-                        const struct PP_Size* size,
-                        struct PP_CompletionCallback cc);
-  int32_t (*SetImage)(PP_Resource layer,
-                      PP_Resource image_data,
-                      const struct PP_Size* size,
-                      struct PP_CompletionCallback cc);
-  int32_t (*SetClipRect)(PP_Resource layer, const struct PP_Rect* rect);
-  int32_t (*SetTransform)(PP_Resource layer, const float matrix[16]);
-  int32_t (*SetOpacity)(PP_Resource layer, float opacity);
-  int32_t (*SetBlendMode)(PP_Resource layer, PP_BlendMode mode);
-  int32_t (*SetSourceRect)(PP_Resource layer, const struct PP_FloatRect* rect);
-  int32_t (*SetPremultipliedAlpha)(PP_Resource layer, PP_Bool premult);
-};
-/**
- * @}
- */
-
-#endif  /* PPAPI_C_PPB_COMPOSITOR_LAYER_H_ */
-
diff --git a/ppapi/cpp/BUILD.gn b/ppapi/cpp/BUILD.gn
index da877e90..65d744a 100644
--- a/ppapi/cpp/BUILD.gn
+++ b/ppapi/cpp/BUILD.gn
@@ -65,10 +65,6 @@
     "audio_encoder.cc",
     "audio_encoder.h",
     "completion_callback.h",
-    "compositor.cc",
-    "compositor.h",
-    "compositor_layer.cc",
-    "compositor_layer.h",
     "core.cc",
     "core.h",
     "directory_entry.cc",
diff --git a/ppapi/cpp/compositor.cc b/ppapi/cpp/compositor.cc
deleted file mode 100644
index 163a241..0000000
--- a/ppapi/cpp/compositor.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ppapi/cpp/compositor.h"
-
-#include "ppapi/c/pp_errors.h"
-#include "ppapi/cpp/completion_callback.h"
-#include "ppapi/cpp/module_impl.h"
-#include "ppapi/cpp/var.h"
-
-namespace pp {
-
-namespace {
-
-template <> const char* interface_name<PPB_Compositor_0_1>() {
-  return PPB_COMPOSITOR_INTERFACE_0_1;
-}
-
-}  // namespace
-
-Compositor::Compositor() {
-}
-
-Compositor::Compositor(const InstanceHandle& instance) {
-  if (has_interface<PPB_Compositor_0_1>()) {
-    PassRefFromConstructor(get_interface<PPB_Compositor_0_1>()->Create(
-        instance.pp_instance()));
-  }
-}
-
-Compositor::Compositor(
-    const Compositor& other) : Resource(other) {
-}
-
-Compositor::Compositor(const Resource& resource)
-    : Resource(resource) {
-  PP_DCHECK(IsCompositor(resource));
-}
-
-Compositor::Compositor(PassRef, PP_Resource resource)
-    : Resource(PASS_REF, resource) {
-}
-
-Compositor::~Compositor() {
-}
-
-CompositorLayer Compositor::AddLayer() {
-  PP_Resource layer = 0;
-  if (has_interface<PPB_Compositor_0_1>()) {
-    layer = get_interface<PPB_Compositor_0_1>()->AddLayer(pp_resource());
-  }
-  return CompositorLayer(PASS_REF, layer);
-}
-
-int32_t Compositor::CommitLayers(const CompletionCallback& cc) {
-  if (has_interface<PPB_Compositor_0_1>()) {
-    return get_interface<PPB_Compositor_0_1>()->CommitLayers(
-        pp_resource(), cc.pp_completion_callback());
-  }
-  return cc.MayForce(PP_ERROR_NOINTERFACE);
-}
-
-int32_t Compositor::ResetLayers() {
-  if (has_interface<PPB_Compositor_0_1>()) {
-    return get_interface<PPB_Compositor_0_1>()->ResetLayers(pp_resource());
-  }
-  return PP_ERROR_NOINTERFACE;
-}
-
-bool Compositor::IsCompositor(const Resource& resource) {
-  if (has_interface<PPB_Compositor_0_1>()) {
-    return PP_ToBool(get_interface<PPB_Compositor_0_1>()->
-        IsCompositor(resource.pp_resource()));
-  }
-  return false;
-}
-
-}  // namespace pp
diff --git a/ppapi/cpp/compositor.h b/ppapi/cpp/compositor.h
deleted file mode 100644
index 940acdf..0000000
--- a/ppapi/cpp/compositor.h
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PPAPI_CPP_COMPOSITOR_H_
-#define PPAPI_CPP_COMPOSITOR_H_
-
-#include <stdint.h>
-
-#include "ppapi/c/ppb_compositor.h"
-#include "ppapi/cpp/completion_callback.h"
-#include "ppapi/cpp/compositor_layer.h"
-#include "ppapi/cpp/resource.h"
-
-/// @file
-/// This file defines the API to create a compositor in the browser.
-namespace pp {
-
-/// The <code>Compositor</code> interface is used for setting
-/// <code>CompositorLayer</code> layers to the Chromium compositor for
-/// compositing. This allows a plugin to combine different sources of visual
-/// data efficiently, such as <code>ImageData</code> images and OpenGL textures.
-/// See also <code>CompositorLayer</code> for more information.
-class Compositor : public Resource {
- public:
-  /// Default constructor for creating an is_null()
-  /// <code>Compositor</code> object.
-  Compositor();
-
-  /// A constructor for creating and initializing a compositor.
-  ///
-  /// On failure, the object will be is_null().
-  explicit Compositor(const InstanceHandle& instance);
-
-  /// The copy constructor for <code>Compositor</code>.
-  ///
-  /// @param[in] other A reference to a <code>Compositor</code>.
-  Compositor(const Compositor& other);
-
-  /// Constructs a <code>Compositor</code> from a <code>Resource</code>.
-  ///
-  /// @param[in] resource A <code>PPB_Compositor</code> resource.
-  explicit Compositor(const Resource& resource);
-
-  /// A constructor used when you have received a <code>PP_Resource</code> as a
-  /// return value that has had 1 ref added on behalf of the caller.
-  ///
-  /// @param[in] resource A <code>PPB_Compositor</code> resource.
-  Compositor(PassRef, PP_Resource resource);
-
-  /// Destructor.
-  ~Compositor();
-
-  /// Creates a new <code>CompositorLayer</code> and adds it to the end of the
-  /// layer stack. A <code>CompositorLayer</code> containing the layer is
-  /// returned. It is uninitialized, <code>SetColor()</code>,
-  /// <code>SetTexture</code> or <code>SetImage</code> should be used to
-  /// initialize it. The layer will appear above other pre-existing layers.
-  /// If <code>ResetLayers</code> is called or the <code>PPB_Compositor</code>
-  /// is released, the returned layer will be invalidated, and any further calls
-  /// on the layer will return <code>PP_ERROR_BADRESOURCE</code>.
-  ///
-  /// @return A <code>CompositorLayer</code> containing the compositor layer
-  /// resource.
-  CompositorLayer AddLayer();
-
-  /// Commits layers added by <code>AddLayer()</code> to the chromium
-  /// compositor.
-  ///
-  /// @param[in] cc A <code>CompletionCallback</code> to be called when
-  /// layers have been represented on screen.
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t CommitLayers(const CompletionCallback& cc);
-
-  /// Resets layers added by <code>AddLayer()</code>
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t ResetLayers();
-
-  /// Checks whether a <code>Resource</code> is a compositor, to test whether
-  /// it is appropriate for use with the <code>Compositor</code> constructor.
-  ///
-  /// @param[in] resource A <code>Resource</code> to test.
-  ///
-  /// @return True if <code>resource</code> is a compositor.
-  static bool IsCompositor(const Resource& resource);
-};
-
-}  // namespace pp
-
-#endif  // PPAPI_CPP_COMPOSITOR_H_
diff --git a/ppapi/cpp/compositor_layer.cc b/ppapi/cpp/compositor_layer.cc
deleted file mode 100644
index d15a6731..0000000
--- a/ppapi/cpp/compositor_layer.cc
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ppapi/cpp/compositor_layer.h"
-
-#include "ppapi/c/pp_errors.h"
-#include "ppapi/cpp/completion_callback.h"
-#include "ppapi/cpp/module_impl.h"
-#include "ppapi/cpp/var.h"
-
-namespace pp {
-
-namespace {
-
-template <> const char* interface_name<PPB_CompositorLayer_0_1>() {
-  return PPB_COMPOSITORLAYER_INTERFACE_0_1;
-}
-
-template <> const char* interface_name<PPB_CompositorLayer_0_2>() {
-  return PPB_COMPOSITORLAYER_INTERFACE_0_2;
-}
-
-}  // namespace
-
-CompositorLayer::CompositorLayer() {
-}
-
-CompositorLayer::CompositorLayer(
-    const CompositorLayer& other) : Resource(other) {
-}
-
-CompositorLayer::CompositorLayer(const Resource& resource)
-    : Resource(resource) {
-  PP_DCHECK(IsCompositorLayer(resource));
-}
-
-CompositorLayer::CompositorLayer(PassRef, PP_Resource resource)
-    : Resource(PASS_REF, resource) {
-}
-
-CompositorLayer::~CompositorLayer() {
-}
-
-int32_t CompositorLayer::SetColor(float red,
-                                  float green,
-                                  float blue,
-                                  float alpha,
-                                  const Size& size) {
-  if (has_interface<PPB_CompositorLayer_0_2>()) {
-    return get_interface<PPB_CompositorLayer_0_2>()->SetColor(
-        pp_resource(), red, green, blue, alpha, &size.pp_size());
-  }
-  if (has_interface<PPB_CompositorLayer_0_1>()) {
-    return get_interface<PPB_CompositorLayer_0_1>()->SetColor(
-        pp_resource(), red, green, blue, alpha, &size.pp_size());
-  }
-  return PP_ERROR_NOINTERFACE;
-}
-
-int32_t CompositorLayer::SetTexture(const Graphics3D& context,
-                                    uint32_t target,
-                                    uint32_t texture,
-                                    const Size& size,
-                                    const CompletionCallback& cc) {
-  if (has_interface<PPB_CompositorLayer_0_2>()) {
-    return get_interface<PPB_CompositorLayer_0_2>()->SetTexture(
-        pp_resource(), context.pp_resource(), target, texture, &size.pp_size(),
-        cc.pp_completion_callback());
-  }
-  if (has_interface<PPB_CompositorLayer_0_1>()) {
-    if (target != 0x0DE1) // 0x0DE1 GL_TEXTURE_2D
-      return cc.MayForce(PP_ERROR_NOTSUPPORTED);
-    return get_interface<PPB_CompositorLayer_0_1>()->SetTexture(
-        pp_resource(), context.pp_resource(), texture, &size.pp_size(),
-        cc.pp_completion_callback());
-  }
-  return cc.MayForce(PP_ERROR_NOINTERFACE);
-}
-
-int32_t CompositorLayer::SetImage(const ImageData& image,
-                                  const CompletionCallback& cc) {
-  if (has_interface<PPB_CompositorLayer_0_2>()) {
-    return get_interface<PPB_CompositorLayer_0_2>()->SetImage(
-        pp_resource(), image.pp_resource(), NULL,
-        cc.pp_completion_callback());
-  }
-  if (has_interface<PPB_CompositorLayer_0_1>()) {
-    return get_interface<PPB_CompositorLayer_0_1>()->SetImage(
-        pp_resource(), image.pp_resource(), NULL,
-        cc.pp_completion_callback());
-  }
-  return cc.MayForce(PP_ERROR_NOINTERFACE);
-}
-
-int32_t CompositorLayer::SetImage(const ImageData& image,
-                                  const Size& size,
-                                  const CompletionCallback& cc) {
-  if (has_interface<PPB_CompositorLayer_0_2>()) {
-    return get_interface<PPB_CompositorLayer_0_2>()->SetImage(
-        pp_resource(), image.pp_resource(), &size.pp_size(),
-        cc.pp_completion_callback());
-  }
-  if (has_interface<PPB_CompositorLayer_0_1>()) {
-    return get_interface<PPB_CompositorLayer_0_1>()->SetImage(
-        pp_resource(), image.pp_resource(), &size.pp_size(),
-        cc.pp_completion_callback());
-  }
-  return cc.MayForce(PP_ERROR_NOINTERFACE);
-}
-
-int32_t CompositorLayer::SetClipRect(const Rect& rect) {
-  if (has_interface<PPB_CompositorLayer_0_2>()) {
-    return get_interface<PPB_CompositorLayer_0_2>()->SetClipRect(
-        pp_resource(), &rect.pp_rect());
-  }
-  if (has_interface<PPB_CompositorLayer_0_1>()) {
-    return get_interface<PPB_CompositorLayer_0_1>()->SetClipRect(
-        pp_resource(), &rect.pp_rect());
-  }
-  return PP_ERROR_NOINTERFACE;
-}
-
-int32_t CompositorLayer::SetTransform(const float matrix[16]) {
-  if (has_interface<PPB_CompositorLayer_0_2>()) {
-    return get_interface<PPB_CompositorLayer_0_2>()->SetTransform(
-        pp_resource(), matrix);
-  }
-  if (has_interface<PPB_CompositorLayer_0_1>()) {
-    return get_interface<PPB_CompositorLayer_0_1>()->SetTransform(
-        pp_resource(), matrix);
-  }
-  return PP_ERROR_NOINTERFACE;
-}
-
-int32_t CompositorLayer::SetOpacity(float opacity) {
-  if (has_interface<PPB_CompositorLayer_0_2>()) {
-    return get_interface<PPB_CompositorLayer_0_2>()->SetOpacity(
-        pp_resource(), opacity);
-  }
-  if (has_interface<PPB_CompositorLayer_0_1>()) {
-    return get_interface<PPB_CompositorLayer_0_1>()->SetOpacity(
-        pp_resource(), opacity);
-  }
-  return PP_ERROR_NOINTERFACE;
-}
-
-int32_t CompositorLayer::SetBlendMode(PP_BlendMode mode) {
-  if (has_interface<PPB_CompositorLayer_0_2>()) {
-    return get_interface<PPB_CompositorLayer_0_2>()->SetBlendMode(
-        pp_resource(), mode);
-  }
-  if (has_interface<PPB_CompositorLayer_0_1>()) {
-    return get_interface<PPB_CompositorLayer_0_1>()->SetBlendMode(
-        pp_resource(), mode);
-  }
-  return PP_ERROR_NOINTERFACE;
-}
-
-int32_t CompositorLayer::SetSourceRect(const FloatRect& rect) {
-  if (has_interface<PPB_CompositorLayer_0_2>()) {
-    return get_interface<PPB_CompositorLayer_0_2>()->SetSourceRect(
-        pp_resource(), &rect.pp_float_rect());
-  }
-  if (has_interface<PPB_CompositorLayer_0_1>()) {
-    return get_interface<PPB_CompositorLayer_0_1>()->SetSourceRect(
-        pp_resource(), &rect.pp_float_rect());
-  }
-  return PP_ERROR_NOINTERFACE;
-}
-
-int32_t CompositorLayer::SetPremultipliedAlpha(bool premult) {
-  if (has_interface<PPB_CompositorLayer_0_2>()) {
-    return get_interface<PPB_CompositorLayer_0_2>()->SetPremultipliedAlpha(
-        pp_resource(), PP_FromBool(premult));
-  }
-  if (has_interface<PPB_CompositorLayer_0_1>()) {
-    return get_interface<PPB_CompositorLayer_0_1>()->SetPremultipliedAlpha(
-        pp_resource(), PP_FromBool(premult));
-  }
-  return PP_ERROR_NOINTERFACE;
-}
-
-bool CompositorLayer::IsCompositorLayer(const Resource& resource) {
-  if (has_interface<PPB_CompositorLayer_0_2>()) {
-    return PP_ToBool(get_interface<PPB_CompositorLayer_0_2>()->
-        IsCompositorLayer(resource.pp_resource()));
-  }
-  if (has_interface<PPB_CompositorLayer_0_1>()) {
-    return PP_ToBool(get_interface<PPB_CompositorLayer_0_1>()->
-        IsCompositorLayer(resource.pp_resource()));
-  }
-  return false;
-}
-
-}  // namespace pp
diff --git a/ppapi/cpp/compositor_layer.h b/ppapi/cpp/compositor_layer.h
deleted file mode 100644
index b4c46dcf..0000000
--- a/ppapi/cpp/compositor_layer.h
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PPAPI_CPP_COMPOSITOR_LAYER_H_
-#define PPAPI_CPP_COMPOSITOR_LAYER_H_
-
-#include <stdint.h>
-
-#include "ppapi/c/ppb_compositor_layer.h"
-#include "ppapi/cpp/graphics_3d.h"
-#include "ppapi/cpp/image_data.h"
-#include "ppapi/cpp/rect.h"
-#include "ppapi/cpp/resource.h"
-#include "ppapi/cpp/size.h"
-
-namespace pp {
-
-class CompositorLayer : public Resource {
- public:
-  /// Default constructor for creating an is_null()
-  /// <code>CompositorLayer</code> object.
-  CompositorLayer();
-
-  /// The copy constructor for <code>CompositorLayer</code>.
-  ///
-  /// @param[in] other A reference to a <code>CompositorLayer</code>.
-  CompositorLayer(const CompositorLayer& other);
-
-  /// Constructs a <code>CompositorLayer</code> from a <code>Resource</code>.
-  ///
-  /// @param[in] resource A <code>PPB_CompositorLayer</code> resource.
-  explicit CompositorLayer(const Resource& resource);
-
-  /// A constructor used when you have received a <code>PP_Resource</code> as a
-  /// return value that has had 1 ref added for you.
-  ///
-  /// @param[in] resource A <code>PPB_CompositorLayer</code> resource.
-  CompositorLayer(PassRef, PP_Resource resource);
-
-  /// Destructor.
-  ~CompositorLayer();
-
-  /// Sets the color of a solid color layer. If the layer is uninitialized, it
-  /// will initialize the layer first, and then set its color. If the layer has
-  /// been initialized to another kind of layer, the layer will not be changed,
-  /// and <code>PP_ERROR_BADARGUMENT</code> will be returned.
-  ///
-  /// param[in] red A <code>float</code> for the red color component. It will be
-  /// clamped to [0, 1]
-  /// param[in] green A <code>float</code> for the green color component.
-  /// It will be clamped to [0, 1].
-  /// param[in] blue A <code>float</code> for the blue color component. It will
-  /// be clamped to [0, 1].
-  /// param[in] alpha A <code>float</code> for the alpha color component.
-  /// It will be clamped to [0, 1].
-  /// param[in] size A <code>Size</code> for the size of the layer before
-  /// transform.
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t SetColor(float red,
-                   float green,
-                   float blue,
-                   float alpha,
-                   const Size& size);
-
-  /// Sets the texture of a texture layer. If the layer is uninitialized, it
-  /// will initialize the layer first, and then set its texture. The source rect
-  /// will be set to ((0, 0), (1, 1)). If the layer has been initialized to
-  /// another kind of layer, the layer will not be changed, and
-  /// <code>PP_ERROR_BADARGUMENT</code> will be returned.
-  ///
-  /// param[in] context A <code>Graphics3D</code> corresponding to a graphics 3d
-  /// resource which owns the GL texture.
-  /// param[in] target GL texture target (GL_TEXTURE_2D, etc).
-  /// param[in] texture A GL texture object id.
-  /// param[in] size A <code>Size</code> for the size of the layer before
-  /// transform.
-  /// param[in] cc A <code>CompletionCallback</code> to be called when
-  /// the texture is released by Chromium compositor.
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t SetTexture(const Graphics3D& context,
-                     uint32_t target,
-                     uint32_t texture,
-                     const Size& size,
-                     const CompletionCallback& cc);
-
-  /// Sets the image of an image layer. If the layer is uninitialized, it will
-  /// initiliaze the layer first, and then set the image of it. If the layer has
-  /// been initialized to another kind of layer, the layer will not be changed,
-  /// and <code>PP_ERROR_BADARGUMENT</code> will be returned.
-  ///
-  /// param[in] image_data A <code>PP_Resource</code> corresponding to an image
-  /// data resource.
-  /// param[in] cc A <code>CompletionCallback</code> to be called when
-  /// the image data is released by Chromium compositor.
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t SetImage(const ImageData& image,
-                   const CompletionCallback& callback);
-
-  /// Sets the image of an image layer. If the layer is uninitialized, it will
-  /// initialize the layer first, and then set its image. The layer size will
-  /// be set to the image's size. The source rect will be set to the full image.
-  /// If the layer has been initialized to another kind of layer, the layer will
-  /// not be changed, and <code>PP_ERROR_BADARGUMENT</code> will be returned.
-  ///
-  /// param[in] image_data A <code>ImageData</code> corresponding to an image
-  /// data resource.
-  /// param[in] size A <code>Size</code> for the size of the layer before
-  /// transform.
-  /// param[in] cc A <code>CompletionCallback</code> to be called when the image
-  /// data is released by Chromium compositor.
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t SetImage(const ImageData& image,
-                   const Size& size,
-                   const CompletionCallback& callback);
-
-  /// Sets a clip rectangle for a compositor layer. The Chromium compositor
-  /// applies a transform matrix on the layer first, and then clips the layer
-  /// with the rectangle.
-  ///
-  /// param[in] rect The clip rectangle. The origin is top-left corner of
-  /// the plugin.
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t SetClipRect(const Rect& rect);
-
-  /// Sets a transform matrix which is used to composite the layer.
-  ///
-  /// param[in] matrix A float array with 16 elements. The matrix is coloum
-  /// major. The default transform matrix is an identity matrix.
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t SetTransform(const float matrix[16]);
-
-  /// Sets the opacity value which will be applied to the layer. The effective
-  /// value of each pixel is computed as:
-  ///
-  ///   if (premult_alpha)
-  ///     pixel.rgb = pixel.rgb * opacity;
-  ///   pixel.a = pixel.a * opactiy;
-  ///
-  /// param[in] opacity A <code>float</code> for the opacity value.
-  /// The default value is 1.0f.
-  ///
-  ///@return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t SetOpacity(float opacity);
-
-  /// Sets the blend mode which is used to composite the layer.
-  ///
-  /// param[in] mode A <code>PP_BlendMode</code>. The default value is
-  /// <code>PP_BLENDMODE_SRC_OVER</code>.
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t SetBlendMode(PP_BlendMode mode);
-
-  /// Sets a source rectangle for a texture layer or an image layer.
-  ///
-  /// param[in] rect A <code>FloatRect</code> for an area of the source to
-  /// consider. For a texture layer, rect is in uv coordinates. For an image
-  /// layer, rect is in pixels. If the rect is beyond the dimensions of the
-  /// texture or image, <code>PP_ERROR_BADARGUMENT</code> will be returned.
-  /// If the layer size does not match the source rect size, bilinear scaling
-  /// will be used.
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t SetSourceRect(const FloatRect& rect);
-
-  /// Sets the premultiplied alpha for an texture layer.
-  ///
-  /// param[in] premult A <code>bool</code> with <code>true</code> if
-  /// pre-multiplied alpha is used.
-  ///
-  /// @return An int32_t containing a result code from <code>pp_errors.h</code>.
-  int32_t SetPremultipliedAlpha(bool premult);
-
-  /// Checks whether a <code>Resource</code> is a compositor layer, to test
-  /// whether it is appropriate for use with the <code>CompositorLayer</code>
-  /// constructor.
-  ///
-  /// @param[in] resource A <code>Resource</code> to test.
-  ///
-  /// @return True if <code>resource</code> is a compositor layer.
-  static bool IsCompositorLayer(const Resource& resource);
-};
-
-}  // namespace pp
-
-#endif  // PPAPI_CPP_COMPOSITOR_LAYER_H_
diff --git a/ppapi/cpp/instance.cc b/ppapi/cpp/instance.cc
index 08c81557..ee673e7 100644
--- a/ppapi/cpp/instance.cc
+++ b/ppapi/cpp/instance.cc
@@ -10,7 +10,6 @@
 #include "ppapi/c/ppb_instance.h"
 #include "ppapi/c/ppb_messaging.h"
 #include "ppapi/c/ppp_message_handler.h"
-#include "ppapi/cpp/compositor.h"
 #include "ppapi/cpp/graphics_2d.h"
 #include "ppapi/cpp/graphics_3d.h"
 #include "ppapi/cpp/image_data.h"
@@ -126,13 +125,6 @@
       pp_instance(), graphics.pp_resource()));
 }
 
-bool Instance::BindGraphics(const Compositor& compositor) {
-  if (!has_interface<PPB_Instance_1_0>())
-    return false;
-  return PP_ToBool(get_interface<PPB_Instance_1_0>()->BindGraphics(
-      pp_instance(), compositor.pp_resource()));
-}
-
 bool Instance::IsFullFrame() {
   if (!has_interface<PPB_Instance_1_0>())
     return false;
diff --git a/ppapi/cpp/instance.h b/ppapi/cpp/instance.h
index 1101f51..9418103 100644
--- a/ppapi/cpp/instance.h
+++ b/ppapi/cpp/instance.h
@@ -26,7 +26,6 @@
 /// The C++ interface to the Pepper API.
 namespace pp {
 
-class Compositor;
 class Graphics2D;
 class Graphics3D;
 class InputEvent;
@@ -317,17 +316,6 @@
   /// instance, so the caller can release its reference if it chooses.
   bool BindGraphics(const Graphics3D& graphics);
 
-  /// Binds the given Compositor as the current display surface.
-  /// Refer to <code>BindGraphics(const Graphics2D& graphics)</code> for
-  /// further information.
-  ///
-  /// @param[in] compositor A <code>Compositor</code> to bind.
-  ///
-  /// @return true if bind was successful or false if the device was not the
-  /// correct type. On success, a reference to the device will be held by the
-  /// instance, so the caller can release its reference if it chooses.
-  bool BindGraphics(const Compositor& compositor);
-
   /// IsFullFrame() determines if the instance is full-frame (repr).
   /// Such an instance represents the entire document in a frame rather than an
   /// embedded resource. This can happen if the user does a top-level
diff --git a/ppapi/examples/BUILD.gn b/ppapi/examples/BUILD.gn
index b80d5f93..0b938dd 100644
--- a/ppapi/examples/BUILD.gn
+++ b/ppapi/examples/BUILD.gn
@@ -9,7 +9,6 @@
     "//ppapi/examples/audio",
     "//ppapi/examples/audio_encode",
     "//ppapi/examples/audio_input",
-    "//ppapi/examples/compositor",
     "//ppapi/examples/crxfs",
     "//ppapi/examples/enumerate_devices",
     "//ppapi/examples/file_chooser",
diff --git a/ppapi/examples/compositor/BUILD.gn b/ppapi/examples/compositor/BUILD.gn
deleted file mode 100644
index d7007a1..0000000
--- a/ppapi/examples/compositor/BUILD.gn
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//ppapi/examples/ppapi_example.gni")
-
-ppapi_example("compositor") {
-  output_name = "ppapi_example_compositor"
-  sources = [
-    "compositor.cc",
-    "spinning_cube.cc",
-    "spinning_cube.h",
-  ]
-  deps = [
-    "//ppapi/cpp",
-    "//ppapi/lib/gl/gles2",
-  ]
-}
diff --git a/ppapi/examples/compositor/compositor.cc b/ppapi/examples/compositor/compositor.cc
deleted file mode 100644
index df315e8..0000000
--- a/ppapi/examples/compositor/compositor.cc
+++ /dev/null
@@ -1,453 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Needed on Windows to get |M_PI| from math.h.
-#ifdef _WIN32
-#define _USE_MATH_DEFINES
-#endif
-
-#include <math.h>
-
-#include <vector>
-
-#include "ppapi/c/pp_errors.h"
-#include "ppapi/c/pp_input_event.h"
-#include "ppapi/cpp/compositor.h"
-#include "ppapi/cpp/compositor_layer.h"
-#include "ppapi/cpp/graphics_3d.h"
-#include "ppapi/cpp/graphics_3d_client.h"
-#include "ppapi/cpp/image_data.h"
-#include "ppapi/cpp/input_event.h"
-#include "ppapi/cpp/instance.h"
-#include "ppapi/cpp/module.h"
-#include "ppapi/cpp/rect.h"
-#include "ppapi/cpp/var_dictionary.h"
-#include "ppapi/examples/compositor/spinning_cube.h"
-#include "ppapi/lib/gl/gles2/gl2ext_ppapi.h"
-#include "ppapi/lib/gl/include/GLES2/gl2.h"
-#include "ppapi/lib/gl/include/GLES2/gl2ext.h"
-#include "ppapi/utility/completion_callback_factory.h"
-
-// Use assert as a makeshift CHECK, even in non-debug mode.
-// Since <assert.h> redefines assert on every inclusion (it doesn't use
-// include-guards), make sure this is the last file #include'd in this file.
-#undef NDEBUG
-#include <assert.h>
-#include <stddef.h>
-#include <stdint.h>
-
-// When compiling natively on Windows, PostMessage can be #define-d to
-// something else.
-#ifdef PostMessage
-#undef PostMessage
-#endif
-
-// Assert |context_| isn't holding any GL Errors.  Done as a macro instead of a
-// function to preserve line number information in the failure message.
-#define AssertNoGLError() \
-  PP_DCHECK(!glGetError());
-
-namespace {
-
-const int32_t kTextureWidth = 800;
-const int32_t kTextureHeight = 800;
-const int32_t kImageWidth = 256;
-const int32_t kImageHeight = 256;
-
-class DemoInstance : public pp::Instance, public pp::Graphics3DClient {
- public:
-  DemoInstance(PP_Instance instance);
-  virtual ~DemoInstance();
-
-  // pp::Instance implementation (see PPP_Instance).
-  virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
-  virtual void DidChangeView(const pp::Rect& position,
-                             const pp::Rect& clip);
-  virtual bool HandleInputEvent(const pp::InputEvent& event);
-
-  // pp::Graphics3DClient implementation.
-  virtual void Graphics3DContextLost();
-
- private:
-  // GL-related functions.
-  void InitGL(int32_t result);
-  GLuint PrepareFramebuffer();
-  pp::ImageData PrepareImage();
-  void PrepareLayers(int32_t frame);
-  void Paint(int32_t result, int32_t frame);
-  void OnTextureReleased(int32_t result, GLuint texture);
-  void OnImageReleased(int32_t result, const pp::ImageData& image);
-
-  pp::CompletionCallbackFactory<DemoInstance> callback_factory_;
-
-  // Owned data.
-  pp::Graphics3D* context_;
-
-  GLuint fbo_;
-  GLuint rbo_;
-
-  std::vector<GLuint> textures_;
-  std::vector<pp::ImageData> images_;
-
-  pp::Compositor compositor_;
-  pp::CompositorLayer color_layer_;
-  pp::CompositorLayer stable_texture_layer_;
-  pp::CompositorLayer texture_layer_;
-  pp::CompositorLayer image_layer_;
-
-  bool rebuild_layers_;
-  int32_t total_resource_;
-
-  SpinningCube* cube_;
-};
-
-DemoInstance::DemoInstance(PP_Instance instance)
-    : pp::Instance(instance),
-      pp::Graphics3DClient(this),
-      callback_factory_(this),
-      context_(NULL),
-      fbo_(0),
-      rbo_(0),
-      rebuild_layers_(true),
-      total_resource_(0),
-      cube_(new SpinningCube()) {
-  RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE);
-}
-
-DemoInstance::~DemoInstance() {
-  delete cube_;
-  assert(glTerminatePPAPI());
-  delete context_;
-}
-
-bool DemoInstance::Init(uint32_t /*argc*/,
-                        const char* /*argn*/[],
-                        const char* /*argv*/[]) {
-  return !!glInitializePPAPI(pp::Module::Get()->get_browser_interface());
-}
-
-void DemoInstance::DidChangeView(
-    const pp::Rect& position, const pp::Rect& /*clip*/) {
-  if (position.width() == 0 || position.height() == 0)
-    return;
-  // Initialize graphics.
-  InitGL(0);
-}
-
-bool DemoInstance::HandleInputEvent(const pp::InputEvent& event) {
-  switch (event.GetType()) {
-    case PP_INPUTEVENT_TYPE_MOUSEDOWN:
-      rebuild_layers_ = true;
-      return true;
-    default:
-      break;
-  }
-  return false;
-}
-
-void DemoInstance::Graphics3DContextLost() {
-  fbo_ = 0;
-  rbo_ = 0;
-  rebuild_layers_ = true;
-  total_resource_ -= static_cast<int32_t>(textures_.size());
-  textures_.clear();
-  delete context_;
-  context_ = NULL;
-  cube_->OnGLContextLost();
-  pp::CompletionCallback cb = callback_factory_.NewCallback(
-      &DemoInstance::InitGL);
-  pp::Module::Get()->core()->CallOnMainThread(0, cb, 0);
-}
-
-void DemoInstance::InitGL(int32_t /*result*/) {
-  if (context_)
-    return;
-  int32_t context_attributes[] = {
-    PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
-    PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8,
-    PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8,
-    PP_GRAPHICS3DATTRIB_RED_SIZE, 8,
-    PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0,
-    PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
-    PP_GRAPHICS3DATTRIB_SAMPLES, 0,
-    PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
-    PP_GRAPHICS3DATTRIB_WIDTH, 32,
-    PP_GRAPHICS3DATTRIB_HEIGHT, 32,
-    PP_GRAPHICS3DATTRIB_NONE,
-  };
-  context_ = new pp::Graphics3D(this, context_attributes);
-  assert(!context_->is_null());
-  assert(BindGraphics(compositor_));
-
-  glSetCurrentContextPPAPI(context_->pp_resource());
-
-  cube_->Init(kTextureWidth, kTextureHeight);
-
-  Paint(PP_OK, 0);
-}
-
-GLuint DemoInstance::PrepareFramebuffer() {
-  GLuint texture = 0;
-  if (textures_.empty()) {
-    total_resource_++;
-    // Create a texture object
-    glGenTextures(1, &texture);
-    glBindTexture(GL_TEXTURE_2D, texture);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureWidth, kTextureHeight, 0,
-                 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    glBindTexture(GL_TEXTURE_2D, 0);
-  } else {
-    texture = textures_.back();
-    textures_.pop_back();
-  }
-
-  if (!rbo_) {
-    // create a renderbuffer object to store depth info
-    glGenRenderbuffers(1, &rbo_);
-    glBindRenderbuffer(GL_RENDERBUFFER, rbo_);
-    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
-        kTextureWidth, kTextureHeight);
-    glBindRenderbuffer(GL_RENDERBUFFER, 0);
-  }
-
-  if (!fbo_) {
-    // create a framebuffer object
-    glGenFramebuffers(1, &fbo_);
-  }
-
-  glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
-
-  // attach the texture to FBO color attachment point
-  glFramebufferTexture2D(GL_FRAMEBUFFER,
-                         GL_COLOR_ATTACHMENT0,
-                         GL_TEXTURE_2D,
-                         texture,
-                         0);
-
-  // attach the renderbuffer to depth attachment point
-  glFramebufferRenderbuffer(GL_FRAMEBUFFER,
-                            GL_DEPTH_ATTACHMENT,
-                            GL_RENDERBUFFER,
-                            rbo_);
-
-  // check FBO status
-  GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-  assert(status == GL_FRAMEBUFFER_COMPLETE);
-
-  AssertNoGLError();
-  return texture;
-}
-
-pp::ImageData DemoInstance::PrepareImage() {
-  if (images_.empty()) {
-    total_resource_++;
-    return pp::ImageData(this,
-                         PP_IMAGEDATAFORMAT_RGBA_PREMUL,
-                         pp::Size(kImageWidth, kImageHeight),
-                         false);
-  }
-  pp::ImageData image = images_.back();
-  images_.pop_back();
-  return image;
-}
-
-void DemoInstance::Paint(int32_t result, int32_t frame) {
-  assert(result == PP_OK);
-  if (result != PP_OK || !context_)
-    return;
-
-  if (rebuild_layers_) {
-    compositor_ = pp::Compositor(this);
-    assert(BindGraphics(compositor_));
-    color_layer_ = pp::CompositorLayer();
-    stable_texture_layer_ = pp::CompositorLayer();
-    texture_layer_ = pp::CompositorLayer();
-    image_layer_ = pp::CompositorLayer();
-    frame = 0;
-    rebuild_layers_ = false;
-  }
-
-  PrepareLayers(frame);
-
-  int32_t rv = compositor_.CommitLayers(
-      callback_factory_.NewCallback(&DemoInstance::Paint, ++frame));
-  assert(rv == PP_OK_COMPLETIONPENDING);
-
-  pp::VarDictionary dict;
-  dict.Set("total_resource", total_resource_);
-  size_t free_resource = textures_.size() + images_.size();
-  dict.Set("free_resource", static_cast<int32_t>(free_resource));
-  PostMessage(dict);
-}
-
-void DemoInstance::PrepareLayers(int32_t frame) {
-  int32_t rv;
-  float factor_sin = sin(static_cast<float>(M_PI) / 180 * frame);
-  float factor_cos = cos(static_cast<float>(M_PI) / 180 * frame);
-  {
-    // Set the background color layer.
-    if (color_layer_.is_null()) {
-      color_layer_ = compositor_.AddLayer();
-      assert(!color_layer_.is_null());
-      static const float transform[16] = {
-        1.0f, 0.0f, 0.0f, 0.0f,
-        0.0f, 1.0f, 0.0f, 0.0f,
-        0.0f, 0.0f, 1.0f, 0.0f,
-        0.0f, 0.0f, 0.0f, 1.0f,
-      };
-      rv = color_layer_.SetTransform(transform);
-      assert(rv == PP_OK);
-    }
-    rv = color_layer_.SetColor(fabs(factor_sin),
-                               fabs(factor_cos),
-                               fabs(factor_sin * factor_cos),
-                               1.0f,
-                               pp::Size(800, 600));
-    assert(rv == PP_OK);
-  }
-
-  {
-    // Set the image layer
-    if (image_layer_.is_null()) {
-      image_layer_ = compositor_.AddLayer();
-      assert(!image_layer_.is_null());
-    }
-    float x = static_cast<float>(frame % 800);
-    float y = 200 - 200 * factor_sin;
-    const float transform[16] = {
-      fabsf(factor_sin) + 0.2f, 0.0f, 0.0f, 0.0f,
-      0.0f, fabsf(factor_sin) + 0.2f, 0.0f, 0.0f,
-      0.0f, 0.0f, 1.0f, 0.0f,
-         x,    y, 0.0f, 1.0f,
-    };
-    rv = image_layer_.SetTransform(transform);
-    assert(rv == PP_OK);
-
-    pp::ImageData image = PrepareImage();
-    uint8_t *p = static_cast<uint8_t*>(image.data());
-    for (int x = 0; x < kImageWidth; ++x) {
-      for (int y = 0; y < kImageHeight; ++y) {
-        *(p++) = static_cast<uint8_t>(frame);
-        *(p++) = static_cast<uint8_t>(frame * x);
-        *(p++) = static_cast<uint8_t>(frame * y);
-        *(p++) = 255;
-      }
-    }
-    rv = image_layer_.SetImage(image, pp::Size(kImageWidth, kImageHeight),
-        callback_factory_.NewCallback(&DemoInstance::OnImageReleased, image));
-    assert(rv == PP_OK_COMPLETIONPENDING);
-  }
-
-  {
-    // Set the stable texture layer
-    if (stable_texture_layer_.is_null()) {
-      stable_texture_layer_ = compositor_.AddLayer();
-      assert(!stable_texture_layer_.is_null());
-      GLuint texture = PrepareFramebuffer();
-      cube_->UpdateForTimeDelta(0.02f);
-      cube_->Draw();
-      rv = stable_texture_layer_.SetTexture(
-          *context_,
-          GL_TEXTURE_2D,
-          texture,
-          pp::Size(600, 600),
-          callback_factory_.NewCallback(&DemoInstance::OnTextureReleased,
-                                        texture));
-      assert(rv == PP_OK_COMPLETIONPENDING);
-      rv = stable_texture_layer_.SetPremultipliedAlpha(PP_FALSE);
-      assert(rv == PP_OK);
-    }
-
-    int32_t delta = static_cast<int32_t>(200 * fabsf(factor_sin));
-    if (delta != 0) {
-      int32_t x_y = 25 + delta;
-      int32_t w_h =  650 - delta - delta;
-      rv = stable_texture_layer_.SetClipRect(pp::Rect(x_y, x_y, w_h, w_h));
-    } else {
-      rv = stable_texture_layer_.SetClipRect(pp::Rect());
-    }
-    assert(rv == PP_OK);
-
-    const float transform[16] = {
-      factor_cos, -factor_sin, 0.0f, 0.0f,
-      factor_sin, factor_cos, 0.0f, 0.0f,
-      0.0f, 0.0f, 1.0f, 0.0f,
-      50.0f, 50.0f, 0.0f, 1.0f,
-    };
-    rv = stable_texture_layer_.SetTransform(transform);
-    assert(rv == PP_OK);
-  }
-
-  {
-    // Set the dynamic texture layer.
-    if (texture_layer_.is_null()) {
-      texture_layer_ = compositor_.AddLayer();
-      assert(!texture_layer_.is_null());
-      static const float transform[16] = {
-        1.0f, 0.0f, 0.0f, 0.0f,
-        0.0f, 1.0f, 0.0f, 0.0f,
-        0.0f, 0.0f, 1.0f, 0.0f,
-        200.0f, 0.0f, 0.0f, 1.0f,
-      };
-      rv = texture_layer_.SetTransform(transform);
-      assert(rv == PP_OK);
-     }
-
-    GLuint texture = PrepareFramebuffer();
-    cube_->UpdateForTimeDelta(0.02f);
-    cube_->Draw();
-    rv = texture_layer_.SetTexture(
-        *context_,
-        GL_TEXTURE_2D,
-        texture,
-        pp::Size(400, 400),
-        callback_factory_.NewCallback(&DemoInstance::OnTextureReleased,
-                                      texture));
-    assert(rv == PP_OK_COMPLETIONPENDING);
-    rv = texture_layer_.SetPremultipliedAlpha(PP_FALSE);
-    assert(rv == PP_OK);
-  }
-}
-
-void DemoInstance::OnTextureReleased(int32_t result, GLuint texture) {
-  if (result == PP_OK) {
-    textures_.push_back(texture);
-  } else {
-    glDeleteTextures(1, &texture);
-    total_resource_--;
-  }
-}
-
-void DemoInstance::OnImageReleased(int32_t result, const pp::ImageData& image) {
-  if (result == PP_OK) {
-    images_.push_back(image);
-  } else {
-    total_resource_--;
-  }
-}
-
-// This object is the global object representing this plugin library as long
-// as it is loaded.
-class DemoModule : public pp::Module {
- public:
-  DemoModule() : Module() {}
-  virtual ~DemoModule() {}
-
-  virtual pp::Instance* CreateInstance(PP_Instance instance) {
-    return new DemoInstance(instance);
-  }
-};
-
-}  // anonymous namespace
-
-namespace pp {
-// Factory function for your specialization of the Module object.
-Module* CreateModule() {
-  return new DemoModule();
-}
-}  // namespace pp
diff --git a/ppapi/examples/compositor/compositor.html b/ppapi/examples/compositor/compositor.html
deleted file mode 100644
index a0dcfbef..0000000
--- a/ppapi/examples/compositor/compositor.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <!--
-  Copyright 2014 The Chromium Authors. All rights reserved.
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
-  -->
-<head>
-  <title>GLES2 Spinning Cube Example</title>
-</head>
-
-<body>
-
-<embed id="plugin" type="application/x-ppapi-example-compositor"
-  width="800" height="600"/> <br/>
-  Total resource: <input id="total" > <br/>
-  Free resource: <input id="free" > <br/>
-<script type="text/javascript">
-  var plugin = document.getElementById('plugin');
-  var total = document.getElementById('total');
-  var free = document.getElementById('free');
-  plugin.addEventListener('message', function(message) {
-    total.value = message.data.total_resource;
-    free.value = message.data.free_resource;
-  }, false);
-</script>
-</body>
-</html>
diff --git a/ppapi/examples/compositor/spinning_cube.cc b/ppapi/examples/compositor/spinning_cube.cc
deleted file mode 100644
index 2e76699..0000000
--- a/ppapi/examples/compositor/spinning_cube.cc
+++ /dev/null
@@ -1,459 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This example program is based on Simple_VertexShader.c from:
-
-//
-// Book:      OpenGL(R) ES 2.0 Programming Guide
-// Authors:   Aaftab Munshi, Dan Ginsburg, Dave Shreiner
-// ISBN-10:   0321502795
-// ISBN-13:   9780321502797
-// Publisher: Addison-Wesley Professional
-// URLs:      http://safari.informit.com/9780321563835
-//            http://www.opengles-book.com
-//
-
-#include "ppapi/examples/compositor/spinning_cube.h"
-
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <algorithm>
-
-#include "ppapi/lib/gl/include/GLES2/gl2.h"
-
-namespace {
-
-const float kPi = 3.14159265359f;
-
-int GenerateCube(GLuint *vbo_vertices,
-                 GLuint *vbo_indices) {
-  const int num_indices = 36;
-
-  const GLfloat cube_vertices[] = {
-    -0.5f, -0.5f, -0.5f,
-     0.5f, -0.5f, -0.5f,
-     0.5f, -0.5f,  0.5f,
-    -0.5f, -0.5f,  0.5f,
-    -0.5f,  0.5f, -0.5f,
-     0.5f,  0.5f, -0.5f,
-     0.5f,  0.5f,  0.5f,
-    -0.5f,  0.5f,  0.5f,
-  };
-
-  const GLushort cube_indices[] = {
-    0, 2, 1,
-    0, 3, 2,
-    4, 5, 6,
-    4, 6, 7,
-    3, 6, 2,
-    3, 7, 6,
-    0, 1, 5,
-    0, 5, 4,
-    0, 7, 3,
-    0, 4, 7,
-    1, 2, 6,
-    1, 6, 5,
-  };
-
-  if (vbo_vertices) {
-    glGenBuffers(1, vbo_vertices);
-    glBindBuffer(GL_ARRAY_BUFFER, *vbo_vertices);
-    glBufferData(GL_ARRAY_BUFFER,
-                 sizeof(cube_vertices),
-                 cube_vertices,
-                 GL_STATIC_DRAW);
-    glBindBuffer(GL_ARRAY_BUFFER, 0);
-  }
-
-  if (vbo_indices) {
-    glGenBuffers(1, vbo_indices);
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *vbo_indices);
-    glBufferData(GL_ELEMENT_ARRAY_BUFFER,
-                 sizeof(cube_indices),
-                 cube_indices,
-                 GL_STATIC_DRAW);
-    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-  }
-
-  return num_indices;
-}
-
-GLuint LoadShader(GLenum type,
-                  const char* shader_source) {
-  GLuint shader = glCreateShader(type);
-  glShaderSource(shader, 1, &shader_source, NULL);
-  glCompileShader(shader);
-
-  GLint compiled = 0;
-  glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
-
-  if (!compiled) {
-    glDeleteShader(shader);
-    return 0;
-  }
-
-  return shader;
-}
-
-GLuint LoadProgram(const char* vertext_shader_source,
-                   const char* fragment_shader_source) {
-  GLuint vertex_shader = LoadShader(GL_VERTEX_SHADER,
-                                    vertext_shader_source);
-  if (!vertex_shader)
-    return 0;
-
-  GLuint fragment_shader = LoadShader(GL_FRAGMENT_SHADER,
-                                      fragment_shader_source);
-  if (!fragment_shader) {
-    glDeleteShader(vertex_shader);
-    return 0;
-  }
-
-  GLuint program_object = glCreateProgram();
-  glAttachShader(program_object, vertex_shader);
-  glAttachShader(program_object, fragment_shader);
-
-  glLinkProgram(program_object);
-
-  glDeleteShader(vertex_shader);
-  glDeleteShader(fragment_shader);
-
-  GLint linked = 0;
-  glGetProgramiv(program_object, GL_LINK_STATUS, &linked);
-
-  if (!linked) {
-    glDeleteProgram(program_object);
-    return 0;
-  }
-
-  return program_object;
-}
-
-class ESMatrix {
- public:
-  GLfloat m[4][4];
-
-  ESMatrix() {
-    LoadZero();
-  }
-
-  void LoadZero() {
-    memset(this, 0x0, sizeof(ESMatrix));
-  }
-
-  void LoadIdentity() {
-    LoadZero();
-    m[0][0] = 1.0f;
-    m[1][1] = 1.0f;
-    m[2][2] = 1.0f;
-    m[3][3] = 1.0f;
-  }
-
-  void Multiply(ESMatrix* a, ESMatrix* b) {
-    ESMatrix result;
-    for (int i = 0; i < 4; ++i) {
-      result.m[i][0] = (a->m[i][0] * b->m[0][0]) +
-                       (a->m[i][1] * b->m[1][0]) +
-                       (a->m[i][2] * b->m[2][0]) +
-                       (a->m[i][3] * b->m[3][0]);
-
-      result.m[i][1] = (a->m[i][0] * b->m[0][1]) +
-                       (a->m[i][1] * b->m[1][1]) +
-                       (a->m[i][2] * b->m[2][1]) +
-                       (a->m[i][3] * b->m[3][1]);
-
-      result.m[i][2] = (a->m[i][0] * b->m[0][2]) +
-                       (a->m[i][1] * b->m[1][2]) +
-                       (a->m[i][2] * b->m[2][2]) +
-                       (a->m[i][3] * b->m[3][2]);
-
-      result.m[i][3] = (a->m[i][0] * b->m[0][3]) +
-                       (a->m[i][1] * b->m[1][3]) +
-                       (a->m[i][2] * b->m[2][3]) +
-                       (a->m[i][3] * b->m[3][3]);
-    }
-    *this = result;
-  }
-
-  void Frustum(float left,
-               float right,
-               float bottom,
-               float top,
-               float near_z,
-               float far_z) {
-    float delta_x = right - left;
-    float delta_y = top - bottom;
-    float delta_z = far_z - near_z;
-
-    if ((near_z <= 0.0f) ||
-        (far_z <= 0.0f) ||
-        (delta_z <= 0.0f) ||
-        (delta_y <= 0.0f) ||
-        (delta_y <= 0.0f))
-      return;
-
-    ESMatrix frust;
-    frust.m[0][0] = 2.0f * near_z / delta_x;
-    frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
-
-    frust.m[1][1] = 2.0f * near_z / delta_y;
-    frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
-
-    frust.m[2][0] = (right + left) / delta_x;
-    frust.m[2][1] = (top + bottom) / delta_y;
-    frust.m[2][2] = -(near_z + far_z) / delta_z;
-    frust.m[2][3] = -1.0f;
-
-    frust.m[3][2] = -2.0f * near_z * far_z / delta_z;
-    frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
-
-    Multiply(&frust, this);
-  }
-
-  void Perspective(float fov_y, float aspect, float near_z, float far_z) {
-    GLfloat frustum_h = tanf(fov_y / 360.0f * kPi) * near_z;
-    GLfloat frustum_w = frustum_h * aspect;
-    Frustum(-frustum_w, frustum_w, -frustum_h, frustum_h, near_z, far_z);
-  }
-
-  void Translate(GLfloat tx, GLfloat ty, GLfloat tz) {
-    m[3][0] += m[0][0] * tx + m[1][0] * ty + m[2][0] * tz;
-    m[3][1] += m[0][1] * tx + m[1][1] * ty + m[2][1] * tz;
-    m[3][2] += m[0][2] * tx + m[1][2] * ty + m[2][2] * tz;
-    m[3][3] += m[0][3] * tx + m[1][3] * ty + m[2][3] * tz;
-  }
-
-  void Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) {
-    GLfloat mag = sqrtf(x * x + y * y + z * z);
-
-    GLfloat sin_angle = sinf(angle * kPi / 180.0f);
-    GLfloat cos_angle = cosf(angle * kPi / 180.0f);
-    if (mag > 0.0f) {
-      GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs;
-      GLfloat one_minus_cos;
-      ESMatrix rotation;
-
-      x /= mag;
-      y /= mag;
-      z /= mag;
-
-      xx = x * x;
-      yy = y * y;
-      zz = z * z;
-      xy = x * y;
-      yz = y * z;
-      zx = z * x;
-      xs = x * sin_angle;
-      ys = y * sin_angle;
-      zs = z * sin_angle;
-      one_minus_cos = 1.0f - cos_angle;
-
-      rotation.m[0][0] = (one_minus_cos * xx) + cos_angle;
-      rotation.m[0][1] = (one_minus_cos * xy) - zs;
-      rotation.m[0][2] = (one_minus_cos * zx) + ys;
-      rotation.m[0][3] = 0.0F;
-
-      rotation.m[1][0] = (one_minus_cos * xy) + zs;
-      rotation.m[1][1] = (one_minus_cos * yy) + cos_angle;
-      rotation.m[1][2] = (one_minus_cos * yz) - xs;
-      rotation.m[1][3] = 0.0F;
-
-      rotation.m[2][0] = (one_minus_cos * zx) - ys;
-      rotation.m[2][1] = (one_minus_cos * yz) + xs;
-      rotation.m[2][2] = (one_minus_cos * zz) + cos_angle;
-      rotation.m[2][3] = 0.0F;
-
-      rotation.m[3][0] = 0.0F;
-      rotation.m[3][1] = 0.0F;
-      rotation.m[3][2] = 0.0F;
-      rotation.m[3][3] = 1.0F;
-
-      Multiply(&rotation, this);
-    }
-  }
-};
-
-float RotationForTimeDelta(float delta_time) {
-  return delta_time * 40.0f;
-}
-
-float RotationForDragDistance(float drag_distance) {
-  return drag_distance / 5; // Arbitrary damping.
-}
-
-}  // namespace
-
-class SpinningCube::GLState {
- public:
-  GLState();
-
-  void OnGLContextLost();
-
-  GLfloat angle_;  // Survives losing the GL context.
-
-  GLuint program_object_;
-  GLint position_location_;
-  GLint mvp_location_;
-  GLuint vbo_vertices_;
-  GLuint vbo_indices_;
-  int num_indices_;
-  ESMatrix mvp_matrix_;
-};
-
-SpinningCube::GLState::GLState()
-    : angle_(0) {
-  OnGLContextLost();
-}
-
-void SpinningCube::GLState::OnGLContextLost() {
-  program_object_ = 0;
-  position_location_ = 0;
-  mvp_location_ = 0;
-  vbo_vertices_ = 0;
-  vbo_indices_ = 0;
-  num_indices_ = 0;
-}
-
-SpinningCube::SpinningCube()
-    : initialized_(false),
-      width_(0),
-      height_(0),
-      state_(new GLState()),
-      fling_multiplier_(1.0f),
-      direction_(1) {
-  state_->angle_ = 45.0f;
-}
-
-SpinningCube::~SpinningCube() {
-  if (!initialized_)
-    return;
-  if (state_->vbo_vertices_)
-    glDeleteBuffers(1, &state_->vbo_vertices_);
-  if (state_->vbo_indices_)
-    glDeleteBuffers(1, &state_->vbo_indices_);
-  if (state_->program_object_)
-    glDeleteProgram(state_->program_object_);
-
-  delete state_;
-}
-
-void SpinningCube::Init(uint32_t width, uint32_t height) {
-  width_ = width;
-  height_ = height;
-
-  if (!initialized_) {
-    initialized_ = true;
-    const char vertext_shader_source[] =
-        "uniform mat4 u_mvpMatrix;                   \n"
-        "attribute vec4 a_position;                  \n"
-        "varying vec4 v_color;                       \n"
-        "void main()                                 \n"
-        "{                                           \n"
-        "   gl_Position = u_mvpMatrix * a_position;  \n"
-        "   v_color = vec4(a_position.x + 0.5,       \n"
-        "                  a_position.y + 0.5,       \n"
-        "                  a_position.z + 0.5,       \n"
-        "                  0.8);                     \n"
-        "}                                           \n";
-
-    const char fragment_shader_source[] =
-        "precision mediump float;                    \n"
-        "varying vec4 v_color;                       \n"
-        "void main()                                 \n"
-        "{                                           \n"
-        "  gl_FragColor = v_color;                   \n"
-        "}                                           \n";
-
-    state_->program_object_ = LoadProgram(
-        vertext_shader_source, fragment_shader_source);
-    state_->position_location_ = glGetAttribLocation(
-        state_->program_object_, "a_position");
-    state_->mvp_location_ = glGetUniformLocation(
-        state_->program_object_, "u_mvpMatrix");
-    state_->num_indices_ = GenerateCube(&state_->vbo_vertices_,
-                                        &state_->vbo_indices_);
-
-    glClearColor(0.0f, 0.0f, 0.0f, 0.2f);
-  }
-}
-
-void SpinningCube::OnGLContextLost() {
-  // TODO(yzshen): Is it correct that in this case we don't need to do cleanup
-  // for program and buffers?
-  initialized_ = false;
-  height_ = 0;
-  width_ = 0;
-  state_->OnGLContextLost();
-}
-
-void SpinningCube::SetFlingMultiplier(float drag_distance,
-                                      float drag_time) {
-  fling_multiplier_ = RotationForDragDistance(drag_distance) /
-      RotationForTimeDelta(drag_time);
-
-}
-
-void SpinningCube::UpdateForTimeDelta(float delta_time) {
-  state_->angle_ += RotationForTimeDelta(delta_time) * fling_multiplier_;
-  if (state_->angle_ >= 360.0f)
-    state_->angle_ -= 360.0f;
-
-  // Arbitrary 50-step linear reduction in spin speed.
-  if (fling_multiplier_ > 1.0f) {
-    fling_multiplier_ =
-        std::max(1.0f, fling_multiplier_ - (fling_multiplier_ - 1.0f) / 50);
-  }
-
-  Update();
-}
-
-void SpinningCube::UpdateForDragDistance(float distance) {
-  state_->angle_ += RotationForDragDistance(distance);
-  if (state_->angle_ >= 360.0f )
-    state_->angle_ -= 360.0f;
-
-  Update();
-}
-
-void SpinningCube::Draw() {
-  glViewport(0, 0, width_, height_);
-  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-  glEnable(GL_DEPTH_TEST);
-  glUseProgram(state_->program_object_);
-
-  glBindBuffer(GL_ARRAY_BUFFER, state_->vbo_vertices_);
-  glVertexAttribPointer(state_->position_location_,
-                        3,
-                        GL_FLOAT,
-                        GL_FALSE, 3 * sizeof(GLfloat),
-                        0);
-  glEnableVertexAttribArray(state_->position_location_);
-
-  glUniformMatrix4fv(state_->mvp_location_,
-                     1,
-                     GL_FALSE,
-                     (GLfloat*) &state_->mvp_matrix_.m[0][0]);
-  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state_->vbo_indices_);
-  glDrawElements(GL_TRIANGLES,
-                 state_->num_indices_,
-                 GL_UNSIGNED_SHORT,
-                 0);
-}
-
-void SpinningCube::Update() {
-  float aspect = static_cast<GLfloat>(width_) / static_cast<GLfloat>(height_);
-
-  ESMatrix perspective;
-  perspective.LoadIdentity();
-  perspective.Perspective(60.0f, aspect, 1.0f, 20.0f );
-
-  ESMatrix modelview;
-  modelview.LoadIdentity();
-  modelview.Translate(0.0, 0.0, -2.0);
-  modelview.Rotate(state_->angle_ * direction_, 1.0, 0.0, 1.0);
-
-  state_->mvp_matrix_.Multiply(&modelview, &perspective);
-}
diff --git a/ppapi/examples/compositor/spinning_cube.h b/ppapi/examples/compositor/spinning_cube.h
deleted file mode 100644
index 8472606..0000000
--- a/ppapi/examples/compositor/spinning_cube.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PPAPI_EXAMPLES_COMPOSITOR_SPINNING_CUBE_H_
-#define PPAPI_EXAMPLES_COMPOSITOR_SPINNING_CUBE_H_
-
-#include "ppapi/c/pp_stdint.h"
-
-class SpinningCube {
- public:
-  SpinningCube();
-  ~SpinningCube();
-
-  void Init(uint32_t width, uint32_t height);
-  void set_direction(int direction) { direction_ = direction; }
-  void SetFlingMultiplier(float drag_distance, float drag_time);
-  void UpdateForTimeDelta(float delta_time);
-  void UpdateForDragDistance(float distance);
-  void Draw();
-
-  void OnGLContextLost();
-
- private:
-  class GLState;
-
-  // Disallow copy and assign.
-  SpinningCube(const SpinningCube& other);
-  SpinningCube& operator=(const SpinningCube& other);
-
-  void Update();
-
-  bool initialized_;
-  uint32_t width_;
-  uint32_t height_;
-  // Owned ptr.
-  GLState* state_;
-  float fling_multiplier_;
-  int direction_;
-};
-
-#endif  // PPAPI_EXAMPLES_COMPOSITOR_SPINNING_CUBE_H_
diff --git a/ppapi/host/resource_host.cc b/ppapi/host/resource_host.cc
index 260e855..af161631 100644
--- a/ppapi/host/resource_host.cc
+++ b/ppapi/host/resource_host.cc
@@ -50,10 +50,6 @@
   host_->SendReply(context, msg);
 }
 
-bool ResourceHost::IsCompositorHost() {
-  return false;
-}
-
 bool ResourceHost::IsFileRefHost() {
   return false;
 }
diff --git a/ppapi/host/resource_host.h b/ppapi/host/resource_host.h
index 8c1b0571..1189d33a 100644
--- a/ppapi/host/resource_host.h
+++ b/ppapi/host/resource_host.h
@@ -56,7 +56,6 @@
 
   // Simple RTTI. A subclass that is a host for one of these APIs will override
   // the appropriate function and return true.
-  virtual bool IsCompositorHost();
   virtual bool IsFileRefHost();
   virtual bool IsFileSystemHost();
   virtual bool IsGraphics2DHost();
diff --git a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c
index 41cb623..5a350c2 100644
--- a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c
+++ b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018 The Chromium Authors. All rights reserved.
+/* Copyright (c) 2019 The Chromium Authors. All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
@@ -18,8 +18,6 @@
 #include "ppapi/c/dev/ppb_video_capture_dev.h"
 #include "ppapi/c/dev/ppb_video_decoder_dev.h"
 #include "ppapi/c/ppb_audio_encoder.h"
-#include "ppapi/c/ppb_compositor.h"
-#include "ppapi/c/ppb_compositor_layer.h"
 #include "ppapi/c/ppb_console.h"
 #include "ppapi/c/ppb_core.h"
 #include "ppapi/c/ppb_file_io.h"
@@ -89,9 +87,6 @@
 /* BEGIN Declarations for all Wrapper Infos */
 
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_AudioEncoder_0_1;
-static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Compositor_0_1;
-static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_CompositorLayer_0_1;
-static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_CompositorLayer_0_2;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Console_1_0;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Core_1_0;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_FileIO_1_0;
@@ -266,143 +261,6 @@
 
 /* End wrapper methods for PPB_AudioEncoder_0_1 */
 
-/* Begin wrapper methods for PPB_Compositor_0_1 */
-
-static PP_Bool Pnacl_M37_PPB_Compositor_IsCompositor(PP_Resource resource) {
-  const struct PPB_Compositor_0_1 *iface = Pnacl_WrapperInfo_PPB_Compositor_0_1.real_iface;
-  return iface->IsCompositor(resource);
-}
-
-static PP_Resource Pnacl_M37_PPB_Compositor_Create(PP_Instance instance) {
-  const struct PPB_Compositor_0_1 *iface = Pnacl_WrapperInfo_PPB_Compositor_0_1.real_iface;
-  return iface->Create(instance);
-}
-
-static PP_Resource Pnacl_M37_PPB_Compositor_AddLayer(PP_Resource compositor) {
-  const struct PPB_Compositor_0_1 *iface = Pnacl_WrapperInfo_PPB_Compositor_0_1.real_iface;
-  return iface->AddLayer(compositor);
-}
-
-static int32_t Pnacl_M37_PPB_Compositor_CommitLayers(PP_Resource compositor, struct PP_CompletionCallback* cc) {
-  const struct PPB_Compositor_0_1 *iface = Pnacl_WrapperInfo_PPB_Compositor_0_1.real_iface;
-  return iface->CommitLayers(compositor, *cc);
-}
-
-static int32_t Pnacl_M37_PPB_Compositor_ResetLayers(PP_Resource compositor) {
-  const struct PPB_Compositor_0_1 *iface = Pnacl_WrapperInfo_PPB_Compositor_0_1.real_iface;
-  return iface->ResetLayers(compositor);
-}
-
-/* End wrapper methods for PPB_Compositor_0_1 */
-
-/* Begin wrapper methods for PPB_CompositorLayer_0_1 */
-
-static PP_Bool Pnacl_M37_PPB_CompositorLayer_IsCompositorLayer(PP_Resource resource) {
-  const struct PPB_CompositorLayer_0_1 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_1.real_iface;
-  return iface->IsCompositorLayer(resource);
-}
-
-static int32_t Pnacl_M37_PPB_CompositorLayer_SetColor(PP_Resource layer, float red, float green, float blue, float alpha, const struct PP_Size* size) {
-  const struct PPB_CompositorLayer_0_1 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_1.real_iface;
-  return iface->SetColor(layer, red, green, blue, alpha, size);
-}
-
-static int32_t Pnacl_M37_PPB_CompositorLayer_SetTexture(PP_Resource layer, PP_Resource context, uint32_t texture, const struct PP_Size* size, struct PP_CompletionCallback* cc) {
-  const struct PPB_CompositorLayer_0_1 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_1.real_iface;
-  return iface->SetTexture(layer, context, texture, size, *cc);
-}
-
-static int32_t Pnacl_M37_PPB_CompositorLayer_SetImage(PP_Resource layer, PP_Resource image_data, const struct PP_Size* size, struct PP_CompletionCallback* cc) {
-  const struct PPB_CompositorLayer_0_1 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_1.real_iface;
-  return iface->SetImage(layer, image_data, size, *cc);
-}
-
-static int32_t Pnacl_M37_PPB_CompositorLayer_SetClipRect(PP_Resource layer, const struct PP_Rect* rect) {
-  const struct PPB_CompositorLayer_0_1 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_1.real_iface;
-  return iface->SetClipRect(layer, rect);
-}
-
-static int32_t Pnacl_M37_PPB_CompositorLayer_SetTransform(PP_Resource layer, const float matrix[16]) {
-  const struct PPB_CompositorLayer_0_1 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_1.real_iface;
-  return iface->SetTransform(layer, matrix);
-}
-
-static int32_t Pnacl_M37_PPB_CompositorLayer_SetOpacity(PP_Resource layer, float opacity) {
-  const struct PPB_CompositorLayer_0_1 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_1.real_iface;
-  return iface->SetOpacity(layer, opacity);
-}
-
-static int32_t Pnacl_M37_PPB_CompositorLayer_SetBlendMode(PP_Resource layer, PP_BlendMode mode) {
-  const struct PPB_CompositorLayer_0_1 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_1.real_iface;
-  return iface->SetBlendMode(layer, mode);
-}
-
-static int32_t Pnacl_M37_PPB_CompositorLayer_SetSourceRect(PP_Resource layer, const struct PP_FloatRect* rect) {
-  const struct PPB_CompositorLayer_0_1 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_1.real_iface;
-  return iface->SetSourceRect(layer, rect);
-}
-
-static int32_t Pnacl_M37_PPB_CompositorLayer_SetPremultipliedAlpha(PP_Resource layer, PP_Bool premult) {
-  const struct PPB_CompositorLayer_0_1 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_1.real_iface;
-  return iface->SetPremultipliedAlpha(layer, premult);
-}
-
-/* End wrapper methods for PPB_CompositorLayer_0_1 */
-
-/* Begin wrapper methods for PPB_CompositorLayer_0_2 */
-
-static PP_Bool Pnacl_M38_PPB_CompositorLayer_IsCompositorLayer(PP_Resource resource) {
-  const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface;
-  return iface->IsCompositorLayer(resource);
-}
-
-static int32_t Pnacl_M38_PPB_CompositorLayer_SetColor(PP_Resource layer, float red, float green, float blue, float alpha, const struct PP_Size* size) {
-  const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface;
-  return iface->SetColor(layer, red, green, blue, alpha, size);
-}
-
-static int32_t Pnacl_M38_PPB_CompositorLayer_SetTexture(PP_Resource layer, PP_Resource context, uint32_t target, uint32_t texture, const struct PP_Size* size, struct PP_CompletionCallback* cc) {
-  const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface;
-  return iface->SetTexture(layer, context, target, texture, size, *cc);
-}
-
-static int32_t Pnacl_M38_PPB_CompositorLayer_SetImage(PP_Resource layer, PP_Resource image_data, const struct PP_Size* size, struct PP_CompletionCallback* cc) {
-  const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface;
-  return iface->SetImage(layer, image_data, size, *cc);
-}
-
-static int32_t Pnacl_M38_PPB_CompositorLayer_SetClipRect(PP_Resource layer, const struct PP_Rect* rect) {
-  const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface;
-  return iface->SetClipRect(layer, rect);
-}
-
-static int32_t Pnacl_M38_PPB_CompositorLayer_SetTransform(PP_Resource layer, const float matrix[16]) {
-  const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface;
-  return iface->SetTransform(layer, matrix);
-}
-
-static int32_t Pnacl_M38_PPB_CompositorLayer_SetOpacity(PP_Resource layer, float opacity) {
-  const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface;
-  return iface->SetOpacity(layer, opacity);
-}
-
-static int32_t Pnacl_M38_PPB_CompositorLayer_SetBlendMode(PP_Resource layer, PP_BlendMode mode) {
-  const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface;
-  return iface->SetBlendMode(layer, mode);
-}
-
-static int32_t Pnacl_M38_PPB_CompositorLayer_SetSourceRect(PP_Resource layer, const struct PP_FloatRect* rect) {
-  const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface;
-  return iface->SetSourceRect(layer, rect);
-}
-
-static int32_t Pnacl_M38_PPB_CompositorLayer_SetPremultipliedAlpha(PP_Resource layer, PP_Bool premult) {
-  const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface;
-  return iface->SetPremultipliedAlpha(layer, premult);
-}
-
-/* End wrapper methods for PPB_CompositorLayer_0_2 */
-
 /* Begin wrapper methods for PPB_Console_1_0 */
 
 static void Pnacl_M25_PPB_Console_Log(PP_Instance instance, PP_LogLevel level, struct PP_Var* value) {
@@ -4501,40 +4359,6 @@
     .Close = (void (*)(PP_Resource audio_encoder))&Pnacl_M47_PPB_AudioEncoder_Close
 };
 
-static const struct PPB_Compositor_0_1 Pnacl_Wrappers_PPB_Compositor_0_1 = {
-    .IsCompositor = (PP_Bool (*)(PP_Resource resource))&Pnacl_M37_PPB_Compositor_IsCompositor,
-    .Create = (PP_Resource (*)(PP_Instance instance))&Pnacl_M37_PPB_Compositor_Create,
-    .AddLayer = (PP_Resource (*)(PP_Resource compositor))&Pnacl_M37_PPB_Compositor_AddLayer,
-    .CommitLayers = (int32_t (*)(PP_Resource compositor, struct PP_CompletionCallback cc))&Pnacl_M37_PPB_Compositor_CommitLayers,
-    .ResetLayers = (int32_t (*)(PP_Resource compositor))&Pnacl_M37_PPB_Compositor_ResetLayers
-};
-
-static const struct PPB_CompositorLayer_0_1 Pnacl_Wrappers_PPB_CompositorLayer_0_1 = {
-    .IsCompositorLayer = (PP_Bool (*)(PP_Resource resource))&Pnacl_M37_PPB_CompositorLayer_IsCompositorLayer,
-    .SetColor = (int32_t (*)(PP_Resource layer, float red, float green, float blue, float alpha, const struct PP_Size* size))&Pnacl_M37_PPB_CompositorLayer_SetColor,
-    .SetTexture = (int32_t (*)(PP_Resource layer, PP_Resource context, uint32_t texture, const struct PP_Size* size, struct PP_CompletionCallback cc))&Pnacl_M37_PPB_CompositorLayer_SetTexture,
-    .SetImage = (int32_t (*)(PP_Resource layer, PP_Resource image_data, const struct PP_Size* size, struct PP_CompletionCallback cc))&Pnacl_M37_PPB_CompositorLayer_SetImage,
-    .SetClipRect = (int32_t (*)(PP_Resource layer, const struct PP_Rect* rect))&Pnacl_M37_PPB_CompositorLayer_SetClipRect,
-    .SetTransform = (int32_t (*)(PP_Resource layer, const float matrix[16]))&Pnacl_M37_PPB_CompositorLayer_SetTransform,
-    .SetOpacity = (int32_t (*)(PP_Resource layer, float opacity))&Pnacl_M37_PPB_CompositorLayer_SetOpacity,
-    .SetBlendMode = (int32_t (*)(PP_Resource layer, PP_BlendMode mode))&Pnacl_M37_PPB_CompositorLayer_SetBlendMode,
-    .SetSourceRect = (int32_t (*)(PP_Resource layer, const struct PP_FloatRect* rect))&Pnacl_M37_PPB_CompositorLayer_SetSourceRect,
-    .SetPremultipliedAlpha = (int32_t (*)(PP_Resource layer, PP_Bool premult))&Pnacl_M37_PPB_CompositorLayer_SetPremultipliedAlpha
-};
-
-static const struct PPB_CompositorLayer_0_2 Pnacl_Wrappers_PPB_CompositorLayer_0_2 = {
-    .IsCompositorLayer = (PP_Bool (*)(PP_Resource resource))&Pnacl_M38_PPB_CompositorLayer_IsCompositorLayer,
-    .SetColor = (int32_t (*)(PP_Resource layer, float red, float green, float blue, float alpha, const struct PP_Size* size))&Pnacl_M38_PPB_CompositorLayer_SetColor,
-    .SetTexture = (int32_t (*)(PP_Resource layer, PP_Resource context, uint32_t target, uint32_t texture, const struct PP_Size* size, struct PP_CompletionCallback cc))&Pnacl_M38_PPB_CompositorLayer_SetTexture,
-    .SetImage = (int32_t (*)(PP_Resource layer, PP_Resource image_data, const struct PP_Size* size, struct PP_CompletionCallback cc))&Pnacl_M38_PPB_CompositorLayer_SetImage,
-    .SetClipRect = (int32_t (*)(PP_Resource layer, const struct PP_Rect* rect))&Pnacl_M38_PPB_CompositorLayer_SetClipRect,
-    .SetTransform = (int32_t (*)(PP_Resource layer, const float matrix[16]))&Pnacl_M38_PPB_CompositorLayer_SetTransform,
-    .SetOpacity = (int32_t (*)(PP_Resource layer, float opacity))&Pnacl_M38_PPB_CompositorLayer_SetOpacity,
-    .SetBlendMode = (int32_t (*)(PP_Resource layer, PP_BlendMode mode))&Pnacl_M38_PPB_CompositorLayer_SetBlendMode,
-    .SetSourceRect = (int32_t (*)(PP_Resource layer, const struct PP_FloatRect* rect))&Pnacl_M38_PPB_CompositorLayer_SetSourceRect,
-    .SetPremultipliedAlpha = (int32_t (*)(PP_Resource layer, PP_Bool premult))&Pnacl_M38_PPB_CompositorLayer_SetPremultipliedAlpha
-};
-
 static const struct PPB_Console_1_0 Pnacl_Wrappers_PPB_Console_1_0 = {
     .Log = (void (*)(PP_Instance instance, PP_LogLevel level, struct PP_Var value))&Pnacl_M25_PPB_Console_Log,
     .LogWithSource = (void (*)(PP_Instance instance, PP_LogLevel level, struct PP_Var source, struct PP_Var value))&Pnacl_M25_PPB_Console_LogWithSource
@@ -5674,24 +5498,6 @@
   .real_iface = NULL
 };
 
-static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Compositor_0_1 = {
-  .iface_macro = PPB_COMPOSITOR_INTERFACE_0_1,
-  .wrapped_iface = (const void *) &Pnacl_Wrappers_PPB_Compositor_0_1,
-  .real_iface = NULL
-};
-
-static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_CompositorLayer_0_1 = {
-  .iface_macro = PPB_COMPOSITORLAYER_INTERFACE_0_1,
-  .wrapped_iface = (const void *) &Pnacl_Wrappers_PPB_CompositorLayer_0_1,
-  .real_iface = NULL
-};
-
-static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_CompositorLayer_0_2 = {
-  .iface_macro = PPB_COMPOSITORLAYER_INTERFACE_0_2,
-  .wrapped_iface = (const void *) &Pnacl_Wrappers_PPB_CompositorLayer_0_2,
-  .real_iface = NULL
-};
-
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Console_1_0 = {
   .iface_macro = PPB_CONSOLE_INTERFACE_1_0,
   .wrapped_iface = (const void *) &Pnacl_Wrappers_PPB_Console_1_0,
@@ -6312,9 +6118,6 @@
 
 static struct __PnaclWrapperInfo *s_ppb_wrappers[] = {
   &Pnacl_WrapperInfo_PPB_AudioEncoder_0_1,
-  &Pnacl_WrapperInfo_PPB_Compositor_0_1,
-  &Pnacl_WrapperInfo_PPB_CompositorLayer_0_1,
-  &Pnacl_WrapperInfo_PPB_CompositorLayer_0_2,
   &Pnacl_WrapperInfo_PPB_Console_1_0,
   &Pnacl_WrapperInfo_PPB_Core_1_0,
   &Pnacl_WrapperInfo_PPB_FileIO_1_0,
diff --git a/ppapi/proxy/BUILD.gn b/ppapi/proxy/BUILD.gn
index 70e131d9..8c843220 100644
--- a/ppapi/proxy/BUILD.gn
+++ b/ppapi/proxy/BUILD.gn
@@ -36,10 +36,6 @@
     "camera_capabilities_resource.h",
     "camera_device_resource.cc",
     "camera_device_resource.h",
-    "compositor_layer_resource.cc",
-    "compositor_layer_resource.h",
-    "compositor_resource.cc",
-    "compositor_resource.h",
     "connection.h",
     "dispatcher.cc",
     "dispatcher.h",
diff --git a/ppapi/proxy/compositor_layer_resource.cc b/ppapi/proxy/compositor_layer_resource.cc
deleted file mode 100644
index 6fa9252..0000000
--- a/ppapi/proxy/compositor_layer_resource.cc
+++ /dev/null
@@ -1,384 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ppapi/proxy/compositor_layer_resource.h"
-
-#include <limits>
-
-#include "base/logging.h"
-#include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/gles2_implementation.h"
-#include "gpu/command_buffer/common/mailbox.h"
-#include "gpu/command_buffer/common/sync_token.h"
-#include "ppapi/proxy/compositor_resource.h"
-#include "ppapi/shared_impl/ppb_graphics_3d_shared.h"
-#include "ppapi/thunk/enter.h"
-#include "ppapi/thunk/ppb_graphics_3d_api.h"
-#include "ppapi/thunk/ppb_image_data_api.h"
-
-using gpu::gles2::GLES2Implementation;
-using ppapi::thunk::EnterResourceNoLock;
-using ppapi::thunk::PPB_ImageData_API;
-using ppapi::thunk::PPB_Graphics3D_API;
-
-namespace ppapi {
-namespace proxy {
-
-namespace {
-
-float clamp(float value) {
-  return std::min(std::max(value, 0.0f), 1.0f);
-}
-
-void OnTextureReleased(const ScopedPPResource& layer,
-                       const ScopedPPResource& context,
-                       uint32_t texture,
-                       const scoped_refptr<TrackedCallback>& release_callback,
-                       int32_t result,
-                       const gpu::SyncToken& sync_token,
-                       bool is_lost) {
-  if (!TrackedCallback::IsPending(release_callback))
-    return;
-
-  if (result != PP_OK) {
-    release_callback->Run(result);
-    return;
-  }
-
-  do {
-    if (!sync_token.HasData())
-      break;
-
-    EnterResourceNoLock<PPB_Graphics3D_API> enter(context.get(), true);
-    if (enter.failed())
-      break;
-
-    PPB_Graphics3D_Shared* graphics =
-        static_cast<PPB_Graphics3D_Shared*>(enter.object());
-
-    GLES2Implementation* gl = graphics->gles2_impl();
-    gl->WaitSyncTokenCHROMIUM(sync_token.GetConstData());
-  } while (false);
-
-  release_callback->Run(is_lost ? PP_ERROR_FAILED : PP_OK);
-}
-
-void OnImageReleased(const ScopedPPResource& layer,
-                     const ScopedPPResource& image,
-                     const scoped_refptr<TrackedCallback>& release_callback,
-                     int32_t result,
-                     const gpu::SyncToken& sync_token,
-                     bool is_lost) {
-  if (!TrackedCallback::IsPending(release_callback))
-    return;
-  release_callback->Run(result);
-}
-
-}  // namespace
-
-CompositorLayerResource::CompositorLayerResource(
-    Connection connection,
-    PP_Instance instance,
-    const CompositorResource* compositor)
-    : PluginResource(connection, instance),
-      compositor_(compositor),
-      source_size_(PP_MakeFloatSize(0.0f, 0.0f)) {
-}
-
-CompositorLayerResource::~CompositorLayerResource() {
-  DCHECK(!compositor_);
-  DCHECK(release_callback_.is_null());
-}
-
-thunk::PPB_CompositorLayer_API*
-CompositorLayerResource::AsPPB_CompositorLayer_API() {
-  return this;
-}
-
-int32_t CompositorLayerResource::SetColor(float red,
-                                          float green,
-                                          float blue,
-                                          float alpha,
-                                          const PP_Size* size) {
-  if (!compositor_)
-    return PP_ERROR_BADRESOURCE;
-
-  if (compositor_->IsInProgress())
-    return PP_ERROR_INPROGRESS;
-
-  if (!SetType(TYPE_COLOR))
-    return PP_ERROR_BADARGUMENT;
-  DCHECK(data_.color);
-
-  if (!size)
-    return PP_ERROR_BADARGUMENT;
-
-  data_.color->red = clamp(red);
-  data_.color->green = clamp(green);
-  data_.color->blue = clamp(blue);
-  data_.color->alpha = clamp(alpha);
-  data_.common.size = *size;
-
-  return PP_OK;
-}
-
-int32_t CompositorLayerResource::SetTexture0_1(
-    PP_Resource context,
-    uint32_t texture,
-    const PP_Size* size,
-    const scoped_refptr<TrackedCallback>& release_callback) {
-  return SetTexture(context, GL_TEXTURE_2D, texture, size, release_callback);
-}
-
-int32_t CompositorLayerResource::SetTexture(
-    PP_Resource context,
-    uint32_t target,
-    uint32_t texture,
-    const PP_Size* size,
-    const scoped_refptr<TrackedCallback>& release_callback) {
-  int32_t rv = CheckForSetTextureAndImage(TYPE_TEXTURE, release_callback);
-  if (rv != PP_OK)
-    return rv;
-  DCHECK(data_.texture);
-
-  EnterResourceNoLock<PPB_Graphics3D_API> enter(context, true);
-  if (enter.failed())
-    return PP_ERROR_BADRESOURCE;
-
-  if (target != GL_TEXTURE_2D &&
-      target != GL_TEXTURE_EXTERNAL_OES &&
-      target != GL_TEXTURE_RECTANGLE_ARB) {
-    return PP_ERROR_BADARGUMENT;
-  }
-
-  if (!size || size->width <= 0 || size->height <= 0)
-    return PP_ERROR_BADARGUMENT;
-
-  PPB_Graphics3D_Shared* graphics =
-      static_cast<PPB_Graphics3D_Shared*>(enter.object());
-
-  GLES2Implementation* gl = graphics->gles2_impl();
-
-  // Generate a Mailbox for the texture.
-  gl->ProduceTextureDirectCHROMIUM(texture, data_.texture->mailbox.name);
-
-  // Set the source size to (1, 1). It will be used to verify the source_rect
-  // passed to SetSourceRect().
-  source_size_ = PP_MakeFloatSize(1.0f, 1.0f);
-
-  data_.common.size = *size;
-  data_.common.resource_id = compositor_->GenerateResourceId();
-  data_.texture->target = target;
-  data_.texture->source_rect.point = PP_MakeFloatPoint(0.0f, 0.0f);
-  data_.texture->source_rect.size = source_size_;
-
-  gl->GenSyncTokenCHROMIUM(data_.texture->sync_token.GetData());
-
-  // If the PP_Resource of this layer is released by the plugin, the
-  // release_callback will be aborted immediately, but the texture or image
-  // in this layer may still being used by chromium compositor. So we have to
-  // use ScopedPPResource to keep this resource alive until the texture or image
-  // is released by the chromium compositor.
-  release_callback_ = base::Bind(
-      &OnTextureReleased,
-      ScopedPPResource(pp_resource()), // Keep layer alive.
-      ScopedPPResource(context), // Keep context alive
-      texture,
-      release_callback);
-
-  return PP_OK_COMPLETIONPENDING;
-}
-
-int32_t CompositorLayerResource::SetImage(
-    PP_Resource image_data,
-    const PP_Size* size,
-    const scoped_refptr<TrackedCallback>& release_callback) {
-  int32_t rv = CheckForSetTextureAndImage(TYPE_IMAGE, release_callback);
-  if (rv != PP_OK)
-    return rv;
-  DCHECK(data_.image);
-
-  EnterResourceNoLock<PPB_ImageData_API> enter(image_data, true);
-  if (enter.failed())
-    return PP_ERROR_BADRESOURCE;
-
-  PP_ImageDataDesc desc;
-  if (!enter.object()->Describe(&desc))
-    return PP_ERROR_BADARGUMENT;
-
-  // TODO(penghuang): Support image which width * 4 != stride.
-  if (desc.size.width * 4 != desc.stride)
-    return PP_ERROR_BADARGUMENT;
-
-  // TODO(penghuang): Support all formats.
-  if (desc.format != PP_IMAGEDATAFORMAT_RGBA_PREMUL)
-    return PP_ERROR_BADARGUMENT;
-
-  if (size && (size->width <= 0 || size->height <= 0))
-    return PP_ERROR_BADARGUMENT;
-
-  // Set the source size to image's size. It will be used to verify
-  // the source_rect passed to SetSourceRect().
-  source_size_ = PP_MakeFloatSize(desc.size.width, desc.size.height);
-
-  data_.common.size = size ? *size : desc.size;
-  data_.common.resource_id = compositor_->GenerateResourceId();
-  data_.image->resource = enter.resource()->host_resource().host_resource();
-  data_.image->source_rect.point = PP_MakeFloatPoint(0.0f, 0.0f);
-  data_.image->source_rect.size = source_size_;
-
-  // If the PP_Resource of this layer is released by the plugin, the
-  // release_callback will be aborted immediately, but the texture or image
-  // in this layer may still being used by chromium compositor. So we have to
-  // use ScopedPPResource to keep this resource alive until the texture or image
-  // is released by the chromium compositor.
-  release_callback_ = base::Bind(
-      &OnImageReleased,
-      ScopedPPResource(pp_resource()), // Keep layer alive.
-      ScopedPPResource(image_data), // Keep image_data alive.
-      release_callback);
-
-  return PP_OK_COMPLETIONPENDING;
-}
-
-int32_t CompositorLayerResource::SetClipRect(const PP_Rect* rect) {
-  if (!compositor_)
-    return PP_ERROR_BADRESOURCE;
-
-  if (compositor_->IsInProgress())
-    return PP_ERROR_INPROGRESS;
-
-  data_.common.clip_rect = rect ? *rect : PP_MakeRectFromXYWH(0, 0, 0, 0);
-  return PP_OK;
-}
-
-int32_t CompositorLayerResource::SetTransform(const float matrix[16]) {
-  if (!compositor_)
-    return PP_ERROR_BADRESOURCE;
-
-  if (compositor_->IsInProgress())
-    return PP_ERROR_INPROGRESS;
-
-  std::copy(matrix, matrix + 16, data_.common.transform.matrix);
-  return PP_OK;
-}
-
-int32_t CompositorLayerResource::SetOpacity(float opacity) {
-  if (!compositor_)
-    return PP_ERROR_BADRESOURCE;
-
-  if (compositor_->IsInProgress())
-    return PP_ERROR_INPROGRESS;
-
-  data_.common.opacity = clamp(opacity);
-  return PP_OK;
-}
-
-int32_t CompositorLayerResource::SetBlendMode(PP_BlendMode mode) {
-  if (!compositor_)
-    return PP_ERROR_BADRESOURCE;
-
-  if (compositor_->IsInProgress())
-    return PP_ERROR_INPROGRESS;
-
-  switch (mode) {
-    case PP_BLENDMODE_NONE:
-    case PP_BLENDMODE_SRC_OVER:
-      data_.common.blend_mode = mode;
-      return PP_OK;
-  }
-  return PP_ERROR_BADARGUMENT;
-}
-
-int32_t CompositorLayerResource::SetSourceRect(
-    const PP_FloatRect* rect) {
-  if (!compositor_)
-    return PP_ERROR_BADRESOURCE;
-
-  if (compositor_->IsInProgress())
-    return PP_ERROR_INPROGRESS;
-
-  const float kEpsilon = std::numeric_limits<float>::epsilon();
-  if (!rect ||
-      rect->point.x < -kEpsilon ||
-      rect->point.y < -kEpsilon ||
-      rect->point.x + rect->size.width > source_size_.width + kEpsilon ||
-      rect->point.y + rect->size.height > source_size_.height + kEpsilon) {
-    return PP_ERROR_BADARGUMENT;
-  }
-
-  if (data_.texture) {
-    data_.texture->source_rect = *rect;
-    return PP_OK;
-  }
-  if (data_.image) {
-    data_.image->source_rect = *rect;
-    return PP_OK;
-  }
-  return PP_ERROR_BADARGUMENT;
-}
-
-int32_t CompositorLayerResource::SetPremultipliedAlpha(PP_Bool premult) {
-  if (!compositor_)
-    return PP_ERROR_BADRESOURCE;
-
-  if (compositor_->IsInProgress())
-    return PP_ERROR_INPROGRESS;
-
-  if (data_.texture) {
-    data_.texture->premult_alpha = PP_ToBool(premult);
-    return PP_OK;
-  }
-  return PP_ERROR_BADARGUMENT;
-}
-
-bool CompositorLayerResource::SetType(LayerType type) {
-  if (type == TYPE_COLOR) {
-    if (data_.is_null())
-      data_.color.reset(new CompositorLayerData::ColorLayer());
-    return !!data_.color;
-  }
-
-  if (type == TYPE_TEXTURE) {
-    if (data_.is_null())
-      data_.texture.reset(new CompositorLayerData::TextureLayer());
-    return !!data_.texture;
-  }
-
-  if (type == TYPE_IMAGE) {
-    if (data_.is_null())
-      data_.image.reset(new CompositorLayerData::ImageLayer());
-    return !!data_.image;
-  }
-
-  // Should not be reached.
-  DCHECK(false);
-  return false;
-}
-
-int32_t CompositorLayerResource::CheckForSetTextureAndImage(
-    LayerType type,
-    const scoped_refptr<TrackedCallback>& release_callback) {
-  if (!compositor_)
-    return PP_ERROR_BADRESOURCE;
-
-  if (compositor_->IsInProgress())
-    return PP_ERROR_INPROGRESS;
-
-  if (!SetType(type))
-    return PP_ERROR_BADARGUMENT;
-
-  // The layer's image has been set and it is not committed.
-  if (!release_callback_.is_null())
-    return PP_ERROR_INPROGRESS;
-
-  // Do not allow using a block callback as a release callback.
-  if (release_callback->is_blocking())
-    return PP_ERROR_BADARGUMENT;
-
-  return PP_OK;
-}
-
-}  // namespace proxy
-}  // namespace ppapi
diff --git a/ppapi/proxy/compositor_layer_resource.h b/ppapi/proxy/compositor_layer_resource.h
deleted file mode 100644
index 7bc330a..0000000
--- a/ppapi/proxy/compositor_layer_resource.h
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PPAPI_PROXY_COMPOSITOR_LAYER_RESOURCE_H_
-#define PPAPI_PROXY_COMPOSITOR_LAYER_RESOURCE_H_
-
-#include <stdint.h>
-
-#include "base/macros.h"
-#include "ppapi/c/ppb_compositor_layer.h"
-#include "ppapi/proxy/plugin_resource.h"
-#include "ppapi/proxy/ppapi_proxy_export.h"
-#include "ppapi/shared_impl/compositor_layer_data.h"
-#include "ppapi/shared_impl/scoped_pp_resource.h"
-#include "ppapi/thunk/ppb_compositor_layer_api.h"
-
-namespace gpu {
-struct SyncToken;
-}
-
-namespace ppapi {
-namespace proxy {
-
-class CompositorResource;
-
-class PPAPI_PROXY_EXPORT CompositorLayerResource
-    : public PluginResource,
-      public thunk::PPB_CompositorLayer_API {
- public:
-  // Release callback for texture or image layer.
-  typedef base::Callback<void(int32_t, const gpu::SyncToken&, bool)>
-      ReleaseCallback;
-
-  CompositorLayerResource(Connection connection,
-                          PP_Instance instance,
-                          const CompositorResource* compositor);
-
-  const CompositorLayerData& data() const { return data_; }
-  const ReleaseCallback& release_callback() const {
-    return release_callback_;
-  }
-  void ResetReleaseCallback() { release_callback_.Reset(); }
-  void Invalidate() { compositor_ = NULL; }
-
- private:
-  enum LayerType {
-    TYPE_COLOR,
-    TYPE_TEXTURE,
-    TYPE_IMAGE,
-  };
-
-  ~CompositorLayerResource() override;
-
-  // Resource overrides:
-  thunk::PPB_CompositorLayer_API* AsPPB_CompositorLayer_API() override;
-
-  // thunk::PPB_Compositor_API overrides:
-  int32_t SetColor(float red,
-                   float green,
-                   float blue,
-                   float alpha,
-                   const PP_Size* size) override;
-  int32_t SetTexture0_1(
-      PP_Resource context,
-      uint32_t texture,
-      const PP_Size* size,
-      const scoped_refptr<ppapi::TrackedCallback>& callback) override;
-  int32_t SetTexture(
-      PP_Resource context,
-      uint32_t target,
-      uint32_t texture,
-      const PP_Size* size,
-      const scoped_refptr<TrackedCallback>& callback) override;
-  int32_t SetImage(
-      PP_Resource image_data,
-      const PP_Size* size,
-      const scoped_refptr<TrackedCallback>& callback) override;
-  int32_t SetClipRect(const PP_Rect* rect) override;
-  int32_t SetTransform(const float matrix[16]) override;
-  int32_t SetOpacity(float opacity) override;
-  int32_t SetBlendMode(PP_BlendMode mode) override;
-  int32_t SetSourceRect(const PP_FloatRect* rect) override;
-  int32_t SetPremultipliedAlpha(PP_Bool premult) override;
-
-  bool SetType(LayerType type);
-  int32_t CheckForSetTextureAndImage(
-      LayerType type,
-      const scoped_refptr<TrackedCallback>& release_callback);
-
-  // The CompositorResource which own the layer. The layer is invalidated if
-  // compositor_ is NULL.
-  const CompositorResource* compositor_;
-
-  // Release callback for uncommitted texture or image. When CommitLayers() on
-  // the compositor_ is called, the callback will be copied into a map in the
-  // compositor_, and it will be reset to NULL.
-  ReleaseCallback release_callback_;
-
-  // Size of texture or image. It is used to verify the rect arg of
-  // SetSourceRect().
-  PP_FloatSize source_size_;
-
-  // Layer data.
-  CompositorLayerData data_;
-
-  DISALLOW_COPY_AND_ASSIGN(CompositorLayerResource);
-};
-
-}  // namespace proxy
-}  // namespace ppapi
-
-#endif  // PPAPI_PROXY_COMPOSITOR_LAYER_RESOURCE_H_
diff --git a/ppapi/proxy/compositor_resource.cc b/ppapi/proxy/compositor_resource.cc
deleted file mode 100644
index f15e402..0000000
--- a/ppapi/proxy/compositor_resource.cc
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ppapi/proxy/compositor_resource.h"
-
-#include "base/logging.h"
-#include "ppapi/proxy/ppapi_messages.h"
-#include "ppapi/thunk/enter.h"
-
-namespace ppapi {
-namespace proxy {
-
-CompositorResource::CompositorResource(Connection connection,
-                                       PP_Instance instance)
-    : PluginResource(connection, instance),
-      layer_reset_(true),
-      last_resource_id_(0) {
-  SendCreate(RENDERER, PpapiHostMsg_Compositor_Create());
-}
-
-bool CompositorResource::IsInProgress() const {
-  ProxyLock::AssertAcquiredDebugOnly();
-  return TrackedCallback::IsPending(commit_callback_);
-}
-
-int32_t CompositorResource::GenerateResourceId() const {
-  ProxyLock::AssertAcquiredDebugOnly();
-  return ++last_resource_id_;
-}
-
-CompositorResource::~CompositorResource() {
-  ResetLayersInternal(true);
-
-  // Abort all release callbacks.
-  for (ReleaseCallbackMap::iterator it = release_callback_map_.begin();
-       it != release_callback_map_.end(); ++it) {
-    if (!it->second.is_null())
-      it->second.Run(PP_ERROR_ABORTED, gpu::SyncToken(), false);
-  }
-}
-
-thunk::PPB_Compositor_API* CompositorResource::AsPPB_Compositor_API() {
-  return this;
-}
-
-void CompositorResource::OnReplyReceived(
-    const ResourceMessageReplyParams& params,
-    const IPC::Message& msg) {
-   PPAPI_BEGIN_MESSAGE_MAP(CompositorResource, msg)
-     PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
-         PpapiPluginMsg_Compositor_ReleaseResource,
-         OnPluginMsgReleaseResource)
-     PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
-          PluginResource::OnReplyReceived(params, msg))
-   PPAPI_END_MESSAGE_MAP()
-}
-
-PP_Resource CompositorResource::AddLayer() {
-  scoped_refptr<CompositorLayerResource> resource(new CompositorLayerResource(
-      connection(), pp_instance(), this));
-  layers_.push_back(resource);
-  return resource->GetReference();
-}
-
-int32_t CompositorResource::CommitLayers(
-    const scoped_refptr<ppapi::TrackedCallback>& callback) {
-  if (IsInProgress())
-    return PP_ERROR_INPROGRESS;
-
-  std::vector<CompositorLayerData> layers;
-  layers.reserve(layers_.size());
-
-  for (LayerList::const_iterator it = layers_.begin();
-       it != layers_.end(); ++it) {
-    if ((*it)->data().is_null())
-      return PP_ERROR_FAILED;
-    layers.push_back((*it)->data());
-  }
-
-  commit_callback_ = callback;
-  Call<PpapiPluginMsg_Compositor_CommitLayersReply>(
-      RENDERER,
-      PpapiHostMsg_Compositor_CommitLayers(layers, layer_reset_),
-      base::Bind(&CompositorResource::OnPluginMsgCommitLayersReply,
-                 base::Unretained(this)),
-      callback);
-
-  return PP_OK_COMPLETIONPENDING;
-}
-
-int32_t CompositorResource::ResetLayers() {
-  if (IsInProgress())
-    return PP_ERROR_INPROGRESS;
-
-  ResetLayersInternal(false);
-  return PP_OK;
-}
-
-void CompositorResource::OnPluginMsgCommitLayersReply(
-    const ResourceMessageReplyParams& params) {
-  if (!TrackedCallback::IsPending(commit_callback_))
-    return;
-
-  // On success, we put layers' release_callbacks into a map,
-  // otherwise we will do nothing. So plugin may change layers and
-  // call CommitLayers() again.
-  if (params.result() == PP_OK) {
-    layer_reset_ = false;
-    for (LayerList::iterator it = layers_.begin();
-         it != layers_.end(); ++it) {
-      ReleaseCallback release_callback = (*it)->release_callback();
-      if (!release_callback.is_null()) {
-        release_callback_map_.insert(ReleaseCallbackMap::value_type(
-            (*it)->data().common.resource_id, release_callback));
-        (*it)->ResetReleaseCallback();
-      }
-    }
-  }
-
-  scoped_refptr<TrackedCallback> callback;
-  callback.swap(commit_callback_);
-  callback->Run(params.result());
-}
-
-void CompositorResource::OnPluginMsgReleaseResource(
-    const ResourceMessageReplyParams& params,
-    int32_t id,
-    const gpu::SyncToken& sync_token,
-    bool is_lost) {
-  ReleaseCallbackMap::iterator it = release_callback_map_.find(id);
-  DCHECK(it != release_callback_map_.end()) <<
-      "Can not found release_callback_ by id(" << id << ")!";
-  it->second.Run(PP_OK, sync_token, is_lost);
-  release_callback_map_.erase(it);
-}
-
-void CompositorResource::ResetLayersInternal(bool is_aborted) {
-  for (LayerList::iterator it = layers_.begin();
-       it != layers_.end(); ++it) {
-    ReleaseCallback release_callback = (*it)->release_callback();
-    if (!release_callback.is_null()) {
-      release_callback.Run(is_aborted ? PP_ERROR_ABORTED : PP_OK,
-                           gpu::SyncToken(), false);
-      (*it)->ResetReleaseCallback();
-    }
-    (*it)->Invalidate();
-  }
-
-  layers_.clear();
-  layer_reset_ = true;
-}
-
-}  // namespace proxy
-}  // namespace ppapi
diff --git a/ppapi/proxy/compositor_resource.h b/ppapi/proxy/compositor_resource.h
deleted file mode 100644
index 366e3e7b..0000000
--- a/ppapi/proxy/compositor_resource.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PPAPI_PROXY_COMPOSITOR_RESOURCE_H_
-#define PPAPI_PROXY_COMPOSITOR_RESOURCE_H_
-
-#include <stdint.h>
-
-#include <map>
-
-#include "base/macros.h"
-#include "ppapi/proxy/compositor_layer_resource.h"
-#include "ppapi/proxy/plugin_resource.h"
-#include "ppapi/proxy/ppapi_proxy_export.h"
-#include "ppapi/shared_impl/proxy_lock.h"
-#include "ppapi/thunk/ppb_compositor_api.h"
-
-namespace gpu {
-struct SyncToken;
-}
-
-namespace ppapi {
-namespace proxy {
-
-class PPAPI_PROXY_EXPORT CompositorResource
-    : public PluginResource,
-      public thunk::PPB_Compositor_API {
- public:
-  CompositorResource(Connection connection,
-                     PP_Instance instance);
-
-  bool IsInProgress() const;
-
-  int32_t GenerateResourceId() const;
-
- private:
-  ~CompositorResource() override;
-
-  // Resource overrides:
-  thunk::PPB_Compositor_API* AsPPB_Compositor_API() override;
-
-  // PluginResource overrides:
-  void OnReplyReceived(const ResourceMessageReplyParams& params,
-                       const IPC::Message& msg) override;
-
-  // thunk::PPB_Compositor_API overrides:
-  PP_Resource AddLayer() override;
-  int32_t CommitLayers(const scoped_refptr<TrackedCallback>& callback) override;
-  int32_t ResetLayers() override;
-
-  // IPC msg handlers:
-  void OnPluginMsgCommitLayersReply(const ResourceMessageReplyParams& params);
-  void OnPluginMsgReleaseResource(const ResourceMessageReplyParams& params,
-                                  int32_t id,
-                                  const gpu::SyncToken& sync_token,
-                                  bool is_lost);
-
-  void ResetLayersInternal(bool is_aborted);
-
-  // Callback for CommitLayers().
-  scoped_refptr<TrackedCallback> commit_callback_;
-
-  // True if layers_ has been reset by ResetLayers().
-  bool layer_reset_;
-
-  // Layer stack.
-  typedef std::vector<scoped_refptr<CompositorLayerResource> > LayerList;
-  LayerList layers_;
-
-  // Release callback map for texture and image.
-  typedef CompositorLayerResource::ReleaseCallback ReleaseCallback;
-  typedef std::map<int32_t, ReleaseCallback> ReleaseCallbackMap;
-  ReleaseCallbackMap release_callback_map_;
-
-  // The last resource id for texture or image.
-  mutable int32_t last_resource_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(CompositorResource);
-};
-
-}  // namespace proxy
-}  // namespace ppapi
-
-#endif  // PPAPI_PROXY_COMPOSITOR_RESOURCE_H_
diff --git a/ppapi/proxy/interface_list.cc b/ppapi/proxy/interface_list.cc
index 8e4c9d33..cc72916 100644
--- a/ppapi/proxy/interface_list.cc
+++ b/ppapi/proxy/interface_list.cc
@@ -35,8 +35,6 @@
 #include "ppapi/c/ppb_audio_buffer.h"
 #include "ppapi/c/ppb_audio_config.h"
 #include "ppapi/c/ppb_audio_encoder.h"
-#include "ppapi/c/ppb_compositor.h"
-#include "ppapi/c/ppb_compositor_layer.h"
 #include "ppapi/c/ppb_console.h"
 #include "ppapi/c/ppb_core.h"
 #include "ppapi/c/ppb_file_io.h"
diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h
index 6e7890d..f81ab65 100644
--- a/ppapi/proxy/ppapi_messages.h
+++ b/ppapi/proxy/ppapi_messages.h
@@ -42,7 +42,6 @@
 #include "ppapi/c/pp_size.h"
 #include "ppapi/c/pp_time.h"
 #include "ppapi/c/ppb_audio_config.h"
-#include "ppapi/c/ppb_compositor_layer.h"
 #include "ppapi/c/ppb_image_data.h"
 #include "ppapi/c/ppb_tcp_socket.h"
 #include "ppapi/c/ppb_text_input_controller.h"
@@ -66,7 +65,6 @@
 #include "ppapi/proxy/serialized_handle.h"
 #include "ppapi/proxy/serialized_structs.h"
 #include "ppapi/proxy/serialized_var.h"
-#include "ppapi/shared_impl/compositor_layer_data.h"
 #include "ppapi/shared_impl/dir_contents.h"
 #include "ppapi/shared_impl/file_growth.h"
 #include "ppapi/shared_impl/file_path.h"
@@ -93,7 +91,6 @@
 IPC_ENUM_TRAITS_MAX_VALUE(ppapi::TCPSocketVersion,
                           ppapi::TCP_SOCKET_VERSION_1_1_OR_ABOVE)
 IPC_ENUM_TRAITS_MAX_VALUE(PP_AudioSampleRate, PP_AUDIOSAMPLERATE_LAST)
-IPC_ENUM_TRAITS_MAX_VALUE(PP_BlendMode, PP_BLENDMODE_LAST)
 IPC_ENUM_TRAITS_MAX_VALUE(PP_DeviceType_Dev, PP_DEVICETYPE_DEV_MAX)
 IPC_ENUM_TRAITS_MAX_VALUE(PP_FileSystemType, PP_FILESYSTEMTYPE_ISOLATED)
 IPC_ENUM_TRAITS_MAX_VALUE(PP_FileType, PP_FILETYPE_OTHER)
@@ -305,42 +302,6 @@
   IPC_STRUCT_TRAITS_MEMBER(append_mode_write_amount)
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(ppapi::CompositorLayerData)
-  IPC_STRUCT_TRAITS_MEMBER(common)
-  IPC_STRUCT_TRAITS_MEMBER(color)
-  IPC_STRUCT_TRAITS_MEMBER(texture)
-  IPC_STRUCT_TRAITS_MEMBER(image)
-IPC_STRUCT_TRAITS_END()
-
-IPC_STRUCT_TRAITS_BEGIN(ppapi::CompositorLayerData::LayerCommon)
-  IPC_STRUCT_TRAITS_MEMBER(size)
-  IPC_STRUCT_TRAITS_MEMBER(clip_rect)
-  IPC_STRUCT_TRAITS_MEMBER(transform)
-  IPC_STRUCT_TRAITS_MEMBER(blend_mode)
-  IPC_STRUCT_TRAITS_MEMBER(opacity)
-  IPC_STRUCT_TRAITS_MEMBER(resource_id)
-IPC_STRUCT_TRAITS_END()
-
-IPC_STRUCT_TRAITS_BEGIN(ppapi::CompositorLayerData::ColorLayer)
-  IPC_STRUCT_TRAITS_MEMBER(red)
-  IPC_STRUCT_TRAITS_MEMBER(green)
-  IPC_STRUCT_TRAITS_MEMBER(blue)
-  IPC_STRUCT_TRAITS_MEMBER(alpha)
-IPC_STRUCT_TRAITS_END()
-
-IPC_STRUCT_TRAITS_BEGIN(ppapi::CompositorLayerData::ImageLayer)
-  IPC_STRUCT_TRAITS_MEMBER(resource)
-  IPC_STRUCT_TRAITS_MEMBER(source_rect)
-IPC_STRUCT_TRAITS_END()
-
-IPC_STRUCT_TRAITS_BEGIN(ppapi::CompositorLayerData::TextureLayer)
-  IPC_STRUCT_TRAITS_MEMBER(mailbox)
-  IPC_STRUCT_TRAITS_MEMBER(sync_token)
-  IPC_STRUCT_TRAITS_MEMBER(target)
-  IPC_STRUCT_TRAITS_MEMBER(source_rect)
-  IPC_STRUCT_TRAITS_MEMBER(premult_alpha)
-IPC_STRUCT_TRAITS_END()
-
 IPC_STRUCT_TRAITS_BEGIN(ppapi::DeviceRefData)
   IPC_STRUCT_TRAITS_MEMBER(type)
   IPC_STRUCT_TRAITS_MEMBER(name)
@@ -525,10 +486,6 @@
   IPC_STRUCT_TRAITS_MEMBER(acceleration)
 IPC_STRUCT_TRAITS_END()
 
-IPC_STRUCT_TRAITS_BEGIN(ppapi::CompositorLayerData::Transform)
-  IPC_STRUCT_TRAITS_MEMBER(matrix)
-IPC_STRUCT_TRAITS_END()
-
 #if !defined(OS_NACL) && !defined(NACL_WIN64)
 
 IPC_STRUCT_TRAITS_BEGIN(ppapi::proxy::PPPDecryptor_Buffer)
@@ -1472,17 +1429,6 @@
 IPC_MESSAGE_CONTROL0(PpapiHostMsg_UMA_IsCrashReportingEnabled)
 IPC_MESSAGE_CONTROL0(PpapiPluginMsg_UMA_IsCrashReportingEnabledReply)
 
-// Compositor
-IPC_MESSAGE_CONTROL0(PpapiHostMsg_Compositor_Create)
-IPC_MESSAGE_CONTROL2(PpapiHostMsg_Compositor_CommitLayers,
-                     std::vector<ppapi::CompositorLayerData> /* layers */,
-                     bool /* reset */)
-IPC_MESSAGE_CONTROL0(PpapiPluginMsg_Compositor_CommitLayersReply)
-IPC_MESSAGE_CONTROL3(PpapiPluginMsg_Compositor_ReleaseResource,
-                     int32_t /* id */,
-                     gpu::SyncToken /* sync_token */,
-                     bool /* is_lost */)
-
 // File chooser.
 IPC_MESSAGE_CONTROL0(PpapiHostMsg_FileChooser_Create)
 IPC_MESSAGE_CONTROL4(PpapiHostMsg_FileChooser_Show,
diff --git a/ppapi/proxy/ppapi_param_traits.h b/ppapi/proxy/ppapi_param_traits.h
index 6b32e10..541b69f 100644
--- a/ppapi/proxy/ppapi_param_traits.h
+++ b/ppapi/proxy/ppapi_param_traits.h
@@ -15,7 +15,6 @@
 #include "ppapi/c/pp_rect.h"
 #include "ppapi/c/pp_var.h"
 #include "ppapi/proxy/ppapi_proxy_export.h"
-#include "ppapi/shared_impl/compositor_layer_data.h"
 #include "ppapi/shared_impl/file_path.h"
 #include "ppapi/shared_impl/file_ref_create_info.h"
 #include "ppapi/shared_impl/media_stream_video_track_shared.h"
diff --git a/ppapi/proxy/ppb_instance_proxy.cc b/ppapi/proxy/ppb_instance_proxy.cc
index c453c42..c8f757f 100644
--- a/ppapi/proxy/ppb_instance_proxy.cc
+++ b/ppapi/proxy/ppb_instance_proxy.cc
@@ -42,7 +42,6 @@
 #include "ppapi/shared_impl/scoped_pp_var.h"
 #include "ppapi/shared_impl/var.h"
 #include "ppapi/thunk/enter.h"
-#include "ppapi/thunk/ppb_compositor_api.h"
 #include "ppapi/thunk/ppb_graphics_2d_api.h"
 #include "ppapi/thunk/ppb_graphics_3d_api.h"
 #include "ppapi/thunk/thunk.h"
@@ -54,7 +53,6 @@
 
 using ppapi::thunk::EnterInstanceNoLock;
 using ppapi::thunk::EnterResourceNoLock;
-using ppapi::thunk::PPB_Compositor_API;
 using ppapi::thunk::PPB_Graphics2D_API;
 using ppapi::thunk::PPB_Graphics3D_API;
 using ppapi::thunk::PPB_Instance_API;
@@ -197,13 +195,11 @@
         PpapiGlobals::Get()->GetResourceTracker()->GetResource(device);
     if (!resource || resource->pp_instance() != instance)
       return PP_FALSE;
-    // We need to pass different resource to Graphics 2D, 3D and Compositor
-    // right now.  Once 3D is migrated to the new design, we should be able to
-    // unify this.
+    // We need to pass different resource to Graphics 2D and 3D right now.  Once
+    // 3D is migrated to the new design, we should be able to unify this.
     if (resource->AsPPB_Graphics3D_API()) {
       pp_resource = resource->host_resource().host_resource();
-    } else if (resource->AsPPB_Graphics2D_API() ||
-               resource->AsPPB_Compositor_API()) {
+    } else if (resource->AsPPB_Graphics2D_API()) {
       pp_resource = resource->pp_resource();
     } else {
       // A bad resource.
diff --git a/ppapi/proxy/resource_creation_proxy.cc b/ppapi/proxy/resource_creation_proxy.cc
index 65a92ede..68932e93 100644
--- a/ppapi/proxy/resource_creation_proxy.cc
+++ b/ppapi/proxy/resource_creation_proxy.cc
@@ -11,7 +11,6 @@
 #include "ppapi/proxy/audio_input_resource.h"
 #include "ppapi/proxy/audio_output_resource.h"
 #include "ppapi/proxy/camera_device_resource.h"
-#include "ppapi/proxy/compositor_resource.h"
 #include "ppapi/proxy/connection.h"
 #include "ppapi/proxy/file_chooser_resource.h"
 #include "ppapi/proxy/file_io_resource.h"
@@ -230,10 +229,6 @@
   return (new CameraDeviceResource(GetConnection(), instance))->GetReference();
 }
 
-PP_Resource ResourceCreationProxy::CreateCompositor(PP_Instance instance) {
-  return (new CompositorResource(GetConnection(), instance))->GetReference();
-}
-
 PP_Resource ResourceCreationProxy::CreateFileChooser(
     PP_Instance instance,
     PP_FileChooserMode_Dev mode,
diff --git a/ppapi/proxy/resource_creation_proxy.h b/ppapi/proxy/resource_creation_proxy.h
index 36dd4e0..cee7ed22 100644
--- a/ppapi/proxy/resource_creation_proxy.h
+++ b/ppapi/proxy/resource_creation_proxy.h
@@ -108,7 +108,6 @@
                                 PP_AudioSampleRate sample_rate,
                                 uint32_t sample_frame_count) override;
   PP_Resource CreateCameraDevicePrivate(PP_Instance instance) override;
-  PP_Resource CreateCompositor(PP_Instance instance) override;
   PP_Resource CreateFileChooser(PP_Instance instance,
                                 PP_FileChooserMode_Dev mode,
                                 const PP_Var& accept_types) override;
diff --git a/ppapi/shared_impl/BUILD.gn b/ppapi/shared_impl/BUILD.gn
index f5b7c01..110566b 100644
--- a/ppapi/shared_impl/BUILD.gn
+++ b/ppapi/shared_impl/BUILD.gn
@@ -21,8 +21,6 @@
     "array_writer.h",
     "callback_tracker.cc",
     "callback_tracker.h",
-    "compositor_layer_data.cc",
-    "compositor_layer_data.h",
     "dictionary_var.cc",
     "dictionary_var.h",
     "file_growth.cc",
diff --git a/ppapi/shared_impl/compositor_layer_data.cc b/ppapi/shared_impl/compositor_layer_data.cc
deleted file mode 100644
index 73ad43bc..0000000
--- a/ppapi/shared_impl/compositor_layer_data.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ppapi/shared_impl/compositor_layer_data.h"
-
-namespace ppapi {
-
-namespace {
-
-template <typename T>
-void Copy(std::unique_ptr<T>* a, const std::unique_ptr<T>& b) {
-  if (b) {
-    if (!(*a))
-      a->reset(new T());
-    **a = *b;
-  } else {
-    a->reset();
-  }
-}
-
-}  // namespace
-
-const CompositorLayerData& CompositorLayerData::operator=(
-    const CompositorLayerData& other) {
-  DCHECK(other.is_null() || other.is_valid());
-
-  common = other.common;
-  Copy(&color, other.color);
-  Copy(&texture, other.texture);
-  Copy(&image, other.image);
-
-  return *this;
-}
-
-}  // namespace ppapi
diff --git a/ppapi/shared_impl/compositor_layer_data.h b/ppapi/shared_impl/compositor_layer_data.h
deleted file mode 100644
index 179b693..0000000
--- a/ppapi/shared_impl/compositor_layer_data.h
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PPAPI_SHARED_IMPL_COMPOSITOR_LAYER_DATA_H_
-#define PPAPI_SHARED_IMPL_COMPOSITOR_LAYER_DATA_H_
-
-#include <stdint.h>
-#include <string.h>
-
-#include <memory>
-
-#include "base/logging.h"
-#include "gpu/command_buffer/common/mailbox.h"
-#include "gpu/command_buffer/common/sync_token.h"
-#include "ppapi/c/ppb_compositor_layer.h"
-#include "ppapi/shared_impl/host_resource.h"
-#include "ppapi/shared_impl/ppapi_shared_export.h"
-
-namespace ppapi {
-
-struct PPAPI_SHARED_EXPORT CompositorLayerData {
-
-  struct Transform {
-    Transform() {
-      matrix[0] = 1.0f;
-      matrix[1] = 0.0f;
-      matrix[2] = 0.0f;
-      matrix[3] = 0.0f;
-      matrix[4] = 0.0f;
-      matrix[5] = 1.0f;
-      matrix[6] = 0.0f;
-      matrix[7] = 0.0f;
-      matrix[8] = 0.0f;
-      matrix[9] = 0.0f;
-      matrix[10] = 1.0f;
-      matrix[11] = 0.0f;
-      matrix[12] = 0.0f;
-      matrix[13] = 0.0f;
-      matrix[14] = 0.0f;
-      matrix[15] = 1.0f;
-    }
-
-    float matrix[16];
-  };
-
-  struct LayerCommon {
-    LayerCommon()
-       : size(PP_MakeSize(0, 0)),
-         clip_rect(PP_MakeRectFromXYWH(0, 0, 0, 0)),
-         blend_mode(PP_BLENDMODE_SRC_OVER),
-         opacity(1.0f),
-         resource_id(0) {
-    }
-
-    PP_Size size;
-    PP_Rect clip_rect;
-    Transform transform;
-    PP_BlendMode blend_mode;
-    float opacity;
-    uint32_t resource_id;
-  };
-
-  struct ColorLayer {
-    ColorLayer() : red(0.0f), green(0.0f), blue(0.0f), alpha(0.0f) {}
-
-    float red;
-    float green;
-    float blue;
-    float alpha;
-  };
-
-  struct ImageLayer {
-    ImageLayer()
-       : resource(0),
-         source_rect(PP_MakeFloatRectFromXYWH(0.0f, 0.0f, 0.0f, 0.0f)) {}
-
-    PP_Resource resource;
-    PP_FloatRect source_rect;
-  };
-
-  struct TextureLayer {
-    TextureLayer()
-       : target(0),
-         source_rect(PP_MakeFloatRectFromXYWH(0.0f, 0.0f, 1.0f, 1.0f)),
-         premult_alpha(true) {}
-
-    gpu::Mailbox mailbox;
-    gpu::SyncToken sync_token;
-    uint32_t target;
-    PP_FloatRect source_rect;
-    bool premult_alpha;
-  };
-
-  CompositorLayerData() {}
-
-  CompositorLayerData(const CompositorLayerData& other) {
-    *this = other;
-  }
-
-  bool is_null() const {
-    return !(color || texture || image);
-  }
-
-  bool is_valid() const {
-    int i = 0;
-    if (color) ++i;
-    if (texture) ++i;
-    if (image) ++i;
-    return i == 1;
-  }
-
-  const CompositorLayerData& operator=(const CompositorLayerData& other);
-
-  LayerCommon common;
-  std::unique_ptr<ColorLayer> color;
-  std::unique_ptr<TextureLayer> texture;
-  std::unique_ptr<ImageLayer> image;
-};
-
-}  // namespace ppapi
-
-#endif  // PPAPI_SHARED_IMPL_COMPOSITOR_LAYER_DATA_H_
diff --git a/ppapi/shared_impl/resource.h b/ppapi/shared_impl/resource.h
index e670d36c..b6a0c11 100644
--- a/ppapi/shared_impl/resource.h
+++ b/ppapi/shared_impl/resource.h
@@ -33,8 +33,6 @@
   F(PPB_Buffer_API)                     \
   F(PPB_CameraCapabilities_API)         \
   F(PPB_CameraDevice_API)               \
-  F(PPB_Compositor_API)                 \
-  F(PPB_CompositorLayer_API)            \
   F(PPB_DeviceRef_API)                  \
   F(PPB_Ext_CrxFileSystem_Private_API)  \
   F(PPB_FileChooser_API)                \
diff --git a/ppapi/tests/all_c_includes.h b/ppapi/tests/all_c_includes.h
index c7500be..e6094dd0 100644
--- a/ppapi/tests/all_c_includes.h
+++ b/ppapi/tests/all_c_includes.h
@@ -52,8 +52,6 @@
 #include "ppapi/c/ppb_audio.h"
 #include "ppapi/c/ppb_audio_buffer.h"
 #include "ppapi/c/ppb_audio_config.h"
-#include "ppapi/c/ppb_compositor.h"
-#include "ppapi/c/ppb_compositor_layer.h"
 #include "ppapi/c/ppb_console.h"
 #include "ppapi/c/ppb_core.h"
 #include "ppapi/c/ppb_file_io.h"
diff --git a/ppapi/tests/all_cpp_includes.h b/ppapi/tests/all_cpp_includes.h
index 59b1e45..c4f4561 100644
--- a/ppapi/tests/all_cpp_includes.h
+++ b/ppapi/tests/all_cpp_includes.h
@@ -12,8 +12,6 @@
 #include "ppapi/cpp/audio_buffer.h"
 #include "ppapi/cpp/audio_config.h"
 #include "ppapi/cpp/completion_callback.h"
-#include "ppapi/cpp/compositor.h"
-#include "ppapi/cpp/compositor_layer.h"
 #include "ppapi/cpp/core.h"
 #include "ppapi/cpp/dev/buffer_dev.h"
 #include "ppapi/cpp/dev/device_ref_dev.h"
diff --git a/ppapi/tests/test_compositor.cc b/ppapi/tests/test_compositor.cc
deleted file mode 100644
index df1aad4f..0000000
--- a/ppapi/tests/test_compositor.cc
+++ /dev/null
@@ -1,434 +0,0 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ppapi/tests/test_compositor.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "ppapi/c/ppb_opengles2.h"
-#include "ppapi/cpp/compositor.h"
-#include "ppapi/cpp/compositor_layer.h"
-#include "ppapi/cpp/image_data.h"
-#include "ppapi/lib/gl/gles2/gl2ext_ppapi.h"
-#include "ppapi/lib/gl/include/GLES2/gl2.h"
-#include "ppapi/lib/gl/include/GLES2/gl2ext.h"
-#include "ppapi/tests/test_utils.h"
-
-namespace {
-
-const float kMatrix[16] = {
-  1.0f, 0.0f, 0.0f, 0.0f,
-  0.0f, 1.0f, 0.0f, 0.0f,
-  0.0f, 0.0f, 1.0f, 0.0f,
-  0.0f, 0.0f, 0.0f, 1.0f,
-};
-
-}  // namespace
-
-REGISTER_TEST_CASE(Compositor);
-
-#define VERIFY(r) do { \
-    std::string result = (r); \
-    if (!result.empty()) \
-      return result; \
-  } while (false)
-
-bool TestCompositor::Init() {
-  if (!CheckTestingInterface())
-    return false;
-
-  if (!glInitializePPAPI(pp::Module::Get()->get_browser_interface()))
-    return false;
-
-  return true;
-}
-
-void TestCompositor::RunTests(const std::string& filter) {
-  RUN_CALLBACK_TEST(TestCompositor, Release, filter);
-  RUN_CALLBACK_TEST(TestCompositor, ReleaseWithoutCommit, filter);
-  RUN_CALLBACK_TEST(TestCompositor, CommitTwoTimesWithoutChange, filter);
-  RUN_CALLBACK_TEST(TestCompositor, General, filter);
-
-  RUN_CALLBACK_TEST(TestCompositor, ReleaseUnbound, filter);
-  RUN_CALLBACK_TEST(TestCompositor, ReleaseWithoutCommitUnbound, filter);
-  RUN_CALLBACK_TEST(TestCompositor, CommitTwoTimesWithoutChangeUnbound, filter);
-  RUN_CALLBACK_TEST(TestCompositor, GeneralUnbound, filter);
-
-  RUN_CALLBACK_TEST(TestCompositor, BindUnbind, filter);
-}
-
-std::string TestCompositor::TestRelease() {
-  return TestReleaseInternal(true);
-}
-
-std::string TestCompositor::TestReleaseWithoutCommit() {
-  return TestReleaseWithoutCommitInternal(true);
-}
-
-std::string TestCompositor::TestCommitTwoTimesWithoutChange() {
-  return TestCommitTwoTimesWithoutChangeInternal(true);
-}
-
-std::string TestCompositor::TestGeneral() {
-  return TestGeneralInternal(true);
-}
-
-std::string TestCompositor::TestReleaseUnbound() {
-  return TestReleaseInternal(false);
-}
-
-std::string TestCompositor::TestReleaseWithoutCommitUnbound() {
-  return TestReleaseWithoutCommitInternal(false);
-}
-
-std::string TestCompositor::TestCommitTwoTimesWithoutChangeUnbound() {
-  return TestCommitTwoTimesWithoutChangeInternal(false);
-}
-
-std::string TestCompositor::TestGeneralUnbound() {
-  return TestGeneralInternal(false);
-}
-
-// TODO(penghuang): refactor common part in all tests to a member function.
-std::string TestCompositor::TestBindUnbind() {
-  // Setup GLES2
-  const int32_t attribs[] = {
-    PP_GRAPHICS3DATTRIB_WIDTH, 16,
-    PP_GRAPHICS3DATTRIB_HEIGHT, 16,
-    PP_GRAPHICS3DATTRIB_NONE
-  };
-  pp::Graphics3D graphics_3d(instance_, attribs);
-  ASSERT_FALSE(graphics_3d.is_null());
-  glSetCurrentContextPPAPI(graphics_3d.pp_resource());
-
-  pp::Compositor compositor = pp::Compositor(instance_);
-  ASSERT_FALSE(compositor.is_null());
-
-  // Add layers on an unbound compositor.
-  pp::CompositorLayer color_layer = compositor.AddLayer();
-  ASSERT_FALSE(color_layer.is_null());
-
-  VERIFY(SetColorLayer(color_layer, PP_OK));
-
-  uint32_t texture = 0;
-  VERIFY(CreateTexture(&texture));
-  pp::CompositorLayer texture_layer = compositor.AddLayer();
-  ASSERT_FALSE(texture_layer.is_null());
-  TestCompletionCallback texture_release_callback(instance_->pp_instance(),
-                                                  PP_REQUIRED);
-  ASSERT_EQ(PP_OK_COMPLETIONPENDING,
-            texture_layer.SetTexture(graphics_3d, GL_TEXTURE_2D, texture,
-                                     pp::Size(100, 100),
-                                     texture_release_callback.GetCallback()));
-
-  pp::ImageData image;
-  VERIFY(CreateImage(&image));
-  pp::CompositorLayer image_layer = compositor.AddLayer();
-  TestCompletionCallback image_release_callback(instance_->pp_instance(),
-                                                PP_REQUIRED);
-  ASSERT_EQ(PP_OK_COMPLETIONPENDING,
-            image_layer.SetImage(image, pp::Size(100, 100),
-                                 image_release_callback.GetCallback()));
-
-  // Commit layers to the chromium compositor.
-  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
-  callback.WaitForResult(compositor.CommitLayers(callback.GetCallback()));
-  CHECK_CALLBACK_BEHAVIOR(callback);
-  ASSERT_EQ(PP_OK, callback.result());
-
-  // Bind the compositor and call CommitLayers() again.
-  ASSERT_TRUE(instance_->BindGraphics(compositor));
-  callback.WaitForResult(compositor.CommitLayers(callback.GetCallback()));
-  CHECK_CALLBACK_BEHAVIOR(callback);
-  ASSERT_EQ(PP_OK, callback.result());
-
-  // Unbind the compositor and call CommitLayers() again.
-  ASSERT_TRUE(instance_->BindGraphics(pp::Compositor()));
-  callback.WaitForResult(compositor.CommitLayers(callback.GetCallback()));
-  CHECK_CALLBACK_BEHAVIOR(callback);
-  ASSERT_EQ(PP_OK, callback.result());
-
-  // Reset layers and call CommitLayers() again.
-  ASSERT_EQ(PP_OK, compositor.ResetLayers());
-  callback.WaitForResult(compositor.CommitLayers(callback.GetCallback()));
-  CHECK_CALLBACK_BEHAVIOR(callback);
-  ASSERT_EQ(PP_OK, callback.result());
-
-  texture_release_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
-  ASSERT_EQ(PP_OK, texture_release_callback.result());
-  ReleaseTexture(texture);
-
-  image_release_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
-  ASSERT_EQ(PP_OK, image_release_callback.result());
-
-  // Reset
-  glSetCurrentContextPPAPI(0);
-
-  PASS();
-}
-
-std::string TestCompositor::TestReleaseInternal(bool bind) {
-  // Setup GLES2
-  const int32_t attribs[] = {
-    PP_GRAPHICS3DATTRIB_WIDTH, 16,
-    PP_GRAPHICS3DATTRIB_HEIGHT, 16,
-    PP_GRAPHICS3DATTRIB_NONE
-  };
-  pp::Graphics3D graphics_3d(instance_, attribs);
-  ASSERT_FALSE(graphics_3d.is_null());
-  glSetCurrentContextPPAPI(graphics_3d.pp_resource());
-
-  pp::Compositor compositor = pp::Compositor(instance_);
-  ASSERT_FALSE(compositor.is_null());
-
-  // Bind the compositor to the instance
-  if (bind)
-    ASSERT_TRUE(instance_->BindGraphics(compositor));
-
-  pp::CompositorLayer color_layer = compositor.AddLayer();
-  ASSERT_FALSE(color_layer.is_null());
-
-  VERIFY(SetColorLayer(color_layer, PP_OK));
-
-  uint32_t texture = 0;
-  VERIFY(CreateTexture(&texture));
-  pp::CompositorLayer texture_layer = compositor.AddLayer();
-  ASSERT_FALSE(texture_layer.is_null());
-  TestCompletionCallback texture_release_callback(instance_->pp_instance(),
-                                                  PP_REQUIRED);
-  ASSERT_EQ(PP_OK_COMPLETIONPENDING,
-            texture_layer.SetTexture(graphics_3d, GL_TEXTURE_2D, texture,
-                                     pp::Size(100, 100),
-                                     texture_release_callback.GetCallback()));
-
-  pp::ImageData image;
-  VERIFY(CreateImage(&image));
-  pp::CompositorLayer image_layer = compositor.AddLayer();
-  TestCompletionCallback image_release_callback(instance_->pp_instance(),
-                                                PP_REQUIRED);
-  ASSERT_EQ(PP_OK_COMPLETIONPENDING,
-            image_layer.SetImage(image, pp::Size(100, 100),
-                                 image_release_callback.GetCallback()));
-
-  // Commit layers to the chromium compositor.
-  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
-  callback.WaitForResult(compositor.CommitLayers(callback.GetCallback()));
-  CHECK_CALLBACK_BEHAVIOR(callback);
-  ASSERT_EQ(PP_OK, callback.result());
-
-  // Release the compositor, and then release_callback will be aborted.
-  compositor = pp::Compositor();
-
-  texture_release_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
-  ASSERT_EQ(PP_ERROR_ABORTED, texture_release_callback.result());
-  ReleaseTexture(texture);
-
-  image_release_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
-  ASSERT_EQ(PP_ERROR_ABORTED, image_release_callback.result());
-
-  // Reset
-  glSetCurrentContextPPAPI(0);
-
-  PASS();
-}
-
-std::string TestCompositor::TestReleaseWithoutCommitInternal(bool bind) {
-  // Setup GLES2
-  const int32_t attribs[] = {
-    PP_GRAPHICS3DATTRIB_WIDTH, 16,
-    PP_GRAPHICS3DATTRIB_HEIGHT, 16,
-    PP_GRAPHICS3DATTRIB_NONE
-  };
-  pp::Graphics3D graphics_3d(instance_, attribs);
-  ASSERT_FALSE(graphics_3d.is_null());
-  glSetCurrentContextPPAPI(graphics_3d.pp_resource());
-
-  pp::Compositor compositor = pp::Compositor(instance_);
-  ASSERT_FALSE(compositor.is_null());
-
-  // Bind the compositor to the instance
-  if (bind)
-    ASSERT_TRUE(instance_->BindGraphics(compositor));
-
-  pp::CompositorLayer color_layer = compositor.AddLayer();
-  ASSERT_FALSE(color_layer.is_null());
-
-  VERIFY(SetColorLayer(color_layer, PP_OK));
-
-  uint32_t texture = 0;
-  VERIFY(CreateTexture(&texture));
-  pp::CompositorLayer texture_layer = compositor.AddLayer();
-  ASSERT_FALSE(texture_layer.is_null());
-  TestCompletionCallback texture_release_callback(instance_->pp_instance(),
-                                                  PP_REQUIRED);
-  ASSERT_EQ(PP_OK_COMPLETIONPENDING,
-            texture_layer.SetTexture(graphics_3d, GL_TEXTURE_2D, texture,
-                                     pp::Size(100, 100),
-                                     texture_release_callback.GetCallback()));
-
-  pp::ImageData image;
-  VERIFY(CreateImage(&image));
-  pp::CompositorLayer image_layer = compositor.AddLayer();
-  TestCompletionCallback image_release_callback(instance_->pp_instance(),
-                                                PP_REQUIRED);
-  ASSERT_EQ(PP_OK_COMPLETIONPENDING,
-            image_layer.SetImage(image, pp::Size(100, 100),
-                                 image_release_callback.GetCallback()));
-
-  // Release the compositor, and then release_callback will be aborted.
-  compositor = pp::Compositor();
-
-  // All release_callbacks should be called.
-  texture_release_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
-  ASSERT_EQ(PP_ERROR_ABORTED, texture_release_callback.result());
-  ReleaseTexture(texture);
-
-  image_release_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
-  ASSERT_EQ(PP_ERROR_ABORTED, image_release_callback.result());
-
-  // The layer associated to the compositor will become invalidated.
-  VERIFY(SetColorLayer(color_layer, PP_ERROR_BADRESOURCE));
-
-  // Reset
-  glSetCurrentContextPPAPI(0);
-
-  PASS();
-}
-
-std::string TestCompositor::TestCommitTwoTimesWithoutChangeInternal(bool bind) {
-  pp::Compositor compositor(instance_);
-  ASSERT_FALSE(compositor.is_null());
-  if (bind)
-    ASSERT_TRUE(instance_->BindGraphics(compositor));
-  pp::CompositorLayer layer = compositor.AddLayer();
-  ASSERT_FALSE(layer.is_null());
-  VERIFY(SetColorLayer(layer, PP_OK));
-
-  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
-  callback.WaitForResult(compositor.CommitLayers(callback.GetCallback()));
-  CHECK_CALLBACK_BEHAVIOR(callback);
-  ASSERT_EQ(PP_OK, callback.result());
-
-  // CommitLayers() without any change.
-  callback.WaitForResult(compositor.CommitLayers(callback.GetCallback()));
-  CHECK_CALLBACK_BEHAVIOR(callback);
-  ASSERT_EQ(PP_OK, callback.result());
-
-  PASS();
-}
-
-std::string TestCompositor::TestGeneralInternal(bool bind) {
-  // Setup GLES2
-  const int32_t attribs[] = {
-    PP_GRAPHICS3DATTRIB_WIDTH, 16,
-    PP_GRAPHICS3DATTRIB_HEIGHT, 16,
-    PP_GRAPHICS3DATTRIB_NONE
-  };
-  pp::Graphics3D graphics_3d(instance_, attribs);
-  ASSERT_FALSE(graphics_3d.is_null());
-  glSetCurrentContextPPAPI(graphics_3d.pp_resource());
-
-  // All functions should work with a bound compositor
-  pp::Compositor compositor(instance_);
-  ASSERT_FALSE(compositor.is_null());
-  if (bind)
-    ASSERT_TRUE(instance_->BindGraphics(compositor));
-
-  pp::CompositorLayer color_layer = compositor.AddLayer();
-  ASSERT_FALSE(color_layer.is_null());
-  VERIFY(SetColorLayer(color_layer, PP_OK));
-
-  uint32_t texture = 0;
-  VERIFY(CreateTexture(&texture));
-  pp::CompositorLayer texture_layer = compositor.AddLayer();
-  ASSERT_FALSE(texture_layer.is_null());
-  TestCompletionCallback texture_release_callback(instance_->pp_instance(),
-                                                  PP_REQUIRED);
-  ASSERT_EQ(PP_OK_COMPLETIONPENDING,
-            texture_layer.SetTexture(graphics_3d, texture, GL_TEXTURE_2D,
-                                     pp::Size(100, 100),
-                                     texture_release_callback.GetCallback()));
-
-  pp::ImageData image;
-  VERIFY(CreateImage(&image));
-  pp::CompositorLayer image_layer = compositor.AddLayer();
-  TestCompletionCallback image_release_callback(instance_->pp_instance(),
-                                                PP_REQUIRED);
-  ASSERT_EQ(PP_OK_COMPLETIONPENDING,
-            image_layer.SetImage(image, pp::Size(100, 100),
-                                 image_release_callback.GetCallback()));
-
-  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
-  callback.WaitForResult(compositor.CommitLayers(callback.GetCallback()));
-  CHECK_CALLBACK_BEHAVIOR(callback);
-  ASSERT_EQ(PP_OK, callback.result());
-
-  // After ResetLayers(), all layers should be invalidated.
-  ASSERT_EQ(PP_OK, compositor.ResetLayers());
-  VERIFY(SetColorLayer(color_layer, PP_ERROR_BADRESOURCE));
-
-  // Commit empty layer stack to the chromium compositor, and then the texture
-  // and the image will be released by the chromium compositor soon.
-  callback.WaitForResult(compositor.CommitLayers(callback.GetCallback()));
-  CHECK_CALLBACK_BEHAVIOR(callback);
-  ASSERT_EQ(PP_OK, callback.result());
-
-  texture_release_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
-  ASSERT_EQ(PP_OK, texture_release_callback.result());
-  ReleaseTexture(texture);
-
-  image_release_callback.WaitForResult(PP_OK_COMPLETIONPENDING);
-  ASSERT_EQ(PP_OK, image_release_callback.result());
-
-  // Reset
-  glSetCurrentContextPPAPI(0);
-
-  PASS();
-}
-
-std::string TestCompositor::CreateTexture(uint32_t* texture) {
-  glGenTextures(1, texture);
-  ASSERT_NE(0, *texture);
-  glBindTexture(GL_TEXTURE_2D, *texture);
-  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 400, 400, 0,
-               GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-  glBindTexture(GL_TEXTURE_2D, 0);
-
-  return std::string();
-}
-
-std::string TestCompositor::ReleaseTexture(uint32_t texture) {
-  ASSERT_NE(0u, texture);
-  glDeleteTextures(1, &texture);
-
-  return std::string();
-}
-
-std::string TestCompositor::CreateImage(pp::ImageData* image) {
-  *image = pp::ImageData(instance_, PP_IMAGEDATAFORMAT_RGBA_PREMUL,
-                         pp::Size(400, 400), false);
-  ASSERT_FALSE(image->is_null());
-
-  return std::string();
-}
-
-std::string TestCompositor::SetColorLayer(
-    pp::CompositorLayer layer, int32_t result) {
-  ASSERT_EQ(result, layer.SetColor(255, 255, 255, 255, pp::Size(100, 100)));
-  ASSERT_EQ(result, layer.SetClipRect(pp::Rect(0, 0, 50, 50)));
-  ASSERT_EQ(result, layer.SetTransform(kMatrix));
-  ASSERT_EQ(result, layer.SetOpacity(128));
-
-  return std::string();
-}
-
-
diff --git a/ppapi/tests/test_compositor.h b/ppapi/tests/test_compositor.h
deleted file mode 100644
index 5e418a9..0000000
--- a/ppapi/tests/test_compositor.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PAPPI_TESTS_TEST_COMPOSITOR_H_
-#define PAPPI_TESTS_TEST_COMPOSITOR_H_
-
-#include <stdint.h>
-
-#include <set>
-#include <string>
-
-#include "ppapi/cpp/compositor.h"
-#include "ppapi/cpp/compositor_layer.h"
-#include "ppapi/cpp/graphics_3d.h"
-#include "ppapi/lib/gl/include/GLES2/gl2.h"
-#include "ppapi/tests/test_case.h"
-
-class TestCompositor : public TestCase {
- public:
-  TestCompositor(TestingInstance* instance) : TestCase(instance) {}
-
-  // TestCase implementation.
-  virtual bool Init();
-  virtual void RunTests(const std::string& filter);
-
- private:
-  // Various tests.
-  std::string TestRelease();
-  std::string TestReleaseWithoutCommit();
-  std::string TestCommitTwoTimesWithoutChange();
-  std::string TestGeneral();
-
-  std::string TestReleaseUnbound();
-  std::string TestReleaseWithoutCommitUnbound();
-  std::string TestCommitTwoTimesWithoutChangeUnbound();
-  std::string TestGeneralUnbound();
-
-  std::string TestBindUnbind();
-
-  std::string TestReleaseInternal(bool bind);
-  std::string TestReleaseWithoutCommitInternal(bool bind);
-  std::string TestCommitTwoTimesWithoutChangeInternal(bool bind);
-  std::string TestGeneralInternal(bool bind);
-
-  // Helper functions
-  std::string CreateTexture(uint32_t* texture);
-  std::string ReleaseTexture(uint32_t texture);
-  std::string CreateImage(pp::ImageData* image);
-  std::string SetColorLayer(pp::CompositorLayer layer, int32_t result);
-
-};
-
-#endif  // PAPPI_TESTS_TEST_COMPOSItor_H_
diff --git a/ppapi/thunk/BUILD.gn b/ppapi/thunk/BUILD.gn
index e1299f6..8be4fa9d 100644
--- a/ppapi/thunk/BUILD.gn
+++ b/ppapi/thunk/BUILD.gn
@@ -40,10 +40,6 @@
     "ppb_camera_capabilities_private_thunk.cc",
     "ppb_camera_device_api.h",
     "ppb_camera_device_private_thunk.cc",
-    "ppb_compositor_api.h",
-    "ppb_compositor_layer_api.h",
-    "ppb_compositor_layer_thunk.cc",
-    "ppb_compositor_thunk.cc",
     "ppb_console_thunk.cc",
     "ppb_cursor_control_thunk.cc",
     "ppb_device_ref_api.h",
diff --git a/ppapi/thunk/interfaces_ppb_public_dev_channel.h b/ppapi/thunk/interfaces_ppb_public_dev_channel.h
index 64dd189..73c3698 100644
--- a/ppapi/thunk/interfaces_ppb_public_dev_channel.h
+++ b/ppapi/thunk/interfaces_ppb_public_dev_channel.h
@@ -5,13 +5,12 @@
 // Please see inteface_ppb_public_stable for the documentation on the format of
 // this file.
 
+// no-include-guard-because-multiply-included
+
 #include "ppapi/thunk/interfaces_preamble.h"
 
 // Interfaces go here.
 PROXIED_IFACE(PPB_AUDIOENCODER_INTERFACE_0_1, PPB_AudioEncoder_0_1)
-PROXIED_IFACE(PPB_COMPOSITOR_INTERFACE_0_1, PPB_Compositor_0_1)
-PROXIED_IFACE(PPB_COMPOSITORLAYER_INTERFACE_0_1, PPB_CompositorLayer_0_1)
-PROXIED_IFACE(PPB_COMPOSITORLAYER_INTERFACE_0_2, PPB_CompositorLayer_0_2)
 PROXIED_IFACE(PPB_VIDEODECODER_INTERFACE_0_1, PPB_VideoDecoder_0_1)
 PROXIED_IFACE(PPB_VIDEOENCODER_INTERFACE_0_1, PPB_VideoEncoder_0_1)
 PROXIED_IFACE(PPB_VPNPROVIDER_INTERFACE_0_1, PPB_VpnProvider_0_1)
diff --git a/ppapi/thunk/ppb_compositor_api.h b/ppapi/thunk/ppb_compositor_api.h
deleted file mode 100644
index 895f8f2..0000000
--- a/ppapi/thunk/ppb_compositor_api.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PPAPI_THUNK_PPB_COMPOSITOR_API_H_
-#define PPAPI_THUNK_PPB_COMPOSITOR_API_H_
-
-#include <stdint.h>
-
-#include "base/memory/ref_counted.h"
-#include "ppapi/c/ppb_compositor.h"
-#include "ppapi/shared_impl/tracked_callback.h"
-
-namespace ppapi {
-namespace thunk {
-
-class PPAPI_THUNK_EXPORT PPB_Compositor_API {
- public:
-  virtual ~PPB_Compositor_API() {}
-  virtual PP_Resource AddLayer() = 0;
-  virtual int32_t CommitLayers(
-      const scoped_refptr<ppapi::TrackedCallback>& callback) = 0;
-  virtual int32_t ResetLayers() = 0;
-};
-
-}  // namespace thunk
-}  // namespace ppapi
-
-#endif  // PPAPI_THUNK_PPB_COMPOSITOR_API_H_
diff --git a/ppapi/thunk/ppb_compositor_layer_api.h b/ppapi/thunk/ppb_compositor_layer_api.h
deleted file mode 100644
index 392a2d70..0000000
--- a/ppapi/thunk/ppb_compositor_layer_api.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef PPAPI_THUNK_PPB_COMPOSITOR_LAYER_API_H_
-#define PPAPI_THUNK_PPB_COMPOSITOR_LAYER_API_H_
-
-#include <stdint.h>
-
-#include "base/memory/ref_counted.h"
-#include "ppapi/c/ppb_compositor_layer.h"
-#include "ppapi/shared_impl/tracked_callback.h"
-
-namespace ppapi {
-namespace thunk {
-
-class PPAPI_THUNK_EXPORT PPB_CompositorLayer_API {
- public:
-  virtual ~PPB_CompositorLayer_API() {}
-  virtual int32_t SetColor(float red,
-                           float green,
-                           float blue,
-                           float alpha,
-                           const PP_Size* size) = 0;
-  virtual int32_t SetTexture0_1(
-      PP_Resource context,
-      uint32_t texture,
-      const PP_Size* size,
-      const scoped_refptr<ppapi::TrackedCallback>& callback) = 0;
-  virtual int32_t SetTexture(
-      PP_Resource context,
-      uint32_t target,
-      uint32_t texture,
-      const PP_Size* size,
-      const scoped_refptr<ppapi::TrackedCallback>& callback) = 0;
-  virtual int32_t SetImage(
-      PP_Resource image_data,
-      const PP_Size* size,
-      const scoped_refptr<ppapi::TrackedCallback>& callback) = 0;
-  virtual int32_t SetClipRect(const PP_Rect* rects) = 0;
-  virtual int32_t SetTransform(const float matrix[16]) = 0;
-  virtual int32_t SetOpacity(float opacity) = 0;
-  virtual int32_t SetBlendMode(PP_BlendMode mode) = 0;
-  virtual int32_t SetSourceRect(const PP_FloatRect* rect) = 0;
-  virtual int32_t SetPremultipliedAlpha(PP_Bool premult) = 0;
-};
-
-}  // namespace thunk
-}  // namespace ppapi
-
-#endif  // PPAPI_THUNK_PPB_COMPOSITOR_API_H_
diff --git a/ppapi/thunk/ppb_compositor_layer_thunk.cc b/ppapi/thunk/ppb_compositor_layer_thunk.cc
deleted file mode 100644
index 29645403..0000000
--- a/ppapi/thunk/ppb_compositor_layer_thunk.cc
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// From ppb_compositor_layer.idl modified Wed Jan 27 17:10:16 2016.
-
-#include <stdint.h>
-
-#include "ppapi/c/pp_completion_callback.h"
-#include "ppapi/c/pp_errors.h"
-#include "ppapi/c/ppb_compositor_layer.h"
-#include "ppapi/shared_impl/tracked_callback.h"
-#include "ppapi/thunk/enter.h"
-#include "ppapi/thunk/ppapi_thunk_export.h"
-#include "ppapi/thunk/ppb_compositor_layer_api.h"
-
-namespace ppapi {
-namespace thunk {
-
-namespace {
-
-PP_Bool IsCompositorLayer(PP_Resource resource) {
-  VLOG(4) << "PPB_CompositorLayer::IsCompositorLayer()";
-  EnterResource<PPB_CompositorLayer_API> enter(resource, false);
-  return PP_FromBool(enter.succeeded());
-}
-
-int32_t SetColor(PP_Resource layer,
-                 float red,
-                 float green,
-                 float blue,
-                 float alpha,
-                 const struct PP_Size* size) {
-  VLOG(4) << "PPB_CompositorLayer::SetColor()";
-  EnterResource<PPB_CompositorLayer_API> enter(layer, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.object()->SetColor(red, green, blue, alpha, size);
-}
-
-int32_t SetTexture_0_1(PP_Resource layer,
-                       PP_Resource context,
-                       uint32_t texture,
-                       const struct PP_Size* size,
-                       struct PP_CompletionCallback cc) {
-  VLOG(4) << "PPB_CompositorLayer::SetTexture_0_1()";
-  EnterResource<PPB_CompositorLayer_API> enter(layer, cc, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.SetResult(
-      enter.object()->SetTexture0_1(context, texture, size, enter.callback()));
-}
-
-int32_t SetTexture(PP_Resource layer,
-                   PP_Resource context,
-                   uint32_t target,
-                   uint32_t texture,
-                   const struct PP_Size* size,
-                   struct PP_CompletionCallback cc) {
-  VLOG(4) << "PPB_CompositorLayer::SetTexture()";
-  EnterResource<PPB_CompositorLayer_API> enter(layer, cc, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.SetResult(enter.object()->SetTexture(context, target, texture,
-                                                    size, enter.callback()));
-}
-
-int32_t SetImage(PP_Resource layer,
-                 PP_Resource image_data,
-                 const struct PP_Size* size,
-                 struct PP_CompletionCallback cc) {
-  VLOG(4) << "PPB_CompositorLayer::SetImage()";
-  EnterResource<PPB_CompositorLayer_API> enter(layer, cc, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.SetResult(
-      enter.object()->SetImage(image_data, size, enter.callback()));
-}
-
-int32_t SetClipRect(PP_Resource layer, const struct PP_Rect* rect) {
-  VLOG(4) << "PPB_CompositorLayer::SetClipRect()";
-  EnterResource<PPB_CompositorLayer_API> enter(layer, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.object()->SetClipRect(rect);
-}
-
-int32_t SetTransform(PP_Resource layer, const float matrix[16]) {
-  VLOG(4) << "PPB_CompositorLayer::SetTransform()";
-  EnterResource<PPB_CompositorLayer_API> enter(layer, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.object()->SetTransform(matrix);
-}
-
-int32_t SetOpacity(PP_Resource layer, float opacity) {
-  VLOG(4) << "PPB_CompositorLayer::SetOpacity()";
-  EnterResource<PPB_CompositorLayer_API> enter(layer, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.object()->SetOpacity(opacity);
-}
-
-int32_t SetBlendMode(PP_Resource layer, PP_BlendMode mode) {
-  VLOG(4) << "PPB_CompositorLayer::SetBlendMode()";
-  EnterResource<PPB_CompositorLayer_API> enter(layer, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.object()->SetBlendMode(mode);
-}
-
-int32_t SetSourceRect(PP_Resource layer, const struct PP_FloatRect* rect) {
-  VLOG(4) << "PPB_CompositorLayer::SetSourceRect()";
-  EnterResource<PPB_CompositorLayer_API> enter(layer, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.object()->SetSourceRect(rect);
-}
-
-int32_t SetPremultipliedAlpha(PP_Resource layer, PP_Bool premult) {
-  VLOG(4) << "PPB_CompositorLayer::SetPremultipliedAlpha()";
-  EnterResource<PPB_CompositorLayer_API> enter(layer, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.object()->SetPremultipliedAlpha(premult);
-}
-
-const PPB_CompositorLayer_0_1 g_ppb_compositorlayer_thunk_0_1 = {
-    &IsCompositorLayer, &SetColor,
-    &SetTexture_0_1,    &SetImage,
-    &SetClipRect,       &SetTransform,
-    &SetOpacity,        &SetBlendMode,
-    &SetSourceRect,     &SetPremultipliedAlpha};
-
-const PPB_CompositorLayer_0_2 g_ppb_compositorlayer_thunk_0_2 = {
-    &IsCompositorLayer, &SetColor,
-    &SetTexture,        &SetImage,
-    &SetClipRect,       &SetTransform,
-    &SetOpacity,        &SetBlendMode,
-    &SetSourceRect,     &SetPremultipliedAlpha};
-
-}  // namespace
-
-PPAPI_THUNK_EXPORT const PPB_CompositorLayer_0_1*
-GetPPB_CompositorLayer_0_1_Thunk() {
-  return &g_ppb_compositorlayer_thunk_0_1;
-}
-
-PPAPI_THUNK_EXPORT const PPB_CompositorLayer_0_2*
-GetPPB_CompositorLayer_0_2_Thunk() {
-  return &g_ppb_compositorlayer_thunk_0_2;
-}
-
-}  // namespace thunk
-}  // namespace ppapi
diff --git a/ppapi/thunk/ppb_compositor_thunk.cc b/ppapi/thunk/ppb_compositor_thunk.cc
deleted file mode 100644
index f1593c3..0000000
--- a/ppapi/thunk/ppb_compositor_thunk.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// From ppb_compositor.idl modified Wed Jan 27 17:39:22 2016.
-
-#include <stdint.h>
-
-#include "ppapi/c/pp_completion_callback.h"
-#include "ppapi/c/pp_errors.h"
-#include "ppapi/c/ppb_compositor.h"
-#include "ppapi/shared_impl/tracked_callback.h"
-#include "ppapi/thunk/enter.h"
-#include "ppapi/thunk/ppapi_thunk_export.h"
-#include "ppapi/thunk/ppb_compositor_api.h"
-
-namespace ppapi {
-namespace thunk {
-
-namespace {
-
-PP_Bool IsCompositor(PP_Resource resource) {
-  VLOG(4) << "PPB_Compositor::IsCompositor()";
-  EnterResource<PPB_Compositor_API> enter(resource, false);
-  return PP_FromBool(enter.succeeded());
-}
-
-PP_Resource Create(PP_Instance instance) {
-  VLOG(4) << "PPB_Compositor::Create()";
-  EnterResourceCreation enter(instance);
-  if (enter.failed())
-    return 0;
-  return enter.functions()->CreateCompositor(instance);
-}
-
-PP_Resource AddLayer(PP_Resource compositor) {
-  VLOG(4) << "PPB_Compositor::AddLayer()";
-  EnterResource<PPB_Compositor_API> enter(compositor, true);
-  if (enter.failed())
-    return 0;
-  return enter.object()->AddLayer();
-}
-
-int32_t CommitLayers(PP_Resource compositor, struct PP_CompletionCallback cc) {
-  VLOG(4) << "PPB_Compositor::CommitLayers()";
-  EnterResource<PPB_Compositor_API> enter(compositor, cc, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.SetResult(enter.object()->CommitLayers(enter.callback()));
-}
-
-int32_t ResetLayers(PP_Resource compositor) {
-  VLOG(4) << "PPB_Compositor::ResetLayers()";
-  EnterResource<PPB_Compositor_API> enter(compositor, true);
-  if (enter.failed())
-    return enter.retval();
-  return enter.object()->ResetLayers();
-}
-
-const PPB_Compositor_0_1 g_ppb_compositor_thunk_0_1 = {
-    &IsCompositor, &Create, &AddLayer, &CommitLayers, &ResetLayers};
-
-}  // namespace
-
-PPAPI_THUNK_EXPORT const PPB_Compositor_0_1* GetPPB_Compositor_0_1_Thunk() {
-  return &g_ppb_compositor_thunk_0_1;
-}
-
-}  // namespace thunk
-}  // namespace ppapi
diff --git a/ppapi/thunk/resource_creation_api.h b/ppapi/thunk/resource_creation_api.h
index 3c2080d1..43f9302 100644
--- a/ppapi/thunk/resource_creation_api.h
+++ b/ppapi/thunk/resource_creation_api.h
@@ -133,7 +133,6 @@
                                         PP_AudioSampleRate sample_rate,
                                         uint32_t sample_frame_count) = 0;
   virtual PP_Resource CreateCameraDevicePrivate(PP_Instance instance) = 0;
-  virtual PP_Resource CreateCompositor(PP_Instance instance) = 0;
   virtual PP_Resource CreateFileChooser(PP_Instance instance,
                                         PP_FileChooserMode_Dev mode,
                                         const PP_Var& accept_types) = 0;
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 300036d..b2b0d5b 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -243,8 +243,6 @@
     ]
   }
 
-  defines = [ "SKCMS_LEGACY_TF_INVERT" ]
-
   # LLVM automatically sets the equivalent of GCC's -mfp16-format=ieee on ARM
   # builds by default, while GCC itself does not. We need it to enable support
   # for half-precision floating point data types used by SKCMS on ARM.
diff --git a/skia/ext/benchmarking_canvas.cc b/skia/ext/benchmarking_canvas.cc
index 6d514fbb..9021c1d 100644
--- a/skia/ext/benchmarking_canvas.cc
+++ b/skia/ext/benchmarking_canvas.cc
@@ -209,12 +209,6 @@
     FlagsBuilder builder('|');
     builder.addFlag(paint.isAntiAlias(), "AntiAlias");
     builder.addFlag(paint.isDither(), "Dither");
-    builder.addFlag(paint.isFakeBoldText(), "FakeBoldText");
-    builder.addFlag(paint.isLinearText(), "LinearText");
-    builder.addFlag(paint.isSubpixelText(), "SubpixelText");
-    builder.addFlag(paint.isLCDRenderText(), "LCDRenderText");
-    builder.addFlag(paint.isEmbeddedBitmapText(), "EmbeddedBitmapText");
-    builder.addFlag(paint.isAutohinted(), "Autohinted");
 
     val->SetString("Flags", builder.str());
   }
@@ -228,15 +222,6 @@
                    gFilterQualityStrings[paint.getFilterQuality()]);
   }
 
-  if (paint.getTextSize() != default_paint.getTextSize())
-    val->SetDouble("TextSize", paint.getTextSize());
-
-  if (paint.getTextScaleX() != default_paint.getTextScaleX())
-    val->SetDouble("TextScaleX", paint.getTextScaleX());
-
-  if (paint.getTextSkewX() != default_paint.getTextSkewX())
-    val->SetDouble("TextSkewX", paint.getTextSkewX());
-
   if (paint.getColorFilter())
     val->Set("ColorFilter", AsValue(*paint.getColorFilter()));
 
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index d8314c8..fbe87f6 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -697,6 +697,11 @@
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
+              "cipd_package": "chromium/android_webview/tools/cts_archive",
+              "location": "android_webview/tools/cts_archive",
+              "revision": "version:1.1"
+            },
+            {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
               "location": "bin",
               "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index aa769d7..b69e03f 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -4824,30 +4824,6 @@
       }
     ]
   },
-  "linux-blink-gen-property-trees": {
-    "isolated_scripts": [
-      {
-        "args": [
-          "--num-retries=3",
-          "--debug",
-          "--additional-driver-flag=--enable-blink-features=BlinkGenPropertyTrees"
-        ],
-        "isolate_name": "webkit_layout_tests_exparchive",
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "webkit_layout_tests",
-        "results_handler": "layout tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 12
-        }
-      }
-    ]
-  },
   "linux-blink-heap-incremental-marking": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 32b07f86..df5f3b32 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -21512,7 +21512,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21538,7 +21538,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21566,7 +21566,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21593,7 +21593,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21623,7 +21623,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21650,7 +21650,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21678,7 +21678,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21704,7 +21704,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21730,7 +21730,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21758,7 +21758,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21787,7 +21787,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21810,7 +21810,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21833,7 +21833,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21862,7 +21862,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21891,7 +21891,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21920,7 +21920,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21956,7 +21956,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -21990,7 +21990,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22024,7 +22024,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22058,7 +22058,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22096,7 +22096,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22137,7 +22137,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22186,7 +22186,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22221,7 +22221,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22255,7 +22255,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22292,7 +22292,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22327,7 +22327,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22362,7 +22362,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22397,7 +22397,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22432,7 +22432,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22467,7 +22467,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22502,7 +22502,7 @@
         "trigger_script": {
           "args": [
             "--multiple-trigger-configs",
-            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
             "--multiple-dimension-script-verbose",
             "True"
           ],
@@ -22577,7 +22577,16 @@
           "expiration": 21600,
           "shards": 4
         },
-        "test": "angle_end2end_tests"
+        "test": "angle_end2end_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22594,7 +22603,16 @@
           ],
           "expiration": 21600
         },
-        "test": "angle_gles1_conformance_tests"
+        "test": "angle_gles1_conformance_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22613,7 +22631,16 @@
           ],
           "expiration": 21600
         },
-        "test": "angle_unittests"
+        "test": "angle_unittests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22631,7 +22658,16 @@
           ],
           "expiration": 21600
         },
-        "test": "angle_white_box_tests"
+        "test": "angle_white_box_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22652,7 +22688,16 @@
           ],
           "expiration": 21600
         },
-        "test": "browser_tests"
+        "test": "browser_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22670,7 +22715,16 @@
           ],
           "expiration": 21600
         },
-        "test": "gl_tests"
+        "test": "gl_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22689,7 +22743,16 @@
           ],
           "expiration": 21600
         },
-        "test": "gl_tests"
+        "test": "gl_tests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22706,7 +22769,16 @@
           ],
           "expiration": 21600
         },
-        "test": "gl_unittests"
+        "test": "gl_unittests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22723,7 +22795,16 @@
           ],
           "expiration": 21600
         },
-        "test": "gles2_conform_test"
+        "test": "gles2_conform_test",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22742,7 +22823,16 @@
           ],
           "expiration": 21600
         },
-        "test": "gles2_conform_test"
+        "test": "gles2_conform_test",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22762,7 +22852,16 @@
           ],
           "expiration": 21600
         },
-        "test": "gles2_conform_test"
+        "test": "gles2_conform_test",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "swarming": {
@@ -22776,7 +22875,16 @@
           ],
           "expiration": 21600
         },
-        "test": "gpu_unittests"
+        "test": "gpu_unittests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "swarming": {
@@ -22790,7 +22898,16 @@
           ],
           "expiration": 21600
         },
-        "test": "swiftshader_unittests"
+        "test": "swiftshader_unittests",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22810,7 +22927,16 @@
           ],
           "expiration": 21600
         },
-        "test": "video_decode_accelerator_unittest"
+        "test": "video_decode_accelerator_unittest",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22830,7 +22956,16 @@
           ],
           "expiration": 21600
         },
-        "test": "video_decode_accelerator_unittest"
+        "test": "video_decode_accelerator_unittest",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       },
       {
         "args": [
@@ -22850,7 +22985,16 @@
           ],
           "expiration": 21600
         },
-        "test": "video_decode_accelerator_unittest"
+        "test": "video_decode_accelerator_unittest",
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
+        }
       }
     ],
     "isolated_scripts": [
@@ -22877,6 +23021,15 @@
           ],
           "expiration": 21600,
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -22902,6 +23055,15 @@
           ],
           "expiration": 21600,
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -22927,6 +23089,15 @@
           ],
           "expiration": 21600,
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -22952,6 +23123,15 @@
           ],
           "expiration": 21600,
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -22981,6 +23161,15 @@
           ],
           "expiration": 21600,
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -23013,6 +23202,15 @@
           ],
           "expiration": 21600,
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -23053,6 +23251,15 @@
           ],
           "expiration": 21600,
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -23079,6 +23286,15 @@
           ],
           "expiration": 21600,
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -23104,6 +23320,15 @@
           ],
           "expiration": 21600,
           "idempotent": false
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -23132,6 +23357,15 @@
           "expiration": 21600,
           "idempotent": false,
           "shards": 20
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -23158,6 +23392,15 @@
           "expiration": 21600,
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -23184,6 +23427,15 @@
           "expiration": 21600,
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -23210,6 +23462,15 @@
           "expiration": 21600,
           "idempotent": false,
           "shards": 6
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -23236,6 +23497,15 @@
           "expiration": 21600,
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -23262,6 +23532,15 @@
           "expiration": 21600,
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       },
       {
@@ -23288,6 +23567,15 @@
           "expiration": 21600,
           "idempotent": false,
           "shards": 2
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-trigger-configs",
+            "[{\"gpu\": \"10de:1cb3-23.21.13.8792\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}, {\"gpu\": \"10de:1cb3-23.21.14.1195\", \"os\": \"Windows-2008ServerR2-SP1\", \"pool\": \"Chrome-GPU\"}]",
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/trigger_multiple_dimensions.py"
         }
       }
     ]
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 9aeff90..c95e65de 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -1681,6 +1681,46 @@
       {
         "args": [
           "--gtest-benchmark-name",
+          "angle_perftests",
+          "--non-telemetry=true",
+          "--migrated-test=true",
+          "--shard-timeout=300"
+        ],
+        "isolate_name": "angle_perftests",
+        "merge": {
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "angle_perftests",
+        "override_compile_targets": [
+          "angle_perftests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912",
+              "os": "Windows-10",
+              "pool": "chrome.tests.perf"
+            }
+          ],
+          "expiration": 7200,
+          "hard_timeout": 36000,
+          "ignore_task_failure": false,
+          "io_timeout": 1800,
+          "shards": 1,
+          "upload_test_results": true
+        },
+        "trigger_script": {
+          "args": [
+            "--multiple-dimension-script-verbose",
+            "True"
+          ],
+          "script": "//testing/trigger_scripts/perf_device_trigger.py"
+        }
+      },
+      {
+        "args": [
+          "--gtest-benchmark-name",
           "base_perftests",
           "--non-telemetry=true",
           "--migrated-test=true"
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index f293fa113..bcfe07b7 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -952,7 +952,7 @@
               "os": "Windows-10-15063"
             }
           ],
-          "shards": 10
+          "shards": 15
         },
         "test": "browser_tests"
       },
@@ -970,7 +970,7 @@
               "os": "Windows-10-15063"
             }
           ],
-          "shards": 15
+          "shards": 20
         },
         "test": "browser_tests"
       },
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index 0e2de83ea..4d7cc1bde 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -464,13 +464,15 @@
           'test_results_presentation.py',
       }
     if not tester_config.get('skip_cipd_packages', False):
-      result['swarming']['cipd_packages'] = [
+      cipd_packages = result['swarming'].get('cipd_packages', [])
+      cipd_packages.append(
         {
           'cipd_package': 'infra/tools/luci/logdog/butler/${platform}',
           'location': 'bin',
           'revision': 'git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c',
         }
-      ]
+      )
+      result['swarming']['cipd_packages'] = cipd_packages
     if not tester_config.get('skip_output_links', False):
       result['swarming']['output_links'] = [
         {
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 9e84992..14cef641 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -2695,7 +2695,6 @@
   },
   "webkit_layout_tests": {
     "args": [
-      "--xvfb",
       "../../third_party/blink/tools/run_web_tests.py",
       "--clobber-old-results",
       "--debug-rwt-logging",
@@ -2709,7 +2708,6 @@
   },
   "webkit_layout_tests_exparchive": {
     "args": [
-      "--xvfb",
       "../../third_party/blink/tools/run_web_tests.py",
       "--seed",
       "4",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 6a6e2b00..f951e674 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -219,6 +219,11 @@
         'args': [
           '--disable-features=WebRTC-H264WithOpenH264FFmpeg',
         ],
+        'swarming': {
+          # This is for slow test execution that often becomes a critical path of
+          # swarming jobs. crbug.com/868114
+          'shards': 15,
+        }
       },
       # client.v8.chromium
       'Linux - Future (dbg)': {
@@ -759,6 +764,11 @@
         'args': [
           '--disable-features=WebRTC-H264WithOpenH264FFmpeg',
         ],
+        # This is for slow test execution that often becomes a critical path of
+        # swarming jobs. crbug.com/868114
+        'swarming': {
+          'shards': 20,
+        },
       },
     },
   },
@@ -1376,12 +1386,6 @@
           '--debug',
         ],
       },
-      'linux-blink-gen-property-trees': {
-        'args': [
-          '--debug',
-          '--additional-driver-flag=--enable-blink-features=BlinkGenPropertyTrees',
-        ],
-      },
       'linux-blink-heap-incremental-marking': {
         'args': [
           '--debug',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 763b19ee5..51572ca 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1473,11 +1473,6 @@
           'isolated_scripts': 'chromium_webkit_isolated_scripts',
         },
       },
-      'linux-blink-gen-property-trees': {
-        'test_suites': {
-          'isolated_scripts': 'chromium_webkit_isolated_scripts',
-        },
-      },
       'linux-blink-heap-incremental-marking': {
         'test_suites': {
           'gtest_tests': 'chromium_gtests',
@@ -2815,6 +2810,13 @@
           'gpu_telemetry_tests': 'gpu_fyi_win7_nvidia_release_telemetry_tests',
         },
         'use_multi_dimension_trigger_script': True,
+        'alternate_swarming_dimensions': [
+          {
+            'gpu': '10de:1cb3-23.21.14.1195',
+            'os': 'Windows-2008ServerR2-SP1',
+            'pool': 'Chrome-GPU',
+          },
+        ],
       },
       'Win7 FYI dEQP Release (AMD)': {
         'os_type': 'win',
@@ -2837,6 +2839,14 @@
           'gtest_tests': 'gpu_fyi_win7_gtests',
           'gpu_telemetry_tests': 'gpu_fyi_win7_nvidia_release_telemetry_tests',
         },
+        'use_multi_dimension_trigger_script': True,
+        'alternate_swarming_dimensions': [
+          {
+            'gpu': '10de:1cb3-23.21.14.1195',
+            'os': 'Windows-2008ServerR2-SP1',
+            'pool': 'Chrome-GPU',
+          },
+        ],
       },
       'Win7 FYI x64 dEQP Release (NVIDIA)': {}
     },
diff --git a/testing/scripts/run_telemetry_benchmark_as_googletest.py b/testing/scripts/run_telemetry_benchmark_as_googletest.py
index 464ead6e..44ac668 100755
--- a/testing/scripts/run_telemetry_benchmark_as_googletest.py
+++ b/testing/scripts/run_telemetry_benchmark_as_googletest.py
@@ -26,6 +26,11 @@
 followed by a subsequent Python script. It could be generalized to
 invoke an arbitrary executable.
 
+
+TESTING:
+To test changes to this script, please run
+cd tools/perf
+./run_tests ScriptsSmokeTest.testRunTelemetryBenchmarkAsGoogletest
 """
 
 import argparse
@@ -86,7 +91,7 @@
   return rc
 
 def run_benchmark(args, rest_args, histogram_results):
-  """  Run benchmark with args.
+  """Run benchmark with args.
 
   Args:
     args: the option object resulted from parsing commandline args required for
@@ -102,6 +107,24 @@
     json_test_results: json object contains the Pass/Fail data of the benchmark.
     benchmark_log: string contains the stdout/stderr of the benchmark run.
   """
+  # TODO(crbug.com/920002): These arguments cannot go into
+  # run_performance_tests.py because
+  # run_gtest_perf_tests.py does not yet support them. Note that ideally
+  # we would use common.BaseIsolatedScriptArgsAdapter, but this will take
+  # a good deal of refactoring to accomplish.
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      '--isolated-script-test-repeat', type=int, required=False)
+  parser.add_argument(
+      '--isolated-script-test-launcher-retry-limit', type=int, required=False,
+      choices=[0])  # Telemetry does not support retries. crbug.com/894254#c21
+  parser.add_argument(
+      '--isolated-script-test-also-run-disabled-tests',
+      default=False, action='store_true', required=False)
+  # Parse leftover args not already parsed in run_performance_tests.py or in
+  # main().
+  args, rest_args = parser.parse_known_args(args=rest_args, namespace=args)
+
   env = os.environ.copy()
   env['CHROME_HEADLESS'] = '1'
 
@@ -123,15 +146,16 @@
     filter_list = common.extract_filter_list(args.isolated_script_test_filter)
     # Need to convert this to a valid regex.
     filter_regex = '(' + '|'.join(filter_list) + ')'
-    cmd_args = cmd_args + [
-      '--story-filter=' + filter_regex
-    ]
+    cmd_args.append('--story-filter=' + filter_regex)
+  if args.isolated_script_test_repeat:
+    cmd_args.append('--pageset-repeat=' + str(args.isolated_script_test_repeat))
+  if args.isolated_script_test_also_run_disabled_tests:
+    cmd_args.append('--also-run-disabled-tests')
+  cmd_args.append('--output-dir=' + tempfile_dir)
+  cmd_args.append('--output-format=json-test-results')
+  cmd = [sys.executable] + cmd_args
   rc = 1  # Set default returncode in case there is an exception.
   try:
-    cmd = [sys.executable] + cmd_args + [
-      '--output-dir', tempfile_dir,
-      '--output-format=json-test-results',
-    ]
     if args.xvfb:
       rc = xvfb.run_executable(cmd, env=env, stdoutfile=stdoutfile)
     else:
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 643e11d0..5fdbbe1 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2045,6 +2045,21 @@
             ]
         }
     ],
+    "GwpAsanMallocWindowsLaunch": [
+        {
+            "platforms": [
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "GwpAsanMalloc"
+                    ]
+                }
+            ]
+        }
+    ],
     "HTTPReallyBadFinal": [
         {
             "platforms": [
diff --git a/third_party/blink/public/web/DEPS b/third_party/blink/public/web/DEPS
index 7cac5ba4..1169ede 100644
--- a/third_party/blink/public/web/DEPS
+++ b/third_party/blink/public/web/DEPS
@@ -11,6 +11,7 @@
     "+cc/input/browser_controls_state.h",
     "+cc/paint/paint_canvas.h",
     "+cc/paint/paint_flags.h",
+    "+cc/trees/element_id.h",
     "+mojo/public",
     "+services/network/public/mojom/cors.mojom-shared.h",
     "+services/network/public/mojom/cors_origin_pattern.mojom-shared.h",
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h
index a9bf37b..195aa2c 100644
--- a/third_party/blink/public/web/web_navigation_params.h
+++ b/third_party/blink/public/web/web_navigation_params.h
@@ -16,6 +16,7 @@
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_content_security_policy.h"
 #include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/public/platform/web_source_location.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
@@ -166,6 +167,11 @@
   // Whether this navigation is a result of client redirect.
   bool is_client_redirect = false;
 
+  // The origin in which a navigation should commit. When provided, Blink
+  // should use this origin directly and not compute locally the new document
+  // origin.
+  WebSecurityOrigin origin_to_commit;
+
   // The devtools token for this navigation. See DocumentLoader
   // for details.
   base::UnguessableToken devtools_navigation_token;
diff --git a/third_party/blink/public/web/web_widget.h b/third_party/blink/public/web/web_widget.h
index c5fb93d..2e90d02f 100644
--- a/third_party/blink/public/web/web_widget.h
+++ b/third_party/blink/public/web/web_widget.h
@@ -35,6 +35,7 @@
 #include "base/time/time.h"
 #include "cc/input/browser_controls_state.h"
 #include "cc/paint/paint_canvas.h"
+#include "cc/trees/element_id.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_float_size.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
@@ -179,6 +180,12 @@
   virtual void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel,
                                                  bool has_scrolled_by_touch) {}
 
+  virtual void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      cc::ElementId scroll_latched_element_id) {}
+  virtual void SendScrollEndEventFromImplSide(
+      cc::ElementId scroll_latched_element_id) {}
+
   // Called to inform the WebWidget that mouse capture was lost.
   virtual void MouseCaptureLost() {}
 
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index d63ca150..bfe0325 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -2039,6 +2039,7 @@
     "layout/ng/geometry/ng_physical_offset_test.cc",
     "layout/ng/geometry/ng_physical_rect_test.cc",
     "layout/ng/inline/ng_baseline_test.cc",
+    "layout/ng/inline/ng_caret_navigator_test.cc",
     "layout/ng/inline/ng_caret_position_test.cc",
     "layout/ng/inline/ng_inline_fragment_traversal_test.cc",
     "layout/ng/inline/ng_inline_items_builder_test.cc",
diff --git a/third_party/blink/renderer/core/css/css_image_value.cc b/third_party/blink/renderer/core/css/css_image_value.cc
index ac930500..b8521faa 100644
--- a/third_party/blink/renderer/core/css/css_image_value.cc
+++ b/third_party/blink/renderer/core/css/css_image_value.cc
@@ -80,7 +80,9 @@
     }
     cached_image_ = StyleFetchedImage::Create(
         document, params,
-        image_request_optimization == FetchParameters::kDeferImageLoad);
+        // Only http/https images are eligible to be lazily loaded.
+        params.Url().ProtocolIsInHTTPFamily() &&
+            image_request_optimization == FetchParameters::kDeferImageLoad);
   }
 
   return cached_image_.Get();
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index cddfb1e..2ed8264 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -6450,36 +6450,62 @@
   ContentSecurityPolicy* last_origin_document_csp_ =
       frame_ ? frame_->Loader().GetLastOriginDocumentCSP() : nullptr;
 
+  scoped_refptr<SecurityOrigin> document_origin;
+  cookie_url_ = url_;
+  if (initializer.OriginToCommit()) {
+    // Origin to commit is specified by the browser process, it must be taken
+    // and used directly. It is currently supplied only for session history
+    // navigations, where the origin was already calcuated previously and
+    // stored on the session history entry.
+    document_origin = initializer.OriginToCommit();
+  } else {
+    if (Document* owner_document = initializer.OwnerDocument()) {
+      // Alias certain security properties from |owner_document|. Used for
+      // the case of about:blank pages inheriting the security properties of
+      // their requestor context.
+      // Note that this is currently somewhat broken; Blink always inherits
+      // from the parent or opener, even though it should actually be
+      // inherited from the request initiator.
+      document_origin = owner_document->GetMutableSecurityOrigin();
+      cookie_url_ = owner_document->CookieURL();
+      if (url_.IsEmpty())
+        last_origin_document_csp_ = owner_document->GetContentSecurityPolicy();
+    } else {
+      // Otherwise, create an origin that propagates precursor information
+      // as needed. For non-opaque origins, this creates a standard tuple
+      // origin, but for opaque origins, it creates an origin with the
+      // initiator origin as the precursor.
+      document_origin = SecurityOrigin::CreateWithReferenceOrigin(
+          url_, initializer.InitiatorOrigin().get());
+    }
+  }
+
   if (IsSandboxed(kSandboxOrigin)) {
-    cookie_url_ = url_;
-    scoped_refptr<SecurityOrigin> security_origin =
-        SecurityOrigin::CreateUniqueOpaque();
+    DCHECK(!initializer.ContextDocument());
+    scoped_refptr<SecurityOrigin> sandboxed_origin =
+        initializer.OriginToCommit() ? initializer.OriginToCommit()
+                                     : document_origin->DeriveNewOpaqueOrigin();
+
     // If we're supposed to inherit our security origin from our
     // owner, but we're also sandboxed, the only things we inherit are
     // the origin's potential trustworthiness and the ability to
     // load local resources. The latter lets about:blank iframes in
     // file:// URL documents load images and other resources from
     // the file system.
-    Document* owner = initializer.OwnerDocument();
-    if (owner) {
-      if (owner->GetSecurityOrigin()->IsPotentiallyTrustworthy())
-        security_origin->SetOpaqueOriginIsPotentiallyTrustworthy(true);
-      if (owner->GetSecurityOrigin()->CanLoadLocalResources())
-        security_origin->GrantLoadLocalResources();
-      if (url_.IsEmpty())
-        last_origin_document_csp_ = owner->GetContentSecurityPolicy();
+    if (initializer.OwnerDocument()) {
+      if (document_origin->IsPotentiallyTrustworthy())
+        sandboxed_origin->SetOpaqueOriginIsPotentiallyTrustworthy(true);
+      if (document_origin->CanLoadLocalResources())
+        sandboxed_origin->GrantLoadLocalResources();
+      if (url_.IsEmpty()) {
+        last_origin_document_csp_ =
+            initializer.OwnerDocument()->GetContentSecurityPolicy();
+      }
     }
-    SetSecurityOrigin(std::move(security_origin));
-  } else if (Document* owner = initializer.OwnerDocument()) {
-    cookie_url_ = owner->CookieURL();
-    // We alias the SecurityOrigins to match Firefox, see Bug 15313
-    // https://bugs.webkit.org/show_bug.cgi?id=15313
-    SetSecurityOrigin(owner->GetMutableSecurityOrigin());
-    if (url_.IsEmpty())
-      last_origin_document_csp_ = owner->GetContentSecurityPolicy();
-  } else {
     cookie_url_ = url_;
-    SetSecurityOrigin(SecurityOrigin::Create(url_));
+    SetSecurityOrigin(std::move(sandboxed_origin));
+  } else {
+    SetSecurityOrigin(std::move(document_origin));
   }
 
   // Set the address space before setting up CSP, as the latter may override
diff --git a/third_party/blink/renderer/core/dom/document_init.cc b/third_party/blink/renderer/core/dom/document_init.cc
index af81534..19dd3659 100644
--- a/third_party/blink/renderer/core/dom/document_init.cc
+++ b/third_party/blink/renderer/core/dom/document_init.cc
@@ -168,10 +168,27 @@
 
 DocumentInit& DocumentInit::WithOwnerDocument(Document* owner_document) {
   DCHECK(!owner_document_);
+  DCHECK(!initiator_origin_ || !owner_document ||
+         owner_document->GetSecurityOrigin() == initiator_origin_);
   owner_document_ = owner_document;
   return *this;
 }
 
+DocumentInit& DocumentInit::WithInitiatorOrigin(
+    scoped_refptr<const SecurityOrigin> initiator_origin) {
+  DCHECK(!initiator_origin_);
+  DCHECK(!initiator_origin || !owner_document_ ||
+         owner_document_->GetSecurityOrigin() == initiator_origin);
+  initiator_origin_ = std::move(initiator_origin);
+  return *this;
+}
+
+DocumentInit& DocumentInit::WithOriginToCommit(
+    scoped_refptr<SecurityOrigin> origin_to_commit) {
+  origin_to_commit_ = std::move(origin_to_commit);
+  return *this;
+}
+
 DocumentInit& DocumentInit::WithRegistrationContext(
     V0CustomElementRegistrationContext* registration_context) {
   DCHECK(!create_new_registration_context_);
diff --git a/third_party/blink/renderer/core/dom/document_init.h b/third_party/blink/renderer/core/dom/document_init.h
index e3d0dff0..ac56d1c73 100644
--- a/third_party/blink/renderer/core/dom/document_init.h
+++ b/third_party/blink/renderer/core/dom/document_init.h
@@ -97,6 +97,21 @@
   DocumentInit& WithOwnerDocument(Document*);
   Document* OwnerDocument() const { return owner_document_.Get(); }
 
+  // Specifies the SecurityOrigin in which the URL was requested. This is
+  // relevant for determining properties of the resulting document's origin
+  // when loading data: and about: schemes.
+  DocumentInit& WithInitiatorOrigin(
+      scoped_refptr<const SecurityOrigin> initiator_origin);
+  const scoped_refptr<const SecurityOrigin>& InitiatorOrigin() const {
+    return initiator_origin_;
+  }
+
+  DocumentInit& WithOriginToCommit(
+      scoped_refptr<SecurityOrigin> origin_to_commit);
+  const scoped_refptr<SecurityOrigin>& OriginToCommit() const {
+    return origin_to_commit_;
+  }
+
   DocumentInit& WithRegistrationContext(V0CustomElementRegistrationContext*);
   V0CustomElementRegistrationContext* RegistrationContext(Document*) const;
   DocumentInit& WithNewRegistrationContext();
@@ -119,6 +134,25 @@
   KURL url_;
   Member<Document> owner_document_;
 
+  // Initiator origin is used for calculating the document origin when the
+  // navigation is started in a different process. In such cases, the document
+  // which initiates the navigation sends its origin to the browser process and
+  // it is provided by the browser process here. It is used for cases such as
+  // data: URLs, which inherit their origin from the initiator of the
+  // navigation.
+  // Note: about:blank should also behave this way, however currently it
+  // inherits its origin from the parent frame or opener, regardless of whether
+  // it is the initiator or not.
+  scoped_refptr<const SecurityOrigin> initiator_origin_;
+
+  // The |origin_to_commit_| is to be used directly without calculating the
+  // document origin at initialization time. It is specified by the browser
+  // process for session history navigations. This allows us to preserve
+  // the origin across session history and ensure the exact same origin
+  // is present on such navigations to URLs that inherit their origins (e.g.
+  // about:blank and data: URLs).
+  scoped_refptr<SecurityOrigin> origin_to_commit_;
+
   Member<V0CustomElementRegistrationContext> registration_context_;
   bool create_new_registration_context_;
 };
diff --git a/third_party/blink/renderer/core/dom/live_node_list_registry.cc b/third_party/blink/renderer/core/dom/live_node_list_registry.cc
index 1ca504d3..7250fafc 100644
--- a/third_party/blink/renderer/core/dom/live_node_list_registry.cc
+++ b/third_party/blink/renderer/core/dom/live_node_list_registry.cc
@@ -44,7 +44,7 @@
 
 void LiveNodeListRegistry::ClearWeakMembers(Visitor*) {
   auto* it = std::remove_if(data_.begin(), data_.end(), [](Entry entry) {
-    return !ThreadHeap::IsHeapObjectAlive(entry.first);
+    return !ObjectAliveTrait<LiveNodeListBase>::IsHeapObjectAlive(entry.first);
   });
   if (it == data_.end())
     return;
diff --git a/third_party/blink/renderer/core/dom/shadow_root.cc b/third_party/blink/renderer/core/dom/shadow_root.cc
index 7e88116..04bd6dd 100644
--- a/third_party/blink/renderer/core/dom/shadow_root.cc
+++ b/third_party/blink/renderer/core/dom/shadow_root.cc
@@ -56,6 +56,7 @@
 }
 
 struct SameSizeAsShadowRoot : public DocumentFragment, public TreeScope {
+  char empty_class_fields_due_to_gc_mixin_marker[1];
   Member<void*> member[3];
   unsigned counters_and_flags[1];
 };
diff --git a/third_party/blink/renderer/core/editing/selection_modifier_character.cc b/third_party/blink/renderer/core/editing/selection_modifier_character.cc
index 194724b..10b1f76 100644
--- a/third_party/blink/renderer/core/editing/selection_modifier_character.cc
+++ b/third_party/blink/renderer/core/editing/selection_modifier_character.cc
@@ -39,6 +39,9 @@
 #include "third_party/blink/renderer/core/layout/api/line_layout_item.h"
 #include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
 #include "third_party/blink/renderer/core/layout/line/root_inline_box.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
 
 namespace blink {
 
@@ -148,6 +151,13 @@
       return box.Root().GetLogicalStartNonPseudoBox();
     return box.Root().GetLogicalEndNonPseudoBox();
   }
+
+  static NGCaretNavigator::VisualCaretMovementResult ForwardPositionOf(
+      const NGCaretNavigator& caret_navigator,
+      const NGCaretNavigator::Position& caret_position) {
+    DCHECK(RuntimeEnabledFeatures::BidiCaretAffinityEnabled());
+    return caret_navigator.LeftPositionOf(caret_position);
+  }
 };
 
 // The traversal strategy for |RightPositionOf()|.
@@ -254,6 +264,13 @@
       return box.Root().GetLogicalEndNonPseudoBox();
     return box.Root().GetLogicalStartNonPseudoBox();
   }
+
+  static NGCaretNavigator::VisualCaretMovementResult ForwardPositionOf(
+      const NGCaretNavigator& caret_navigator,
+      const NGCaretNavigator::Position& caret_position) {
+    DCHECK(RuntimeEnabledFeatures::BidiCaretAffinityEnabled());
+    return caret_navigator.RightPositionOf(caret_position);
+  }
 };
 
 template <typename Traversal>
@@ -478,17 +495,111 @@
 }
 
 template <typename Strategy, typename Traversal>
+PositionWithAffinityTemplate<Strategy> TraverseWithBidiCaretAffinity(
+    const PositionWithAffinityTemplate<Strategy> start_position_with_affinity) {
+  const PositionTemplate<Strategy> start_position =
+      start_position_with_affinity.GetPosition();
+  const Position start_position_in_dom = ToPositionInDOMTree(start_position);
+  if (start_position_in_dom.IsNull())
+    return PositionWithAffinityTemplate<Strategy>();
+
+  LayoutBlockFlow* const context =
+      NGOffsetMapping::GetInlineFormattingContextOf(start_position_in_dom);
+  if (!context) {
+    // We reach here if, e.g., the position is in an empty block.
+    // TODO(xiaochengh): Investigate if we reach here for other reaseons.
+#if DCHECK_IS_ON()
+    const Node* node = start_position.ComputeContainerNode();
+    DCHECK(node) << start_position;
+    const LayoutObject* object = node->GetLayoutObject();
+    DCHECK(object) << start_position;
+    DCHECK(object->IsLayoutBlockFlow()) << start_position;
+    DCHECK(!HasRenderedNonAnonymousDescendantsWithHeight(object))
+        << start_position;
+#endif
+    const TextDirection block_direction =
+        DirectionOfEnclosingBlockOf(start_position);
+    // TODO(xiaochengh): Should return the visual line start/end of the position
+    // below.
+    return PositionWithAffinityTemplate<Strategy>(
+        Traversal::ForwardVisuallyDistinctCandidateOf(block_direction,
+                                                      start_position));
+  }
+
+  // TODO(xiaochengh): The double pointer pattern below is confusing and
+  // cumbersome, but necessary for now. Make it easier.
+  std::unique_ptr<NGOffsetMapping> mapping_storage;
+  const NGOffsetMapping* mapping =
+      NGInlineNode::GetOffsetMapping(context, &mapping_storage);
+  DCHECK(mapping);
+
+  const base::Optional<unsigned> start_offset =
+      mapping->GetTextContentOffset(start_position_in_dom);
+  DCHECK(start_offset.has_value());
+
+  // Legacy canonicalization sets downstream affinity when |start_offset| is
+  // at block end. Fix it to upstream in that case.
+  const TextAffinity start_affinity =
+      start_offset.value() < mapping->GetText().length()
+          ? start_position_with_affinity.Affinity()
+          : TextAffinity::kUpstream;
+
+  DCHECK(mapping->GetCaretNavigator());
+  const NGCaretNavigator& caret_navigator = *mapping->GetCaretNavigator();
+
+  const NGCaretNavigator::Position start_caret_position{start_offset.value(),
+                                                        start_affinity};
+  NGCaretNavigator::VisualCaretMovementResult result_caret_position =
+      Traversal::ForwardPositionOf(caret_navigator, start_caret_position);
+  if (result_caret_position.IsWithinContext()) {
+    DCHECK(result_caret_position.position.has_value());
+    const unsigned result_offset = result_caret_position.position->offset;
+    const TextAffinity result_affinity =
+        result_caret_position.position->affinity;
+    const Position result_position =
+        result_affinity == TextAffinity::kDownstream
+            ? mapping->GetLastPosition(result_offset)
+            : mapping->GetFirstPosition(result_offset);
+    const PositionWithAffinity result(result_position, result_affinity);
+    return FromPositionInDOMTree<Strategy>(result);
+  }
+
+  // We reach here if we need to move out of the current block.
+  if (result_caret_position.IsBeforeContext()) {
+    // TODO(xiaochengh): Move to the visual end of the previous block.
+    return PositionWithAffinityTemplate<Strategy>();
+  }
+
+  DCHECK(result_caret_position.IsAfterContext());
+  // TODO(xiaochengh): Move to the visual beginning of the next block.
+  return PositionWithAffinityTemplate<Strategy>();
+}
+
+template <typename Strategy, typename Traversal>
 VisiblePositionTemplate<Strategy> TraverseAlgorithm(
     const VisiblePositionTemplate<Strategy>& visible_position) {
   DCHECK(visible_position.IsValid()) << visible_position;
-  const PositionTemplate<Strategy> pos =
-      TraverseInternalAlgorithm<Strategy, Traversal>(visible_position);
-  // TODO(yosin) Why can't we move left from the last position in a tree?
-  if (pos.AtStartOfTree() || pos.AtEndOfTree())
-    return VisiblePositionTemplate<Strategy>();
 
-  const VisiblePositionTemplate<Strategy> result = CreateVisiblePosition(pos);
-  DCHECK_NE(result.DeepEquivalent(), visible_position.DeepEquivalent());
+  VisiblePositionTemplate<Strategy> result;
+  if (!RuntimeEnabledFeatures::BidiCaretAffinityEnabled()) {
+    const PositionTemplate<Strategy> pos =
+        TraverseInternalAlgorithm<Strategy, Traversal>(visible_position);
+    // TODO(yosin) Why can't we move left from the last position in a tree?
+    if (pos.AtStartOfTree() || pos.AtEndOfTree())
+      return VisiblePositionTemplate<Strategy>();
+    result = CreateVisiblePosition(pos);
+    DCHECK_NE(result.DeepEquivalent(), visible_position.DeepEquivalent());
+  } else {
+    const PositionWithAffinityTemplate<Strategy> pos =
+        TraverseWithBidiCaretAffinity<Strategy, Traversal>(
+            visible_position.ToPositionWithAffinity());
+    if (pos.IsNull())
+      return VisiblePositionTemplate<Strategy>();
+    result = CreateVisiblePosition(pos);
+    DCHECK_NE(result.ToPositionWithAffinity(),
+              visible_position.ToPositionWithAffinity())
+        << visible_position;
+  }
 
   return Traversal::HonorEditingBoundary(
       DirectionOfEnclosingBlockOf(result.DeepEquivalent()), result,
diff --git a/third_party/blink/renderer/core/editing/visible_position.cc b/third_party/blink/renderer/core/editing/visible_position.cc
index 6bd3b46..eceeae9 100644
--- a/third_party/blink/renderer/core/editing/visible_position.cc
+++ b/third_party/blink/renderer/core/editing/visible_position.cc
@@ -36,6 +36,10 @@
 #include "third_party/blink/renderer/core/editing/visible_units.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/html_names.h"
+#include "third_party/blink/renderer/core/layout/layout_object.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/geometry/float_quad.h"
 #include "third_party/blink/renderer/platform/wtf/text/cstring.h"
@@ -95,13 +99,40 @@
     const PositionWithAffinityTemplate<Strategy> upstream_position(
         deep_position, TextAffinity::kUpstream);
 
-    // Check if the upstream and downstream positions are visually the same.
-    // If so, canonicalize affinity to downstream.
-    // TODO(xiaochengh): Check upstream and downstream difference in a more
-    // direct way, instead of checking caret bounds.
-    const IntRect downstream_rect = AbsoluteCaretBoundsOf(downstream_position);
-    const IntRect upstream_rect = AbsoluteCaretBoundsOf(upstream_position);
-    if (downstream_rect != upstream_rect)
+    if (!InSameLine(downstream_position, upstream_position))
+      return VisiblePositionTemplate<Strategy>(upstream_position);
+
+    if (!NGOffsetMapping::AcceptsPosition(ToPositionInDOMTree(deep_position))) {
+      // editing/selection/mixed-editability-10.html reaches here.
+      // We can't check bidi in such case. Use downstream as the default.
+      // TODO(xiaochengh): Investigate why we reach here and how to work around.
+      return VisiblePositionTemplate<Strategy>(downstream_position);
+    }
+
+    // Check if the position is at bidi boundary.
+    const LayoutObject* layout_object =
+        deep_position.AnchorNode()->GetLayoutObject();
+    DCHECK(layout_object) << position_with_affinity;
+    if (!layout_object->IsInline())
+      return VisiblePositionTemplate<Strategy>(downstream_position);
+    LayoutBlockFlow* const context =
+        NGOffsetMapping::GetInlineFormattingContextOf(*layout_object);
+    DCHECK(context);
+
+    // TODO(xiaochengh): The double pointer pattern below is confusing and
+    // cumbersome, but necessary for now. Make it easier.
+    std::unique_ptr<NGOffsetMapping> mapping_storage;
+    const NGOffsetMapping* mapping =
+        NGInlineNode::GetOffsetMapping(context, &mapping_storage);
+    DCHECK(mapping);
+
+    const base::Optional<unsigned> offset =
+        mapping->GetTextContentOffset(ToPositionInDOMTree(deep_position));
+    DCHECK(offset.has_value());
+
+    DCHECK(mapping->GetCaretNavigator());
+    const NGCaretNavigator& caret_navigator = *mapping->GetCaretNavigator();
+    if (caret_navigator.OffsetIsBidiBoundary(offset.value()))
       return VisiblePositionTemplate<Strategy>(upstream_position);
     return VisiblePositionTemplate<Strategy>(downstream_position);
   }
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index b3bbbb81..3d43e4b 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -3293,6 +3293,56 @@
     UseCounter::Count(MainFrameImpl()->GetFrame(), WebFeature::kScrollByTouch);
 }
 
+Node* WebViewImpl::FindNodeFromScrollableCompositorElementId(
+    cc::ElementId element_id) const {
+  if (!GetPage())
+    return nullptr;
+
+  if (element_id == GetPage()->GetVisualViewport().GetCompositorElementId()) {
+    // Return the Document in this case since the window.visualViewport DOM
+    // object is not a node.
+    if (MainFrameImpl())
+      return MainFrameImpl()->GetDocument();
+  }
+
+  if (!GetPage()->GetScrollingCoordinator())
+    return nullptr;
+  ScrollableArea* scrollable_area =
+      GetPage()
+          ->GetScrollingCoordinator()
+          ->ScrollableAreaWithElementIdInAllLocalFrames(element_id);
+  if (!scrollable_area || !scrollable_area->GetLayoutBox())
+    return nullptr;
+
+  return scrollable_area->GetLayoutBox()->GetNode();
+}
+
+void WebViewImpl::SendOverscrollEventFromImplSide(
+    const gfx::Vector2dF& overscroll_delta,
+    cc::ElementId scroll_latched_element_id) {
+  if (!RuntimeEnabledFeatures::OverscrollCustomizationEnabled())
+    return;
+
+  DCHECK(!overscroll_delta.IsZero());
+  Node* target_node =
+      FindNodeFromScrollableCompositorElementId(scroll_latched_element_id);
+  if (target_node) {
+    target_node->GetDocument().EnqueueOverscrollEventForNode(
+        target_node, overscroll_delta.x(), overscroll_delta.y());
+  }
+}
+
+void WebViewImpl::SendScrollEndEventFromImplSide(
+    cc::ElementId scroll_latched_element_id) {
+  if (!RuntimeEnabledFeatures::OverscrollCustomizationEnabled())
+    return;
+
+  Node* target_node =
+      FindNodeFromScrollableCompositorElementId(scroll_latched_element_id);
+  if (target_node)
+    target_node->GetDocument().EnqueueScrollEndEventForNode(target_node);
+}
+
 void WebViewImpl::UpdateLayerTreeViewport() {
   if (!GetPage() || !layer_tree_view_)
     return;
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index cd9421f..2b0e9a2 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -403,6 +403,11 @@
 
   void StopDeferringCommits() { scoped_defer_main_frame_update_.reset(); }
 
+  // This function checks the element ids of ScrollableAreas only and returns
+  // the equivalent DOM Node if such exists.
+  Node* FindNodeFromScrollableCompositorElementId(
+      cc::ElementId element_id) const;
+
   void DeferMainFrameUpdateForTesting();
 
  private:
@@ -448,6 +453,11 @@
   void ApplyViewportChanges(const ApplyViewportChangesArgs& args) override;
   void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel,
                                          bool has_scrolled_by_touch) override;
+  void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      cc::ElementId scroll_latched_element_id) override;
+  void SendScrollEndEventFromImplSide(
+      cc::ElementId scroll_latched_element_id) override;
   void MouseCaptureLost() override;
   void SetFocus(bool enable) override;
   bool SelectionBounds(WebRect& anchor, WebRect& focus) const override;
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 1b5910aa1..e1c9876d 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -48,6 +48,7 @@
 #include "third_party/blink/renderer/core/css/media_query_matcher.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/css/style_media.h"
+#include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/dom/document_init.h"
 #include "third_party/blink/renderer/core/dom/dom_implementation.h"
 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
@@ -74,7 +75,6 @@
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/navigator.h"
-#include "third_party/blink/renderer/core/frame/pausable_timer.h"
 #include "third_party/blink/renderer/core/frame/sandbox_flags.h"
 #include "third_party/blink/renderer/core/frame/screen.h"
 #include "third_party/blink/renderer/core/frame/scroll_to_options.h"
@@ -107,6 +107,7 @@
 #include "third_party/blink/renderer/platform/bindings/microtask.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
 #include "third_party/blink/renderer/platform/scroll/scroll_types.h"
+#include "third_party/blink/renderer/platform/timer.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
@@ -114,72 +115,6 @@
 // Timeout for link preloads to be used after window.onload
 static constexpr TimeDelta kUnusedPreloadTimeout = TimeDelta::FromSeconds(3);
 
-class PostMessageTimer final
-    : public GarbageCollectedFinalized<PostMessageTimer>,
-      public PausableTimer {
-  USING_GARBAGE_COLLECTED_MIXIN(PostMessageTimer);
-
- public:
-  PostMessageTimer(LocalDOMWindow& window,
-                   MessageEvent* event,
-                   scoped_refptr<const SecurityOrigin> target_origin,
-                   std::unique_ptr<SourceLocation> location,
-                   UserGestureToken* user_gesture_token)
-      : PausableTimer(window.document(), TaskType::kPostedMessage),
-        event_(event),
-        window_(&window),
-        target_origin_(std::move(target_origin)),
-        location_(std::move(location)),
-        user_gesture_token_(user_gesture_token),
-        disposal_allowed_(true) {}
-
-  MessageEvent* Event() const { return event_; }
-  const SecurityOrigin* TargetOrigin() const { return target_origin_.get(); }
-  std::unique_ptr<SourceLocation> TakeLocation() {
-    return std::move(location_);
-  }
-  UserGestureToken* GetUserGestureToken() const {
-    return user_gesture_token_.get();
-  }
-  void ContextDestroyed(ExecutionContext* destroyed_context) override {
-    PausableTimer::ContextDestroyed(destroyed_context);
-
-    if (disposal_allowed_)
-      Dispose();
-  }
-
-  // Eager finalization is needed to promptly stop this timer object.
-  // (see DOMTimer comment for more.)
-  EAGERLY_FINALIZE();
-  void Trace(blink::Visitor* visitor) override {
-    visitor->Trace(event_);
-    visitor->Trace(window_);
-    PausableTimer::Trace(visitor);
-  }
-
-  // TODO(alexclarke): Override timerTaskRunner() to pass in a document specific
-  // default task runner.
-
- private:
-  void Fired() override {
-    probe::AsyncTask async_task(window_->document(), this);
-    disposal_allowed_ = false;
-    window_->PostMessageTimerFired(this);
-    Dispose();
-    // Oilpan optimization: unregister as an observer right away.
-    ClearContext();
-  }
-
-  void Dispose() { window_->RemovePostMessageTimer(this); }
-
-  Member<MessageEvent> event_;
-  Member<LocalDOMWindow> window_;
-  scoped_refptr<const SecurityOrigin> target_origin_;
-  std::unique_ptr<SourceLocation> location_;
-  scoped_refptr<UserGestureToken> user_gesture_token_;
-  bool disposal_allowed_;
-};
-
 static void UpdateSuddenTerminationStatus(
     LocalDOMWindow* dom_window,
     bool added_listener,
@@ -603,37 +538,35 @@
   // is problematic; consider imposing a limit or other restriction if this
   // surfaces often as a problem (see crbug.com/587012).
   std::unique_ptr<SourceLocation> location = SourceLocation::Capture(source);
-  PostMessageTimer* timer = MakeGarbageCollected<PostMessageTimer>(
-      *this, event, std::move(target), std::move(location),
-      UserGestureIndicator::CurrentToken());
-  timer->StartOneShot(TimeDelta(), FROM_HERE);
-  timer->PauseIfNeeded();
-  probe::AsyncTaskScheduled(document(), "postMessage", timer);
-  post_message_timers_.insert(timer);
+  document_->GetTaskRunner(TaskType::kPostedMessage)
+      ->PostTask(FROM_HERE,
+                 WTF::Bind(&LocalDOMWindow::DispatchPostMessage,
+                           WrapPersistent(this), WrapPersistent(event),
+                           WrapRefCounted(UserGestureIndicator::CurrentToken()),
+                           std::move(target), std::move(location)));
+  probe::AsyncTaskScheduled(document(), "postMessage", event);
 }
 
-void LocalDOMWindow::PostMessageTimerFired(PostMessageTimer* timer) {
+void LocalDOMWindow::DispatchPostMessage(
+    MessageEvent* event,
+    scoped_refptr<UserGestureToken> token,
+    scoped_refptr<const SecurityOrigin> intended_target_origin,
+    std::unique_ptr<SourceLocation> location) {
+  probe::AsyncTask async_task(document(), event);
   if (!IsCurrentlyDisplayedInFrame())
     return;
 
-  MessageEvent* event = timer->Event();
-
-  UserGestureToken* token = timer->GetUserGestureToken();
   std::unique_ptr<UserGestureIndicator> gesture_indicator;
   if (!RuntimeEnabledFeatures::UserActivationV2Enabled() && token &&
       token->HasGestures() && document()) {
     gesture_indicator =
-        LocalFrame::NotifyUserActivation(document()->GetFrame(), token);
+        LocalFrame::NotifyUserActivation(document()->GetFrame(), token.get());
   }
 
   event->EntangleMessagePorts(document());
 
-  DispatchMessageEventWithOriginCheck(timer->TargetOrigin(), event,
-                                      timer->TakeLocation());
-}
-
-void LocalDOMWindow::RemovePostMessageTimer(PostMessageTimer* timer) {
-  post_message_timers_.erase(timer);
+  DispatchMessageEventWithOriginCheck(intended_target_origin.get(), event,
+                                      std::move(location));
 }
 
 void LocalDOMWindow::DispatchMessageEventWithOriginCheck(
@@ -1594,7 +1527,6 @@
   visitor->Trace(modulator_);
   visitor->Trace(external_);
   visitor->Trace(application_cache_);
-  visitor->Trace(post_message_timers_);
   visitor->Trace(visualViewport_);
   visitor->Trace(event_listener_observers_);
   visitor->Trace(trusted_types_);
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.h b/third_party/blink/renderer/core/frame/local_dom_window.h
index a15415a0..cca42bf 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.h
+++ b/third_party/blink/renderer/core/frame/local_dom_window.h
@@ -60,7 +60,6 @@
 class MessageEvent;
 class Modulator;
 class Navigator;
-class PostMessageTimer;
 class Screen;
 class ScriptedTaskQueueController;
 class ScriptPromise;
@@ -290,8 +289,12 @@
 
   void PrintErrorMessage(const String&) const;
 
-  void PostMessageTimerFired(PostMessageTimer*);
-  void RemovePostMessageTimer(PostMessageTimer*);
+  void DispatchPostMessage(
+      MessageEvent* event,
+      scoped_refptr<UserGestureToken> token,
+      scoped_refptr<const SecurityOrigin> intended_target_origin,
+      std::unique_ptr<SourceLocation>);
+
   void DispatchMessageEventWithOriginCheck(
       const SecurityOrigin* intended_target_origin,
       Event*,
@@ -397,7 +400,6 @@
 
   scoped_refptr<SerializedScriptValue> pending_state_object_;
 
-  HeapHashSet<Member<PostMessageTimer>> post_message_timers_;
   HeapHashSet<WeakMember<EventListenerObserver>> event_listener_observers_;
 
   mutable Member<TrustedTypePolicyFactory> trusted_types_;
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
index 6dc35ef..2267de3 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -286,6 +286,31 @@
   return drag_operation_;
 }
 
+void WebFrameWidgetBase::SendOverscrollEventFromImplSide(
+    const gfx::Vector2dF& overscroll_delta,
+    cc::ElementId scroll_latched_element_id) {
+  if (!RuntimeEnabledFeatures::OverscrollCustomizationEnabled())
+    return;
+
+  Node* target_node = View()->FindNodeFromScrollableCompositorElementId(
+      scroll_latched_element_id);
+  if (target_node) {
+    target_node->GetDocument().EnqueueOverscrollEventForNode(
+        target_node, overscroll_delta.x(), overscroll_delta.y());
+  }
+}
+
+void WebFrameWidgetBase::SendScrollEndEventFromImplSide(
+    cc::ElementId scroll_latched_element_id) {
+  if (!RuntimeEnabledFeatures::OverscrollCustomizationEnabled())
+    return;
+
+  Node* target_node = View()->FindNodeFromScrollableCompositorElementId(
+      scroll_latched_element_id);
+  if (target_node)
+    target_node->GetDocument().EnqueueScrollEndEventForNode(target_node);
+}
+
 WebFloatPoint WebFrameWidgetBase::ViewportToRootFrame(
     const WebFloatPoint& point_in_viewport) const {
   return GetPage()->GetVisualViewport().ViewportToRootFrame(point_in_viewport);
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.h b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
index 91bcfbc7..f2b86f7 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
@@ -97,6 +97,11 @@
                          const WebFloatPoint& screen_point,
                          WebDragOperation) override;
   void DragSourceSystemDragEnded() override;
+  void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      cc::ElementId scroll_latched_element_id) override;
+  void SendScrollEndEventFromImplSide(
+      cc::ElementId scroll_latched_element_id) override;
 
   WebLocalFrame* FocusedWebLocalFrameInWidget() const override;
 
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
index a762ca2c..bba7fb6 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
@@ -103,6 +103,16 @@
   web_view_->RecordWheelAndTouchScrollingCount(has_scrolled_by_wheel,
                                                has_scrolled_by_touch);
 }
+void WebViewFrameWidget::SendOverscrollEventFromImplSide(
+    const gfx::Vector2dF& overscroll_delta,
+    cc::ElementId scroll_latched_element_id) {
+  web_view_->SendOverscrollEventFromImplSide(overscroll_delta,
+                                             scroll_latched_element_id);
+}
+void WebViewFrameWidget::SendScrollEndEventFromImplSide(
+    cc::ElementId scroll_latched_element_id) {
+  web_view_->SendScrollEndEventFromImplSide(scroll_latched_element_id);
+}
 
 void WebViewFrameWidget::MouseCaptureLost() {
   web_view_->MouseCaptureLost();
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.h b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
index 2ec4c80..fdffbc9 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.h
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
@@ -62,6 +62,11 @@
   void ApplyViewportChanges(const ApplyViewportChangesArgs&) override;
   void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel,
                                          bool has_scrolled_by_touch) override;
+  void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      cc::ElementId scroll_latched_element_id) override;
+  void SendScrollEndEventFromImplSide(
+      cc::ElementId scroll_latched_element_id) override;
   void MouseCaptureLost() override;
   void SetFocus(bool) override;
   bool SelectionBounds(WebRect& anchor, WebRect& focus) const override;
diff --git a/third_party/blink/renderer/core/layout/BUILD.gn b/third_party/blink/renderer/core/layout/BUILD.gn
index 75287430..cedbb3b 100644
--- a/third_party/blink/renderer/core/layout/BUILD.gn
+++ b/third_party/blink/renderer/core/layout/BUILD.gn
@@ -335,6 +335,8 @@
     "ng/inline/ng_baseline.h",
     "ng/inline/ng_bidi_paragraph.cc",
     "ng/inline/ng_bidi_paragraph.h",
+    "ng/inline/ng_caret_navigator.cc",
+    "ng/inline/ng_caret_navigator.h",
     "ng/inline/ng_caret_position.cc",
     "ng/inline/ng_caret_position.h",
     "ng/inline/ng_caret_rect.cc",
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index 3b67df83..9ecb5dd 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -49,6 +49,7 @@
 #include "third_party/blink/renderer/core/layout/layout_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/layout_flow_thread.h"
 #include "third_party/blink/renderer/core/layout/layout_grid.h"
+#include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h"
 #include "third_party/blink/renderer/core/layout/layout_object_factory.h"
 #include "third_party/blink/renderer/core/layout/layout_table_cell.h"
@@ -322,7 +323,9 @@
   // If the requested insertion point is not one of our children, then this is
   // because there is an anonymous container within this object that contains
   // the beforeDescendant.
-  if (before_descendant_container->IsAnonymousBlock()) {
+  if (before_descendant_container->IsAnonymousBlock() ||
+      (before_descendant_container->IsLayoutInline() &&
+       ToLayoutInline(before_descendant_container)->IsFirstLineAnonymous())) {
     // Insert the child into the anonymous block box instead of here.
     if (new_child->IsInline() ||
         (new_child->IsFloatingOrOutOfFlowPositioned() &&
diff --git a/third_party/blink/renderer/core/layout/ng/inline/README.md b/third_party/blink/renderer/core/layout/ng/inline/README.md
index 715f1d0e..c604426 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/README.md
+++ b/third_party/blink/renderer/core/layout/ng/inline/README.md
@@ -75,6 +75,7 @@
 [CSS Text Processing Order of Operations]: https://drafts.csswg.org/css-text-3/#order
 
 ### Pre-layout ###
+[Pre-layout]: #pre-layout
 
 For inline layout there is a pre-layout pass that prepares the internal data
 structures needed to perform line layout.
@@ -302,6 +303,16 @@
 
    This is part of the Line Box Construction phase above.
 
+### Interface for Editing ###
+
+[NGOffsetMapping] provides functions for converting between offsets in the text
+content of an inline formatting context (computed in [pre-layout]) and DOM
+positions in the context. See [design doc](https://goo.gl/CJbxky) for details.
+
+[NGCaretNavigator] provides functions for inspecting bidi levels and visual
+ordering of text content, and supports visual left/right caret movements in the
+text. See [design doc](http://bit.ly/2QVAwGq) for details.
+
 
 [ICU BiDi]: http://userguide.icu-project.org/transforms/bidi
 [UAX#9 Unicode Bidirectional Algorithm]: http://unicode.org/reports/tr9/
@@ -311,6 +322,7 @@
 [FontBaseline]: ../../../platform/fonts/FontBaseline.h
 [NGBaselineAlgorithmType]: ng_baseline.h
 [NGBaselineRequest]: ng_baseline.h
+[NGCaretNavigator]: ng_caret_navigator.h
 [NGBidiParagraph]: ng_bidi_paragraph.h
 [NGBlockNode]: ../ng_block_node.h
 [NGBoxFragment]: ../ng_box_fragment.h
@@ -324,6 +336,7 @@
 [NGInlineLayoutAlgorithm]: ng_inline_layout_algorithm.h
 [NGLayoutInputNode]: ../ng_layout_input_node.h
 [NGLineBreaker]: ng_line_breaker.h
+[NGOffsetMapping]: ng_offset_mapping.h
 [NGPhysicalBoxFragment]: ../ng_physical_box_fragment.h
 [NGPhysicalFragment]: ../ng_physical_fragment.h
 [NGPhysicalLineBoxFragment]: ng_physical_line_box_fragment.h
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.cc
new file mode 100644
index 0000000..bdec2ff0
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.cc
@@ -0,0 +1,175 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
+
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
+
+namespace blink {
+
+NGCaretNavigator::~NGCaretNavigator() = default;
+
+NGCaretNavigator::NGCaretNavigator(NGCaretNavigator&& other)
+    : text_(other.text_),
+      bidi_levels_(std::move(other.bidi_levels_)),
+      indices_in_visual_order_(std::move(other.indices_in_visual_order_)),
+      visual_indices_(std::move(other.visual_indices_)),
+      is_bidi_enabled_(other.is_bidi_enabled_),
+      base_direction_(other.base_direction_) {}
+
+NGCaretNavigator::NGCaretNavigator(const NGInlineNodeData& data)
+    : text_(data.text_content),
+      is_bidi_enabled_(data.IsBidiEnabled()),
+      base_direction_(data.BaseDirection()) {
+  if (!is_bidi_enabled_)
+    return;
+
+  bidi_levels_.resize(text_.length());
+  for (const NGInlineItem& item : data.items) {
+    if (!item.Length())
+      continue;
+    for (unsigned i = item.StartOffset(); i < item.EndOffset(); ++i) {
+      DCHECK_LT(i, bidi_levels_.size());
+      bidi_levels_[i] = item.BidiLevel();
+    }
+  }
+
+  indices_in_visual_order_.resize(text_.length());
+  NGBidiParagraph::IndicesInVisualOrder(bidi_levels_,
+                                        &indices_in_visual_order_);
+
+  visual_indices_.resize(text_.length());
+  for (unsigned i = 0; i < indices_in_visual_order_.size(); ++i)
+    visual_indices_[indices_in_visual_order_[i]] = i;
+}
+
+UBiDiLevel NGCaretNavigator::BidiLevelAt(unsigned index) const {
+  DCHECK_LT(index, text_.length());
+  if (!is_bidi_enabled_)
+    return 0;
+  DCHECK_LT(index, bidi_levels_.size());
+  return bidi_levels_[index];
+}
+
+TextDirection NGCaretNavigator::TextDirectionAt(unsigned index) const {
+  UBiDiLevel level = BidiLevelAt(index);
+  return DirectionFromLevel(level);
+}
+
+bool NGCaretNavigator::OffsetIsBidiBoundary(unsigned offset) const {
+  DCHECK_LE(offset, text_.length());
+  if (!is_bidi_enabled_)
+    return false;
+  if (!offset || offset == text_.length())
+    return false;
+  return BidiLevelAt(offset - 1) != BidiLevelAt(offset);
+}
+
+TextDirection NGCaretNavigator::TextDirectionAt(
+    const Position& caret_position) const {
+  return TextDirectionAt(AnchorCharacterIndex(caret_position));
+}
+
+unsigned NGCaretNavigator::AnchorCharacterIndex(
+    const Position& caret_position) const {
+  if (caret_position.affinity == TextAffinity::kDownstream) {
+    DCHECK_LT(caret_position.offset, text_.length());
+    return caret_position.offset;
+  }
+  DCHECK_GT(caret_position.offset, 0u);
+  return caret_position.offset - 1;
+}
+
+NGCaretNavigator::Position NGCaretNavigator::LeftEdgeOf(unsigned index) const {
+  DCHECK_LT(index, text_.length());
+  const TextDirection direction = TextDirectionAt(index);
+  if (IsLtr(direction))
+    return {index, TextAffinity::kDownstream};
+  return {index + 1, TextAffinity::kUpstream};
+}
+
+NGCaretNavigator::Position NGCaretNavigator::RightEdgeOf(unsigned index) const {
+  DCHECK_LT(index, text_.length());
+  const TextDirection direction = TextDirectionAt(index);
+  if (IsRtl(direction))
+    return {index, TextAffinity::kDownstream};
+  return {index + 1, TextAffinity::kUpstream};
+}
+
+NGCaretNavigator::VisualCharacterMovementResult
+NGCaretNavigator::LeftCharacterOf(unsigned index) const {
+  DCHECK_LT(index, text_.length());
+  if (!is_bidi_enabled_) {
+    if (!index)
+      return {VisualMovementResultType::kBeforeContext, base::nullopt};
+    return {VisualMovementResultType::kWithinContext, index - 1};
+  }
+
+  const unsigned visual_index = visual_indices_[index];
+  if (visual_index) {
+    return {VisualMovementResultType::kWithinContext,
+            indices_in_visual_order_[visual_index - 1]};
+  }
+
+  if (IsLtr(base_direction_))
+    return {VisualMovementResultType::kBeforeContext, base::nullopt};
+  return {VisualMovementResultType::kAfterContext, base::nullopt};
+}
+
+NGCaretNavigator::VisualCharacterMovementResult
+NGCaretNavigator::RightCharacterOf(unsigned index) const {
+  DCHECK_LT(index, text_.length());
+  if (bidi_levels_.IsEmpty()) {
+    if (index + 1 == text_.length())
+      return {VisualMovementResultType::kAfterContext, base::nullopt};
+    return {VisualMovementResultType::kWithinContext, index + 1};
+  }
+
+  const unsigned visual_index = visual_indices_[index];
+  if (visual_index + 1 < text_.length()) {
+    return {VisualMovementResultType::kWithinContext,
+            indices_in_visual_order_[visual_index + 1]};
+  }
+
+  if (IsRtl(base_direction_))
+    return {VisualMovementResultType::kBeforeContext, base::nullopt};
+  return {VisualMovementResultType::kAfterContext, base::nullopt};
+}
+
+NGCaretNavigator::VisualCaretMovementResult NGCaretNavigator::LeftPositionOf(
+    const Position& caret_position) const {
+  const unsigned index = AnchorCharacterIndex(caret_position);
+  if (caret_position == RightEdgeOf(index)) {
+    // TODO(xiaochengh): Consider grapheme cluster
+    return {VisualMovementResultType::kWithinContext, LeftEdgeOf(index)};
+  }
+
+  VisualCharacterMovementResult left_character = LeftCharacterOf(index);
+  if (left_character.IsWithinContext()) {
+    DCHECK(left_character.index.has_value());
+    return LeftPositionOf(RightEdgeOf(left_character.index.value()));
+  }
+
+  return {left_character.type, base::nullopt};
+}
+
+NGCaretNavigator::VisualCaretMovementResult NGCaretNavigator::RightPositionOf(
+    const Position& caret_position) const {
+  const unsigned index = AnchorCharacterIndex(caret_position);
+  if (caret_position == LeftEdgeOf(index)) {
+    // TODO(xiaochengh): Consider grapheme cluster
+    return {VisualMovementResultType::kWithinContext, RightEdgeOf(index)};
+  }
+
+  VisualCharacterMovementResult right_character = RightCharacterOf(index);
+  if (right_character.IsWithinContext()) {
+    DCHECK(right_character.index.has_value());
+    return RightPositionOf(LeftEdgeOf(right_character.index.value()));
+  }
+
+  return {right_character.type, base::nullopt};
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h
new file mode 100644
index 0000000..297bda0
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h
@@ -0,0 +1,129 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_CARET_NAVIGATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_CARET_NAVIGATOR_H_
+
+#include "base/optional.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/editing/text_affinity.h"
+#include "third_party/blink/renderer/platform/text/text_direction.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include <unicode/ubidi.h>
+
+namespace blink {
+
+struct NGInlineNodeData;
+
+// Hosts the |text_content| of an inline formatting context and provides
+// bidi-related utilities, including checking bidi levels, computing visual
+// left/right characters and visual left/right caret movements.
+// Design doc: http://bit.ly/2QVAwGq
+class CORE_EXPORT NGCaretNavigator {
+ public:
+  NGCaretNavigator(NGCaretNavigator&&);
+  explicit NGCaretNavigator(const NGInlineNodeData&);
+  ~NGCaretNavigator();
+
+  // Abstraction of a caret position in |text_|.
+  struct Position {
+    unsigned offset;
+    TextAffinity affinity;
+
+    bool operator==(const Position& other) const {
+      return offset == other.offset && affinity == other.affinity;
+    }
+  };
+
+  // Returns the bidi level or resolved direction of the character at the given
+  // logical |index|.
+  UBiDiLevel BidiLevelAt(unsigned index) const;
+  TextDirection TextDirectionAt(unsigned index) const;
+
+  // Returns true if the characters at indexes |offset - 1| and |offset| both
+  // exist and are at different bidi levels.
+  bool OffsetIsBidiBoundary(unsigned offset) const;
+
+  // Returns the resolved direction of the anchor character of a caret position.
+  TextDirection TextDirectionAt(const Position&) const;
+
+  // Returns the logical index that a Position is anchored to.
+  unsigned AnchorCharacterIndex(const Position&) const;
+
+  // Returns the visual left/right edge caret position of the character at the
+  // given logical |index|.
+  Position LeftEdgeOf(unsigned index) const;
+  Position RightEdgeOf(unsigned index) const;
+
+  // Left/right visual movements
+  // TODO(xiaochengh): Handle the following
+  // - Grapheme clusters
+  // - Enterable atomic inlines
+  // - Editing-ignored contents
+  // - Multiple lines
+
+  enum class VisualMovementResultType {
+    kWithinContext,
+    kBeforeContext,
+    kAfterContext
+  };
+
+  // Given the character at the logical |index|, returns the logical index of
+  // the character at its left/right side.
+  struct VisualCharacterMovementResult {
+    bool IsWithinContext() const {
+      return type == VisualMovementResultType::kWithinContext;
+    }
+    bool IsBeforeContext() const {
+      return type == VisualMovementResultType::kBeforeContext;
+    }
+    bool IsAfterContext() const {
+      return type == VisualMovementResultType::kAfterContext;
+    }
+
+    VisualMovementResultType type;
+    base::Optional<unsigned> index;
+  };
+  VisualCharacterMovementResult LeftCharacterOf(unsigned index) const;
+  VisualCharacterMovementResult RightCharacterOf(unsigned index) const;
+
+  // Given a caret position, moves it left/right by one grapheme cluster and
+  // returns the result.
+  struct VisualCaretMovementResult {
+    bool IsWithinContext() const {
+      return type == VisualMovementResultType::kWithinContext;
+    }
+    bool IsBeforeContext() const {
+      return type == VisualMovementResultType::kBeforeContext;
+    }
+    bool IsAfterContext() const {
+      return type == VisualMovementResultType::kAfterContext;
+    }
+
+    VisualMovementResultType type;
+    base::Optional<Position> position;
+  };
+  VisualCaretMovementResult LeftPositionOf(const Position&) const;
+  VisualCaretMovementResult RightPositionOf(const Position&) const;
+
+ private:
+  String text_;
+
+  // TODO(xiaochengh): Add line-aware index.
+  // TODO(xiaochengh): Reduce memory consumption.
+  Vector<UBiDiLevel, 32> bidi_levels_;
+  Vector<int32_t, 32> indices_in_visual_order_;
+  Vector<int32_t, 32> visual_indices_;
+
+  bool is_bidi_enabled_;
+  TextDirection base_direction_;
+
+  DISALLOW_COPY_AND_ASSIGN(NGCaretNavigator);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_CARET_NAVIGATOR_H_
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator_test.cc
new file mode 100644
index 0000000..a7cd9f9
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator_test.cc
@@ -0,0 +1,306 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
+
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
+#include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_test.h"
+
+namespace blink {
+
+class NGCaretNavigatorTest : public RenderingTest,
+                             public testing::WithParamInterface<bool>,
+                             private ScopedLayoutNGForTest,
+                             private ScopedBidiCaretAffinityForTest {
+ public:
+  NGCaretNavigatorTest()
+      : ScopedLayoutNGForTest(GetParam()),
+        ScopedBidiCaretAffinityForTest(true) {}
+
+  void SetupHtml(const char* id, String html) {
+    SetBodyInnerHTML(html);
+
+    LayoutBlockFlow* layout_block_flow =
+        ToLayoutBlockFlow(GetLayoutObjectByElementId(id));
+    DCHECK(layout_block_flow);
+    DCHECK(layout_block_flow->ChildrenInline());
+    DCHECK_EQ(layout_block_flow->IsLayoutNGMixin(), GetParam());
+
+    const NGOffsetMapping* mapping =
+        NGInlineNode::GetOffsetMapping(layout_block_flow, &mapping_storage_);
+    DCHECK(mapping);
+    DCHECK_EQ(!mapping_storage_,
+              GetParam());  // |storage| is used only for legacy.
+
+    caret_navigator_ = mapping->GetCaretNavigator();
+    DCHECK(caret_navigator_);
+  }
+
+  UBiDiLevel BidiLevelAt(unsigned index) const {
+    return caret_navigator_->BidiLevelAt(index);
+  }
+
+  NGCaretNavigator::VisualCharacterMovementResult LeftCharacterOf(
+      unsigned index) const {
+    return caret_navigator_->LeftCharacterOf(index);
+  }
+
+  NGCaretNavigator::VisualCharacterMovementResult RightCharacterOf(
+      unsigned index) const {
+    return caret_navigator_->RightCharacterOf(index);
+  }
+
+  NGCaretNavigator::VisualCaretMovementResult LeftPositionOf(
+      unsigned offset,
+      TextAffinity affinity) const {
+    return caret_navigator_->LeftPositionOf({offset, affinity});
+  }
+
+  NGCaretNavigator::VisualCaretMovementResult RightPositionOf(
+      unsigned offset,
+      TextAffinity affinity) const {
+    return caret_navigator_->RightPositionOf({offset, affinity});
+  }
+
+ protected:
+  std::unique_ptr<NGOffsetMapping> mapping_storage_;
+  const NGCaretNavigator* caret_navigator_;
+};
+
+INSTANTIATE_TEST_CASE_P(All, NGCaretNavigatorTest, testing::Bool());
+
+TEST_P(NGCaretNavigatorTest, BidiLevelAtBasic) {
+  SetupHtml("container",
+            "<div id=container>abc&#x05D0;&#x05D1;&#x05D2;123</div>");
+
+  EXPECT_EQ(0u, BidiLevelAt(0));
+  EXPECT_EQ(0u, BidiLevelAt(1));
+  EXPECT_EQ(0u, BidiLevelAt(2));
+  EXPECT_EQ(1u, BidiLevelAt(3));
+  EXPECT_EQ(1u, BidiLevelAt(4));
+  EXPECT_EQ(1u, BidiLevelAt(5));
+  EXPECT_EQ(2u, BidiLevelAt(6));
+  EXPECT_EQ(2u, BidiLevelAt(7));
+  EXPECT_EQ(2u, BidiLevelAt(8));
+}
+
+TEST_P(NGCaretNavigatorTest, LeftCharacterOfBasic) {
+  SetupHtml("container",
+            "<div id=container>abc&#x05D0;&#x05D1;&#x05D2;123</div>");
+
+  EXPECT_TRUE(LeftCharacterOf(0).IsBeforeContext());
+
+  EXPECT_TRUE(LeftCharacterOf(1).IsWithinContext());
+  EXPECT_EQ(0u, *LeftCharacterOf(1).index);
+
+  EXPECT_TRUE(LeftCharacterOf(2).IsWithinContext());
+  EXPECT_EQ(1u, *LeftCharacterOf(2).index);
+
+  EXPECT_TRUE(LeftCharacterOf(3).IsWithinContext());
+  EXPECT_EQ(4u, *LeftCharacterOf(3).index);
+
+  EXPECT_TRUE(LeftCharacterOf(4).IsWithinContext());
+  EXPECT_EQ(5u, *LeftCharacterOf(4).index);
+
+  EXPECT_TRUE(LeftCharacterOf(5).IsWithinContext());
+  EXPECT_EQ(8u, *LeftCharacterOf(5).index);
+
+  EXPECT_TRUE(LeftCharacterOf(6).IsWithinContext());
+  EXPECT_EQ(2u, *LeftCharacterOf(6).index);
+
+  EXPECT_TRUE(LeftCharacterOf(7).IsWithinContext());
+  EXPECT_EQ(6u, *LeftCharacterOf(7).index);
+
+  EXPECT_TRUE(LeftCharacterOf(8).IsWithinContext());
+  EXPECT_EQ(7u, *LeftCharacterOf(8).index);
+}
+
+TEST_P(NGCaretNavigatorTest, RightCharacterOfBasic) {
+  SetupHtml("container",
+            "<div id=container>abc&#x05D0;&#x05D1;&#x05D2;123</div>");
+
+  EXPECT_TRUE(RightCharacterOf(0).IsWithinContext());
+  EXPECT_EQ(1u, *RightCharacterOf(0).index);
+
+  EXPECT_TRUE(RightCharacterOf(1).IsWithinContext());
+  EXPECT_EQ(2u, *RightCharacterOf(1).index);
+
+  EXPECT_TRUE(RightCharacterOf(2).IsWithinContext());
+  EXPECT_EQ(6u, *RightCharacterOf(2).index);
+
+  EXPECT_TRUE(RightCharacterOf(3).IsAfterContext());
+
+  EXPECT_TRUE(RightCharacterOf(4).IsWithinContext());
+  EXPECT_EQ(3u, *RightCharacterOf(4).index);
+
+  EXPECT_TRUE(RightCharacterOf(5).IsWithinContext());
+  EXPECT_EQ(4u, *RightCharacterOf(5).index);
+
+  EXPECT_TRUE(RightCharacterOf(6).IsWithinContext());
+  EXPECT_EQ(7u, *RightCharacterOf(6).index);
+
+  EXPECT_TRUE(RightCharacterOf(7).IsWithinContext());
+  EXPECT_EQ(8u, *RightCharacterOf(7).index);
+
+  EXPECT_TRUE(RightCharacterOf(8).IsWithinContext());
+  EXPECT_EQ(5u, *RightCharacterOf(8).index);
+}
+
+TEST_P(NGCaretNavigatorTest, LeftPositionOfBasic) {
+  SetupHtml("container",
+            "<div id=container>abc&#x05D0;&#x05D1;&#x05D2;123</div>");
+
+  using Position = NGCaretNavigator::Position;
+
+  EXPECT_TRUE(LeftPositionOf(0u, TextAffinity::kDownstream).IsBeforeContext());
+
+  EXPECT_TRUE(LeftPositionOf(1u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({0u, TextAffinity::kDownstream}),
+            *LeftPositionOf(1u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(1u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({0u, TextAffinity::kDownstream}),
+            *LeftPositionOf(1u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(2u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({1u, TextAffinity::kDownstream}),
+            *LeftPositionOf(2u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(2u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({1u, TextAffinity::kDownstream}),
+            *LeftPositionOf(2u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(3u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({2u, TextAffinity::kDownstream}),
+            *LeftPositionOf(3u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(3u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({4u, TextAffinity::kUpstream}),
+            *LeftPositionOf(3u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(4u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({5u, TextAffinity::kUpstream}),
+            *LeftPositionOf(4u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(4u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({5u, TextAffinity::kUpstream}),
+            *LeftPositionOf(4u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(5u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({6u, TextAffinity::kUpstream}),
+            *LeftPositionOf(5u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(5u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({6u, TextAffinity::kUpstream}),
+            *LeftPositionOf(5u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(6u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({8u, TextAffinity::kDownstream}),
+            *LeftPositionOf(6u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(6u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({2u, TextAffinity::kDownstream}),
+            *LeftPositionOf(6u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(7u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({6u, TextAffinity::kDownstream}),
+            *LeftPositionOf(7u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(7u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({6u, TextAffinity::kDownstream}),
+            *LeftPositionOf(7u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(8u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({7u, TextAffinity::kDownstream}),
+            *LeftPositionOf(8u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(8u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({7u, TextAffinity::kDownstream}),
+            *LeftPositionOf(8u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(LeftPositionOf(9u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({8u, TextAffinity::kDownstream}),
+            *LeftPositionOf(9u, TextAffinity::kUpstream).position);
+}
+
+TEST_P(NGCaretNavigatorTest, RightPositionOfBasic) {
+  SetupHtml("container",
+            "<div id=container>abc&#x05D0;&#x05D1;&#x05D2;123</div>");
+
+  using Position = NGCaretNavigator::Position;
+
+  EXPECT_TRUE(RightPositionOf(0u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({1u, TextAffinity::kUpstream}),
+            *RightPositionOf(0u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(RightPositionOf(1u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({2u, TextAffinity::kUpstream}),
+            *RightPositionOf(1u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(RightPositionOf(1u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({2u, TextAffinity::kUpstream}),
+            *RightPositionOf(1u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(RightPositionOf(2u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({3u, TextAffinity::kUpstream}),
+            *RightPositionOf(2u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(RightPositionOf(2u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({3u, TextAffinity::kUpstream}),
+            *RightPositionOf(2u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(RightPositionOf(3u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({7u, TextAffinity::kUpstream}),
+            *RightPositionOf(3u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(RightPositionOf(3u, TextAffinity::kDownstream).IsAfterContext());
+
+  EXPECT_TRUE(RightPositionOf(4u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({3u, TextAffinity::kDownstream}),
+            *RightPositionOf(4u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(RightPositionOf(4u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({3u, TextAffinity::kDownstream}),
+            *RightPositionOf(4u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(RightPositionOf(5u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({4u, TextAffinity::kDownstream}),
+            *RightPositionOf(5u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(RightPositionOf(5u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({4u, TextAffinity::kDownstream}),
+            *RightPositionOf(5u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(RightPositionOf(6u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({5u, TextAffinity::kDownstream}),
+            *RightPositionOf(6u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(RightPositionOf(6u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({7u, TextAffinity::kUpstream}),
+            *RightPositionOf(6u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(RightPositionOf(7u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({8u, TextAffinity::kUpstream}),
+            *RightPositionOf(7u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(RightPositionOf(7u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({8u, TextAffinity::kUpstream}),
+            *RightPositionOf(7u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(RightPositionOf(8u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({9u, TextAffinity::kUpstream}),
+            *RightPositionOf(8u, TextAffinity::kUpstream).position);
+
+  EXPECT_TRUE(RightPositionOf(8u, TextAffinity::kDownstream).IsWithinContext());
+  EXPECT_EQ(Position({9u, TextAffinity::kUpstream}),
+            *RightPositionOf(8u, TextAffinity::kDownstream).position);
+
+  EXPECT_TRUE(RightPositionOf(9u, TextAffinity::kUpstream).IsWithinContext());
+  EXPECT_EQ(Position({5u, TextAffinity::kDownstream}),
+            *RightPositionOf(9u, TextAffinity::kUpstream).position);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
index 3705c5f1..24d329b6 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/core/layout/logical_values.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_bidi_paragraph.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder.h"
@@ -343,6 +344,13 @@
   }
 }
 
+template <typename OffsetMappingBuilder>
+bool MayBeBidiEnabled(
+    const String& text_content,
+    const NGInlineItemsBuilderTemplate<OffsetMappingBuilder>& builder) {
+  return !text_content.Is8Bit() || builder.HasBidiControls();
+}
+
 }  // namespace
 
 NGInlineNode::NGInlineNode(LayoutBlockFlow* block)
@@ -428,19 +436,41 @@
   CollectInlinesInternal(layout_block_flow, &builder, nullptr, nullptr,
                          update_layout);
 
-  // We need the text for non-NG object. Otherwise |data| already has the text
-  // from the pre-layout phase, check they match.
-  if (data->text_content.IsNull())
+  // For non-NG object, we need the text, and also the inline items to resolve
+  // bidi levels. Otherwise |data| already has the text from the pre-layout
+  // phase, check they match.
+  if (data->text_content.IsNull()) {
+    DCHECK(!layout_block_flow->IsLayoutNGMixin());
     data->text_content = builder.ToString();
-  else
+    if (RuntimeEnabledFeatures::BidiCaretAffinityEnabled()) {
+      // Set |is_bidi_enabled_| for all UTF-16 strings for now, because at this
+      // point the string may or may not contain RTL characters.
+      // |SegmentText()| will analyze the text and reset |is_bidi_enabled_| if
+      // it doesn't contain any RTL characters.
+      data->is_bidi_enabled_ = MayBeBidiEnabled(data->text_content, builder);
+      if (data->is_bidi_enabled_) {
+        // |builder| performs some validity checks with |items|, so we can't
+        // simply move them to |data|, but have to copy.
+        // TODO(xiaochengh): Change it into a move.
+        data->items = items;
+        SegmentBidiRunsInternal(data, layout_block_flow->StyleRef());
+      }
+    }
+  } else {
+    DCHECK(layout_block_flow->IsLayoutNGMixin());
     DCHECK_EQ(data->text_content, builder.ToString());
+  }
+
+  std::unique_ptr<NGCaretNavigator> caret_navigator;
+  if (RuntimeEnabledFeatures::BidiCaretAffinityEnabled())
+    caret_navigator = std::make_unique<NGCaretNavigator>(*data);
 
   // TODO(xiaochengh): This doesn't compute offset mapping correctly when
   // text-transform CSS property changes text length.
   NGOffsetMappingBuilder& mapping_builder = builder.GetOffsetMappingBuilder();
   mapping_builder.SetDestinationString(data->text_content);
-  data->offset_mapping =
-      std::make_unique<NGOffsetMapping>(mapping_builder.Build());
+  data->offset_mapping = std::make_unique<NGOffsetMapping>(
+      mapping_builder.Build(std::move(caret_navigator)));
   DCHECK(data->offset_mapping);
 }
 
@@ -500,8 +530,7 @@
   // point the string may or may not contain RTL characters.
   // |SegmentText()| will analyze the text and reset |is_bidi_enabled_| if it
   // doesn't contain any RTL characters.
-  data->is_bidi_enabled_ =
-      !data->text_content.Is8Bit() || builder.HasBidiControls();
+  data->is_bidi_enabled_ = MayBeBidiEnabled(data->text_content, builder);
   data->is_empty_inline_ = builder.IsEmptyInline();
 }
 
@@ -589,9 +618,11 @@
   }
 }
 
+// static
 // Segment bidi runs by resolving bidi embedding levels.
 // http://unicode.org/reports/tr9/#Resolving_Embedding_Levels
-void NGInlineNode::SegmentBidiRuns(NGInlineNodeData* data) {
+void NGInlineNode::SegmentBidiRunsInternal(NGInlineNodeData* data,
+                                           const ComputedStyle& style) {
   if (!data->is_bidi_enabled_) {
     data->SetBaseDirection(TextDirection::kLtr);
     return;
@@ -599,7 +630,7 @@
 
   NGBidiParagraph bidi;
   data->text_content.Ensure16Bit();
-  if (!bidi.SetParagraph(data->text_content, Style())) {
+  if (!bidi.SetParagraph(data->text_content, style)) {
     // On failure, give up bidi resolving and reordering.
     data->is_bidi_enabled_ = false;
     data->SetBaseDirection(TextDirection::kLtr);
@@ -634,6 +665,10 @@
 #endif
 }
 
+void NGInlineNode::SegmentBidiRuns(NGInlineNodeData* data) {
+  SegmentBidiRunsInternal(data, Style());
+}
+
 void NGInlineNode::ShapeText(NGInlineItemsData* data,
                              NGInlineItemsData* previous_data) {
   const String& text_content = data->text_content;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
index 56670dc..0007417 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
@@ -142,6 +142,11 @@
   static void ComputeOffsetMapping(LayoutBlockFlow* layout_block_flow,
                                    NGInlineNodeData* data);
 
+  // This function shares bidi segmentation code between inline layout algorithm
+  // and |NGCaretNavigator| building, which may run on legacy layout.
+  // TODO(layout-dev): Merge with |SegmentBidiRuns| when we remove legacy layout
+  static void SegmentBidiRunsInternal(NGInlineNodeData*, const ComputedStyle&);
+
   friend class NGLineBreakerTest;
   friend class NGInlineNodeLegacy;
 };
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h
index dbf328bc..2037022 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h
@@ -13,15 +13,18 @@
 
 // Data which is required for inline nodes.
 struct CORE_EXPORT NGInlineNodeData : NGInlineItemsData {
+ public:
+  bool IsBidiEnabled() const { return is_bidi_enabled_; }
+  TextDirection BaseDirection() const {
+    return static_cast<TextDirection>(base_direction_);
+  }
+
  private:
   const NGInlineItemsData& ItemsData(bool is_first_line) const {
     return !is_first_line || !first_line_items_
                ? (const NGInlineItemsData&)*this
                : *first_line_items_;
   }
-  TextDirection BaseDirection() const {
-    return static_cast<TextDirection>(base_direction_);
-  }
   void SetBaseDirection(TextDirection direction) {
     base_direction_ = static_cast<unsigned>(direction);
   }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc
index 9b738bb..ded3d140 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.cc
@@ -6,9 +6,11 @@
 
 #include "third_party/blink/renderer/core/dom/node.h"
 #include "third_party/blink/renderer/core/dom/text.h"
+#include "third_party/blink/renderer/core/editing/editing_utilities.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
 #include "third_party/blink/renderer/core/editing/position.h"
 #include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
 #include "third_party/blink/renderer/platform/text/character.h"
@@ -62,12 +64,24 @@
 LayoutBlockFlow* NGInlineFormattingContextOf(const Position& position) {
   if (!RuntimeEnabledFeatures::LayoutNGEnabled())
     return nullptr;
-  if (!NGOffsetMapping::AcceptsPosition(position))
+  LayoutBlockFlow* block_flow =
+      NGOffsetMapping::GetInlineFormattingContextOf(position);
+  if (!block_flow || !block_flow->IsLayoutNGMixin())
+    return nullptr;
+  return block_flow;
+}
+
+// static
+LayoutBlockFlow* NGOffsetMapping::GetInlineFormattingContextOf(
+    const Position& position) {
+  if (!AcceptsPosition(position))
     return nullptr;
   const auto node_offset_pair = ToNodeOffsetPair(position);
   const LayoutObject* layout_object =
       AssociatedLayoutObjectOf(node_offset_pair.first, node_offset_pair.second);
-  return layout_object->ContainingNGBlockFlow();
+  if (!layout_object)
+    return nullptr;
+  return GetInlineFormattingContextOf(*layout_object);
 }
 
 NGOffsetMappingUnit::NGOffsetMappingUnit(NGOffsetMappingUnitType type,
@@ -206,12 +220,18 @@
 NGOffsetMapping::NGOffsetMapping(NGOffsetMapping&& other)
     : NGOffsetMapping(std::move(other.units_),
                       std::move(other.ranges_),
-                      other.text_) {}
+                      other.text_,
+                      std::move(other.caret_navigator_)) {}
 
-NGOffsetMapping::NGOffsetMapping(UnitVector&& units,
-                                 RangeMap&& ranges,
-                                 String text)
-    : units_(std::move(units)), ranges_(std::move(ranges)), text_(text) {}
+NGOffsetMapping::NGOffsetMapping(
+    UnitVector&& units,
+    RangeMap&& ranges,
+    String text,
+    std::unique_ptr<NGCaretNavigator> caret_navigator)
+    : units_(std::move(units)),
+      ranges_(std::move(ranges)),
+      text_(text),
+      caret_navigator_(std::move(caret_navigator)) {}
 
 NGOffsetMapping::~NGOffsetMapping() = default;
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h
index 9fafd52..5ac360f6 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h
@@ -20,6 +20,7 @@
 
 class LayoutBlockFlow;
 class LayoutObject;
+class NGCaretNavigator;
 
 enum class NGOffsetMappingUnitType { kIdentity, kCollapsed, kExpanded };
 
@@ -105,7 +106,10 @@
       HashMap<Persistent<const Node>, std::pair<unsigned, unsigned>>;
 
   NGOffsetMapping(NGOffsetMapping&&);
-  NGOffsetMapping(UnitVector&&, RangeMap&&, String);
+  NGOffsetMapping(UnitVector&&,
+                  RangeMap&&,
+                  String,
+                  std::unique_ptr<NGCaretNavigator>);
   ~NGOffsetMapping();
 
   const UnitVector& GetUnits() const { return units_; }
@@ -139,6 +143,9 @@
   // NG layout, while NGOffsetMapping is supported on both of them.
   static LayoutBlockFlow* GetInlineFormattingContextOf(const LayoutObject&);
 
+  // Variants taking position instead of |LayoutObject|.
+  static LayoutBlockFlow* GetInlineFormattingContextOf(const Position&);
+
   // ------ Mapping APIs from DOM to text content ------
 
   // Returns the NGOffsetMappingUnit whose DOM range contains the position.
@@ -207,6 +214,10 @@
   // control charcters. Returns true otherwise.
   bool HasBidiControlCharactersOnly(unsigned start, unsigned end) const;
 
+  const NGCaretNavigator* GetCaretNavigator() const {
+    return caret_navigator_.get();
+  }
+
  private:
   // The NGOffsetMappingUnits of the inline formatting context in osrted order.
   UnitVector units_;
@@ -218,6 +229,9 @@
   // |NGInlineNodeData::text_content_|.
   String text_;
 
+  // Helper class for caret nagivation on |text_|.
+  std::unique_ptr<NGCaretNavigator> caret_navigator_;
+
   DISALLOW_COPY_AND_ASSIGN(NGOffsetMapping);
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc
index f80da9ad..5fa4b9b3 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.cc
@@ -6,6 +6,7 @@
 
 #include "third_party/blink/renderer/core/layout/layout_text.h"
 #include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_navigator.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
 
 namespace blink {
@@ -198,7 +199,8 @@
   destination_string_ = string;
 }
 
-NGOffsetMapping NGOffsetMappingBuilder::Build() {
+NGOffsetMapping NGOffsetMappingBuilder::Build(
+    std::unique_ptr<NGCaretNavigator> caret_navigator) {
   // All mapping units are already built. Scan them to build mapping ranges.
   for (unsigned range_start = 0; range_start < mapping_units_.size();) {
     const Node* node = &mapping_units_[range_start].GetOwner();
@@ -214,7 +216,7 @@
   }
 
   return NGOffsetMapping(std::move(mapping_units_), std::move(unit_ranges_),
-                         destination_string_);
+                         destination_string_, std::move(caret_navigator));
 }
 
 void NGOffsetMappingBuilder::EnterInline(const LayoutObject& layout_object) {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h
index c24971a..545ef36 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h
@@ -15,6 +15,7 @@
 namespace blink {
 
 class LayoutObject;
+class NGCaretNavigator;
 
 // This is the helper class for constructing the DOM-to-TextContent offset
 // mapping. It holds an offset mapping, and provides APIs to modify the mapping
@@ -119,7 +120,9 @@
 
   // Finalize and return the offset mapping.
   // This method can only be called once, as it can invalidate the stored data.
-  NGOffsetMapping Build();
+  // Also moves the passed-in |NGCaretNavigator| into the result
+  // |NGOffsetMapping|.
+  NGOffsetMapping Build(std::unique_ptr<NGCaretNavigator>);
 
  private:
   // Helper function for CollapseTrailingSpace() to maintain unit ranges.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
index 07c724c..904ddc7c 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
@@ -97,6 +97,7 @@
     is_anonymous_text_ = true;
   } else {
     is_anonymous_text_ =
+        builder->text_type_ == kGeneratedText ||
         IsPhysicalTextFragmentAnonymousText(builder->layout_object_);
   }
 
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index bc4458e..fae93c6 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -190,6 +190,11 @@
   // where the redirects originated.
   if (is_client_redirect_)
     AppendRedirect(frame_->GetDocument()->Url());
+
+  if (!navigation_params->origin_to_commit.IsNull()) {
+    origin_to_commit_ =
+        navigation_params->origin_to_commit.Get()->IsolatedCopy();
+  }
 }
 
 FrameLoader& DocumentLoader::GetFrameLoader() const {
@@ -788,14 +793,19 @@
   // Prepare a DocumentInit before clearing the frame, because it may need to
   // inherit an aliased security context.
   Document* owner_document = nullptr;
+  scoped_refptr<const SecurityOrigin> initiator_origin =
+      request_.RequestorOrigin();
+
   // TODO(dcheng): This differs from the behavior of both IE and Firefox: the
   // origin is inherited from the document that loaded the URL.
   if (Document::ShouldInheritSecurityOriginFromOwner(Url())) {
     Frame* owner_frame = frame_->Tree().Parent();
     if (!owner_frame)
       owner_frame = frame_->Loader().Opener();
-    if (owner_frame && owner_frame->IsLocalFrame())
+    if (owner_frame && owner_frame->IsLocalFrame()) {
       owner_document = ToLocalFrame(owner_frame)->GetDocument();
+      initiator_origin = owner_document->GetSecurityOrigin();
+    }
   }
   DCHECK(frame_->GetPage());
 
@@ -804,7 +814,7 @@
     parsing_policy = kForceSynchronousParsing;
 
   InstallNewDocument(
-      Url(), owner_document,
+      Url(), initiator_origin, owner_document,
       frame_->ShouldReuseDefaultView(Url(), GetContentSecurityPolicy())
           ? WebGlobalObjectReusePolicy::kUseExisting
           : WebGlobalObjectReusePolicy::kCreateNew,
@@ -1213,6 +1223,7 @@
 
 void DocumentLoader::InstallNewDocument(
     const KURL& url,
+    const scoped_refptr<const SecurityOrigin> initiator_origin,
     Document* owner_document,
     WebGlobalObjectReusePolicy global_object_reuse_policy,
     const AtomicString& mime_type,
@@ -1252,6 +1263,8 @@
           .WithDocumentLoader(this)
           .WithURL(url)
           .WithOwnerDocument(owner_document)
+          .WithInitiatorOrigin(initiator_origin)
+          .WithOriginToCommit(origin_to_commit_)
           .WithNewRegistrationContext(),
       false);
 
@@ -1354,7 +1367,7 @@
     Document* owner_document,
     WebGlobalObjectReusePolicy global_object_reuse_policy,
     const String& source) {
-  InstallNewDocument(url, owner_document, global_object_reuse_policy,
+  InstallNewDocument(url, nullptr, owner_document, global_object_reuse_policy,
                      MimeType(), response_.TextEncodingName(),
                      InstallNewDocumentReason::kJavascriptURL,
                      kForceSynchronousParsing, NullURL());
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index e271424..8028042 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -285,14 +285,16 @@
   // initalizes a bunch of state on the Document (e.g., the state based on
   // response headers).
   enum class InstallNewDocumentReason { kNavigation, kJavascriptURL };
-  void InstallNewDocument(const KURL&,
-                          Document* owner_document,
-                          WebGlobalObjectReusePolicy,
-                          const AtomicString& mime_type,
-                          const AtomicString& encoding,
-                          InstallNewDocumentReason,
-                          ParserSynchronizationPolicy,
-                          const KURL& overriding_url);
+  void InstallNewDocument(
+      const KURL&,
+      const scoped_refptr<const SecurityOrigin> initiator_origin,
+      Document* owner_document,
+      WebGlobalObjectReusePolicy,
+      const AtomicString& mime_type,
+      const AtomicString& encoding,
+      InstallNewDocumentReason,
+      ParserSynchronizationPolicy,
+      const KURL& overriding_url);
   void DidInstallNewDocument(Document*);
   void WillCommitNavigation();
   void DidCommitNavigation(WebGlobalObjectReusePolicy);
@@ -379,6 +381,7 @@
   bool data_received_;
 
   WebNavigationType navigation_type_;
+  scoped_refptr<SecurityOrigin> origin_to_commit_;
 
   DocumentLoadTiming document_load_timing_;
 
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc
index c77936a..8614203 100644
--- a/third_party/blink/renderer/core/loader/image_loader.cc
+++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -81,7 +81,8 @@
 }
 
 bool IsLazyLoadableImage(const LocalFrame* frame,
-                         HTMLImageElement* html_image) {
+                         HTMLImageElement* html_image,
+                         const KURL& url) {
   // Minimum width or height attribute of the image to start lazyloading.
   const unsigned kMinDimensionToLazyLoad = 10;
 
@@ -89,6 +90,9 @@
   if (!html_image->ElementCreatedByParser())
     return false;
 
+  if (!url.ProtocolIsInHTTPFamily())
+    return false;
+
   if (EqualIgnoringASCIICase(
           html_image->FastGetAttribute(html_names::kLazyloadAttr), "off") &&
       !frame->GetDocument()->IsLazyLoadPolicyEnforced()) {
@@ -537,7 +541,8 @@
       if (frame->IsClientLoFiAllowed(params.GetResourceRequest())) {
         params.SetClientLoFiPlaceholder();
       } else if (auto* html_image = ToHTMLImageElementOrNull(GetElement())) {
-        if (IsLazyLoadableImage(frame, html_image)) {
+        if (IsLazyLoadableImage(frame, html_image,
+                                params.GetResourceRequest().Url())) {
           if (frame->GetDocument()->GetSettings()->GetLazyLoadEnabled() &&
               frame->IsLazyLoadingImageAllowed()) {
             params.SetLazyImagePlaceholder();
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
index 5c0424f5..3741218 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
@@ -152,27 +152,37 @@
   }
 }
 
-void ScrollingCoordinator::DidScroll(const gfx::ScrollOffset& offset,
-                                     const CompositorElementId& element_id) {
+ScrollableArea*
+ScrollingCoordinator::ScrollableAreaWithElementIdInAllLocalFrames(
+    const CompositorElementId& id) {
   for (auto* frame = page_->MainFrame(); frame;
        frame = frame->Tree().TraverseNext()) {
-    // Remote frames will receive DidScroll callbacks from their own compositor.
     if (!frame->IsLocalFrame())
       continue;
 
-    // Find the associated scrollable area using the element id and notify it
-    // of the compositor-side scroll. We explicitly do not check the
-    // VisualViewport which handles scroll offset differently (see:
-    // VisualViewport::didScroll).
+    // Find the associated scrollable area using the element id.
     if (LocalFrameView* view = ToLocalFrame(frame)->View()) {
-      if (auto* scrollable = view->ScrollableAreaWithElementId(element_id)) {
-        scrollable->DidScroll(FloatPoint(offset.x(), offset.y()));
-        return;
+      if (auto* scrollable = view->ScrollableAreaWithElementId(id)) {
+        return scrollable;
       }
     }
   }
+  // The ScrollableArea with matching ElementId does not exist in local frames.
+  return nullptr;
+}
+
+void ScrollingCoordinator::DidScroll(const gfx::ScrollOffset& offset,
+                                     const CompositorElementId& element_id) {
+  // Find the associated scrollable area using the element id and notify it of
+  // the compositor-side scroll. We explicitly do not check the VisualViewport
+  // which handles scroll offset differently (see: VisualViewport::didScroll).
+  // Remote frames will receive DidScroll callbacks from their own compositor.
   // The ScrollableArea with matching ElementId may have been deleted and we can
   // safely ignore the DidScroll callback.
+  if (auto* scrollable =
+          ScrollableAreaWithElementIdInAllLocalFrames(element_id)) {
+    scrollable->DidScroll(FloatPoint(offset.x(), offset.y()));
+  }
 }
 
 void ScrollingCoordinator::UpdateAfterPaint(LocalFrameView* frame_view) {
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h
index 115d4ce1..47fb1a9d 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h
@@ -166,6 +166,12 @@
     return programmatic_scroll_animator_timeline_.get();
   }
 
+  // Traverses the frame tree to find the scrollable area using the element id.
+  // This function only checks the local frames. This function does not check
+  // the VisualViewport element id.
+  ScrollableArea* ScrollableAreaWithElementIdInAllLocalFrames(
+      const CompositorElementId&);
+
   // Callback for compositor-side layer scrolls.
   void DidScroll(const gfx::ScrollOffset&, const CompositorElementId&);
 
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
index 42d88b2..9591fc3 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
@@ -24,6 +24,7 @@
 namespace blink {
 
 namespace {
+#ifndef NDEBUG
 String GetImageUrl(const LayoutObject& object) {
   if (object.IsImage()) {
     const ImageResourceContent* cached_image =
@@ -55,6 +56,7 @@
   }
   return concatenated_result.ToString();
 }
+#endif
 
 bool AttachedBackgroundImagesAllLoaded(const LayoutObject& object) {
   DCHECK(ImagePaintTimingDetector::HasContentfulBackgroundImage(object));
@@ -123,7 +125,9 @@
     const ImageRecord& first_image_paint,
     unsigned candidate_index) const {
   value.SetInteger("DOMNodeId", static_cast<int>(first_image_paint.node_id));
+#ifndef NDEBUG
   value.SetString("imageUrl", first_image_paint.image_url);
+#endif
   value.SetInteger("size", static_cast<int>(first_image_paint.first_size));
   value.SetInteger("candidateIndex", candidate_index);
   value.SetString("frame",
@@ -340,7 +344,9 @@
     // Non-trivial image is found.
     std::unique_ptr<ImageRecord> record = std::make_unique<ImageRecord>();
     record->node_id = node_id;
+#ifndef NDEBUG
     record->image_url = GetImageUrl(object);
+#endif
     // Mind that first_size has to be assigned at the push of
     // size_ordered_set_ since it's the sorting key.
     record->first_size = rect_size;
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
index ce36a84..52da648 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.h
@@ -31,7 +31,9 @@
   unsigned frame_index = 0;
   base::TimeTicks first_paint_time_after_loaded = base::TimeTicks();
   bool loaded = false;
+#ifndef NDEBUG
   String image_url = "";
+#endif
 };
 
 // ImagePaintTimingDetector contains Largest Image Paint and Last Image Paint.
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
index 8a8d85d..6ee2cf6b 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
@@ -46,7 +46,9 @@
     const TextRecord& first_text_paint,
     unsigned candidate_index) const {
   value.SetInteger("DOMNodeId", static_cast<int>(first_text_paint.node_id));
+#ifndef NDEBUG
   value.SetString("text", first_text_paint.text);
+#endif
   value.SetInteger("size", static_cast<int>(first_text_paint.first_size));
   value.SetInteger("candidateIndex", candidate_index);
   value.SetString("frame",
@@ -211,8 +213,12 @@
     size_zero_node_ids_.insert(node_id);
   } else {
     // Non-trivial text is found.
-    TextRecord record = {node_id, rect_size, base::TimeTicks(),
-                         ToLayoutText(&object)->GetText()};
+    TextRecord record;
+    record.node_id = node_id;
+    record.first_size = rect_size;
+#ifndef NDEBUG
+    record.text = ToLayoutText(&object)->GetText();
+#endif
     texts_to_record_swap_time_.push_back(record);
   }
 
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
index 72b527e..8403420 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
@@ -22,7 +22,9 @@
   DOMNodeId node_id = kInvalidDOMNodeId;
   uint64_t first_size = 0;
   base::TimeTicks first_paint_time = base::TimeTicks();
+#ifndef NDEBUG
   String text = "";
+#endif
 };
 
 // TextPaintTimingDetector contains Largest Text Paint and Last Text Paint.
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
index c0ef89c..6e8fd06 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/ServiceWorkersView.js
@@ -60,12 +60,12 @@
     const updateOnReloadSetting = Common.settings.createSetting('serviceWorkerUpdateOnReload', false);
     updateOnReloadSetting.setTitle(Common.UIString('Update on reload'));
     const forceUpdate = new UI.ToolbarSettingCheckbox(
-        updateOnReloadSetting, Common.UIString('Force update Service Worker on page reload'));
+        updateOnReloadSetting, ls`On page reload, force the service worker to update, and activate it`);
     this._toolbar.appendToolbarItem(forceUpdate);
     const bypassServiceWorkerSetting = Common.settings.createSetting('bypassServiceWorker', false);
     bypassServiceWorkerSetting.setTitle(Common.UIString('Bypass for network'));
     const fallbackToNetwork = new UI.ToolbarSettingCheckbox(
-        bypassServiceWorkerSetting, Common.UIString('Bypass Service Worker and load resources from the network'));
+        bypassServiceWorkerSetting, ls`Bypass service worker and load resources from the network`);
     this._toolbar.appendToolbarItem(fallbackToNetwork);
 
     /** @type {!Map<!SDK.ServiceWorkerManager, !Array<!Common.EventTarget.EventDescriptor>>}*/
diff --git a/third_party/blink/renderer/modules/cache_storage/OWNERS b/third_party/blink/renderer/modules/cache_storage/OWNERS
index 6df3d341..f8dfe14 100644
--- a/third_party/blink/renderer/modules/cache_storage/OWNERS
+++ b/third_party/blink/renderer/modules/cache_storage/OWNERS
@@ -1,10 +1,4 @@
-kinuko@chromium.org
-jsbell@chromium.org
-falken@chromium.org
-horo@chromium.org
-nhiroki@chromium.org
-jkarlin@chromium.org
-pwnall@chromium.org
+file://content/browser/cache_storage/OWNERS
 
 # TEAM: storage-dev@chromium.org
 # COMPONENT: Blink>Storage>CacheStorage
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
index 70a3096..6fc3bfa 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -52,6 +52,7 @@
 #include "third_party/blink/renderer/core/layout/hit_test_canvas_result.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
+#include "third_party/blink/renderer/core/origin_trials/origin_trials.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/canvas_style.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/hit_region.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/path_2d.h"
@@ -936,6 +937,8 @@
     settings->setColorSpace(ColorSpaceAsString());
     settings->setPixelFormat(PixelFormatAsString());
   }
+  if (origin_trials::LowLatencyCanvasEnabled(&canvas()->GetDocument()))
+    settings->setLowLatency(canvas()->LowLatencyEnabled());
   return settings;
 }
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_settings.idl b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_settings.idl
index 93c38d7..abcef845 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_settings.idl
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_settings.idl
@@ -9,5 +9,7 @@
     boolean alpha = true;
     [RuntimeEnabled=CanvasColorManagement] CanvasColorSpace colorSpace = "srgb";
     [RuntimeEnabled=CanvasColorManagement] CanvasPixelFormat pixelFormat = "uint8";
+    // TODO(crbug.com/788439): remove OriginTrialEnabled.
+    [OriginTrialEnabled=LowLatencyCanvas] boolean lowLatency = false;
 };
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
index e594ea1..50aec59 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
@@ -92,7 +92,10 @@
     }
   }
 
-  void OnError(const IDBDatabaseError& error) override {
+  void SetState(base::WeakPtr<WebIDBCursorImpl> cursor,
+                int64_t transaction_id) override {}
+
+  void Error(int32_t code, const String& message) override {
     if (!promise_resolver_)
       return;
 
@@ -105,18 +108,23 @@
     promise_resolver_.Clear();
   }
 
-  void OnSuccess(
-      const Vector<IDBNameAndVersion>& idb_name_and_version_list) override {
+  void SuccessNamesAndVersionsList(
+      Vector<mojom::blink::IDBNameAndVersionPtr> names_and_versions) override {
     if (!promise_resolver_)
       return;
 
     HeapVector<Member<IDBDatabaseInfo>> name_and_version_list;
-    for (const auto& item : idb_name_and_version_list) {
+    name_and_version_list.ReserveInitialCapacity(name_and_version_list.size());
+    for (const mojom::blink::IDBNameAndVersionPtr& name_version :
+         names_and_versions) {
+      const IDBNameAndVersion idb_name_and_version(name_version->name,
+                                                   name_version->version);
       IDBDatabaseInfo* idb_info = IDBDatabaseInfo::Create();
-      idb_info->setName(item.name);
-      idb_info->setVersion(item.version);
+      idb_info->setName(name_version->name);
+      idb_info->setVersion(name_version->version);
       name_and_version_list.push_back(idb_info);
     }
+
     probe::AsyncTask async_task(
         ExecutionContext::From(promise_resolver_->GetScriptState()), this,
         "success");
@@ -124,45 +132,56 @@
     promise_resolver_.Clear();
   }
 
-  void OnSuccess(const Vector<String>&) override { NOTREACHED(); }
+  void SuccessStringList(const Vector<String>&) override { NOTREACHED(); }
 
-  void OnSuccess(WebIDBCursor* cursor,
-                 std::unique_ptr<IDBKey> key,
-                 std::unique_ptr<IDBKey> primary_key,
-                 std::unique_ptr<IDBValue> value) override {
+  void SuccessCursor(
+      mojom::blink::IDBCursorAssociatedPtrInfo cursor_info,
+      std::unique_ptr<IDBKey> key,
+      std::unique_ptr<IDBKey> primary_key,
+      base::Optional<std::unique_ptr<IDBValue>> optional_value) override {
     NOTREACHED();
   }
 
-  void OnSuccess(WebIDBDatabase* backend,
-                 const IDBDatabaseMetadata& metadata) override {
+  void SuccessCursorPrefetch(
+      Vector<std::unique_ptr<IDBKey>> keys,
+      Vector<std::unique_ptr<IDBKey>> primary_keys,
+      Vector<std::unique_ptr<IDBValue>> values) override {
     NOTREACHED();
   }
 
-  void OnSuccess(std::unique_ptr<IDBKey> key) override { NOTREACHED(); }
-
-  void OnSuccess(std::unique_ptr<IDBValue> value) override { NOTREACHED(); }
-
-  void OnSuccess(Vector<std::unique_ptr<IDBValue>> values) override {
+  void SuccessDatabase(mojom::blink::IDBDatabaseAssociatedPtrInfo backend,
+                       const IDBDatabaseMetadata& metadata) override {
     NOTREACHED();
   }
 
-  void OnSuccess(long long value) override { NOTREACHED(); }
+  void SuccessKey(std::unique_ptr<IDBKey> key) override { NOTREACHED(); }
 
-  void OnSuccess() override { NOTREACHED(); }
-
-  void OnSuccess(std::unique_ptr<IDBKey> key,
-                 std::unique_ptr<IDBKey> primary_key,
-                 std::unique_ptr<IDBValue> value) override {
+  void SuccessValue(mojom::blink::IDBReturnValuePtr return_value) override {
     NOTREACHED();
   }
 
-  void OnBlocked(long long old_version) override { NOTREACHED(); }
+  void SuccessArray(Vector<mojom::blink::IDBReturnValuePtr> values) override {
+    NOTREACHED();
+  }
 
-  void OnUpgradeNeeded(long long old_version,
-                       WebIDBDatabase* database,
-                       const IDBDatabaseMetadata& metadata,
-                       mojom::IDBDataLoss data_loss,
-                       String data_loss_message) override {
+  void SuccessInteger(int64_t value) override { NOTREACHED(); }
+
+  void Success() override { NOTREACHED(); }
+
+  void SuccessCursorContinue(
+      std::unique_ptr<IDBKey> key,
+      std::unique_ptr<IDBKey> primary_key,
+      base::Optional<std::unique_ptr<IDBValue>> value) override {
+    NOTREACHED();
+  }
+
+  void Blocked(int64_t old_version) override { NOTREACHED(); }
+
+  void UpgradeNeeded(mojom::blink::IDBDatabaseAssociatedPtrInfo database,
+                     int64_t old_version,
+                     mojom::IDBDataLoss data_loss,
+                     const String& data_loss_message,
+                     const IDBDatabaseMetadata& metadata) override {
     NOTREACHED();
   }
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_factory_test.cc
index bd69c07..a562379 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory_test.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory_test.cc
@@ -9,6 +9,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/scoped_refptr.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
@@ -82,8 +83,8 @@
   EXPECT_FALSE(on_fulfilled);
   EXPECT_FALSE(on_rejected);
 
-  const Vector<IDBNameAndVersion> name_and_info_list;
-  callbacks->OnSuccess(name_and_info_list);
+  Vector<mojom::blink::IDBNameAndVersionPtr> name_and_info_list;
+  callbacks->SuccessNamesAndVersionsList(std::move(name_and_info_list));
 
   EXPECT_FALSE(on_fulfilled);
   EXPECT_FALSE(on_rejected);
@@ -117,7 +118,7 @@
   EXPECT_FALSE(on_fulfilled);
   EXPECT_FALSE(on_rejected);
 
-  callbacks->OnError(IDBDatabaseError(1));
+  callbacks->Error(0, String());
 
   EXPECT_FALSE(on_fulfilled);
   EXPECT_FALSE(on_rejected);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
index 0aad1d2..51a9b3fa 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
@@ -28,7 +28,10 @@
 #include <memory>
 
 #include "base/memory/scoped_refptr.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
 #include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
@@ -59,6 +62,114 @@
 namespace blink {
 namespace {
 
+class BackendDatabaseWithMockedClose
+    : public testing::StrictMock<mojom::blink::IDBDatabase> {
+ public:
+  explicit BackendDatabaseWithMockedClose(
+      mojom::blink::IDBDatabaseAssociatedRequest request)
+      : binding_(this, std::move(request)) {
+    binding_.set_connection_error_handler(
+        base::BindOnce(&BackendDatabaseWithMockedClose::DatabaseDestroyed,
+                       base::Unretained(this)));
+  }
+
+  void DatabaseDestroyed() { destroyed_ = true; }
+
+  void CreateObjectStore(int64_t transaction_id,
+                         int64_t object_store_id,
+                         const WTF::String& name,
+                         const ::blink::IDBKeyPath& key_path,
+                         bool auto_increment) override {}
+  void DeleteObjectStore(int64_t transaction_id,
+                         int64_t object_store_id) override {}
+  void RenameObjectStore(int64_t transaction_id,
+                         int64_t object_store_id,
+                         const WTF::String& new_name) override {}
+  void CreateTransaction(int64_t transaction_id,
+                         const WTF::Vector<int64_t>& object_store_ids,
+                         mojom::blink::IDBTransactionMode mode) override {}
+  MOCK_METHOD0(Close, void());
+  void VersionChangeIgnored() override {}
+  void AddObserver(int64_t transaction_id,
+                   int32_t observer_id,
+                   bool include_transaction,
+                   bool no_records,
+                   bool values,
+                   uint32_t operation_types) override {}
+  void RemoveObservers(const WTF::Vector<int32_t>& observers) override {}
+  void Get(int64_t transaction_id,
+           int64_t object_store_id,
+           int64_t index_id,
+           mojom::blink::IDBKeyRangePtr key_range,
+           bool key_only,
+           mojom::blink::IDBCallbacksAssociatedPtrInfo callbacks) override {}
+  void GetAll(int64_t transaction_id,
+              int64_t object_store_id,
+              int64_t index_id,
+              mojom::blink::IDBKeyRangePtr key_range,
+              bool key_only,
+              int64_t max_count,
+              mojom::blink::IDBCallbacksAssociatedPtrInfo callbacks) override {}
+  void Put(int64_t transaction_id,
+           int64_t object_store_id,
+           std::unique_ptr<::blink::IDBValue> value,
+           std::unique_ptr<::blink::IDBKey> key,
+           mojom::blink::IDBPutMode mode,
+           WTF::Vector<::blink::IDBIndexKeys> index_keys,
+           mojom::blink::IDBCallbacksAssociatedPtrInfo callbacks) override {}
+  void SetIndexKeys(int64_t transaction_id,
+                    int64_t object_store_id,
+                    std::unique_ptr<::blink::IDBKey> primary_key,
+                    WTF::Vector<::blink::IDBIndexKeys> index_keys) override {}
+  void SetIndexesReady(int64_t transaction_id,
+                       int64_t object_store_id,
+                       const WTF::Vector<int64_t>& index_ids) override {}
+  void OpenCursor(
+      int64_t transaction_id,
+      int64_t object_store_id,
+      int64_t index_id,
+      mojom::blink::IDBKeyRangePtr key_range,
+      mojom::blink::IDBCursorDirection direction,
+      bool key_only,
+      mojom::blink::IDBTaskType task_type,
+      mojom::blink::IDBCallbacksAssociatedPtrInfo callbacks) override {}
+  void Count(int64_t transaction_id,
+             int64_t object_store_id,
+             int64_t index_id,
+             mojom::blink::IDBKeyRangePtr key_range,
+             mojom::blink::IDBCallbacksAssociatedPtrInfo callbacks) override {}
+  void DeleteRange(
+      int64_t transaction_id,
+      int64_t object_store_id,
+      mojom::blink::IDBKeyRangePtr key_range,
+      mojom::blink::IDBCallbacksAssociatedPtrInfo callbacks) override {}
+  void Clear(int64_t transaction_id,
+             int64_t object_store_id,
+             mojom::blink::IDBCallbacksAssociatedPtrInfo callbacks) override {}
+  void CreateIndex(int64_t transaction_id,
+                   int64_t object_store_id,
+                   int64_t index_id,
+                   const WTF::String& name,
+                   const ::blink::IDBKeyPath& key_path,
+                   bool unique,
+                   bool multi_entry) override {}
+  void DeleteIndex(int64_t transaction_id,
+                   int64_t object_store_id,
+                   int64_t index_id) override {}
+  void RenameIndex(int64_t transaction_id,
+                   int64_t object_store_id,
+                   int64_t index_id,
+                   const WTF::String& new_name) override {}
+  void Abort(int64_t transaction_id) override {}
+  void Commit(int64_t transaction_id) override {}
+
+  bool destroyed() { return destroyed_; }
+
+ private:
+  bool destroyed_ = false;
+  mojo::AssociatedBinding<mojom::blink::IDBDatabase> binding_;
+};
+
 class IDBRequestTest : public testing::Test {
  protected:
   void SetUp() override {
@@ -247,8 +358,12 @@
   Persistent<IDBDatabaseCallbacks> callbacks = IDBDatabaseCallbacks::Create();
 
   {
-    std::unique_ptr<MockWebIDBDatabase> backend = MockWebIDBDatabase::Create();
-    EXPECT_CALL(*backend, Close()).Times(1);
+    mojom::blink::IDBDatabaseAssociatedPtr ptr;
+    std::unique_ptr<BackendDatabaseWithMockedClose> mock_database =
+        std::make_unique<BackendDatabaseWithMockedClose>(
+            mojo::MakeRequestAssociatedWithDedicatedPipe(&ptr));
+    EXPECT_CALL(*mock_database, Close()).Times(1);
+
     IDBOpenDBRequest* request = IDBOpenDBRequest::Create(
         scope.GetScriptState(), callbacks, kTransactionId, kVersion,
         IDBRequest::AsyncTraceState());
@@ -256,13 +371,18 @@
     std::unique_ptr<WebIDBCallbacks> callbacks = request->CreateWebCallbacks();
 
     scope.GetExecutionContext()->NotifyContextDestroyed();
-    callbacks->OnUpgradeNeeded(kOldVersion, backend.release(), metadata,
-                               mojom::IDBDataLoss::None, String());
+    callbacks->UpgradeNeeded(ptr.PassInterface(), kOldVersion,
+                             mojom::IDBDataLoss::None, String(), metadata);
+    platform_->RunUntilIdle();
   }
 
   {
-    std::unique_ptr<MockWebIDBDatabase> backend = MockWebIDBDatabase::Create();
-    EXPECT_CALL(*backend, Close()).Times(1);
+    mojom::blink::IDBDatabaseAssociatedPtr ptr;
+    std::unique_ptr<BackendDatabaseWithMockedClose> mock_database =
+        std::make_unique<BackendDatabaseWithMockedClose>(
+            mojo::MakeRequestAssociatedWithDedicatedPipe(&ptr));
+    EXPECT_CALL(*mock_database, Close()).Times(1);
+
     IDBOpenDBRequest* request = IDBOpenDBRequest::Create(
         scope.GetScriptState(), callbacks, kTransactionId, kVersion,
         IDBRequest::AsyncTraceState());
@@ -270,7 +390,8 @@
     std::unique_ptr<WebIDBCallbacks> callbacks = request->CreateWebCallbacks();
 
     scope.GetExecutionContext()->NotifyContextDestroyed();
-    callbacks->OnSuccess(backend.release(), metadata);
+    callbacks->SuccessDatabase(ptr.PassInterface(), metadata);
+    platform_->RunUntilIdle();
   }
 }
 
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.cc b/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.cc
index 766b67c..114fe11e 100644
--- a/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.cc
@@ -11,96 +11,55 @@
 #include "third_party/blink/renderer/modules/indexeddb/idb_name_and_version.h"
 #include "third_party/blink/renderer/modules/indexeddb/indexed_db_dispatcher.h"
 #include "third_party/blink/renderer/modules/indexeddb/web_idb_callbacks.h"
-#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.h"
 #include "third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h"
 
-using blink::mojom::blink::IDBDatabaseAssociatedPtrInfo;
-
 namespace blink {
 
-namespace {
-
-std::unique_ptr<IDBValue> ConvertReturnValue(
-    const mojom::blink::IDBReturnValuePtr& input) {
-  if (!input) {
-    return IDBValue::Create(scoped_refptr<SharedBuffer>(),
-                            Vector<WebBlobInfo>());
-  }
-
-  std::unique_ptr<IDBValue> output = std::move(input->value);
-  output->SetInjectedPrimaryKey(std::move(input->primary_key), input->key_path);
-  return output;
-}
-
-IDBNameAndVersion ConvertNameVersion(
-    const mojom::blink::IDBNameAndVersionPtr& name_and_version) {
-  return IDBNameAndVersion(name_and_version->name, name_and_version->version);
-}
-
-}  // namespace
-
 IndexedDBCallbacksImpl::IndexedDBCallbacksImpl(
-    std::unique_ptr<WebIDBCallbacks> callbacks,
-    int64_t transaction_id,
-    const base::WeakPtr<WebIDBCursorImpl>& cursor)
-    : callbacks_(std::move(callbacks)),
-      cursor_(cursor),
-      transaction_id_(transaction_id) {}
+    std::unique_ptr<WebIDBCallbacks> callbacks)
+    : callbacks_(std::move(callbacks)) {}
 
 IndexedDBCallbacksImpl::~IndexedDBCallbacksImpl() = default;
 
 void IndexedDBCallbacksImpl::Error(int32_t code, const WTF::String& message) {
-  callbacks_->OnError(IDBDatabaseError(code, String(message)));
+  callbacks_->Error(code, message);
   callbacks_.reset();
 }
 
 void IndexedDBCallbacksImpl::SuccessNamesAndVersionsList(
     Vector<mojom::blink::IDBNameAndVersionPtr> names_and_versions) {
-  Vector<IDBNameAndVersion> output_names_and_versions;
-  output_names_and_versions.ReserveInitialCapacity(names_and_versions.size());
-  for (const mojom::blink::IDBNameAndVersionPtr& name_version :
-       names_and_versions)
-    output_names_and_versions.emplace_back(ConvertNameVersion(name_version));
-  callbacks_->OnSuccess(output_names_and_versions);
+  callbacks_->SuccessNamesAndVersionsList(std::move(names_and_versions));
   callbacks_.reset();
 }
 
 void IndexedDBCallbacksImpl::SuccessStringList(const Vector<String>& value) {
-  Vector<String> values(value.size());
-  std::transform(value.begin(), value.end(), values.begin(),
-                 [](const WTF::String& s) { return String(s); });
-  callbacks_->OnSuccess(values);
+  callbacks_->SuccessStringList(std::move(value));
   callbacks_.reset();
 }
 
 void IndexedDBCallbacksImpl::Blocked(int64_t existing_version) {
-  callbacks_->OnBlocked(existing_version);
+  callbacks_->Blocked(existing_version);
   // Not resetting |callbacks_|.  In this instance we will have to forward at
-  // least one other call in the set OnUpgradeNeeded() / OnSuccess() /
-  // OnError().
+  // least one other call in the set UpgradeNeeded() / Success() /
+  // Error().
 }
 
 void IndexedDBCallbacksImpl::UpgradeNeeded(
-    IDBDatabaseAssociatedPtrInfo database_info,
+    mojom::blink::IDBDatabaseAssociatedPtrInfo database_info,
     int64_t old_version,
     mojom::IDBDataLoss data_loss,
     const String& data_loss_message,
     const IDBDatabaseMetadata& metadata) {
-  WebIDBDatabase* database = new WebIDBDatabaseImpl(std::move(database_info));
-  callbacks_->OnUpgradeNeeded(old_version, database, metadata, data_loss,
-                              String(data_loss_message));
+  callbacks_->UpgradeNeeded(std::move(database_info), old_version,
+                            std::move(data_loss), data_loss_message, metadata);
   // Not resetting |callbacks_|.  In this instance we will have to forward at
   // least one other call in the set OnSuccess() / OnError().
 }
 
 void IndexedDBCallbacksImpl::SuccessDatabase(
-    IDBDatabaseAssociatedPtrInfo database_info,
+    mojom::blink::IDBDatabaseAssociatedPtrInfo database_info,
     const IDBDatabaseMetadata& metadata) {
-  WebIDBDatabase* database = nullptr;
-  if (database_info.is_valid())
-    database = new WebIDBDatabaseImpl(std::move(database_info));
-
-  callbacks_->OnSuccess(database, metadata);
+  callbacks_->SuccessDatabase(std::move(database_info), metadata);
   callbacks_.reset();
 }
 
@@ -109,24 +68,14 @@
     std::unique_ptr<IDBKey> key,
     std::unique_ptr<IDBKey> primary_key,
     base::Optional<std::unique_ptr<IDBValue>> optional_value) {
-  WebIDBCursorImpl* cursor =
-      new WebIDBCursorImpl(std::move(cursor_info), transaction_id_);
-  std::unique_ptr<IDBValue> value;
-  if (optional_value.has_value()) {
-    value = std::move(optional_value.value());
-  } else {
-    value =
-        IDBValue::Create(scoped_refptr<SharedBuffer>(), Vector<WebBlobInfo>());
-  }
-  DCHECK(value);
-  callbacks_->OnSuccess(cursor, std::move(key), std::move(primary_key),
-                        std::move(value));
+  callbacks_->SuccessCursor(std::move(cursor_info), std::move(key),
+                            std::move(primary_key), std::move(optional_value));
   callbacks_.reset();
 }
 
 void IndexedDBCallbacksImpl::SuccessValue(
     mojom::blink::IDBReturnValuePtr value) {
-  callbacks_->OnSuccess(ConvertReturnValue(value));
+  callbacks_->SuccessValue(std::move(value));
   callbacks_.reset();
 }
 
@@ -134,16 +83,8 @@
     std::unique_ptr<IDBKey> key,
     std::unique_ptr<IDBKey> primary_key,
     base::Optional<std::unique_ptr<IDBValue>> optional_value) {
-  std::unique_ptr<IDBValue> value;
-  if (optional_value.has_value()) {
-    value = std::move(optional_value.value());
-  } else {
-    value =
-        IDBValue::Create(scoped_refptr<SharedBuffer>(), Vector<WebBlobInfo>());
-  }
-  DCHECK(value);
-  callbacks_->OnSuccess(std::move(key), std::move(primary_key),
-                        std::move(value));
+  callbacks_->SuccessCursorContinue(std::move(key), std::move(primary_key),
+                                    std::move(optional_value));
   callbacks_.reset();
 }
 
@@ -151,36 +92,29 @@
     Vector<std::unique_ptr<IDBKey>> keys,
     Vector<std::unique_ptr<IDBKey>> primary_keys,
     Vector<std::unique_ptr<IDBValue>> values) {
-  if (cursor_) {
-    cursor_->SetPrefetchData(std::move(keys), std::move(primary_keys),
-                             std::move(values));
-    cursor_->CachedContinue(callbacks_.get());
-  }
+  callbacks_->SuccessCursorPrefetch(std::move(keys), std::move(primary_keys),
+                                    std::move(values));
   callbacks_.reset();
 }
 
 void IndexedDBCallbacksImpl::SuccessArray(
     Vector<mojom::blink::IDBReturnValuePtr> values) {
-  Vector<std::unique_ptr<IDBValue>> idb_values;
-  idb_values.ReserveInitialCapacity(values.size());
-  for (const mojom::blink::IDBReturnValuePtr& value : values)
-    idb_values.emplace_back(ConvertReturnValue(value));
-  callbacks_->OnSuccess(std::move(idb_values));
+  callbacks_->SuccessArray(std::move(values));
   callbacks_.reset();
 }
 
 void IndexedDBCallbacksImpl::SuccessKey(std::unique_ptr<IDBKey> key) {
-  callbacks_->OnSuccess(std::move(key));
+  callbacks_->SuccessKey(std::move(key));
   callbacks_.reset();
 }
 
 void IndexedDBCallbacksImpl::SuccessInteger(int64_t value) {
-  callbacks_->OnSuccess(value);
+  callbacks_->SuccessInteger(value);
   callbacks_.reset();
 }
 
 void IndexedDBCallbacksImpl::Success() {
-  callbacks_->OnSuccess();
+  callbacks_->Success();
   callbacks_.reset();
 }
 
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h b/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h
index 1888279..8622e83 100644
--- a/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_callbacks_impl.h
@@ -18,7 +18,6 @@
 namespace blink {
 
 class WebIDBCallbacks;
-class WebIDBCursorImpl;
 
 // Implements the child-process end of the pipe used to deliver callbacks.
 // |callback_runner_| is used to post tasks back to the thread which owns the
@@ -30,9 +29,7 @@
   // cases.
   enum : int64_t { kNoTransaction = -1 };
 
-  IndexedDBCallbacksImpl(std::unique_ptr<WebIDBCallbacks> callbacks,
-                         int64_t transaction_id,
-                         const base::WeakPtr<WebIDBCursorImpl>& cursor);
+  IndexedDBCallbacksImpl(std::unique_ptr<WebIDBCallbacks> callbacks);
   ~IndexedDBCallbacksImpl() override;
 
   // mojom::blink::IDBCallbacks implementation:
@@ -68,8 +65,6 @@
  private:
   scoped_refptr<base::SingleThreadTaskRunner> callback_runner_;
   std::unique_ptr<WebIDBCallbacks> callbacks_;
-  base::WeakPtr<WebIDBCursorImpl> cursor_;
-  int64_t transaction_id_;
 
   DISALLOW_COPY_AND_ASSIGN(IndexedDBCallbacksImpl);
 };
diff --git a/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.cc b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.cc
index 2a77587..3a4254d 100644
--- a/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.cc
+++ b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.cc
@@ -13,29 +13,35 @@
 
 MockWebIDBCallbacks::~MockWebIDBCallbacks() {}
 
-void MockWebIDBCallbacks::OnSuccess(std::unique_ptr<IDBKey> key,
-                                    std::unique_ptr<IDBKey> primaryKey,
-                                    std::unique_ptr<IDBValue> value) {
-  DoOnSuccess(key, primaryKey, value);
+void MockWebIDBCallbacks::SetState(base::WeakPtr<WebIDBCursorImpl> cursor,
+                                   int64_t transaction_id) {}
+
+void MockWebIDBCallbacks::SuccessCursorContinue(
+    std::unique_ptr<IDBKey> key,
+    std::unique_ptr<IDBKey> primary_key,
+    base::Optional<std::unique_ptr<IDBValue>> value) {
+  DoSuccessCursorContinue(key, primary_key, value);
 }
 
-void MockWebIDBCallbacks::OnSuccess(WebIDBCursor* cursor,
-                                    std::unique_ptr<IDBKey> key,
-                                    std::unique_ptr<IDBKey> primaryKey,
-                                    std::unique_ptr<IDBValue> value) {
-  DoOnSuccess(cursor, key, primaryKey, value);
+void MockWebIDBCallbacks::SuccessCursor(
+    mojom::blink::IDBCursorAssociatedPtrInfo cursor_info,
+    std::unique_ptr<IDBKey> key,
+    std::unique_ptr<IDBKey> primary_key,
+    base::Optional<std::unique_ptr<IDBValue>> optional_value) {
+  DoSuccessCursor(cursor_info, key, primary_key, optional_value);
 }
 
-void MockWebIDBCallbacks::OnSuccess(std::unique_ptr<IDBKey> key) {
-  DoOnSuccess(key);
+void MockWebIDBCallbacks::SuccessKey(std::unique_ptr<IDBKey> key) {
+  DoSuccessKey(key);
 }
 
-void MockWebIDBCallbacks::OnSuccess(std::unique_ptr<IDBValue> value) {
-  DoOnSuccess(value);
+void MockWebIDBCallbacks::SuccessValue(mojom::blink::IDBReturnValuePtr value) {
+  DoSuccessValue(value);
 }
 
-void MockWebIDBCallbacks::OnSuccess(Vector<std::unique_ptr<IDBValue>> values) {
-  DoOnSuccess(values);
+void MockWebIDBCallbacks::SuccessArray(
+    Vector<mojom::blink::IDBReturnValuePtr> values) {
+  DoSuccessArray(values);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h
index 7b2a22a..c980ee3 100644
--- a/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h
+++ b/third_party/blink/renderer/modules/indexeddb/mock_web_idb_callbacks.h
@@ -6,7 +6,9 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_MOCK_WEB_IDB_CALLBACKS_H_
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/public/platform/web_blob_info.h"
 #include "third_party/blink/public/web/web_heap.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_database_error.h"
@@ -20,48 +22,69 @@
  public:
   MockWebIDBCallbacks();
   ~MockWebIDBCallbacks() override;
-  MOCK_METHOD1(OnError, void(const IDBDatabaseError&));
 
-  void OnSuccess(std::unique_ptr<IDBKey>,
-                 std::unique_ptr<IDBKey> primaryKey,
-                 std::unique_ptr<IDBValue>) override;
-  MOCK_METHOD3(DoOnSuccess,
+  void SetState(base::WeakPtr<WebIDBCursorImpl>, int64_t);
+
+  MOCK_METHOD2(Error, void(int32_t, const String&));
+
+  void SuccessCursorContinue(
+      std::unique_ptr<IDBKey>,
+      std::unique_ptr<IDBKey> primaryKey,
+      base::Optional<std::unique_ptr<IDBValue>>) override;
+  MOCK_METHOD3(DoSuccessCursorContinue,
                void(const std::unique_ptr<IDBKey>& key,
                     const std::unique_ptr<IDBKey>& primaryKey,
-                    const std::unique_ptr<IDBValue>& value));
+                    const base::Optional<std::unique_ptr<IDBValue>>& value));
 
-  MOCK_METHOD1(OnSuccess, void(const Vector<IDBNameAndVersion>&));
-  MOCK_METHOD1(OnSuccess, void(const Vector<String>&));
+  MOCK_METHOD1(SuccessNamesAndVersionsList,
+               void(Vector<mojom::blink::IDBNameAndVersionPtr>));
 
-  void OnSuccess(WebIDBCursor* cursor,
-                 std::unique_ptr<IDBKey> key,
-                 std::unique_ptr<IDBKey> primaryKey,
-                 std::unique_ptr<IDBValue> value) override;
-  MOCK_METHOD4(DoOnSuccess,
-               void(WebIDBCursor*,
-                    const std::unique_ptr<IDBKey>&,
-                    const std::unique_ptr<IDBKey>& primaryKey,
-                    const std::unique_ptr<IDBValue>&));
+  MOCK_METHOD1(SuccessStringList, void(const Vector<String>&));
 
-  MOCK_METHOD2(OnSuccess, void(WebIDBDatabase*, const IDBDatabaseMetadata&));
-  void OnSuccess(std::unique_ptr<IDBKey>) override;
-  MOCK_METHOD1(DoOnSuccess, void(const std::unique_ptr<IDBKey>&));
+  void SuccessCursor(
+      mojom::blink::IDBCursorAssociatedPtrInfo cursor_info,
+      std::unique_ptr<IDBKey> key,
+      std::unique_ptr<IDBKey> primary_key,
+      base::Optional<std::unique_ptr<IDBValue>> optional_value) override;
+  MOCK_METHOD4(
+      DoSuccessCursor,
+      void(const mojom::blink::IDBCursorAssociatedPtrInfo& cursor_info,
+           const std::unique_ptr<IDBKey>& key,
+           const std::unique_ptr<IDBKey>& primary_key,
+           const base::Optional<std::unique_ptr<IDBValue>>& optional_value));
 
-  void OnSuccess(std::unique_ptr<IDBValue>) override;
-  MOCK_METHOD1(DoOnSuccess, void(const std::unique_ptr<IDBValue>&));
+  MOCK_METHOD3(SuccessCursorPrefetch,
+               void(Vector<std::unique_ptr<IDBKey>> keys,
+                    Vector<std::unique_ptr<IDBKey>> primary_keys,
+                    Vector<std::unique_ptr<IDBValue>> values));
 
-  void OnSuccess(Vector<std::unique_ptr<IDBValue>>) override;
-  MOCK_METHOD1(DoOnSuccess, void(const Vector<std::unique_ptr<IDBValue>>&));
+  MOCK_METHOD2(SuccessDatabase,
+               void(mojom::blink::IDBDatabaseAssociatedPtrInfo,
+                    const IDBDatabaseMetadata&));
 
-  MOCK_METHOD1(OnSuccess, void(long long));
-  MOCK_METHOD0(OnSuccess, void());
-  MOCK_METHOD1(OnBlocked, void(long long oldVersion));
-  MOCK_METHOD5(OnUpgradeNeeded,
-               void(long long oldVersion,
-                    WebIDBDatabase*,
-                    const IDBDatabaseMetadata&,
+  void SuccessKey(std::unique_ptr<IDBKey>) override;
+  MOCK_METHOD1(DoSuccessKey, void(const std::unique_ptr<IDBKey>&));
+
+  void SuccessValue(mojom::blink::IDBReturnValuePtr) override;
+  MOCK_METHOD1(DoSuccessValue, void(const mojom::blink::IDBReturnValuePtr&));
+
+  void SuccessArray(Vector<mojom::blink::IDBReturnValuePtr>) override;
+  MOCK_METHOD1(DoSuccessArray,
+               void(const Vector<mojom::blink::IDBReturnValuePtr>&));
+
+  MOCK_METHOD1(SuccessInteger, void(int64_t));
+
+  MOCK_METHOD0(Success, void());
+
+  MOCK_METHOD1(Blocked, void(int64_t oldVersion));
+
+  MOCK_METHOD5(UpgradeNeeded,
+               void(mojom::blink::IDBDatabaseAssociatedPtrInfo,
+                    int64_t oldVersion,
                     mojom::IDBDataLoss dataLoss,
-                    String dataLossMessage));
+                    const String& dataLossMessage,
+                    const IDBDatabaseMetadata&));
+
   MOCK_METHOD0(Detach, void());
 
  private:
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks.h b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks.h
index d3ed0bb..f25deea 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks.h
@@ -26,51 +26,57 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_CALLBACKS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_CALLBACKS_H_
 
-#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-shared.h"
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
-namespace WTF {
-class String;
-}
-
 namespace blink {
 
-class IDBDatabaseError;
 class IDBKey;
 class IDBValue;
-class WebIDBCursor;
-class WebIDBDatabase;
+class WebIDBCursorImpl;
 struct IDBDatabaseMetadata;
-struct IDBNameAndVersion;
 
 class WebIDBCallbacks {
  public:
   virtual ~WebIDBCallbacks() = default;
 
+  virtual void SetState(base::WeakPtr<WebIDBCursorImpl> cursor,
+                        int64_t transaction_id) = 0;
+
   // Pointers transfer ownership.
-  virtual void OnError(const IDBDatabaseError&) = 0;
-  virtual void OnSuccess(const Vector<IDBNameAndVersion>&) = 0;
-  virtual void OnSuccess(const Vector<WTF::String>&) = 0;
-  virtual void OnSuccess(WebIDBCursor*,
-                         std::unique_ptr<IDBKey>,
-                         std::unique_ptr<IDBKey> primary_key,
-                         std::unique_ptr<IDBValue>) = 0;
-  virtual void OnSuccess(WebIDBDatabase*, const IDBDatabaseMetadata&) = 0;
-  virtual void OnSuccess(std::unique_ptr<IDBKey>) = 0;
-  virtual void OnSuccess(std::unique_ptr<IDBValue>) = 0;
-  virtual void OnSuccess(Vector<std::unique_ptr<IDBValue>>) = 0;
-  virtual void OnSuccess(long long) = 0;
-  virtual void OnSuccess() = 0;
-  virtual void OnSuccess(std::unique_ptr<IDBKey>,
-                         std::unique_ptr<IDBKey> primary_key,
-                         std::unique_ptr<IDBValue>) = 0;
-  virtual void OnBlocked(long long old_version) = 0;
-  virtual void OnUpgradeNeeded(long long old_version,
-                               WebIDBDatabase*,
-                               const IDBDatabaseMetadata&,
-                               mojom::IDBDataLoss data_loss,
-                               WTF::String data_loss_message) = 0;
+  virtual void Error(int32_t code, const String& message) = 0;
+  virtual void SuccessNamesAndVersionsList(
+      Vector<mojom::blink::IDBNameAndVersionPtr>) = 0;
+  virtual void SuccessStringList(const Vector<String>&) = 0;
+  virtual void SuccessCursor(
+      mojom::blink::IDBCursorAssociatedPtrInfo cursor_info,
+      std::unique_ptr<IDBKey> key,
+      std::unique_ptr<IDBKey> primary_key,
+      base::Optional<std::unique_ptr<IDBValue>> optional_value) = 0;
+  virtual void SuccessCursorPrefetch(
+      Vector<std::unique_ptr<IDBKey>> keys,
+      Vector<std::unique_ptr<IDBKey>> primary_keys,
+      Vector<std::unique_ptr<IDBValue>> values) = 0;
+  virtual void SuccessDatabase(
+      mojom::blink::IDBDatabaseAssociatedPtrInfo database_info,
+      const IDBDatabaseMetadata& metadata) = 0;
+  virtual void SuccessKey(std::unique_ptr<IDBKey>) = 0;
+  virtual void SuccessValue(mojom::blink::IDBReturnValuePtr) = 0;
+  virtual void SuccessArray(Vector<mojom::blink::IDBReturnValuePtr>) = 0;
+  virtual void SuccessInteger(int64_t) = 0;
+  virtual void Success() = 0;
+  virtual void SuccessCursorContinue(
+      std::unique_ptr<IDBKey>,
+      std::unique_ptr<IDBKey> primary_key,
+      base::Optional<std::unique_ptr<IDBValue>>) = 0;
+  virtual void Blocked(int64_t old_version) = 0;
+  virtual void UpgradeNeeded(mojom::blink::IDBDatabaseAssociatedPtrInfo,
+                             int64_t old_version,
+                             mojom::IDBDataLoss data_loss,
+                             const String& data_loss_message,
+                             const IDBDatabaseMetadata&) = 0;
   virtual void Detach() = 0;
 };
 
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
index 9243be0..c4f87a8 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.cc
@@ -31,21 +31,39 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/modules/indexed_db_names.h"
-#include "third_party/blink/renderer/modules/indexeddb/idb_database_error.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_metadata.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_name_and_version.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_request.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_value.h"
 #include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.h"
 #include "third_party/blink/renderer/modules/indexeddb/web_idb_database.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h"
 #include "third_party/blink/renderer/platform/shared_buffer.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 
 namespace blink {
 
+namespace {
+
+std::unique_ptr<IDBValue> ConvertReturnValue(
+    const mojom::blink::IDBReturnValuePtr& input) {
+  if (!input) {
+    return IDBValue::Create(scoped_refptr<SharedBuffer>(),
+                            Vector<WebBlobInfo>());
+  }
+
+  std::unique_ptr<IDBValue> output = std::move(input->value);
+  output->SetInjectedPrimaryKey(std::move(input->primary_key), input->key_path);
+  return output;
+}
+
+}  // namespace
+
 // static
 std::unique_ptr<WebIDBCallbacksImpl> WebIDBCallbacksImpl::Create(
     IDBRequest* request) {
@@ -68,22 +86,28 @@
   }
 }
 
-void WebIDBCallbacksImpl::OnError(const IDBDatabaseError& error) {
+void WebIDBCallbacksImpl::SetState(base::WeakPtr<WebIDBCursorImpl> cursor,
+                                   int64_t transaction_id) {
+  cursor_ = cursor;
+  transaction_id_ = transaction_id;
+}
+
+void WebIDBCallbacksImpl::Error(int32_t code, const String& message) {
   if (!request_)
     return;
 
   probe::AsyncTask async_task(request_->GetExecutionContext(), this, "error");
-  request_->HandleResponse(DOMException::Create(
-      static_cast<DOMExceptionCode>(error.Code()), error.Message()));
+  request_->HandleResponse(
+      DOMException::Create(static_cast<DOMExceptionCode>(code), message));
 }
 
-void WebIDBCallbacksImpl::OnSuccess(
-    const Vector<IDBNameAndVersion>& name_and_version_list) {
+void WebIDBCallbacksImpl::SuccessNamesAndVersionsList(
+    Vector<mojom::blink::IDBNameAndVersionPtr> name_and_version_list) {
   // Only implemented in idb_factory.cc for the promise-based databases() call.
   NOTREACHED();
 }
 
-void WebIDBCallbacksImpl::OnSuccess(const Vector<String>& string_list) {
+void WebIDBCallbacksImpl::SuccessStringList(const Vector<String>& string_list) {
   if (!request_)
     return;
 
@@ -91,25 +115,51 @@
 #if DCHECK_IS_ON()
   DCHECK(!request_->TransactionHasQueuedResults());
 #endif  // DCHECK_IS_ON()
-  request_->EnqueueResponse(string_list);
+  request_->EnqueueResponse(std::move(string_list));
 }
 
-void WebIDBCallbacksImpl::OnSuccess(WebIDBCursor* cursor,
-                                    std::unique_ptr<IDBKey> key,
-                                    std::unique_ptr<IDBKey> primary_key,
-                                    std::unique_ptr<IDBValue> value) {
+void WebIDBCallbacksImpl::SuccessCursor(
+    mojom::blink::IDBCursorAssociatedPtrInfo cursor_info,
+    std::unique_ptr<IDBKey> key,
+    std::unique_ptr<IDBKey> primary_key,
+    base::Optional<std::unique_ptr<IDBValue>> optional_value) {
   if (!request_)
     return;
 
+  std::unique_ptr<WebIDBCursorImpl> cursor = std::make_unique<WebIDBCursorImpl>(
+      std::move(cursor_info), transaction_id_);
+  std::unique_ptr<IDBValue> value;
+  if (optional_value.has_value()) {
+    value = std::move(optional_value.value());
+  } else {
+    value =
+        IDBValue::Create(scoped_refptr<SharedBuffer>(), Vector<WebBlobInfo>());
+  }
+  DCHECK(value);
+
   probe::AsyncTask async_task(request_->GetExecutionContext(), this, "success");
   value->SetIsolate(request_->GetIsolate());
-  request_->HandleResponse(base::WrapUnique(cursor), std::move(key),
+  request_->HandleResponse(std::move(cursor), std::move(key),
                            std::move(primary_key), std::move(value));
 }
 
-void WebIDBCallbacksImpl::OnSuccess(WebIDBDatabase* backend,
-                                    const IDBDatabaseMetadata& metadata) {
-  std::unique_ptr<WebIDBDatabase> db = base::WrapUnique(backend);
+void WebIDBCallbacksImpl::SuccessCursorPrefetch(
+    Vector<std::unique_ptr<IDBKey>> keys,
+    Vector<std::unique_ptr<IDBKey>> primary_keys,
+    Vector<std::unique_ptr<IDBValue>> values) {
+  if (cursor_) {
+    cursor_->SetPrefetchData(std::move(keys), std::move(primary_keys),
+                             std::move(values));
+    cursor_->CachedContinue(this);
+  }
+}
+
+void WebIDBCallbacksImpl::SuccessDatabase(
+    mojom::blink::IDBDatabaseAssociatedPtrInfo database_info,
+    const IDBDatabaseMetadata& metadata) {
+  std::unique_ptr<WebIDBDatabase> db;
+  if (database_info.is_valid())
+    db = std::make_unique<WebIDBDatabaseImpl>(std::move(database_info));
   if (request_) {
     probe::AsyncTask async_task(request_->GetExecutionContext(), this,
                                 "success");
@@ -122,7 +172,7 @@
   }
 }
 
-void WebIDBCallbacksImpl::OnSuccess(std::unique_ptr<IDBKey> key) {
+void WebIDBCallbacksImpl::SuccessKey(std::unique_ptr<IDBKey> key) {
   if (!request_)
     return;
 
@@ -130,27 +180,34 @@
   request_->HandleResponse(std::move(key));
 }
 
-void WebIDBCallbacksImpl::OnSuccess(std::unique_ptr<IDBValue> value) {
+void WebIDBCallbacksImpl::SuccessValue(
+    mojom::blink::IDBReturnValuePtr return_value) {
   if (!request_)
     return;
 
+  std::unique_ptr<IDBValue> value = ConvertReturnValue(return_value);
   probe::AsyncTask async_task(request_->GetExecutionContext(), this, "success");
   value->SetIsolate(request_->GetIsolate());
   request_->HandleResponse(std::move(value));
 }
 
-void WebIDBCallbacksImpl::OnSuccess(Vector<std::unique_ptr<IDBValue>> values) {
+void WebIDBCallbacksImpl::SuccessArray(
+    Vector<mojom::blink::IDBReturnValuePtr> values) {
   if (!request_)
     return;
 
   probe::AsyncTask async_task(request_->GetExecutionContext(), this, "success");
-  for (const std::unique_ptr<IDBValue>& value : values) {
-    value->SetIsolate(request_->GetIsolate());
+  Vector<std::unique_ptr<IDBValue>> idb_values;
+  idb_values.ReserveInitialCapacity(values.size());
+  for (const mojom::blink::IDBReturnValuePtr& value : values) {
+    std::unique_ptr<IDBValue> idb_value = ConvertReturnValue(value);
+    idb_value->SetIsolate(request_->GetIsolate());
+    idb_values.emplace_back(std::move(idb_value));
   }
-  request_->HandleResponse(std::move(values));
+  request_->HandleResponse(std::move(idb_values));
 }
 
-void WebIDBCallbacksImpl::OnSuccess(long long value) {
+void WebIDBCallbacksImpl::SuccessInteger(int64_t value) {
   if (!request_)
     return;
 
@@ -158,7 +215,7 @@
   request_->HandleResponse(value);
 }
 
-void WebIDBCallbacksImpl::OnSuccess() {
+void WebIDBCallbacksImpl::Success() {
   if (!request_)
     return;
 
@@ -166,19 +223,28 @@
   request_->HandleResponse();
 }
 
-void WebIDBCallbacksImpl::OnSuccess(std::unique_ptr<IDBKey> key,
-                                    std::unique_ptr<IDBKey> primary_key,
-                                    std::unique_ptr<IDBValue> value) {
+void WebIDBCallbacksImpl::SuccessCursorContinue(
+    std::unique_ptr<IDBKey> key,
+    std::unique_ptr<IDBKey> primary_key,
+    base::Optional<std::unique_ptr<IDBValue>> optional_value) {
   if (!request_)
     return;
 
   probe::AsyncTask async_task(request_->GetExecutionContext(), this, "success");
+  std::unique_ptr<IDBValue> value;
+  if (optional_value.has_value()) {
+    value = std::move(optional_value.value());
+  } else {
+    value =
+        IDBValue::Create(scoped_refptr<SharedBuffer>(), Vector<WebBlobInfo>());
+  }
+  DCHECK(value);
   value->SetIsolate(request_->GetIsolate());
   request_->HandleResponse(std::move(key), std::move(primary_key),
                            std::move(value));
 }
 
-void WebIDBCallbacksImpl::OnBlocked(long long old_version) {
+void WebIDBCallbacksImpl::Blocked(int64_t old_version) {
   if (!request_)
     return;
 
@@ -189,12 +255,15 @@
   request_->EnqueueBlocked(old_version);
 }
 
-void WebIDBCallbacksImpl::OnUpgradeNeeded(long long old_version,
-                                          WebIDBDatabase* database,
-                                          const IDBDatabaseMetadata& metadata,
-                                          mojom::IDBDataLoss data_loss,
-                                          String data_loss_message) {
-  std::unique_ptr<WebIDBDatabase> db = base::WrapUnique(database);
+void WebIDBCallbacksImpl::UpgradeNeeded(
+    mojom::blink::IDBDatabaseAssociatedPtrInfo database_info,
+    int64_t old_version,
+    mojom::IDBDataLoss data_loss,
+    const String& data_loss_message,
+    const IDBDatabaseMetadata& metadata) {
+  std::unique_ptr<WebIDBDatabase> db;
+  if (database_info.is_valid())
+    db = std::make_unique<WebIDBDatabaseImpl>(std::move(database_info));
   if (request_) {
     probe::AsyncTask async_task(request_->GetExecutionContext(), this,
                                 "upgradeNeeded");
@@ -204,7 +273,7 @@
     request_->EnqueueUpgradeNeeded(old_version, std::move(db),
                                    IDBDatabaseMetadata(metadata), data_loss,
                                    data_loss_message);
-  } else {
+  } else if (db) {
     db->Close();
   }
 }
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.h b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.h
index 1263370..59d17ab 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.h
@@ -31,18 +31,18 @@
 
 #include <memory>
 
+#include "base/memory/weak_ptr.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h"
 #include "third_party/blink/renderer/modules/indexeddb/web_idb_callbacks.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
 
-class IDBDatabaseError;
 class IDBKey;
 class IDBRequest;
 class IDBValue;
-class WebIDBCursor;
-class WebIDBDatabase;
+class WebIDBCursorImpl;
 struct IDBDatabaseMetadata;
 
 class WebIDBCallbacksImpl final : public WebIDBCallbacks {
@@ -53,35 +53,47 @@
 
   ~WebIDBCallbacksImpl() override;
 
+  void SetState(base::WeakPtr<WebIDBCursorImpl> cursor,
+                int64_t transaction_id) override;
+
   // Pointers transfer ownership.
-  void OnError(const IDBDatabaseError&) override;
-  void OnSuccess(const Vector<IDBNameAndVersion>&) override;
-  void OnSuccess(const Vector<String>&) override;
-  void OnSuccess(WebIDBCursor*,
-                 std::unique_ptr<IDBKey>,
-                 std::unique_ptr<IDBKey> primary_key,
-                 std::unique_ptr<IDBValue>) override;
-  void OnSuccess(WebIDBDatabase*, const IDBDatabaseMetadata&) override;
-  void OnSuccess(std::unique_ptr<IDBKey>) override;
-  void OnSuccess(std::unique_ptr<IDBValue>) override;
-  void OnSuccess(Vector<std::unique_ptr<IDBValue>>) override;
-  void OnSuccess(long long) override;
-  void OnSuccess() override;
-  void OnSuccess(std::unique_ptr<IDBKey>,
-                 std::unique_ptr<IDBKey> primary_key,
-                 std::unique_ptr<IDBValue>) override;
-  void OnBlocked(long long old_version) override;
-  void OnUpgradeNeeded(long long old_version,
-                       WebIDBDatabase*,
-                       const IDBDatabaseMetadata&,
-                       mojom::IDBDataLoss data_loss,
-                       String data_loss_message) override;
+  void Error(int32_t code, const String& message) override;
+  void SuccessNamesAndVersionsList(
+      Vector<mojom::blink::IDBNameAndVersionPtr>) override;
+  void SuccessStringList(const Vector<String>&) override;
+  void SuccessCursor(
+      mojom::blink::IDBCursorAssociatedPtrInfo cursor_info,
+      std::unique_ptr<IDBKey> key,
+      std::unique_ptr<IDBKey> primary_key,
+      base::Optional<std::unique_ptr<IDBValue>> optional_value) override;
+  void SuccessCursorPrefetch(Vector<std::unique_ptr<IDBKey>> keys,
+                             Vector<std::unique_ptr<IDBKey>> primary_keys,
+                             Vector<std::unique_ptr<IDBValue>> values) override;
+  void SuccessDatabase(mojom::blink::IDBDatabaseAssociatedPtrInfo database_info,
+                       const IDBDatabaseMetadata& metadata) override;
+  void SuccessKey(std::unique_ptr<IDBKey>) override;
+  void SuccessValue(mojom::blink::IDBReturnValuePtr) override;
+  void SuccessArray(Vector<mojom::blink::IDBReturnValuePtr>) override;
+  void SuccessInteger(int64_t) override;
+  void Success() override;
+  void SuccessCursorContinue(
+      std::unique_ptr<IDBKey>,
+      std::unique_ptr<IDBKey> primary_key,
+      base::Optional<std::unique_ptr<IDBValue>>) override;
+  void Blocked(int64_t old_version) override;
+  void UpgradeNeeded(mojom::blink::IDBDatabaseAssociatedPtrInfo,
+                     int64_t old_version,
+                     mojom::IDBDataLoss data_loss,
+                     const String& data_loss_message,
+                     const IDBDatabaseMetadata&) override;
   void Detach() override;
 
  private:
   explicit WebIDBCallbacksImpl(IDBRequest*);
 
   Persistent<IDBRequest> request_;
+  base::WeakPtr<WebIDBCursorImpl> cursor_;
+  int64_t transaction_id_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc
index 9d508a43..f5261ad 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl.cc
@@ -48,8 +48,9 @@
   }
   ResetPrefetchCache();
 
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      std::move(callbacks), transaction_id_, weak_factory_.GetWeakPtr());
+  callbacks->SetState(weak_factory_.GetWeakPtr(), transaction_id_);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(std::move(callbacks));
   cursor_->Advance(count, GetCallbacksProxy(std::move(callbacks_impl)));
 }
 
@@ -74,8 +75,9 @@
       // Request pre-fetch.
       ++pending_onsuccess_callbacks_;
 
-      auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-          std::move(callbacks), transaction_id_, weak_factory_.GetWeakPtr());
+      callbacks->SetState(weak_factory_.GetWeakPtr(), transaction_id_);
+      auto callbacks_impl =
+          std::make_unique<IndexedDBCallbacksImpl>(std::move(callbacks));
       cursor_->Prefetch(prefetch_amount_,
                         GetCallbacksProxy(std::move(callbacks_impl)));
 
@@ -91,8 +93,9 @@
     ResetPrefetchCache();
   }
 
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      std::move(callbacks), transaction_id_, weak_factory_.GetWeakPtr());
+  callbacks->SetState(weak_factory_.GetWeakPtr(), transaction_id_);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(std::move(callbacks));
   cursor_->CursorContinue(IDBKey::Clone(key), IDBKey::Clone(primary_key),
                           GetCallbacksProxy(std::move(callbacks_impl)));
 }
@@ -172,8 +175,8 @@
     ResetPrefetchCache();
   }
 
-  callbacks->OnSuccess(std::move(key), std::move(primary_key),
-                       std::move(value));
+  callbacks->SuccessCursorContinue(std::move(key), std::move(primary_key),
+                                   std::move(value));
 }
 
 void WebIDBCursorImpl::ResetPrefetchCache() {
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc
index eed3843..b0d9889 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_cursor_impl_unittest.cc
@@ -83,13 +83,14 @@
                         Vector<WebBlobInfo>* blobs = nullptr)
       : key_(key), blobs_(blobs) {}
 
-  void OnSuccess(std::unique_ptr<IDBKey> key,
-                 std::unique_ptr<IDBKey> primaryKey,
-                 std::unique_ptr<IDBValue> value) override {
+  void SuccessCursorContinue(
+      std::unique_ptr<IDBKey> key,
+      std::unique_ptr<IDBKey> primaryKey,
+      base::Optional<std::unique_ptr<IDBValue>> value) override {
     if (key_)
       *key_ = IDBKey::Clone(key);
-    if (blobs_)
-      *blobs_ = value->BlobInfo();
+    if (blobs_ && value.has_value())
+      *blobs_ = value.value()->BlobInfo();
   }
 
  private:
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc
index 242bf09..4d474c92 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.cc
@@ -4,13 +4,7 @@
 
 #include "third_party/blink/renderer/modules/indexeddb/web_idb_database_impl.h"
 
-#include <stddef.h>
-
-#include <string>
-#include <vector>
-
 #include "base/format_macros.h"
-#include "base/memory/ptr_util.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding.h"
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_database_error.h"
@@ -91,8 +85,9 @@
 
   mojom::blink::IDBKeyRangePtr key_range_ptr =
       mojom::blink::IDBKeyRange::From(key_range);
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
+  callbacks->SetState(nullptr, transaction_id);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(base::WrapUnique(callbacks));
   database_->Get(transaction_id, object_store_id, index_id,
                  std::move(key_range_ptr), key_only,
                  GetCallbacksProxy(std::move(callbacks_impl)));
@@ -109,8 +104,9 @@
 
   mojom::blink::IDBKeyRangePtr key_range_ptr =
       mojom::blink::IDBKeyRange::From(key_range);
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
+  callbacks->SetState(nullptr, transaction_id);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(base::WrapUnique(callbacks));
   database_->GetAll(transaction_id, object_store_id, index_id,
                     std::move(key_range_ptr), key_only, max_count,
                     GetCallbacksProxy(std::move(callbacks_impl)));
@@ -136,16 +132,17 @@
   size_t arg_size =
       value->DataSize() + primary_key->SizeEstimate() + index_keys_size;
   if (arg_size >= max_put_value_size_) {
-    callbacks->OnError(IDBDatabaseError(
+    callbacks->Error(
         blink::kWebIDBDatabaseExceptionUnknownError,
         String::Format("The serialized keys and/or value are too large"
                        " (size=%" PRIuS " bytes, max=%" PRIuS " bytes).",
-                       arg_size, max_put_value_size_)));
+                       arg_size, max_put_value_size_));
     return;
   }
 
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
+  callbacks->SetState(nullptr, transaction_id);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(base::WrapUnique(callbacks));
   database_->Put(transaction_id, object_store_id, std::move(value),
                  std::move(primary_key), put_mode, std::move(index_keys),
                  GetCallbacksProxy(std::move(callbacks_impl)));
@@ -178,8 +175,9 @@
 
   mojom::blink::IDBKeyRangePtr key_range_ptr =
       mojom::blink::IDBKeyRange::From(key_range);
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
+  callbacks->SetState(nullptr, transaction_id);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(base::WrapUnique(callbacks));
   database_->OpenCursor(transaction_id, object_store_id, index_id,
                         std::move(key_range_ptr), direction, key_only,
                         task_type,
@@ -195,8 +193,9 @@
 
   mojom::blink::IDBKeyRangePtr key_range_ptr =
       mojom::blink::IDBKeyRange::From(key_range);
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
+  callbacks->SetState(nullptr, transaction_id);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(base::WrapUnique(callbacks));
   database_->Count(transaction_id, object_store_id, index_id,
                    std::move(key_range_ptr),
                    GetCallbacksProxy(std::move(callbacks_impl)));
@@ -210,8 +209,9 @@
 
   mojom::blink::IDBKeyRangePtr key_range_ptr =
       mojom::blink::IDBKeyRange::From(IDBKeyRange::Create(primary_key));
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
+  callbacks->SetState(nullptr, transaction_id);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(base::WrapUnique(callbacks));
   database_->DeleteRange(transaction_id, object_store_id,
                          std::move(key_range_ptr),
                          GetCallbacksProxy(std::move(callbacks_impl)));
@@ -225,8 +225,9 @@
 
   mojom::blink::IDBKeyRangePtr key_range_ptr =
       mojom::blink::IDBKeyRange::From(key_range);
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
+  callbacks->SetState(nullptr, transaction_id);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(base::WrapUnique(callbacks));
   database_->DeleteRange(transaction_id, object_store_id,
                          std::move(key_range_ptr),
                          GetCallbacksProxy(std::move(callbacks_impl)));
@@ -237,8 +238,9 @@
                                WebIDBCallbacks* callbacks) {
   IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id, nullptr);
 
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
+  callbacks->SetState(nullptr, transaction_id);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(base::WrapUnique(callbacks));
   database_->Clear(transaction_id, object_store_id,
                    GetCallbacksProxy(std::move(callbacks_impl)));
 }
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl_unittest.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl_unittest.cc
index aaec742..2b9489a 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl_unittest.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_database_impl_unittest.cc
@@ -42,7 +42,7 @@
 
   ASSERT_GT(value_data->size() + key->SizeEstimate(), kMaxValueSizeForTesting);
   ThreadState::Current()->CollectAllGarbage();
-  EXPECT_CALL(callbacks, OnError(_)).Times(1);
+  EXPECT_CALL(callbacks, Error(_, _)).Times(1);
 
   WebIDBDatabaseImpl database_impl(nullptr);
   database_impl.max_put_value_size_ = kMaxValueSizeForTesting;
@@ -83,7 +83,7 @@
   DCHECK_GT(value_data->size() + key->SizeEstimate(), kMaxValueSizeForTesting);
 
   ThreadState::Current()->CollectAllGarbage();
-  EXPECT_CALL(callbacks, OnError(_)).Times(1);
+  EXPECT_CALL(callbacks, Error(_, _)).Times(1);
 
   WebIDBDatabaseImpl database_impl(nullptr);
   database_impl.max_put_value_size_ = kMaxValueSizeForTesting;
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_factory_impl.cc b/third_party/blink/renderer/modules/indexeddb/web_idb_factory_impl.cc
index d40c9531..801ca37 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_factory_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_factory_impl.cc
@@ -21,18 +21,18 @@
 void WebIDBFactoryImpl::GetDatabaseInfo(
     WebIDBCallbacks* callbacks,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), IndexedDBCallbacksImpl::kNoTransaction,
-      nullptr);
+  callbacks->SetState(nullptr, IndexedDBCallbacksImpl::kNoTransaction);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(base::WrapUnique(callbacks));
   factory_->GetDatabaseInfo(GetCallbacksProxy(std::move(callbacks_impl)));
 }
 
 void WebIDBFactoryImpl::GetDatabaseNames(
     WebIDBCallbacks* callbacks,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), IndexedDBCallbacksImpl::kNoTransaction,
-      nullptr);
+  callbacks->SetState(nullptr, IndexedDBCallbacksImpl::kNoTransaction);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(base::WrapUnique(callbacks));
   factory_->GetDatabaseNames(GetCallbacksProxy(std::move(callbacks_impl)));
 }
 
@@ -43,8 +43,9 @@
     WebIDBCallbacks* callbacks,
     WebIDBDatabaseCallbacks* database_callbacks,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), transaction_id, nullptr);
+  callbacks->SetState(nullptr, IndexedDBCallbacksImpl::kNoTransaction);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(base::WrapUnique(callbacks));
   auto database_callbacks_impl =
       std::make_unique<IndexedDBDatabaseCallbacksImpl>(
           base::WrapUnique(database_callbacks));
@@ -59,9 +60,9 @@
     WebIDBCallbacks* callbacks,
     bool force_close,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
-      base::WrapUnique(callbacks), IndexedDBCallbacksImpl::kNoTransaction,
-      nullptr);
+  callbacks->SetState(nullptr, IndexedDBCallbacksImpl::kNoTransaction);
+  auto callbacks_impl =
+      std::make_unique<IndexedDBCallbacksImpl>(base::WrapUnique(callbacks));
   DCHECK(!name.IsNull());
   factory_->DeleteDatabase(GetCallbacksProxy(std::move(callbacks_impl)), name,
                            force_close);
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context.cc b/third_party/blink/renderer/modules/webaudio/audio_context.cc
index 3a883114..5cf8a84 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context.cc
@@ -107,15 +107,6 @@
       audio_context->destination()->maxChannelCount());
   sample_rate_histogram.Sample(audio_context->sampleRate());
 
-  // Warn users about new autoplay policy when it does not apply to them.
-  if (RuntimeEnabledFeatures::AutoplayIgnoresWebAudioEnabled()) {
-    document.AddConsoleMessage(ConsoleMessage::Create(
-        kOtherMessageSource, kWarningMessageLevel,
-        "The Web Audio autoplay policy will be re-enabled in Chrome 71 ("
-        "December 2018). Please check that your website is compatible with it. "
-        "https://goo.gl/7K7WLu"));
-  }
-
   probe::didCreateAudioContext(&document);
 
   return audio_context;
diff --git a/third_party/blink/renderer/modules/webmidi/midi_dispatcher.h b/third_party/blink/renderer/modules/webmidi/midi_dispatcher.h
index 30be342..f40a3cc4 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_dispatcher.h
+++ b/third_party/blink/renderer/modules/webmidi/midi_dispatcher.h
@@ -18,7 +18,6 @@
                        public midi::mojom::blink::MidiSessionClient {
  public:
   static MIDIDispatcher& Instance();
-  MIDIDispatcher();
   ~MIDIDispatcher() override;
 
   void Trace(Visitor* visitor);
@@ -45,6 +44,10 @@
                     base::TimeTicks timestamp) override;
 
  private:
+  friend class ConstructTrait<MIDIDispatcher>;
+
+  MIDIDispatcher();
+
   midi::mojom::blink::MidiSessionProvider& GetMidiSessionProvider();
   midi::mojom::blink::MidiSession& GetMidiSession();
 
diff --git a/third_party/blink/renderer/platform/fonts/web_font_decoder.cc b/third_party/blink/renderer/platform/fonts/web_font_decoder.cc
index bc30044..dad31d2 100644
--- a/third_party/blink/renderer/platform/fonts/web_font_decoder.cc
+++ b/third_party/blink/renderer/platform/fonts/web_font_decoder.cc
@@ -37,6 +37,7 @@
 #include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
 #include "third_party/ots/include/ots-memory-stream.h"
 #include "third_party/skia/include/core/SkStream.h"
@@ -209,7 +210,7 @@
     return nullptr;
   }
 
-  const size_t decoded_length = output.Tell();
+  const size_t decoded_length = SafeCast<size_t>(output.Tell());
   RecordDecodeSpeedHistogram(data, buffer->size(), CurrentTime() - start,
                              decoded_length);
 
diff --git a/third_party/blink/renderer/platform/graphics/logging_canvas.cc b/third_party/blink/renderer/platform/graphics/logging_canvas.cc
index 126779d5..202d204 100644
--- a/third_party/blink/renderer/platform/graphics/logging_canvas.cc
+++ b/third_party/blink/renderer/platform/graphics/logging_canvas.cc
@@ -415,27 +415,8 @@
   };
 }
 
-String HintingName(SkFontHinting hinting) {
-  switch (hinting) {
-    case SkFontHinting::kNone:
-      return "None";
-    case SkFontHinting::kSlight:
-      return "Slight";
-    case SkFontHinting::kNormal:
-      return "Normal";
-    case SkFontHinting::kFull:
-      return "Full";
-    default:
-      NOTREACHED();
-      return "?";
-  };
-}
-
 std::unique_ptr<JSONObject> ObjectForSkPaint(const SkPaint& paint) {
   std::unique_ptr<JSONObject> paint_item = JSONObject::Create();
-  paint_item->SetDouble("textSize", paint.getTextSize());
-  paint_item->SetDouble("textScaleX", paint.getTextScaleX());
-  paint_item->SetDouble("textSkewX", paint.getTextSkewX());
   if (SkShader* shader = paint.getShader())
     paint_item->SetObject("shader", ObjectForSkShader(*shader));
   paint_item->SetString("color", StringForSkColor(paint.getColor()));
@@ -447,7 +428,6 @@
   paint_item->SetString("strokeCap", StrokeCapName(paint.getStrokeCap()));
   paint_item->SetString("strokeJoin", StrokeJoinName(paint.getStrokeJoin()));
   paint_item->SetString("styleName", StyleName(paint.getStyle()));
-  paint_item->SetString("hinting", HintingName(paint.getHinting()));
   if (paint.getBlendMode() != SkBlendMode::kSrcOver)
     paint_item->SetString("blendMode", SkBlendMode_Name(paint.getBlendMode()));
   if (paint.getImageFilter())
diff --git a/third_party/blink/renderer/platform/heap/garbage_collected.h b/third_party/blink/renderer/platform/heap/garbage_collected.h
index 6bb10aa..7326e43 100644
--- a/third_party/blink/renderer/platform/heap/garbage_collected.h
+++ b/third_party/blink/renderer/platform/heap/garbage_collected.h
@@ -47,17 +47,12 @@
   static const bool value = sizeof(CheckMarker<T>(nullptr)) == sizeof(YesType);
 };
 
-// TraceDescriptor is used to describe how to trace an object.
 struct TraceDescriptor {
   STACK_ALLOCATED();
 
  public:
-  // The adjusted base pointer of the object that should be traced.
   void* base_object_payload;
-  // A callback for tracing the object.
   TraceCallback callback;
-  // Indicator whether this object can be traced recursively or whether it
-  // requires iterative tracing.
   bool can_trace_eagerly;
 };
 
@@ -77,14 +72,11 @@
 //   USING_GARBAGE_COLLECTED_MIXIN(A);
 // };
 //
-// The classes involved and the helper macros allow for properly handling
-// definitions for Member<B> and friends. The mechanisms for handling Member<B>
-// involve dynamic dispatch. Note that for Member<A> all methods and pointers
-// are statically computed and no dynamic dispatch is involved.
-//
-// Note that garbage collections are allowed during mixin construction as
-// conservative scanning of objects does not rely on the Trace method but rather
-// scans the object field by field.
+// With the helper, as long as we are using Member<B>, TypeTrait<B> will
+// dispatch dynamically to retrieve the necessary tracing and header methods.
+// Note that this is only enabled for Member<B>. For Member<A> which we can
+// compute the necessary methods and pointers statically and this dynamic
+// dispatch is not used.
 class PLATFORM_EXPORT GarbageCollectedMixin {
  public:
   typedef int IsGarbageCollectedMixinMarker;
@@ -94,8 +86,7 @@
   // these objects can processed later on. This is necessary as
   // not-fully-constructed mixin objects potentially require being processed
   // as part emitting a write barrier for incremental marking. See
-  // |IncrementalMarkingTest::WriteBarrierDuringMixinConstruction| as an
-  // example.
+  // IncrementalMarkingTest::WriteBarrierDuringMixinConstruction as an example.
   //
   // The not-fully-constructed objects are handled as follows:
   //   1. Write barrier or marking of not fully constructed mixin gets called.
@@ -136,34 +127,95 @@
                                                                              \
  private:
 
-// The Oilpan GC plugin checks for proper usages of the
-// USING_GARBAGE_COLLECTED_MIXIN macro using a typedef marker.
-#define DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE) \
- public:                                                        \
-  typedef int HasUsingGarbageCollectedMixinMacro;               \
-                                                                \
+// A C++ object's vptr will be initialized to its leftmost base's vtable after
+// the constructors of all its subclasses have run, so if a subclass constructor
+// tries to access any of the vtbl entries of its leftmost base prematurely,
+// it'll find an as-yet incorrect vptr and fail. Which is exactly what a
+// garbage collector will try to do if it tries to access the leftmost base
+// while one of the subclass constructors of a GC mixin object triggers a GC.
+// It is consequently not safe to allow any GCs while these objects are under
+// (sub constructor) construction.
+//
+// To prevent GCs in that restricted window of a mixin object's construction:
+//
+//  - The initial allocation of the mixin object will enter a no GC scope.
+//    This is done by overriding 'operator new' for mixin instances.
+//  - When the constructor for the mixin is invoked, after all the
+//    derived constructors have run, it will invoke the constructor
+//    for a field whose only purpose is to leave the GC scope.
+//    GarbageCollectedMixinConstructorMarker's constructor takes care of
+//    this and the field is declared by way of USING_GARBAGE_COLLECTED_MIXIN().
+
+#define DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE)           \
+ public:                                                                  \
+  GarbageCollectedMixinConstructorMarker<ThreadingTrait<TYPE>::kAffinity> \
+      mixin_constructor_marker_;                                          \
+                                                                          \
  private:
 
-// The USING_GARBAGE_COLLECTED_MIXIN macro defines all methods and markers
-// needed for handling mixins.
+// Mixins that wrap/nest others requires extra handling:
+//
+//  class A : public GarbageCollected<A>, public GarbageCollectedMixin {
+//  USING_GARBAGE_COLLECTED_MIXIN(A);
+//  ...
+//  }'
+//  public B final : public A, public SomeOtherMixinInterface {
+//  USING_GARBAGE_COLLECTED_MIXIN(B);
+//  ...
+//  };
+//
+// The "operator new" for B will enter the forbidden GC scope, but
+// upon construction, two GarbageCollectedMixinConstructorMarker constructors
+// will run -- one for A (first) and another for B (secondly). Only
+// the second one should leave the forbidden GC scope. This is realized by
+// recording the address of B's GarbageCollectedMixinConstructorMarker
+// when the "operator new" for B runs, and leaving the forbidden GC scope
+// when the constructor of the recorded GarbageCollectedMixinConstructorMarker
+// runs.
 #define USING_GARBAGE_COLLECTED_MIXIN(TYPE)    \
   IS_GARBAGE_COLLECTED_TYPE();                 \
   DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(TYPE) \
   DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE)
 
+// An empty class with a constructor that's arranged invoked when all derived
+// constructors of a mixin instance have completed and it is safe to allow GCs
+// again. See AllocateObjectTrait<> comment for more.
+//
+// USING_GARBAGE_COLLECTED_MIXIN() declares a
+// GarbageCollectedMixinConstructorMarker<> private field. By following Blink
+// convention of using the macro at the top of a class declaration, its
+// constructor will run first.
+class GarbageCollectedMixinConstructorMarkerBase {};
+template <ThreadAffinity affinity>
+class GarbageCollectedMixinConstructorMarker
+    : public GarbageCollectedMixinConstructorMarkerBase {
+ public:
+  GarbageCollectedMixinConstructorMarker() {
+    // FIXME: if prompt conservative GCs are needed, forced GCs that
+    // were denied while within this scope, could now be performed.
+    // For now, assume the next out-of-line allocation request will
+    // happen soon enough and take care of it. Mixin objects aren't
+    // overly common.
+    ThreadState* state = ThreadStateFor<affinity>::GetState();
+    state->LeaveGCForbiddenScopeIfNeeded(this);
+  }
+};
+
 // Merge two or more Mixins into one:
 //
 //  class A : public GarbageCollectedMixin {};
 //  class B : public GarbageCollectedMixin {};
 //  class C : public A, public B {
 //    // C::GetTraceDescriptor is now ambiguous because there are two
-//    // candidates: A::GetTraceDescriptor and B::GetTraceDescriptor.  Ditto for
-//    // other functions.
+//    candidates:
+//    // A::GetTraceDescriptor and B::GetTraceDescriptor.  Ditto for other
+//    functions.
 //
 //    MERGE_GARBAGE_COLLECTED_MIXINS();
 //    // The macro defines C::GetTraceDescriptor, etc. so that they are no
-//    // longer ambiguous. USING_GARBAGE_COLLECTED_MIXIN(TYPE) overrides them
-//    // later and provides the implementations.
+//    longer
+//    // ambiguous. USING_GARBAGE_COLLECTED_MIXIN(TYPE) overrides them later
+//    // and provides the implementations.
 //  };
 #define MERGE_GARBAGE_COLLECTED_MIXINS()                                 \
  public:                                                                 \
diff --git a/third_party/blink/renderer/platform/heap/heap.cc b/third_party/blink/renderer/platform/heap/heap.cc
index cf507ab..5dddc0e 100644
--- a/third_party/blink/renderer/platform/heap/heap.cc
+++ b/third_party/blink/renderer/platform/heap/heap.cc
@@ -165,31 +165,10 @@
 
 void ThreadHeap::DecommitCallbackStacks() {
   marking_worklist_.reset(nullptr);
+  not_fully_constructed_worklist_.reset(nullptr);
   previously_not_fully_constructed_worklist_.reset(nullptr);
   weak_callback_worklist_.reset(nullptr);
   ephemeron_callbacks_.clear();
-
-  // The fixed point iteration may have found not-fully-constructed objects.
-  // Such objects should have already been found through the stack scan though
-  // and should thus already be marked.
-  if (!not_fully_constructed_worklist_->IsGlobalEmpty()) {
-#if DCHECK_IS_ON()
-    NotFullyConstructedItem item;
-    while (not_fully_constructed_worklist_->Pop(WorklistTaskId::MainThread,
-                                                &item)) {
-      BasePage* const page = PageFromObject(item);
-      HeapObjectHeader* const header =
-          page->IsLargeObjectPage()
-              ? static_cast<LargeObjectPage*>(page)->ObjectHeader()
-              : static_cast<NormalPage*>(page)->FindHeaderFromAddress(
-                    reinterpret_cast<Address>(const_cast<void*>(item)));
-      DCHECK(header->IsMarked());
-    }
-#else
-    not_fully_constructed_worklist_->Clear();
-#endif
-  }
-  not_fully_constructed_worklist_.reset(nullptr);
 }
 
 HeapCompact* ThreadHeap::Compaction() {
@@ -281,6 +260,8 @@
 }  // namespace
 
 bool ThreadHeap::AdvanceMarking(MarkingVisitor* visitor, TimeTicks deadline) {
+  // const size_t kDeadlineCheckInterval = 2500;
+  // size_t processed_callback_count = 0;
   bool finished;
   // Ephemeron fixed point loop.
   do {
@@ -290,13 +271,11 @@
       ThreadHeapStatsCollector::Scope stats_scope(
           stats_collector(), ThreadHeapStatsCollector::kMarkProcessWorklist);
 
-      finished = DrainWorklistWithDeadline(
-          deadline, marking_worklist_.get(),
-          [visitor](const MarkingItem& item) {
-            DCHECK(!HeapObjectHeader::FromPayload(item.object)
-                        ->IsInConstruction());
-            item.callback(visitor, item.object);
-          });
+      finished =
+          DrainWorklistWithDeadline(deadline, marking_worklist_.get(),
+                                    [visitor](const MarkingItem& item) {
+                                      item.callback(visitor, item.object);
+                                    });
       if (!finished)
         return false;
 
@@ -621,12 +600,6 @@
   if (header->IsMarked())
     return;
 
-  if (header->IsInConstruction()) {
-    not_fully_constructed_worklist_->Push(WorklistTaskId::MainThread,
-                                          header->Payload());
-    return;
-  }
-
   // Mark and push trace callback.
   header->Mark();
   marking_worklist_->Push(
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index 0f81c2b..6919e657 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -121,8 +121,6 @@
 template <typename T>
 class UntracedMember;
 
-namespace internal {
-
 template <typename T, bool = NeedsAdjustPointer<T>::value>
 class ObjectAliveTrait;
 
@@ -145,17 +143,10 @@
   NO_SANITIZE_ADDRESS
   static bool IsHeapObjectAlive(const T* object) {
     static_assert(sizeof(T), "T must be fully defined");
-    const HeapObjectHeader* header = object->GetHeapObjectHeader();
-    if (header == BlinkGC::kNotFullyConstructedObject) {
-      // Objects under construction are always alive.
-      return true;
-    }
-    return header->IsMarked();
+    return object->GetHeapObjectHeader()->IsMarked();
   }
 };
 
-}  // namespace internal
-
 class PLATFORM_EXPORT ThreadHeap {
  public:
   explicit ThreadHeap(ThreadState*);
@@ -182,7 +173,7 @@
       return true;
     DCHECK(&ThreadState::Current()->Heap() ==
            &PageFromObject(object)->Arena()->GetThreadState()->Heap());
-    return internal::ObjectAliveTrait<T>::IsHeapObjectAlive(object);
+    return ObjectAliveTrait<T>::IsHeapObjectAlive(object);
   }
   template <typename T>
   static inline bool IsHeapObjectAlive(const Member<T>& member) {
@@ -555,18 +546,53 @@
   DISALLOW_COPY_AND_ASSIGN(GarbageCollected);
 };
 
+template <typename T, bool is_mixin = IsGarbageCollectedMixin<T>::value>
+class ConstructTrait {
+ public:
+};
+
+template <typename T>
+class ConstructTrait<T, false> {
+ public:
+  template <typename... Args>
+  static T* Construct(Args&&... args) {
+    void* memory =
+        T::AllocateObject(sizeof(T), IsEagerlyFinalizedType<T>::value);
+    HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory);
+    header->MarkIsInConstruction();
+    // Placement new as regular operator new() is deleted.
+    T* object = ::new (memory) T(std::forward<Args>(args)...);
+    header->UnmarkIsInConstruction();
+    return object;
+  }
+};
+
+template <typename T>
+class ConstructTrait<T, true> {
+ public:
+  template <typename... Args>
+  NO_SANITIZE_UNRELATED_CAST static T* Construct(Args&&... args) {
+    void* memory =
+        T::AllocateObject(sizeof(T), IsEagerlyFinalizedType<T>::value);
+    HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory);
+    header->MarkIsInConstruction();
+    ThreadState* state =
+        ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState();
+    state->EnterGCForbiddenScopeIfNeeded(
+        &(reinterpret_cast<T*>(memory)->mixin_constructor_marker_));
+    // Placement new as regular operator new() is deleted.
+    T* object = ::new (memory) T(std::forward<Args>(args)...);
+    header->UnmarkIsInConstruction();
+    return object;
+  }
+};
+
 // Constructs an instance of T, which is a garbage collected type.
 template <typename T, typename... Args>
 T* MakeGarbageCollected(Args&&... args) {
   static_assert(WTF::IsGarbageCollectedType<T>::value,
                 "T needs to be a garbage collected object");
-  void* memory = T::AllocateObject(sizeof(T), IsEagerlyFinalizedType<T>::value);
-  HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory);
-  header->MarkIsInConstruction();
-  // Placement new as regular operator new() is deleted.
-  T* object = ::new (memory) T(std::forward<Args>(args)...);
-  header->UnmarkIsInConstruction();
-  return object;
+  return ConstructTrait<T>::Construct(std::forward<Args>(args)...);
 }
 
 // Assigning class types to their arenas.
@@ -727,7 +753,7 @@
       // preserved to avoid reviving objects in containers.
       return;
     }
-    if (!ThreadHeap::IsHeapObjectAlive(contents))
+    if (!ObjectAliveTrait<T>::IsHeapObjectAlive(contents))
       *cell = nullptr;
   }
 }
diff --git a/third_party/blink/renderer/platform/heap/heap_test.cc b/third_party/blink/renderer/platform/heap/heap_test.cc
index c042269..4b1c81e 100644
--- a/third_party/blink/renderer/platform/heap/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_test.cc
@@ -5367,6 +5367,59 @@
   return true;
 }
 
+static bool CheckGCForbidden() {
+  DCHECK(ThreadState::Current()->IsGCForbidden());
+  return true;
+}
+
+class MixinClass : public GarbageCollectedMixin {
+ public:
+  MixinClass() : dummy_(CheckGCForbidden()) {}
+
+ private:
+  bool dummy_;
+};
+
+class ClassWithGarbageCollectingMixinConstructor
+    : public GarbageCollected<ClassWithGarbageCollectingMixinConstructor>,
+      public MixinClass {
+  USING_GARBAGE_COLLECTED_MIXIN(ClassWithGarbageCollectingMixinConstructor);
+
+ public:
+  static int trace_called_;
+
+  ClassWithGarbageCollectingMixinConstructor()
+      : trace_counter_(TraceCounter::Create()),
+        wrapper_(IntWrapper::Create(32)) {}
+
+  void Trace(blink::Visitor* visitor) override {
+    trace_called_++;
+    visitor->Trace(trace_counter_);
+    visitor->Trace(wrapper_);
+  }
+
+  void Verify() {
+    EXPECT_EQ(32, wrapper_->Value());
+    EXPECT_EQ(0, trace_counter_->TraceCount());
+    EXPECT_EQ(0, trace_called_);
+  }
+
+ private:
+  Member<TraceCounter> trace_counter_;
+  Member<IntWrapper> wrapper_;
+};
+
+int ClassWithGarbageCollectingMixinConstructor::trace_called_ = 0;
+
+// Regression test for out of bounds call through vtable.
+// Passes if it doesn't crash.
+TEST(HeapTest, GarbageCollectionDuringMixinConstruction) {
+  ClassWithGarbageCollectingMixinConstructor::trace_called_ = 0;
+  ClassWithGarbageCollectingMixinConstructor* a =
+      MakeGarbageCollected<ClassWithGarbageCollectingMixinConstructor>();
+  a->Verify();
+}
+
 template <typename T>
 class TraceIfNeededTester
     : public GarbageCollectedFinalized<TraceIfNeededTester<T>> {
@@ -5875,7 +5928,11 @@
   USING_GARBAGE_COLLECTED_MIXIN(TestMixinAllocationA);
 
  public:
-  TestMixinAllocationA() = default;
+  TestMixinAllocationA() {
+    // Completely wrong in general, but test only
+    // runs this constructor while constructing another mixin.
+    DCHECK(ThreadState::Current()->IsGCForbidden());
+  }
 
   void Trace(blink::Visitor* visitor) override {}
 };
@@ -5885,8 +5942,14 @@
 
  public:
   TestMixinAllocationB()
-      // Construct object during a mixin construction.
-      : a_(MakeGarbageCollected<TestMixinAllocationA>()) {}
+      : a_(MakeGarbageCollected<TestMixinAllocationA>())  // Construct object
+                                                          // during a mixin
+                                                          // construction.
+  {
+    // Completely wrong in general, but test only
+    // runs this constructor while constructing another mixin.
+    DCHECK(ThreadState::Current()->IsGCForbidden());
+  }
 
   void Trace(blink::Visitor* visitor) override {
     visitor->Trace(a_);
@@ -5931,6 +5994,47 @@
   }
 };
 
+class TestMixinAllocatingObject final
+    : public TestMixinAllocationB,
+      public ObjectWithLargeAmountsOfAllocationInConstructor {
+  USING_GARBAGE_COLLECTED_MIXIN(TestMixinAllocatingObject);
+
+ public:
+  static TestMixinAllocatingObject* Create(ClassWithMember* member) {
+    return MakeGarbageCollected<TestMixinAllocatingObject>(member);
+  }
+
+  TestMixinAllocatingObject(ClassWithMember* member)
+      : ObjectWithLargeAmountsOfAllocationInConstructor(600, member),
+        trace_counter_(TraceCounter::Create()) {
+    DCHECK(!ThreadState::Current()->IsGCForbidden());
+    ConservativelyCollectGarbage();
+    EXPECT_GT(member->TraceCount(), 0);
+    EXPECT_GT(TraceCount(), 0);
+  }
+
+  void Trace(blink::Visitor* visitor) override {
+    visitor->Trace(trace_counter_);
+    TestMixinAllocationB::Trace(visitor);
+  }
+
+  int TraceCount() const { return trace_counter_->TraceCount(); }
+
+ private:
+  Member<TraceCounter> trace_counter_;
+};
+
+TEST(HeapTest, MixinConstructionNoGC) {
+  ClearOutOldGarbage();
+  Persistent<ClassWithMember> object = ClassWithMember::Create();
+  EXPECT_EQ(0, object->TraceCount());
+  TestMixinAllocatingObject* mixin =
+      TestMixinAllocatingObject::Create(object.Get());
+  EXPECT_TRUE(mixin);
+  EXPECT_GT(object->TraceCount(), 0);
+  EXPECT_GT(mixin->TraceCount(), 0);
+}
+
 class WeakPersistentHolder final {
  public:
   explicit WeakPersistentHolder(IntWrapper* object) : object_(object) {}
@@ -6316,25 +6420,85 @@
   vector.ShrinkToFit();
 }
 
+namespace {
+
+class MixinCheckingConstructionScope : public GarbageCollectedMixin {
+ public:
+  MixinCheckingConstructionScope() {
+    // Oilpan treats mixin construction as forbidden scopes for garbage
+    // collection.
+    CHECK(ThreadState::Current()->IsMixinInConstruction());
+  }
+};
+
+class UsingMixinCheckingConstructionScope
+    : public GarbageCollected<UsingMixinCheckingConstructionScope>,
+      public MixinCheckingConstructionScope {
+  USING_GARBAGE_COLLECTED_MIXIN(UsingMixinCheckingConstructionScope);
+};
+
+}  // namespace
+
+TEST(HeapTest, NoConservativeGCDuringMixinConstruction) {
+  // Regression test: https://crbug.com/904546
+  MakeGarbageCollected<UsingMixinCheckingConstructionScope>();
+}
+
+namespace {
+
+class ObjectCheckingForInConstruction
+    : public GarbageCollected<ObjectCheckingForInConstruction> {
+ public:
+  ObjectCheckingForInConstruction() {
+    CHECK(HeapObjectHeader::FromPayload(this)->IsInConstruction());
+  }
+
+  virtual void Trace(Visitor* v) { v->Trace(foo_); }
+
+ private:
+  Member<IntWrapper> foo_;
+};
+
+class MixinCheckingInConstruction : public GarbageCollectedMixin {
+ public:
+  MixinCheckingInConstruction() {
+    BasePage* const page = PageFromObject(reinterpret_cast<Address>(this));
+    HeapObjectHeader* const header =
+        static_cast<NormalPage*>(page)->FindHeaderFromAddress(
+            reinterpret_cast<Address>(
+                const_cast<MixinCheckingInConstruction*>(this)));
+    CHECK(header->IsInConstruction());
+  }
+
+  void Trace(Visitor* v) override { v->Trace(bar_); }
+
+ private:
+  Member<IntWrapper> bar_;
+};
+
+class MixinAppCheckingInConstruction
+    : public GarbageCollected<MixinAppCheckingInConstruction>,
+      public MixinCheckingInConstruction {
+  USING_GARBAGE_COLLECTED_MIXIN(MixinAppCheckingInConstruction)
+ public:
+  MixinAppCheckingInConstruction() {
+    CHECK(HeapObjectHeader::FromPayload(this)->IsInConstruction());
+  }
+
+  void Trace(Visitor* v) override { v->Trace(foo_); }
+
+ private:
+  Member<IntWrapper> foo_;
+};
+
+}  // namespace
+
 TEST(HeapTest, GarbageCollectedInConstruction) {
-  using O = ObjectWithCallbackBeforeInitializer<IntWrapper>;
-  MakeGarbageCollected<O>(base::BindOnce([](O* thiz) {
-    CHECK(HeapObjectHeader::FromPayload(thiz)->IsInConstruction());
-  }));
+  MakeGarbageCollected<ObjectCheckingForInConstruction>();
 }
 
 TEST(HeapTest, GarbageCollectedMixinInConstruction) {
-  using O = ObjectWithMixinWithCallbackBeforeInitializer<IntWrapper>;
-  MakeGarbageCollected<O>(base::BindOnce([](O::Mixin* thiz) {
-    CHECK(HeapObjectHeader::FromPayload(static_cast<O*>(thiz))
-              ->IsInConstruction());
-  }));
-}
-
-TEST(HeapTest, GarbageCollectedMixinIsAliveDuringConstruction) {
-  using O = ObjectWithMixinWithCallbackBeforeInitializer<IntWrapper>;
-  MakeGarbageCollected<O>(base::BindOnce(
-      [](O::Mixin* thiz) { CHECK(ThreadHeap::IsHeapObjectAlive(thiz)); }));
+  MakeGarbageCollected<MixinAppCheckingInConstruction>();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/heap_test_utilities.h b/third_party/blink/renderer/platform/heap/heap_test_utilities.h
index 0e05f426..97778731 100644
--- a/third_party/blink/renderer/platform/heap/heap_test_utilities.h
+++ b/third_party/blink/renderer/platform/heap/heap_test_utilities.h
@@ -7,12 +7,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TEST_UTILITIES_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TEST_UTILITIES_H_
 
-#include "base/callback.h"
 #include "third_party/blink/renderer/platform/heap/blink_gc.h"
-#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/heap/heap.h"
-#include "third_party/blink/renderer/platform/heap/trace_traits.h"
-#include "third_party/blink/renderer/platform/heap/visitor.h"
 
 namespace blink {
 
@@ -21,89 +16,6 @@
     BlinkGC::SweepingType sweeping_type = BlinkGC::kEagerSweeping);
 void ClearOutOldGarbage();
 
-template <typename T>
-class ObjectWithCallbackBeforeInitializer : public GarbageCollected<T> {
- public:
-  ObjectWithCallbackBeforeInitializer(
-      base::OnceCallback<void(ObjectWithCallbackBeforeInitializer<T>*)>&& cb,
-      T* value)
-      : bool_(ExecuteCallbackReturnTrue(this, std::move(cb))), value_(value) {}
-
-  ObjectWithCallbackBeforeInitializer(
-      base::OnceCallback<void(ObjectWithCallbackBeforeInitializer<T>*)>&& cb)
-      : bool_(ExecuteCallbackReturnTrue(this, std::move(cb))) {}
-
-  virtual void Trace(Visitor* visitor) { visitor->Trace(value_); }
-
-  T* value() const { return value_.Get(); }
-
- private:
-  static bool ExecuteCallbackReturnTrue(
-      ObjectWithCallbackBeforeInitializer* thiz,
-      base::OnceCallback<void(ObjectWithCallbackBeforeInitializer<T>*)>&& cb) {
-    std::move(cb).Run(thiz);
-    return true;
-  }
-
-  bool bool_;
-  Member<T> value_;
-};
-
-template <typename T>
-class MixinWithCallbackBeforeInitializer : public GarbageCollectedMixin {
- public:
-  MixinWithCallbackBeforeInitializer(
-      base::OnceCallback<void(MixinWithCallbackBeforeInitializer<T>*)>&& cb,
-      T* value)
-      : bool_(ExecuteCallbackReturnTrue(this, std::move(cb))), value_(value) {}
-
-  MixinWithCallbackBeforeInitializer(
-      base::OnceCallback<void(MixinWithCallbackBeforeInitializer<T>*)>&& cb)
-      : bool_(ExecuteCallbackReturnTrue(this, std::move(cb))) {}
-
-  void Trace(Visitor* visitor) override { visitor->Trace(value_); }
-
-  T* value() const { return value_.Get(); }
-
- private:
-  static bool ExecuteCallbackReturnTrue(
-      MixinWithCallbackBeforeInitializer* thiz,
-      base::OnceCallback<void(MixinWithCallbackBeforeInitializer<T>*)>&& cb) {
-    std::move(cb).Run(thiz);
-    return true;
-  }
-
-  bool bool_;
-  Member<T> value_;
-};
-
-class BoolMixin {
- protected:
-  bool bool_ = false;
-};
-
-template <typename T>
-class ObjectWithMixinWithCallbackBeforeInitializer
-    : public GarbageCollected<ObjectWithMixinWithCallbackBeforeInitializer<T>>,
-      public BoolMixin,
-      public MixinWithCallbackBeforeInitializer<T> {
-  USING_GARBAGE_COLLECTED_MIXIN(ObjectWithMixinWithCallbackBeforeInitializer);
-
- public:
-  using Mixin = MixinWithCallbackBeforeInitializer<T>;
-
-  ObjectWithMixinWithCallbackBeforeInitializer(
-      base::OnceCallback<void(Mixin*)>&& cb,
-      T* value)
-      : Mixin(std::move(cb), value) {}
-
-  ObjectWithMixinWithCallbackBeforeInitializer(
-      base::OnceCallback<void(Mixin*)>&& cb)
-      : Mixin(std::move(cb)) {}
-
-  void Trace(Visitor* visitor) override { Mixin::Trace(visitor); }
-};
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TEST_UTILITIES_H_
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
index b359297..69587d14 100644
--- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -1563,30 +1563,28 @@
     thread_state_->IncrementalMarkingStart(BlinkGC::GCReason::kTesting);
   }
 
-  bool SingleStep(BlinkGC::StackState stack_state =
-                      BlinkGC::StackState::kNoHeapPointersOnStack) {
+  bool SingleStep() {
     CHECK(thread_state_->IsIncrementalMarking());
     if (thread_state_->GetGCState() ==
         ThreadState::kIncrementalMarkingStepScheduled) {
-      thread_state_->IncrementalMarkingStep(stack_state);
+      thread_state_->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
       return true;
     }
     return false;
   }
 
-  void FinishSteps(BlinkGC::StackState stack_state =
-                       BlinkGC::StackState::kNoHeapPointersOnStack) {
+  void FinishSteps() {
     CHECK(thread_state_->IsIncrementalMarking());
-    while (SingleStep(stack_state)) {
+    while (SingleStep()) {
     }
   }
 
   void FinishGC() {
     CHECK(thread_state_->IsIncrementalMarking());
-    FinishSteps(BlinkGC::StackState::kNoHeapPointersOnStack);
+    FinishSteps();
     CHECK_EQ(ThreadState::kIncrementalMarkingFinalizeScheduled,
              thread_state_->GetGCState());
-    thread_state_->RunScheduledGC(BlinkGC::StackState::kNoHeapPointersOnStack);
+    thread_state_->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
     CHECK(!thread_state_->IsIncrementalMarking());
     thread_state_->CompleteSweep();
   }
@@ -1803,80 +1801,6 @@
   ConservativelyCollectGarbage();
 }
 
-namespace {
-
-template <typename T>
-class ObjectHolder : public GarbageCollected<ObjectHolder<T>> {
- public:
-  ObjectHolder() = default;
-
-  virtual void Trace(Visitor* visitor) { visitor->Trace(holder_); }
-
-  void set_value(T* value) { holder_ = value; }
-  T* value() const { return holder_.Get(); }
-
- private:
-  Member<T> holder_;
-};
-
-}  // namespace
-
-TEST(IncrementalMarkingTest, StepDuringObjectConstruction) {
-  // Test ensures that objects in construction are delayed for processing to
-  // allow omitting write barriers on initializing stores.
-
-  using O = ObjectWithCallbackBeforeInitializer<Object>;
-  using Holder = ObjectHolder<O>;
-  Persistent<Holder> holder(MakeGarbageCollected<Holder>());
-  IncrementalMarkingTestDriver driver(ThreadState::Current());
-  driver.Start();
-  MakeGarbageCollected<O>(
-      base::BindOnce(
-          [](IncrementalMarkingTestDriver* driver, Holder* holder, O* thiz) {
-            // Publish not-fully-constructed object |thiz| by triggering write
-            // barrier for the object.
-            holder->set_value(thiz);
-            CHECK(HeapObjectHeader::FromPayload(holder->value())->IsValid());
-            // Finish call incremental steps.
-            driver->FinishSteps(BlinkGC::StackState::kHeapPointersOnStack);
-          },
-          &driver, holder.Get()),
-      MakeGarbageCollected<Object>());
-  driver.FinishGC();
-  CHECK(HeapObjectHeader::FromPayload(holder->value())->IsValid());
-  CHECK(HeapObjectHeader::FromPayload(holder->value()->value())->IsValid());
-  PreciselyCollectGarbage();
-}
-
-TEST(IncrementalMarkingTest, StepDuringMixinObjectConstruction) {
-  // Test ensures that mixin objects in construction are delayed for processing
-  // to allow omitting write barriers on initializing stores.
-
-  using Parent = ObjectWithMixinWithCallbackBeforeInitializer<Object>;
-  using Mixin = MixinWithCallbackBeforeInitializer<Object>;
-  using Holder = ObjectHolder<Mixin>;
-  Persistent<Holder> holder(MakeGarbageCollected<Holder>());
-  IncrementalMarkingTestDriver driver(ThreadState::Current());
-  driver.Start();
-  MakeGarbageCollected<Parent>(
-      base::BindOnce(
-          [](IncrementalMarkingTestDriver* driver, Holder* holder,
-             Mixin* thiz) {
-            // Publish not-fully-constructed object
-            // |thiz| by triggering write barrier for
-            // the object.
-            holder->set_value(thiz);
-            // Finish call incremental steps.
-            driver->FinishSteps(BlinkGC::StackState::kHeapPointersOnStack);
-          },
-          &driver, holder.Get()),
-      MakeGarbageCollected<Object>());
-  driver.FinishGC();
-  CHECK(holder->value()->GetHeapObjectHeader()->IsValid());
-  CHECK(HeapObjectHeader::FromPayload(holder->value()->value())->IsValid());
-  PreciselyCollectGarbage();
-}
-
 }  // namespace incremental_marking_test
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.cc b/third_party/blink/renderer/platform/heap/marking_visitor.cc
index b95eb73..6efd4e027 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.cc
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -64,10 +64,11 @@
   if (!header || header->IsMarked())
     return;
 
-  // Simple case for fully constructed objects.
+  // Simple case for fully constructed objects or those that have no vtable
+  // which make dispatching to member fields trivial.
   const GCInfo* gc_info =
       GCInfoTable::Get().GCInfoFromIndex(header->GcInfoIndex());
-  if (!header->IsInConstruction()) {
+  if (!gc_info->HasVTable() || !header->IsInConstruction()) {
     MarkHeader(header, gc_info->trace_);
     return;
   }
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.h b/third_party/blink/renderer/platform/heap/marking_visitor.h
index a53f0bf..cbb41e8 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.h
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.h
@@ -51,14 +51,11 @@
 
   // Marking implementation.
 
-  // Conservatively marks an object if pointed to by Address. The object may
-  // be in construction as the scan is conservative without relying on a
-  // Trace method.
+  // Conservatively marks an object if pointed to by Address.
   void ConservativelyMarkAddress(BasePage*, Address);
 
   // Marks an object dynamically using any address within its body and adds a
-  // tracing callback for processing of the object. The object is not allowed
-  // to be in construction.
+  // tracing callback for processing of the object.
   void DynamicallyMarkAddress(Address);
 
   // Marks an object and adds a tracing callback for processing of the object.
@@ -100,11 +97,8 @@
       // that lead to many recursions.
       DCHECK(Heap().GetStackFrameDepth().IsAcceptableStackUse());
       if (LIKELY(Heap().GetStackFrameDepth().IsSafeToRecurse())) {
-        HeapObjectHeader* header =
-            HeapObjectHeader::FromPayload(desc.base_object_payload);
-        if (header->IsInConstruction()) {
-          not_fully_constructed_worklist_.Push(desc.base_object_payload);
-        } else if (MarkHeaderNoTracing(header)) {
+        if (MarkHeaderNoTracing(
+                HeapObjectHeader::FromPayload(desc.base_object_payload))) {
           desc.callback(this, desc.base_object_payload);
         }
         return;
@@ -205,9 +199,7 @@
   DCHECK(header);
   DCHECK(callback);
 
-  if (header->IsInConstruction()) {
-    not_fully_constructed_worklist_.Push(header->Payload());
-  } else if (MarkHeaderNoTracing(header)) {
+  if (MarkHeaderNoTracing(header)) {
     marking_worklist_.Push(
         {reinterpret_cast<void*>(header->Payload()), callback});
   }
diff --git a/third_party/blink/renderer/platform/heap/persistent.h b/third_party/blink/renderer/platform/heap/persistent.h
index 69db982b..35ed1d2 100644
--- a/third_party/blink/renderer/platform/heap/persistent.h
+++ b/third_party/blink/renderer/platform/heap/persistent.h
@@ -269,7 +269,7 @@
                        weaknessConfiguration, crossThreadnessConfiguration>;
     Base* persistent = reinterpret_cast<Base*>(persistent_pointer);
     T* object = persistent->Get();
-    if (object && !ThreadHeap::IsHeapObjectAlive(object))
+    if (object && !ObjectAliveTrait<T>::IsHeapObjectAlive(object))
       ClearWeakPersistent(persistent);
   }
 
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index a35433a..1e7ffa8 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -177,6 +177,7 @@
       mixins_being_constructed_count_(0),
       object_resurrection_forbidden_(false),
       in_atomic_pause_(false),
+      gc_mixin_marker_(nullptr),
       gc_state_(kNoGCScheduled),
       gc_phase_(GCPhase::kNone),
       reason_for_scheduled_gc_(BlinkGC::GCReason::kMaxValue),
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index 00282d6..7e7a50e6 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -62,6 +62,7 @@
 class IncrementalMarkingTestDriver;
 }  // namespace incremental_marking_test
 
+class GarbageCollectedMixinConstructorMarkerBase;
 class MarkingVisitor;
 class PersistentNode;
 class PersistentRegion;
@@ -479,6 +480,29 @@
     perform_cleanup_ = perform_cleanup;
   }
 
+  // By entering a gc-forbidden scope, conservative GCs will not
+  // be allowed while handling an out-of-line allocation request.
+  // Intended used when constructing subclasses of GC mixins, where
+  // the object being constructed cannot be safely traced & marked
+  // fully should a GC be allowed while its subclasses are being
+  // constructed.
+  void EnterGCForbiddenScopeIfNeeded(
+      GarbageCollectedMixinConstructorMarkerBase* gc_mixin_marker) {
+    DCHECK(CheckThread());
+    if (!gc_mixin_marker_) {
+      EnterMixinConstructionScope();
+      gc_mixin_marker_ = gc_mixin_marker;
+    }
+  }
+  void LeaveGCForbiddenScopeIfNeeded(
+      GarbageCollectedMixinConstructorMarkerBase* gc_mixin_marker) {
+    DCHECK(CheckThread());
+    if (gc_mixin_marker_ == gc_mixin_marker) {
+      LeaveMixinConstructionScope();
+      gc_mixin_marker_ = nullptr;
+    }
+  }
+
   void FreePersistentNode(PersistentRegion*, PersistentNode*);
 
   using PersistentClearCallback = void (*)(void*);
@@ -683,6 +707,8 @@
   TimeDelta next_incremental_marking_step_duration_;
   TimeDelta previous_incremental_marking_time_left_;
 
+  GarbageCollectedMixinConstructorMarkerBase* gc_mixin_marker_;
+
   GCState gc_state_;
   GCPhase gc_phase_;
   BlinkGC::GCReason reason_for_scheduled_gc_;
diff --git a/third_party/blink/renderer/platform/testing/data/apng19-ref.png b/third_party/blink/renderer/platform/testing/data/apng19-ref.png
index b9bf822..ebf3fe7 100644
--- a/third_party/blink/renderer/platform/testing/data/apng19-ref.png
+++ b/third_party/blink/renderer/platform/testing/data/apng19-ref.png
Binary files differ
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.cc b/third_party/blink/renderer/platform/weborigin/security_origin.cc
index cd541ccd..267e2e6 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin.cc
@@ -618,7 +618,6 @@
     return this;
 
   DCHECK(IsOpaque());
-  DCHECK(!precursor_origin_->IsOpaque());
   return precursor_origin_.get();
 }
 
diff --git a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
index d1ff7c1..bd51525 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -291,7 +291,7 @@
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/click-in-focusable-link-should-not-clear-selection.html [ Crash ]
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/doubleclick-whitespace-crash.html [ Crash ]
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/legal-positions.html [ Crash ]
-crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/paint-hyphen.html [ Crash ]
+crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/paint-hyphen.html [ Pass ]
 crbug.com/591099 virtual/bidi-caret-affinity/editing/selection/word-granularity.html [ Crash ]
 crbug.com/916511 virtual/composite-after-paint/paint/background/scrolling-background-with-negative-z-child.html [ Failure Crash ]
 crbug.com/591099 virtual/composite-after-paint/paint/invalidation/box/margin.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index b1695e8..8961497 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -343,14 +343,6 @@
 # Display locking, currently only available via a virtual test.
 crbug.com/882663 display-lock [ Skip ]
 
-# These tests will pass when the cc side of overscroll customization is implemeneted.
-crbug.com/907601 virtual/threaded/fast/scrolling/events/overscroll-event-fired-to-document.html [ Skip ]
-crbug.com/907601 virtual/threaded/fast/scrolling/events/overscroll-event-fired-to-element-with-overscroll-behavior.html [ Skip ]
-crbug.com/907601 virtual/threaded/fast/scrolling/events/overscroll-event-fired-to-scrolled-element.html [ Skip ]
-crbug.com/907601 virtual/threaded/fast/scrolling/events/scrollend-event-fired-to-document.html [ Skip ]
-crbug.com/907601 virtual/threaded/fast/scrolling/events/scrollend-event-fired-to-element-with-overscroll-behavior.html [ Skip ]
-crbug.com/907601 virtual/threaded/fast/scrolling/events/scrollend-event-fired-to-scrolled-element.html [ Skip ]
-
 # Sheriff 2018/05/25
 crbug.com/846747 http/tests/navigation/navigation-interrupted-by-fragment.html  [ Pass Timeout ]
 crbug.com/846747 virtual/outofblink-cors/http/tests/navigation/navigation-interrupted-by-fragment.html  [ Pass Timeout ]
@@ -1779,50 +1771,32 @@
 crbug.com/900060 virtual/bidi-caret-affinity/editing/selection/mixed-editability-8.html [ Failure ]
 crbug.com/751952 [ Mac ] virtual/bidi-caret-affinity/editing/selection/modify_extend/extend_by_character.html [ Failure Pass ]
 crbug.com/751952 [ Win ] virtual/bidi-caret-affinity/editing/selection/modify_extend/extend_by_character.html [ Failure Pass ]
-crbug.com/636424 virtual/bidi-caret-affinity/editing/selection/modify_move/move-by-word-visually-crash-test-5.html [ Pass Timeout ]
 crbug.com/851812 virtual/bidi-caret-affinity/editing/selection/paint-hyphen.html [ Failure ]
 crbug.com/725470 virtual/bidi-caret-affinity/editing/shadow/doubleclick-on-meter-in-shadow-crash.html [ Crash Failure ]
 
 ### True failures start from here
 
+### virtual/bidi-caret-affinity/editing/deleting/
+### Needs to implement: left and right movement crossing inline-block boundaries
+crbug.com/894651 virtual/bidi-caret-affinity/editing/deleting/delete-inserts-br-after-button.html [ Failure ]
+
 ### virtual/bidi-caret-affinity/editing/selection/
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/css-pseudo-element.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/css-pseudo-element-hang.html [ Crash ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/css-pseudo-element.html [ Failure Crash ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/move-bidi-isolate.html [ Crash ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/select-bidi-run.html [ Failure ]
 
 ### virtual/bidi-caret-affinity/editing/selection/modify_move/
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move-by-word-visually-crash-test-3.html [ Crash ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move-by-word-visually-crash-test-5.html [ Pass Timeout Crash ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move-by-word-visually-inline-block-positioned-element.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move-by-word-visually-mac.html [ Timeout ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move-by-word-visually-multi-space.html [ Timeout ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move-by-word-visually-single-space-inline-element.html [ Timeout ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move-by-word-visually-single-space-one-element.html [ Timeout ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move-by-word-visually-wrong-left-right.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_01_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_02_ltr.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move-by-word-visually-wrong-left-right.html [ Failure Crash ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_03_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_04_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_05_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_05_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_06_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_06_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_07_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_07_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_08_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_08_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_09_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_09_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_10_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_10_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_11_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_11_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_12_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_12_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_13_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_13_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_14_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_14_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_15_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_15_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_16_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_16_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_17_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_17_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_18_ltr.html [ Failure ]
@@ -1835,78 +1809,30 @@
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_21_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_22_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_22_rtl.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_23_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_23_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_24_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_25_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_25_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_26_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_26_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_27_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_28_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_29_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_29_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_30_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_30_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_31_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_31_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_32_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_32_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_33_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_33_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_34_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_34_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_35_ltr.html [ Crash Timeout ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_35_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_36_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_36_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_37_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_37_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_38_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_38_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_39_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_39_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_40_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_40_rtl.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_24_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_41_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_42_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_43_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_character_43_rtl.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_right_character_in_mixed_bidi.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_01_rtl_multi_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_02_rtl_multi_line.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_02_ltr_multi_line.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_02_rtl_multi_line.html [ Failure Crash ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_03_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_04_ltr_multi_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_05_ltr_multi_line.html [ Timeout ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_04_rtl_multi_line.html [ Crash ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_05_ltr_multi_line.html [ Timeout Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_05_rtl_multi_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_06_ltr_multi_line.html [ Timeout ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_06_rtl_multi_line.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_06_ltr_multi_line.html [ Timeout Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_06_rtl_multi_line.html [ Failure Crash ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_07_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_07_rtl_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_08_rtl_multi_line.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_09_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_left_word_09_rtl_multi_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_01_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_02_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_03_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_04_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_05_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_05_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_06_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_06_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_07_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_07_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_08_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_08_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_09_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_09_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_10_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_10_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_11_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_11_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_12_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_12_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_13_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_15_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_15_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_16_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_16_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_17_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_17_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_18_ltr.html [ Failure ]
@@ -1919,52 +1845,26 @@
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_21_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_22_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_22_rtl.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_23_ltr.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_23_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_24_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_25_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_25_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_26_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_26_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_27_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_28_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_29_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_29_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_30_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_30_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_31_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_31_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_32_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_32_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_33_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_33_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_34_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_34_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_35_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_35_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_36_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_36_rtl.html [ Crash Timeout ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_37_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_37_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_38_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_38_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_39_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_39_rtl.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_40_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_40_rtl.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_24_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_41_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_42_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_43_ltr.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_character_43_rtl.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_01_rtl_multi_line.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_02_ltr_multi_line.html [ Crash ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_02_rtl_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_03_ltr_multi_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_04_ltr_multi_line.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_04_ltr_multi_line.html [ Failure Crash ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_04_rtl_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_05_ltr_multi_line.html [ Failure Timeout ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_05_rtl_multi_line.html [ Timeout ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_06_ltr_multi_line.html [ Failure ]
-crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_06_rtl_multi_line.html [ Timeout ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_05_rtl_multi_line.html [ Timeout Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_06_ltr_multi_line.html [ Failure Crash ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_06_rtl_multi_line.html [ Timeout Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_07_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_07_rtl_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_08_rtl_multi_line.html [ Failure ]
+crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_09_ltr_multi_line.html [ Failure ]
 crbug.com/894651 virtual/bidi-caret-affinity/editing/selection/modify_move/move_right_word_09_rtl_multi_line.html [ Failure ]
 
 # ====== Bidi caret affinity failures until here ======
diff --git a/third_party/blink/web_tests/display-lock/lock-after-append/locked-element-shifted-down-expected.html b/third_party/blink/web_tests/display-lock/lock-after-append/locked-element-shifted-down-expected.html
new file mode 100644
index 0000000..0710d7c
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-after-append/locked-element-shifted-down-expected.html
@@ -0,0 +1,24 @@
+<!doctype HTML>
+
+<style>
+#container {
+  contain: style layout;
+  width: 150px;
+  height: 150px;
+}
+#spacer {
+  width: 100px;
+  height: 100px;
+  background: lightgreen;
+}
+#checker {
+  width: 100px;
+  height: 50px;
+  background: green;
+}
+</style>
+
+<div id="log">PASS</div>
+<div id="spacer"></div>
+<div id="container"></div>
+<div id="checker"></div>
diff --git a/third_party/blink/web_tests/display-lock/lock-after-append/locked-element-shifted-down.html b/third_party/blink/web_tests/display-lock/lock-after-append/locked-element-shifted-down.html
new file mode 100644
index 0000000..cc667644
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-after-append/locked-element-shifted-down.html
@@ -0,0 +1,52 @@
+<!doctype HTML>
+
+<!--
+Runs an acquire, then the element is shifted down by a div above.
+-->
+
+<style>
+#container {
+  contain: style layout;
+  width: 150px;
+  height: 150px;
+  background: lightblue;
+}
+#spacer {
+  width: 100px;
+  height: 50px;
+  background: lightgreen;
+}
+#checker {
+  width: 100px;
+  height: 50px;
+  background: green;
+}
+</style>
+
+<div id="log"></div>
+<div id="spacer"></div>
+<div id="container"></div>
+<div id="checker"></div>
+
+<script>
+// TODO(vmpstr): In WPT this needs to be replaced with reftest-wait.
+if (window.testRunner)
+  window.testRunner.waitUntilDone();
+
+function finishTest(status_string) {
+  if (document.getElementById("log").innerHTML === "")
+    document.getElementById("log").innerHTML = status_string;
+  if (window.testRunner)
+    window.testRunner.notifyDone();
+}
+
+function runTest() {
+  let container = document.getElementById("container");
+  container.getDisplayLock().acquire({ timeout: Infinity }).then(() => {
+    document.getElementById("spacer").style.height = "100px";
+    finishTest("PASS");
+  });
+}
+
+window.onload = runTest;
+</script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_01_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_01_rtl.html
index 374f74a0..14c3d82 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_01_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_01_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|c</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc</div>'
+      : '<div contenteditable dir="rtl">ab|c</div>',
   '1-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bc</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc</div>'
+      : '<div contenteditable dir="rtl">abc|</div>',
   '1-1 rtl left character');
 
 selection_test(
@@ -24,6 +30,8 @@
 selection_test(
   '<div contenteditable dir="rtl">abc|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">ab|c</div>'
+      : '<div contenteditable dir="rtl">abc|</div>',
   '1-3 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_02_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_02_ltr.html
index 2680a3a..4962440 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_02_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_02_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2</div>',
   '2-0 ltr left character');
 
 selection_test(
@@ -18,12 +22,16 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2</div>',
   '2-2 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|</div>'
+      : '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2</div>',
   '2-3 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_05_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_05_ltr.html
index 97142d7..9cb88d3 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_05_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_05_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2def</div>'
+      : '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2def</div>',
   '5-3 ltr left character');
 
 selection_test(
@@ -36,7 +40,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2|def</div>'
+      : '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2def</div>',
   '5-5 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_05_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_05_rtl.html
index 8241df3..bda3a65 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_05_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_05_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0|\u05D1\u05D2def</div>'
+      : '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2def</div>',
   '5-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bc\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2def</div>'
+      : '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2def</div>',
   '5-1 rtl left character');
 
 selection_test(
@@ -42,13 +48,17 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2de|f</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2de|f</div>',
   '5-6 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2d|ef</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2def|</div>',
   '5-7 rtl left character');
 
 selection_test(
@@ -60,6 +70,8 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2def|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2de|f</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2def|</div>',
   '5-9 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_06_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_06_ltr.html
index 34fee1a..6e52a74 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_06_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_06_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0|\u05d1\u05d2abc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5</div>',
   '6-0 ltr left character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2|abc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5</div>',
   '6-2 ltr left character');
 
 selection_test(
@@ -42,7 +48,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc|\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2ab|c\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc\u05d3|\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2ab|c\u05D3\u05D4\u05D5</div>',
   '6-6 ltr left character');
 
 selection_test(
@@ -54,12 +62,16 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc\u05D3\u05D4|\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc|\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc\u05d3\u05d4\u05d5|</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc|\u05D3\u05D4\u05D5</div>',
   '6-8 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc\u05D3|\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2ab|c\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc\u05D3|\u05D4\u05D5</div>',
   '6-9 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_06_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_06_rtl.html
index 6b25934..e600d40 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_06_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_06_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2ab|c\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2abc\u05d3|\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2ab|c\u05D3\u05D4\u05D5</div>',
   '6-3 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|bc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc|\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2|abc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc|\u05D3\u05D4\u05D5</div>',
   '6-4 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_07_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_07_ltr.html
index fd91d830..00df74b 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_07_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_07_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0|\u05d1\u05d2\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   '7-3 ltr left character');
 
 selection_test(
@@ -54,12 +58,16 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2\u05D3\u05D4|\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5|</div>'
+      : '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   '7-8 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">ab|c\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   '7-9 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_07_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_07_rtl.html
index 26cfd71..2eec1a9 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_07_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_07_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0|\u05d1\u05d2\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   '7-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   '7-1 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_08_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_08_ltr.html
index 2e49453..709e703 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_08_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_08_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abcdef</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abcdef</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0|\u05d1\u05d2abcdef</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abcdef</div>',
   '8-0 ltr left character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2abcdef</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abcdef</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2|abcdef</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abcdef</div>',
   '8-2 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_08_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_08_rtl.html
index f51e9f0..c82390f 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_08_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_08_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2abcdef</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abcdef</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abcde|f</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2|abcdef</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abcde|f</div>',
   '8-3 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|bcdef</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abcdef|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2|abcdef</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abcdef|</div>',
   '8-4 rtl left character');
 
 selection_test(
@@ -60,6 +66,8 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abcdef|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abcdef|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2abcde|f</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abcdef|</div>',
   '8-9 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_09_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_09_ltr.html
index 4efdf52..32063491 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_09_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_09_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u0661\u0662\u0663\u0627\u0628\u0629</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u0661\u0662\u0663\u0627\u0628\u0629</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627|\u0628\u0629</div>'
+      : '<div contenteditable dir="ltr">|\u0661\u0662\u0663\u0627\u0628\u0629</div>',
   '9-0 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u0661|\u0662\u0663\u0627\u0628\u0629</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627|\u0628\u0629</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u0661\u0662\u0663\u0627\u0628\u0629</div>'
+      : '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627|\u0628\u0629</div>',
   '9-1 ltr left character');
 
 selection_test(
@@ -24,7 +30,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u0661\u0662\u0663|\u0627\u0628\u0629</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u0661\u0662|\u0663\u0627\u0628\u0629</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627|\u0628\u0629</div>'
+      : '<div contenteditable dir="ltr">\u0661\u0662|\u0663\u0627\u0628\u0629</div>',
   '9-3 ltr left character');
 
 selection_test(
@@ -36,12 +44,16 @@
 selection_test(
   '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627\u0628|\u0629</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u0661\u0662\u0663\u0627\u0628\u0629</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627\u0628\u0629|</div>'
+      : '<div contenteditable dir="ltr">|\u0661\u0662\u0663\u0627\u0628\u0629</div>',
   '9-5 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627\u0628\u0629|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u0661\u0662|\u0663\u0627\u0628\u0629</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627\u0628\u0629|</div>'
+      : '<div contenteditable dir="ltr">\u0661\u0662|\u0663\u0627\u0628\u0629</div>',
   '9-6 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_09_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_09_rtl.html
index 7929963..9fa78ac1 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_09_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_09_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u0661\u0662\u0663\u0627\u0628\u0629</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u0661\u0662|\u0663\u0627\u0628\u0629</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u0661\u0662\u0663\u0627|\u0628\u0629</div>'
+      : '<div contenteditable dir="rtl">\u0661\u0662|\u0663\u0627\u0628\u0629</div>',
   '9-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u0661|\u0662\u0663\u0627\u0628\u0629</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u0661\u0662\u0663|\u0627\u0628\u0629</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|\u0661\u0662\u0663\u0627\u0628\u0629</div>'
+      : '<div contenteditable dir="rtl">\u0661\u0662\u0663|\u0627\u0628\u0629</div>',
   '9-1 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_10_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_10_ltr.html
index d8acc84e..5524368 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_10_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_10_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u0627\u0628\u0629\u0661\u0662\u0663</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u0627\u0628\u0629\u0661\u0662\u0663</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0627|\u0628\u0629\u0661\u0662\u0663</div>'
+      : '<div contenteditable dir="ltr">|\u0627\u0628\u0629\u0661\u0662\u0663</div>',
   '10-0 ltr left character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u0627\u0628|\u0629\u0661\u0662\u0663</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661\u0662|\u0663</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0627\u0628\u0629|\u0661\u0662\u0663</div>'
+      : '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661\u0662|\u0663</div>',
   '10-2 ltr left character');
 
 selection_test(
@@ -30,7 +36,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661|\u0662\u0663</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u0627\u0628\u0629\u0661\u0662\u0663</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0627\u0628\u0629|\u0661\u0662\u0663</div>'
+      : '<div contenteditable dir="ltr">|\u0627\u0628\u0629\u0661\u0662\u0663</div>',
   '10-4 ltr left character');
 
 selection_test(
@@ -42,6 +50,8 @@
 selection_test(
   '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661\u0662\u0663|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u0627|\u0628\u0629\u0661\u0662\u0663</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661\u0662|\u0663</div>'
+      : '<div contenteditable dir="ltr">\u0627|\u0628\u0629\u0661\u0662\u0663</div>',
   '10-6 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_10_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_10_rtl.html
index a0e63b9..1cdfd64 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_10_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_10_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u0627\u0628\u0629\u0661\u0662\u0663</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u0627\u0628\u0629|\u0661\u0662\u0663</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661\u0662|\u0663</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u0627\u0628\u0629|\u0661\u0662\u0663</div>'
+      : '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661\u0662|\u0663</div>',
   '10-3 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661|\u0662\u0663</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661\u0662\u0663|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u0627\u0628\u0629|\u0661\u0662\u0663</div>'
+      : '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661\u0662\u0663|</div>',
   '10-4 rtl left character');
 
 selection_test(
@@ -42,6 +48,8 @@
 selection_test(
   '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661\u0662\u0663|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661\u0662\u0663|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661\u0662|\u0663</div>'
+      : '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661\u0662\u0663|</div>',
   '10-6 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_11_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_11_ltr.html
index 12687b9..1d68fb27 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_11_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_11_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|<span>abc</span>\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -30,13 +32,17 @@
 selection_test(
   '<div contenteditable dir="ltr"><span>abc|</span>\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span>ab|c</span>\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>abc</span>\u05d0|\u05d1\u05d2def</div>'
+      : '<div contenteditable dir="ltr"><span>ab|c</span>\u05D0\u05D1\u05D2def</div>',
   '11-4 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span>abc</span>|\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span>ab|c</span>\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>abc</span>\u05d0|\u05d1\u05d2def</div>'
+      : '<div contenteditable dir="ltr"><span>ab|c</span>\u05D0\u05D1\u05D2def</div>',
   '11-5 ltr left character');
 
 selection_test(
@@ -48,7 +54,9 @@
 selection_test(
   '<div contenteditable dir="ltr"><span>abc</span>\u05D0\u05D1|\u05D2def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span>abc|</span>\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>abc</span>\u05d0\u05d1\u05d2|def</div>'
+      : '<div contenteditable dir="ltr"><span>abc|</span>\u05D0\u05D1\u05D2def</div>',
   '11-7 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_11_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_11_rtl.html
index 6df514b0..563a455 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_11_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_11_rtl.html
@@ -3,22 +3,30 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|<span>abc</span>\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span>ab|c</span>\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>abc</span>\u05d0|\u05d1\u05d2def</div>'
+      : '<div contenteditable dir="rtl"><span>ab|c</span>\u05D0\u05D1\u05D2def</div>',
   '11-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span>|abc</span>\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span>ab|c</span>\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>abc</span>\u05d0|\u05d1\u05d2def</div>'
+      : '<div contenteditable dir="rtl"><span>ab|c</span>\u05D0\u05D1\u05D2def</div>',
   '11-1 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span>a|bc</span>\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span>abc|</span>\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>|abc</span>\u05d0\u05d1\u05d2def</div>'
+      : '<div contenteditable dir="rtl"><span>abc|</span>\u05D0\u05D1\u05D2def</div>',
   '11-2 rtl left character');
 
 selection_test(
@@ -54,13 +62,17 @@
 selection_test(
   '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2|def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2de|f</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>abc</span>\u05d0\u05d1\u05d2|def</div>'
+      : '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2de|f</div>',
   '11-8 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2d|ef</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>abc</span>\u05d0\u05d1\u05d2|def</div>'
+      : '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2def|</div>',
   '11-9 rtl left character');
 
 selection_test(
@@ -72,6 +84,8 @@
 selection_test(
   '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2def|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>abc</span>\u05d0\u05d1\u05d2de|f</div>'
+      : '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2def|</div>',
   '11-11 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_12_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_12_ltr.html
index 21328144..844a71df 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_12_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_12_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|<span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|<span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>\u05d0|\u05d1\u05d2</span>abc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">|<span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   '12-0 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span>|\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span>|\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>\u05d0|\u05d1\u05d2</span>abc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr"><span>|\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   '12-1 ltr left character');
 
 selection_test(
@@ -24,7 +30,9 @@
 selection_test(
   '<div contenteditable dir="ltr"><span>\u05D0\u05D1|\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span>|\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>\u05d0\u05d1\u05d2|</span>abc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr"><span>|\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   '12-3 ltr left character');
 
 selection_test(
@@ -54,7 +62,9 @@
 selection_test(
   '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc|\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>ab|c\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>\u05d0\u05d1\u05d2</span>abc\u05d3|\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>ab|c\u05D3\u05D4\u05D5</div>',
   '12-8 ltr left character');
 
 selection_test(
@@ -66,12 +76,16 @@
 selection_test(
   '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4|\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc|\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>\u05d0\u05d1\u05d2</span>abc\u05d3\u05d4\u05d5|</div>'
+      : '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc|\u05D3\u05D4\u05D5</div>',
   '12-10 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc\u05D3|\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>\u05d0\u05d1\u05d2</span>ab|c\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc\u05D3|\u05D4\u05D5</div>',
   '12-11 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_12_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_12_rtl.html
index 3748e5a..9e980cd3 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_12_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_12_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|<span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -30,19 +32,25 @@
 selection_test(
   '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2|</span>abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2</span>ab|c\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>\u05d0\u05d1\u05d2</span>abc\u05d3|\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2</span>ab|c\u05D3\u05D4\u05D5</div>',
   '12-4 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2</span>|abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2</span>ab|c\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>\u05d0\u05d1\u05d2</span>abc\u05d3|\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2</span>ab|c\u05D3\u05D4\u05D5</div>',
   '12-5 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2</span>a|bc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2</span>abc|\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>\u05d0\u05d1\u05d2|</span>abc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2</span>abc|\u05D3\u05D4\u05D5</div>',
   '12-6 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_13_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_13_ltr.html
index e60bf2a..eba70b29 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_13_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_13_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0|\u05d1\u05d2123\u05d3\u05d4\u05d5def</div>'
+      : '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
   '13-3 ltr left character');
 
 selection_test(
@@ -36,7 +40,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2123\u05D3\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123|\u05D3\u05D4\u05D5def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2|123\u05d3\u05d4\u05d5def</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123|\u05D3\u05D4\u05D5def</div>',
   '13-5 ltr left character');
 
 selection_test(
@@ -60,7 +66,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123|\u05D3\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D212|3\u05D3\u05D4\u05D5def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2123\u05d3|\u05d4\u05d5def</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D212|3\u05D3\u05D4\u05D5def</div>',
   '13-9 ltr left character');
 
 selection_test(
@@ -72,7 +80,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123\u05D3\u05D4|\u05D5def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5|def</div>'
+      : '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
   '13-11 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_13_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_13_rtl.html
index d2a4d4b..d1013b649 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_13_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_13_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0|\u05d1\u05d2123\u05d3\u05d4\u05d5def</div>'
+      : '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
   '13-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5def</div>'
+      : '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
   '13-1 rtl left character');
 
 selection_test(
@@ -42,13 +48,17 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|123\u05D3\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D212|3\u05D3\u05D4\u05D5def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2123\u05d3|\u05d4\u05d5def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D212|3\u05D3\u05D4\u05D5def</div>',
   '13-6 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D21|23\u05D3\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123|\u05D3\u05D4\u05D5def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2|123\u05d3\u05d4\u05d5def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123|\u05D3\u05D4\u05D5def</div>',
   '13-7 rtl left character');
 
 selection_test(
@@ -78,13 +88,17 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5|def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5de|f</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5|def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5de|f</div>',
   '13-12 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5d|ef</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5|def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def|</div>',
   '13-13 rtl left character');
 
 selection_test(
@@ -96,6 +110,8 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5de|f</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def|</div>',
   '13-15 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_14_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_14_ltr.html
index 4de0096..9d4c39c 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_14_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_14_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2123</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2123</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2123</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0|\u05d1\u05d2123</div>'
+      : '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2123</div>',
   '14-3 ltr left character');
 
 selection_test(
@@ -36,7 +40,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2123</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D212|3</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2|123</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D212|3</div>',
   '14-5 ltr left character');
 
 selection_test(
@@ -48,7 +54,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D21|23</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2123</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2|123</div>'
+      : '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2123</div>',
   '14-7 ltr left character');
 
 selection_test(
@@ -60,6 +68,8 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2123</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d212|3</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2123</div>',
   '14-9 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_14_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_14_rtl.html
index 4a61bd4e..4942e68 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_14_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_14_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2123</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2123</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0|\u05d1\u05d2123</div>'
+      : '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2123</div>',
   '14-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bc\u05D0\u05D1\u05D2123</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2123</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc\u05d0\u05d1\u05d2123</div>'
+      : '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2123</div>',
   '14-1 rtl left character');
 
 selection_test(
@@ -42,13 +48,17 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|123</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D212|3</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2|123</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D212|3</div>',
   '14-6 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D21|23</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2|123</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123|</div>',
   '14-7 rtl left character');
 
 selection_test(
@@ -60,6 +70,8 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d212|3</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123|</div>',
   '14-9 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_15_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_15_ltr.html
index 7f02b3d1..438396b 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_15_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_15_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2123def</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2123def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2123def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0|\u05d1\u05d2123def</div>'
+      : '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2123def</div>',
   '15-3 ltr left character');
 
 selection_test(
@@ -36,7 +40,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2123def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D212|3def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2|123def</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D212|3def</div>',
   '15-5 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_15_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_15_rtl.html
index 3160668ce..7bb332a7 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_15_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_15_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2123def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2123def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0|\u05d1\u05d2123def</div>'
+      : '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2123def</div>',
   '15-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bc\u05D0\u05D1\u05D2123def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2123def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc\u05d0\u05d1\u05d2123def</div>'
+      : '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2123def</div>',
   '15-1 rtl left character');
 
 selection_test(
@@ -42,13 +48,17 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|123def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123de|f</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2|123def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123de|f</div>',
   '15-6 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D21|23def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2|123def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123def|</div>',
   '15-7 rtl left character');
 
 selection_test(
@@ -78,6 +88,8 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123def|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2123de|f</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123def|</div>',
   '15-12 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_16_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_16_ltr.html
index 7f500e7..e9322ed 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_16_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_16_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0|\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-0 ltr left character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123|\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2|123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123|\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-2 ltr left character');
 
 selection_test(
@@ -42,7 +48,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123|\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D212|3\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3|\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D212|3\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-6 ltr left character');
 
 selection_test(
@@ -54,7 +62,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4|\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5|abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-8 ltr left character');
 
 selection_test(
@@ -78,7 +88,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc|\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5ab|c\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6|\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5ab|c\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-12 ltr left character');
 
 selection_test(
@@ -90,7 +102,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7|\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456|\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8|456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456|\u05D9\u05DB\u05DC</div>',
   '16-14 ltr left character');
 
 selection_test(
@@ -114,7 +128,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456|\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D845|6\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9|\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D845|6\u05D9\u05DB\u05DC</div>',
   '16-18 ltr left character');
 
 selection_test(
@@ -126,12 +142,16 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB|\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc|\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc|</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc|\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-20 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6|\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5ab|c\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6|\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-21 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_16_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_16_rtl.html
index 35ed3cc..5115964 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_16_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_16_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D212|3\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2123\u05d3|\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D212|3\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-3 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D21|23\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123|\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2|123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123|\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-4 rtl left character');
 
 selection_test(
@@ -60,13 +66,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5|abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5ab|c\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6|\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5ab|c\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-9 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5a|bc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc|\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5|abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc|\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-10 rtl left character');
 
 selection_test(
@@ -96,13 +106,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8|456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D845|6\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9|\u05db\u05dc</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D845|6\u05D9\u05DB\u05DC</div>',
   '16-15 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D84|56\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456|\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8|456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456|\u05D9\u05DB\u05DC</div>',
   '16-16 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_25_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_25_ltr.html
index 8bd9a684..1f9c64b 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_25_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_25_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -42,7 +44,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abcdef|\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abcde|f\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abcdef\u05d0|\u05d1\u05d2<img>\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">abcde|f\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   '25-6 ltr left character');
 
 selection_test(
@@ -78,12 +82,16 @@
 selection_test(
   '<div contenteditable dir="ltr">abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4|\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abcdef|\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abcdef\u05d0\u05d1\u05d2<img>\u05d3\u05d4\u05d5|</div>'
+      : '<div contenteditable dir="ltr">abcdef|\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   '25-12 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abcdef\u05D0|\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abcde|f\u05d0\u05d1\u05d2<img>\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">abcdef\u05D0|\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   '25-13 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_25_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_25_rtl.html
index dff132a..b3f438e 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_25_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_25_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abcde|f\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abcdef\u05d0|\u05d1\u05d2<img>\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl">abcde|f\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   '25-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abcdef|\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abcdef\u05d0\u05d1\u05d2<img>\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl">abcdef|\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   '25-1 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_26_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_26_ltr.html
index c3daf162..d7f4b5c 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_26_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_26_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0|\u05d1\u05d2\u05d3\u05d4\u05d5abc<img>def</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def</div>',
   '26-0 ltr left character');
 
 selection_test(
@@ -36,7 +40,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2\u05D3\u05D4|\u05D5abc<img>def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5|abc<img>def</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def</div>',
   '26-5 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_26_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_26_rtl.html
index 890990ad..0c5f38c 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_26_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_26_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -42,13 +44,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5|abc<img>def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>de|f</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5|abc<img>def</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>de|f</div>',
   '26-6 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5a|bc<img>def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5|abc<img>def</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def|</div>',
   '26-7 rtl left character');
 
 selection_test(
@@ -84,6 +90,8 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5abc<img>de|f</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def|</div>',
   '26-13 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_27_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_27_ltr.html
index 561a473..e4cd0951 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_27_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_27_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -30,7 +32,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc<input>|\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc|<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc<input>\u05D0|\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>'
+      : '<div contenteditable dir="ltr">abc|<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   '27-4 ltr left character');
 
 selection_test(
@@ -72,7 +76,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4|\u05D5ghi</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc<input>|\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5|ghi</div>'
+      : '<div contenteditable dir="ltr">abc<input>|\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   '27-11 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_27_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_27_rtl.html
index 0754521..e61f5f8 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_27_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_27_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|c<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<input>|\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>'
+      : '<div contenteditable dir="rtl">ab|c<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   '27-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>'
+      : '<div contenteditable dir="rtl">abc|<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   '27-1 rtl left character');
 
 selection_test(
@@ -78,13 +84,17 @@
 selection_test(
   '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5|ghi</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5gh|i</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5|ghi</div>'
+      : '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5gh|i</div>',
   '27-12 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5g|hi</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5|ghi</div>'
+      : '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi|</div>',
   '27-13 rtl left character');
 
 selection_test(
@@ -96,6 +106,8 @@
 selection_test(
   '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5gh|i</div>'
+      : '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi|</div>',
   '27-15 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_28_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_28_ltr.html
index 0c1c148d..5224f3f 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_28_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_28_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
   '28-0 ltr left character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|<input>abc<img><img>def\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
   '28-2 ltr left character');
 
 selection_test(
@@ -78,7 +84,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def|\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>de|f\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3|\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>de|f\u05D3\u05D4\u05D5</div>',
   '28-12 ltr left character');
 
 selection_test(
@@ -90,12 +98,16 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4|\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def|\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5|</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def|\u05D3\u05D4\u05D5</div>',
   '28-14 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3|\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>de|f\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3|\u05D4\u05D5</div>',
   '28-15 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_28_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_28_rtl.html
index 1d511f6..f2512aa 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_28_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_28_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -30,13 +32,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>|abc<img><img>def\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>abc<img><img>de|f\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3|\u05D4\u05D5</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>abc<img><img>de|f\u05D3\u05D4\u05D5</div>',
   '28-4 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>a|bc<img><img>def\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>abc<img><img>def|\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>|abc<img><img>def\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>abc<img><img>def|\u05D3\u05D4\u05D5</div>',
   '28-5 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_29_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_29_ltr.html
index aeaae02..b197f893 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_29_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_29_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0|\u05d1\u05d2<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   '29-3 ltr left character');
 
 selection_test(
@@ -60,18 +64,24 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4|\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2<span>\u05d3\u05d4\u05d5|</span></div>'
+      : '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   '29-9 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5|</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">ab|c\u05d0\u05d1\u05d2<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   '29-10 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span>|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">ab|c\u05d0\u05d1\u05d2<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   '29-11 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_29_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_29_rtl.html
index a4d9547..80fa7f92 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_29_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_29_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0|\u05d1\u05d2<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   '29-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc\u05d0\u05d1\u05d2<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   '29-1 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_30_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_30_ltr.html
index e48fdd62..77c48d3 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_30_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_30_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc<span>def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0|\u05d1\u05d2abc<span>def</span></div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc<span>def</span></div>',
   '30-0 ltr left character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2abc<span>def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2|abc<span>def</span></div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc<span>def</span></div>',
   '30-2 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_30_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_30_rtl.html
index 64930ad4..bc41af6 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_30_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_30_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2abc<span>def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc<span>def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>de|f</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2|abc<span>def</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>de|f</span></div>',
   '30-3 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|bc<span>def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>def|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2|abc<span>def</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>def|</span></div>',
   '30-4 rtl left character');
 
 selection_test(
@@ -66,12 +72,16 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>def|</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>def|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2abc<span>de|f</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>def|</span></div>',
   '30-10 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>def</span>|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>def</span>|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2abc<span>de|f</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>def</span>|</div>',
   '30-11 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_31_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_31_ltr.html
index 6f3dbea..b3be925 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_31_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_31_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|ab<span>c\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -30,7 +32,9 @@
 selection_test(
   '<div contenteditable dir="ltr">ab<span>c|\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab|<span>c\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">ab<span>c\u05d0|\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="ltr">ab|<span>c\u05D0\u05D1\u05D2def</span></div>',
   '31-4 ltr left character');
 
 selection_test(
@@ -42,7 +46,9 @@
 selection_test(
   '<div contenteditable dir="ltr">ab<span>c\u05D0\u05D1|\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab<span>c|\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">ab<span>c\u05d0\u05d1\u05d2|def</span></div>'
+      : '<div contenteditable dir="ltr">ab<span>c|\u05D0\u05D1\u05D2def</span></div>',
   '31-6 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_31_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_31_rtl.html
index bf743c04..1e44a60 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_31_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_31_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|ab<span>c\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|<span>c\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">ab<span>c\u05d0|\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">ab|<span>c\u05D0\u05D1\u05D2def</span></div>',
   '31-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|b<span>c\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab<span>c|\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|ab<span>c\u05d0\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">ab<span>c|\u05D0\u05D1\u05D2def</span></div>',
   '31-1 rtl left character');
 
 selection_test(
@@ -48,13 +54,17 @@
 selection_test(
   '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2|def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2de|f</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">ab<span>c\u05d0\u05d1\u05d2|def</span></div>'
+      : '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2de|f</span></div>',
   '31-7 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2d|ef</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2def|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">ab<span>c\u05d0\u05d1\u05d2|def</span></div>'
+      : '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2def|</span></div>',
   '31-8 rtl left character');
 
 selection_test(
@@ -66,12 +76,16 @@
 selection_test(
   '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2def|</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2def|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">ab<span>c\u05d0\u05d1\u05d2de|f</span></div>'
+      : '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2def|</span></div>',
   '31-10 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2def</span>|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2def</span>|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">ab<span>c\u05d0\u05d1\u05d2de|f</span></div>'
+      : '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2def</span>|</div>',
   '31-11 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_32_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_32_ltr.html
index 9f309cd4..00c3a02 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_32_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_32_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0|\u05d1<span>\u05d2abc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
   '32-0 ltr left character');
 
 selection_test(
@@ -18,13 +22,17 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1|<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1<span>\u05d2|abc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
   '32-2 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1<span>|\u05D2abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1<span>\u05d2|abc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
   '32-3 ltr left character');
 
 selection_test(
@@ -48,7 +56,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc|\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2ab|c\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1<span>\u05d2abc\u05d3|\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2ab|c\u05D3\u05D4\u05D5</span></div>',
   '32-7 ltr left character');
 
 selection_test(
@@ -60,18 +70,24 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4|\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc|\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1<span>\u05d2abc\u05d3\u05d4\u05d5|</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc|\u05D3\u05D4\u05D5</span></div>',
   '32-9 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5|</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3|\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1<span>\u05d2ab|c\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3|\u05D4\u05D5</span></div>',
   '32-10 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span>|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3|\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1<span>\u05d2ab|c\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3|\u05D4\u05D5</span></div>',
   '32-11 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_32_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_32_rtl.html
index 3aa0c11d..2e47540 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_32_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_32_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -30,13 +32,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1<span>\u05D2|abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1<span>\u05D2ab|c\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1<span>\u05d2abc\u05d3|\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1<span>\u05D2ab|c\u05D3\u05D4\u05D5</span></div>',
   '32-4 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1<span>\u05D2a|bc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1<span>\u05D2abc|\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1<span>\u05d2|abc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1<span>\u05D2abc|\u05D3\u05D4\u05D5</span></div>',
   '32-5 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_33_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_33_ltr.html
index 1ef4e11..006ed22 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_33_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_33_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc<span>\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|<span>\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab|c<span>\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc<span>\u05d0|\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="ltr">ab|c<span>\u05D0\u05D1\u05D2def</span></div>',
   '33-3 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc<span>|\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab|c<span>\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc<span>\u05d0|\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="ltr">ab|c<span>\u05D0\u05D1\u05D2def</span></div>',
   '33-4 ltr left character');
 
 selection_test(
@@ -42,7 +48,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc<span>\u05D0\u05D1|\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc|<span>\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc<span>\u05d0\u05d1\u05d2|def</span></div>'
+      : '<div contenteditable dir="ltr">abc|<span>\u05D0\u05D1\u05D2def</span></div>',
   '33-6 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_33_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_33_rtl.html
index 13c6155..46d80a7 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_33_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_33_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc<span>\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|c<span>\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<span>\u05d0|\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">ab|c<span>\u05D0\u05D1\u05D2def</span></div>',
   '33-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bc<span>\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|<span>\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc<span>\u05d0\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">abc|<span>\u05D0\u05D1\u05D2def</span></div>',
   '33-1 rtl left character');
 
 selection_test(
@@ -48,13 +54,17 @@
 selection_test(
   '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2|def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2de|f</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<span>\u05d0\u05d1\u05d2|def</span></div>'
+      : '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2de|f</span></div>',
   '33-7 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2d|ef</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2def|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<span>\u05d0\u05d1\u05d2|def</span></div>'
+      : '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2def|</span></div>',
   '33-8 rtl left character');
 
 selection_test(
@@ -66,12 +76,16 @@
 selection_test(
   '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2def|</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2def|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<span>\u05d0\u05d1\u05d2de|f</span></div>'
+      : '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2def|</span></div>',
   '33-10 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2def</span>|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2def</span>|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<span>\u05d0\u05d1\u05d2de|f</span></div>'
+      : '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2def</span>|</div>',
   '33-11 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_34_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_34_ltr.html
index 1a5f97a..9cbcd277 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_34_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_34_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0|\u05d1\u05d2<span>abc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
   '34-0 ltr left character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2|<span>abc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
   '34-2 ltr left character');
 
 selection_test(
@@ -48,7 +54,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc|\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>ab|c\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2<span>abc\u05d3|\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>ab|c\u05D3\u05D4\u05D5</span></div>',
   '34-7 ltr left character');
 
 selection_test(
@@ -60,18 +68,24 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4|\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc|\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2<span>abc\u05d3\u05d4\u05d5|</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc|\u05D3\u05D4\u05D5</span></div>',
   '34-9 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5|</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3|\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2<span>ab|c\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3|\u05D4\u05D5</span></div>',
   '34-10 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span>|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3|\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2<span>ab|c\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3|\u05D4\u05D5</span></div>',
   '34-11 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_34_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_34_rtl.html
index d5f83dfa..b16fd00 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_34_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_34_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,19 +26,25 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|<span>abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<span>ab|c\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2<span>abc\u05d3|\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<span>ab|c\u05D3\u05D4\u05D5</span></div>',
   '34-3 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<span>|abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<span>ab|c\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2<span>abc\u05d3|\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<span>ab|c\u05D3\u05D4\u05D5</span></div>',
   '34-4 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<span>a|bc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<span>abc|\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2|<span>abc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<span>abc|\u05D3\u05D4\u05D5</span></div>',
   '34-5 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_35_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_35_ltr.html
index f10bca2..56a547a 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_35_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_35_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0def</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab|c\u05D0def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05D0|def</div>'
+      : '<div contenteditable dir="ltr">ab|c\u05D0def</div>',
   '35-3 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0|def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc|\u05D0def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05D0|def</div>'
+      : '<div contenteditable dir="ltr">abc|\u05D0def</div>',
   '35-4 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_35_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_35_rtl.html
index 925732e..c7f53ee2 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_35_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_35_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|c\u05D0def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0|def</div>'
+      : '<div contenteditable dir="rtl">ab|c\u05D0def</div>',
   '35-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bc\u05D0def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|\u05D0def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc\u05D0def</div>'
+      : '<div contenteditable dir="rtl">abc|\u05D0def</div>',
   '35-1 rtl left character');
 
 selection_test(
@@ -30,13 +36,17 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0|def</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0de|f</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0|def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0de|f</div>',
   '35-4 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0d|ef</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0|def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0def|</div>',
   '35-5 rtl left character');
 
 selection_test(
@@ -48,6 +58,8 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0def|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0def|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0de|f</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0def|</div>',
   '35-7 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_36_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_36_ltr.html
index de7a945..28c720b 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_36_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_36_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2a\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5</div>',
   '36-0 ltr left character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2a\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|a\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5</div>',
   '36-2 ltr left character');
 
 selection_test(
@@ -30,7 +36,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a|\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|a\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3|\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|a\u05D3\u05D4\u05D5</div>',
   '36-4 ltr left character');
 
 selection_test(
@@ -42,12 +50,16 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3\u05D4|\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a|\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5|</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a|\u05D3\u05D4\u05D5</div>',
   '36-6 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3|\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|a\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3|\u05D4\u05D5</div>',
   '36-7 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_36_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_36_rtl.html
index e7e04215..c950e1b 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_36_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_36_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|a\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a\u05D3|\u05D4\u05D5</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|\u05D3\u05D4\u05D5</div>',
   '36-3 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_37_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_37_ltr.html
index dd6a145..3104db3d 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_37_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_37_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2<span>def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2<span>def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0|\u05d1\u05d2<span>def</span></div>'
+      : '<div contenteditable dir="ltr">ab|c\u05D0\u05D1\u05D2<span>def</span></div>',
   '37-3 ltr left character');
 
 selection_test(
@@ -36,7 +40,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2<span>def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2|<span>def</span></div>'
+      : '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2<span>def</span></div>',
   '37-5 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_37_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_37_rtl.html
index 1bfbe8e2..8a947aa 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_37_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_37_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0|\u05D1\u05D2<span>def</span></div>'
+      : '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2<span>def</span></div>',
   '37-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bc\u05D0\u05D1\u05D2<span>def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>def</span></div>'
+      : '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2<span>def</span></div>',
   '37-1 rtl left character');
 
 selection_test(
@@ -42,19 +48,25 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|<span>def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>de|f</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|<span>def</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>de|f</span></div>',
   '37-6 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>|def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>de|f</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>|def</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>de|f</span></div>',
   '37-7 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>d|ef</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>def|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|<span>def</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>def|</span></div>',
   '37-8 rtl left character');
 
 selection_test(
@@ -66,12 +78,16 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>def|</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>def|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>de|f</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>def|</span></div>',
   '37-10 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>def</span>|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>def</span>|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>de|f</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>def</span>|</div>',
   '37-11 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_38_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_38_ltr.html
index 87cc8085..a8d5ff8 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_38_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_38_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0|\u05d1\u05d2abc<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
   '38-0 ltr left character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2|abc<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
   '38-2 ltr left character');
 
 selection_test(
@@ -42,13 +48,17 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc|<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2ab|c<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc<span>\u05d3|\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2ab|c<span>\u05D3\u05D4\u05D5</span></div>',
   '38-6 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>|\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2ab|c<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc<span>\u05d3|\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2ab|c<span>\u05D3\u05D4\u05D5</span></div>',
   '38-7 ltr left character');
 
 selection_test(
@@ -60,18 +70,24 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4|\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc|<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc<span>\u05d3\u05d4\u05d5|</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc|<span>\u05D3\u05D4\u05D5</span></div>',
   '38-9 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5|</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3|\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2ab|c<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3|\u05D4\u05D5</span></div>',
   '38-10 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span>|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3|\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2ab|c<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3|\u05D4\u05D5</span></div>',
   '38-11 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_38_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_38_rtl.html
index 6a82cf7..05276330 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_38_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_38_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2ab|c<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>\u05D3|\u05D4\u05D5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2ab|c<span>\u05D3\u05D4\u05D5</span></div>',
   '38-3 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|bc<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc|<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc<span>\u05D3\u05D4\u05D5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc|<span>\u05D3\u05D4\u05D5</span></div>',
   '38-4 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_39_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_39_ltr.html
index 68a2965..751e853 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_39_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_39_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0<span>\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0<span>\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">ab|c\u05D0<span>\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0|<span>\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="ltr">ab|c\u05D0<span>\u05D1\u05D2def</span></div>',
   '39-3 ltr left character');
 
 selection_test(
@@ -42,7 +46,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0<span>\u05D1|\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">abc|\u05D0<span>\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0<span>\u05d1\u05d2|def</span></div>'
+      : '<div contenteditable dir="ltr">abc|\u05D0<span>\u05D1\u05D2def</span></div>',
   '39-6 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_39_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_39_rtl.html
index 2a409037..2d2ecd9 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_39_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_39_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0<span>\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">ab|c\u05D0<span>\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0|<span>\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">ab|c\u05D0<span>\u05D1\u05D2def</span></div>',
   '39-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">a|bc\u05D0<span>\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc|\u05D0<span>\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">|abc\u05d0<span>\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">abc|\u05D0<span>\u05D1\u05D2def</span></div>',
   '39-1 rtl left character');
 
 selection_test(
@@ -48,13 +54,17 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2|def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2de|f</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0<span>\u05d1\u05d2|def</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2de|f</span></div>',
   '39-7 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2d|ef</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2def|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0<span>\u05d1\u05d2|def</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2def|</span></div>',
   '39-8 rtl left character');
 
 selection_test(
@@ -66,12 +76,16 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2def|</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2def|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0<span>\u05d1\u05d2de|f</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2def|</span></div>',
   '39-10 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2def</span>|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2def</span>|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0<span>\u05d1\u05d2de|f</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2def</span>|</div>',
   '39-11 rtl left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_40_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_40_ltr.html
index 1b1732c..9f262251 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_40_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_40_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0|\u05d1\u05d2a<span>bc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
   '40-0 ltr left character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2|a<span>bc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
   '40-2 ltr left character');
 
 selection_test(
@@ -48,7 +54,9 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc|\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>b|c\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a<span>bc\u05d3|\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>b|c\u05D3\u05D4\u05D5</span></div>',
   '40-7 ltr left character');
 
 selection_test(
@@ -60,18 +68,24 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4|\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc|\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a<span>bc\u05d3\u05d4\u05d5|</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc|\u05D3\u05D4\u05D5</span></div>',
   '40-9 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5|</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3|\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a<span>b|c\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3|\u05D4\u05D5</span></div>',
   '40-10 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span>|</div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3|\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a<span>b|c\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3|\u05D4\u05D5</span></div>',
   '40-11 ltr left character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_40_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_40_rtl.html
index 485a0c1..441937c 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_40_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_40_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
@@ -24,19 +26,25 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|a<span>bc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a<span>b|c\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2a<span>bc\u05d3|\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a<span>b|c\u05D3\u05D4\u05D5</span></div>',
   '40-3 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|<span>bc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a<span>bc|\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2|a<span>bc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a<span>bc|\u05D3\u05D4\u05D5</span></div>',
   '40-4 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a<span>|bc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a<span>bc|\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2|a<span>bc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a<span>bc|\u05D3\u05D4\u05D5</span></div>',
   '40-5 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_43_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_43_ltr.html
index a2e135f..b74c48e 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_43_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_43_ltr.html
@@ -3,22 +3,30 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|<span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr">|<span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7|\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr">|<span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-0 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7|\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-1 ltr left character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">a|bc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7|\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7|\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-2 ltr left character');
 
 selection_test(
@@ -30,7 +38,9 @@
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">abc|\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">ab|c\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7|\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">ab|c\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-4 ltr left character');
 
 selection_test(
@@ -42,7 +52,9 @@
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7|\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123|\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7|123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123|\u05E0\u05E0\u05E0def</span></div>',
   '43-6 ltr left character');
 
 selection_test(
@@ -66,7 +78,9 @@
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123|\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E712|3\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0|\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E712|3\u05E0\u05E0\u05E0def</span></div>',
   '43-10 ltr left character');
 
 selection_test(
@@ -78,7 +92,9 @@
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0|\u05E0def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0de|f</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0|def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0de|f</span></div>',
   '43-12 ltr left character');
 
 selection_test(
@@ -90,7 +106,9 @@
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0d|ef</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0|def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-14 ltr left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_43_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_43_rtl.html
index cad0084..7745f5d6 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_43_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_left_character_43_rtl.html
@@ -3,22 +3,30 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|<span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">ab|c\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7|\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">ab|c\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-0 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">ab|c\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7|\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">ab|c\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-1 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span dir="rtl">a|bc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">abc|\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">abc|\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-2 rtl left character');
 
 selection_test(
@@ -48,13 +56,17 @@
 selection_test(
   '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7|123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E712|3\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0|\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E712|3\u05E0\u05E0\u05E0def</span></div>',
   '43-7 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E71|23\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123|\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7|123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123|\u05E0\u05E0\u05E0def</span></div>',
   '43-8 rtl left character');
 
 selection_test(
@@ -84,13 +96,17 @@
 selection_test(
   '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0|def</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0de|f</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0|def</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0de|f</span></div>',
   '43-13 rtl left character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0d|ef</span></div>',
   selection => selection.modify('move', 'left', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0|def</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def|</span></div>',
   '43-14 rtl left character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_01_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_01_rtl.html
index 4cc6651..79fa7bb0 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_01_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_01_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">a|bc</div>'
+      : '<div contenteditable dir="rtl">|abc</div>',
   '1-0 rtl right character');
 
 selection_test(
@@ -18,12 +22,16 @@
 selection_test(
   '<div contenteditable dir="rtl">ab|c</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc|</div>'
+      : '<div contenteditable dir="rtl">|abc</div>',
   '1-2 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">a|bc</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc|</div>'
+      : '<div contenteditable dir="rtl">a|bc</div>',
   '1-3 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_02_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_02_ltr.html
index d35d5b60..c0d43439 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_02_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_02_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2</div>',
   '2-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|</div>',
   '2-1 ltr right character');
 
 selection_test(
@@ -24,6 +30,8 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|</div>',
   '2-3 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_05_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_05_ltr.html
index f0db4c8..795e4db0 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_05_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_05_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2d|ef</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2def</div>',
   '5-3 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2|def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc|\u05d0\u05d1\u05d2def</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2|def</div>',
   '5-4 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_05_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_05_rtl.html
index 5ee5467..c4a1bc6 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_05_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_05_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">a|bc\u05d0\u05d1\u05d2def</div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2def</div>',
   '5-0 rtl right character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc|\u05d0\u05d1\u05d2def</div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2def</div>',
   '5-2 rtl right character');
 
 selection_test(
@@ -42,7 +48,9 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1|\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2d|ef</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1|\u05D2def</div>',
   '5-6 rtl right character');
 
 selection_test(
@@ -54,12 +62,16 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2de|f</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2def|</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|def</div>',
   '5-8 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2def|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2d|ef</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1|\u05d2def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2d|ef</div>',
   '5-9 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_06_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_06_ltr.html
index 20170571..37055b77 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_06_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_06_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a|bc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2abc\u05D3\u05D4\u05D5</div>',
   '6-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05d0\u05d1\u05d2abc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|abc\u05D3\u05D4\u05D5</div>',
   '6-1 ltr right character');
 
 selection_test(
@@ -42,13 +48,17 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc|\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc\u05D3\u05D4|\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc|\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc\u05D3\u05D4|\u05D5</div>',
   '6-6 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc\u05D3|\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc|\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5|</div>',
   '6-7 ltr right character');
 
 selection_test(
@@ -60,6 +70,8 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc\u05d3\u05d4|\u05d5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5|</div>',
   '6-9 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_06_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_06_rtl.html
index a6f7a90..20580e1f 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_06_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_06_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2a|bc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2abc\u05D3\u05D4\u05D5</div>',
   '6-3 rtl right character');
 
 selection_test(
@@ -36,7 +40,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2ab|c\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2abc|\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc\u05D3\u05D4\u05D5</div>',
   '6-5 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_07_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_07_ltr.html
index a76541d..6fec319 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_07_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_07_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2\u05D3\u05D4|\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc|\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2\u05D3\u05D4|\u05D5</div>',
   '7-3 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc|\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5|</div>',
   '7-4 ltr right character');
 
 selection_test(
@@ -60,6 +66,8 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2\u05d3\u05d4|\u05d5</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5|</div>',
   '7-9 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_07_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_07_rtl.html
index 3a201ef..e141077f 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_07_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_07_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">a|bc\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   '7-0 rtl right character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc|\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5</div>',
   '7-2 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_08_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_08_ltr.html
index 41733cd6..5062f12 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_08_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_08_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abcdef</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2abcdef</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a|bcdef</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2abcdef</div>',
   '8-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2abcdef</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|abcdef</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05d0\u05d1\u05d2abcdef</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|abcdef</div>',
   '8-1 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_08_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_08_rtl.html
index 1bbf020..a0061f8 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_08_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_08_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2abcdef</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abcdef</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2abcdef</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2a|bcdef</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2abcdef</div>',
   '8-3 rtl right character');
 
 selection_test(
@@ -54,12 +58,16 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abcde|f</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abcdef</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2abcdef|</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abcdef</div>',
   '8-8 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abcdef|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|bcdef</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1|\u05d2abcdef</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|bcdef</div>',
   '8-9 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_09_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_09_ltr.html
index 2cc1e0aa..da1d29c 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_09_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_09_ltr.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u0661\u0662\u0663\u0627\u0628\u0629</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627\u0628|\u0629</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0661|\u0662\u0663\u0627\u0628\u0629</div>'
+      : '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627\u0628|\u0629</div>',
   '9-0 ltr right character');
 
 selection_test(
@@ -18,19 +22,25 @@
 selection_test(
   '<div contenteditable dir="ltr">\u0661\u0662|\u0663\u0627\u0628\u0629</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627\u0628\u0629|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0661\u0662\u0663|\u0627\u0628\u0629</div>'
+      : '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627\u0628\u0629|</div>',
   '9-2 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u0661\u0662\u0663|\u0627\u0628\u0629</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u0661\u0662\u0663|\u0627\u0628\u0629</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0661|\u0662\u0663\u0627\u0628\u0629</div>'
+      : '<div contenteditable dir="ltr">\u0661\u0662\u0663|\u0627\u0628\u0629</div>',
   '9-3 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627|\u0628\u0629</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u0661|\u0662\u0663\u0627\u0628\u0629</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0661\u0662\u0663|\u0627\u0628\u0629</div>'
+      : '<div contenteditable dir="ltr">\u0661|\u0662\u0663\u0627\u0628\u0629</div>',
   '9-4 ltr right character');
 
 selection_test(
@@ -42,6 +52,8 @@
 selection_test(
   '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627\u0628\u0629|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627\u0628\u0629|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627\u0628|\u0629</div>'
+      : '<div contenteditable dir="ltr">\u0661\u0662\u0663\u0627\u0628\u0629|</div>',
   '9-6 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_09_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_09_rtl.html
index bcfb5ee..e3770fa63 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_09_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_09_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u0661\u0662\u0663\u0627\u0628\u0629</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|\u0661\u0662\u0663\u0627\u0628\u0629</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u0661|\u0662\u0663\u0627\u0628\u0629</div>'
+      : '<div contenteditable dir="rtl">|\u0661\u0662\u0663\u0627\u0628\u0629</div>',
   '9-0 rtl right character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u0661\u0662|\u0663\u0627\u0628\u0629</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|\u0661\u0662\u0663\u0627\u0628\u0629</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u0661\u0662\u0663|\u0627\u0628\u0629</div>'
+      : '<div contenteditable dir="rtl">|\u0661\u0662\u0663\u0627\u0628\u0629</div>',
   '9-2 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_10_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_10_ltr.html
index 7c1c5046..d39a8d1a 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_10_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_10_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u0627\u0628\u0629\u0661\u0662\u0663</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661|\u0662\u0663</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u0627\u0628\u0629\u0661\u0662\u0663</div>'
+      : '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661|\u0662\u0663</div>',
   '10-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u0627|\u0628\u0629\u0661\u0662\u0663</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661\u0662\u0663|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u0627\u0628\u0629\u0661\u0662\u0663</div>'
+      : '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661\u0662\u0663|</div>',
   '10-1 ltr right character');
 
 selection_test(
@@ -36,12 +42,16 @@
 selection_test(
   '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661\u0662|\u0663</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u0627\u0628|\u0629\u0661\u0662\u0663</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661\u0662\u0663|</div>'
+      : '<div contenteditable dir="ltr">\u0627\u0628|\u0629\u0661\u0662\u0663</div>',
   '10-5 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661\u0662\u0663|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661\u0662\u0663|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u0627\u0628|\u0629\u0661\u0662\u0663</div>'
+      : '<div contenteditable dir="ltr">\u0627\u0628\u0629\u0661\u0662\u0663|</div>',
   '10-6 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_10_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_10_rtl.html
index e9c1d7df..0dd23e6 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_10_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_10_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u0627\u0628\u0629\u0661\u0662\u0663</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u0627\u0628\u0629|\u0661\u0662\u0663</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u0627\u0628|\u0629\u0661\u0662\u0663</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661|\u0662\u0663</div>'
+      : '<div contenteditable dir="rtl">\u0627\u0628|\u0629\u0661\u0662\u0663</div>',
   '10-3 rtl right character');
 
 selection_test(
@@ -36,12 +40,16 @@
 selection_test(
   '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661\u0662|\u0663</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u0627\u0628\u0629|\u0661\u0662\u0663</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661\u0662\u0663|</div>'
+      : '<div contenteditable dir="rtl">\u0627\u0628\u0629|\u0661\u0662\u0663</div>',
   '10-5 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661\u0662\u0663|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661|\u0662\u0663</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u0627\u0628|\u0629\u0661\u0662\u0663</div>'
+      : '<div contenteditable dir="rtl">\u0627\u0628\u0629\u0661|\u0662\u0663</div>',
   '10-6 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_11_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_11_ltr.html
index 5f0cfd8..b99d1f90 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_11_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_11_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|<span>abc</span>\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -30,19 +32,25 @@
 selection_test(
   '<div contenteditable dir="ltr"><span>abc|</span>\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span>abc</span>\u05D0\u05D1|\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>abc</span>\u05d0\u05d1\u05d2d|ef</div>'
+      : '<div contenteditable dir="ltr"><span>abc</span>\u05D0\u05D1|\u05D2def</div>',
   '11-4 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span>abc</span>|\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span>abc</span>\u05D0\u05D1|\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>abc</span>\u05d0\u05d1\u05d2d|ef</div>'
+      : '<div contenteditable dir="ltr"><span>abc</span>\u05D0\u05D1|\u05D2def</div>',
   '11-5 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span>abc</span>\u05D0|\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span>abc</span>\u05D0\u05D1\u05D2|def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>abc|</span>\u05d0\u05d1\u05d2def</div>'
+      : '<div contenteditable dir="ltr"><span>abc</span>\u05D0\u05D1\u05D2|def</div>',
   '11-6 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_11_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_11_rtl.html
index f2df883..a816397a 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_11_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_11_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|<span>abc</span>\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|<span>abc</span>\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>a|bc</span>\u05d0\u05d1\u05d2def</div>'
+      : '<div contenteditable dir="rtl">|<span>abc</span>\u05D0\u05D1\u05D2def</div>',
   '11-0 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span>|abc</span>\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span>|abc</span>\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>a|bc</span>\u05d0\u05d1\u05d2def</div>'
+      : '<div contenteditable dir="rtl"><span>|abc</span>\u05D0\u05D1\u05D2def</div>',
   '11-1 rtl right character');
 
 selection_test(
@@ -24,7 +30,9 @@
 selection_test(
   '<div contenteditable dir="rtl"><span>ab|c</span>\u05D0\u05D1\u05D2def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span>|abc</span>\u05D0\u05D1\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>abc|</span>\u05d0\u05d1\u05d2def</div>'
+      : '<div contenteditable dir="rtl"><span>|abc</span>\u05D0\u05D1\u05D2def</div>',
   '11-3 rtl right character');
 
 selection_test(
@@ -54,7 +62,9 @@
 selection_test(
   '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2|def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1|\u05D2def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>abc</span>\u05d0\u05d1\u05d2d|ef</div>'
+      : '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1|\u05D2def</div>',
   '11-8 rtl right character');
 
 selection_test(
@@ -66,12 +76,16 @@
 selection_test(
   '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2de|f</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2|def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>abc</span>\u05d0\u05d1\u05d2def|</div>'
+      : '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2|def</div>',
   '11-10 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2def|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2d|ef</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>abc</span>\u05d0\u05d1|\u05d2def</div>'
+      : '<div contenteditable dir="rtl"><span>abc</span>\u05D0\u05D1\u05D2d|ef</div>',
   '11-11 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_12_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_12_ltr.html
index 54a3b2e5..b75e5a0b 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_12_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_12_ltr.html
@@ -3,22 +3,30 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|<span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span>\u05D0\u05D1|\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>\u05d0\u05d1\u05d2</span>a|bc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr"><span>\u05D0\u05D1|\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   '12-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span>|\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span>\u05D0\u05D1|\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>\u05d0\u05d1\u05d2</span>a|bc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr"><span>\u05D0\u05D1|\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   '12-1 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span>\u05D0|\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2|</span>abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>|\u05d0\u05d1\u05d2</span>abc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2|</span>abc\u05D3\u05D4\u05D5</div>',
   '12-2 ltr right character');
 
 selection_test(
@@ -54,13 +62,17 @@
 selection_test(
   '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc|\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4|\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>\u05d0\u05d1\u05d2</span>abc|\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4|\u05D5</div>',
   '12-8 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc\u05D3|\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>\u05d0\u05d1\u05d2</span>abc|\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5|</div>',
   '12-9 ltr right character');
 
 selection_test(
@@ -72,6 +84,8 @@
 selection_test(
   '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span>\u05d0\u05d1\u05d2</span>abc\u05d3\u05d4|\u05d5</div>'
+      : '<div contenteditable dir="ltr"><span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5|</div>',
   '12-11 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_12_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_12_rtl.html
index 505d755..daf4853 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_12_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_12_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|<span>\u05D0\u05D1\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -30,13 +32,17 @@
 selection_test(
   '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2|</span>abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span>\u05D0\u05D1|\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>\u05d0\u05d1\u05d2</span>a|bc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl"><span>\u05D0\u05D1|\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   '12-4 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2</span>|abc\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span>\u05D0\u05D1|\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>\u05d0\u05d1\u05d2</span>a|bc\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl"><span>\u05D0\u05D1|\u05D2</span>abc\u05D3\u05D4\u05D5</div>',
   '12-5 rtl right character');
 
 selection_test(
@@ -48,7 +54,9 @@
 selection_test(
   '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2</span>ab|c\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2|</span>abc\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span>\u05d0\u05d1\u05d2</span>abc|\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl"><span>\u05D0\u05D1\u05D2|</span>abc\u05D3\u05D4\u05D5</div>',
   '12-7 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_13_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_13_ltr.html
index 89bb0e5..60ec21d 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_13_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_13_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123\u05D3\u05D4|\u05D5def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5d|ef</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123\u05D3\u05D4|\u05D5def</div>',
   '13-3 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2123\u05D3\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5|def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc|\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5def</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5|def</div>',
   '13-4 ltr right character');
 
 selection_test(
@@ -60,13 +66,17 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123|\u05D3\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2123\u05D3\u05D4\u05D5def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d21|23\u05d3\u05d4\u05d5def</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2123\u05D3\u05D4\u05D5def</div>',
   '13-9 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123\u05D3|\u05D4\u05D5def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2|123\u05D3\u05D4\u05D5def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2123|\u05d3\u05d4\u05d5def</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2|123\u05D3\u05D4\u05D5def</div>',
   '13-10 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_15_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_15_ltr.html
index ada28691..17857fe6 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_15_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_15_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2123def</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2123def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D21|23def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2123d|ef</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D21|23def</div>',
   '15-3 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2123def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123|def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc|\u05d0\u05d1\u05d2123def</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2123|def</div>',
   '15-4 ltr right character');
 
 selection_test(
@@ -54,7 +60,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D212|3def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2123def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2123|def</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2123def</div>',
   '15-8 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_15_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_15_rtl.html
index 20409a3..e4150c7 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_15_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_15_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2123def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2123def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">a|bc\u05d0\u05d1\u05d2123def</div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2123def</div>',
   '15-0 rtl right character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2123def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2123def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc|\u05d0\u05d1\u05d2123def</div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2123def</div>',
   '15-2 rtl right character');
 
 selection_test(
@@ -42,7 +48,9 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|123def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1|\u05D2123def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d21|23def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1|\u05D2123def</div>',
   '15-6 rtl right character');
 
 selection_test(
@@ -72,12 +80,16 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123de|f</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|123def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1\u05d2123def|</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|123def</div>',
   '15-11 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2123def|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D21|23def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0\u05d1|\u05d2123def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D21|23def</div>',
   '15-12 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_16_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_16_ltr.html
index 4f31a21..54c0562 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_16_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_16_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4|\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5a|bc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4|\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5|abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5|abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-1 ltr right character');
 
 selection_test(
@@ -42,13 +48,17 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123|\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d21|23\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-6 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3|\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123|\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-7 ltr right character');
 
 selection_test(
@@ -78,13 +88,17 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc|\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB|\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc|\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB|\u05DC</div>',
   '16-12 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6|\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc|\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC|</div>',
   '16-13 ltr right character');
 
 selection_test(
@@ -114,13 +128,17 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456|\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7|\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d84|56\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7|\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-18 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9|\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8|456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456|\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8|456\u05D9\u05DB\u05DC</div>',
   '16-19 ltr right character');
 
 selection_test(
@@ -132,6 +150,8 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9\u05db|\u05dc</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC|</div>',
   '16-21 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_16_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_16_rtl.html
index 12f1c09..167d9c0 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_16_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_16_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d21|23\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-3 rtl right character');
 
 selection_test(
@@ -36,7 +40,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D212|3\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2123|\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-5 rtl right character');
 
 selection_test(
@@ -60,7 +66,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5|abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4|\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5a|bc\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4|\u05D5abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-9 rtl right character');
 
 selection_test(
@@ -72,7 +80,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5ab|c\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5|abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc|\u05d6\u05d7\u05d8456\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5|abc\u05D6\u05D7\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-11 rtl right character');
 
 selection_test(
@@ -96,7 +106,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8|456\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7|\u05D8456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d84|56\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7|\u05D8456\u05D9\u05DB\u05DC</div>',
   '16-15 rtl right character');
 
 selection_test(
@@ -108,7 +120,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D845|6\u05D9\u05DB\u05DC</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8|456\u05D9\u05DB\u05DC</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2123\u05d3\u05d4\u05d5abc\u05d6\u05d7\u05d8456|\u05d9\u05db\u05dc</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2123\u05D3\u05D4\u05D5abc\u05D6\u05D7\u05D8|456\u05D9\u05DB\u05DC</div>',
   '16-17 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_25_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_25_ltr.html
index cc5f792..bd7940c9 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_25_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_25_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -42,13 +44,17 @@
 selection_test(
   '<div contenteditable dir="ltr">abcdef|\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4|\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abcdef|\u05d0\u05d1\u05d2<img>\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4|\u05D5</div>',
   '25-6 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abcdef\u05D0|\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abcdef|\u05d0\u05d1\u05d2<img>\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="ltr">abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5|</div>',
   '25-7 ltr right character');
 
 selection_test(
@@ -84,6 +90,8 @@
 selection_test(
   '<div contenteditable dir="ltr">abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abcdef\u05d0\u05d1\u05d2<img>\u05d3\u05d4|\u05d5</div>'
+      : '<div contenteditable dir="ltr">abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5|</div>',
   '25-13 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_25_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_25_rtl.html
index 29e6ad6..49f47e93 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_25_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_25_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">a|bcdef\u05d0\u05d1\u05d2<img>\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl">|abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   '25-0 rtl right character');
 
 selection_test(
@@ -36,7 +40,9 @@
 selection_test(
   '<div contenteditable dir="rtl">abcde|f\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abcdef|\u05d0\u05d1\u05d2<img>\u05d3\u05d4\u05d5</div>'
+      : '<div contenteditable dir="rtl">|abcdef\u05D0\u05D1\u05D2<img>\u05D3\u05D4\u05D5</div>',
   '25-5 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_26_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_26_ltr.html
index 2066a0fc..63be6d4 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_26_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_26_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2\u05D3\u05D4|\u05D5abc<img>def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5a|bc<img>def</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2\u05D3\u05D4|\u05D5abc<img>def</div>',
   '26-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5|abc<img>def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5abc<img>def</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5|abc<img>def</div>',
   '26-1 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_26_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_26_rtl.html
index 0495298..8f1732f 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_26_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_26_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -42,7 +44,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5|abc<img>def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4|\u05D5abc<img>def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5a|bc<img>def</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4|\u05D5abc<img>def</div>',
   '26-6 rtl right character');
 
 selection_test(
@@ -78,12 +82,16 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>de|f</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5|abc<img>def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5abc<img>def|</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5|abc<img>def</div>',
   '26-12 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5abc<img>def|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5a|bc<img>def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2\u05d3\u05d4|\u05d5abc<img>def</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5a|bc<img>def</div>',
   '26-13 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_27_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_27_ltr.html
index b0533f9..6e21005 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_27_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_27_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -30,13 +32,17 @@
 selection_test(
   '<div contenteditable dir="ltr">abc<input>|\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4|\u05D5ghi</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5g|hi</div>'
+      : '<div contenteditable dir="ltr">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4|\u05D5ghi</div>',
   '27-4 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc<input>\u05D0|\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5|ghi</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc<input>|\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>'
+      : '<div contenteditable dir="ltr">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5|ghi</div>',
   '27-5 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_27_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_27_rtl.html
index 88baf52c..ecada0b 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_27_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_27_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">a|bc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>'
+      : '<div contenteditable dir="rtl">|abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   '27-0 rtl right character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="rtl">ab|c<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc|<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>'
+      : '<div contenteditable dir="rtl">|abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi</div>',
   '27-2 rtl right character');
 
 selection_test(
@@ -78,7 +84,9 @@
 selection_test(
   '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5|ghi</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4|\u05D5ghi</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5g|hi</div>'
+      : '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4|\u05D5ghi</div>',
   '27-12 rtl right character');
 
 selection_test(
@@ -90,12 +98,16 @@
 selection_test(
   '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5gh|i</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5|ghi</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi|</div>'
+      : '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5|ghi</div>',
   '27-14 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5ghi|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5g|hi</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4|\u05D5ghi</div>'
+      : '<div contenteditable dir="rtl">abc<input>\u05D0\u05D1\u05D2<img><img>\u05D3\u05D4\u05D5g|hi</div>',
   '27-15 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_28_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_28_ltr.html
index 791ddf5..6c7d611 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_28_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_28_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>|abc<img><img>def\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
   '28-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
   '28-1 ltr right character');
 
 selection_test(
@@ -78,13 +84,17 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def|\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4|\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def|\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4|\u05D5</div>',
   '28-12 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3|\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def|\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5|</div>',
   '28-13 ltr right character');
 
 selection_test(
@@ -96,6 +106,8 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4|\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5|</div>',
   '28-15 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_28_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_28_rtl.html
index ced055f..ba04228 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_28_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_28_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -30,7 +32,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>|abc<img><img>def\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>a|bc<img><img>def\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|<input>abc<img><img>def\u05D3\u05D4\u05D5</div>',
   '28-4 rtl right character');
 
 selection_test(
@@ -72,7 +76,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>abc<img><img>de|f\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>|abc<img><img>def\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>abc<img><img>def|\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<input>|abc<img><img>def\u05D3\u05D4\u05D5</div>',
   '28-11 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_29_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_29_ltr.html
index 21dbb64..2733815c 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_29_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_29_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4|\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc|\u05d0\u05d1\u05d2<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4|\u05D5</span></div>',
   '29-3 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc|\u05d0\u05d1\u05d2<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5|</span></div>',
   '29-4 ltr right character');
 
 selection_test(
@@ -66,12 +72,16 @@
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5|</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2<span>\u05d3\u05d4|\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5|</span></div>',
   '29-10 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span>|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span>|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2<span>\u05d3\u05d4|\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span>|</div>',
   '29-11 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_29_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_29_rtl.html
index 8b1eb81d..186577b 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_29_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_29_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">a|bc\u05d0\u05d1\u05d2<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   '29-0 rtl right character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc|\u05d0\u05d1\u05d2<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>\u05D3\u05D4\u05D5</span></div>',
   '29-2 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_30_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_30_ltr.html
index 431ec56..6b47898 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_30_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_30_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc<span>def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2abc<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a|bc<span>def</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2abc<span>def</span></div>',
   '30-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2abc<span>def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|abc<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05d0\u05d1\u05d2abc<span>def</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|abc<span>def</span></div>',
   '30-1 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_30_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_30_rtl.html
index 0de7dbc..ebb4828 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_30_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_30_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2abc<span>def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc<span>def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2abc<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2a|bc<span>def</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2abc<span>def</span></div>',
   '30-3 rtl right character');
 
 selection_test(
@@ -60,18 +64,24 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>de|f</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2abc<span>def|</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc<span>def</span></div>',
   '30-9 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>def|</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|bc<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1|\u05d2abc<span>def</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|bc<span>def</span></div>',
   '30-10 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc<span>def</span>|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|bc<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1|\u05d2abc<span>def</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|bc<span>def</span></div>',
   '30-11 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_31_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_31_ltr.html
index 46a7575..15cbf6d8 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_31_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_31_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|ab<span>c\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -30,13 +32,17 @@
 selection_test(
   '<div contenteditable dir="ltr">ab<span>c|\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">ab<span>c\u05D0\u05D1|\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">ab<span>c\u05d0\u05d1\u05d2d|ef</span></div>'
+      : '<div contenteditable dir="ltr">ab<span>c\u05D0\u05D1|\u05D2def</span></div>',
   '31-4 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">ab<span>c\u05D0|\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">ab<span>c\u05D0\u05D1\u05D2|def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">ab<span>c|\u05d0\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="ltr">ab<span>c\u05D0\u05D1\u05D2|def</span></div>',
   '31-5 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_31_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_31_rtl.html
index bd72796..1316316 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_31_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_31_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|ab<span>c\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|ab<span>c\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">a|b<span>c\u05d0\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">|ab<span>c\u05D0\u05D1\u05D2def</span></div>',
   '31-0 rtl right character');
 
 selection_test(
@@ -18,13 +22,17 @@
 selection_test(
   '<div contenteditable dir="rtl">ab|<span>c\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|ab<span>c\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">ab<span>c|\u05d0\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">|ab<span>c\u05D0\u05D1\u05D2def</span></div>',
   '31-2 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">ab<span>|c\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|ab<span>c\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">ab<span>c|\u05d0\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">|ab<span>c\u05D0\u05D1\u05D2def</span></div>',
   '31-3 rtl right character');
 
 selection_test(
@@ -48,7 +56,9 @@
 selection_test(
   '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2|def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1|\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">ab<span>c\u05d0\u05d1\u05d2d|ef</span></div>'
+      : '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1|\u05D2def</span></div>',
   '31-7 rtl right character');
 
 selection_test(
@@ -60,18 +70,24 @@
 selection_test(
   '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2de|f</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2|def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">ab<span>c\u05d0\u05d1\u05d2def|</span></div>'
+      : '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2|def</span></div>',
   '31-9 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2def|</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2d|ef</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">ab<span>c\u05d0\u05d1|\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2d|ef</span></div>',
   '31-10 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2def</span>|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2d|ef</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">ab<span>c\u05d0\u05d1|\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">ab<span>c\u05D0\u05D1\u05D2d|ef</span></div>',
   '31-11 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_32_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_32_ltr.html
index 08c56a6..f2133ff 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_32_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_32_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1|<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1<span>\u05d2a|bc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1|<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
   '32-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0|\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2|abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05d0\u05d1<span>\u05d2abc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2|abc\u05D3\u05D4\u05D5</span></div>',
   '32-1 ltr right character');
 
 selection_test(
@@ -48,13 +54,17 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc|\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4|\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1<span>\u05d2abc|\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4|\u05D5</span></div>',
   '32-7 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3|\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1<span>\u05d2abc|\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5|</span></div>',
   '32-8 ltr right character');
 
 selection_test(
@@ -66,12 +76,16 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5|</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1<span>\u05d2abc\u05d3\u05d4|\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5|</span></div>',
   '32-10 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span>|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span>|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1<span>\u05d2abc\u05d3\u05d4|\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span>|</div>',
   '32-11 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_32_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_32_rtl.html
index 37975dc1..44ed480 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_32_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_32_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -30,7 +32,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1<span>\u05D2|abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1|<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1<span>\u05d2a|bc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1|<span>\u05D2abc\u05D3\u05D4\u05D5</span></div>',
   '32-4 rtl right character');
 
 selection_test(
@@ -42,7 +46,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1<span>\u05D2ab|c\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1<span>\u05D2|abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1<span>\u05d2abc|\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1<span>\u05D2|abc\u05D3\u05D4\u05D5</span></div>',
   '32-6 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_33_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_33_ltr.html
index 7aae4f2..157c390 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_33_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_33_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc<span>\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,19 +26,25 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|<span>\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc<span>\u05D0\u05D1|\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc<span>\u05d0\u05d1\u05d2d|ef</span></div>'
+      : '<div contenteditable dir="ltr">abc<span>\u05D0\u05D1|\u05D2def</span></div>',
   '33-3 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc<span>|\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc<span>\u05D0\u05D1|\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc<span>\u05d0\u05d1\u05d2d|ef</span></div>'
+      : '<div contenteditable dir="ltr">abc<span>\u05D0\u05D1|\u05D2def</span></div>',
   '33-4 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc<span>\u05D0|\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc<span>\u05D0\u05D1\u05D2|def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc|<span>\u05d0\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="ltr">abc<span>\u05D0\u05D1\u05D2|def</span></div>',
   '33-5 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_33_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_33_rtl.html
index 839ad23..0e8d3bd 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_33_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_33_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc<span>\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc<span>\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">a|bc<span>\u05d0\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">|abc<span>\u05D0\u05D1\u05D2def</span></div>',
   '33-0 rtl right character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="rtl">ab|c<span>\u05D0\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc<span>\u05D0\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc|<span>\u05d0\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">|abc<span>\u05D0\u05D1\u05D2def</span></div>',
   '33-2 rtl right character');
 
 selection_test(
@@ -48,7 +54,9 @@
 selection_test(
   '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2|def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1|\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<span>\u05d0\u05d1\u05d2d|ef</span></div>'
+      : '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1|\u05D2def</span></div>',
   '33-7 rtl right character');
 
 selection_test(
@@ -60,18 +68,24 @@
 selection_test(
   '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2de|f</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2|def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<span>\u05d0\u05d1\u05d2def|</span></div>'
+      : '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2|def</span></div>',
   '33-9 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2def|</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2d|ef</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<span>\u05d0\u05d1|\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2d|ef</span></div>',
   '33-10 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2def</span>|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2d|ef</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc<span>\u05d0\u05d1|\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">abc<span>\u05D0\u05D1\u05D2d|ef</span></div>',
   '33-11 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_34_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_34_ltr.html
index f251a394..d71e013 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_34_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_34_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2<span>a|bc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
   '34-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|<span>abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05d0\u05d1\u05d2<span>abc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|<span>abc\u05D3\u05D4\u05D5</span></div>',
   '34-1 ltr right character');
 
 selection_test(
@@ -48,13 +54,17 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc|\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4|\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2<span>abc|\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4|\u05D5</span></div>',
   '34-7 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3|\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2<span>abc|\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5|</span></div>',
   '34-8 ltr right character');
 
 selection_test(
@@ -66,12 +76,16 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5|</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2<span>abc\u05d3\u05d4|\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5|</span></div>',
   '34-10 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span>|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span>|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2<span>abc\u05d3\u05d4|\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span>|</div>',
   '34-11 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_34_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_34_rtl.html
index 6c19fbc..c70b477 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_34_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_34_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|<span>abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2<span>a|bc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
   '34-3 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<span>|abc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2<span>a|bc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2<span>abc\u05D3\u05D4\u05D5</span></div>',
   '34-4 rtl right character');
 
 selection_test(
@@ -42,7 +48,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2<span>ab|c\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|<span>abc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2<span>abc|\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|<span>abc\u05D3\u05D4\u05D5</span></div>',
   '34-6 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_35_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_35_ltr.html
index 04f68ffc..ae73a45 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_35_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_35_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0def</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0|def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05D0d|ef</div>'
+      : '<div contenteditable dir="ltr">abc\u05D0|def</div>',
   '35-3 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_35_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_35_rtl.html
index 9cefcfdd..dde1002 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_35_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_35_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">a|bc\u05D0def</div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0def</div>',
   '35-0 rtl right character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="rtl">ab|c\u05D0def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc|\u05D0def</div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0def</div>',
   '35-2 rtl right character');
 
 selection_test(
@@ -30,7 +36,9 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0|def</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc|\u05D0def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0d|ef</div>'
+      : '<div contenteditable dir="rtl">abc|\u05D0def</div>',
   '35-4 rtl right character');
 
 selection_test(
@@ -42,12 +50,16 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0de|f</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0|def</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0def|</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0|def</div>',
   '35-6 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0def|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0d|ef</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc|\u05D0def</div>'
+      : '<div contenteditable dir="rtl">abc\u05D0d|ef</div>',
   '35-7 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_36_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_36_ltr.html
index 824e5ab1..c50c1f4 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_36_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_36_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2a\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a|\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2a\u05D3\u05D4\u05D5</div>',
   '36-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2a\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|a\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|a\u05D3\u05D4\u05D5</div>',
   '36-1 ltr right character');
 
 selection_test(
@@ -30,13 +36,17 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a|\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3\u05D4|\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a|\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3\u05D4|\u05D5</div>',
   '36-4 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3|\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a|\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5|</div>',
   '36-5 ltr right character');
 
 selection_test(
@@ -48,6 +58,8 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3\u05D4|\u05D5</div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5|</div>',
   '36-7 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_36_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_36_rtl.html
index e76cca2..9d2b8f1 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_36_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_36_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2a\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|a\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2a\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2a\u05D3\u05D4\u05D5</div>',
   '36-3 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|\u05D3\u05D4\u05D5</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|a\u05D3\u05D4\u05D5</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|\u05D3\u05D4\u05D5</div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|a\u05D3\u05D4\u05D5</div>',
   '36-4 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_37_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_37_ltr.html
index b697598..5979377 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_37_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_37_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0\u05D1\u05D2<span>def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,13 +26,17 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0\u05D1\u05D2<span>def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0\u05d1\u05d2<span>d|ef</span></div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1|\u05D2<span>def</span></div>',
   '37-3 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0|\u05D1\u05D2<span>def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2|<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc|\u05d0\u05d1\u05d2<span>def</span></div>'
+      : '<div contenteditable dir="ltr">abc\u05D0\u05D1\u05D2|<span>def</span></div>',
   '37-4 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_37_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_37_rtl.html
index 7e15184f..49cd57e 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_37_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_37_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">a|bc\u05D0\u05D1\u05D2<span>def</span></div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>def</span></div>',
   '37-0 rtl right character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="rtl">ab|c\u05D0\u05D1\u05D2<span>def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc|\u05D0\u05D1\u05D2<span>def</span></div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0\u05D1\u05D2<span>def</span></div>',
   '37-2 rtl right character');
 
 selection_test(
@@ -42,13 +48,17 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|<span>def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1|\u05D2<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>d|ef</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1|\u05D2<span>def</span></div>',
   '37-6 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>|def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1|\u05D2<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>d|ef</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1|\u05D2<span>def</span></div>',
   '37-7 rtl right character');
 
 selection_test(
@@ -60,18 +70,24 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>de|f</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|<span>def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>def|</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2|<span>def</span></div>',
   '37-9 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>def|</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>d|ef</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1|\u05D2<span>def</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>d|ef</span></div>',
   '37-10 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>def</span>|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>d|ef</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05D0\u05D1|\u05D2<span>def</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0\u05D1\u05D2<span>d|ef</span></div>',
   '37-11 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_38_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_38_ltr.html
index 4b26c7806..902a38f 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_38_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_38_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a|bc<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
   '38-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|abc<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05d0\u05d1\u05d2abc<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|abc<span>\u05D3\u05D4\u05D5</span></div>',
   '38-1 ltr right character');
 
 selection_test(
@@ -42,19 +48,25 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc|<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4|\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc|<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4|\u05D5</span></div>',
   '38-6 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>|\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4|\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc<span>|\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4|\u05D5</span></div>',
   '38-7 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3|\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc|<span>\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5|</span></div>',
   '38-8 ltr right character');
 
 selection_test(
@@ -66,12 +78,16 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5|</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc<span>\u05d3\u05d4|\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5|</span></div>',
   '38-10 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span>|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span>|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2abc<span>\u05d3\u05d4|\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span>|</div>',
   '38-11 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_38_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_38_rtl.html
index 1589445e..48a0d29a 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_38_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_38_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a|bc<span>\u05D3\u05D4\u05D5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2abc<span>\u05D3\u05D4\u05D5</span></div>',
   '38-3 rtl right character');
 
 selection_test(
@@ -36,7 +40,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2ab|c<span>\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc<span>\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2abc|<span>\u05D3\u05D4\u05D5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|abc<span>\u05D3\u05D4\u05D5</span></div>',
   '38-5 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_39_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_39_ltr.html
index c4d86a7b..face355f 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_39_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_39_ltr.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|abc\u05D0<span>\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,19 +26,25 @@
 selection_test(
   '<div contenteditable dir="ltr">abc|\u05D0<span>\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0<span>\u05D1|\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc\u05d0<span>\u05d1\u05d2d|ef</span></div>'
+      : '<div contenteditable dir="ltr">abc\u05D0<span>\u05D1|\u05D2def</span></div>',
   '39-3 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0|<span>\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0<span>\u05D1\u05D2|def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc|\u05d0<span>\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="ltr">abc\u05D0<span>\u05D1\u05D2|def</span></div>',
   '39-4 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">abc\u05D0<span>|\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">abc\u05D0<span>\u05D1\u05D2|def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">abc|\u05d0<span>\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="ltr">abc\u05D0<span>\u05D1\u05D2|def</span></div>',
   '39-5 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_39_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_39_rtl.html
index ea82c85..0170d4f 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_39_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_39_rtl.html
@@ -3,10 +3,14 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|abc\u05D0<span>\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0<span>\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">a|bc\u05d0<span>\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0<span>\u05D1\u05D2def</span></div>',
   '39-0 rtl right character');
 
 selection_test(
@@ -18,7 +22,9 @@
 selection_test(
   '<div contenteditable dir="rtl">ab|c\u05D0<span>\u05D1\u05D2def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|abc\u05D0<span>\u05D1\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc|\u05d0<span>\u05d1\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">|abc\u05D0<span>\u05D1\u05D2def</span></div>',
   '39-2 rtl right character');
 
 selection_test(
@@ -48,7 +54,9 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2|def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1|\u05D2def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0<span>\u05d1\u05d2d|ef</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1|\u05D2def</span></div>',
   '39-7 rtl right character');
 
 selection_test(
@@ -60,18 +68,24 @@
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2de|f</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2|def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0<span>\u05d1\u05d2def|</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2|def</span></div>',
   '39-9 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2def|</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2d|ef</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0<span>\u05d1|\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2d|ef</span></div>',
   '39-10 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2def</span>|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2d|ef</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">abc\u05d0<span>\u05d1|\u05d2def</span></div>'
+      : '<div contenteditable dir="rtl">abc\u05D0<span>\u05D1\u05D2d|ef</span></div>',
   '39-11 rtl right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_40_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_40_ltr.html
index dc60dc9..5dcd3968 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_40_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_40_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a|<span>bc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1|\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
   '40-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0|\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|a<span>bc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">|\u05d0\u05d1\u05d2a<span>bc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2|a<span>bc\u05D3\u05D4\u05D5</span></div>',
   '40-1 ltr right character');
 
 selection_test(
@@ -48,13 +54,17 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc|\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4|\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a<span>bc|\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4|\u05D5</span></div>',
   '40-7 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3|\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a<span>bc|\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5|</span></div>',
   '40-8 ltr right character');
 
 selection_test(
@@ -66,12 +76,16 @@
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5|</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a<span>bc\u05d3\u05d4|\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5|</span></div>',
   '40-10 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span>|</div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span>|</div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr">\u05d0\u05d1\u05d2a<span>bc\u05d3\u05d4|\u05d5</span></div>'
+      : '<div contenteditable dir="ltr">\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span>|</div>',
   '40-11 ltr right character');
 </script>
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_40_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_40_rtl.html
index deca0f5..639edfb 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_40_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_40_rtl.html
@@ -3,6 +3,8 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|\u05D0\u05D1\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
@@ -24,7 +26,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|a<span>bc\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2a|<span>bc\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1|\u05D2a<span>bc\u05D3\u05D4\u05D5</span></div>',
   '40-3 rtl right character');
 
 selection_test(
@@ -42,7 +46,9 @@
 selection_test(
   '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2a<span>b|c\u05D3\u05D4\u05D5</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|a<span>bc\u05D3\u05D4\u05D5</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl">\u05d0\u05d1\u05d2a<span>bc|\u05d3\u05d4\u05d5</span></div>'
+      : '<div contenteditable dir="rtl">\u05D0\u05D1\u05D2|a<span>bc\u05D3\u05D4\u05D5</span></div>',
   '40-6 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_43_ltr.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_43_ltr.html
index 156f1a6..46d056a 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_43_ltr.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_43_ltr.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="ltr">|<span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0d|ef</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">a|bc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0d|ef</span></div>',
   '43-0 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0d|ef</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">a|bc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0d|ef</span></div>',
   '43-1 ltr right character');
 
 selection_test(
@@ -24,19 +30,25 @@
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">ab|c\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def|</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">abc|\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def|</span></div>',
   '43-3 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">abc|\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">abc|\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">a|bc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">abc|\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-4 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7|\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">a|bc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">abc|\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">a|bc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-5 ltr right character');
 
 selection_test(
@@ -66,13 +78,17 @@
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123|\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7|\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E71|23\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7|\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-10 ltr right character');
 
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0|\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7|123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123|\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7|123\u05E0\u05E0\u05E0def</span></div>',
   '43-11 ltr right character');
 
 selection_test(
@@ -96,7 +112,9 @@
 selection_test(
   '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0de|f</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0|\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def|</span></div>'
+      : '<div contenteditable dir="ltr"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0|\u05E0def</span></div>',
   '43-15 ltr right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_43_rtl.html b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_43_rtl.html
index e7dace23..a811317 100644
--- a/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_43_rtl.html
+++ b/third_party/blink/web_tests/editing/selection/modify_move/move_right_character_43_rtl.html
@@ -3,16 +3,22 @@
 <script src="../../../resources/testharnessreport.js"></script>
 <script src="../../assert_selection.js"></script>
 <script>
+const usesBidiAffinity = window.internals && internals.runtimeFlags.bidiCaretAffinityEnabled;
+
 selection_test(
   '<div contenteditable dir="rtl">|<span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl">|<span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">a|bc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="rtl">|<span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-0 rtl right character');
 
 selection_test(
   '<div contenteditable dir="rtl"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">a|bc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-1 rtl right character');
 
 selection_test(
@@ -24,7 +30,9 @@
 selection_test(
   '<div contenteditable dir="rtl"><span dir="rtl">ab|c\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">abc|\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">|abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-3 rtl right character');
 
 selection_test(
@@ -48,7 +56,9 @@
 selection_test(
   '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7|123\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7|\u05E7123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E71|23\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7|\u05E7123\u05E0\u05E0\u05E0def</span></div>',
   '43-7 rtl right character');
 
 selection_test(
@@ -60,7 +70,9 @@
 selection_test(
   '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E712|3\u05E0\u05E0\u05E0def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7|123\u05E0\u05E0\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123|\u05E0\u05E0\u05E0def</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7|123\u05E0\u05E0\u05E0def</span></div>',
   '43-9 rtl right character');
 
 selection_test(
@@ -84,7 +96,9 @@
 selection_test(
   '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0|def</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0|\u05E0def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0d|ef</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0|\u05E0def</span></div>',
   '43-13 rtl right character');
 
 selection_test(
@@ -96,7 +110,9 @@
 selection_test(
   '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0de|f</span></div>',
   selection => selection.modify('move', 'right', 'character'),
-  '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0|def</span></div>',
+  usesBidiAffinity
+      ? '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0def|</span></div>'
+      : '<div contenteditable dir="rtl"><span dir="rtl">abc\u05E7\u05E7\u05E7123\u05E0\u05E0\u05E0|def</span></div>',
   '43-15 rtl right character');
 
 selection_test(
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index 6df4710..403f4737 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -115082,6 +115082,11 @@
      {}
     ]
    ],
+   "animation-worklet/common.js": [
+    [
+     {}
+    ]
+   ],
    "animation-worklet/idlharness.any-expected.txt": [
     [
      {}
@@ -151562,6 +151567,11 @@
      {}
     ]
    ],
+   "css/mediaqueries/prefers-color-scheme-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/mediaqueries/reference/ref-green-body.xht": [
     [
      {}
@@ -156157,6 +156167,26 @@
      {}
     ]
    ],
+   "encoding/idlharness.any-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "encoding/idlharness.any.serviceworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "encoding/idlharness.any.sharedworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "encoding/idlharness.any.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "encoding/iso-2022-jp-decoder-expected.txt": [
     [
      {}
@@ -175667,6 +175697,26 @@
      {}
     ]
    ],
+   "performance-timeline/idlharness.any-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "performance-timeline/idlharness.any.serviceworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "performance-timeline/idlharness.any.sharedworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "performance-timeline/idlharness.any.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "performance-timeline/performanceobservers.js": [
     [
      {}
@@ -185092,6 +185142,26 @@
      {}
     ]
    ],
+   "user-timing/idlharness.any-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "user-timing/idlharness.any.serviceworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "user-timing/idlharness.any.sharedworker-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "user-timing/idlharness.any.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "user-timing/resources/webperftestharness.js": [
     [
      {}
@@ -199982,6 +200052,12 @@
      {}
     ]
    ],
+   "animation-worklet/worklet-animation-with-fill-mode.https.html": [
+    [
+     "/animation-worklet/worklet-animation-with-fill-mode.https.html",
+     {}
+    ]
+   ],
    "apng/supported-in-source-type.html": [
     [
      "/apng/supported-in-source-type.html",
@@ -202204,6 +202280,12 @@
      {}
     ]
    ],
+   "content-security-policy/form-action/form-action-src-javascript-prevented.html": [
+    [
+     "/content-security-policy/form-action/form-action-src-javascript-prevented.html",
+     {}
+    ]
+   ],
    "content-security-policy/form-action/form-action-src-redirect-allowed-target-blank.sub.html": [
     [
      "/content-security-policy/form-action/form-action-src-redirect-allowed-target-blank.sub.html",
@@ -207310,6 +207392,18 @@
      {}
     ]
    ],
+   "css/css-flexbox/hittest-overlapping-margin.html": [
+    [
+     "/css/css-flexbox/hittest-overlapping-margin.html",
+     {}
+    ]
+   ],
+   "css/css-flexbox/hittest-overlapping-relative.html": [
+    [
+     "/css/css-flexbox/hittest-overlapping-relative.html",
+     {}
+    ]
+   ],
    "css/css-flexbox/order_value.html": [
     [
      "/css/css-flexbox/order_value.html",
@@ -217900,6 +217994,12 @@
      {}
     ]
    ],
+   "css/mediaqueries/prefers-color-scheme.html": [
+    [
+     "/css/mediaqueries/prefers-color-scheme.html",
+     {}
+    ]
+   ],
    "css/mediaqueries/test_media_queries.html": [
     [
      "/css/mediaqueries/test_media_queries.html",
@@ -282291,7 +282391,9 @@
    "webrtc/RTCRtpReceiver-getSynchronizationSources.https.html": [
     [
      "/webrtc/RTCRtpReceiver-getSynchronizationSources.https.html",
-     {}
+     {
+      "timeout": "long"
+     }
     ]
    ],
    "webrtc/RTCRtpSender-getCapabilities.html": [
@@ -286943,7 +287045,9 @@
    "worklets/layout-worklet-csp.https.html": [
     [
      "/worklets/layout-worklet-csp.https.html",
-     {}
+     {
+      "timeout": "long"
+     }
     ]
    ],
    "worklets/layout-worklet-import.https.html": [
@@ -302923,6 +303027,10 @@
    "c6918965843779e02522af1abf48acda8d0a128b",
    "support"
   ],
+  "animation-worklet/common.js": [
+   "933e6b4eab9a31b3d3606e00f5ff39cb48ba07d0",
+   "support"
+  ],
   "animation-worklet/idlharness.any-expected.txt": [
    "8bf12c874d83a6697102753d2cba6fc5745e69b2",
    "support"
@@ -302935,6 +303043,10 @@
    "f73208c2a3c700d03ea1d39322a3e061b5caa5f7",
    "support"
   ],
+  "animation-worklet/worklet-animation-with-fill-mode.https.html": [
+   "90abe0828578929f129313d8054ef7364a420a42",
+   "testharness"
+  ],
   "apng/META.yml": [
    "a660c7e19ebdcb13e7457ea0bb5729e18bfe2e0e",
    "support"
@@ -305071,6 +305183,10 @@
    "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
    "support"
   ],
+  "content-security-policy/form-action/form-action-src-javascript-prevented.html": [
+   "feae47ee796fd78124a0c021d347662f0bc6ee19",
+   "testharness"
+  ],
   "content-security-policy/form-action/form-action-src-redirect-allowed-target-blank.sub.html": [
    "41c68b68f9e661ebe9eb56aae2c04d7127880a3c",
    "testharness"
@@ -334083,6 +334199,14 @@
    "9f94febe7749dbe3d5b1e8cb9a2928c48bd84e92",
    "testharness"
   ],
+  "css/css-flexbox/hittest-overlapping-margin.html": [
+   "5c3601d96aeb42694c66aaff0845c1029474cda1",
+   "testharness"
+  ],
+  "css/css-flexbox/hittest-overlapping-relative.html": [
+   "985ac1aed565c3cc146a46f7a4350ac2fd985179",
+   "testharness"
+  ],
   "css/css-flexbox/interactive/flexbox_interactive_break-after-column-item.html": [
    "f015d29f7cdea9307cb49248e1e1ad7f441da8b7",
    "manual"
@@ -342520,11 +342644,11 @@
    "testharness"
   ],
   "css/css-grid/abspos/grid-positioned-items-padding-001.html": [
-   "4941d77a71af73c5eb2c70a14629596aacdcf767",
+   "643a68830d4aecdad182f40d8b74fb8470da8b54",
    "testharness"
   ],
   "css/css-grid/abspos/grid-positioned-items-unknown-named-grid-line-001.html": [
-   "24d9d769f99079d19519d05f82e8fd637906fe8a",
+   "6e61f7c2de36a1d799aba0de6d10c52147d406d6",
    "testharness"
   ],
   "css/css-grid/abspos/grid-positioned-items-within-grid-implicit-track-001.html": [
@@ -378067,6 +378191,14 @@
    "d039281c0254e2bb794e229baecbe4d39c547baa",
    "reftest"
   ],
+  "css/mediaqueries/prefers-color-scheme-expected.txt": [
+   "600af2aac02524bf8604714923dd3417df96c418",
+   "support"
+  ],
+  "css/mediaqueries/prefers-color-scheme.html": [
+   "705a7ca47f87692e0b888cf4cdcd4523304395c9",
+   "testharness"
+  ],
   "css/mediaqueries/reference/ref-green-body.xht": [
    "f79255de9cb1af511c23247de02918844a9afcc4",
    "support"
@@ -388211,10 +388343,26 @@
    "a6074f975d34fb6a216bdce4c7f560270512fd20",
    "testharness"
   ],
+  "encoding/idlharness.any-expected.txt": [
+   "8287d10c2fc1cfcb43aaa39d61ed52ba228cdb3c",
+   "support"
+  ],
   "encoding/idlharness.any.js": [
    "7a057f14e3a78c73b39a17f1debd3b8ad1e754d6",
    "testharness"
   ],
+  "encoding/idlharness.any.serviceworker-expected.txt": [
+   "8287d10c2fc1cfcb43aaa39d61ed52ba228cdb3c",
+   "support"
+  ],
+  "encoding/idlharness.any.sharedworker-expected.txt": [
+   "8287d10c2fc1cfcb43aaa39d61ed52ba228cdb3c",
+   "support"
+  ],
+  "encoding/idlharness.any.worker-expected.txt": [
+   "8287d10c2fc1cfcb43aaa39d61ed52ba228cdb3c",
+   "support"
+  ],
   "encoding/iso-2022-jp-decoder-expected.txt": [
    "e2530320ed21b50220eb6fc8eb0cb8b2c01b57e6",
    "support"
@@ -414204,7 +414352,7 @@
    "support"
   ],
   "interfaces/encoding.idl": [
-   "b805363c534feafcb606dd13701736f13497ef99",
+   "b3086b8588cdfec129a4c038c074021cacff83c5",
    "support"
   ],
   "interfaces/encrypted-media.idl": [
@@ -414488,7 +414636,7 @@
    "support"
   ],
   "interfaces/user-timing.idl": [
-   "60b22776c9bce58204a3aa6d9db634b1c42cb7a0",
+   "723b063156a07aa7524f3dab1496b46e51b1e9f8",
    "support"
   ],
   "interfaces/vibration.idl": [
@@ -426743,10 +426891,26 @@
    "33d6589e275e265180e0571ca3103dbf9aa179b8",
    "testharness"
   ],
+  "performance-timeline/idlharness.any-expected.txt": [
+   "0c8d31efef6ad214514ce2128b40011a9abd20ad",
+   "support"
+  ],
   "performance-timeline/idlharness.any.js": [
    "585bda92dced217354f93fb5beebe6d930890c0e",
    "testharness"
   ],
+  "performance-timeline/idlharness.any.serviceworker-expected.txt": [
+   "0c8d31efef6ad214514ce2128b40011a9abd20ad",
+   "support"
+  ],
+  "performance-timeline/idlharness.any.sharedworker-expected.txt": [
+   "0c8d31efef6ad214514ce2128b40011a9abd20ad",
+   "support"
+  ],
+  "performance-timeline/idlharness.any.worker-expected.txt": [
+   "0c8d31efef6ad214514ce2128b40011a9abd20ad",
+   "support"
+  ],
   "performance-timeline/performanceentry-tojson.any.js": [
    "44f0156eec19241970028c28e5897be4d6f1cb81",
    "testharness"
@@ -444988,7 +445152,7 @@
    "testharness"
   ],
   "trusted-types/TrustedTypePolicy-createXXX.tentative.html": [
-   "475a264790482aedf714958f63a4d47d69661941",
+   "6a0151ad9bcf7d02b6a98532ba53db509e086f83",
    "testharness"
   ],
   "trusted-types/TrustedTypePolicy-exposed.tentative.html": [
@@ -445000,7 +445164,7 @@
    "testharness"
   ],
   "trusted-types/TrustedTypePolicyFactory-createPolicy-createXYZTests.tentative.html": [
-   "37e245ee27aa5828bd97568d9390d9cbfbb6f968",
+   "a162d84cd820051d6c5868c35b58cd347b0026e5",
    "testharness"
   ],
   "trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-noNamesGiven.tentative.html": [
@@ -445076,7 +445240,7 @@
    "testharness"
   ],
   "trusted-types/block-string-assignment-to-Element-setAttribute.tentative.html": [
-   "c243757887a83611d28c61c1c1ce45a0fda925b2",
+   "9bda8d95a7fe4f0cb92c7e0e9c542bb3ddcb1015",
    "testharness"
   ],
   "trusted-types/block-string-assignment-to-Element-setAttributeNS.tentative.html": [
@@ -445088,7 +445252,7 @@
    "support"
   ],
   "trusted-types/block-string-assignment-to-HTMLElement-generic.tentative.html": [
-   "4a5c2cc8d62e22888b463b724bab255500943af5",
+   "0eef00bd7772b50bd6d774425cbe688eacf0183f",
    "testharness"
   ],
   "trusted-types/block-string-assignment-to-Location-assign.tentative.html": [
@@ -445963,10 +446127,26 @@
    "1e37453d09d42ea9c530bc2902cccb8f66fd6efd",
    "testharness"
   ],
+  "user-timing/idlharness.any-expected.txt": [
+   "4fc30f1becd51605e1b3bd51b4ac4ca8f453e5b6",
+   "support"
+  ],
   "user-timing/idlharness.any.js": [
    "d1cb9b57a780143296a70c98579e3c816bfaad81",
    "testharness"
   ],
+  "user-timing/idlharness.any.serviceworker-expected.txt": [
+   "4fc30f1becd51605e1b3bd51b4ac4ca8f453e5b6",
+   "support"
+  ],
+  "user-timing/idlharness.any.sharedworker-expected.txt": [
+   "4fc30f1becd51605e1b3bd51b4ac4ca8f453e5b6",
+   "support"
+  ],
+  "user-timing/idlharness.any.worker-expected.txt": [
+   "4fc30f1becd51605e1b3bd51b4ac4ca8f453e5b6",
+   "support"
+  ],
   "user-timing/invoke_with_timing_attributes.html": [
    "6c06f5e9d527b2a0cf3d7d2c02b334a24c9d4f25",
    "testharness"
@@ -449812,11 +449992,11 @@
    "testharness"
   ],
   "webrtc/RTCRtpReceiver-getSynchronizationSources.https-expected.txt": [
-   "446b998121e628714e6aaef3df1241e393ed0b86",
+   "aea423081c2f1c064d8b34e0a916e6db6b48d1d6",
    "support"
   ],
   "webrtc/RTCRtpReceiver-getSynchronizationSources.https.html": [
-   "80685580e227ec7116ad8fa6cf8855be66d20d45",
+   "36460b479903231e15427f303f15c3b6a3de16e8",
    "testharness"
   ],
   "webrtc/RTCRtpSender-getCapabilities.html": [
@@ -449952,7 +450132,7 @@
    "testharness"
   ],
   "webrtc/idlharness.https.window-expected.txt": [
-   "f8f1f896d8e79478861ed546e42ded2b1954ab1b",
+   "493ac14919cc7f4c4b4882c5bf2723502fe7ed1d",
    "support"
   ],
   "webrtc/idlharness.https.window.js": [
@@ -456156,7 +456336,7 @@
    "testharness"
   ],
   "worklets/layout-worklet-csp.https.html": [
-   "854df8c8e18a1370cc78962e0ca198af2910b8b2",
+   "a670f802654efd220e2f7b693acabf0dfa3df923",
    "testharness"
   ],
   "worklets/layout-worklet-import.https.html": [
diff --git a/third_party/blink/web_tests/external/wpt/content-security-policy/form-action/form-action-src-javascript-prevented.html b/third_party/blink/web_tests/external/wpt/content-security-policy/form-action/form-action-src-javascript-prevented.html
new file mode 100644
index 0000000..feae47e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/content-security-policy/form-action/form-action-src-javascript-prevented.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<meta http-equiv="Content-Security-Policy" content="form-action 'none'; script-src 'self' 'nonce-noncynonce'; connect-src 'self';">
+</head>
+
+<body>
+  <form action='/content-security-policy/support/postmessage-pass-to-opener.html'
+        id='form_id'
+        target="_blank">
+        <input type="submit" />
+  </form>
+
+  <p>
+    Test that "form-action 'none'" doesn't create a violation report if the event was prevented.
+  </p>
+</body>
+
+<script nonce='noncynonce'>
+  async_test(t => {
+    document.addEventListener('securitypolicyviolation', function(e) {
+      assert_unreached('Form submission was blocked.');
+    });
+
+    window.addEventListener('message', function(event) {
+      assert_unreached('Form submission was blocked.');
+    })
+
+    window.addEventListener("load", function() {
+      let form = document.getElementById("form_id");
+      form.addEventListener("submit", e => {
+        e.preventDefault();
+        setTimeout(() => {
+          t.done();
+        }, 0);
+      });
+      // clicking the input is used here as form.submit() will submit a form without an event and should also be blocked.
+      form.querySelector("input").click();
+    });
+  }, "The form submission should not be blocked by when javascript prevents the load.");
+</script>
+
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-first-letter-insert-crash.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-first-letter-insert-crash.html
new file mode 100644
index 0000000..17f035de
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/first-line-first-letter-insert-crash.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Should not crash when inserting an element inside a :first-line pseudo.</title>
+<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/">
+<style>
+p:first-line, p:first-letter {
+  font-family: serif;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p id=target>
+  test<sup>sup</sup>
+</p>
+<script>
+test(()=>{
+  const target = document.getElementById('target');
+  target.insertBefore(document.createElement('img'), target.lastChild)
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/encoding/idlharness.any-expected.txt b/third_party/blink/web_tests/external/wpt/encoding/idlharness.any-expected.txt
new file mode 100644
index 0000000..8287d10c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/encoding/idlharness.any-expected.txt
@@ -0,0 +1,58 @@
+This is a testharness.js-based test.
+Found 54 tests; 51 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idl_test setup
+PASS TextDecoder interface: existence and properties of interface object
+PASS TextDecoder interface object length
+PASS TextDecoder interface object name
+PASS TextDecoder interface: existence and properties of interface prototype object
+PASS TextDecoder interface: existence and properties of interface prototype object's "constructor" property
+PASS TextDecoder interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextDecoder interface: operation decode(BufferSource, TextDecodeOptions)
+PASS TextDecoder interface: attribute encoding
+PASS TextDecoder interface: attribute fatal
+PASS TextDecoder interface: attribute ignoreBOM
+PASS TextDecoder must be primary interface of new TextDecoder()
+PASS Stringification of new TextDecoder()
+PASS TextDecoder interface: new TextDecoder() must inherit property "decode(BufferSource, TextDecodeOptions)" with the proper type
+PASS TextDecoder interface: calling decode(BufferSource, TextDecodeOptions) on new TextDecoder() with too few arguments must throw TypeError
+PASS TextDecoder interface: new TextDecoder() must inherit property "encoding" with the proper type
+PASS TextDecoder interface: new TextDecoder() must inherit property "fatal" with the proper type
+PASS TextDecoder interface: new TextDecoder() must inherit property "ignoreBOM" with the proper type
+PASS TextEncoder interface: existence and properties of interface object
+PASS TextEncoder interface object length
+PASS TextEncoder interface object name
+PASS TextEncoder interface: existence and properties of interface prototype object
+PASS TextEncoder interface: existence and properties of interface prototype object's "constructor" property
+PASS TextEncoder interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextEncoder interface: operation encode(USVString)
+FAIL TextEncoder interface: operation encodeInto(USVString, Uint8Array) assert_own_property: interface prototype object missing non-static operation expected property "encodeInto" missing
+PASS TextEncoder interface: attribute encoding
+PASS TextEncoder must be primary interface of new TextEncoder()
+PASS Stringification of new TextEncoder()
+PASS TextEncoder interface: new TextEncoder() must inherit property "encode(USVString)" with the proper type
+PASS TextEncoder interface: calling encode(USVString) on new TextEncoder() with too few arguments must throw TypeError
+FAIL TextEncoder interface: new TextEncoder() must inherit property "encodeInto(USVString, Uint8Array)" with the proper type assert_inherits: property "encodeInto" not found in prototype chain
+FAIL TextEncoder interface: calling encodeInto(USVString, Uint8Array) on new TextEncoder() with too few arguments must throw TypeError assert_inherits: property "encodeInto" not found in prototype chain
+PASS TextEncoder interface: new TextEncoder() must inherit property "encoding" with the proper type
+PASS TextDecoderStream interface: existence and properties of interface object
+PASS TextDecoderStream interface object length
+PASS TextDecoderStream interface object name
+PASS TextDecoderStream interface: existence and properties of interface prototype object
+PASS TextDecoderStream interface: existence and properties of interface prototype object's "constructor" property
+PASS TextDecoderStream interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextDecoderStream interface: attribute encoding
+PASS TextDecoderStream interface: attribute fatal
+PASS TextDecoderStream interface: attribute ignoreBOM
+PASS TextDecoderStream interface: attribute readable
+PASS TextDecoderStream interface: attribute writable
+PASS TextEncoderStream interface: existence and properties of interface object
+PASS TextEncoderStream interface object length
+PASS TextEncoderStream interface object name
+PASS TextEncoderStream interface: existence and properties of interface prototype object
+PASS TextEncoderStream interface: existence and properties of interface prototype object's "constructor" property
+PASS TextEncoderStream interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextEncoderStream interface: attribute encoding
+PASS TextEncoderStream interface: attribute readable
+PASS TextEncoderStream interface: attribute writable
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/encoding/idlharness.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/encoding/idlharness.any.serviceworker-expected.txt
new file mode 100644
index 0000000..8287d10c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/encoding/idlharness.any.serviceworker-expected.txt
@@ -0,0 +1,58 @@
+This is a testharness.js-based test.
+Found 54 tests; 51 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idl_test setup
+PASS TextDecoder interface: existence and properties of interface object
+PASS TextDecoder interface object length
+PASS TextDecoder interface object name
+PASS TextDecoder interface: existence and properties of interface prototype object
+PASS TextDecoder interface: existence and properties of interface prototype object's "constructor" property
+PASS TextDecoder interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextDecoder interface: operation decode(BufferSource, TextDecodeOptions)
+PASS TextDecoder interface: attribute encoding
+PASS TextDecoder interface: attribute fatal
+PASS TextDecoder interface: attribute ignoreBOM
+PASS TextDecoder must be primary interface of new TextDecoder()
+PASS Stringification of new TextDecoder()
+PASS TextDecoder interface: new TextDecoder() must inherit property "decode(BufferSource, TextDecodeOptions)" with the proper type
+PASS TextDecoder interface: calling decode(BufferSource, TextDecodeOptions) on new TextDecoder() with too few arguments must throw TypeError
+PASS TextDecoder interface: new TextDecoder() must inherit property "encoding" with the proper type
+PASS TextDecoder interface: new TextDecoder() must inherit property "fatal" with the proper type
+PASS TextDecoder interface: new TextDecoder() must inherit property "ignoreBOM" with the proper type
+PASS TextEncoder interface: existence and properties of interface object
+PASS TextEncoder interface object length
+PASS TextEncoder interface object name
+PASS TextEncoder interface: existence and properties of interface prototype object
+PASS TextEncoder interface: existence and properties of interface prototype object's "constructor" property
+PASS TextEncoder interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextEncoder interface: operation encode(USVString)
+FAIL TextEncoder interface: operation encodeInto(USVString, Uint8Array) assert_own_property: interface prototype object missing non-static operation expected property "encodeInto" missing
+PASS TextEncoder interface: attribute encoding
+PASS TextEncoder must be primary interface of new TextEncoder()
+PASS Stringification of new TextEncoder()
+PASS TextEncoder interface: new TextEncoder() must inherit property "encode(USVString)" with the proper type
+PASS TextEncoder interface: calling encode(USVString) on new TextEncoder() with too few arguments must throw TypeError
+FAIL TextEncoder interface: new TextEncoder() must inherit property "encodeInto(USVString, Uint8Array)" with the proper type assert_inherits: property "encodeInto" not found in prototype chain
+FAIL TextEncoder interface: calling encodeInto(USVString, Uint8Array) on new TextEncoder() with too few arguments must throw TypeError assert_inherits: property "encodeInto" not found in prototype chain
+PASS TextEncoder interface: new TextEncoder() must inherit property "encoding" with the proper type
+PASS TextDecoderStream interface: existence and properties of interface object
+PASS TextDecoderStream interface object length
+PASS TextDecoderStream interface object name
+PASS TextDecoderStream interface: existence and properties of interface prototype object
+PASS TextDecoderStream interface: existence and properties of interface prototype object's "constructor" property
+PASS TextDecoderStream interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextDecoderStream interface: attribute encoding
+PASS TextDecoderStream interface: attribute fatal
+PASS TextDecoderStream interface: attribute ignoreBOM
+PASS TextDecoderStream interface: attribute readable
+PASS TextDecoderStream interface: attribute writable
+PASS TextEncoderStream interface: existence and properties of interface object
+PASS TextEncoderStream interface object length
+PASS TextEncoderStream interface object name
+PASS TextEncoderStream interface: existence and properties of interface prototype object
+PASS TextEncoderStream interface: existence and properties of interface prototype object's "constructor" property
+PASS TextEncoderStream interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextEncoderStream interface: attribute encoding
+PASS TextEncoderStream interface: attribute readable
+PASS TextEncoderStream interface: attribute writable
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/encoding/idlharness.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/encoding/idlharness.any.sharedworker-expected.txt
new file mode 100644
index 0000000..8287d10c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/encoding/idlharness.any.sharedworker-expected.txt
@@ -0,0 +1,58 @@
+This is a testharness.js-based test.
+Found 54 tests; 51 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idl_test setup
+PASS TextDecoder interface: existence and properties of interface object
+PASS TextDecoder interface object length
+PASS TextDecoder interface object name
+PASS TextDecoder interface: existence and properties of interface prototype object
+PASS TextDecoder interface: existence and properties of interface prototype object's "constructor" property
+PASS TextDecoder interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextDecoder interface: operation decode(BufferSource, TextDecodeOptions)
+PASS TextDecoder interface: attribute encoding
+PASS TextDecoder interface: attribute fatal
+PASS TextDecoder interface: attribute ignoreBOM
+PASS TextDecoder must be primary interface of new TextDecoder()
+PASS Stringification of new TextDecoder()
+PASS TextDecoder interface: new TextDecoder() must inherit property "decode(BufferSource, TextDecodeOptions)" with the proper type
+PASS TextDecoder interface: calling decode(BufferSource, TextDecodeOptions) on new TextDecoder() with too few arguments must throw TypeError
+PASS TextDecoder interface: new TextDecoder() must inherit property "encoding" with the proper type
+PASS TextDecoder interface: new TextDecoder() must inherit property "fatal" with the proper type
+PASS TextDecoder interface: new TextDecoder() must inherit property "ignoreBOM" with the proper type
+PASS TextEncoder interface: existence and properties of interface object
+PASS TextEncoder interface object length
+PASS TextEncoder interface object name
+PASS TextEncoder interface: existence and properties of interface prototype object
+PASS TextEncoder interface: existence and properties of interface prototype object's "constructor" property
+PASS TextEncoder interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextEncoder interface: operation encode(USVString)
+FAIL TextEncoder interface: operation encodeInto(USVString, Uint8Array) assert_own_property: interface prototype object missing non-static operation expected property "encodeInto" missing
+PASS TextEncoder interface: attribute encoding
+PASS TextEncoder must be primary interface of new TextEncoder()
+PASS Stringification of new TextEncoder()
+PASS TextEncoder interface: new TextEncoder() must inherit property "encode(USVString)" with the proper type
+PASS TextEncoder interface: calling encode(USVString) on new TextEncoder() with too few arguments must throw TypeError
+FAIL TextEncoder interface: new TextEncoder() must inherit property "encodeInto(USVString, Uint8Array)" with the proper type assert_inherits: property "encodeInto" not found in prototype chain
+FAIL TextEncoder interface: calling encodeInto(USVString, Uint8Array) on new TextEncoder() with too few arguments must throw TypeError assert_inherits: property "encodeInto" not found in prototype chain
+PASS TextEncoder interface: new TextEncoder() must inherit property "encoding" with the proper type
+PASS TextDecoderStream interface: existence and properties of interface object
+PASS TextDecoderStream interface object length
+PASS TextDecoderStream interface object name
+PASS TextDecoderStream interface: existence and properties of interface prototype object
+PASS TextDecoderStream interface: existence and properties of interface prototype object's "constructor" property
+PASS TextDecoderStream interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextDecoderStream interface: attribute encoding
+PASS TextDecoderStream interface: attribute fatal
+PASS TextDecoderStream interface: attribute ignoreBOM
+PASS TextDecoderStream interface: attribute readable
+PASS TextDecoderStream interface: attribute writable
+PASS TextEncoderStream interface: existence and properties of interface object
+PASS TextEncoderStream interface object length
+PASS TextEncoderStream interface object name
+PASS TextEncoderStream interface: existence and properties of interface prototype object
+PASS TextEncoderStream interface: existence and properties of interface prototype object's "constructor" property
+PASS TextEncoderStream interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextEncoderStream interface: attribute encoding
+PASS TextEncoderStream interface: attribute readable
+PASS TextEncoderStream interface: attribute writable
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/encoding/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/encoding/idlharness.any.worker-expected.txt
new file mode 100644
index 0000000..8287d10c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/encoding/idlharness.any.worker-expected.txt
@@ -0,0 +1,58 @@
+This is a testharness.js-based test.
+Found 54 tests; 51 PASS, 3 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idl_test setup
+PASS TextDecoder interface: existence and properties of interface object
+PASS TextDecoder interface object length
+PASS TextDecoder interface object name
+PASS TextDecoder interface: existence and properties of interface prototype object
+PASS TextDecoder interface: existence and properties of interface prototype object's "constructor" property
+PASS TextDecoder interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextDecoder interface: operation decode(BufferSource, TextDecodeOptions)
+PASS TextDecoder interface: attribute encoding
+PASS TextDecoder interface: attribute fatal
+PASS TextDecoder interface: attribute ignoreBOM
+PASS TextDecoder must be primary interface of new TextDecoder()
+PASS Stringification of new TextDecoder()
+PASS TextDecoder interface: new TextDecoder() must inherit property "decode(BufferSource, TextDecodeOptions)" with the proper type
+PASS TextDecoder interface: calling decode(BufferSource, TextDecodeOptions) on new TextDecoder() with too few arguments must throw TypeError
+PASS TextDecoder interface: new TextDecoder() must inherit property "encoding" with the proper type
+PASS TextDecoder interface: new TextDecoder() must inherit property "fatal" with the proper type
+PASS TextDecoder interface: new TextDecoder() must inherit property "ignoreBOM" with the proper type
+PASS TextEncoder interface: existence and properties of interface object
+PASS TextEncoder interface object length
+PASS TextEncoder interface object name
+PASS TextEncoder interface: existence and properties of interface prototype object
+PASS TextEncoder interface: existence and properties of interface prototype object's "constructor" property
+PASS TextEncoder interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextEncoder interface: operation encode(USVString)
+FAIL TextEncoder interface: operation encodeInto(USVString, Uint8Array) assert_own_property: interface prototype object missing non-static operation expected property "encodeInto" missing
+PASS TextEncoder interface: attribute encoding
+PASS TextEncoder must be primary interface of new TextEncoder()
+PASS Stringification of new TextEncoder()
+PASS TextEncoder interface: new TextEncoder() must inherit property "encode(USVString)" with the proper type
+PASS TextEncoder interface: calling encode(USVString) on new TextEncoder() with too few arguments must throw TypeError
+FAIL TextEncoder interface: new TextEncoder() must inherit property "encodeInto(USVString, Uint8Array)" with the proper type assert_inherits: property "encodeInto" not found in prototype chain
+FAIL TextEncoder interface: calling encodeInto(USVString, Uint8Array) on new TextEncoder() with too few arguments must throw TypeError assert_inherits: property "encodeInto" not found in prototype chain
+PASS TextEncoder interface: new TextEncoder() must inherit property "encoding" with the proper type
+PASS TextDecoderStream interface: existence and properties of interface object
+PASS TextDecoderStream interface object length
+PASS TextDecoderStream interface object name
+PASS TextDecoderStream interface: existence and properties of interface prototype object
+PASS TextDecoderStream interface: existence and properties of interface prototype object's "constructor" property
+PASS TextDecoderStream interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextDecoderStream interface: attribute encoding
+PASS TextDecoderStream interface: attribute fatal
+PASS TextDecoderStream interface: attribute ignoreBOM
+PASS TextDecoderStream interface: attribute readable
+PASS TextDecoderStream interface: attribute writable
+PASS TextEncoderStream interface: existence and properties of interface object
+PASS TextEncoderStream interface object length
+PASS TextEncoderStream interface object name
+PASS TextEncoderStream interface: existence and properties of interface prototype object
+PASS TextEncoderStream interface: existence and properties of interface prototype object's "constructor" property
+PASS TextEncoderStream interface: existence and properties of interface prototype object's @@unscopables property
+PASS TextEncoderStream interface: attribute encoding
+PASS TextEncoderStream interface: attribute readable
+PASS TextEncoderStream interface: attribute writable
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/encoding.idl b/third_party/blink/web_tests/external/wpt/interfaces/encoding.idl
index b805363c..b3086b8 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/encoding.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/encoding.idl
@@ -29,10 +29,16 @@
   readonly attribute DOMString encoding;
 };
 
+dictionary TextEncoderEncodeIntoResult {
+  unsigned long long read;
+  unsigned long long written;
+};
+
 [Constructor,
  Exposed=(Window,Worker)]
 interface TextEncoder {
   [NewObject] Uint8Array encode(optional USVString input = "");
+  TextEncoderEncodeIntoResult encodeInto(USVString source, Uint8Array destination);
 };
 TextEncoder includes TextEncoderCommon;
 
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/user-timing.idl b/third_party/blink/web_tests/external/wpt/interfaces/user-timing.idl
index 60b22776..723b063 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/user-timing.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/user-timing.idl
@@ -1,19 +1,33 @@
 // GENERATED CONTENT - DO NOT EDIT
 // Content was automatically extracted by Reffy into reffy-reports
 // (https://github.com/tidoust/reffy-reports)
-// Source: User Timing Level 2 (https://w3c.github.io/user-timing/)
+// Source: User Timing Level 3 (https://w3c.github.io/user-timing/)
+
+dictionary PerformanceMarkOptions {
+    any detail = null;
+    DOMHighResTimeStamp startTime;
+};
+
+dictionary PerformanceMeasureOptions {
+    any detail = null;
+    (DOMString or DOMHighResTimeStamp) startTime;
+    DOMHighResTimeStamp duration;
+    (DOMString or DOMHighResTimeStamp) endTime;
+};
 
 partial interface Performance {
-    void mark(DOMString markName);
+    PerformanceMark mark(DOMString markName, optional PerformanceMarkOptions markOptions);
     void clearMarks(optional DOMString markName);
-    void measure(DOMString measureName, optional DOMString startMark, optional DOMString endMark);
+    PerformanceMeasure measure(DOMString measureName, optional (DOMString or PerformanceMeasureOptions) startOrMeasureOptions, optional DOMString endMark);
     void clearMeasures(optional DOMString measureName);
 };
 
 [Exposed=(Window,Worker)]
 interface PerformanceMark : PerformanceEntry {
+  readonly attribute any detail;
 };
 
 [Exposed=(Window,Worker)]
 interface PerformanceMeasure : PerformanceEntry {
+  readonly attribute any detail;
 };
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any-expected.txt
new file mode 100644
index 0000000..0c8d31ef
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any-expected.txt
@@ -0,0 +1,75 @@
+This is a testharness.js-based test.
+Found 71 tests; 69 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idlharness
+PASS idl_test setup
+PASS Partial interface Performance: original interface defined
+PASS PerformanceEntry interface: existence and properties of interface object
+PASS PerformanceEntry interface object length
+PASS PerformanceEntry interface object name
+PASS PerformanceEntry interface: existence and properties of interface prototype object
+PASS PerformanceEntry interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceEntry interface: existence and properties of interface prototype object's @@unscopables property
+PASS PerformanceEntry interface: attribute name
+PASS PerformanceEntry interface: attribute entryType
+PASS PerformanceEntry interface: attribute startTime
+PASS PerformanceEntry interface: attribute duration
+PASS PerformanceEntry interface: operation toJSON()
+PASS PerformanceObserver interface: existence and properties of interface object
+PASS PerformanceObserver interface object length
+PASS PerformanceObserver interface object name
+PASS PerformanceObserver interface: existence and properties of interface prototype object
+PASS PerformanceObserver interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceObserver interface: existence and properties of interface prototype object's @@unscopables property
+PASS PerformanceObserver interface: operation observe(PerformanceObserverInit)
+PASS PerformanceObserver interface: operation disconnect()
+PASS PerformanceObserver interface: operation takeRecords()
+PASS PerformanceObserver interface: attribute supportedEntryTypes
+PASS PerformanceObserver must be primary interface of observer
+PASS Stringification of observer
+PASS PerformanceObserver interface: observer must inherit property "observe(PerformanceObserverInit)" with the proper type
+PASS PerformanceObserver interface: calling observe(PerformanceObserverInit) on observer with too few arguments must throw TypeError
+PASS PerformanceObserver interface: observer must inherit property "disconnect()" with the proper type
+PASS PerformanceObserver interface: observer must inherit property "takeRecords()" with the proper type
+PASS PerformanceObserver interface: observer must inherit property "supportedEntryTypes" with the proper type
+PASS PerformanceObserverEntryList interface: existence and properties of interface object
+PASS PerformanceObserverEntryList interface object length
+PASS PerformanceObserverEntryList interface object name
+PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object
+PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's @@unscopables property
+PASS PerformanceObserverEntryList interface: operation getEntries()
+PASS PerformanceObserverEntryList interface: operation getEntriesByType(DOMString)
+PASS PerformanceObserverEntryList interface: operation getEntriesByName(DOMString, DOMString)
+PASS PerformanceObserverEntryList must be primary interface of entryList
+PASS Stringification of entryList
+PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntries()" with the proper type
+PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByType(DOMString)" with the proper type
+PASS PerformanceObserverEntryList interface: calling getEntriesByType(DOMString) on entryList with too few arguments must throw TypeError
+PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
+PASS PerformanceObserverEntryList interface: calling getEntriesByName(DOMString, DOMString) on entryList with too few arguments must throw TypeError
+PASS Performance interface: operation getEntries()
+PASS Performance interface: operation getEntriesByType(DOMString)
+PASS Performance interface: operation getEntriesByName(DOMString, DOMString)
+PASS Performance interface: performance must inherit property "getEntries()" with the proper type
+PASS Performance interface: performance must inherit property "getEntriesByType(DOMString)" with the proper type
+PASS Performance interface: calling getEntriesByType(DOMString) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
+PASS Performance interface: calling getEntriesByName(DOMString, DOMString) on performance with too few arguments must throw TypeError
+PASS PerformanceMark interface: existence and properties of interface object
+PASS PerformanceMark interface object length
+PASS PerformanceMark interface object name
+PASS PerformanceMark interface: existence and properties of interface prototype object
+PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property
+FAIL PerformanceMark interface: attribute detail assert_true: The prototype object must have a property "detail" expected true got false
+PASS PerformanceMark must be primary interface of mark
+PASS Stringification of mark
+FAIL PerformanceMark interface: mark must inherit property "detail" with the proper type assert_inherits: property "detail" not found in prototype chain
+PASS PerformanceEntry interface: mark must inherit property "name" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "entryType" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "startTime" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "duration" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "toJSON()" with the proper type
+PASS Test default toJSON operation of PerformanceMark
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt
new file mode 100644
index 0000000..0c8d31ef
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.serviceworker-expected.txt
@@ -0,0 +1,75 @@
+This is a testharness.js-based test.
+Found 71 tests; 69 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idlharness
+PASS idl_test setup
+PASS Partial interface Performance: original interface defined
+PASS PerformanceEntry interface: existence and properties of interface object
+PASS PerformanceEntry interface object length
+PASS PerformanceEntry interface object name
+PASS PerformanceEntry interface: existence and properties of interface prototype object
+PASS PerformanceEntry interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceEntry interface: existence and properties of interface prototype object's @@unscopables property
+PASS PerformanceEntry interface: attribute name
+PASS PerformanceEntry interface: attribute entryType
+PASS PerformanceEntry interface: attribute startTime
+PASS PerformanceEntry interface: attribute duration
+PASS PerformanceEntry interface: operation toJSON()
+PASS PerformanceObserver interface: existence and properties of interface object
+PASS PerformanceObserver interface object length
+PASS PerformanceObserver interface object name
+PASS PerformanceObserver interface: existence and properties of interface prototype object
+PASS PerformanceObserver interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceObserver interface: existence and properties of interface prototype object's @@unscopables property
+PASS PerformanceObserver interface: operation observe(PerformanceObserverInit)
+PASS PerformanceObserver interface: operation disconnect()
+PASS PerformanceObserver interface: operation takeRecords()
+PASS PerformanceObserver interface: attribute supportedEntryTypes
+PASS PerformanceObserver must be primary interface of observer
+PASS Stringification of observer
+PASS PerformanceObserver interface: observer must inherit property "observe(PerformanceObserverInit)" with the proper type
+PASS PerformanceObserver interface: calling observe(PerformanceObserverInit) on observer with too few arguments must throw TypeError
+PASS PerformanceObserver interface: observer must inherit property "disconnect()" with the proper type
+PASS PerformanceObserver interface: observer must inherit property "takeRecords()" with the proper type
+PASS PerformanceObserver interface: observer must inherit property "supportedEntryTypes" with the proper type
+PASS PerformanceObserverEntryList interface: existence and properties of interface object
+PASS PerformanceObserverEntryList interface object length
+PASS PerformanceObserverEntryList interface object name
+PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object
+PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's @@unscopables property
+PASS PerformanceObserverEntryList interface: operation getEntries()
+PASS PerformanceObserverEntryList interface: operation getEntriesByType(DOMString)
+PASS PerformanceObserverEntryList interface: operation getEntriesByName(DOMString, DOMString)
+PASS PerformanceObserverEntryList must be primary interface of entryList
+PASS Stringification of entryList
+PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntries()" with the proper type
+PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByType(DOMString)" with the proper type
+PASS PerformanceObserverEntryList interface: calling getEntriesByType(DOMString) on entryList with too few arguments must throw TypeError
+PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
+PASS PerformanceObserverEntryList interface: calling getEntriesByName(DOMString, DOMString) on entryList with too few arguments must throw TypeError
+PASS Performance interface: operation getEntries()
+PASS Performance interface: operation getEntriesByType(DOMString)
+PASS Performance interface: operation getEntriesByName(DOMString, DOMString)
+PASS Performance interface: performance must inherit property "getEntries()" with the proper type
+PASS Performance interface: performance must inherit property "getEntriesByType(DOMString)" with the proper type
+PASS Performance interface: calling getEntriesByType(DOMString) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
+PASS Performance interface: calling getEntriesByName(DOMString, DOMString) on performance with too few arguments must throw TypeError
+PASS PerformanceMark interface: existence and properties of interface object
+PASS PerformanceMark interface object length
+PASS PerformanceMark interface object name
+PASS PerformanceMark interface: existence and properties of interface prototype object
+PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property
+FAIL PerformanceMark interface: attribute detail assert_true: The prototype object must have a property "detail" expected true got false
+PASS PerformanceMark must be primary interface of mark
+PASS Stringification of mark
+FAIL PerformanceMark interface: mark must inherit property "detail" with the proper type assert_inherits: property "detail" not found in prototype chain
+PASS PerformanceEntry interface: mark must inherit property "name" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "entryType" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "startTime" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "duration" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "toJSON()" with the proper type
+PASS Test default toJSON operation of PerformanceMark
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt
new file mode 100644
index 0000000..0c8d31ef
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.sharedworker-expected.txt
@@ -0,0 +1,75 @@
+This is a testharness.js-based test.
+Found 71 tests; 69 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idlharness
+PASS idl_test setup
+PASS Partial interface Performance: original interface defined
+PASS PerformanceEntry interface: existence and properties of interface object
+PASS PerformanceEntry interface object length
+PASS PerformanceEntry interface object name
+PASS PerformanceEntry interface: existence and properties of interface prototype object
+PASS PerformanceEntry interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceEntry interface: existence and properties of interface prototype object's @@unscopables property
+PASS PerformanceEntry interface: attribute name
+PASS PerformanceEntry interface: attribute entryType
+PASS PerformanceEntry interface: attribute startTime
+PASS PerformanceEntry interface: attribute duration
+PASS PerformanceEntry interface: operation toJSON()
+PASS PerformanceObserver interface: existence and properties of interface object
+PASS PerformanceObserver interface object length
+PASS PerformanceObserver interface object name
+PASS PerformanceObserver interface: existence and properties of interface prototype object
+PASS PerformanceObserver interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceObserver interface: existence and properties of interface prototype object's @@unscopables property
+PASS PerformanceObserver interface: operation observe(PerformanceObserverInit)
+PASS PerformanceObserver interface: operation disconnect()
+PASS PerformanceObserver interface: operation takeRecords()
+PASS PerformanceObserver interface: attribute supportedEntryTypes
+PASS PerformanceObserver must be primary interface of observer
+PASS Stringification of observer
+PASS PerformanceObserver interface: observer must inherit property "observe(PerformanceObserverInit)" with the proper type
+PASS PerformanceObserver interface: calling observe(PerformanceObserverInit) on observer with too few arguments must throw TypeError
+PASS PerformanceObserver interface: observer must inherit property "disconnect()" with the proper type
+PASS PerformanceObserver interface: observer must inherit property "takeRecords()" with the proper type
+PASS PerformanceObserver interface: observer must inherit property "supportedEntryTypes" with the proper type
+PASS PerformanceObserverEntryList interface: existence and properties of interface object
+PASS PerformanceObserverEntryList interface object length
+PASS PerformanceObserverEntryList interface object name
+PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object
+PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's @@unscopables property
+PASS PerformanceObserverEntryList interface: operation getEntries()
+PASS PerformanceObserverEntryList interface: operation getEntriesByType(DOMString)
+PASS PerformanceObserverEntryList interface: operation getEntriesByName(DOMString, DOMString)
+PASS PerformanceObserverEntryList must be primary interface of entryList
+PASS Stringification of entryList
+PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntries()" with the proper type
+PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByType(DOMString)" with the proper type
+PASS PerformanceObserverEntryList interface: calling getEntriesByType(DOMString) on entryList with too few arguments must throw TypeError
+PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
+PASS PerformanceObserverEntryList interface: calling getEntriesByName(DOMString, DOMString) on entryList with too few arguments must throw TypeError
+PASS Performance interface: operation getEntries()
+PASS Performance interface: operation getEntriesByType(DOMString)
+PASS Performance interface: operation getEntriesByName(DOMString, DOMString)
+PASS Performance interface: performance must inherit property "getEntries()" with the proper type
+PASS Performance interface: performance must inherit property "getEntriesByType(DOMString)" with the proper type
+PASS Performance interface: calling getEntriesByType(DOMString) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
+PASS Performance interface: calling getEntriesByName(DOMString, DOMString) on performance with too few arguments must throw TypeError
+PASS PerformanceMark interface: existence and properties of interface object
+PASS PerformanceMark interface object length
+PASS PerformanceMark interface object name
+PASS PerformanceMark interface: existence and properties of interface prototype object
+PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property
+FAIL PerformanceMark interface: attribute detail assert_true: The prototype object must have a property "detail" expected true got false
+PASS PerformanceMark must be primary interface of mark
+PASS Stringification of mark
+FAIL PerformanceMark interface: mark must inherit property "detail" with the proper type assert_inherits: property "detail" not found in prototype chain
+PASS PerformanceEntry interface: mark must inherit property "name" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "entryType" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "startTime" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "duration" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "toJSON()" with the proper type
+PASS Test default toJSON operation of PerformanceMark
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt
new file mode 100644
index 0000000..0c8d31ef
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/idlharness.any.worker-expected.txt
@@ -0,0 +1,75 @@
+This is a testharness.js-based test.
+Found 71 tests; 69 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idlharness
+PASS idl_test setup
+PASS Partial interface Performance: original interface defined
+PASS PerformanceEntry interface: existence and properties of interface object
+PASS PerformanceEntry interface object length
+PASS PerformanceEntry interface object name
+PASS PerformanceEntry interface: existence and properties of interface prototype object
+PASS PerformanceEntry interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceEntry interface: existence and properties of interface prototype object's @@unscopables property
+PASS PerformanceEntry interface: attribute name
+PASS PerformanceEntry interface: attribute entryType
+PASS PerformanceEntry interface: attribute startTime
+PASS PerformanceEntry interface: attribute duration
+PASS PerformanceEntry interface: operation toJSON()
+PASS PerformanceObserver interface: existence and properties of interface object
+PASS PerformanceObserver interface object length
+PASS PerformanceObserver interface object name
+PASS PerformanceObserver interface: existence and properties of interface prototype object
+PASS PerformanceObserver interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceObserver interface: existence and properties of interface prototype object's @@unscopables property
+PASS PerformanceObserver interface: operation observe(PerformanceObserverInit)
+PASS PerformanceObserver interface: operation disconnect()
+PASS PerformanceObserver interface: operation takeRecords()
+PASS PerformanceObserver interface: attribute supportedEntryTypes
+PASS PerformanceObserver must be primary interface of observer
+PASS Stringification of observer
+PASS PerformanceObserver interface: observer must inherit property "observe(PerformanceObserverInit)" with the proper type
+PASS PerformanceObserver interface: calling observe(PerformanceObserverInit) on observer with too few arguments must throw TypeError
+PASS PerformanceObserver interface: observer must inherit property "disconnect()" with the proper type
+PASS PerformanceObserver interface: observer must inherit property "takeRecords()" with the proper type
+PASS PerformanceObserver interface: observer must inherit property "supportedEntryTypes" with the proper type
+PASS PerformanceObserverEntryList interface: existence and properties of interface object
+PASS PerformanceObserverEntryList interface object length
+PASS PerformanceObserverEntryList interface object name
+PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object
+PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceObserverEntryList interface: existence and properties of interface prototype object's @@unscopables property
+PASS PerformanceObserverEntryList interface: operation getEntries()
+PASS PerformanceObserverEntryList interface: operation getEntriesByType(DOMString)
+PASS PerformanceObserverEntryList interface: operation getEntriesByName(DOMString, DOMString)
+PASS PerformanceObserverEntryList must be primary interface of entryList
+PASS Stringification of entryList
+PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntries()" with the proper type
+PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByType(DOMString)" with the proper type
+PASS PerformanceObserverEntryList interface: calling getEntriesByType(DOMString) on entryList with too few arguments must throw TypeError
+PASS PerformanceObserverEntryList interface: entryList must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
+PASS PerformanceObserverEntryList interface: calling getEntriesByName(DOMString, DOMString) on entryList with too few arguments must throw TypeError
+PASS Performance interface: operation getEntries()
+PASS Performance interface: operation getEntriesByType(DOMString)
+PASS Performance interface: operation getEntriesByName(DOMString, DOMString)
+PASS Performance interface: performance must inherit property "getEntries()" with the proper type
+PASS Performance interface: performance must inherit property "getEntriesByType(DOMString)" with the proper type
+PASS Performance interface: calling getEntriesByType(DOMString) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "getEntriesByName(DOMString, DOMString)" with the proper type
+PASS Performance interface: calling getEntriesByName(DOMString, DOMString) on performance with too few arguments must throw TypeError
+PASS PerformanceMark interface: existence and properties of interface object
+PASS PerformanceMark interface object length
+PASS PerformanceMark interface object name
+PASS PerformanceMark interface: existence and properties of interface prototype object
+PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property
+FAIL PerformanceMark interface: attribute detail assert_true: The prototype object must have a property "detail" expected true got false
+PASS PerformanceMark must be primary interface of mark
+PASS Stringification of mark
+FAIL PerformanceMark interface: mark must inherit property "detail" with the proper type assert_inherits: property "detail" not found in prototype chain
+PASS PerformanceEntry interface: mark must inherit property "name" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "entryType" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "startTime" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "duration" with the proper type
+PASS PerformanceEntry interface: mark must inherit property "toJSON()" with the proper type
+PASS Test default toJSON operation of PerformanceMark
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any-expected.txt b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any-expected.txt
new file mode 100644
index 0000000..4fc30f1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any-expected.txt
@@ -0,0 +1,37 @@
+This is a testharness.js-based test.
+PASS idl_test setup
+PASS Partial interface Performance: original interface defined
+PASS PerformanceMark interface: existence and properties of interface object
+PASS PerformanceMark interface object length
+PASS PerformanceMark interface object name
+PASS PerformanceMark interface: existence and properties of interface prototype object
+PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property
+FAIL PerformanceMark interface: attribute detail assert_true: The prototype object must have a property "detail" expected true got false
+PASS PerformanceMark must be primary interface of mark
+PASS Stringification of mark
+FAIL PerformanceMark interface: mark must inherit property "detail" with the proper type assert_inherits: property "detail" not found in prototype chain
+PASS PerformanceMeasure interface: existence and properties of interface object
+PASS PerformanceMeasure interface object length
+PASS PerformanceMeasure interface object name
+PASS PerformanceMeasure interface: existence and properties of interface prototype object
+PASS PerformanceMeasure interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceMeasure interface: existence and properties of interface prototype object's @@unscopables property
+FAIL PerformanceMeasure interface: attribute detail assert_true: The prototype object must have a property "detail" expected true got false
+PASS PerformanceMeasure must be primary interface of measure
+PASS Stringification of measure
+FAIL PerformanceMeasure interface: measure must inherit property "detail" with the proper type assert_inherits: property "detail" not found in prototype chain
+PASS Performance interface: operation mark(DOMString, PerformanceMarkOptions)
+PASS Performance interface: operation clearMarks(DOMString)
+PASS Performance interface: operation measure(DOMString, [object Object],[object Object], DOMString)
+PASS Performance interface: operation clearMeasures(DOMString)
+PASS Performance interface: performance must inherit property "mark(DOMString, PerformanceMarkOptions)" with the proper type
+PASS Performance interface: calling mark(DOMString, PerformanceMarkOptions) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "clearMarks(DOMString)" with the proper type
+PASS Performance interface: calling clearMarks(DOMString) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "measure(DOMString, [object Object],[object Object], DOMString)" with the proper type
+PASS Performance interface: calling measure(DOMString, [object Object],[object Object], DOMString) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "clearMeasures(DOMString)" with the proper type
+PASS Performance interface: calling clearMeasures(DOMString) on performance with too few arguments must throw TypeError
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.serviceworker-expected.txt
new file mode 100644
index 0000000..4fc30f1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.serviceworker-expected.txt
@@ -0,0 +1,37 @@
+This is a testharness.js-based test.
+PASS idl_test setup
+PASS Partial interface Performance: original interface defined
+PASS PerformanceMark interface: existence and properties of interface object
+PASS PerformanceMark interface object length
+PASS PerformanceMark interface object name
+PASS PerformanceMark interface: existence and properties of interface prototype object
+PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property
+FAIL PerformanceMark interface: attribute detail assert_true: The prototype object must have a property "detail" expected true got false
+PASS PerformanceMark must be primary interface of mark
+PASS Stringification of mark
+FAIL PerformanceMark interface: mark must inherit property "detail" with the proper type assert_inherits: property "detail" not found in prototype chain
+PASS PerformanceMeasure interface: existence and properties of interface object
+PASS PerformanceMeasure interface object length
+PASS PerformanceMeasure interface object name
+PASS PerformanceMeasure interface: existence and properties of interface prototype object
+PASS PerformanceMeasure interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceMeasure interface: existence and properties of interface prototype object's @@unscopables property
+FAIL PerformanceMeasure interface: attribute detail assert_true: The prototype object must have a property "detail" expected true got false
+PASS PerformanceMeasure must be primary interface of measure
+PASS Stringification of measure
+FAIL PerformanceMeasure interface: measure must inherit property "detail" with the proper type assert_inherits: property "detail" not found in prototype chain
+PASS Performance interface: operation mark(DOMString, PerformanceMarkOptions)
+PASS Performance interface: operation clearMarks(DOMString)
+PASS Performance interface: operation measure(DOMString, [object Object],[object Object], DOMString)
+PASS Performance interface: operation clearMeasures(DOMString)
+PASS Performance interface: performance must inherit property "mark(DOMString, PerformanceMarkOptions)" with the proper type
+PASS Performance interface: calling mark(DOMString, PerformanceMarkOptions) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "clearMarks(DOMString)" with the proper type
+PASS Performance interface: calling clearMarks(DOMString) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "measure(DOMString, [object Object],[object Object], DOMString)" with the proper type
+PASS Performance interface: calling measure(DOMString, [object Object],[object Object], DOMString) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "clearMeasures(DOMString)" with the proper type
+PASS Performance interface: calling clearMeasures(DOMString) on performance with too few arguments must throw TypeError
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.sharedworker-expected.txt
new file mode 100644
index 0000000..4fc30f1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.sharedworker-expected.txt
@@ -0,0 +1,37 @@
+This is a testharness.js-based test.
+PASS idl_test setup
+PASS Partial interface Performance: original interface defined
+PASS PerformanceMark interface: existence and properties of interface object
+PASS PerformanceMark interface object length
+PASS PerformanceMark interface object name
+PASS PerformanceMark interface: existence and properties of interface prototype object
+PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property
+FAIL PerformanceMark interface: attribute detail assert_true: The prototype object must have a property "detail" expected true got false
+PASS PerformanceMark must be primary interface of mark
+PASS Stringification of mark
+FAIL PerformanceMark interface: mark must inherit property "detail" with the proper type assert_inherits: property "detail" not found in prototype chain
+PASS PerformanceMeasure interface: existence and properties of interface object
+PASS PerformanceMeasure interface object length
+PASS PerformanceMeasure interface object name
+PASS PerformanceMeasure interface: existence and properties of interface prototype object
+PASS PerformanceMeasure interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceMeasure interface: existence and properties of interface prototype object's @@unscopables property
+FAIL PerformanceMeasure interface: attribute detail assert_true: The prototype object must have a property "detail" expected true got false
+PASS PerformanceMeasure must be primary interface of measure
+PASS Stringification of measure
+FAIL PerformanceMeasure interface: measure must inherit property "detail" with the proper type assert_inherits: property "detail" not found in prototype chain
+PASS Performance interface: operation mark(DOMString, PerformanceMarkOptions)
+PASS Performance interface: operation clearMarks(DOMString)
+PASS Performance interface: operation measure(DOMString, [object Object],[object Object], DOMString)
+PASS Performance interface: operation clearMeasures(DOMString)
+PASS Performance interface: performance must inherit property "mark(DOMString, PerformanceMarkOptions)" with the proper type
+PASS Performance interface: calling mark(DOMString, PerformanceMarkOptions) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "clearMarks(DOMString)" with the proper type
+PASS Performance interface: calling clearMarks(DOMString) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "measure(DOMString, [object Object],[object Object], DOMString)" with the proper type
+PASS Performance interface: calling measure(DOMString, [object Object],[object Object], DOMString) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "clearMeasures(DOMString)" with the proper type
+PASS Performance interface: calling clearMeasures(DOMString) on performance with too few arguments must throw TypeError
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.worker-expected.txt
new file mode 100644
index 0000000..4fc30f1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/user-timing/idlharness.any.worker-expected.txt
@@ -0,0 +1,37 @@
+This is a testharness.js-based test.
+PASS idl_test setup
+PASS Partial interface Performance: original interface defined
+PASS PerformanceMark interface: existence and properties of interface object
+PASS PerformanceMark interface object length
+PASS PerformanceMark interface object name
+PASS PerformanceMark interface: existence and properties of interface prototype object
+PASS PerformanceMark interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceMark interface: existence and properties of interface prototype object's @@unscopables property
+FAIL PerformanceMark interface: attribute detail assert_true: The prototype object must have a property "detail" expected true got false
+PASS PerformanceMark must be primary interface of mark
+PASS Stringification of mark
+FAIL PerformanceMark interface: mark must inherit property "detail" with the proper type assert_inherits: property "detail" not found in prototype chain
+PASS PerformanceMeasure interface: existence and properties of interface object
+PASS PerformanceMeasure interface object length
+PASS PerformanceMeasure interface object name
+PASS PerformanceMeasure interface: existence and properties of interface prototype object
+PASS PerformanceMeasure interface: existence and properties of interface prototype object's "constructor" property
+PASS PerformanceMeasure interface: existence and properties of interface prototype object's @@unscopables property
+FAIL PerformanceMeasure interface: attribute detail assert_true: The prototype object must have a property "detail" expected true got false
+PASS PerformanceMeasure must be primary interface of measure
+PASS Stringification of measure
+FAIL PerformanceMeasure interface: measure must inherit property "detail" with the proper type assert_inherits: property "detail" not found in prototype chain
+PASS Performance interface: operation mark(DOMString, PerformanceMarkOptions)
+PASS Performance interface: operation clearMarks(DOMString)
+PASS Performance interface: operation measure(DOMString, [object Object],[object Object], DOMString)
+PASS Performance interface: operation clearMeasures(DOMString)
+PASS Performance interface: performance must inherit property "mark(DOMString, PerformanceMarkOptions)" with the proper type
+PASS Performance interface: calling mark(DOMString, PerformanceMarkOptions) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "clearMarks(DOMString)" with the proper type
+PASS Performance interface: calling clearMarks(DOMString) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "measure(DOMString, [object Object],[object Object], DOMString)" with the proper type
+PASS Performance interface: calling measure(DOMString, [object Object],[object Object], DOMString) on performance with too few arguments must throw TypeError
+PASS Performance interface: performance must inherit property "clearMeasures(DOMString)" with the proper type
+PASS Performance interface: calling clearMeasures(DOMString) on performance with too few arguments must throw TypeError
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/web-locks/query-order.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/web-locks/query-order.tentative.https.any.js
deleted file mode 100644
index 3317cd0..0000000
--- a/third_party/blink/web_tests/external/wpt/web-locks/query-order.tentative.https.any.js
+++ /dev/null
@@ -1,110 +0,0 @@
-// META: title=Web Locks API: navigator.locks.query ordering
-// META: script=resources/helpers.js
-// META: global=window,dedicatedworker,sharedworker,serviceworker
-
-'use strict';
-
-// Grab a lock and hold until a release function is called. Resolves
-// to a release function.
-function getLockAndHoldUntilReleased(name, options) {
-  let release;
-  const promise = new Promise(resolve => { release = resolve; });
-  return new Promise(resolve => {
-    navigator.locks.request(name, options || {}, lock => {
-      resolve(release);
-      return promise;
-    }).catch(_ => {});
-  });
-}
-
-promise_test(async t => {
-  const res1 = uniqueName(t);
-  const res2 = uniqueName(t);
-  const res3 = uniqueName(t);
-
-  // These will never be released.
-  await Promise.all([
-    getLockAndHoldUntilReleased(res1),
-    getLockAndHoldUntilReleased(res2),
-    getLockAndHoldUntilReleased(res3)
-  ]);
-
-  // These requests should be blocked.
-  navigator.locks.request(res3, {mode: 'shared'}, lock => {});
-  navigator.locks.request(res2, {mode: 'shared'}, lock => {});
-  navigator.locks.request(res1, {mode: 'shared'}, lock => {});
-
-  const state = await navigator.locks.query();
-
-  const relevant_pending_names = state.pending.map(lock => lock.name)
-                        .filter(name => [res1, res2, res3].includes(name));
-
-  assert_array_equals(relevant_pending_names, [res3, res2, res1],
-                      'Pending locks should appear in order.');
-}, 'Requests appear in state in order made');
-
-promise_test(async t => {
-  const res1 = uniqueName(t);
-  const res2 = uniqueName(t);
-  const res3 = uniqueName(t);
-
-  // These should be granted, and will be held until released.
-  const [release1, release2, release3] = await Promise.all([
-    getLockAndHoldUntilReleased(res1),
-    getLockAndHoldUntilReleased(res2),
-    getLockAndHoldUntilReleased(res3)
-  ]);
-
-  // These requests should be blocked.
-  const requests = [
-    getLockAndHoldUntilReleased(res1),
-    getLockAndHoldUntilReleased(res2),
-    getLockAndHoldUntilReleased(res3)
-  ];
-
-  // Ensure the requests have had a chance to get queued by
-  // waiting for something else to make it through the queue.
-  await navigator.locks.request(uniqueName(t), lock => {});
-
-  // Now release the previous holders.
-  release2();
-  release3();
-  release1();
-
-  // Wait until the subsequent requests make it through.
-  await Promise.all(requests);
-
-  const state = await navigator.locks.query();
-  const relevant_held_names = state.held.map(lock => lock.name)
-                        .filter(name => [res1, res2, res3].includes(name));
-
-  assert_array_equals(relevant_held_names, [res2, res3, res1],
-                      'Held locks should appear in granted order.');
-}, 'Held locks appear in state in order granted');
-
-promise_test(async t => {
-  const res1 = uniqueName(t);
-  const res2 = uniqueName(t);
-  const res3 = uniqueName(t);
-
-  // These should be granted, and will be held until stolen.
-  await Promise.all([
-    getLockAndHoldUntilReleased(res1),
-    getLockAndHoldUntilReleased(res2),
-    getLockAndHoldUntilReleased(res3)
-  ]);
-
-  // Steal in a different order.
-  await Promise.all([
-    getLockAndHoldUntilReleased(res3, {steal: true}),
-    getLockAndHoldUntilReleased(res1, {steal: true}),
-    getLockAndHoldUntilReleased(res2, {steal: true})
-  ]);
-
-  const state = await navigator.locks.query();
-  const relevant_held_names = state.held.map(lock => lock.name)
-                        .filter(name => [res1, res2, res3].includes(name));
-
-  assert_array_equals(relevant_held_names, [res3, res1, res2],
-                      'Held locks should appear in granted order.');
-}, 'Held locks appear in state in order granted, including when stolen');
diff --git a/third_party/blink/web_tests/external/wpt/web-locks/query-ordering.tentative.https.html b/third_party/blink/web_tests/external/wpt/web-locks/query-ordering.tentative.https.html
new file mode 100644
index 0000000..bbbee38
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-locks/query-ordering.tentative.https.html
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Web Locks API: navigator.locks.query ordering</title>
+<link rel=help href="https://wicg.github.io/web-locks/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<style>iframe { display: none; }</style>
+<script>
+'use strict';
+
+// Grab a lock and hold until a release function is called. Resolves
+// to a release function.
+function getLockAndHoldUntilReleased(name, options) {
+  let release;
+  const promise = new Promise(resolve => { release = resolve; });
+  return new Promise(resolve => {
+    navigator.locks.request(name, options || {}, lock => {
+      resolve(release);
+      return promise;
+    }).catch(_ => {});
+  });
+}
+
+// Returns a promise resolved by the next message event.
+function nextMessage() {
+  return new Promise(resolve => {
+    window.addEventListener('message', event => {
+      resolve(event.data);
+    }, {once: true});
+  });
+}
+
+// Tests the ordering constraints on the requested lock state returned by
+// navigator.locks.query(). Three separate iframes are instantiated to make
+// lock requests on the same resource, first in one order and then in another,
+// different order. For each set of requests, it is verified that the requests
+// appear in the result of navigator.locks.query() in the same order in which
+// they were made.
+//
+// It is necessary to use separate iframes here so that the lock requests have
+// distinguishable client_ids (otherwise it would not be possible to
+// distinguish the requests and thus impossible to verify ordering).
+promise_test(async testCase => {
+  const resourceName = uniqueName(testCase);
+
+  // Set up clients.
+  const frame1 = await iframe('resources/iframe.html');
+  const frame2 = await iframe('resources/iframe.html');
+  const frame3 = await iframe('resources/iframe.html');
+  testCase.add_cleanup(() => { frame1.remove(); });
+  testCase.add_cleanup(() => { frame2.remove(); });
+  testCase.add_cleanup(() => { frame3.remove(); });
+
+  // Collect the client ids.
+  const clientId1 =
+      (await postToFrameAndWait(frame1, {op: 'client_id',
+                                         name: resourceName})).client_id;
+  const clientId2 =
+      (await postToFrameAndWait(frame2, {op: 'client_id',
+                                         name: resourceName})).client_id;
+  const clientId3 =
+      (await postToFrameAndWait(frame3, {op: 'client_id',
+                                         name: resourceName})).client_id;
+
+  // Preemptively take the lock.
+  const firstRequestGroupReleaseFunction =
+      await getLockAndHoldUntilReleased(resourceName);
+
+  // Queue the first group of lock requests from the different clients. These
+  // will be blocked until firstRequestGroupReleaseFunction() is called.
+  let lockId1;
+  let lockId2;
+  const lockPromise1 =
+      postToFrameAndWait(frame1, {op: 'request', name: resourceName})
+          .then(val => {lockId1 = val.lock_id;});
+  const lockPromise2 =
+      postToFrameAndWait(frame2, {op: 'request', name: resourceName})
+          .then(val => {lockId2 = val.lock_id;});
+
+  // This third request will later be granted and held in order to block a
+  // second group of requests to test a different client ordering. It is not
+  // meant to be released.
+  postToFrameAndWait(frame3, {op: 'request', name: resourceName});
+
+  // Request and wait for the release of a separate lock to ensure all previous
+  // requests are processed.
+  const checkpointName = uniqueName(testCase, 'checkpoint');
+  const checkpointId = (await postToFrameAndWait(
+                                  frame3,
+                                  {op: 'request', name: checkpointName})).lock_id;
+  await postToFrameAndWait(frame3, {op: 'release', lock_id: checkpointId});
+
+  // Query the state and test the ordering of requested locks.
+  const state = await navigator.locks.query();
+  const relevant_pending_ids = state.pending
+      .filter(lock => [clientId1, clientId2, clientId3].includes(lock.clientId))
+      .map(lock => lock.clientId);
+  assert_array_equals(
+      [clientId1, clientId2, clientId3],
+      relevant_pending_ids,
+      'Querying the state should return requested locks in the order they were '
+      + 'requested.');
+
+  // Add the second group of requests from the clients in a new order.
+  postToFrameAndWait(frame3, {op: 'request', name: resourceName});
+  postToFrameAndWait(frame1, {op: 'request', name: resourceName});
+  postToFrameAndWait(frame2, {op: 'request', name: resourceName});
+
+  // Release locks such that only the newly added locks are requested. This
+  // acts like a checkpoint for the newly queued requests.
+  firstRequestGroupReleaseFunction();
+  await lockPromise1;
+  await postToFrameAndWait(frame1, {op: 'release', lock_id: lockId1});
+  await lockPromise2;
+  await postToFrameAndWait(frame2, {op: 'release', lock_id: lockId2});
+
+  // Query the state and test the new ordering.
+  const state2 = await navigator.locks.query();
+  const relevant_pending_ids2 = state2.pending
+      .filter(lock => [clientId1, clientId2, clientId3].includes(lock.clientId))
+      .map(lock => lock.clientId);
+  assert_array_equals(
+      [clientId3, clientId1, clientId2],
+      relevant_pending_ids2,
+      'Querying the state should return requested locks in the order they were '
+      + 'requested.');
+
+}, 'Requests appear in state in order made.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/web-locks/resources/helpers.js b/third_party/blink/web_tests/external/wpt/web-locks/resources/helpers.js
index d6a4af4..9723ff4f 100644
--- a/third_party/blink/web_tests/external/wpt/web-locks/resources/helpers.js
+++ b/third_party/blink/web_tests/external/wpt/web-locks/resources/helpers.js
@@ -5,8 +5,8 @@
   // test case name. This is useful to avoid lock interference between
   // test cases.
   let res_num = 0;
-  self.uniqueName = testCase => {
-    return `${self.location.pathname}-${testCase.name}-${++res_num}`;
+  self.uniqueName = (testCase, prefix) => {
+    return `${self.location.pathname}-${prefix}-${testCase.name}-${++res_num}`;
   };
 
   // Inject an iframe showing the given url into the page, and resolve
diff --git a/third_party/blink/web_tests/external/wpt/web-locks/resources/iframe.html b/third_party/blink/web_tests/external/wpt/web-locks/resources/iframe.html
index d3799f4..ba63c77 100644
--- a/third_party/blink/web_tests/external/wpt/web-locks/resources/iframe.html
+++ b/third_party/blink/web_tests/external/wpt/web-locks/resources/iframe.html
@@ -38,6 +38,15 @@
       held.delete(e.data.lock_id);
       respond({ack: 'release', lock_id: e.data.lock_id});
       break;
+
+    case 'client_id':
+      navigator.locks.request(e.data.name, async lock => {
+        const lock_state = await navigator.locks.query();
+        const held_lock =
+            lock_state.held.filter(l => l.name === lock.name)[0];
+        respond({ack: 'client_id', client_id: held_lock.clientId});
+      });
+      break;
   }
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/worklets/layout-worklet-csp.https.html b/third_party/blink/web_tests/external/wpt/worklets/layout-worklet-csp.https.html
index 854df8c..a670f80 100644
--- a/third_party/blink/web_tests/external/wpt/worklets/layout-worklet-csp.https.html
+++ b/third_party/blink/web_tests/external/wpt/worklets/layout-worklet-csp.https.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 <html>
 <head>
+    <meta name="timeout" content="long">
     <script src="/common/get-host-info.sub.js"></script>
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/fast/canvas-api/canvas-context-attributes-default-value.html b/third_party/blink/web_tests/fast/canvas-api/canvas-context-attributes-default-value.html
index b3065a0d..212487e 100644
--- a/third_party/blink/web_tests/fast/canvas-api/canvas-context-attributes-default-value.html
+++ b/third_party/blink/web_tests/fast/canvas-api/canvas-context-attributes-default-value.html
@@ -1,12 +1,14 @@
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
 <script>
-  var trueAttributes = {
+  var defaultAttributes = {
     alpha : true,
+    lowLatency : false,
   };
 
   var falseAttributes = {
     alpha : false,
+    lowLatency : false,
   };
 
   function testAttributes(expectedAttributes, checkValue) {
@@ -33,12 +35,12 @@
   }
 
   test(function(t) {
-    testAttributes(trueAttributes);
+    testAttributes(defaultAttributes);
   }, 'Test default value');
 
   test(function(t) {
-    testAttributes(trueAttributes, undefined);
-  }, 'Test undfined value');
+    testAttributes(defaultAttributes, undefined);
+  }, 'Test undefined value');
 
   test(function(t) {
     testAttributes(falseAttributes, null);
diff --git a/third_party/blink/web_tests/fast/canvas-api/canvas-lowlatency-getContext.html b/third_party/blink/web_tests/fast/canvas-api/canvas-lowlatency-getContext.html
index 41831cbf..0983141 100644
--- a/third_party/blink/web_tests/fast/canvas-api/canvas-lowlatency-getContext.html
+++ b/third_party/blink/web_tests/fast/canvas-api/canvas-lowlatency-getContext.html
@@ -7,8 +7,7 @@
     canvas = document.createElement('canvas');
     var ctx_2d = canvas.getContext('2d', {lowLatency : true});
     assert_true(ctx_2d instanceof CanvasRenderingContext2D);
-    // TODO(mcasas): Check ctx_2d.getContextAttributes().lowLatency after
-    // https://github.com/whatwg/html/issues/2563.
+    assert_true(ctx_2d.getContextAttributes().lowLatency);
 
     var ctx_2d_second = canvas.getContext('2d', {lowLatency : true});
     assert_equals(ctx_2d, ctx_2d_second);
diff --git a/third_party/blink/web_tests/http/tests/devtools/layers/layer-canvas-log-expected.txt b/third_party/blink/web_tests/http/tests/devtools/layers/layer-canvas-log-expected.txt
index 0083cbeb..c12bb1e1 100644
--- a/third_party/blink/web_tests/http/tests/devtools/layers/layer-canvas-log-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/layers/layer-canvas-log-expected.txt
@@ -10,15 +10,11 @@
                 color : "#FF0000FF"
                 filterLevel : "Low"
                 flags : "AntiAlias"
-                hinting : "Normal"
                 strokeCap : "Butt"
                 strokeJoin : "Miter"
                 strokeMiter : 4
                 strokeWidth : 0
                 styleName : "Fill"
-                textScaleX : 1
-                textSize : 12
-                textSkewX : 0
             }
             rect : {
                 bottom : 100
@@ -36,15 +32,11 @@
                 color : "#FFFF0000"
                 filterLevel : "Low"
                 flags : "AntiAlias"
-                hinting : "Normal"
                 strokeCap : "Butt"
                 strokeJoin : "Miter"
                 strokeMiter : 4
                 strokeWidth : 0
                 styleName : "Fill"
-                textScaleX : 1
-                textSize : 12
-                textSkewX : 0
             }
             rect : {
                 bottom : 50
@@ -73,15 +65,11 @@
                 color : "#FF000000"
                 filterLevel : "Medium"
                 flags : "AntiAlias"
-                hinting : "Normal"
                 strokeCap : "Butt"
                 strokeJoin : "Miter"
                 strokeMiter : 4
                 strokeWidth : 0
                 styleName : "Fill"
-                textScaleX : 1
-                textSize : 12
-                textSkewX : 0
             }
             src : {
                 bottom : 21
@@ -126,15 +114,11 @@
                 color : "#7F000000"
                 filterLevel : "Low"
                 flags : "AntiAlias"
-                hinting : "Normal"
                 strokeCap : "Butt"
                 strokeJoin : "Miter"
                 strokeMiter : 4
                 strokeWidth : 0
                 styleName : "Fill"
-                textScaleX : 1
-                textSize : 12
-                textSkewX : 0
             }
             rect : {
                 bottom : 10
diff --git a/third_party/blink/web_tests/http/tests/media/media-source/mediasource-htmlmediaelement-lifetime.html b/third_party/blink/web_tests/http/tests/media/media-source/mediasource-htmlmediaelement-lifetime.html
index bbb45f4..937fab1 100644
--- a/third_party/blink/web_tests/http/tests/media/media-source/mediasource-htmlmediaelement-lifetime.html
+++ b/third_party/blink/web_tests/http/tests/media/media-source/mediasource-htmlmediaelement-lifetime.html
@@ -4,97 +4,300 @@
 <script src="/w3c/resources/testharnessreport.js"></script>
 <script>
 
-async_test(function(test) {
-    var video = document.createElement("video");
-    var media_source = new MediaSource();
-    var object_url = URL.createObjectURL(media_source);
+function TestScope() {
+    this.video = null;
+    this.media_source = null;
+    this.object_url = null;
+    this.source_buffer_list = null;
+    this.expect_durationchange = false;
+    this.expect_sourceclose = false;
+    this.received_sourceclose = false;
+    this.expect_error = false;
+    this.received_error = false;
+}
 
-    media_source.onsourceopen = test.step_func(function() {
-        URL.revokeObjectURL(object_url);
-        video = null;
-        // We still have a reference to the MediaSource, so gc() shouldn't collect
-        // HTMLME+MSE.
+function setup_htmlme_mse_lifetime_test(test, test_scope, handler_setup_cb, setup_done_cb) {
+    // |handler_setup_cb|, if not null, is synchronously called after
+    // test_scope is populated with HTMLME and MSE references, but prior to
+    // attaching HTMLME to MSE API.
+    //
+    // Next, attaches the HTMLME to the MediaSource object and sets a timeout to later
+    // call |setup_done_cb| so that hopefully any pending events or other work
+    // are complete before the main part of the test proceeds.
+
+    test_scope.video = document.createElement("video");
+    test_scope.media_source = new MediaSource();
+    test_scope.object_url = URL.createObjectURL(test_scope.media_source);
+    if (handler_setup_cb != null)
+        handler_setup_cb();
+
+    test_scope.media_source.onsourceopen = test.step_func(function() {
+        test_scope.media_source.onsourceopen = null;
+        URL.revokeObjectURL(test_scope.object_url);
+        setTimeout(setup_done_cb, 0);
+    });
+
+    test_scope.video.src = test_scope.object_url;
+}
+
+async_test(function(test) {
+    var test_scope = new TestScope();
+
+    var handler_setup_cb = null;
+
+    var setup_done_cb = test.step_func(function() {
+        test_scope.video = null;
+        // |test_scope.media_source| should be the only remaining reference to HTMLME+MSE.
+        // GC shouldn't collect HTMLME+MSE due to that live reference.
+        assert_equals(test_scope.media_source.readyState, "open", "MediaSource object is open before gc().");
+
+        // Debug note: setting test_scope.media_source to null here, and commenting out the assert_equals()
+        // and test.done() lines, below, demonstrates with debug logging that HTMLME+MSE is collected, followed
+        // eventually by a timeout.
         gc();
 
-        setTimeout(test.step_func(function() {
-            assert_equals(media_source.readyState, "open", "MediaSource object is open.");
-            // Setting |media_source|.duration should synchronously enqueue a
-            // 'durationchange' event on the media element (for which we have
-            // no reference, but we do have an event listener -- see below).
-            media_source.duration = 100;
-
-            // Though we drop the reference to the MediaSource here, gc()
-            // shouldn't collect HTMLME+MSE due to pending 'durationchange'
-            // event dispatch on HTMLME.
-            media_source = null;
-            gc();
-        }), 0);
-    });
-
-    video.ondurationchange = test.step_func_done();
-    video.src = object_url;
-
-    // Debugging notes: If this test times out, it's likely due to failure in
-    // keeping HTMLME+MSE alive and pending event dispatch on their wrappers alive
-    // through gc(); either gc() over-collected HTMLME+MSE or event dispatch was
-    // perturbed by gc().
-}, "GC of HTMLME+MediaSource preserves HTMLME+MediaSource when only MediaSource reference is alive, then HTMLME.ondurationchange is dispatched if it was pending when GC occurred with no HTMLME+MSE references");
-
-async_test(function(test) {
-    var video = document.createElement("video");
-    var media_source = new MediaSource();
-    var object_url = URL.createObjectURL(media_source);
-    var source_opened = false;
-    var detach_started = false;
-
-    media_source.onsourceopen = test.step_func(function() {
-        URL.revokeObjectURL(object_url);
-        source_opened = true;
-    });
-
-    media_source.onsourceclose = test.step_func(function() {
-        assert_true(source_opened, "sourceopen should fire before sourceclose");
-        assert_true(detach_started, "We should explicitly start detachment before receiving sourceclose");
+        assert_equals(test_scope.media_source.readyState, "open", "MediaSource object is open after gc().");
         test.done();
     });
 
-    // Forget all live references to the MediaSource, except object_url (until
-    // that is also revoked later during sourceopen handling.)
-    media_source = null;
+    setup_htmlme_mse_lifetime_test(test, test_scope, handler_setup_cb, setup_done_cb)
+}, "GC of HTMLME+MediaSource preserves at least MediaSource when only the MediaSource reference is held by JS");
 
-    video.src = object_url;
+async_test(function(test) {
+    // This test builds on the previous test. It should fail if the previous test fails.
 
-    var gc_then_detach_if_attached = test.step_func(function() {
-        // GC shouldn't collect HTMLME+MSE, even if source_opened is true and
-        // object_url has been revoked, because we still have a reference to HTMLME.
+    var test_scope = new TestScope();
+
+    var handler_setup_cb = test.step_func(function() {
+        test_scope.video.ondurationchange = test.step_func(function() {
+            assert_true(test_scope.expect_durationchange, "HTMLME durationchange event is expected only after changing MediaSource duration");
+            test.done();
+        });
+    });
+
+    var setup_done_cb = test.step_func(function() {
+        test_scope.video = null;
+        // |test_scope.media_source| should be the only remaining reference to HTMLME+MSE.
+        // GC shouldn't collect HTMLME+MSE due to that live reference.
+        assert_equals(test_scope.media_source.readyState, "open", "MediaSource object is open before gc().");
         gc();
-        if (!source_opened) {
-            // Keep trying until we're attached.
-            setTimeout(gc_then_detach_if_attached, 10);
-            return;
-        }
+        assert_equals(test_scope.media_source.readyState, "open", "MediaSource object is open after gc().");
 
-        // Invoking the load algorithm by setting src to '' should detach MSE and also
-        // schedule a 'sourceclose' event on the previously attached MediaSource.
-        detach_started = true;
+        // Verify that HTMLME is still alive by changing the MediaSource object's duration, which causes
+        // a durationchange event to become queued for dispatch on the attached HTMLME (and such dispatch ends this
+        // test.)
+        // Debug note: commenting out the next line should result in test timeout.
+        test_scope.media_source.duration = 100;
+        test_scope.expect_durationchange = true;
+    });
 
-        video.src = '';
-        // There should be a pending sourceclose event on the MediaSource, so even if we drop
-        // the reference to the HTMLME here, gc() still shouldn't collect the MediaSource
-        // due to that pending event dispatch on it.
-        video = null;
+    setup_htmlme_mse_lifetime_test(test, test_scope, handler_setup_cb, setup_done_cb)
+}, "GC of HTMLME+MediaSource preserves at least MediaSource and HTMLME when only the MediaSource reference is held by JS");
+
+async_test(function(test) {
+    // This test builds on the previous two tests. It should fail if either of the previous two tests fails.
+
+    var test_scope = new TestScope();
+
+    var handler_setup_cb = test.step_func(function() {
+        test_scope.video.ondurationchange = test.step_func(function(e) {
+            assert_true(test_scope.expect_durationchange, "HTMLME durationchange event is expected only after changing MediaSource duration");
+            assert_equals(e.target.custom_test_wrapper_update, "testing", "HTMLME wrapper, as adjusted by the test, should be retained");
+            test.done();
+        });
+    });
+
+    var setup_done_cb = test.step_func(function() {
+        // Update the HTMLME JS wrapper with a custom property to be verified later.
+        test_scope.video.custom_test_wrapper_update = "testing";
+        test_scope.video = null;
+        // |test_scope.media_source| should be the only remaining reference to HTMLME+MSE.
+        // GC shouldn't collect HTMLME+MSE due to that live reference.
+        assert_equals(test_scope.media_source.readyState, "open", "MediaSource object is open before gc().");
+        gc();
+        assert_equals(test_scope.media_source.readyState, "open", "MediaSource object is open after gc().");
+
+        // Verify that HTMLME is still alive by changing the MediaSource object's duration, which causes
+        // a durationchange event to become queued for dispatch on the attached HTMLME (and such dispatch ends this
+        // test.)
+        // Debug note: commenting out the next line demonstrates with debug logging that HTMLME+MSE is collected,
+        // followed eventually by a timeout.
+        test_scope.media_source.duration = 100;
+
+        test_scope.expect_durationchange = true;
+        test_scope.media_source = null;
         gc();
     });
 
-    gc_then_detach_if_attached();
+    setup_htmlme_mse_lifetime_test(test, test_scope, handler_setup_cb, setup_done_cb)
+}, "GC of HTMLME+MediaSource preserves at least HTMLME and its wrapper when no references held by JS, but there is a pending HTMLME event");
 
-    // Debugging notes: If this test times out, it's likely due to failure in
-    // keeping HTMLME+MSE alive and pending event dispatch on their wrappers alive
-    // through gc(); either gc() over-collected HTMLME+MSE or event dispatch was
-    // perturbed by gc().
-    // If the asserts in media_source.onsourceclose fail, they are self-explanatory.
-}, "GC of HTMLME+MediaSource preserves HTMLME+MediaSource when only HTMLMediaElement reference is alive, then MediaSource.onsourceclose is dispatched if it was pending when GC occurred with no HTMLME+MSE references");
+async_test(function(test) {
+    var test_scope = new TestScope();
 
+    var handler_setup_cb = null;
+
+    var setup_done_cb = test.step_func(function() {
+        assert_equals(test_scope.media_source.readyState, "open", "MediaSource object is open before gc().");
+        test_scope.media_source = null;
+        assert_equals(test_scope.video.src, test_scope.object_url, "HTMLME src attribute is correct before gc().");
+        // |test_scope.video| should be the only remaining reference to HTMLME+MSE.
+        // GC shouldn't collect HTMLME+MSE due to that live reference.
+
+        // Debug note: setting test_scope.video to null here, and commenting out the assert_equals()
+        // and test.done() lines, below, demonstrates with debug logging that HTMLME+MSE is collected, followed
+        // eventually by a timeout.
+        gc();
+
+        assert_equals(test_scope.video.src, test_scope.object_url, "HTMLME src attribute is correct after gc().");
+        test.done();
+    });
+
+    setup_htmlme_mse_lifetime_test(test, test_scope, handler_setup_cb, setup_done_cb)
+}, "GC of HTMLME+MediaSource preserves at least HTMLME when only the HTMLME reference is held by JS");
+
+async_test(function(test) {
+    // This test builds on the previous test. It should fail if the previous test fails.
+
+    var test_scope = new TestScope();
+
+    var handler_setup_cb = test.step_func(function() {
+        test_scope.media_source.onsourceclose = test.step_func(function() {
+            assert_true(test_scope.expect_sourceclose, "MediaSource sourceclose event is expected only after clearing HTMLME src attribute");
+            // Both HTMLME.onerror and MediaSource.onsourceclose are required to finish this test.
+            if (test_scope.received_error)
+               test.done();
+            test_scope.received_sourceclose = true;
+            test_scope.expect_sourceclose = false;  // Only one sourceclose is expected.
+        });
+
+        test_scope.video.onerror = test.step_func(function() {
+            assert_true(test_scope.expect_error, "HTMLME error event is expected only after clearing HTMLME src attribute");
+            // Both HTMLME.onerror and MediaSource.onsourceclose are required to finish this test.
+            if (test_scope.received_sourceclose)
+               test.done();
+            test_scope.received_error = true;
+            test_scope.expect_error = false;  // Only one error is expected.
+        });
+    });
+
+    var setup_done_cb = test.step_func(function() {
+        assert_equals(test_scope.media_source.readyState, "open", "MediaSource object is open before gc().");
+        test_scope.media_source = null;
+        assert_equals(test_scope.video.src, test_scope.object_url, "HTMLME src attribute is correct before gc().");
+        // |test_scope.video| should be the only remaining reference to HTMLME+MSE.
+        // GC shouldn't collect HTMLME+MSE due to that live reference.
+
+        gc();
+
+        assert_equals(test_scope.video.src, test_scope.object_url, "HTMLME src attribute is correct after gc().");
+
+        // Verify that MediaSource is still alive by clearing the HTMLME object's src attribute, which causes a
+        // sourceclose event to become queued for dispatch on the previously attached MediaSource and an error
+        // event to become queued for dispatch on the HTMLME object (and such dispatches end this test.)
+        // Debug note: commenting out the next line should result in test timeout.
+        test_scope.video.src = "";
+        test_scope.expect_sourceclose = true;
+        test_scope.expect_error = true;
+    });
+
+    setup_htmlme_mse_lifetime_test(test, test_scope, handler_setup_cb, setup_done_cb)
+}, "GC of HTMLME+MediaSource preserves at least MediaSource and HTMLME when only the HTMLME reference is held by JS");
+
+async_test(function(test) {
+    // This test builds on the previous two tests. It should fail if either of the previous two tests fails.
+
+    var test_scope = new TestScope();
+
+    var handler_setup_cb = test.step_func(function() {
+        test_scope.media_source.onsourceclose = test.step_func(function(e) {
+            assert_true(test_scope.expect_sourceclose, "MediaSource sourceclose event is expected only after clearing HTMLME src attribute");
+            assert_equals(e.target.custom_test_wrapper_update, "testing-mediasource", "MediaSource wrapper, as adjusted by the test, should be retained");
+            // Both HTMLME.onerror and MediaSource.onsourceclose are required to finish this test.
+            if (test_scope.received_error)
+               test.done();
+            test_scope.received_sourceclose = true;
+            test_scope.expect_sourceclose = false;  // Only one sourceclose is expected.
+        });
+
+        test_scope.video.onerror = test.step_func(function(e) {
+            assert_true(test_scope.expect_error, "HTMLME error event is expected only after clearing HTMLME src attribute");
+            assert_equals(e.target.custom_test_wrapper_update, "testing-htmlme", "HTMLME wrapper, as adjusted by the test, should be retained");
+            // Both HTMLME.onerror and MediaSource.onsourceclose are required to finish this test.
+            if (test_scope.received_sourceclose)
+               test.done();
+            test_scope.received_error = true;
+            test_scope.expect_error = false;  // Only one error is expected.
+        });
+    });
+
+    var setup_done_cb = test.step_func(function() {
+        // Update the HTMLME and MediaSource JS wrappers with custom properties to be verified later.
+        test_scope.video.custom_test_wrapper_update = "testing-htmlme";
+        test_scope.media_source.custom_test_wrapper_update = "testing-mediasource";
+
+        assert_equals(test_scope.media_source.readyState, "open", "MediaSource object is open before gc().");
+        test_scope.media_source = null;
+        assert_equals(test_scope.video.src, test_scope.object_url, "HTMLME src attribute is correct before gc().");
+        // |test_scope.video| should be the only remaining reference to HTMLME+MSE.
+        // GC shouldn't collect HTMLME+MSE due to that live reference.
+
+        gc();
+
+        assert_equals(test_scope.video.src, test_scope.object_url, "HTMLME src attribute is correct after gc().");
+
+        // Verify that MediaSource is still alive by clearing the HTMLME object's src attribute, which causes a
+        // sourceclose event to become queued for dispatch on the previously attached MediaSource and an error
+        // event to become queued for dispatch on the HTMLME object (and such dispatches end this test.)
+        // Debug note: commenting out the next line demonstrates with debug logging that HTMLME+MSE is collected,
+        // followed eventually by a timeout.
+        test_scope.video.src = "";
+
+        test_scope.expect_sourceclose = true;
+        test_scope.expect_error = true;
+        test_scope.video = null;
+        gc();
+    });
+
+    setup_htmlme_mse_lifetime_test(test, test_scope, handler_setup_cb, setup_done_cb)
+}, "GC of HTMLME+MediaSource preserves at least MediaSource and HTMLME and their wrappers when no references held by JS, but there is a pending event on each of HTMLME and MediaSource");
+
+async_test(function(test) {
+    var test_scope = new TestScope();
+
+    var handler_setup_cb = null;
+
+    var setup_done_cb = test.step_func(function() {
+        var source_buffer = test_scope.media_source.addSourceBuffer('video/webm; codecs="vp8"');
+        test_scope.source_buffer_list = test_scope.media_source.sourceBuffers;
+        source_buffer.custom_test_wrapper_update = "testing-sourcebuffer";
+        assert_true(source_buffer === test_scope.source_buffer_list[0]);
+
+        assert_equals(test_scope.media_source.readyState, "open", "MediaSource object is open before gc().");
+        assert_equals(test_scope.video.src, test_scope.object_url, "HTMLME src attribute is correct before gc().");
+
+        source_buffer = null;
+        test_scope.media_source = null;
+        test_scope.video = null;
+
+
+        // |test_scope.source_buffer_list| should be the only remaining reference to HTMLME+MSE+SBL+SB.
+        // GC shouldn't collected this group due to that live reference.
+
+        // Debug note: setting test_scope.source_buffer_list to null here, and commenting out the assert_equals()
+        // and test.done() lines, below, demonstrates with debug logging that HTMLME+MSE is collected, followed
+        // eventually by a timeout.
+        gc();
+
+        assert_equals(test_scope.source_buffer_list.length, 1, "SBL should survive gc().");
+        assert_equals(test_scope.source_buffer_list[0].custom_test_wrapper_update, "testing-sourcebuffer", "SBL[0]'s wrapper should survive gc().");
+        test.done();
+    });
+
+    setup_htmlme_mse_lifetime_test(test, test_scope, handler_setup_cb, setup_done_cb)
+}, "GC of HTMLME+MediaSource+SBL+SB preserves at least SBL+SB when only the SourceBufferList reference is held by JS");
+
+// TODO(wolenetz): Consider further refactoring to extract specific testing concerns from the following test:
 async_test(function(test) {
     var video = document.createElement("video");
     var media_source = new MediaSource();
@@ -113,6 +316,7 @@
         assert_true(source_buffer === source_buffer_list[0]);
         source_buffer = null;
         media_source = null;
+        video = null;
         // source_buffer_list's reference by us is the only thing keeping HTMLME+MSE alive.
         gc();
 
diff --git a/third_party/blink/web_tests/platform/android/webaudio/dom-exceptions-expected.txt b/third_party/blink/web_tests/platform/android/webaudio/dom-exceptions-expected.txt
index f3b4630c7..6d34db4 100644
--- a/third_party/blink/web_tests/platform/android/webaudio/dom-exceptions-expected.txt
+++ b/third_party/blink/web_tests/platform/android/webaudio/dom-exceptions-expected.txt
@@ -1,5 +1,3 @@
-CONSOLE WARNING: line 39: The Web Audio autoplay policy will be re-enabled in Chrome 71 (December 2018). Please check that your website is compatible with it. https://goo.gl/7K7WLu
-CONSOLE WARNING: line 43: The Web Audio autoplay policy will be re-enabled in Chrome 71 (December 2018). Please check that your website is compatible with it. https://goo.gl/7K7WLu
 CONSOLE WARNING: line 347: The provided value 'fancy' is not a valid enum value of type ChannelCountMode.
 CONSOLE WARNING: line 353: The provided value 'undefined' is not a valid enum value of type ChannelInterpretation.
 CONSOLE WARNING: line 523: The provided value '9x' is not a valid enum value of type OverSampleType.
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
index 6e5c968..61ce18b1 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-image-shape-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-image-shape-expected.png
index cb7c834..5a305b0d 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-image-shape-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-image-shape-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png
index b5c052c..5c24627 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
index 47e0214b..bb16739 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
deleted file mode 100644
index 1d114a199..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/jpeg-with-color-profile-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/jpeg-with-color-profile-expected.png
index b639c3ec..cbeb2e6 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/jpeg-with-color-profile-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/jpeg-with-color-profile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/png-with-color-profile-expected.png b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/png-with-color-profile-expected.png
index 68a45519..5826e67 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/png-with-color-profile-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/gpu-rasterization/images/png-with-color-profile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png
index 8958cfc..23ac474 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
index 30eac617..0f5a854 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
index c8af289..d8c22a3f 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
index c8af289..d8c22a3f 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-filter-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-filter-expected.png
index ba6f77e..78af5617 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-filter-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-filter-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-canvas-svg-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-canvas-svg-expected.png
index d2a33ef23..5534a0a 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-canvas-svg-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-canvas-svg-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-expected.png
index 9f18da4..dfd5dd9b 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
index a24c8a80..ce9a8ec 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-shape-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-shape-expected.png
index 33e921a..a047b8a9 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-shape-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-image-shape-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png
index 9fdab925..99a0fa2 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
index 8836ec8..0ec66be 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
index 17f1093..812b4172 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/jpeg-with-color-profile-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/jpeg-with-color-profile-expected.png
index 606a2ba..409034d 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/jpeg-with-color-profile-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/jpeg-with-color-profile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/png-suite/test-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/png-suite/test-expected.png
index 5669961b..a27c8f7 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/png-suite/test-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/png-suite/test-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/png-with-color-profile-expected.png b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/png-with-color-profile-expected.png
index e980abc..1793297 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/png-with-color-profile-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/gpu-rasterization/images/png-with-color-profile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
index 0f9200da..2e8ba71 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
index 0f9200da..2e8ba71 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-background-image-cross-fade-png-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-filter-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-filter-expected.png
index cab7e39..ed55e04 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-filter-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-filter-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-canvas-svg-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-canvas-svg-expected.png
index f746b29..2b52d15 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-canvas-svg-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-canvas-svg-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-expected.png
index 9189b38..3fb2ae1 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
index 0edf47c..bd8f0a4 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-filter-all-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-shape-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-shape-expected.png
index 945d627..c5fd94b 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-shape-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-image-shape-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png
index 643fd57..8ec518c 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-munsell-adobe-to-srgb-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
index 1ae879f3..fa90c8d 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/color-profile-svg-fill-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
index 1d114a199..1dde38a 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/cross-fade-background-size-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/jpeg-with-color-profile-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/jpeg-with-color-profile-expected.png
index 3b367c8c..0f34595 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/jpeg-with-color-profile-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/jpeg-with-color-profile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/png-suite/test-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/png-suite/test-expected.png
index 07f962b..c2a39b179 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/png-suite/test-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/png-suite/test-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/png-with-color-profile-expected.png b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/png-with-color-profile-expected.png
index 63723587..d108381f 100644
--- a/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/png-with-color-profile-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/gpu-rasterization/images/png-with-color-profile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/cHRM_color_spin-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/cHRM_color_spin-expected.png
index 98f4e326..25c8469 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/cHRM_color_spin-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/cHRM_color_spin-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-jpeg-with-color-profile-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-jpeg-with-color-profile-expected.png
index 38fd7e76e6..116b3af 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-jpeg-with-color-profile-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-jpeg-with-color-profile-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-animate-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-animate-expected.png
index 4272612..a0005890 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-animate-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-animate-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-animate-rotate-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-animate-rotate-expected.png
index 6ffa5b9..f5f0fd2e 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-animate-rotate-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-animate-rotate-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-cover-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-cover-expected.png
index bd4d0a92..bb4b4529 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-cover-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-cover-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png
index 7e35ead..302ccbb 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-repeat-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-space-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-space-expected.png
index 4af1e46..72b8ba2f 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-space-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-background-image-space-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-border-fade-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-border-fade-expected.png
index 394fc2d..2a828885 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-border-fade-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-border-fade-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-border-image-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
index 19253f9..7f4af2f 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-clip-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-clip-expected.png
index 4a6d733..33914da 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-clip-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-clip-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-iframe-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-iframe-expected.png
index 38fb2934..ed1c1b5 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-iframe-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-iframe-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-canvas-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-canvas-expected.png
index 946733b..2a2b17a 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-canvas-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-canvas-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-canvas-pattern-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-canvas-pattern-expected.png
index ce811bd..e433e7a 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-canvas-pattern-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-canvas-pattern-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-object-fit-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-object-fit-expected.png
index 20942d0b..4eb6af0f 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-object-fit-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-object-fit-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-pseudo-content-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-pseudo-content-expected.png
index f5ea9c3c..ec814c1 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-pseudo-content-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-pseudo-content-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-svg-resource-url-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-svg-resource-url-expected.png
index 596f7fc..f054daf 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-svg-resource-url-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-image-svg-resource-url-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png
index 5a6c79d..d6e52497 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-mask-image-svg-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-object-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-object-expected.png
index a442c0b..d2a4136 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-object-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-object-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-svg-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-svg-expected.png
index 1e9d0a8..0c889d8 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-svg-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-svg-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-svg-foreign-object-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-svg-foreign-object-expected.png
index f2de994b6e..9ca782e 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-svg-foreign-object-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/color-profile-svg-foreign-object-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/webp-color-profile-lossless-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/webp-color-profile-lossless-expected.png
index fae815da..9c5eeca 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/webp-color-profile-lossless-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/webp-color-profile-lossless-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/webp-color-profile-lossy-alpha-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/webp-color-profile-lossy-alpha-expected.png
index 8905b3a..4c789fc 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/webp-color-profile-lossy-alpha-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/webp-color-profile-lossy-alpha-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/gpu-rasterization/images/webp-color-profile-lossy-expected.png b/third_party/blink/web_tests/virtual/gpu-rasterization/images/webp-color-profile-lossy-expected.png
index 3a6fa1f..a4a111e 100644
--- a/third_party/blink/web_tests/virtual/gpu-rasterization/images/webp-color-profile-lossy-expected.png
+++ b/third_party/blink/web_tests/virtual/gpu-rasterization/images/webp-color-profile-lossy-expected.png
Binary files differ
diff --git a/third_party/feed/README.chromium b/third_party/feed/README.chromium
index 9f8130b..c90513ded 100644
--- a/third_party/feed/README.chromium
+++ b/third_party/feed/README.chromium
@@ -2,7 +2,7 @@
 Short name: feed
 URL: https://chromium.googlesource.com/feed
 Version: 0
-Revision: 4da2d86623e7cb85126f5ef2c6894a76941e8c69
+Revision: 45d963c29e87258796293589f724ac08dab37cd0
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/libjingle_xmpp/xmpp/prexmppauth.h b/third_party/libjingle_xmpp/xmpp/prexmppauth.h
index 2ba0f4b..7543b20 100644
--- a/third_party/libjingle_xmpp/xmpp/prexmppauth.h
+++ b/third_party/libjingle_xmpp/xmpp/prexmppauth.h
@@ -49,7 +49,6 @@
 
   virtual void StartPreXmppAuth(
     const Jid& jid,
-    const rtc::SocketAddress& server,
     const std::string& pass,
     const std::string& auth_mechanism,
     const std::string& auth_token) = 0;
diff --git a/third_party/libjingle_xmpp/xmpp/xmppclient.cc b/third_party/libjingle_xmpp/xmpp/xmppclient.cc
index 06956e3..40e7b3f 100644
--- a/third_party/libjingle_xmpp/xmpp/xmppclient.cc
+++ b/third_party/libjingle_xmpp/xmpp/xmppclient.cc
@@ -30,7 +30,6 @@
     client_(client),
     socket_(),
     engine_(),
-    proxy_port_(0),
     pre_engine_error_(XmppEngine::ERROR_NONE),
     pre_engine_subcode_(0),
     signal_closed_(false),
@@ -53,8 +52,6 @@
   std::string auth_mechanism_;
   std::string auth_token_;
   rtc::SocketAddress server_;
-  std::string proxy_host_;
-  int proxy_port_;
   XmppEngine::Error pre_engine_error_;
   int pre_engine_subcode_;
   CaptchaChallenge captcha_challenge_;
@@ -133,8 +130,6 @@
   d_->auth_mechanism_ = settings.auth_mechanism();
   d_->auth_token_ = settings.auth_token();
   d_->server_ = settings.server();
-  d_->proxy_host_ = settings.proxy_host();
-  d_->proxy_port_ = settings.proxy_port();
   d_->allow_plain_ = settings.allow_plain();
   d_->pre_auth_.reset(pre_auth);
 
@@ -197,7 +192,7 @@
   if (d_->pre_auth_) {
     d_->pre_auth_->SignalAuthDone.connect(this, &XmppClient::OnAuthDone);
     d_->pre_auth_->StartPreXmppAuth(
-        d_->engine_->GetUser(), d_->server_, d_->pass_,
+        d_->engine_->GetUser(), d_->pass_,
         d_->auth_mechanism_, d_->auth_token_);
     d_->pass_.clear(); // done with this;
     return STATE_PRE_XMPP_LOGIN;
diff --git a/third_party/libjingle_xmpp/xmpp/xmppclientsettings.h b/third_party/libjingle_xmpp/xmpp/xmppclientsettings.h
index dd2330f1f..d96317e4 100644
--- a/third_party/libjingle_xmpp/xmpp/xmppclientsettings.h
+++ b/third_party/libjingle_xmpp/xmpp/xmppclientsettings.h
@@ -21,8 +21,6 @@
   PROTO_SSLTCP = 2,  // Pseudo-TLS.
 };
 
-enum ProxyType { PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN };
-
 class XmppUserSettings {
  public:
   XmppUserSettings()
@@ -69,41 +67,19 @@
 
 class XmppClientSettings : public XmppUserSettings {
  public:
-  XmppClientSettings()
-      : protocol_(PROTO_TCP),
-        proxy_(PROXY_NONE),
-        proxy_port_(80),
-        use_proxy_auth_(false) {}
+  XmppClientSettings() : protocol_(PROTO_TCP) {}
 
   void set_server(const rtc::SocketAddress& server) {
       server_ = server;
   }
   void set_protocol(ProtocolType protocol) { protocol_ = protocol; }
-  void set_proxy(ProxyType f) { proxy_ = f; }
-  void set_proxy_host(const std::string& host) { proxy_host_ = host; }
-  void set_proxy_port(int port) { proxy_port_ = port; };
-  void set_use_proxy_auth(bool f) { use_proxy_auth_ = f; }
-  void set_proxy_user(const std::string& user) { proxy_user_ = user; }
-  void set_proxy_pass(const std::string& pass) { proxy_pass_ = pass; }
 
   const rtc::SocketAddress& server() const { return server_; }
   ProtocolType protocol() const { return protocol_; }
-  ProxyType proxy() const { return proxy_; }
-  const std::string& proxy_host() const { return proxy_host_; }
-  int proxy_port() const { return proxy_port_; }
-  bool use_proxy_auth() const { return use_proxy_auth_; }
-  const std::string& proxy_user() const { return proxy_user_; }
-  const std::string& proxy_pass() const { return proxy_pass_; }
 
  private:
   rtc::SocketAddress server_;
   ProtocolType protocol_;
-  ProxyType proxy_;
-  std::string proxy_host_;
-  int proxy_port_;
-  bool use_proxy_auth_;
-  std::string proxy_user_;
-  std::string proxy_pass_;
 };
 
 }
diff --git a/third_party/liblouis/PRESUBMIT.py b/third_party/liblouis/PRESUBMIT.py
new file mode 100644
index 0000000..d44e905a
--- /dev/null
+++ b/third_party/liblouis/PRESUBMIT.py
@@ -0,0 +1,54 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Presubmit script for liblouis repository."""
+
+import json
+import sys
+
+def CheckChangeOnUpload(input_api, output_api):
+  if not sys.platform.startswith('linux'):
+    return []
+  sys.path.insert(0, input_api.os_path.join(
+      input_api.PresubmitLocalPath()))
+  try:
+    import liblouis_list_tables
+  finally:
+    sys.path.pop(0)
+  errors, new_tables = liblouis_list_tables.CheckTables("tables.json")
+  results = []
+  for x in errors:
+    results.append(output_api.PresubmitError(x))
+
+  # Write a suggested json for new tables.
+  if len(new_tables) > 0:
+    new_json = []
+    for table in new_tables:
+      name = table.split('.')[0]
+      name_parts = name.split('-')
+
+      # These are guesses as to the grade and dots based on the filename. The
+      # suggestion still needs to be validated.
+      dots = "6"
+      if "comp8" in name:
+        dots = "8"
+
+      grade = "1"
+      if "g0" in name:
+        grade = "0"
+      elif "g2" in name:
+        grade = "2"
+      elif "g3" in name:
+        grade = "3"
+
+      new_json.append({
+          "id": name,
+          "locale": name_parts[0],
+          "dots": dots,
+          "grade": grade,
+          "fileNames": table
+      })
+    results.append(output_api.PresubmitError("Suggested additions to " +
+                                             "tables.json (please edit and validate):"))
+    results.append(output_api.PresubmitError(json.dumps(new_json, indent=2)))
+  return results
diff --git a/third_party/liblouis/copy_tables.py b/third_party/liblouis/copy_tables.py
deleted file mode 100755
index 8cab92e..0000000
--- a/third_party/liblouis/copy_tables.py
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-'''Copies the liblouis braille translation tables to a destination.'''
-
-import liblouis_list_tables
-import optparse
-import os
-import shutil
-
-
-def LinkOrCopyFiles(sources, dest_dir):
-  def LinkOrCopyOneFile(src, dst):
-    if os.path.exists(dst):
-      os.unlink(dst)
-    try:
-      os.link(src, dst)
-    except:
-      shutil.copy(src, dst)
-
-  if not os.path.exists(dest_dir):
-    os.makedirs(dest_dir)
-  for source in sources:
-    LinkOrCopyOneFile(source, os.path.join(dest_dir, os.path.basename(source)))
-
-
-def WriteDepfile(depfile, infiles):
-  stampfile = depfile + '.stamp'
-  with open(stampfile, 'w'):
-    os.utime(stampfile, None)
-  content = '%s: %s' % (stampfile, ' '.join(infiles))
-  open(depfile, 'w').write(content)
-
-
-
-def main():
-  parser = optparse.OptionParser(description=__doc__)
-  parser.add_option('-D', '--directory', dest='directories',
-                     action='append', help='Where to search for table files')
-  parser.add_option('-e', '--extra_file', dest='extra_files', action='append',
-                    default=[], help='Extra liblouis table file to process')
-  parser.add_option('-d', '--dest_dir', action='store', metavar='DIR',
-                    help=('Destination directory.  Used when translating ' +
-                          'input paths to output paths and when copying '
-                          'files.'))
-  parser.add_option('--depfile', metavar='FILENAME',
-                    help=('Store .d style dependencies in FILENAME and touch '
-                          'FILENAME.stamp after copying the files'))
-  options, args = parser.parse_args()
-
-  if len(args) != 1:
-    parser.error('Expecting exactly one argument')
-  if not options.directories:
-    parser.error('At least one --directory option must be specified')
-  if not options.dest_dir:
-    parser.error('At least one --dest_dir option must be specified')
-  files = liblouis_list_tables.GetTableFiles(args[0], options.directories,
-                                             options.extra_files)
-  LinkOrCopyFiles(files, options.dest_dir)
-  if options.depfile:
-    WriteDepfile(options.depfile, files)
-
-
-if __name__ == '__main__':
-  main()
diff --git a/third_party/liblouis/liblouis_list_tables.py b/third_party/liblouis/liblouis_list_tables.py
index 6283400..6b2b300c 100755
--- a/third_party/liblouis/liblouis_list_tables.py
+++ b/third_party/liblouis/liblouis_list_tables.py
@@ -13,6 +13,14 @@
 # Matches the include statement in the braille table files.
 INCLUDE_RE = re.compile(r"^\s*include\s+([^#\s]+)")
 
+# Exclude these files from table validation.
+IGNORE_TABLES = set()
+IGNORE_TABLES.add('boxes.ctb')
+IGNORE_TABLES.add('unicode-braille.utb')
+IGNORE_TABLES.add('IPA.utb')
+IGNORE_TABLES.add('spaces.ctb')
+IGNORE_TABLES.add('ethio-g1.ctb')
+IGNORE_TABLES.add('or-in-g1.utb')
 
 def Error(msg):
   print >> sys.stderr, 'liblouis_list_tables: %s' % msg
@@ -40,6 +48,14 @@
   Error('File not found: %s' % filename)
 
 
+def FindAllTableFiles(directory):
+  ret = set()
+  for filename in os.listdir(directory):
+    if filename.endswith(".ctb") or filename.endswith(".utb"):
+      ret.add(filename)
+  return ret
+
+
 def GetIncludeFiles(filename):
   result = []
   with open(ToNativePath(filename), 'r') as fh:
@@ -50,13 +66,13 @@
   return result
 
 
-def ProcessFile(output_set, filename, directories):
+def ProcessFile(output_set, filename, directories, indent=0):
   fullname = FindFile(filename, directories)
   if fullname in output_set:
     return
-  output_set.add(fullname)
+  output_set.add(indent*" " + fullname)
   for include_file in GetIncludeFiles(fullname):
-    ProcessFile(output_set, include_file, directories)
+    ProcessFile(output_set, include_file, directories, indent=2)
 
 
 def GetTableFiles(tables_file, directories, extra_files):
@@ -70,6 +86,25 @@
   return output_set
 
 
+def CheckTables(tables_file):
+  tables = LoadTablesFile(tables_file)
+  actual_set = set();
+  for table in tables:
+    for name in table['fileNames'].split(','):
+      actual_set.add(name)
+  expected_set = FindAllTableFiles("src/tables")
+  output = []
+  for table in actual_set:
+    if table not in expected_set and table not in IGNORE_TABLES:
+      output.append("Error: obsolete table not in liblouis " + table)
+
+  new_tables = []
+  for table in expected_set:
+    if table not in actual_set and table not in IGNORE_TABLES:
+      output.append("Error: table not found in tables file " + table)
+      new_tables.append(table)
+  return (output, new_tables)
+
 def DoMain(argv):
   "Entry point for gyp's pymod_do_main command."
   parser = optparse.OptionParser()
diff --git a/third_party/liblouis/tables.json b/third_party/liblouis/tables.json
index a4cdda7..2ba86b4 100644
--- a/third_party/liblouis/tables.json
+++ b/third_party/liblouis/tables.json
@@ -390,5 +390,754 @@
     "locale": "zh",
     "dots": "8",
     "fileNames": "zh-hk.ctb"
+  },
+  {
+    "locale": "ml",
+    "dots": "6",
+    "id": "ml-in-g1",
+    "grade": "1",
+    "fileNames": "ml-in-g1.utb"
+  },
+  {
+    "locale": "nl",
+    "dots": "6",
+    "id": "nl-NL-g0",
+    "grade": "0",
+    "fileNames": "nl-NL-g0.utb"
+  },
+  {
+    "locale": "da",
+    "dots": "8",
+    "id": "da-dk-g28l",
+    "grade": "2",
+    "fileNames": "da-dk-g28l.ctb"
+  },
+  {
+    "locale": "sk",
+    "dots": "6",
+    "id": "sk-g1",
+    "grade": "1",
+    "fileNames": "sk-g1.ctb"
+  },
+  {
+    "locale": "hr",
+    "dots": "8",
+    "id": "hr-comp8",
+    "grade": "1",
+    "fileNames": "hr-comp8.utb"
+  },
+  {
+    "locale": "sa",
+    "dots": "6",
+    "id": "sa-in-g1",
+    "grade": "1",
+    "fileNames": "sa-in-g1.utb"
+  },
+  {
+    "locale": "gu",
+    "dots": "6",
+    "id": "gu-in-g1",
+    "grade": "1",
+    "fileNames": "gu-in-g1.utb"
+  },
+  {
+    "locale": "mwr",
+    "dots": "6",
+    "id": "mwr",
+    "grade": "1",
+    "fileNames": "mwr.ctb"
+  },
+  {
+    "locale": "tr",
+    "dots": "6",
+    "id": "tr-g1",
+    "grade": "1",
+    "fileNames": "tr-g1.ctb"
+  },
+  {
+    "locale": "fr",
+    "dots": "6",
+    "id": "fr-bfu-comp6",
+    "grade": "1",
+    "fileNames": "fr-bfu-comp6.utb"
+  },
+  {
+    "locale": "en",
+    "dots": "6",
+    "id": "en-us-mathtext",
+    "grade": "1",
+    "fileNames": "en-us-mathtext.ctb"
+  },
+  {
+    "locale": "iu",
+    "dots": "6",
+    "id": "iu-ca-g1",
+    "grade": "1",
+    "fileNames": "iu-ca-g1.ctb"
+  },
+  {
+    "locale": "si",
+    "dots": "6",
+    "id": "si-in-g1",
+    "grade": "1",
+    "fileNames": "si-in-g1.utb"
+  },
+  {
+    "locale": "ga",
+    "dots": "6",
+    "id": "ga-g2",
+    "grade": "2",
+    "fileNames": "ga-g2.ctb"
+  },
+  {
+    "locale": "no",
+    "dots": "8",
+    "id": "no-no-comp8",
+    "grade": "1",
+    "fileNames": "no-no-comp8.ctb"
+  },
+  {
+    "locale": "en",
+    "dots": "6",
+    "id": "en-us-compbrl",
+    "grade": "1",
+    "fileNames": "en-us-compbrl.ctb"
+  },
+  {
+    "locale": "vi",
+    "dots": "6",
+    "id": "vi-g1",
+    "grade": "1",
+    "fileNames": "vi-g1.ctb"
+  },
+  {
+    "locale": "as",
+    "dots": "6",
+    "id": "as-in-g1",
+    "grade": "1",
+    "fileNames": "as-in-g1.utb"
+  },
+  {
+    "locale": "mt",
+    "dots": "6",
+    "id": "mt",
+    "grade": "1",
+    "fileNames": "mt.ctb"
+  },
+  {
+    "locale": "ne",
+    "dots": "6",
+    "id": "ne",
+    "grade": "1",
+    "fileNames": "ne.ctb"
+  },
+  {
+    "locale": "be",
+    "dots": "6",
+    "id": "be-in-g1",
+    "grade": "1",
+    "fileNames": "be-in-g1.utb"
+  },
+  {
+    "locale": "mn",
+    "dots": "6",
+    "id": "mn-MN-g2",
+    "grade": "2",
+    "fileNames": "mn-MN-g2.ctb"
+  },
+  {
+    "locale": "fi",
+    "dots": "6",
+    "id": "fi",
+    "grade": "1",
+    "fileNames": "fi.utb"
+  },
+  {
+    "locale": "ne",
+    "dots": "6",
+    "id": "np-in-g1",
+    "grade": "1",
+    "fileNames": "np-in-g1.utb"
+  },
+  {
+    "locale": "ko",
+    "dots": "6",
+    "id": "ko-2006-g2",
+    "grade": "2",
+    "fileNames": "ko-2006-g2.ctb"
+  },
+  {
+    "locale": "mn",
+    "dots": "6",
+    "id": "mn-in-g1",
+    "grade": "1",
+    "fileNames": "mn-in-g1.utb"
+  },
+  {
+    "locale": "ckb",
+    "dots": "6",
+    "id": "ckb-g1",
+    "grade": "1",
+    "fileNames": "ckb-g1.ctb"
+  },
+  {
+    "locale": "et",
+    "dots": "6",
+    "id": "et",
+    "grade": "1",
+    "fileNames": "et.ctb"
+  },
+  {
+    "locale": "el",
+    "dots": "6",
+    "id": "gr-bb",
+    "grade": "1",
+    "fileNames": "gr-bb.ctb"
+  },
+  {
+    "locale": "bh",
+    "dots": "6",
+    "id": "bh",
+    "grade": "1",
+    "fileNames": "bh.ctb"
+  },
+  {
+    "locale": "mun",
+    "dots": "6",
+    "id": "mun",
+    "grade": "1",
+    "fileNames": "mun.ctb"
+  },
+  {
+    "locale": "en",
+    "dots": "6",
+    "id": "en-in-g1",
+    "grade": "1",
+    "fileNames": "en-in-g1.ctb"
+  },
+  {
+    "locale": "gon",
+    "dots": "6",
+    "id": "gon",
+    "grade": "1",
+    "fileNames": "gon.ctb"
+  },
+  {
+    "locale": "hy",
+    "dots": "6",
+    "id": "hy",
+    "grade": "1",
+    "fileNames": "hy.ctb"
+  },
+  {
+    "locale": "en",
+    "dots": "6",
+    "id": "en-us-comp6",
+    "grade": "1",
+    "fileNames": "en-us-comp6.ctb"
+  },
+  {
+    "locale": "en",
+    "dots": "6",
+    "id": "en-chess",
+    "grade": "1",
+    "fileNames": "en-chess.ctb"
+  },
+  {
+    "locale": "ta",
+    "dots": "6",
+    "id": "ta",
+    "grade": "1",
+    "fileNames": "ta.ctb"
+  },
+  {
+    "locale": "mao",
+    "dots": "6",
+    "id": "mao-nz-g1",
+    "grade": "1",
+    "fileNames": "mao-nz-g1.ctb"
+  },
+  {
+    "locale": "bo",
+    "dots": "6",
+    "id": "bo",
+    "grade": "1",
+    "fileNames": "bo.ctb"
+  },
+  {
+    "locale": "pu",
+    "dots": "6",
+    "id": "pu-in-g1",
+    "grade": "1",
+    "fileNames": "pu-in-g1.utb"
+  },
+  {
+    "locale": "fa",
+    "dots": "8",
+    "id": "fa-ir-comp8",
+    "grade": "1",
+    "fileNames": "fa-ir-comp8.ctb"
+  },
+  {
+    "locale": "sin",
+    "dots": "6",
+    "id": "sin",
+    "grade": "1",
+    "fileNames": "sin.utb"
+  },
+  {
+    "locale": "kh",
+    "dots": "6",
+    "id": "kh-in-g1",
+    "grade": "1",
+    "fileNames": "kh-in-g1.utb"
+  },
+  {
+    "locale": "cs",
+    "dots": "8",
+    "id": "cs-comp8",
+    "grade": "1",
+    "fileNames": "cs-comp8.utb"
+  },
+  {
+    "locale": "zh",
+    "dots": "6",
+    "id": "zhcn-g1",
+    "grade": "1",
+    "fileNames": "zhcn-g1.ctb"
+  },
+  {
+    "locale": "tsn",
+    "dots": "6",
+    "id": "tsn-za-g1",
+    "grade": "1",
+    "fileNames": "tsn-za-g1.ctb"
+  },
+  {
+    "locale": "sv",
+    "dots": "6",
+    "id": "sv-1989",
+    "grade": "1",
+    "fileNames": "sv-1989.ctb"
+  },
+  {
+    "locale": "br",
+    "dots": "6",
+    "id": "br-in-g1",
+    "grade": "1",
+    "fileNames": "br-in-g1.utb"
+  },
+  {
+    "locale": "ko",
+    "dots": "6",
+    "id": "ko-2006-g1",
+    "grade": "1",
+    "fileNames": "ko-2006-g1.ctb"
+  },
+  {
+    "locale": "pl",
+    "dots": "8",
+    "id": "pl-pl-comp8",
+    "grade": "1",
+    "fileNames": "pl-pl-comp8.ctb"
+  },
+  {
+    "locale": "ur",
+    "dots": "6",
+    "id": "ur-pk-g2",
+    "grade": "2",
+    "fileNames": "ur-pk-g2.ctb"
+  },
+  {
+    "locale": "ka",
+    "dots": "6",
+    "id": "ka-in-g1",
+    "grade": "1",
+    "fileNames": "ka-in-g1.utb"
+  },
+  {
+    "locale": "ru",
+    "dots": "6",
+    "id": "ru-ru-g1",
+    "grade": "1",
+    "fileNames": "ru-ru-g1.utb"
+  },
+  {
+    "locale": "en",
+    "dots": "6",
+    "id": "haw-us-g1",
+    "grade": "1",
+    "fileNames": "haw-us-g1.ctb"
+  },
+  {
+    "locale": "no",
+    "dots": "6",
+    "id": "no-no-generic",
+    "grade": "1",
+    "fileNames": "no-no-generic.ctb"
+  },
+  {
+    "locale": "cy",
+    "dots": "6",
+    "id": "cy-cy-g2",
+    "grade": "2",
+    "fileNames": "cy-cy-g2.ctb"
+  },
+  {
+    "locale": "en",
+    "dots": "6",
+    "id": "en-us-interline",
+    "grade": "1",
+    "fileNames": "en-us-interline.ctb"
+  },
+  {
+    "locale": "nl",
+    "dots": "6",
+    "id": "nl-BE-g0",
+    "grade": "0",
+    "fileNames": "nl-BE-g0.utb"
+  },
+  {
+    "locale": "ta",
+    "dots": "6",
+    "id": "ta-ta-g1",
+    "grade": "1",
+    "fileNames": "ta-ta-g1.ctb"
+  },
+  {
+    "locale": "da",
+    "dots": "6",
+    "id": "da-lt",
+    "grade": "1",
+    "fileNames": "da-lt.ctb"
+  },
+  {
+    "locale": "fi",
+    "dots": "6",
+    "id": "fi-fi",
+    "grade": "1",
+    "fileNames": "fi-fi.ctb"
+  },
+  {
+    "locale": "tsn",
+    "dots": "6",
+    "id": "afr-za-g1",
+    "grade": "1",
+    "fileNames": "afr-za-g1.ctb"
+  },
+  {
+    "locale": "da",
+    "dots": "6",
+    "id": "da-dk-g26-lit",
+    "grade": "2",
+    "fileNames": "da-dk-g26-lit.ctb"
+  },
+  {
+    "locale": "no",
+    "dots": "6",
+    "id": "no-no-8dot-fallback-6dot-g0",
+    "grade": "0",
+    "fileNames": "no-no-8dot-fallback-6dot-g0.utb"
+  },
+  {
+    "locale": "en_US",
+    "dots": "8",
+    "id": "en-us-comp8-ext",
+    "grade": "1",
+    "fileNames": "en-us-comp8-ext.utb"
+  },
+  {
+    "locale": "es",
+    "dots": "6",
+    "id": "Es-Es-g1",
+    "grade": "1",
+    "fileNames": "Es-Es-g1.utb"
+  },
+  {
+    "locale": "fi",
+    "dots": "6",
+    "id": "fi1",
+    "grade": "1",
+    "fileNames": "fi1.ctb"
+  },
+  {
+    "locale": "el",
+    "dots": "6",
+    "id": "el",
+    "grade": "1",
+    "fileNames": "el.ctb"
+  },
+  {
+    "locale": "kru",
+    "dots": "6",
+    "id": "kru",
+    "grade": "1",
+    "fileNames": "kru.ctb"
+  },
+  {
+    "locale": "en",
+    "dots": "6",
+    "id": "mr-in-g1",
+    "grade": "1",
+    "fileNames": "mr-in-g1.utb"
+  },
+  {
+    "locale": "zh",
+    "dots": "6",
+    "id": "zh-chn",
+    "grade": "1",
+    "fileNames": "zh-chn.ctb"
+  },
+  {
+    "locale": "de",
+    "dots": "6",
+    "id": "de-chess",
+    "grade": "1",
+    "fileNames": "de-chess.ctb"
+  },
+  {
+    "locale": "en",
+    "dots": "6",
+    "id": "en-ueb-math",
+    "grade": "1",
+    "fileNames": "en-ueb-math.ctb"
+  },
+  {
+    "locale": "cy",
+    "dots": "6",
+    "id": "cy-cy-g1",
+    "grade": "1",
+    "fileNames": "cy-cy-g1.utb"
+  },
+  {
+    "locale": "lt",
+    "dots": "6",
+    "id": "lt-6dot",
+    "grade": "1",
+    "fileNames": "lt-6dot.utb"
+  },
+  {
+    "locale": "eo",
+    "dots": "6",
+    "id": "eo-g1",
+    "grade": "1",
+    "fileNames": "eo-g1.ctb"
+  },
+  {
+    "locale": "pi",
+    "dots": "6",
+    "id": "pi",
+    "grade": "1",
+    "fileNames": "pi.ctb"
+  },
+  {
+    "locale": "zh",
+    "dots": "6",
+    "id": "zhcn-g2",
+    "grade": "2",
+    "fileNames": "zhcn-g2.ctb"
+  },
+  {
+    "locale": "fr",
+    "dots": "8",
+    "id": "fr-bfu-comp8",
+    "grade": "1",
+    "fileNames": "fr-bfu-comp8.utb"
+  },
+  {
+    "locale": "eo",
+    "dots": "6",
+    "id": "eo-g1-x-system",
+    "grade": "1",
+    "fileNames": "eo-g1-x-system.ctb"
+  },
+  {
+    "locale": "ks",
+    "dots": "6",
+    "id": "ks-in-g1",
+    "grade": "1",
+    "fileNames": "ks-in-g1.utb"
+  },
+  {
+    "locale": "da",
+    "dots": "8",
+    "id": "da-dk-g28",
+    "grade": "2",
+    "fileNames": "da-dk-g28.ctb"
+  },
+  {
+    "locale": "kok",
+    "dots": "6",
+    "id": "kok",
+    "grade": "1",
+    "fileNames": "kok.ctb"
+  },
+  {
+    "locale": "da",
+    "dots": "6",
+    "id": "da-dk-g26l-lit",
+    "grade": "2",
+    "fileNames": "da-dk-g26l-lit.ctb"
+  },
+  {
+    "locale": "ar",
+    "dots": "6",
+    "id": "ar-fa",
+    "grade": "1",
+    "fileNames": "ar-fa.utb"
+  },
+  {
+    "locale": "da",
+    "dots": "6",
+    "id": "da-dk-g16-lit",
+    "grade": "1",
+    "fileNames": "da-dk-g16-lit.ctb"
+  },
+  {
+    "locale": "hr",
+    "dots": "6",
+    "id": "hr-g1",
+    "grade": "1",
+    "fileNames": "hr-g1.ctb"
+  },
+  {
+    "locale": "uk",
+    "dots": "6",
+    "id": "uk",
+    "grade": "1",
+    "fileNames": "uk.utb"
+  },
+  {
+    "locale": "aw",
+    "dots": "6",
+    "id": "aw-in-g1",
+    "grade": "1",
+    "fileNames": "aw-in-g1.utb"
+  },
+  {
+    "locale": "en",
+    "dots": "6",
+    "id": "chr-us-g1",
+    "grade": "1",
+    "fileNames": "chr-us-g1.ctb"
+  },
+  {
+    "locale": "no",
+    "dots": "8",
+    "id": "no-no-8dot",
+    "grade": "1",
+    "fileNames": "no-no-8dot.utb"
+  },
+  {
+    "locale": "da",
+    "dots": "8",
+    "id": "da-dk-g08",
+    "grade": "0",
+    "fileNames": "da-dk-g08.ctb"
+  },
+  {
+    "locale": "en",
+    "dots": "6",
+    "id": "sot-za-g1",
+    "grade": "1",
+    "fileNames": "sot-za-g1.ctb"
+  },
+  {
+    "locale": "da",
+    "dots": "6",
+    "id": "da-dk-g26l",
+    "grade": "2",
+    "fileNames": "da-dk-g26l.ctb"
+  },
+  {
+    "locale": "dra",
+    "dots": "6",
+    "id": "dra",
+    "grade": "1",
+    "fileNames": "dra.ctb"
+  },
+  {
+    "locale": "hu",
+    "dots": "6",
+    "id": "hu-hu-g2",
+    "grade": "2",
+    "fileNames": "hu-hu-g2.ctb"
+  },
+  {
+    "locale": "ga",
+    "dots": "6",
+    "id": "ga-g1",
+    "grade": "1",
+    "fileNames": "ga-g1.utb"
+  },
+  {
+    "locale": "te",
+    "dots": "6",
+    "id": "te-in-g1",
+    "grade": "1",
+    "fileNames": "te-in-g1.utb"
+  },
+  {
+    "locale": "fi",
+    "dots": "6",
+    "id": "fi2",
+    "grade": "2",
+    "fileNames": "fi2.ctb"
+  },
+  {
+    "locale": "sk",
+    "dots": "6",
+    "id": "sk-sk",
+    "grade": "1",
+    "fileNames": "sk-sk.utb"
+  },
+  {
+    "locale": "ur",
+    "dots": "6",
+    "id": "ur-pk-g1",
+    "grade": "1",
+    "fileNames": "ur-pk-g1.utb"
+  },
+  {
+    "locale": "sv",
+    "dots": "6",
+    "id": "se-se",
+    "grade": "1",
+    "fileNames": "se-se.ctb"
+  },
+  {
+    "locale": "ru",
+    "dots": "6",
+    "id": "ru",
+    "grade": "1",
+    "fileNames": "ru.ctb"
+  },
+    {
+    "locale": "fa",
+    "dots": "6",
+    "id": "fa-ir-g1",
+    "grade": "1",
+    "fileNames": "fa-ir-g1.utb"
+  },
+  {
+    "locale": "gd",
+    "dots": "6",
+    "id": "gd",
+    "grade": "1",
+    "fileNames": "gd.ctb"
+  },
+  {
+    "locale": "fr",
+    "dots": "6",
+    "id": "fr-bfu-g2",
+    "grade": "2",
+    "fileNames": "fr-bfu-g2.ctb"
+  },
+  {
+    "locale": "mn",
+    "dots": "6",
+    "id": "mn-MN-g1",
+    "grade": "1",
+    "fileNames": "mn-MN-g1.utb"
   }
 ]
diff --git a/third_party/metrics_proto/README.chromium b/third_party/metrics_proto/README.chromium
index 00ffc94..45449ac9 100644
--- a/third_party/metrics_proto/README.chromium
+++ b/third_party/metrics_proto/README.chromium
@@ -1,8 +1,8 @@
 Name: Metrics Protos
 Short Name: metrics_proto
 URL: This is the canonical public repository
-Version: 221728188
-Date: 2018/11/16 UTC
+Version: 228615406
+Date: 2019/01/10 UTC
 License: BSD
 Security Critical: Yes
 
diff --git a/third_party/metrics_proto/omnibox_event.proto b/third_party/metrics_proto/omnibox_event.proto
index 4f1146e9..9b057c3 100644
--- a/third_party/metrics_proto/omnibox_event.proto
+++ b/third_party/metrics_proto/omnibox_event.proto
@@ -193,9 +193,9 @@
     // This enum value is currently only used by Android GSA. It represents
     // a suggestion powered by a Chrome content provider.
     ON_DEVICE_CHROME = 13;
-    CLIPBOARD_URL = 14;  // Suggestion coming from clipboard (iOS only).
-    PHYSICAL_WEB = 15;   // Suggestions triggered by URLs broadcast by nearby
-                         // devices (iOS and Android).
+    CLIPBOARD_URL = 14;  // Suggestion coming from clipboard.
+    PHYSICAL_WEB = 15;   // DEPRECATED. Suggestions triggered by URLs broadcast
+                         // by nearby devices.
     DOCUMENT = 16;       // Suggestions for documents in a cloud corpus.
   }
 
@@ -264,11 +264,11 @@
                                      // suggestions of any type.
       CALCULATOR = 23;               // A calculator answer.
       CLIPBOARD = 24;                // An URL based on the clipboard.
-      PHYSICAL_WEB = 25;             // A Physical Web nearby URL.
-      PHYSICAL_WEB_OVERFLOW = 26;    // An item representing multiple Physical
-                                     // Web nearby URLs.
-      DOCUMENT = 27;                 // An item representing a cloud document
-                                     // suggestion.
+      PHYSICAL_WEB = 25;             // DEPRECATED. A Physical Web nearby URL.
+      PHYSICAL_WEB_OVERFLOW = 26;  // DEPRECATED. An item representing multiple
+                                   // Physical Web nearby URLs.
+      DOCUMENT = 27;               // An item representing a cloud document
+                                   // suggestion.
     }
     optional ResultType result_type = 2;
 
diff --git a/third_party/metrics_proto/system_profile.proto b/third_party/metrics_proto/system_profile.proto
index 83fb2a053..2936414 100644
--- a/third_party/metrics_proto/system_profile.proto
+++ b/third_party/metrics_proto/system_profile.proto
@@ -103,6 +103,10 @@
     // of whether the operating system has been jailbroken.
     optional bool is_jailbroken = 4;
 
+    // The build number for the OS version. The same OS version may have a
+    // different build number. The meaning of this field is OS-dependent.
+    optional string build_number = 5;
+
     // The version of the kernel. Linux based operating systems, such as
     // ChromeOS and Android, have a kernel version that the OS release version
     // was built with that differs from the version field above.
@@ -113,7 +117,9 @@
   // Information on the user's hardware.
   // Next tag: 19
   message Hardware {
-    // The CPU architecture (x86, PowerPC, x86_64, ...)
+    // CPU architecture. Taken from uname -m and modified in Chromium logic.
+    // Common options are: x86, x86_64, armv7l, armv8l, aarch64.
+    // Not recorded on iOS.
     optional string cpu_architecture = 1;
 
     // The amount of RAM present on the system, in megabytes.
diff --git a/third_party/sudden_motion_sensor/sudden_motion_sensor_mac.cc b/third_party/sudden_motion_sensor/sudden_motion_sensor_mac.cc
index 4d5d2a9..6ac3321 100644
--- a/third_party/sudden_motion_sensor/sudden_motion_sensor_mac.cc
+++ b/third_party/sudden_motion_sensor/sudden_motion_sensor_mac.cc
@@ -57,6 +57,7 @@
 
 #include "base/logging.h"
 #include "base/mac/scoped_cftyperef.h"
+#include "base/stl_util.h"
 
 struct SuddenMotionSensor::GenericMacbookSensor {
   // Name of device to be read.
@@ -338,7 +339,7 @@
 
   // Look for the current model in the supported sensor list.
   base::ScopedCFTypeRef<CFDataRef> board_id_data;
-  const int kNumSensors = arraysize(kSupportedSensors);
+  const int kNumSensors = base::size(kSupportedSensors);
 
   for (int i = 0; i < kNumSensors; ++i) {
     // Check if the supported sensor model name is a prefix
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 3470d54..8b60aa30 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -662,7 +662,6 @@
       'fuchsia-x64-cast': 'release_trybot_fuchsia_cast',
       'layout_test_leak_detection': 'release_trybot',
       'leak_detection_linux': 'release_trybot',
-      'linux-blink-gen-property-trees': 'release_trybot',
       'linux-blink-heap-incremental-marking': 'debug_trybot_enable_blink_heap_incremental_marking',
       'linux-blink-heap-verification-try': 'release_trybot_enable_blink_heap_verification',
       'linux-coverage-rel': 'clang_code_coverage_trybot',
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index e8d28b1c..200d238 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -7497,6 +7497,14 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="IOSInspectPageVisited">
+  <owner>eugenebut@chromium.org</owner>
+  <owner>michaeldo@chromium.org</owner>
+  <description>
+    Recorded when the user loads the chrome://inspect page on iOS.
+  </description>
+</action>
+
 <action name="IOSLaunchedBySearchInChromeIntent">
   <owner>justincohen@chromium.org</owner>
   <description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 89e96fe..385daa4f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -28123,6 +28123,11 @@
   <int value="2" label="Chrome on Mac"/>
 </enum>
 
+<enum name="IOSInspectConsoleAction">
+  <int value="0" label="Start Logging"/>
+  <int value="1" label="Stop Logging"/>
+</enum>
+
 <enum name="IOSITunesURLsStoreKitHandlingResult">
   <int value="0" label="Handling failed"/>
   <int value="1" label="Single App URL, handled"/>
@@ -30258,6 +30263,7 @@
   <int value="-2001869199" label="ShillSandboxing:enabled"/>
   <int value="-2000567059" label="SimplifyHttpsIndicator:enabled"/>
   <int value="-1999892428" label="force-ui-direction"/>
+  <int value="-1999617045" label="PDFAnnotations:disabled"/>
   <int value="-1998927516" label="enable-md-settings"/>
   <int value="-1989747818" label="TabStripKeyboardFocus:disabled"/>
   <int value="-1985239289" label="AutofillRichMetadataQueries:enabled"/>
@@ -31369,6 +31375,7 @@
   <int value="-68877684" label="BackgroundVideoTrackOptimization:enabled"/>
   <int value="-68619312" label="PassiveDocumentWheelEventListeners:disabled"/>
   <int value="-68225452" label="enable-translate-new-ux"/>
+  <int value="-67616014" label="PDFAnnotations:enabled"/>
   <int value="-67297229" label="OfflinePagesDescriptivePendingStatus:disabled"/>
   <int value="-64839201" label="SyncUSSAutofillWalletData:disabled"/>
   <int value="-59530055" label="ChromeVoxArcSupport:enabled"/>
@@ -31867,6 +31874,7 @@
   <int value="841276069" label="ChromeHomeDoodle:disabled"/>
   <int value="841343322" label="disable-new-korean-ime"/>
   <int value="841779535" label="password-export:enabled"/>
+  <int value="841935705" label="UseAPDownloadProtection:enabled"/>
   <int value="842432903" label="CaptureThumbnailOnNavigatingAway:enabled"/>
   <int value="842789526" label="CloudPrinterHandler:disabled"/>
   <int value="843896452" label="UserActivationV2:enabled"/>
@@ -32140,6 +32148,7 @@
   <int value="1304636193" label="ArcEnableUnifiedAudioFocus:enabled"/>
   <int value="1307003774" label="AutofillEnableCompanyName:disabled"/>
   <int value="1308537004" label="force-pnacl-subzero"/>
+  <int value="1311802125" label="UseAPDownloadProtection:disabled"/>
   <int value="1311860720" label="ChromeHomeNtpRedesign:disabled"/>
   <int value="1312025202" label="NTPOfflinePageSuggestions:disabled"/>
   <int value="1314681756" label="NoStatePrefetch:disabled"/>
@@ -41577,7 +41586,7 @@
   <int value="8760108" label="PPB_Testing_Private;1.0"/>
   <int value="10714106" label="PPB_OutputProtection_Private;0.1"/>
   <int value="11143977" label="PPB_PlatformVerification_Private;0.2"/>
-  <int value="12033600" label="PPB_Compositor;0.1"/>
+  <int value="12033600" label="PPB_Compositor;0.1 (Removed)"/>
   <int value="13662160" label="PPB_CharSet(Dev);0.4"/>
   <int value="22816901" label="PPB_FileChooser(Dev);0.5"/>
   <int value="28187368" label="PPB_IMEInputEvent(Dev);0.2"/>
@@ -41596,14 +41605,14 @@
   <int value="156766028" label="PPB_UMA_Private;0.3"/>
   <int value="162107265" label="PPB_NetworkMonitor;1.0"/>
   <int value="180906214" label="PPB_Instance_Private;0.1"/>
-  <int value="206043276" label="PPB_CompositorLayer;0.1"/>
+  <int value="206043276" label="PPB_CompositorLayer;0.1 (Removed)"/>
   <int value="221802429" label="PPB_URLUtil(Dev);0.7"/>
   <int value="225125520" label="PPB_Find(Private);0.3"/>
   <int value="226206264" label="PPB_FileRef;1.1"/>
   <int value="229560990" label="PPB_Var(Deprecated);0.3"/>
   <int value="250764663" label="PPB_Graphics2D(Dev);0.2"/>
   <int value="320267009" label="PPB_Flash_File_ModuleLocal;3"/>
-  <int value="344923193" label="PPB_CompositorLayer;0.2"/>
+  <int value="344923193" label="PPB_CompositorLayer;0.2 (Removed)"/>
   <int value="348907389" label="PPB_TCPSocket_Private;0.4"/>
   <int value="382780521" label="PPB_FileRef;1.2"/>
   <int value="415548516" label="PPB_MessageLoop;1.0"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index ecd706f1..a578b4e4 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -43053,6 +43053,15 @@
   </summary>
 </histogram>
 
+<histogram name="IOS.Inspect.Console" enum="IOSInspectConsoleAction">
+  <owner>eugenebut@chromium.org</owner>
+  <owner>michaeldo@chromium.org</owner>
+  <summary>
+    Recorded when the iOS JavaScript Console logging is manually enabled or
+    manually disabled.
+  </summary>
+</histogram>
+
 <histogram name="IOS.IPHBubbleDismissalReason" enum="BubbleDismissalReason">
   <owner>gchatz@chromium.org</owner>
   <summary>
@@ -129285,7 +129294,12 @@
       SubresourceFilter data.
     </obsolete>
   </suffix>
-  <suffix name="Clients.Ads.Google" label="Includes only Google ads."/>
+  <suffix name="Clients.Ads.Google" label="Includes only Google ads.">
+    <obsolete>
+      Deprecated January 2019 as we're transitioning to only using
+      SubresourceFilter data.
+    </obsolete>
+  </suffix>
   <suffix name="Clients.Ads.SubresourceFilter"
       label="Includes only ads discovered by the SubResourceFilter."/>
   <affected-histogram name="PageLoad.Bytes.AdFrames.Aggregate.Network"/>
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 94f0321..544a19f 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -298,6 +298,14 @@
           ],
         },
         {
+          'isolate': 'angle_perftests',
+          'num_shards': 1,
+          'telemetry': False,
+          'extra_args': [
+              '--shard-timeout=300'
+          ],
+        },
+        {
           'isolate': 'media_perftests',
           'num_shards': 1,
           'telemetry': False,
diff --git a/tools/perf/scripts_smoke_unittest.py b/tools/perf/scripts_smoke_unittest.py
index d99bc0b2..f2b130f4 100644
--- a/tools/perf/scripts_smoke_unittest.py
+++ b/tools/perf/scripts_smoke_unittest.py
@@ -63,14 +63,20 @@
     return_code, stdout = self.RunPerfScript(
         '../../testing/scripts/run_telemetry_benchmark_as_googletest.py '
         'run_benchmark dummy_benchmark.stable_benchmark_1 --browser=%s '
+        '--isolated-script-test-repeat=2 '
+        '--isolated-script-test-also-run-disabled-tests '
         '--isolated-script-test-output=output.json '
         '--isolated-script-test-chartjson-output=chartjson_output.json '
         '--output-format=chartjson' % browser_type)
     self.assertEquals(return_code, 0, stdout)
     try:
       with open('../../tools/perf/output.json') as f:
+        test_results = json.load(f)
         self.assertIsNotNone(
-            json.load(f), 'json_test_results should be populated: ' + stdout)
+            test_results, 'json_test_results should be populated: ' + stdout)
+        test_repeats = test_results['num_failures_by_type']['PASS']
+        self.assertEqual(
+            test_repeats, 2, '--isolated-script-test-repeat=2 should work.')
       os.remove('../../tools/perf/output.json')
     except IOError as e:
       self.fail('json_test_results should be populated: ' + stdout + str(e))
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index ff43f5b..a445b56 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -375,6 +375,11 @@
   }
   void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel,
                                          bool has_scrolled_by_touch) override {}
+  void SendOverscrollEventFromImplSide(
+      const gfx::Vector2dF& overscroll_delta,
+      cc::ElementId scroll_latched_element_id) override {}
+  void SendScrollEndEventFromImplSide(
+      cc::ElementId scroll_latched_element_id) override {}
   void RequestNewLayerTreeFrameSink() override;
   void DidInitializeLayerTreeFrameSink() override {}
   void DidFailToInitializeLayerTreeFrameSink() override;
diff --git a/ui/display/win/screen_win.cc b/ui/display/win/screen_win.cc
index 7fd3b5b..cb17d76 100644
--- a/ui/display/win/screen_win.cc
+++ b/ui/display/win/screen_win.cc
@@ -70,7 +70,8 @@
 //
 // Respects the forced device scale factor, and will fall back to the global
 // scale factor if per-monitor DPI is not supported.
-float GetMonitorScaleFactorImpl(HMONITOR monitor, bool include_accessibility) {
+float GetMonitorScaleFactor(HMONITOR monitor,
+                            bool include_accessibility = true) {
   DCHECK(monitor);
   if (Display::HasForceDeviceScaleFactor())
     return Display::GetForcedDeviceScaleFactor();
@@ -88,19 +89,6 @@
   return scale_factor;
 }
 
-// Rounds a scale factor to one we can display safely in pixels without
-// smearing.
-double RoundToNearestSafeScaleFactor(float scale_factor) {
-  return std::max(1.0f, std::round(4.0f * scale_factor) * 0.25f);
-}
-
-// Returns a pixel safe monitor scale factor rounded to a pixel-safe value.
-float GetSafeMonitorScaleFactor(HMONITOR monitor,
-                                bool include_accessibility = true) {
-  return RoundToNearestSafeScaleFactor(
-      GetMonitorScaleFactorImpl(monitor, include_accessibility));
-}
-
 bool GetPathInfo(HMONITOR monitor, DISPLAYCONFIG_PATH_INFO* path_info) {
   LONG result;
   uint32_t num_path_array_elements = 0;
@@ -292,7 +280,7 @@
       reinterpret_cast<std::vector<DisplayInfo>*>(data);
   DCHECK(display_infos);
   display_infos->push_back(DisplayInfo(MonitorInfoFromHMONITOR(monitor),
-                                       GetSafeMonitorScaleFactor(monitor),
+                                       GetMonitorScaleFactor(monitor),
                                        GetMonitorSDRWhiteLevel(monitor)));
   return TRUE;
 }
@@ -480,8 +468,7 @@
   if (!monitor)
     monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
 
-  float scale_factor =
-      GetSafeMonitorScaleFactor(monitor, include_accessibility);
+  float scale_factor = GetMonitorScaleFactor(monitor, include_accessibility);
 
   // We'll then pull up the system metrics scaled by the appropriate amount.
   return GetSystemMetricsForScaleFactor(scale_factor, metric);
@@ -519,9 +506,8 @@
 
 // static
 float ScreenWin::GetScaleFactorForDPI(int dpi) {
-  return RoundToNearestSafeScaleFactor(
-      display::win::internal::GetScalingFactorFromDPI(dpi) *
-      UwpTextScaleFactor::Instance()->GetTextScaleFactor());
+  return display::win::internal::GetScalingFactorFromDPI(dpi) *
+         UwpTextScaleFactor::Instance()->GetTextScaleFactor();
 }
 
 // static
diff --git a/ui/webui/resources/cr_elements/shared_vars_css.html b/ui/webui/resources/cr_elements/shared_vars_css.html
index 021b74e..77c4c9e 100644
--- a/ui/webui/resources/cr_elements/shared_vars_css.html
+++ b/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -14,16 +14,17 @@
     --google-grey-400: #BDC1C6;
     --google-grey-600-rgb: 128, 134, 139;
     --google-grey-600: rgb(var(--google-grey-600-rgb));
+    --google-grey-800-rgb: 60, 64, 67;
+    --google-grey-800: rgb(var(--google-grey-800-rgb));
+    --google-grey-900: #202124;
+    --google-red-600: #D93025;
 
     /* -refresh differentiate from polymer's color.html. */
     --google-green-refresh-700: #188038;
     --google-grey-refresh-100: #F1F3F4;
     --google-grey-refresh-300: #DADCE0;
     --google-grey-refresh-700: #5F6368;
-    --google-grey-800-rgb: 60, 64, 67;
-    --google-grey-800: rgb(var(--google-grey-800-rgb));
-    --google-grey-900: #202124;
-    --google-red-600: #D93025;
+    --google-red-refresh-300: #f28b82;
 
     --cr-primary-text-color: var(--google-grey-900);
     --cr-secondary-text-color: var(--google-grey-refresh-700);