Import Cobalt 24.master.0.311288
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b7b0dee..cde10f9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -45,7 +45,8 @@
             (?x)^(
                 starboard/[^/]+/i18n/|
                 cobalt/content/licenses/|
-                cobalt/fetch/embedded_scripts
+                cobalt/fetch/embedded_scripts|
+                cobalt/loader/cors_preflight.cc
             )
 
 -   repo: local
@@ -107,9 +108,14 @@
         stages: [push]
         exclude: |
             (?x)^(
+                .pre-commit-config.yaml$|
                 cobalt/media/|
                 cobalt/layout_tests/testdata/|
-                starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java$
+                nb/|
+                starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java$|
+                starboard/shared/starboard/player/filter/decoded_audio_queue.cc$|
+                starboard/shared/starboard/player/filter/decoded_audio_queue.h$|
+                starboard/shared/starboard/player/filter/wsola_internal.cc$
             )
         exclude_types: [markdown]
         verbose: true
diff --git a/BUILD_STATUS.md b/BUILD_STATUS.md
new file mode 100644
index 0000000..c209877
--- /dev/null
+++ b/BUILD_STATUS.md
@@ -0,0 +1,21 @@
+# Build Status
+
+| Workflow  | Main | 23.lts.1+ | 22.lts.1+ | 21.lts.1+ | 20.lts.1+ | 19.lts.1+ | RC11 | COBALT 9 |
+| --------- | ---- | --------- | --------- | --------- | --------- | --------- | ---- | ---------|
+| Lint      | [![lint](https://github.com/youtube/cobalt/actions/workflows/lint.yaml/badge.svg?branch=main&event=push)](https://github.com/youtube/cobalt/actions/workflows/lint.yaml?query=event%3Apush+branch%3Amain) | | | | | | | |
+| Android   | [![android](https://github.com/youtube/cobalt/actions/workflows/android.yaml/badge.svg?branch=main&event=push)](https://github.com/youtube/cobalt/actions/workflows/android.yaml?query=event%3Apush+branch%3Amain) | [![android_23.lts.1+](https://github.com/youtube/cobalt/actions/workflows/android_23.lts.1+.yaml/badge.svg?branch=23.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/android_23.lts.1+.yaml?query=event%3Apush+branch%3A23.lts.1%2B) | [![android_22.lts.1+](https://github.com/youtube/cobalt/actions/workflows/android_22.lts.1+.yaml/badge.svg?branch=22.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/android_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Apush) | | | | | |
+| Evergreen | [![evergreen](https://github.com/youtube/cobalt/actions/workflows/evergreen.yaml/badge.svg?branch=main&event=push)](https://github.com/youtube/cobalt/actions/workflows/evergreen.yaml?query=event%3Apush+branch%3Amain) | [![evergreen_23.lts.1+](https://github.com/youtube/cobalt/actions/workflows/evergreen_23.lts.1+.yaml/badge.svg?branch=23.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/evergreen_23.lts.1+.yaml?query=event%3Apush+branch%3A23.lts.1%2B) | [![evergreen_22.lts.1+](https://github.com/youtube/cobalt/actions/workflows/evergreen_22.lts.1+.yaml/badge.svg?branch=22.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/evergreen_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Apush) | [![evergreen_21.lts.1+](https://github.com/youtube/cobalt/actions/workflows/evergreen_21.lts.1+.yaml/badge.svg?branch=21.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/evergreen_21.lts.1+.yaml) | | | | |
+| Linux     | [![linux](https://github.com/youtube/cobalt/actions/workflows/linux.yaml/badge.svg?branch=main&event=push)](https://github.com/youtube/cobalt/actions/workflows/linux.yaml?query=event%3Apush+branch%3Amain) | [![linux_23.lts.1+](https://github.com/youtube/cobalt/actions/workflows/linux_23.lts.1+.yaml/badge.svg?branch=23.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/linux_23.lts.1+.yaml?query=event%3Apush+branch%3A23.lts.1%2B) | [![linux_22.lts.1+](https://github.com/youtube/cobalt/actions/workflows/linux_22.lts.1+.yaml/badge.svg?branch=22.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/linux_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Apush) | [![linux_21.lts.1+](https://github.com/youtube/cobalt/actions/workflows/linux_21.lts.1+.yaml/badge.svg?branch=21.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/linux_21.lts.1+.yaml) | [![linux_20.lts.1+](https://github.com/youtube/cobalt/actions/workflows/linux_20.lts.1+.yaml/badge.svg?branch=20.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/linux_20.lts.1+.yaml?query=branch%3A20.lts.1%2B+event%3Apush) | [![linux_19.lts.1+](https://github.com/youtube/cobalt/actions/workflows/linux_19.lts.1+.yaml/badge.svg?branch=19.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/linux_19.lts.1+.yaml?query=branch%3A19.lts.1%2B+event%3Apush) | [![linux_rc_11](https://github.com/youtube/cobalt/actions/workflows/linux_rc_11.yaml/badge.svg?branch=rc_11&event=push)](https://github.com/youtube/cobalt/actions/workflows/linux_rc_11.yaml?query=event%3Apush+branch%3Arc_11) | [![linux_COBALT_9](https://github.com/youtube/cobalt/actions/workflows/linux_COBALT_9.yaml/badge.svg?branch=COBALT_9&event=push)](https://github.com/youtube/cobalt/actions/workflows/linux_COBALT_9.yaml?query=event%3Apush+branch%3ACOBALT_9) |
+| raspi-2   | [![raspi-2](https://github.com/youtube/cobalt/actions/workflows/raspi-2.yaml/badge.svg?branch=main&event=push)](https://github.com/youtube/cobalt/actions/workflows/raspi-2.yaml?query=event%3Apush+branch%3Amain) | [![raspi-2_23.lts.1+](https://github.com/youtube/cobalt/actions/workflows/raspi-2_23.lts.1+.yaml/badge.svg?branch=23.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_23.lts.1+.yaml?query=event%3Apush+branch%3A23.lts.1%2B) | [![raspi-2_22.lts.1+](https://github.com/youtube/cobalt/actions/workflows/raspi-2_22.lts.1+.yaml/badge.svg?branch=22.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Apush) | [![raspi-2_21.lts.1+](https://github.com/youtube/cobalt/actions/workflows/raspi-2_21.lts.1+.yaml/badge.svg?branch=21.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_21.lts.1+.yaml) | [![raspi-2_20.lts.1+](https://github.com/youtube/cobalt/actions/workflows/raspi-2_20.lts.1+.yaml/badge.svg?branch=20.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_20.lts.1+.yaml?query=branch%3A20.lts.1%2B+event%3Apush) | [![raspi-2_19.lts.1+](https://github.com/youtube/cobalt/actions/workflows/raspi-2_19.lts.1+.yaml/badge.svg?branch=19.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_19.lts.1+.yaml?query=branch%3A19.lts.1%2B+event%3Apush) | [![raspi-2_rc_11](https://github.com/youtube/cobalt/actions/workflows/raspi-2_rc_11.yaml/badge.svg?branch=rc_11&event=push)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_rc_11.yaml?query=event%3Apush+branch%3Arc_11) | [![raspi-2_COBALT_9](https://github.com/youtube/cobalt/actions/workflows/raspi-2_COBALT_9.yaml/badge.svg?branch=COBALT_9&event=push)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_COBALT_9.yaml?query=event%3Apush+branch%3ACOBALT_9) |
+| stub      | [![stub](https://github.com/youtube/cobalt/actions/workflows/stub.yaml/badge.svg?branch=main&event=push)](https://github.com/youtube/cobalt/actions/workflows/stub.yaml?query=event%3Apush+branch%3Amain) | [![stub_23.lts.1+](https://github.com/youtube/cobalt/actions/workflows/stub_23.lts.1+.yaml/badge.svg?branch=23.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/stub_23.lts.1+.yaml?query=event%3Apush+branch%3A23.lts.1%2B) | | | | | | |
+| Win32     | [![win32](https://github.com/youtube/cobalt/actions/workflows/win32.yaml/badge.svg?branch=main&event=push)](https://github.com/youtube/cobalt/actions/workflows/win32.yaml?query=event%3Apush+branch%3Amain) | [![win32_23.lts.1+](https://github.com/youtube/cobalt/actions/workflows/win32_23.lts.1+.yaml/badge.svg?branch=23.lts.1%2B&event=push)](https://github.com/youtube/cobalt/actions/workflows/win32_23.lts.1+.yaml?query=event%3Apush+branch%3A23.lts.1%2B) | | | | | | |
+
+
+# Nightly builds
+| Workflow  | main | 23.lts.1+ | 22.lts.1+ | 21.lts.1+ | 20.lts.1+ | 19.lts.1+ | RC11 | COBALT 9 |
+| --------- | ---- | --------- | --------- | --------- | --------- | --------- | ---- | ---------|
+| Android   | [![android](https://github.com/youtube/cobalt/actions/workflows/android.yaml/badge.svg?branch=main&event=schedule)](https://github.com/youtube/cobalt/actions/workflows/android.yaml?query=event%3Aschedule+branch%3Amain) | [![android_23.lts.1+](https://github.com/youtube/cobalt/actions/workflows/android_23.lts.1+.yaml/badge.svg?branch=23.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/android_23.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A23.lts.1%2B) | [![android_22.lts.1+](https://github.com/youtube/cobalt/actions/workflows/android_22.lts.1+.yaml/badge.svg?branch=22.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/android_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Aworkflow_dispatch) | | | | | |
+| Evergreen | [![evergreen](https://github.com/youtube/cobalt/actions/workflows/evergreen.yaml/badge.svg?branch=main&event=schedule)](https://github.com/youtube/cobalt/actions/workflows/evergreen.yaml?query=event%3Aschedule+branch%3Amain) | [![evergreen_23.lts.1+](https://github.com/youtube/cobalt/actions/workflows/evergreen_23.lts.1+.yaml/badge.svg?branch=23.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/evergreen_23.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A23.lts.1%2B) | [![evergreen_22.lts.1+](https://github.com/youtube/cobalt/actions/workflows/evergreen_22.lts.1+.yaml/badge.svg?branch=22.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/evergreen_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Aworkflow_dispatch) | [![evergreen_21.lts.1+](https://github.com/youtube/cobalt/actions/workflows/evergreen_21.lts.1+.yaml/badge.svg?branch=21.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/evergreen_21.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A21.lts.1%2B) | | | | |
+| Linux | [![linux](https://github.com/youtube/cobalt/actions/workflows/linux.yaml/badge.svg?branch=main&event=schedule)](https://github.com/youtube/cobalt/actions/workflows/linux.yaml?query=event%3Aschedule+branch%3Amain) | [![linux_23.lts.1+](https://github.com/youtube/cobalt/actions/workflows/linux_23.lts.1+.yaml/badge.svg?branch=23.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/linux_23.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A23.lts.1%2B) | [![linux_22.lts.1+](https://github.com/youtube/cobalt/actions/workflows/linux_22.lts.1+.yaml/badge.svg?branch=22.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/linux_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Aworkflow_dispatch) | [![linux_21.lts.1+](https://github.com/youtube/cobalt/actions/workflows/linux_21.lts.1+.yaml/badge.svg?branch=21.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/linux_21.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A21.lts.1%2B) | [![linux_20.lts.1+](https://github.com/youtube/cobalt/actions/workflows/linux_20.lts.1+.yaml/badge.svg?branch=20.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/linux_20.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A20.lts.1%2B) | [![linux_19.lts.1+](https://github.com/youtube/cobalt/actions/workflows/linux_19.lts.1+.yaml/badge.svg?branch=19.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/linux_19.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A19.lts.1%2B) | [![linux_rc_11](https://github.com/youtube/cobalt/actions/workflows/linux_rc_11.yaml/badge.svg?branch=rc_11&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/linux_rc_11.yaml?query=event%3Aworkflow_dispatch+branch%3Arc_11) | [![linux_COBALT_9](https://github.com/youtube/cobalt/actions/workflows/linux_COBALT_9.yaml/badge.svg?branch=COBALT_9&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/linux_COBALT_9.yaml?query=event%3Aworkflow_dispatch+branch%3ACOBALT_9) |
+| raspi-2 | [![raspi-2](https://github.com/youtube/cobalt/actions/workflows/raspi-2.yaml/badge.svg?branch=main&event=schedule)](https://github.com/youtube/cobalt/actions/workflows/raspi-2.yaml?query=event%3Aschedule+branch%3Amain) | [![raspi-2_23.lts.1+](https://github.com/youtube/cobalt/actions/workflows/raspi-2_23.lts.1+.yaml/badge.svg?branch=23.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_23.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A23.lts.1%2B) | [![raspi-2_22.lts.1+](https://github.com/youtube/cobalt/actions/workflows/raspi-2_22.lts.1+.yaml/badge.svg?branch=22.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_22.lts.1+.yaml?query=branch%3A22.lts.1%2B+event%3Aworkflow_dispatch) | [![raspi-2_21.lts.1+](https://github.com/youtube/cobalt/actions/workflows/raspi-2_21.lts.1+.yaml/badge.svg?branch=21.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_21.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A21.lts.1%2B) | [![raspi-2_20.lts.1+](https://github.com/youtube/cobalt/actions/workflows/raspi-2_20.lts.1+.yaml/badge.svg?branch=20.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_20.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A20.lts.1%2B) | [![raspi-2_19.lts.1+](https://github.com/youtube/cobalt/actions/workflows/raspi-2_19.lts.1+.yaml/badge.svg?branch=19.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_19.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A19.lts.1%2B) | [![raspi-2_rc_11](https://github.com/youtube/cobalt/actions/workflows/raspi-2_rc_11.yaml/badge.svg?branch=rc_11&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_rc_11.yaml?query=event%3Aworkflow_dispatch+branch%3Arc_11) | [![raspi-2_COBALT_9](https://github.com/youtube/cobalt/actions/workflows/raspi-2_COBALT_9.yaml/badge.svg?branch=COBALT_9&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/raspi-2_COBALT_9.yaml?query=event%3Aworkflow_dispatch+branch%3ACOBALT_9) |
+| Win32 | [![win32](https://github.com/youtube/cobalt/actions/workflows/win32.yaml/badge.svg?branch=main&event=schedule)](https://github.com/youtube/cobalt/actions/workflows/win32.yaml?query=event%3Aschedule+branch%3Amain) | [![win32_23.lts.1+](https://github.com/youtube/cobalt/actions/workflows/win32_23.lts.1+.yaml/badge.svg?branch=23.lts.1%2B&event=workflow_dispatch)](https://github.com/youtube/cobalt/actions/workflows/win32_23.lts.1+.yaml?query=event%3Aworkflow_dispatch+branch%3A23.lts.1%2B) | | | | | | |
diff --git a/README.md b/README.md
index 6c96aa9..1ffff01 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Cobalt
+# Cobalt [![Build Status](https://img.shields.io/badge/-Build%20Status-blueviolet)](https://github.com/youtube/cobalt/blob/main/BUILD_STATUS.md)
 
 ## Overview
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index e6ccec8..93f6256 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3197,6 +3197,7 @@
   if (is_starboard){
     data_deps += [
       ":base_unittests_bundle_data",
+      "//cobalt/network:copy_ssl_certificates",
       "//third_party/icu:icudata",
     ]
   }
diff --git a/base/message_loop/message_pump_ui_starboard.h b/base/message_loop/message_pump_ui_starboard.h
index 12e6e38..214e18c 100644
--- a/base/message_loop/message_pump_ui_starboard.h
+++ b/base/message_loop/message_pump_ui_starboard.h
@@ -40,7 +40,11 @@
 class BASE_EXPORT MessagePumpUIStarboard : public MessagePump {
  public:
   MessagePumpUIStarboard();
-  virtual ~MessagePumpUIStarboard() {}
+  virtual ~MessagePumpUIStarboard() {
+    // There is probability that MessagePump may send event to MessageLoop that
+    // was already destroyed. To avoid this delete all tasks in pump_
+    Quit();
+  }
 
   // Runs one iteration of the run loop, and reschedules another call, if
   // necessary.
diff --git a/build/config/clang/clang.gni b/build/config/clang/clang.gni
index e8f794c..9cfbf6b 100644
--- a/build/config/clang/clang.gni
+++ b/build/config/clang/clang.gni
@@ -4,15 +4,7 @@
 
 import("//build/toolchain/toolchain.gni")
 
-if (is_starboard) {
-  import("//starboard/build/toolchain/starboard_toolchains.gni")
-
-  declare_args() {
-    clang_revision = "365097-f7e52fbd-8"
-  }
-
-  default_clang_base_path = "$starboard_toolchains_path/x86_64-linux-gnu-clang-chromium-${clang_revision}"  
-} else {
+if (!use_cobalt_customizations) {
   default_clang_base_path = "//third_party/llvm-build/Release+Asserts"
 }
 
@@ -24,5 +16,7 @@
       is_clang && !is_nacl && !use_xcode_clang &&
       default_toolchain != "//build/toolchain/cros:target"
 
-  clang_base_path = default_clang_base_path
+  if (!use_cobalt_customizations) {
+    clang_base_path = default_clang_base_path
+  }
 }
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 157c841..838ebcd 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -13,7 +13,7 @@
 import("//build/config/compiler/compiler.gni")
 import("//build/config/coverage/coverage.gni")
 import("//build/config/dcheck_always_on.gni")
-if (!is_starboard) {
+if (!use_cobalt_customizations) {
   import("//build/config/gclient_args.gni")
 }
 import("//build/config/host_byteorder.gni")
@@ -1230,7 +1230,8 @@
 }
 
 config("clang_revision") {
-  if (!is_starboard && is_clang && clang_base_path == default_clang_base_path) {
+  if (!use_cobalt_customizations && is_clang &&
+      clang_base_path == default_clang_base_path) {
     update_args = [
       "--print-revision",
       "--verify-version=$clang_version",
@@ -1292,7 +1293,7 @@
   # android:runtime_library.  This is to ensure libc++ appears before
   # libandroid_support in the -isystem include order.  Otherwise, there will be
   # build errors related to symbols declared in math.h.
-  if (!is_starboard && use_custom_libcxx) {
+  if (!use_cobalt_customizations && use_custom_libcxx) {
     configs += [ "//build/config/c++:runtime_library" ]
   }
 
@@ -1585,7 +1586,8 @@
         cflags += [ "-Wno-nonportable-include-path" ]
       }
 
-      if ((current_toolchain == host_toolchain || !use_xcode_clang) && !using_old_compiler) {
+      if ((current_toolchain == host_toolchain || !use_xcode_clang) &&
+          !using_old_compiler) {
         # Flags NaCl (Clang 3.7) and Xcode 9.2 (Clang clang-900.0.39.2) do not
         # recognize.
         cflags += [
@@ -1593,7 +1595,7 @@
           # TODO(thakis): Only for no_chromium_code? http://crbug.com/912662
           "-Wno-ignored-pragma-optimize",
         ]
-        if (!is_starboard) {
+        if (!use_cobalt_customizations) {
           cflags += [
             # An ABI compat warning we don't care about, https://crbug.com/1102157
             # TODO(thakis): Push this to the (few) targets that need it,
@@ -1622,7 +1624,7 @@
           "-Wno-implicit-fallthrough",
         ]
 
-        if (!is_starboard) {
+        if (!use_cobalt_customizations) {
           if (enable_wmax_tokens) {
             cflags += [ "-Wmax-tokens" ]
           } else {
@@ -1781,7 +1783,7 @@
       # suppressing them individually, we just blanket suppress them here.
       "-Wno-unused-variable",
     ]
-    if (!is_starboard && !is_nacl &&
+    if (!use_cobalt_customizations && !is_nacl &&
         (current_toolchain == host_toolchain || !use_xcode_clang)) {
       cflags += [
         # TODO(https://crbug.com/1202159): Clean up and enable.
diff --git a/build/config/sanitizers/BUILD.gn b/build/config/sanitizers/BUILD.gn
index 2fbfe97..50aa4c9 100644
--- a/build/config/sanitizers/BUILD.gn
+++ b/build/config/sanitizers/BUILD.gn
@@ -115,7 +115,7 @@
   ]
   sources = [ "//build/sanitizers/sanitizer_options.cc" ]
 
-  if (!is_starboard) {
+  if (!use_cobalt_customizations) {
     # Don't compile this target with any sanitizer code. It can be called from
     # the sanitizer runtimes, so instrumenting these functions could cause
     # recursive calls into the runtime if there is an error.
diff --git a/build/config/sysroot.gni b/build/config/sysroot.gni
index 9f616b7..765623b 100644
--- a/build/config/sysroot.gni
+++ b/build/config/sysroot.gni
@@ -25,7 +25,7 @@
                 current_cpu == "arm" || current_cpu == "arm64" ||
                 current_cpu == "mipsel" || current_cpu == "mips64el"
 
-  if (is_starboard) {
+  if (use_cobalt_customizations) {
     use_sysroot = false
   }
 }
diff --git a/build/toolchain/gcc_toolchain.gni b/build/toolchain/gcc_toolchain.gni
index f325e56..891cf34 100644
--- a/build/toolchain/gcc_toolchain.gni
+++ b/build/toolchain/gcc_toolchain.gni
@@ -150,7 +150,7 @@
       # needs to be passed through unchanged to all secondary toolchains to
       # ensure that it's always the same, regardless of the values that may be
       # set on those toolchains.
-      if (!is_starboard) {
+      if (!use_cobalt_customizations) {
         host_toolchain = host_toolchain
 
         if (!defined(invoker_toolchain_args.v8_current_cpu)) {
diff --git a/cobalt/BUILD.gn b/cobalt/BUILD.gn
index 23b7aca..2c221a3 100644
--- a/cobalt/BUILD.gn
+++ b/cobalt/BUILD.gn
@@ -28,6 +28,7 @@
     "//cobalt/layout_tests:web_platform_tests",
     "//cobalt/loader:loader_test",
     "//cobalt/loader/image/sandbox:image_decoder_sandbox",
+    "//cobalt/media:media_test",
     "//cobalt/media/sandbox:media_sandbox",
     "//cobalt/media/sandbox:web_media_player_sandbox",
     "//cobalt/media_capture:media_capture_test",
diff --git a/cobalt/audio/audio_node_input_output_test.cc b/cobalt/audio/audio_node_input_output_test.cc
index 12cb069..2723382 100644
--- a/cobalt/audio/audio_node_input_output_test.cc
+++ b/cobalt/audio/audio_node_input_output_test.cc
@@ -94,9 +94,10 @@
  public:
   AudioNodeInputOutputTest() {
     web_context_.reset(web::Agent::CreateContext("AudioNodeInputOutputTest"));
-    web_context_->setup_environment_settings(
+    web_context_->SetupEnvironmentSettings(
         new dom::testing::StubEnvironmentSettings);
     web_context_->global_environment()->CreateGlobalObject();
+    web_context_->SetupFinished();
   }
 
   ~AudioNodeInputOutputTest() {}
diff --git a/cobalt/base/BUILD.gn b/cobalt/base/BUILD.gn
index 898475d..e335403 100644
--- a/cobalt/base/BUILD.gn
+++ b/cobalt/base/BUILD.gn
@@ -124,5 +124,8 @@
     "//testing/gmock",
     "//testing/gtest",
   ]
-  data_deps = [ "//third_party/icu:icudata" ]
+  data_deps = [
+    "//cobalt/network:copy_ssl_certificates",
+    "//third_party/icu:icudata",
+  ]
 }
diff --git a/cobalt/black_box_tests/black_box_tests.py b/cobalt/black_box_tests/black_box_tests.py
index 129c8a8..079b829 100644
--- a/cobalt/black_box_tests/black_box_tests.py
+++ b/cobalt/black_box_tests/black_box_tests.py
@@ -36,6 +36,8 @@
     'android-arm/devel',
     'android-arm64/devel',
     'android-x86/devel',
+    'evergreen-arm/devel',
+    'evergreen-x64/devel',
     'raspi-0/devel',
 ]
 
@@ -53,7 +55,8 @@
     'preload_font',
     'preload_visibility',
     'preload_launch_parameter',
-    'signal_handler_doesnt_crash',
+    # TODO(b/254502632): Investigate the cause of the flakiness from this test.
+    # 'signal_handler_doesnt_crash',
     'suspend_visibility',
     'timer_hit_after_preload',
     'timer_hit_in_preload',
@@ -67,14 +70,14 @@
     'h5vcc_storage_write_verify_test',
     'http_cache',
     'persistent_cookie',
+    'service_worker_add_to_cache_test',
     'service_worker_cache_keys_test',
     'service_worker_controller_activation_test',
     'service_worker_get_registrations_test',
+    'service_worker_fetch_main_resource_test',
     'service_worker_fetch_test',
     'service_worker_message_test',
     'service_worker_test',
-    # TODO(b/259731731) disable for now until persisted registrations
-    # activate correctly.
     'service_worker_persist_test',
     'soft_mic_platform_service_test',
     'text_encoding_test',
@@ -225,13 +228,8 @@
     if self.proxy_port == '-1':
       return 1
 
-    # Temporary means to determine if we are running on CI
-    # TODO: Update to IS_CI environment variable or similar
-    out_dir = _launcher_params.out_directory
-    is_ci = out_dir and 'mh_lab' in out_dir  # pylint: disable=unsupported-membership-test
-
-    if is_ci and (f'{_launcher_params.platform}/{_launcher_params.config}'
-                  in _DISABLED_BLACKBOXTEST_CONFIGS):
+    if (f'{_launcher_params.platform}/{_launcher_params.config}'
+        in _DISABLED_BLACKBOXTEST_CONFIGS):
       logging.warning('Blackbox tests disabled for platform:%s config:%s',
                       _launcher_params.platform, _launcher_params.config)
       return 0
diff --git a/cobalt/black_box_tests/testdata/service_worker_add_to_cache_test.html b/cobalt/black_box_tests/testdata/service_worker_add_to_cache_test.html
new file mode 100644
index 0000000..c8a0a85
--- /dev/null
+++ b/cobalt/black_box_tests/testdata/service_worker_add_to_cache_test.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!--
+  Copyright 2023 The Cobalt Authors. All Rights Reserved.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<html>
+<head>
+  <title>Cobalt Service Worker Add to Cache Test</title>
+  <script src='black_box_js_test_utils.js'></script>
+</head>
+<body>
+  <script>
+    h5vcc.storage.clearServiceWorkerCache();
+
+    const unregisterAll = () => navigator.serviceWorker.getRegistrations().then(registrations =>
+        Promise.all(registrations.map(r => r.unregister())));
+    const fail = msg => {
+      if (msg) {
+        console.error(msg);
+      }
+      unregisterAll().then(notReached);
+    };
+    const timeoutId = window.setTimeout(fail, 10000);
+    const success = () => unregisterAll().then(() => {
+      clearTimeout(timeoutId);
+      onEndTest();
+    });
+    navigator.serviceWorker.onmessage = event => {
+      if (event.data === 'SUCCESS') {
+        success();
+        return;
+      }
+      fail(event.data);
+    };
+
+    navigator.serviceWorker.ready.then(registration => {
+      registration.active.postMessage('start-test');
+    });
+
+    unregisterAll().then(() => {
+      navigator.serviceWorker.register('service_worker_add_to_cache_test.js').catch(fail);
+    });
+
+    setupFinished();
+  </script>
+</body>
+</html>
diff --git a/cobalt/black_box_tests/testdata/service_worker_add_to_cache_test.js b/cobalt/black_box_tests/testdata/service_worker_add_to_cache_test.js
new file mode 100644
index 0000000..5a259e7
--- /dev/null
+++ b/cobalt/black_box_tests/testdata/service_worker_add_to_cache_test.js
@@ -0,0 +1,97 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+const interceptedBody = 'window.activeServiceWorker.postMessage("script-intercepted");';
+let shouldIntercept = false;
+
+const postMessage = message => {
+  const options = {
+    includeUncontrolled: false, type: 'window'
+  };
+  self.clients.matchAll(options).then(clients => {
+    clients.forEach(c => {
+      c.postMessage(message);
+    });
+  });
+};
+
+self.addEventListener("install", event => {
+  self.skipWaiting();
+});
+
+self.addEventListener('activate', event => {
+  self.clients.claim();
+});
+
+const noop = () => {};
+const fail = msg => {
+  if (msg) {
+    console.error(msg);
+  }
+  postMessage('FAIL');
+};
+const assertEquals = (expected, actual, msg) => {
+  if (expected === actual) {
+    return;
+  }
+  const errorMessage = `Expected: '${expected}', but was '${actual}'`;
+  fail(msg ? `${msg}(${errorMessage})` : errorMessage);
+};
+const assertNotEquals = (notExpected, actual, msg) => {
+  if (notExpected !== actual) {
+    return;
+  }
+  const errorMessage = `Not expected: '${notExpected}'`;
+  fail(msg ? `${msg}(${errorMessage})` : errorMessage);
+};
+
+const ASSET_CACHE_NAME = 'asset-cache-name';
+const RESOURCE = self.location.pathname;
+const RESOURCE_WITH_QUERY = self.location.pathname + '?foo=bar';
+const RESOURCE_NOT_INDIRECTLY_CACHED = self.location.pathname + '?should_not_resolve_to_resource1';
+const RESOURCE_NOT_PRESENT = self.location.pathname + '.not_present';
+
+let assetCache = null;
+const getAssetCache = async () => {
+  if (!assetCache) {
+    assetCache = await self.caches.open(ASSET_CACHE_NAME);
+  }
+  return assetCache;
+};
+const cacheUrl = async (cache, url) => {
+  if (await cache.match(url)) {
+    return;
+  }
+  return cache.add(url);
+};
+const cacheAssetUrls = async urls => {
+  const cache = await getAssetCache();
+  return Promise.all(urls.map(url => cacheUrl(cache, url).catch(noop)));
+};
+
+self.addEventListener('message', async event => {
+  if (event.data === 'start-test') {
+    await self.caches.delete(ASSET_CACHE_NAME);
+    await cacheAssetUrls([RESOURCE, RESOURCE_WITH_QUERY, RESOURCE_NOT_PRESENT]);
+    const cache = await self.caches.open(ASSET_CACHE_NAME);
+    assertEquals(2, (await cache.keys()).length);
+    assertNotEquals(undefined, await cache.match(RESOURCE));
+    assertNotEquals(undefined, await cache.match(RESOURCE_WITH_QUERY));
+    assertEquals(undefined, await cache.match(RESOURCE_NOT_INDIRECTLY_CACHED));
+    assertEquals(undefined, await cache.match(RESOURCE_NOT_PRESENT));
+    postMessage('SUCCESS');
+    return;
+  }
+  postMessage(`${JSON.stringify(event.data)}-unexpected-message`);
+});
diff --git a/cobalt/black_box_tests/testdata/service_worker_cache_keys_test.html b/cobalt/black_box_tests/testdata/service_worker_cache_keys_test.html
index c5a57e9..0bedc3e 100644
--- a/cobalt/black_box_tests/testdata/service_worker_cache_keys_test.html
+++ b/cobalt/black_box_tests/testdata/service_worker_cache_keys_test.html
@@ -25,6 +25,7 @@
 </head>
 <body>
   <script>
+    h5vcc.storage.clearServiceWorkerCache();
     const unregisterAll = () => navigator.serviceWorker.getRegistrations().then(registrations =>
         Promise.all(registrations.map(r => r.unregister())));
     const fail = msg => {
diff --git a/cobalt/black_box_tests/testdata/service_worker_controller_activation_test_script.js b/cobalt/black_box_tests/testdata/service_worker_controller_activation_test_script.js
index d373dba..b5d6ee9 100644
--- a/cobalt/black_box_tests/testdata/service_worker_controller_activation_test_script.js
+++ b/cobalt/black_box_tests/testdata/service_worker_controller_activation_test_script.js
@@ -12,9 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-/**
-* @param {KeyboardEvent} event
-*/
+h5vcc.storage.clearServiceWorkerCache();
 
 const unregisterAll = () => navigator.serviceWorker.getRegistrations().then(registrations =>
   Promise.all(registrations.map(r => r.unregister())));
@@ -25,6 +23,9 @@
   test();
 }
 
+/**
+* @param {KeyboardEvent} event
+*/
 window.onkeydown = function (event) {
   assertEqual(undefined, self.clients);
 
diff --git a/cobalt/black_box_tests/testdata/service_worker_csp_test.html b/cobalt/black_box_tests/testdata/service_worker_csp_test.html
index ff058e3..7e6b711 100644
--- a/cobalt/black_box_tests/testdata/service_worker_csp_test.html
+++ b/cobalt/black_box_tests/testdata/service_worker_csp_test.html
@@ -22,6 +22,8 @@
 
 <body>
   <script>
+  h5vcc.storage.clearServiceWorkerCache();
+
   var window_onerror_count = 0;
   window.onerror = (message, filename, lineno, colno, error) => {
     ++window_onerror_count;
diff --git a/cobalt/black_box_tests/testdata/service_worker_fetch_main_resource.html b/cobalt/black_box_tests/testdata/service_worker_fetch_main_resource.html
new file mode 100644
index 0000000..244cc4c
--- /dev/null
+++ b/cobalt/black_box_tests/testdata/service_worker_fetch_main_resource.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<!--
+  Copyright 2023 The Cobalt Authors. All Rights Reserved.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<html>
+<head>
+  <title>Cobalt Service Worker Fetch Main Resource</title>
+  <script src='black_box_js_test_utils.js'></script>
+</head>
+<body>
+  <script>
+    const unregisterAll = () => navigator.serviceWorker.getRegistrations().then(registrations =>
+        Promise.all(registrations.map(r => r.unregister())));
+    const fail = msg => {
+      if (msg) {
+        console.error(msg);
+      }
+      unregisterAll().then(notReached);
+    };
+    const success = () => unregisterAll().then(() => {
+      clearTimeout(timeoutId);
+      onEndTest();
+    });
+    const assertEquals = (expected, actual, msg) => {
+      if (expected === actual) {
+        return;
+      }
+      const errorMessage = `Expected: '${expected}', but was '${actual}'`;
+      fail(msg ? `${msg}(${errorMessage})` : errorMessage);
+    };
+    const timeoutId = window.setTimeout(fail, 5000);
+    navigator.serviceWorker.onmessage = event => {
+      const requests = event.data;
+      assertEquals(2, requests.length);
+      // The sub resource is black_box_js_test_utils.js.
+      const [mainResourceRequest, subResourceRequest] = requests;
+      assertEquals('navigate', mainResourceRequest.mode);
+      assertEquals('cors', subResourceRequest.mode);
+      success();
+    };
+
+    navigator.serviceWorker.getRegistration().then(registration => {
+      if (!registration || !registration.active) {
+        fail('Expected active registration, but none present.');
+      }
+      registration.active.postMessage('start');
+    });
+
+    setupFinished();
+  </script>
+</body>
+</html>
diff --git a/cobalt/black_box_tests/testdata/service_worker_fetch_main_resource_test.html b/cobalt/black_box_tests/testdata/service_worker_fetch_main_resource_test.html
new file mode 100644
index 0000000..eb9d331
--- /dev/null
+++ b/cobalt/black_box_tests/testdata/service_worker_fetch_main_resource_test.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<!--
+  Copyright 2023 The Cobalt Authors. All Rights Reserved.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<html>
+<head>
+  <title>Cobalt Service Worker Fetch Main Resource Test</title>
+  <script src='black_box_js_test_utils.js'></script>
+</head>
+<body>
+  <script>
+    h5vcc.storage.enableCache();
+    const mebibyte = 1024 * 1024;
+    const res = h5vcc.storage.setQuota({
+      other: 14 * mebibyte,
+      html: mebibyte,
+      css: mebibyte,
+      image: mebibyte,
+      font: mebibyte,
+      splash: mebibyte,
+      uncompiled_js: mebibyte,
+      compiled_js: mebibyte,
+      cache_api: mebibyte,
+      service_worker_js: mebibyte,
+      total: 23 * mebibyte,
+    });
+    h5vcc.storage.clearServiceWorkerCache();
+
+    const unregisterAll = () => navigator.serviceWorker.getRegistrations().then(registrations =>
+        Promise.all(registrations.map(r => r.unregister())));
+    const fail = msg => {
+      if (msg) {
+        console.error(msg);
+      }
+      unregisterAll().then(notReached);
+    };
+    const timeoutId = window.setTimeout(fail, 5000);
+    navigator.serviceWorker.ready.then(async () => {
+      clearTimeout(timeoutId);
+      window.location = 'service_worker_fetch_main_resource.html';
+    }).catch(fail);
+
+    unregisterAll().then(() =>
+      navigator.serviceWorker.register('service_worker_fetch_main_resource_test.js')).catch(fail);
+  </script>
+</body>
+</html>
diff --git a/cobalt/black_box_tests/testdata/service_worker_fetch_main_resource_test.js b/cobalt/black_box_tests/testdata/service_worker_fetch_main_resource_test.js
new file mode 100644
index 0000000..19e761f
--- /dev/null
+++ b/cobalt/black_box_tests/testdata/service_worker_fetch_main_resource_test.js
@@ -0,0 +1,36 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+const postMessage = message => {
+  const options = {
+    includeUncontrolled: false,
+    type: 'window',
+  };
+  self.clients.matchAll(options).then(clients => {
+    clients.forEach(c => {
+      c.postMessage(message);
+    });
+  });
+};
+
+self.addEventListener("install", () => self.skipWaiting());
+self.addEventListener('activate', () => self.clients.claim());
+
+const fetchRequests = [];
+self.addEventListener('message', () => postMessage(fetchRequests));
+self.addEventListener('fetch', ({request: {url, method, mode, headers}}) => {
+  fetchRequests.push({
+    url, method, mode, headers: Array.from(headers.entries()),
+  });
+});
diff --git a/cobalt/black_box_tests/testdata/service_worker_fetch_test.html b/cobalt/black_box_tests/testdata/service_worker_fetch_test.html
index a325b69..1dcc7b7 100644
--- a/cobalt/black_box_tests/testdata/service_worker_fetch_test.html
+++ b/cobalt/black_box_tests/testdata/service_worker_fetch_test.html
@@ -25,11 +25,13 @@
 </head>
 <body>
   <script>
+    h5vcc.storage.clearServiceWorkerCache();
+
     const unregisterAll = () => navigator.serviceWorker.getRegistrations().then(registrations =>
         Promise.all(registrations.map(r => r.unregister())));
     const unregisterAndNotReached = () => unregisterAll().then(notReached);
 
-    const fetch_resource = () => fetch('service_worker_fetch_resource.js').then(r => r.text());
+    const fetchResource = () => fetch('service_worker_fetch_resource.js').then(r => r.text());
     const timeoutId = window.setTimeout(unregisterAndNotReached, 10000);
     window.activeServiceWorker = null;
     navigator.serviceWorker.onmessage = event => {
@@ -44,7 +46,7 @@
           unregisterAndNotReached();
           return;
         }
-        fetch_resource().then(body => {
+        fetchResource().then(body => {
           window.activeServiceWorker.postMessage({test: event.data.test, result: body});
         });
         return;
@@ -64,7 +66,7 @@
       window.activeServiceWorker.postMessage('start-test');
     });
 
-    unregisterAll().then(fetch_resource).then(body => {
+    unregisterAll().then(fetchResource).then(body => {
       if ('window.activeServiceWorker.postMessage("script-not-intercepted");\n' !== body) {
         unregisterAndNotReached();
         return;
diff --git a/cobalt/black_box_tests/testdata/service_worker_get_registrations_test.html b/cobalt/black_box_tests/testdata/service_worker_get_registrations_test.html
index 21cf71d..2c8283b 100644
--- a/cobalt/black_box_tests/testdata/service_worker_get_registrations_test.html
+++ b/cobalt/black_box_tests/testdata/service_worker_get_registrations_test.html
@@ -25,6 +25,8 @@
 </head>
 <body>
   <script>
+    h5vcc.storage.clearServiceWorkerCache();
+
     // TODO: check multiple registrations.
     const unregisterAll = () => navigator.serviceWorker.getRegistrations().then(registrations =>
         Promise.all(registrations.map(r => r.unregister())));
diff --git a/cobalt/black_box_tests/testdata/service_worker_message_test.html b/cobalt/black_box_tests/testdata/service_worker_message_test.html
index 8b74c05..4eb4cf4 100644
--- a/cobalt/black_box_tests/testdata/service_worker_message_test.html
+++ b/cobalt/black_box_tests/testdata/service_worker_message_test.html
@@ -27,6 +27,8 @@
 
 <body>
     <script>
+        h5vcc.storage.clearServiceWorkerCache();
+
         var expected_event_count = 0;
 
         function count_event(expected_value) {
diff --git a/cobalt/black_box_tests/testdata/service_worker_persist_test_script.js b/cobalt/black_box_tests/testdata/service_worker_persist_test_script.js
index 82d19b4..888aec0 100644
--- a/cobalt/black_box_tests/testdata/service_worker_persist_test_script.js
+++ b/cobalt/black_box_tests/testdata/service_worker_persist_test_script.js
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+h5vcc.storage.clearServiceWorkerCache();
 
 /**
 * @param {KeyboardEvent} event
diff --git a/cobalt/black_box_tests/testdata/service_worker_test.html b/cobalt/black_box_tests/testdata/service_worker_test.html
index 34782ec..d50434c 100644
--- a/cobalt/black_box_tests/testdata/service_worker_test.html
+++ b/cobalt/black_box_tests/testdata/service_worker_test.html
@@ -22,12 +22,12 @@
 <html>
 
 <head>
-    <title>Cobalt Service Worker Test</title>
-    <script src='black_box_js_test_utils.js'></script>
+  <title>Cobalt Service Worker Test</title>
+  <script nonce="blackboxtest" src='black_box_js_test_utils.js'></script>
 </head>
 
 <body>
-    <script src='service_worker_test_script.js'></script>
+  <script nonce="blackboxtest" src='service_worker_test_script.js'></script>
 </body>
 
 </html>
diff --git a/cobalt/black_box_tests/testdata/service_worker_test_persisted_worker.js b/cobalt/black_box_tests/testdata/service_worker_test_persisted_worker.js
index 003deda..c808e3c 100644
--- a/cobalt/black_box_tests/testdata/service_worker_test_persisted_worker.js
+++ b/cobalt/black_box_tests/testdata/service_worker_test_persisted_worker.js
@@ -12,4 +12,4 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-console.log('Service Worker Script Started');
+console.log('Service Worker Persist Script Started');
diff --git a/cobalt/black_box_tests/testdata/service_worker_test_script.js b/cobalt/black_box_tests/testdata/service_worker_test_script.js
index 55d40eb..3b5f371 100644
--- a/cobalt/black_box_tests/testdata/service_worker_test_script.js
+++ b/cobalt/black_box_tests/testdata/service_worker_test_script.js
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+h5vcc.storage.clearServiceWorkerCache();
+
 const eventCounts = {};
 const countEvent = (fnName, expectedEventCount) => {
   if (fnName in eventCounts) {
@@ -292,7 +294,7 @@
   () => navigator.serviceWorker.register('service_worker_test_nonexist.js')
       .then(fail(`Unknown script did not raise error.`))
       .catch(error => {
-        assertEqual('NetworkError', error.name);
+        assertEqual('SecurityError', error.name);
       }),
   checkSuccessfulRegistration,
   unregisterAll,
diff --git a/cobalt/black_box_tests/tests/service_worker_add_to_cache_test.py b/cobalt/black_box_tests/tests/service_worker_add_to_cache_test.py
new file mode 100644
index 0000000..eaa8051
--- /dev/null
+++ b/cobalt/black_box_tests/tests/service_worker_add_to_cache_test.py
@@ -0,0 +1,28 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests Service Worker Add to Cache functionality."""
+
+from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
+
+
+class ServiceWorkerAddToCacheTest(black_box_tests.BlackBoxTestCase):
+
+  def test_service_worker_add_to_cache(self):
+    with ThreadedWebServer(binding_address=self.GetBindingAddress()) as server:
+      url = server.GetURL(
+          file_name='testdata/service_worker_add_to_cache_test.html')
+      with self.CreateCobaltRunner(url=url) as runner:
+        runner.WaitForJSTestsSetup()
+        self.assertTrue(runner.JSTestsSucceeded())
diff --git a/cobalt/black_box_tests/tests/service_worker_fetch_main_resource_test.py b/cobalt/black_box_tests/tests/service_worker_fetch_main_resource_test.py
new file mode 100644
index 0000000..aeec28d
--- /dev/null
+++ b/cobalt/black_box_tests/tests/service_worker_fetch_main_resource_test.py
@@ -0,0 +1,28 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Tests Service Worker Fetch Main Resource functionality."""
+
+from cobalt.black_box_tests import black_box_tests
+from cobalt.black_box_tests.threaded_web_server import ThreadedWebServer
+
+
+class ServiceWorkerFetchMainResourceTest(black_box_tests.BlackBoxTestCase):
+
+  def test_service_worker_fetch_main_resource(self):
+    with ThreadedWebServer(binding_address=self.GetBindingAddress()) as server:
+      url = server.GetURL(
+          file_name='testdata/service_worker_fetch_main_resource_test.html')
+      with self.CreateCobaltRunner(url=url) as runner:
+        runner.WaitForJSTestsSetup()
+        self.assertTrue(runner.JSTestsSucceeded())
diff --git a/cobalt/black_box_tests/tests/service_worker_test.py b/cobalt/black_box_tests/tests/service_worker_test.py
index d06fecb..fc5c7e5 100644
--- a/cobalt/black_box_tests/tests/service_worker_test.py
+++ b/cobalt/black_box_tests/tests/service_worker_test.py
@@ -23,6 +23,19 @@
 # The base path of the requested assets is the parent directory.
 _SERVER_ROOT_PATH = os.path.join(os.path.dirname(__file__), os.pardir)
 
+paths_to_headers = {
+    'service_worker_test.html': {
+        'Content-Security-Policy':
+            "script-src 'nonce-blackboxtest' ; worker-src 'self'"
+    },
+    'service_worker_test_worker.js': {
+        'Content-Security-Policy':
+            "connect-src 'self' ; script-src 'none' ; worker-src 'self'"
+    }
+}
+
+#path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+
 
 class ServiceWorkerRequestDetector(MakeRequestHandlerClass(_SERVER_ROOT_PATH)):
   """Proxies everything to SimpleHTTPRequestHandler, except some paths."""
@@ -32,6 +45,11 @@
     SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self)
 
   def send_header(self, header, value):
+    for partial_path, headers in paths_to_headers.items():
+      if partial_path in self.path:
+        for header_key, header_value in headers.items():
+          SimpleHTTPServer.SimpleHTTPRequestHandler.send_header(
+              self, header_key, header_value)
     # Ensure that the Content-Type for paths ending in '.js' are always
     # 'text/javascript'.
     if header == 'Content-Type' and self.path.endswith('.js'):
diff --git a/cobalt/browser/BUILD.gn b/cobalt/browser/BUILD.gn
index e8cf8d3..72217bd 100644
--- a/cobalt/browser/BUILD.gn
+++ b/cobalt/browser/BUILD.gn
@@ -70,6 +70,9 @@
       "//cobalt/content/fonts:fonts_xml",
     ]
   }
+  if (sb_is_evergreen) {
+    data_deps += [ "//cobalt/updater/version_manifest:copy_version_manifest" ]
+  }
   if (!is_gold) {
     data_deps += [
       "//cobalt/debug/backend/content:copy_backend_web_files",
@@ -209,10 +212,7 @@
       "lifecycle_console_commands.cc",
       "lifecycle_console_commands.h",
     ]
-    defines = [
-      "ENABLE_ABOUT_SCHEME",
-      "COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING",
-    ]
+    defines = [ "ENABLE_ABOUT_SCHEME" ]
     deps += [
       "//cobalt/debug",
       "//cobalt/debug:console_command_manager",
diff --git a/cobalt/browser/application.cc b/cobalt/browser/application.cc
index 37c304c..4156bdd 100644
--- a/cobalt/browser/application.cc
+++ b/cobalt/browser/application.cc
@@ -852,8 +852,9 @@
 #endif  // defined(COBALT_FORCE_CSP)
 
   network_module_options.https_requirement = security_flags.https_requirement;
-  options.web_module_options.require_csp = security_flags.csp_header_policy;
-  options.web_module_options.csp_enforcement_mode = web::kCspEnforcementEnable;
+  options.web_module_options.csp_header_policy =
+      security_flags.csp_header_policy;
+  options.web_module_options.csp_enforcement_type = web::kCspEnforcementEnable;
 
   options.requested_viewport_size = requested_viewport_size;
 
diff --git a/cobalt/browser/browser_module.cc b/cobalt/browser/browser_module.cc
index f694893..e4e1481 100644
--- a/cobalt/browser/browser_module.cc
+++ b/cobalt/browser/browser_module.cc
@@ -293,7 +293,7 @@
 
   platform_info_.reset(new browser::UserAgentPlatformInfo());
   service_worker_registry_.reset(new ServiceWorkerRegistry(
-      &web_settings_, network_module, platform_info_.get()));
+      &web_settings_, network_module, platform_info_.get(), url));
 
 #if SB_HAS(CORE_DUMP_HANDLER_SUPPORT)
   SbCoreDumpRegisterHandler(BrowserModule::CoreDumpHandler, this);
@@ -529,6 +529,15 @@
 
   main_web_module_layer_->Reset();
 
+  // Service worker should only start for HTTP or HTTPS fetches.
+  // https://fetch.spec.whatwg.org/commit-snapshots/8f8ab504da6ca9681db5c7f8aa3d1f4b6bf8840c/#http-fetch
+  bool can_start_service_worker = url.SchemeIsHTTPOrHTTPS();
+  auto service_worker_started_event = std::make_unique<base::WaitableEvent>();
+  if (can_start_service_worker) {
+    service_worker_registry_->EnsureServiceWorkerStarted(
+        url::Origin::Create(url), url, service_worker_started_event.get());
+  }
+
   // Wait until after the old WebModule is destroyed before setting the navigate
   // time so that it won't be included in the time taken to load the URL.
   navigate_time_ = base::TimeTicks::Now().ToInternalValue();
@@ -617,6 +626,10 @@
       service_worker_registry_->service_worker_jobs();
   options.web_options.platform_info = platform_info_.get();
   web_module_.reset(new WebModule("MainWebModule"));
+  // Wait for service worker to start if one exists.
+  if (can_start_service_worker) {
+    service_worker_started_event->Wait();
+  }
   web_module_->Run(
       url, application_state_, scroll_engine_.get(),
       base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
@@ -1191,6 +1204,7 @@
 
 void BrowserModule::OnError(const GURL& url, const std::string& error) {
   TRACE_EVENT0("cobalt::browser", "BrowserModule::OnError()");
+  LOG(ERROR) << error;
   if (base::MessageLoop::current() != self_message_loop_) {
     self_message_loop_->task_runner()->PostTask(
         FROM_HERE, base::Bind(&BrowserModule::OnError, weak_this_, url, error));
@@ -2084,6 +2098,7 @@
                                        ->user_agent_data();
   h5vcc_settings.global_environment = settings->context()->global_environment();
   h5vcc_settings.persistent_settings = options_.persistent_settings;
+  h5vcc_settings.can_play_type_handler = can_play_type_handler_.get();
 
   auto* h5vcc_object = new h5vcc::H5vcc(h5vcc_settings);
   if (!web_module_created_callback_.is_null()) {
diff --git a/cobalt/browser/debug_console.cc b/cobalt/browser/debug_console.cc
index 197233b..b08fed0 100644
--- a/cobalt/browser/debug_console.cc
+++ b/cobalt/browser/debug_console.cc
@@ -121,7 +121,7 @@
   web_module_options.image_cache_capacity = 0;
   // Disable CSP for the Debugger's WebModule. This will also allow eval() in
   // javascript.
-  web_module_options.csp_enforcement_mode = web::kCspEnforcementDisable;
+  web_module_options.csp_enforcement_type = web::kCspEnforcementDisable;
   web_module_options.csp_insecure_allowed_token =
       web::CspDelegateFactory::GetInsecureAllowedToken();
 
diff --git a/cobalt/browser/memory_tracker/tool/malloc_logger_tool.cc b/cobalt/browser/memory_tracker/tool/malloc_logger_tool.cc
index ddaca24..1bae210 100644
--- a/cobalt/browser/memory_tracker/tool/malloc_logger_tool.cc
+++ b/cobalt/browser/memory_tracker/tool/malloc_logger_tool.cc
@@ -22,7 +22,6 @@
 #include "cobalt/browser/memory_tracker/tool/params.h"
 #include "cobalt/browser/memory_tracker/tool/util.h"
 #include "nb/memory_scope.h"
-#include "starboard/atomic.h"
 #include "starboard/common/string.h"
 #include "starboard/types.h"
 
diff --git a/cobalt/browser/memory_tracker/tool/malloc_logger_tool.h b/cobalt/browser/memory_tracker/tool/malloc_logger_tool.h
index add5271..2086d79 100644
--- a/cobalt/browser/memory_tracker/tool/malloc_logger_tool.h
+++ b/cobalt/browser/memory_tracker/tool/malloc_logger_tool.h
@@ -23,7 +23,7 @@
 #include "cobalt/browser/memory_tracker/tool/params.h"
 #include "cobalt/browser/memory_tracker/tool/tool_impl.h"
 #include "nb/memory_scope.h"
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/memory_reporter.h"
 
 namespace cobalt {
diff --git a/cobalt/browser/service_worker_registry.cc b/cobalt/browser/service_worker_registry.cc
index 2fdd068..23a1a1c 100644
--- a/cobalt/browser/service_worker_registry.cc
+++ b/cobalt/browser/service_worker_registry.cc
@@ -39,7 +39,7 @@
 
 ServiceWorkerRegistry::ServiceWorkerRegistry(
     web::WebSettings* web_settings, network::NetworkModule* network_module,
-    web::UserAgentPlatformInfo* platform_info)
+    web::UserAgentPlatformInfo* platform_info, const GURL& url)
     : thread_("ServiceWorkerRegistry") {
   if (!thread_.Start()) return;
   DCHECK(message_loop());
@@ -47,7 +47,7 @@
   message_loop()->task_runner()->PostTask(
       FROM_HERE,
       base::Bind(&ServiceWorkerRegistry::Initialize, base::Unretained(this),
-                 web_settings, network_module, platform_info));
+                 web_settings, network_module, platform_info, url));
 
   // Register as a destruction observer to shut down the Web Agent once all
   // pending tasks have been executed and the message loop is about to be
@@ -73,8 +73,16 @@
   // Ensure that the destruction observer got added before stopping the thread.
   // Stop the thread. This will cause the destruction observer to be notified.
   destruction_observer_added_.Wait();
-  service_worker_jobs_->Stop();
+  DCHECK_NE(thread_.message_loop(), base::MessageLoop::current());
   thread_.Stop();
+  DCHECK(!service_worker_jobs_);
+}
+
+void ServiceWorkerRegistry::EnsureServiceWorkerStarted(
+    const url::Origin& storage_key, const GURL& client_url,
+    base::WaitableEvent* done_event) {
+  service_worker_jobs()->EnsureServiceWorkerStarted(storage_key, client_url,
+                                                    done_event);
 }
 
 worker::ServiceWorkerJobs* ServiceWorkerRegistry::service_worker_jobs() {
@@ -85,11 +93,11 @@
 
 void ServiceWorkerRegistry::Initialize(
     web::WebSettings* web_settings, network::NetworkModule* network_module,
-    web::UserAgentPlatformInfo* platform_info) {
+    web::UserAgentPlatformInfo* platform_info, const GURL& url) {
   TRACE_EVENT0("cobalt::browser", "ServiceWorkerRegistry::Initialize()");
   DCHECK_EQ(base::MessageLoop::current(), message_loop());
   service_worker_jobs_.reset(new worker::ServiceWorkerJobs(
-      web_settings, network_module, platform_info, message_loop()));
+      web_settings, network_module, platform_info, message_loop(), url));
 }
 
 }  // namespace browser
diff --git a/cobalt/browser/service_worker_registry.h b/cobalt/browser/service_worker_registry.h
index 14abedd..8c37083 100644
--- a/cobalt/browser/service_worker_registry.h
+++ b/cobalt/browser/service_worker_registry.h
@@ -34,7 +34,8 @@
  public:
   ServiceWorkerRegistry(web::WebSettings* web_settings,
                         network::NetworkModule* network_module,
-                        web::UserAgentPlatformInfo* platform_info);
+                        web::UserAgentPlatformInfo* platform_info,
+                        const GURL& url);
   ~ServiceWorkerRegistry();
 
   // The message loop this object is running on.
@@ -43,6 +44,10 @@
   // From base::MessageLoop::DestructionObserver.
   void WillDestroyCurrentMessageLoop() override;
 
+  void EnsureServiceWorkerStarted(const url::Origin& storage_key,
+                                  const GURL& client_url,
+                                  base::WaitableEvent* done_event);
+
   worker::ServiceWorkerJobs* service_worker_jobs();
 
  private:
@@ -50,7 +55,7 @@
   // the dedicated thread.
   void Initialize(web::WebSettings* web_settings,
                   network::NetworkModule* network_module,
-                  web::UserAgentPlatformInfo* platform_info);
+                  web::UserAgentPlatformInfo* platform_info, const GURL& url);
 
   // The thread created and owned by the Service Worker Registry.
   // All registry mutations occur on this thread. The thread has to outlive all
diff --git a/cobalt/browser/web_module.cc b/cobalt/browser/web_module.cc
index dc7ae32..f746098 100644
--- a/cobalt/browser/web_module.cc
+++ b/cobalt/browser/web_module.cc
@@ -27,11 +27,11 @@
 #include "base/optional.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_checker.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/base/c_val.h"
 #include "cobalt/base/debugger_hooks.h"
 #include "cobalt/base/language.h"
-#include "cobalt/base/startup_timer.h"
 #include "cobalt/base/tokens.h"
 #include "cobalt/base/type_id.h"
 #include "cobalt/browser/splash_screen_cache.h"
@@ -109,6 +109,25 @@
   }
 }
 
+void CancelScroll(ui_navigation::scroll_engine::ScrollEngine* scroll_engine,
+                  const scoped_refptr<ui_navigation::NavItem>& nav_item) {
+  auto nav_items = std::vector<scoped_refptr<ui_navigation::NavItem>>{nav_item};
+  scroll_engine->thread()->message_loop()->task_runner()->PostTask(
+      FROM_HERE, base::Bind(&ui_navigation::scroll_engine::ScrollEngine::
+                                CancelActiveScrollsForNavItems,
+                            base::Unretained(scroll_engine), nav_items));
+}
+
+dom::Window::NavItemCallback CancelScrollCallback(
+    ui_navigation::scroll_engine::ScrollEngine* scroll_engine) {
+  if (scroll_engine) {
+    return base::Bind(CancelScroll, base::Unretained(scroll_engine));
+  } else {
+    return base::Callback<void(
+        const scoped_refptr<cobalt::ui_navigation::NavItem>& nav_item)>();
+  }
+}
+
 }  // namespace
 
 // Private WebModule implementation. Each WebModule owns a single instance of
@@ -238,9 +257,6 @@
 
   void ReduceMemory();
 
-  void LogScriptError(const base::SourceLocation& source_location,
-                      const std::string& error_message);
-
   void IsReadyToFreeze(volatile bool* is_ready_to_freeze) {
     if (window_->media_session()->media_session_client() == NULL) {
       *is_ready_to_freeze = true;
@@ -291,8 +307,6 @@
   void ProcessOnRenderTreeRasterized(const base::TimeTicks& produced_time,
                                      const base::TimeTicks& rasterized_time);
 
-  void OnCspPolicyChanged();
-
   scoped_refptr<script::GlobalEnvironment> global_environment() {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
     return web_context_->global_environment();
@@ -318,10 +332,6 @@
     }
   }
 
-  // Report an error encountered while running JS.
-  // Returns whether or not the error was handled.
-  bool ReportScriptError(const script::ErrorReport& error_report);
-
   // Inject the DOM event object into the window or the element.
   void InjectInputEvent(scoped_refptr<dom::Element> element,
                         const scoped_refptr<web::Event>& event);
@@ -498,11 +508,6 @@
 #endif  // defined(ENABLE_DEBUGGER)
       synchronous_loader_interrupt_(data.synchronous_loader_interrupt) {
   DCHECK(web_context_);
-#if defined(COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING)
-  script::JavaScriptEngine::ErrorHandler error_handler =
-      base::Bind(&WebModule::Impl::LogScriptError, base::Unretained(this));
-  web_context_->javascript_engine()->RegisterErrorHandler(error_handler);
-#endif
   css_parser::Parser::SupportsMapToMeshFlag supports_map_to_mesh =
       data.options.enable_map_to_mesh
           ? css_parser::Parser::kSupportsMapToMesh
@@ -514,7 +519,7 @@
   dom_parser_.reset(new dom_parser::Parser(
       kDOMMaxElementDepth,
       base::Bind(&WebModule::Impl::OnLoadComplete, base::Unretained(this)),
-      data.options.require_csp));
+      data.options.csp_header_policy));
   DCHECK(dom_parser_);
 
   on_before_unload_fired_but_not_handled_ =
@@ -581,7 +586,7 @@
     memory_info = stub_decoder_buffer_memory_info_.get();
   }
 
-  web_context_->setup_environment_settings(new dom::DOMSettings(
+  web_context_->SetupEnvironmentSettings(new dom::DOMSettings(
       debugger_hooks_, kDOMMaxElementDepth, media_source_registry_.get(),
       data.can_play_type_handler, memory_info, &mutation_observer_task_manager_,
       data.options.dom_settings_options));
@@ -597,6 +602,9 @@
   dom::Window::CacheCallback splash_screen_cache_callback =
       CacheUrlContentCallback(data.options.splash_screen_cache);
 
+  dom::Window::NavItemCallback cancel_scroll_callback =
+      CancelScrollCallback(data.scroll_engine);
+
   // These members will reference other |Traceable|s, however are not
   // accessible from |Window|, so we must explicitly add them as roots.
   web_context_->global_environment()->AddRoot(&mutation_observer_task_manager_);
@@ -635,9 +643,10 @@
       base::GetSystemLanguageScript(), data.options.navigation_callback,
       base::Bind(&WebModule::Impl::OnLoadComplete, base::Unretained(this)),
       web_context_->network_module()->cookie_jar(),
-      web_context_->network_module()->GetPostSender(), data.options.require_csp,
-      data.options.csp_enforcement_mode,
-      base::Bind(&WebModule::Impl::OnCspPolicyChanged, base::Unretained(this)),
+      web::CspDelegate::Options(web_context_->network_module()->GetPostSender(),
+                                data.options.csp_header_policy,
+                                data.options.csp_enforcement_type,
+                                data.options.csp_insecure_allowed_token),
       base::Bind(&WebModule::Impl::OnRanAnimationFrameCallbacks,
                  base::Unretained(this)),
       data.window_close_callback, data.window_minimize_callback,
@@ -645,10 +654,11 @@
       base::Bind(&WebModule::Impl::OnStartDispatchEvent,
                  base::Unretained(this)),
       base::Bind(&WebModule::Impl::OnStopDispatchEvent, base::Unretained(this)),
-      data.options.provide_screenshot_function, synchronous_loader_interrupt_,
-      data.options.enable_inline_script_warnings, data.ui_nav_root,
-      data.options.enable_map_to_mesh, data.options.csp_insecure_allowed_token,
-      data.dom_max_element_depth, data.options.video_playback_rate_multiplier,
+      data.options.provide_screenshot_function, cancel_scroll_callback,
+      synchronous_loader_interrupt_, data.options.enable_inline_script_warnings,
+      data.ui_nav_root, data.options.enable_map_to_mesh,
+      data.options.csp_insecure_allowed_token, data.dom_max_element_depth,
+      data.options.video_playback_rate_multiplier,
 #if defined(ENABLE_TEST_RUNNER)
       data.options.layout_trigger == layout::LayoutManager::kTestRunnerMode
           ? dom::Window::kClockTypeTestRunner
@@ -699,21 +709,6 @@
       data.options.clear_window_with_background_color));
   DCHECK(layout_manager_);
 
-#if !defined(COBALT_FORCE_CSP)
-  if (data.options.csp_enforcement_mode == web::kCspEnforcementDisable) {
-    // If CSP is disabled, enable eval(). Otherwise, it will be enabled by
-    // a CSP directive.
-    web_context_->global_environment()->EnableEval();
-  }
-#endif
-
-  web_context_->global_environment()->SetReportEvalCallback(
-      base::Bind(&web::CspDelegate::ReportEval,
-                 base::Unretained(window_->csp_delegate())));
-
-  web_context_->global_environment()->SetReportErrorCallback(
-      base::Bind(&WebModule::Impl::ReportScriptError, base::Unretained(this)));
-
   if (!data.options.loaded_callbacks.empty()) {
     document_load_observer_.reset(
         new DocumentLoadedObserver(data.options.loaded_callbacks));
@@ -733,6 +728,7 @@
   report_unload_timing_info_callback_ =
       data.options.collect_unload_event_time_callback;
 
+  web_context_->SetupFinished();
   is_running_ = true;
 }
 
@@ -1015,29 +1011,6 @@
   }
 }
 
-void WebModule::Impl::OnCspPolicyChanged() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(is_running_);
-  DCHECK(window_);
-  DCHECK(window_->csp_delegate());
-
-  std::string eval_disabled_message;
-  bool allow_eval = window_->csp_delegate()->AllowEval(&eval_disabled_message);
-  if (allow_eval) {
-    web_context_->global_environment()->EnableEval();
-  } else {
-    web_context_->global_environment()->DisableEval(eval_disabled_message);
-  }
-}
-
-bool WebModule::Impl::ReportScriptError(
-    const script::ErrorReport& error_report) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(is_running_);
-  DCHECK(window_);
-  return window_->ReportScriptError(error_report);
-}
-
 #if defined(ENABLE_WEBDRIVER)
 void WebModule::Impl::CreateWindowDriver(
     const webdriver::protocol::WindowId& window_id,
@@ -1235,25 +1208,6 @@
   }
 }
 
-void WebModule::Impl::LogScriptError(
-    const base::SourceLocation& source_location,
-    const std::string& error_message) {
-  std::string file_name =
-      base::FilePath(source_location.file_path).BaseName().value();
-
-  std::stringstream ss;
-  base::TimeDelta dt = base::StartupTimer::TimeElapsed();
-
-  // Create the error output.
-  // Example:
-  //   JS:50250:file.js(29,80): ka(...) is not iterable
-  //   JS:<time millis><js-file-name>(<line>,<column>):<message>
-  ss << "JS:" << dt.InMilliseconds() << ":" << file_name << "("
-     << source_location.line_number << "," << source_location.column_number
-     << "): " << error_message << "\n";
-  SbLogRaw(ss.str().c_str());
-}
-
 void WebModule::Impl::InjectBeforeUnloadEvent() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (window_ && window_->HasEventListener(base::Tokens::beforeunload())) {
diff --git a/cobalt/browser/web_module.h b/cobalt/browser/web_module.h
index d3fa41c..8c28dce 100644
--- a/cobalt/browser/web_module.h
+++ b/cobalt/browser/web_module.h
@@ -24,7 +24,6 @@
 #include "base/message_loop/message_loop.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread.h"
-#include "base/threading/thread_checker.h"
 #include "cobalt/base/address_sanitizer.h"
 #include "cobalt/base/source_location.h"
 #include "cobalt/browser/lifecycle_observer.h"
@@ -59,7 +58,7 @@
 #include "cobalt/web/environment_settings.h"
 #include "cobalt/web/user_agent_platform_info.h"
 #include "cobalt/webdriver/session_driver.h"
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "url/gurl.h"
 
 #if defined(ENABLE_DEBUGGER)
@@ -121,7 +120,7 @@
     dom::DOMSettings::Options dom_settings_options;
 
     // Whether Cobalt is forbidden to render without receiving CSP headers.
-    csp::CSPHeaderPolicy require_csp;
+    csp::CSPHeaderPolicy csp_header_policy;
 
     // If true, Cobalt will log a warning each time it parses a non-async
     // <script> tag inlined in HTML.  Cobalt has a known issue where if it is
@@ -149,7 +148,7 @@
     bool enable_map_to_mesh = true;
 
     // Content Security Policy enforcement mode for this web module.
-    web::CspEnforcementType csp_enforcement_mode = web::kCspEnforcementEnable;
+    web::CspEnforcementType csp_enforcement_type = web::kCspEnforcementEnable;
 
     // Token obtained from CSP to allow creation of insecure delegates.
     int csp_insecure_allowed_token = 0;
diff --git a/cobalt/cache/cache.cc b/cobalt/cache/cache.cc
index f275abb..d88a6ec 100644
--- a/cobalt/cache/cache.cc
+++ b/cobalt/cache/cache.cc
@@ -22,6 +22,7 @@
 #include "base/files/file_util.h"
 #include "base/memory/singleton.h"
 #include "base/optional.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/values.h"
 #include "cobalt/configuration/configuration.h"
 #include "cobalt/persistent_storage/persistent_settings.h"
@@ -246,7 +247,8 @@
 
 void Cache::Resize(disk_cache::ResourceType resource_type, uint32_t bytes) {
   if (resource_type != disk_cache::ResourceType::kCacheApi &&
-      resource_type != disk_cache::ResourceType::kCompiledScript)
+      resource_type != disk_cache::ResourceType::kCompiledScript &&
+      resource_type != disk_cache::ResourceType::kServiceWorkerScript)
     return;
   if (bytes == disk_cache::kTypeMetadata[resource_type].max_size_bytes) return;
 
@@ -322,12 +324,24 @@
 
 bool Cache::CanCache(disk_cache::ResourceType resource_type,
                      uint32_t data_size) {
-  return enabled_ &&
-         cobalt::configuration::Configuration::GetInstance()
-             ->CobaltCanStoreCompiledJavascript() &&
-         data_size > 0u &&
-         data_size >= GetMinSizeToCacheInBytes(resource_type) &&
-         data_size <= GetMaxCacheStorageInBytes(resource_type);
+  bool size_okay = data_size > 0u &&
+                   data_size >= GetMinSizeToCacheInBytes(resource_type) &&
+                   data_size <= GetMaxCacheStorageInBytes(resource_type);
+  if (!size_okay) {
+    return false;
+  }
+  if (resource_type == disk_cache::ResourceType::kServiceWorkerScript ||
+      resource_type == disk_cache::ResourceType::kCacheApi) {
+    return true;
+  }
+  if (!enabled_) {
+    return false;
+  }
+  if (resource_type == disk_cache::ResourceType::kCompiledScript) {
+    return cobalt::configuration::Configuration::GetInstance()
+        ->CobaltCanStoreCompiledJavascript();
+  }
+  return true;
 }
 
 }  // namespace cache
diff --git a/cobalt/content/fonts/config/android/fonts.xml b/cobalt/content/fonts/config/android/fonts.xml
index 9ced68c..dcdfb2b 100644
--- a/cobalt/content/fonts/config/android/fonts.xml
+++ b/cobalt/content/fonts/config/android/fonts.xml
@@ -170,15 +170,13 @@
     -->
     <family fallback_priority="0" name="Noto Naskh Arabic UI">
         <font weight="400" style="normal">NotoNaskhArabicUI-Regular.ttf</font>
-        <font weight="400" style="normal">NotoNaskhUI-Regular.ttf</font>
         <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
-        <font weight="700" style="normal">NotoNaskhUI-Bold.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansEthiopic-Bold.ttf</font>
-        <font weight="400" style="normal">NotoSansEthiopic-VF.ttf</font>
-        <font weight="700" style="normal">NotoSansEthiopic-VF.ttf</font>
+        <font weight="400" style="normal" tag_wght="400" font_name="Noto Sans Ethiopic" postscript_name="NotoSansEthiopic-Regular">NotoSansEthiopic-VF.ttf</font>
+        <font weight="700" style="normal" tag_wght="700" font_name="Noto Sans Ethiopic Bold" postscript_name="NotoSansEthiopic-Bold">NotoSansEthiopic-VF.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
@@ -193,22 +191,24 @@
         <font weight="400" style="normal">NotoSansArmenian-Regular.otf</font>
         <font weight="700" style="normal">NotoSansArmenian-Bold.ttf</font>
         <font weight="700" style="normal">NotoSansArmenian-Bold.otf</font>
-        <font weight="400" style="normal">NotoSansArmenian-VF.ttf</font>
-        <font weight="700" style="normal">NotoSansArmenian-VF.ttf</font>
+        <font weight="400" style="normal" tag_wght="400" font_name="Noto Sans Armenian" postscript_name="NotoSansArmenian-Regular">NotoSansArmenian-VF.ttf</font>
+        <font weight="700" style="normal" tag_wght="700" font_name="Noto Sans Armenian Bold" postscript_name="NotoSansArmenian-Bold">NotoSansArmenian-VF.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
         <font weight="400" style="normal">NotoSansGeorgian-Regular.otf</font>
         <font weight="700" style="normal">NotoSansGeorgian-Bold.ttf</font>
         <font weight="700" style="normal">NotoSansGeorgian-Bold.otf</font>
+        <font weight="400" style="normal" tag_wght="400" font_name="Noto Sans Georgian" postscript_name="NotoSansGeorgian-Regular">NotoSansGeorgian-VF.ttf</font>
+        <font weight="700" style="normal" tag_wght="700" font_name="Noto Sans Georgian Bold" postscript_name="NotoSansGeorgian-Bold">NotoSansGeorgian-VF.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf</font>
         <font weight="400" style="normal">NotoSansDevanagariUI-Regular.otf</font>
         <font weight="700" style="normal">NotoSansDevanagariUI-Bold.ttf</font>
         <font weight="700" style="normal">NotoSansDevanagariUI-Bold.otf</font>
-        <font weight="400" style="normal">NotoSansDevanagariUI-VF.ttf</font>
-        <font weight="700" style="normal">NotoSansDevanagariUI-VF.ttf</font>
+        <font weight="400" style="normal" tag_wght="400" font_name="Noto Sans Devanagari UI" postscript_name="NotoSansDevanagariUI-Regular">NotoSansDevanagariUI-VF.ttf</font>
+        <font weight="700" style="normal" tag_wght="700" font_name="Noto Sans Devanagari UI Bold" postscript_name="NotoSansDevanagariUI-Bold">NotoSansDevanagariUI-VF.ttf</font>
     </family>
     <!-- Gujarati should come after Devanagari -->
     <family>
@@ -219,44 +219,44 @@
     <family>
         <font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansGurmukhiUI-Bold.ttf</font>
-        <font weight="400" style="normal">NotoSansGurmukhiUI-VF.ttf</font>
-        <font weight="700" style="normal">NotoSansGurmukhiUI-VF.ttf</font>
+        <font weight="400" style="normal" tag_wght="400" font_name="Noto Sans Gurmukhi UI" postscript_name="NotoSansGurmukhiUI-Regular">NotoSansGurmukhiUI-VF.ttf</font>
+        <font weight="700" style="normal" tag_wght="700" font_name="Noto Sans Gurmukhi UI Bold" postscript_name="NotoSansGurmukhiUI-Bold">NotoSansGurmukhiUI-VF.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansTamilUI-Regular.ttf</font>
         <font weight="400" style="normal">NotoSansTamilUI-Regular.otf</font>
         <font weight="700" style="normal">NotoSansTamilUI-Bold.ttf</font>
         <font weight="700" style="normal">NotoSansTamilUI-Bold.otf</font>
-        <font weight="400" style="normal">NotoSansTamilUI-VF.ttf</font>
-        <font weight="700" style="normal">NotoSansTamilUI-VF.ttf</font>
+        <font weight="400" style="normal" tag_wght="400" font_name="Noto Sans Tamil UI" postscript_name="NotoSansTamilUI-Regular">NotoSansTamilUI-VF.ttf</font>
+        <font weight="700" style="normal" tag_wght="700" font_name="Noto Sans Tamil UI Bold" postscript_name="NotoSansTamilUI-Bold">NotoSansTamilUI-VF.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf</font>
         <font weight="400" style="normal">NotoSansMalayalamUI-Regular.otf</font>
         <font weight="700" style="normal">NotoSansMalayalamUI-Bold.ttf</font>
         <font weight="700" style="normal">NotoSansMalayalamUI-Bold.otf</font>
-        <font weight="400" style="normal">NotoSansMalayalamUI-VF.ttf</font>
-        <font weight="700" style="normal">NotoSansMalayalamUI-VF.ttf</font>
+        <font weight="400" style="normal" tag_wght="400" font_name="Noto Sans Malayalam UI" postscript_name="NotoSansMalayalamUI-Regular">NotoSansMalayalamUI-VF.ttf</font>
+        <font weight="700" style="normal" tag_wght="700" font_name="Noto Sans Malayalam UI Bold" postscript_name="NotoSansMalayalamUI-Bold">NotoSansMalayalamUI-VF.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
         <font weight="400" style="normal">NotoSansBengaliUI-Regular.otf</font>
         <font weight="700" style="normal">NotoSansBengaliUI-Bold.ttf</font>
         <font weight="700" style="normal">NotoSansBengaliUI-Bold.otf</font>
-        <font weight="400" style="normal">NotoSansBengaliUI-VF.ttf</font>
-        <font weight="700" style="normal">NotoSansBengaliUI-VF.ttf</font>
+        <font weight="400" style="normal" tag_wght="400" font_name="Noto Sans Bengali UI" postscript_name="NotoSansBengaliUI-Regular">NotoSansBengaliUI-VF.ttf</font>
+        <font weight="700" style="normal" tag_wght="700" font_name="Noto Sans Bengali UI Bold" postscript_name="NotoSansBengaliUI-Bold">NotoSansBengaliUI-VF.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansTeluguUI-Bold.ttf</font>
-        <font weight="400" style="normal">NotoSansTeluguUI-VF.ttf</font>
-        <font weight="700" style="normal">NotoSansTeluguUI-VF.ttf</font>
+        <font weight="400" style="normal" tag_wght="400" font_name="Noto Sans Telugu UI" postscript_name="NotoSansTeluguUI-Regular">NotoSansTeluguUI-VF.ttf</font>
+        <font weight="700" style="normal" tag_wght="700" font_name="Noto Sans Telugu UI Bold" postscript_name="NotoSansTeluguUI-Bold">NotoSansTeluguUI-VF.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansKannadaUI-Bold.ttf</font>
-        <font weight="400" style="normal">NotoSansKannadaUI-VF.ttf</font>
-        <font weight="700" style="normal">NotoSansKannadaUI-VF.ttf</font>
+        <font weight="400" style="normal" tag_wght="400" font_name="Noto Sans Kannada UI" postscript_name="NotoSansKannadaUI-Regular">NotoSansKannadaUI-VF.ttf</font>
+        <font weight="700" style="normal" tag_wght="700" font_name="Noto Sans Kannada UI Bold" postscript_name="NotoSansKannadaUI-Bold">NotoSansKannadaUI-VF.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansOriyaUI-Regular.ttf</font>
@@ -267,8 +267,8 @@
         <font weight="400" style="normal">NotoSansSinhala-Regular.otf</font>
         <font weight="700" style="normal">NotoSansSinhala-Bold.ttf</font>
         <font weight="700" style="normal">NotoSansSinhala-Bold.otf</font>
-        <font weight="400" style="normal">NotoSansSinhala-VF.ttf</font>
-        <font weight="700" style="normal">NotoSansSinhala-VF.ttf</font>
+        <font weight="400" style="normal" tag_wght="400" font_name="Noto Sans Sinhala" postscript_name="NotoSansSinhala-Regular">NotoSansSinhala-VF.ttf</font>
+        <font weight="700" style="normal" tag_wght="700" font_name="Noto Sans Sinhala Bold" postscript_name="NotoSansSinhala-Bold">NotoSansSinhala-VF.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
@@ -294,6 +294,7 @@
     </family>
     <family>
         <font weight="400" style="normal">NotoSansAdlam-Regular.ttf</font>
+        <font weight="400" style="normal" tag_wght="400" font_name="Noto Sans Adlam" postscript_name="NotoSansAdlam-Regular">NotoSansAdlam-VF.ttf</font>
     </family>
     <family>
         <font weight="400" style="normal">NotoSansAvestan-Regular.ttf</font>
@@ -481,18 +482,11 @@
         <font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
     </family>
     <family>
-        <font weight="400" style="normal">Lohit-Odia.ttf</font>
-    </family>
-    <family>
         <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
     </family>
     <family lang="zh-Hans">
         <font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
     </family>
-    <family lang="zh-Hans">
-        <font weight="400" style="normal">NotoSansSC-Regular.otf</font>
-        <font weight="400" style="normal">NotoSansHans-Regular.otf</font>
-    </family>
     <family lang="zh-Hant">
         <font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
     </family>
@@ -509,12 +503,6 @@
     <family lang="ko">
         <font weight="400" style="normal" index="1">NotoSansCJK-Regular.ttc</font>
     </family>
-    <family lang="ko">
-        <font weight="400" style="normal">NotoSansKR-Regular.otf</font>
-    </family>
-    <family>
-        <font weight="400" style="normal">NanumGothic.ttf</font>
-    </family>
     <family>
         <font weight="400" style="normal" disable_synthetic_bolding="true">NotoColorEmoji.ttf</font>
     </family>
@@ -529,9 +517,6 @@
     <family>
         <font weight="400" style="normal" disable_synthetic_bolding="true">DroidSansFallback.ttf</font>
     </family>
-    <family lang="ja">
-        <font weight="400" style="normal">MTLmr3m.ttf</font>
-    </family>
     <!--
         Tai Le, Yi, Mongolian, and Phags-pa are intentionally kept last, to make sure they don't
         override the East Asian punctuation for Chinese.
@@ -548,49 +533,4 @@
     <family>
         <font weight="400" style="normal">NotoSansPhagsPa-Regular.ttf</font>
     </family>
-
-
-    <!--
-        Include font files on android version R under /system/fonts directory.
-        The program will find the first font file that contains the glyph for the character,
-        when no font file has the glyph, it will show broken string, to avoid this, include fonts
-        files on the android OS.
-    -->
-    <family>
-            <font weight="400" style="normal">NotoSansAdlam-VF.ttf</font>
-            <font weight="700" style="normal">NotoSansAdlam-VF.ttf</font>
-    </family>
-    <family>
-            <font weight="400" style="normal">NotoSansGeorgian-VF.ttf</font>
-            <font weight="700" style="normal">NotoSansGeorgian-VF.ttf</font>
-    </family>
-    <family>
-            <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font>
-    </family>
-    <family>
-            <font weight="400" style="normal">RobotoCondensed-Medium.ttf</font>
-    </family>
-    <family>
-            <font weight="400" style="normal">RobotoCondensed-MediumItalic.ttf</font>
-    </family>
-
-    <!--
-        Include font files on android version S under /system/fonts directory.
-    -->
-    <family lang="zh-Hans">
-        <font weight="400" style="normal" index="2">NotoSansCJKjp-Regular.otc</font>
-        <font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font>
-    </family>
-    <family lang="zh-Hant,zh-Bopo">
-        <font weight="400" style="normal" index="3">NotoSansCJKjp-Regular.otc</font>
-        <font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font>
-    </family>
-    <family lang="ja">
-        <font weight="400" style="normal" index="0">NotoSansCJKjp-Regular.otc</font>
-        <font weight="400" style="normal" index="0" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font>
-    </family>
-    <family lang="ko">
-        <font weight="400" style="normal" index="1">NotoSansCJKjp-Regular.otc</font>
-        <font weight="400" style="normal" index="1" fallbackFor="serif">NotoSerifCJKjp-Regular.otc</font>
-    </family>
 </familyset>
diff --git a/cobalt/content/licenses/platform/android/licenses_cobalt.txt b/cobalt/content/licenses/platform/android/licenses_cobalt.txt
index a286d10..8821b6d 100644
--- a/cobalt/content/licenses/platform/android/licenses_cobalt.txt
+++ b/cobalt/content/licenses/platform/android/licenses_cobalt.txt
@@ -1487,885 +1487,6 @@
 
 
 
-  nss
-
-
-    NSS is available under the Mozilla Public License, version 2, a copy of which
-  is below.
-
-  Note on GPL Compatibility
-  -------------------------
-
-  The MPL 2, section 3.3, permits you to combine NSS with code under the GNU
-  General Public License (GPL) version 2, or any later version of that
-  license, to make a Larger Work, and distribute the result under the GPL.
-  The only condition is that you must also make NSS, and any changes you
-  have made to it, available to recipients under the terms of the MPL 2 also.
-
-  Anyone who receives the combined code from you does not have to continue
-  to dual licence in this way, and may, if they wish, distribute under the
-  terms of either of the two licences - either the MPL alone or the GPL
-  alone. However, we discourage people from distributing copies of NSS under
-  the GPL alone, because it means that any improvements they make cannot be
-  reincorporated into the main version of NSS. There is never a need to do
-  this for license compatibility reasons.
-
-  Note on LGPL Compatibility
-  --------------------------
-
-  The above also applies to combining MPLed code in a single library with
-  code under the GNU Lesser General Public License (LGPL) version 2.1, or
-  any later version of that license. If the LGPLed code and the MPLed code
-  are not in the same library, then the copyleft coverage of the two
-  licences does not overlap, so no issues arise.
-
-
-  Mozilla Public License Version 2.0
-  ==================================
-
-  1. Definitions
-  --------------
-
-  1.1. "Contributor"
-      means each individual or legal entity that creates, contributes to
-      the creation of, or owns Covered Software.
-
-  1.2. "Contributor Version"
-      means the combination of the Contributions of others (if any) used
-      by a Contributor and that particular Contributor's Contribution.
-
-  1.3. "Contribution"
-      means Covered Software of a particular Contributor.
-
-  1.4. "Covered Software"
-      means Source Code Form to which the initial Contributor has attached
-      the notice in Exhibit A, the Executable Form of such Source Code
-      Form, and Modifications of such Source Code Form, in each case
-      including portions thereof.
-
-  1.5. "Incompatible With Secondary Licenses"
-      means
-
-      (a) that the initial Contributor has attached the notice described
-          in Exhibit B to the Covered Software; or
-
-      (b) that the Covered Software was made available under the terms of
-          version 1.1 or earlier of the License, but not also under the
-          terms of a Secondary License.
-
-  1.6. "Executable Form"
-      means any form of the work other than Source Code Form.
-
-  1.7. "Larger Work"
-      means a work that combines Covered Software with other material, in
-      a separate file or files, that is not Covered Software.
-
-  1.8. "License"
-      means this document.
-
-  1.9. "Licensable"
-      means having the right to grant, to the maximum extent possible,
-      whether at the time of the initial grant or subsequently, any and
-      all of the rights conveyed by this License.
-
-  1.10. "Modifications"
-      means any of the following:
-
-      (a) any file in Source Code Form that results from an addition to,
-          deletion from, or modification of the contents of Covered
-          Software; or
-
-      (b) any new file in Source Code Form that contains any Covered
-          Software.
-
-  1.11. "Patent Claims" of a Contributor
-      means any patent claim(s), including without limitation, method,
-      process, and apparatus claims, in any patent Licensable by such
-      Contributor that would be infringed, but for the grant of the
-      License, by the making, using, selling, offering for sale, having
-      made, import, or transfer of either its Contributions or its
-      Contributor Version.
-
-  1.12. "Secondary License"
-      means either the GNU General Public License, Version 2.0, the GNU
-      Lesser General Public License, Version 2.1, the GNU Affero General
-      Public License, Version 3.0, or any later versions of those
-      licenses.
-
-  1.13. "Source Code Form"
-      means the form of the work preferred for making modifications.
-
-  1.14. "You" (or "Your")
-      means an individual or a legal entity exercising rights under this
-      License. For legal entities, "You" includes any entity that
-      controls, is controlled by, or is under common control with You. For
-      purposes of this definition, "control" means (a) the power, direct
-      or indirect, to cause the direction or management of such entity,
-      whether by contract or otherwise, or (b) ownership of more than
-      fifty percent (50%) of the outstanding shares or beneficial
-      ownership of such entity.
-
-  2. License Grants and Conditions
-  --------------------------------
-
-  2.1. Grants
-
-  Each Contributor hereby grants You a world-wide, royalty-free,
-  non-exclusive license:
-
-  (a) under intellectual property rights (other than patent or trademark)
-      Licensable by such Contributor to use, reproduce, make available,
-      modify, display, perform, distribute, and otherwise exploit its
-      Contributions, either on an unmodified basis, with Modifications, or
-      as part of a Larger Work; and
-
-  (b) under Patent Claims of such Contributor to make, use, sell, offer
-      for sale, have made, import, and otherwise transfer either its
-      Contributions or its Contributor Version.
-
-  2.2. Effective Date
-
-  The licenses granted in Section 2.1 with respect to any Contribution
-  become effective for each Contribution on the date the Contributor first
-  distributes such Contribution.
-
-  2.3. Limitations on Grant Scope
-
-  The licenses granted in this Section 2 are the only rights granted under
-  this License. No additional rights or licenses will be implied from the
-  distribution or licensing of Covered Software under this License.
-  Notwithstanding Section 2.1(b) above, no patent license is granted by a
-  Contributor:
-
-  (a) for any code that a Contributor has removed from Covered Software;
-      or
-
-  (b) for infringements caused by: (i) Your and any other third party's
-      modifications of Covered Software, or (ii) the combination of its
-      Contributions with other software (except as part of its Contributor
-      Version); or
-
-  (c) under Patent Claims infringed by Covered Software in the absence of
-      its Contributions.
-
-  This License does not grant any rights in the trademarks, service marks,
-  or logos of any Contributor (except as may be necessary to comply with
-  the notice requirements in Section 3.4).
-
-  2.4. Subsequent Licenses
-
-  No Contributor makes additional grants as a result of Your choice to
-  distribute the Covered Software under a subsequent version of this
-  License (see Section 10.2) or under the terms of a Secondary License (if
-  permitted under the terms of Section 3.3).
-
-  2.5. Representation
-
-  Each Contributor represents that the Contributor believes its
-  Contributions are its original creation(s) or it has sufficient rights
-  to grant the rights to its Contributions conveyed by this License.
-
-  2.6. Fair Use
-
-  This License is not intended to limit any rights You have under
-  applicable copyright doctrines of fair use, fair dealing, or other
-  equivalents.
-
-  2.7. Conditions
-
-  Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-  in Section 2.1.
-
-  3. Responsibilities
-  -------------------
-
-  3.1. Distribution of Source Form
-
-  All distribution of Covered Software in Source Code Form, including any
-  Modifications that You create or to which You contribute, must be under
-  the terms of this License. You must inform recipients that the Source
-  Code Form of the Covered Software is governed by the terms of this
-  License, and how they can obtain a copy of this License. You may not
-  attempt to alter or restrict the recipients' rights in the Source Code
-  Form.
-
-  3.2. Distribution of Executable Form
-
-  If You distribute Covered Software in Executable Form then:
-
-  (a) such Covered Software must also be made available in Source Code
-      Form, as described in Section 3.1, and You must inform recipients of
-      the Executable Form how they can obtain a copy of such Source Code
-      Form by reasonable means in a timely manner, at a charge no more
-      than the cost of distribution to the recipient; and
-
-  (b) You may distribute such Executable Form under the terms of this
-      License, or sublicense it under different terms, provided that the
-      license for the Executable Form does not attempt to limit or alter
-      the recipients' rights in the Source Code Form under this License.
-
-  3.3. Distribution of a Larger Work
-
-  You may create and distribute a Larger Work under terms of Your choice,
-  provided that You also comply with the requirements of this License for
-  the Covered Software. If the Larger Work is a combination of Covered
-  Software with a work governed by one or more Secondary Licenses, and the
-  Covered Software is not Incompatible With Secondary Licenses, this
-  License permits You to additionally distribute such Covered Software
-  under the terms of such Secondary License(s), so that the recipient of
-  the Larger Work may, at their option, further distribute the Covered
-  Software under the terms of either this License or such Secondary
-  License(s).
-
-  3.4. Notices
-
-  You may not remove or alter the substance of any license notices
-  (including copyright notices, patent notices, disclaimers of warranty,
-  or limitations of liability) contained within the Source Code Form of
-  the Covered Software, except that You may alter any license notices to
-  the extent required to remedy known factual inaccuracies.
-
-  3.5. Application of Additional Terms
-
-  You may choose to offer, and to charge a fee for, warranty, support,
-  indemnity or liability obligations to one or more recipients of Covered
-  Software. However, You may do so only on Your own behalf, and not on
-  behalf of any Contributor. You must make it absolutely clear that any
-  such warranty, support, indemnity, or liability obligation is offered by
-  You alone, and You hereby agree to indemnify every Contributor for any
-  liability incurred by such Contributor as a result of warranty, support,
-  indemnity or liability terms You offer. You may include additional
-  disclaimers of warranty and limitations of liability specific to any
-  jurisdiction.
-
-  4. Inability to Comply Due to Statute or Regulation
-  ---------------------------------------------------
-
-  If it is impossible for You to comply with any of the terms of this
-  License with respect to some or all of the Covered Software due to
-  statute, judicial order, or regulation then You must: (a) comply with
-  the terms of this License to the maximum extent possible; and (b)
-  describe the limitations and the code they affect. Such description must
-  be placed in a text file included with all distributions of the Covered
-  Software under this License. Except to the extent prohibited by statute
-  or regulation, such description must be sufficiently detailed for a
-  recipient of ordinary skill to be able to understand it.
-
-  5. Termination
-  --------------
-
-  5.1. The rights granted under this License will terminate automatically
-  if You fail to comply with any of its terms. However, if You become
-  compliant, then the rights granted under this License from a particular
-  Contributor are reinstated (a) provisionally, unless and until such
-  Contributor explicitly and finally terminates Your grants, and (b) on an
-  ongoing basis, if such Contributor fails to notify You of the
-  non-compliance by some reasonable means prior to 60 days after You have
-  come back into compliance. Moreover, Your grants from a particular
-  Contributor are reinstated on an ongoing basis if such Contributor
-  notifies You of the non-compliance by some reasonable means, this is the
-  first time You have received notice of non-compliance with this License
-  from such Contributor, and You become compliant prior to 30 days after
-  Your receipt of the notice.
-
-  5.2. If You initiate litigation against any entity by asserting a patent
-  infringement claim (excluding declaratory judgment actions,
-  counter-claims, and cross-claims) alleging that a Contributor Version
-  directly or indirectly infringes any patent, then the rights granted to
-  You by any and all Contributors for the Covered Software under Section
-  2.1 of this License shall terminate.
-
-  5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-  end user license agreements (excluding distributors and resellers) which
-  have been validly granted by You or Your distributors under this License
-  prior to termination shall survive termination.
-
-  ************************************************************************
-  *                                                                      *
-  *  6. Disclaimer of Warranty                                           *
-  *  -------------------------                                           *
-  *                                                                      *
-  *  Covered Software is provided under this License on an "as is"       *
-  *  basis, without warranty of any kind, either expressed, implied, or  *
-  *  statutory, including, without limitation, warranties that the       *
-  *  Covered Software is free of defects, merchantable, fit for a        *
-  *  particular purpose or non-infringing. The entire risk as to the     *
-  *  quality and performance of the Covered Software is with You.        *
-  *  Should any Covered Software prove defective in any respect, You     *
-  *  (not any Contributor) assume the cost of any necessary servicing,   *
-  *  repair, or correction. This disclaimer of warranty constitutes an   *
-  *  essential part of this License. No use of any Covered Software is   *
-  *  authorized under this License except under this disclaimer.         *
-  *                                                                      *
-  ************************************************************************
-
-  ************************************************************************
-  *                                                                      *
-  *  7. Limitation of Liability                                          *
-  *  --------------------------                                          *
-  *                                                                      *
-  *  Under no circumstances and under no legal theory, whether tort      *
-  *  (including negligence), contract, or otherwise, shall any           *
-  *  Contributor, or anyone who distributes Covered Software as          *
-  *  permitted above, be liable to You for any direct, indirect,         *
-  *  special, incidental, or consequential damages of any character      *
-  *  including, without limitation, damages for lost profits, loss of    *
-  *  goodwill, work stoppage, computer failure or malfunction, or any    *
-  *  and all other commercial damages or losses, even if such party      *
-  *  shall have been informed of the possibility of such damages. This   *
-  *  limitation of liability shall not apply to liability for death or   *
-  *  personal injury resulting from such party's negligence to the       *
-  *  extent applicable law prohibits such limitation. Some               *
-  *  jurisdictions do not allow the exclusion or limitation of           *
-  *  incidental or consequential damages, so this exclusion and          *
-  *  limitation may not apply to You.                                    *
-  *                                                                      *
-  ************************************************************************
-
-  8. Litigation
-  -------------
-
-  Any litigation relating to this License may be brought only in the
-  courts of a jurisdiction where the defendant maintains its principal
-  place of business and such litigation shall be governed by laws of that
-  jurisdiction, without reference to its conflict-of-law provisions.
-  Nothing in this Section shall prevent a party's ability to bring
-  cross-claims or counter-claims.
-
-  9. Miscellaneous
-  ----------------
-
-  This License represents the complete agreement concerning the subject
-  matter hereof. If any provision of this License is held to be
-  unenforceable, such provision shall be reformed only to the extent
-  necessary to make it enforceable. Any law or regulation which provides
-  that the language of a contract shall be construed against the drafter
-  shall not be used to construe this License against a Contributor.
-
-  10. Versions of the License
-  ---------------------------
-
-  10.1. New Versions
-
-  Mozilla Foundation is the license steward. Except as provided in Section
-  10.3, no one other than the license steward has the right to modify or
-  publish new versions of this License. Each version will be given a
-  distinguishing version number.
-
-  10.2. Effect of New Versions
-
-  You may distribute the Covered Software under the terms of the version
-  of the License under which You originally received the Covered Software,
-  or under the terms of any subsequent version published by the license
-  steward.
-
-  10.3. Modified Versions
-
-  If you create software not governed by this License, and you want to
-  create a new license for such software, you may create and use a
-  modified version of this License if you rename the license and remove
-  any references to the name of the license steward (except to note that
-  such modified license differs from this License).
-
-  10.4. Distributing Source Code Form that is Incompatible With Secondary
-  Licenses
-
-  If You choose to distribute Source Code Form that is Incompatible With
-  Secondary Licenses under the terms of this version of the License, the
-  notice described in Exhibit B of this License must be attached.
-
-  Exhibit A - Source Code Form License Notice
-  -------------------------------------------
-
-    This Source Code Form is subject to the terms of the Mozilla Public
-    License, v. 2.0. If a copy of the MPL was not distributed with this
-    file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-  If it is not possible or desirable to put the notice in a particular
-  file, then You may include the notice in a location (such as a LICENSE
-  file in a relevant directory) where a recipient would be likely to look
-  for such a notice.
-
-  You may add additional accurate notices of copyright ownership.
-
-  Exhibit B - "Incompatible With Secondary Licenses" Notice
-  ---------------------------------------------------------
-
-    This Source Code Form is "Incompatible With Secondary Licenses", as
-    defined by the Mozilla Public License, v. 2.0.
-
-
-
-  mozilla_security_manager
-
-                             MOZILLA PUBLIC LICENSE
-                                Version 1.1
-
-                              ---------------
-
-  1. Definitions.
-
-     1.0.1. "Commercial Use" means distribution or otherwise making the
-     Covered Code available to a third party.
-
-     1.1. "Contributor" means each entity that creates or contributes to
-     the creation of Modifications.
-
-     1.2. "Contributor Version" means the combination of the Original
-     Code, prior Modifications used by a Contributor, and the Modifications
-     made by that particular Contributor.
-
-     1.3. "Covered Code" means the Original Code or Modifications or the
-     combination of the Original Code and Modifications, in each case
-     including portions thereof.
-
-     1.4. "Electronic Distribution Mechanism" means a mechanism generally
-     accepted in the software development community for the electronic
-     transfer of data.
-
-     1.5. "Executable" means Covered Code in any form other than Source
-     Code.
-
-     1.6. "Initial Developer" means the individual or entity identified
-     as the Initial Developer in the Source Code notice required by Exhibit
-     A.
-
-     1.7. "Larger Work" means a work which combines Covered Code or
-     portions thereof with code not governed by the terms of this License.
-
-     1.8. "License" means this document.
-
-     1.8.1. "Licensable" means having the right to grant, to the maximum
-     extent possible, whether at the time of the initial grant or
-     subsequently acquired, any and all of the rights conveyed herein.
-
-     1.9. "Modifications" means any addition to or deletion from the
-     substance or structure of either the Original Code or any previous
-     Modifications. When Covered Code is released as a series of files, a
-     Modification is:
-          A. Any addition to or deletion from the contents of a file
-          containing Original Code or previous Modifications.
-
-          B. Any new file that contains any part of the Original Code or
-          previous Modifications.
-
-     1.10. "Original Code" means Source Code of computer software code
-     which is described in the Source Code notice required by Exhibit A as
-     Original Code, and which, at the time of its release under this
-     License is not already Covered Code governed by this License.
-
-     1.10.1. "Patent Claims" means any patent claim(s), now owned or
-     hereafter acquired, including without limitation,  method, process,
-     and apparatus claims, in any patent Licensable by grantor.
-
-     1.11. "Source Code" means the preferred form of the Covered Code for
-     making modifications to it, including all modules it contains, plus
-     any associated interface definition files, scripts used to control
-     compilation and installation of an Executable, or source code
-     differential comparisons against either the Original Code or another
-     well known, available Covered Code of the Contributor's choice. The
-     Source Code can be in a compressed or archival form, provided the
-     appropriate decompression or de-archiving software is widely available
-     for no charge.
-
-     1.12. "You" (or "Your")  means an individual or a legal entity
-     exercising rights under, and complying with all of the terms of, this
-     License or a future version of this License issued under Section 6.1.
-     For legal entities, "You" includes any entity which controls, is
-     controlled by, or is under common control with You. For purposes of
-     this definition, "control" means (a) the power, direct or indirect,
-     to cause the direction or management of such entity, whether by
-     contract or otherwise, or (b) ownership of more than fifty percent
-     (50%) of the outstanding shares or beneficial ownership of such
-     entity.
-
-  2. Source Code License.
-
-     2.1. The Initial Developer Grant.
-     The Initial Developer hereby grants You a world-wide, royalty-free,
-     non-exclusive license, subject to third party intellectual property
-     claims:
-          (a)  under intellectual property rights (other than patent or
-          trademark) Licensable by Initial Developer to use, reproduce,
-          modify, display, perform, sublicense and distribute the Original
-          Code (or portions thereof) with or without Modifications, and/or
-          as part of a Larger Work; and
-
-          (b) under Patents Claims infringed by the making, using or
-          selling of Original Code, to make, have made, use, practice,
-          sell, and offer for sale, and/or otherwise dispose of the
-          Original Code (or portions thereof).
-
-          (c) the licenses granted in this Section 2.1(a) and (b) are
-          effective on the date Initial Developer first distributes
-          Original Code under the terms of this License.
-
-          (d) Notwithstanding Section 2.1(b) above, no patent license is
-          granted: 1) for code that You delete from the Original Code; 2)
-          separate from the Original Code;  or 3) for infringements caused
-          by: i) the modification of the Original Code or ii) the
-          combination of the Original Code with other software or devices.
-
-     2.2. Contributor Grant.
-     Subject to third party intellectual property claims, each Contributor
-     hereby grants You a world-wide, royalty-free, non-exclusive license
-
-          (a)  under intellectual property rights (other than patent or
-          trademark) Licensable by Contributor, to use, reproduce, modify,
-          display, perform, sublicense and distribute the Modifications
-          created by such Contributor (or portions thereof) either on an
-          unmodified basis, with other Modifications, as Covered Code
-          and/or as part of a Larger Work; and
-
-          (b) under Patent Claims infringed by the making, using, or
-          selling of  Modifications made by that Contributor either alone
-          and/or in combination with its Contributor Version (or portions
-          of such combination), to make, use, sell, offer for sale, have
-          made, and/or otherwise dispose of: 1) Modifications made by that
-          Contributor (or portions thereof); and 2) the combination of
-          Modifications made by that Contributor with its Contributor
-          Version (or portions of such combination).
-
-          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
-          effective on the date Contributor first makes Commercial Use of
-          the Covered Code.
-
-          (d)    Notwithstanding Section 2.2(b) above, no patent license is
-          granted: 1) for any code that Contributor has deleted from the
-          Contributor Version; 2)  separate from the Contributor Version;
-          3)  for infringements caused by: i) third party modifications of
-          Contributor Version or ii)  the combination of Modifications made
-          by that Contributor with other software  (except as part of the
-          Contributor Version) or other devices; or 4) under Patent Claims
-          infringed by Covered Code in the absence of Modifications made by
-          that Contributor.
-
-  3. Distribution Obligations.
-
-     3.1. Application of License.
-     The Modifications which You create or to which You contribute are
-     governed by the terms of this License, including without limitation
-     Section 2.2. The Source Code version of Covered Code may be
-     distributed only under the terms of this License or a future version
-     of this License released under Section 6.1, and You must include a
-     copy of this License with every copy of the Source Code You
-     distribute. You may not offer or impose any terms on any Source Code
-     version that alters or restricts the applicable version of this
-     License or the recipients' rights hereunder. However, You may include
-     an additional document offering the additional rights described in
-     Section 3.5.
-
-     3.2. Availability of Source Code.
-     Any Modification which You create or to which You contribute must be
-     made available in Source Code form under the terms of this License
-     either on the same media as an Executable version or via an accepted
-     Electronic Distribution Mechanism to anyone to whom you made an
-     Executable version available; and if made available via Electronic
-     Distribution Mechanism, must remain available for at least twelve (12)
-     months after the date it initially became available, or at least six
-     (6) months after a subsequent version of that particular Modification
-     has been made available to such recipients. You are responsible for
-     ensuring that the Source Code version remains available even if the
-     Electronic Distribution Mechanism is maintained by a third party.
-
-     3.3. Description of Modifications.
-     You must cause all Covered Code to which You contribute to contain a
-     file documenting the changes You made to create that Covered Code and
-     the date of any change. You must include a prominent statement that
-     the Modification is derived, directly or indirectly, from Original
-     Code provided by the Initial Developer and including the name of the
-     Initial Developer in (a) the Source Code, and (b) in any notice in an
-     Executable version or related documentation in which You describe the
-     origin or ownership of the Covered Code.
-
-     3.4. Intellectual Property Matters
-          (a) Third Party Claims.
-          If Contributor has knowledge that a license under a third party's
-          intellectual property rights is required to exercise the rights
-          granted by such Contributor under Sections 2.1 or 2.2,
-          Contributor must include a text file with the Source Code
-          distribution titled "LEGAL" which describes the claim and the
-          party making the claim in sufficient detail that a recipient will
-          know whom to contact. If Contributor obtains such knowledge after
-          the Modification is made available as described in Section 3.2,
-          Contributor shall promptly modify the LEGAL file in all copies
-          Contributor makes available thereafter and shall take other steps
-          (such as notifying appropriate mailing lists or newsgroups)
-          reasonably calculated to inform those who received the Covered
-          Code that new knowledge has been obtained.
-
-          (b) Contributor APIs.
-          If Contributor's Modifications include an application programming
-          interface and Contributor has knowledge of patent licenses which
-          are reasonably necessary to implement that API, Contributor must
-          also include this information in the LEGAL file.
-
-               (c)    Representations.
-          Contributor represents that, except as disclosed pursuant to
-          Section 3.4(a) above, Contributor believes that Contributor's
-          Modifications are Contributor's original creation(s) and/or
-          Contributor has sufficient rights to grant the rights conveyed by
-          this License.
-
-     3.5. Required Notices.
-     You must duplicate the notice in Exhibit A in each file of the Source
-     Code.  If it is not possible to put such notice in a particular Source
-     Code file due to its structure, then You must include such notice in a
-     location (such as a relevant directory) where a user would be likely
-     to look for such a notice.  If You created one or more Modification(s)
-     You may add your name as a Contributor to the notice described in
-     Exhibit A.  You must also duplicate this License in any documentation
-     for the Source Code where You describe recipients' rights or ownership
-     rights relating to Covered Code.  You may choose to offer, and to
-     charge a fee for, warranty, support, indemnity or liability
-     obligations to one or more recipients of Covered Code. However, You
-     may do so only on Your own behalf, and not on behalf of the Initial
-     Developer or any Contributor. You must make it absolutely clear than
-     any such warranty, support, indemnity or liability obligation is
-     offered by You alone, and You hereby agree to indemnify the Initial
-     Developer and every Contributor for any liability incurred by the
-     Initial Developer or such Contributor as a result of warranty,
-     support, indemnity or liability terms You offer.
-
-     3.6. Distribution of Executable Versions.
-     You may distribute Covered Code in Executable form only if the
-     requirements of Section 3.1-3.5 have been met for that Covered Code,
-     and if You include a notice stating that the Source Code version of
-     the Covered Code is available under the terms of this License,
-     including a description of how and where You have fulfilled the
-     obligations of Section 3.2. The notice must be conspicuously included
-     in any notice in an Executable version, related documentation or
-     collateral in which You describe recipients' rights relating to the
-     Covered Code. You may distribute the Executable version of Covered
-     Code or ownership rights under a license of Your choice, which may
-     contain terms different from this License, provided that You are in
-     compliance with the terms of this License and that the license for the
-     Executable version does not attempt to limit or alter the recipient's
-     rights in the Source Code version from the rights set forth in this
-     License. If You distribute the Executable version under a different
-     license You must make it absolutely clear that any terms which differ
-     from this License are offered by You alone, not by the Initial
-     Developer or any Contributor. You hereby agree to indemnify the
-     Initial Developer and every Contributor for any liability incurred by
-     the Initial Developer or such Contributor as a result of any such
-     terms You offer.
-
-     3.7. Larger Works.
-     You may create a Larger Work by combining Covered Code with other code
-     not governed by the terms of this License and distribute the Larger
-     Work as a single product. In such a case, You must make sure the
-     requirements of this License are fulfilled for the Covered Code.
-
-  4. Inability to Comply Due to Statute or Regulation.
-
-     If it is impossible for You to comply with any of the terms of this
-     License with respect to some or all of the Covered Code due to
-     statute, judicial order, or regulation then You must: (a) comply with
-     the terms of this License to the maximum extent possible; and (b)
-     describe the limitations and the code they affect. Such description
-     must be included in the LEGAL file described in Section 3.4 and must
-     be included with all distributions of the Source Code. Except to the
-     extent prohibited by statute or regulation, such description must be
-     sufficiently detailed for a recipient of ordinary skill to be able to
-     understand it.
-
-  5. Application of this License.
-
-     This License applies to code to which the Initial Developer has
-     attached the notice in Exhibit A and to related Covered Code.
-
-  6. Versions of the License.
-
-     6.1. New Versions.
-     Netscape Communications Corporation ("Netscape") may publish revised
-     and/or new versions of the License from time to time. Each version
-     will be given a distinguishing version number.
-
-     6.2. Effect of New Versions.
-     Once Covered Code has been published under a particular version of the
-     License, You may always continue to use it under the terms of that
-     version. You may also choose to use such Covered Code under the terms
-     of any subsequent version of the License published by Netscape. No one
-     other than Netscape has the right to modify the terms applicable to
-     Covered Code created under this License.
-
-     6.3. Derivative Works.
-     If You create or use a modified version of this License (which you may
-     only do in order to apply it to code which is not already Covered Code
-     governed by this License), You must (a) rename Your license so that
-     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
-     "MPL", "NPL" or any confusingly similar phrase do not appear in your
-     license (except to note that your license differs from this License)
-     and (b) otherwise make it clear that Your version of the license
-     contains terms which differ from the Mozilla Public License and
-     Netscape Public License. (Filling in the name of the Initial
-     Developer, Original Code or Contributor in the notice described in
-     Exhibit A shall not of themselves be deemed to be modifications of
-     this License.)
-
-  7. DISCLAIMER OF WARRANTY.
-
-     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
-     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
-     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
-     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
-     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
-     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
-     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
-     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
-     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
-     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-  8. TERMINATION.
-
-     8.1.  This License and the rights granted hereunder will terminate
-     automatically if You fail to comply with terms herein and fail to cure
-     such breach within 30 days of becoming aware of the breach. All
-     sublicenses to the Covered Code which are properly granted shall
-     survive any termination of this License. Provisions which, by their
-     nature, must remain in effect beyond the termination of this License
-     shall survive.
-
-     8.2.  If You initiate litigation by asserting a patent infringement
-     claim (excluding declatory judgment actions) against Initial Developer
-     or a Contributor (the Initial Developer or Contributor against whom
-     You file such action is referred to as "Participant")  alleging that:
-
-     (a)  such Participant's Contributor Version directly or indirectly
-     infringes any patent, then any and all rights granted by such
-     Participant to You under Sections 2.1 and/or 2.2 of this License
-     shall, upon 60 days notice from Participant terminate prospectively,
-     unless if within 60 days after receipt of notice You either: (i)
-     agree in writing to pay Participant a mutually agreeable reasonable
-     royalty for Your past and future use of Modifications made by such
-     Participant, or (ii) withdraw Your litigation claim with respect to
-     the Contributor Version against such Participant.  If within 60 days
-     of notice, a reasonable royalty and payment arrangement are not
-     mutually agreed upon in writing by the parties or the litigation claim
-     is not withdrawn, the rights granted by Participant to You under
-     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
-     the 60 day notice period specified above.
-
-     (b)  any software, hardware, or device, other than such Participant's
-     Contributor Version, directly or indirectly infringes any patent, then
-     any rights granted to You by such Participant under Sections 2.1(b)
-     and 2.2(b) are revoked effective as of the date You first made, used,
-     sold, distributed, or had made, Modifications made by that
-     Participant.
-
-     8.3.  If You assert a patent infringement claim against Participant
-     alleging that such Participant's Contributor Version directly or
-     indirectly infringes any patent where such claim is resolved (such as
-     by license or settlement) prior to the initiation of patent
-     infringement litigation, then the reasonable value of the licenses
-     granted by such Participant under Sections 2.1 or 2.2 shall be taken
-     into account in determining the amount or value of any payment or
-     license.
-
-     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
-     all end user license agreements (excluding distributors and resellers)
-     which have been validly granted by You or any distributor hereunder
-     prior to termination shall survive termination.
-
-  9. LIMITATION OF LIABILITY.
-
-     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
-     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
-     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
-     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
-     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
-     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
-     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
-     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
-     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
-     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
-     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
-     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
-     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
-     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-  10. U.S. GOVERNMENT END USERS.
-
-     The Covered Code is a "commercial item," as that term is defined in
-     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
-     software" and "commercial computer software documentation," as such
-     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
-     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
-     all U.S. Government End Users acquire Covered Code with only those
-     rights set forth herein.
-
-  11. MISCELLANEOUS.
-
-     This License represents the complete agreement concerning subject
-     matter hereof. If any provision of this License is held to be
-     unenforceable, such provision shall be reformed only to the extent
-     necessary to make it enforceable. This License shall be governed by
-     California law provisions (except to the extent applicable law, if
-     any, provides otherwise), excluding its conflict-of-law provisions.
-     With respect to disputes in which at least one party is a citizen of,
-     or an entity chartered or registered to do business in the United
-     States of America, any litigation relating to this License shall be
-     subject to the jurisdiction of the Federal Courts of the Northern
-     District of California, with venue lying in Santa Clara County,
-     California, with the losing party responsible for costs, including
-     without limitation, court costs and reasonable attorneys' fees and
-     expenses. The application of the United Nations Convention on
-     Contracts for the International Sale of Goods is expressly excluded.
-     Any law or regulation which provides that the language of a contract
-     shall be construed against the drafter shall not apply to this
-     License.
-
-  12. RESPONSIBILITY FOR CLAIMS.
-
-     As between Initial Developer and the Contributors, each party is
-     responsible for claims and damages arising, directly or indirectly,
-     out of its utilization of rights under this License and You agree to
-     work with Initial Developer and Contributors to distribute such
-     responsibility on an equitable basis. Nothing herein is intended or
-     shall be deemed to constitute any admission of liability.
-
-  13. MULTIPLE-LICENSED CODE.
-
-     Initial Developer may designate portions of the Covered Code as
-     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
-     Developer permits you to utilize portions of the Covered Code under
-     Your choice of the NPL or the alternative licenses, if any, specified
-     by the Initial Developer in the file described in Exhibit A.
-
-  EXHIBIT A -Mozilla Public License.
-
-   The contents of this file are subject to the Mozilla Public License Version
-   1.1 (the "License"); you may not use this file except in compliance with
-   the License. You may obtain a copy of the License at
-   http://www.mozilla.org/MPL/
-
-   Software distributed under the License is distributed on an "AS IS" basis,
-   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-   for the specific language governing rights and limitations under the
-   License.
-
-   The Original Code is mozilla.org code.
-
-   The Initial Developer of the Original Code is
-   Netscape Communications Corporation.
-   Portions created by the Initial Developer are Copyright (C) 2001
-   the Initial Developer. All Rights Reserved.
-
-   Contributor(s):
-
-   Alternatively, the contents of this file may be used under the terms of
-   either the GNU General Public License Version 2 or later (the "GPL"), or
-   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-   in which case the provisions of the GPL or the LGPL are applicable instead
-   of those above. If you wish to allow use of your version of this file only
-   under the terms of either the GPL or the LGPL, and not to allow others to
-   use your version of this file under the terms of the MPL, indicate your
-   decision by deleting the provisions above and replace them with the notice
-   and other provisions required by the GPL or the LGPL. If you do not delete
-   the provisions above, a recipient may use your version of this file under
-   the terms of any one of the MPL, the GPL or the LGPL.
-
-
   mozilla(url/third_party/mozilla)
 
 
@@ -3181,6 +2302,115 @@
 
 
 
+  libjpeg-turbo
+
+  libjpeg-turbo Licenses libjpeg-turbo is covered by three compatible BSD-style
+  open source licenses:
+
+  The IJG (Independent JPEG Group) License, which is listed in README.ijg
+
+  This license applies to the libjpeg API library and associated programs (any
+  code inherited from libjpeg, and any modifications to that code.)
+
+  The Modified (3-clause) BSD License, which is listed below
+
+  This license covers the TurboJPEG API library and associated programs, as well
+  as the build system.
+
+  The zlib License
+
+  This license is a subset of the other two, and it covers the libjpeg-turbo
+  SIMD extensions.
+
+  Complying with the libjpeg-turbo Licenses This section provides a roll-up of
+  the libjpeg-turbo licensing terms, to the best of our understanding.
+
+  If you are distributing a modified version of the libjpeg-turbo source, then:
+
+  You cannot alter or remove any existing copyright or license notices from the
+  source.
+
+  Origin
+
+  Clause 1 of the IJG License Clause 1 of the Modified BSD License Clauses 1 and
+  3 of the zlib License You must add your own copyright notice to the header of
+  each source file you modified, so others can tell that you modified that file
+  (if there is not an existing copyright header in that file, then you can
+  simply add a notice stating that you modified the file.)
+
+  Origin
+
+  Clause 1 of the IJG License Clause 2 of the zlib License You must include the
+  IJG README file, and you must not alter any of the copyright or license text
+  in that file.
+
+  Origin
+
+  Clause 1 of the IJG License If you are distributing only libjpeg-turbo
+  binaries without the source, or if you are distributing an application that
+  statically links with libjpeg-turbo, then:
+
+  Your product documentation must include a message stating:
+
+  This software is based in part on the work of the Independent JPEG Group.
+
+  Origin
+
+  Clause 2 of the IJG license If your binary distribution includes or uses the
+  TurboJPEG API, then your product documentation must include the text of the
+  Modified BSD License (see below.)
+
+  Origin
+
+  Clause 2 of the Modified BSD License You cannot use the name of the IJG or The
+  libjpeg-turbo Project or the contributors thereof in advertising, publicity,
+  etc.
+
+  Origin
+
+  IJG License Clause 3 of the Modified BSD License The IJG and The libjpeg-turbo
+  Project do not warrant libjpeg-turbo to be free of defects, nor do we accept
+  any liability for undesirable consequences resulting from your use of the
+  software.
+
+  Origin
+
+  IJG License Modified BSD License zlib License The Modified (3-clause) BSD
+  License Copyright (C)2009-2021 D. R. Commander. All Rights Reserved.
+  Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.  Redistributions in binary
+  form must reproduce the above copyright notice, this list of conditions and
+  the following disclaimer in the documentation and/or other materials provided
+  with the distribution.  Neither the name of the libjpeg-turbo Project nor the
+  names of its contributors may be used to endorse or promote products derived
+  from this software without specific prior written permission.  THIS SOFTWARE
+  IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+  EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Why Three Licenses?  The zlib License could have been used instead of the
+  Modified (3-clause) BSD License, and since the IJG License effectively
+  subsumes the distribution conditions of the zlib License, this would have
+  effectively placed libjpeg-turbo binary distributions under the IJG License.
+  However, the IJG License specifically refers to the Independent JPEG Group and
+  does not extend attribution and endorsement protections to other entities.
+  Thus, it was desirable to choose a license that granted us the same
+  protections for new code that were granted to the IJG for code derived from
+  their software.
+
+
   libpng
 
 
@@ -3997,6 +3227,41 @@
   POSSIBILITY OF SUCH DAMAGE.
 
 
+
+  flac
+
+
+  Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007  Josh Coalson
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  - Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+  - Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+  - Neither the name of the Xiph.org Foundation nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
   angle
 
 
@@ -5003,6 +4268,36 @@
   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
+
+  lz4_lib
+
+
+  LZ4 Library
+  Copyright (c) 2011-2016, Yann Collet
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without modification,
+  are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright notice, this
+    list of conditions and the following disclaimer in the documentation and/or
+    other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
   libwebp
 
   Copyright (c) 2010, Google Inc. All rights reserved.  Redistribution and use
diff --git a/cobalt/content/licenses/platform/darwin/licenses_cobalt.txt b/cobalt/content/licenses/platform/darwin/licenses_cobalt.txt
new file mode 100644
index 0000000..d80e0a5
--- /dev/null
+++ b/cobalt/content/licenses/platform/darwin/licenses_cobalt.txt
@@ -0,0 +1,4343 @@
+Where applicable, source code for modified versions of the libraries below is available at:
+https://cobalt.googlesource.com/cobalt.
+
+
+
+  Cobalt
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+   1. Definitions.
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+   END OF TERMS AND CONDITIONS
+   APPENDIX: How to apply the Apache License to your work.
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+   Copyright [yyyy] [name of copyright owner]
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+       http://www.apache.org/licenses/LICENSE-2.0
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+  Chromium
+
+  // Copyright 2015 The Chromium Authors. All rights reserved.
+  //
+  // Redistribution and use in source and binary forms, with or without
+  // modification, are permitted provided that the following conditions are
+  // met:
+  //
+  //    * Redistributions of source code must retain the above copyright
+  // notice, this list of conditions and the following disclaimer.
+  //    * Redistributions in binary form must reproduce the above
+  // copyright notice, this list of conditions and the following disclaimer
+  // in the documentation and/or other materials provided with the
+  // distribution.
+  //    * Neither the name of Google Inc. nor the names of its
+  // contributors may be used to endorse or promote products derived from
+  // this software without specific prior written permission.
+  //
+  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  V8
+
+  This license applies to all parts of V8 that are not externally
+  maintained libraries.  The externally maintained libraries used by V8
+  are:
+
+    - PCRE test suite, located in
+      test/mjsunit/third_party/regexp-pcre/regexp-pcre.js.  This is based on the
+      test suite from PCRE-7.3, which is copyrighted by the University
+      of Cambridge and Google, Inc.  The copyright notice and license
+      are embedded in regexp-pcre.js.
+
+    - Layout tests, located in test/mjsunit/third_party/object-keys.  These are
+      based on layout tests from webkit.org which are copyrighted by
+      Apple Computer, Inc. and released under a 3-clause BSD license.
+
+    - Strongtalk assembler, the basis of the files assembler-arm-inl.h,
+      assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h,
+      assembler-ia32.cc, assembler-ia32.h, assembler-x64-inl.h,
+      assembler-x64.cc, assembler-x64.h, assembler-mips-inl.h,
+      assembler-mips.cc, assembler-mips.h, assembler.cc and assembler.h.
+      This code is copyrighted by Sun Microsystems Inc. and released
+      under a 3-clause BSD license.
+
+    - Valgrind client API header, located at src/third_party/valgrind/valgrind.h
+      This is released under the BSD license.
+
+    - The Wasm C/C++ API headers, located at third_party/wasm-api/wasm.{h,hh}
+      This is released under the Apache license. The API's upstream prototype
+      implementation also formed the basis of V8's implementation in
+      src/wasm/c-api.cc.
+
+  These libraries have their own licenses; we recommend you read them,
+  as their terms may differ from the terms below.
+
+  Further license information can be found in LICENSE files located in
+  sub-directories.
+
+  Copyright 2014, the V8 project authors. All rights reserved.
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+      * Neither the name of Google Inc. nor the names of its
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  devtools
+
+
+  // Copyright 2014 The Chromium Authors. All rights reserved.
+  //
+  // Redistribution and use in source and binary forms, with or without
+  // modification, are permitted provided that the following conditions are
+  // met:
+  //
+  //    * Redistributions of source code must retain the above copyright
+  // notice, this list of conditions and the following disclaimer.
+  //    * Redistributions in binary form must reproduce the above
+  // copyright notice, this list of conditions and the following disclaimer
+  // in the documentation and/or other materials provided with the
+  // distribution.
+  //    * Neither the name of Google Inc. nor the names of its
+  // contributors may be used to endorse or promote products derived from
+  // this software without specific prior written permission.
+  //
+  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+  boringssl
+
+
+  BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
+  licensing. Files that are completely new have a Google copyright and an ISC
+  license. This license is reproduced at the bottom of this file.
+
+  Contributors to BoringSSL are required to follow the CLA rules for Chromium:
+  https://cla.developers.google.com/clas
+
+  Files in third_party/ have their own licenses, as described therein. The MIT
+  license, for third_party/fiat, which, unlike other third_party directories, is
+  compiled into non-test libraries, is included below.
+
+  The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the
+  OpenSSL License and the original SSLeay license apply to the toolkit. See below
+  for the actual license texts. Actually both licenses are BSD-style Open Source
+  licenses. In case of any license issues related to OpenSSL please contact
+  openssl-core@openssl.org.
+
+  The following are Google-internal bug numbers where explicit permission from
+  some authors is recorded for use of their work. (This is purely for our own
+  record keeping.)
+    27287199
+    27287880
+    27287883
+
+    OpenSSL License
+    ---------------
+
+  /* ====================================================================
+   * Copyright (c) 1998-2011 The OpenSSL Project.  All rights reserved.
+   *
+   * Redistribution and use in source and binary forms, with or without
+   * modification, are permitted provided that the following conditions
+   * are met:
+   *
+   * 1. Redistributions of source code must retain the above copyright
+   *    notice, this list of conditions and the following disclaimer.
+   *
+   * 2. Redistributions in binary form must reproduce the above copyright
+   *    notice, this list of conditions and the following disclaimer in
+   *    the documentation and/or other materials provided with the
+   *    distribution.
+   *
+   * 3. All advertising materials mentioning features or use of this
+   *    software must display the following acknowledgment:
+   *    "This product includes software developed by the OpenSSL Project
+   *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+   *
+   * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+   *    endorse or promote products derived from this software without
+   *    prior written permission. For written permission, please contact
+   *    openssl-core@openssl.org.
+   *
+   * 5. Products derived from this software may not be called "OpenSSL"
+   *    nor may "OpenSSL" appear in their names without prior written
+   *    permission of the OpenSSL Project.
+   *
+   * 6. Redistributions of any form whatsoever must retain the following
+   *    acknowledgment:
+   *    "This product includes software developed by the OpenSSL Project
+   *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+   *
+   * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+   * OF THE POSSIBILITY OF SUCH DAMAGE.
+   * ====================================================================
+   *
+   * This product includes cryptographic software written by Eric Young
+   * (eay@cryptsoft.com).  This product includes software written by Tim
+   * Hudson (tjh@cryptsoft.com).
+   *
+   */
+
+   Original SSLeay License
+   -----------------------
+
+  /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+   * All rights reserved.
+   *
+   * This package is an SSL implementation written
+   * by Eric Young (eay@cryptsoft.com).
+   * The implementation was written so as to conform with Netscapes SSL.
+   *
+   * This library is free for commercial and non-commercial use as long as
+   * the following conditions are aheared to.  The following conditions
+   * apply to all code found in this distribution, be it the RC4, RSA,
+   * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+   * included with this distribution is covered by the same copyright terms
+   * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+   *
+   * Copyright remains Eric Young's, and as such any Copyright notices in
+   * the code are not to be removed.
+   * If this package is used in a product, Eric Young should be given attribution
+   * as the author of the parts of the library used.
+   * This can be in the form of a textual message at program startup or
+   * in documentation (online or textual) provided with the package.
+   *
+   * Redistribution and use in source and binary forms, with or without
+   * modification, are permitted provided that the following conditions
+   * are met:
+   * 1. Redistributions of source code must retain the copyright
+   *    notice, this list of conditions and the following disclaimer.
+   * 2. Redistributions in binary form must reproduce the above copyright
+   *    notice, this list of conditions and the following disclaimer in the
+   *    documentation and/or other materials provided with the distribution.
+   * 3. All advertising materials mentioning features or use of this software
+   *    must display the following acknowledgement:
+   *    "This product includes cryptographic software written by
+   *     Eric Young (eay@cryptsoft.com)"
+   *    The word 'cryptographic' can be left out if the rouines from the library
+   *    being used are not cryptographic related :-).
+   * 4. If you include any Windows specific code (or a derivative thereof) from
+   *    the apps directory (application code) you must include an acknowledgement:
+   *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+   *
+   * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+   * SUCH DAMAGE.
+   *
+   * The licence and distribution terms for any publically available version or
+   * derivative of this code cannot be changed.  i.e. this code cannot simply be
+   * copied and put under another distribution licence
+   * [including the GNU Public Licence.]
+   */
+
+
+  ISC license used for completely new code in BoringSSL:
+
+  /* Copyright (c) 2015, Google Inc.
+   *
+   * Permission to use, copy, modify, and/or distribute this software for any
+   * purpose with or without fee is hereby granted, provided that the above
+   * copyright notice and this permission notice appear in all copies.
+   *
+   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+   * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+   * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+   * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+
+  The code in third_party/fiat carries the MIT license:
+
+  Copyright (c) 2015-2016 the fiat-crypto authors (see
+  https://github.com/mit-plv/fiat-crypto/blob/master/AUTHORS).
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in all
+  copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+
+
+  Licenses for support code
+  -------------------------
+
+  Parts of the TLS test suite are under the Go license. This code is not included
+  in BoringSSL (i.e. libcrypto and libssl) when compiled, however, so
+  distributing code linked against BoringSSL does not trigger this license:
+
+  Copyright (c) 2009 The Go Authors. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+     * Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+     * Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the following disclaimer
+  in the documentation and/or other materials provided with the
+  distribution.
+     * Neither the name of Google Inc. nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+  BoringSSL uses the Chromium test infrastructure to run a continuous build,
+  trybots etc. The scripts which manage this, and the script for generating build
+  metadata, are under the Chromium license. Distributing code linked against
+  BoringSSL does not trigger this license.
+
+  Copyright 2015 The Chromium Authors. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+     * Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+     * Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the following disclaimer
+  in the documentation and/or other materials provided with the
+  distribution.
+     * Neither the name of Google Inc. nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  dmg_fp
+
+
+  /****************************************************************
+   *
+   * The author of this software is David M. Gay.
+   *
+   * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.
+   *
+   * Permission to use, copy, modify, and distribute this software for any
+   * purpose without fee is hereby granted, provided that this entire notice
+   * is included in all copies of any software which is or includes a copy
+   * or modification of this software and in all copies of the supporting
+   * documentation for such software.
+   *
+   * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
+   * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY
+   * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
+   * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
+   *
+   ***************************************************************/
+
+
+
+  dynamic_annotations
+
+
+  /* Copyright (c) 2008-2009, Google Inc.
+   * All rights reserved.
+   *
+   * Redistribution and use in source and binary forms, with or without
+   * modification, are permitted provided that the following conditions are
+   * met:
+   *
+   *     * Redistributions of source code must retain the above copyright
+   * notice, this list of conditions and the following disclaimer.
+   *     * Neither the name of Google Inc. nor the names of its
+   * contributors may be used to endorse or promote products derived from
+   * this software without specific prior written permission.
+   *
+   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+   *
+   * ---
+   * Author: Kostya Serebryany
+   */
+
+
+
+  icu(base/third_party/icu)
+
+
+  COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later)
+
+  Copyright © 1991-2017 Unicode, Inc. All rights reserved.
+  Distributed under the Terms of Use in http://www.unicode.org/copyright.html
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of the Unicode data files and any associated documentation
+  (the "Data Files") or Unicode software and any associated documentation
+  (the "Software") to deal in the Data Files or Software
+  without restriction, including without limitation the rights to use,
+  copy, modify, merge, publish, distribute, and/or sell copies of
+  the Data Files or Software, and to permit persons to whom the Data Files
+  or Software are furnished to do so, provided that either
+  (a) this copyright and permission notice appear with all copies
+  of the Data Files or Software, or
+  (b) this copyright and permission notice appear in associated
+  Documentation.
+
+  THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+  ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+  WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+  NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+  DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+  DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+  TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+  PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+  Except as contained in this notice, the name of a copyright holder
+  shall not be used in advertising or otherwise to promote the sale,
+  use or other dealings in these Data Files or Software without prior
+  written authorization of the copyright holder.
+
+  ---------------------
+
+  Third-Party Software Licenses
+
+  This section contains third-party software notices and/or additional
+  terms for licensed third-party software components included within ICU
+  libraries.
+
+  1. ICU License - ICU 1.8.1 to ICU 57.1
+
+  COPYRIGHT AND PERMISSION NOTICE
+
+  Copyright (c) 1995-2016 International Business Machines Corporation and others
+  All rights reserved.
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, and/or sell copies of the Software, and to permit persons
+  to whom the Software is furnished to do so, provided that the above
+  copyright notice(s) and this permission notice appear in all copies of
+  the Software and that both the above copyright notice(s) and this
+  permission notice appear in supporting documentation.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+  OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+  HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
+  SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+  RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+  CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+  Except as contained in this notice, the name of a copyright holder
+  shall not be used in advertising or otherwise to promote the sale, use
+  or other dealings in this Software without prior written authorization
+  of the copyright holder.
+
+  All trademarks and registered trademarks mentioned herein are the
+  property of their respective owners.
+
+
+
+  libxml
+
+
+  LibXml Ruby Project
+    Copyright (c) 2008-2013 Charlie Savage and contributors
+    Copyright (c) 2002-2007 Sean Chittenden and contributors
+    Copyright (c) 2001 Wai-Sun "Squidster" Chia
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy of
+  this software and associated documentation files (the "Software"), to deal in
+  the Software without restriction, including without limitation the rights to
+  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+  of the Software, and to permit persons to whom the Software is furnished to do
+  so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in all
+  copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+
+
+
+  Netscape Portable Runtime (NSPR)
+
+                             MOZILLA PUBLIC LICENSE
+                                Version 1.1
+
+                              ---------------
+
+  1. Definitions.
+
+     1.0.1. "Commercial Use" means distribution or otherwise making the
+     Covered Code available to a third party.
+
+     1.1. "Contributor" means each entity that creates or contributes to
+     the creation of Modifications.
+
+     1.2. "Contributor Version" means the combination of the Original
+     Code, prior Modifications used by a Contributor, and the Modifications
+     made by that particular Contributor.
+
+     1.3. "Covered Code" means the Original Code or Modifications or the
+     combination of the Original Code and Modifications, in each case
+     including portions thereof.
+
+     1.4. "Electronic Distribution Mechanism" means a mechanism generally
+     accepted in the software development community for the electronic
+     transfer of data.
+
+     1.5. "Executable" means Covered Code in any form other than Source
+     Code.
+
+     1.6. "Initial Developer" means the individual or entity identified
+     as the Initial Developer in the Source Code notice required by Exhibit
+     A.
+
+     1.7. "Larger Work" means a work which combines Covered Code or
+     portions thereof with code not governed by the terms of this License.
+
+     1.8. "License" means this document.
+
+     1.8.1. "Licensable" means having the right to grant, to the maximum
+     extent possible, whether at the time of the initial grant or
+     subsequently acquired, any and all of the rights conveyed herein.
+
+     1.9. "Modifications" means any addition to or deletion from the
+     substance or structure of either the Original Code or any previous
+     Modifications. When Covered Code is released as a series of files, a
+     Modification is:
+          A. Any addition to or deletion from the contents of a file
+          containing Original Code or previous Modifications.
+
+          B. Any new file that contains any part of the Original Code or
+          previous Modifications.
+
+     1.10. "Original Code" means Source Code of computer software code
+     which is described in the Source Code notice required by Exhibit A as
+     Original Code, and which, at the time of its release under this
+     License is not already Covered Code governed by this License.
+
+     1.10.1. "Patent Claims" means any patent claim(s), now owned or
+     hereafter acquired, including without limitation,  method, process,
+     and apparatus claims, in any patent Licensable by grantor.
+
+     1.11. "Source Code" means the preferred form of the Covered Code for
+     making modifications to it, including all modules it contains, plus
+     any associated interface definition files, scripts used to control
+     compilation and installation of an Executable, or source code
+     differential comparisons against either the Original Code or another
+     well known, available Covered Code of the Contributor's choice. The
+     Source Code can be in a compressed or archival form, provided the
+     appropriate decompression or de-archiving software is widely available
+     for no charge.
+
+     1.12. "You" (or "Your")  means an individual or a legal entity
+     exercising rights under, and complying with all of the terms of, this
+     License or a future version of this License issued under Section 6.1.
+     For legal entities, "You" includes any entity which controls, is
+     controlled by, or is under common control with You. For purposes of
+     this definition, "control" means (a) the power, direct or indirect,
+     to cause the direction or management of such entity, whether by
+     contract or otherwise, or (b) ownership of more than fifty percent
+     (50%) of the outstanding shares or beneficial ownership of such
+     entity.
+
+  2. Source Code License.
+
+     2.1. The Initial Developer Grant.
+     The Initial Developer hereby grants You a world-wide, royalty-free,
+     non-exclusive license, subject to third party intellectual property
+     claims:
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Initial Developer to use, reproduce,
+          modify, display, perform, sublicense and distribute the Original
+          Code (or portions thereof) with or without Modifications, and/or
+          as part of a Larger Work; and
+
+          (b) under Patents Claims infringed by the making, using or
+          selling of Original Code, to make, have made, use, practice,
+          sell, and offer for sale, and/or otherwise dispose of the
+          Original Code (or portions thereof).
+
+          (c) the licenses granted in this Section 2.1(a) and (b) are
+          effective on the date Initial Developer first distributes
+          Original Code under the terms of this License.
+
+          (d) Notwithstanding Section 2.1(b) above, no patent license is
+          granted: 1) for code that You delete from the Original Code; 2)
+          separate from the Original Code;  or 3) for infringements caused
+          by: i) the modification of the Original Code or ii) the
+          combination of the Original Code with other software or devices.
+
+     2.2. Contributor Grant.
+     Subject to third party intellectual property claims, each Contributor
+     hereby grants You a world-wide, royalty-free, non-exclusive license
+
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Contributor, to use, reproduce, modify,
+          display, perform, sublicense and distribute the Modifications
+          created by such Contributor (or portions thereof) either on an
+          unmodified basis, with other Modifications, as Covered Code
+          and/or as part of a Larger Work; and
+
+          (b) under Patent Claims infringed by the making, using, or
+          selling of  Modifications made by that Contributor either alone
+          and/or in combination with its Contributor Version (or portions
+          of such combination), to make, use, sell, offer for sale, have
+          made, and/or otherwise dispose of: 1) Modifications made by that
+          Contributor (or portions thereof); and 2) the combination of
+          Modifications made by that Contributor with its Contributor
+          Version (or portions of such combination).
+
+          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
+          effective on the date Contributor first makes Commercial Use of
+          the Covered Code.
+
+          (d)    Notwithstanding Section 2.2(b) above, no patent license is
+          granted: 1) for any code that Contributor has deleted from the
+          Contributor Version; 2)  separate from the Contributor Version;
+          3)  for infringements caused by: i) third party modifications of
+          Contributor Version or ii)  the combination of Modifications made
+          by that Contributor with other software  (except as part of the
+          Contributor Version) or other devices; or 4) under Patent Claims
+          infringed by Covered Code in the absence of Modifications made by
+          that Contributor.
+
+  3. Distribution Obligations.
+
+     3.1. Application of License.
+     The Modifications which You create or to which You contribute are
+     governed by the terms of this License, including without limitation
+     Section 2.2. The Source Code version of Covered Code may be
+     distributed only under the terms of this License or a future version
+     of this License released under Section 6.1, and You must include a
+     copy of this License with every copy of the Source Code You
+     distribute. You may not offer or impose any terms on any Source Code
+     version that alters or restricts the applicable version of this
+     License or the recipients' rights hereunder. However, You may include
+     an additional document offering the additional rights described in
+     Section 3.5.
+
+     3.2. Availability of Source Code.
+     Any Modification which You create or to which You contribute must be
+     made available in Source Code form under the terms of this License
+     either on the same media as an Executable version or via an accepted
+     Electronic Distribution Mechanism to anyone to whom you made an
+     Executable version available; and if made available via Electronic
+     Distribution Mechanism, must remain available for at least twelve (12)
+     months after the date it initially became available, or at least six
+     (6) months after a subsequent version of that particular Modification
+     has been made available to such recipients. You are responsible for
+     ensuring that the Source Code version remains available even if the
+     Electronic Distribution Mechanism is maintained by a third party.
+
+     3.3. Description of Modifications.
+     You must cause all Covered Code to which You contribute to contain a
+     file documenting the changes You made to create that Covered Code and
+     the date of any change. You must include a prominent statement that
+     the Modification is derived, directly or indirectly, from Original
+     Code provided by the Initial Developer and including the name of the
+     Initial Developer in (a) the Source Code, and (b) in any notice in an
+     Executable version or related documentation in which You describe the
+     origin or ownership of the Covered Code.
+
+     3.4. Intellectual Property Matters
+          (a) Third Party Claims.
+          If Contributor has knowledge that a license under a third party's
+          intellectual property rights is required to exercise the rights
+          granted by such Contributor under Sections 2.1 or 2.2,
+          Contributor must include a text file with the Source Code
+          distribution titled "LEGAL" which describes the claim and the
+          party making the claim in sufficient detail that a recipient will
+          know whom to contact. If Contributor obtains such knowledge after
+          the Modification is made available as described in Section 3.2,
+          Contributor shall promptly modify the LEGAL file in all copies
+          Contributor makes available thereafter and shall take other steps
+          (such as notifying appropriate mailing lists or newsgroups)
+          reasonably calculated to inform those who received the Covered
+          Code that new knowledge has been obtained.
+
+          (b) Contributor APIs.
+          If Contributor's Modifications include an application programming
+          interface and Contributor has knowledge of patent licenses which
+          are reasonably necessary to implement that API, Contributor must
+          also include this information in the LEGAL file.
+
+               (c)    Representations.
+          Contributor represents that, except as disclosed pursuant to
+          Section 3.4(a) above, Contributor believes that Contributor's
+          Modifications are Contributor's original creation(s) and/or
+          Contributor has sufficient rights to grant the rights conveyed by
+          this License.
+
+     3.5. Required Notices.
+     You must duplicate the notice in Exhibit A in each file of the Source
+     Code.  If it is not possible to put such notice in a particular Source
+     Code file due to its structure, then You must include such notice in a
+     location (such as a relevant directory) where a user would be likely
+     to look for such a notice.  If You created one or more Modification(s)
+     You may add your name as a Contributor to the notice described in
+     Exhibit A.  You must also duplicate this License in any documentation
+     for the Source Code where You describe recipients' rights or ownership
+     rights relating to Covered Code.  You may choose to offer, and to
+     charge a fee for, warranty, support, indemnity or liability
+     obligations to one or more recipients of Covered Code. However, You
+     may do so only on Your own behalf, and not on behalf of the Initial
+     Developer or any Contributor. You must make it absolutely clear than
+     any such warranty, support, indemnity or liability obligation is
+     offered by You alone, and You hereby agree to indemnify the Initial
+     Developer and every Contributor for any liability incurred by the
+     Initial Developer or such Contributor as a result of warranty,
+     support, indemnity or liability terms You offer.
+
+     3.6. Distribution of Executable Versions.
+     You may distribute Covered Code in Executable form only if the
+     requirements of Section 3.1-3.5 have been met for that Covered Code,
+     and if You include a notice stating that the Source Code version of
+     the Covered Code is available under the terms of this License,
+     including a description of how and where You have fulfilled the
+     obligations of Section 3.2. The notice must be conspicuously included
+     in any notice in an Executable version, related documentation or
+     collateral in which You describe recipients' rights relating to the
+     Covered Code. You may distribute the Executable version of Covered
+     Code or ownership rights under a license of Your choice, which may
+     contain terms different from this License, provided that You are in
+     compliance with the terms of this License and that the license for the
+     Executable version does not attempt to limit or alter the recipient's
+     rights in the Source Code version from the rights set forth in this
+     License. If You distribute the Executable version under a different
+     license You must make it absolutely clear that any terms which differ
+     from this License are offered by You alone, not by the Initial
+     Developer or any Contributor. You hereby agree to indemnify the
+     Initial Developer and every Contributor for any liability incurred by
+     the Initial Developer or such Contributor as a result of any such
+     terms You offer.
+
+     3.7. Larger Works.
+     You may create a Larger Work by combining Covered Code with other code
+     not governed by the terms of this License and distribute the Larger
+     Work as a single product. In such a case, You must make sure the
+     requirements of this License are fulfilled for the Covered Code.
+
+  4. Inability to Comply Due to Statute or Regulation.
+
+     If it is impossible for You to comply with any of the terms of this
+     License with respect to some or all of the Covered Code due to
+     statute, judicial order, or regulation then You must: (a) comply with
+     the terms of this License to the maximum extent possible; and (b)
+     describe the limitations and the code they affect. Such description
+     must be included in the LEGAL file described in Section 3.4 and must
+     be included with all distributions of the Source Code. Except to the
+     extent prohibited by statute or regulation, such description must be
+     sufficiently detailed for a recipient of ordinary skill to be able to
+     understand it.
+
+  5. Application of this License.
+
+     This License applies to code to which the Initial Developer has
+     attached the notice in Exhibit A and to related Covered Code.
+
+  6. Versions of the License.
+
+     6.1. New Versions.
+     Netscape Communications Corporation ("Netscape") may publish revised
+     and/or new versions of the License from time to time. Each version
+     will be given a distinguishing version number.
+
+     6.2. Effect of New Versions.
+     Once Covered Code has been published under a particular version of the
+     License, You may always continue to use it under the terms of that
+     version. You may also choose to use such Covered Code under the terms
+     of any subsequent version of the License published by Netscape. No one
+     other than Netscape has the right to modify the terms applicable to
+     Covered Code created under this License.
+
+     6.3. Derivative Works.
+     If You create or use a modified version of this License (which you may
+     only do in order to apply it to code which is not already Covered Code
+     governed by this License), You must (a) rename Your license so that
+     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+     "MPL", "NPL" or any confusingly similar phrase do not appear in your
+     license (except to note that your license differs from this License)
+     and (b) otherwise make it clear that Your version of the license
+     contains terms which differ from the Mozilla Public License and
+     Netscape Public License. (Filling in the name of the Initial
+     Developer, Original Code or Contributor in the notice described in
+     Exhibit A shall not of themselves be deemed to be modifications of
+     this License.)
+
+  7. DISCLAIMER OF WARRANTY.
+
+     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
+     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
+     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
+     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
+     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+  8. TERMINATION.
+
+     8.1.  This License and the rights granted hereunder will terminate
+     automatically if You fail to comply with terms herein and fail to cure
+     such breach within 30 days of becoming aware of the breach. All
+     sublicenses to the Covered Code which are properly granted shall
+     survive any termination of this License. Provisions which, by their
+     nature, must remain in effect beyond the termination of this License
+     shall survive.
+
+     8.2.  If You initiate litigation by asserting a patent infringement
+     claim (excluding declatory judgment actions) against Initial Developer
+     or a Contributor (the Initial Developer or Contributor against whom
+     You file such action is referred to as "Participant")  alleging that:
+
+     (a)  such Participant's Contributor Version directly or indirectly
+     infringes any patent, then any and all rights granted by such
+     Participant to You under Sections 2.1 and/or 2.2 of this License
+     shall, upon 60 days notice from Participant terminate prospectively,
+     unless if within 60 days after receipt of notice You either: (i)
+     agree in writing to pay Participant a mutually agreeable reasonable
+     royalty for Your past and future use of Modifications made by such
+     Participant, or (ii) withdraw Your litigation claim with respect to
+     the Contributor Version against such Participant.  If within 60 days
+     of notice, a reasonable royalty and payment arrangement are not
+     mutually agreed upon in writing by the parties or the litigation claim
+     is not withdrawn, the rights granted by Participant to You under
+     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+     the 60 day notice period specified above.
+
+     (b)  any software, hardware, or device, other than such Participant's
+     Contributor Version, directly or indirectly infringes any patent, then
+     any rights granted to You by such Participant under Sections 2.1(b)
+     and 2.2(b) are revoked effective as of the date You first made, used,
+     sold, distributed, or had made, Modifications made by that
+     Participant.
+
+     8.3.  If You assert a patent infringement claim against Participant
+     alleging that such Participant's Contributor Version directly or
+     indirectly infringes any patent where such claim is resolved (such as
+     by license or settlement) prior to the initiation of patent
+     infringement litigation, then the reasonable value of the licenses
+     granted by such Participant under Sections 2.1 or 2.2 shall be taken
+     into account in determining the amount or value of any payment or
+     license.
+
+     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
+     all end user license agreements (excluding distributors and resellers)
+     which have been validly granted by You or any distributor hereunder
+     prior to termination shall survive termination.
+
+  9. LIMITATION OF LIABILITY.
+
+     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
+     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
+     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
+     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
+     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
+     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
+     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+  10. U.S. GOVERNMENT END USERS.
+
+     The Covered Code is a "commercial item," as that term is defined in
+     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+     software" and "commercial computer software documentation," as such
+     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+     all U.S. Government End Users acquire Covered Code with only those
+     rights set forth herein.
+
+  11. MISCELLANEOUS.
+
+     This License represents the complete agreement concerning subject
+     matter hereof. If any provision of this License is held to be
+     unenforceable, such provision shall be reformed only to the extent
+     necessary to make it enforceable. This License shall be governed by
+     California law provisions (except to the extent applicable law, if
+     any, provides otherwise), excluding its conflict-of-law provisions.
+     With respect to disputes in which at least one party is a citizen of,
+     or an entity chartered or registered to do business in the United
+     States of America, any litigation relating to this License shall be
+     subject to the jurisdiction of the Federal Courts of the Northern
+     District of California, with venue lying in Santa Clara County,
+     California, with the losing party responsible for costs, including
+     without limitation, court costs and reasonable attorneys' fees and
+     expenses. The application of the United Nations Convention on
+     Contracts for the International Sale of Goods is expressly excluded.
+     Any law or regulation which provides that the language of a contract
+     shall be construed against the drafter shall not apply to this
+     License.
+
+  12. RESPONSIBILITY FOR CLAIMS.
+
+     As between Initial Developer and the Contributors, each party is
+     responsible for claims and damages arising, directly or indirectly,
+     out of its utilization of rights under this License and You agree to
+     work with Initial Developer and Contributors to distribute such
+     responsibility on an equitable basis. Nothing herein is intended or
+     shall be deemed to constitute any admission of liability.
+
+  13. MULTIPLE-LICENSED CODE.
+
+     Initial Developer may designate portions of the Covered Code as
+     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
+     Developer permits you to utilize portions of the Covered Code under
+     Your choice of the NPL or the alternative licenses, if any, specified
+     by the Initial Developer in the file described in Exhibit A.
+
+  EXHIBIT A -Mozilla Public License.
+
+   The contents of this file are subject to the Mozilla Public License Version
+   1.1 (the "License"); you may not use this file except in compliance with
+   the License. You may obtain a copy of the License at
+   http://www.mozilla.org/MPL/
+
+   Software distributed under the License is distributed on an "AS IS" basis,
+   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   for the specific language governing rights and limitations under the
+   License.
+
+   The Original Code is the Netscape Portable Runtime (NSPR).
+
+   The Initial Developer of the Original Code is
+   Netscape Communications Corporation.
+   Portions created by the Initial Developer are Copyright (C) 1998-2000
+   the Initial Developer. All Rights Reserved.
+
+   Contributor(s):
+
+   Alternatively, the contents of this file may be used under the terms of
+   either the GNU General Public License Version 2 or later (the "GPL"), or
+   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+   in which case the provisions of the GPL or the LGPL are applicable instead
+   of those above. If you wish to allow use of your version of this file only
+   under the terms of either the GPL or the LGPL, and not to allow others to
+   use your version of this file under the terms of the MPL, indicate your
+   decision by deleting the provisions above and replace them with the notice
+   and other provisions required by the GPL or the LGPL. If you do not delete
+   the provisions above, a recipient may use your version of this file under
+   the terms of any one of the MPL, the GPL or the LGPL.
+
+
+  symbolize
+
+
+  // Copyright (c) 2006, Google Inc.
+  // All rights reserved.
+  //
+  // Redistribution and use in source and binary forms, with or without
+  // modification, are permitted provided that the following conditions are
+  // met:
+  //
+  //     * Redistributions of source code must retain the above copyright
+  // notice, this list of conditions and the following disclaimer.
+  //     * Redistributions in binary form must reproduce the above
+  // copyright notice, this list of conditions and the following disclaimer
+  // in the documentation and/or other materials provided with the
+  // distribution.
+  //     * Neither the name of Google Inc. nor the names of its
+  // contributors may be used to endorse or promote products derived from
+  // this software without specific prior written permission.
+  //
+  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+  valgrind
+
+
+   Notice that the following BSD-style license applies to the Valgrind header
+   files used by Chromium (valgrind.h and memcheck.h). However, the rest of
+   Valgrind is licensed under the terms of the GNU General Public License,
+   version 2, unless otherwise indicated.
+
+   ----------------------------------------------------------------
+
+   Copyright (C) 2000-2008 Julian Seward.  All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+   2. The origin of this software must not be misrepresented; you must
+      not claim that you wrote the original software.  If you use this
+      software in a product, an acknowledgment in the product
+      documentation would be appreciated but is not required.
+
+   3. Altered source versions must be plainly marked as such, and must
+      not be misrepresented as being the original software.
+
+   4. The name of the author may not be used to endorse or promote
+      products derived from this software without specific prior written
+      permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+  uri_template
+
+
+                                   Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+
+  mozilla(url/third_party/mozilla)
+
+
+  Copyright 2007, Google Inc.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+      * Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the following disclaimer
+  in the documentation and/or other materials provided with the
+  distribution.
+      * Neither the name of Google Inc. nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  -------------------------------------------------------------------------------
+
+  The file url_parse.cc is based on nsURLParsers.cc from Mozilla. This file is
+  licensed separately as follows:
+
+  --------------------------------------------------------------------------------
+                          MOZILLA PUBLIC LICENSE
+                                Version 1.1
+
+                              ---------------
+
+  1. Definitions.
+
+     1.0.1. "Commercial Use" means distribution or otherwise making the
+     Covered Code available to a third party.
+
+     1.1. "Contributor" means each entity that creates or contributes to
+     the creation of Modifications.
+
+     1.2. "Contributor Version" means the combination of the Original
+     Code, prior Modifications used by a Contributor, and the Modifications
+     made by that particular Contributor.
+
+     1.3. "Covered Code" means the Original Code or Modifications or the
+     combination of the Original Code and Modifications, in each case
+     including portions thereof.
+
+     1.4. "Electronic Distribution Mechanism" means a mechanism generally
+     accepted in the software development community for the electronic
+     transfer of data.
+
+     1.5. "Executable" means Covered Code in any form other than Source
+     Code.
+
+     1.6. "Initial Developer" means the individual or entity identified
+     as the Initial Developer in the Source Code notice required by Exhibit
+     A.
+
+     1.7. "Larger Work" means a work which combines Covered Code or
+     portions thereof with code not governed by the terms of this License.
+
+     1.8. "License" means this document.
+
+     1.8.1. "Licensable" means having the right to grant, to the maximum
+     extent possible, whether at the time of the initial grant or
+     subsequently acquired, any and all of the rights conveyed herein.
+
+     1.9. "Modifications" means any addition to or deletion from the
+     substance or structure of either the Original Code or any previous
+     Modifications. When Covered Code is released as a series of files, a
+     Modification is:
+          A. Any addition to or deletion from the contents of a file
+          containing Original Code or previous Modifications.
+
+          B. Any new file that contains any part of the Original Code or
+          previous Modifications.
+
+     1.10. "Original Code" means Source Code of computer software code
+     which is described in the Source Code notice required by Exhibit A as
+     Original Code, and which, at the time of its release under this
+     License is not already Covered Code governed by this License.
+
+     1.10.1. "Patent Claims" means any patent claim(s), now owned or
+     hereafter acquired, including without limitation,  method, process,
+     and apparatus claims, in any patent Licensable by grantor.
+
+     1.11. "Source Code" means the preferred form of the Covered Code for
+     making modifications to it, including all modules it contains, plus
+     any associated interface definition files, scripts used to control
+     compilation and installation of an Executable, or source code
+     differential comparisons against either the Original Code or another
+     well known, available Covered Code of the Contributor's choice. The
+     Source Code can be in a compressed or archival form, provided the
+     appropriate decompression or de-archiving software is widely available
+     for no charge.
+
+     1.12. "You" (or "Your")  means an individual or a legal entity
+     exercising rights under, and complying with all of the terms of, this
+     License or a future version of this License issued under Section 6.1.
+     For legal entities, "You" includes any entity which controls, is
+     controlled by, or is under common control with You. For purposes of
+     this definition, "control" means (a) the power, direct or indirect,
+     to cause the direction or management of such entity, whether by
+     contract or otherwise, or (b) ownership of more than fifty percent
+     (50%) of the outstanding shares or beneficial ownership of such
+     entity.
+
+  2. Source Code License.
+
+     2.1. The Initial Developer Grant.
+     The Initial Developer hereby grants You a world-wide, royalty-free,
+     non-exclusive license, subject to third party intellectual property
+     claims:
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Initial Developer to use, reproduce,
+          modify, display, perform, sublicense and distribute the Original
+          Code (or portions thereof) with or without Modifications, and/or
+          as part of a Larger Work; and
+
+          (b) under Patents Claims infringed by the making, using or
+          selling of Original Code, to make, have made, use, practice,
+          sell, and offer for sale, and/or otherwise dispose of the
+          Original Code (or portions thereof).
+
+          (c) the licenses granted in this Section 2.1(a) and (b) are
+          effective on the date Initial Developer first distributes
+          Original Code under the terms of this License.
+
+          (d) Notwithstanding Section 2.1(b) above, no patent license is
+          granted: 1) for code that You delete from the Original Code; 2)
+          separate from the Original Code;  or 3) for infringements caused
+          by: i) the modification of the Original Code or ii) the
+          combination of the Original Code with other software or devices.
+
+     2.2. Contributor Grant.
+     Subject to third party intellectual property claims, each Contributor
+     hereby grants You a world-wide, royalty-free, non-exclusive license
+
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Contributor, to use, reproduce, modify,
+          display, perform, sublicense and distribute the Modifications
+          created by such Contributor (or portions thereof) either on an
+          unmodified basis, with other Modifications, as Covered Code
+          and/or as part of a Larger Work; and
+
+          (b) under Patent Claims infringed by the making, using, or
+          selling of  Modifications made by that Contributor either alone
+          and/or in combination with its Contributor Version (or portions
+          of such combination), to make, use, sell, offer for sale, have
+          made, and/or otherwise dispose of: 1) Modifications made by that
+          Contributor (or portions thereof); and 2) the combination of
+          Modifications made by that Contributor with its Contributor
+          Version (or portions of such combination).
+
+          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
+          effective on the date Contributor first makes Commercial Use of
+          the Covered Code.
+
+          (d)    Notwithstanding Section 2.2(b) above, no patent license is
+          granted: 1) for any code that Contributor has deleted from the
+          Contributor Version; 2)  separate from the Contributor Version;
+          3)  for infringements caused by: i) third party modifications of
+          Contributor Version or ii)  the combination of Modifications made
+          by that Contributor with other software  (except as part of the
+          Contributor Version) or other devices; or 4) under Patent Claims
+          infringed by Covered Code in the absence of Modifications made by
+          that Contributor.
+
+  3. Distribution Obligations.
+
+     3.1. Application of License.
+     The Modifications which You create or to which You contribute are
+     governed by the terms of this License, including without limitation
+     Section 2.2. The Source Code version of Covered Code may be
+     distributed only under the terms of this License or a future version
+     of this License released under Section 6.1, and You must include a
+     copy of this License with every copy of the Source Code You
+     distribute. You may not offer or impose any terms on any Source Code
+     version that alters or restricts the applicable version of this
+     License or the recipients' rights hereunder. However, You may include
+     an additional document offering the additional rights described in
+     Section 3.5.
+
+     3.2. Availability of Source Code.
+     Any Modification which You create or to which You contribute must be
+     made available in Source Code form under the terms of this License
+     either on the same media as an Executable version or via an accepted
+     Electronic Distribution Mechanism to anyone to whom you made an
+     Executable version available; and if made available via Electronic
+     Distribution Mechanism, must remain available for at least twelve (12)
+     months after the date it initially became available, or at least six
+     (6) months after a subsequent version of that particular Modification
+     has been made available to such recipients. You are responsible for
+     ensuring that the Source Code version remains available even if the
+     Electronic Distribution Mechanism is maintained by a third party.
+
+     3.3. Description of Modifications.
+     You must cause all Covered Code to which You contribute to contain a
+     file documenting the changes You made to create that Covered Code and
+     the date of any change. You must include a prominent statement that
+     the Modification is derived, directly or indirectly, from Original
+     Code provided by the Initial Developer and including the name of the
+     Initial Developer in (a) the Source Code, and (b) in any notice in an
+     Executable version or related documentation in which You describe the
+     origin or ownership of the Covered Code.
+
+     3.4. Intellectual Property Matters
+          (a) Third Party Claims.
+          If Contributor has knowledge that a license under a third party's
+          intellectual property rights is required to exercise the rights
+          granted by such Contributor under Sections 2.1 or 2.2,
+          Contributor must include a text file with the Source Code
+          distribution titled "LEGAL" which describes the claim and the
+          party making the claim in sufficient detail that a recipient will
+          know whom to contact. If Contributor obtains such knowledge after
+          the Modification is made available as described in Section 3.2,
+          Contributor shall promptly modify the LEGAL file in all copies
+          Contributor makes available thereafter and shall take other steps
+          (such as notifying appropriate mailing lists or newsgroups)
+          reasonably calculated to inform those who received the Covered
+          Code that new knowledge has been obtained.
+
+          (b) Contributor APIs.
+          If Contributor's Modifications include an application programming
+          interface and Contributor has knowledge of patent licenses which
+          are reasonably necessary to implement that API, Contributor must
+          also include this information in the LEGAL file.
+
+               (c)    Representations.
+          Contributor represents that, except as disclosed pursuant to
+          Section 3.4(a) above, Contributor believes that Contributor's
+          Modifications are Contributor's original creation(s) and/or
+          Contributor has sufficient rights to grant the rights conveyed by
+          this License.
+
+     3.5. Required Notices.
+     You must duplicate the notice in Exhibit A in each file of the Source
+     Code.  If it is not possible to put such notice in a particular Source
+     Code file due to its structure, then You must include such notice in a
+     location (such as a relevant directory) where a user would be likely
+     to look for such a notice.  If You created one or more Modification(s)
+     You may add your name as a Contributor to the notice described in
+     Exhibit A.  You must also duplicate this License in any documentation
+     for the Source Code where You describe recipients' rights or ownership
+     rights relating to Covered Code.  You may choose to offer, and to
+     charge a fee for, warranty, support, indemnity or liability
+     obligations to one or more recipients of Covered Code. However, You
+     may do so only on Your own behalf, and not on behalf of the Initial
+     Developer or any Contributor. You must make it absolutely clear than
+     any such warranty, support, indemnity or liability obligation is
+     offered by You alone, and You hereby agree to indemnify the Initial
+     Developer and every Contributor for any liability incurred by the
+     Initial Developer or such Contributor as a result of warranty,
+     support, indemnity or liability terms You offer.
+
+     3.6. Distribution of Executable Versions.
+     You may distribute Covered Code in Executable form only if the
+     requirements of Section 3.1-3.5 have been met for that Covered Code,
+     and if You include a notice stating that the Source Code version of
+     the Covered Code is available under the terms of this License,
+     including a description of how and where You have fulfilled the
+     obligations of Section 3.2. The notice must be conspicuously included
+     in any notice in an Executable version, related documentation or
+     collateral in which You describe recipients' rights relating to the
+     Covered Code. You may distribute the Executable version of Covered
+     Code or ownership rights under a license of Your choice, which may
+     contain terms different from this License, provided that You are in
+     compliance with the terms of this License and that the license for the
+     Executable version does not attempt to limit or alter the recipient's
+     rights in the Source Code version from the rights set forth in this
+     License. If You distribute the Executable version under a different
+     license You must make it absolutely clear that any terms which differ
+     from this License are offered by You alone, not by the Initial
+     Developer or any Contributor. You hereby agree to indemnify the
+     Initial Developer and every Contributor for any liability incurred by
+     the Initial Developer or such Contributor as a result of any such
+     terms You offer.
+
+     3.7. Larger Works.
+     You may create a Larger Work by combining Covered Code with other code
+     not governed by the terms of this License and distribute the Larger
+     Work as a single product. In such a case, You must make sure the
+     requirements of this License are fulfilled for the Covered Code.
+
+  4. Inability to Comply Due to Statute or Regulation.
+
+     If it is impossible for You to comply with any of the terms of this
+     License with respect to some or all of the Covered Code due to
+     statute, judicial order, or regulation then You must: (a) comply with
+     the terms of this License to the maximum extent possible; and (b)
+     describe the limitations and the code they affect. Such description
+     must be included in the LEGAL file described in Section 3.4 and must
+     be included with all distributions of the Source Code. Except to the
+     extent prohibited by statute or regulation, such description must be
+     sufficiently detailed for a recipient of ordinary skill to be able to
+     understand it.
+
+  5. Application of this License.
+
+     This License applies to code to which the Initial Developer has
+     attached the notice in Exhibit A and to related Covered Code.
+
+  6. Versions of the License.
+
+     6.1. New Versions.
+     Netscape Communications Corporation ("Netscape") may publish revised
+     and/or new versions of the License from time to time. Each version
+     will be given a distinguishing version number.
+
+     6.2. Effect of New Versions.
+     Once Covered Code has been published under a particular version of the
+     License, You may always continue to use it under the terms of that
+     version. You may also choose to use such Covered Code under the terms
+     of any subsequent version of the License published by Netscape. No one
+     other than Netscape has the right to modify the terms applicable to
+     Covered Code created under this License.
+
+     6.3. Derivative Works.
+     If You create or use a modified version of this License (which you may
+     only do in order to apply it to code which is not already Covered Code
+     governed by this License), You must (a) rename Your license so that
+     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+     "MPL", "NPL" or any confusingly similar phrase do not appear in your
+     license (except to note that your license differs from this License)
+     and (b) otherwise make it clear that Your version of the license
+     contains terms which differ from the Mozilla Public License and
+     Netscape Public License. (Filling in the name of the Initial
+     Developer, Original Code or Contributor in the notice described in
+     Exhibit A shall not of themselves be deemed to be modifications of
+     this License.)
+
+  7. DISCLAIMER OF WARRANTY.
+
+     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
+     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
+     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
+     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
+     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+  8. TERMINATION.
+
+     8.1.  This License and the rights granted hereunder will terminate
+     automatically if You fail to comply with terms herein and fail to cure
+     such breach within 30 days of becoming aware of the breach. All
+     sublicenses to the Covered Code which are properly granted shall
+     survive any termination of this License. Provisions which, by their
+     nature, must remain in effect beyond the termination of this License
+     shall survive.
+
+     8.2.  If You initiate litigation by asserting a patent infringement
+     claim (excluding declatory judgment actions) against Initial Developer
+     or a Contributor (the Initial Developer or Contributor against whom
+     You file such action is referred to as "Participant")  alleging that:
+
+     (a)  such Participant's Contributor Version directly or indirectly
+     infringes any patent, then any and all rights granted by such
+     Participant to You under Sections 2.1 and/or 2.2 of this License
+     shall, upon 60 days notice from Participant terminate prospectively,
+     unless if within 60 days after receipt of notice You either: (i)
+     agree in writing to pay Participant a mutually agreeable reasonable
+     royalty for Your past and future use of Modifications made by such
+     Participant, or (ii) withdraw Your litigation claim with respect to
+     the Contributor Version against such Participant.  If within 60 days
+     of notice, a reasonable royalty and payment arrangement are not
+     mutually agreed upon in writing by the parties or the litigation claim
+     is not withdrawn, the rights granted by Participant to You under
+     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+     the 60 day notice period specified above.
+
+     (b)  any software, hardware, or device, other than such Participant's
+     Contributor Version, directly or indirectly infringes any patent, then
+     any rights granted to You by such Participant under Sections 2.1(b)
+     and 2.2(b) are revoked effective as of the date You first made, used,
+     sold, distributed, or had made, Modifications made by that
+     Participant.
+
+     8.3.  If You assert a patent infringement claim against Participant
+     alleging that such Participant's Contributor Version directly or
+     indirectly infringes any patent where such claim is resolved (such as
+     by license or settlement) prior to the initiation of patent
+     infringement litigation, then the reasonable value of the licenses
+     granted by such Participant under Sections 2.1 or 2.2 shall be taken
+     into account in determining the amount or value of any payment or
+     license.
+
+     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
+     all end user license agreements (excluding distributors and resellers)
+     which have been validly granted by You or any distributor hereunder
+     prior to termination shall survive termination.
+
+  9. LIMITATION OF LIABILITY.
+
+     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
+     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
+     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
+     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
+     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
+     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
+     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+  10. U.S. GOVERNMENT END USERS.
+
+     The Covered Code is a "commercial item," as that term is defined in
+     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+     software" and "commercial computer software documentation," as such
+     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+     all U.S. Government End Users acquire Covered Code with only those
+     rights set forth herein.
+
+  11. MISCELLANEOUS.
+
+     This License represents the complete agreement concerning subject
+     matter hereof. If any provision of this License is held to be
+     unenforceable, such provision shall be reformed only to the extent
+     necessary to make it enforceable. This License shall be governed by
+     California law provisions (except to the extent applicable law, if
+     any, provides otherwise), excluding its conflict-of-law provisions.
+     With respect to disputes in which at least one party is a citizen of,
+     or an entity chartered or registered to do business in the United
+     States of America, any litigation relating to this License shall be
+     subject to the jurisdiction of the Federal Courts of the Northern
+     District of California, with venue lying in Santa Clara County,
+     California, with the losing party responsible for costs, including
+     without limitation, court costs and reasonable attorneys' fees and
+     expenses. The application of the United Nations Convention on
+     Contracts for the International Sale of Goods is expressly excluded.
+     Any law or regulation which provides that the language of a contract
+     shall be construed against the drafter shall not apply to this
+     License.
+
+  12. RESPONSIBILITY FOR CLAIMS.
+
+     As between Initial Developer and the Contributors, each party is
+     responsible for claims and damages arising, directly or indirectly,
+     out of its utilization of rights under this License and You agree to
+     work with Initial Developer and Contributors to distribute such
+     responsibility on an equitable basis. Nothing herein is intended or
+     shall be deemed to constitute any admission of liability.
+
+  13. MULTIPLE-LICENSED CODE.
+
+     Initial Developer may designate portions of the Covered Code as
+     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
+     Developer permits you to utilize portions of the Covered Code under
+     Your choice of the NPL or the alternative licenses, if any, specified
+     by the Initial Developer in the file described in Exhibit A.
+
+  EXHIBIT A -Mozilla Public License.
+
+  The contents of this file are subject to the Mozilla Public License Version
+  1.1 (the "License"); you may not use this file except in compliance with
+  the License. You may obtain a copy of the License at
+  http://www.mozilla.org/MPL/
+
+  Software distributed under the License is distributed on an "AS IS" basis,
+  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  for the specific language governing rights and limitations under the
+  License.
+
+  The Original Code is mozilla.org code.
+
+  The Initial Developer of the Original Code is
+  Netscape Communications Corporation.
+  Portions created by the Initial Developer are Copyright (C) 1998
+  the Initial Developer. All Rights Reserved.
+
+  Contributor(s):
+    Darin Fisher (original author)
+
+  Alternatively, the contents of this file may be used under the terms of
+  either the GNU General Public License Version 2 or later (the "GPL"), or
+  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+  in which case the provisions of the GPL or the LGPL are applicable instead
+  of those above. If you wish to allow use of your version of this file only
+  under the terms of either the GPL or the LGPL, and not to allow others to
+  use your version of this file under the terms of the MPL, indicate your
+  decision by deleting the provisions above and replace them with the notice
+  and other provisions required by the GPL or the LGPL. If you do not delete
+  the provisions above, a recipient may use your version of this file under
+  the terms of any one of the MPL, the GPL or the LGPL.
+
+
+  nist-pkits
+
+  Name: NIST Public Key Interoperability Test Suite
+  Short Name: NIST PKITS
+  URL: http://csrc.nist.gov/groups/ST/crypto_apps_infra/pki/pkitesting.html
+  Version: 1.0.1
+  Date: April 14, 2011
+  License: Public Domain: United States Government Work under 17 U.S.C. 105
+  License File: NOT_SHIPPED
+
+  Description:
+  The Public Key Interoperability Test Suite (PKITS) is a comprehensive X.509
+  path validation test suite that was developed by NIST in conjunction with BAE
+  Systems and NSA.  The PKITS path validation test suite is designed to cover
+  most of the features specified in X.509 and RFC 3280.
+
+  Local Modifications:
+  Only the certs/ and crls/ directories were extracted from PKITS_data.zip.
+
+  pkits_testcases-inl.h is generated from the test descriptions in PKITS.pdf
+  using generate_tests.py.
+
+
+
+  FreeType
+
+
+                      The FreeType Project LICENSE
+                      ----------------------------
+
+                              2006-Jan-27
+
+                      Copyright 1996-2002, 2006 by
+            David Turner, Robert Wilhelm, and Werner Lemberg
+
+
+
+  Introduction
+  ============
+
+    The FreeType  Project is distributed in  several archive packages;
+    some of them may contain, in addition to the FreeType font engine,
+    various tools and  contributions which rely on, or  relate to, the
+    FreeType Project.
+
+    This  license applies  to all  files found  in such  packages, and
+    which do not  fall under their own explicit  license.  The license
+    affects  thus  the  FreeType   font  engine,  the  test  programs,
+    documentation and makefiles, at the very least.
+
+    This  license   was  inspired  by  the  BSD,   Artistic,  and  IJG
+    (Independent JPEG  Group) licenses, which  all encourage inclusion
+    and  use of  free  software in  commercial  and freeware  products
+    alike.  As a consequence, its main points are that:
+
+      o We don't promise that this software works. However, we will be
+        interested in any kind of bug reports. (`as is' distribution)
+
+      o You can  use this software for whatever you  want, in parts or
+        full form, without having to pay us. (`royalty-free' usage)
+
+      o You may not pretend that  you wrote this software.  If you use
+        it, or  only parts of it,  in a program,  you must acknowledge
+        somewhere  in  your  documentation  that  you  have  used  the
+        FreeType code. (`credits')
+
+    We  specifically  permit  and  encourage  the  inclusion  of  this
+    software, with  or without modifications,  in commercial products.
+    We  disclaim  all warranties  covering  The  FreeType Project  and
+    assume no liability related to The FreeType Project.
+
+
+    Finally,  many  people  asked  us  for  a  preferred  form  for  a
+    credit/disclaimer to use in compliance with this license.  We thus
+    encourage you to use the following text:
+
+     """
+      Portions of this software are copyright © <year> The FreeType
+      Project (www.freetype.org).  All rights reserved.
+     """
+
+    Please replace <year> with the value from the FreeType version you
+    actually use.
+
+
+  Legal Terms
+  ===========
+
+  0. Definitions
+  --------------
+
+    Throughout this license,  the terms `package', `FreeType Project',
+    and  `FreeType  archive' refer  to  the  set  of files  originally
+    distributed  by the  authors  (David Turner,  Robert Wilhelm,  and
+    Werner Lemberg) as the `FreeType Project', be they named as alpha,
+    beta or final release.
+
+    `You' refers to  the licensee, or person using  the project, where
+    `using' is a generic term including compiling the project's source
+    code as  well as linking it  to form a  `program' or `executable'.
+    This  program is  referred to  as  `a program  using the  FreeType
+    engine'.
+
+    This  license applies  to all  files distributed  in  the original
+    FreeType  Project,   including  all  source   code,  binaries  and
+    documentation,  unless  otherwise  stated   in  the  file  in  its
+    original, unmodified form as  distributed in the original archive.
+    If you are  unsure whether or not a particular  file is covered by
+    this license, you must contact us to verify this.
+
+    The FreeType  Project is copyright (C) 1996-2000  by David Turner,
+    Robert Wilhelm, and Werner Lemberg.  All rights reserved except as
+    specified below.
+
+  1. No Warranty
+  --------------
+
+    THE FREETYPE PROJECT  IS PROVIDED `AS IS' WITHOUT  WARRANTY OF ANY
+    KIND, EITHER  EXPRESS OR IMPLIED,  INCLUDING, BUT NOT  LIMITED TO,
+    WARRANTIES  OF  MERCHANTABILITY   AND  FITNESS  FOR  A  PARTICULAR
+    PURPOSE.  IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE  FOR ANY DAMAGES CAUSED  BY THE USE OR  THE INABILITY TO
+    USE, OF THE FREETYPE PROJECT.
+
+  2. Redistribution
+  -----------------
+
+    This  license  grants  a  worldwide, royalty-free,  perpetual  and
+    irrevocable right  and license to use,  execute, perform, compile,
+    display,  copy,   create  derivative  works   of,  distribute  and
+    sublicense the  FreeType Project (in  both source and  object code
+    forms)  and  derivative works  thereof  for  any  purpose; and  to
+    authorize others  to exercise  some or all  of the  rights granted
+    herein, subject to the following conditions:
+
+      o Redistribution of  source code  must retain this  license file
+        (`FTL.TXT') unaltered; any  additions, deletions or changes to
+        the original  files must be clearly  indicated in accompanying
+        documentation.   The  copyright   notices  of  the  unaltered,
+        original  files must  be  preserved in  all  copies of  source
+        files.
+
+      o Redistribution in binary form must provide a  disclaimer  that
+        states  that  the software is based in part of the work of the
+        FreeType Team,  in  the  distribution  documentation.  We also
+        encourage you to put an URL to the FreeType web page  in  your
+        documentation, though this isn't mandatory.
+
+    These conditions  apply to any  software derived from or  based on
+    the FreeType Project,  not just the unmodified files.   If you use
+    our work, you  must acknowledge us.  However, no  fee need be paid
+    to us.
+
+  3. Advertising
+  --------------
+
+    Neither the  FreeType authors and  contributors nor you  shall use
+    the name of the  other for commercial, advertising, or promotional
+    purposes without specific prior written permission.
+
+    We suggest,  but do not require, that  you use one or  more of the
+    following phrases to refer  to this software in your documentation
+    or advertising  materials: `FreeType Project',  `FreeType Engine',
+    `FreeType library', or `FreeType Distribution'.
+
+    As  you have  not signed  this license,  you are  not  required to
+    accept  it.   However,  as  the FreeType  Project  is  copyrighted
+    material, only  this license, or  another one contracted  with the
+    authors, grants you  the right to use, distribute,  and modify it.
+    Therefore,  by  using,  distributing,  or modifying  the  FreeType
+    Project, you indicate that you understand and accept all the terms
+    of this license.
+
+  4. Contacts
+  -----------
+
+    There are two mailing lists related to FreeType:
+
+      o freetype@nongnu.org
+
+        Discusses general use and applications of FreeType, as well as
+        future and  wanted additions to the  library and distribution.
+        If  you are looking  for support,  start in  this list  if you
+        haven't found anything to help you in the documentation.
+
+      o freetype-devel@nongnu.org
+
+        Discusses bugs,  as well  as engine internals,  design issues,
+        specific licenses, porting, etc.
+
+    Our home page can be found at
+
+      http://www.freetype.org
+
+
+  --- end of FTL.TXT ---
+
+
+
+  harfbuzz-ng
+
+  HarfBuzz is licensed under the so-called "Old MIT" license.  Details follow.
+  For parts of HarfBuzz that are licensed under different licenses see individual
+  files names COPYING in subdirectories where applicable.
+
+  Copyright © 2010,2011,2012  Google, Inc.
+  Copyright © 2012  Mozilla Foundation
+  Copyright © 2011  Codethink Limited
+  Copyright © 2008,2010  Nokia Corporation and/or its subsidiary(-ies)
+  Copyright © 2009  Keith Stribley
+  Copyright © 2009  Martin Hosken and SIL International
+  Copyright © 2007  Chris Wilson
+  Copyright © 2006  Behdad Esfahbod
+  Copyright © 2005  David Turner
+  Copyright © 2004,2007,2008,2009,2010  Red Hat, Inc.
+  Copyright © 1998-2004  David Turner and Werner Lemberg
+
+  For full copyright notices consult the individual files in the package.
+
+
+  Permission is hereby granted, without written agreement and without
+  license or royalty fees, to use, copy, modify, and distribute this
+  software and its documentation for any purpose, provided that the
+  above copyright notice and the following two paragraphs appear in
+  all copies of this software.
+
+  IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+  DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+  ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+  IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.
+
+  THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+  BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+  FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+  ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+  PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+
+
+  icu
+
+
+  ICU License - ICU 1.8.1 and later
+
+  COPYRIGHT AND PERMISSION NOTICE
+
+  Copyright (c) 1995-2010 International Business Machines Corporation and others
+
+  All rights reserved.
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"),
+  to deal in the Software without restriction, including without limitation
+  the rights to use, copy, modify, merge, publish, distribute, and/or sell
+  copies of the Software, and to permit persons
+  to whom the Software is furnished to do so, provided that the above
+  copyright notice(s) and this permission notice appear in all copies
+  of the Software and that both the above copyright notice(s) and this
+  permission notice appear in supporting documentation.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT
+  SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY
+  CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+  WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+  Except as contained in this notice, the name of a copyright holder shall not be
+  used in advertising or otherwise to promote the sale, use or other dealings in
+  this Software without prior written authorization of the copyright holder.
+
+  All trademarks and registered trademarks mentioned herein are the property of
+  their respective owners.
+
+
+
+  libevent
+
+  Copyright 2000-2007 Niels Provos <provos@citi.umich.edu>
+  Copyright 2007-2009 Niels Provos and Nick Mathewson
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. The name of the author may not be used to endorse or promote products
+     derived from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  libjpeg-turbo
+
+  libjpeg-turbo Licenses libjpeg-turbo is covered by three compatible BSD-style
+  open source licenses:
+
+  The IJG (Independent JPEG Group) License, which is listed in README.ijg
+
+  This license applies to the libjpeg API library and associated programs (any
+  code inherited from libjpeg, and any modifications to that code.)
+
+  The Modified (3-clause) BSD License, which is listed below
+
+  This license covers the TurboJPEG API library and associated programs, as well
+  as the build system.
+
+  The zlib License
+
+  This license is a subset of the other two, and it covers the libjpeg-turbo
+  SIMD extensions.
+
+  Complying with the libjpeg-turbo Licenses This section provides a roll-up of
+  the libjpeg-turbo licensing terms, to the best of our understanding.
+
+  If you are distributing a modified version of the libjpeg-turbo source, then:
+
+  You cannot alter or remove any existing copyright or license notices from the
+  source.
+
+  Origin
+
+  Clause 1 of the IJG License Clause 1 of the Modified BSD License Clauses 1 and
+  3 of the zlib License You must add your own copyright notice to the header of
+  each source file you modified, so others can tell that you modified that file
+  (if there is not an existing copyright header in that file, then you can
+  simply add a notice stating that you modified the file.)
+
+  Origin
+
+  Clause 1 of the IJG License Clause 2 of the zlib License You must include the
+  IJG README file, and you must not alter any of the copyright or license text
+  in that file.
+
+  Origin
+
+  Clause 1 of the IJG License If you are distributing only libjpeg-turbo
+  binaries without the source, or if you are distributing an application that
+  statically links with libjpeg-turbo, then:
+
+  Your product documentation must include a message stating:
+
+  This software is based in part on the work of the Independent JPEG Group.
+
+  Origin
+
+  Clause 2 of the IJG license If your binary distribution includes or uses the
+  TurboJPEG API, then your product documentation must include the text of the
+  Modified BSD License (see below.)
+
+  Origin
+
+  Clause 2 of the Modified BSD License You cannot use the name of the IJG or The
+  libjpeg-turbo Project or the contributors thereof in advertising, publicity,
+  etc.
+
+  Origin
+
+  IJG License Clause 3 of the Modified BSD License The IJG and The libjpeg-turbo
+  Project do not warrant libjpeg-turbo to be free of defects, nor do we accept
+  any liability for undesirable consequences resulting from your use of the
+  software.
+
+  Origin
+
+  IJG License Modified BSD License zlib License The Modified (3-clause) BSD
+  License Copyright (C)2009-2021 D. R. Commander. All Rights Reserved.
+  Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.  Redistributions in binary
+  form must reproduce the above copyright notice, this list of conditions and
+  the following disclaimer in the documentation and/or other materials provided
+  with the distribution.  Neither the name of the libjpeg-turbo Project nor the
+  names of its contributors may be used to endorse or promote products derived
+  from this software without specific prior written permission.  THIS SOFTWARE
+  IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+  EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Why Three Licenses?  The zlib License could have been used instead of the
+  Modified (3-clause) BSD License, and since the IJG License effectively
+  subsumes the distribution conditions of the zlib License, this would have
+  effectively placed libjpeg-turbo binary distributions under the IJG License.
+  However, the IJG License specifically refers to the Independent JPEG Group and
+  does not extend attribution and endorsement protections to other entities.
+  Thus, it was desirable to choose a license that granted us the same
+  protections for new code that were granted to the IJG for code derived from
+  their software.
+
+
+  libpng
+
+
+  This copy of the libpng notices is provided for your convenience.  In case of
+  any discrepancy between this copy and the notices in the file png.h that is
+  included in the libpng distribution, the latter shall prevail.
+
+  COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
+
+  If you modify libpng you may insert additional notices immediately following
+  this sentence.
+
+  This code is released under the libpng license.
+
+  libpng versions 1.2.6, August 15, 2004, through 1.2.45, July 7, 2011, are
+  Copyright (c) 2004, 2006-2009 Glenn Randers-Pehrson, and are
+  distributed according to the same disclaimer and license as libpng-1.2.5
+  with the following individual added to the list of Contributing Authors
+
+     Cosmin Truta
+
+  libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are
+  Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are
+  distributed according to the same disclaimer and license as libpng-1.0.6
+  with the following individuals added to the list of Contributing Authors
+
+     Simon-Pierre Cadieux
+     Eric S. Raymond
+     Gilles Vollant
+
+  and with the following additions to the disclaimer:
+
+     There is no warranty against interference with your enjoyment of the
+     library or against infringement.  There is no warranty that our
+     efforts or the library will fulfill any of your particular purposes
+     or needs.  This library is provided with all faults, and the entire
+     risk of satisfactory quality, performance, accuracy, and effort is with
+     the user.
+
+  libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
+  Copyright (c) 1998, 1999 Glenn Randers-Pehrson, and are
+  distributed according to the same disclaimer and license as libpng-0.96,
+  with the following individuals added to the list of Contributing Authors:
+
+     Tom Lane
+     Glenn Randers-Pehrson
+     Willem van Schaik
+
+  libpng versions 0.89, June 1996, through 0.96, May 1997, are
+  Copyright (c) 1996, 1997 Andreas Dilger
+  Distributed according to the same disclaimer and license as libpng-0.88,
+  with the following individuals added to the list of Contributing Authors:
+
+     John Bowler
+     Kevin Bracey
+     Sam Bushell
+     Magnus Holmgren
+     Greg Roelofs
+     Tom Tanner
+
+  libpng versions 0.5, May 1995, through 0.88, January 1996, are
+  Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+
+  For the purposes of this copyright and license, "Contributing Authors"
+  is defined as the following set of individuals:
+
+     Andreas Dilger
+     Dave Martindale
+     Guy Eric Schalnat
+     Paul Schmidt
+     Tim Wegner
+
+  The PNG Reference Library is supplied "AS IS".  The Contributing Authors
+  and Group 42, Inc. disclaim all warranties, expressed or implied,
+  including, without limitation, the warranties of merchantability and of
+  fitness for any purpose.  The Contributing Authors and Group 42, Inc.
+  assume no liability for direct, indirect, incidental, special, exemplary,
+  or consequential damages, which may result from the use of the PNG
+  Reference Library, even if advised of the possibility of such damage.
+
+  Permission is hereby granted to use, copy, modify, and distribute this
+  source code, or portions hereof, for any purpose, without fee, subject
+  to the following restrictions:
+
+  1. The origin of this source code must not be misrepresented.
+
+  2. Altered versions must be plainly marked as such and must not
+     be misrepresented as being the original source.
+
+  3. This Copyright notice may not be removed or altered from any
+     source or altered source distribution.
+
+  The Contributing Authors and Group 42, Inc. specifically permit, without
+  fee, and encourage the use of this source code as a component to
+  supporting the PNG file format in commercial products.  If you use this
+  source code in a product, acknowledgment is not required but would be
+  appreciated.
+
+
+  A "png_get_copyright" function is available, for convenient use in "about"
+  boxes and the like:
+
+     printf("%s",png_get_copyright(NULL));
+
+  Also, the PNG logo (in PNG format, of course) is supplied in the
+  files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31).
+
+  Libpng is OSI Certified Open Source Software.  OSI Certified Open Source is a
+  certification mark of the Open Source Initiative.
+
+  Glenn Randers-Pehrson
+  glennrp at users.sourceforge.net
+  July 7, 2011
+
+
+
+
+
+  WebP image encoder/decoder
+
+
+  Copyright (c) 2010, Google Inc. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+
+    * Neither the name of Google nor the names of its contributors may
+      be used to endorse or promote products derived from this software
+      without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Additional IP Rights Grant (Patents)
+
+  "This implementation" means the copyrightable works distributed by
+  Google as part of the WebM Project.
+
+  Google hereby grants to you a perpetual, worldwide, non-exclusive,
+  no-charge, royalty-free, irrevocable (except as stated in this section)
+  patent license to make, have made, use, offer to sell, sell, import,
+  transfer, and otherwise run, modify and propagate the contents of this
+  implementation of VP8, where such license applies only to those patent
+  claims, both currently owned by Google and acquired in the future,
+  licensable by Google that are necessarily infringed by this
+  implementation of VP8. This grant does not include claims that would be
+  infringed only as a consequence of further modification of this
+  implementation. If you or your agent or exclusive licensee institute or
+  order or agree to the institution of patent litigation against any
+  entity (including a cross-claim or counterclaim in a lawsuit) alleging
+  that this implementation of VP8 or any code incorporated within this
+  implementation of VP8 constitutes direct or contributory patent
+  infringement, or inducement of patent infringement, then any patent
+  rights granted to you under this License for this implementation of VP8
+  shall terminate as of the date such litigation is filed.
+
+
+
+  libxml
+
+
+  Except where otherwise noted in the source code (e.g. the files hash.c,
+  list.c and the trio files, which are covered by a similar licence but
+  with different Copyright notices) all the files are:
+
+   Copyright (C) 1998-2003 Daniel Veillard.  All Rights Reserved.
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is fur-
+  nished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+  NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+  DANIEL VEILLARD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+  NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+  Except as contained in this notice, the name of Daniel Veillard shall not
+  be used in advertising or otherwise to promote the sale, use or other deal-
+  ings in this Software without prior written authorization from him.
+
+
+
+  modp base64 decoder
+
+
+   * MODP_B64 - High performance base64 encoder/decoder
+   * Version 1.3 -- 17-Mar-2006
+   * http://modp.com/release/base64
+   *
+   * Copyright (c) 2005, 2006  Nick Galbreath -- nickg [at] modp [dot] com
+   * All rights reserved.
+   *
+   * Redistribution and use in source and binary forms, with or without
+   * modification, are permitted provided that the following conditions are
+   * met:
+   *
+   *   Redistributions of source code must retain the above copyright
+   *   notice, this list of conditions and the following disclaimer.
+   *
+   *   Redistributions in binary form must reproduce the above copyright
+   *   notice, this list of conditions and the following disclaimer in the
+   *   documentation and/or other materials provided with the distribution.
+   *
+   *   Neither the name of the modp.com nor the names of its
+   *   contributors may be used to endorse or promote products derived from
+   *   this software without specific prior written permission.
+   *
+   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  OTS (OpenType Sanitizer)
+
+
+  // Copyright (c) 2009 The Chromium Authors. All rights reserved.
+  //
+  // Redistribution and use in source and binary forms, with or without
+  // modification, are permitted provided that the following conditions are
+  // met:
+  //
+  //    * Redistributions of source code must retain the above copyright
+  // notice, this list of conditions and the following disclaimer.
+  //    * Redistributions in binary form must reproduce the above
+  // copyright notice, this list of conditions and the following disclaimer
+  // in the documentation and/or other materials provided with the
+  // distribution.
+  //    * Neither the name of Google Inc. nor the names of its
+  // contributors may be used to endorse or promote products derived from
+  // this software without specific prior written permission.
+  //
+  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  protobuf
+
+  This license applies to all parts of Protocol Buffers except the following:
+
+    - Atomicops support for generic gcc, located in
+      src/google/protobuf/stubs/atomicops_internals_generic_gcc.h.
+      This file is copyrighted by Red Hat Inc.
+
+    - Atomicops support for AIX/POWER, located in
+      src/google/protobuf/stubs/atomicops_internals_power.h.
+      This file is copyrighted by Bloomberg Finance LP.
+
+  Copyright 2014, Google Inc.  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+      * Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the following disclaimer
+  in the documentation and/or other materials provided with the
+  distribution.
+      * Neither the name of Google Inc. nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Code generated by the Protocol Buffer compiler is owned by the owner
+  of the input file used when generating it.  This code is not
+  standalone and requires a support library to be linked with it.  This
+  support library is itself covered by the above license.
+
+
+
+  Skia
+
+
+  // Copyright (c) 2011 Google Inc. All rights reserved.
+  //
+  // Redistribution and use in source and binary forms, with or without
+  // modification, are permitted provided that the following conditions are
+  // met:
+  //
+  //    * Redistributions of source code must retain the above copyright
+  // notice, this list of conditions and the following disclaimer.
+  //    * Redistributions in binary form must reproduce the above
+  // copyright notice, this list of conditions and the following disclaimer
+  // in the documentation and/or other materials provided with the
+  // distribution.
+  //    * Neither the name of Google Inc. nor the names of its
+  // contributors may be used to endorse or promote products derived from
+  // this software without specific prior written permission.
+  //
+  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+  zlib
+
+
+  /* zlib.h -- interface of the 'zlib' general purpose compression library
+    version 1.2.4, March 14th, 2010
+
+    Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
+
+    This software is provided 'as-is', without any express or implied
+    warranty.  In no event will the authors be held liable for any damages
+    arising from the use of this software.
+
+    Permission is granted to anyone to use this software for any purpose,
+    including commercial applications, and to alter it and redistribute it
+    freely, subject to the following restrictions:
+
+    1. The origin of this software must not be misrepresented; you must not
+       claim that you wrote the original software. If you use this software
+       in a product, an acknowledgment in the product documentation would be
+       appreciated but is not required.
+    2. Altered source versions must be plainly marked as such, and must not be
+       misrepresented as being the original software.
+    3. This notice may not be removed or altered from any source distribution.
+
+    Jean-loup Gailly
+    Mark Adler
+
+  */
+
+
+
+  WebKit
+
+  Copyright (C) 2006, 2007, 2008, 2009 Apple Inc.  All rights reserved.
+  Copyright (C) 2007-2009 Torch Mobile, Inc.
+  Copyright (C) Research In Motion Limited 2010. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  SuperFastHash
+
+  Paul Hsieh OLD BSD license
+
+  Copyright (c) 2010, Paul Hsieh
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without modification,
+  are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above copyright notice, this
+    list of conditions and the following disclaimer in the documentation and/or
+    other materials provided with the distribution.
+  * Neither my name, Paul Hsieh, nor the names of any other contributors to the
+    code use may not be used to endorse or promote products derived from this
+    software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  musl
+
+  ----------------------------------------------------------------------
+  Copyright © 2005-2014 Rich Felker, et al.
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+  ----------------------------------------------------------------------
+
+
+
+  brotli
+
+  Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors.
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+
+
+
+  woff2
+
+  Copyright (c) 2013-2017 by the WOFF2 Authors.
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+
+
+
+  jsmn(indirect usage in ce_cdm)
+
+  Copyright (c) 2010 Serge A. Zaitsev
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+
+
+
+  libvpx
+
+
+  Copyright (c) 2010, The WebM Project authors. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+
+    * Neither the name of Google, nor the WebM Project, nor the names
+      of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written
+      permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  ots
+
+
+  Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+     * Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+     * Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the following disclaimer
+  in the documentation and/or other materials provided with the
+  distribution.
+     * Neither the name of Google Inc. nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  quiche
+
+  // Copyright 2015 The Chromium Authors. All rights reserved.
+  //
+  // Redistribution and use in source and binary forms, with or without
+  // modification, are permitted provided that the following conditions are
+  // met:
+  //
+  //    * Redistributions of source code must retain the above copyright
+  // notice, this list of conditions and the following disclaimer.
+  //    * Redistributions in binary form must reproduce the above
+  // copyright notice, this list of conditions and the following disclaimer
+  // in the documentation and/or other materials provided with the
+  // distribution.
+  //    * Neither the name of Google Inc. nor the names of its
+  // contributors may be used to endorse or promote products derived from
+  // this software without specific prior written permission.
+  //
+  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  jinja2
+
+  Copyright (c) 2009 by the Jinja Team, see AUTHORS for more details.
+
+  Some rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+
+      * The names of the contributors may not be used to endorse or
+        promote products derived from this software without specific
+        prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  opus
+
+
+  Contributions to the collaboration shall not be considered confidential.
+
+  Each contributor represents and warrants that it has the right and
+  authority to license copyright in its contributions to the collaboration.
+
+  Each contributor agrees to license the copyright in the contributions
+  under the Modified (2-clause or 3-clause) BSD License or the Clear BSD License.
+
+  Please see the IPR statements submitted to the IETF for the complete
+  patent licensing details:
+
+  Xiph.Org Foundation:
+  https://datatracker.ietf.org/ipr/1524/
+
+  Microsoft Corporation:
+  https://datatracker.ietf.org/ipr/1914/
+
+  Skype Limited:
+  https://datatracker.ietf.org/ipr/1602/
+
+  Broadcom Corporation:
+  https://datatracker.ietf.org/ipr/1526/
+
+
+
+  LLVM
+
+
+  ==============================================================================
+  LLVM Release License
+  ==============================================================================
+  University of Illinois/NCSA
+  Open Source License
+
+  Copyright (c) 2003-2018 University of Illinois at Urbana-Champaign.
+  All rights reserved.
+
+  Developed by:
+
+      LLVM Team
+
+      University of Illinois at Urbana-Champaign
+
+      http://llvm.org
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy of
+  this software and associated documentation files (the "Software"), to deal with
+  the Software without restriction, including without limitation the rights to
+  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+  of the Software, and to permit persons to whom the Software is furnished to do
+  so, subject to the following conditions:
+
+      * Redistributions of source code must retain the above copyright notice,
+        this list of conditions and the following disclaimers.
+
+      * Redistributions in binary form must reproduce the above copyright notice,
+        this list of conditions and the following disclaimers in the
+        documentation and/or other materials provided with the distribution.
+
+      * Neither the names of the LLVM Team, University of Illinois at
+        Urbana-Champaign, nor the names of its contributors may be used to
+        endorse or promote products derived from this Software without specific
+        prior written permission.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+  CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+  SOFTWARE.
+
+  ==============================================================================
+  Copyrights and Licenses for Third Party Software Distributed with LLVM:
+  ==============================================================================
+  The LLVM software contains code written by third parties.  Such software will
+  have its own individual LICENSE.TXT file in the directory in which it appears.
+  This file will describe the copyrights, license, and restrictions which apply
+  to that code.
+
+  The disclaimer of warranty in the University of Illinois Open Source License
+  applies to all code in the LLVM Distribution, and nothing in any of the
+  other licenses gives permission to use the names of the LLVM Team or the
+  University of Illinois to endorse or promote products derived from this
+  Software.
+
+  The following pieces of software have additional or alternate copyrights,
+  licenses, and/or restrictions:
+
+  Program             Directory
+  -------             ---------
+  Google Test         llvm/utils/unittest/googletest
+  OpenBSD regex       llvm/lib/Support/{reg*, COPYRIGHT.regex}
+  pyyaml tests        llvm/test/YAMLParser/{*.data, LICENSE.TXT}
+  ARM contributions   llvm/lib/Target/ARM/LICENSE.TXT
+  md5 contributions   llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h
+
+
+
+  libaom(headers only)
+
+
+  Copyright (c) 2016, Alliance for Open Media. All rights reserved.
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  POSSIBILITY OF SUCH DAMAGE.
+
+
+  flac
+
+
+  Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007  Josh Coalson
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  - Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+  - Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+  - Neither the name of the Xiph.org Foundation nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  angle
+
+
+  // Copyright 2018 The ANGLE Project Authors.
+  // All rights reserved.
+  //
+  // Redistribution and use in source and binary forms, with or without
+  // modification, are permitted provided that the following conditions
+  // are met:
+  //
+  //     Redistributions of source code must retain the above copyright
+  //     notice, this list of conditions and the following disclaimer.
+  //
+  //     Redistributions in binary form must reproduce the above
+  //     copyright notice, this list of conditions and the following
+  //     disclaimer in the documentation and/or other materials provided
+  //     with the distribution.
+  //
+  //     Neither the name of TransGaming Inc., Google Inc., 3DLabs Inc.
+  //     Ltd., nor the names of their contributors may be used to endorse
+  //     or promote products derived from this software without specific
+  //     prior written permission.
+  //
+  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+  // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+  // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  // POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  quirc
+
+
+  quirc -- QR-code recognition library
+  Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
+
+  Permission to use, copy, modify, and/or distribute this software for
+  any purpose with or without fee is hereby granted, provided that the
+  above copyright notice and this permission notice appear in all
+  copies.
+
+  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+  WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+  AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+  DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+  PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+  TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+  PERFORMANCE OF THIS SOFTWARE.
+
+
+
+  markupsafe
+
+
+  Copyright (c) 2010 by Armin Ronacher and contributors.  See AUTHORS
+  for more details.
+
+  Some rights reserved.
+
+  Redistribution and use in source and binary forms of the software as well
+  as documentation, with or without modification, are permitted provided
+  that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above
+    copyright notice, this list of conditions and the following
+    disclaimer in the documentation and/or other materials provided
+    with the distribution.
+
+  * The names of the contributors may not be used to endorse or
+    promote products derived from this software without specific
+    prior written permission.
+
+  THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
+  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.
+
+
+
+  websocket-client
+
+
+  Copyright 2018 Hiroki Ohtani.
+
+  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+   pyjson5
+
+
+   Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+
+  proxy_py
+
+
+  Copyright (c) 2013-2018 by Abhinav Singh and contributors.
+
+  Some rights reserved.
+
+  Redistribution and use in source and binary forms of the software as well
+  as documentation, with or without modification, are permitted provided
+  that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above
+    copyright notice, this list of conditions and the following
+    disclaimer in the documentation and/or other materials provided
+    with the distribution.
+
+  * The names of the contributors may not be used to endorse or
+    promote products derived from this software without specific
+    prior written permission.
+
+  THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
+  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.
+
+
+
+  web platform tests
+
+
+  This repository is covered by the dual-licensing approach described in:
+
+    http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html
+
+
+
+  ply
+
+
+  PLY (Python Lex-Yacc)                   Version 3.4
+
+  Copyright (C) 2001-2011,
+  David M. Beazley (Dabeaz LLC)
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+  * Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+  * Neither the name of the David Beazley or Dabeaz LLC may be used to
+    endorse or promote products derived from this software without
+    specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  khronos
+
+
+  Copyright (c) 2007-2010 The Khronos Group Inc.
+
+  Permission is hereby granted, free of charge, to any person obtaining a
+  copy of this software and/or associated documentation files (the
+  "Materials"), to deal in the Materials without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Materials, and to
+  permit persons to whom the Materials are furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be included
+  in all copies or substantial portions of the Materials.
+
+  THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+  MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+
+  SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
+
+  Copyright (C) 1992 Silicon Graphics, Inc. All Rights Reserved.
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy of
+  this software and associated documentation files (the "Software"), to deal in
+  the Software without restriction, including without limitation the rights to
+  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+  of the Software, and to permit persons to whom the Software is furnished to do
+  so, subject to the following conditions:
+
+  The above copyright notice including the dates of first publication and either
+  this permission notice or a reference to http://oss.sgi.com/projects/FreeB/
+  shall be included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON
+  GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+  Except as contained in this notice, the name of Silicon Graphics, Inc. shall
+  not be used in advertising or otherwise to promote the sale, use or other
+  dealings in this Software without prior written authorization from Silicon
+  Graphics, Inc.
+
+
+
+  google_benchmark
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+
+  crashpad
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+
+  libdav1d
+
+
+  Copyright © 2018-2019, VideoLAN and dav1d authors
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  1. Redistributions of source code must retain the above copyright notice, this
+     list of conditions and the following disclaimer.
+
+  2. Redistributions in binary form must reproduce the above copyright notice,
+     this list of conditions and the following disclaimer in the documentation
+     and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  linux-syscall-support
+
+
+  Copyright (c) 2005-2011, Google Inc.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+  * Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the following disclaimer
+  in the documentation and/or other materials provided with the
+  distribution.
+  * Neither the name of Google Inc. nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  ---
+  Author: Markus Gutschke
+
+
+
+  mini_chromium
+
+
+  // Copyright 2006-2008 The Chromium Authors. All rights reserved.
+  //
+  // Redistribution and use in source and binary forms, with or without
+  // modification, are permitted provided that the following conditions are
+  // met:
+  //
+  //    * Redistributions of source code must retain the above copyright
+  // notice, this list of conditions and the following disclaimer.
+  //    * Redistributions in binary form must reproduce the above
+  // copyright notice, this list of conditions and the following disclaimer
+  // in the documentation and/or other materials provided with the
+  // distribution.
+  //    * Neither the name of Google Inc. nor the names of its
+  // contributors may be used to endorse or promote products derived from
+  // this software without specific prior written permission.
+  //
+  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  googletest
+
+
+  Copyright 2008, Google Inc.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+      * Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+      * Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the following disclaimer
+  in the documentation and/or other materials provided with the
+  distribution.
+      * Neither the name of Google Inc. nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+  lz4_lib
+
+
+  LZ4 Library
+  Copyright (c) 2011-2016, Yann Collet
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without modification,
+  are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright notice, this
+    list of conditions and the following disclaimer in the documentation and/or
+    other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+  libwebp
+
+  Copyright (c) 2010, Google Inc. All rights reserved.  Redistribution and use
+  in source and binary forms, with or without modification, are permitted
+  provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of Google nor the names of its contributors may be used
+      to endorse or promote products derived from this software without specific
+      prior written permission.
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
+  HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  Additional IP Rights Grant (Patents) ------------------------------------
+  "These implementations" means the copyrightable works that implement the
+  WebM codecs distributed by Google as part of the WebM Project.  Google
+  hereby grants to you a perpetual, worldwide, non-exclusive, no-charge,
+  royalty-free, irrevocable (except as stated in this section) patent
+  license to make, have made, use, offer to sell, sell, import, transfer,
+  and otherwise run, modify and propagate the contents of these
+  implementations of WebM, where such license applies only to those patent
+  claims, both currently owned by Google and acquired in the future,
+  licensable by Google that are necessarily infringed by these
+  implementations of WebM. This grant does not include claims that would be
+  infringed only as a consequence of further modification of these
+  implementations. If you or your agent or exclusive licensee institute or
+  order or agree to the institution of patent litigation or any other patent
+  enforcement activity against any entity (including a cross-claim or
+  counterclaim in a lawsuit) alleging that any of these implementations of
+  WebM or any code incorporated within any of these implementations of WebM
+  constitute direct or contributory patent infringement, or inducement of
+  patent infringement, then any patent rights granted to you under this
+  License for these implementations of WebM shall terminate as of the date
+  such litigation is filed.
diff --git a/cobalt/content/licenses/platform/default/licenses_cobalt.txt b/cobalt/content/licenses/platform/default/licenses_cobalt.txt
index be071f5..7d3b733 100644
--- a/cobalt/content/licenses/platform/default/licenses_cobalt.txt
+++ b/cobalt/content/licenses/platform/default/licenses_cobalt.txt
@@ -1729,885 +1729,6 @@
 
 
 
-  nss
-
-
-    NSS is available under the Mozilla Public License, version 2, a copy of which
-  is below.
-
-  Note on GPL Compatibility
-  -------------------------
-
-  The MPL 2, section 3.3, permits you to combine NSS with code under the GNU
-  General Public License (GPL) version 2, or any later version of that
-  license, to make a Larger Work, and distribute the result under the GPL.
-  The only condition is that you must also make NSS, and any changes you
-  have made to it, available to recipients under the terms of the MPL 2 also.
-
-  Anyone who receives the combined code from you does not have to continue
-  to dual licence in this way, and may, if they wish, distribute under the
-  terms of either of the two licences - either the MPL alone or the GPL
-  alone. However, we discourage people from distributing copies of NSS under
-  the GPL alone, because it means that any improvements they make cannot be
-  reincorporated into the main version of NSS. There is never a need to do
-  this for license compatibility reasons.
-
-  Note on LGPL Compatibility
-  --------------------------
-
-  The above also applies to combining MPLed code in a single library with
-  code under the GNU Lesser General Public License (LGPL) version 2.1, or
-  any later version of that license. If the LGPLed code and the MPLed code
-  are not in the same library, then the copyleft coverage of the two
-  licences does not overlap, so no issues arise.
-
-
-  Mozilla Public License Version 2.0
-  ==================================
-
-  1. Definitions
-  --------------
-
-  1.1. "Contributor"
-      means each individual or legal entity that creates, contributes to
-      the creation of, or owns Covered Software.
-
-  1.2. "Contributor Version"
-      means the combination of the Contributions of others (if any) used
-      by a Contributor and that particular Contributor's Contribution.
-
-  1.3. "Contribution"
-      means Covered Software of a particular Contributor.
-
-  1.4. "Covered Software"
-      means Source Code Form to which the initial Contributor has attached
-      the notice in Exhibit A, the Executable Form of such Source Code
-      Form, and Modifications of such Source Code Form, in each case
-      including portions thereof.
-
-  1.5. "Incompatible With Secondary Licenses"
-      means
-
-      (a) that the initial Contributor has attached the notice described
-          in Exhibit B to the Covered Software; or
-
-      (b) that the Covered Software was made available under the terms of
-          version 1.1 or earlier of the License, but not also under the
-          terms of a Secondary License.
-
-  1.6. "Executable Form"
-      means any form of the work other than Source Code Form.
-
-  1.7. "Larger Work"
-      means a work that combines Covered Software with other material, in
-      a separate file or files, that is not Covered Software.
-
-  1.8. "License"
-      means this document.
-
-  1.9. "Licensable"
-      means having the right to grant, to the maximum extent possible,
-      whether at the time of the initial grant or subsequently, any and
-      all of the rights conveyed by this License.
-
-  1.10. "Modifications"
-      means any of the following:
-
-      (a) any file in Source Code Form that results from an addition to,
-          deletion from, or modification of the contents of Covered
-          Software; or
-
-      (b) any new file in Source Code Form that contains any Covered
-          Software.
-
-  1.11. "Patent Claims" of a Contributor
-      means any patent claim(s), including without limitation, method,
-      process, and apparatus claims, in any patent Licensable by such
-      Contributor that would be infringed, but for the grant of the
-      License, by the making, using, selling, offering for sale, having
-      made, import, or transfer of either its Contributions or its
-      Contributor Version.
-
-  1.12. "Secondary License"
-      means either the GNU General Public License, Version 2.0, the GNU
-      Lesser General Public License, Version 2.1, the GNU Affero General
-      Public License, Version 3.0, or any later versions of those
-      licenses.
-
-  1.13. "Source Code Form"
-      means the form of the work preferred for making modifications.
-
-  1.14. "You" (or "Your")
-      means an individual or a legal entity exercising rights under this
-      License. For legal entities, "You" includes any entity that
-      controls, is controlled by, or is under common control with You. For
-      purposes of this definition, "control" means (a) the power, direct
-      or indirect, to cause the direction or management of such entity,
-      whether by contract or otherwise, or (b) ownership of more than
-      fifty percent (50%) of the outstanding shares or beneficial
-      ownership of such entity.
-
-  2. License Grants and Conditions
-  --------------------------------
-
-  2.1. Grants
-
-  Each Contributor hereby grants You a world-wide, royalty-free,
-  non-exclusive license:
-
-  (a) under intellectual property rights (other than patent or trademark)
-      Licensable by such Contributor to use, reproduce, make available,
-      modify, display, perform, distribute, and otherwise exploit its
-      Contributions, either on an unmodified basis, with Modifications, or
-      as part of a Larger Work; and
-
-  (b) under Patent Claims of such Contributor to make, use, sell, offer
-      for sale, have made, import, and otherwise transfer either its
-      Contributions or its Contributor Version.
-
-  2.2. Effective Date
-
-  The licenses granted in Section 2.1 with respect to any Contribution
-  become effective for each Contribution on the date the Contributor first
-  distributes such Contribution.
-
-  2.3. Limitations on Grant Scope
-
-  The licenses granted in this Section 2 are the only rights granted under
-  this License. No additional rights or licenses will be implied from the
-  distribution or licensing of Covered Software under this License.
-  Notwithstanding Section 2.1(b) above, no patent license is granted by a
-  Contributor:
-
-  (a) for any code that a Contributor has removed from Covered Software;
-      or
-
-  (b) for infringements caused by: (i) Your and any other third party's
-      modifications of Covered Software, or (ii) the combination of its
-      Contributions with other software (except as part of its Contributor
-      Version); or
-
-  (c) under Patent Claims infringed by Covered Software in the absence of
-      its Contributions.
-
-  This License does not grant any rights in the trademarks, service marks,
-  or logos of any Contributor (except as may be necessary to comply with
-  the notice requirements in Section 3.4).
-
-  2.4. Subsequent Licenses
-
-  No Contributor makes additional grants as a result of Your choice to
-  distribute the Covered Software under a subsequent version of this
-  License (see Section 10.2) or under the terms of a Secondary License (if
-  permitted under the terms of Section 3.3).
-
-  2.5. Representation
-
-  Each Contributor represents that the Contributor believes its
-  Contributions are its original creation(s) or it has sufficient rights
-  to grant the rights to its Contributions conveyed by this License.
-
-  2.6. Fair Use
-
-  This License is not intended to limit any rights You have under
-  applicable copyright doctrines of fair use, fair dealing, or other
-  equivalents.
-
-  2.7. Conditions
-
-  Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-  in Section 2.1.
-
-  3. Responsibilities
-  -------------------
-
-  3.1. Distribution of Source Form
-
-  All distribution of Covered Software in Source Code Form, including any
-  Modifications that You create or to which You contribute, must be under
-  the terms of this License. You must inform recipients that the Source
-  Code Form of the Covered Software is governed by the terms of this
-  License, and how they can obtain a copy of this License. You may not
-  attempt to alter or restrict the recipients' rights in the Source Code
-  Form.
-
-  3.2. Distribution of Executable Form
-
-  If You distribute Covered Software in Executable Form then:
-
-  (a) such Covered Software must also be made available in Source Code
-      Form, as described in Section 3.1, and You must inform recipients of
-      the Executable Form how they can obtain a copy of such Source Code
-      Form by reasonable means in a timely manner, at a charge no more
-      than the cost of distribution to the recipient; and
-
-  (b) You may distribute such Executable Form under the terms of this
-      License, or sublicense it under different terms, provided that the
-      license for the Executable Form does not attempt to limit or alter
-      the recipients' rights in the Source Code Form under this License.
-
-  3.3. Distribution of a Larger Work
-
-  You may create and distribute a Larger Work under terms of Your choice,
-  provided that You also comply with the requirements of this License for
-  the Covered Software. If the Larger Work is a combination of Covered
-  Software with a work governed by one or more Secondary Licenses, and the
-  Covered Software is not Incompatible With Secondary Licenses, this
-  License permits You to additionally distribute such Covered Software
-  under the terms of such Secondary License(s), so that the recipient of
-  the Larger Work may, at their option, further distribute the Covered
-  Software under the terms of either this License or such Secondary
-  License(s).
-
-  3.4. Notices
-
-  You may not remove or alter the substance of any license notices
-  (including copyright notices, patent notices, disclaimers of warranty,
-  or limitations of liability) contained within the Source Code Form of
-  the Covered Software, except that You may alter any license notices to
-  the extent required to remedy known factual inaccuracies.
-
-  3.5. Application of Additional Terms
-
-  You may choose to offer, and to charge a fee for, warranty, support,
-  indemnity or liability obligations to one or more recipients of Covered
-  Software. However, You may do so only on Your own behalf, and not on
-  behalf of any Contributor. You must make it absolutely clear that any
-  such warranty, support, indemnity, or liability obligation is offered by
-  You alone, and You hereby agree to indemnify every Contributor for any
-  liability incurred by such Contributor as a result of warranty, support,
-  indemnity or liability terms You offer. You may include additional
-  disclaimers of warranty and limitations of liability specific to any
-  jurisdiction.
-
-  4. Inability to Comply Due to Statute or Regulation
-  ---------------------------------------------------
-
-  If it is impossible for You to comply with any of the terms of this
-  License with respect to some or all of the Covered Software due to
-  statute, judicial order, or regulation then You must: (a) comply with
-  the terms of this License to the maximum extent possible; and (b)
-  describe the limitations and the code they affect. Such description must
-  be placed in a text file included with all distributions of the Covered
-  Software under this License. Except to the extent prohibited by statute
-  or regulation, such description must be sufficiently detailed for a
-  recipient of ordinary skill to be able to understand it.
-
-  5. Termination
-  --------------
-
-  5.1. The rights granted under this License will terminate automatically
-  if You fail to comply with any of its terms. However, if You become
-  compliant, then the rights granted under this License from a particular
-  Contributor are reinstated (a) provisionally, unless and until such
-  Contributor explicitly and finally terminates Your grants, and (b) on an
-  ongoing basis, if such Contributor fails to notify You of the
-  non-compliance by some reasonable means prior to 60 days after You have
-  come back into compliance. Moreover, Your grants from a particular
-  Contributor are reinstated on an ongoing basis if such Contributor
-  notifies You of the non-compliance by some reasonable means, this is the
-  first time You have received notice of non-compliance with this License
-  from such Contributor, and You become compliant prior to 30 days after
-  Your receipt of the notice.
-
-  5.2. If You initiate litigation against any entity by asserting a patent
-  infringement claim (excluding declaratory judgment actions,
-  counter-claims, and cross-claims) alleging that a Contributor Version
-  directly or indirectly infringes any patent, then the rights granted to
-  You by any and all Contributors for the Covered Software under Section
-  2.1 of this License shall terminate.
-
-  5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-  end user license agreements (excluding distributors and resellers) which
-  have been validly granted by You or Your distributors under this License
-  prior to termination shall survive termination.
-
-  ************************************************************************
-  *                                                                      *
-  *  6. Disclaimer of Warranty                                           *
-  *  -------------------------                                           *
-  *                                                                      *
-  *  Covered Software is provided under this License on an "as is"       *
-  *  basis, without warranty of any kind, either expressed, implied, or  *
-  *  statutory, including, without limitation, warranties that the       *
-  *  Covered Software is free of defects, merchantable, fit for a        *
-  *  particular purpose or non-infringing. The entire risk as to the     *
-  *  quality and performance of the Covered Software is with You.        *
-  *  Should any Covered Software prove defective in any respect, You     *
-  *  (not any Contributor) assume the cost of any necessary servicing,   *
-  *  repair, or correction. This disclaimer of warranty constitutes an   *
-  *  essential part of this License. No use of any Covered Software is   *
-  *  authorized under this License except under this disclaimer.         *
-  *                                                                      *
-  ************************************************************************
-
-  ************************************************************************
-  *                                                                      *
-  *  7. Limitation of Liability                                          *
-  *  --------------------------                                          *
-  *                                                                      *
-  *  Under no circumstances and under no legal theory, whether tort      *
-  *  (including negligence), contract, or otherwise, shall any           *
-  *  Contributor, or anyone who distributes Covered Software as          *
-  *  permitted above, be liable to You for any direct, indirect,         *
-  *  special, incidental, or consequential damages of any character      *
-  *  including, without limitation, damages for lost profits, loss of    *
-  *  goodwill, work stoppage, computer failure or malfunction, or any    *
-  *  and all other commercial damages or losses, even if such party      *
-  *  shall have been informed of the possibility of such damages. This   *
-  *  limitation of liability shall not apply to liability for death or   *
-  *  personal injury resulting from such party's negligence to the       *
-  *  extent applicable law prohibits such limitation. Some               *
-  *  jurisdictions do not allow the exclusion or limitation of           *
-  *  incidental or consequential damages, so this exclusion and          *
-  *  limitation may not apply to You.                                    *
-  *                                                                      *
-  ************************************************************************
-
-  8. Litigation
-  -------------
-
-  Any litigation relating to this License may be brought only in the
-  courts of a jurisdiction where the defendant maintains its principal
-  place of business and such litigation shall be governed by laws of that
-  jurisdiction, without reference to its conflict-of-law provisions.
-  Nothing in this Section shall prevent a party's ability to bring
-  cross-claims or counter-claims.
-
-  9. Miscellaneous
-  ----------------
-
-  This License represents the complete agreement concerning the subject
-  matter hereof. If any provision of this License is held to be
-  unenforceable, such provision shall be reformed only to the extent
-  necessary to make it enforceable. Any law or regulation which provides
-  that the language of a contract shall be construed against the drafter
-  shall not be used to construe this License against a Contributor.
-
-  10. Versions of the License
-  ---------------------------
-
-  10.1. New Versions
-
-  Mozilla Foundation is the license steward. Except as provided in Section
-  10.3, no one other than the license steward has the right to modify or
-  publish new versions of this License. Each version will be given a
-  distinguishing version number.
-
-  10.2. Effect of New Versions
-
-  You may distribute the Covered Software under the terms of the version
-  of the License under which You originally received the Covered Software,
-  or under the terms of any subsequent version published by the license
-  steward.
-
-  10.3. Modified Versions
-
-  If you create software not governed by this License, and you want to
-  create a new license for such software, you may create and use a
-  modified version of this License if you rename the license and remove
-  any references to the name of the license steward (except to note that
-  such modified license differs from this License).
-
-  10.4. Distributing Source Code Form that is Incompatible With Secondary
-  Licenses
-
-  If You choose to distribute Source Code Form that is Incompatible With
-  Secondary Licenses under the terms of this version of the License, the
-  notice described in Exhibit B of this License must be attached.
-
-  Exhibit A - Source Code Form License Notice
-  -------------------------------------------
-
-    This Source Code Form is subject to the terms of the Mozilla Public
-    License, v. 2.0. If a copy of the MPL was not distributed with this
-    file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-  If it is not possible or desirable to put the notice in a particular
-  file, then You may include the notice in a location (such as a LICENSE
-  file in a relevant directory) where a recipient would be likely to look
-  for such a notice.
-
-  You may add additional accurate notices of copyright ownership.
-
-  Exhibit B - "Incompatible With Secondary Licenses" Notice
-  ---------------------------------------------------------
-
-    This Source Code Form is "Incompatible With Secondary Licenses", as
-    defined by the Mozilla Public License, v. 2.0.
-
-
-
-  mozilla_security_manager
-
-                             MOZILLA PUBLIC LICENSE
-                                Version 1.1
-
-                              ---------------
-
-  1. Definitions.
-
-     1.0.1. "Commercial Use" means distribution or otherwise making the
-     Covered Code available to a third party.
-
-     1.1. "Contributor" means each entity that creates or contributes to
-     the creation of Modifications.
-
-     1.2. "Contributor Version" means the combination of the Original
-     Code, prior Modifications used by a Contributor, and the Modifications
-     made by that particular Contributor.
-
-     1.3. "Covered Code" means the Original Code or Modifications or the
-     combination of the Original Code and Modifications, in each case
-     including portions thereof.
-
-     1.4. "Electronic Distribution Mechanism" means a mechanism generally
-     accepted in the software development community for the electronic
-     transfer of data.
-
-     1.5. "Executable" means Covered Code in any form other than Source
-     Code.
-
-     1.6. "Initial Developer" means the individual or entity identified
-     as the Initial Developer in the Source Code notice required by Exhibit
-     A.
-
-     1.7. "Larger Work" means a work which combines Covered Code or
-     portions thereof with code not governed by the terms of this License.
-
-     1.8. "License" means this document.
-
-     1.8.1. "Licensable" means having the right to grant, to the maximum
-     extent possible, whether at the time of the initial grant or
-     subsequently acquired, any and all of the rights conveyed herein.
-
-     1.9. "Modifications" means any addition to or deletion from the
-     substance or structure of either the Original Code or any previous
-     Modifications. When Covered Code is released as a series of files, a
-     Modification is:
-          A. Any addition to or deletion from the contents of a file
-          containing Original Code or previous Modifications.
-
-          B. Any new file that contains any part of the Original Code or
-          previous Modifications.
-
-     1.10. "Original Code" means Source Code of computer software code
-     which is described in the Source Code notice required by Exhibit A as
-     Original Code, and which, at the time of its release under this
-     License is not already Covered Code governed by this License.
-
-     1.10.1. "Patent Claims" means any patent claim(s), now owned or
-     hereafter acquired, including without limitation,  method, process,
-     and apparatus claims, in any patent Licensable by grantor.
-
-     1.11. "Source Code" means the preferred form of the Covered Code for
-     making modifications to it, including all modules it contains, plus
-     any associated interface definition files, scripts used to control
-     compilation and installation of an Executable, or source code
-     differential comparisons against either the Original Code or another
-     well known, available Covered Code of the Contributor's choice. The
-     Source Code can be in a compressed or archival form, provided the
-     appropriate decompression or de-archiving software is widely available
-     for no charge.
-
-     1.12. "You" (or "Your")  means an individual or a legal entity
-     exercising rights under, and complying with all of the terms of, this
-     License or a future version of this License issued under Section 6.1.
-     For legal entities, "You" includes any entity which controls, is
-     controlled by, or is under common control with You. For purposes of
-     this definition, "control" means (a) the power, direct or indirect,
-     to cause the direction or management of such entity, whether by
-     contract or otherwise, or (b) ownership of more than fifty percent
-     (50%) of the outstanding shares or beneficial ownership of such
-     entity.
-
-  2. Source Code License.
-
-     2.1. The Initial Developer Grant.
-     The Initial Developer hereby grants You a world-wide, royalty-free,
-     non-exclusive license, subject to third party intellectual property
-     claims:
-          (a)  under intellectual property rights (other than patent or
-          trademark) Licensable by Initial Developer to use, reproduce,
-          modify, display, perform, sublicense and distribute the Original
-          Code (or portions thereof) with or without Modifications, and/or
-          as part of a Larger Work; and
-
-          (b) under Patents Claims infringed by the making, using or
-          selling of Original Code, to make, have made, use, practice,
-          sell, and offer for sale, and/or otherwise dispose of the
-          Original Code (or portions thereof).
-
-          (c) the licenses granted in this Section 2.1(a) and (b) are
-          effective on the date Initial Developer first distributes
-          Original Code under the terms of this License.
-
-          (d) Notwithstanding Section 2.1(b) above, no patent license is
-          granted: 1) for code that You delete from the Original Code; 2)
-          separate from the Original Code;  or 3) for infringements caused
-          by: i) the modification of the Original Code or ii) the
-          combination of the Original Code with other software or devices.
-
-     2.2. Contributor Grant.
-     Subject to third party intellectual property claims, each Contributor
-     hereby grants You a world-wide, royalty-free, non-exclusive license
-
-          (a)  under intellectual property rights (other than patent or
-          trademark) Licensable by Contributor, to use, reproduce, modify,
-          display, perform, sublicense and distribute the Modifications
-          created by such Contributor (or portions thereof) either on an
-          unmodified basis, with other Modifications, as Covered Code
-          and/or as part of a Larger Work; and
-
-          (b) under Patent Claims infringed by the making, using, or
-          selling of  Modifications made by that Contributor either alone
-          and/or in combination with its Contributor Version (or portions
-          of such combination), to make, use, sell, offer for sale, have
-          made, and/or otherwise dispose of: 1) Modifications made by that
-          Contributor (or portions thereof); and 2) the combination of
-          Modifications made by that Contributor with its Contributor
-          Version (or portions of such combination).
-
-          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
-          effective on the date Contributor first makes Commercial Use of
-          the Covered Code.
-
-          (d)    Notwithstanding Section 2.2(b) above, no patent license is
-          granted: 1) for any code that Contributor has deleted from the
-          Contributor Version; 2)  separate from the Contributor Version;
-          3)  for infringements caused by: i) third party modifications of
-          Contributor Version or ii)  the combination of Modifications made
-          by that Contributor with other software  (except as part of the
-          Contributor Version) or other devices; or 4) under Patent Claims
-          infringed by Covered Code in the absence of Modifications made by
-          that Contributor.
-
-  3. Distribution Obligations.
-
-     3.1. Application of License.
-     The Modifications which You create or to which You contribute are
-     governed by the terms of this License, including without limitation
-     Section 2.2. The Source Code version of Covered Code may be
-     distributed only under the terms of this License or a future version
-     of this License released under Section 6.1, and You must include a
-     copy of this License with every copy of the Source Code You
-     distribute. You may not offer or impose any terms on any Source Code
-     version that alters or restricts the applicable version of this
-     License or the recipients' rights hereunder. However, You may include
-     an additional document offering the additional rights described in
-     Section 3.5.
-
-     3.2. Availability of Source Code.
-     Any Modification which You create or to which You contribute must be
-     made available in Source Code form under the terms of this License
-     either on the same media as an Executable version or via an accepted
-     Electronic Distribution Mechanism to anyone to whom you made an
-     Executable version available; and if made available via Electronic
-     Distribution Mechanism, must remain available for at least twelve (12)
-     months after the date it initially became available, or at least six
-     (6) months after a subsequent version of that particular Modification
-     has been made available to such recipients. You are responsible for
-     ensuring that the Source Code version remains available even if the
-     Electronic Distribution Mechanism is maintained by a third party.
-
-     3.3. Description of Modifications.
-     You must cause all Covered Code to which You contribute to contain a
-     file documenting the changes You made to create that Covered Code and
-     the date of any change. You must include a prominent statement that
-     the Modification is derived, directly or indirectly, from Original
-     Code provided by the Initial Developer and including the name of the
-     Initial Developer in (a) the Source Code, and (b) in any notice in an
-     Executable version or related documentation in which You describe the
-     origin or ownership of the Covered Code.
-
-     3.4. Intellectual Property Matters
-          (a) Third Party Claims.
-          If Contributor has knowledge that a license under a third party's
-          intellectual property rights is required to exercise the rights
-          granted by such Contributor under Sections 2.1 or 2.2,
-          Contributor must include a text file with the Source Code
-          distribution titled "LEGAL" which describes the claim and the
-          party making the claim in sufficient detail that a recipient will
-          know whom to contact. If Contributor obtains such knowledge after
-          the Modification is made available as described in Section 3.2,
-          Contributor shall promptly modify the LEGAL file in all copies
-          Contributor makes available thereafter and shall take other steps
-          (such as notifying appropriate mailing lists or newsgroups)
-          reasonably calculated to inform those who received the Covered
-          Code that new knowledge has been obtained.
-
-          (b) Contributor APIs.
-          If Contributor's Modifications include an application programming
-          interface and Contributor has knowledge of patent licenses which
-          are reasonably necessary to implement that API, Contributor must
-          also include this information in the LEGAL file.
-
-               (c)    Representations.
-          Contributor represents that, except as disclosed pursuant to
-          Section 3.4(a) above, Contributor believes that Contributor's
-          Modifications are Contributor's original creation(s) and/or
-          Contributor has sufficient rights to grant the rights conveyed by
-          this License.
-
-     3.5. Required Notices.
-     You must duplicate the notice in Exhibit A in each file of the Source
-     Code.  If it is not possible to put such notice in a particular Source
-     Code file due to its structure, then You must include such notice in a
-     location (such as a relevant directory) where a user would be likely
-     to look for such a notice.  If You created one or more Modification(s)
-     You may add your name as a Contributor to the notice described in
-     Exhibit A.  You must also duplicate this License in any documentation
-     for the Source Code where You describe recipients' rights or ownership
-     rights relating to Covered Code.  You may choose to offer, and to
-     charge a fee for, warranty, support, indemnity or liability
-     obligations to one or more recipients of Covered Code. However, You
-     may do so only on Your own behalf, and not on behalf of the Initial
-     Developer or any Contributor. You must make it absolutely clear than
-     any such warranty, support, indemnity or liability obligation is
-     offered by You alone, and You hereby agree to indemnify the Initial
-     Developer and every Contributor for any liability incurred by the
-     Initial Developer or such Contributor as a result of warranty,
-     support, indemnity or liability terms You offer.
-
-     3.6. Distribution of Executable Versions.
-     You may distribute Covered Code in Executable form only if the
-     requirements of Section 3.1-3.5 have been met for that Covered Code,
-     and if You include a notice stating that the Source Code version of
-     the Covered Code is available under the terms of this License,
-     including a description of how and where You have fulfilled the
-     obligations of Section 3.2. The notice must be conspicuously included
-     in any notice in an Executable version, related documentation or
-     collateral in which You describe recipients' rights relating to the
-     Covered Code. You may distribute the Executable version of Covered
-     Code or ownership rights under a license of Your choice, which may
-     contain terms different from this License, provided that You are in
-     compliance with the terms of this License and that the license for the
-     Executable version does not attempt to limit or alter the recipient's
-     rights in the Source Code version from the rights set forth in this
-     License. If You distribute the Executable version under a different
-     license You must make it absolutely clear that any terms which differ
-     from this License are offered by You alone, not by the Initial
-     Developer or any Contributor. You hereby agree to indemnify the
-     Initial Developer and every Contributor for any liability incurred by
-     the Initial Developer or such Contributor as a result of any such
-     terms You offer.
-
-     3.7. Larger Works.
-     You may create a Larger Work by combining Covered Code with other code
-     not governed by the terms of this License and distribute the Larger
-     Work as a single product. In such a case, You must make sure the
-     requirements of this License are fulfilled for the Covered Code.
-
-  4. Inability to Comply Due to Statute or Regulation.
-
-     If it is impossible for You to comply with any of the terms of this
-     License with respect to some or all of the Covered Code due to
-     statute, judicial order, or regulation then You must: (a) comply with
-     the terms of this License to the maximum extent possible; and (b)
-     describe the limitations and the code they affect. Such description
-     must be included in the LEGAL file described in Section 3.4 and must
-     be included with all distributions of the Source Code. Except to the
-     extent prohibited by statute or regulation, such description must be
-     sufficiently detailed for a recipient of ordinary skill to be able to
-     understand it.
-
-  5. Application of this License.
-
-     This License applies to code to which the Initial Developer has
-     attached the notice in Exhibit A and to related Covered Code.
-
-  6. Versions of the License.
-
-     6.1. New Versions.
-     Netscape Communications Corporation ("Netscape") may publish revised
-     and/or new versions of the License from time to time. Each version
-     will be given a distinguishing version number.
-
-     6.2. Effect of New Versions.
-     Once Covered Code has been published under a particular version of the
-     License, You may always continue to use it under the terms of that
-     version. You may also choose to use such Covered Code under the terms
-     of any subsequent version of the License published by Netscape. No one
-     other than Netscape has the right to modify the terms applicable to
-     Covered Code created under this License.
-
-     6.3. Derivative Works.
-     If You create or use a modified version of this License (which you may
-     only do in order to apply it to code which is not already Covered Code
-     governed by this License), You must (a) rename Your license so that
-     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
-     "MPL", "NPL" or any confusingly similar phrase do not appear in your
-     license (except to note that your license differs from this License)
-     and (b) otherwise make it clear that Your version of the license
-     contains terms which differ from the Mozilla Public License and
-     Netscape Public License. (Filling in the name of the Initial
-     Developer, Original Code or Contributor in the notice described in
-     Exhibit A shall not of themselves be deemed to be modifications of
-     this License.)
-
-  7. DISCLAIMER OF WARRANTY.
-
-     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
-     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
-     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
-     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
-     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
-     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
-     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
-     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
-     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
-     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-  8. TERMINATION.
-
-     8.1.  This License and the rights granted hereunder will terminate
-     automatically if You fail to comply with terms herein and fail to cure
-     such breach within 30 days of becoming aware of the breach. All
-     sublicenses to the Covered Code which are properly granted shall
-     survive any termination of this License. Provisions which, by their
-     nature, must remain in effect beyond the termination of this License
-     shall survive.
-
-     8.2.  If You initiate litigation by asserting a patent infringement
-     claim (excluding declatory judgment actions) against Initial Developer
-     or a Contributor (the Initial Developer or Contributor against whom
-     You file such action is referred to as "Participant")  alleging that:
-
-     (a)  such Participant's Contributor Version directly or indirectly
-     infringes any patent, then any and all rights granted by such
-     Participant to You under Sections 2.1 and/or 2.2 of this License
-     shall, upon 60 days notice from Participant terminate prospectively,
-     unless if within 60 days after receipt of notice You either: (i)
-     agree in writing to pay Participant a mutually agreeable reasonable
-     royalty for Your past and future use of Modifications made by such
-     Participant, or (ii) withdraw Your litigation claim with respect to
-     the Contributor Version against such Participant.  If within 60 days
-     of notice, a reasonable royalty and payment arrangement are not
-     mutually agreed upon in writing by the parties or the litigation claim
-     is not withdrawn, the rights granted by Participant to You under
-     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
-     the 60 day notice period specified above.
-
-     (b)  any software, hardware, or device, other than such Participant's
-     Contributor Version, directly or indirectly infringes any patent, then
-     any rights granted to You by such Participant under Sections 2.1(b)
-     and 2.2(b) are revoked effective as of the date You first made, used,
-     sold, distributed, or had made, Modifications made by that
-     Participant.
-
-     8.3.  If You assert a patent infringement claim against Participant
-     alleging that such Participant's Contributor Version directly or
-     indirectly infringes any patent where such claim is resolved (such as
-     by license or settlement) prior to the initiation of patent
-     infringement litigation, then the reasonable value of the licenses
-     granted by such Participant under Sections 2.1 or 2.2 shall be taken
-     into account in determining the amount or value of any payment or
-     license.
-
-     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
-     all end user license agreements (excluding distributors and resellers)
-     which have been validly granted by You or any distributor hereunder
-     prior to termination shall survive termination.
-
-  9. LIMITATION OF LIABILITY.
-
-     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
-     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
-     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
-     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
-     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
-     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
-     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
-     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
-     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
-     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
-     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
-     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
-     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
-     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-  10. U.S. GOVERNMENT END USERS.
-
-     The Covered Code is a "commercial item," as that term is defined in
-     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
-     software" and "commercial computer software documentation," as such
-     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
-     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
-     all U.S. Government End Users acquire Covered Code with only those
-     rights set forth herein.
-
-  11. MISCELLANEOUS.
-
-     This License represents the complete agreement concerning subject
-     matter hereof. If any provision of this License is held to be
-     unenforceable, such provision shall be reformed only to the extent
-     necessary to make it enforceable. This License shall be governed by
-     California law provisions (except to the extent applicable law, if
-     any, provides otherwise), excluding its conflict-of-law provisions.
-     With respect to disputes in which at least one party is a citizen of,
-     or an entity chartered or registered to do business in the United
-     States of America, any litigation relating to this License shall be
-     subject to the jurisdiction of the Federal Courts of the Northern
-     District of California, with venue lying in Santa Clara County,
-     California, with the losing party responsible for costs, including
-     without limitation, court costs and reasonable attorneys' fees and
-     expenses. The application of the United Nations Convention on
-     Contracts for the International Sale of Goods is expressly excluded.
-     Any law or regulation which provides that the language of a contract
-     shall be construed against the drafter shall not apply to this
-     License.
-
-  12. RESPONSIBILITY FOR CLAIMS.
-
-     As between Initial Developer and the Contributors, each party is
-     responsible for claims and damages arising, directly or indirectly,
-     out of its utilization of rights under this License and You agree to
-     work with Initial Developer and Contributors to distribute such
-     responsibility on an equitable basis. Nothing herein is intended or
-     shall be deemed to constitute any admission of liability.
-
-  13. MULTIPLE-LICENSED CODE.
-
-     Initial Developer may designate portions of the Covered Code as
-     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
-     Developer permits you to utilize portions of the Covered Code under
-     Your choice of the NPL or the alternative licenses, if any, specified
-     by the Initial Developer in the file described in Exhibit A.
-
-  EXHIBIT A -Mozilla Public License.
-
-   The contents of this file are subject to the Mozilla Public License Version
-   1.1 (the "License"); you may not use this file except in compliance with
-   the License. You may obtain a copy of the License at
-   http://www.mozilla.org/MPL/
-
-   Software distributed under the License is distributed on an "AS IS" basis,
-   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-   for the specific language governing rights and limitations under the
-   License.
-
-   The Original Code is mozilla.org code.
-
-   The Initial Developer of the Original Code is
-   Netscape Communications Corporation.
-   Portions created by the Initial Developer are Copyright (C) 2001
-   the Initial Developer. All Rights Reserved.
-
-   Contributor(s):
-
-   Alternatively, the contents of this file may be used under the terms of
-   either the GNU General Public License Version 2 or later (the "GPL"), or
-   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-   in which case the provisions of the GPL or the LGPL are applicable instead
-   of those above. If you wish to allow use of your version of this file only
-   under the terms of either the GPL or the LGPL, and not to allow others to
-   use your version of this file under the terms of the MPL, indicate your
-   decision by deleting the provisions above and replace them with the notice
-   and other provisions required by the GPL or the LGPL. If you do not delete
-   the provisions above, a recipient may use your version of this file under
-   the terms of any one of the MPL, the GPL or the LGPL.
-
-
   mozilla(url/third_party/mozilla)
 
 
@@ -6886,6 +6007,7 @@
   License for these implementations of WebM shall terminate as of the date
   such litigation is filed.
 
+
   QR-Code-generator
 
   Copyright © 2020 Project Nayuki. (MIT License)
diff --git a/cobalt/content/licenses/platform/evergreen/licenses_cobalt.txt b/cobalt/content/licenses/platform/evergreen/licenses_cobalt.txt
index 9a104c6..e889e54 100644
--- a/cobalt/content/licenses/platform/evergreen/licenses_cobalt.txt
+++ b/cobalt/content/licenses/platform/evergreen/licenses_cobalt.txt
@@ -176,68 +176,6 @@
    limitations under the License.
 
 
-  Fraunhofer FDK AAC Codec Library
-
-  Software License for The Fraunhofer FDK AAC Codec Library for Android
-  © Copyright  1995 - 2013 Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V.
-    All rights reserved.
-  1.    INTRODUCTION
-  The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software that implements
-  the MPEG Advanced Audio Coding ("AAC") encoding and decoding scheme for digital audio.
-  This FDK AAC Codec software is intended to be used on a wide variety of Android devices.
-  AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient general perceptual
-  audio codecs. AAC-ELD is considered the best-performing full-bandwidth communications codec by
-  independent studies and is widely deployed. AAC has been standardized by ISO and IEC as part
-  of the MPEG specifications.
-  Patent licenses for necessary patent claims for the FDK AAC Codec (including those of Fraunhofer)
-  may be obtained through Via Licensing (www.vialicensing.com) or through the respective patent owners
-  individually for the purpose of encoding or decoding bit streams in products that are compliant with
-  the ISO/IEC MPEG audio standards. Please note that most manufacturers of Android devices already license
-  these patent claims through Via Licensing or directly from the patent owners, and therefore FDK AAC Codec
-  software may already be covered under those patent licenses when it is used for those licensed purposes only.
-  Commercially-licensed AAC software libraries, including floating-point versions with enhanced sound quality,
-  are also available from Fraunhofer. Users are encouraged to check the Fraunhofer website for additional
-  applications information and documentation.
-  2.    COPYRIGHT LICENSE
-  Redistribution and use in source and binary forms, with or without modification, are permitted without
-  payment of copyright license fees provided that you satisfy the following conditions:
-  You must retain the complete text of this software license in redistributions of the FDK AAC Codec or
-  your modifications thereto in source code form.
-  You must retain the complete text of this software license in the documentation and/or other materials
-  provided with redistributions of the FDK AAC Codec or your modifications thereto in binary form.
-  You must make available free of charge copies of the complete source code of the FDK AAC Codec and your
-  modifications thereto to recipients of copies in binary form.
-  The name of Fraunhofer may not be used to endorse or promote products derived from this library without
-  prior written permission.
-  You may not charge copyright license fees for anyone to use, copy or distribute the FDK AAC Codec
-  software or your modifications thereto.
-  Your modified versions of the FDK AAC Codec must carry prominent notices stating that you changed the software
-  and the date of any change. For modified versions of the FDK AAC Codec, the term
-  "Fraunhofer FDK AAC Codec Library for Android" must be replaced by the term
-  "Third-Party Modified Version of the Fraunhofer FDK AAC Codec Library for Android."
-  3.    NO PATENT LICENSE
-  NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without limitation the patents of Fraunhofer,
-  ARE GRANTED BY THIS SOFTWARE LICENSE. Fraunhofer provides no warranty of patent non-infringement with
-  respect to this software.
-  You may use this FDK AAC Codec software or modifications thereto only for purposes that are authorized
-  by appropriate patent licenses.
-  4.    DISCLAIMER
-  This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright holders and contributors
-  "AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, including but not limited to the implied warranties
-  of merchantability and fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
-  CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary, or consequential damages,
-  including but not limited to procurement of substitute goods or services; loss of use, data, or profits,
-  or business interruption, however caused and on any theory of liability, whether in contract, strict
-  liability, or tort (including negligence), arising in any way out of the use of this software, even if
-  advised of the possibility of such damage.
-  5.    CONTACT INFORMATION
-  Fraunhofer Institute for Integrated Circuits IIS
-  Attention: Audio and Multimedia Departments - FDK AAC LL
-  Am Wolfsmantel 33
-  91058 Erlangen, Germany
-  www.iis.fraunhofer.de/amm
-  amm-info@iis.fraunhofer.de
-
 
   Chromium
 
@@ -1729,885 +1667,6 @@
 
 
 
-  nss
-
-
-    NSS is available under the Mozilla Public License, version 2, a copy of which
-  is below.
-
-  Note on GPL Compatibility
-  -------------------------
-
-  The MPL 2, section 3.3, permits you to combine NSS with code under the GNU
-  General Public License (GPL) version 2, or any later version of that
-  license, to make a Larger Work, and distribute the result under the GPL.
-  The only condition is that you must also make NSS, and any changes you
-  have made to it, available to recipients under the terms of the MPL 2 also.
-
-  Anyone who receives the combined code from you does not have to continue
-  to dual licence in this way, and may, if they wish, distribute under the
-  terms of either of the two licences - either the MPL alone or the GPL
-  alone. However, we discourage people from distributing copies of NSS under
-  the GPL alone, because it means that any improvements they make cannot be
-  reincorporated into the main version of NSS. There is never a need to do
-  this for license compatibility reasons.
-
-  Note on LGPL Compatibility
-  --------------------------
-
-  The above also applies to combining MPLed code in a single library with
-  code under the GNU Lesser General Public License (LGPL) version 2.1, or
-  any later version of that license. If the LGPLed code and the MPLed code
-  are not in the same library, then the copyleft coverage of the two
-  licences does not overlap, so no issues arise.
-
-
-  Mozilla Public License Version 2.0
-  ==================================
-
-  1. Definitions
-  --------------
-
-  1.1. "Contributor"
-      means each individual or legal entity that creates, contributes to
-      the creation of, or owns Covered Software.
-
-  1.2. "Contributor Version"
-      means the combination of the Contributions of others (if any) used
-      by a Contributor and that particular Contributor's Contribution.
-
-  1.3. "Contribution"
-      means Covered Software of a particular Contributor.
-
-  1.4. "Covered Software"
-      means Source Code Form to which the initial Contributor has attached
-      the notice in Exhibit A, the Executable Form of such Source Code
-      Form, and Modifications of such Source Code Form, in each case
-      including portions thereof.
-
-  1.5. "Incompatible With Secondary Licenses"
-      means
-
-      (a) that the initial Contributor has attached the notice described
-          in Exhibit B to the Covered Software; or
-
-      (b) that the Covered Software was made available under the terms of
-          version 1.1 or earlier of the License, but not also under the
-          terms of a Secondary License.
-
-  1.6. "Executable Form"
-      means any form of the work other than Source Code Form.
-
-  1.7. "Larger Work"
-      means a work that combines Covered Software with other material, in
-      a separate file or files, that is not Covered Software.
-
-  1.8. "License"
-      means this document.
-
-  1.9. "Licensable"
-      means having the right to grant, to the maximum extent possible,
-      whether at the time of the initial grant or subsequently, any and
-      all of the rights conveyed by this License.
-
-  1.10. "Modifications"
-      means any of the following:
-
-      (a) any file in Source Code Form that results from an addition to,
-          deletion from, or modification of the contents of Covered
-          Software; or
-
-      (b) any new file in Source Code Form that contains any Covered
-          Software.
-
-  1.11. "Patent Claims" of a Contributor
-      means any patent claim(s), including without limitation, method,
-      process, and apparatus claims, in any patent Licensable by such
-      Contributor that would be infringed, but for the grant of the
-      License, by the making, using, selling, offering for sale, having
-      made, import, or transfer of either its Contributions or its
-      Contributor Version.
-
-  1.12. "Secondary License"
-      means either the GNU General Public License, Version 2.0, the GNU
-      Lesser General Public License, Version 2.1, the GNU Affero General
-      Public License, Version 3.0, or any later versions of those
-      licenses.
-
-  1.13. "Source Code Form"
-      means the form of the work preferred for making modifications.
-
-  1.14. "You" (or "Your")
-      means an individual or a legal entity exercising rights under this
-      License. For legal entities, "You" includes any entity that
-      controls, is controlled by, or is under common control with You. For
-      purposes of this definition, "control" means (a) the power, direct
-      or indirect, to cause the direction or management of such entity,
-      whether by contract or otherwise, or (b) ownership of more than
-      fifty percent (50%) of the outstanding shares or beneficial
-      ownership of such entity.
-
-  2. License Grants and Conditions
-  --------------------------------
-
-  2.1. Grants
-
-  Each Contributor hereby grants You a world-wide, royalty-free,
-  non-exclusive license:
-
-  (a) under intellectual property rights (other than patent or trademark)
-      Licensable by such Contributor to use, reproduce, make available,
-      modify, display, perform, distribute, and otherwise exploit its
-      Contributions, either on an unmodified basis, with Modifications, or
-      as part of a Larger Work; and
-
-  (b) under Patent Claims of such Contributor to make, use, sell, offer
-      for sale, have made, import, and otherwise transfer either its
-      Contributions or its Contributor Version.
-
-  2.2. Effective Date
-
-  The licenses granted in Section 2.1 with respect to any Contribution
-  become effective for each Contribution on the date the Contributor first
-  distributes such Contribution.
-
-  2.3. Limitations on Grant Scope
-
-  The licenses granted in this Section 2 are the only rights granted under
-  this License. No additional rights or licenses will be implied from the
-  distribution or licensing of Covered Software under this License.
-  Notwithstanding Section 2.1(b) above, no patent license is granted by a
-  Contributor:
-
-  (a) for any code that a Contributor has removed from Covered Software;
-      or
-
-  (b) for infringements caused by: (i) Your and any other third party's
-      modifications of Covered Software, or (ii) the combination of its
-      Contributions with other software (except as part of its Contributor
-      Version); or
-
-  (c) under Patent Claims infringed by Covered Software in the absence of
-      its Contributions.
-
-  This License does not grant any rights in the trademarks, service marks,
-  or logos of any Contributor (except as may be necessary to comply with
-  the notice requirements in Section 3.4).
-
-  2.4. Subsequent Licenses
-
-  No Contributor makes additional grants as a result of Your choice to
-  distribute the Covered Software under a subsequent version of this
-  License (see Section 10.2) or under the terms of a Secondary License (if
-  permitted under the terms of Section 3.3).
-
-  2.5. Representation
-
-  Each Contributor represents that the Contributor believes its
-  Contributions are its original creation(s) or it has sufficient rights
-  to grant the rights to its Contributions conveyed by this License.
-
-  2.6. Fair Use
-
-  This License is not intended to limit any rights You have under
-  applicable copyright doctrines of fair use, fair dealing, or other
-  equivalents.
-
-  2.7. Conditions
-
-  Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-  in Section 2.1.
-
-  3. Responsibilities
-  -------------------
-
-  3.1. Distribution of Source Form
-
-  All distribution of Covered Software in Source Code Form, including any
-  Modifications that You create or to which You contribute, must be under
-  the terms of this License. You must inform recipients that the Source
-  Code Form of the Covered Software is governed by the terms of this
-  License, and how they can obtain a copy of this License. You may not
-  attempt to alter or restrict the recipients' rights in the Source Code
-  Form.
-
-  3.2. Distribution of Executable Form
-
-  If You distribute Covered Software in Executable Form then:
-
-  (a) such Covered Software must also be made available in Source Code
-      Form, as described in Section 3.1, and You must inform recipients of
-      the Executable Form how they can obtain a copy of such Source Code
-      Form by reasonable means in a timely manner, at a charge no more
-      than the cost of distribution to the recipient; and
-
-  (b) You may distribute such Executable Form under the terms of this
-      License, or sublicense it under different terms, provided that the
-      license for the Executable Form does not attempt to limit or alter
-      the recipients' rights in the Source Code Form under this License.
-
-  3.3. Distribution of a Larger Work
-
-  You may create and distribute a Larger Work under terms of Your choice,
-  provided that You also comply with the requirements of this License for
-  the Covered Software. If the Larger Work is a combination of Covered
-  Software with a work governed by one or more Secondary Licenses, and the
-  Covered Software is not Incompatible With Secondary Licenses, this
-  License permits You to additionally distribute such Covered Software
-  under the terms of such Secondary License(s), so that the recipient of
-  the Larger Work may, at their option, further distribute the Covered
-  Software under the terms of either this License or such Secondary
-  License(s).
-
-  3.4. Notices
-
-  You may not remove or alter the substance of any license notices
-  (including copyright notices, patent notices, disclaimers of warranty,
-  or limitations of liability) contained within the Source Code Form of
-  the Covered Software, except that You may alter any license notices to
-  the extent required to remedy known factual inaccuracies.
-
-  3.5. Application of Additional Terms
-
-  You may choose to offer, and to charge a fee for, warranty, support,
-  indemnity or liability obligations to one or more recipients of Covered
-  Software. However, You may do so only on Your own behalf, and not on
-  behalf of any Contributor. You must make it absolutely clear that any
-  such warranty, support, indemnity, or liability obligation is offered by
-  You alone, and You hereby agree to indemnify every Contributor for any
-  liability incurred by such Contributor as a result of warranty, support,
-  indemnity or liability terms You offer. You may include additional
-  disclaimers of warranty and limitations of liability specific to any
-  jurisdiction.
-
-  4. Inability to Comply Due to Statute or Regulation
-  ---------------------------------------------------
-
-  If it is impossible for You to comply with any of the terms of this
-  License with respect to some or all of the Covered Software due to
-  statute, judicial order, or regulation then You must: (a) comply with
-  the terms of this License to the maximum extent possible; and (b)
-  describe the limitations and the code they affect. Such description must
-  be placed in a text file included with all distributions of the Covered
-  Software under this License. Except to the extent prohibited by statute
-  or regulation, such description must be sufficiently detailed for a
-  recipient of ordinary skill to be able to understand it.
-
-  5. Termination
-  --------------
-
-  5.1. The rights granted under this License will terminate automatically
-  if You fail to comply with any of its terms. However, if You become
-  compliant, then the rights granted under this License from a particular
-  Contributor are reinstated (a) provisionally, unless and until such
-  Contributor explicitly and finally terminates Your grants, and (b) on an
-  ongoing basis, if such Contributor fails to notify You of the
-  non-compliance by some reasonable means prior to 60 days after You have
-  come back into compliance. Moreover, Your grants from a particular
-  Contributor are reinstated on an ongoing basis if such Contributor
-  notifies You of the non-compliance by some reasonable means, this is the
-  first time You have received notice of non-compliance with this License
-  from such Contributor, and You become compliant prior to 30 days after
-  Your receipt of the notice.
-
-  5.2. If You initiate litigation against any entity by asserting a patent
-  infringement claim (excluding declaratory judgment actions,
-  counter-claims, and cross-claims) alleging that a Contributor Version
-  directly or indirectly infringes any patent, then the rights granted to
-  You by any and all Contributors for the Covered Software under Section
-  2.1 of this License shall terminate.
-
-  5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-  end user license agreements (excluding distributors and resellers) which
-  have been validly granted by You or Your distributors under this License
-  prior to termination shall survive termination.
-
-  ************************************************************************
-  *                                                                      *
-  *  6. Disclaimer of Warranty                                           *
-  *  -------------------------                                           *
-  *                                                                      *
-  *  Covered Software is provided under this License on an "as is"       *
-  *  basis, without warranty of any kind, either expressed, implied, or  *
-  *  statutory, including, without limitation, warranties that the       *
-  *  Covered Software is free of defects, merchantable, fit for a        *
-  *  particular purpose or non-infringing. The entire risk as to the     *
-  *  quality and performance of the Covered Software is with You.        *
-  *  Should any Covered Software prove defective in any respect, You     *
-  *  (not any Contributor) assume the cost of any necessary servicing,   *
-  *  repair, or correction. This disclaimer of warranty constitutes an   *
-  *  essential part of this License. No use of any Covered Software is   *
-  *  authorized under this License except under this disclaimer.         *
-  *                                                                      *
-  ************************************************************************
-
-  ************************************************************************
-  *                                                                      *
-  *  7. Limitation of Liability                                          *
-  *  --------------------------                                          *
-  *                                                                      *
-  *  Under no circumstances and under no legal theory, whether tort      *
-  *  (including negligence), contract, or otherwise, shall any           *
-  *  Contributor, or anyone who distributes Covered Software as          *
-  *  permitted above, be liable to You for any direct, indirect,         *
-  *  special, incidental, or consequential damages of any character      *
-  *  including, without limitation, damages for lost profits, loss of    *
-  *  goodwill, work stoppage, computer failure or malfunction, or any    *
-  *  and all other commercial damages or losses, even if such party      *
-  *  shall have been informed of the possibility of such damages. This   *
-  *  limitation of liability shall not apply to liability for death or   *
-  *  personal injury resulting from such party's negligence to the       *
-  *  extent applicable law prohibits such limitation. Some               *
-  *  jurisdictions do not allow the exclusion or limitation of           *
-  *  incidental or consequential damages, so this exclusion and          *
-  *  limitation may not apply to You.                                    *
-  *                                                                      *
-  ************************************************************************
-
-  8. Litigation
-  -------------
-
-  Any litigation relating to this License may be brought only in the
-  courts of a jurisdiction where the defendant maintains its principal
-  place of business and such litigation shall be governed by laws of that
-  jurisdiction, without reference to its conflict-of-law provisions.
-  Nothing in this Section shall prevent a party's ability to bring
-  cross-claims or counter-claims.
-
-  9. Miscellaneous
-  ----------------
-
-  This License represents the complete agreement concerning the subject
-  matter hereof. If any provision of this License is held to be
-  unenforceable, such provision shall be reformed only to the extent
-  necessary to make it enforceable. Any law or regulation which provides
-  that the language of a contract shall be construed against the drafter
-  shall not be used to construe this License against a Contributor.
-
-  10. Versions of the License
-  ---------------------------
-
-  10.1. New Versions
-
-  Mozilla Foundation is the license steward. Except as provided in Section
-  10.3, no one other than the license steward has the right to modify or
-  publish new versions of this License. Each version will be given a
-  distinguishing version number.
-
-  10.2. Effect of New Versions
-
-  You may distribute the Covered Software under the terms of the version
-  of the License under which You originally received the Covered Software,
-  or under the terms of any subsequent version published by the license
-  steward.
-
-  10.3. Modified Versions
-
-  If you create software not governed by this License, and you want to
-  create a new license for such software, you may create and use a
-  modified version of this License if you rename the license and remove
-  any references to the name of the license steward (except to note that
-  such modified license differs from this License).
-
-  10.4. Distributing Source Code Form that is Incompatible With Secondary
-  Licenses
-
-  If You choose to distribute Source Code Form that is Incompatible With
-  Secondary Licenses under the terms of this version of the License, the
-  notice described in Exhibit B of this License must be attached.
-
-  Exhibit A - Source Code Form License Notice
-  -------------------------------------------
-
-    This Source Code Form is subject to the terms of the Mozilla Public
-    License, v. 2.0. If a copy of the MPL was not distributed with this
-    file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-  If it is not possible or desirable to put the notice in a particular
-  file, then You may include the notice in a location (such as a LICENSE
-  file in a relevant directory) where a recipient would be likely to look
-  for such a notice.
-
-  You may add additional accurate notices of copyright ownership.
-
-  Exhibit B - "Incompatible With Secondary Licenses" Notice
-  ---------------------------------------------------------
-
-    This Source Code Form is "Incompatible With Secondary Licenses", as
-    defined by the Mozilla Public License, v. 2.0.
-
-
-
-  mozilla_security_manager
-
-                             MOZILLA PUBLIC LICENSE
-                                Version 1.1
-
-                              ---------------
-
-  1. Definitions.
-
-     1.0.1. "Commercial Use" means distribution or otherwise making the
-     Covered Code available to a third party.
-
-     1.1. "Contributor" means each entity that creates or contributes to
-     the creation of Modifications.
-
-     1.2. "Contributor Version" means the combination of the Original
-     Code, prior Modifications used by a Contributor, and the Modifications
-     made by that particular Contributor.
-
-     1.3. "Covered Code" means the Original Code or Modifications or the
-     combination of the Original Code and Modifications, in each case
-     including portions thereof.
-
-     1.4. "Electronic Distribution Mechanism" means a mechanism generally
-     accepted in the software development community for the electronic
-     transfer of data.
-
-     1.5. "Executable" means Covered Code in any form other than Source
-     Code.
-
-     1.6. "Initial Developer" means the individual or entity identified
-     as the Initial Developer in the Source Code notice required by Exhibit
-     A.
-
-     1.7. "Larger Work" means a work which combines Covered Code or
-     portions thereof with code not governed by the terms of this License.
-
-     1.8. "License" means this document.
-
-     1.8.1. "Licensable" means having the right to grant, to the maximum
-     extent possible, whether at the time of the initial grant or
-     subsequently acquired, any and all of the rights conveyed herein.
-
-     1.9. "Modifications" means any addition to or deletion from the
-     substance or structure of either the Original Code or any previous
-     Modifications. When Covered Code is released as a series of files, a
-     Modification is:
-          A. Any addition to or deletion from the contents of a file
-          containing Original Code or previous Modifications.
-
-          B. Any new file that contains any part of the Original Code or
-          previous Modifications.
-
-     1.10. "Original Code" means Source Code of computer software code
-     which is described in the Source Code notice required by Exhibit A as
-     Original Code, and which, at the time of its release under this
-     License is not already Covered Code governed by this License.
-
-     1.10.1. "Patent Claims" means any patent claim(s), now owned or
-     hereafter acquired, including without limitation,  method, process,
-     and apparatus claims, in any patent Licensable by grantor.
-
-     1.11. "Source Code" means the preferred form of the Covered Code for
-     making modifications to it, including all modules it contains, plus
-     any associated interface definition files, scripts used to control
-     compilation and installation of an Executable, or source code
-     differential comparisons against either the Original Code or another
-     well known, available Covered Code of the Contributor's choice. The
-     Source Code can be in a compressed or archival form, provided the
-     appropriate decompression or de-archiving software is widely available
-     for no charge.
-
-     1.12. "You" (or "Your")  means an individual or a legal entity
-     exercising rights under, and complying with all of the terms of, this
-     License or a future version of this License issued under Section 6.1.
-     For legal entities, "You" includes any entity which controls, is
-     controlled by, or is under common control with You. For purposes of
-     this definition, "control" means (a) the power, direct or indirect,
-     to cause the direction or management of such entity, whether by
-     contract or otherwise, or (b) ownership of more than fifty percent
-     (50%) of the outstanding shares or beneficial ownership of such
-     entity.
-
-  2. Source Code License.
-
-     2.1. The Initial Developer Grant.
-     The Initial Developer hereby grants You a world-wide, royalty-free,
-     non-exclusive license, subject to third party intellectual property
-     claims:
-          (a)  under intellectual property rights (other than patent or
-          trademark) Licensable by Initial Developer to use, reproduce,
-          modify, display, perform, sublicense and distribute the Original
-          Code (or portions thereof) with or without Modifications, and/or
-          as part of a Larger Work; and
-
-          (b) under Patents Claims infringed by the making, using or
-          selling of Original Code, to make, have made, use, practice,
-          sell, and offer for sale, and/or otherwise dispose of the
-          Original Code (or portions thereof).
-
-          (c) the licenses granted in this Section 2.1(a) and (b) are
-          effective on the date Initial Developer first distributes
-          Original Code under the terms of this License.
-
-          (d) Notwithstanding Section 2.1(b) above, no patent license is
-          granted: 1) for code that You delete from the Original Code; 2)
-          separate from the Original Code;  or 3) for infringements caused
-          by: i) the modification of the Original Code or ii) the
-          combination of the Original Code with other software or devices.
-
-     2.2. Contributor Grant.
-     Subject to third party intellectual property claims, each Contributor
-     hereby grants You a world-wide, royalty-free, non-exclusive license
-
-          (a)  under intellectual property rights (other than patent or
-          trademark) Licensable by Contributor, to use, reproduce, modify,
-          display, perform, sublicense and distribute the Modifications
-          created by such Contributor (or portions thereof) either on an
-          unmodified basis, with other Modifications, as Covered Code
-          and/or as part of a Larger Work; and
-
-          (b) under Patent Claims infringed by the making, using, or
-          selling of  Modifications made by that Contributor either alone
-          and/or in combination with its Contributor Version (or portions
-          of such combination), to make, use, sell, offer for sale, have
-          made, and/or otherwise dispose of: 1) Modifications made by that
-          Contributor (or portions thereof); and 2) the combination of
-          Modifications made by that Contributor with its Contributor
-          Version (or portions of such combination).
-
-          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
-          effective on the date Contributor first makes Commercial Use of
-          the Covered Code.
-
-          (d)    Notwithstanding Section 2.2(b) above, no patent license is
-          granted: 1) for any code that Contributor has deleted from the
-          Contributor Version; 2)  separate from the Contributor Version;
-          3)  for infringements caused by: i) third party modifications of
-          Contributor Version or ii)  the combination of Modifications made
-          by that Contributor with other software  (except as part of the
-          Contributor Version) or other devices; or 4) under Patent Claims
-          infringed by Covered Code in the absence of Modifications made by
-          that Contributor.
-
-  3. Distribution Obligations.
-
-     3.1. Application of License.
-     The Modifications which You create or to which You contribute are
-     governed by the terms of this License, including without limitation
-     Section 2.2. The Source Code version of Covered Code may be
-     distributed only under the terms of this License or a future version
-     of this License released under Section 6.1, and You must include a
-     copy of this License with every copy of the Source Code You
-     distribute. You may not offer or impose any terms on any Source Code
-     version that alters or restricts the applicable version of this
-     License or the recipients' rights hereunder. However, You may include
-     an additional document offering the additional rights described in
-     Section 3.5.
-
-     3.2. Availability of Source Code.
-     Any Modification which You create or to which You contribute must be
-     made available in Source Code form under the terms of this License
-     either on the same media as an Executable version or via an accepted
-     Electronic Distribution Mechanism to anyone to whom you made an
-     Executable version available; and if made available via Electronic
-     Distribution Mechanism, must remain available for at least twelve (12)
-     months after the date it initially became available, or at least six
-     (6) months after a subsequent version of that particular Modification
-     has been made available to such recipients. You are responsible for
-     ensuring that the Source Code version remains available even if the
-     Electronic Distribution Mechanism is maintained by a third party.
-
-     3.3. Description of Modifications.
-     You must cause all Covered Code to which You contribute to contain a
-     file documenting the changes You made to create that Covered Code and
-     the date of any change. You must include a prominent statement that
-     the Modification is derived, directly or indirectly, from Original
-     Code provided by the Initial Developer and including the name of the
-     Initial Developer in (a) the Source Code, and (b) in any notice in an
-     Executable version or related documentation in which You describe the
-     origin or ownership of the Covered Code.
-
-     3.4. Intellectual Property Matters
-          (a) Third Party Claims.
-          If Contributor has knowledge that a license under a third party's
-          intellectual property rights is required to exercise the rights
-          granted by such Contributor under Sections 2.1 or 2.2,
-          Contributor must include a text file with the Source Code
-          distribution titled "LEGAL" which describes the claim and the
-          party making the claim in sufficient detail that a recipient will
-          know whom to contact. If Contributor obtains such knowledge after
-          the Modification is made available as described in Section 3.2,
-          Contributor shall promptly modify the LEGAL file in all copies
-          Contributor makes available thereafter and shall take other steps
-          (such as notifying appropriate mailing lists or newsgroups)
-          reasonably calculated to inform those who received the Covered
-          Code that new knowledge has been obtained.
-
-          (b) Contributor APIs.
-          If Contributor's Modifications include an application programming
-          interface and Contributor has knowledge of patent licenses which
-          are reasonably necessary to implement that API, Contributor must
-          also include this information in the LEGAL file.
-
-               (c)    Representations.
-          Contributor represents that, except as disclosed pursuant to
-          Section 3.4(a) above, Contributor believes that Contributor's
-          Modifications are Contributor's original creation(s) and/or
-          Contributor has sufficient rights to grant the rights conveyed by
-          this License.
-
-     3.5. Required Notices.
-     You must duplicate the notice in Exhibit A in each file of the Source
-     Code.  If it is not possible to put such notice in a particular Source
-     Code file due to its structure, then You must include such notice in a
-     location (such as a relevant directory) where a user would be likely
-     to look for such a notice.  If You created one or more Modification(s)
-     You may add your name as a Contributor to the notice described in
-     Exhibit A.  You must also duplicate this License in any documentation
-     for the Source Code where You describe recipients' rights or ownership
-     rights relating to Covered Code.  You may choose to offer, and to
-     charge a fee for, warranty, support, indemnity or liability
-     obligations to one or more recipients of Covered Code. However, You
-     may do so only on Your own behalf, and not on behalf of the Initial
-     Developer or any Contributor. You must make it absolutely clear than
-     any such warranty, support, indemnity or liability obligation is
-     offered by You alone, and You hereby agree to indemnify the Initial
-     Developer and every Contributor for any liability incurred by the
-     Initial Developer or such Contributor as a result of warranty,
-     support, indemnity or liability terms You offer.
-
-     3.6. Distribution of Executable Versions.
-     You may distribute Covered Code in Executable form only if the
-     requirements of Section 3.1-3.5 have been met for that Covered Code,
-     and if You include a notice stating that the Source Code version of
-     the Covered Code is available under the terms of this License,
-     including a description of how and where You have fulfilled the
-     obligations of Section 3.2. The notice must be conspicuously included
-     in any notice in an Executable version, related documentation or
-     collateral in which You describe recipients' rights relating to the
-     Covered Code. You may distribute the Executable version of Covered
-     Code or ownership rights under a license of Your choice, which may
-     contain terms different from this License, provided that You are in
-     compliance with the terms of this License and that the license for the
-     Executable version does not attempt to limit or alter the recipient's
-     rights in the Source Code version from the rights set forth in this
-     License. If You distribute the Executable version under a different
-     license You must make it absolutely clear that any terms which differ
-     from this License are offered by You alone, not by the Initial
-     Developer or any Contributor. You hereby agree to indemnify the
-     Initial Developer and every Contributor for any liability incurred by
-     the Initial Developer or such Contributor as a result of any such
-     terms You offer.
-
-     3.7. Larger Works.
-     You may create a Larger Work by combining Covered Code with other code
-     not governed by the terms of this License and distribute the Larger
-     Work as a single product. In such a case, You must make sure the
-     requirements of this License are fulfilled for the Covered Code.
-
-  4. Inability to Comply Due to Statute or Regulation.
-
-     If it is impossible for You to comply with any of the terms of this
-     License with respect to some or all of the Covered Code due to
-     statute, judicial order, or regulation then You must: (a) comply with
-     the terms of this License to the maximum extent possible; and (b)
-     describe the limitations and the code they affect. Such description
-     must be included in the LEGAL file described in Section 3.4 and must
-     be included with all distributions of the Source Code. Except to the
-     extent prohibited by statute or regulation, such description must be
-     sufficiently detailed for a recipient of ordinary skill to be able to
-     understand it.
-
-  5. Application of this License.
-
-     This License applies to code to which the Initial Developer has
-     attached the notice in Exhibit A and to related Covered Code.
-
-  6. Versions of the License.
-
-     6.1. New Versions.
-     Netscape Communications Corporation ("Netscape") may publish revised
-     and/or new versions of the License from time to time. Each version
-     will be given a distinguishing version number.
-
-     6.2. Effect of New Versions.
-     Once Covered Code has been published under a particular version of the
-     License, You may always continue to use it under the terms of that
-     version. You may also choose to use such Covered Code under the terms
-     of any subsequent version of the License published by Netscape. No one
-     other than Netscape has the right to modify the terms applicable to
-     Covered Code created under this License.
-
-     6.3. Derivative Works.
-     If You create or use a modified version of this License (which you may
-     only do in order to apply it to code which is not already Covered Code
-     governed by this License), You must (a) rename Your license so that
-     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
-     "MPL", "NPL" or any confusingly similar phrase do not appear in your
-     license (except to note that your license differs from this License)
-     and (b) otherwise make it clear that Your version of the license
-     contains terms which differ from the Mozilla Public License and
-     Netscape Public License. (Filling in the name of the Initial
-     Developer, Original Code or Contributor in the notice described in
-     Exhibit A shall not of themselves be deemed to be modifications of
-     this License.)
-
-  7. DISCLAIMER OF WARRANTY.
-
-     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
-     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
-     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
-     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
-     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
-     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
-     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
-     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
-     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
-     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
-
-  8. TERMINATION.
-
-     8.1.  This License and the rights granted hereunder will terminate
-     automatically if You fail to comply with terms herein and fail to cure
-     such breach within 30 days of becoming aware of the breach. All
-     sublicenses to the Covered Code which are properly granted shall
-     survive any termination of this License. Provisions which, by their
-     nature, must remain in effect beyond the termination of this License
-     shall survive.
-
-     8.2.  If You initiate litigation by asserting a patent infringement
-     claim (excluding declatory judgment actions) against Initial Developer
-     or a Contributor (the Initial Developer or Contributor against whom
-     You file such action is referred to as "Participant")  alleging that:
-
-     (a)  such Participant's Contributor Version directly or indirectly
-     infringes any patent, then any and all rights granted by such
-     Participant to You under Sections 2.1 and/or 2.2 of this License
-     shall, upon 60 days notice from Participant terminate prospectively,
-     unless if within 60 days after receipt of notice You either: (i)
-     agree in writing to pay Participant a mutually agreeable reasonable
-     royalty for Your past and future use of Modifications made by such
-     Participant, or (ii) withdraw Your litigation claim with respect to
-     the Contributor Version against such Participant.  If within 60 days
-     of notice, a reasonable royalty and payment arrangement are not
-     mutually agreed upon in writing by the parties or the litigation claim
-     is not withdrawn, the rights granted by Participant to You under
-     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
-     the 60 day notice period specified above.
-
-     (b)  any software, hardware, or device, other than such Participant's
-     Contributor Version, directly or indirectly infringes any patent, then
-     any rights granted to You by such Participant under Sections 2.1(b)
-     and 2.2(b) are revoked effective as of the date You first made, used,
-     sold, distributed, or had made, Modifications made by that
-     Participant.
-
-     8.3.  If You assert a patent infringement claim against Participant
-     alleging that such Participant's Contributor Version directly or
-     indirectly infringes any patent where such claim is resolved (such as
-     by license or settlement) prior to the initiation of patent
-     infringement litigation, then the reasonable value of the licenses
-     granted by such Participant under Sections 2.1 or 2.2 shall be taken
-     into account in determining the amount or value of any payment or
-     license.
-
-     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
-     all end user license agreements (excluding distributors and resellers)
-     which have been validly granted by You or any distributor hereunder
-     prior to termination shall survive termination.
-
-  9. LIMITATION OF LIABILITY.
-
-     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
-     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
-     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
-     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
-     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
-     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
-     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
-     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
-     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
-     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
-     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
-     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
-     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
-     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
-
-  10. U.S. GOVERNMENT END USERS.
-
-     The Covered Code is a "commercial item," as that term is defined in
-     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
-     software" and "commercial computer software documentation," as such
-     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
-     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
-     all U.S. Government End Users acquire Covered Code with only those
-     rights set forth herein.
-
-  11. MISCELLANEOUS.
-
-     This License represents the complete agreement concerning subject
-     matter hereof. If any provision of this License is held to be
-     unenforceable, such provision shall be reformed only to the extent
-     necessary to make it enforceable. This License shall be governed by
-     California law provisions (except to the extent applicable law, if
-     any, provides otherwise), excluding its conflict-of-law provisions.
-     With respect to disputes in which at least one party is a citizen of,
-     or an entity chartered or registered to do business in the United
-     States of America, any litigation relating to this License shall be
-     subject to the jurisdiction of the Federal Courts of the Northern
-     District of California, with venue lying in Santa Clara County,
-     California, with the losing party responsible for costs, including
-     without limitation, court costs and reasonable attorneys' fees and
-     expenses. The application of the United Nations Convention on
-     Contracts for the International Sale of Goods is expressly excluded.
-     Any law or regulation which provides that the language of a contract
-     shall be construed against the drafter shall not apply to this
-     License.
-
-  12. RESPONSIBILITY FOR CLAIMS.
-
-     As between Initial Developer and the Contributors, each party is
-     responsible for claims and damages arising, directly or indirectly,
-     out of its utilization of rights under this License and You agree to
-     work with Initial Developer and Contributors to distribute such
-     responsibility on an equitable basis. Nothing herein is intended or
-     shall be deemed to constitute any admission of liability.
-
-  13. MULTIPLE-LICENSED CODE.
-
-     Initial Developer may designate portions of the Covered Code as
-     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
-     Developer permits you to utilize portions of the Covered Code under
-     Your choice of the NPL or the alternative licenses, if any, specified
-     by the Initial Developer in the file described in Exhibit A.
-
-  EXHIBIT A -Mozilla Public License.
-
-   The contents of this file are subject to the Mozilla Public License Version
-   1.1 (the "License"); you may not use this file except in compliance with
-   the License. You may obtain a copy of the License at
-   http://www.mozilla.org/MPL/
-
-   Software distributed under the License is distributed on an "AS IS" basis,
-   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
-   for the specific language governing rights and limitations under the
-   License.
-
-   The Original Code is mozilla.org code.
-
-   The Initial Developer of the Original Code is
-   Netscape Communications Corporation.
-   Portions created by the Initial Developer are Copyright (C) 2001
-   the Initial Developer. All Rights Reserved.
-
-   Contributor(s):
-
-   Alternatively, the contents of this file may be used under the terms of
-   either the GNU General Public License Version 2 or later (the "GPL"), or
-   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
-   in which case the provisions of the GPL or the LGPL are applicable instead
-   of those above. If you wish to allow use of your version of this file only
-   under the terms of either the GPL or the LGPL, and not to allow others to
-   use your version of this file under the terms of the MPL, indicate your
-   decision by deleting the provisions above and replace them with the notice
-   and other provisions required by the GPL or the LGPL. If you do not delete
-   the provisions above, a recipient may use your version of this file under
-   the terms of any one of the MPL, the GPL or the LGPL.
-
-
   mozilla(url/third_party/mozilla)
 
 
@@ -3423,6 +2482,115 @@
 
 
 
+  libjpeg-turbo
+
+  libjpeg-turbo Licenses libjpeg-turbo is covered by three compatible BSD-style
+  open source licenses:
+
+  The IJG (Independent JPEG Group) License, which is listed in README.ijg
+
+  This license applies to the libjpeg API library and associated programs (any
+  code inherited from libjpeg, and any modifications to that code.)
+
+  The Modified (3-clause) BSD License, which is listed below
+
+  This license covers the TurboJPEG API library and associated programs, as well
+  as the build system.
+
+  The zlib License
+
+  This license is a subset of the other two, and it covers the libjpeg-turbo
+  SIMD extensions.
+
+  Complying with the libjpeg-turbo Licenses This section provides a roll-up of
+  the libjpeg-turbo licensing terms, to the best of our understanding.
+
+  If you are distributing a modified version of the libjpeg-turbo source, then:
+
+  You cannot alter or remove any existing copyright or license notices from the
+  source.
+
+  Origin
+
+  Clause 1 of the IJG License Clause 1 of the Modified BSD License Clauses 1 and
+  3 of the zlib License You must add your own copyright notice to the header of
+  each source file you modified, so others can tell that you modified that file
+  (if there is not an existing copyright header in that file, then you can
+  simply add a notice stating that you modified the file.)
+
+  Origin
+
+  Clause 1 of the IJG License Clause 2 of the zlib License You must include the
+  IJG README file, and you must not alter any of the copyright or license text
+  in that file.
+
+  Origin
+
+  Clause 1 of the IJG License If you are distributing only libjpeg-turbo
+  binaries without the source, or if you are distributing an application that
+  statically links with libjpeg-turbo, then:
+
+  Your product documentation must include a message stating:
+
+  This software is based in part on the work of the Independent JPEG Group.
+
+  Origin
+
+  Clause 2 of the IJG license If your binary distribution includes or uses the
+  TurboJPEG API, then your product documentation must include the text of the
+  Modified BSD License (see below.)
+
+  Origin
+
+  Clause 2 of the Modified BSD License You cannot use the name of the IJG or The
+  libjpeg-turbo Project or the contributors thereof in advertising, publicity,
+  etc.
+
+  Origin
+
+  IJG License Clause 3 of the Modified BSD License The IJG and The libjpeg-turbo
+  Project do not warrant libjpeg-turbo to be free of defects, nor do we accept
+  any liability for undesirable consequences resulting from your use of the
+  software.
+
+  Origin
+
+  IJG License Modified BSD License zlib License The Modified (3-clause) BSD
+  License Copyright (C)2009-2021 D. R. Commander. All Rights Reserved.
+  Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.  Redistributions in binary
+  form must reproduce the above copyright notice, this list of conditions and
+  the following disclaimer in the documentation and/or other materials provided
+  with the distribution.  Neither the name of the libjpeg-turbo Project nor the
+  names of its contributors may be used to endorse or promote products derived
+  from this software without specific prior written permission.  THIS SOFTWARE
+  IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+  EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  Why Three Licenses?  The zlib License could have been used instead of the
+  Modified (3-clause) BSD License, and since the IJG License effectively
+  subsumes the distribution conditions of the zlib License, this would have
+  effectively placed libjpeg-turbo binary distributions under the IJG License.
+  However, the IJG License specifically refers to the Independent JPEG Group and
+  does not extend attribution and endorsement protections to other entities.
+  Thus, it was desirable to choose a license that granted us the same
+  protections for new code that were granted to the IJG for code derived from
+  their software.
+
+
   libpng
 
 
@@ -6731,6 +5899,52 @@
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
+  libwebp
+
+  Copyright (c) 2010, Google Inc. All rights reserved.  Redistribution and use
+  in source and binary forms, with or without modification, are permitted
+  provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * Neither the name of Google nor the names of its contributors may be used
+      to endorse or promote products derived from this software without specific
+      prior written permission.
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
+  HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+  COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  Additional IP Rights Grant (Patents) ------------------------------------
+  "These implementations" means the copyrightable works that implement the
+  WebM codecs distributed by Google as part of the WebM Project.  Google
+  hereby grants to you a perpetual, worldwide, non-exclusive, no-charge,
+  royalty-free, irrevocable (except as stated in this section) patent
+  license to make, have made, use, offer to sell, sell, import, transfer,
+  and otherwise run, modify and propagate the contents of these
+  implementations of WebM, where such license applies only to those patent
+  claims, both currently owned by Google and acquired in the future,
+  licensable by Google that are necessarily infringed by these
+  implementations of WebM. This grant does not include claims that would be
+  infringed only as a consequence of further modification of these
+  implementations. If you or your agent or exclusive licensee institute or
+  order or agree to the institution of patent litigation or any other patent
+  enforcement activity against any entity (including a cross-claim or
+  counterclaim in a lawsuit) alleging that any of these implementations of
+  WebM or any code incorporated within any of these implementations of WebM
+  constitute direct or contributory patent infringement, or inducement of
+  patent infringement, then any patent rights granted to you under this
+  License for these implementations of WebM shall terminate as of the date
+  such litigation is filed.
+
 
   openh264
 
diff --git a/cobalt/content/ssl/certs/5273a94c.0 b/cobalt/content/ssl/certs/5273a94c.0
deleted file mode 100644
index 9748599..0000000
--- a/cobalt/content/ssl/certs/5273a94c.0
+++ /dev/null
@@ -1,36 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV
-BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC
-aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV
-BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1
-Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz
-MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+
-BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp
-em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
-ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY
-B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH
-D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF
-Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo
-q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D
-k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH
-fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut
-dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM
-ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8
-zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
-rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX
-U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6
-Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5
-XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF
-Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR
-HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY
-GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c
-77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3
-+GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK
-vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6
-FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl
-yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P
-AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD
-y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d
-NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA==
------END CERTIFICATE-----
diff --git a/cobalt/csp/content_security_policy.cc b/cobalt/csp/content_security_policy.cc
index ae9e12e..926ec1b 100644
--- a/cobalt/csp/content_security_policy.cc
+++ b/cobalt/csp/content_security_policy.cc
@@ -110,9 +110,6 @@
 
 ResponseHeaders::ResponseHeaders(
     const scoped_refptr<net::HttpResponseHeaders>& response) {
-  if (response == nullptr) {
-    return;
-  }
   response->GetNormalizedHeader("Content-Security-Policy",
                                 &content_security_policy_);
   response->GetNormalizedHeader("Content-Security-Policy-Report-Only",
diff --git a/cobalt/demos/content/partial-audio-frame/partial-audio-frame.js b/cobalt/demos/content/partial-audio-frame/partial-audio-frame.js
index 67f659b..60226bb 100644
--- a/cobalt/demos/content/partial-audio-frame/partial-audio-frame.js
+++ b/cobalt/demos/content/partial-audio-frame/partial-audio-frame.js
@@ -14,9 +14,27 @@
 
 "use strict";
 
-function getFmp4AacData(onDataReady) {
-  const FILE_NAME = 'fmp4-aac-44100-tiny.mp4';
+function getContentType(codec) {
+  if (codec == "aac") {
+    return 'audio/mp4; codecs="mp4a.40.2"';
+  } else if (codec == "opus") {
+    return 'audio/webm; codecs="opus"';
+  } else {
+    throw "Invalid codec: " + codec;
+  }
+}
 
+function getContentFilename(codec) {
+  if (codec == "aac") {
+    return 'fmp4-aac-44100-tiny.mp4';
+  } else if (codec == "opus") {
+    return 'webm-opus-48000-tiny.webm';
+  } else {
+    throw "Invalid codec: " + codec;
+  }
+}
+
+function getFmp4AacData(codec, onDataReady) {
   var xhr = new XMLHttpRequest;
   xhr.responseType = 'arraybuffer';
   xhr.addEventListener('readystatechange', function onreadystatechange() {
@@ -27,7 +45,7 @@
       onDataReady(xhr.response);
     }
   });
-  xhr.open('GET', FILE_NAME, true);
+  xhr.open('GET', getContentFilename(codec), true);
   console.log('Sending request for media segment ...');
   xhr.send();
 }
@@ -112,18 +130,24 @@
 }
 
 function playPartialAudio() {
+  var codec = 'aac';
+  if (window.location.search.indexOf('codec=opus') != -1) {
+    codec = "opus";
+  }
+
   window.setInterval(function() {
     document.getElementById('status').textContent =
-        'currentTime ' + document.getElementById('video').currentTime;
+        'codec: ' + codec + ', currentTime ' + document.getElementById('video').currentTime;
   }, 100);
 
-  getFmp4AacData(function(mediaSegment) {
+  console.log('Playing ' + codec);
+
+  getFmp4AacData(codec, function(mediaSegment) {
     createAndAttachMediaSource(function(event) {
       var mediaSource = event.target;
 
       console.log('Adding SourceBuffer ...');
-      var sourceBuffer =
-          mediaSource.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');
+      var sourceBuffer = mediaSource.addSourceBuffer(getContentType(codec));
 
       appendMediaSegment(mediaSource, sourceBuffer, mediaSegment);
     });
diff --git a/cobalt/demos/content/partial-audio-frame/webm-opus-48000-tiny.webm b/cobalt/demos/content/partial-audio-frame/webm-opus-48000-tiny.webm
new file mode 100644
index 0000000..080e95a
--- /dev/null
+++ b/cobalt/demos/content/partial-audio-frame/webm-opus-48000-tiny.webm
Binary files differ
diff --git a/cobalt/dom/dom_stat_tracker.cc b/cobalt/dom/dom_stat_tracker.cc
index 2846654..4fb57b9 100644
--- a/cobalt/dom/dom_stat_tracker.cc
+++ b/cobalt/dom/dom_stat_tracker.cc
@@ -20,7 +20,7 @@
 namespace dom {
 
 DomStatTracker::DomStatTracker(const std::string& name)
-    : web::StatTracker(name),
+    : web::StatTracker(name, "DOM"),
       count_html_element_(
           base::StringPrintf("Count.%s.DOM.HtmlElement", name.c_str()), 0,
           "Total number of HTML elements."),
diff --git a/cobalt/dom/eme/media_encrypted_event.cc b/cobalt/dom/eme/media_encrypted_event.cc
index 9302890..8752f76 100644
--- a/cobalt/dom/eme/media_encrypted_event.cc
+++ b/cobalt/dom/eme/media_encrypted_event.cc
@@ -15,24 +15,28 @@
 #include "cobalt/dom/eme/media_encrypted_event.h"
 
 #include "cobalt/base/tokens.h"
+#include "cobalt/web/environment_settings_helper.h"
 
 namespace cobalt {
 namespace dom {
 namespace eme {
 
 // See step 5 in https://www.w3.org/TR/encrypted-media/#initdata-encountered.
-MediaEncryptedEvent::MediaEncryptedEvent(const std::string& type)
+MediaEncryptedEvent::MediaEncryptedEvent(
+    script::EnvironmentSettings* environment_settings, const std::string& type)
     : Event(base::Token(type), kNotBubbles, kNotCancelable) {}
 
 // See step 5 in https://www.w3.org/TR/encrypted-media/#initdata-encountered.
 MediaEncryptedEvent::MediaEncryptedEvent(
-    const std::string& type, const MediaEncryptedEventInit& event_init_dict)
+    script::EnvironmentSettings* environment_settings, const std::string& type,
+    const MediaEncryptedEventInit& event_init_dict)
     : Event(base::Token(type), kNotBubbles, kNotCancelable),
       init_data_type_(event_init_dict.init_data_type()) {
   if (event_init_dict.init_data() && !event_init_dict.init_data()->IsNull()) {
+    auto* global_wrappable = web::get_global_wrappable(environment_settings);
     init_data_reference_.reset(
         new script::ScriptValue<script::ArrayBuffer>::Reference(
-            this, *event_init_dict.init_data()));
+            global_wrappable, *event_init_dict.init_data()));
   }
 }
 
diff --git a/cobalt/dom/eme/media_encrypted_event.h b/cobalt/dom/eme/media_encrypted_event.h
index 0f16091..390d6ad 100644
--- a/cobalt/dom/eme/media_encrypted_event.h
+++ b/cobalt/dom/eme/media_encrypted_event.h
@@ -21,6 +21,7 @@
 #include "base/memory/ref_counted.h"
 #include "cobalt/dom/eme/media_encrypted_event_init.h"
 #include "cobalt/script/array_buffer.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/wrappable.h"
 #include "cobalt/web/event.h"
 
@@ -35,8 +36,10 @@
   // Web API: MediaEncryptedEvent
   //
 
-  explicit MediaEncryptedEvent(const std::string& type);
-  MediaEncryptedEvent(const std::string& type,
+  MediaEncryptedEvent(script::EnvironmentSettings* environment_settings,
+                      const std::string& type);
+  MediaEncryptedEvent(script::EnvironmentSettings* environment_settings,
+                      const std::string& type,
                       const MediaEncryptedEventInit& event_init_dict);
 
   const std::string& init_data_type() const { return init_data_type_; }
diff --git a/cobalt/dom/eme/media_encrypted_event.idl b/cobalt/dom/eme/media_encrypted_event.idl
index 67a6acf..4ae1c50 100644
--- a/cobalt/dom/eme/media_encrypted_event.idl
+++ b/cobalt/dom/eme/media_encrypted_event.idl
@@ -14,7 +14,10 @@
 
 // https://www.w3.org/TR/encrypted-media/#mediaencryptedevent
 
-[Constructor(DOMString type, optional MediaEncryptedEventInit eventInitDict)]
+[
+  Constructor(DOMString type, optional MediaEncryptedEventInit eventInitDict),
+  ConstructorCallWith=EnvironmentSettings,
+]
 interface MediaEncryptedEvent : Event {
   readonly attribute DOMString initDataType;
   readonly attribute ArrayBuffer? initData;
diff --git a/cobalt/dom/eme/media_key_session.cc b/cobalt/dom/eme/media_key_session.cc
index c36eff2..a703d66 100644
--- a/cobalt/dom/eme/media_key_session.cc
+++ b/cobalt/dom/eme/media_key_session.cc
@@ -29,6 +29,7 @@
 #include "cobalt/web/context.h"
 #include "cobalt/web/dom_exception.h"
 #include "cobalt/web/environment_settings.h"
+#include "cobalt/web/environment_settings_helper.h"
 
 namespace cobalt {
 namespace dom {
@@ -137,14 +138,17 @@
 
   // 10.9. Use the CDM.
   initiated_by_generate_request_ = true;
+  auto* global_wrappable = web::get_global_wrappable(settings);
   drm_system_session_->GenerateUpdateRequest(
       init_data_type, init_data_buffer, init_data_buffer_size,
       base::Bind(&MediaKeySession::OnSessionUpdateRequestGenerated,
                  base::AsWeakPtr(this), settings,
-                 base::Owned(new VoidPromiseValue::Reference(this, promise))),
+                 base::Owned(new VoidPromiseValue::Reference(global_wrappable,
+                                                             promise))),
       base::Bind(&MediaKeySession::OnSessionUpdateRequestDidNotGenerate,
                  base::AsWeakPtr(this),
-                 base::Owned(new VoidPromiseValue::Reference(this, promise))));
+                 base::Owned(new VoidPromiseValue::Reference(global_wrappable,
+                                                             promise))));
 
   // 11. Return promise.
   return promise;
@@ -152,6 +156,7 @@
 
 // See https://www.w3.org/TR/encrypted-media/#dom-mediakeysession-update.
 script::Handle<script::Promise<void>> MediaKeySession::Update(
+    script::EnvironmentSettings* environment_settings,
     const web::BufferSource& response) {
   TRACE_EVENT0("cobalt::dom::eme", "MediaKeySession::Update()");
   script::Handle<script::Promise<void>> promise =
@@ -183,12 +188,15 @@
   // Sanitation is the responsibility of Starboard implementers.
 
   // 6.7. Use the CDM.
+  auto* global_wrappable = web::get_global_wrappable(environment_settings);
   drm_system_session_->Update(
       response_buffer, response_buffer_size,
       base::Bind(&MediaKeySession::OnSessionUpdated, base::AsWeakPtr(this),
-                 base::Owned(new VoidPromiseValue::Reference(this, promise))),
+                 base::Owned(new VoidPromiseValue::Reference(global_wrappable,
+                                                             promise))),
       base::Bind(&MediaKeySession::OnSessionDidNotUpdate, base::AsWeakPtr(this),
-                 base::Owned(new VoidPromiseValue::Reference(this, promise))));
+                 base::Owned(new VoidPromiseValue::Reference(global_wrappable,
+                                                             promise))));
 
   // 7. Return promise.
   return promise;
diff --git a/cobalt/dom/eme/media_key_session.h b/cobalt/dom/eme/media_key_session.h
index 0de0834..75bbd2d 100644
--- a/cobalt/dom/eme/media_key_session.h
+++ b/cobalt/dom/eme/media_key_session.h
@@ -64,6 +64,7 @@
       script::EnvironmentSettings* settings, const std::string& init_data_type,
       const web::BufferSource& init_data);
   script::Handle<script::Promise<void>> Update(
+      script::EnvironmentSettings* environment_settings,
       const web::BufferSource& response);
   script::Handle<script::Promise<void>> Close();
 
diff --git a/cobalt/dom/eme/media_key_session.idl b/cobalt/dom/eme/media_key_session.idl
index fd5a171..21032b7 100644
--- a/cobalt/dom/eme/media_key_session.idl
+++ b/cobalt/dom/eme/media_key_session.idl
@@ -25,7 +25,7 @@
   [CallWith=EnvironmentSettings] Promise<void> generateRequest(DOMString initDataType, BufferSource initData);
   // TODO: Trivially implement persistent sessions.
   // Promise<boolean> load(DOMString sessionId);
-  Promise<void> update(BufferSource response);
+  [CallWith=EnvironmentSettings] Promise<void> update(BufferSource response);
   Promise<void> close();
   // TODO: Implement |remove|.
   // Promise<void> remove();
diff --git a/cobalt/dom/eme/media_keys.cc b/cobalt/dom/eme/media_keys.cc
index e902445..5a3c25c 100644
--- a/cobalt/dom/eme/media_keys.cc
+++ b/cobalt/dom/eme/media_keys.cc
@@ -22,6 +22,7 @@
 #include "cobalt/dom/eme/media_key_session.h"
 #include "cobalt/web/context.h"
 #include "cobalt/web/dom_exception.h"
+#include "cobalt/web/environment_settings_helper.h"
 
 namespace cobalt {
 namespace dom {
@@ -93,10 +94,12 @@
   // 5.1 Let sanitized certificate be a validated and/or sanitized version of
   //     certificate.
   // 5.2 Use this object's cdm instance to process sanitized certificate.
+  auto* global_wrappable = web::get_global_wrappable(dom_settings_);
   drm_system_->UpdateServerCertificate(
       server_certificate_buffer, server_certificate_buffer_size,
       base::Bind(&MediaKeys::OnServerCertificateUpdated, base::AsWeakPtr(this),
-                 base::Owned(new BoolPromiseValue::Reference(this, promise))));
+                 base::Owned(new BoolPromiseValue::Reference(global_wrappable,
+                                                             promise))));
 
   // 5.3 and 5.4 are pending processing in OnServerCertificateUpdated().
   // 6. Return promise.
diff --git a/cobalt/dom/html_element.cc b/cobalt/dom/html_element.cc
index a30f22b..36ad437 100644
--- a/cobalt/dom/html_element.cc
+++ b/cobalt/dom/html_element.cc
@@ -580,6 +580,7 @@
 
   // 11. Scroll the element to x,scrollTop, with the scroll behavior being
   //     "auto".
+  node_document()->window()->CancelScroll(ui_nav_item_);
   float left, top;
   ui_nav_item_->GetContentOffset(&left, &top);
   ui_nav_item_->SetContentOffset(x, top);
@@ -633,6 +634,7 @@
 
   // 11. Scroll the element to scrollLeft,y, with the scroll behavior being
   //     "auto".
+  node_document()->window()->CancelScroll(ui_nav_item_);
   float left, top;
   ui_nav_item_->GetContentOffset(&left, &top);
   ui_nav_item_->SetContentOffset(left, y);
diff --git a/cobalt/dom/html_media_element.cc b/cobalt/dom/html_media_element.cc
index 5812afb..aaa4675 100644
--- a/cobalt/dom/html_media_element.cc
+++ b/cobalt/dom/html_media_element.cc
@@ -1713,8 +1713,9 @@
                                  init_data_length)
             .GetScriptValue());
   }
-  event_queue_.Enqueue(
-      new eme::MediaEncryptedEvent("encrypted", media_encrypted_event_init));
+  auto* environment_settings = html_element_context()->environment_settings();
+  event_queue_.Enqueue(new eme::MediaEncryptedEvent(
+      environment_settings, "encrypted", media_encrypted_event_init));
 }
 
 void HTMLMediaElement::ClearMediaSource() {
diff --git a/cobalt/dom/html_script_element.cc b/cobalt/dom/html_script_element.cc
index 3634e62..1a1f7ba 100644
--- a/cobalt/dom/html_script_element.cc
+++ b/cobalt/dom/html_script_element.cc
@@ -341,7 +341,7 @@
           base::Bind(
               &loader::FetcherFactory::CreateSecureFetcher,
               base::Unretained(html_element_context()->fetcher_factory()), url_,
-              csp_callback, request_mode_,
+              /*main_resource=*/false, csp_callback, request_mode_,
               document_->location() ? document_->location()->GetOriginAsObject()
                                     : loader::Origin(),
               disk_cache::kUncompiledScript, net::HttpRequestHeaders(),
@@ -443,7 +443,9 @@
             &prevent_gc_until_error_event_dispatch_);
       }
     } break;
-    default: { NOTREACHED(); }
+    default: {
+      NOTREACHED();
+    }
   }
 }
 
diff --git a/cobalt/dom/media_settings.cc b/cobalt/dom/media_settings.cc
index 411889d..2915df3 100644
--- a/cobalt/dom/media_settings.cc
+++ b/cobalt/dom/media_settings.cc
@@ -53,6 +53,12 @@
       LOG(INFO) << name << ": set to " << value;
       return true;
     }
+  } else if (name == "MediaSource.EnableCallingEndedWhenClosed") {
+    if (value == 0 || value == 1) {
+      is_calling_ended_when_closed_enabled_ = value != 0;
+      LOG(INFO) << name << ": set to " << value;
+      return true;
+    }
   } else if (name == "MediaSource.MaxSizeForImmediateJob") {
     if (value >= 0) {
       max_size_for_immediate_job_ = value;
diff --git a/cobalt/dom/media_settings.h b/cobalt/dom/media_settings.h
index a6acb1c..eace022 100644
--- a/cobalt/dom/media_settings.h
+++ b/cobalt/dom/media_settings.h
@@ -35,6 +35,7 @@
       const = 0;
   virtual base::Optional<bool> IsAsynchronousReductionEnabled() const = 0;
   virtual base::Optional<bool> IsAvoidCopyingArrayBufferEnabled() const = 0;
+  virtual base::Optional<bool> IsCallingEndedWhenClosedEnabled() const = 0;
   virtual base::Optional<int> GetMaxSizeForImmediateJob() const = 0;
   virtual base::Optional<int> GetMaxSourceBufferAppendSizeInBytes() const = 0;
 
@@ -71,6 +72,10 @@
     base::AutoLock auto_lock(lock_);
     return is_avoid_copying_array_buffer_enabled_;
   }
+  base::Optional<bool> IsCallingEndedWhenClosedEnabled() const override {
+    base::AutoLock auto_lock(lock_);
+    return is_calling_ended_when_closed_enabled_;
+  }
   base::Optional<int> GetMaxSizeForImmediateJob() const override {
     base::AutoLock auto_lock(lock_);
     return max_size_for_immediate_job_;
@@ -96,6 +101,7 @@
   base::Optional<int> minimum_processor_count_to_offload_algorithm_;
   base::Optional<bool> is_asynchronous_reduction_enabled_;
   base::Optional<bool> is_avoid_copying_array_buffer_enabled_;
+  base::Optional<bool> is_calling_ended_when_closed_enabled_;
   base::Optional<int> max_size_for_immediate_job_;
   base::Optional<int> max_source_buffer_append_size_in_bytes_;
 
diff --git a/cobalt/dom/media_settings_test.cc b/cobalt/dom/media_settings_test.cc
index 9b10847..8850f72 100644
--- a/cobalt/dom/media_settings_test.cc
+++ b/cobalt/dom/media_settings_test.cc
@@ -27,6 +27,7 @@
   EXPECT_FALSE(impl.GetMinimumProcessorCountToOffloadAlgorithm());
   EXPECT_FALSE(impl.IsAsynchronousReductionEnabled());
   EXPECT_FALSE(impl.IsAvoidCopyingArrayBufferEnabled());
+  EXPECT_FALSE(impl.IsCallingEndedWhenClosedEnabled());
   EXPECT_FALSE(impl.GetMaxSizeForImmediateJob());
   EXPECT_FALSE(impl.GetMaxSourceBufferAppendSizeInBytes());
   EXPECT_FALSE(impl.GetMediaElementTimeupdateEventIntervalInMilliseconds());
@@ -40,6 +41,7 @@
       impl.Set("MediaSource.MinimumProcessorCountToOffloadAlgorithm", 101));
   ASSERT_TRUE(impl.Set("MediaSource.EnableAsynchronousReduction", 1));
   ASSERT_TRUE(impl.Set("MediaSource.EnableAvoidCopyingArrayBuffer", 1));
+  ASSERT_TRUE(impl.Set("MediaSource.EnableCallingEndedWhenClosed", 1));
   ASSERT_TRUE(impl.Set("MediaSource.MaxSizeForImmediateJob", 103));
   ASSERT_TRUE(impl.Set("MediaSource.MaxSourceBufferAppendSizeInBytes", 100000));
   ASSERT_TRUE(
@@ -49,6 +51,7 @@
   EXPECT_EQ(impl.GetMinimumProcessorCountToOffloadAlgorithm().value(), 101);
   EXPECT_TRUE(impl.IsAsynchronousReductionEnabled().value());
   EXPECT_TRUE(impl.IsAvoidCopyingArrayBufferEnabled().value());
+  EXPECT_TRUE(impl.IsCallingEndedWhenClosedEnabled().value());
   EXPECT_EQ(impl.GetMaxSizeForImmediateJob().value(), 103);
   EXPECT_EQ(impl.GetMaxSourceBufferAppendSizeInBytes().value(), 100000);
   EXPECT_EQ(impl.GetMediaElementTimeupdateEventIntervalInMilliseconds().value(),
@@ -63,6 +66,7 @@
       impl.Set("MediaSource.MinimumProcessorCountToOffloadAlgorithm", -101));
   ASSERT_FALSE(impl.Set("MediaSource.EnableAsynchronousReduction", 2));
   ASSERT_FALSE(impl.Set("MediaSource.EnableAvoidCopyingArrayBuffer", 2));
+  ASSERT_FALSE(impl.Set("MediaSource.EnableCallingEndedWhenClosed", 2));
   ASSERT_FALSE(impl.Set("MediaSource.MaxSizeForImmediateJob", -103));
   ASSERT_FALSE(impl.Set("MediaSource.MaxSourceBufferAppendSizeInBytes", 0));
   ASSERT_FALSE(
@@ -72,6 +76,7 @@
   EXPECT_FALSE(impl.GetMinimumProcessorCountToOffloadAlgorithm());
   EXPECT_FALSE(impl.IsAsynchronousReductionEnabled());
   EXPECT_FALSE(impl.IsAvoidCopyingArrayBufferEnabled());
+  EXPECT_FALSE(impl.IsCallingEndedWhenClosedEnabled());
   EXPECT_FALSE(impl.GetMaxSizeForImmediateJob());
   EXPECT_FALSE(impl.GetMaxSourceBufferAppendSizeInBytes());
   EXPECT_FALSE(impl.GetMediaElementTimeupdateEventIntervalInMilliseconds());
@@ -85,6 +90,7 @@
       impl.Set("MediaSource.MinimumProcessorCountToOffloadAlgorithm", 0));
   ASSERT_TRUE(impl.Set("MediaSource.EnableAsynchronousReduction", 0));
   ASSERT_TRUE(impl.Set("MediaSource.EnableAvoidCopyingArrayBuffer", 0));
+  ASSERT_TRUE(impl.Set("MediaSource.EnableCallingEndedWhenClosed", 0));
   ASSERT_TRUE(impl.Set("MediaSource.MaxSizeForImmediateJob", 0));
   // O is an invalid value for "MediaSource.MaxSourceBufferAppendSizeInBytes".
   // O is an invalid value for
@@ -94,6 +100,7 @@
   EXPECT_EQ(impl.GetMinimumProcessorCountToOffloadAlgorithm().value(), 0);
   EXPECT_FALSE(impl.IsAsynchronousReductionEnabled().value());
   EXPECT_FALSE(impl.IsAvoidCopyingArrayBufferEnabled().value());
+  EXPECT_FALSE(impl.IsCallingEndedWhenClosedEnabled().value());
   EXPECT_EQ(impl.GetMaxSizeForImmediateJob().value(), 0);
 }
 
@@ -105,6 +112,7 @@
       impl.Set("MediaSource.MinimumProcessorCountToOffloadAlgorithm", 0));
   ASSERT_TRUE(impl.Set("MediaSource.EnableAsynchronousReduction", 0));
   ASSERT_TRUE(impl.Set("MediaSource.EnableAvoidCopyingArrayBuffer", 0));
+  ASSERT_TRUE(impl.Set("MediaSource.EnableCallingEndedWhenClosed", 0));
   ASSERT_TRUE(impl.Set("MediaSource.MaxSizeForImmediateJob", 0));
   ASSERT_TRUE(impl.Set("MediaSource.MaxSourceBufferAppendSizeInBytes", 1));
   ASSERT_TRUE(
@@ -115,6 +123,7 @@
       impl.Set("MediaSource.MinimumProcessorCountToOffloadAlgorithm", 1));
   ASSERT_TRUE(impl.Set("MediaSource.EnableAsynchronousReduction", 1));
   ASSERT_TRUE(impl.Set("MediaSource.EnableAvoidCopyingArrayBuffer", 1));
+  ASSERT_TRUE(impl.Set("MediaSource.EnableCallingEndedWhenClosed", 1));
   ASSERT_TRUE(impl.Set("MediaSource.MaxSizeForImmediateJob", 1));
   ASSERT_TRUE(impl.Set("MediaSource.MaxSourceBufferAppendSizeInBytes", 2));
   ASSERT_TRUE(
@@ -124,6 +133,7 @@
   EXPECT_EQ(impl.GetMinimumProcessorCountToOffloadAlgorithm().value(), 1);
   EXPECT_TRUE(impl.IsAsynchronousReductionEnabled().value());
   EXPECT_TRUE(impl.IsAvoidCopyingArrayBufferEnabled().value());
+  EXPECT_TRUE(impl.IsCallingEndedWhenClosedEnabled().value());
   EXPECT_EQ(impl.GetMaxSizeForImmediateJob().value(), 1);
   EXPECT_EQ(impl.GetMaxSourceBufferAppendSizeInBytes().value(), 2);
   EXPECT_EQ(impl.GetMediaElementTimeupdateEventIntervalInMilliseconds().value(),
diff --git a/cobalt/dom/media_source.cc b/cobalt/dom/media_source.cc
index e3e5223..8871b83 100644
--- a/cobalt/dom/media_source.cc
+++ b/cobalt/dom/media_source.cc
@@ -107,6 +107,15 @@
       false);
 }
 
+// If this function returns true, MediaSource::EndOfStreamAlgorithm() will call
+// SetReadyState(kMediaSourceReadyStateEnded) even if MediaSource object is
+// closed.
+// The default value is false.
+bool IsCallingEndedWhenClosedEnabled(web::EnvironmentSettings* settings) {
+  return GetMediaSettings(settings).IsCallingEndedWhenClosedEnabled().value_or(
+      false);
+}
+
 // If the size of a job that is part of an algorithm is less than or equal to
 // the return value of this function, the implementation will run the job
 // immediately instead of scheduling it to run later to reduce latency.
@@ -289,7 +298,23 @@
 }
 
 void MediaSource::EndOfStreamAlgorithm(MediaSourceEndOfStreamError error) {
-  SetReadyState(kMediaSourceReadyStateEnded);
+  if (IsClosed()) {
+    if (IsCallingEndedWhenClosedEnabled(environment_settings())) {
+      LOG(INFO) << "Setting state to ended when MediaSource object is closed";
+      // Calling the function below here leads to ANR in production, as
+      // EndOfStreamAlgorithm() can be called by SetReadyState().
+      // Calling SetReadyState() nestedly leads to re-entrance of Abort() on
+      // the SourceBuffer algorithm handle, where a mutex gets re-acquired.
+      // Keep this code path here so we have the option to revert it to the
+      // original behavior in production.
+      SetReadyState(kMediaSourceReadyStateEnded);
+    } else {
+      LOG(INFO)
+          << "Skip setting state to ended when MediaSource object is closed";
+    }
+  } else {
+    SetReadyState(kMediaSourceReadyStateEnded);
+  }
 
   PipelineStatus pipeline_status = PIPELINE_OK;
 
diff --git a/cobalt/dom/navigator_licenses_test.cc b/cobalt/dom/navigator_licenses_test.cc
index fade4a7..e5277aa 100644
--- a/cobalt/dom/navigator_licenses_test.cc
+++ b/cobalt/dom/navigator_licenses_test.cc
@@ -23,10 +23,11 @@
 // Tests the Navigator::licenses function for non-empty return.
 TEST(NavigatorLicensesTest, NonEmpty) {
   std::unique_ptr<web::Context> web_context(new web::testing::StubWebContext());
-  web_context->setup_environment_settings(
+  web_context->SetupEnvironmentSettings(
       new dom::testing::StubEnvironmentSettings());
   scoped_refptr<cobalt::dom::Navigator> navigator =
       new cobalt::dom::Navigator(web_context->environment_settings(), nullptr);
+  web_context->SetupFinished();
 
   ASSERT_TRUE(navigator != nullptr);
   EXPECT_FALSE(navigator->licenses().empty());
diff --git a/cobalt/dom/on_screen_keyboard.cc b/cobalt/dom/on_screen_keyboard.cc
index 6f8da1b..ec41ff4 100644
--- a/cobalt/dom/on_screen_keyboard.cc
+++ b/cobalt/dom/on_screen_keyboard.cc
@@ -19,6 +19,7 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "cobalt/dom/window.h"
+#include "cobalt/web/environment_settings_helper.h"
 #include "cobalt/web/event_target.h"
 
 namespace cobalt {
@@ -87,6 +88,7 @@
     : web::EventTarget(settings),
       bridge_(bridge),
       script_value_factory_(script_value_factory),
+      environment_settings_(settings),
       next_ticket_(0) {
   DCHECK(bridge_) << "OnScreenKeyboardBridge must not be NULL";
   suggestions_supported_ = bridge_->SuggestionsSupported();
@@ -96,10 +98,12 @@
   script::Handle<script::Promise<void>> promise =
       script_value_factory_->CreateBasicPromise<void>();
   int ticket = next_ticket_++;
+  auto* global_wrappable = web::get_global_wrappable(environment_settings_);
   bool is_emplaced =
       ticket_to_show_promise_map_
           .emplace(ticket, std::unique_ptr<VoidPromiseValue::Reference>(
-                               new VoidPromiseValue::Reference(this, promise)))
+                               new VoidPromiseValue::Reference(global_wrappable,
+                                                               promise)))
           .second;
   DCHECK(is_emplaced);
   bridge_->Show(data_.c_str(), ticket);
@@ -125,10 +129,12 @@
   script::Handle<script::Promise<void>> promise =
       script_value_factory_->CreateBasicPromise<void>();
   int ticket = next_ticket_++;
+  auto* global_wrappable = web::get_global_wrappable(environment_settings_);
   bool is_emplaced =
       ticket_to_hide_promise_map_
           .emplace(ticket, std::unique_ptr<VoidPromiseValue::Reference>(
-                               new VoidPromiseValue::Reference(this, promise)))
+                               new VoidPromiseValue::Reference(global_wrappable,
+                                                               promise)))
           .second;
   DCHECK(is_emplaced);
   bridge_->Hide(ticket);
@@ -139,10 +145,12 @@
   script::Handle<script::Promise<void>> promise =
       script_value_factory_->CreateBasicPromise<void>();
   int ticket = next_ticket_++;
+  auto* global_wrappable = web::get_global_wrappable(environment_settings_);
   bool is_emplaced =
       ticket_to_focus_promise_map_
           .emplace(ticket, std::unique_ptr<VoidPromiseValue::Reference>(
-                               new VoidPromiseValue::Reference(this, promise)))
+                               new VoidPromiseValue::Reference(global_wrappable,
+                                                               promise)))
           .second;
   DCHECK(is_emplaced);
   bridge_->Focus(ticket);
@@ -153,10 +161,12 @@
   script::Handle<script::Promise<void>> promise =
       script_value_factory_->CreateBasicPromise<void>();
   int ticket = next_ticket_++;
+  auto* global_wrappable = web::get_global_wrappable(environment_settings_);
   bool is_emplaced =
       ticket_to_blur_promise_map_
           .emplace(ticket, std::unique_ptr<VoidPromiseValue::Reference>(
-                               new VoidPromiseValue::Reference(this, promise)))
+                               new VoidPromiseValue::Reference(global_wrappable,
+                                                               promise)))
           .second;
   DCHECK(is_emplaced);
   bridge_->Blur(ticket);
@@ -169,11 +179,12 @@
       script_value_factory_->CreateBasicPromise<void>();
   if (suggestions_supported_) {
     int ticket = next_ticket_++;
+    auto* global_wrappable = web::get_global_wrappable(environment_settings_);
     bool is_emplaced =
         ticket_to_update_suggestions_promise_map_
-            .emplace(ticket,
-                     std::unique_ptr<VoidPromiseValue::Reference>(
-                         new VoidPromiseValue::Reference(this, promise)))
+            .emplace(ticket, std::unique_ptr<VoidPromiseValue::Reference>(
+                                 new VoidPromiseValue::Reference(
+                                     global_wrappable, promise)))
             .second;
     DCHECK(is_emplaced);
     bridge_->UpdateSuggestions(suggestions, ticket);
diff --git a/cobalt/dom/on_screen_keyboard.h b/cobalt/dom/on_screen_keyboard.h
index ecad6bf..927a197 100644
--- a/cobalt/dom/on_screen_keyboard.h
+++ b/cobalt/dom/on_screen_keyboard.h
@@ -137,6 +137,7 @@
   OnScreenKeyboardBridge* bridge_;
 
   script::ScriptValueFactory* const script_value_factory_;
+  script::EnvironmentSettings* environment_settings_;
 
   std::string data_;
   bool is_composing_;
diff --git a/cobalt/dom/pointer_state.cc b/cobalt/dom/pointer_state.cc
index 619fadf..79f7974 100644
--- a/cobalt/dom/pointer_state.cc
+++ b/cobalt/dom/pointer_state.cc
@@ -15,6 +15,7 @@
 #include "cobalt/dom/pointer_state.h"
 
 #include <algorithm>
+#include <memory>
 #include <utility>
 
 #include "base/trace_event/trace_event.h"
@@ -297,6 +298,43 @@
   client_time_stamps_.erase(pointer_id);
 }
 
+void PointerState::SetPossibleScrollTargets(
+    int32_t pointer_id,
+    std::unique_ptr<PossibleScrollTargets> possible_scroll_targets) {
+  client_possible_scroll_targets_[pointer_id] =
+      std::move(possible_scroll_targets);
+}
+PossibleScrollTargets* PointerState::GetPossibleScrollTargets(
+    int32_t pointer_id) {
+  auto possible_scroll_targets =
+      client_possible_scroll_targets_.find(pointer_id);
+  if (possible_scroll_targets != client_possible_scroll_targets_.end()) {
+    return possible_scroll_targets->second.get();
+  }
+  return nullptr;
+}
+void PointerState::ClearPossibleScrollTargets(int32_t pointer_id) {
+  client_possible_scroll_targets_.erase(pointer_id);
+}
+
+void PointerState::SetClientTransformMatrix(int32_t pointer_id,
+                                            const math::Matrix3F& matrix) {
+  client_matrices_.emplace(pointer_id, matrix);
+}
+
+const math::Matrix3F& PointerState::GetClientTransformMatrix(
+    int32_t pointer_id) {
+  auto matrix_entry = client_matrices_.find(pointer_id);
+  if (matrix_entry != client_matrices_.end()) {
+    return matrix_entry->second;
+  }
+  return identity_matrix_;
+}
+
+void PointerState::ClearMatrix(int32_t pointer_id) {
+  client_matrices_.erase(pointer_id);
+}
+
 void PointerState::SetWasCancelled(int32_t pointer_id) {
   client_cancellations_.insert(pointer_id);
 }
diff --git a/cobalt/dom/pointer_state.h b/cobalt/dom/pointer_state.h
index 5552f9d..1c3e5a2 100644
--- a/cobalt/dom/pointer_state.h
+++ b/cobalt/dom/pointer_state.h
@@ -16,6 +16,7 @@
 #define COBALT_DOM_POINTER_STATE_H_
 
 #include <map>
+#include <memory>
 #include <queue>
 #include <set>
 
@@ -24,6 +25,7 @@
 #include "base/memory/weak_ptr.h"
 #include "cobalt/dom/html_element.h"
 #include "cobalt/dom/pointer_event_init.h"
+#include "cobalt/math/matrix3_f.h"
 #include "cobalt/math/vector2d_f.h"
 #include "cobalt/web/dom_exception.h"
 #include "cobalt/web/event.h"
@@ -31,6 +33,13 @@
 namespace cobalt {
 namespace dom {
 
+struct PossibleScrollTargets {
+  scoped_refptr<dom::HTMLElement> up;
+  scoped_refptr<dom::HTMLElement> down;
+  scoped_refptr<dom::HTMLElement> left;
+  scoped_refptr<dom::HTMLElement> right;
+};
+
 // This class contains various state related to pointer and mouse support.
 class PointerState {
  public:
@@ -85,6 +94,17 @@
   base::Optional<uint64> GetClientTimeStamp(int32_t pointer_id);
   void ClearTimeStamp(int32_t pointer_id);
 
+  void SetPossibleScrollTargets(
+      int32_t pointer_id,
+      std::unique_ptr<PossibleScrollTargets> possible_scroll_targets);
+  PossibleScrollTargets* GetPossibleScrollTargets(int32_t pointer_id);
+  void ClearPossibleScrollTargets(int32_t pointer_id);
+
+  void SetClientTransformMatrix(int32_t pointer_id,
+                                const math::Matrix3F& matrix);
+  const math::Matrix3F& GetClientTransformMatrix(int32_t pointer_id);
+  void ClearMatrix(int32_t pointer_id);
+
   // Tracks whether a certain pointer was cancelled, i.e. if it panned the
   // page viewport.
   // https://www.w3.org/TR/pointerevents1/#the-pointercancel-event
@@ -96,12 +116,12 @@
 
  private:
   // Stores pointer events until they are handled after a layout.
-  std::queue<scoped_refptr<web::Event> > pointer_events_;
+  std::queue<scoped_refptr<web::Event>> pointer_events_;
 
   // This stores the elements with target overrides
   //   https://www.w3.org/TR/2015/REC-pointerevents-20150224/#pointer-capture
-  std::map<int32_t, base::WeakPtr<Element> > target_override_;
-  std::map<int32_t, base::WeakPtr<Element> > pending_target_override_;
+  std::map<int32_t, base::WeakPtr<Element>> target_override_;
+  std::map<int32_t, base::WeakPtr<Element>> pending_target_override_;
 
   // Store the set of active pointers.
   //   https://www.w3.org/TR/2015/REC-pointerevents-20150224/#dfn-active-pointer
@@ -113,7 +133,12 @@
 
   std::map<int32_t, math::Vector2dF> client_coordinates_;
   std::map<int32_t, uint64> client_time_stamps_;
+  std::map<int32_t, std::unique_ptr<PossibleScrollTargets>>
+      client_possible_scroll_targets_;
+  std::map<int32_t, const math::Matrix3F> client_matrices_;
   std::set<int32_t> client_cancellations_;
+
+  const math::Matrix3F identity_matrix_ = math::Matrix3F::Identity();
 };
 
 }  // namespace dom
diff --git a/cobalt/dom/source_buffer.idl b/cobalt/dom/source_buffer.idl
index 1fd8053..39a9248 100644
--- a/cobalt/dom/source_buffer.idl
+++ b/cobalt/dom/source_buffer.idl
@@ -16,16 +16,11 @@
 // https://www.w3.org/TR/2016/CR-media-source-20160705/#sourcebuffer
 
 interface SourceBuffer : EventTarget {
-  [RaisesException] readonly attribute TimeRanges buffered;
-  [RaisesException] attribute double timestampOffset;
-
-  [RaisesException] void abort();
-
   // MSE 2016 Interface
   [RaisesException] attribute SourceBufferAppendMode mode;
   readonly attribute boolean updating;
-  // [RaisesException] readonly attribute TimeRanges buffered;
-  // [RaisesException] attribute double timestampOffset;
+  [RaisesException] readonly attribute TimeRanges buffered;
+  [RaisesException] attribute double timestampOffset;
   readonly attribute AudioTrackList audioTracks;
   readonly attribute VideoTrackList videoTracks;
   // readonly attribute TextTrackList textTracks;
@@ -35,7 +30,7 @@
   [RaisesException] void appendBuffer(ArrayBufferView data);
 
   // appendStream() omitted as it is not used.
-  // [RaisesException] void abort();
+  [RaisesException] void abort();
   [RaisesException] void remove(double start, unrestricted double end);
   [RaisesException] attribute TrackDefaultList trackDefaults;
 };
diff --git a/cobalt/dom/source_buffer_algorithm.cc b/cobalt/dom/source_buffer_algorithm.cc
index a24e51c..b55e9cd 100644
--- a/cobalt/dom/source_buffer_algorithm.cc
+++ b/cobalt/dom/source_buffer_algorithm.cc
@@ -101,6 +101,7 @@
 void SourceBufferAppendAlgorithm::Finalize() {
   TRACE_EVENT1("cobalt::dom", "SourceBufferAppendAlgorithm::Finalize()",
                "succeeded", succeeded_);
+
   if (succeeded_) {
     schedule_event_cb_.Run(base::Tokens::update());
     schedule_event_cb_.Run(base::Tokens::updateend());
@@ -143,6 +144,7 @@
 
 void SourceBufferRemoveAlgorithm::Finalize() {
   TRACE_EVENT0("cobalt::dom", "SourceBufferRemoveAlgorithm::Finalize()");
+
   schedule_event_cb_.Run(base::Tokens::update());
   schedule_event_cb_.Run(base::Tokens::updateend());
   std::move(finalized_cb_).Run();
diff --git a/cobalt/dom/testing/fake_document.h b/cobalt/dom/testing/fake_document.h
index 494da4e..429481f 100644
--- a/cobalt/dom/testing/fake_document.h
+++ b/cobalt/dom/testing/fake_document.h
@@ -19,7 +19,6 @@
 #include <utility>
 
 #include "cobalt/dom/document.h"
-
 #include "cobalt/web/csp_delegate_factory.h"
 
 namespace cobalt {
@@ -29,30 +28,14 @@
 class FakeDocument : public dom::Document {
  public:
   explicit FakeDocument(dom::HTMLElementContext* html_element_context)
-      : dom::Document(html_element_context) {
-    web::WindowOrWorkerGlobalScope::Options options(
-        base::ApplicationState::kApplicationStateStarted);
-    std::unique_ptr<web::CspViolationReporter> violation_reporter(
-        new web::CspViolationReporter(nullptr, options.post_sender));
-    csp_delegate_ = web::CspDelegateFactory::GetInstance()->Create(
-        options.csp_enforcement_mode, std::move(violation_reporter),
-        environment_settings()->creation_url(), options.require_csp,
-        options.csp_policy_changed_callback,
-        options.csp_insecure_allowed_token);
-  }
+      : FakeDocument(html_element_context, dom::Document::Options()) {}
 
   FakeDocument(dom::HTMLElementContext* html_element_context,
-               dom::Document::Options doc_options)
+               const dom::Document::Options& doc_options)
       : dom::Document(html_element_context, doc_options) {
-    web::WindowOrWorkerGlobalScope::Options options(
-        base::ApplicationState::kApplicationStateStarted);
-    std::unique_ptr<web::CspViolationReporter> violation_reporter(
-        new web::CspViolationReporter(nullptr, options.post_sender));
-    csp_delegate_ = web::CspDelegateFactory::GetInstance()->Create(
-        options.csp_enforcement_mode, std::move(violation_reporter),
-        environment_settings()->creation_url(), options.require_csp,
-        options.csp_policy_changed_callback,
-        options.csp_insecure_allowed_token);
+    web::WindowOrWorkerGlobalScope::Options options;
+    csp_delegate_ =
+        web::CspDelegateFactory::Create(nullptr, options.csp_options);
   }
 
   web::CspDelegate* GetCSPDelegate() const override {
diff --git a/cobalt/dom/testing/stub_window.h b/cobalt/dom/testing/stub_window.h
index 34a2d94..41f6bf1 100644
--- a/cobalt/dom/testing/stub_window.h
+++ b/cobalt/dom/testing/stub_window.h
@@ -28,6 +28,7 @@
 #include "cobalt/cssom/viewport_size.h"
 #include "cobalt/dom/captions/system_caption_settings.h"
 #include "cobalt/dom/dom_settings.h"
+#include "cobalt/dom/dom_stat_tracker.h"
 #include "cobalt/dom/local_storage_database.h"
 #include "cobalt/dom/testing/stub_environment_settings.h"
 #include "cobalt/dom/window.h"
@@ -107,6 +108,8 @@
     dom_parser_.reset(
         new dom_parser::Parser(base::Bind(&StubLoadCompleteCallback)));
     dom_stat_tracker_.reset(new dom::DomStatTracker("StubWindow"));
+    web::CspDelegate::Options csp_options;
+    csp_options.header_policy = csp::kCSPOptional;
     window_ = new dom::Window(
         web_context()->environment_settings(), cssom::ViewportSize(1920, 1080),
         base::kApplicationStateStarted, css_parser_.get(), dom_parser_.get(),
@@ -115,15 +118,14 @@
         nullptr, nullptr, nullptr, nullptr,
         web_context()->global_environment()->script_value_factory(), nullptr,
         dom_stat_tracker_.get(), "en", base::Callback<void(const GURL&)>(),
-        base::Bind(&StubLoadCompleteCallback), nullptr,
-        network_bridge::PostSender(), csp::kCSPOptional,
-        web::kCspEnforcementEnable, base::Closure() /* csp_policy_changed */,
-        base::Closure() /* ran_animation_frame_callbacks */,
+        base::Bind(&StubLoadCompleteCallback), nullptr /* cookie_jar */,
+        csp_options, base::Closure() /* ran_animation_frame_callbacks */,
         dom::Window::CloseCallback() /* window_close */,
         base::Closure() /* window_minimize */, on_screen_keyboard_bridge_,
         nullptr /* camera_3d */, dom::Window::OnStartDispatchEventCallback(),
         dom::Window::OnStopDispatchEventCallback(),
         dom::ScreenshotManager::ProvideScreenshotFunctionCallback(),
+        dom::Window::NavItemCallback(),
         nullptr /* synchronous_loader_interrupt */,
         false /* enable_inline_script_warnings */, nullptr /* ui_nav_root */,
         true /* enable_map_to_mesh */, 0 /* csp_insecure_allowed_token */,
@@ -131,9 +133,10 @@
         dom::Window::kClockTypeSystemTime /* clock_type */,
         dom::Window::CacheCallback() /* splash_screen_cache_callback */,
         system_caption_settings_ /* captions */
-        );
+    );
     global_environment()->CreateGlobalObject(
         window_, web_context()->environment_settings());
+    web_context()->SetupFinished();
   }
 
  private:
@@ -142,7 +145,7 @@
 
   void InitializeWebContext() {
     web_context_.reset(new web::testing::StubWebContext());
-    web_context()->setup_environment_settings(
+    web_context()->SetupEnvironmentSettings(
         new dom::testing::StubEnvironmentSettings(options_));
     web_context()->environment_settings()->set_creation_url(
         GURL("about:blank"));
diff --git a/cobalt/dom/window.cc b/cobalt/dom/window.cc
index c5d5ac0..1644cdc 100644
--- a/cobalt/dom/window.cc
+++ b/cobalt/dom/window.cc
@@ -53,8 +53,7 @@
 #include "cobalt/speech/speech_synthesis.h"
 #include "cobalt/web/context.h"
 #include "cobalt/web/environment_settings.h"
-#include "cobalt/web/error_event.h"
-#include "cobalt/web/error_event_init.h"
+#include "cobalt/web/environment_settings_helper.h"
 #include "cobalt/web/event.h"
 #include "cobalt/web/window_or_worker_global_scope.h"
 #include "starboard/file.h"
@@ -107,10 +106,7 @@
     const base::Callback<void(const GURL&)> navigation_callback,
     const loader::Decoder::OnCompleteFunction& load_complete_callback,
     network_bridge::CookieJar* cookie_jar,
-    const network_bridge::PostSender& post_sender,
-    csp::CSPHeaderPolicy require_csp,
-    web::CspEnforcementType csp_enforcement_mode,
-    const base::Closure& csp_policy_changed_callback,
+    const web::CspDelegate::Options& csp_options,
     const base::Closure& ran_animation_frame_callbacks_callback,
     CloseCallback window_close_callback, base::Closure window_minimize_callback,
     OnScreenKeyboardBridge* on_screen_keyboard_bridge,
@@ -119,6 +115,7 @@
     const OnStopDispatchEventCallback& on_stop_dispatch_event_callback,
     const ScreenshotManager::ProvideScreenshotFunctionCallback&
         screenshot_function_callback,
+    const NavItemCallback& cancel_scroll_callback,
     base::WaitableEvent* synchronous_loader_interrupt,
     bool enable_inline_script_warnings,
     const scoped_refptr<ui_navigation::NavItem>& ui_nav_root,
@@ -130,14 +127,11 @@
     // 'window' object EventTargets require special handling for onerror events,
     // see EventTarget constructor for more details.
     : web::WindowOrWorkerGlobalScope(
-          settings, dom_stat_tracker,
+          settings,
           web::WindowOrWorkerGlobalScope::Options(
-              initial_application_state, post_sender, require_csp,
-              csp_enforcement_mode, csp_policy_changed_callback,
-              csp_insecure_allowed_token)),
+              initial_application_state, csp_options, dom_stat_tracker)),
       viewport_size_(view_size),
       is_resize_event_pending_(false),
-      is_reporting_script_error_(false),
 #if defined(ENABLE_TEST_RUNNER)
       test_runner_(new TestRunner()),
 #endif  // ENABLE_TEST_RUNNER
@@ -178,6 +172,7 @@
                                                      script_value_factory)
                               : NULL),
       splash_screen_cache_callback_(splash_screen_cache_callback),
+      cancel_scroll_callback_(cancel_scroll_callback),
       on_start_dispatch_event_callback_(on_start_dispatch_event_callback),
       on_stop_dispatch_event_callback_(on_stop_dispatch_event_callback),
       screenshot_manager_(settings, screenshot_function_callback),
@@ -213,7 +208,8 @@
     const loader::Decoder::OnCompleteFunction& load_complete_callback) {
   document_loader_.reset(new loader::Loader(
       base::Bind(&loader::FetcherFactory::CreateFetcher,
-                 base::Unretained(fetcher_factory), url, disk_cache::kHTML),
+                 base::Unretained(fetcher_factory), url, /*main_resource=*/true,
+                 disk_cache::kHTML),
       base::Bind(&Parser::ParseDocumentAsync, base::Unretained(dom_parser),
                  document_, base::SourceLocation(url.spec(), 1, 1)),
       load_complete_callback));
@@ -268,7 +264,8 @@
 
 const scoped_refptr<Navigator>& Window::navigator() const { return navigator_; }
 
-script::Handle<ScreenshotManager::InterfacePromise> Window::Screenshot() {
+script::Handle<ScreenshotManager::InterfacePromise> Window::Screenshot(
+    script::EnvironmentSettings* environment_settings) {
   scoped_refptr<render_tree::Node> render_tree_root =
       document_->DoSynchronousLayoutAndGetRenderTree();
 
@@ -277,9 +274,10 @@
           ->script_value_factory()
           ->CreateInterfacePromise<dom::Screenshot>();
 
+  auto* global_wrappable = web::get_global_wrappable(environment_settings);
   std::unique_ptr<ScreenshotManager::InterfacePromiseValue::Reference>
       promise_reference(new ScreenshotManager::InterfacePromiseValue::Reference(
-          this, promise));
+          global_wrappable, promise));
 
   screenshot_manager_.Screenshot(
       loader::image::EncodedStaticImage::ImageFormat::kPNG, render_tree_root,
@@ -488,63 +486,6 @@
   window_timers_.SetApplicationState(state);
 }
 
-bool Window::ReportScriptError(const script::ErrorReport& error_report) {
-  // Runtime script errors: when the user agent is required to report an error
-  // for a particular script, it must run these steps, after which the error is
-  // either handled or not handled:
-  //   https://www.w3.org/TR/html50/webappapis.html#runtime-script-errors
-
-  // 1. If target is in error reporting mode, then abort these steps; the error
-  //    is not handled.
-  if (is_reporting_script_error_) {
-    return false;
-  }
-
-  // 2. Let target be in error reporting mode.
-  is_reporting_script_error_ = true;
-
-  // 7. Let event be a new trusted ErrorEvent object that does not bubble but is
-  //    cancelable, and which has the event name error.
-  // NOTE: Cobalt does not currently support trusted events.
-  web::ErrorEventInit error;
-  error.set_bubbles(false);
-  error.set_cancelable(true);
-
-  if (error_report.is_muted) {
-    // 6. If script has muted errors, then set message to "Script error.", set
-    //    location to the empty string, set line and col to 0, and set error
-    //    object to null.
-    error.set_message("Script error.");
-    error.set_filename("");
-    error.set_lineno(0);
-    error.set_colno(0);
-    error.set_error(NULL);
-  } else {
-    // 8. Initialize event's message attribute to message.
-    error.set_message(error_report.message);
-    // 9. Initialize event's filename attribute to location.
-    error.set_filename(error_report.filename);
-    // 10. Initialize event's lineno attribute to line.
-    error.set_lineno(error_report.line_number);
-    // 11. Initialize event's colno attribute to col.
-    error.set_colno(error_report.column_number);
-    // 12. Initialize event's error attribute to error object.
-    error.set_error(error_report.error ? error_report.error.get() : NULL);
-  }
-
-  scoped_refptr<web::ErrorEvent> error_event(new web::ErrorEvent(error));
-
-  // 13. Dispatch event at target.
-  DispatchEvent(error_event);
-
-  // 14. Let target no longer be in error reporting mode.
-  is_reporting_script_error_ = false;
-
-  // 15. If event was canceled, then the error is handled. Otherwise, the error
-  //     is not handled.
-  return error_event->default_prevented();
-}
-
 void Window::SetSynchronousLayoutCallback(const base::Closure& callback) {
   document_->set_synchronous_layout_callback(callback);
 }
@@ -669,6 +610,14 @@
   splash_screen_cache_callback_.Run(content, topic);
 }
 
+void Window::CancelScroll(
+    const scoped_refptr<ui_navigation::NavItem>& nav_item) {
+  if (cancel_scroll_callback_.is_null()) {
+    return;
+  }
+  cancel_scroll_callback_.Run(nav_item);
+}
+
 const scoped_refptr<OnScreenKeyboard>& Window::on_screen_keyboard() const {
   return on_screen_keyboard_;
 }
diff --git a/cobalt/dom/window.h b/cobalt/dom/window.h
index 73688a1..5bbf76b 100644
--- a/cobalt/dom/window.h
+++ b/cobalt/dom/window.h
@@ -123,6 +123,9 @@
   typedef base::Callback<void(const std::string&,
                               const base::Optional<std::string>&)>
       CacheCallback;
+  typedef base::Callback<void(
+      const scoped_refptr<ui_navigation::NavItem>& nav_item)>
+      NavItemCallback;
 
   enum ClockType {
     kClockTypeTestRunner,
@@ -154,10 +157,7 @@
       const base::Callback<void(const GURL&)> navigation_callback,
       const loader::Decoder::OnCompleteFunction& load_complete_callback,
       network_bridge::CookieJar* cookie_jar,
-      const network_bridge::PostSender& post_sender,
-      csp::CSPHeaderPolicy require_csp,
-      web::CspEnforcementType csp_enforcement_mode,
-      const base::Closure& csp_policy_changed_callback,
+      const web::CspDelegate::Options& csp_options,
       const base::Closure& ran_animation_frame_callbacks_callback,
       CloseCallback window_close_callback,
       base::Closure window_minimize_callback,
@@ -168,6 +168,7 @@
       const OnStopDispatchEventCallback& stop_tracking_dispatch_event_callback,
       const ScreenshotManager::ProvideScreenshotFunctionCallback&
           screenshot_function_callback,
+      const NavItemCallback& cancel_scroll_callback,
       base::WaitableEvent* synchronous_loader_interrupt,
       bool enable_inline_script_warnings = false,
       const scoped_refptr<ui_navigation::NavItem>& ui_nav_root = nullptr,
@@ -203,7 +204,8 @@
 
   const scoped_refptr<Navigator>& navigator() const;
 
-  script::Handle<ScreenshotManager::InterfacePromise> Screenshot();
+  script::Handle<ScreenshotManager::InterfacePromise> Screenshot(
+      script::EnvironmentSettings* environment_settings);
 
   // Web API: CSSOM (partial interface)
   // Returns the computed style of the given element, as described in
@@ -338,11 +340,6 @@
   void SetApplicationState(base::ApplicationState state,
                            SbTimeMonotonic timestamp);
 
-  // Performs the steps specified for runtime script errors:
-  //   https://www.w3.org/TR/html50/webappapis.html#runtime-script-errors
-  // Returns whether or not the script was handled.
-  bool ReportScriptError(const script::ErrorReport& error_report);
-
   // ApplicationLifecycleState::Observer implementation.
   void OnWindowFocusChanged(bool has_focus) override;
   void OnVisibilityStateChanged(VisibilityState visibility_state) override;
@@ -359,6 +356,8 @@
   void CacheSplashScreen(const std::string& content,
                          const base::Optional<std::string>& topic);
 
+  void CancelScroll(const scoped_refptr<ui_navigation::NavItem>& nav_item);
+
   // Custom on screen keyboard.
   const scoped_refptr<OnScreenKeyboard>& on_screen_keyboard() const;
   void ReleaseOnScreenKeyboard();
@@ -410,11 +409,6 @@
   // visibility state changes to visible.
   bool is_resize_event_pending_;
 
-  // Whether or not the window is currently reporting a script error. This is
-  // used to prevent infinite recursion, because reporting the error causes an
-  // event to be dispatched, which can generate a new script error.
-  bool is_reporting_script_error_;
-
 #if defined(ENABLE_TEST_RUNNER)
   scoped_refptr<TestRunner> test_runner_;
 #endif  // ENABLE_TEST_RUNNER
@@ -445,6 +439,8 @@
 
   CacheCallback splash_screen_cache_callback_;
 
+  NavItemCallback cancel_scroll_callback_;
+
   OnStartDispatchEventCallback on_start_dispatch_event_callback_;
   OnStopDispatchEventCallback on_stop_dispatch_event_callback_;
 
diff --git a/cobalt/dom/window.idl b/cobalt/dom/window.idl
index fc782d5..1e7597b 100644
--- a/cobalt/dom/window.idl
+++ b/cobalt/dom/window.idl
@@ -48,7 +48,7 @@
   // https://developer.mozilla.org/en-US/docs/Web/API/Window/minimize
   void minimize();
 
-  Promise<Screenshot> screenshot();
+  [CallWith=EnvironmentSettings] Promise<Screenshot> screenshot();
 };
 Window implements GlobalEventHandlers;
 Window implements WindowEventHandlers;
diff --git a/cobalt/dom/window_test.cc b/cobalt/dom/window_test.cc
index 2bf4d12..a984cb9 100644
--- a/cobalt/dom/window_test.cc
+++ b/cobalt/dom/window_test.cc
@@ -249,7 +249,8 @@
       "error", FakeScriptValue<web::EventListener>(fake_event_listener_.get()),
       true);
   fake_event_listener_->ExpectHandleEventCall("error", window());
-  window()->DispatchEvent(new web::ErrorEvent());
+  window()->DispatchEvent(
+      new web::ErrorEvent(/*environment_settings=*/nullptr));
 }
 
 TEST_F(WindowTest, OnErrorEvent) {
diff --git a/cobalt/dom_parser/html_decoder.cc b/cobalt/dom_parser/html_decoder.cc
index 01267f3..1e3bc18 100644
--- a/cobalt/dom_parser/html_decoder.cc
+++ b/cobalt/dom_parser/html_decoder.cc
@@ -28,12 +28,12 @@
     const scoped_refptr<dom::Node>& reference_node,
     const int dom_max_element_depth, const base::SourceLocation& input_location,
     const loader::Decoder::OnCompleteFunction& load_complete_callback,
-    const bool should_run_scripts, const csp::CSPHeaderPolicy require_csp)
+    const bool should_run_scripts, const csp::CSPHeaderPolicy csp_header_policy)
     : libxml_html_parser_wrapper_(new LibxmlHTMLParserWrapper(
           document, parent_node, reference_node, dom_max_element_depth,
           input_location, load_complete_callback, should_run_scripts)),
       document_(document),
-      require_csp_(require_csp),
+      csp_header_policy_(csp_header_policy),
       load_complete_callback_(load_complete_callback) {}
 
 HTMLDecoder::~HTMLDecoder() {}
@@ -61,7 +61,7 @@
 
   csp::ResponseHeaders csp_headers(headers);
   if (document_->GetCSPDelegate()->OnReceiveHeaders(csp_headers) ||
-      require_csp_ == csp::kCSPOptional) {
+      csp_header_policy_ == csp::kCSPOptional) {
     return loader::kLoadResponseContinue;
   } else {
     LOG(ERROR) << "Failure receiving Content Security Policy headers "
diff --git a/cobalt/dom_parser/html_decoder.h b/cobalt/dom_parser/html_decoder.h
index ecc5cd7..b6de892 100644
--- a/cobalt/dom_parser/html_decoder.h
+++ b/cobalt/dom_parser/html_decoder.h
@@ -49,7 +49,7 @@
               const base::SourceLocation& input_location,
               const loader::Decoder::OnCompleteFunction& load_complete_callback,
               const bool should_run_scripts,
-              const csp::CSPHeaderPolicy require_csp);
+              const csp::CSPHeaderPolicy csp_header_policy);
 
   ~HTMLDecoder();
 
@@ -73,7 +73,7 @@
   THREAD_CHECKER(thread_checker_);
 
   // If Cobalt user forbids rendering Cobalt without csp headers.
-  const csp::CSPHeaderPolicy require_csp_;
+  const csp::CSPHeaderPolicy csp_header_policy_;
 
   const loader::Decoder::OnCompleteFunction load_complete_callback_;
 
diff --git a/cobalt/dom_parser/parser.cc b/cobalt/dom_parser/parser.cc
index 060886a..72f17b9 100644
--- a/cobalt/dom_parser/parser.cc
+++ b/cobalt/dom_parser/parser.cc
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/dom_parser/parser.h"
 
+#include <memory>
+
 #include "base/logging.h"
 #include "cobalt/dom/document.h"
 #include "cobalt/dom/xml_document.h"
@@ -33,7 +33,7 @@
       new dom::Document(html_element_context);
   HTMLDecoder html_decoder(document, document, NULL, dom_max_element_depth_,
                            input_location, load_complete_callback_, false,
-                           require_csp_);
+                           csp_header_policy_);
   html_decoder.DecodeChunk(input.c_str(), input.length());
   html_decoder.Finish();
   return document;
@@ -60,7 +60,7 @@
     const base::SourceLocation& input_location) {
   HTMLDecoder html_decoder(document, parent_node, reference_node,
                            dom_max_element_depth_, input_location,
-                           load_complete_callback_, false, require_csp_);
+                           load_complete_callback_, false, csp_header_policy_);
   html_decoder.DecodeChunk(input.c_str(), input.length());
   html_decoder.Finish();
 }
@@ -85,7 +85,7 @@
     const loader::Decoder::OnCompleteFunction& load_complete_callback) {
   return std::unique_ptr<loader::Decoder>(new HTMLDecoder(
       document, document, NULL, dom_max_element_depth_, input_location,
-      load_complete_callback, true, require_csp_));
+      load_complete_callback, true, csp_header_policy_));
 }
 
 std::unique_ptr<loader::Decoder> Parser::ParseXMLDocumentAsync(
diff --git a/cobalt/dom_parser/parser.h b/cobalt/dom_parser/parser.h
index e6527fb..637828b 100644
--- a/cobalt/dom_parser/parser.h
+++ b/cobalt/dom_parser/parser.h
@@ -34,18 +34,18 @@
       : dom_max_element_depth_(kDefaultDOMMaxElementDepth),
         ALLOW_THIS_IN_INITIALIZER_LIST(load_complete_callback_(
             base::Bind(&Parser::LoadCompleteCallback, base::Unretained(this)))),
-        require_csp_(csp::kCSPRequired) {}
+        csp_header_policy_(csp::kCSPRequired) {}
   explicit Parser(
       const loader::Decoder::OnCompleteFunction& load_complete_callback)
       : dom_max_element_depth_(kDefaultDOMMaxElementDepth),
         load_complete_callback_(load_complete_callback),
-        require_csp_(csp::kCSPRequired) {}
+        csp_header_policy_(csp::kCSPRequired) {}
   Parser(const int dom_max_element_depth,
          const loader::Decoder::OnCompleteFunction& load_complete_callback,
-         csp::CSPHeaderPolicy require_csp)
+         csp::CSPHeaderPolicy csp_header_policy)
       : dom_max_element_depth_(dom_max_element_depth),
         load_complete_callback_(load_complete_callback),
-        require_csp_(require_csp) {}
+        csp_header_policy_(csp_header_policy) {}
   ~Parser() override {}
 
   // From dom::Parser.
@@ -91,7 +91,7 @@
 
   // Cobalt user can specify if they want to forbid Cobalt rendering without csp
   // headers.
-  csp::CSPHeaderPolicy require_csp_;
+  csp::CSPHeaderPolicy csp_header_policy_;
 
   DISALLOW_COPY_AND_ASSIGN(Parser);
 };
diff --git a/cobalt/encoding/BUILD.gn b/cobalt/encoding/BUILD.gn
index 904043f..321f41f 100644
--- a/cobalt/encoding/BUILD.gn
+++ b/cobalt/encoding/BUILD.gn
@@ -52,5 +52,8 @@
     "//testing/gtest",
   ]
 
-  data_deps = [ "//third_party/icu:icudata" ]
+  data_deps = [
+    "//cobalt/network:copy_ssl_certificates",
+    "//third_party/icu:icudata",
+  ]
 }
diff --git a/cobalt/fetch/embedded_scripts/fetch.js b/cobalt/fetch/embedded_scripts/fetch.js
index 87435e2..c181f0d 100644
--- a/cobalt/fetch/embedded_scripts/fetch.js
+++ b/cobalt/fetch/embedded_scripts/fetch.js
@@ -11,7 +11,7 @@
 d.split(":");if(d=l.shift().trim())l=l.join(":").trim(),c.append(d,l)});return c}function u(a,b){b||={};this[Q]="default";this[y]="status"in b?b.status:200;if(200>this[y]||599<this[y])throw new ba("Invalid response status");this[ca]=200<=this[y]&&300>this[y];if("statusText"in b){var c=b.statusText;for(var d=0,l=c.length,f;d<l;d++)if(f=c.charCodeAt(d),9!==f&&(32>f||255<f||127===f))throw e("Invalid response status text");}else c="OK";this[R]=c;this[x]=L(b.headers,S);this[C]=b.url||"";if(a&&-1<da.indexOf(this[y]))throw new e("Response body is not allowed with a null body status");
 this[E]=b.is_aborted||!1;this._initBody(a)}if(!h.fetch){var K=h.Array,aa=h.ArrayBuffer,qa=h.Object.create,W=h.Object.defineProperties,k=h.Symbol,Ba=k.iterator,P=h.Map,ba=h.RangeError,e=h.TypeError,v=h.Uint8Array,z=h.Promise,V=h.ReadableStream,ea=h.ReadableStreamTee,ta=h.IsReadableStreamDisturbed,ra=h.IsReadableStreamLocked,r=k("body"),U=k("bodyUsed"),G=k("cache"),H=k("credentials"),A=k("guardCallback"),x=k("headers"),I=k("integrity"),p=k("map"),D=k("method"),t=k("mode"),ca=k("ok"),N=k("redirect"),
 y=k("status"),R=k("statusText"),Q=k("type"),C=k("url"),E=k("is_aborted"),F=k("signal"),la="accept-charset accept-encoding access-control-request-headers access-control-request-method connection content-length cookie cookie2 date dnt expect host keep-alive origin referer te trailer transfer-encoding upgrade via".split(" "),pa=["set-cookie","set-cookie2"],na=["accept","accept-language","content-language"],oa=["application/x-www-form-urlencoded","multipart/form-data","text/plain"],ua="default no-store reload no-cache force-cache only-if-cached".split(" "),
-va=["omit","same-origin","include"],wa="DELETE GET HEAD OPTIONS POST PUT".split(" "),ya=["GET","HEAD","POST"],xa=["same-origin","no-cors","cors"],za=["follow","error","manual"],da=[101,204,205,304],Ca=[301,302,303,307,308],Da="[object Int8Array];[object Uint8Array];[object Uint8ClampedArray];[object Int16Array];[object Uint16Array];[object Int32Array];[object Uint32Array];[object Float32Array];[object Float64Array]".split(";"),sa=aa.isView||function(a){return a&&-1<Da.indexOf(Object.prototype.toString.call(a))};
+va=["omit","same-origin","include"],wa="DELETE GET HEAD OPTIONS POST PUT".split(" "),ya=["GET","HEAD","POST"],xa=["same-origin","no-cors","cors","navigate"],za=["follow","error","manual"],da=[101,204,205,304],Ca=[301,302,303,307,308],Da="[object Int8Array];[object Uint8Array];[object Uint8ClampedArray];[object Int16Array];[object Uint16Array];[object Int32Array];[object Uint32Array];[object Float32Array];[object Float64Array]".split(";"),sa=aa.isView||function(a){return a&&-1<Da.indexOf(Object.prototype.toString.call(a))};
 n.prototype.append=function(a,b){if(2!==arguments.length)throw e("Invalid parameters to append");a=J(a);b=Y(b);this[A](a,b)||(this[p].has(a)?this[p].set(a,this[p].get(a)+", "+b):this[p].set(a,b))};n.prototype["delete"]=function(a){if(1!==arguments.length)throw e("Invalid parameters to delete");this[A](a,"invalid")||this[p].delete(J(a))};n.prototype.get=function(a){if(1!==arguments.length)throw e("Invalid parameters to get");a=J(a);var b=this[p].get(a);return void 0!==b?b:null};n.prototype.has=function(a){if(1!==
 arguments.length)throw e("Invalid parameters to has");return this[p].has(J(a))};n.prototype.set=function(a,b){if(2!==arguments.length)throw e("Invalid parameters to set");a=J(a);b=Y(b);this[A](a,b)||this[p].set(a,b)};n.prototype.forEach=function(a,b){var c=this;K.from(this[p].entries()).sort().forEach(function(d){a.call(b,d[1],d[0],c)})};n.prototype.keys=function(){return(new P(K.from(this[p].entries()).sort())).keys()};n.prototype.values=function(){return(new P(K.from(this[p].entries()).sort())).values()};
 n.prototype.entries=function(){return(new P(K.from(this[p].entries()).sort())).entries()};n.prototype[Ba]=n.prototype.entries;B.prototype.clone=function(){var a=null;null!==this[r]&&(a=ea(this[r],!0),this[r]=a[0],a=a[1]);return new B(this,{cloneBody:a,signal:this[F]})};W(B.prototype,{cache:{get:function(){return this[G]}},credentials:{get:function(){return this[H]}},headers:{get:function(){return this[x]}},integrity:{get:function(){return this[I]}},method:{get:function(){return this[D]}},mode:{get:function(){return this[t]}},
diff --git a/cobalt/fetch/fetch.js b/cobalt/fetch/fetch.js
index cae8152..5940740 100644
--- a/cobalt/fetch/fetch.js
+++ b/cobalt/fetch/fetch.js
@@ -146,8 +146,11 @@
   const VALID_METHODS_NOCORS = ['GET', 'HEAD', 'POST']
 
   // Modes that are allowed for RequestInit. Although 'navigate' is a valid
-  // request mode, it is not allowed in the RequestInit parameter.
-  const VALID_MODES = ['same-origin', 'no-cors', 'cors']
+  // request mode, it is not allowed in the RequestInit parameter. However,
+  // the request mode 'navigate' needs to be allowed since Request is
+  // polyfilled, so there is no other way to set request mode to 'navigae'
+  // even when it is correct to do so (requesting the main resource).
+  const VALID_MODES = ['same-origin', 'no-cors', 'cors', 'navigate']
 
   // Values that are allowed for Request redirect mode.
   const VALID_REDIRECT_MODES = ['follow', 'error', 'manual']
diff --git a/cobalt/h5vcc/BUILD.gn b/cobalt/h5vcc/BUILD.gn
index c31a775..7ecc1ee 100644
--- a/cobalt/h5vcc/BUILD.gn
+++ b/cobalt/h5vcc/BUILD.gn
@@ -85,6 +85,7 @@
     "//cobalt/trace_event",
     "//cobalt/watchdog",
     "//cobalt/web:dom_exception",
+    "//cobalt/worker",
     "//net",
     "//net:http_server",
     "//starboard",
diff --git a/cobalt/h5vcc/h5vcc.cc b/cobalt/h5vcc/h5vcc.cc
index 6f2b436..48c2ed5 100644
--- a/cobalt/h5vcc/h5vcc.cc
+++ b/cobalt/h5vcc/h5vcc.cc
@@ -33,7 +33,7 @@
   runtime_ = new H5vccRuntime(settings.event_dispatcher);
   settings_ =
       new H5vccSettings(settings.set_web_setting_func, settings.media_module,
-                        settings.network_module,
+                        settings.can_play_type_handler, settings.network_module,
 #if SB_IS(EVERGREEN)
                         settings.updater_module,
 #endif
diff --git a/cobalt/h5vcc/h5vcc.h b/cobalt/h5vcc/h5vcc.h
index 138a67b..7836da7 100644
--- a/cobalt/h5vcc/h5vcc.h
+++ b/cobalt/h5vcc/h5vcc.h
@@ -46,6 +46,7 @@
   struct Settings {
     Settings()
         : media_module(NULL),
+          can_play_type_handler(NULL),
           network_module(NULL),
 #if SB_IS(EVERGREEN)
           updater_module(NULL),
@@ -57,6 +58,7 @@
     }
     H5vccSettings::SetSettingFunc set_web_setting_func;
     media::MediaModule* media_module;
+    media::CanPlayTypeHandler* can_play_type_handler;
     network::NetworkModule* network_module;
 #if SB_IS(EVERGREEN)
     updater::UpdaterModule* updater_module;
diff --git a/cobalt/h5vcc/h5vcc_account_manager_internal.cc b/cobalt/h5vcc/h5vcc_account_manager_internal.cc
index 1c0a3c2..d3897c1 100644
--- a/cobalt/h5vcc/h5vcc_account_manager_internal.cc
+++ b/cobalt/h5vcc/h5vcc_account_manager_internal.cc
@@ -12,22 +12,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/h5vcc/h5vcc_account_manager_internal.h"
 
+#include <memory>
+
 #include "base/command_line.h"
 #include "base/memory/weak_ptr.h"
 #include "cobalt/browser/switches.h"
+#include "cobalt/web/environment_settings_helper.h"
 #include "starboard/user.h"
 
 namespace cobalt {
 namespace h5vcc {
 
-H5vccAccountManagerInternal::H5vccAccountManagerInternal()
+H5vccAccountManagerInternal::H5vccAccountManagerInternal(
+    script::EnvironmentSettings* environment_settings)
     : user_authorizer_(account::UserAuthorizer::Create()),
       owning_message_loop_(base::MessageLoop::current()),
-      thread_("AccountManager") {
+      thread_("AccountManager"),
+      environment_settings_(environment_settings) {
   thread_.Start();
 }
 
@@ -62,8 +65,9 @@
 void H5vccAccountManagerInternal::PostOperation(
     OperationType operation_type, const AccessTokenCallbackHolder& callback) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  auto* global_wrappable = web::get_global_wrappable(environment_settings_);
   AccessTokenCallbackReference* token_callback =
-      new AccessTokenCallbackHolder::Reference(this, callback);
+      new AccessTokenCallbackHolder::Reference(global_wrappable, callback);
   pending_callbacks_.push_back(
       std::unique_ptr<AccessTokenCallbackReference>(token_callback));
   thread_.message_loop()->task_runner()->PostTask(
diff --git a/cobalt/h5vcc/h5vcc_account_manager_internal.h b/cobalt/h5vcc/h5vcc_account_manager_internal.h
index 94585a0..bc32e6d 100644
--- a/cobalt/h5vcc/h5vcc_account_manager_internal.h
+++ b/cobalt/h5vcc/h5vcc_account_manager_internal.h
@@ -26,6 +26,7 @@
 #include "base/threading/thread_checker.h"
 #include "cobalt/account/user_authorizer.h"
 #include "cobalt/script/callback_function.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/script_value.h"
 #include "cobalt/script/wrappable.h"
 
@@ -50,7 +51,8 @@
   // "H5vccAccountManager".
   static bool IsSupported();
 
-  H5vccAccountManagerInternal();
+  explicit H5vccAccountManagerInternal(
+      script::EnvironmentSettings* environment_settings);
 
   // H5vccAccountManagerInternal interface.
   void GetAuthToken(const AccessTokenCallbackHolder& callback);
@@ -114,6 +116,8 @@
   // message loop gets flushed.
   base::Thread thread_;
 
+  script::EnvironmentSettings* environment_settings_;
+
   friend class scoped_refptr<H5vccAccountManagerInternal>;
   DISALLOW_COPY_AND_ASSIGN(H5vccAccountManagerInternal);
 };
diff --git a/cobalt/h5vcc/h5vcc_account_manager_internal.idl b/cobalt/h5vcc/h5vcc_account_manager_internal.idl
index 0c648a2..76a3364 100644
--- a/cobalt/h5vcc/h5vcc_account_manager_internal.idl
+++ b/cobalt/h5vcc/h5vcc_account_manager_internal.idl
@@ -13,7 +13,8 @@
 // limitations under the License.
 [
   Conditional=COBALT_ENABLE_ACCOUNT_MANAGER,
-  Constructor
+  Constructor,
+  ConstructorCallWith=EnvironmentSettings,
 ]
 interface H5vccAccountManagerInternal {
   void getAuthToken(AccessTokenCallback callback);
diff --git a/cobalt/h5vcc/h5vcc_settings.cc b/cobalt/h5vcc/h5vcc_settings.cc
index 1eef9e6..ca8a59c 100644
--- a/cobalt/h5vcc/h5vcc_settings.cc
+++ b/cobalt/h5vcc/h5vcc_settings.cc
@@ -19,16 +19,29 @@
 namespace cobalt {
 namespace h5vcc {
 
-H5vccSettings::H5vccSettings(const SetSettingFunc& set_web_setting_func,
-                             cobalt::media::MediaModule* media_module,
-                             cobalt::network::NetworkModule* network_module,
+namespace {
+// Only including needed video combinations for the moment.
+// option 0 disables all video codecs except h264
+// option 1 disables all video codecs except av1
+// option 2 disables all video codecs except vp9
+constexpr std::array<const char*, 3> kDisableCodecCombinations{
+    "av01;hev1;hvc1;vp09;vp8.vp9", "avc1;avc3;hev1;hvc1;vp09;vp8;vp9",
+    "av01;avc1;avc3;hev1;hvc1;vp8"};
+};  // namespace
+
+H5vccSettings::H5vccSettings(
+    const SetSettingFunc& set_web_setting_func,
+    cobalt::media::MediaModule* media_module,
+    cobalt::media::CanPlayTypeHandler* can_play_type_handler,
+    cobalt::network::NetworkModule* network_module,
 #if SB_IS(EVERGREEN)
-                             cobalt::updater::UpdaterModule* updater_module,
+    cobalt::updater::UpdaterModule* updater_module,
 #endif
-                             web::NavigatorUAData* user_agent_data,
-                             script::GlobalEnvironment* global_environment)
+    web::NavigatorUAData* user_agent_data,
+    script::GlobalEnvironment* global_environment)
     : set_web_setting_func_(set_web_setting_func),
       media_module_(media_module),
+      can_play_type_handler_(can_play_type_handler),
       network_module_(network_module),
 #if SB_IS(EVERGREEN)
       updater_module_(updater_module),
@@ -39,6 +52,7 @@
 
 bool H5vccSettings::Set(const std::string& name, int32 value) const {
   const char kMediaPrefix[] = "Media.";
+  const char kDisableMediaCodec[] = "DisableMediaCodec";
   const char kNavigatorUAData[] = "NavigatorUAData";
   const char kQUIC[] = "QUIC";
 
@@ -46,6 +60,13 @@
   const char kUpdaterMinFreeSpaceBytes[] = "Updater.MinFreeSpaceBytes";
 #endif
 
+  if (name == kDisableMediaCodec &&
+      value < static_cast<int32>(kDisableCodecCombinations.size())) {
+    can_play_type_handler_->SetDisabledMediaCodecs(
+        kDisableCodecCombinations[value]);
+    return true;
+  }
+
   if (set_web_setting_func_ && set_web_setting_func_.Run(name, value)) {
     return true;
   }
diff --git a/cobalt/h5vcc/h5vcc_settings.h b/cobalt/h5vcc/h5vcc_settings.h
index 83a3adb..e6e5b4a 100644
--- a/cobalt/h5vcc/h5vcc_settings.h
+++ b/cobalt/h5vcc/h5vcc_settings.h
@@ -40,6 +40,7 @@
 
   H5vccSettings(const SetSettingFunc& set_web_setting_func,
                 cobalt::media::MediaModule* media_module,
+                cobalt::media::CanPlayTypeHandler* can_play_type_handler,
                 cobalt::network::NetworkModule* network_module,
 #if SB_IS(EVERGREEN)
                 cobalt::updater::UpdaterModule* updater_module,
@@ -57,6 +58,7 @@
  private:
   const SetSettingFunc set_web_setting_func_;
   cobalt::media::MediaModule* media_module_ = nullptr;
+  cobalt::media::CanPlayTypeHandler* can_play_type_handler_ = nullptr;
   cobalt::network::NetworkModule* network_module_ = nullptr;
 #if SB_IS(EVERGREEN)
   cobalt::updater::UpdaterModule* updater_module_ = nullptr;
diff --git a/cobalt/h5vcc/h5vcc_storage.cc b/cobalt/h5vcc/h5vcc_storage.cc
index 301c0f2..6c83852 100644
--- a/cobalt/h5vcc/h5vcc_storage.cc
+++ b/cobalt/h5vcc/h5vcc_storage.cc
@@ -25,6 +25,7 @@
 #include "cobalt/cache/cache.h"
 #include "cobalt/persistent_storage/persistent_settings.h"
 #include "cobalt/storage/storage_manager.h"
+#include "cobalt/worker/service_worker_consts.h"
 #include "net/base/completion_once_callback.h"
 #include "net/disk_cache/cobalt/cobalt_backend_impl.h"
 #include "net/disk_cache/cobalt/resource_type.h"
@@ -68,13 +69,32 @@
   return response;
 }
 
+void DeleteCacheResourceTypeDirectory(disk_cache::ResourceType type) {
+  auto metadata = disk_cache::kTypeMetadata[type];
+  std::vector<char> cache_dir(kSbFileMaxPath + 1, 0);
+  SbSystemGetPath(kSbSystemPathCacheDirectory, cache_dir.data(),
+                  kSbFileMaxPath);
+  base::FilePath cache_type_dir =
+      base::FilePath(cache_dir.data())
+          .Append(FILE_PATH_LITERAL(metadata.directory));
+  starboard::SbFileDeleteRecursive(cache_type_dir.value().data(), true);
+}
+
 void ClearCacheHelper(disk_cache::Backend* backend) {
   backend->DoomAllEntries(base::DoNothing());
+
+
+  for (int type_index = 0; type_index < disk_cache::kTypeCount; type_index++) {
+    DeleteCacheResourceTypeDirectory(
+        static_cast<disk_cache::ResourceType>(type_index));
+  }
 }
 
 void ClearCacheOfTypeHelper(disk_cache::ResourceType type,
                             disk_cache::CobaltBackendImpl* backend) {
   backend->DoomAllEntriesOfType(type, base::DoNothing());
+
+  DeleteCacheResourceTypeDirectory(type);
 }
 
 }  // namespace
@@ -377,6 +397,35 @@
   cobalt::cache::Cache::GetInstance()->DeleteAll();
 }
 
+void H5vccStorage::ClearCacheOfType(int type_index) {
+  if (type_index < 0 || type_index > disk_cache::kTypeCount) {
+    DLOG(INFO)
+        << "Invalid type_index, out of bounds of disk_cache::kTypeMetadata";
+    return;
+  }
+  disk_cache::ResourceType type =
+      static_cast<disk_cache::ResourceType>(type_index);
+  if (ValidatedCacheBackend()) {
+    network_module_->task_runner()->PostTask(
+        FROM_HERE, base::Bind(&ClearCacheOfTypeHelper, type,
+                              base::Unretained(cache_backend_)));
+  }
+  cobalt::cache::Cache::GetInstance()->Delete(type);
+}
+
+void H5vccStorage::ClearServiceWorkerCache() {
+  ClearCacheOfType(
+      static_cast<int>(disk_cache::ResourceType::kServiceWorkerScript));
+  // Add deletion of service worker persistent settings file
+  std::vector<char> storage_dir(kSbFileMaxPath, 0);
+  SbSystemGetPath(kSbSystemPathCacheDirectory, storage_dir.data(),
+                  kSbFileMaxPath);
+  std::string service_worker_file_path =
+      std::string(storage_dir.data()) + kSbFileSepString +
+      worker::ServiceWorkerConsts::kSettingsJson;
+  SbFileDelete(service_worker_file_path.c_str());
+}
+
 bool H5vccStorage::ValidatedCacheBackend() {
   if (!http_cache_) {
     return false;
diff --git a/cobalt/h5vcc/h5vcc_storage.h b/cobalt/h5vcc/h5vcc_storage.h
index 54b06c8..1ba86e3 100644
--- a/cobalt/h5vcc/h5vcc_storage.h
+++ b/cobalt/h5vcc/h5vcc_storage.h
@@ -66,6 +66,10 @@
 
   void ClearCache();
 
+  void ClearCacheOfType(int type_index);
+
+  void ClearServiceWorkerCache();
+
   DEFINE_WRAPPABLE_TYPE(H5vccStorage);
 
  private:
diff --git a/cobalt/h5vcc/h5vcc_storage.idl b/cobalt/h5vcc/h5vcc_storage.idl
index f1a15d2..9d4b1aa 100644
--- a/cobalt/h5vcc/h5vcc_storage.idl
+++ b/cobalt/h5vcc/h5vcc_storage.idl
@@ -29,4 +29,6 @@
   void disableCache();
 
   void clearCache();
+  void clearCacheOfType(octet type_index);
+  void clearServiceWorkerCache();
 };
diff --git a/cobalt/layout/box.cc b/cobalt/layout/box.cc
index 2e48d7d..99f53dc 100644
--- a/cobalt/layout/box.cc
+++ b/cobalt/layout/box.cc
@@ -2159,54 +2159,34 @@
   return false;
 }
 
-bool Box::ApplyTransformActionToCoordinate(TransformAction action,
-                                           math::Vector2dF* coordinate) const {
+bool Box::ApplyTransformActionToCoordinate(math::Vector2dF* coordinate) const {
   std::vector<math::Vector2dF> coordinate_vector;
   coordinate_vector.push_back(*coordinate);
-  bool result = ApplyTransformActionToCoordinates(action, &coordinate_vector);
+  bool result = ApplyTransformActionToCoordinates(&coordinate_vector);
   *coordinate = coordinate_vector[0];
   return result;
 }
 
 bool Box::ApplyTransformActionToCoordinates(
-    TransformAction action, std::vector<math::Vector2dF>* coordinates) const {
+    std::vector<math::Vector2dF>* coordinates) const {
   const scoped_refptr<cssom::PropertyValue>& transform =
       computed_style()->transform();
   if (transform == cssom::KeywordValue::GetNone()) {
     return true;
   }
 
-  // The border box offset is calculated in two steps because we want to
-  // stop at the second transform and not the first (which is this box).
   Vector2dLayoutUnit containing_block_offset_from_root =
       GetContainingBlockOffsetFromRoot(true /*transform_forms_root*/);
 
-  // The transform rect always includes the offset from the containing block.
-  // However, in the case where the action is entering the transform, the full
-  // offset from the root needs to be included in the transform.
-  Vector2dLayoutUnit transform_rect_offset =
-      margin_box_offset_from_containing_block() +
-      GetBorderBoxOffsetFromMarginBox();
-  if (action == kEnterTransform) {
-    transform_rect_offset += containing_block_offset_from_root;
-  }
+  auto matrix = GetCSSTransformForBoxWithPredefinedOffset(
+      containing_block_offset_from_root);
 
-  // Transform the coordinates.
-  math::Matrix3F matrix = GetCSSTransform(
-      transform.get(), computed_style()->transform_origin().get(),
-      math::RectF(transform_rect_offset.x().toFloat(),
-                  transform_rect_offset.y().toFloat(),
-                  GetBorderBoxWidth().toFloat(),
-                  GetBorderBoxHeight().toFloat()),
-      ComputeUiNavFocusForTransform());
   if (!matrix.IsIdentity()) {
-    if (action == kEnterTransform) {
-      matrix = matrix.Inverse();
-      // The matrix is not invertible. Return that applying the transform
-      // failed.
-      if (matrix.IsZeros()) {
-        return false;
-      }
+    matrix = matrix.Inverse();
+    // The matrix is not invertible. Return that applying the transform
+    // failed.
+    if (matrix.IsZeros()) {
+      return false;
     }
 
     for (std::vector<math::Vector2dF>::iterator coordinate_iter =
@@ -2230,15 +2210,61 @@
            coordinates->begin();
        coordinate_iter != coordinates->end(); ++coordinate_iter) {
     math::Vector2dF& coordinate = *coordinate_iter;
-    if (action == kEnterTransform) {
-      coordinate -= containing_block_offset_from_root_as_float;
-    } else {
-      coordinate += containing_block_offset_from_root_as_float;
-    }
+    coordinate -= containing_block_offset_from_root_as_float;
   }
   return true;
 }
 
+math::Matrix3F Box::GetCSSTransformForBox() const {
+  if (!IsTransformed()) {
+    return math::Matrix3F::Identity();
+  }
+
+  Vector2dLayoutUnit containing_block_offset_from_root =
+      GetContainingBlockOffsetFromRoot(true /*transform_forms_root*/);
+  return GetCSSTransformForBoxWithPredefinedOffset(
+      containing_block_offset_from_root);
+}
+
+math::Matrix3F Box::GetCSSTransformForBoxWithPredefinedOffset(
+    Vector2dLayoutUnit containing_block_offset_from_root) const {
+  if (!IsTransformed()) {
+    return math::Matrix3F::Identity();
+  }
+
+  // The border box offset is calculated in two steps because we want to
+  // stop at the second transform and not the first (which is the containing
+  // block).
+  //
+  // The transform rect always includes the offset from the containing block.
+  // However, in the case where the action is entering the transform, the full
+  // offset from the root needs to be included in the transform.
+  Vector2dLayoutUnit transform_rect_offset =
+      margin_box_offset_from_containing_block() +
+      GetBorderBoxOffsetFromMarginBox() + containing_block_offset_from_root;
+
+  // Transform the coordinates.
+  math::Matrix3F matrix =
+      GetCSSTransform(computed_style()->transform().get(),
+                      computed_style()->transform_origin().get(),
+                      math::RectF(transform_rect_offset.x().toFloat(),
+                                  transform_rect_offset.y().toFloat(),
+                                  GetBorderBoxWidth().toFloat(),
+                                  GetBorderBoxHeight().toFloat()),
+                      ComputeUiNavFocusForTransform());
+
+  return matrix;
+}
+
+bool Box::CoordinateCanTarget(const math::Vector2dF* coordinate) const {
+  if (!cssom::IsOverflowCropped(computed_style())) {
+    return true;
+  }
+  const Vector2dLayoutUnit layout_coordinate(LayoutUnit(coordinate->x()),
+                                             LayoutUnit(coordinate->y()));
+  return IsUnderCoordinate(layout_coordinate);
+}
+
 void Box::AddIntersectionObserverRootsAndTargets(
     BoxIntersectionObserverModule::IntersectionObserverRootVector&& roots,
     BoxIntersectionObserverModule::IntersectionObserverTargetVector&& targets) {
diff --git a/cobalt/layout/box.h b/cobalt/layout/box.h
index 9c17a87..05449cd 100644
--- a/cobalt/layout/box.h
+++ b/cobalt/layout/box.h
@@ -268,6 +268,8 @@
   // Returns the offset from root to this box's containing block.
   Vector2dLayoutUnit GetContainingBlockOffsetFromRoot(
       bool transform_forms_root) const;
+  math::Matrix3F GetCSSTransformForBoxWithPredefinedOffset(
+      Vector2dLayoutUnit containing_block_offset_from_root) const;
 
   // Returns the offset from the containing block (which can be either the
   // containing block's content box or padding box) to its content box.
@@ -705,10 +707,12 @@
   // Applies the specified transform action to the provided coordinates.
   // Returns false if the transform is not invertible and the action requires
   // it being inverted.
-  bool ApplyTransformActionToCoordinate(TransformAction action,
-                                        math::Vector2dF* coordinate) const;
+  bool ApplyTransformActionToCoordinate(math::Vector2dF* coordinate) const;
   bool ApplyTransformActionToCoordinates(
-      TransformAction action, std::vector<math::Vector2dF>* coordinates) const;
+      std::vector<math::Vector2dF>* coordinates) const;
+
+  bool CoordinateCanTarget(const math::Vector2dF* coordinate) const;
+  math::Matrix3F GetCSSTransformForBox() const;
 
   // Intended to be set to false on the initial containing block, this indicates
   // that when the background color is rendered, it will be blended with what,
diff --git a/cobalt/layout/topmost_event_target.cc b/cobalt/layout/topmost_event_target.cc
index fec6280..d7ce92e 100644
--- a/cobalt/layout/topmost_event_target.cc
+++ b/cobalt/layout/topmost_event_target.cc
@@ -14,6 +14,8 @@
 
 #include "cobalt/layout/topmost_event_target.h"
 
+#include <memory>
+#include <utility>
 #include <vector>
 
 #include "base/optional.h"
@@ -82,36 +84,40 @@
   return NULL;
 }
 
-scoped_refptr<dom::HTMLElement> FindFirstElementWithScrollType(
-    scoped_refptr<dom::HTMLElement> target_element,
-    ui_navigation::scroll_engine::ScrollType major_scroll_axis,
-    bool scrolling_right, bool scrolling_down) {
-  auto current_element = target_element;
-  bool scrolling_left = !scrolling_right;
-  bool scrolling_up = !scrolling_down;
-  bool horizontal_scroll_axis =
-      major_scroll_axis == ui_navigation::scroll_engine::ScrollType::Horizontal;
-  bool vertical_scroll_axis =
-      major_scroll_axis == ui_navigation::scroll_engine::ScrollType::Vertical;
+std::unique_ptr<dom::PossibleScrollTargets> FindPossibleScrollTargets(
+    scoped_refptr<dom::Element> target_element) {
+  std::unique_ptr<dom::PossibleScrollTargets> possible_scroll_targets =
+      std::make_unique<dom::PossibleScrollTargets>();
 
-  while (true) {
+  for (auto current_element = target_element; !!current_element;
+       current_element = current_element->parent_element()) {
+    auto current_html_element = current_element->AsHTMLElement();
+    if (!current_html_element) {
+      continue;
+    }
+
+    bool pointer_events_enabled =
+        current_html_element->computed_style()->pointer_events() !=
+        cssom::KeywordValue::GetNone();
+    if (!pointer_events_enabled) {
+      continue;
+    }
+
+    auto current_ui_nav_item = current_html_element->GetUiNavItem();
+    if (!current_ui_nav_item) {
+      continue;
+    }
+
     float scroll_top_lower_bound;
     float scroll_left_lower_bound;
     float scroll_top_upper_bound;
     float scroll_left_upper_bound;
-    float offset_x;
-    float offset_y;
-
-    if (!current_element->parent_element()) {
-      break;
-    }
-    current_element = current_element->parent_element()->AsHTMLElement();
-    auto current_ui_nav_item = current_element->GetUiNavItem();
-    if (!current_ui_nav_item) continue;
-
     current_ui_nav_item->GetBounds(
         &scroll_top_lower_bound, &scroll_left_lower_bound,
         &scroll_top_upper_bound, &scroll_left_upper_bound);
+
+    float offset_x;
+    float offset_y;
     current_ui_nav_item->GetContentOffset(&offset_x, &offset_y);
 
     bool can_scroll_left = scroll_left_lower_bound < offset_x;
@@ -119,197 +125,72 @@
     bool can_scroll_up = scroll_top_lower_bound < offset_y;
     bool can_scroll_down = scroll_top_upper_bound > offset_y;
 
-    if ((scrolling_left && can_scroll_left && horizontal_scroll_axis) ||
-        (scrolling_right && can_scroll_right && horizontal_scroll_axis) ||
-        (scrolling_up && can_scroll_up && vertical_scroll_axis) ||
-        (scrolling_down && can_scroll_down && vertical_scroll_axis)) {
-      return current_element;
+    if (can_scroll_left && !possible_scroll_targets->left) {
+      possible_scroll_targets->left = current_html_element;
     }
+    if (can_scroll_right && !possible_scroll_targets->right) {
+      possible_scroll_targets->right = current_html_element;
+    }
+    if (can_scroll_up && !possible_scroll_targets->up) {
+      possible_scroll_targets->up = current_html_element;
+    }
+    if (can_scroll_down && !possible_scroll_targets->down) {
+      possible_scroll_targets->down = current_html_element;
+    }
+  }
+  return possible_scroll_targets;
+}
+
+scoped_refptr<dom::HTMLElement> FindFirstElementWithScrollType(
+    dom::PossibleScrollTargets* possible_scroll_targets,
+    ui_navigation::scroll_engine::ScrollType major_scroll_axis,
+    bool scrolling_right, bool scrolling_down) {
+  bool scrolling_left = !scrolling_right;
+  bool scrolling_up = !scrolling_down;
+  bool horizontal_scroll_axis =
+      major_scroll_axis == ui_navigation::scroll_engine::ScrollType::Horizontal;
+  bool vertical_scroll_axis =
+      major_scroll_axis == ui_navigation::scroll_engine::ScrollType::Vertical;
+
+  if (scrolling_left && horizontal_scroll_axis) {
+    return possible_scroll_targets->left;
+  } else if (scrolling_right && horizontal_scroll_axis) {
+    return possible_scroll_targets->right;
+  } else if (scrolling_up && vertical_scroll_axis) {
+    return possible_scroll_targets->up;
+  } else if (scrolling_down && vertical_scroll_axis) {
+    return possible_scroll_targets->down;
   }
   return nullptr;
 }
 
 bool TransformCanBeAppliedToBox(const Box* box, math::Vector2dF* coordinate) {
-  return !box->IsTransformed() || box->ApplyTransformActionToCoordinate(
-                                      Box::kEnterTransform, coordinate);
+  return !box->IsTransformed() ||
+         box->ApplyTransformActionToCoordinate(coordinate);
 }
 
-bool CoordinateCanTargetBox(const Box* box, math::Vector2dF* coordinate) {
-  if (!cssom::IsOverflowCropped(box->computed_style())) {
-    return true;
+struct CanTargetBox {
+  const math::Vector2dF* coordinate;
+  explicit CanTargetBox(const math::Vector2dF* coordinate)
+      : coordinate(coordinate) {}
+  bool operator()(const Box* box) const {
+    return box->CoordinateCanTarget(coordinate);
   }
-  LayoutUnit coordinate_x(coordinate->x());
-  LayoutUnit coordinate_y(coordinate->y());
-
-  bool transform_forms_root = false;
-  auto padding_box = box->GetClampedPaddingBox(transform_forms_root);
-  return padding_box.Contains(coordinate_x, coordinate_y);
-}
+};
 
 bool ShouldConsiderElementAndChildren(dom::Element* element,
                                       math::Vector2dF* coordinate) {
   LayoutBoxes* layout_boxes = GetLayoutBoxesIfNotEmpty(element);
-  const Box* box = layout_boxes->boxes().front();
+  const Boxes boxes = layout_boxes->boxes();
+  const Box* box = boxes.front();
   if (!box->computed_style()) {
     return true;
   }
 
   return TransformCanBeAppliedToBox(box, coordinate) &&
-         CoordinateCanTargetBox(box, coordinate);
+         std::any_of(boxes.begin(), boxes.end(), CanTargetBox(coordinate));
 }
 
-}  // namespace
-void TopmostEventTarget::ConsiderElement(dom::Element* element,
-                                         const math::Vector2dF& coordinate) {
-  if (!element) return;
-  math::Vector2dF element_coordinate(coordinate);
-  LayoutBoxes* layout_boxes = GetLayoutBoxesIfNotEmpty(element);
-  if (layout_boxes) {
-    if (!ShouldConsiderElementAndChildren(element, &element_coordinate)) {
-      return;
-    }
-    scoped_refptr<dom::HTMLElement> html_element = element->AsHTMLElement();
-    if (html_element && html_element->CanBeDesignatedByPointerIfDisplayed()) {
-      ConsiderBoxes(html_element, layout_boxes, element_coordinate);
-    }
-  }
-
-  for (dom::Element* child_element = element->first_element_child();
-       child_element; child_element = child_element->next_element_sibling()) {
-    ConsiderElement(child_element, element_coordinate);
-  }
-}
-
-void TopmostEventTarget::ConsiderBoxes(
-    const scoped_refptr<dom::HTMLElement>& html_element,
-    LayoutBoxes* layout_boxes, const math::Vector2dF& coordinate) {
-  const Boxes& boxes = layout_boxes->boxes();
-  Vector2dLayoutUnit layout_coordinate(LayoutUnit(coordinate.x()),
-                                       LayoutUnit(coordinate.y()));
-  for (Boxes::const_iterator box_iterator = boxes.begin();
-       box_iterator != boxes.end(); ++box_iterator) {
-    Box* box = *box_iterator;
-    do {
-      if (box->IsUnderCoordinate(layout_coordinate)) {
-        Box::RenderSequence render_sequence = box->GetRenderSequence();
-        if (Box::IsRenderedLater(render_sequence, render_sequence_)) {
-          html_element_ = html_element;
-          box_ = box;
-          render_sequence_.swap(render_sequence);
-        }
-      }
-      box = box->GetSplitSibling();
-    } while (box != NULL);
-  }
-}
-
-void TopmostEventTarget::CancelScrollsInParentNavItems(
-    scoped_refptr<dom::HTMLElement> target_element) {
-  // Cancel any scrolls in the tree.
-  std::vector<scoped_refptr<ui_navigation::NavItem>> scrolls_to_cancel;
-  auto current_element = target_element;
-  while (true) {
-    if (!current_element->parent_element()) {
-      break;
-    }
-    current_element = current_element->parent_element()->AsHTMLElement();
-    auto current_ui_nav_item = current_element->GetUiNavItem();
-    if (current_ui_nav_item) {
-      scrolls_to_cancel.push_back(current_ui_nav_item);
-    }
-  }
-
-  scroll_engine_->thread()->message_loop()->task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&ui_navigation::scroll_engine::ScrollEngine::
-                     CancelActiveScrollsForNavItems,
-                 base::Unretained(scroll_engine_), scrolls_to_cancel));
-}
-
-void TopmostEventTarget::HandleScrollState(
-    scoped_refptr<dom::HTMLElement> target_element,
-    const dom::PointerEvent* pointer_event, dom::PointerState* pointer_state,
-    dom::PointerEventInit* event_init) {
-  // On pointer down, cancel any scrolls happening for UI nav items above
-  // that element. Additionally, save the pointer coordinates.
-  //
-  // On pointer move, check if we've reached the threshold to start
-  // scrolling. If we have, find the first scroll container we can scroll.
-  // Then send that scroll container, initial pointer event coords, current
-  // pointer event coords, scroll direction.
-  bool pointer_type_is_accepted = pointer_event->pointer_type() == "mouse" ||
-                                  pointer_event->pointer_type() == "pen" ||
-                                  pointer_event->pointer_type() == "touch";
-  if (!scroll_engine_ || !pointer_event || !pointer_type_is_accepted) {
-    return;
-  }
-
-  bool should_clear_pointer_state =
-      pointer_event->type() == base::Tokens::pointerup();
-
-  auto pointer_id = pointer_event->pointer_id();
-  auto pointer_coordinates =
-      math::Vector2dF(pointer_event->client_x(), pointer_event->client_y());
-
-  if (pointer_event->type() == base::Tokens::pointerdown()) {
-    CancelScrollsInParentNavItems(target_element);
-    pointer_state->SetClientCoordinates(pointer_id, pointer_coordinates);
-    pointer_state->SetClientTimeStamp(pointer_id, pointer_event->time_stamp());
-    return;
-  }
-
-  auto initial_coordinates = pointer_state->GetClientCoordinates(pointer_id);
-  auto initial_time_stamp = pointer_state->GetClientTimeStamp(pointer_id);
-  if (pointer_event->type() == base::Tokens::pointermove() &&
-      initial_coordinates.has_value() && initial_time_stamp.has_value()) {
-    cobalt::math::Vector2dF drag_vector =
-        initial_coordinates.value() - pointer_coordinates;
-    float x = drag_vector.x();
-    float y = drag_vector.y();
-
-    if (drag_vector.Length() >=
-        ui_navigation::scroll_engine::kDragDistanceThreshold) {
-      // Get major scroll direction.
-      ui_navigation::scroll_engine::ScrollType scroll_type =
-          std::abs(x) > std::abs(y)
-              ? ui_navigation::scroll_engine::ScrollType::Horizontal
-              : ui_navigation::scroll_engine::ScrollType::Vertical;
-      auto element_to_scroll = FindFirstElementWithScrollType(
-          target_element, scroll_type, x > 0, y > 0);
-      if (!element_to_scroll) {
-        return;
-      }
-
-      const scoped_refptr<dom::Window>& view = event_init->view();
-      element_to_scroll->DispatchEvent(new dom::PointerEvent(
-          base::Tokens::pointercancel(), web::Event::kBubbles,
-          web::Event::kNotCancelable, view, *event_init));
-      element_to_scroll->DispatchEvent(
-          new dom::PointerEvent(base::Tokens::pointerout(), view, *event_init));
-      element_to_scroll->DispatchEvent(new dom::PointerEvent(
-          base::Tokens::pointerleave(), web::Event::kNotBubbles,
-          web::Event::kNotCancelable, view, *event_init));
-      pointer_state->SetWasCancelled(pointer_id);
-
-      should_clear_pointer_state = true;
-      scroll_engine_->thread()->message_loop()->task_runner()->PostTask(
-          FROM_HERE,
-          base::Bind(
-              &ui_navigation::scroll_engine::ScrollEngine::HandleScrollStart,
-              base::Unretained(scroll_engine_),
-              element_to_scroll->GetUiNavItem(), scroll_type, pointer_id,
-              initial_coordinates.value(), initial_time_stamp.value(),
-              pointer_coordinates, pointer_event->time_stamp()));
-    }
-  }
-
-  if (should_clear_pointer_state) {
-    pointer_state->ClearClientCoordinates(pointer_id);
-    pointer_state->ClearTimeStamp(pointer_id);
-  }
-}
-
-namespace {
 // Return the nearest common ancestor of previous_element and target_element
 scoped_refptr<dom::Element> GetNearestCommonAncestor(
     scoped_refptr<dom::HTMLElement> previous_element,
@@ -398,6 +279,15 @@
   }
 }
 
+void SendStateChangeLeaveEvents(
+    bool is_pointer_event, scoped_refptr<dom::HTMLElement> previous_element,
+    dom::PointerEventInit* event_init) {
+  scoped_refptr<dom::HTMLElement> target_element = nullptr;
+  scoped_refptr<dom::Element> nearest_common_ancestor = nullptr;
+  SendStateChangeLeaveEvents(is_pointer_event, previous_element, target_element,
+                             nearest_common_ancestor, event_init);
+}
+
 void SendStateChangeEnterEvents(
     bool is_pointer_event, scoped_refptr<dom::HTMLElement> previous_element,
     scoped_refptr<dom::HTMLElement> target_element,
@@ -520,8 +410,190 @@
     event_init->set_is_primary(pointer_event->is_primary());
   }
 }
+
+void DispatchPointerEventsForScrollStart(
+    scoped_refptr<dom::HTMLElement> element,
+    dom::PointerEventInit* event_init) {
+  const scoped_refptr<dom::Window>& view = event_init->view();
+  element->DispatchEvent(
+      new dom::PointerEvent(base::Tokens::pointercancel(), web::Event::kBubbles,
+                            web::Event::kNotCancelable, view, *event_init));
+  bool is_pointer_event = true;
+  SendStateChangeLeaveEvents(is_pointer_event, element, event_init);
+}
+
+math::Matrix3F GetCompleteTransformMatrix(dom::Element* element) {
+  auto complete_matrix = math::Matrix3F::Identity();
+  auto current_element = element;
+  while (current_element) {
+    LayoutBoxes* layout_boxes = GetLayoutBoxesIfNotEmpty(current_element);
+    const Box* box = layout_boxes->boxes().front();
+    complete_matrix = complete_matrix * box->GetCSSTransformForBox();
+    current_element = current_element->parent_element();
+  }
+  return complete_matrix;
+}
+
 }  // namespace
 
+void TopmostEventTarget::ConsiderElement(dom::Element* element,
+                                         const math::Vector2dF& coordinate) {
+  if (!element) return;
+  math::Vector2dF element_coordinate(coordinate);
+  LayoutBoxes* layout_boxes = GetLayoutBoxesIfNotEmpty(element);
+  if (layout_boxes) {
+    if (!ShouldConsiderElementAndChildren(element, &element_coordinate)) {
+      return;
+    }
+    scoped_refptr<dom::HTMLElement> html_element = element->AsHTMLElement();
+    if (html_element && html_element->CanBeDesignatedByPointerIfDisplayed()) {
+      ConsiderBoxes(html_element, layout_boxes, element_coordinate);
+    }
+  }
+
+  for (dom::Element* child_element = element->first_element_child();
+       child_element; child_element = child_element->next_element_sibling()) {
+    ConsiderElement(child_element, element_coordinate);
+  }
+}
+
+void TopmostEventTarget::ConsiderBoxes(
+    const scoped_refptr<dom::HTMLElement>& html_element,
+    LayoutBoxes* layout_boxes, const math::Vector2dF& coordinate) {
+  const Boxes& boxes = layout_boxes->boxes();
+  Vector2dLayoutUnit layout_coordinate(LayoutUnit(coordinate.x()),
+                                       LayoutUnit(coordinate.y()));
+  for (Boxes::const_iterator box_iterator = boxes.begin();
+       box_iterator != boxes.end(); ++box_iterator) {
+    Box* box = *box_iterator;
+    do {
+      if (box->IsUnderCoordinate(layout_coordinate)) {
+        Box::RenderSequence render_sequence = box->GetRenderSequence();
+        if (Box::IsRenderedLater(render_sequence, render_sequence_)) {
+          html_element_ = html_element;
+          box_ = box;
+          render_sequence_.swap(render_sequence);
+        }
+      }
+      box = box->GetSplitSibling();
+    } while (box != NULL);
+  }
+}
+
+void TopmostEventTarget::CancelScrollsInParentNavItems(
+    scoped_refptr<dom::HTMLElement> target_element) {
+  // Cancel any scrolls in the tree.
+  std::vector<scoped_refptr<ui_navigation::NavItem>> scrolls_to_cancel;
+  auto current_element = target_element;
+  while (true) {
+    if (!current_element->parent_element()) {
+      break;
+    }
+    current_element = current_element->parent_element()->AsHTMLElement();
+    auto current_ui_nav_item = current_element->GetUiNavItem();
+    if (current_ui_nav_item) {
+      scrolls_to_cancel.push_back(current_ui_nav_item);
+    }
+  }
+
+  scroll_engine_->thread()->message_loop()->task_runner()->PostTask(
+      FROM_HERE,
+      base::Bind(&ui_navigation::scroll_engine::ScrollEngine::
+                     CancelActiveScrollsForNavItems,
+                 base::Unretained(scroll_engine_), scrolls_to_cancel));
+}
+
+void TopmostEventTarget::HandleScrollState(
+    scoped_refptr<dom::HTMLElement> target_element,
+    const dom::PointerEvent* pointer_event, dom::PointerState* pointer_state,
+    dom::PointerEventInit* event_init) {
+  // On pointer down, cancel any scrolls happening for UI nav items above
+  // that element. Additionally, save the pointer coordinates.
+  //
+  // On pointer move, check if we've reached the threshold to start
+  // scrolling. If we have, find the first scroll container we can scroll.
+  // Then send that scroll container, initial pointer event coords, current
+  // pointer event coords, scroll direction.
+  bool pointer_type_is_accepted = pointer_event->pointer_type() == "mouse" ||
+                                  pointer_event->pointer_type() == "pen" ||
+                                  pointer_event->pointer_type() == "touch";
+  if (!scroll_engine_ || !pointer_event || !pointer_type_is_accepted) {
+    return;
+  }
+
+  bool should_clear_pointer_state =
+      pointer_event->type() == base::Tokens::pointerup();
+
+  auto pointer_id = pointer_event->pointer_id();
+  auto pointer_coordinates =
+      math::Vector2dF(pointer_event->client_x(), pointer_event->client_y());
+
+  if (pointer_event->type() == base::Tokens::pointerdown()) {
+    CancelScrollsInParentNavItems(target_element);
+
+    auto initial_possible_scroll_targets =
+        FindPossibleScrollTargets(target_element);
+    pointer_state->SetPossibleScrollTargets(
+        pointer_id, std::move(initial_possible_scroll_targets));
+
+    auto transform_matrix = GetCompleteTransformMatrix(target_element.get());
+    pointer_state->SetClientTransformMatrix(pointer_id, transform_matrix);
+    pointer_state->SetClientCoordinates(pointer_id, pointer_coordinates);
+    pointer_state->SetClientTimeStamp(pointer_id, pointer_event->time_stamp());
+    return;
+  }
+
+  auto initial_coordinates = pointer_state->GetClientCoordinates(pointer_id);
+  auto initial_time_stamp = pointer_state->GetClientTimeStamp(pointer_id);
+  auto possible_scroll_targets =
+      pointer_state->GetPossibleScrollTargets(pointer_id);
+  auto initial_transform = pointer_state->GetClientTransformMatrix(pointer_id);
+
+  if (pointer_event->type() == base::Tokens::pointermove() &&
+      initial_coordinates.has_value() && initial_time_stamp.has_value() &&
+      possible_scroll_targets) {
+    cobalt::math::Vector2dF drag_vector =
+        initial_coordinates.value() - pointer_coordinates;
+    float x = drag_vector.x();
+    float y = drag_vector.y();
+
+    if (drag_vector.Length() >=
+        ui_navigation::scroll_engine::kDragDistanceThreshold) {
+      // Get major scroll direction.
+      ui_navigation::scroll_engine::ScrollType scroll_type =
+          std::abs(x) > std::abs(y)
+              ? ui_navigation::scroll_engine::ScrollType::Horizontal
+              : ui_navigation::scroll_engine::ScrollType::Vertical;
+      auto element_to_scroll = FindFirstElementWithScrollType(
+          possible_scroll_targets, scroll_type, x > 0, y > 0);
+      if (!element_to_scroll) {
+        return;
+      }
+
+      DispatchPointerEventsForScrollStart(target_element, event_init);
+      pointer_state->SetWasCancelled(pointer_id);
+
+      should_clear_pointer_state = true;
+      scroll_engine_->thread()->message_loop()->task_runner()->PostTask(
+          FROM_HERE,
+          base::Bind(
+              &ui_navigation::scroll_engine::ScrollEngine::HandleScrollStart,
+              base::Unretained(scroll_engine_),
+              element_to_scroll->GetUiNavItem(), scroll_type, pointer_id,
+              initial_coordinates.value(), initial_time_stamp.value(),
+              pointer_coordinates, pointer_event->time_stamp(),
+              initial_transform));
+    }
+  }
+
+  if (should_clear_pointer_state) {
+    pointer_state->ClearPossibleScrollTargets(pointer_id);
+    pointer_state->ClearClientCoordinates(pointer_id);
+    pointer_state->ClearTimeStamp(pointer_id);
+    pointer_state->ClearMatrix(pointer_id);
+  }
+}
+
 TopmostEventTarget::TopmostEventTarget(
     ui_navigation::scroll_engine::ScrollEngine* scroll_engine)
     : scroll_engine_(scroll_engine) {}
diff --git a/cobalt/layout_tests/testdata/web-platform-tests/service-workers/web_platform_tests.txt b/cobalt/layout_tests/testdata/web-platform-tests/service-workers/web_platform_tests.txt
index a26a02e..8072c2b 100644
--- a/cobalt/layout_tests/testdata/web-platform-tests/service-workers/web_platform_tests.txt
+++ b/cobalt/layout_tests/testdata/web-platform-tests/service-workers/web_platform_tests.txt
@@ -1,59 +1,57 @@
 # Service Worker API tests
 
+service-worker/clients-matchall-on-evaluation.https.html, PASS
+service-worker/fetch-event-add-async.https.html, PASS
+service-worker/import-scripts-cross-origin.https.html, PASS
+service-worker/import-scripts-mime-types.https.html, PASS
+service-worker/import-scripts-resource-map.https.html, PASS
+service-worker/import-scripts-updated-flag.https.html, PASS
 service-worker/register-default-scope.https.html, PASS
+service-worker/registration-basic.https.html, PASS
+service-worker/registration-security-error.https.html, PASS
+service-worker/registration-script-url.https.html, PASS
 service-worker/rejections.https.html, PASS
 service-worker/serviceworkerobject-scripturl.https.html, PASS
+service-worker/service-worker-csp-default.https.html, PASS
+service-worker/service-worker-csp-connect.https.html, PASS
+service-worker/service-worker-header.https.html, PASS
+service-worker/service-worker-csp-script.https.html, PASS
+service-worker/Service-Worker-Allowed-header.https.html, PASS
+service-worker/skip-waiting-without-client.https.html, PASS
+service-worker/uncontrolled-page.https.html, PASS
 service-worker/unregister.https.html, PASS
-service-worker/registration-script-url.https.html, DISABLE
-service-worker/registration-security-error.https.html, DISABLE
-service-worker/Service-Worker-Allowed-header.https.html, DISABLE
-service-worker/update-result.https.html, DISABLE
-service-worker/update-missing-import-scripts.https.html, DISABLE
+service-worker/update-missing-import-scripts.https.html, PASS
+service-worker/update-result.https.html, PASS
 
 # Tests pass with memory leakage issue.
 service-worker/update-no-cache-request-headers.https.html, DISABLE
 
-# b/266605500
-# Caught signal: SIGSEGV on linux-x64x11-sbversion-12_evergreen-x64-sbversion-12_nightly
-service-worker/clients-matchall-on-evaluation.https.html, DISABLE
-
-# b/266605087 TypeError: Cannot set property 'onerror' of null"
-service-worker/skip-waiting-without-client.https.html, DISABLE
-
 # b/234788479 Implement waiting for update worker state tasks in Install algorithm.
 service-worker/activation-after-registration.https.html, DISABLE
-
-# b/264920834 heap-use-after-free bug
-service-worker/registration-basic.https.html, DISABLE
 service-worker/activate-event-after-install-state-change.https.html, DISABLE
-service-worker/clients-matchall-frozen.https.html, DISABLE
-service-worker/fetch-event-add-async.https.html, DISABLE
+service-worker/import-scripts-redirect.https.html, DISABLE
 service-worker/multiple-update.https.html, DISABLE
 service-worker/register-wait-forever-in-install-worker.https.html, DISABLE
-service-worker/registration-events.https.html, DISABLE
-service-worker/registration-schedule-job.https.html, DISABLE
-service-worker/service-worker-header.https.html, DISABLE
+service-worker/registration-service-worker-attributes.https.html, DISABLE
 service-worker/state.https.html, DISABLE
-service-worker/uncontrolled-page.https.html, DISABLE
-
-# b/267538636 assert_throws_js: eval() should throw EvalError
-service-worker/service-worker-csp-connect.https.html, DISABLE
-service-worker/service-worker-csp-default.https.html, DISABLE
-service-worker/service-worker-csp-script.https.html, DISABLE
+service-worker/synced-state.https.html, DISABLE
 
 # "Module" type of dedicated worker is supported in Cobalt
 service-worker/dedicated-worker-service-worker-interception.https.html, DISABLE
+service-worker/registration-schedule-job.https.html, DISABLE
 service-worker/registration-scope-module-static-import.https.html, DISABLE
+service-worker/registration-script-module.https.html, DISABLE
 service-worker/update-module-request-mode.https.html, DISABLE
 service-worker/update-registration-with-type.https.html, DISABLE
-service-worker/registration-script-module.https.html, DISABLE
 
 # Channel API is not supported in Cobalt
+service-worker/clients-matchall-frozen.https.html, DISABLE
 service-worker/extendable-event-waituntil.https.html, DISABLE
 service-worker/immutable-prototype-serviceworker.https.html, DISABLE
 service-worker/indexeddb.https.html, DISABLE
 service-worker/postmessage.https.html, DISABLE
 service-worker/registration-end-to-end.https.html, DISABLE
+service-worker/registration-events.https.html, DISABLE
 
 # Below are manual tests from web-platform-tests without automation.
 service-worker/fetch-event-is-history-backward-navigation-manual.https.html, DISABLE
@@ -61,11 +59,6 @@
 service-worker/fetch-event-is-reload-navigation-manual.https.html, DISABLE
 
 # b/265841607 Unhandled rejection with value: object "TypeError"
-service-worker/import-scripts-cross-origin.https.html, DISABLE
-service-worker/import-scripts-mime-types.https.html, DISABLE
-service-worker/import-scripts-redirect.https.html, DISABLE
-service-worker/import-scripts-resource-map.https.html, DISABLE
-service-worker/import-scripts-updated-flag.https.html, DISABLE
 service-worker/same-site-cookies.https.html, DISABLE
 
 # b/265844662
@@ -95,12 +88,6 @@
 service-worker/unregister-immediately-before-installed.https.html, DISABLE
 service-worker/update-not-allowed.https.html, DISABLE
 
-# b/265981629
-service-worker/registration-service-worker-attributes.https.html, DISABLE
-
-# b/265983449
-service-worker/synced-state.https.html, DISABLE
-
 # websocket is not supported in Cobalt
 service-worker/websocket-in-service-worker.https.html, DISABLE
 
diff --git a/cobalt/layout_tests/testdata/web-platform-tests/workers/web_platform_tests.txt b/cobalt/layout_tests/testdata/web-platform-tests/workers/web_platform_tests.txt
index 7845616..48654c3 100644
--- a/cobalt/layout_tests/testdata/web-platform-tests/workers/web_platform_tests.txt
+++ b/cobalt/layout_tests/testdata/web-platform-tests/workers/web_platform_tests.txt
@@ -5,14 +5,12 @@
 # features that are expected to currently work.
 
 Worker_basic.htm, PASS
-
-# b/266711887 SIGSEGV
-Worker_dispatchEvent_ErrorEvent.htm, DISABLE
+Worker_dispatchEvent_ErrorEvent.htm, PASS
 
 # b/225037465
 Worker_cross_origin_security_err.htm, DISABLE
 
-# b/226640425
+# b/275741116
 Worker_ErrorEvent_bubbles_cancelable.htm, DISABLE
 Worker_ErrorEvent_filename.htm, DISABLE
 Worker_ErrorEvent_lineno.htm, DISABLE
diff --git a/cobalt/layout_tests/web_platform_tests.cc b/cobalt/layout_tests/web_platform_tests.cc
index 860755a..ac6e3fd 100644
--- a/cobalt/layout_tests/web_platform_tests.cc
+++ b/cobalt/layout_tests/web_platform_tests.cc
@@ -55,21 +55,22 @@
  public:
   CspDelegatePermissive(
       std::unique_ptr<web::CspViolationReporter> violation_reporter,
-      const GURL& url, csp::CSPHeaderPolicy require_csp,
+      const GURL& url, csp::CSPHeaderPolicy csp_header_policy,
       const base::Closure& policy_changed_callback)
-      : web::CspDelegateSecure(std::move(violation_reporter), url, require_csp,
-                               policy_changed_callback) {
+      : web::CspDelegateSecure(std::move(violation_reporter), url,
+                               csp_header_policy, policy_changed_callback) {
     // Lies, but some checks in our parent require this.
     was_header_received_ = true;
   }
 
   static CspDelegate* Create(
       std::unique_ptr<web::CspViolationReporter> violation_reporter,
-      const GURL& url, csp::CSPHeaderPolicy require_csp,
+      const GURL& url, csp::CSPHeaderPolicy csp_header_policy,
       const base::Closure& policy_changed_callback,
       int insecure_allowed_token) {
     return new CspDelegatePermissive(std::move(violation_reporter), url,
-                                     require_csp, policy_changed_callback);
+                                     csp_header_policy,
+                                     policy_changed_callback);
   }
 
   bool OnReceiveHeaders(const csp::ResponseHeaders& headers) override {
@@ -196,6 +197,7 @@
   // Network module
   network::NetworkModule::Options net_options;
   net_options.https_requirement = network::kHTTPSOptional;
+  net_options.ignore_certificate_errors = true;
   std::string custom_proxy =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII("proxy");
   if (!custom_proxy.empty()) net_options.custom_proxy = custom_proxy;
@@ -232,7 +234,8 @@
   // Create Service Worker Registry
   browser::ServiceWorkerRegistry* service_worker_registry =
       new browser::ServiceWorkerRegistry(&web_settings, &network_module,
-                                         new browser::UserAgentPlatformInfo());
+                                         new browser::UserAgentPlatformInfo(),
+                                         url);
   web_module_options.web_options.service_worker_jobs =
       service_worker_registry->service_worker_jobs();
 
diff --git a/cobalt/loader/BUILD.gn b/cobalt/loader/BUILD.gn
index 8d50f70..2db902d 100644
--- a/cobalt/loader/BUILD.gn
+++ b/cobalt/loader/BUILD.gn
@@ -116,6 +116,7 @@
     "//cobalt/renderer/test/png_utils",
     "//cobalt/web",
     "//nb",
+    "//net",
     "//third_party/libjpeg-turbo:libjpeg",
     "//third_party/libpng",
     "//third_party/libwebp",
diff --git a/cobalt/loader/cors_preflight.cc b/cobalt/loader/cors_preflight.cc
index eb3b70a..53e501d 100644
--- a/cobalt/loader/cors_preflight.cc
+++ b/cobalt/loader/cors_preflight.cc
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "cobalt/loader/cors_preflight.h"
+
 #include <algorithm>
 #include <cstring>
 #include <iterator>
@@ -23,7 +25,6 @@
 #include "base/basictypes.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
-#include "cobalt/loader/cors_preflight.h"
 #include "starboard/common/string.h"
 
 namespace cobalt {
@@ -412,17 +413,17 @@
   size_t iter = 0;
   if (!response_headers.EnumerateHeader(&iter, kAccessControlAllowOrigin,
                                         &allowed_origin)) {
-    DLOG(WARNING) << "Insecure cross-origin network request returned response "
-                     "with no Access-Control-Allow-Origin header. Request "
-                     "aborted.";
+    LOG(WARNING) << "Insecure cross-origin network request returned response "
+                    "with no Access-Control-Allow-Origin header. Request "
+                    "aborted: ";
     return false;
   }
   DCHECK(iter);
   if (response_headers.EnumerateHeader(&iter, kAccessControlAllowOrigin,
                                        &empty_container)) {
-    DLOG(WARNING) << "Insecure cross-origin network request returned response "
-                     "with multiple Access-Control-Allow-Origin headers. "
-                     "Behavior disallowed and request aborted";
+    LOG(WARNING) << "Insecure cross-origin network request returned response "
+                    "with multiple Access-Control-Allow-Origin headers. "
+                    "Behavior disallowed and request aborted: ";
     return false;
   }
   // 3. If request's credentials mode is not "include" and origin is `*`, return
@@ -432,13 +433,14 @@
   }
   // 2. If origin is null or failure, return failure.
   if (allowed_origin.empty()) {
+    LOG(WARNING) << "CORS check failed: Origin is null or failure.";
     return false;
   }
   // 4. If request's origin, serialized and UTF-8 encoded, is not origin, return
   //    failure.
   if (allowed_origin != serialized_origin) {
-    DLOG(WARNING) << "Network request origin is not allowed by server's "
-                     "Access-Control-Allow-Origin header, request aborted.";
+    LOG(WARNING) << "Network request origin is not allowed by server's "
+                    "Access-Control-Allow-Origin header, request aborted.";
     return false;
   }
   // 5. If request's credentials mode is not "include", return success.
@@ -451,7 +453,7 @@
                                            &allow_credentials)) {
     // 7. If credentials is `true`, return success.
     if (allow_credentials != "true") {
-      DLOG(WARNING)
+      LOG(WARNING)
           << "Network request failed because request want to include credential"
              "but server disallow it.";
       return false;
@@ -459,6 +461,7 @@
       return true;
     }
   }
+  LOG(WARNING) << "CORS check failed.";
   return false;
 }
 
diff --git a/cobalt/loader/fetch_interceptor_coordinator.cc b/cobalt/loader/fetch_interceptor_coordinator.cc
index f4db96c..a7599fc 100644
--- a/cobalt/loader/fetch_interceptor_coordinator.cc
+++ b/cobalt/loader/fetch_interceptor_coordinator.cc
@@ -37,7 +37,9 @@
 
 
 void FetchInterceptorCoordinator::TryIntercept(
-    const GURL& url,
+    const GURL& url, bool main_resource,
+    const net::HttpRequestHeaders& request_headers,
+    scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner,
     base::OnceCallback<void(std::unique_ptr<std::string>)> callback,
     base::OnceCallback<void(const net::LoadTimingInfo&)>
         report_load_timing_info,
@@ -66,7 +68,8 @@
   //       Consider moving the interception out of the ServiceWorkerGlobalScope
   //       to avoid a race condition. Fetches should be able to be intercepted
   //       by an active, registered, service worker.
-  fetch_interceptor_->StartFetch(url, std::move(callback),
+  fetch_interceptor_->StartFetch(url, main_resource, request_headers,
+                                 callback_task_runner, std::move(callback),
                                  std::move(report_load_timing_info),
                                  std::move(fallback));
 }
diff --git a/cobalt/loader/fetch_interceptor_coordinator.h b/cobalt/loader/fetch_interceptor_coordinator.h
index d8b0bbd..b266807 100644
--- a/cobalt/loader/fetch_interceptor_coordinator.h
+++ b/cobalt/loader/fetch_interceptor_coordinator.h
@@ -19,7 +19,9 @@
 #include <string>
 
 #include "base/bind.h"
+#include "base/single_thread_task_runner.h"
 #include "net/base/load_timing_info.h"
+#include "net/http/http_request_headers.h"
 #include "url/gurl.h"
 
 namespace base {
@@ -33,7 +35,9 @@
 class FetchInterceptor {
  public:
   virtual void StartFetch(
-      const GURL& url,
+      const GURL& url, bool main_resource,
+      const net::HttpRequestHeaders& request_headers,
+      scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner,
       base::OnceCallback<void(std::unique_ptr<std::string>)> callback,
       base::OnceCallback<void(const net::LoadTimingInfo&)>
           report_load_timing_info,
@@ -52,7 +56,9 @@
   void Clear() { fetch_interceptor_ = nullptr; }
 
   void TryIntercept(
-      const GURL& url,
+      const GURL& url, bool main_resource,
+      const net::HttpRequestHeaders& request_headers,
+      scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner,
       base::OnceCallback<void(std::unique_ptr<std::string>)> callback,
       base::OnceCallback<void(const net::LoadTimingInfo&)>
           report_load_timing_info,
diff --git a/cobalt/loader/fetcher_cache.cc b/cobalt/loader/fetcher_cache.cc
index 9b0244f..8d6b707 100644
--- a/cobalt/loader/fetcher_cache.cc
+++ b/cobalt/loader/fetcher_cache.cc
@@ -14,6 +14,8 @@
 
 #include "cobalt/loader/fetcher_cache.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
@@ -300,8 +302,15 @@
   if (data->size() <= capacity_) {
     auto entry = new CacheEntry(headers, last_url_origin,
                                 did_fail_from_transient_error, data);
+
+    bool inserted = cache_entries_.insert(std::make_pair(url, entry)).second;
+    if (!inserted) {
+      // The resource is already cached.
+      delete entry;
+      return;
+    }
+
     total_size_ += entry->capacity();
-    cache_entries_.insert(std::make_pair(url, entry));
     while (total_size_ > capacity_) {
       DCHECK(!cache_entries_.empty());
       total_size_ -= cache_entries_.begin()->second->capacity();
diff --git a/cobalt/loader/fetcher_factory.cc b/cobalt/loader/fetcher_factory.cc
index d938046..89322cd 100644
--- a/cobalt/loader/fetcher_factory.cc
+++ b/cobalt/loader/fetcher_factory.cc
@@ -84,15 +84,17 @@
       read_cache_callback_(read_cache_callback) {}
 
 std::unique_ptr<Fetcher> FetcherFactory::CreateFetcher(
-    const GURL& url, const disk_cache::ResourceType type,
+    const GURL& url, bool main_resource, const disk_cache::ResourceType type,
     Fetcher::Handler* handler) {
-  return CreateSecureFetcher(url, csp::SecurityCallback(), kNoCORSMode,
-                             Origin(), type, net::HttpRequestHeaders(),
+  return CreateSecureFetcher(url, main_resource, csp::SecurityCallback(),
+                             kNoCORSMode, Origin(), type,
+                             net::HttpRequestHeaders(),
                              /*skip_fetch_intercept=*/false, handler);
 }
 
 std::unique_ptr<Fetcher> FetcherFactory::CreateSecureFetcher(
-    const GURL& url, const csp::SecurityCallback& url_security_callback,
+    const GURL& url, bool main_resource,
+    const csp::SecurityCallback& url_security_callback,
     RequestMode request_mode, const Origin& origin,
     const disk_cache::ResourceType type, net::HttpRequestHeaders headers,
     bool skip_fetch_intercept, Fetcher::Handler* handler) {
@@ -112,8 +114,8 @@
     options.headers = std::move(headers);
     options.skip_fetch_intercept = skip_fetch_intercept;
     return std::unique_ptr<Fetcher>(
-        new NetFetcher(url, url_security_callback, handler, network_module_,
-                       options, request_mode, origin));
+        new NetFetcher(url, main_resource, url_security_callback, handler,
+                       network_module_, options, request_mode, origin));
   }
 
   if (url.SchemeIs("blob") && !blob_resolver_.is_null()) {
diff --git a/cobalt/loader/fetcher_factory.h b/cobalt/loader/fetcher_factory.h
index cb46a6a..7cff2d8 100644
--- a/cobalt/loader/fetcher_factory.h
+++ b/cobalt/loader/fetcher_factory.h
@@ -50,12 +50,13 @@
           base::Callback<int(const std::string&, std::unique_ptr<char[]>*)>());
 
   // Creates a fetcher. Returns NULL if the creation fails.
-  std::unique_ptr<Fetcher> CreateFetcher(const GURL& url,
+  std::unique_ptr<Fetcher> CreateFetcher(const GURL& url, bool main_resource,
                                          const disk_cache::ResourceType type,
                                          Fetcher::Handler* handler);
 
   std::unique_ptr<Fetcher> CreateSecureFetcher(
-      const GURL& url, const csp::SecurityCallback& url_security_callback,
+      const GURL& url, bool main_resource,
+      const csp::SecurityCallback& url_security_callback,
       RequestMode request_mode, const Origin& origin,
       const disk_cache::ResourceType type, net::HttpRequestHeaders headers,
       bool skip_fetch_intercept, Fetcher::Handler* handler);
diff --git a/cobalt/loader/fetcher_factory_test.cc b/cobalt/loader/fetcher_factory_test.cc
index 9767e69..1f579db 100644
--- a/cobalt/loader/fetcher_factory_test.cc
+++ b/cobalt/loader/fetcher_factory_test.cc
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "cobalt/loader/fetcher_factory.h"
+
 #include <memory>
 #include <string>
 
 #include "base/optional.h"
 #include "base/run_loop.h"
-#include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/file_fetcher.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -83,7 +84,8 @@
   StubFetcherHandler stub_fetcher_handler(&run_loop);
 
   fetcher_ = fetcher_factory_.CreateFetcher(
-      GURL("invalid-url"), disk_cache::kOther, &stub_fetcher_handler);
+      GURL("invalid-url"), /*main_resource=*/false, disk_cache::kOther,
+      &stub_fetcher_handler);
   EXPECT_TRUE(fetcher_);
 
   run_loop.Run();
@@ -95,8 +97,9 @@
   base::RunLoop run_loop;
   StubFetcherHandler stub_fetcher_handler(&run_loop);
 
-  fetcher_ = fetcher_factory_.CreateFetcher(
-      GURL("file:///"), disk_cache::kOther, &stub_fetcher_handler);
+  fetcher_ =
+      fetcher_factory_.CreateFetcher(GURL("file:///"), /*main_resource=*/false,
+                                     disk_cache::kOther, &stub_fetcher_handler);
   EXPECT_TRUE(fetcher_);
 
   run_loop.Run();
@@ -109,7 +112,8 @@
   StubFetcherHandler stub_fetcher_handler(&run_loop);
 
   fetcher_ = fetcher_factory_.CreateFetcher(
-      GURL("file://file.txt"), disk_cache::kOther, &stub_fetcher_handler);
+      GURL("file://file.txt"), /*main_resource=*/false, disk_cache::kOther,
+      &stub_fetcher_handler);
   EXPECT_TRUE(fetcher_);
 
   run_loop.Run();
@@ -124,14 +128,14 @@
   base::RunLoop run_loop;
   StubFetcherHandler stub_fetcher_handler(&run_loop);
 
-  fetcher_ =
-      fetcher_factory_.CreateFetcher(GURL("file:///nonempty-url-1"),
-                                     disk_cache::kOther, &stub_fetcher_handler);
+  fetcher_ = fetcher_factory_.CreateFetcher(
+      GURL("file:///nonempty-url-1"), /*main_resource=*/false,
+      disk_cache::kOther, &stub_fetcher_handler);
   EXPECT_TRUE(fetcher_);
 
-  fetcher_ =
-      fetcher_factory_.CreateFetcher(GURL("file:///nonempty-url-2"),
-                                     disk_cache::kOther, &stub_fetcher_handler);
+  fetcher_ = fetcher_factory_.CreateFetcher(
+      GURL("file:///nonempty-url-2"), /*main_resource=*/false,
+      disk_cache::kOther, &stub_fetcher_handler);
   EXPECT_TRUE(fetcher_);
   run_loop.Run();
   EXPECT_EQ(fetcher_.get(), stub_fetcher_handler.fetcher());
diff --git a/cobalt/loader/net_fetcher.cc b/cobalt/loader/net_fetcher.cc
index 4fa52ec..7f43139 100644
--- a/cobalt/loader/net_fetcher.cc
+++ b/cobalt/loader/net_fetcher.cc
@@ -91,7 +91,7 @@
 
 }  // namespace
 
-NetFetcher::NetFetcher(const GURL& url,
+NetFetcher::NetFetcher(const GURL& url, bool main_resource,
                        const csp::SecurityCallback& security_callback,
                        Handler* handler,
                        const network::NetworkModule* network_module,
@@ -105,7 +105,9 @@
       origin_(origin),
       request_script_(options.resource_type == disk_cache::kUncompiledScript),
       task_runner_(base::MessageLoop::current()->task_runner()),
-      skip_fetch_intercept_(options.skip_fetch_intercept) {
+      skip_fetch_intercept_(options.skip_fetch_intercept),
+      will_destroy_current_message_loop_(false),
+      main_resource_(main_resource) {
   url_fetcher_ = net::URLFetcher::Create(url, options.request_method, this);
   if (!options.headers.IsEmpty()) {
     url_fetcher_->SetExtraRequestHeaders(options.headers.ToString());
@@ -138,6 +140,11 @@
   // while a loader is still being constructed.
   base::MessageLoop::current()->task_runner()->PostTask(
       FROM_HERE, start_callback_.callback());
+  base::MessageLoop::current()->AddDestructionObserver(this);
+}
+
+void NetFetcher::WillDestroyCurrentMessageLoop() {
+  will_destroy_current_message_loop_.store(true);
 }
 
 void NetFetcher::Start() {
@@ -152,12 +159,11 @@
       return;
     }
     FetchInterceptorCoordinator::GetInstance()->TryIntercept(
-        original_url,
-        base::BindOnce(&NetFetcher::OnFetchIntercepted, base::Unretained(this)),
-        base::BindOnce(&NetFetcher::ReportLoadTimingInfo,
-                       base::Unretained(this)),
-        base::BindOnce(&net::URLFetcher::Start,
-                       base::Unretained(url_fetcher_.get())));
+        original_url, main_resource_, url_fetcher_->GetRequestHeaders(),
+        task_runner_,
+        base::BindOnce(&NetFetcher::OnFetchIntercepted, AsWeakPtr()),
+        base::BindOnce(&NetFetcher::ReportLoadTimingInfo, AsWeakPtr()),
+        base::BindOnce(&NetFetcher::InterceptFallback, AsWeakPtr()));
 
   } else {
     std::string msg(base::StringPrintf("URL %s rejected by security policy.",
@@ -166,7 +172,12 @@
   }
 }
 
+void NetFetcher::InterceptFallback() { url_fetcher_->Start(); }
+
 void NetFetcher::OnFetchIntercepted(std::unique_ptr<std::string> body) {
+  if (will_destroy_current_message_loop_.load()) {
+    return;
+  }
   if (task_runner_ != base::MessageLoop::current()->task_runner()) {
     task_runner_->PostTask(
         FROM_HERE, base::BindOnce(&NetFetcher::OnFetchIntercepted,
@@ -303,6 +314,9 @@
 NetFetcher::~NetFetcher() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   start_callback_.Cancel();
+  if (!will_destroy_current_message_loop_.load()) {
+    base::MessageLoop::current()->RemoveDestructionObserver(this);
+  }
 }
 
 NetFetcher::ReturnWrapper NetFetcher::HandleError(const std::string& message) {
diff --git a/cobalt/loader/net_fetcher.h b/cobalt/loader/net_fetcher.h
index 3a6d3d8..984bdcb 100644
--- a/cobalt/loader/net_fetcher.h
+++ b/cobalt/loader/net_fetcher.h
@@ -30,13 +30,17 @@
 #include "net/http/http_request_headers.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_fetcher_delegate.h"
+#include "starboard/common/atomic.h"
 #include "url/gurl.h"
 
 namespace cobalt {
 namespace loader {
 
 // NetFetcher is for fetching data from the network.
-class NetFetcher : public Fetcher, public net::URLFetcherDelegate {
+class NetFetcher : public Fetcher,
+                   public net::URLFetcherDelegate,
+                   public base::SupportsWeakPtr<NetFetcher>,
+                   public base::MessageLoopCurrent::DestructionObserver {
  public:
   struct Options {
    public:
@@ -50,8 +54,9 @@
     bool skip_fetch_intercept;
   };
 
-  NetFetcher(const GURL& url, const csp::SecurityCallback& security_callback,
-             Handler* handler, const network::NetworkModule* network_module,
+  NetFetcher(const GURL& url, bool main_resource,
+             const csp::SecurityCallback& security_callback, Handler* handler,
+             const network::NetworkModule* network_module,
              const Options& options, RequestMode request_mode,
              const Origin& origin);
   ~NetFetcher() override;
@@ -64,6 +69,9 @@
                                   int64_t current_network_bytes) override;
   void ReportLoadTimingInfo(const net::LoadTimingInfo& timing_info) override;
 
+  // base::MessageLoopCurrent::DestructionObserver
+  void WillDestroyCurrentMessageLoop() override;
+
   void OnFetchIntercepted(std::unique_ptr<std::string> body);
 
   net::URLFetcher* url_fetcher() const {
@@ -79,6 +87,7 @@
   }
 
   void Start();
+  void InterceptFallback();
 
   // Empty struct to ensure the caller of |HandleError()| knows that |this|
   // may have been destroyed and handles it appropriately.
@@ -122,6 +131,8 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> const task_runner_;
   bool skip_fetch_intercept_;
+  starboard::atomic_bool will_destroy_current_message_loop_;
+  bool main_resource_;
 
   DISALLOW_COPY_AND_ASSIGN(NetFetcher);
 };
diff --git a/cobalt/loader/script_loader_factory.cc b/cobalt/loader/script_loader_factory.cc
index 97d096d..080b4a5 100644
--- a/cobalt/loader/script_loader_factory.cc
+++ b/cobalt/loader/script_loader_factory.cc
@@ -76,10 +76,10 @@
     bool skip_fetch_intercept) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  return base::Bind(&FetcherFactory::CreateSecureFetcher,
-                    base::Unretained(fetcher_factory_), url,
-                    url_security_callback, request_mode, origin, type,
-                    std::move(headers), skip_fetch_intercept);
+  return base::Bind(
+      &FetcherFactory::CreateSecureFetcher, base::Unretained(fetcher_factory_),
+      url, /*main_resource=*/false, url_security_callback, request_mode, origin,
+      type, std::move(headers), skip_fetch_intercept);
 }
 
 void ScriptLoaderFactory::OnLoaderCreated(Loader* loader) {
diff --git a/cobalt/media/BUILD.gn b/cobalt/media/BUILD.gn
index 1b27130..ee1625e 100644
--- a/cobalt/media/BUILD.gn
+++ b/cobalt/media/BUILD.gn
@@ -28,6 +28,8 @@
   sources = [
     "base/audio_bus.cc",
     "base/audio_bus.h",
+    "base/cval_stats.cc",
+    "base/cval_stats.h",
     "base/data_source.cc",
     "base/data_source.h",
     "base/decode_target_provider.h",
@@ -111,6 +113,8 @@
   testonly = true
 
   sources = [
+    "base/cval_stats_test.cc",
+    "base/decoder_buffer_cache_test.cc",
     "file_data_source_test.cc",
     "progressive/demuxer_extension_wrapper_test.cc",
     "progressive/mock_data_source_reader.h",
diff --git a/cobalt/media/base/cval_stats.cc b/cobalt/media/base/cval_stats.cc
new file mode 100644
index 0000000..9ee4acf
--- /dev/null
+++ b/cobalt/media/base/cval_stats.cc
@@ -0,0 +1,129 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/media/base/cval_stats.h"
+
+#include <numeric>
+
+namespace cobalt {
+namespace media {
+
+CValContainer::CValContainer(std::string name,
+                             int max_samples_before_calculation)
+    : cval_name_{name},
+      max_samples_before_calculation_{max_samples_before_calculation},
+      latest_{"Media." + name + ".Latest", 0, "Latest sample in microseconds."},
+      average_{"Media." + name + ".Average", 0,
+               "Average sample in microseconds."},
+      maximum_{"Media." + name + ".Maximum", 0,
+               "Maximum sample in microseconds."},
+      median_{"Media." + name + ".Median", 0, "Median sample in microseconds."},
+      minimum_{"Media." + name + ".Minimum", 0,
+               "Minimum sample in microseconds."} {}
+
+void CValContainer::UpdateCVal(SbTime event_timing) {
+  latest_ = event_timing;
+  accumulated_sample_count_++;
+
+  if (!first_sample_added_) {
+    minimum_ = event_timing;
+    maximum_ = event_timing;
+    first_sample_added_ = true;
+  }
+
+  samples_[sample_write_index_] = event_timing;
+  sample_write_index_ = (sample_write_index_ + 1) % kMaxSamples;
+
+  if (accumulated_sample_count_ % max_samples_before_calculation_ == 0) {
+    std::vector<SbTime> copy;
+    int copy_size = std::min(accumulated_sample_count_, kMaxSamples);
+    copy.reserve(copy_size);
+    copy.assign(samples_, samples_ + copy_size);
+    std::sort(copy.begin(), copy.end());
+    if (copy.size() % 2 == 0) {
+      median_ = (copy[copy.size() / 2 - 1] + copy[copy.size() / 2]) / 2;
+    } else {
+      median_ = copy[copy.size() / 2];
+    }
+
+    minimum_ = std::min(minimum_.value(), copy[0]);
+    maximum_ = std::max(maximum_.value(), copy[copy.size() - 1]);
+
+    auto local_average = std::accumulate(copy.begin(), copy.end(), 0);
+    average_ += (local_average - average_.value()) / accumulated_sample_count_;
+  }
+}
+
+CValStats::CValStats() {
+  cval_containers_.emplace(
+      std::piecewise_construct,
+      std::forward_as_tuple(MediaTiming::SbPlayerCreate),
+      std::forward_as_tuple("SbPlayerCreateTime",
+                            kMediaDefaultMaxSamplesBeforeCalculation));
+  cval_containers_.emplace(
+      std::piecewise_construct,
+      std::forward_as_tuple(MediaTiming::SbPlayerDestroy),
+      std::forward_as_tuple("SbPlayerDestructionTime",
+                            kMediaDefaultMaxSamplesBeforeCalculation));
+  cval_containers_.emplace(
+      std::piecewise_construct,
+      std::forward_as_tuple(MediaTiming::SbPlayerWriteSamples),
+      std::forward_as_tuple("SbPlayerWriteSamplesTime",
+                            kMediaHighFrequencyMaxSamplesBeforeCalculation));
+}
+
+void CValStats::StartTimer(MediaTiming event_type,
+                           std::string pipeline_identifier) {
+  if (!enabled_) return;
+
+  running_timers_[std::make_pair(event_type, pipeline_identifier)] =
+      SbTimeGetMonotonicNow();
+}
+
+void CValStats::StopTimer(MediaTiming event_type,
+                          std::string pipeline_identifier) {
+  if (!enabled_) return;
+
+  auto key = std::make_pair(event_type, pipeline_identifier);
+  DCHECK(running_timers_.find(key) != running_timers_.end());
+
+  SbTime time_taken = SbTimeGetMonotonicNow() - running_timers_[key];
+  auto cval_container = cval_containers_.find(event_type);
+  if (cval_container != cval_containers_.end()) {
+    cval_container->second.UpdateCVal(time_taken);
+  }
+}
+
+// for testing only
+size_t CValStats::GetBufferIndexForTest() {
+  DCHECK(cval_containers_.size() > 0);
+  auto cval_container = cval_containers_.find(MediaTiming::SbPlayerCreate);
+  if (cval_container != cval_containers_.end()) {
+    return cval_container->second.GetSampleIndex();
+  }
+  return 0;
+}
+
+// for testing only
+size_t CValStats::GetBufferSampleSizeForTest() {
+  DCHECK(cval_containers_.size() > 0);
+  auto cval_container = cval_containers_.find(MediaTiming::SbPlayerCreate);
+  if (cval_container != cval_containers_.end()) {
+    return cval_container->second.GetNumberSamples();
+  }
+  return 0;
+}
+
+}  // namespace media
+}  // namespace cobalt
diff --git a/cobalt/media/base/cval_stats.h b/cobalt/media/base/cval_stats.h
new file mode 100644
index 0000000..2ae3099
--- /dev/null
+++ b/cobalt/media/base/cval_stats.h
@@ -0,0 +1,101 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_MEDIA_BASE_CVAL_STATS_H_
+#define COBALT_MEDIA_BASE_CVAL_STATS_H_
+
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "cobalt/base/c_val.h"
+
+
+namespace cobalt {
+namespace media {
+
+enum class MediaTiming {
+  SbPlayerCreate,
+  SbPlayerDestroy,
+  SbPlayerWriteSamples
+};
+
+// size of the sample array
+constexpr size_t kMaxSamples = 100;
+// number of samples taken before we trigger historical calculations
+constexpr int kMediaDefaultMaxSamplesBeforeCalculation = 5;
+constexpr int kMediaHighFrequencyMaxSamplesBeforeCalculation = 25;
+
+class CValContainer {
+ public:
+  CValContainer(std::string name, int max_samples_before_calculation);
+  ~CValContainer() = default;
+
+  void StartTimer();
+  void StopTimer();
+  void UpdateCVal(SbTime event_time);
+
+  // for testing only
+  size_t GetSampleIndex() { return sample_write_index_; }
+  size_t GetNumberSamples() {
+    return std::min(accumulated_sample_count_, kMaxSamples);
+  }
+
+ public:
+  int max_samples_before_calculation_{0};
+  std::string cval_name_;
+
+ private:
+  base::CVal<SbTime, base::CValPublic> latest_;
+  base::CVal<SbTime, base::CValPublic> average_;
+  base::CVal<SbTime, base::CValPublic> maximum_;
+  base::CVal<SbTime, base::CValPublic> median_;
+  base::CVal<SbTime, base::CValPublic> minimum_;
+
+  SbTime samples_[kMaxSamples];
+  size_t sample_write_index_{0};
+  size_t accumulated_sample_count_{0};
+  bool first_sample_added_{false};
+
+  SbTime latest_time_start_{0};
+  SbTime latest_time_stop_{0};
+};
+
+class CValStats {
+ public:
+  CValStats();
+  ~CValStats() = default;
+
+  void StartTimer(MediaTiming event_type, std::string pipeline_identifier);
+  void StopTimer(MediaTiming event_type, std::string pipeline_identifier);
+  void Enable(bool enabled) { enabled_ = enabled; }
+
+  // just for testing
+  size_t GetBufferIndexForTest();
+  size_t GetBufferSampleSizeForTest();
+
+ private:
+  std::map<MediaTiming, CValContainer> cval_containers_;
+  std::map<std::pair<MediaTiming, std::string>, SbTime> running_timers_;
+
+  bool enabled_{false};
+};
+
+}  // namespace media
+}  // namespace cobalt
+
+#endif  // COBALT_MEDIA_BASE_CVAL_STATS_H_
diff --git a/cobalt/media/base/cval_stats_test.cc b/cobalt/media/base/cval_stats_test.cc
new file mode 100644
index 0000000..673e20c
--- /dev/null
+++ b/cobalt/media/base/cval_stats_test.cc
@@ -0,0 +1,190 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <unistd.h>
+#endif
+
+#include <set>
+#include <string>
+
+#include "cobalt/media/base/cval_stats.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+const char kCValnameLatest[] = "Media.SbPlayerCreateTime.Latest";
+const char kCValnameAverage[] = "Media.SbPlayerCreateTime.Average";
+const char kCValnameMaximum[] = "Media.SbPlayerCreateTime.Maximum";
+const char kCValnameMedian[] = "Media.SbPlayerCreateTime.Median";
+const char kCValnameMinimum[] = "Media.SbPlayerCreateTime.Minimum";
+
+const char kPipelineIdentifier[] = "test_pipeline";
+
+constexpr int kSleepTimeMs = 50;
+
+namespace cobalt {
+namespace media {
+
+namespace {
+void sleep_ms(int ms) {
+#ifdef _WIN32
+  Sleep(ms);
+#else
+  usleep(ms);
+#endif
+}
+}  // namespace
+
+TEST(MediaCValStatsTest, InitiallyEmpty) {
+  base::CValManager* cvm = base::CValManager::GetInstance();
+  EXPECT_TRUE(cvm);
+  std::set<std::string> cval_names = cvm->GetOrderedCValNames();
+  std::set<std::string>::size_type size = cval_names.size();
+  EXPECT_EQ(size, 0);
+}
+
+TEST(MediaCValStatsTest, CValStatsCreation) {
+  base::CValManager* cvm = base::CValManager::GetInstance();
+  EXPECT_TRUE(cvm);
+  CValStats cval_stats_;
+  std::set<std::string> cval_names = cvm->GetOrderedCValNames();
+  std::set<std::string>::size_type size = cval_names.size();
+  EXPECT_EQ(size, 15);
+}
+
+TEST(MediaCValStatsTest, NothingRecorded) {
+  base::CValManager* cvm = base::CValManager::GetInstance();
+  EXPECT_TRUE(cvm);
+  CValStats cval_stats_;
+
+  cval_stats_.StartTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
+  sleep_ms(kSleepTimeMs);
+  cval_stats_.StopTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
+
+  base::Optional<std::string> result =
+      cvm->GetValueAsPrettyString(kCValnameLatest);
+  EXPECT_TRUE(result);
+  EXPECT_EQ(*result, "0");
+}
+
+TEST(MediaCValStatsTest, EnableRecording) {
+  base::CValManager* cvm = base::CValManager::GetInstance();
+  EXPECT_TRUE(cvm);
+  CValStats cval_stats_;
+
+  cval_stats_.Enable(true);
+
+  cval_stats_.StartTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
+  sleep_ms(kSleepTimeMs);
+  cval_stats_.StopTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
+
+  base::Optional<std::string> result =
+      cvm->GetValueAsPrettyString(kCValnameLatest);
+  EXPECT_TRUE(result);
+  EXPECT_NE(*result, "0");
+
+  result = cvm->GetValueAsPrettyString(kCValnameMinimum);
+  EXPECT_TRUE(result);
+  EXPECT_NE(*result, "0");
+
+  result = cvm->GetValueAsPrettyString(kCValnameMaximum);
+  EXPECT_TRUE(result);
+  EXPECT_NE(*result, "0");
+}
+
+TEST(MediaCValStatsTest, DontGenerateHistoricalData) {
+  base::CValManager* cvm = base::CValManager::GetInstance();
+  EXPECT_TRUE(cvm);
+  CValStats cval_stats_;
+
+  cval_stats_.Enable(true);
+
+  for (int i = 0; i < kMediaDefaultMaxSamplesBeforeCalculation - 1; i++) {
+    cval_stats_.StartTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
+    sleep_ms(kSleepTimeMs);
+    cval_stats_.StopTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
+  }
+
+  base::Optional<std::string> result =
+      cvm->GetValueAsPrettyString(kCValnameLatest);
+  EXPECT_TRUE(result);
+  EXPECT_NE(*result, "0");
+
+  result = cvm->GetValueAsPrettyString(kCValnameAverage);
+  EXPECT_TRUE(result);
+  EXPECT_EQ(*result, "0");
+
+  result = cvm->GetValueAsPrettyString(kCValnameMedian);
+  EXPECT_TRUE(result);
+  EXPECT_EQ(*result, "0");
+}
+
+TEST(MediaCValStatsTest, GenerateHistoricalData) {
+  base::CValManager* cvm = base::CValManager::GetInstance();
+  EXPECT_TRUE(cvm);
+  CValStats cval_stats_;
+
+  cval_stats_.Enable(true);
+
+  for (int i = 0; i < kMediaDefaultMaxSamplesBeforeCalculation; i++) {
+    cval_stats_.StartTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
+    sleep_ms(kSleepTimeMs);
+    cval_stats_.StopTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
+  }
+
+  base::Optional<std::string> result =
+      cvm->GetValueAsPrettyString(kCValnameAverage);
+  EXPECT_TRUE(result);
+  EXPECT_NE(*result, "0");
+
+  result = cvm->GetValueAsPrettyString(kCValnameMedian);
+  EXPECT_TRUE(result);
+  EXPECT_NE(*result, "0");
+}
+
+TEST(MediaCValStatsTest, CircularBufferDontWrapIndex) {
+  base::CValManager* cvm = base::CValManager::GetInstance();
+  EXPECT_TRUE(cvm);
+  CValStats cval_stats_;
+
+  cval_stats_.Enable(true);
+
+  for (int i = 0; i < kMaxSamples - 1; i++) {
+    cval_stats_.StartTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
+    cval_stats_.StopTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
+  }
+
+  EXPECT_EQ(cval_stats_.GetBufferIndexForTest(), 99);
+}
+
+
+TEST(MediaCValStatsTest, CircularBufferWrapIndex) {
+  base::CValManager* cvm = base::CValManager::GetInstance();
+  EXPECT_TRUE(cvm);
+  CValStats cval_stats_;
+
+  cval_stats_.Enable(true);
+
+  for (int i = 0; i < kMaxSamples + 5; i++) {
+    cval_stats_.StartTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
+    cval_stats_.StopTimer(MediaTiming::SbPlayerCreate, kPipelineIdentifier);
+  }
+
+  EXPECT_EQ(cval_stats_.GetBufferIndexForTest(), 5);
+}
+
+}  // namespace media
+}  // namespace cobalt
diff --git a/cobalt/media/base/decoder_buffer_cache.cc b/cobalt/media/base/decoder_buffer_cache.cc
index 440053d..d1d84ab 100644
--- a/cobalt/media/base/decoder_buffer_cache.cc
+++ b/cobalt/media/base/decoder_buffer_cache.cc
@@ -14,91 +14,114 @@
 
 #include "cobalt/media/base/decoder_buffer_cache.h"
 
+#include <algorithm>
+#include <cstring>
+#include <utility>
+
 namespace cobalt {
 namespace media {
+namespace {
 
-DecoderBufferCache::DecoderBufferCache()
-    : audio_buffer_index_(0), video_buffer_index_(0) {}
+bool SafeStrEqual(const char* ptr1, const char* ptr2) {
+  if (!ptr1 || !ptr2) {
+    return ptr1 == ptr2;
+  }
+  return strcmp(ptr1, ptr2) == 0;
+}
 
-void DecoderBufferCache::AddBuffer(DemuxerStream::Type type,
-                                   const scoped_refptr<DecoderBuffer>& buffer) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}  // namespace
 
-  if (type == DemuxerStream::AUDIO) {
-    audio_buffers_.push_back(buffer);
-    if (!buffer->end_of_stream() && buffer->is_key_frame()) {
-      audio_key_frame_timestamps_.push_back(buffer->timestamp());
-    }
-  } else {
-    DCHECK_EQ(type, DemuxerStream::VIDEO);
-    video_buffers_.push_back(buffer);
-    if (!buffer->end_of_stream() && buffer->is_key_frame()) {
-      video_key_frame_timestamps_.push_back(buffer->timestamp());
-    }
+bool operator!=(const SbMediaAudioStreamInfo& left,
+                const SbMediaAudioStreamInfo& right) {
+  if (left.codec == kSbMediaAudioCodecNone) {
+    return right.codec != kSbMediaAudioCodecNone;
+  }
+  return left.codec != right.codec || !SafeStrEqual(left.mime, right.mime) ||
+         left.number_of_channels != right.number_of_channels ||
+         left.samples_per_second != right.samples_per_second ||
+         left.bits_per_sample != right.bits_per_sample ||
+         left.audio_specific_config_size != right.audio_specific_config_size ||
+         memcmp(left.audio_specific_config, right.audio_specific_config,
+                left.audio_specific_config_size) != 0;
+}
+
+bool operator!=(const SbMediaVideoStreamInfo& left,
+                const SbMediaVideoStreamInfo& right) {
+  if (left.codec == kSbMediaVideoCodecNone) {
+    return right.codec != kSbMediaVideoCodecNone;
+  }
+  return left.codec != right.codec || !SafeStrEqual(left.mime, right.mime) ||
+         !SafeStrEqual(left.max_video_capabilities,
+                       right.max_video_capabilities) ||
+         left.frame_width != right.frame_width ||
+         left.frame_height != right.frame_height ||
+         memcmp(&left.color_metadata, &right.color_metadata,
+                sizeof(SbMediaColorMetadata)) != 0;
+}
+
+DecoderBufferCache::BufferGroup::Segment::Segment(
+    const SbMediaAudioStreamInfo& stream_info) {
+  DCHECK_NE(stream_info.codec, kSbMediaAudioCodecNone);
+
+  audio_stream_info = stream_info;
+  if (stream_info.audio_specific_config_size > 0) {
+    auto specific_config =
+        static_cast<const uint8_t*>(stream_info.audio_specific_config);
+    audio_specific_config.assign(
+        specific_config,
+        specific_config + stream_info.audio_specific_config_size);
+    audio_stream_info.audio_specific_config = audio_specific_config.data();
+  }
+  if (stream_info.mime) {
+    mime = stream_info.mime;
+    audio_stream_info.mime = mime.c_str();
   }
 }
 
-void DecoderBufferCache::ClearSegmentsBeforeMediaTime(
-    base::TimeDelta media_time) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+DecoderBufferCache::BufferGroup::Segment::Segment(
+    const SbMediaVideoStreamInfo& stream_info) {
+  DCHECK_NE(stream_info.codec, kSbMediaVideoCodecNone);
 
-  audio_buffer_index_ -= ClearSegmentsBeforeMediaTime(
-      media_time, &audio_buffers_, &audio_key_frame_timestamps_);
-  video_buffer_index_ -= ClearSegmentsBeforeMediaTime(
-      media_time, &video_buffers_, &video_key_frame_timestamps_);
-}
-
-void DecoderBufferCache::ClearAll() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  audio_buffers_.clear();
-  audio_key_frame_timestamps_.clear();
-  video_buffers_.clear();
-  video_key_frame_timestamps_.clear();
-  audio_buffer_index_ = 0;
-  video_buffer_index_ = 0;
-}
-
-void DecoderBufferCache::StartResuming() {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  audio_buffer_index_ = 0;
-  video_buffer_index_ = 0;
-}
-
-scoped_refptr<::media::DecoderBuffer> DecoderBufferCache::GetBuffer(
-    DemuxerStream::Type type) const {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  if (type == DemuxerStream::AUDIO) {
-    if (audio_buffer_index_ < audio_buffers_.size()) {
-      return audio_buffers_[audio_buffer_index_];
-    }
-    return NULL;
+  video_stream_info = stream_info;
+  if (stream_info.mime) {
+    mime = stream_info.mime;
+    video_stream_info.mime = mime.c_str();
   }
-
-  DCHECK_EQ(type, DemuxerStream::VIDEO);
-  if (video_buffer_index_ < video_buffers_.size()) {
-    return video_buffers_[video_buffer_index_];
-  }
-  return NULL;
-}
-
-void DecoderBufferCache::AdvanceToNextBuffer(DemuxerStream::Type type) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  if (type == DemuxerStream::AUDIO) {
-    ++audio_buffer_index_;
-  } else {
-    DCHECK_EQ(type, DemuxerStream::VIDEO);
-    ++video_buffer_index_;
+  if (stream_info.max_video_capabilities) {
+    max_video_capabilities = stream_info.max_video_capabilities;
+    video_stream_info.max_video_capabilities = max_video_capabilities.c_str();
   }
 }
 
-// static
-size_t DecoderBufferCache::ClearSegmentsBeforeMediaTime(
-    base::TimeDelta media_time, Buffers* buffers,
-    KeyFrameTimestamps* key_frame_timestamps) {
+DecoderBufferCache::BufferGroup::BufferGroup(DemuxerStream::Type type)
+    : type_(type) {}
+
+void DecoderBufferCache::BufferGroup::AddBuffers(
+    const DecoderBuffers& buffers, const SbMediaAudioStreamInfo& stream_info) {
+  DCHECK(type_ == DemuxerStream::Type::AUDIO);
+  if (buffers.empty()) {
+    return;
+  }
+  if (segments_.empty() || segments_.back().audio_stream_info != stream_info) {
+    AddStreamInfo(stream_info);
+  }
+  AddBuffers(buffers);
+}
+
+void DecoderBufferCache::BufferGroup::AddBuffers(
+    const DecoderBuffers& buffers, const SbMediaVideoStreamInfo& stream_info) {
+  DCHECK(type_ == DemuxerStream::Type::VIDEO);
+  if (buffers.empty()) {
+    return;
+  }
+  if (segments_.empty() || segments_.back().video_stream_info != stream_info) {
+    AddStreamInfo(stream_info);
+  }
+  AddBuffers(buffers);
+}
+
+void DecoderBufferCache::BufferGroup::ClearSegmentsBeforeMediaTime(
+    const base::TimeDelta& media_time) {
   // Use K to denote a key frame and N for non-key frame.  If the cache contains
   // K N N N N N N N N K N N N N N N N N K N N N N N N N N
   //                     |
@@ -108,26 +131,217 @@
   //                   K N N N N N N N N K N N N N N N N N
   //                     |
   //                 media_time
-  // So we need at least two keyframes before we can remove any frames.
-  while (key_frame_timestamps->size() > 1 &&
-         key_frame_timestamps->at(1) <= media_time) {
-    key_frame_timestamps->erase(key_frame_timestamps->begin());
-  }
-  if (key_frame_timestamps->empty()) {
-    return 0;
+  // So we need at least two keyframes before we can remove any buffers.
+  while (key_frame_timestamps_.size() > 1 &&
+         key_frame_timestamps_[1] <= media_time) {
+    key_frame_timestamps_.pop_front();
   }
 
-  size_t buffers_removed = 0;
-  while (scoped_refptr<DecoderBuffer> buffer = buffers->front()) {
-    if (buffer->is_key_frame() &&
-        buffer->timestamp() == key_frame_timestamps->front()) {
-      break;
+  if (key_frame_timestamps_.empty()) {
+    return;
+  }
+
+  base::TimeDelta key_frame_timestamp = key_frame_timestamps_.front();
+  // Remove all buffers and groups before the key frame timestamp.
+  while (!segments_.empty()) {
+    auto& first_segment_buffers = segments_.front().buffers;
+    while (!first_segment_buffers.empty()) {
+      auto& buffer = first_segment_buffers.front();
+      if (buffer->is_key_frame() &&
+          buffer->timestamp() == key_frame_timestamp) {
+        return;
+      }
+      first_segment_buffers.pop_front();
+      if (segment_index_ == 0) {
+        if (buffer_index_ == 0) {
+          LOG(ERROR) << "Trying to remove unwritten buffers.";
+        } else {
+          buffer_index_--;
+        }
+      }
     }
-    buffers->pop_front();
-    ++buffers_removed;
-  }
 
-  return buffers_removed;
+    if (first_segment_buffers.empty()) {
+      DCHECK_GT(segments_.size(), 1);
+      DCHECK_GT(segment_index_, 0);
+
+      segments_.pop_front();
+      segment_index_--;
+    }
+  }
+  DCHECK(!segments_.empty() && segment_index_ < segments_.size());
+  DCHECK(buffer_index_ < segments_[segment_index_].buffers.size());
+}
+
+
+void DecoderBufferCache::BufferGroup::ClearAll() {
+  segments_.clear();
+  key_frame_timestamps_.clear();
+  segment_index_ = 0;
+  buffer_index_ = 0;
+}
+
+bool DecoderBufferCache::BufferGroup::HasMoreBuffers() const {
+  if (segments_.size() == 0) {
+    return false;
+  }
+  DCHECK(segment_index_ < segments_.size());
+  DCHECK(buffer_index_ <= segments_[segment_index_].buffers.size());
+  if (buffer_index_ == segments_[segment_index_].buffers.size()) {
+    DCHECK(segment_index_ == segments_.size() - 1);
+    return false;
+  }
+  return true;
+}
+
+template <typename StreamInfo>
+void DecoderBufferCache::BufferGroup::AddStreamInfo(
+    const StreamInfo& stream_info) {
+  segments_.emplace_back(stream_info);
+  AdvanceGroupIndexIfNecessary();
+}
+
+void DecoderBufferCache::BufferGroup::AddBuffers(
+    const DecoderBuffers& buffers) {
+  DCHECK(!segments_.empty() && segment_index_ < segments_.size());
+  DCHECK(!buffers.empty());
+  auto& last_segment_buffers = segments_.back().buffers;
+  for (auto buffer : buffers) {
+    if (!buffer->end_of_stream() && buffer->is_key_frame()) {
+      key_frame_timestamps_.push_back(buffer->timestamp());
+    }
+  }
+  last_segment_buffers.insert(last_segment_buffers.end(), buffers.begin(),
+                              buffers.end());
+}
+
+void DecoderBufferCache::BufferGroup::ReadBuffers(
+    DecoderBuffers* buffers, size_t max_buffers_per_read,
+    SbMediaAudioStreamInfo* stream_info) {
+  DCHECK_EQ(type_, DemuxerStream::AUDIO);
+  DCHECK_GT(max_buffers_per_read, 0);
+  DCHECK(HasMoreBuffers());
+
+  *stream_info = segments_[segment_index_].audio_stream_info;
+  ReadBuffers(buffers, max_buffers_per_read);
+}
+
+void DecoderBufferCache::BufferGroup::ReadBuffers(
+    DecoderBuffers* buffers, size_t max_buffers_per_read,
+    SbMediaVideoStreamInfo* stream_info) {
+  DCHECK_EQ(type_, DemuxerStream::VIDEO);
+  DCHECK_GT(max_buffers_per_read, 0);
+  DCHECK(HasMoreBuffers());
+
+  *stream_info = segments_[segment_index_].video_stream_info;
+  ReadBuffers(buffers, max_buffers_per_read);
+}
+
+void DecoderBufferCache::BufferGroup::ResetIndex() {
+  segment_index_ = 0;
+  buffer_index_ = 0;
+}
+
+void DecoderBufferCache::BufferGroup::AdvanceGroupIndexIfNecessary() {
+  DCHECK(segment_index_ < segments_.size());
+  DCHECK(buffer_index_ <= segments_[segment_index_].buffers.size());
+  if (buffer_index_ == segments_[segment_index_].buffers.size() &&
+      segment_index_ + 1 < segments_.size()) {
+    segment_index_++;
+    buffer_index_ = 0;
+  }
+}
+
+void DecoderBufferCache::BufferGroup::ReadBuffers(DecoderBuffers* buffers,
+                                                  size_t max_buffers_per_read) {
+  DCHECK(segment_index_ < segments_.size());
+  DCHECK(buffer_index_ < segments_[segment_index_].buffers.size());
+
+  auto& current_segment_buffers = segments_[segment_index_].buffers;
+  size_t buffers_to_read = std::min(
+      max_buffers_per_read, current_segment_buffers.size() - buffer_index_);
+  DCHECK_GT(buffers_to_read, 0);
+
+  buffers->insert(
+      buffers->end(), current_segment_buffers.begin() + buffer_index_,
+      current_segment_buffers.begin() + buffer_index_ + buffers_to_read);
+  buffer_index_ += buffers_to_read;
+
+  AdvanceGroupIndexIfNecessary();
+}
+
+DecoderBufferCache::DecoderBufferCache()
+    : audio_buffer_group_(DemuxerStream::Type::AUDIO),
+      video_buffer_group_(DemuxerStream::Type::VIDEO) {}
+
+void DecoderBufferCache::AddBuffers(const DecoderBuffers& buffers,
+                                    const SbMediaAudioStreamInfo& stream_info) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  audio_buffer_group_.AddBuffers(buffers, stream_info);
+}
+
+void DecoderBufferCache::AddBuffers(const DecoderBuffers& buffers,
+                                    const SbMediaVideoStreamInfo& stream_info) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  video_buffer_group_.AddBuffers(buffers, stream_info);
+}
+
+void DecoderBufferCache::ClearSegmentsBeforeMediaTime(
+    const base::TimeDelta& media_time) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  audio_buffer_group_.ClearSegmentsBeforeMediaTime(media_time);
+  video_buffer_group_.ClearSegmentsBeforeMediaTime(media_time);
+}
+
+void DecoderBufferCache::ClearAll() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  audio_buffer_group_.ClearAll();
+  video_buffer_group_.ClearAll();
+}
+
+void DecoderBufferCache::StartResuming() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  audio_buffer_group_.ResetIndex();
+  video_buffer_group_.ResetIndex();
+}
+
+bool DecoderBufferCache::HasMoreBuffers(DemuxerStream::Type type) const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (type == DemuxerStream::Type::AUDIO) {
+    return audio_buffer_group_.HasMoreBuffers();
+  }
+  DCHECK(type == DemuxerStream::Type::VIDEO);
+  return video_buffer_group_.HasMoreBuffers();
+}
+
+void DecoderBufferCache::ReadBuffers(DecoderBuffers* buffers,
+                                     size_t max_buffers_per_read,
+                                     SbMediaAudioStreamInfo* stream_info) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(buffers);
+  DCHECK(buffers->empty());
+  DCHECK(stream_info);
+
+  if (max_buffers_per_read == 0 || !audio_buffer_group_.HasMoreBuffers()) {
+    return;
+  }
+  audio_buffer_group_.ReadBuffers(buffers, max_buffers_per_read, stream_info);
+  DCHECK(!buffers->empty());
+}
+
+void DecoderBufferCache::ReadBuffers(DecoderBuffers* buffers,
+                                     size_t max_buffers_per_read,
+                                     SbMediaVideoStreamInfo* stream_info) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(buffers);
+  DCHECK(buffers->empty());
+  CHECK(stream_info);
+
+  if (max_buffers_per_read == 0 || !video_buffer_group_.HasMoreBuffers()) {
+    return;
+  }
+  video_buffer_group_.ReadBuffers(buffers, max_buffers_per_read, stream_info);
+  DCHECK(!buffers->empty());
 }
 
 }  // namespace media
diff --git a/cobalt/media/base/decoder_buffer_cache.h b/cobalt/media/base/decoder_buffer_cache.h
index 67c95b5..64a56f6 100644
--- a/cobalt/media/base/decoder_buffer_cache.h
+++ b/cobalt/media/base/decoder_buffer_cache.h
@@ -16,10 +16,14 @@
 #define COBALT_MEDIA_BASE_DECODER_BUFFER_CACHE_H_
 
 #include <deque>
+#include <map>
+#include <string>
+#include <vector>
 
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
+#include "starboard/media.h"
 #include "third_party/chromium//media/base/decoder_buffer.h"
 #include "third_party/chromium//media/base/demuxer_stream.h"
 
@@ -35,39 +39,93 @@
  public:
   typedef ::media::DecoderBuffer DecoderBuffer;
   typedef ::media::DemuxerStream DemuxerStream;
+  typedef std::vector<scoped_refptr<DecoderBuffer>> DecoderBuffers;
 
   DecoderBufferCache();
 
-  void AddBuffer(DemuxerStream::Type type,
-                 const scoped_refptr<DecoderBuffer>& buffer);
-  void ClearSegmentsBeforeMediaTime(base::TimeDelta media_time);
+  // TODO(b/268098991): Replace them with AudioStreamInfo and VideoStreamInfo
+  //                    wrapper classes.
+  void AddBuffers(const DecoderBuffers& buffers,
+                  const SbMediaAudioStreamInfo& stream_info);
+  void AddBuffers(const DecoderBuffers& buffers,
+                  const SbMediaVideoStreamInfo& stream_info);
+  void ClearSegmentsBeforeMediaTime(const base::TimeDelta& media_time);
   void ClearAll();
 
   // Start resuming, reset indices to audio/video buffers to the very beginning.
   void StartResuming();
-  scoped_refptr<DecoderBuffer> GetBuffer(DemuxerStream::Type type) const;
-  void AdvanceToNextBuffer(DemuxerStream::Type type);
+  bool HasMoreBuffers(DemuxerStream::Type type) const;
+  // |buffers| and |stream_info| cannot be null.
+  void ReadBuffers(DecoderBuffers* buffers, size_t max_buffers_per_read,
+                   SbMediaAudioStreamInfo* stream_info);
+  void ReadBuffers(DecoderBuffers* buffers, size_t max_buffers_per_read,
+                   SbMediaVideoStreamInfo* stream_info);
 
  private:
-  typedef std::deque<scoped_refptr<DecoderBuffer>> Buffers;
-  typedef std::deque<base::TimeDelta> KeyFrameTimestamps;
+  // The buffers with same stream info will be stored in same segment.
+  class BufferGroup {
+   public:
+    explicit BufferGroup(DemuxerStream::Type type);
+    void AddBuffers(const DecoderBuffers& buffers,
+                    const SbMediaAudioStreamInfo& stream_info);
+    void AddBuffers(const DecoderBuffers& buffers,
+                    const SbMediaVideoStreamInfo& stream_info);
+    void ClearSegmentsBeforeMediaTime(const base::TimeDelta& media_time);
+    void ClearAll();
 
-  static size_t ClearSegmentsBeforeMediaTime(
-      base::TimeDelta media_time, Buffers* buffers,
-      KeyFrameTimestamps* key_frame_timestamps);
+    bool HasMoreBuffers() const;
+    void ReadBuffers(DecoderBuffers* buffers, size_t max_buffers_per_read,
+                     SbMediaAudioStreamInfo* stream_info);
+    void ReadBuffers(DecoderBuffers* buffers, size_t max_buffers_per_read,
+                     SbMediaVideoStreamInfo* stream_info);
+    void ResetIndex();
+
+   private:
+    struct Segment {
+      explicit Segment(const SbMediaAudioStreamInfo& stream_info);
+      explicit Segment(const SbMediaVideoStreamInfo& stream_info);
+
+      std::deque<scoped_refptr<DecoderBuffer>> buffers;
+      union {
+        SbMediaAudioStreamInfo audio_stream_info;
+        SbMediaVideoStreamInfo video_stream_info;
+      };
+      // Deep copy of the audio and video stream infos.
+      std::string mime;
+      std::string max_video_capabilities;
+      std::vector<uint8_t> audio_specific_config;
+    };
+
+    BufferGroup(const BufferGroup&) = delete;
+    BufferGroup& operator=(const BufferGroup&) = delete;
+
+    template <typename StreamInfo>
+    void AddStreamInfo(const StreamInfo& stream_info);
+    void AddBuffers(const DecoderBuffers& buffers);
+    void AdvanceGroupIndexIfNecessary();
+    void ReadBuffers(DecoderBuffers* buffers, size_t max_buffers_per_read);
+
+    const DemuxerStream::Type type_;
+    std::deque<Segment> segments_;
+    std::deque<base::TimeDelta> key_frame_timestamps_;
+    size_t segment_index_ = 0;
+    size_t buffer_index_ = 0;
+  };
 
   THREAD_CHECKER(thread_checker_);
 
-  Buffers audio_buffers_;
-  KeyFrameTimestamps audio_key_frame_timestamps_;
-
-  Buffers video_buffers_;
-  KeyFrameTimestamps video_key_frame_timestamps_;
-
-  size_t audio_buffer_index_;
-  size_t video_buffer_index_;
+  BufferGroup audio_buffer_group_;
+  BufferGroup video_buffer_group_;
 };
 
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+// Expose the functions for tests.
+bool operator!=(const SbMediaAudioStreamInfo& left,
+                const SbMediaAudioStreamInfo& right);
+bool operator!=(const SbMediaVideoStreamInfo& left,
+                const SbMediaVideoStreamInfo& right);
+#endif  // !defined(COBALT_BUILD_TYPE_GOLD)
+
 }  // namespace media
 }  // namespace cobalt
 
diff --git a/cobalt/media/base/decoder_buffer_cache_test.cc b/cobalt/media/base/decoder_buffer_cache_test.cc
new file mode 100644
index 0000000..737dc20
--- /dev/null
+++ b/cobalt/media/base/decoder_buffer_cache_test.cc
@@ -0,0 +1,484 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/media/base/decoder_buffer_cache.h"
+
+#include <algorithm>
+
+#include "base/time/time.h"
+#include "cobalt/media/decoder_buffer_allocator.h"
+#include "starboard/media.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/chromium//media/base/decoder_buffer.h"
+
+namespace cobalt {
+namespace media {
+namespace {
+
+using ::media::DecoderBuffer;
+using ::media::DemuxerStream;
+
+constexpr size_t kMaxSamplesPerRead = 8;
+constexpr size_t kBufferArraySize = 8;
+constexpr size_t kBufferSize = 8;
+constexpr uint8_t kBufferDataArray[kBufferArraySize][kBufferSize] = {
+    {0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01},
+    {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02},
+    {0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03},
+    {0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04},
+    {0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05},
+    {0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06},
+    {0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07},
+    {0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08},
+};
+
+const size_t kVideoKeyFrameFrequency = 4;
+const size_t kDurationPerFrame = 16;
+
+bool operator==(const SbMediaAudioStreamInfo& left,
+                const SbMediaAudioStreamInfo& right) {
+  return !(left != right);
+}
+
+bool operator==(const SbMediaVideoStreamInfo& left,
+                const SbMediaVideoStreamInfo& right) {
+  return !(left != right);
+}
+
+SbMediaAudioStreamInfo GetAudioStreamInfo() {
+  SbMediaAudioStreamInfo stream_info;
+  stream_info.codec = kSbMediaAudioCodecAac;
+  stream_info.mime = "";
+  stream_info.number_of_channels = 2;
+  stream_info.samples_per_second = 44100;
+  stream_info.audio_specific_config_size = 0;
+  return stream_info;
+}
+
+SbMediaVideoStreamInfo GetVideoStreamInfo() {
+  SbMediaVideoStreamInfo stream_info;
+  stream_info.codec = kSbMediaVideoCodecH264;
+  stream_info.mime = "";
+  stream_info.max_video_capabilities = "";
+  stream_info.frame_width = 1920;
+  stream_info.frame_height = 1080;
+  return stream_info;
+}
+
+DecoderBufferCache::DecoderBuffers GetAudioSourceBuffers() {
+  DecoderBufferCache::DecoderBuffers source_buffers;
+  for (size_t i = 0; i < kBufferArraySize; i++) {
+    auto decoder_buffer =
+        media::DecoderBuffer::CopyFrom(kBufferDataArray[i], kBufferSize);
+    decoder_buffer->set_is_key_frame(true);
+    decoder_buffer->set_timestamp(
+        base::TimeDelta::FromMilliseconds(kDurationPerFrame * i));
+    source_buffers.push_back(decoder_buffer);
+  }
+  return source_buffers;
+}
+
+DecoderBufferCache::DecoderBuffers GetVideoSourceBuffers() {
+  DecoderBufferCache::DecoderBuffers source_buffers;
+  for (size_t i = 0; i < kBufferArraySize; i++) {
+    auto decoder_buffer =
+        media::DecoderBuffer::CopyFrom(kBufferDataArray[i], kBufferSize);
+    if (i % kVideoKeyFrameFrequency == 0) {
+      decoder_buffer->set_is_key_frame(true);
+      decoder_buffer->set_timestamp(
+          base::TimeDelta::FromMilliseconds(kDurationPerFrame * i));
+    } else {
+      // Intended to reverse the timestamp orders.
+      decoder_buffer->set_timestamp(base::TimeDelta::FromMilliseconds(
+          kDurationPerFrame *
+          (i + kVideoKeyFrameFrequency - 2 * (i % kVideoKeyFrameFrequency))));
+    }
+    source_buffers.push_back(decoder_buffer);
+  }
+  return source_buffers;
+}
+
+bool CompareOutputBuffers(const DecoderBufferCache::DecoderBuffers& source,
+                          size_t start_index,
+                          const DecoderBufferCache::DecoderBuffers& output) {
+  if (start_index + output.size() > source.size()) {
+    return false;
+  }
+  for (size_t i = 0; i < output.size(); i++) {
+    if (source[start_index + i] != output[i]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+template <typename StreamInfo>
+void ReadAndCompareOutputs(DecoderBufferCache* cache, size_t buffers_to_read,
+                           const DecoderBufferCache::DecoderBuffers& source,
+                           size_t start_index,
+                           const StreamInfo& source_stream_info) {
+  StreamInfo output_stream_info;
+  DecoderBufferCache::DecoderBuffers output_buffers;
+  while (buffers_to_read > 0) {
+    output_buffers.clear();
+    cache->ReadBuffers(&output_buffers,
+                       std::min(kMaxSamplesPerRead, buffers_to_read),
+                       &output_stream_info);
+    ASSERT_TRUE(source_stream_info == output_stream_info);
+    ASSERT_GT(output_buffers.size(), 0);
+    ASSERT_TRUE(CompareOutputBuffers(source, start_index, output_buffers));
+    start_index += output_buffers.size();
+    buffers_to_read -= output_buffers.size();
+  }
+}
+
+size_t FindKeyFrameIndexBeforeTime(
+    const DecoderBufferCache::DecoderBuffers& source,
+    const base::TimeDelta& time) {
+  SB_DCHECK(!source.empty());
+  SB_DCHECK(source[0]->is_key_frame());
+
+  size_t previous_key_frame_index = 0;
+  for (size_t i = 1; i < source.size(); i++) {
+    if (source[i]->is_key_frame()) {
+      if (source[i]->timestamp() <= time) {
+        previous_key_frame_index = i;
+      } else {
+        return previous_key_frame_index;
+      }
+    }
+  }
+  return previous_key_frame_index;
+}
+
+}  // namespace
+
+TEST(DecoderBufferCacheTest, SunnyDay) {
+  DecoderBufferAllocator allocator;
+  DecoderBufferCache cache;
+
+  SbMediaAudioStreamInfo source_audio_stream_info = GetAudioStreamInfo();
+  SbMediaVideoStreamInfo source_video_stream_info = GetVideoStreamInfo();
+  DecoderBufferCache::DecoderBuffers source_audio_buffers =
+      GetAudioSourceBuffers();
+  DecoderBufferCache::DecoderBuffers source_video_buffers =
+      GetVideoSourceBuffers();
+  cache.AddBuffers(source_audio_buffers, source_audio_stream_info);
+  cache.AddBuffers(source_video_buffers, source_video_stream_info);
+
+  ASSERT_NO_FATAL_FAILURE(
+      ReadAndCompareOutputs(&cache, source_audio_buffers.size(),
+                            source_audio_buffers, 0, source_audio_stream_info));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::AUDIO));
+
+  ASSERT_NO_FATAL_FAILURE(
+      ReadAndCompareOutputs(&cache, source_video_buffers.size(),
+                            source_video_buffers, 0, source_video_stream_info));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::VIDEO));
+
+  cache.ClearAll();
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::AUDIO));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::VIDEO));
+}
+
+TEST(DecoderBufferCacheTest, EmptyCache) {
+  DecoderBufferCache cache;
+
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::AUDIO));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::VIDEO));
+
+  DecoderBufferCache::DecoderBuffers buffers;
+  SbMediaAudioStreamInfo audio_stream_info;
+  SbMediaVideoStreamInfo video_stream_info;
+
+  cache.ReadBuffers(&buffers, kMaxSamplesPerRead, &audio_stream_info);
+  ASSERT_TRUE(buffers.empty());
+  cache.ReadBuffers(&buffers, kMaxSamplesPerRead, &video_stream_info);
+  ASSERT_TRUE(buffers.empty());
+
+  cache.ClearSegmentsBeforeMediaTime(base::TimeDelta());
+
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::AUDIO));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::VIDEO));
+
+  cache.ReadBuffers(&buffers, kMaxSamplesPerRead, &audio_stream_info);
+  ASSERT_TRUE(buffers.empty());
+
+  cache.ReadBuffers(&buffers, kMaxSamplesPerRead, &video_stream_info);
+  ASSERT_TRUE(buffers.empty());
+}
+
+TEST(DecoderBufferCacheTest, ClearSegmentsWithZero) {
+  DecoderBufferAllocator allocator;
+  DecoderBufferCache cache;
+
+  SbMediaAudioStreamInfo source_audio_stream_info = GetAudioStreamInfo();
+  SbMediaVideoStreamInfo source_video_stream_info = GetVideoStreamInfo();
+  DecoderBufferCache::DecoderBuffers source_audio_buffers =
+      GetAudioSourceBuffers();
+  DecoderBufferCache::DecoderBuffers source_video_buffers =
+      GetVideoSourceBuffers();
+  cache.AddBuffers(source_audio_buffers, source_audio_stream_info);
+  cache.AddBuffers(source_video_buffers, source_video_stream_info);
+
+  // Cache should not clear anything.
+  cache.ClearSegmentsBeforeMediaTime(base::TimeDelta::FromMilliseconds(0));
+
+  ASSERT_NO_FATAL_FAILURE(
+      ReadAndCompareOutputs(&cache, source_audio_buffers.size(),
+                            source_audio_buffers, 0, source_audio_stream_info));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::AUDIO));
+
+  ASSERT_NO_FATAL_FAILURE(
+      ReadAndCompareOutputs(&cache, source_video_buffers.size(),
+                            source_video_buffers, 0, source_video_stream_info));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::VIDEO));
+}
+
+TEST(DecoderBufferCacheTest, ClearSegmentsAndResume) {
+  DecoderBufferAllocator allocator;
+  DecoderBufferCache cache;
+
+  SbMediaAudioStreamInfo source_audio_stream_info = GetAudioStreamInfo();
+  SbMediaVideoStreamInfo source_video_stream_info = GetVideoStreamInfo();
+  DecoderBufferCache::DecoderBuffers source_audio_buffers =
+      GetAudioSourceBuffers();
+  DecoderBufferCache::DecoderBuffers source_video_buffers =
+      GetVideoSourceBuffers();
+  cache.AddBuffers(source_audio_buffers, source_audio_stream_info);
+  cache.AddBuffers(source_video_buffers, source_video_stream_info);
+
+  base::TimeDelta clear_time =
+      base::TimeDelta::FromMilliseconds(kDurationPerFrame);
+  cache.ClearSegmentsBeforeMediaTime(clear_time);
+
+  size_t start_index =
+      FindKeyFrameIndexBeforeTime(source_audio_buffers, clear_time);
+  ASSERT_NO_FATAL_FAILURE(ReadAndCompareOutputs(
+      &cache, source_audio_buffers.size() - start_index, source_audio_buffers,
+      start_index, source_audio_stream_info));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::AUDIO));
+
+  start_index = FindKeyFrameIndexBeforeTime(source_video_buffers, clear_time);
+  ASSERT_NO_FATAL_FAILURE(ReadAndCompareOutputs(
+      &cache, source_video_buffers.size() - start_index, source_video_buffers,
+      start_index, source_video_stream_info));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::VIDEO));
+
+  cache.StartResuming();
+
+  start_index = FindKeyFrameIndexBeforeTime(source_audio_buffers, clear_time);
+  ASSERT_NO_FATAL_FAILURE(ReadAndCompareOutputs(
+      &cache, source_audio_buffers.size() - start_index, source_audio_buffers,
+      start_index, source_audio_stream_info));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::AUDIO));
+
+  start_index = FindKeyFrameIndexBeforeTime(source_video_buffers, clear_time);
+  ASSERT_NO_FATAL_FAILURE(ReadAndCompareOutputs(
+      &cache, source_video_buffers.size() - start_index, source_video_buffers,
+      start_index, source_video_stream_info));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::VIDEO));
+
+  clear_time = base::TimeDelta::FromMilliseconds(kDurationPerFrame *
+                                                 (kVideoKeyFrameFrequency + 1));
+  cache.ClearSegmentsBeforeMediaTime(clear_time);
+  cache.StartResuming();
+
+  start_index = FindKeyFrameIndexBeforeTime(source_audio_buffers, clear_time);
+  ASSERT_NO_FATAL_FAILURE(ReadAndCompareOutputs(
+      &cache, source_audio_buffers.size() - start_index, source_audio_buffers,
+      start_index, source_audio_stream_info));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::AUDIO));
+
+  start_index = FindKeyFrameIndexBeforeTime(source_video_buffers, clear_time);
+  ASSERT_NO_FATAL_FAILURE(ReadAndCompareOutputs(
+      &cache, source_video_buffers.size() - start_index, source_video_buffers,
+      start_index, source_video_stream_info));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::VIDEO));
+}
+
+TEST(DecoderBufferCacheTest, MultipleStreamInfos) {
+  DecoderBufferAllocator allocator;
+  DecoderBufferCache cache;
+
+  SbMediaAudioStreamInfo source_audio_stream_info_1 = GetAudioStreamInfo();
+  SbMediaVideoStreamInfo source_video_stream_info_1 = GetVideoStreamInfo();
+
+  SbMediaAudioStreamInfo source_audio_stream_info_2 = GetAudioStreamInfo();
+  source_audio_stream_info_2.samples_per_second *= 2;
+  SbMediaVideoStreamInfo source_video_stream_info_2 = GetVideoStreamInfo();
+  source_video_stream_info_2.frame_width *= 2;
+  source_video_stream_info_2.frame_height *= 2;
+
+  DecoderBufferCache::DecoderBuffers source_audio_buffers =
+      GetAudioSourceBuffers();
+  DecoderBufferCache::DecoderBuffers source_video_buffers =
+      GetVideoSourceBuffers();
+
+  DecoderBufferCache::DecoderBuffers source_audio_buffers_1(
+      source_audio_buffers.begin(),
+      source_audio_buffers.begin() + kVideoKeyFrameFrequency);
+  DecoderBufferCache::DecoderBuffers source_video_buffers_1(
+      source_video_buffers.begin(),
+      source_video_buffers.begin() + kVideoKeyFrameFrequency);
+
+  DecoderBufferCache::DecoderBuffers source_audio_buffers_2(
+      source_audio_buffers.begin() + kVideoKeyFrameFrequency,
+      source_audio_buffers.end());
+  DecoderBufferCache::DecoderBuffers source_video_buffers_2(
+      source_video_buffers.begin() + kVideoKeyFrameFrequency,
+      source_video_buffers.end());
+
+  cache.AddBuffers(source_audio_buffers_1, source_audio_stream_info_1);
+  cache.AddBuffers(source_video_buffers_1, source_video_stream_info_1);
+  cache.AddBuffers(source_audio_buffers_2, source_audio_stream_info_2);
+  cache.AddBuffers(source_video_buffers_2, source_video_stream_info_2);
+
+  DecoderBufferCache::DecoderBuffers output_buffers;
+  SbMediaAudioStreamInfo output_audio_stream_info;
+  SbMediaVideoStreamInfo output_video_stream_info;
+
+  // The output buffers in one read call should have same configuration.
+  cache.ReadBuffers(&output_buffers, kMaxSamplesPerRead,
+                    &output_audio_stream_info);
+  ASSERT_TRUE(source_audio_stream_info_1 == output_audio_stream_info);
+  ASSERT_EQ(output_buffers.size(), source_audio_buffers_1.size());
+
+  output_buffers.clear();
+  cache.ReadBuffers(&output_buffers, kMaxSamplesPerRead,
+                    &output_video_stream_info);
+  ASSERT_TRUE(source_video_stream_info_1 == output_video_stream_info);
+  ASSERT_EQ(output_buffers.size(), source_video_buffers_1.size());
+
+  cache.StartResuming();
+  ASSERT_NO_FATAL_FAILURE(ReadAndCompareOutputs(
+      &cache, source_audio_buffers_1.size(), source_audio_buffers_1, 0,
+      source_audio_stream_info_1));
+
+  ASSERT_NO_FATAL_FAILURE(ReadAndCompareOutputs(
+      &cache, source_video_buffers_1.size(), source_video_buffers_1, 0,
+      source_video_stream_info_1));
+
+  ASSERT_NO_FATAL_FAILURE(ReadAndCompareOutputs(
+      &cache, source_audio_buffers_2.size(), source_audio_buffers_2, 0,
+      source_audio_stream_info_2));
+
+  ASSERT_NO_FATAL_FAILURE(ReadAndCompareOutputs(
+      &cache, source_video_buffers_2.size(), source_video_buffers_2, 0,
+      source_video_stream_info_2));
+
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::AUDIO));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::VIDEO));
+
+  base::TimeDelta clear_time = base::TimeDelta::FromMilliseconds(
+      kDurationPerFrame * (kVideoKeyFrameFrequency));
+  cache.ClearSegmentsBeforeMediaTime(clear_time);
+  cache.StartResuming();
+
+  ASSERT_NO_FATAL_FAILURE(ReadAndCompareOutputs(
+      &cache, source_audio_buffers_2.size(), source_audio_buffers_2, 0,
+      source_audio_stream_info_2));
+
+  ASSERT_NO_FATAL_FAILURE(ReadAndCompareOutputs(
+      &cache, source_video_buffers_2.size(), source_video_buffers_2, 0,
+      source_video_stream_info_2));
+
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::AUDIO));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::VIDEO));
+}
+
+TEST(DecoderBufferCacheTest, AudioOnly) {
+  DecoderBufferAllocator allocator;
+  DecoderBufferCache cache;
+
+  SbMediaAudioStreamInfo source_audio_stream_info = GetAudioStreamInfo();
+  DecoderBufferCache::DecoderBuffers source_audio_buffers =
+      GetAudioSourceBuffers();
+  cache.AddBuffers(source_audio_buffers, source_audio_stream_info);
+
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::VIDEO));
+
+  ASSERT_NO_FATAL_FAILURE(
+      ReadAndCompareOutputs(&cache, source_audio_buffers.size(),
+                            source_audio_buffers, 0, source_audio_stream_info));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::AUDIO));
+}
+
+TEST(DecoderBufferCacheTest, VideoOnly) {
+  DecoderBufferAllocator allocator;
+  DecoderBufferCache cache;
+
+  SbMediaVideoStreamInfo source_video_stream_info = GetVideoStreamInfo();
+  DecoderBufferCache::DecoderBuffers source_video_buffers =
+      GetVideoSourceBuffers();
+  cache.AddBuffers(source_video_buffers, source_video_stream_info);
+
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::AUDIO));
+
+  ASSERT_NO_FATAL_FAILURE(
+      ReadAndCompareOutputs(&cache, source_video_buffers.size(),
+                            source_video_buffers, 0, source_video_stream_info));
+  ASSERT_FALSE(cache.HasMoreBuffers(DemuxerStream::VIDEO));
+}
+
+TEST(DecoderBufferCacheTest, StreamInfosDeepCopied) {
+  const char* kAudioMime = "audio/mp4; codecs=\"mp4a.40.2\"";
+  const char* kAudioSpecificConfig = "0000";
+  const char* kVideoMime = "video/mp4; codecs=\"avc1.42001E\"";
+  const char* kMaxVideoCapabilities = "width=1920; height=1080; framerate=15";
+
+  DecoderBufferAllocator allocator;
+  DecoderBufferCache cache;
+
+  SbMediaAudioStreamInfo source_audio_stream_info = GetAudioStreamInfo();
+  source_audio_stream_info.mime = kAudioMime;
+  source_audio_stream_info.audio_specific_config_size =
+      strlen(kAudioSpecificConfig);
+  source_audio_stream_info.audio_specific_config = kAudioSpecificConfig;
+
+  SbMediaVideoStreamInfo source_video_stream_info = GetVideoStreamInfo();
+  source_video_stream_info.mime = kVideoMime;
+  source_video_stream_info.max_video_capabilities = kMaxVideoCapabilities;
+
+  DecoderBufferCache::DecoderBuffers source_audio_buffers =
+      GetAudioSourceBuffers();
+  DecoderBufferCache::DecoderBuffers source_video_buffers =
+      GetVideoSourceBuffers();
+  cache.AddBuffers(source_audio_buffers, source_audio_stream_info);
+  cache.AddBuffers(source_video_buffers, source_video_stream_info);
+
+  DecoderBufferCache::DecoderBuffers output_buffers;
+  SbMediaAudioStreamInfo output_audio_stream_info;
+  SbMediaVideoStreamInfo output_video_stream_info;
+
+  output_buffers.clear();
+  cache.ReadBuffers(&output_buffers, kMaxSamplesPerRead,
+                    &output_audio_stream_info);
+  ASSERT_TRUE(source_audio_stream_info == output_audio_stream_info);
+  ASSERT_NE(source_audio_stream_info.mime, output_audio_stream_info.mime);
+  ASSERT_NE(source_audio_stream_info.audio_specific_config,
+            output_audio_stream_info.audio_specific_config);
+
+  output_buffers.clear();
+  cache.ReadBuffers(&output_buffers, kMaxSamplesPerRead,
+                    &output_video_stream_info);
+  ASSERT_TRUE(source_video_stream_info == output_video_stream_info);
+  ASSERT_NE(source_video_stream_info.mime, output_video_stream_info.mime);
+  ASSERT_NE(source_video_stream_info.max_video_capabilities,
+            output_video_stream_info.max_video_capabilities);
+}
+
+
+}  // namespace media
+}  // namespace cobalt
diff --git a/cobalt/media/base/drm_system.cc b/cobalt/media/base/drm_system.cc
index d44e583..f92f1a7 100644
--- a/cobalt/media/base/drm_system.cc
+++ b/cobalt/media/base/drm_system.cc
@@ -101,7 +101,11 @@
 DrmSystem::~DrmSystem() {
   ON_INSTANCE_RELEASED(DrmSystem);
 
-  SbDrmDestroySystem(wrapped_drm_system_);
+  if (is_valid()) {
+    SbDrmDestroySystem(wrapped_drm_system_);
+  } else {
+    LOG(WARNING) << "Attempting to close invalid SbDrmSystem";
+  }
 }
 
 std::unique_ptr<DrmSystem::Session> DrmSystem::CreateSession(
@@ -114,6 +118,8 @@
 
 bool DrmSystem::IsServerCertificateUpdatable() {
   DCHECK(message_loop_->BelongsToCurrentThread());
+  DCHECK(is_valid());
+
   if (SbDrmIsServerCertificateUpdatable(wrapped_drm_system_)) {
     LOG(INFO) << "SbDrmSystem (" << wrapped_drm_system_
               << ") supports server certificate update";
@@ -129,12 +135,22 @@
     ServerCertificateUpdatedCallback server_certificate_updated_callback) {
   DCHECK(message_loop_->BelongsToCurrentThread());
   DCHECK(IsServerCertificateUpdatable());
+  DCHECK(is_valid());
+
+  if (!certificate) {
+    LOG(ERROR) << "Updating server with NULL certificate";
+    return;
+  }
 
   LOG(INFO) << "Updating server certificate of drm system ("
             << wrapped_drm_system_
             << "), certificate size: " << certificate_size;
 
   int ticket = next_ticket_++;
+  if (!SbDrmTicketIsValid(ticket)) {
+    LOG(ERROR) << "Updating server with invalid ticket";
+    return;
+  }
   ticket_to_server_certificate_updated_map_.insert(
       std::make_pair(ticket, server_certificate_updated_callback));
   SbDrmUpdateServerCertificate(wrapped_drm_system_, ticket, certificate,
@@ -166,6 +182,12 @@
     const SessionUpdateRequestDidNotGenerateCallback&
         session_update_request_did_not_generate_callback) {
   DCHECK(message_loop_->BelongsToCurrentThread());
+  DCHECK(is_valid());
+
+  if (!init_data) {
+    LOG(ERROR) << "Generate session update request with invalid init_data";
+    return;
+  }
 
   // Store the context of the call.
   SessionUpdateRequest session_update_request;
@@ -175,6 +197,12 @@
   session_update_request.did_not_generate_callback =
       session_update_request_did_not_generate_callback;
   int ticket = next_ticket_++;
+
+  if (!SbDrmTicketIsValid(ticket)) {
+    LOG(ERROR) << "Generate session update request with invalid ticket";
+    return;
+  }
+
   ticket_to_session_update_request_map_.insert(
       std::make_pair(ticket, session_update_request));
 
@@ -192,12 +220,19 @@
     const SessionUpdatedCallback& session_updated_callback,
     const SessionDidNotUpdateCallback& session_did_not_update_callback) {
   DCHECK(message_loop_->BelongsToCurrentThread());
+  DCHECK(is_valid());
 
   // Store the context of the call.
   SessionUpdate session_update;
   session_update.updated_callback = session_updated_callback;
   session_update.did_not_update_callback = session_did_not_update_callback;
   int ticket = next_ticket_++;
+
+  if (!SbDrmTicketIsValid(ticket)) {
+    LOG(ERROR) << "Update session with invalid ticket";
+    return;
+  }
+
   ticket_to_session_update_map_.insert(std::make_pair(ticket, session_update));
 
   LOG(INFO) << "Update session of drm system (" << wrapped_drm_system_
@@ -210,6 +245,7 @@
 
 void DrmSystem::CloseSession(const std::string& session_id) {
   DCHECK(message_loop_->BelongsToCurrentThread());
+  DCHECK(is_valid());
 
   LOG(INFO) << "Close session of drm system (" << wrapped_drm_system_
             << "), session id: " << session_id;
diff --git a/cobalt/media/base/playback_statistics.cc b/cobalt/media/base/playback_statistics.cc
index babd725..f8c62be 100644
--- a/cobalt/media/base/playback_statistics.cc
+++ b/cobalt/media/base/playback_statistics.cc
@@ -130,7 +130,11 @@
                        "The status of the media pipeline."),
       error_message_(base::StringPrintf("Media.Pipeline.%s.ErrorMessage",
                                         pipeline_identifier.c_str()),
-                     "", "The error message of the media pipeline error.") {}
+                     "", "The error message of the media pipeline error."),
+      current_video_codec_(
+          base::StringPrintf("Media.Pipeline.%s.CurrentCodec",
+                             pipeline_identifier.c_str()),
+          "", "The currently configured Codec in VideoDecoderConfig.") {}
 
 PlaybackStatistics::~PlaybackStatistics() {
   if (has_active_instance_) {
@@ -165,6 +169,7 @@
 
   video_width_ = video_config.natural_size().width();
   video_height_ = video_config.natural_size().height();
+  current_video_codec_ = GetCodecName(video_config.codec());
 
   const auto width = static_cast<SbAtomic32>(video_width_);
   const auto height = static_cast<SbAtomic32>(video_height_);
diff --git a/cobalt/media/base/playback_statistics.h b/cobalt/media/base/playback_statistics.h
index 220d472..44330ed 100644
--- a/cobalt/media/base/playback_statistics.h
+++ b/cobalt/media/base/playback_statistics.h
@@ -58,6 +58,7 @@
   base::CVal<bool> is_video_eos_written_;
   base::CVal<PipelineStatus> pipeline_status_;
   base::CVal<std::string> error_message_;
+  base::CVal<std::string> current_video_codec_;
   bool has_active_instance_ = false;
   bool is_first_audio_buffer_written_ = false;
   bool is_first_video_buffer_written_ = false;
diff --git a/cobalt/media/base/sbplayer_bridge.cc b/cobalt/media/base/sbplayer_bridge.cc
index 6598dc8..7194560 100644
--- a/cobalt/media/base/sbplayer_bridge.cc
+++ b/cobalt/media/base/sbplayer_bridge.cc
@@ -44,7 +44,80 @@
   base::Statistics<SbTime, int, 1024> startup_latency{
       "Media.PlaybackStartupLatency"};
 };
+
+void SetStreamInfo(
+    const SbMediaAudioStreamInfo& stream_info,
+    CobaltExtensionEnhancedAudioMediaAudioSampleInfo* sample_info) {
+  DCHECK(sample_info);
+
+  sample_info->stream_info.codec = stream_info.codec;
+  sample_info->stream_info.mime = stream_info.mime;
+  sample_info->stream_info.number_of_channels = stream_info.number_of_channels;
+  sample_info->stream_info.samples_per_second = stream_info.samples_per_second;
+  sample_info->stream_info.bits_per_sample = stream_info.bits_per_sample;
+  sample_info->stream_info.audio_specific_config_size =
+      stream_info.audio_specific_config_size;
+  sample_info->stream_info.audio_specific_config =
+      stream_info.audio_specific_config;
+}
+
+void SetStreamInfo(const SbMediaAudioStreamInfo& stream_info,
+                   SbMediaAudioSampleInfo* sample_info) {
+  DCHECK(sample_info);
+
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  sample_info->stream_info = stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  *sample_info = stream_info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION}
+}
+
+void SetStreamInfo(
+    const SbMediaVideoStreamInfo& stream_info,
+    CobaltExtensionEnhancedAudioMediaVideoSampleInfo* sample_info) {
+  DCHECK(sample_info);
+
+  sample_info->stream_info.codec = stream_info.codec;
+  sample_info->stream_info.mime = stream_info.mime;
+  sample_info->stream_info.max_video_capabilities =
+      stream_info.max_video_capabilities;
+  sample_info->stream_info.frame_width = stream_info.frame_width;
+  sample_info->stream_info.frame_height = stream_info.frame_height;
+}
+
+void SetStreamInfo(const SbMediaVideoStreamInfo& stream_info,
+                   SbMediaVideoSampleInfo* sample_info) {
+  DCHECK(sample_info);
+
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  sample_info->stream_info = stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  *sample_info = stream_info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION}
+}
+
+void SetDiscardPadding(
+    const ::media::DecoderBuffer::DiscardPadding& discard_padding,
+    CobaltExtensionEnhancedAudioMediaAudioSampleInfo* sample_info) {
+  DCHECK(sample_info);
+
+  sample_info->discarded_duration_from_front = discard_padding.first.ToSbTime();
+  sample_info->discarded_duration_from_back = discard_padding.second.ToSbTime();
+}
+
+void SetDiscardPadding(
+    const ::media::DecoderBuffer::DiscardPadding& discard_padding,
+    SbMediaAudioSampleInfo* sample_info) {
+  DCHECK(sample_info);
+
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  sample_info->discarded_duration_from_front = discard_padding.first.ToSbTime();
+  sample_info->discarded_duration_from_back = discard_padding.second.ToSbTime();
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION}
+}
+
 }  // namespace
+
 SB_ONCE_INITIALIZE_FUNCTION(StatisticsWrapper, StatisticsWrapper::GetInstance);
 
 SbPlayerBridge::CallbackHelper::CallbackHelper(SbPlayerBridge* player_bridge)
@@ -107,7 +180,8 @@
     bool prefer_decode_to_texture,
     const OnEncryptedMediaInitDataEncounteredCB&
         on_encrypted_media_init_data_encountered_cb,
-    DecodeTargetProvider* const decode_target_provider)
+    DecodeTargetProvider* const decode_target_provider,
+    std::string pipeline_identifier)
     : url_(url),
       sbplayer_interface_(interface),
       task_runner_(task_runner),
@@ -120,6 +194,8 @@
       on_encrypted_media_init_data_encountered_cb_(
           on_encrypted_media_init_data_encountered_cb),
       decode_target_provider_(decode_target_provider),
+      cval_stats_(&interface->cval_stats_),
+      pipeline_identifier_(pipeline_identifier),
       is_url_based_(true) {
   DCHECK(host_);
   DCHECK(set_bounds_helper_);
@@ -146,7 +222,7 @@
     SbPlayerSetBoundsHelper* set_bounds_helper, bool allow_resume_after_suspend,
     bool prefer_decode_to_texture,
     DecodeTargetProvider* const decode_target_provider,
-    const std::string& max_video_capabilities)
+    const std::string& max_video_capabilities, std::string pipeline_identifier)
     : sbplayer_interface_(interface),
       task_runner_(task_runner),
       get_decode_target_graphics_context_provider_func_(
@@ -161,7 +237,9 @@
       audio_config_(audio_config),
       video_config_(video_config),
       decode_target_provider_(decode_target_provider),
-      max_video_capabilities_(max_video_capabilities)
+      max_video_capabilities_(max_video_capabilities),
+      cval_stats_(&interface->cval_stats_),
+      pipeline_identifier_(pipeline_identifier)
 #if SB_HAS(PLAYER_WITH_URL)
       ,
       is_url_based_(false)
@@ -173,8 +251,8 @@
   DCHECK(set_bounds_helper_);
   DCHECK(decode_target_provider_);
 
-  audio_sample_info_.codec = kSbMediaAudioCodecNone;
-  video_sample_info_.codec = kSbMediaVideoCodecNone;
+  audio_stream_info_.codec = kSbMediaAudioCodecNone;
+  video_stream_info_.codec = kSbMediaVideoCodecNone;
 
   if (audio_config.IsValidConfig()) {
     UpdateAudioConfig(audio_config, audio_mime_type);
@@ -206,7 +284,9 @@
   decode_target_provider_->ResetGetCurrentSbDecodeTargetFunction();
 
   if (SbPlayerIsValid(player_)) {
+    cval_stats_->StartTimer(MediaTiming::SbPlayerDestroy, pipeline_identifier_);
     sbplayer_interface_->Destroy(player_);
+    cval_stats_->StopTimer(MediaTiming::SbPlayerDestroy, pipeline_identifier_);
   }
 }
 
@@ -220,9 +300,9 @@
 
   audio_config_ = audio_config;
   audio_mime_type_ = mime_type;
-  audio_sample_info_ = MediaAudioConfigToSbMediaAudioSampleInfo(
+  audio_stream_info_ = MediaAudioConfigToSbMediaAudioStreamInfo(
       audio_config_, audio_mime_type_.c_str());
-  LOG(INFO) << "Converted to SbMediaAudioSampleInfo -- " << audio_sample_info_;
+  LOG(INFO) << "Converted to SbMediaAudioStreamInfo -- " << audio_stream_info_;
 }
 
 void SbPlayerBridge::UpdateVideoConfig(const VideoDecoderConfig& video_config,
@@ -234,19 +314,19 @@
             << video_config.AsHumanReadableString();
 
   video_config_ = video_config;
-  video_sample_info_.frame_width =
+  video_stream_info_.frame_width =
       static_cast<int>(video_config_.natural_size().width());
-  video_sample_info_.frame_height =
+  video_stream_info_.frame_height =
       static_cast<int>(video_config_.natural_size().height());
-  video_sample_info_.codec =
+  video_stream_info_.codec =
       MediaVideoCodecToSbMediaVideoCodec(video_config_.codec());
-  video_sample_info_.color_metadata =
+  video_stream_info_.color_metadata =
       MediaToSbMediaColorMetadata(video_config_.color_space_info(),
                                   video_config_.hdr_metadata(), mime_type);
   video_mime_type_ = mime_type;
-  video_sample_info_.mime = video_mime_type_.c_str();
-  video_sample_info_.max_video_capabilities = max_video_capabilities_.c_str();
-  LOG(INFO) << "Converted to SbMediaVideoSampleInfo -- " << video_sample_info_;
+  video_stream_info_.mime = video_mime_type_.c_str();
+  video_stream_info_.max_video_capabilities = max_video_capabilities_.c_str();
+  LOG(INFO) << "Converted to SbMediaVideoStreamInfo -- " << video_stream_info_;
 }
 
 void SbPlayerBridge::WriteBuffers(
@@ -258,9 +338,11 @@
 #endif  // SB_HAS(PLAYER_WITH_URL)
 
   if (allow_resume_after_suspend_) {
-    for (const auto& buffer : buffers) {
-      DCHECK(buffer);
-      decoder_buffer_cache_.AddBuffer(type, buffer);
+    if (type == DemuxerStream::Type::AUDIO) {
+      decoder_buffer_cache_.AddBuffers(buffers, audio_stream_info_);
+    } else {
+      DCHECK(type == DemuxerStream::Type::VIDEO);
+      decoder_buffer_cache_.AddBuffers(buffers, video_stream_info_);
     }
     if (state_ != kSuspended) {
       WriteNextBuffersFromCache(type, buffers.size());
@@ -268,7 +350,13 @@
     return;
   }
 
-  WriteBuffersInternal(type, buffers);
+  if (sbplayer_interface_->IsEnhancedAudioExtensionEnabled()) {
+    WriteBuffersInternal<CobaltExtensionEnhancedAudioPlayerSampleInfo>(
+        type, buffers, &audio_stream_info_, &video_stream_info_);
+  } else {
+    WriteBuffersInternal<SbPlayerSampleInfo>(type, buffers, &audio_stream_info_,
+                                             &video_stream_info_);
+  }
 }
 
 void SbPlayerBridge::SetBounds(int z_index, const gfx::Rect& rect) {
@@ -396,21 +484,25 @@
   DCHECK(is_url_based_);
 
   if (state_ == kSuspended) {
-    *frame_width = video_sample_info_.frame_width;
-    *frame_height = video_sample_info_.frame_height;
+    *frame_width = video_stream_info_.frame_width;
+    *frame_height = video_stream_info_.frame_height;
     return;
   }
 
   DCHECK(SbPlayerIsValid(player_));
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerInfo out_player_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbPlayerInfo2 out_player_info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   sbplayer_interface_->GetInfo(player_, &out_player_info);
 
-  video_sample_info_.frame_width = out_player_info.frame_width;
-  video_sample_info_.frame_height = out_player_info.frame_height;
+  video_stream_info_.frame_width = out_player_info.frame_width;
+  video_stream_info_.frame_height = out_player_info.frame_height;
 
-  *frame_width = video_sample_info_.frame_width;
-  *frame_height = video_sample_info_.frame_height;
+  *frame_width = video_stream_info_.frame_width;
+  *frame_height = video_stream_info_.frame_height;
 }
 
 base::TimeDelta SbPlayerBridge::GetDuration() {
@@ -422,7 +514,11 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerInfo info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbPlayerInfo2 info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   sbplayer_interface_->GetInfo(player_, &info);
   if (info.duration == SB_PLAYER_NO_DURATION) {
     // URL-based player may not have loaded asset yet, so map no duration to 0.
@@ -440,7 +536,11 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerInfo info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbPlayerInfo2 info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   sbplayer_interface_->GetInfo(player_, &info);
   return base::TimeDelta::FromMicroseconds(info.start_date);
 }
@@ -477,7 +577,9 @@
       DecodeTargetProvider::kOutputModeInvalid);
   decode_target_provider_->ResetGetCurrentSbDecodeTargetFunction();
 
+  cval_stats_->StartTimer(MediaTiming::SbPlayerDestroy, pipeline_identifier_);
   sbplayer_interface_->Destroy(player_);
+  cval_stats_->StopTimer(MediaTiming::SbPlayerDestroy, pipeline_identifier_);
 
   player_ = kSbPlayerInvalid;
 }
@@ -558,10 +660,12 @@
 
   player_creation_time_ = SbTimeGetMonotonicNow();
 
+  cval_stats_->StartTimer(MediaTiming::SbPlayerCreate, pipeline_identifier_);
   player_ = sbplayer_interface_->CreateUrlPlayer(
       url.c_str(), window_, &SbPlayerBridge::PlayerStatusCB,
       &SbPlayerBridge::EncryptedMediaInitDataEncounteredCB,
       &SbPlayerBridge::PlayerErrorCB, this);
+  cval_stats_->StopTimer(MediaTiming::SbPlayerCreate, pipeline_identifier_);
   DCHECK(SbPlayerIsValid(player_));
 
   if (output_mode_ == kSbPlayerOutputModeDecodeToTexture) {
@@ -584,9 +688,7 @@
   DCHECK(task_runner_->BelongsToCurrentThread());
 
   bool is_visible = SbWindowIsValid(window_);
-  SbMediaAudioCodec audio_codec = audio_sample_info_.codec;
-
-  bool has_audio = audio_codec != kSbMediaAudioCodecNone;
+  bool has_audio = audio_stream_info_.codec != kSbMediaAudioCodecNone;
 
   is_creating_player_ = true;
 
@@ -598,21 +700,33 @@
 
   SbPlayerCreationParam creation_param = {};
   creation_param.drm_system = drm_system_;
-  creation_param.audio_sample_info = audio_sample_info_;
-  creation_param.video_sample_info = video_sample_info_;
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  creation_param.audio_stream_info = audio_stream_info_;
+  creation_param.video_stream_info = video_stream_info_;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  creation_param.audio_sample_info = audio_stream_info_;
+  creation_param.video_sample_info = video_stream_info_;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
   // TODO: This is temporary for supporting background media playback.
   //       Need to be removed with media refactor.
   if (!is_visible) {
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+    creation_param.video_stream_info.codec = kSbMediaVideoCodecNone;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
     creation_param.video_sample_info.codec = kSbMediaVideoCodecNone;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   }
   creation_param.output_mode = output_mode_;
   DCHECK_EQ(sbplayer_interface_->GetPreferredOutputMode(&creation_param),
             output_mode_);
+  cval_stats_->StartTimer(MediaTiming::SbPlayerCreate, pipeline_identifier_);
   player_ = sbplayer_interface_->Create(
       window_, &creation_param, &SbPlayerBridge::DeallocateSampleCB,
       &SbPlayerBridge::DecoderStatusCB, &SbPlayerBridge::PlayerStatusCB,
       &SbPlayerBridge::PlayerErrorCB, this,
       get_decode_target_graphics_context_provider_func_.Run());
+  cval_stats_->StopTimer(MediaTiming::SbPlayerCreate, pipeline_identifier_);
 
   is_creating_player_ = false;
 
@@ -643,34 +757,56 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
-  std::vector<scoped_refptr<DecoderBuffer>> buffers;
-  buffers.reserve(max_buffers_per_write);
-
-  // TODO: DecoderBufferCache doesn't respect config change during resume
-  // b/243308409
-  for (int i = 0; i < max_buffers_per_write; i++) {
-    const scoped_refptr<DecoderBuffer>& buffer =
-        decoder_buffer_cache_.GetBuffer(type);
-    if (!buffer) {
-      break;
+  if (type == DemuxerStream::AUDIO) {
+    std::vector<scoped_refptr<DecoderBuffer>> buffers;
+    SbMediaAudioStreamInfo stream_info;
+    decoder_buffer_cache_.ReadBuffers(&buffers, max_buffers_per_write,
+                                      &stream_info);
+    if (buffers.size() > 0) {
+      if (sbplayer_interface_->IsEnhancedAudioExtensionEnabled()) {
+        WriteBuffersInternal<CobaltExtensionEnhancedAudioPlayerSampleInfo>(
+            type, buffers, &stream_info, nullptr);
+      } else {
+        WriteBuffersInternal<SbPlayerSampleInfo>(type, buffers, &stream_info,
+                                                 nullptr);
+      }
     }
-    decoder_buffer_cache_.AdvanceToNextBuffer(type);
-    buffers.push_back(buffer);
+  } else {
+    DCHECK_EQ(type, DemuxerStream::VIDEO);
+    std::vector<scoped_refptr<DecoderBuffer>> buffers;
+    SbMediaVideoStreamInfo stream_info;
+    decoder_buffer_cache_.ReadBuffers(&buffers, max_buffers_per_write,
+                                      &stream_info);
+    if (buffers.size() > 0) {
+      if (sbplayer_interface_->IsEnhancedAudioExtensionEnabled()) {
+        WriteBuffersInternal<CobaltExtensionEnhancedAudioPlayerSampleInfo>(
+            type, buffers, nullptr, &stream_info);
+      } else {
+        WriteBuffersInternal<SbPlayerSampleInfo>(type, buffers, nullptr,
+                                                 &stream_info);
+      }
+    }
   }
-
-  WriteBuffersInternal(type, buffers);
 }
 
+template <typename PlayerSampleInfo>
 void SbPlayerBridge::WriteBuffersInternal(
     DemuxerStream::Type type,
-    const std::vector<scoped_refptr<DecoderBuffer>>& buffers) {
+    const std::vector<scoped_refptr<DecoderBuffer>>& buffers,
+    const SbMediaAudioStreamInfo* audio_stream_info,
+    const SbMediaVideoStreamInfo* video_stream_info) {
 #if SB_HAS(PLAYER_WITH_URL)
   DCHECK(!is_url_based_);
 #endif  // SB_HAS(PLAYER_WITH_URL)
 
   auto sample_type = DemuxerStreamTypeToSbMediaType(type);
+  if (buffers.size() == 1 && buffers[0]->end_of_stream()) {
+    sbplayer_interface_->WriteEndOfStream(player_,
+                                          DemuxerStreamTypeToSbMediaType(type));
+    return;
+  }
 
-  std::vector<SbPlayerSampleInfo> gathered_sbplayer_sample_infos;
+  std::vector<PlayerSampleInfo> gathered_sbplayer_sample_infos;
   std::vector<SbDrmSampleInfo> gathered_sbplayer_sample_infos_drm_info;
   std::vector<SbDrmSubSampleMapping>
       gathered_sbplayer_sample_infos_subsample_mapping;
@@ -685,22 +821,12 @@
     const auto& buffer = buffers[i];
     if (buffer->end_of_stream()) {
       DCHECK_EQ(i, buffers.size() - 1);
-      if (buffers.size() > 1) {
-        if (type == DemuxerStream::AUDIO) {
-          pending_audio_eos_buffer_ = true;
-        } else {
-          pending_video_eos_buffer_ = true;
-        }
-
-        DCHECK(!gathered_sbplayer_sample_infos.empty());
-        sbplayer_interface_->WriteSample(player_, sample_type,
-                                         gathered_sbplayer_sample_infos.data(),
-                                         gathered_sbplayer_sample_infos.size());
+      if (type == DemuxerStream::AUDIO) {
+        pending_audio_eos_buffer_ = true;
       } else {
-        sbplayer_interface_->WriteEndOfStream(
-            player_, DemuxerStreamTypeToSbMediaType(type));
+        pending_video_eos_buffer_ = true;
       }
-      return;
+      break;
     }
 
     DecodingBuffers::iterator iter = decoding_buffers_.find(buffer->data());
@@ -735,7 +861,7 @@
     SbPlayerSampleSideData* side_data =
         &gathered_sbplayer_sample_infos_side_data[i];
 
-    SbPlayerSampleInfo sample_info = {};
+    PlayerSampleInfo sample_info = {};
     sample_info.type = sample_type;
     sample_info.buffer = buffer->data();
     sample_info.buffer_size = buffer->data_size();
@@ -750,10 +876,14 @@
     }
 
     if (sample_type == kSbMediaTypeAudio) {
-      sample_info.audio_sample_info = audio_sample_info_;
+      DCHECK(audio_stream_info);
+      SetStreamInfo(*audio_stream_info, &sample_info.audio_sample_info);
+      SetDiscardPadding(buffer->discard_padding(),
+                        &sample_info.audio_sample_info);
     } else {
       DCHECK(sample_type == kSbMediaTypeVideo);
-      sample_info.video_sample_info = video_sample_info_;
+      DCHECK(video_stream_info);
+      SetStreamInfo(*video_stream_info, &sample_info.video_sample_info);
       sample_info.video_sample_info.is_key_frame = buffer->is_key_frame();
     }
     if (drm_info->subsample_count > 0) {
@@ -765,9 +895,13 @@
   }
 
   if (!gathered_sbplayer_sample_infos.empty()) {
-    sbplayer_interface_->WriteSample(player_, sample_type,
-                                     gathered_sbplayer_sample_infos.data(),
-                                     gathered_sbplayer_sample_infos.size());
+    cval_stats_->StartTimer(MediaTiming::SbPlayerWriteSamples,
+                            pipeline_identifier_);
+    sbplayer_interface_->WriteSamples(player_, sample_type,
+                                      gathered_sbplayer_sample_infos.data(),
+                                      gathered_sbplayer_sample_infos.size());
+    cval_stats_->StopTimer(MediaTiming::SbPlayerWriteSamples,
+                           pipeline_identifier_);
   }
 }
 
@@ -798,7 +932,11 @@
 
   DCHECK(SbPlayerIsValid(player_));
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerInfo info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbPlayerInfo2 info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   sbplayer_interface_->GetInfo(player_, &info);
 
   if (media_time) {
@@ -877,16 +1015,15 @@
   auto max_number_of_samples_to_write =
       SbPlayerGetMaximumNumberOfSamplesPerWrite(player_, type);
   if (state_ == kResuming) {
-    if (decoder_buffer_cache_.GetBuffer(stream_type)) {
+    if (decoder_buffer_cache_.HasMoreBuffers(stream_type)) {
       WriteNextBuffersFromCache(stream_type, max_number_of_samples_to_write);
       return;
     }
-    if (!decoder_buffer_cache_.GetBuffer(DemuxerStream::AUDIO) &&
-        !decoder_buffer_cache_.GetBuffer(DemuxerStream::VIDEO)) {
+    if (!decoder_buffer_cache_.HasMoreBuffers(DemuxerStream::AUDIO) &&
+        !decoder_buffer_cache_.HasMoreBuffers(DemuxerStream::VIDEO)) {
       state_ = kPlaying;
     }
   }
-
   host_->OnNeedData(stream_type, max_number_of_samples_to_write);
 }
 
@@ -1046,8 +1183,13 @@
     bool prefer_decode_to_texture) const {
   SbPlayerCreationParam creation_param = {};
   creation_param.drm_system = drm_system_;
-  creation_param.audio_sample_info = audio_sample_info_;
-  creation_param.video_sample_info = video_sample_info_;
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  creation_param.audio_stream_info = audio_stream_info_;
+  creation_param.video_stream_info = video_stream_info_;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  creation_param.audio_sample_info = audio_stream_info_;
+  creation_param.video_sample_info = video_stream_info_;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 
   // Try to choose |kSbPlayerOutputModeDecodeToTexture| when
   // |prefer_decode_to_texture| is true.
diff --git a/cobalt/media/base/sbplayer_bridge.h b/cobalt/media/base/sbplayer_bridge.h
index 1ea4cf6..3ea0b76 100644
--- a/cobalt/media/base/sbplayer_bridge.h
+++ b/cobalt/media/base/sbplayer_bridge.h
@@ -24,6 +24,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/synchronization/lock.h"
 #include "base/time/time.h"
+#include "cobalt/media/base/cval_stats.h"
 #include "cobalt/media/base/decode_target_provider.h"
 #include "cobalt/media/base/decoder_buffer_cache.h"
 #include "cobalt/media/base/sbplayer_interface.h"
@@ -74,7 +75,8 @@
                  bool allow_resume_after_suspend, bool prefer_decode_to_texture,
                  const OnEncryptedMediaInitDataEncounteredCB&
                      encrypted_media_init_data_encountered_cb,
-                 DecodeTargetProvider* const decode_target_provider);
+                 DecodeTargetProvider* const decode_target_provider,
+                 std::string pipeline_identifier);
 #endif  // SB_HAS(PLAYER_WITH_URL)
   // Create a SbPlayerBridge with normal player
   SbPlayerBridge(SbPlayerInterface* interface,
@@ -89,7 +91,8 @@
                  SbPlayerSetBoundsHelper* set_bounds_helper,
                  bool allow_resume_after_suspend, bool prefer_decode_to_texture,
                  DecodeTargetProvider* const decode_target_provider,
-                 const std::string& max_video_capabilities);
+                 const std::string& max_video_capabilities,
+                 std::string pipeline_identifier);
 
   ~SbPlayerBridge();
 
@@ -195,9 +198,13 @@
 
   void WriteNextBuffersFromCache(DemuxerStream::Type type,
                                  int max_buffers_per_write);
+
+  template <typename PlayerSampleInfo>
   void WriteBuffersInternal(
       DemuxerStream::Type type,
-      const std::vector<scoped_refptr<DecoderBuffer>>& buffers);
+      const std::vector<scoped_refptr<DecoderBuffer>>& buffers,
+      const SbMediaAudioStreamInfo* audio_stream_info,
+      const SbMediaVideoStreamInfo* video_stream_info);
 
   void GetInfo_Locked(uint32* video_frames_decoded,
                       uint32* video_frames_dropped,
@@ -253,8 +260,10 @@
   // |task_runner_|.
   AudioDecoderConfig audio_config_;
   VideoDecoderConfig video_config_;
-  SbMediaAudioSampleInfo audio_sample_info_ = {};
-  SbMediaVideoSampleInfo video_sample_info_ = {};
+  // TODO(b/268098991): Replace them with AudioStreamInfo and VideoStreamInfo
+  //                    wrapper classes.
+  SbMediaAudioStreamInfo audio_stream_info_ = {};
+  SbMediaVideoStreamInfo video_stream_info_ = {};
   DecodingBuffers decoding_buffers_;
   int ticket_ = SB_PLAYER_INITIAL_TICKET;
   float volume_ = 1.0f;
@@ -307,6 +316,9 @@
   // Used for Gathered Sample Write.
   bool pending_audio_eos_buffer_ = false;
   bool pending_video_eos_buffer_ = false;
+
+  CValStats* cval_stats_;
+  std::string pipeline_identifier_;
 };
 
 }  // namespace media
diff --git a/cobalt/media/base/sbplayer_interface.cc b/cobalt/media/base/sbplayer_interface.cc
index 7c44dae..a3777f0 100644
--- a/cobalt/media/base/sbplayer_interface.cc
+++ b/cobalt/media/base/sbplayer_interface.cc
@@ -14,9 +14,31 @@
 
 #include "cobalt/media/base/sbplayer_interface.h"
 
+#include <string>
+
+#include "base/logging.h"
+#include "starboard/system.h"
+
 namespace cobalt {
 namespace media {
 
+DefaultSbPlayerInterface::DefaultSbPlayerInterface() {
+  const CobaltExtensionEnhancedAudioApi* extension_api =
+      static_cast<const CobaltExtensionEnhancedAudioApi*>(
+          SbSystemGetExtension(kCobaltExtensionEnhancedAudioName));
+  if (!extension_api) {
+    return;
+  }
+
+  DCHECK_EQ(extension_api->name,
+            // Avoid comparing raw string pointers for equal.
+            std::string(kCobaltExtensionEnhancedAudioName));
+  DCHECK_EQ(extension_api->version, 1u);
+  DCHECK_NE(extension_api->PlayerWriteSamples, nullptr);
+
+  enhanced_audio_player_write_samples_ = extension_api->PlayerWriteSamples;
+}
+
 SbPlayer DefaultSbPlayerInterface::Create(
     SbWindow window, const SbPlayerCreationParam* creation_param,
     SbPlayerDeallocateSampleFunc sample_deallocate_func,
@@ -39,14 +61,37 @@
 
 void DefaultSbPlayerInterface::Seek(SbPlayer player, SbTime seek_to_timestamp,
                                     int ticket) {
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerSeek(player, seek_to_timestamp, ticket);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbPlayerSeek2(player, seek_to_timestamp, ticket);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 }
 
-void DefaultSbPlayerInterface::WriteSample(
+bool DefaultSbPlayerInterface::IsEnhancedAudioExtensionEnabled() const {
+  return enhanced_audio_player_write_samples_ != nullptr;
+}
+
+void DefaultSbPlayerInterface::WriteSamples(
     SbPlayer player, SbMediaType sample_type,
     const SbPlayerSampleInfo* sample_infos, int number_of_sample_infos) {
+  DCHECK(!IsEnhancedAudioExtensionEnabled());
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerWriteSamples(player, sample_type, sample_infos,
+                       number_of_sample_infos);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbPlayerWriteSample2(player, sample_type, sample_infos,
                        number_of_sample_infos);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+}
+
+void DefaultSbPlayerInterface::WriteSamples(
+    SbPlayer player, SbMediaType sample_type,
+    const CobaltExtensionEnhancedAudioPlayerSampleInfo* sample_infos,
+    int number_of_sample_infos) {
+  DCHECK(IsEnhancedAudioExtensionEnabled());
+  enhanced_audio_player_write_samples_(player, sample_type, sample_infos,
+                                       number_of_sample_infos);
 }
 
 int DefaultSbPlayerInterface::GetMaximumNumberOfSamplesPerWrite(
@@ -74,8 +119,13 @@
 }
 
 void DefaultSbPlayerInterface::GetInfo(SbPlayer player,
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+                                       SbPlayerInfo* out_player_info) {
+  SbPlayerGetInfo(player, out_player_info);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
                                        SbPlayerInfo2* out_player_info2) {
   SbPlayerGetInfo2(player, out_player_info2);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 }
 
 SbDecodeTarget DefaultSbPlayerInterface::GetCurrentFrame(SbPlayer player) {
diff --git a/cobalt/media/base/sbplayer_interface.h b/cobalt/media/base/sbplayer_interface.h
index d240e0b..893f1a2 100644
--- a/cobalt/media/base/sbplayer_interface.h
+++ b/cobalt/media/base/sbplayer_interface.h
@@ -15,6 +15,8 @@
 #ifndef COBALT_MEDIA_BASE_SBPLAYER_INTERFACE_H_
 #define COBALT_MEDIA_BASE_SBPLAYER_INTERFACE_H_
 
+#include "cobalt/media/base/cval_stats.h"
+#include "starboard/extension/enhanced_audio.h"
 #include "starboard/player.h"
 
 #if SB_HAS(PLAYER_WITH_URL)
@@ -39,9 +41,16 @@
       const SbPlayerCreationParam* creation_param) = 0;
   virtual void Destroy(SbPlayer player) = 0;
   virtual void Seek(SbPlayer player, SbTime seek_to_timestamp, int ticket) = 0;
-  virtual void WriteSample(SbPlayer player, SbMediaType sample_type,
-                           const SbPlayerSampleInfo* sample_infos,
-                           int number_of_sample_infos) = 0;
+
+  virtual bool IsEnhancedAudioExtensionEnabled() const = 0;
+  virtual void WriteSamples(SbPlayer player, SbMediaType sample_type,
+                            const SbPlayerSampleInfo* sample_infos,
+                            int number_of_sample_infos) = 0;
+  virtual void WriteSamples(
+      SbPlayer player, SbMediaType sample_type,
+      const CobaltExtensionEnhancedAudioPlayerSampleInfo* sample_infos,
+      int number_of_sample_infos) = 0;
+
   virtual int GetMaximumNumberOfSamplesPerWrite(SbPlayer player,
                                                 SbMediaType sample_type) = 0;
   virtual void WriteEndOfStream(SbPlayer player, SbMediaType stream_type) = 0;
@@ -49,7 +58,12 @@
                          int height) = 0;
   virtual bool SetPlaybackRate(SbPlayer player, double playback_rate) = 0;
   virtual void SetVolume(SbPlayer player, double volume) = 0;
+
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  virtual void GetInfo(SbPlayer player, SbPlayerInfo* out_player_info) = 0;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   virtual void GetInfo(SbPlayer player, SbPlayerInfo2* out_player_info2) = 0;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   virtual SbDecodeTarget GetCurrentFrame(SbPlayer player) = 0;
 
 #if SB_HAS(PLAYER_WITH_URL)
@@ -66,10 +80,18 @@
   virtual void GetUrlPlayerExtraInfo(
       SbPlayer player, SbUrlPlayerExtraInfo* out_url_player_info) = 0;
 #endif  // SB_HAS(PLAYER_WITH_URL)
+
+  // disabled by default, but can be enabled via h5vcc setting.
+  void EnableCValStats(bool should_enable) {
+    cval_stats_.Enable(should_enable);
+  }
+  CValStats cval_stats_;
 };
 
 class DefaultSbPlayerInterface final : public SbPlayerInterface {
  public:
+  DefaultSbPlayerInterface();
+
   SbPlayer Create(
       SbWindow window, const SbPlayerCreationParam* creation_param,
       SbPlayerDeallocateSampleFunc sample_deallocate_func,
@@ -81,9 +103,14 @@
       const SbPlayerCreationParam* creation_param) override;
   void Destroy(SbPlayer player) override;
   void Seek(SbPlayer player, SbTime seek_to_timestamp, int ticket) override;
-  void WriteSample(SbPlayer player, SbMediaType sample_type,
-                   const SbPlayerSampleInfo* sample_infos,
-                   int number_of_sample_infos) override;
+  bool IsEnhancedAudioExtensionEnabled() const override;
+  void WriteSamples(SbPlayer player, SbMediaType sample_type,
+                    const SbPlayerSampleInfo* sample_infos,
+                    int number_of_sample_infos) override;
+  void WriteSamples(
+      SbPlayer player, SbMediaType sample_type,
+      const CobaltExtensionEnhancedAudioPlayerSampleInfo* sample_infos,
+      int number_of_sample_infos) override;
   int GetMaximumNumberOfSamplesPerWrite(SbPlayer player,
                                         SbMediaType sample_type) override;
   void WriteEndOfStream(SbPlayer player, SbMediaType stream_type) override;
@@ -91,7 +118,11 @@
                  int height) override;
   bool SetPlaybackRate(SbPlayer player, double playback_rate) override;
   void SetVolume(SbPlayer player, double volume) override;
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  void GetInfo(SbPlayer player, SbPlayerInfo* out_player_info) override;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   void GetInfo(SbPlayer player, SbPlayerInfo2* out_player_info2) override;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbDecodeTarget GetCurrentFrame(SbPlayer player) override;
 
 #if SB_HAS(PLAYER_WITH_URL)
@@ -106,6 +137,12 @@
   void GetUrlPlayerExtraInfo(
       SbPlayer player, SbUrlPlayerExtraInfo* out_url_player_info) override;
 #endif  // SB_HAS(PLAYER_WITH_URL)
+
+ private:
+  void (*enhanced_audio_player_write_samples_)(
+      SbPlayer player, SbMediaType sample_type,
+      const CobaltExtensionEnhancedAudioPlayerSampleInfo* sample_infos,
+      int number_of_sample_infos) = nullptr;
 };
 
 
diff --git a/cobalt/media/base/sbplayer_pipeline.cc b/cobalt/media/base/sbplayer_pipeline.cc
index 0d69a83..78d7b6b 100644
--- a/cobalt/media/base/sbplayer_pipeline.cc
+++ b/cobalt/media/base/sbplayer_pipeline.cc
@@ -339,6 +339,8 @@
   base::CVal<SbTime> last_media_time_;
   // Time when we last checked the media time.
   SbTime last_time_media_time_retrieved_ = 0;
+  // Counter for retrograde media time.
+  size_t retrograde_media_time_counter_ = 0;
   // The maximum video playback capabilities required for the playback.
   base::CVal<std::string> max_video_capabilities_;
 
@@ -617,6 +619,7 @@
   // decide when to delay.
   audio_read_delayed_ = false;
   StoreMediaTime(seek_time_);
+  retrograde_media_time_counter_ = 0;
   timestamp_of_last_written_audio_ = 0;
 
 #if SB_HAS(PLAYER_WITH_URL)
@@ -704,10 +707,17 @@
 
   // Guarantee that we report monotonically increasing media time
   if (media_time.ToSbTime() < last_media_time_) {
-    DLOG(WARNING) << "The new media timestamp player reported ("
-                  << media_time.ToSbTime() << ") is less than the last one ("
-                  << last_media_time_ << ").";
+    if (retrograde_media_time_counter_ == 0) {
+      DLOG(WARNING) << "Received retrograde media time, new:"
+                    << media_time.ToSbTime() << ", last: " << last_media_time_
+                    << ".";
+    }
     media_time = base::TimeDelta::FromMicroseconds(last_media_time_);
+    retrograde_media_time_counter_++;
+  } else if (retrograde_media_time_counter_ != 0) {
+    DLOG(WARNING) << "Received " << retrograde_media_time_counter_
+                  << " retrograde media time before recovered.";
+    retrograde_media_time_counter_ = 0;
   }
   StoreMediaTime(media_time);
   return media_time;
@@ -927,7 +937,8 @@
         sbplayer_interface_, task_runner_, source_url, window_, this,
         set_bounds_helper_.get(), allow_resume_after_suspend_,
         *decode_to_texture_output_mode_,
-        on_encrypted_media_init_data_encountered_cb_, decode_target_provider_));
+        on_encrypted_media_init_data_encountered_cb_, decode_target_provider_,
+        pipeline_identifier_));
     if (player_bridge_->IsValid()) {
       SetPlaybackRateTask(playback_rate_);
       SetVolumeTask(volume_);
@@ -1032,7 +1043,7 @@
         audio_mime_type, video_config, video_mime_type, window_, drm_system,
         this, set_bounds_helper_.get(), allow_resume_after_suspend_,
         *decode_to_texture_output_mode_, decode_target_provider_,
-        max_video_capabilities_));
+        max_video_capabilities_, pipeline_identifier_));
     if (player_bridge_->IsValid()) {
       SetPlaybackRateTask(playback_rate_);
       SetVolumeTask(volume_);
@@ -1405,6 +1416,10 @@
 void SbPlayerPipeline::UpdateDecoderConfig(DemuxerStream* stream) {
   DCHECK(task_runner_->BelongsToCurrentThread());
 
+  if (!player_bridge_) {
+    return;
+  }
+
   if (stream->type() == DemuxerStream::AUDIO) {
     const AudioDecoderConfig& decoder_config = stream->audio_decoder_config();
     player_bridge_->UpdateAudioConfig(decoder_config, stream->mime_type());
@@ -1492,7 +1507,17 @@
 
   window_ = window;
 
-  if (player_bridge_) {
+  bool resumable = true;
+  bool resume_to_background_mode = !SbWindowIsValid(window_);
+  bool is_audioless = !HasAudio();
+  if (resume_to_background_mode && is_audioless) {
+    // Avoid resuming an audioless video to background mode. SbPlayerBridge will
+    // try to create an SbPlayer with only the video stream disabled, and may
+    // crash in this case as SbPlayerCreate() will fail without an audio or
+    // video stream.
+    resumable = false;
+  }
+  if (player_bridge_ && resumable) {
     player_bridge_->Resume(window);
     if (!player_bridge_->IsValid()) {
       std::string error_message;
@@ -1509,8 +1534,6 @@
                   "SbPlayerPipeline::ResumeTask failed to create a valid "
                   "SbPlayerBridge - " +
                       time_information + " \'" + error_message + "\'");
-      done_event->Signal();
-      return;
     }
   }
 
diff --git a/cobalt/media/decoder_buffer_allocator.h b/cobalt/media/decoder_buffer_allocator.h
index e001547..b324064 100644
--- a/cobalt/media/decoder_buffer_allocator.h
+++ b/cobalt/media/decoder_buffer_allocator.h
@@ -21,7 +21,7 @@
 #include "cobalt/media/decoder_buffer_memory_info.h"
 #include "nb/bidirectional_fit_reuse_allocator.h"
 #include "nb/starboard_memory_allocator.h"
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/mutex.h"
 #include "starboard/media.h"
 #include "third_party/chromium/media/base/decoder_buffer.h"
diff --git a/cobalt/media/media_module.cc b/cobalt/media/media_module.cc
index 6159298..95965a5 100644
--- a/cobalt/media/media_module.cc
+++ b/cobalt/media/media_module.cc
@@ -189,6 +189,11 @@
     LOG(INFO) << (allow_batched_sample_write_ ? "Enabling" : "Disabling")
               << " batched sample write.";
     return true;
+  } else if (name == "EnableMetrics") {
+    sbplayer_interface_->EnableCValStats(value);
+    LOG(INFO) << (value ? "Enabling" : "Disabling")
+              << " media metrics collection.";
+    return true;
   }
   return false;
 }
diff --git a/cobalt/media/player/web_media_player_impl.cc b/cobalt/media/player/web_media_player_impl.cc
index 1844a6a..8073366 100644
--- a/cobalt/media/player/web_media_player_impl.cc
+++ b/cobalt/media/player/web_media_player_impl.cc
@@ -17,6 +17,7 @@
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/base/instance_counter.h"
@@ -641,9 +642,14 @@
   if (ready_state_ == WebMediaPlayer::kReadyStateHaveNothing) {
     // Any error that occurs before reaching ReadyStateHaveMetadata should
     // be considered a format error.
-    SetNetworkError(WebMediaPlayer::kNetworkStateFormatError,
-                    message.empty() ? "Ready state have nothing."
-                                    : "Ready state have nothing: " + message);
+    SetNetworkError(
+        WebMediaPlayer::kNetworkStateFormatError,
+        message.empty()
+            ? base::StringPrintf("Ready state have nothing. Error: (%d)",
+                                 static_cast<int>(error))
+            : base::StringPrintf(
+                  "Ready state have nothing: Error: (%d), Message: %s",
+                  static_cast<int>(error), message.c_str()));
     return;
   }
 
diff --git a/cobalt/media_capture/media_devices.cc b/cobalt/media_capture/media_devices.cc
index c5ff88c..e754755 100644
--- a/cobalt/media_capture/media_devices.cc
+++ b/cobalt/media_capture/media_devices.cc
@@ -27,6 +27,8 @@
 #include "cobalt/speech/microphone_fake.h"
 #include "cobalt/speech/microphone_starboard.h"
 #include "cobalt/web/dom_exception.h"
+#include "cobalt/web/environment_settings.h"
+#include "cobalt/web/environment_settings_helper.h"
 #include "starboard/common/string.h"
 
 namespace cobalt {
@@ -144,8 +146,9 @@
         base::Bind(&MediaDevices::OnMicrophoneStopped, weak_this_));
   }
 
+  auto* global_wrappable = web::get_global_wrappable(settings_);
   std::unique_ptr<MediaStreamPromiseValue::Reference> promise_reference(
-      new MediaStreamPromiseValue::Reference(this, promise));
+      new MediaStreamPromiseValue::Reference(global_wrappable, promise));
   pending_microphone_promises_.push_back(std::move(promise_reference));
 
   if (!pending_microphone_track_) {
diff --git a/cobalt/network/BUILD.gn b/cobalt/network/BUILD.gn
index 4af6239..bbfa1cb 100644
--- a/cobalt/network/BUILD.gn
+++ b/cobalt/network/BUILD.gn
@@ -123,7 +123,6 @@
     "//cobalt/content/ssl/certs/4b718d9b.0",
     "//cobalt/content/ssl/certs/4bfab552.0",
     "//cobalt/content/ssl/certs/4f316efb.0",
-    "//cobalt/content/ssl/certs/5273a94c.0",
     "//cobalt/content/ssl/certs/5443e9e3.0",
     "//cobalt/content/ssl/certs/54657681.0",
     "//cobalt/content/ssl/certs/57bcb2da.0",
diff --git a/cobalt/persistent_storage/persistent_settings.cc b/cobalt/persistent_storage/persistent_settings.cc
index 9de6143..c4db368 100644
--- a/cobalt/persistent_storage/persistent_settings.cc
+++ b/cobalt/persistent_storage/persistent_settings.cc
@@ -141,7 +141,6 @@
   auto persistent_settings = pref_store_->GetValues();
   const base::Value* result = persistent_settings->FindKey(key);
   if (result && result->is_bool()) return result->GetBool();
-  LOG(INFO) << "Persistent setting does not exist: " << key;
   return default_setting;
 }
 
@@ -151,7 +150,6 @@
   auto persistent_settings = pref_store_->GetValues();
   const base::Value* result = persistent_settings->FindKey(key);
   if (result && result->is_int()) return result->GetInt();
-  LOG(INFO) << "Persistent setting does not exist: " << key;
   return default_setting;
 }
 
@@ -161,7 +159,6 @@
   auto persistent_settings = pref_store_->GetValues();
   const base::Value* result = persistent_settings->FindKey(key);
   if (result && result->is_double()) return result->GetDouble();
-  LOG(INFO) << "Persistent setting does not exist: " << key;
   return default_setting;
 }
 
@@ -171,7 +168,6 @@
   auto persistent_settings = pref_store_->GetValues();
   const base::Value* result = persistent_settings->FindKey(key);
   if (result && result->is_string()) return result->GetString();
-  LOG(INFO) << "Persistent setting does not exist: " << key;
   return default_setting;
 }
 
@@ -183,7 +179,6 @@
   if (result && result->is_list()) {
     return std::move(result->TakeList());
   }
-  LOG(INFO) << "Persistent setting does not exist: " << key;
   return std::vector<base::Value>();
 }
 
@@ -201,7 +196,6 @@
     }
     return dict;
   }
-  LOG(INFO) << "Persistent setting does not exist: " << key;
   return dict;
 }
 
diff --git a/cobalt/renderer/BUILD.gn b/cobalt/renderer/BUILD.gn
index 21a89fc..2b4ac9d 100644
--- a/cobalt/renderer/BUILD.gn
+++ b/cobalt/renderer/BUILD.gn
@@ -426,6 +426,8 @@
     "rasterizer/testdata/SimpleTextIn40PtFont-expected.png",
     "rasterizer/testdata/SimpleTextIn500PtFont-expected.png",
     "rasterizer/testdata/SimpleTextIn80PtFont-expected.png",
+    "rasterizer/testdata/SimpleTextInEthiopic-expected.png",
+    "rasterizer/testdata/SimpleTextInEthiopicBold-expected.png",
     "rasterizer/testdata/SimpleTextInRed40PtChineseFont-expected.png",
     "rasterizer/testdata/SimpleTextInRed40PtFont-expected.png",
     "rasterizer/testdata/SimpleTextInRed40PtThaiFont-expected.png",
diff --git a/cobalt/renderer/rasterizer/pixel_test.cc b/cobalt/renderer/rasterizer/pixel_test.cc
index 20fedaf..51cc164 100644
--- a/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/cobalt/renderer/rasterizer/pixel_test.cc
@@ -105,9 +105,9 @@
 using cobalt::render_tree::ImageData;
 using cobalt::render_tree::ImageDataDescriptor;
 using cobalt::render_tree::ImageNode;
+using cobalt::render_tree::LinearGradientBrush;
 using cobalt::render_tree::LottieAnimation;
 using cobalt::render_tree::LottieNode;
-using cobalt::render_tree::LinearGradientBrush;
 using cobalt::render_tree::MapToMeshFilter;
 using cobalt::render_tree::MatrixTransform3DNode;
 using cobalt::render_tree::MatrixTransformNode;
@@ -1182,6 +1182,19 @@
                                        ColorRGBA(0, 0, 0, 1.0)));
 }
 
+TEST_F(PixelTest, SimpleTextInEthiopic) {
+  TestTree(CreateTextNodeWithinSurface(
+      GetResourceProvider(), "ብመንፅ", FontStyle(), 40, ColorRGBA(0, 0, 0, 1.0),
+      std::vector<Shadow>(), "Noto Sans Ethiopic"));
+}
+
+TEST_F(PixelTest, SimpleTextInEthiopicBold) {
+  FontStyle font_style(FontStyle::kBoldWeight, FontStyle::kUprightSlant);
+  TestTree(CreateTextNodeWithinSurface(
+      GetResourceProvider(), "ብመንፅ", font_style, 40, ColorRGBA(0, 0, 0, 1.0),
+      std::vector<Shadow>(), "Noto Sans Ethiopic"));
+}
+
 TEST_F(PixelTest, SimpleTextInRed40PtFont) {
   TestTree(CreateTextNodeWithinSurface(GetResourceProvider(), "Cobalt",
                                        FontStyle(), 40,
diff --git a/cobalt/renderer/rasterizer/testdata/SimpleTextInEthiopic-expected.png b/cobalt/renderer/rasterizer/testdata/SimpleTextInEthiopic-expected.png
new file mode 100644
index 0000000..45e4a97
--- /dev/null
+++ b/cobalt/renderer/rasterizer/testdata/SimpleTextInEthiopic-expected.png
Binary files differ
diff --git a/cobalt/renderer/rasterizer/testdata/SimpleTextInEthiopicBold-expected.png b/cobalt/renderer/rasterizer/testdata/SimpleTextInEthiopicBold-expected.png
new file mode 100644
index 0000000..eb85815
--- /dev/null
+++ b/cobalt/renderer/rasterizer/testdata/SimpleTextInEthiopicBold-expected.png
Binary files differ
diff --git a/cobalt/script/script_value.h b/cobalt/script/script_value.h
index 4fcec5e..25567d7 100644
--- a/cobalt/script/script_value.h
+++ b/cobalt/script/script_value.h
@@ -71,7 +71,6 @@
         : owner_(wrappable), referenced_value_(script_value.MakeCopy()) {
       DCHECK(!referenced_value_->IsNull());
       referenced_value_->RegisterOwner(owner_);
-      referenced_value_->PreventGarbageCollection();
     }
 
     Reference(Wrappable* wrappable, const Handle<T>& local)
@@ -79,7 +78,6 @@
           referenced_value_(local.GetScriptValue()->MakeCopy()) {
       DCHECK(!referenced_value_->IsNull());
       referenced_value_->RegisterOwner(owner_);
-      referenced_value_->PreventGarbageCollection();
     }
 
     const T& value() const { return *(referenced_value_->GetValue()); }
@@ -91,10 +89,7 @@
       return *(referenced_value_.get());
     }
 
-    ~Reference() {
-      referenced_value_->AllowGarbageCollection();
-      referenced_value_->DeregisterOwner(owner_);
-    }
+    ~Reference() { referenced_value_->DeregisterOwner(owner_); }
 
    private:
     Wrappable* const owner_;
diff --git a/cobalt/script/v8c/v8c_array_buffer.cc b/cobalt/script/v8c/v8c_array_buffer.cc
index 939ea56..838784b 100644
--- a/cobalt/script/v8c/v8c_array_buffer.cc
+++ b/cobalt/script/v8c/v8c_array_buffer.cc
@@ -12,10 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <memory>
-
 #include "cobalt/script/v8c/v8c_array_buffer.h"
 
+#include <memory>
+
 #include "cobalt/base/polymorphic_downcast.h"
 #include "starboard/memory.h"
 
@@ -39,7 +39,15 @@
   if (byte_length_ == new_byte_length) {
     return;
   }
-  data_ = static_cast<uint8_t*>(SbMemoryReallocate(data_, new_byte_length));
+  // Calling reallocate with size 0 and a non-null pointer causes memory leaks
+  // on many platforms, since it may return nullptr while also not deallocating
+  // the previously allocated memory.
+  if (data_ != nullptr && new_byte_length == 0) {
+    SbMemoryDeallocate(data_);
+    data_ = nullptr;
+  } else {
+    data_ = static_cast<uint8_t*>(SbMemoryReallocate(data_, new_byte_length));
+  }
   byte_length_ = new_byte_length;
 }
 
diff --git a/cobalt/site/docs/development/setup-linux.md b/cobalt/site/docs/development/setup-linux.md
index 190cac0..78ecf3f 100644
--- a/cobalt/site/docs/development/setup-linux.md
+++ b/cobalt/site/docs/development/setup-linux.md
@@ -22,7 +22,7 @@
         pkgconf ninja-build bison yasm binutils clang libgles2-mesa-dev \
         mesa-common-dev libpulse-dev libavresample-dev libasound2-dev \
         libxrender-dev libxcomposite-dev libxml2-dev curl git \
-        python3.8-venv
+        python3.8-venv libxi-dev
     ```
 
 1.  Install Node.js via `nvm`:
@@ -72,7 +72,7 @@
     export PYTHONPATH="/fullpathto/cobalt:${PYTHONPATH}"
     ```
 
-    You should also run the above command in your termainal so it's available
+    You should also run the above command in your terminal so it's available
     immediately, rather than when you next login.
 
 ### Set up Developer Tools
@@ -104,6 +104,12 @@
     $ git checkout -b <my-branch-name> origin/master
     ```
 
+1.  Download clang++:
+
+    ```
+    $ ./starboard/tools/download_clang.sh
+    ```
+
 ## Build and Run Cobalt
 
 1.  Build the code running the following command in the top-level `cobalt`
diff --git a/cobalt/site/docs/reference/starboard/modules/media.md b/cobalt/site/docs/reference/starboard/modules/media.md
index f7ccacd..d927473 100644
--- a/cobalt/site/docs/reference/starboard/modules/media.md
+++ b/cobalt/site/docs/reference/starboard/modules/media.md
@@ -33,6 +33,7 @@
 *   `kSbMediaAudioCodecMp3`
 *   `kSbMediaAudioCodecFlac`
 *   `kSbMediaAudioCodecPcm`
+*   `kSbMediaAudioCodecIamf`
 
 ### SbMediaAudioCodingType ###
 
diff --git a/cobalt/speech/sandbox/audio_loader.cc b/cobalt/speech/sandbox/audio_loader.cc
index da35a2c..f41aea0 100644
--- a/cobalt/speech/sandbox/audio_loader.cc
+++ b/cobalt/speech/sandbox/audio_loader.cc
@@ -100,7 +100,7 @@
   loader_ = base::WrapUnique(new loader::Loader(
       base::Bind(&loader::FetcherFactory::CreateFetcher,
                  base::Unretained(fetcher_factory_.get()), url,
-                 disk_cache::kOther),
+                 /*main_resource=*/false, disk_cache::kOther),
       base::Bind(&DummyDecoder::Create, base::Bind(&AudioLoader::OnLoadingDone,
                                                    base::Unretained(this))),
       base::Bind(&AudioLoader::OnLoadingError, base::Unretained(this))));
diff --git a/cobalt/speech/url_fetcher_fake.h b/cobalt/speech/url_fetcher_fake.h
index aa2bc08..7b6d3cb 100644
--- a/cobalt/speech/url_fetcher_fake.h
+++ b/cobalt/speech/url_fetcher_fake.h
@@ -19,7 +19,9 @@
 
 #if defined(ENABLE_FAKE_MICROPHONE)
 
+#include <memory>
 #include <string>
+#include <utility>
 
 #include "base/optional.h"
 #include "base/threading/thread_checker.h"
@@ -103,6 +105,9 @@
   void SetAutomaticallyRetryOnNetworkChanges(int max_retries) override {
     NOTREACHED();
   }
+  const net::HttpRequestHeaders& GetRequestHeaders() const override {
+    return extra_request_headers_;
+  }
   net::HttpResponseHeaders* GetResponseHeaders() const override {
     NOTREACHED();
     return NULL;
@@ -158,6 +163,7 @@
   base::Optional<base::RepeatingTimer> download_timer_;
   net::ProxyServer proxy_server_;
   std::unique_ptr<net::URLFetcherResponseWriter> response_data_writer_;
+  net::HttpRequestHeaders extra_request_headers_;
   THREAD_CHECKER(thread_checker_);
 };
 
diff --git a/cobalt/test/document_loader.h b/cobalt/test/document_loader.h
index 6e6b7da..6d23802 100644
--- a/cobalt/test/document_loader.h
+++ b/cobalt/test/document_loader.h
@@ -83,7 +83,8 @@
     document_->AddObserver(this);
     document_loader_.reset(new loader::Loader(
         base::Bind(&loader::FetcherFactory::CreateFetcher,
-                   base::Unretained(&fetcher_factory_), url, disk_cache::kHTML),
+                   base::Unretained(&fetcher_factory_), url,
+                   /*main_resource=*/true, disk_cache::kHTML),
         base::Bind(&dom_parser::Parser::ParseDocumentAsync,
                    base::Unretained(dom_parser_.get()), document_,
                    base::SourceLocation(url.spec(), 1, 1)),
diff --git a/cobalt/ui_navigation/nav_item.cc b/cobalt/ui_navigation/nav_item.cc
index 8795d59..21874ac 100644
--- a/cobalt/ui_navigation/nav_item.cc
+++ b/cobalt/ui_navigation/nav_item.cc
@@ -87,7 +87,9 @@
     reinterpret_cast<intptr_t>(kNativeItemInvalid));
 
 NativeCallbacks NavItem::s_callbacks_ = {
-    &NavItem::OnBlur, &NavItem::OnFocus, &NavItem::OnScroll,
+    &NavItem::OnBlur,
+    &NavItem::OnFocus,
+    &NavItem::OnScroll,
 };
 
 NavItem::NavItem(NativeItemType type, const base::Closure& onblur_callback,
@@ -111,10 +113,9 @@
 }
 
 NavItem::~NavItem() {
+  SetEnabled(false);
+
   starboard::ScopedSpinLock lock(&g_pending_updates_lock);
-  DCHECK(state_ == kStatePendingDelete);
-  DCHECK(SbAtomicNoBarrier_LoadPtr(&s_focused_nav_item_) !=
-         reinterpret_cast<intptr_t>(nav_item_));
   g_pending_updates->emplace_back(
       nav_item_, base::Bind(GetInterface().destroy_item, nav_item_));
   if (--g_nav_item_count == 0) {
diff --git a/cobalt/ui_navigation/scroll_engine/BUILD.gn b/cobalt/ui_navigation/scroll_engine/BUILD.gn
index 485dbea..0c3cd62 100644
--- a/cobalt/ui_navigation/scroll_engine/BUILD.gn
+++ b/cobalt/ui_navigation/scroll_engine/BUILD.gn
@@ -15,6 +15,8 @@
 static_library("scroll_engine") {
   has_pedantic_warnings = true
   sources = [
+    "free_scrolling_nav_item.cc",
+    "free_scrolling_nav_item.h",
     "scroll_engine.cc",
     "scroll_engine.h",
   ]
diff --git a/cobalt/ui_navigation/scroll_engine/free_scrolling_nav_item.cc b/cobalt/ui_navigation/scroll_engine/free_scrolling_nav_item.cc
new file mode 100644
index 0000000..29eb3ae
--- /dev/null
+++ b/cobalt/ui_navigation/scroll_engine/free_scrolling_nav_item.cc
@@ -0,0 +1,70 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/ui_navigation/scroll_engine/free_scrolling_nav_item.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+
+namespace cobalt {
+namespace ui_navigation {
+namespace scroll_engine {
+
+FreeScrollingNavItem::FreeScrollingNavItem(scoped_refptr<NavItem> nav_item,
+                                           math::Vector2dF initial_offset,
+                                           math::Vector2dF target_offset,
+                                           base::TimeDelta animation_duration,
+                                           float animation_slope)
+    : nav_item_(nav_item),
+      initial_offset_(initial_offset),
+      target_offset_(target_offset),
+      animation_duration_(animation_duration) {
+  initial_change_ = base::Time::Now();
+
+  // Constants are derived from the ease-in-out curve definition here:
+  // https://www.w3.org/TR/2023/CRD-css-easing-1-20230213/#typedef-cubic-bezier-easing-function
+  animation_function_ =
+      base::WrapRefCounted(new cssom::CubicBezierTimingFunction(
+          0.42f, 0.42f * animation_slope, 0.58f, 1.f));
+}
+
+float FreeScrollingNavItem::GetFractionOfCurrentProgress() {
+  if (animation_duration_.is_zero()) {
+    return 1.f;
+  }
+
+  auto now = base::Time::Now();
+  auto time_delta = now - initial_change_;
+  auto fraction_of_progress =
+      time_delta.InMillisecondsF() / animation_duration_.InMillisecondsF();
+  return std::min<float>(static_cast<float>(fraction_of_progress), 1.f);
+}
+
+bool FreeScrollingNavItem::AnimationIsComplete() {
+  auto fraction_of_current_progress = GetFractionOfCurrentProgress();
+  return fraction_of_current_progress >= 1.f;
+}
+
+math::Vector2dF FreeScrollingNavItem::GetCurrentOffset() {
+  auto fraction_of_current_progress = GetFractionOfCurrentProgress();
+  float progress = animation_function_->Evaluate(fraction_of_current_progress);
+  auto distance_delta = target_offset_ - initial_offset_;
+  distance_delta.Scale(progress);
+  return initial_offset_ + distance_delta;
+}
+
+}  // namespace scroll_engine
+}  // namespace ui_navigation
+}  // namespace cobalt
diff --git a/cobalt/ui_navigation/scroll_engine/free_scrolling_nav_item.h b/cobalt/ui_navigation/scroll_engine/free_scrolling_nav_item.h
new file mode 100644
index 0000000..cbd8592
--- /dev/null
+++ b/cobalt/ui_navigation/scroll_engine/free_scrolling_nav_item.h
@@ -0,0 +1,53 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_UI_NAVIGATION_SCROLL_ENGINE_FREE_SCROLLING_NAV_ITEM_H_
+#define COBALT_UI_NAVIGATION_SCROLL_ENGINE_FREE_SCROLLING_NAV_ITEM_H_
+
+#include "cobalt/cssom/timing_function.h"
+#include "cobalt/math/vector2d_f.h"
+#include "cobalt/ui_navigation/nav_item.h"
+
+namespace cobalt {
+namespace ui_navigation {
+namespace scroll_engine {
+
+class FreeScrollingNavItem {
+ public:
+  FreeScrollingNavItem(scoped_refptr<NavItem> nav_item,
+                       math::Vector2dF initial_offset,
+                       math::Vector2dF target_offset,
+                       base::TimeDelta animation_duration,
+                       float animation_slope);
+
+ public:
+  scoped_refptr<NavItem> nav_item() { return nav_item_; }
+  float GetFractionOfCurrentProgress();
+  bool AnimationIsComplete();
+  math::Vector2dF GetCurrentOffset();
+
+ private:
+  scoped_refptr<NavItem> nav_item_;
+  math::Vector2dF initial_offset_;
+  math::Vector2dF target_offset_;
+  scoped_refptr<cssom::CubicBezierTimingFunction> animation_function_;
+  base::TimeDelta animation_duration_;
+  base::Time initial_change_;
+};
+
+}  // namespace scroll_engine
+}  // namespace ui_navigation
+}  // namespace cobalt
+
+#endif  // COBALT_UI_NAVIGATION_SCROLL_ENGINE_FREE_SCROLLING_NAV_ITEM_H_
diff --git a/cobalt/ui_navigation/scroll_engine/scroll_engine.cc b/cobalt/ui_navigation/scroll_engine/scroll_engine.cc
index bd5495c..4c14f73 100644
--- a/cobalt/ui_navigation/scroll_engine/scroll_engine.cc
+++ b/cobalt/ui_navigation/scroll_engine/scroll_engine.cc
@@ -16,7 +16,9 @@
 
 #include <algorithm>
 
+#include "base/logging.h"
 #include "cobalt/dom/pointer_event.h"
+#include "cobalt/math/clamp.h"
 
 namespace cobalt {
 namespace ui_navigation {
@@ -24,7 +26,7 @@
 
 namespace {
 
-const base::TimeDelta kFreeScrollDuration =
+const base::TimeDelta kMaxFreeScrollDuration =
     base::TimeDelta::FromMilliseconds(700);
 
 void BoundValuesByNavItemBounds(scoped_refptr<ui_navigation::NavItem> nav_item,
@@ -91,10 +93,97 @@
   nav_item->SetContentOffset(offset_x, offset_y);
 }
 
+float GetMaxAbsoluteDimension(math::Vector2dF vector) {
+  return std::abs(vector.x()) >= std::abs(vector.y()) ? vector.x() : vector.y();
+}
+
+math::Vector2dF GetVelocityInMilliseconds(
+    EventPositionWithTimeStamp previous_event,
+    EventPositionWithTimeStamp current_event) {
+  auto time_delta = current_event.time_stamp - previous_event.time_stamp;
+  auto distance_delta = current_event.position - previous_event.position;
+
+  // Don't recognize infinite velocity.
+  if (time_delta.is_zero()) {
+    return math::Vector2dF(0, 0);
+  }
+
+  distance_delta.Scale(static_cast<float>(1.f / time_delta.InMilliseconds()));
+  return distance_delta;
+}
+
+math::Vector2dF GetNewTarget(EventPositionWithTimeStamp previous_event,
+                             EventPositionWithTimeStamp current_event) {
+  auto velocity = GetVelocityInMilliseconds(previous_event, current_event);
+  velocity.Scale(kMaxFreeScrollDuration.InMilliseconds());
+  velocity.Scale(-1);
+  return current_event.position + velocity;
+}
+
+math::Vector2dF GetNewDelta(EventPositionWithTimeStamp previous_event,
+                            EventPositionWithTimeStamp current_event) {
+  auto new_target = GetNewTarget(previous_event, current_event);
+  return new_target - current_event.position;
+}
+
+base::TimeDelta GetAnimationDurationTimeBound(
+    EventPositionWithTimeStamp previous_event,
+    EventPositionWithTimeStamp current_event) {
+  const float time_bound_multiplier = 2.5f;
+  auto time_delta = current_event.time_stamp - previous_event.time_stamp;
+  return time_delta * time_bound_multiplier;
+}
+
+base::TimeDelta GetAnimationDurationEaseInOutBound(
+    EventPositionWithTimeStamp previous_event,
+    EventPositionWithTimeStamp current_event) {
+  auto new_delta = GetNewDelta(previous_event, current_event);
+  auto duration = std::sqrt(std::abs(GetMaxAbsoluteDimension(new_delta)));
+  return base::TimeDelta::FromMillisecondsD(duration);
+}
+
+base::TimeDelta GetAnimationDuration(EventPositionWithTimeStamp previous_event,
+                                     EventPositionWithTimeStamp current_event) {
+  // TODO(b/265864360): Duration should be calculated as it is in the comment
+  //                    below, but it seems to always be too small. Re-evaluate
+  //                    once something workable is in.
+  // auto duration_time_bound =
+  //     GetAnimationDurationTimeBound(previous_event, current_event);
+  // auto ease_in_out_bound =
+  //     GetAnimationDurationEaseInOutBound(previous_event, current_event);
+  // auto min_bound = duration_time_bound < ease_in_out_bound ?
+  // duration_time_bound
+  //                                                          :
+  //                                                          ease_in_out_bound;
+  // return min_bound < kMaxFreeScrollDuration ? min_bound
+  //                                           : kMaxFreeScrollDuration;
+  return kMaxFreeScrollDuration;
+}
+
+float GetAnimationSlope(EventPositionWithTimeStamp previous_event,
+                        EventPositionWithTimeStamp current_event) {
+  auto animation_duration = GetAnimationDuration(previous_event, current_event);
+  auto time_delta = previous_event.time_stamp - current_event.time_stamp;
+  if (time_delta.is_zero()) {
+    return 0.f;
+  }
+  auto slope = std::abs(animation_duration / time_delta);
+  float cubic_bezier_ease_in_out_x1 = 0.42f;
+  return math::Clamp(static_cast<float>(slope) * cubic_bezier_ease_in_out_x1,
+                     0.f, 1.f);
+}
+
+math::Matrix3F CalculateActiveTransform(math::Matrix3F initial_transform) {
+  auto active_transform = initial_transform.Inverse();
+  if (active_transform.IsZeros()) {
+    return math::Matrix3F::Identity();
+  }
+  return active_transform;
+}
+
 }  // namespace
 
-ScrollEngine::ScrollEngine()
-    : timing_function_(cssom::TimingFunction::GetEaseInOut()) {}
+ScrollEngine::ScrollEngine() : active_transform_(math::Matrix3F::Identity()) {}
 ScrollEngine::~ScrollEngine() { free_scroll_timer_.Stop(); }
 
 void ScrollEngine::MaybeFreeScrollActiveNavItem() {
@@ -105,34 +194,25 @@
     return;
   }
 
-  auto previous_event = previous_events_.back();
-  auto current_event = previous_events_.front();
-  math::Vector2dF distance_delta =
-      previous_event.position - current_event.position;
-  // TODO(andrewsavage): See if we need this
-  // if (distance_delta.Length() < kFreeScrollThreshold) {
-  //   return;
-  // }
+  auto current_event = previous_events_.back();
+  auto previous_event = previous_events_.front();
 
-  // Get the average velocity for the entire run
-  math::Vector2dF average_velocity = distance_delta;
-  average_velocity.Scale(1.0f / static_cast<float>(current_event.time_stamp -
-                                                   previous_event.time_stamp));
-  average_velocity.Scale(0.5);
-
-  // Get the distance
-  average_velocity.Scale(kFreeScrollDuration.ToSbTime());
+  auto new_delta = GetNewDelta(previous_event, current_event);
+  base::TimeDelta animation_duration =
+      GetAnimationDuration(previous_event, current_event);
+  float animation_slope = GetAnimationSlope(previous_event, current_event);
 
   float initial_offset_x;
   float initial_offset_y;
   active_item_->GetContentOffset(&initial_offset_x, &initial_offset_y);
   math::Vector2dF initial_offset(initial_offset_x, initial_offset_y);
 
-  math::Vector2dF target_offset = initial_offset + average_velocity;
+  math::Vector2dF target_offset = initial_offset + new_delta;
   target_offset = BoundValuesByNavItemBounds(active_item_, target_offset);
 
   nav_items_with_decaying_scroll_.push_back(
-      FreeScrollingNavItem(active_item_, initial_offset, target_offset));
+      FreeScrollingNavItem(active_item_, initial_offset, target_offset,
+                           animation_duration, animation_slope));
   if (!free_scroll_timer_.IsRunning()) {
     free_scroll_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(5),
                              this,
@@ -159,15 +239,22 @@
     return;
   }
 
+  auto transformed_point =
+      active_transform_ * math::PointF(pointer_event->x(), pointer_event->y());
   auto current_coordinates =
-      math::Vector2dF(pointer_event->x(), pointer_event->y());
+      math::Vector2dF(transformed_point.x(), transformed_point.y());
+  auto current_time = base::Time::FromJsTime(pointer_event->time_stamp());
   if (previous_events_.size() != 2) {
     // This is an error.
-    previous_events_.push(EventPositionWithTimeStamp(
-        current_coordinates, pointer_event->time_stamp()));
+    previous_events_.push(
+        EventPositionWithTimeStamp(current_coordinates, current_time));
     return;
   }
 
+  previous_events_.pop();
+  previous_events_.push(
+      EventPositionWithTimeStamp(current_coordinates, current_time));
+
   auto drag_vector = previous_events_.front().position - current_coordinates;
   if (active_scroll_type_ == ScrollType::Horizontal) {
     drag_vector.set_y(0.0f);
@@ -175,10 +262,6 @@
     drag_vector.set_x(0.0f);
   }
 
-  previous_events_.push(EventPositionWithTimeStamp(
-      current_coordinates, pointer_event->time_stamp()));
-  previous_events_.pop();
-
   active_velocity_ = drag_vector;
 
   ScrollNavItemWithVector(active_item_, drag_vector);
@@ -227,8 +310,23 @@
     scoped_refptr<ui_navigation::NavItem> scroll_container,
     ScrollType scroll_type, int32_t pointer_id,
     math::Vector2dF initial_coordinates, uint64 initial_time_stamp,
-    math::Vector2dF current_coordinates, uint64 current_time_stamp) {
+    math::Vector2dF current_coordinates, uint64 current_time_stamp,
+    const math::Matrix3F& initial_transform) {
   DCHECK(base::MessageLoop::current() == scroll_engine_.message_loop());
+  active_transform_ = CalculateActiveTransform(initial_transform);
+  auto initial_point =
+      active_transform_ *
+      math::PointF(initial_coordinates.x(), initial_coordinates.y());
+  auto current_point =
+      active_transform_ *
+      math::PointF(current_coordinates.x(), current_coordinates.y());
+  initial_coordinates.SetVector(initial_point.x(), initial_point.y());
+  current_coordinates.SetVector(current_point.x(), current_point.y());
+
+  if (active_item_) {
+    events_to_handle_.erase(pointer_id);
+    return;
+  }
 
   auto drag_vector = initial_coordinates - current_coordinates;
   if (ShouldFreeScroll(scroll_container, drag_vector)) {
@@ -243,10 +341,10 @@
     drag_vector.set_x(0.0f);
   }
 
-  previous_events_.push(
-      EventPositionWithTimeStamp(initial_coordinates, initial_time_stamp));
-  previous_events_.push(
-      EventPositionWithTimeStamp(current_coordinates, current_time_stamp));
+  previous_events_.push(EventPositionWithTimeStamp(
+      initial_coordinates, base::Time::FromJsTime(initial_time_stamp)));
+  previous_events_.push(EventPositionWithTimeStamp(
+      current_coordinates, base::Time::FromJsTime(current_time_stamp)));
 
   active_velocity_ = drag_vector;
   ScrollNavItemWithVector(active_item_, drag_vector);
@@ -254,6 +352,7 @@
   auto event_to_handle = events_to_handle_.find(pointer_id);
   if (event_to_handle != events_to_handle_.end()) {
     HandlePointerEventForActiveItem(event_to_handle->second);
+    events_to_handle_.erase(pointer_id);
   }
 }
 
@@ -265,7 +364,7 @@
     for (std::vector<FreeScrollingNavItem>::iterator it =
              nav_items_with_decaying_scroll_.begin();
          it != nav_items_with_decaying_scroll_.end();) {
-      if (it->nav_item.get() == scroll_to_cancel.get()) {
+      if (it->nav_item().get() == scroll_to_cancel.get()) {
         it = nav_items_with_decaying_scroll_.erase(it);
       } else {
         it++;
@@ -281,21 +380,14 @@
     free_scroll_timer_.Stop();
     return;
   }
+
   for (std::vector<FreeScrollingNavItem>::iterator it =
            nav_items_with_decaying_scroll_.begin();
        it != nav_items_with_decaying_scroll_.end();) {
-    auto now = base::Time::Now();
-    auto update_delta = now - it->last_change;
-    float fraction_of_time =
-        std::max<float>(update_delta / kFreeScrollDuration, 1.0);
-    float progress = timing_function_->Evaluate(fraction_of_time);
-    math::Vector2dF current_offset = it->target_offset - it->initial_offset;
-    current_offset.Scale(progress);
-    current_offset += it->initial_offset;
-    it->nav_item->SetContentOffset(current_offset.x(), current_offset.y());
-    it->last_change = now;
+    auto current_offset = it->GetCurrentOffset();
+    it->nav_item()->SetContentOffset(current_offset.x(), current_offset.y());
 
-    if (fraction_of_time == 1.0) {
+    if (it->AnimationIsComplete()) {
       it = nav_items_with_decaying_scroll_.erase(it);
     } else {
       it++;
diff --git a/cobalt/ui_navigation/scroll_engine/scroll_engine.h b/cobalt/ui_navigation/scroll_engine/scroll_engine.h
index 2399457..93e1e33 100644
--- a/cobalt/ui_navigation/scroll_engine/scroll_engine.h
+++ b/cobalt/ui_navigation/scroll_engine/scroll_engine.h
@@ -27,6 +27,7 @@
 #include "cobalt/dom/pointer_event_init.h"
 #include "cobalt/math/vector2d_f.h"
 #include "cobalt/ui_navigation/nav_item.h"
+#include "cobalt/ui_navigation/scroll_engine/free_scrolling_nav_item.h"
 
 namespace cobalt {
 namespace ui_navigation {
@@ -42,6 +43,13 @@
   Free,
 } ScrollType;
 
+struct EventPositionWithTimeStamp {
+  EventPositionWithTimeStamp(math::Vector2dF position, base::Time time_stamp)
+      : position(position), time_stamp(time_stamp) {}
+  math::Vector2dF position;
+  base::Time time_stamp;
+};
+
 class ScrollEngine {
  public:
   ScrollEngine();
@@ -53,7 +61,8 @@
                          math::Vector2dF initial_coordinates,
                          uint64 initial_time_stamp,
                          math::Vector2dF current_coordinates,
-                         uint64 current_time_stamp);
+                         uint64 current_time_stamp,
+                         const math::Matrix3F& initial_transform);
   void CancelActiveScrollsForNavItems(
       std::vector<scoped_refptr<ui_navigation::NavItem>> scrolls_to_cancel);
 
@@ -68,32 +77,11 @@
   base::Thread scroll_engine_{"ScrollEngineThread"};
   base::RepeatingTimer free_scroll_timer_;
 
-  struct EventPositionWithTimeStamp {
-    EventPositionWithTimeStamp(math::Vector2dF position, uint64 time_stamp)
-        : position(position), time_stamp(time_stamp) {}
-    math::Vector2dF position;
-    uint64 time_stamp;
-  };
-
-  struct FreeScrollingNavItem {
-    FreeScrollingNavItem(scoped_refptr<NavItem> nav_item,
-                         math::Vector2dF initial_offset,
-                         math::Vector2dF target_offset)
-        : nav_item(nav_item),
-          initial_offset(initial_offset),
-          target_offset(target_offset),
-          last_change(base::Time::Now()) {}
-    scoped_refptr<NavItem> nav_item;
-    math::Vector2dF initial_offset;
-    math::Vector2dF target_offset;
-    base::Time last_change;
-  };
-
   std::queue<EventPositionWithTimeStamp> previous_events_;
-  const scoped_refptr<cssom::TimingFunction>& timing_function_;
 
   scoped_refptr<NavItem> active_item_;
   math::Vector2dF active_velocity_;
+  math::Matrix3F active_transform_;
   ScrollType active_scroll_type_ = ScrollType::Unknown;
   std::map<uint32_t, scoped_refptr<dom::PointerEvent>> events_to_handle_;
   std::vector<FreeScrollingNavItem> nav_items_with_decaying_scroll_;
diff --git a/cobalt/updater/configurator.h b/cobalt/updater/configurator.h
index 992f176..1286b68 100644
--- a/cobalt/updater/configurator.h
+++ b/cobalt/updater/configurator.h
@@ -28,7 +28,7 @@
 #include "cobalt/network/network_module.h"
 #include "components/update_client/configurator.h"
 #include "components/update_client/persisted_data.h"
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 
 class GURL;
 class PrefService;
diff --git a/cobalt/updater/version_manifest/BUILD.gn b/cobalt/updater/version_manifest/BUILD.gn
new file mode 100644
index 0000000..fc727ec
--- /dev/null
+++ b/cobalt/updater/version_manifest/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright 2022 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+copy("copy_version_manifest") {
+  install_content = true
+  sources = [ "manifest.json" ]
+  outputs = [ "$root_out_dir/manifest.json" ]
+}
diff --git a/cobalt/updater/version_manifest/manifest.json b/cobalt/updater/version_manifest/manifest.json
new file mode 100644
index 0000000..fd8f610
--- /dev/null
+++ b/cobalt/updater/version_manifest/manifest.json
@@ -0,0 +1,6 @@
+{
+    "manifest_version": 2,
+    "name": "Cobalt",
+    "description": "Cobalt",
+    "version": "1.0.0"
+}
diff --git a/cobalt/watchdog/watchdog.h b/cobalt/watchdog/watchdog.h
index ff2e486..b689e86 100644
--- a/cobalt/watchdog/watchdog.h
+++ b/cobalt/watchdog/watchdog.h
@@ -23,7 +23,7 @@
 #include "cobalt/base/application_state.h"
 #include "cobalt/persistent_storage/persistent_settings.h"
 #include "cobalt/watchdog/singleton.h"
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/condition_variable.h"
 #include "starboard/common/mutex.h"
 #include "starboard/thread.h"
diff --git a/cobalt/web/BUILD.gn b/cobalt/web/BUILD.gn
index 122f57c..fe6c751 100644
--- a/cobalt/web/BUILD.gn
+++ b/cobalt/web/BUILD.gn
@@ -16,6 +16,7 @@
   sources = [
     "custom_event.h",
     "environment_settings.h",
+    "environment_settings_helper.h",
     "error_event.h",
     "event.cc",
     "event.h",
@@ -79,7 +80,6 @@
     "csp_violation_reporter.cc",
     "csp_violation_reporter.h",
     "environment_settings_helper.cc",
-    "environment_settings_helper.h",
     "location_base.h",
     "message_event.cc",
     "message_event.h",
@@ -126,6 +126,10 @@
     ":web_events",
     "//cobalt/browser:generated_types",
   ]
+
+  if (!is_gold) {
+    defines = [ "COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING" ]
+  }
 }
 
 static_library("window_timers") {
diff --git a/cobalt/web/agent.cc b/cobalt/web/agent.cc
index d111610..9914d4d 100644
--- a/cobalt/web/agent.cc
+++ b/cobalt/web/agent.cc
@@ -21,6 +21,7 @@
 #include "base/observer_list.h"
 #include "base/threading/thread_checker.h"
 #include "base/trace_event/trace_event.h"
+#include "cobalt/base/startup_timer.h"
 #include "cobalt/loader/fetcher_factory.h"
 #include "cobalt/loader/script_loader_factory.h"
 #include "cobalt/script/environment_settings.h"
@@ -94,18 +95,19 @@
   }
 
   const std::string& name() const final { return name_; };
-  void setup_environment_settings(
+  void SetupEnvironmentSettings(
       EnvironmentSettings* environment_settings) final {
     for (auto& observer : environment_settings_change_observers_) {
       observer.OnEnvironmentSettingsChanged(!!environment_settings);
     }
     environment_settings_.reset(environment_settings);
-    if (environment_settings_) environment_settings_->set_context(this);
-    if (service_worker_jobs_) {
-      service_worker_jobs_->SetActiveWorker(environment_settings);
+    if (environment_settings_) {
+      environment_settings_->set_context(this);
     }
   }
 
+  void SetupFinished();
+
   EnvironmentSettings* environment_settings() const final {
     DCHECK(environment_settings_);
     DCHECK_EQ(environment_settings_->context(), this);
@@ -226,6 +228,24 @@
       environment_settings_change_observers_;
 };
 
+void LogScriptError(const base::SourceLocation& source_location,
+                    const std::string& error_message) {
+  std::string file_name =
+      base::FilePath(source_location.file_path).BaseName().value();
+
+  std::stringstream ss;
+  base::TimeDelta dt = base::StartupTimer::TimeElapsed();
+
+  // Create the error output.
+  // Example:
+  //   JS:50250:file.js(29,80): ka(...) is not iterable
+  //   JS:<time millis><js-file-name>(<line>,<column>):<message>
+  ss << "JS:" << dt.InMilliseconds() << ":" << file_name << "("
+     << source_location.line_number << "," << source_location.column_number
+     << "): " << error_message << "\n";
+  SbLogRaw(ss.str().c_str());
+}
+
 Impl::Impl(const std::string& name, const Agent::Options& options)
     : name_(name), web_settings_(options.web_settings) {
   TRACE_EVENT0("cobalt::web", "Agent::Impl::Impl()");
@@ -247,6 +267,12 @@
       script::JavaScriptEngine::CreateEngine(options.javascript_engine_options);
   DCHECK(javascript_engine_);
 
+#if defined(COBALT_ENABLE_JAVASCRIPT_ERROR_LOGGING)
+  script::JavaScriptEngine::ErrorHandler error_handler =
+      base::Bind(&LogScriptError);
+  javascript_engine_->RegisterErrorHandler(error_handler);
+#endif
+
   global_environment_ = javascript_engine_->CreateGlobalEnvironment();
   DCHECK(global_environment_);
 
@@ -285,7 +311,7 @@
         script::GlobalEnvironment::ReportErrorCallback());
   }
 
-  setup_environment_settings(nullptr);
+  SetupEnvironmentSettings(nullptr);
   environment_settings_change_observers_.Clear();
   blob_registry_.reset();
   script_runner_.reset();
@@ -314,6 +340,26 @@
   environment_settings_change_observers_.RemoveObserver(observer);
 }
 
+void Impl::SetupFinished() {
+  auto* global_scope = GetWindowOrWorkerGlobalScope();
+#if !defined(COBALT_FORCE_CSP)
+  if (global_scope && global_scope->options().csp_options.enforcement_type ==
+                          web::kCspEnforcementDisable) {
+    // If CSP is disabled, enable eval(). Otherwise, it will be enabled by
+    // a CSP directive.
+    global_environment_->EnableEval();
+  }
+#endif
+
+  if (service_worker_jobs_) {
+    service_worker_jobs_->RegisterWebContext(this);
+  }
+  if (service_worker_jobs_) {
+    service_worker_jobs_->SetActiveWorker(environment_settings_.get());
+  }
+}
+
+
 void Impl::InjectGlobalObjectAttributes(
     const Agent::Options::InjectedGlobalObjectAttributes& attributes) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -553,9 +599,6 @@
                               base::MessageLoop* message_loop) {
   auto* context = new Impl(name, options);
   context->set_message_loop(message_loop);
-  if (options.service_worker_jobs) {
-    options.service_worker_jobs->RegisterWebContext(context);
-  }
   return context;
 }
 
diff --git a/cobalt/web/cache.cc b/cobalt/web/cache.cc
index 9aa3155..a47a3c6 100644
--- a/cobalt/web/cache.cc
+++ b/cobalt/web/cache.cc
@@ -153,44 +153,45 @@
 script::HandlePromiseAny Cache::Match(
     script::EnvironmentSettings* environment_settings,
     const script::ValueHandleHolder& request) {
-  script::HandlePromiseAny promise =
-      get_script_value_factory(environment_settings)
-          ->CreateBasicPromise<script::Any>();
-  auto promise_reference =
-      std::make_unique<script::ValuePromiseAny::Reference>(this, promise);
+  auto* isolate = get_isolate(environment_settings);
+  script::v8c::EntryScope entry_scope(isolate);
+  auto resolver =
+      v8::Promise::Resolver::New(isolate->GetCurrentContext()).ToLocalChecked();
+  std::vector<v8::TracedGlobal<v8::Value>*> traced_globals;
+  base::OnceClosure cleanup_traced;
+  cache_utils::Trace(isolate, {resolver}, traced_globals, cleanup_traced);
+  auto traced_resolver = traced_globals[0]->As<v8::Promise::Resolver>();
   auto context = get_context(environment_settings);
   context->message_loop()->task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(
           [](script::EnvironmentSettings* environment_settings, uint32_t key,
-             std::unique_ptr<script::ValuePromiseAny::Reference>
-                 promise_reference) {
-            auto global_environment =
-                get_global_environment(environment_settings);
-            auto* isolate = global_environment->isolate();
+             v8::TracedGlobal<v8::Promise::Resolver> traced_resolver,
+             base::OnceClosure cleanup_traced) {
+            base::ScopedClosureRunner finally(std::move(cleanup_traced));
+            auto* isolate = get_isolate(environment_settings);
             auto cached =
                 cache::Cache::GetInstance()->Retrieve(kResourceType, key);
             auto metadata =
                 cache::Cache::GetInstance()->Metadata(kResourceType, key);
+            script::v8c::EntryScope entry_scope(isolate);
+            auto resolver = traced_resolver.Get(isolate);
             if (!cached || !metadata || !metadata->FindKey("options")) {
-              promise_reference->value().Resolve(
-                  cache_utils::FromV8Value(isolate, v8::Undefined(isolate)));
+              cache_utils::Resolve(resolver);
               return;
             }
-            script::v8c::EntryScope entry_scope(isolate);
             auto response = cache_utils::CreateResponse(
                 isolate, *cached, *(metadata->FindKey("options")));
             if (!response) {
-              promise_reference->value().Reject();
-            } else {
-              promise_reference->value().Resolve(
-                  cache_utils::FromV8Value(isolate, response.value()));
+              cache_utils::Reject(resolver);
+              return;
             }
+            cache_utils::Resolve(resolver, response.value());
           },
           environment_settings,
           cache_utils::GetKey(environment_settings->base_url(), request),
-          std::move(promise_reference)));
-  return promise;
+          traced_resolver, std::move(cleanup_traced)));
+  return cache_utils::FromResolver(resolver);
 }
 
 void Cache::PerformAdd(
@@ -224,13 +225,16 @@
 script::HandlePromiseVoid Cache::Add(
     script::EnvironmentSettings* environment_settings,
     const script::ValueHandleHolder& request) {
+  auto* global_wrappable = get_global_wrappable(environment_settings);
   auto request_reference =
-      std::make_unique<script::ValueHandleHolder::Reference>(this, request);
+      std::make_unique<script::ValueHandleHolder::Reference>(global_wrappable,
+                                                             request);
   script::HandlePromiseVoid promise =
       get_script_value_factory(environment_settings)
           ->CreateBasicPromise<void>();
   auto promise_reference =
-      std::make_unique<script::ValuePromiseVoid::Reference>(this, promise);
+      std::make_unique<script::ValuePromiseVoid::Reference>(global_wrappable,
+                                                            promise);
   auto context = get_context(environment_settings);
   context->message_loop()->task_runner()->PostTask(
       FROM_HERE,
@@ -244,15 +248,19 @@
     script::EnvironmentSettings* environment_settings,
     const script::ValueHandleHolder& request,
     const script::ValueHandleHolder& response) {
+  auto* global_wrappable = get_global_wrappable(environment_settings);
   auto request_reference =
-      std::make_unique<script::ValueHandleHolder::Reference>(this, request);
+      std::make_unique<script::ValueHandleHolder::Reference>(global_wrappable,
+                                                             request);
   auto response_reference =
-      std::make_unique<script::ValueHandleHolder::Reference>(this, response);
+      std::make_unique<script::ValueHandleHolder::Reference>(global_wrappable,
+                                                             response);
   script::HandlePromiseVoid promise =
       get_script_value_factory(environment_settings)
           ->CreateBasicPromise<void>();
   auto promise_reference =
-      std::make_unique<script::ValuePromiseVoid::Reference>(this, promise);
+      std::make_unique<script::ValuePromiseVoid::Reference>(global_wrappable,
+                                                            promise);
 
   auto* global_environment = get_global_environment(environment_settings);
   auto* isolate = global_environment->isolate();
@@ -312,13 +320,16 @@
 script::HandlePromiseBool Cache::Delete(
     script::EnvironmentSettings* environment_settings,
     const script::ValueHandleHolder& request) {
+  auto* global_wrappable = get_global_wrappable(environment_settings);
   script::HandlePromiseBool promise =
       get_script_value_factory(environment_settings)
           ->CreateBasicPromise<bool>();
   auto request_reference =
-      std::make_unique<script::ValueHandleHolder::Reference>(this, request);
+      std::make_unique<script::ValueHandleHolder::Reference>(global_wrappable,
+                                                             request);
   auto promise_reference =
-      std::make_unique<script::ValuePromiseBool::Reference>(this, promise);
+      std::make_unique<script::ValuePromiseBool::Reference>(global_wrappable,
+                                                            promise);
   auto context = get_context(environment_settings);
   context->message_loop()->task_runner()->PostTask(
       FROM_HERE,
@@ -345,11 +356,12 @@
 
 script::HandlePromiseAny Cache::Keys(
     script::EnvironmentSettings* environment_settings) {
+  auto* global_wrappable = get_global_wrappable(environment_settings);
   script::HandlePromiseAny promise =
       get_script_value_factory(environment_settings)
           ->CreateBasicPromise<script::Any>();
-  auto promise_reference =
-      std::make_unique<script::ValuePromiseAny::Reference>(this, promise);
+  auto promise_reference = std::make_unique<script::ValuePromiseAny::Reference>(
+      global_wrappable, promise);
   auto context = get_context(environment_settings);
   context->message_loop()->task_runner()->PostTask(
       FROM_HERE,
@@ -400,7 +412,9 @@
 void Cache::OnFetchCompletedMainThread(uint32_t key, bool success) {
   auto* fetcher = fetchers_[key].get();
   auto* promises = &(fetch_contexts_[key].first);
-  if (!success) {
+  int status = fetcher->response_code();
+  // |status| of 200-299 excluding 206 "Partial Content" should be cached.
+  if (!success || status == 206 || status < 200 || status > 299) {
     {
       base::AutoLock auto_lock(*fetcher->lock());
       while (promises->size() > 0) {
@@ -416,21 +430,24 @@
     base::DictionaryValue metadata;
     metadata.SetKey("url", base::Value(fetcher->url().spec()));
     base::DictionaryValue options;
-    options.SetKey("status", base::Value(fetcher->response_code()));
+    options.SetKey("status", base::Value(status));
     options.SetKey("statusText", base::Value(fetcher->status_text()));
     options.SetKey("headers", std::move(fetcher->headers()));
     metadata.SetKey("options", std::move(options));
 
     cache::Cache::GetInstance()->Store(
         kResourceType, key, fetcher->BufferToVector(), std::move(metadata));
-    if (fetcher->mime_type() == "text/javascript") {
-      auto* environment_settings = fetch_contexts_[key].second;
-      auto* global_environment = get_global_environment(environment_settings);
-      auto* isolate = global_environment->isolate();
-      script::v8c::EntryScope entry_scope(isolate);
-      global_environment->Compile(script::SourceCode::CreateSourceCode(
-          fetcher->BufferToString(), base::SourceLocation(__FILE__, 1, 1)));
-    }
+  }
+  if (fetcher->mime_type() == "text/javascript") {
+    auto* environment_settings = fetch_contexts_[key].second;
+    auto* global_environment = get_global_environment(environment_settings);
+    auto* isolate = global_environment->isolate();
+    script::v8c::EntryScope entry_scope(isolate);
+    // TODO: compile async or maybe don't cache if compile fails.
+    global_environment->Compile(script::SourceCode::CreateSourceCode(
+        fetcher->BufferToString(), base::SourceLocation(__FILE__, 1, 1)));
+  }
+  {
     base::AutoLock auto_lock(*fetcher->lock());
     while (promises->size() > 0) {
       promises->back()->value().Resolve();
diff --git a/cobalt/web/cache_storage.cc b/cobalt/web/cache_storage.cc
index 222b617..d110a02 100644
--- a/cobalt/web/cache_storage.cc
+++ b/cobalt/web/cache_storage.cc
@@ -51,6 +51,7 @@
 script::HandlePromiseWrappable CacheStorage::Open(
     script::EnvironmentSettings* environment_settings,
     const std::string& cache_name) {
+  auto* global_wrappable = get_global_wrappable(environment_settings);
   script::HandlePromiseWrappable promise =
       get_script_value_factory(environment_settings)
           ->CreateInterfacePromise<scoped_refptr<Cache>>();
@@ -59,18 +60,20 @@
       FROM_HERE,
       base::BindOnce(&CacheStorage::PerformOpen, base::Unretained(this),
                      std::make_unique<script::ValuePromiseWrappable::Reference>(
-                         this, promise)));
+                         global_wrappable, promise)));
   return promise;
 }
 
 script::HandlePromiseBool CacheStorage::Delete(
     script::EnvironmentSettings* environment_settings,
     const std::string& cache_name) {
+  auto* global_wrappable = get_global_wrappable(environment_settings);
   script::HandlePromiseBool promise =
       get_script_value_factory(environment_settings)
           ->CreateBasicPromise<bool>();
   auto promise_reference =
-      std::make_unique<script::ValuePromiseBool::Reference>(this, promise);
+      std::make_unique<script::ValuePromiseBool::Reference>(global_wrappable,
+                                                            promise);
   auto* context = get_context(environment_settings);
   context->message_loop()->task_runner()->PostTask(
       FROM_HERE, base::BindOnce(
diff --git a/cobalt/web/cache_utils.cc b/cobalt/web/cache_utils.cc
index 06bd976..d3ce8cc 100644
--- a/cobalt/web/cache_utils.cc
+++ b/cobalt/web/cache_utils.cc
@@ -19,8 +19,10 @@
 
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "cobalt/script/v8c/conversion_helpers.h"
+#include "cobalt/script/v8c/native_promise.h"
 #include "cobalt/web/environment_settings_helper.h"
 #include "starboard/common/murmurhash2.h"
 
@@ -324,6 +326,59 @@
   return resulting_promise.ToLocalChecked();
 }
 
+void Resolve(v8::Local<v8::Promise::Resolver> resolver,
+             v8::Local<v8::Value> value) {
+  auto* isolate = resolver->GetIsolate();
+  script::v8c::EntryScope entry_scope(isolate);
+  auto context = isolate->GetCurrentContext();
+  if (value.IsEmpty()) {
+    value = v8::Undefined(isolate);
+  }
+  auto result = resolver->Resolve(context, value);
+  DCHECK(result.FromJust());
+}
+
+void Reject(v8::Local<v8::Promise::Resolver> resolver) {
+  auto* isolate = resolver->GetIsolate();
+  script::v8c::EntryScope entry_scope(isolate);
+  auto context = isolate->GetCurrentContext();
+  auto result = resolver->Reject(context, v8::Undefined(isolate));
+  DCHECK(result.FromJust());
+}
+
+script::HandlePromiseAny FromResolver(
+    v8::Local<v8::Promise::Resolver> resolver) {
+  auto* isolate = resolver->GetIsolate();
+  return script::HandlePromiseAny(
+      new script::v8c::V8cUserObjectHolder<
+          script::v8c::NativePromise<script::Any>>(isolate, resolver));
+}
+
+void Trace(v8::Isolate* isolate,
+           std::initializer_list<v8::Local<v8::Value>> values,
+           std::vector<v8::TracedGlobal<v8::Value>*>& traced_globals_out,
+           base::OnceClosure& cleanup_traced) {
+  auto heap_tracer =
+      script::v8c::V8cEngine::GetFromIsolate(isolate)->heap_tracer();
+  std::vector<std::unique_ptr<v8::TracedGlobal<v8::Value>>> traced_globals;
+  for (auto value : values) {
+    auto traced_global =
+        std::make_unique<v8::TracedGlobal<v8::Value>>(isolate, value);
+    heap_tracer->AddRoot(traced_global.get());
+    traced_globals_out.push_back(traced_global.get());
+    traced_globals.push_back(std::move(traced_global));
+  }
+  cleanup_traced = base::BindOnce(
+      [](script::v8c::V8cHeapTracer* heap_tracer,
+         std::vector<std::unique_ptr<v8::TracedGlobal<v8::Value>>>
+             traced_globals) {
+        for (int i = 0; i < traced_globals.size(); i++) {
+          heap_tracer->RemoveRoot(traced_globals[i].get());
+        }
+      },
+      heap_tracer, std::move(traced_globals));
+}
+
 script::Any FromV8Value(v8::Isolate* isolate, v8::Local<v8::Value> value) {
   return script::Any(new script::v8c::V8cValueHandleHolder(isolate, value));
 }
@@ -356,8 +411,27 @@
 }
 
 base::Optional<v8::Local<v8::Value>> CreateRequest(v8::Isolate* isolate,
-                                                   const std::string& url) {
-  return CreateInstance(isolate, "Request", {V8String(isolate, url)});
+                                                   const std::string& url,
+                                                   const base::Value& options) {
+  auto v8_options = v8::Object::New(isolate);
+  auto mode = options.FindKey("mode");
+  if (mode) {
+    Set(v8_options, "mode", V8String(isolate, mode->GetString()));
+  }
+  auto headers = options.FindKey("headers");
+  if (headers) {
+    auto v8_headers = v8::Object::New(isolate);
+    for (const auto& header : headers->GetList()) {
+      const auto& pair = header.GetList();
+      DCHECK(pair.size() == 2);
+      auto name = pair[0].GetString();
+      auto value = pair[1].GetString();
+      Set(v8_headers, name, V8String(isolate, value));
+    }
+    Set(v8_options, "headers", v8_headers);
+  }
+  return CreateInstance(isolate, "Request",
+                        {V8String(isolate, url), v8_options});
 }
 
 base::Optional<v8::Local<v8::Value>> CreateResponse(
diff --git a/cobalt/web/cache_utils.h b/cobalt/web/cache_utils.h
index 863a079..25958f8 100644
--- a/cobalt/web/cache_utils.h
+++ b/cobalt/web/cache_utils.h
@@ -93,6 +93,24 @@
     std::initializer_list<v8::Local<v8::Value>> args = {});
 base::Optional<v8::Local<v8::Value>> Then(v8::Local<v8::Value> value,
                                           OnFullfilled on_fullfilled);
+void Resolve(v8::Local<v8::Promise::Resolver> resolver,
+             v8::Local<v8::Value> value = v8::Local<v8::Value>());
+void Reject(v8::Local<v8::Promise::Resolver> resolver);
+script::HandlePromiseAny FromResolver(
+    v8::Local<v8::Promise::Resolver> resolver);
+
+// Keeps |values| from being garbage collected until |cleanup_traced| is run.
+// The caller needs |traced_globals_out| to get a valid reference of the
+// |v8::Value|. To get a reference, the caller needs to create a
+// |v8::HandleScope| and then call |v8::TracedGlobal::Get| to get a valid
+// |v8::Value|.
+// |values| will have a 1:1 relationship with |traced_globals_out|.
+// It is up to the caller to ensure that |cleanup_traced| is run in all
+// possible code paths.
+void Trace(v8::Isolate* isolate,
+           std::initializer_list<v8::Local<v8::Value>> values,
+           std::vector<v8::TracedGlobal<v8::Value>*>& traced_globals_out,
+           base::OnceClosure& cleanup_traced);
 
 std::string Stringify(v8::Isolate* isolate, v8::Local<v8::Value> value);
 base::Optional<v8::Local<v8::Value>> BaseToV8(v8::Isolate* isolate,
@@ -112,8 +130,9 @@
 base::Optional<v8::Local<v8::Value>> Evaluate(v8::Isolate* isolate,
                                               const std::string& js_code);
 
-base::Optional<v8::Local<v8::Value>> CreateRequest(v8::Isolate* isolate,
-                                                   const std::string& url);
+base::Optional<v8::Local<v8::Value>> CreateRequest(
+    v8::Isolate* isolate, const std::string& url,
+    const base::Value& options = base::DictionaryValue());
 base::Optional<v8::Local<v8::Value>> CreateResponse(
     v8::Isolate* isolate, const std::vector<uint8_t>& body,
     const base::Value& options);
diff --git a/cobalt/web/context.h b/cobalt/web/context.h
index 66825db..3fdf42c 100644
--- a/cobalt/web/context.h
+++ b/cobalt/web/context.h
@@ -68,7 +68,9 @@
   virtual worker::ServiceWorkerJobs* service_worker_jobs() const = 0;
 
   virtual const std::string& name() const = 0;
-  virtual void setup_environment_settings(EnvironmentSettings* settings) = 0;
+  virtual void SetupEnvironmentSettings(EnvironmentSettings* settings) = 0;
+  virtual void SetupFinished() = 0;
+
   virtual EnvironmentSettings* environment_settings() const = 0;
 
   virtual scoped_refptr<worker::ServiceWorkerRegistration>
diff --git a/cobalt/web/csp_delegate.cc b/cobalt/web/csp_delegate.cc
index 2a54b9c..3fe54b5 100644
--- a/cobalt/web/csp_delegate.cc
+++ b/cobalt/web/csp_delegate.cc
@@ -28,9 +28,9 @@
 
 CspDelegateSecure::CspDelegateSecure(
     std::unique_ptr<CspViolationReporter> violation_reporter, const GURL& url,
-    csp::CSPHeaderPolicy require_csp,
+    csp::CSPHeaderPolicy csp_header_policy,
     const base::Closure& policy_changed_callback) {
-  require_csp_ = require_csp;
+  csp_header_policy_ = csp_header_policy;
   was_header_received_ = false;
   policy_changed_callback_ = policy_changed_callback;
 
@@ -77,7 +77,7 @@
     if (type == kLocation) {
       should_allow = csp_->AllowNavigateToSource(url, redirect_status);
     }
-    if (require_csp_ == csp::kCSPRequired || should_allow) {
+    if (csp_header_policy_ == csp::kCSPRequired || should_allow) {
       return should_allow;
     } else {
       DLOG(WARNING) << "Page must include Content-Security-Policy header, it "
diff --git a/cobalt/web/csp_delegate.h b/cobalt/web/csp_delegate.h
index 4fa1cfb..95ce400 100644
--- a/cobalt/web/csp_delegate.h
+++ b/cobalt/web/csp_delegate.h
@@ -20,6 +20,7 @@
 
 #include "cobalt/base/source_location.h"
 #include "cobalt/csp/content_security_policy.h"
+#include "cobalt/web/csp_delegate_type.h"
 #include "cobalt/web/csp_violation_reporter.h"
 
 namespace cobalt {
@@ -31,6 +32,22 @@
 // Note that any thread may call CanLoad().
 class CspDelegate {
  public:
+  struct Options {
+    Options() = default;
+    Options(const network_bridge::PostSender& post_sender,
+            csp::CSPHeaderPolicy header_policy,
+            CspEnforcementType enforcement_type, int insecure_allowed_token = 0)
+        : post_sender(post_sender),
+          header_policy(header_policy),
+          enforcement_type(enforcement_type),
+          insecure_allowed_token(insecure_allowed_token) {}
+
+    network_bridge::PostSender post_sender;
+    csp::CSPHeaderPolicy header_policy = csp::kCSPRequired;
+    CspEnforcementType enforcement_type = kCspEnforcementEnable;
+    int insecure_allowed_token = 0;
+  };
+
   enum ResourceType {
     kFont,
     kImage,
@@ -117,7 +134,7 @@
 class CspDelegateSecure : public CspDelegate {
  public:
   CspDelegateSecure(std::unique_ptr<CspViolationReporter> violation_reporter,
-                    const GURL& url, csp::CSPHeaderPolicy require_csp,
+                    const GURL& url, csp::CSPHeaderPolicy csp_header_policy,
                     const base::Closure& policy_changed_callback);
   ~CspDelegateSecure();
 
@@ -172,7 +189,7 @@
   base::Closure policy_changed_callback_;
 
   // Whether Cobalt is forbidden to render without receiving CSP header.
-  csp::CSPHeaderPolicy require_csp_;
+  csp::CSPHeaderPolicy csp_header_policy_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(CspDelegateSecure);
diff --git a/cobalt/web/csp_delegate_factory.cc b/cobalt/web/csp_delegate_factory.cc
index 89d546c..5eb8065 100644
--- a/cobalt/web/csp_delegate_factory.cc
+++ b/cobalt/web/csp_delegate_factory.cc
@@ -12,16 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "cobalt/web/csp_delegate_factory.h"
+
 #include <memory>
 #include <utility>
 
-#include "cobalt/web/csp_delegate_factory.h"
-
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/threading/thread_local.h"
-
 #include "cobalt/web/csp_delegate.h"
+#include "cobalt/web/csp_violation_reporter.h"
+#include "cobalt/web/window_or_worker_global_scope.h"
 
 namespace cobalt {
 namespace web {
@@ -48,7 +49,7 @@
 
 CspDelegate* CreateInsecureDelegate(
     std::unique_ptr<CspViolationReporter> violation_reporter, const GURL& url,
-    csp::CSPHeaderPolicy require_csp,
+    csp::CSPHeaderPolicy header_policy,
     const base::Closure& policy_changed_callback, int insecure_allowed_token) {
   if (InsecureAllowed(insecure_allowed_token)) {
     return new CspDelegateInsecure();
@@ -60,10 +61,10 @@
 
 CspDelegate* CreateSecureDelegate(
     std::unique_ptr<CspViolationReporter> violation_reporter, const GURL& url,
-    csp::CSPHeaderPolicy require_csp,
+    csp::CSPHeaderPolicy header_policy,
     const base::Closure& policy_changed_callback, int insecure_allowed_token) {
-  return new CspDelegateSecure(std::move(violation_reporter), url, require_csp,
-                               policy_changed_callback);
+  return new CspDelegateSecure(std::move(violation_reporter), url,
+                               header_policy, policy_changed_callback);
 }
 }  // namespace
 
@@ -84,14 +85,16 @@
 #endif  // !defined(COBALT_FORCE_CSP)
 }
 
-std::unique_ptr<CspDelegate> CspDelegateFactory::Create(
-    CspEnforcementType type,
-    std::unique_ptr<CspViolationReporter> violation_reporter, const GURL& url,
-    csp::CSPHeaderPolicy require_csp,
-    const base::Closure& policy_changed_callback, int insecure_allowed_token) {
-  std::unique_ptr<CspDelegate> delegate(
-      method_[type](std::move(violation_reporter), url, require_csp,
-                    policy_changed_callback, insecure_allowed_token));
+std::unique_ptr<CspDelegate> CspDelegateFactory::CreateDelegate(
+    WindowOrWorkerGlobalScope* global, const CspDelegate::Options& options,
+    const base::Closure& policy_changed_callback) {
+  std::unique_ptr<CspViolationReporter> violation_reporter(
+      new CspViolationReporter(global, options.post_sender));
+  std::unique_ptr<CspDelegate> delegate(method_[options.enforcement_type](
+      std::move(violation_reporter),
+      (global ? global->environment_settings()->creation_url() : GURL()),
+      options.header_policy, policy_changed_callback,
+      options.insecure_allowed_token));
   return delegate;
 }
 
diff --git a/cobalt/web/csp_delegate_factory.h b/cobalt/web/csp_delegate_factory.h
index aa10ac9..f9717e5 100644
--- a/cobalt/web/csp_delegate_factory.h
+++ b/cobalt/web/csp_delegate_factory.h
@@ -18,10 +18,13 @@
 #include <memory>
 #include <string>
 
+#include "base/bind.h"
 #include "base/callback_forward.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/singleton.h"
 #include "cobalt/csp/content_security_policy.h"
+#include "cobalt/network_bridge/net_poster.h"
+#include "cobalt/web/csp_delegate.h"
 #include "cobalt/web/csp_delegate_type.h"
 #include "url/gurl.h"
 
@@ -41,22 +44,27 @@
 class CspDelegateFactory {
  public:
   static CspDelegateFactory* GetInstance();
-  std::unique_ptr<CspDelegate> Create(
-      CspEnforcementType type,
-      std::unique_ptr<CspViolationReporter> violation_reporter, const GURL& url,
-      csp::CSPHeaderPolicy require_csp,
-      const base::Closure& policy_changed_callback,
-      int insecure_allowed_token = 0);
+  static std::unique_ptr<CspDelegate> Create(
+      WindowOrWorkerGlobalScope* global, const CspDelegate::Options& options,
+      const base::Closure& policy_changed_callback = base::Closure()) {
+    return GetInstance()->CreateDelegate(global, options,
+                                         policy_changed_callback);
+  }
+
 
   typedef CspDelegate* (*CspDelegateCreator)(
       std::unique_ptr<CspViolationReporter> violation_reporter, const GURL& url,
-      csp::CSPHeaderPolicy require_csp,
+      csp::CSPHeaderPolicy csp_header_policy,
       const base::Closure& policy_changed_callback, int insecure_allowed_token);
 
 #if !defined(COBALT_FORCE_CSP)
   // Allow tests to have the factory create a different delegate type.
   void OverrideCreator(CspEnforcementType type, CspDelegateCreator creator);
 #endif
+ protected:
+  std::unique_ptr<CspDelegate> CreateDelegate(
+      WindowOrWorkerGlobalScope* global, const CspDelegate::Options& options,
+      const base::Closure& policy_changed_callback);
 
  private:
 #if !defined(COBALT_FORCE_CSP)
diff --git a/cobalt/web/csp_delegate_test.cc b/cobalt/web/csp_delegate_test.cc
index 96e5ccc..f4e920f 100644
--- a/cobalt/web/csp_delegate_test.cc
+++ b/cobalt/web/csp_delegate_test.cc
@@ -12,12 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "cobalt/web/csp_delegate.h"
+
 #include <memory>
 #include <utility>
 
 #include "base/strings/stringprintf.h"
 #include "cobalt/base/polymorphic_downcast.h"
-#include "cobalt/web/csp_delegate.h"
 #include "cobalt/web/csp_delegate_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -81,7 +82,7 @@
 class MockViolationReporter : public CspViolationReporter {
  public:
   MockViolationReporter()
-      : CspViolationReporter(NULL, network_bridge::PostSender()) {}
+      : CspViolationReporter(nullptr, network_bridge::PostSender()) {}
   MOCK_METHOD1(Report, void(const csp::ViolationInfo&));
 };
 
@@ -105,7 +106,7 @@
 
   ~ScopedLogInterceptor() {
     logging::SetLogMessageHandler(old_handler_);
-    log_interceptor_ = NULL;
+    log_interceptor_ = nullptr;
   }
 
   static bool LogHandler(int severity, const char* file, int line,
@@ -166,11 +167,11 @@
                         ResourcePairName);
 
 TEST(CspDelegateFactoryTest, Secure) {
+  CspDelegate::Options options;
+  options.enforcement_type = kCspEnforcementEnable;
   std::unique_ptr<CspDelegate> delegate =
-      CspDelegateFactory::GetInstance()->Create(
-          kCspEnforcementEnable, std::unique_ptr<CspViolationReporter>(),
-          GURL(), csp::kCSPRequired, base::Closure());
-  EXPECT_TRUE(delegate != NULL);
+      CspDelegateFactory::Create(nullptr, options);
+  EXPECT_TRUE(delegate != nullptr);
 }
 
 TEST(CspDelegateFactoryTest, InsecureBlocked) {
@@ -179,10 +180,10 @@
     // Capture the output, because we should get a FATAL log and we don't
     // want to crash.
     ScopedLogInterceptor li(&output);
+    CspDelegate::Options options;
+    options.enforcement_type = kCspEnforcementDisable;
     std::unique_ptr<CspDelegate> delegate =
-        CspDelegateFactory::GetInstance()->Create(
-            kCspEnforcementDisable, std::unique_ptr<CspViolationReporter>(),
-            GURL(), csp::kCSPRequired, base::Closure());
+        CspDelegateFactory::Create(nullptr, options);
 
     std::unique_ptr<CspDelegate> empty_delegate;
     EXPECT_EQ(empty_delegate.get(), delegate.get());
@@ -193,12 +194,13 @@
 TEST(CspDelegateFactoryTest, InsecureAllowed) {
   // This only compiles because this test is a friend of CspDelegateFactory,
   // otherwise GetInsecureAllowedToken is private.
-  int token = CspDelegateFactory::GetInstance()->GetInsecureAllowedToken();
+  CspDelegate::Options options;
+  options.enforcement_type = kCspEnforcementDisable;
+  options.insecure_allowed_token =
+      CspDelegateFactory::GetInstance()->GetInsecureAllowedToken();
   std::unique_ptr<CspDelegate> delegate =
-      CspDelegateFactory::GetInstance()->Create(
-          kCspEnforcementDisable, std::unique_ptr<CspViolationReporter>(),
-          GURL(), csp::kCSPRequired, base::Closure(), token);
-  EXPECT_TRUE(delegate != NULL);
+      CspDelegateFactory::Create(nullptr, options);
+  EXPECT_TRUE(delegate != nullptr);
 }
 
 }  // namespace web
diff --git a/cobalt/web/custom_event.h b/cobalt/web/custom_event.h
index ca9d46e..f5107d5 100644
--- a/cobalt/web/custom_event.h
+++ b/cobalt/web/custom_event.h
@@ -18,8 +18,10 @@
 #include <memory>
 #include <string>
 
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/value_handle.h"
 #include "cobalt/web/custom_event_init.h"
+#include "cobalt/web/environment_settings_helper.h"
 #include "cobalt/web/event.h"
 
 namespace cobalt {
@@ -29,9 +31,12 @@
 //   https://www.w3.org/TR/2015/REC-dom-20151119/#customevent
 class CustomEvent : public web::Event {
  public:
-  explicit CustomEvent(const std::string& type) : Event(type) {}
-  CustomEvent(const std::string& type, const CustomEventInit& init_dict)
-      : Event(type, init_dict) {
+  explicit CustomEvent(script::EnvironmentSettings* environment_settings,
+                       const std::string& type)
+      : Event(type), environment_settings_(environment_settings) {}
+  CustomEvent(script::EnvironmentSettings* environment_settings,
+              const std::string& type, const CustomEventInit& init_dict)
+      : Event(type, init_dict), environment_settings_(environment_settings) {
     set_detail(init_dict.detail());
   }
 
@@ -49,7 +54,11 @@
 
   void set_detail(const script::ValueHandleHolder* detail) {
     if (detail) {
-      detail_.reset(new script::ValueHandleHolder::Reference(this, *detail));
+      auto* wrappable = environment_settings_
+                            ? get_global_wrappable(environment_settings_)
+                            : this;
+      detail_.reset(
+          new script::ValueHandleHolder::Reference(wrappable, *detail));
     } else {
       detail_.reset();
     }
@@ -69,6 +78,9 @@
   ~CustomEvent() override {}
 
   std::unique_ptr<script::ValueHandleHolder::Reference> detail_;
+
+ private:
+  script::EnvironmentSettings* environment_settings_;
 };
 
 }  // namespace web
diff --git a/cobalt/web/custom_event.idl b/cobalt/web/custom_event.idl
index f327201..69b471d 100644
--- a/cobalt/web/custom_event.idl
+++ b/cobalt/web/custom_event.idl
@@ -15,6 +15,7 @@
 // https://www.w3.org/TR/2015/REC-dom-20151119/#customevent
 [
   Constructor(DOMString type, optional CustomEventInit eventInitDict),
+  ConstructorCallWith=EnvironmentSettings,
   Exposed = (Window,Worker)
 ] interface CustomEvent : Event {
   readonly attribute any detail;
diff --git a/cobalt/web/custom_event_test.cc b/cobalt/web/custom_event_test.cc
index 5d1bded..7d567e6 100644
--- a/cobalt/web/custom_event_test.cc
+++ b/cobalt/web/custom_event_test.cc
@@ -35,7 +35,8 @@
 }  // namespace
 
 TEST(CustomEventTest, ConstructorWithEventTypeString) {
-  scoped_refptr<CustomEvent> event = new CustomEvent("mytestevent");
+  scoped_refptr<CustomEvent> event =
+      new CustomEvent(/*environment_settings=*/nullptr, "mytestevent");
 
   EXPECT_EQ("mytestevent", event->type());
   EXPECT_EQ(NULL, event->target().get());
@@ -52,7 +53,8 @@
 
 TEST(CustomEventTest, ConstructorWithEventTypeAndDefaultInitDict) {
   CustomEventInit init;
-  scoped_refptr<CustomEvent> event = new CustomEvent("mytestevent", init);
+  scoped_refptr<CustomEvent> event =
+      new CustomEvent(/*environment_settings=*/nullptr, "mytestevent", init);
 
   EXPECT_EQ("mytestevent", event->type());
   EXPECT_EQ(NULL, event->target().get());
diff --git a/cobalt/web/environment_settings_helper.cc b/cobalt/web/environment_settings_helper.cc
index 3c50954..6e3110a 100644
--- a/cobalt/web/environment_settings_helper.cc
+++ b/cobalt/web/environment_settings_helper.cc
@@ -14,6 +14,7 @@
 
 #include "cobalt/web/environment_settings_helper.h"
 
+#include "base/message_loop/message_loop.h"
 #include "cobalt/base/polymorphic_downcast.h"
 #include "cobalt/web/context.h"
 #include "cobalt/web/environment_settings.h"
diff --git a/cobalt/web/environment_settings_helper.h b/cobalt/web/environment_settings_helper.h
index 5511937..181fdf2 100644
--- a/cobalt/web/environment_settings_helper.h
+++ b/cobalt/web/environment_settings_helper.h
@@ -15,15 +15,25 @@
 #ifndef COBALT_WEB_ENVIRONMENT_SETTINGS_HELPER_H_
 #define COBALT_WEB_ENVIRONMENT_SETTINGS_HELPER_H_
 
-#include "cobalt/script/environment_settings.h"
-#include "cobalt/script/global_environment.h"
-#include "cobalt/script/script_value_factory.h"
-#include "cobalt/web/context.h"
 #include "v8/include/v8.h"
 
+namespace base {
+class MessageLoop;
+}  // namespace base
+
 namespace cobalt {
+
+namespace script {
+class EnvironmentSettings;
+class GlobalEnvironment;
+class ScriptValueFactory;
+class Wrappable;
+}  // namespace script
+
 namespace web {
 
+class Context;
+
 Context* get_context(script::EnvironmentSettings* environment_settings);
 v8::Isolate* get_isolate(script::EnvironmentSettings* environment_settings);
 script::GlobalEnvironment* get_global_environment(
diff --git a/cobalt/web/error_event.h b/cobalt/web/error_event.h
index 8fad2b6..c06c3d0 100644
--- a/cobalt/web/error_event.h
+++ b/cobalt/web/error_event.h
@@ -20,7 +20,9 @@
 
 #include "cobalt/base/token.h"
 #include "cobalt/base/tokens.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/value_handle.h"
+#include "cobalt/web/environment_settings_helper.h"
 #include "cobalt/web/error_event_init.h"
 #include "cobalt/web/event.h"
 
@@ -33,22 +35,30 @@
 //   https://www.w3.org/TR/html50/webappapis.html#errorevent
 class ErrorEvent : public Event {
  public:
-  ErrorEvent() : Event(base::Tokens::error()) {}
-  explicit ErrorEvent(const std::string& type) : Event(type) {}
-  explicit ErrorEvent(const web::ErrorEventInit& init_dict)
+  explicit ErrorEvent(script::EnvironmentSettings* environment_settings)
+      : Event(base::Tokens::error()),
+        environment_settings_(environment_settings) {}
+  ErrorEvent(script::EnvironmentSettings* environment_settings,
+             const std::string& type)
+      : Event(type), environment_settings_(environment_settings) {}
+  ErrorEvent(script::EnvironmentSettings* environment_settings,
+             const web::ErrorEventInit& init_dict)
       : Event(base::Tokens::error(), init_dict),
         message_(init_dict.message()),
         filename_(init_dict.filename()),
         lineno_(init_dict.lineno()),
-        colno_(init_dict.colno()) {
+        colno_(init_dict.colno()),
+        environment_settings_(environment_settings) {
     InitError(init_dict);
   }
-  ErrorEvent(const std::string& type, const web::ErrorEventInit& init_dict)
+  ErrorEvent(script::EnvironmentSettings* environment_settings,
+             const std::string& type, const web::ErrorEventInit& init_dict)
       : Event(type, init_dict),
         message_(init_dict.message()),
         filename_(init_dict.filename()),
         lineno_(init_dict.lineno()),
-        colno_(init_dict.colno()) {
+        colno_(init_dict.colno()),
+        environment_settings_(environment_settings) {
     InitError(init_dict);
   }
 
@@ -75,7 +85,10 @@
   void InitError(const web::ErrorEventInit& init_dict) {
     const script::ValueHandleHolder* error = init_dict.error();
     if (error) {
-      error_.reset(new script::ValueHandleHolder::Reference(this, *error));
+      auto* wrappable = environment_settings_
+                            ? get_global_wrappable(environment_settings_)
+                            : this;
+      error_.reset(new script::ValueHandleHolder::Reference(wrappable, *error));
     }
   }
 
@@ -83,6 +96,7 @@
   std::string filename_;
   uint32 lineno_ = 0;
   uint32 colno_ = 0;
+  script::EnvironmentSettings* environment_settings_;
   std::unique_ptr<script::ValueHandleHolder::Reference> error_;
 };
 
diff --git a/cobalt/web/error_event.idl b/cobalt/web/error_event.idl
index 0b9be41..996c269 100644
--- a/cobalt/web/error_event.idl
+++ b/cobalt/web/error_event.idl
@@ -16,7 +16,8 @@
 
 [
   Exposed = (Window,Worker),
-  Constructor(DOMString type, optional ErrorEventInit eventInitDict)
+  Constructor(DOMString type, optional ErrorEventInit eventInitDict),
+  ConstructorCallWith=EnvironmentSettings,
 ] interface ErrorEvent : Event {
   readonly attribute DOMString message;
   readonly attribute DOMString filename;
diff --git a/cobalt/web/error_event_test.cc b/cobalt/web/error_event_test.cc
index 9f7990b..5a7cb68 100644
--- a/cobalt/web/error_event_test.cc
+++ b/cobalt/web/error_event_test.cc
@@ -19,21 +19,34 @@
 
 #include "base/bind.h"
 #include "base/logging.h"
-#include "cobalt/dom/testing/test_with_javascript.h"
+#include "base/test/scoped_task_environment.h"
+#include "cobalt/script/testing/fake_script_value.h"
 #include "cobalt/web/error_event_init.h"
 #include "cobalt/web/testing/gtest_workarounds.h"
+#include "cobalt/web/testing/mock_event_listener.h"
+#include "cobalt/web/testing/test_with_javascript.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace cobalt {
 namespace web {
 
+using ::testing::ContainsRegex;
+
 namespace {
-class ErrorEventTestWithJavaScript : public dom::testing::TestWithJavaScript {};
+class ErrorEventTestWithJavaScript : public testing::TestWebWithJavaScript {
+ protected:
+  ErrorEventTestWithJavaScript() {
+    fake_event_listener_ = testing::MockEventListener::Create();
+  }
+
+  std::unique_ptr<testing::MockEventListener> fake_event_listener_;
+};
 }  // namespace
 
 TEST(ErrorEventTest, ConstructorWithEventTypeString) {
-  scoped_refptr<ErrorEvent> event = new ErrorEvent("mytestevent");
+  scoped_refptr<ErrorEvent> event =
+      new ErrorEvent(/*environment_settings=*/nullptr, "mytestevent");
 
   EXPECT_EQ("mytestevent", event->type());
   EXPECT_EQ(NULL, event->target().get());
@@ -54,7 +67,8 @@
 
 TEST(ErrorEventTest, ConstructorWithEventTypeAndDefaultInitDict) {
   ErrorEventInit init;
-  scoped_refptr<ErrorEvent> event = new ErrorEvent("mytestevent", init);
+  scoped_refptr<ErrorEvent> event =
+      new ErrorEvent(/*environment_settings=*/nullptr, "mytestevent", init);
 
   EXPECT_EQ("mytestevent", event->type());
   EXPECT_EQ(NULL, event->target().get());
@@ -73,7 +87,7 @@
   EXPECT_EQ(NULL, event->error());
 }
 
-TEST_F(ErrorEventTestWithJavaScript, ConstructorWithEventTypeAndErrorInitDict) {
+TEST_P(ErrorEventTestWithJavaScript, ConstructorWithEventTypeAndErrorInitDict) {
   std::string result;
   bool success = EvaluateScript(
       "var event = new ErrorEvent('dog', "
@@ -103,5 +117,50 @@
   }
 }
 
+TEST_P(ErrorEventTestWithJavaScript, ErrorEventIsDispatched) {
+  GetWindowOrWorkerGlobalScope()->AddEventListener(
+      "error",
+      script::testing::FakeScriptValue<web::EventListener>(
+          fake_event_listener_.get()),
+      true);
+  fake_event_listener_->ExpectHandleEventCall("error",
+                                              GetWindowOrWorkerGlobalScope());
+  GetWindowOrWorkerGlobalScope()->DispatchEvent(new web::Event("error"));
+}
+
+TEST_P(ErrorEventTestWithJavaScript, ErrorEventIsFiredFromSyntaxError) {
+  GetWindowOrWorkerGlobalScope()->AddEventListener(
+      "error",
+      script::testing::FakeScriptValue<web::EventListener>(
+          fake_event_listener_.get()),
+      true);
+  fake_event_listener_->ExpectHandleEventCall("error",
+                                              GetWindowOrWorkerGlobalScope());
+  std::string result;
+  bool success = EvaluateScript("my_syntax_error_here", &result);
+  EXPECT_THAT(result, ContainsRegex("my_syntax_error_here"));
+  EXPECT_FALSE(success);
+}
+
+TEST_P(ErrorEventTestWithJavaScript, ErrorEventIsFiredFromThrow) {
+  GetWindowOrWorkerGlobalScope()->AddEventListener(
+      "error",
+      script::testing::FakeScriptValue<web::EventListener>(
+          fake_event_listener_.get()),
+      true);
+  fake_event_listener_->ExpectHandleEventCall("error",
+                                              GetWindowOrWorkerGlobalScope());
+  std::string result;
+  bool success =
+      EvaluateScript("throw (new Error('my_thrown_error'))", &result);
+  EXPECT_THAT(result, ContainsRegex("my_thrown_error"));
+  EXPECT_FALSE(success);
+}
+
+INSTANTIATE_TEST_CASE_P(
+    ErrorEventTestsWithJavaScript, ErrorEventTestWithJavaScript,
+    ::testing::ValuesIn(testing::TestWebWithJavaScript::GetWebTypes()),
+    testing::TestWebWithJavaScript::GetTypeName);
+
 }  // namespace web
 }  // namespace cobalt
diff --git a/cobalt/web/event_target.h b/cobalt/web/event_target.h
index 134fe79..1663f80 100644
--- a/cobalt/web/event_target.h
+++ b/cobalt/web/event_target.h
@@ -16,6 +16,7 @@
 #define COBALT_WEB_EVENT_TARGET_H_
 
 #include <memory>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -536,6 +537,14 @@
     return environment_settings_;
   }
 
+  std::set<base::Token>& event_listener_event_types() const {
+    static std::set<base::Token> event_listener_event_types;
+    for (auto& event_listener_info : event_listener_infos_) {
+      event_listener_event_types.insert(event_listener_info->type());
+    }
+    return event_listener_event_types;
+  }
+
  protected:
   virtual ~EventTarget() { environment_settings_ = nullptr; }
 
diff --git a/cobalt/web/stat_tracker.cc b/cobalt/web/stat_tracker.cc
index edead88..6f2899e 100644
--- a/cobalt/web/stat_tracker.cc
+++ b/cobalt/web/stat_tracker.cc
@@ -18,18 +18,21 @@
 
 namespace cobalt {
 namespace web {
-StatTracker::StatTracker(const std::string& name)
+StatTracker::StatTracker(const std::string& name, const char* component)
     : count_window_timers_interval_(
-          base::StringPrintf("Count.%s.DOM.WindowTimers.Interval",
-                             name.c_str()),
+          base::StringPrintf("Count.%s.%s.WindowTimers.Interval", name.c_str(),
+                             component),
           0, "Number of active WindowTimer Intervals."),
       count_window_timers_timeout_(
-          base::StringPrintf("Count.%s.DOM.WindowTimers.Timeout", name.c_str()),
+          base::StringPrintf("Count.%s.%s.WindowTimers.Timeout", name.c_str(),
+                             component),
           0, "Number of active WindowTimer Timeouts."),
       count_window_timers_interval_created_(0),
       count_window_timers_interval_destroyed_(0),
       count_window_timers_timeout_created_(0),
-      count_window_timers_timeout_destroyed_(0) {}
+      count_window_timers_timeout_destroyed_(0) {
+  DCHECK(component);
+}
 
 StatTracker::~StatTracker() {
   FlushPeriodicTracking();
diff --git a/cobalt/web/stat_tracker.h b/cobalt/web/stat_tracker.h
index 3e9edc8..5d23345 100644
--- a/cobalt/web/stat_tracker.h
+++ b/cobalt/web/stat_tracker.h
@@ -25,7 +25,7 @@
 // Tracks stats for web resources.
 class StatTracker {
  public:
-  explicit StatTracker(const std::string& name);
+  StatTracker(const std::string& name, const char* component);
   ~StatTracker();
 
   void OnWindowTimersIntervalCreated() {
diff --git a/cobalt/web/testing/stub_web_context.h b/cobalt/web/testing/stub_web_context.h
index 311c255..87268f0 100644
--- a/cobalt/web/testing/stub_web_context.h
+++ b/cobalt/web/testing/stub_web_context.h
@@ -116,16 +116,18 @@
   }
 
   const std::string& name() const final { return name_; };
-  void setup_environment_settings(
+  void SetupEnvironmentSettings(
       EnvironmentSettings* environment_settings) final {
     environment_settings_.reset(environment_settings);
     if (environment_settings_) environment_settings_->set_context(this);
   }
+  void SetupFinished() final {}
+
   EnvironmentSettings* environment_settings() const final {
     return environment_settings_.get();
   }
   EnvironmentSettings* setup_stub_environment_settings() {
-    setup_environment_settings(new testing::StubEnvironmentSettings);
+    SetupEnvironmentSettings(new testing::StubEnvironmentSettings);
     return environment_settings();
   }
 
diff --git a/cobalt/web/testing/test_with_javascript.h b/cobalt/web/testing/test_with_javascript.h
index 011ab7a..f5eeebb 100644
--- a/cobalt/web/testing/test_with_javascript.h
+++ b/cobalt/web/testing/test_with_javascript.h
@@ -22,7 +22,9 @@
 #include "cobalt/dom/dom_settings.h"
 #include "cobalt/dom/testing/stub_window.h"
 #include "cobalt/dom/window.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/wrappable.h"
+#include "cobalt/web/window_or_worker_global_scope.h"
 #include "cobalt/worker/testing/test_with_javascript.h"
 
 namespace cobalt {
@@ -51,6 +53,14 @@
         TypeIdProvider>::web_context();
   }
 
+  web::WindowOrWorkerGlobalScope* GetWindowOrWorkerGlobalScope() const {
+    return web_context()->GetWindowOrWorkerGlobalScope();
+  }
+
+  script::EnvironmentSettings* environment_settings() {
+    return web_context()->environment_settings();
+  }
+
  private:
   std::unique_ptr<dom::testing::StubWindow> window_;
 };
diff --git a/cobalt/web/window_or_worker_global_scope.cc b/cobalt/web/window_or_worker_global_scope.cc
index 769cd77..a3426fa 100644
--- a/cobalt/web/window_or_worker_global_scope.cc
+++ b/cobalt/web/window_or_worker_global_scope.cc
@@ -16,8 +16,11 @@
 
 #include <utility>
 
+#include "base/bind.h"
 #include "cobalt/script/environment_settings.h"
 #include "cobalt/web/csp_delegate_factory.h"
+#include "cobalt/web/error_event.h"
+#include "cobalt/web/error_event_init.h"
 #include "cobalt/web/event_target.h"
 
 namespace cobalt {
@@ -30,22 +33,37 @@
 }  // namespace worker
 namespace web {
 WindowOrWorkerGlobalScope::WindowOrWorkerGlobalScope(
-    script::EnvironmentSettings* settings, StatTracker* stat_tracker,
-    Options options)
+    script::EnvironmentSettings* settings, const Options& options)
     // Global scope object EventTargets require special handling for onerror
     // events, see EventTarget constructor for more details.
     : EventTarget(settings, kUnpackOnErrorEvents),
+      options_(options),
       caches_(new CacheStorage()),
       crypto_(new Crypto()),
-      window_timers_(this, stat_tracker, debugger_hooks(),
+      window_timers_(this, options.stat_tracker, debugger_hooks(),
                      options.initial_state),
       preflight_cache_(new loader::CORSPreflightCache()) {
-  std::unique_ptr<web::CspViolationReporter> violation_reporter(
-      new web::CspViolationReporter(this, options.post_sender));
-  csp_delegate_ = web::CspDelegateFactory::GetInstance()->Create(
-      options.csp_enforcement_mode, std::move(violation_reporter),
-      environment_settings()->creation_url(), options.require_csp,
-      options.csp_policy_changed_callback, options.csp_insecure_allowed_token);
+  csp_delegate_ = CspDelegateFactory::Create(
+      this, options.csp_options,
+      base::Bind(&WindowOrWorkerGlobalScope::OnCspPolicyChanged,
+                 base::Unretained(this)));
+
+  environment_settings()
+      ->context()
+      ->global_environment()
+      ->SetReportEvalCallback(base::Bind(&web::CspDelegate::ReportEval,
+                                         base::Unretained(csp_delegate())));
+
+  environment_settings()
+      ->context()
+      ->global_environment()
+      ->SetReportErrorCallback(
+          base::Bind(&WindowOrWorkerGlobalScope::ReportScriptError,
+                     base::Unretained(this)));
+}
+
+WindowOrWorkerGlobalScope::~WindowOrWorkerGlobalScope() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 }
 
 bool WindowOrWorkerGlobalScope::IsWindow() {
@@ -66,8 +84,24 @@
          base::GetTypeId<worker::ServiceWorkerGlobalScope>();
 }
 
+void WindowOrWorkerGlobalScope::OnCspPolicyChanged() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(csp_delegate());
+  DCHECK(environment_settings()->context()->global_environment());
+
+  std::string eval_disabled_message;
+  bool allow_eval = csp_delegate()->AllowEval(&eval_disabled_message);
+  if (allow_eval) {
+    environment_settings()->context()->global_environment()->EnableEval();
+  } else {
+    environment_settings()->context()->global_environment()->DisableEval(
+        eval_disabled_message);
+  }
+}
+
 int WindowOrWorkerGlobalScope::SetTimeout(
-    const web::WindowTimers::TimerCallbackArg& handler, int timeout) {
+    const WindowTimers::TimerCallbackArg& handler, int timeout) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return window_timers_.SetTimeout(handler, timeout);
 }
 
@@ -76,7 +110,8 @@
 }
 
 int WindowOrWorkerGlobalScope::SetInterval(
-    const web::WindowTimers::TimerCallbackArg& handler, int timeout) {
+    const WindowTimers::TimerCallbackArg& handler, int timeout) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return window_timers_.SetInterval(handler, timeout);
 }
 
@@ -85,6 +120,7 @@
 }
 
 void WindowOrWorkerGlobalScope::DestroyTimers() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   window_timers_.DisableCallbacks();
 }
 
@@ -96,5 +132,64 @@
   return crypto_;
 }
 
+bool WindowOrWorkerGlobalScope::ReportScriptError(
+    const script::ErrorReport& error_report) {
+  // Runtime script errors: when the user agent is required to report an error
+  // for a particular script, it must run these steps, after which the error is
+  // either handled or not handled:
+  //   https://www.w3.org/TR/html50/webappapis.html#runtime-script-errors
+
+  // 1. If target is in error reporting mode, then abort these steps; the error
+  //    is not handled.
+  if (is_reporting_script_error_) {
+    return false;
+  }
+
+  // 2. Let target be in error reporting mode.
+  is_reporting_script_error_ = true;
+
+  // 7. Let event be a new trusted ErrorEvent object that does not bubble but is
+  //    cancelable, and which has the event name error.
+  // NOTE: Cobalt does not currently support trusted events.
+  web::ErrorEventInit error;
+  error.set_bubbles(false);
+  error.set_cancelable(true);
+
+  if (error_report.is_muted) {
+    // 6. If script has muted errors, then set message to "Script error.", set
+    //    location to the empty string, set line and col to 0, and set error
+    //    object to null.
+    error.set_message("Script error.");
+    error.set_filename("");
+    error.set_lineno(0);
+    error.set_colno(0);
+    error.set_error(NULL);
+  } else {
+    // 8. Initialize event's message attribute to message.
+    error.set_message(error_report.message);
+    // 9. Initialize event's filename attribute to location.
+    error.set_filename(error_report.filename);
+    // 10. Initialize event's lineno attribute to line.
+    error.set_lineno(error_report.line_number);
+    // 11. Initialize event's colno attribute to col.
+    error.set_colno(error_report.column_number);
+    // 12. Initialize event's error attribute to error object.
+    error.set_error(error_report.error ? error_report.error.get() : NULL);
+  }
+
+  scoped_refptr<web::ErrorEvent> error_event(
+      new web::ErrorEvent(environment_settings(), error));
+
+  // 13. Dispatch event at target.
+  DispatchEvent(error_event);
+
+  // 14. Let target no longer be in error reporting mode.
+  is_reporting_script_error_ = false;
+
+  // 15. If event was canceled, then the error is handled. Otherwise, the error
+  //     is not handled.
+  return error_event->default_prevented();
+}
+
 }  // namespace web
 }  // namespace cobalt
diff --git a/cobalt/web/window_or_worker_global_scope.h b/cobalt/web/window_or_worker_global_scope.h
index d2580ac..fec3083 100644
--- a/cobalt/web/window_or_worker_global_scope.h
+++ b/cobalt/web/window_or_worker_global_scope.h
@@ -21,15 +21,16 @@
 
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
 #include "cobalt/loader/cors_preflight_cache.h"
 #include "cobalt/script/environment_settings.h"
 #include "cobalt/web/cache_storage.h"
 #include "cobalt/web/crypto.h"
 #include "cobalt/web/csp_delegate.h"
-#include "cobalt/web/csp_delegate_type.h"
 #include "cobalt/web/event_target.h"
 #include "cobalt/web/event_target_listener_info.h"
 #include "cobalt/web/navigator_base.h"
+#include "cobalt/web/stat_tracker.h"
 #include "cobalt/web/window_timers.h"
 
 namespace cobalt {
@@ -48,34 +49,21 @@
 class WindowOrWorkerGlobalScope : public EventTarget {
  public:
   struct Options {
-    explicit Options(base::ApplicationState initial_state)
-        : initial_state(initial_state),
-          csp_enforcement_mode(web::kCspEnforcementEnable) {}
-
+    Options() = default;
     Options(base::ApplicationState initial_state,
-            const network_bridge::PostSender& post_sender,
-            csp::CSPHeaderPolicy require_csp,
-            web::CspEnforcementType csp_enforcement_mode,
-            base::Closure csp_policy_changed_callback,
-            int csp_insecure_allowed_token = 0)
+            const web::CspDelegate::Options& csp_options,
+            StatTracker* stat_tracker = nullptr)
         : initial_state(initial_state),
-          post_sender(post_sender),
-          require_csp(require_csp),
-          csp_enforcement_mode(csp_enforcement_mode),
-          csp_policy_changed_callback(csp_policy_changed_callback),
-          csp_insecure_allowed_token(csp_insecure_allowed_token) {}
+          csp_options(csp_options),
+          stat_tracker(stat_tracker) {}
 
-    base::ApplicationState initial_state;
-    network_bridge::PostSender post_sender;
-    csp::CSPHeaderPolicy require_csp;
-    web::CspEnforcementType csp_enforcement_mode;
-    base::Closure csp_policy_changed_callback;
-    int csp_insecure_allowed_token;
+    base::ApplicationState initial_state = base::kApplicationStateStarted;
+    web::CspDelegate::Options csp_options;
+    StatTracker* stat_tracker = nullptr;
   };
 
   explicit WindowOrWorkerGlobalScope(script::EnvironmentSettings* settings,
-                                     StatTracker* stat_tracker,
-                                     Options options);
+                                     const Options& options);
   WindowOrWorkerGlobalScope(const WindowOrWorkerGlobalScope&) = delete;
   WindowOrWorkerGlobalScope& operator=(const WindowOrWorkerGlobalScope&) =
       delete;
@@ -101,7 +89,7 @@
 
   // The CspDelegate gives access to the CSP list of the policy container
   //   https://html.spec.whatwg.org/commit-snapshots/ae3c91103aada3d6d346a6dd3c5356773195fc79/#policy-container
-  web::CspDelegate* csp_delegate() const {
+  CspDelegate* csp_delegate() const {
     DCHECK(csp_delegate_);
     return csp_delegate_.get();
   }
@@ -110,26 +98,25 @@
     return preflight_cache_;
   }
 
-  DEFINE_WRAPPABLE_TYPE(WindowOrWorkerGlobalScope);
+  // Callback from the CspDelegate.
+  void OnCspPolicyChanged();
 
   // Web API: WindowTimers (implements)
   //   https://www.w3.org/TR/html50/webappapis.html#timers
   //
-  int SetTimeout(const web::WindowTimers::TimerCallbackArg& handler) {
+  int SetTimeout(const WindowTimers::TimerCallbackArg& handler) {
     return SetTimeout(handler, 0);
   }
 
-  int SetTimeout(const web::WindowTimers::TimerCallbackArg& handler,
-                 int timeout);
+  int SetTimeout(const WindowTimers::TimerCallbackArg& handler, int timeout);
 
   void ClearTimeout(int handle);
 
-  int SetInterval(const web::WindowTimers::TimerCallbackArg& handler) {
+  int SetInterval(const WindowTimers::TimerCallbackArg& handler) {
     return SetInterval(handler, 0);
   }
 
-  int SetInterval(const web::WindowTimers::TimerCallbackArg& handler,
-                  int timeout);
+  int SetInterval(const WindowTimers::TimerCallbackArg& handler, int timeout);
 
   void ClearInterval(int handle);
 
@@ -143,18 +130,38 @@
   //   https://www.w3.org/TR/WebCryptoAPI/#crypto-interface
   scoped_refptr<Crypto> crypto() const;
 
- protected:
-  virtual ~WindowOrWorkerGlobalScope() {}
+  const Options& options() { return options_; }
 
+  // Report an error encountered while running JS.
+  // Performs the steps specified for runtime script errors:
+  //   https://www.w3.org/TR/html50/webappapis.html#runtime-script-errors
+  // Returns whether or not the script was handled.
+  bool ReportScriptError(const script::ErrorReport& error_report);
+
+  DEFINE_WRAPPABLE_TYPE(WindowOrWorkerGlobalScope);
+
+ protected:
+  virtual ~WindowOrWorkerGlobalScope();
+
+  Options options_;
   scoped_refptr<CacheStorage> caches_;
   scoped_refptr<Crypto> crypto_;
-  web::WindowTimers window_timers_;
+  WindowTimers window_timers_;
 
  private:
-  std::unique_ptr<web::CspDelegate> csp_delegate_;
+  std::unique_ptr<CspDelegate> csp_delegate_;
   NavigatorBase* navigator_base_ = nullptr;
   // Global preflight cache.
   scoped_refptr<loader::CORSPreflightCache> preflight_cache_;
+
+  // Whether or not the window is currently reporting a script error. This is
+  // used to prevent infinite recursion, because reporting the error causes an
+  // event to be dispatched, which can generate a new script error.
+  bool is_reporting_script_error_ = false;
+
+  // Thread checker ensures all calls to the WebModule are made from the same
+  // thread that it is created in.
+  THREAD_CHECKER(thread_checker_);
 };
 
 }  // namespace web
diff --git a/cobalt/web/window_timers_test.cc b/cobalt/web/window_timers_test.cc
index f389997..51e75a3 100644
--- a/cobalt/web/window_timers_test.cc
+++ b/cobalt/web/window_timers_test.cc
@@ -27,7 +27,6 @@
 #include "cobalt/script/testing/fake_script_value.h"
 #include "cobalt/web/stat_tracker.h"
 #include "net/test/test_with_scoped_task_environment.h"
-
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -90,7 +89,7 @@
   WindowTimersTest()
       : WithScopedTaskEnvironment(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME),
-        stat_tracker_("WindowTimersTest"),
+        stat_tracker_("WindowTimersTest", "Test"),
         callback_(&mock_timer_callback_) {
     script::Wrappable* foo = nullptr;
     timers_.reset(
@@ -244,11 +243,11 @@
   stat_tracker_.FlushPeriodicTracking();
   EXPECT_EQ("0", base::CValManager::GetInstance()
                      ->GetValueAsString(
-                         "Count.WindowTimersTest.DOM.WindowTimers.Interval")
+                         "Count.WindowTimersTest.Test.WindowTimers.Interval")
                      .value_or("Foo"));
   EXPECT_EQ("2", base::CValManager::GetInstance()
                      ->GetValueAsString(
-                         "Count.WindowTimersTest.DOM.WindowTimers.Timeout")
+                         "Count.WindowTimersTest.Test.WindowTimers.Timeout")
                      .value_or("Foo"));
 
   EXPECT_EQ(GetPendingMainThreadTaskCount(), 2);
@@ -262,11 +261,11 @@
   stat_tracker_.FlushPeriodicTracking();
   EXPECT_EQ("0", base::CValManager::GetInstance()
                      ->GetValueAsString(
-                         "Count.WindowTimersTest.DOM.WindowTimers.Interval")
+                         "Count.WindowTimersTest.Test.WindowTimers.Interval")
                      .value_or("Foo"));
   EXPECT_EQ("0", base::CValManager::GetInstance()
                      ->GetValueAsString(
-                         "Count.WindowTimersTest.DOM.WindowTimers.Timeout")
+                         "Count.WindowTimersTest.Test.WindowTimers.Timeout")
                      .value_or("Foo"));
 }
 
@@ -398,11 +397,11 @@
   stat_tracker_.FlushPeriodicTracking();
   EXPECT_EQ("2", base::CValManager::GetInstance()
                      ->GetValueAsString(
-                         "Count.WindowTimersTest.DOM.WindowTimers.Interval")
+                         "Count.WindowTimersTest.Test.WindowTimers.Interval")
                      .value_or("Foo"));
   EXPECT_EQ("0", base::CValManager::GetInstance()
                      ->GetValueAsString(
-                         "Count.WindowTimersTest.DOM.WindowTimers.Timeout")
+                         "Count.WindowTimersTest.Test.WindowTimers.Timeout")
                      .value_or("Foo"));
 
   EXPECT_EQ(GetPendingMainThreadTaskCount(), 2);
@@ -417,11 +416,11 @@
   stat_tracker_.FlushPeriodicTracking();
   EXPECT_EQ("2", base::CValManager::GetInstance()
                      ->GetValueAsString(
-                         "Count.WindowTimersTest.DOM.WindowTimers.Interval")
+                         "Count.WindowTimersTest.Test.WindowTimers.Interval")
                      .value_or("Foo"));
   EXPECT_EQ("0", base::CValManager::GetInstance()
                      ->GetValueAsString(
-                         "Count.WindowTimersTest.DOM.WindowTimers.Timeout")
+                         "Count.WindowTimersTest.Test.WindowTimers.Timeout")
                      .value_or("Foo"));
 }
 
@@ -440,11 +439,11 @@
   stat_tracker_.FlushPeriodicTracking();
   EXPECT_EQ("2", base::CValManager::GetInstance()
                      ->GetValueAsString(
-                         "Count.WindowTimersTest.DOM.WindowTimers.Interval")
+                         "Count.WindowTimersTest.Test.WindowTimers.Interval")
                      .value_or("Foo"));
   EXPECT_EQ("2", base::CValManager::GetInstance()
                      ->GetValueAsString(
-                         "Count.WindowTimersTest.DOM.WindowTimers.Timeout")
+                         "Count.WindowTimersTest.Test.WindowTimers.Timeout")
                      .value_or("Foo"));
 
   EXPECT_EQ(GetPendingMainThreadTaskCount(), 4);
@@ -459,11 +458,11 @@
   stat_tracker_.FlushPeriodicTracking();
   EXPECT_EQ("2", base::CValManager::GetInstance()
                      ->GetValueAsString(
-                         "Count.WindowTimersTest.DOM.WindowTimers.Interval")
+                         "Count.WindowTimersTest.Test.WindowTimers.Interval")
                      .value_or("Foo"));
   EXPECT_EQ("0", base::CValManager::GetInstance()
                      ->GetValueAsString(
-                         "Count.WindowTimersTest.DOM.WindowTimers.Timeout")
+                         "Count.WindowTimersTest.Test.WindowTimers.Timeout")
                      .value_or("Foo"));
 }
 
diff --git a/cobalt/websocket/web_socket_impl_test.cc b/cobalt/websocket/web_socket_impl_test.cc
index 66da04c..d7ddb7b 100644
--- a/cobalt/websocket/web_socket_impl_test.cc
+++ b/cobalt/websocket/web_socket_impl_test.cc
@@ -63,10 +63,11 @@
 
  protected:
   WebSocketImplTest() : web_context_(new web::testing::StubWebContext()) {
-    web_context_->setup_environment_settings(
+    web_context_->SetupEnvironmentSettings(
         new dom::testing::StubEnvironmentSettings());
     web_context_->environment_settings()->set_creation_url(
         GURL("https://127.0.0.1:1234"));
+    web_context_->SetupFinished();
     std::vector<std::string> sub_protocols;
     sub_protocols.push_back("chat");
     network_task_runner_ = web_context_->network_module()
diff --git a/cobalt/worker/BUILD.gn b/cobalt/worker/BUILD.gn
index f56409c..24d8147 100644
--- a/cobalt/worker/BUILD.gn
+++ b/cobalt/worker/BUILD.gn
@@ -36,6 +36,8 @@
     "navigation_preload_manager.h",
     "service_worker.cc",
     "service_worker.h",
+    "service_worker_consts.cc",
+    "service_worker_consts.h",
     "service_worker_container.cc",
     "service_worker_container.h",
     "service_worker_global_scope.cc",
@@ -69,6 +71,7 @@
     "//cobalt/browser:generated_bindings",
     "//cobalt/loader",
     "//cobalt/web",
+    "//net",
     "//url",
   ]
 
diff --git a/cobalt/worker/clients.cc b/cobalt/worker/clients.cc
index 0a81e08..c7c7f1a 100644
--- a/cobalt/worker/clients.cc
+++ b/cobalt/worker/clients.cc
@@ -67,7 +67,8 @@
           ->script_value_factory()
           ->CreateInterfacePromise<scoped_refptr<Client>>();
   std::unique_ptr<script::ValuePromiseWrappable::Reference> promise_reference(
-      new script::ValuePromiseWrappable::Reference(this, promise));
+      new script::ValuePromiseWrappable::Reference(
+          settings_->context()->GetWindowOrWorkerGlobalScope(), promise));
 
   // 2. Run these substeps in parallel:
   ServiceWorkerJobs* jobs = settings_->context()->service_worker_jobs();
@@ -96,8 +97,8 @@
                      ->script_value_factory()
                      ->CreateBasicPromise<script::SequenceWrappable>();
   std::unique_ptr<script::ValuePromiseSequenceWrappable::Reference>
-      promise_reference(
-          new script::ValuePromiseSequenceWrappable::Reference(this, promise));
+      promise_reference(new script::ValuePromiseSequenceWrappable::Reference(
+          settings_->context()->GetWindowOrWorkerGlobalScope(), promise));
   // 2. Run the following steps in parallel:
   ServiceWorkerJobs* jobs = settings_->context()->service_worker_jobs();
   DCHECK(jobs);
@@ -125,7 +126,8 @@
                      ->script_value_factory()
                      ->CreateBasicPromise<void>();
   std::unique_ptr<script::ValuePromiseVoid::Reference> promise_reference(
-      new script::ValuePromiseVoid::Reference(this, promise));
+      new script::ValuePromiseVoid::Reference(
+          settings_->context()->GetWindowOrWorkerGlobalScope(), promise));
 
   // 1. If the service worker is not an active worker, return a promise rejected
   // with an "InvalidStateError" DOMException.
diff --git a/cobalt/worker/dedicated_worker.cc b/cobalt/worker/dedicated_worker.cc
index 39251a7..43f2b0d 100644
--- a/cobalt/worker/dedicated_worker.cc
+++ b/cobalt/worker/dedicated_worker.cc
@@ -77,6 +77,15 @@
       environment_settings()->context()->web_settings();
   options.web_options.network_module =
       environment_settings()->context()->network_module();
+
+  // Propagate the CSP Options from the current environment settings.
+  options.global_scope_options.csp_options =
+      environment_settings()
+          ->context()
+          ->GetWindowOrWorkerGlobalScope()
+          ->options()
+          .csp_options;
+
   // 6. Let worker be a new Worker object.
   // 7. Let outside port be a new MessagePort in outside settings's Realm.
   // 8. Associate the outside port with worker.
diff --git a/cobalt/worker/dedicated_worker_global_scope.cc b/cobalt/worker/dedicated_worker_global_scope.cc
index 7043148..948803d 100644
--- a/cobalt/worker/dedicated_worker_global_scope.cc
+++ b/cobalt/worker/dedicated_worker_global_scope.cc
@@ -26,8 +26,10 @@
 namespace worker {
 DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(
     script::EnvironmentSettings* settings,
+    const web::WindowOrWorkerGlobalScope::Options& options,
     bool parent_cross_origin_isolated_capability)
-    : WorkerGlobalScope(settings), cross_origin_isolated_capability_(false) {
+    : WorkerGlobalScope(settings, options),
+      cross_origin_isolated_capability_(false) {
   // Algorithm for 'run a worker'
   //   https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#run-a-worker
   // 14.9. If is shared is false and owner's cross-origin isolated
diff --git a/cobalt/worker/dedicated_worker_global_scope.h b/cobalt/worker/dedicated_worker_global_scope.h
index 4252b4f..a0c2ee4 100644
--- a/cobalt/worker/dedicated_worker_global_scope.h
+++ b/cobalt/worker/dedicated_worker_global_scope.h
@@ -36,6 +36,7 @@
  public:
   explicit DedicatedWorkerGlobalScope(
       script::EnvironmentSettings* settings,
+      const web::WindowOrWorkerGlobalScope::Options& options,
       bool parent_cross_origin_isolated_capability = false);
   DedicatedWorkerGlobalScope(const DedicatedWorkerGlobalScope&) = delete;
   DedicatedWorkerGlobalScope& operator=(const DedicatedWorkerGlobalScope&) =
diff --git a/cobalt/worker/extendable_event.cc b/cobalt/worker/extendable_event.cc
index 5aaa923..87d48cb 100644
--- a/cobalt/worker/extendable_event.cc
+++ b/cobalt/worker/extendable_event.cc
@@ -17,6 +17,14 @@
 namespace cobalt {
 namespace worker {
 
+void ExtendableEvent::OnEnvironmentSettingsChanged(bool context_valid) {
+  if (!context_valid) {
+    while (traced_global_promises_.size() > 0) {
+      LessenLifetime(traced_global_promises_.begin()->first);
+    }
+  }
+}
+
 void ExtendableEvent::WaitUntil(
     script::EnvironmentSettings* settings,
     std::unique_ptr<script::Promise<script::ValueHandle*>>& promise,
@@ -33,6 +41,7 @@
     return;
   }
   // 3. Add promise to event’s extend lifetime promises.
+  ExtendLifetime(promise.get());
   // 4. Increment event’s pending promises count by one.
   ++pending_promise_count_;
   // 5. Upon fulfillment or rejection of promise, queue a microtask to run
@@ -76,8 +85,46 @@
                                  ->service_worker_object()
                                  ->containing_service_worker_registration())));
   }
+  LessenLifetime(promise);
   delete promise;
 }
 
+void ExtendableEvent::ExtendLifetime(
+    const script::Promise<script::ValueHandle*>* promise) {
+  auto heap_tracer =
+      script::v8c::V8cEngine::GetFromIsolate(isolate_)->heap_tracer();
+  if (traced_global_promises_.size() == 0) {
+    heap_tracer->AddRoot(this);
+  }
+  auto* traced_global =
+      new v8::TracedGlobal<v8::Value>(isolate_, promise->promise());
+  traced_global_promises_[promise] = traced_global;
+  heap_tracer->AddRoot(traced_global);
+}
+
+void ExtendableEvent::LessenLifetime(
+    const script::Promise<script::ValueHandle*>* promise) {
+  auto heap_tracer =
+      script::v8c::V8cEngine::GetFromIsolate(isolate_)->heap_tracer();
+  if (traced_global_promises_.count(promise) == 1) {
+    auto* traced_global = traced_global_promises_[promise];
+    heap_tracer->RemoveRoot(traced_global);
+    traced_global_promises_.erase(promise);
+    delete traced_global;
+  }
+  if (traced_global_promises_.size() == 0) {
+    heap_tracer->RemoveRoot(this);
+  }
+}
+
+void ExtendableEvent::InitializeEnvironmentSettingsChangeObserver(
+    script::EnvironmentSettings* settings) {
+  web::Context* context = web::get_context(settings);
+  context->AddEnvironmentSettingsChangeObserver(this);
+  remove_environment_settings_change_observer_ =
+      base::BindOnce(&web::Context::RemoveEnvironmentSettingsChangeObserver,
+                     base::Unretained(context), base::Unretained(this));
+}
+
 }  // namespace worker
 }  // namespace cobalt
diff --git a/cobalt/worker/extendable_event.h b/cobalt/worker/extendable_event.h
index f915189..fbd8136 100644
--- a/cobalt/worker/extendable_event.h
+++ b/cobalt/worker/extendable_event.h
@@ -15,6 +15,7 @@
 #ifndef COBALT_WORKER_EXTENDABLE_EVENT_H_
 #define COBALT_WORKER_EXTENDABLE_EVENT_H_
 
+#include <map>
 #include <memory>
 #include <string>
 #include <utility>
@@ -30,6 +31,7 @@
 #include "cobalt/web/context.h"
 #include "cobalt/web/dom_exception.h"
 #include "cobalt/web/environment_settings.h"
+#include "cobalt/web/environment_settings_helper.h"
 #include "cobalt/web/event.h"
 #include "cobalt/web/window_or_worker_global_scope.h"
 #include "cobalt/worker/extendable_event_init.h"
@@ -37,21 +39,38 @@
 #include "cobalt/worker/service_worker_jobs.h"
 #include "cobalt/worker/service_worker_object.h"
 #include "cobalt/worker/service_worker_registration_object.h"
+#include "v8/include/v8.h"
 
 namespace cobalt {
 namespace worker {
 
-class ExtendableEvent : public web::Event {
+class ExtendableEvent : public web::Event,
+                        public web::Context::EnvironmentSettingsChangeObserver {
  public:
-  explicit ExtendableEvent(const std::string& type) : Event(type) {}
-  ExtendableEvent(const std::string& type, const ExtendableEventInit& init_dict)
-      : ExtendableEvent(base::Token(type), init_dict) {}
-  ExtendableEvent(base::Token type,
+  explicit ExtendableEvent(script::EnvironmentSettings* settings,
+                           const std::string& type)
+      : Event(type), isolate_(web::get_isolate(settings)) {
+    InitializeEnvironmentSettingsChangeObserver(settings);
+  }
+  ExtendableEvent(script::EnvironmentSettings* settings,
+                  const std::string& type, const ExtendableEventInit& init_dict)
+      : ExtendableEvent(settings, base::Token(type), init_dict) {}
+  ExtendableEvent(script::EnvironmentSettings* settings, base::Token type,
                   base::OnceCallback<void(bool)> done_callback =
                       base::OnceCallback<void(bool)>())
-      : Event(type), done_callback_(std::move(done_callback)) {}
-  ExtendableEvent(base::Token type, const ExtendableEventInit& init_dict)
-      : Event(type, init_dict) {}
+      : Event(type),
+        done_callback_(std::move(done_callback)),
+        isolate_(web::get_isolate(settings)) {
+    InitializeEnvironmentSettingsChangeObserver(settings);
+  }
+  ExtendableEvent(script::EnvironmentSettings* settings, base::Token type,
+                  const ExtendableEventInit& init_dict)
+      : Event(type, init_dict), isolate_(web::get_isolate(settings)) {
+    InitializeEnvironmentSettingsChangeObserver(settings);
+  }
+
+  // web::Context::EnvironmentSettingsChangeObserver
+  void OnEnvironmentSettingsChanged(bool context_valid) override;
 
   void WaitUntil(
       script::EnvironmentSettings* settings,
@@ -75,9 +94,16 @@
   DEFINE_WRAPPABLE_TYPE(ExtendableEvent);
 
  protected:
-  ~ExtendableEvent() override {}
+  ~ExtendableEvent() override {
+    std::move(remove_environment_settings_change_observer_).Run();
+  }
 
  private:
+  void ExtendLifetime(const script::Promise<script::ValueHandle*>* promise);
+  void LessenLifetime(const script::Promise<script::ValueHandle*>* promise);
+  void InitializeEnvironmentSettingsChangeObserver(
+      script::EnvironmentSettings* settings);
+
   // https://www.w3.org/TR/2022/CRD-service-workers-20220712/#extendableevent-extend-lifetime-promises
   // std::list<script::Promise<script::ValueHandle*>> extend_lifetime_promises_;
   int pending_promise_count_ = 0;
@@ -86,6 +112,12 @@
   bool timed_out_flag_ = false;
 
   base::OnceCallback<void(bool)> done_callback_;
+  base::OnceClosure remove_environment_settings_change_observer_;
+  v8::Isolate* isolate_;
+
+  std::map<const script::Promise<script::ValueHandle*>*,
+           v8::TracedGlobal<v8::Value>*>
+      traced_global_promises_;
 };
 
 }  // namespace worker
diff --git a/cobalt/worker/extendable_event.idl b/cobalt/worker/extendable_event.idl
index 8a12344..a0db8d1 100644
--- a/cobalt/worker/extendable_event.idl
+++ b/cobalt/worker/extendable_event.idl
@@ -16,6 +16,7 @@
 
 [
   Exposed = ServiceWorker,
+  ConstructorCallWith=EnvironmentSettings,
   Constructor(DOMString type, optional ExtendableEventInit eventInitDict)
 ] interface ExtendableEvent : Event {
   [RaisesException, CallWith = EnvironmentSettings] void waitUntil(Promise<any> f);
diff --git a/cobalt/worker/extendable_message_event.cc b/cobalt/worker/extendable_message_event.cc
index aca29cb..30a253c 100644
--- a/cobalt/worker/extendable_message_event.cc
+++ b/cobalt/worker/extendable_message_event.cc
@@ -28,8 +28,9 @@
 namespace worker {
 
 ExtendableMessageEvent::ExtendableMessageEvent(
-    base::Token type, const ExtendableMessageEventInit& init_dict)
-    : ExtendableEvent(type, init_dict) {
+    script::EnvironmentSettings* settings, base::Token type,
+    const ExtendableMessageEventInit& init_dict)
+    : ExtendableEvent(settings, type, init_dict) {
   if (init_dict.has_data() && init_dict.data()) {
     DCHECK(init_dict.data());
     data_ = script::SerializeScriptValue(*(init_dict.data()));
diff --git a/cobalt/worker/extendable_message_event.h b/cobalt/worker/extendable_message_event.h
index 965f79e..c9b2fea 100644
--- a/cobalt/worker/extendable_message_event.h
+++ b/cobalt/worker/extendable_message_event.h
@@ -22,6 +22,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
 #include "cobalt/base/token.h"
+#include "cobalt/script/environment_settings.h"
 #include "cobalt/script/union_type.h"
 #include "cobalt/script/value_handle.h"
 #include "cobalt/script/wrappable.h"
@@ -42,18 +43,24 @@
                                  scoped_refptr<ServiceWorker>,
                                  scoped_refptr<web::MessagePort>>;
 
-  explicit ExtendableMessageEvent(const std::string& type)
-      : ExtendableEvent(type) {}
-  explicit ExtendableMessageEvent(base::Token type) : ExtendableEvent(type) {}
-  ExtendableMessageEvent(const std::string& type,
+  explicit ExtendableMessageEvent(script::EnvironmentSettings* settings,
+                                  const std::string& type)
+      : ExtendableEvent(settings, type) {}
+  explicit ExtendableMessageEvent(script::EnvironmentSettings* settings,
+                                  base::Token type)
+      : ExtendableEvent(settings, type) {}
+  ExtendableMessageEvent(script::EnvironmentSettings* settings,
+                         const std::string& type,
                          const ExtendableMessageEventInit& init_dict)
-      : ExtendableMessageEvent(base::Token(type), init_dict) {}
-  ExtendableMessageEvent(base::Token type,
+      : ExtendableMessageEvent(settings, base::Token(type), init_dict) {}
+  ExtendableMessageEvent(script::EnvironmentSettings* settings,
+                         base::Token type,
                          const ExtendableMessageEventInit& init_dict);
-  ExtendableMessageEvent(base::Token type,
+  ExtendableMessageEvent(script::EnvironmentSettings* settings,
+                         base::Token type,
                          const ExtendableMessageEventInit& init_dict,
                          std::unique_ptr<script::DataBuffer> data)
-      : ExtendableMessageEvent(type, init_dict) {
+      : ExtendableMessageEvent(settings, type, init_dict) {
     data_ = std::move(data);
   }
 
diff --git a/cobalt/worker/extendable_message_event.idl b/cobalt/worker/extendable_message_event.idl
index 30a2001..f117668 100644
--- a/cobalt/worker/extendable_message_event.idl
+++ b/cobalt/worker/extendable_message_event.idl
@@ -16,6 +16,7 @@
 
 [
   Exposed = ServiceWorker,
+  ConstructorCallWith=EnvironmentSettings,
   Constructor(DOMString type, optional ExtendableMessageEventInit eventInitDict)
 ] interface ExtendableMessageEvent : ExtendableEvent {
   [CallWith = EnvironmentSettings] readonly attribute any data;
diff --git a/cobalt/worker/extendable_message_event_test.cc b/cobalt/worker/extendable_message_event_test.cc
index 2e8e64f..40591dd 100644
--- a/cobalt/worker/extendable_message_event_test.cc
+++ b/cobalt/worker/extendable_message_event_test.cc
@@ -54,7 +54,7 @@
 TEST_F(ExtendableMessageEventTestWithJavaScript,
        ConstructorWithEventTypeString) {
   scoped_refptr<ExtendableMessageEvent> event =
-      new ExtendableMessageEvent("mytestevent");
+      new ExtendableMessageEvent(environment_settings(), "mytestevent");
 
   EXPECT_EQ("mytestevent", event->type());
   EXPECT_EQ(NULL, event->target().get());
@@ -81,7 +81,7 @@
   EXPECT_GT(data->size, 0U);
   const ExtendableMessageEventInit init;
   scoped_refptr<ExtendableMessageEvent> event = new ExtendableMessageEvent(
-      base::Tokens::message(), init, std::move(data));
+      environment_settings(), base::Tokens::message(), init, std::move(data));
 
   EXPECT_EQ("message", event->type());
   EXPECT_EQ(NULL, event->target().get());
@@ -109,7 +109,7 @@
        ConstructorWithEventTypeAndDefaultInitDict) {
   ExtendableMessageEventInit init;
   scoped_refptr<ExtendableMessageEvent> event =
-      new ExtendableMessageEvent("mytestevent", init);
+      new ExtendableMessageEvent(environment_settings(), "mytestevent", init);
 
   EXPECT_EQ("mytestevent", event->type());
   EXPECT_EQ(nullptr, event->target().get());
@@ -145,7 +145,7 @@
       Client::Create(web_context()->environment_settings()));
   init.set_source(client);
   scoped_refptr<ExtendableMessageEvent> event =
-      new ExtendableMessageEvent("mytestevent", init);
+      new ExtendableMessageEvent(environment_settings(), "mytestevent", init);
 
   EXPECT_EQ("mytestevent", event->type());
   EXPECT_EQ(nullptr, event->target().get());
diff --git a/cobalt/worker/fetch_event.cc b/cobalt/worker/fetch_event.cc
index 82e0927..99497fb 100644
--- a/cobalt/worker/fetch_event.cc
+++ b/cobalt/worker/fetch_event.cc
@@ -29,14 +29,18 @@
                        const std::string& type,
                        const FetchEventInit& event_init_dict)
     : FetchEvent(environment_settings, base::Token(type), event_init_dict,
+                 base::MessageLoop::current()->task_runner(),
                  RespondWithCallback(), ReportLoadTimingInfo()) {}
 
-FetchEvent::FetchEvent(script::EnvironmentSettings* environment_settings,
-                       base::Token type, const FetchEventInit& event_init_dict,
-                       RespondWithCallback respond_with_callback,
-                       ReportLoadTimingInfo report_load_timing_info)
-    : ExtendableEvent(type, event_init_dict),
+FetchEvent::FetchEvent(
+    script::EnvironmentSettings* environment_settings, base::Token type,
+    const FetchEventInit& event_init_dict,
+    scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner,
+    RespondWithCallback respond_with_callback,
+    ReportLoadTimingInfo report_load_timing_info)
+    : ExtendableEvent(environment_settings, type, event_init_dict),
       environment_settings_(environment_settings),
+      callback_task_runner_(callback_task_runner),
       respond_with_callback_(std::move(respond_with_callback)),
       report_load_timing_info_(std::move(report_load_timing_info)) {
   auto script_value_factory =
@@ -45,6 +49,8 @@
       this, script_value_factory->CreateBasicPromise<void>());
   request_ = std::make_unique<script::ValueHandleHolder::Reference>(
       this, event_init_dict.request());
+  respond_with_done_ = std::make_unique<script::ValuePromiseVoid::Reference>(
+      this, script_value_factory->CreateBasicPromise<void>());
 
   load_timing_info_.request_start = base::TimeTicks::Now();
   load_timing_info_.request_start_time = base::Time::Now();
@@ -55,44 +61,54 @@
 
 base::Optional<v8::Local<v8::Promise>> FetchEvent::GetText(
     v8::Local<v8::Promise> response_promise) {
-  std::move(report_load_timing_info_).Run(load_timing_info_);
+  callback_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(
+                     [](ReportLoadTimingInfo report_load_timing_info,
+                        const net::LoadTimingInfo& load_timing_info) {
+                       std::move(report_load_timing_info).Run(load_timing_info);
+                     },
+                     std::move(report_load_timing_info_), load_timing_info_));
   handled_property_->value().Resolve();
   return web::cache_utils::OptionalPromise(
       web::cache_utils::Call(response_promise->Result(), "text"));
 }
 
+void FetchEvent::RespondWithDone() { respond_with_done_->value().Resolve(); }
+
 base::Optional<v8::Local<v8::Promise>> FetchEvent::DoRespondWith(
     v8::Local<v8::Promise> text_promise) {
   auto* isolate = text_promise->GetIsolate();
   auto context = isolate->GetCurrentContext();
-  auto resolver = v8::Promise::Resolver::New(context);
-  if (resolver.IsEmpty()) {
-    return base::nullopt;
-  }
   auto body = web::cache_utils::FromV8String(text_promise->GetIsolate(),
                                              text_promise->Result());
-  auto callback = base::BindOnce(
-      [](v8::Local<v8::Context> context,
-         v8::Local<v8::Promise::Resolver> resolver) {
-        DCHECK(resolver->Resolve(context, v8::Undefined(resolver->GetIsolate()))
-                   .FromMaybe(false));
-      },
-      context, resolver.ToLocalChecked());
+  auto callback =
+      base::BindOnce(&FetchEvent::RespondWithDone, base::Unretained(this));
   web::get_context(environment_settings_)
       ->network_module()
       ->task_runner()
       ->PostTask(
           FROM_HERE,
           base::BindOnce(
-              [](RespondWithCallback respond_with_callback, std::string body,
+              [](scoped_refptr<base::SingleThreadTaskRunner>
+                     callback_task_runner,
+                 RespondWithCallback respond_with_callback, std::string body,
                  base::MessageLoop* loop, base::OnceClosure callback) {
-                std::move(respond_with_callback)
-                    .Run(std::make_unique<std::string>(std::move(body)));
+                callback_task_runner->PostTask(
+                    FROM_HERE,
+                    base::BindOnce(
+                        [](RespondWithCallback respond_with_callback,
+                           std::string body) {
+                          std::move(respond_with_callback)
+                              .Run(std::make_unique<std::string>(
+                                  std::move(body)));
+                        },
+                        std::move(respond_with_callback), std::move(body)));
                 loop->task_runner()->PostTask(FROM_HERE, std::move(callback));
               },
-              std::move(respond_with_callback_), std::move(body),
-              base::MessageLoop::current(), std::move(callback)));
-  return resolver.ToLocalChecked()->GetPromise();
+              callback_task_runner_, std::move(respond_with_callback_),
+              std::move(body), base::MessageLoop::current(),
+              std::move(callback)));
+  return respond_with_done_->value().promise();
 }
 
 void FetchEvent::RespondWith(
@@ -116,6 +132,7 @@
   std::unique_ptr<script::Promise<script::ValueHandle*>> wait_promise;
   script::v8c::FromJSValue(isolate, done_promise.value(), 0, exception_state,
                            &wait_promise);
+  WaitUntil(environment_settings_, response, exception_state);
   WaitUntil(environment_settings_, wait_promise, exception_state);
 }
 
diff --git a/cobalt/worker/fetch_event.h b/cobalt/worker/fetch_event.h
index 3b9aa46..2b129c0 100644
--- a/cobalt/worker/fetch_event.h
+++ b/cobalt/worker/fetch_event.h
@@ -41,6 +41,7 @@
              const FetchEventInit& event_init_dict);
   FetchEvent(script::EnvironmentSettings*, base::Token type,
              const FetchEventInit& event_init_dict,
+             scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner,
              RespondWithCallback respond_with_callback,
              ReportLoadTimingInfo report_load_timing_info);
   ~FetchEvent() override = default;
@@ -63,12 +64,15 @@
       v8::Local<v8::Promise> response_promise);
   base::Optional<v8::Local<v8::Promise>> DoRespondWith(
       v8::Local<v8::Promise> text_promise);
+  void RespondWithDone();
 
   script::EnvironmentSettings* environment_settings_;
+  scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner_;
   RespondWithCallback respond_with_callback_;
   ReportLoadTimingInfo report_load_timing_info_;
   std::unique_ptr<script::ValueHandleHolder::Reference> request_;
   std::unique_ptr<script::ValuePromiseVoid::Reference> handled_property_;
+  std::unique_ptr<script::ValuePromiseVoid::Reference> respond_with_done_;
   bool respond_with_called_ = false;
   net::LoadTimingInfo load_timing_info_;
 };
diff --git a/cobalt/worker/service_worker_consts.cc b/cobalt/worker/service_worker_consts.cc
new file mode 100644
index 0000000..4254b85
--- /dev/null
+++ b/cobalt/worker/service_worker_consts.cc
@@ -0,0 +1,67 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "cobalt/worker/service_worker_consts.h"
+
+namespace cobalt {
+namespace worker {
+const char ServiceWorkerConsts::kServiceWorkerRegisterBadMIMEError[] =
+    "Service Worker Register failed: The script has an unsupported MIME type "
+    "('%s').";
+
+const char ServiceWorkerConsts::kServiceWorkerRegisterNoMIMEError[] =
+    "Service Worker Register failed: The script does not have a MIME type.";
+
+const char
+    ServiceWorkerConsts::kServiceWorkerRegisterScriptOriginNotSameError[] =
+        "Service Worker Register failed: Script URL ('%s') and referrer ('%s') "
+        "origin are not the same.";
+
+const char
+    ServiceWorkerConsts::kServiceWorkerRegisterScopeOriginNotSameError[] =
+        "Service Worker Register failed: Scope URL ('%s') and referrer ('%s') "
+        "origin are not the same.";
+
+const char ServiceWorkerConsts::kServiceWorkerRegisterBadScopeError[] =
+    "Service Worker Register failed: Scope ('%s') is not allowed.";
+
+const char
+    ServiceWorkerConsts::kServiceWorkerUnregisterScopeOriginNotSameError[] =
+        "Service Worker Unregister failed: Scope origin does not match.";
+
+const char ServiceWorkerConsts::kServiceWorkerAllowed[] =
+    "Service-Worker-Allowed";
+
+const char ServiceWorkerConsts::kSettingsJson[] =
+    "service_worker_settings.json";
+
+const char* const ServiceWorkerConsts::kJavaScriptMimeTypes[16] = {
+    "application/ecmascript",
+    "application/javascript",
+    "application/x-ecmascript",
+    "application/x-javascript",
+    "text/ecmascript",
+    "text/javascript",
+    "text/javascript1.0",
+    "text/javascript1.1",
+    "text/javascript1.2",
+    "text/javascript1.3",
+    "text/javascript1.4",
+    "text/javascript1.5",
+    "text/jscript",
+    "text/livescript",
+    "text/x-ecmascript",
+    "text/x-javascript"};
+}  // namespace worker
+}  // namespace cobalt
diff --git a/cobalt/worker/service_worker_consts.h b/cobalt/worker/service_worker_consts.h
new file mode 100644
index 0000000..5bb8dd1
--- /dev/null
+++ b/cobalt/worker/service_worker_consts.h
@@ -0,0 +1,42 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COBALT_WORKER_SERVICE_WORKER_CONSTS_H_
+#define COBALT_WORKER_SERVICE_WORKER_CONSTS_H_
+
+namespace cobalt {
+namespace worker {
+
+struct ServiceWorkerConsts {
+  // Constants for error messages.
+  static const char kServiceWorkerRegisterBadMIMEError[];
+  static const char kServiceWorkerRegisterNoMIMEError[];
+  static const char kServiceWorkerRegisterScriptOriginNotSameError[];
+  static const char kServiceWorkerRegisterScopeOriginNotSameError[];
+  static const char kServiceWorkerRegisterBadScopeError[];
+  static const char kServiceWorkerUnregisterScopeOriginNotSameError[];
+  static const char kServiceWorkerAllowed[];
+
+  // Constants for ServiceWorkerPersistentSettings
+  static const char kSettingsJson[];
+
+  // Array of JavaScript mime types, according to the MIME Sniffinc spec:
+  //   https://mimesniff.spec.whatwg.org/#javascript-mime-type
+  static const char* const kJavaScriptMimeTypes[16];
+};
+
+}  // namespace worker
+}  // namespace cobalt
+
+#endif  // COBALT_WORKER_SERVICE_WORKER_CONSTS_H_
diff --git a/cobalt/worker/service_worker_container.cc b/cobalt/worker/service_worker_container.cc
index 05b953e..9350212 100644
--- a/cobalt/worker/service_worker_container.cc
+++ b/cobalt/worker/service_worker_container.cc
@@ -69,8 +69,9 @@
                          ->script_value_factory()
                          ->CreateInterfacePromise<
                              scoped_refptr<ServiceWorkerRegistration>>();
-    promise_reference_.reset(
-        new script::ValuePromiseWrappable::Reference(this, ready_promise_));
+    promise_reference_.reset(new script::ValuePromiseWrappable::Reference(
+        environment_settings()->context()->GetWindowOrWorkerGlobalScope(),
+        ready_promise_));
   }
   // 2. Let readyPromise be this's ready promise.
   script::HandlePromiseWrappable ready_promise(ready_promise_);
@@ -132,7 +133,9 @@
           ->script_value_factory()
           ->CreateInterfacePromise<scoped_refptr<ServiceWorkerRegistration>>();
   std::unique_ptr<script::ValuePromiseWrappable::Reference> promise_reference(
-      new script::ValuePromiseWrappable::Reference(this, promise));
+      new script::ValuePromiseWrappable::Reference(
+          environment_settings()->context()->GetWindowOrWorkerGlobalScope(),
+          promise));
 
   // 2. Let client be this's service worker client.
   web::Context* client = environment_settings()->context();
@@ -170,14 +173,15 @@
   // Perform the rest of the steps in a task, because the promise has to be
   // returned before we can safely reject or resolve it.
   auto promise =
-      base::polymorphic_downcast<web::EnvironmentSettings*>(
-          environment_settings())
+      environment_settings()
           ->context()
           ->global_environment()
           ->script_value_factory()
           ->CreateInterfacePromise<scoped_refptr<ServiceWorkerRegistration>>();
   std::unique_ptr<script::ValuePromiseWrappable::Reference> promise_reference(
-      new script::ValuePromiseWrappable::Reference(this, promise));
+      new script::ValuePromiseWrappable::Reference(
+          environment_settings()->context()->GetWindowOrWorkerGlobalScope(),
+          promise));
   base::MessageLoop::current()->task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&ServiceWorkerContainer::GetRegistrationTask,
                                 base::Unretained(this), url,
@@ -248,8 +252,9 @@
                      ->script_value_factory()
                      ->CreateBasicPromise<script::SequenceWrappable>();
   std::unique_ptr<script::ValuePromiseSequenceWrappable::Reference>
-      promise_reference(
-          new script::ValuePromiseSequenceWrappable::Reference(this, promise));
+      promise_reference(new script::ValuePromiseSequenceWrappable::Reference(
+          environment_settings()->context()->GetWindowOrWorkerGlobalScope(),
+          promise));
   base::MessageLoop::current()->task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(&ServiceWorkerContainer::GetRegistrationsTask,
diff --git a/cobalt/worker/service_worker_global_scope.cc b/cobalt/worker/service_worker_global_scope.cc
index c357b15..1598b81 100644
--- a/cobalt/worker/service_worker_global_scope.cc
+++ b/cobalt/worker/service_worker_global_scope.cc
@@ -34,9 +34,12 @@
 
 namespace cobalt {
 namespace worker {
+
 ServiceWorkerGlobalScope::ServiceWorkerGlobalScope(
-    script::EnvironmentSettings* settings, ServiceWorkerObject* service_worker)
-    : WorkerGlobalScope(settings),
+    script::EnvironmentSettings* settings,
+    const web::WindowOrWorkerGlobalScope::Options& options,
+    ServiceWorkerObject* service_worker)
+    : WorkerGlobalScope(settings, options),
       clients_(new Clients(settings)),
       service_worker_object_(base::AsWeakPtr(service_worker)) {
   loader::FetchInterceptorCoordinator::GetInstance()->Add(this);
@@ -119,6 +122,7 @@
             //     last update check time to the current time.
             // 11. If response’s unsafe response is a bad import script
             //     response, then return a network error.
+            // This is checked in WorkerGlobalScope.
 
             // 12. Set map[url] to response.
             service_worker->SetScriptResource(url, content);
@@ -190,25 +194,47 @@
 }
 
 void ServiceWorkerGlobalScope::StartFetch(
-    const GURL& url,
+    const GURL& url, bool main_resource,
+    const net::HttpRequestHeaders& request_headers,
+    scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner,
     base::OnceCallback<void(std::unique_ptr<std::string>)> callback,
     base::OnceCallback<void(const net::LoadTimingInfo&)>
         report_load_timing_info,
     base::OnceClosure fallback) {
+  // Only HTTP or HTTPS fetches should be intercepted.
+  // https://fetch.spec.whatwg.org/commit-snapshots/8f8ab504da6ca9681db5c7f8aa3d1f4b6bf8840c/#http-fetch
+  if (!url.SchemeIsHTTPOrHTTPS()) {
+    std::move(fallback).Run();
+    return;
+  }
   if (base::MessageLoop::current() !=
       environment_settings()->context()->message_loop()) {
     environment_settings()->context()->message_loop()->task_runner()->PostTask(
         FROM_HERE,
         base::BindOnce(&ServiceWorkerGlobalScope::StartFetch,
-                       base::Unretained(this), url, std::move(callback),
-                       std::move(report_load_timing_info),
+                       base::Unretained(this), url, main_resource,
+                       request_headers, callback_task_runner,
+                       std::move(callback), std::move(report_load_timing_info),
                        std::move(fallback)));
     return;
   }
   if (!service_worker()) {
-    std::move(fallback).Run();
+    callback_task_runner->PostTask(FROM_HERE, std::move(fallback));
     return;
   }
+
+  auto* registration =
+      service_worker_object_->containing_service_worker_registration();
+  if (registration && (main_resource || registration->stale())) {
+    worker::ServiceWorkerJobs* jobs =
+        environment_settings()->context()->service_worker_jobs();
+    jobs->message_loop()->task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ServiceWorkerJobs::SoftUpdate, base::Unretained(jobs),
+                       base::Unretained(registration),
+                       /*force_bypass_cache=*/false));
+  }
+
   // TODO: handle the following steps in
   //       https://www.w3.org/TR/2022/CRD-service-workers-20220712/#handle-fetch.
   // 22. If activeWorker’s state is "activating", wait for activeWorker’s state
@@ -219,9 +245,22 @@
   auto* global_environment = get_global_environment(environment_settings());
   auto* isolate = global_environment->isolate();
   script::v8c::EntryScope entry_scope(isolate);
-  auto request = web::cache_utils::CreateRequest(isolate, url.spec());
+  base::DictionaryValue options;
+  if (main_resource) {
+    options.SetKey("mode", base::Value("navigate"));
+  }
+  base::ListValue headers;
+  for (auto header_pair : request_headers.GetHeaderVector()) {
+    base::ListValue header;
+    header.GetList().emplace_back(header_pair.key);
+    header.GetList().emplace_back(header_pair.value);
+    headers.GetList().push_back(std::move(header));
+  }
+  options.SetKey("headers", std::move(headers));
+  auto request =
+      web::cache_utils::CreateRequest(isolate, url.spec(), std::move(options));
   if (!request) {
-    std::move(fallback).Run();
+    callback_task_runner->PostTask(FROM_HERE, std::move(fallback));
     return;
   }
 
@@ -230,12 +269,13 @@
       web::cache_utils::FromV8Value(isolate, request.value()).GetScriptValue());
   scoped_refptr<FetchEvent> fetch_event =
       new FetchEvent(environment_settings(), base::Tokens::fetch(), event_init,
-                     std::move(callback), std::move(report_load_timing_info));
+                     callback_task_runner, std::move(callback),
+                     std::move(report_load_timing_info));
   // 24. Create and dispatch event.
   DispatchEvent(fetch_event);
   // TODO: implement steps 25 and 26.
   if (!fetch_event->respond_with_called()) {
-    std::move(fallback).Run();
+    callback_task_runner->PostTask(FROM_HERE, std::move(fallback));
   }
 }
 
diff --git a/cobalt/worker/service_worker_global_scope.h b/cobalt/worker/service_worker_global_scope.h
index 3e06e53..a32ba66 100644
--- a/cobalt/worker/service_worker_global_scope.h
+++ b/cobalt/worker/service_worker_global_scope.h
@@ -38,6 +38,7 @@
 #include "cobalt/worker/service_worker_registration.h"
 #include "cobalt/worker/worker_global_scope.h"
 #include "net/base/load_timing_info.h"
+#include "net/http/http_request_headers.h"
 
 namespace cobalt {
 namespace worker {
@@ -48,8 +49,10 @@
 class ServiceWorkerGlobalScope : public WorkerGlobalScope,
                                  public loader::FetchInterceptor {
  public:
-  explicit ServiceWorkerGlobalScope(script::EnvironmentSettings* settings,
-                                    ServiceWorkerObject* service_worker);
+  explicit ServiceWorkerGlobalScope(
+      script::EnvironmentSettings* settings,
+      const web::WindowOrWorkerGlobalScope::Options& options,
+      ServiceWorkerObject* service_worker);
   ServiceWorkerGlobalScope(const ServiceWorkerGlobalScope&) = delete;
   ServiceWorkerGlobalScope& operator=(const ServiceWorkerGlobalScope&) = delete;
 
@@ -72,7 +75,9 @@
   script::HandlePromiseVoid SkipWaiting();
 
   void StartFetch(
-      const GURL& url,
+      const GURL& url, bool main_resource,
+      const net::HttpRequestHeaders& request_headers,
+      scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner,
       base::OnceCallback<void(std::unique_ptr<std::string>)> callback,
       base::OnceCallback<void(const net::LoadTimingInfo&)>
           report_load_timing_info,
diff --git a/cobalt/worker/service_worker_jobs.cc b/cobalt/worker/service_worker_jobs.cc
index 98f0488..6b332d0 100644
--- a/cobalt/worker/service_worker_jobs.cc
+++ b/cobalt/worker/service_worker_jobs.cc
@@ -28,6 +28,7 @@
 #include "base/message_loop/message_loop_current.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/synchronization/lock.h"
 #include "base/task_runner.h"
 #include "base/time/time.h"
@@ -51,6 +52,7 @@
 #include "cobalt/worker/extendable_message_event.h"
 #include "cobalt/worker/frame_type.h"
 #include "cobalt/worker/service_worker.h"
+#include "cobalt/worker/service_worker_consts.h"
 #include "cobalt/worker/service_worker_container.h"
 #include "cobalt/worker/service_worker_global_scope.h"
 #include "cobalt/worker/service_worker_registration.h"
@@ -60,7 +62,7 @@
 #include "cobalt/worker/worker_type.h"
 #include "net/base/mime_util.h"
 #include "net/base/url_util.h"
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -69,6 +71,13 @@
 namespace worker {
 
 namespace {
+
+const base::TimeDelta kWaitForAsynchronousExtensionsTimeout =
+    base::TimeDelta::FromSeconds(3);
+
+const base::TimeDelta kShutdownWaitTimeoutSecs =
+    base::TimeDelta::FromSeconds(5);
+
 bool PathContainsEscapedSlash(const GURL& url) {
   const std::string path = url.path();
   return (path.find("%2f") != std::string::npos ||
@@ -129,7 +138,8 @@
 ServiceWorkerJobs::ServiceWorkerJobs(web::WebSettings* web_settings,
                                      network::NetworkModule* network_module,
                                      web::UserAgentPlatformInfo* platform_info,
-                                     base::MessageLoop* message_loop)
+                                     base::MessageLoop* message_loop,
+                                     const GURL& url)
     : message_loop_(message_loop) {
   DCHECK_EQ(message_loop_, base::MessageLoop::current());
   fetcher_factory_.reset(new loader::FetcherFactory(network_module));
@@ -140,7 +150,7 @@
   DCHECK(script_loader_factory_);
 
   ServiceWorkerPersistentSettings::Options options(web_settings, network_module,
-                                                   platform_info, this);
+                                                   platform_info, this, url);
   scope_to_registration_map_.reset(new ServiceWorkerRegistrationMap(options));
   DCHECK(scope_to_registration_map_);
 }
@@ -150,15 +160,21 @@
   scope_to_registration_map_->HandleUserAgentShutdown(this);
   scope_to_registration_map_->AbortAllActive();
   scope_to_registration_map_.reset();
-  while (!web_context_registrations_.empty()) {
-    // Wait for web context registrations to be cleared.
-    web_context_registrations_cleared_.Wait();
-  }
-}
+  if (!web_context_registrations_.empty()) {
+    // Abort any Service Workers that remain.
+    for (auto& context : web_context_registrations_) {
+      DCHECK(context);
+      if (context->GetWindowOrWorkerGlobalScope()->IsServiceWorker()) {
+        ServiceWorkerGlobalScope* service_worker =
+            context->GetWindowOrWorkerGlobalScope()->AsServiceWorker();
+        if (service_worker && service_worker->service_worker_object()) {
+          service_worker->service_worker_object()->Abort();
+        }
+      }
+    }
 
-void ServiceWorkerJobs::Stop() {
-  if (!done_event_.IsSignaled()) {
-    done_event_.Signal();
+    // Wait for web context registrations to be cleared.
+    web_context_registrations_cleared_.TimedWait(kShutdownWaitTimeoutSecs);
   }
 }
 
@@ -495,10 +511,13 @@
   if (!job_script_origin.IsSameOriginWith(job_referrer_origin)) {
     // 2.1. Invoke Reject Job Promise with job and "SecurityError" DOMException.
     RejectJobPromise(
-        job, PromiseErrorData(
-                 web::DOMException::kSecurityErr,
-                 "Service Worker Register failed: Script URL and referrer "
-                 "origin are not the same."));
+        job,
+        PromiseErrorData(
+            web::DOMException::kSecurityErr,
+            base::StringPrintf(
+                ServiceWorkerConsts::
+                    kServiceWorkerRegisterScriptOriginNotSameError,
+                job->script_url.spec().c_str(), job->referrer.spec().c_str())));
     // 2.2. Invoke Finish Job with job and abort these steps.
     FinishJob(job);
     return;
@@ -510,10 +529,13 @@
   if (!job_scope_origin.IsSameOriginWith(job_referrer_origin)) {
     // 3.1. Invoke Reject Job Promise with job and "SecurityError" DOMException.
     RejectJobPromise(
-        job, PromiseErrorData(
-                 web::DOMException::kSecurityErr,
-                 "Service Worker Register failed: Scope URL and referrer "
-                 "origin are not the same."));
+        job,
+        PromiseErrorData(
+            web::DOMException::kSecurityErr,
+            base::StringPrintf(
+                ServiceWorkerConsts::
+                    kServiceWorkerRegisterScopeOriginNotSameError,
+                job->scope_url.spec().c_str(), job->referrer.spec().c_str())));
 
     // 3.2. Invoke Finish Job with job and abort these steps.
     FinishJob(job);
@@ -559,8 +581,7 @@
 }
 
 void ServiceWorkerJobs::SoftUpdate(
-    scoped_refptr<ServiceWorkerRegistrationObject> registration,
-    bool force_bypass_cache) {
+    ServiceWorkerRegistrationObject* registration, bool force_bypass_cache) {
   TRACE_EVENT0("cobalt::worker", "ServiceWorkerJobs::SoftUpdate()");
   DCHECK_EQ(message_loop(), base::MessageLoop::current());
   DCHECK(registration);
@@ -578,9 +599,9 @@
   // 3. Let job be the result of running Create Job with update, registration’s
   // storage key, registration’s scope url, newestWorker’s script url, null, and
   // null.
-  std::unique_ptr<Job> job =
-      CreateJob(kUpdate, registration->storage_key(), registration->scope_url(),
-                newest_worker->script_url());
+  std::unique_ptr<Job> job = CreateJobWithoutPromise(
+      kUpdate, registration->storage_key(), registration->scope_url(),
+      newest_worker->script_url());
 
   // 4. Set job’s worker type to newestWorker’s type.
   // Cobalt only supports 'classic' worker type.
@@ -595,6 +616,33 @@
   DCHECK(!job.get());
 }
 
+void ServiceWorkerJobs::EnsureServiceWorkerStarted(
+    const url::Origin& storage_key, const GURL& client_url,
+    base::WaitableEvent* done_event) {
+  if (message_loop() != base::MessageLoop::current()) {
+    message_loop()->task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ServiceWorkerJobs::EnsureServiceWorkerStarted,
+                       base::Unretained(this), storage_key, client_url,
+                       done_event));
+    return;
+  }
+  base::ScopedClosureRunner signal_done(base::BindOnce(
+      [](base::WaitableEvent* done_event) { done_event->Signal(); },
+      done_event));
+  base::TimeTicks start = base::TimeTicks::Now();
+  auto registration =
+      scope_to_registration_map_->GetRegistration(storage_key, client_url);
+  if (!registration) {
+    return;
+  }
+  auto service_worker_object = registration->active_worker();
+  if (!service_worker_object || service_worker_object->is_running()) {
+    return;
+  }
+  service_worker_object->ObtainWebAgentAndWaitUntilDone();
+}
+
 void ServiceWorkerJobs::Update(Job* job) {
   TRACE_EVENT0("cobalt::worker", "ServiceWorkerJobs::Update()");
   DCHECK_EQ(message_loop(), base::MessageLoop::current());
@@ -685,28 +733,6 @@
       /*skip_fetch_intercept=*/true);
 }
 
-namespace {
-// Array of JavaScript mime types, according to the MIME Sniffinc spec:
-//   https://mimesniff.spec.whatwg.org/#javascript-mime-type
-static const char* const kJavaScriptMimeTypes[] = {"application/ecmascript",
-                                                   "application/javascript",
-                                                   "application/x-ecmascript",
-                                                   "application/x-javascript",
-                                                   "text/ecmascript",
-                                                   "text/javascript",
-                                                   "text/javascript1.0",
-                                                   "text/javascript1.1",
-                                                   "text/javascript1.2",
-                                                   "text/javascript1.3",
-                                                   "text/javascript1.4",
-                                                   "text/javascript1.5",
-                                                   "text/jscript",
-                                                   "text/livescript",
-                                                   "text/x-ecmascript",
-                                                   "text/x-javascript"};
-
-}  // namespace
-
 bool ServiceWorkerJobs::UpdateOnResponseStarted(
     scoped_refptr<UpdateJobState> state, loader::Fetcher* fetcher,
     const scoped_refptr<net::HttpResponseHeaders>& headers) {
@@ -715,7 +741,15 @@
   if (headers->GetNormalizedHeader("Content-type", &content_type)) {
     //   8.7.  Extract a MIME type from the response’s header list. If this MIME
     //         type (ignoring parameters) is not a JavaScript MIME type, then:
-    for (auto mime_type : kJavaScriptMimeTypes) {
+    if (content_type.empty()) {
+      RejectJobPromise(
+          state->job,
+          PromiseErrorData(
+              web::DOMException::kSecurityErr,
+              ServiceWorkerConsts::kServiceWorkerRegisterNoMIMEError));
+      return true;
+    }
+    for (auto mime_type : ServiceWorkerConsts::kJavaScriptMimeTypes) {
       if (net::MatchesMimeType(mime_type, content_type)) {
         mime_type_is_javascript = true;
         break;
@@ -726,17 +760,20 @@
     //   8.7.1. Invoke Reject Job Promise with job and "SecurityError"
     //          DOMException.
     //   8.7.2. Asynchronously complete these steps with a network error.
-    RejectJobPromise(state->job,
-                     PromiseErrorData(web::DOMException::kSecurityErr,
-                                      "Service Worker Script is not "
-                                      "JavaScript MIME type."));
+    RejectJobPromise(
+        state->job,
+        PromiseErrorData(
+            web::DOMException::kSecurityErr,
+            base::StringPrintf(
+                ServiceWorkerConsts::kServiceWorkerRegisterBadMIMEError,
+                content_type.c_str())));
     return true;
   }
   //   8.8.  Let serviceWorkerAllowed be the result of extracting header list
   //         values given `Service-Worker-Allowed` and response’s header list.
   std::string service_worker_allowed;
   bool service_worker_allowed_exists = headers->GetNormalizedHeader(
-      "Service-Worker-Allowed", &service_worker_allowed);
+      ServiceWorkerConsts::kServiceWorkerAllowed, &service_worker_allowed);
   //   8.9.  Set policyContainer to the result of creating a policy container
   //         from a fetch response given response.
   state->script_headers = headers;
@@ -778,9 +815,13 @@
     //   8.16.1. Invoke Reject Job Promise with job and "SecurityError"
     //           DOMException.
     //   8.16.2. Asynchronously complete these steps with a network error.
-    RejectJobPromise(state->job,
-                     PromiseErrorData(web::DOMException::kSecurityErr,
-                                      "Scope not allowed."));
+    RejectJobPromise(
+        state->job,
+        PromiseErrorData(
+            web::DOMException::kSecurityErr,
+            base::StringPrintf(
+                ServiceWorkerConsts::kServiceWorkerRegisterBadScopeError,
+                scope_string.c_str())));
     return true;
   }
   return true;
@@ -806,6 +847,14 @@
   DCHECK(updated_script_content);
   //   8.19. If response’s cache state is not "local", set registration’s last
   //         update check time to the current time.
+  scoped_refptr<ServiceWorkerRegistrationObject> registration =
+      scope_to_registration_map_->GetRegistration(state->job->storage_key,
+                                                  state->job->scope_url);
+  if (registration) {
+    registration->set_last_update_check_time(base::Time::Now());
+    scope_to_registration_map_->PersistRegistration(registration->storage_key(),
+                                                    registration->scope_url());
+  }
   // TODO(b/228904017):
   //   8.20. Set hasUpdatedResources to true if any of the following are true:
   //          - newestWorker is null.
@@ -840,7 +889,12 @@
   TRACE_EVENT0("cobalt::worker",
                "ServiceWorkerJobs::UpdateOnLoadingComplete()");
   DCHECK_EQ(message_loop(), base::MessageLoop::current());
-  if (!state->job->promise.get() || !state->job->client) {
+  bool check_promise = !state->job->no_promise_okay;
+  if (state->job->no_promise_okay && !state->job->client &&
+      web_context_registrations_.size() > 0) {
+    state->job->client = *(web_context_registrations_.begin());
+  }
+  if ((check_promise && !state->job->promise.get()) || !state->job->client) {
     // The job is already rejected, which means there was an error, or the
     // client is already shutdown, so finish the job and skip the remaining
     // steps.
@@ -851,7 +905,7 @@
   if (error) {
     RejectJobPromise(
         state->job,
-        PromiseErrorData(web::DOMException::kNetworkErr, error.value()));
+        PromiseErrorData(web::DOMException::kSecurityErr, error.value()));
     if (state->newest_worker == nullptr) {
       scope_to_registration_map_->RemoveRegistration(state->job->storage_key,
                                                      state->job->scope_url);
@@ -1012,6 +1066,23 @@
   return worker->start_status();
 }
 
+bool ServiceWorkerJobs::WaitForAsynchronousExtensions(
+    const scoped_refptr<ServiceWorkerRegistrationObject>& registration) {
+  // TODO(b/240164388): Investigate a better approach for combining waiting
+  // for the ExtendableEvent while also allowing use of algorithms that run
+  // on the same thread from the event handler.
+  base::TimeTicks wait_start_time = base::TimeTicks::Now();
+  do {
+    if (registration->done_event()->TimedWait(
+            base::TimeDelta::FromMilliseconds(100)))
+      break;
+    base::MessageLoopCurrent::ScopedNestableTaskAllower allow;
+    base::RunLoop().RunUntilIdle();
+  } while ((base::TimeTicks::Now() - wait_start_time) <
+           kWaitForAsynchronousExtensionsTimeout);
+  return registration->done_event()->IsSignaled();
+}
+
 void ServiceWorkerJobs::Install(
     Job* job, const scoped_refptr<ServiceWorkerObject>& worker,
     const scoped_refptr<ServiceWorkerRegistrationObject>& registration) {
@@ -1044,7 +1115,7 @@
   UpdateWorkerState(registration->installing_worker(),
                     kServiceWorkerStateInstalling);
   // 6. Assert: job’s job promise is not null.
-  DCHECK(job->promise.get() != nullptr);
+  DCHECK(job->no_promise_okay || job->promise.get() != nullptr);
   // 7. Invoke Resolve Job Promise with job and registration.
   ResolveJobPromise(job, registration);
   // 8. Let settingsObjects be all environment settings objects whose origin is
@@ -1105,8 +1176,8 @@
     } else {
       // 11.3.1. Queue a task task on installingWorker’s event loop using the
       //         DOM manipulation task source to run the following steps:
-      DCHECK(done_event_.IsSignaled());
-      done_event_.Reset();
+      DCHECK(registration->done_event()->IsSignaled());
+      registration->done_event()->Reset();
       installing_worker->web_agent()
           ->context()
           ->message_loop()
@@ -1140,8 +1211,12 @@
                           done_event->Signal();
                         },
                         done_event, install_failed);
-                    scoped_refptr<ExtendableEvent> event(new ExtendableEvent(
-                        base::Tokens::install(), std::move(done_callback)));
+                    auto* settings = installing_worker->web_agent()
+                                         ->context()
+                                         ->environment_settings();
+                    scoped_refptr<ExtendableEvent> event(
+                        new ExtendableEvent(settings, base::Tokens::install(),
+                                            std::move(done_callback)));
                     installing_worker->worker_global_scope()->DispatchEvent(
                         event);
                     if (!event->IsActive()) {
@@ -1151,18 +1226,15 @@
                       done_event->Signal();
                     }
                   },
-                  base::Unretained(installing_worker), &done_event_,
-                  install_failed));
+                  base::Unretained(installing_worker),
+                  registration->done_event(), install_failed));
       // 11.3.2. Wait for task to have executed or been discarded.
       // This waiting is done inside PostBlockingTask above.
       // 11.3.3. Wait for the step labeled WaitForAsynchronousExtensions to
       //         complete.
-      // TODO(b/240164388): Investigate a better approach for combining waiting
-      // for the ExtendableEvent while also allowing use of algorithms that run
-      // on the same thread from the event handler.
-      while (!done_event_.TimedWait(base::TimeDelta::FromMilliseconds(100))) {
-        base::MessageLoopCurrent::ScopedNestableTaskAllower allow;
-        base::RunLoop().RunUntilIdle();
+      if (!WaitForAsynchronousExtensions(registration)) {
+        // Timeout
+        install_failed->store(true);
       }
     }
   }
@@ -1395,8 +1467,8 @@
                 active_worker->worker_global_scope()
                     ->environment_settings()
                     ->context());
-      DCHECK(done_event_.IsSignaled());
-      done_event_.Reset();
+      DCHECK(registration->done_event()->IsSignaled());
+      registration->done_event()->Reset();
       active_worker->web_agent()
           ->context()
           ->message_loop()
@@ -1410,8 +1482,12 @@
                         base::BindOnce([](base::WaitableEvent* done_event,
                                           bool) { done_event->Signal(); },
                                        done_event);
-                    scoped_refptr<ExtendableEvent> event(new ExtendableEvent(
-                        base::Tokens::activate(), std::move(done_callback)));
+                    auto* settings = active_worker->web_agent()
+                                         ->context()
+                                         ->environment_settings();
+                    scoped_refptr<ExtendableEvent> event(
+                        new ExtendableEvent(settings, base::Tokens::activate(),
+                                            std::move(done_callback)));
                     // 11.1.1.1. Let e be the result of creating an event with
                     //           ExtendableEvent.
                     // 11.1.1.2. Initialize e’s type attribute to activate.
@@ -1426,7 +1502,7 @@
                       done_event->Signal();
                     }
                   },
-                  base::Unretained(active_worker), &done_event_));
+                  base::Unretained(active_worker), registration->done_event()));
       // 11.1.2. Wait for task to have executed or been discarded.
       // This waiting is done inside PostBlockingTask above.
       // 11.1.3. Wait for the step labeled WaitForAsynchronousExtensions to
@@ -1434,10 +1510,12 @@
       // TODO(b/240164388): Investigate a better approach for combining waiting
       // for the ExtendableEvent while also allowing use of algorithms that run
       // on the same thread from the event handler.
-      while (!done_event_.TimedWait(base::TimeDelta::FromMilliseconds(100))) {
-        base::MessageLoopCurrent::ScopedNestableTaskAllower allow;
-        base::RunLoop().RunUntilIdle();
+      if (!WaitForAsynchronousExtensions(registration)) {
+        // Timeout
+        activated = false;
       }
+    } else {
+      activated = false;
     }
   }
   // 12. Run the Update Worker State algorithm passing registration’s active
@@ -1765,7 +1843,8 @@
 
   // 5. If registration is unregistered, invoke Try Clear Registration with
   //    registration.
-  if (scope_to_registration_map_->IsUnregistered(registration)) {
+  if (scope_to_registration_map_ &&
+      scope_to_registration_map_->IsUnregistered(registration)) {
     TryClearRegistration(registration);
   }
 
@@ -1843,9 +1922,9 @@
     // 1.1. Invoke Reject Job Promise with job and "SecurityError" DOMException.
     RejectJobPromise(
         job,
-        PromiseErrorData(
-            web::DOMException::kSecurityErr,
-            "Service Worker Unregister failed: Scope origin does not match."));
+        PromiseErrorData(web::DOMException::kSecurityErr,
+                         ServiceWorkerConsts::
+                             kServiceWorkerUnregisterScopeOriginNotSameError));
 
     // 1.2. Invoke Finish Job with job and abort these steps.
     FinishJob(job);
@@ -2702,6 +2781,7 @@
 
                       event_target->DispatchEvent(
                           new worker::ExtendableMessageEvent(
+                              event_target->environment_settings(),
                               base::Tokens::message(), init_dict,
                               std::move(serialize_result)));
                     },
diff --git a/cobalt/worker/service_worker_jobs.h b/cobalt/worker/service_worker_jobs.h
index 12181f5..50c4cdf 100644
--- a/cobalt/worker/service_worker_jobs.h
+++ b/cobalt/worker/service_worker_jobs.h
@@ -42,13 +42,14 @@
 #include "cobalt/worker/client_query_options.h"
 #include "cobalt/worker/frame_type.h"
 #include "cobalt/worker/service_worker.h"
+#include "cobalt/worker/service_worker_consts.h"
 #include "cobalt/worker/service_worker_object.h"
 #include "cobalt/worker/service_worker_registration.h"
 #include "cobalt/worker/service_worker_registration_map.h"
 #include "cobalt/worker/service_worker_registration_object.h"
 #include "cobalt/worker/service_worker_update_via_cache.h"
 #include "cobalt/worker/worker_type.h"
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -129,6 +130,7 @@
     JobQueue* containing_job_queue = nullptr;
     std::deque<std::unique_ptr<Job>> equivalent_jobs;
     bool force_bypass_cache_flag = false;
+    bool no_promise_okay = false;
 
     // Custom, not in the spec.
     //
@@ -190,11 +192,9 @@
   ServiceWorkerJobs(web::WebSettings* web_settings,
                     network::NetworkModule* network_module,
                     web::UserAgentPlatformInfo* platform_info,
-                    base::MessageLoop* message_loop);
+                    base::MessageLoop* message_loop, const GURL& url);
   ~ServiceWorkerJobs();
 
-  void Stop();
-
   base::MessageLoop* message_loop() { return message_loop_; }
 
   // https://www.w3.org/TR/2022/CRD-service-workers-20220712/#start-register-algorithm
@@ -301,6 +301,15 @@
     return CreateJob(type, storage_key, scope_url, script_url,
                      JobPromiseType::Create(std::move(promise)), client);
   }
+  std::unique_ptr<Job> CreateJobWithoutPromise(JobType type,
+                                               const url::Origin& storage_key,
+                                               const GURL& scope_url,
+                                               const GURL& script_url) {
+    auto job = CreateJob(type, storage_key, scope_url, script_url,
+                         std::unique_ptr<JobPromiseType>(), /*client=*/nullptr);
+    job->no_promise_okay = true;
+    return job;
+  }
   std::unique_ptr<Job> CreateJob(
       JobType type, const url::Origin& storage_key, const GURL& scope_url,
       const GURL& script_url, std::unique_ptr<JobPromiseType> promise = nullptr,
@@ -315,12 +324,13 @@
   // https://www.w3.org/TR/2022/CRD-service-workers-20220712/#clear-registration-algorithm
   void ClearRegistration(ServiceWorkerRegistrationObject* registration);
 
-  // https://www.w3.org/TR/2022/CRD-service-workers-20220712/#update-state-algorithm
-  void UpdateWorkerState(ServiceWorkerObject* worker, ServiceWorkerState state);
-
   // https://www.w3.org/TR/2022/CRD-service-workers-20220712/#soft-update
-  void SoftUpdate(scoped_refptr<ServiceWorkerRegistrationObject> registration,
-                  bool force_bypass_cache = false);
+  void SoftUpdate(ServiceWorkerRegistrationObject* registration,
+                  bool force_bypass_cache);
+
+  void EnsureServiceWorkerStarted(const url::Origin& storage_key,
+                                  const GURL& client_url,
+                                  base::WaitableEvent* done_event);
 
  private:
   // State used for the 'Update' algorithm.
@@ -400,7 +410,6 @@
                                 scoped_refptr<ServiceWorkerObject> worker,
                                 bool run_result);
 
-
   // https://www.w3.org/TR/2022/CRD-service-workers-20220712/#unregister-algorithm
   void Unregister(Job* job);
 
@@ -443,6 +452,9 @@
       ServiceWorkerRegistrationObject* registration, RegistrationState target,
       const scoped_refptr<ServiceWorkerObject>& source);
 
+  // https://www.w3.org/TR/2022/CRD-service-workers-20220712/#update-state-algorithm
+  void UpdateWorkerState(ServiceWorkerObject* worker, ServiceWorkerState state);
+
   // https://www.w3.org/TR/2022/CRD-service-workers-20220712/#on-client-unload-algorithm
   void HandleServiceWorkerClientUnload(web::Context* client);
 
@@ -458,6 +470,10 @@
   bool IsAnyClientUsingRegistration(
       ServiceWorkerRegistrationObject* registration);
 
+  // Returns false when the timeout is reached.
+  bool WaitForAsynchronousExtensions(
+      const scoped_refptr<ServiceWorkerRegistrationObject>& registration);
+
   // FetcherFactory that is used to create a fetcher according to URL.
   std::unique_ptr<loader::FetcherFactory> fetcher_factory_;
   // LoaderFactory that is used to acquire references to resources from a URL.
@@ -472,10 +488,6 @@
   base::WaitableEvent web_context_registrations_cleared_ = {
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED};
-
-  base::WaitableEvent done_event_ = {
-      base::WaitableEvent::ResetPolicy::MANUAL,
-      base::WaitableEvent::InitialState::SIGNALED};
 };
 
 }  // namespace worker
diff --git a/cobalt/worker/service_worker_object.cc b/cobalt/worker/service_worker_object.cc
index eb8171c..b16feb6 100644
--- a/cobalt/worker/service_worker_object.cc
+++ b/cobalt/worker/service_worker_object.cc
@@ -97,6 +97,14 @@
   return entry != script_resource_map_.end() ? &entry->second : nullptr;
 }
 
+void ServiceWorkerObject::SetScriptResourceHasEverBeenEvaluated(
+    const GURL& url) {
+  auto entry = script_resource_map_.find(url);
+  if (entry != script_resource_map_.end()) {
+    entry->second.has_ever_been_evaluated = true;
+  }
+}
+
 void ServiceWorkerObject::PurgeScriptResourceMap() {
   // Steps 13-15 of Algorithm for Install:
   //   https://www.w3.org/TR/2022/CRD-service-workers-20220712/#installation-algorithm
@@ -139,9 +147,10 @@
 bool ServiceWorkerObject::ShouldSkipEvent(base::Token event_name) {
   // Algorithm for Should Skip Event:
   //   https://www.w3.org/TR/2022/CRD-service-workers-20220712/#should-skip-event-algorithm
-  // TODO(b/229622132): Implementing this algorithm will improve performance.
-  NOTIMPLEMENTED();
-  return false;
+  // 1. If serviceWorker’s set of event types to handle does not contain
+  // eventName, then the user agent may return true.
+  return (set_of_event_types_to_handle_.find(event_name) ==
+          set_of_event_types_to_handle_.end());
 }
 
 void ServiceWorkerObject::Initialize(web::Context* context) {
@@ -180,10 +189,11 @@
   //      origin to an implementation-defined value, target browsing context to
   //      null, and active service worker to null.
 
-  web_context_->setup_environment_settings(worker_settings);
+  web_context_->SetupEnvironmentSettings(worker_settings);
   web_context_->environment_settings()->set_creation_url(script_url_);
   scoped_refptr<ServiceWorkerGlobalScope> service_worker_global_scope =
-      new ServiceWorkerGlobalScope(web_context_->environment_settings(), this);
+      new ServiceWorkerGlobalScope(web_context_->environment_settings(),
+                                   options_.global_scope_options, this);
   worker_global_scope_ = service_worker_global_scope;
   web_context_->global_environment()->CreateGlobalObject(
       service_worker_global_scope, web_context_->environment_settings());
@@ -226,6 +236,7 @@
     DLOG(WARNING) << "Warning: No Content Security Header received for the "
                      "service worker.";
   }
+  web_context_->SetupFinished();
   // 8.11. If serviceWorker is an active worker, and there are any tasks queued
   //       in serviceWorker’s containing service worker registration’s task
   //       queues, queue them to serviceWorker’s event loop’s task queues in the
@@ -266,11 +277,32 @@
   // 8.16. Set serviceWorker’s start status to evaluationStatus.
   start_status_.reset(new std::string(retval));
   // 8.17. If script’s has ever been evaluated flag is unset, then:
-  // 8.17.1. For each eventType of settingsObject’s global object's associated
-  //         list of event listeners' event types:
-  // 8.17.1.1. Append eventType to workerGlobalScope’s associated service
-  //           worker's set of event types to handle.
-  // 8.17.1.2. Set script’s has ever been evaluated flag.
+  if (!script_resource->has_ever_been_evaluated) {
+    // 8.17.1. For each eventType of settingsObject’s global object's associated
+    //         list of event listeners' event types:
+    auto event_types =
+        service_worker_global_scope->event_listener_event_types();
+    for (auto& event_type : event_types) {
+      // 8.17.1.1. Append eventType to workerGlobalScope’s associated service
+      //           worker's set of event types to handle.
+      service_worker_global_scope->service_worker_object()
+          ->set_of_event_types_to_handle()
+          .insert(event_type);
+    }
+    // 8.17.2. Set script’s has ever been evaluated flag.
+    SetScriptResourceHasEverBeenEvaluated(script_url_);
+
+    if (event_types.empty()) {
+      // NOTE: If the global object’s associated list of event listeners does
+      // not have any event listener added at this moment, the service worker’s
+      // set of event types to handle remains an empty set. The user agents are
+      // encouraged to show a warning that the event listeners must be added on
+      // the very first evaluation of the worker script.
+      DLOG(WARNING) << "ServiceWorkerGlobalScope's event listeners must be "
+                       "added on the first evaluation of the worker script.";
+    }
+    event_types.clear();
+  }
   // 8.18. Run the responsible event loop specified by settingsObject until it
   //       is destroyed.
   // 8.19. Empty workerGlobalScope’s list of active timers.
diff --git a/cobalt/worker/service_worker_object.h b/cobalt/worker/service_worker_object.h
index 88a9d97..be0ad93 100644
--- a/cobalt/worker/service_worker_object.h
+++ b/cobalt/worker/service_worker_object.h
@@ -30,7 +30,7 @@
 #include "cobalt/web/web_settings.h"
 #include "cobalt/worker/service_worker_state.h"
 #include "cobalt/worker/worker_global_scope.h"
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "url/gurl.h"
 
 #if defined(ENABLE_DEBUGGER)
@@ -70,6 +70,7 @@
 
     std::string name;
     web::Agent::Options web_options;
+    web::WindowOrWorkerGlobalScope::Options global_scope_options;
     ServiceWorkerRegistrationObject* containing_service_worker_registration;
   };
 
@@ -101,7 +102,7 @@
   void AppendToSetOfUsedScripts(const GURL& url) {
     set_of_used_scripts_.insert(url);
   }
-  std::set<GURL> set_of_used_scripts() { return set_of_used_scripts_; }
+  std::set<GURL>& set_of_used_scripts() { return set_of_used_scripts_; }
 
   // https://www.w3.org/TR/2022/CRD-service-workers-20220712/#dfn-script-resource-map
   void set_script_resource_map(ScriptResourceMap&& resource_map) {
@@ -110,6 +111,7 @@
   void SetScriptResource(const GURL& url, std::string* resource);
   bool HasScriptResource() const;
   const ScriptResource* LookupScriptResource(const GURL& url) const;
+  void SetScriptResourceHasEverBeenEvaluated(const GURL& url);
 
   // Steps 13-15 of Algorithm for Install.
   //   https://www.w3.org/TR/2022/CRD-service-workers-20220712/#installation-algorithm
@@ -146,6 +148,10 @@
 
   std::string options_name() { return options_.name; }
 
+  std::set<base::Token>& set_of_event_types_to_handle() {
+    return set_of_event_types_to_handle_;
+  }
+
  private:
   // Called by ObtainWebAgentAndWaitUntilDone to perform initialization required
   // on the dedicated thread.
@@ -193,6 +199,9 @@
   starboard::atomic_bool start_failed_;
 
   scoped_refptr<WorkerGlobalScope> worker_global_scope_;
+
+  // https://www.w3.org/TR/2022/CRD-service-workers-20220712/#dfn-set-of-event-types-to-handle
+  std::set<base::Token> set_of_event_types_to_handle_;
 };
 
 }  // namespace worker
diff --git a/cobalt/worker/service_worker_persistent_settings.cc b/cobalt/worker/service_worker_persistent_settings.cc
index 3befd91..ba47191 100644
--- a/cobalt/worker/service_worker_persistent_settings.cc
+++ b/cobalt/worker/service_worker_persistent_settings.cc
@@ -30,8 +30,7 @@
 #include "cobalt/script/promise.h"
 #include "cobalt/script/script_value.h"
 #include "cobalt/web/cache_utils.h"
-#include "cobalt/web/context.h"
-#include "cobalt/web/environment_settings.h"
+#include "cobalt/worker/service_worker_consts.h"
 #include "cobalt/worker/service_worker_jobs.h"
 #include "cobalt/worker/service_worker_registration_object.h"
 #include "cobalt/worker/service_worker_update_via_cache.h"
@@ -46,7 +45,6 @@
 
 namespace {
 // ServiceWorkerRegistrationMap persistent settings keys.
-const char kSettingsJson[] = "service_worker_settings.json";
 const char kSettingsKeyList[] = "key_list";
 
 // ServiceWorkerRegistrationObject persistent settings keys.
@@ -66,6 +64,7 @@
 const char kSettingsSetOfUsedScriptsKey[] = "set_of_used_scripts";
 const char kSettingsSkipWaitingKey[] = "skip_waiting";
 const char kSettingsClassicScriptsImportedKey[] = "classic_scripts_imported";
+const char kSettingsRawHeadersKey[] = "raw_headers";
 
 bool CheckPersistentValue(
     std::string key_string, std::string settings_key,
@@ -87,8 +86,8 @@
 ServiceWorkerPersistentSettings::ServiceWorkerPersistentSettings(
     const Options& options)
     : options_(options) {
-  persistent_settings_.reset(
-      new cobalt::persistent_storage::PersistentSettings(kSettingsJson));
+  persistent_settings_.reset(new cobalt::persistent_storage::PersistentSettings(
+      ServiceWorkerConsts::kSettingsJson));
   persistent_settings_->ValidatePersistentSettings();
   DCHECK(persistent_settings_);
 
@@ -113,7 +112,7 @@
         persistent_settings_->GetPersistentSettingAsDictionary(key_string);
     if (dict.empty()) {
       DLOG(INFO) << "Key: " << key_string << " does not exist in "
-                 << kSettingsJson;
+                 << ServiceWorkerConsts::kSettingsJson;
       continue;
     }
     if (!CheckPersistentValue(key_string, kSettingsStorageKeyKey, dict,
@@ -122,6 +121,12 @@
     url::Origin storage_key =
         url::Origin::Create(GURL(dict[kSettingsStorageKeyKey]->GetString()));
 
+    // Only add persisted workers to the registration_map
+    // if their storage_key matches the origin of the initial_url.
+    if (!storage_key.IsSameOriginWith(url::Origin::Create(options_.url))) {
+      continue;
+    }
+
     if (!CheckPersistentValue(key_string, kSettingsScopeUrlKey, dict,
                               base::Value::Type::STRING))
       continue;
@@ -157,24 +162,32 @@
       continue;
 
     if (CheckPersistentValue(key_string, kSettingsLastUpdateCheckTimeKey, dict,
-                             base::Value::Type::DOUBLE)) {
-      double last_update_check_time =
-          dict[kSettingsLastUpdateCheckTimeKey]->GetDouble();
+                             base::Value::Type::STRING)) {
+      int64_t last_update_check_time =
+          std::atol(dict[kSettingsLastUpdateCheckTimeKey]->GetString().c_str());
       registration->set_last_update_check_time(
           base::Time::FromDeltaSinceWindowsEpoch(
-              base::TimeDelta::FromMicroseconds(
-                  (int64_t)dict[kSettingsLastUpdateCheckTimeKey]
-                      ->GetDouble())));
+              base::TimeDelta::FromMicroseconds(last_update_check_time)));
     }
 
-    // Persisted registration and worker are valid, add the registration
-    // to the registration_map and key_set_.
     key_set_.insert(key_string);
     registration_map.insert(std::make_pair(key, registration));
+    registration->set_is_persisted(true);
 
-    // TODO(b/228904017)
-    // Not in spec. Run SoftUpdate on the new registration.
-    options_.service_worker_jobs->SoftUpdate(registration);
+    options_.service_worker_jobs->message_loop()->task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ServiceWorkerJobs::Activate,
+                       base::Unretained(options_.service_worker_jobs),
+                       registration));
+
+    auto job = options_.service_worker_jobs->CreateJobWithoutPromise(
+        ServiceWorkerJobs::JobType::kUpdate, storage_key, scope,
+        registration->waiting_worker()->script_url());
+    options_.service_worker_jobs->message_loop()->task_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ServiceWorkerJobs::ScheduleJob,
+                       base::Unretained(options_.service_worker_jobs),
+                       std::move(job)));
   }
 }
 
@@ -236,10 +249,21 @@
       if (data == nullptr) {
         return false;
       }
-      std::string script_string(data->begin(), data->end());
-      auto result = script_resource_map.insert(std::make_pair(
-          script_url,
-          ScriptResource(std::make_unique<std::string>(script_string))));
+      auto script_resource = ScriptResource(std::make_unique<std::string>(
+          std::string(data->begin(), data->end())));
+      if (script_url == worker->script_url()) {
+        // Get the persistent headers for the ServiceWorkerObject script_url_.
+        // This is used in ServiceWorkerObject::Initialize().
+        base::Value* raw_header_value = value_dict->FindKeyOfType(
+            kSettingsRawHeadersKey, base::Value::Type::STRING);
+        if (raw_header_value == nullptr) return false;
+        const scoped_refptr<net::HttpResponseHeaders> headers =
+            scoped_refptr<net::HttpResponseHeaders>(
+                new net::HttpResponseHeaders(raw_header_value->GetString()));
+        script_resource.headers = headers;
+      }
+      auto result = script_resource_map.insert(
+          std::make_pair(script_url, std::move(script_resource)));
       DCHECK(result.second);
     }
   }
@@ -248,9 +272,7 @@
   }
   worker->set_script_resource_map(std::move(script_resource_map));
 
-  options_.service_worker_jobs->UpdateWorkerState(worker,
-                                                  kServiceWorkerStateActivated);
-  registration->set_active_worker(worker);
+  registration->set_waiting_worker(worker);
 
   return true;
 }
@@ -310,10 +332,10 @@
       std::make_unique<base::Value>(registration->update_via_cache_mode()));
 
   dict.try_emplace(kSettingsLastUpdateCheckTimeKey,
-                   std::make_unique<base::Value>(static_cast<double>(
-                       registration->last_update_check_time()
-                           .ToDeltaSinceWindowsEpoch()
-                           .InMicroseconds())));
+                   std::make_unique<base::Value>(
+                       std::to_string(registration->last_update_check_time()
+                                          .ToDeltaSinceWindowsEpoch()
+                                          .InMicroseconds())));
 
   persistent_settings_->SetPersistentSetting(
       key_string, std::make_unique<base::Value>(dict));
@@ -365,6 +387,14 @@
         web::cache_utils::GetKey(registration_key_string + script_url_string),
         data,
         /* metadata */ base::nullopt);
+
+    if (script_url_string == service_worker_object->script_url().spec()) {
+      // Persist the raw headers from the ServiceWorkerObject script_url_
+      // ScriptResource headers.
+      dict.try_emplace(kSettingsRawHeadersKey,
+                       std::make_unique<base::Value>(
+                           script_resource.second.headers->raw_headers()));
+    }
   }
   dict.try_emplace(kSettingsScriptResourceMapScriptUrlsKey,
                    std::make_unique<base::Value>(std::move(script_urls_value)));
diff --git a/cobalt/worker/service_worker_persistent_settings.h b/cobalt/worker/service_worker_persistent_settings.h
index b260d76..efda66f 100644
--- a/cobalt/worker/service_worker_persistent_settings.h
+++ b/cobalt/worker/service_worker_persistent_settings.h
@@ -33,7 +33,6 @@
 #include "cobalt/script/promise.h"
 #include "cobalt/script/script_value.h"
 #include "cobalt/script/script_value_factory.h"
-#include "cobalt/web/environment_settings.h"
 #include "cobalt/web/web_settings.h"
 #include "cobalt/worker/service_worker_registration_object.h"
 #include "cobalt/worker/service_worker_update_via_cache.h"
@@ -49,15 +48,17 @@
     Options(web::WebSettings* web_settings,
             network::NetworkModule* network_module,
             web::UserAgentPlatformInfo* platform_info,
-            ServiceWorkerJobs* service_worker_jobs)
+            ServiceWorkerJobs* service_worker_jobs, const GURL& url)
         : web_settings(web_settings),
           network_module(network_module),
           platform_info(platform_info),
-          service_worker_jobs(service_worker_jobs) {}
+          service_worker_jobs(service_worker_jobs),
+          url(url) {}
     web::WebSettings* web_settings;
     network::NetworkModule* network_module;
     web::UserAgentPlatformInfo* platform_info;
     ServiceWorkerJobs* service_worker_jobs;
+    const GURL& url;
   };
 
   explicit ServiceWorkerPersistentSettings(const Options& options);
diff --git a/cobalt/worker/service_worker_registration.cc b/cobalt/worker/service_worker_registration.cc
index 5452d8f..4e30763 100644
--- a/cobalt/worker/service_worker_registration.cc
+++ b/cobalt/worker/service_worker_registration.cc
@@ -50,7 +50,9 @@
           ->script_value_factory()
           ->CreateInterfacePromise<scoped_refptr<ServiceWorkerRegistration>>();
   std::unique_ptr<script::ValuePromiseWrappable::Reference> promise_reference(
-      new script::ValuePromiseWrappable::Reference(this, promise));
+      new script::ValuePromiseWrappable::Reference(
+          environment_settings()->context()->GetWindowOrWorkerGlobalScope(),
+          promise));
   // Perform the rest of the steps in a task, because the promise has to be
   // returned before we can safely reject or resolve it.
   base::MessageLoop::current()->task_runner()->PostTask(
@@ -134,7 +136,9 @@
                                           ->script_value_factory()
                                           ->CreateBasicPromise<bool>();
   std::unique_ptr<script::ValuePromiseBool::Reference> promise_reference(
-      new script::ValuePromiseBool::Reference(this, promise));
+      new script::ValuePromiseBool::Reference(
+          environment_settings()->context()->GetWindowOrWorkerGlobalScope(),
+          promise));
 
   // Perform the rest of the steps in a task, so that unregister doesn't race
   // past any previously submitted update requests.
diff --git a/cobalt/worker/service_worker_registration_map.cc b/cobalt/worker/service_worker_registration_map.cc
index b2da495..760c457 100644
--- a/cobalt/worker/service_worker_registration_map.cc
+++ b/cobalt/worker/service_worker_registration_map.cc
@@ -23,13 +23,12 @@
 
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
+#include "base/strings/string_util.h"
 #include "base/synchronization/lock.h"
 #include "base/trace_event/trace_event.h"
 #include "cobalt/script/exception_message.h"
 #include "cobalt/script/promise.h"
 #include "cobalt/script/script_value.h"
-#include "cobalt/web/context.h"
-#include "cobalt/web/environment_settings.h"
 #include "cobalt/worker/service_worker_jobs.h"
 #include "cobalt/worker/service_worker_registration_object.h"
 #include "cobalt/worker/service_worker_update_via_cache.h"
@@ -44,12 +43,14 @@
 
 // Returns the serialized URL excluding the fragment.
 std::string SerializeExcludingFragment(const GURL& url) {
-  url::Replacements<char> replacements;
+  GURL::Replacements replacements;
+  replacements.ClearUsername();
+  replacements.ClearPassword();
+  replacements.ClearQuery();
   replacements.ClearRef();
-  GURL no_fragment_url = url.ReplaceComponents(replacements);
-  DCHECK(!no_fragment_url.has_ref() || no_fragment_url.ref().empty());
-  DCHECK(!no_fragment_url.is_empty());
-  return no_fragment_url.spec();
+  return base::TrimString(url.ReplaceComponents(replacements).spec(), "/",
+                          base::TrimPositions::TRIM_TRAILING)
+      .as_string();
 }
 
 }  // namespace
@@ -239,6 +240,7 @@
   // is not this service worker registration.
   //   https://www.w3.org/TR/2022/CRD-service-workers-20220712/#dfn-service-worker-registration-unregistered
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (!registration) return true;
   std::string scope_string =
       SerializeExcludingFragment(registration->scope_url());
   RegistrationMapKey registration_key(registration->storage_key(),
diff --git a/cobalt/worker/service_worker_registration_map.h b/cobalt/worker/service_worker_registration_map.h
index a81bacc..f998319 100644
--- a/cobalt/worker/service_worker_registration_map.h
+++ b/cobalt/worker/service_worker_registration_map.h
@@ -29,7 +29,6 @@
 #include "cobalt/script/promise.h"
 #include "cobalt/script/script_value.h"
 #include "cobalt/script/script_value_factory.h"
-#include "cobalt/web/environment_settings.h"
 #include "cobalt/worker/service_worker_persistent_settings.h"
 #include "cobalt/worker/service_worker_registration_object.h"
 #include "cobalt/worker/service_worker_update_via_cache.h"
diff --git a/cobalt/worker/service_worker_registration_object.cc b/cobalt/worker/service_worker_registration_object.cc
index 0124895..b720645 100644
--- a/cobalt/worker/service_worker_registration_object.cc
+++ b/cobalt/worker/service_worker_registration_object.cc
@@ -29,13 +29,17 @@
     : storage_key_(storage_key),
       scope_url_(scope_url),
       update_via_cache_mode_(update_via_cache_mode),
-      last_update_check_time_(base::Time()) {}
+      last_update_check_time_(base::Time()),
+      is_persisted_(false) {}
 
 ServiceWorkerRegistrationObject::~ServiceWorkerRegistrationObject() {
   AbortAll();
 }
 
 void ServiceWorkerRegistrationObject::AbortAll() {
+  if (!done_event()->IsSignaled()) {
+    done_event()->Signal();
+  }
   if (installing_worker()) {
     installing_worker()->Abort();
   }
diff --git a/cobalt/worker/service_worker_registration_object.h b/cobalt/worker/service_worker_registration_object.h
index e36bebe..f3785a1 100644
--- a/cobalt/worker/service_worker_registration_object.h
+++ b/cobalt/worker/service_worker_registration_object.h
@@ -79,14 +79,13 @@
   }
 
   // https://www.w3.org/TR/2022/CRD-service-workers-20220712/#service-worker-registration-stale
-  bool stale() {
+  bool stale() const {
     return !last_update_check_time_.is_null() &&
            (base::Time::Now() - last_update_check_time_).InSeconds() >
                kStaleServiceWorkerRegistrationTimeout;
   }
 
-  base::Time last_update_check_time() { return last_update_check_time_; }
-
+  base::Time last_update_check_time() const { return last_update_check_time_; }
   void set_last_update_check_time(base::Time time) {
     last_update_check_time_ = time;
   }
@@ -96,6 +95,11 @@
 
   const int kStaleServiceWorkerRegistrationTimeout = 86400;
 
+  base::WaitableEvent* done_event() { return &done_event_; }
+
+  bool is_persisted() const { return is_persisted_; }
+  void set_is_persisted(bool value) { is_persisted_ = value; }
+
  private:
   // This lock is to allow atomic operations on the registration object.
   base::Lock mutex_;
@@ -108,6 +112,12 @@
   scoped_refptr<ServiceWorkerObject> active_worker_;
 
   base::Time last_update_check_time_;
+
+  base::WaitableEvent done_event_ = {
+      base::WaitableEvent::ResetPolicy::MANUAL,
+      base::WaitableEvent::InitialState::SIGNALED};
+
+  bool is_persisted_;
 };
 
 }  // namespace worker
diff --git a/cobalt/worker/testing/BUILD.gn b/cobalt/worker/testing/BUILD.gn
index e91994b..ac54117 100644
--- a/cobalt/worker/testing/BUILD.gn
+++ b/cobalt/worker/testing/BUILD.gn
@@ -28,6 +28,7 @@
     "//cobalt/script",
     "//cobalt/web",
     "//cobalt/web:dom_exception",
+    "//cobalt/web:stat_tracker",
     "//cobalt/web/testing:web_testing",
     "//cobalt/worker",
     "//testing/gmock",
diff --git a/cobalt/worker/testing/test_with_javascript.h b/cobalt/worker/testing/test_with_javascript.h
index e40483d..e540d2f 100644
--- a/cobalt/worker/testing/test_with_javascript.h
+++ b/cobalt/worker/testing/test_with_javascript.h
@@ -28,7 +28,9 @@
 #include "cobalt/script/source_code.h"
 #include "cobalt/script/testing/mock_exception_state.h"
 #include "cobalt/web/context.h"
+#include "cobalt/web/stat_tracker.h"
 #include "cobalt/web/testing/stub_web_context.h"
+#include "cobalt/web/window_or_worker_global_scope.h"
 #include "cobalt/worker/dedicated_worker_global_scope.h"
 #include "cobalt/worker/service_worker_global_scope.h"
 #include "cobalt/worker/worker_settings.h"
@@ -42,15 +44,17 @@
 template <class TypeIdProvider>
 class TestWithJavaScriptBase : public TypeIdProvider {
  public:
-  TestWithJavaScriptBase() {
+  TestWithJavaScriptBase() : stat_tracker_("TestWithJavaScriptBase", "Test") {
     web_context_.reset(new web::testing::StubWebContext());
-    web_context_->setup_environment_settings(new WorkerSettings());
+    web_context_->SetupEnvironmentSettings(new WorkerSettings());
     web_context_->environment_settings()->set_creation_url(GURL("about:blank"));
+    web::WindowOrWorkerGlobalScope::Options global_scope_options;
+    global_scope_options.stat_tracker = &stat_tracker_;
 
     if (TypeIdProvider::GetGlobalScopeTypeId() ==
         base::GetTypeId<DedicatedWorkerGlobalScope>()) {
-      dedicated_worker_global_scope_ =
-          new DedicatedWorkerGlobalScope(web_context_->environment_settings());
+      dedicated_worker_global_scope_ = new DedicatedWorkerGlobalScope(
+          web_context_->environment_settings(), global_scope_options);
       dedicated_worker_global_scope_->set_name("TestWithJavaScriptBase");
       web_context_->global_environment()->CreateGlobalObject(
           dedicated_worker_global_scope_, web_context_->environment_settings());
@@ -66,11 +70,13 @@
               web_context_->network_module(),
               containing_service_worker_registration_));
       service_worker_global_scope_ = new ServiceWorkerGlobalScope(
-          web_context_->environment_settings(), service_worker_object_);
+          web_context_->environment_settings(), global_scope_options,
+          service_worker_object_);
       web_context_->global_environment()->CreateGlobalObject(
           service_worker_global_scope_, web_context_->environment_settings());
       worker_global_scope_ = service_worker_global_scope_.get();
     }
+    web_context_->SetupFinished();
   }
 
   ~TestWithJavaScriptBase() { ClearWebContext(); }
@@ -93,6 +99,10 @@
     return web_context_.get();
   }
 
+  web::EnvironmentSettings* environment_settings() const {
+    return web_context()->environment_settings();
+  }
+
   scoped_refptr<script::GlobalEnvironment> global_environment() const {
     DCHECK(this->web_context());
     return this->web_context()->global_environment();
@@ -137,6 +147,7 @@
       containing_service_worker_registration_;
   scoped_refptr<ServiceWorkerGlobalScope> service_worker_global_scope_;
   ::testing::StrictMock<script::testing::MockExceptionState> exception_state_;
+  web::StatTracker stat_tracker_;
 };
 
 template <class GlobalScope>
diff --git a/cobalt/worker/worker.cc b/cobalt/worker/worker.cc
index a27476f..5e9d507 100644
--- a/cobalt/worker/worker.cc
+++ b/cobalt/worker/worker.cc
@@ -39,10 +39,6 @@
 namespace cobalt {
 namespace worker {
 
-namespace {
-bool PermitAnyURL(const GURL&, bool) { return true; }
-}  // namespace
-
 Worker::Worker(const char* name, const Options& options) : options_(options) {
   // Algorithm for 'run a worker'
   //   https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#run-a-worker
@@ -99,7 +95,7 @@
   //   https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#set-up-a-worker-environment-settings-object
   worker_settings->set_origin(
       options_.outside_context->environment_settings()->GetOrigin());
-  web_context_->setup_environment_settings(worker_settings);
+  web_context_->SetupEnvironmentSettings(worker_settings);
   // From algorithm for to setup up a worker environment settings object:
   //   https://html.spec.whatwg.org/commit-snapshots/465a6b672c703054de278b0f8133eb3ad33d93f4/#set-up-a-worker-environment-settings-object
   // 5. Set settings object's creation URL to worker global scope's url.
@@ -108,8 +104,9 @@
   // 8. Let worker global scope be the global object of realm execution
   //    context's Realm component.
   scoped_refptr<DedicatedWorkerGlobalScope> dedicated_worker_global_scope =
-      new DedicatedWorkerGlobalScope(web_context_->environment_settings(),
-                                     false);
+      new DedicatedWorkerGlobalScope(
+          web_context_->environment_settings(), options_.global_scope_options,
+          /*parent_cross_origin_isolated_capability*/ false);
   worker_global_scope_ = dedicated_worker_global_scope;
   // 9. Set up a worker environment settings object with realm execution
   //    context, outside settings, and unsafeWorkerCreationTime, and let
@@ -134,6 +131,7 @@
 
   // 10. Set worker global scope's name to the value of options's name member.
   dedicated_worker_global_scope->set_name(options_.options.name());
+  web_context_->SetupFinished();
   // (Moved) 2. Let owner be the relevant owner to add given outside settings.
   web::WindowOrWorkerGlobalScope* owner =
       options_.outside_context->GetWindowOrWorkerGlobalScope();
@@ -229,7 +227,8 @@
               error.set_filename(location.file_path);
               error.set_lineno(location.line_number);
               error.set_colno(location.column_number);
-              global_scope->DispatchEvent(new web::ErrorEvent(error));
+              global_scope->DispatchEvent(new web::ErrorEvent(
+                  global_scope->environment_settings(), error));
             },
             base::Unretained(
                 options_.outside_context->GetWindowOrWorkerGlobalScope()),
@@ -295,7 +294,7 @@
               error.set_message(message);
               error.set_filename(filename);
               context->GetWindowOrWorkerGlobalScope()->DispatchEvent(
-                  new web::ErrorEvent(error));
+                  new web::ErrorEvent(context->environment_settings(), error));
             },
             options_.outside_context, retval,
             web_context_->environment_settings()->creation_url().spec()));
@@ -337,11 +336,13 @@
     worker_global_scope_->owner_set()->clear();
   }
   if (web_agent_) {
-    DCHECK(message_loop());
-    web_agent_->WaitUntilDone();
-    web_agent_->Stop();
-    web_agent_.reset();
+    std::unique_ptr<web::Agent> web_agent(std::move(web_agent_));
+    DCHECK(web_agent);
+    DCHECK(!web_agent_);
+    web_agent->WaitUntilDone();
     web_context_ = nullptr;
+    web_agent->Stop();
+    web_agent.reset();
   }
 }
 
diff --git a/cobalt/worker/worker.h b/cobalt/worker/worker.h
index 9d7043c..9d8d779 100644
--- a/cobalt/worker/worker.h
+++ b/cobalt/worker/worker.h
@@ -57,6 +57,7 @@
   // Worker Options needed at thread run time.
   struct Options {
     web::Agent::Options web_options;
+    web::WindowOrWorkerGlobalScope::Options global_scope_options;
 
     // Holds the source location where the worker was constructed.
     base::SourceLocation construction_location;
diff --git a/cobalt/worker/worker_global_scope.cc b/cobalt/worker/worker_global_scope.cc
index d9025b9..33c7b86 100644
--- a/cobalt/worker/worker_global_scope.cc
+++ b/cobalt/worker/worker_global_scope.cc
@@ -31,9 +31,11 @@
 #include "cobalt/web/user_agent_platform_info.h"
 #include "cobalt/web/window_or_worker_global_scope.h"
 #include "cobalt/web/window_timers.h"
+#include "cobalt/worker/service_worker_consts.h"
 #include "cobalt/worker/service_worker_object.h"
 #include "cobalt/worker/worker_location.h"
 #include "cobalt/worker/worker_navigator.h"
+#include "net/base/mime_util.h"
 #include "starboard/atomic.h"
 #include "url/gurl.h"
 
@@ -41,7 +43,6 @@
 namespace worker {
 
 namespace {
-bool PermitAnyURL(const GURL& url, bool) { return true; }
 
 class ScriptLoader : public base::MessageLoop::DestructionObserver {
  public:
@@ -93,7 +94,7 @@
     script_loader_factory_.reset();
   }
 
-  void Load(const loader::Origin& origin,
+  void Load(web::CspDelegate* csp_delegate, const loader::Origin& origin,
             const std::vector<GURL>& resolved_urls) {
     TRACE_EVENT0("cobalt::worker", "ScriptLoader::Load()");
     number_of_loads_ = resolved_urls.size();
@@ -105,20 +106,23 @@
     for (int i = 0; i < resolved_urls.size(); ++i) {
       const GURL& url = resolved_urls[i];
       thread_->message_loop()->task_runner()->PostTask(
-          FROM_HERE, base::BindOnce(&ScriptLoader::LoaderTask,
-                                    base::Unretained(this), &loaders_[i],
-                                    origin, url, &contents_[i], &errors_[i]));
+          FROM_HERE,
+          base::BindOnce(&ScriptLoader::LoaderTask, base::Unretained(this),
+                         csp_delegate, &loaders_[i], origin, url, &contents_[i],
+                         &errors_[i]));
     }
     load_finished_.Wait();
   }
 
-  void LoaderTask(std::unique_ptr<loader::Loader>* loader,
+  void LoaderTask(web::CspDelegate* csp_delegate,
+                  std::unique_ptr<loader::Loader>* loader,
                   const loader::Origin& origin, const GURL& url,
                   std::unique_ptr<std::string>* content,
                   std::unique_ptr<std::string>* error) {
     TRACE_EVENT0("cobalt::worker", "ScriptLoader::LoaderTask()");
-    // Todo: implement csp check (b/225037465)
-    csp::SecurityCallback csp_callback = base::Bind(&PermitAnyURL);
+    csp::SecurityCallback csp_callback =
+        base::Bind(&web::CspDelegate::CanLoad, base::Unretained(csp_delegate),
+                   web::CspDelegate::kWorker);
 
     bool skip_fetch_intercept =
         context_->GetWindowOrWorkerGlobalScope()->IsServiceWorker();
@@ -133,9 +137,11 @@
               *output_content = std::move(content);
             },
             content),
+        base::Bind(&ScriptLoader::UpdateOnResponseStarted,
+                   base::Unretained(this), error),
         base::Bind(&ScriptLoader::LoadingCompleteCallback,
                    base::Unretained(this), loader, error),
-        skip_fetch_intercept);
+        net::HttpRequestHeaders(), skip_fetch_intercept);
   }
 
   void LoadingCompleteCallback(std::unique_ptr<loader::Loader>* loader,
@@ -157,6 +163,31 @@
     }
   }
 
+  bool UpdateOnResponseStarted(
+      std::unique_ptr<std::string>* error, loader::Fetcher* fetcher,
+      const scoped_refptr<net::HttpResponseHeaders>& headers) {
+    std::string content_type;
+    bool mime_type_is_javascript = false;
+    if (headers->GetNormalizedHeader("Content-type", &content_type)) {
+      for (auto mime_type : ServiceWorkerConsts::kJavaScriptMimeTypes) {
+        if (net::MatchesMimeType(mime_type, content_type)) {
+          mime_type_is_javascript = true;
+          break;
+        }
+      }
+    }
+    if (content_type.empty()) {
+      error->reset(new std::string(base::StringPrintf(
+          ServiceWorkerConsts::kServiceWorkerRegisterNoMIMEError,
+          content_type.c_str())));
+    } else if (!mime_type_is_javascript) {
+      error->reset(new std::string(base::StringPrintf(
+          ServiceWorkerConsts::kServiceWorkerRegisterBadMIMEError,
+          content_type.c_str())));
+    }
+    return true;
+  }
+
   std::unique_ptr<std::string>& GetContents(int index) {
     return contents_[index];
   }
@@ -186,14 +217,10 @@
 
 }  // namespace
 
-WorkerGlobalScope::WorkerGlobalScope(script::EnvironmentSettings* settings)
-    : web::WindowOrWorkerGlobalScope(
-          settings, /*stat_tracker=*/NULL,
-          // Using default options for CSP
-          web::WindowOrWorkerGlobalScope::Options(
-              // TODO (b/233788170): once application state is
-              // available, update this to use the actual state.
-              base::ApplicationState::kApplicationStateStarted)),
+WorkerGlobalScope::WorkerGlobalScope(
+    script::EnvironmentSettings* settings,
+    const web::WindowOrWorkerGlobalScope::Options& options)
+    : web::WindowOrWorkerGlobalScope(settings, options),
       location_(new WorkerLocation(settings->creation_url())),
       navigator_(new WorkerNavigator(settings)) {
   set_navigator_base(navigator_);
@@ -286,9 +313,8 @@
   web::EnvironmentSettings* settings = environment_settings();
   const GURL& base_url = settings->base_url();
   loader::Origin origin = loader::Origin(base_url.GetOrigin());
-  // TODO(b/241801523): Apply CSP.
   ScriptLoader script_loader(settings->context());
-  script_loader.Load(origin, request_urls);
+  script_loader.Load(csp_delegate(), origin, request_urls);
 
   for (int index = 0; index < request_urls.size(); ++index) {
     const auto& error = script_loader.GetError(index);
@@ -378,9 +404,8 @@
   //      object, passing along any custom perform the fetch steps provided.
   //      If this succeeds, let script be the result. Otherwise, rethrow the
   //      exception.
-  // TODO(b/241801523): Apply CSP.
   ScriptLoader script_loader(settings->context());
-  script_loader.Load(origin, request_urls);
+  script_loader.Load(csp_delegate(), origin, request_urls);
 
   // 5. For each url in the resulting URL records, run these substeps:
   int content_lookup_index = 0;
diff --git a/cobalt/worker/worker_global_scope.h b/cobalt/worker/worker_global_scope.h
index e387991..9c3468f 100644
--- a/cobalt/worker/worker_global_scope.h
+++ b/cobalt/worker/worker_global_scope.h
@@ -35,6 +35,7 @@
 #include "cobalt/web/user_agent_platform_info.h"
 #include "cobalt/web/window_or_worker_global_scope.h"
 #include "cobalt/web/window_timers.h"
+#include "cobalt/worker/service_worker_consts.h"
 #include "cobalt/worker/worker_location.h"
 #include "cobalt/worker/worker_navigator.h"
 #include "net/http/http_response_headers.h"
@@ -55,6 +56,7 @@
       : content(std::move(content)), headers(headers) {}
   std::unique_ptr<std::string> content;
   scoped_refptr<net::HttpResponseHeaders> headers;
+  bool has_ever_been_evaluated = false;
 };
 
 using ScriptResourceMap = std::map<GURL, ScriptResource>;
@@ -70,7 +72,9 @@
   using ResponseCallback =
       base::Callback<std::string*(const GURL& url, std::string*)>;
 
-  explicit WorkerGlobalScope(script::EnvironmentSettings* settings);
+  explicit WorkerGlobalScope(
+      script::EnvironmentSettings* settings,
+      const web::WindowOrWorkerGlobalScope::Options& options);
   WorkerGlobalScope(const WorkerGlobalScope&) = delete;
   WorkerGlobalScope& operator=(const WorkerGlobalScope&) = delete;
 
diff --git a/cobalt/worker/worker_global_scope_test.cc b/cobalt/worker/worker_global_scope_test.cc
index 216da15..4c30e99 100644
--- a/cobalt/worker/worker_global_scope_test.cc
+++ b/cobalt/worker/worker_global_scope_test.cc
@@ -145,7 +145,8 @@
       "error", FakeScriptValue<web::EventListener>(fake_event_listener_.get()),
       true);
   fake_event_listener_->ExpectHandleEventCall("error", worker_global_scope());
-  worker_global_scope()->DispatchEvent(new web::ErrorEvent());
+  worker_global_scope()->DispatchEvent(
+      new web::ErrorEvent(/*environment_settings=*/nullptr));
 }
 
 TEST_P(WorkerGlobalScopeTest, OnErrorEvent) {
diff --git a/cobalt/xhr/xml_http_request.cc b/cobalt/xhr/xml_http_request.cc
index 8b45ac0..edd864a 100644
--- a/cobalt/xhr/xml_http_request.cc
+++ b/cobalt/xhr/xml_http_request.cc
@@ -359,6 +359,7 @@
       upload_listener_(false),
       with_credentials_(false),
       xhr_(xhr),
+      will_destroy_current_message_loop_(false),
       active_requests_count_(0),
       http_status_(0),
       redirect_times_(0),
@@ -369,6 +370,7 @@
       timeout_ms_(0),
       upload_complete_(false) {
   DCHECK(environment_settings());
+  base::MessageLoop::current()->AddDestructionObserver(this);
 }
 
 void XMLHttpRequestImpl::Abort() {
@@ -396,7 +398,6 @@
                               const base::Optional<std::string>& password,
                               script::ExceptionState* exception_state) {
   TRACK_MEMORY_SCOPE("XHR");
-
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   XMLHttpRequest::State previous_state = state_;
@@ -518,13 +519,11 @@
                                ->IsServiceWorker();
   if (!in_service_worker && method_ == net::URLFetcher::GET) {
     loader::FetchInterceptorCoordinator::GetInstance()->TryIntercept(
-        request_url_,
-        base::BindOnce(&XMLHttpRequestImpl::SendIntercepted,
-                       base::Unretained(this)),
-        base::BindOnce(&XMLHttpRequestImpl::ReportLoadTimingInfo,
-                       base::Unretained(this)),
-        base::BindOnce(&XMLHttpRequestImpl::SendFallback,
-                       base::Unretained(this), request_body, exception_state));
+        request_url_, /*main_resource=*/false, request_headers_, task_runner_,
+        base::BindOnce(&XMLHttpRequestImpl::SendIntercepted, AsWeakPtr()),
+        base::BindOnce(&XMLHttpRequestImpl::ReportLoadTimingInfo, AsWeakPtr()),
+        base::BindOnce(&XMLHttpRequestImpl::SendFallback, AsWeakPtr(),
+                       request_body, exception_state));
     return;
   }
   SendFallback(request_body, exception_state);
@@ -532,10 +531,13 @@
 
 void XMLHttpRequestImpl::SendIntercepted(
     std::unique_ptr<std::string> response) {
+  if (will_destroy_current_message_loop_.load()) {
+    return;
+  }
   if (task_runner_ != base::MessageLoop::current()->task_runner()) {
-    task_runner_->PostTask(
-        FROM_HERE, base::BindOnce(&XMLHttpRequestImpl::SendIntercepted,
-                                  base::Unretained(this), std::move(response)));
+    task_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(&XMLHttpRequestImpl::SendIntercepted,
+                                          AsWeakPtr(), std::move(response)));
     return;
   }
   sent_ = true;
@@ -597,6 +599,9 @@
 void XMLHttpRequestImpl::SendFallback(
     const base::Optional<XMLHttpRequest::RequestBodyType>& request_body,
     script::ExceptionState* exception_state) {
+  if (will_destroy_current_message_loop_.load()) {
+    return;
+  }
   if (task_runner_ != base::MessageLoop::current()->task_runner()) {
     task_runner_->PostTask(
         FROM_HERE,
@@ -1202,6 +1207,10 @@
   this->StartRequest(request_body_text_);
 }
 
+void XMLHttpRequestImpl::WillDestroyCurrentMessageLoop() {
+  will_destroy_current_message_loop_.store(true);
+}
+
 void XMLHttpRequestImpl::ReportLoadTimingInfo(
     const net::LoadTimingInfo& timing_info) {
   load_timing_info_ = timing_info;
diff --git a/cobalt/xhr/xml_http_request.h b/cobalt/xhr/xml_http_request.h
index 30c606d..7b83f57 100644
--- a/cobalt/xhr/xml_http_request.h
+++ b/cobalt/xhr/xml_http_request.h
@@ -21,6 +21,8 @@
 #include <vector>
 
 #include "base/gtest_prod_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop_current.h"
 #include "base/optional.h"
 #include "base/timer/timer.h"
 #include "cobalt/dom/document.h"
@@ -44,6 +46,7 @@
 #include "net/http/http_response_headers.h"
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_fetcher_delegate.h"
+#include "starboard/common/atomic.h"
 #include "url/gurl.h"
 
 namespace cobalt {
@@ -220,12 +223,18 @@
 };
 
 
-class XMLHttpRequestImpl {
+class XMLHttpRequestImpl
+    : public base::SupportsWeakPtr<XMLHttpRequestImpl>,
+      public base::MessageLoopCurrent::DestructionObserver {
  public:
   explicit XMLHttpRequestImpl(XMLHttpRequest* xhr);
   XMLHttpRequestImpl(const XMLHttpRequestImpl&) = delete;
   XMLHttpRequestImpl& operator=(const XMLHttpRequestImpl&) = delete;
-  virtual ~XMLHttpRequestImpl() {}
+  virtual ~XMLHttpRequestImpl() {
+    if (!will_destroy_current_message_loop_.load()) {
+      base::MessageLoop::current()->RemoveDestructionObserver(this);
+    }
+  }
 
   void Abort();
   void Open(const std::string& method, const std::string& url, bool async,
@@ -298,6 +307,9 @@
                                 int64 total);
   void OnRedirect(const net::HttpResponseHeaders& headers);
 
+  // base::MessageLoopCurrent::DestructionObserver
+  void WillDestroyCurrentMessageLoop();
+
   // Called from bindings layer to tie objects' lifetimes to this XHR instance.
   XMLHttpRequestUpload* upload_or_null() { return upload_.get(); }
 
@@ -335,6 +347,7 @@
   bool upload_listener_;
   bool with_credentials_;
   XMLHttpRequest* xhr_;
+  starboard::atomic_bool will_destroy_current_message_loop_;
 
   // A corspreflight instance for potentially sending preflight
   // request and performing cors check for all cross origin requests.
@@ -448,7 +461,11 @@
   explicit DOMXMLHttpRequestImpl(XMLHttpRequest* xhr);
   DOMXMLHttpRequestImpl(const DOMXMLHttpRequestImpl&) = delete;
   DOMXMLHttpRequestImpl& operator=(const DOMXMLHttpRequestImpl&) = delete;
-  ~DOMXMLHttpRequestImpl() override {}
+  ~DOMXMLHttpRequestImpl() override {
+    if (!will_destroy_current_message_loop_.load()) {
+      base::MessageLoop::current()->RemoveDestructionObserver(this);
+    }
+  }
 
   scoped_refptr<dom::Document> response_xml(
       script::ExceptionState* exception_state) override;
diff --git a/docker-compose-windows.yml b/docker-compose-windows.yml
index 7999d4b..9d62c11 100644
--- a/docker-compose-windows.yml
+++ b/docker-compose-windows.yml
@@ -19,9 +19,6 @@
 
 x-common-definitions: &common-definitions
   stdin_open: true
-  environment:
-    - SCCACHE_DIR=c:/root/sccache
-    - SCCACHE_CACHE_SIZE="30G"
   tty: true
   volumes:
     - ${COBALT_SRC:-.}:c:/code/
@@ -36,6 +33,9 @@
   CONFIG: ${CONFIG:-devel}
   TARGET: ${TARGET:-cobalt_install}
   NINJA_FLAGS: ${NINJA_FLAGS}
+  SCCACHE: 1
+  SCCACHE_DIR: c:/root/sccache
+  SCCACHE_CACHE_SIZE: "30G"
 
 services:
 
@@ -52,6 +52,15 @@
     image: visual-studio-base
     scale: 0
 
+  visual-studio-2022-base:
+    build:
+      context: ./docker/windows/base/visualstudio2022
+      dockerfile: ./Dockerfile
+      args:
+        - FROM_IMAGE=mcr.microsoft.com/windows/servercore:ltsc2019
+    image: visual-studio-2022-base
+    scale: 0
+
   visual-studio-win32-base:
     build:
       context: ./docker/windows/base/visualstudio2017
@@ -85,6 +94,17 @@
     depends_on:
       - visual-studio-base
 
+  cobalt-build-vs2022-win-base:
+    build:
+      context: ./docker/windows/base/build
+      dockerfile: ./Dockerfile
+      args:
+        - FROM_IMAGE=visual-studio-2022-base
+    image: cobalt-build-vs2022-win-base
+    scale: 0
+    depends_on:
+      - visual-studio-2022-base
+
   cobalt-build-win32-base:
     build:
       context: ./docker/windows/base/build
@@ -112,6 +132,8 @@
     build:
       context: ./docker/windows/win32
       dockerfile: ./Dockerfile
+      args:
+        - FROM_IMAGE=cobalt-build-win32-base
     depends_on:
       - cobalt-build-win32-base
     image: cobalt-build-win-win32
diff --git a/docker-compose.yml b/docker-compose.yml
index faff98a..2e67fc8 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -483,3 +483,28 @@
   unittest-parallel-3:
     <<: *shared-unittest-definitions
     entrypoint: ["python3", "/unittest_docker_launcher.py", "3"]
+
+  xvfb:
+    image: xvfb
+    ports:
+      - "99:99"
+    image: cobalt-xvfb
+    build:
+      context: ./docker/linux/
+      dockerfile: integrationtest/Dockerfile.xvfb
+    depends_on: [ unittest ]
+
+  integration_test:
+    <<: *build-common-definitions
+    image: cobalt-integrationtest
+    build:
+      context: ./docker/linux/
+      dockerfile: integrationtest/Dockerfile
+    depends_on: [ xvfb ]
+    working_dir: /code
+    environment:
+      - DISPLAY=xvfb:99
+      - IPV6_AVAILABLE=0
+      - PYTHONPATH=/code
+      - PLATFORM=${PLATFORM:-linux-x64x11}
+      - CONFIG=${CONFIG:-devel}
diff --git a/docker/docsite/Dockerfile b/docker/docsite/Dockerfile
index 02b4681..fb25afe 100644
--- a/docker/docsite/Dockerfile
+++ b/docker/docsite/Dockerfile
@@ -34,7 +34,10 @@
     && rm -rf /var/lib/{apt,dpkg,cache,log}
 
 COPY Gemfile /app/Gemfile
-
+# Note: This file was generated by running a working version of this Docker
+# container. Then it was committed back to the codebase to be copied over as
+# part of the build steps.
+COPY Gemfile.lock /app/Gemfile.lock
 RUN bundle install --gemfile=/app/Gemfile
 
 # We create and use a non-root user explicitly so that the generated and
diff --git a/docker/docsite/Gemfile.lock b/docker/docsite/Gemfile.lock
new file mode 100644
index 0000000..074ae8c
--- /dev/null
+++ b/docker/docsite/Gemfile.lock
@@ -0,0 +1,167 @@
+GEM
+  remote: https://rubygems.org/
+  specs:
+    RedCloth (4.2.9)
+    activesupport (6.1.7.2)
+      concurrent-ruby (~> 1.0, >= 1.0.2)
+      i18n (>= 1.6, < 2)
+      minitest (>= 5.1)
+      tzinfo (~> 2.0)
+      zeitwerk (~> 2.3)
+    addressable (2.8.1)
+      public_suffix (>= 2.0.2, < 6.0)
+    blankslate (2.1.2.4)
+    bourbon (7.3.0)
+      thor (~> 1.0)
+    classifier-reborn (2.3.0)
+      fast-stemmer (~> 1.0)
+      matrix (~> 0.4)
+    coffee-script (2.4.1)
+      coffee-script-source
+      execjs
+    coffee-script-source (1.12.2)
+    colorator (0.1)
+    concurrent-ruby (1.2.2)
+    execjs (2.8.1)
+    faraday (1.10.3)
+      faraday-em_http (~> 1.0)
+      faraday-em_synchrony (~> 1.0)
+      faraday-excon (~> 1.1)
+      faraday-httpclient (~> 1.0)
+      faraday-multipart (~> 1.0)
+      faraday-net_http (~> 1.0)
+      faraday-net_http_persistent (~> 1.0)
+      faraday-patron (~> 1.0)
+      faraday-rack (~> 1.0)
+      faraday-retry (~> 1.0)
+      ruby2_keywords (>= 0.0.4)
+    faraday-em_http (1.0.0)
+    faraday-em_synchrony (1.0.0)
+    faraday-excon (1.1.0)
+    faraday-httpclient (1.0.1)
+    faraday-multipart (1.0.4)
+      multipart-post (~> 2)
+    faraday-net_http (1.0.1)
+    faraday-net_http_persistent (1.2.0)
+    faraday-patron (1.0.0)
+    faraday-rack (1.0.0)
+    faraday-retry (1.0.3)
+    fast-stemmer (1.0.2)
+    ffi (1.15.5)
+    gemoji (2.1.0)
+    html-pipeline (1.9.0)
+      activesupport (>= 2)
+      nokogiri (~> 1.4)
+    i18n (1.12.0)
+      concurrent-ruby (~> 1.0)
+    jekyll (2.4.0)
+      classifier-reborn (~> 2.0)
+      colorator (~> 0.1)
+      jekyll-coffeescript (~> 1.0)
+      jekyll-gist (~> 1.0)
+      jekyll-paginate (~> 1.0)
+      jekyll-sass-converter (~> 1.0)
+      jekyll-watch (~> 1.1)
+      kramdown (~> 1.3)
+      liquid (~> 2.6.1)
+      mercenary (~> 0.3.3)
+      pygments.rb (~> 0.6.0)
+      redcarpet (~> 3.1)
+      safe_yaml (~> 1.0)
+      toml (~> 0.1.0)
+    jekyll-coffeescript (1.0.1)
+      coffee-script (~> 2.2)
+    jekyll-feed (0.3.1)
+    jekyll-gist (1.5.0)
+      octokit (~> 4.2)
+    jekyll-mentions (0.2.1)
+      html-pipeline (~> 1.9.0)
+      jekyll (~> 2.0)
+    jekyll-paginate (1.1.0)
+    jekyll-redirect-from (0.8.0)
+      jekyll (>= 2.0)
+    jekyll-sass-converter (1.3.0)
+      sass (~> 3.2)
+    jekyll-sitemap (0.8.1)
+    jekyll-watch (1.5.1)
+      listen (~> 3.0)
+    jemoji (0.5.0)
+      gemoji (~> 2.0)
+      html-pipeline (~> 1.9)
+      jekyll (>= 2.0)
+    kramdown (1.5.0)
+    liquid (2.6.2)
+    listen (3.8.0)
+      rb-fsevent (~> 0.10, >= 0.10.3)
+      rb-inotify (~> 0.9, >= 0.9.10)
+    maruku (0.7.0)
+    matrix (0.4.2)
+    mercenary (0.3.6)
+    mini_portile2 (2.6.1)
+    minitest (5.15.0)
+    multipart-post (2.3.0)
+    neat (1.7.4)
+      bourbon (>= 4.0)
+      sass (>= 3.3)
+    nokogiri (1.12.5)
+      mini_portile2 (~> 2.6.1)
+      racc (~> 1.4)
+    octokit (4.25.1)
+      faraday (>= 1, < 3)
+      sawyer (~> 0.9)
+    parslet (1.5.0)
+      blankslate (~> 2.0)
+    posix-spawn (0.3.15)
+    public_suffix (4.0.7)
+    pygments.rb (0.6.3)
+      posix-spawn (~> 0.3.6)
+      yajl-ruby (~> 1.2.0)
+    racc (1.6.2)
+    rb-fsevent (0.11.2)
+    rb-inotify (0.10.1)
+      ffi (~> 1.0)
+    rdiscount (2.1.7)
+    redcarpet (3.3.2)
+    ruby2_keywords (0.0.5)
+    safe_yaml (1.0.5)
+    sass (3.7.4)
+      sass-listen (~> 4.0.0)
+    sass-listen (4.0.0)
+      rb-fsevent (~> 0.9, >= 0.9.4)
+      rb-inotify (~> 0.9, >= 0.9.7)
+    sawyer (0.9.2)
+      addressable (>= 2.3.5)
+      faraday (>= 0.17.3, < 3)
+    thor (1.2.1)
+    toml (0.1.2)
+      parslet (~> 1.5.0)
+    tzinfo (2.0.6)
+      concurrent-ruby (~> 1.0)
+    yajl-ruby (1.2.3)
+    zeitwerk (2.6.7)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  RedCloth (= 4.2.9)
+  bourbon
+  i18n
+  jekyll (= 2.4.0)
+  jekyll-coffeescript (= 1.0.1)
+  jekyll-feed (= 0.3.1)
+  jekyll-mentions (= 0.2.1)
+  jekyll-redirect-from (= 0.8.0)
+  jekyll-sass-converter (= 1.3.0)
+  jekyll-sitemap (= 0.8.1)
+  jemoji (= 0.5.0)
+  kramdown (= 1.5.0)
+  liquid (= 2.6.2)
+  maruku (= 0.7.0)
+  neat
+  pygments.rb (= 0.6.3)
+  rdiscount (= 2.1.7)
+  redcarpet (= 3.3.2)
+
+BUNDLED WITH
+   1.17.3
diff --git a/docker/linux/base/build/Dockerfile b/docker/linux/base/build/Dockerfile
index d93caf6..2247568 100644
--- a/docker/linux/base/build/Dockerfile
+++ b/docker/linux/base/build/Dockerfile
@@ -22,6 +22,7 @@
     && apt install -qqy --no-install-recommends \
         binutils \
         bison \
+        nasm \
         ninja-build \
         pkgconf \
         unzip \
@@ -32,7 +33,7 @@
 ARG NVM_SHA256SUM="f068e17dacb88f73302790cc076956c7a0d459ce9b01df842ff3e75744f9e2fe /tmp/install.sh"
 ARG NVM_URL="https://cobalt.googlesource.com/third_party/nvm/+/refs/tags/v0.35.3/install.sh?format=TEXT"
 ENV NVM_DIR=${HOME}/.nvm
-ENV NODE_VERSION 12.17.0
+ENV NODE_VERSION 16.19.1
 
 RUN curl --silent -o- ${NVM_URL} \
   | base64 -d > /tmp/install.sh \
diff --git a/docker/linux/integrationtest/Dockerfile b/docker/linux/integrationtest/Dockerfile
new file mode 100644
index 0000000..9ca12f3
--- /dev/null
+++ b/docker/linux/integrationtest/Dockerfile
@@ -0,0 +1,17 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+FROM cobalt-unittest
+
+CMD python3 cobalt/black_box_tests/black_box_tests.py --platform ${PLATFORM} -c ${CONFIG}
diff --git a/docker/linux/integrationtest/Dockerfile.xvfb b/docker/linux/integrationtest/Dockerfile.xvfb
new file mode 100644
index 0000000..c09d17e
--- /dev/null
+++ b/docker/linux/integrationtest/Dockerfile.xvfb
@@ -0,0 +1,26 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+FROM cobalt-unittest
+
+RUN apt update -qqy \
+    && apt install -qqy --no-install-recommends \
+        xauth \
+        xvfb \
+    && /opt/clean-after-apt.sh
+
+ENV DISPLAY 99
+
+CMD Xvfb -ac -listen tcp :$DISPLAY \
+    -noreset +extension GLX +render -screen 0 1920x1080x24
diff --git a/docker/windows/base/visualstudio2022/Dockerfile b/docker/windows/base/visualstudio2022/Dockerfile
index f7e0530..0b249bc 100644
--- a/docker/windows/base/visualstudio2022/Dockerfile
+++ b/docker/windows/base/visualstudio2022/Dockerfile
@@ -32,6 +32,8 @@
         --installPath C:\BuildTools `
         --add Microsoft.VisualStudio.Component.VC.CoreIde `
         --add Microsoft.VisualStudio.Component.VC.14.34.17.4.x86.x64 `
+        --add Microsoft.VisualStudio.Component.VC.Llvm.Clang `
+        --add Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset `
         --add Microsoft.VisualStudio.Component.Windows10SDK.18362';`
     Write-Host ('Cleaning up vs_buildtools.exe');`
     Remove-Item -Force -Recurse ${env:ProgramFiles(x86)}\'Microsoft Visual Studio'\Installer;`
diff --git a/nb/analytics/memory_tracker_helpers.h b/nb/analytics/memory_tracker_helpers.h
index 124bfaa..26bcfd7 100644
--- a/nb/analytics/memory_tracker_helpers.h
+++ b/nb/analytics/memory_tracker_helpers.h
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef NB_MEMORY_TRACKER_HELPERS_H_
-#define NB_MEMORY_TRACKER_HELPERS_H_
+#ifndef NB_ANALYTICS_MEMORY_TRACKER_HELPERS_H_
+#define NB_ANALYTICS_MEMORY_TRACKER_HELPERS_H_
 
+#include <functional>
 #include <map>
+#include <string>
+#include <utility>
 #include <vector>
 
 #include "nb/analytics/memory_tracker.h"
@@ -25,7 +28,7 @@
 #include "nb/std_allocator.h"
 #include "nb/thread_local_boolean.h"
 #include "nb/thread_local_pointer.h"
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/log.h"
 #include "starboard/common/mutex.h"
 #include "starboard/memory.h"
@@ -220,4 +223,4 @@
 }  // namespace analytics
 }  // namespace nb
 
-#endif  // NB_MEMORY_TRACKER_HELPERS_H_
+#endif  // NB_ANALYTICS_MEMORY_TRACKER_HELPERS_H_
diff --git a/nb/concurrent_ptr.h b/nb/concurrent_ptr.h
index d92f064..358f106 100644
--- a/nb/concurrent_ptr.h
+++ b/nb/concurrent_ptr.h
@@ -22,12 +22,11 @@
 #include <functional>
 #include <iostream>
 #include <memory>
-#include <mutex>
 #include <string>
 #include <utility>
 #include <vector>
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/log.h"
 #include "starboard/common/mutex.h"
 #include "starboard/memory.h"
@@ -81,7 +80,7 @@
 template <typename T, typename KeyT = int64_t, typename HashT = std::hash<KeyT>>
 class ConcurrentPtr {
  public:
-  ConcurrentPtr(T* ptr, size_t number_locks = 31) : ptr_(NULL) {
+  explicit ConcurrentPtr(T* ptr, size_t number_locks = 31) : ptr_(NULL) {
     internal_construct(ptr, number_locks);
   }
   ~ConcurrentPtr() { internal_destruct(); }
diff --git a/nb/concurrent_ptr_test.cc b/nb/concurrent_ptr_test.cc
index f9ce9f8..b564cd1 100644
--- a/nb/concurrent_ptr_test.cc
+++ b/nb/concurrent_ptr_test.cc
@@ -17,7 +17,7 @@
 #include "nb/concurrent_ptr.h"
 
 #include "nb/simple_thread.h"
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/semaphore.h"
 #include "starboard/thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/nb/simple_thread.h b/nb/simple_thread.h
index 1f9132d..e4b7386 100644
--- a/nb/simple_thread.h
+++ b/nb/simple_thread.h
@@ -14,12 +14,12 @@
  * limitations under the License.

  */

 

-#ifndef NB_THREAD_H_

-#define NB_THREAD_H_

+#ifndef NB_SIMPLE_THREAD_H_

+#define NB_SIMPLE_THREAD_H_

 

 #include <string>

 

-#include "starboard/atomic.h"

+#include "starboard/common/atomic.h"

 #include "starboard/thread.h"

 #include "starboard/time.h"

 #include "starboard/types.h"

@@ -62,4 +62,4 @@
 

 }  // namespace nb

 

-#endif  // NB_THREAD_H_

+#endif  // NB_SIMPLE_THREAD_H_

diff --git a/nb/thread_local_object_test.cc b/nb/thread_local_object_test.cc
index 9a243e3..7b214d1 100644
--- a/nb/thread_local_object_test.cc
+++ b/nb/thread_local_object_test.cc
@@ -18,7 +18,7 @@
 #include "nb/scoped_ptr.h"
 #include "nb/test_thread.h"
 #include "nb/thread_local_object.h"
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/mutex.h"
 #include "starboard/thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -41,8 +41,8 @@
 template <typename TYPE>
 class CreateThreadLocalObjectThenExit : public TestThread {
  public:
-  explicit CreateThreadLocalObjectThenExit(
-      ThreadLocalObject<TYPE>* tlo) : tlo_(tlo) {}
+  explicit CreateThreadLocalObjectThenExit(ThreadLocalObject<TYPE>* tlo)
+      : tlo_(tlo) {}
 
   virtual void Run() {
     // volatile as a defensive measure to prevent compiler from optimizing this
@@ -57,11 +57,11 @@
 template <typename TYPE>
 class DestroyTypeOnThread : public TestThread {
  public:
-  explicit DestroyTypeOnThread(TYPE* ptr)
-      : ptr_(ptr) {}
+  explicit DestroyTypeOnThread(TYPE* ptr) : ptr_(ptr) {}
   virtual void Run() {
     ptr_.reset(NULL);  // Destroys the object.
   }
+
  private:
   nb::scoped_ptr<TYPE> ptr_;
 };
@@ -135,9 +135,9 @@
 
   // Create the TLO object and then immediately destroy it.
   nb::scoped_ptr<TLO> tlo_ptr(new TLO);
-  tlo_ptr->GetOrCreate(); // Instantiate the internal object.
+  tlo_ptr->GetOrCreate();  // Instantiate the internal object.
   EXPECT_EQ(1, CountsInstances::NumInstances());
-  tlo_ptr.reset(NULL);    // Should destroy all outstanding allocs.
+  tlo_ptr.reset(NULL);  // Should destroy all outstanding allocs.
   // Now the TLO is destroyed and therefore the destructor should run on the
   // internal object.
   EXPECT_EQ(0, CountsInstances::NumInstances());
@@ -161,8 +161,8 @@
   tlo_ptr.reset(NULL);
   // 1 instance left, which is held in last_ref.
   EXPECT_EQ(1, CountsInstances::NumInstances());
-  last_ref.reset(NULL);   // Now the object should be destroyed and the
-                          // instance count drops to 0.
+  last_ref.reset(NULL);  // Now the object should be destroyed and the
+                         // instance count drops to 0.
   EXPECT_EQ(0, CountsInstances::NumInstances());
   CountsInstances::ResetNumInstances();
 }
@@ -210,7 +210,7 @@
   // Thread will simply create the thread local object (CountsInstances)
   // and then return.
   nb::scoped_ptr<TestThread> thread_ptr(
-        new CreateThreadLocalObjectThenExit<CountsInstances>(tlo));
+      new CreateThreadLocalObjectThenExit<CountsInstances>(tlo));
   thread_ptr->Start();  // Object is now created.
   thread_ptr->Join();   // ...then destroyed.
   thread_ptr.reset(NULL);
@@ -233,4 +233,4 @@
 }
 
 }  // anonymous namespace
-}  // nb namespace
+}  // namespace nb
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 1044e1d..ddbaa64 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -4232,6 +4232,7 @@
     data_deps = [
       ":net_unittest_files",
       ":third_party_unittest_files",
+      "//cobalt/network:copy_ssl_certificates",
       "//third_party/icu:icudata",
     ]
   }
diff --git a/net/base/io_buffer.cc b/net/base/io_buffer.cc
index d198c93..c2ffac6 100644
--- a/net/base/io_buffer.cc
+++ b/net/base/io_buffer.cc
@@ -137,8 +137,16 @@
 void GrowableIOBuffer::SetCapacity(int capacity) {
   DCHECK_GE(capacity, 0);
   // realloc will crash if it fails.
-  real_data_.reset(
-      static_cast<char*>(SbMemoryReallocate(real_data_.release(), capacity)));
+  // Calling reallocate with size 0 and a non-null pointer causes memory leaks
+  // on many platforms, since it may return nullptr while also not deallocating
+  // the previously allocated memory.
+  if (real_data_ && capacity == 0) {
+    SbMemoryDeallocate(real_data_.release());
+    real_data_.reset();
+  } else {
+    real_data_.reset(
+        static_cast<char*>(SbMemoryReallocate(real_data_.release(), capacity)));
+  }
   capacity_ = capacity;
   if (offset_ > capacity)
     set_offset(capacity);
diff --git a/net/url_request/test_url_fetcher_factory.cc b/net/url_request/test_url_fetcher_factory.cc
index 4216187..a5b1354 100644
--- a/net/url_request/test_url_fetcher_factory.cc
+++ b/net/url_request/test_url_fetcher_factory.cc
@@ -208,6 +208,10 @@
   return response_writer_.get();
 }
 
+const HttpRequestHeaders& TestURLFetcher::GetRequestHeaders() const {
+  return fake_extra_request_headers_;
+}
+
 HttpResponseHeaders* TestURLFetcher::GetResponseHeaders() const {
   return fake_response_headers_.get();
 }
diff --git a/net/url_request/test_url_fetcher_factory.h b/net/url_request/test_url_fetcher_factory.h
index ae2dd54..880d078 100644
--- a/net/url_request/test_url_fetcher_factory.h
+++ b/net/url_request/test_url_fetcher_factory.h
@@ -138,6 +138,7 @@
 #if defined(STARBOARD)
   URLFetcherResponseWriter* GetResponseWriter() const override;
 #endif
+  const HttpRequestHeaders& GetRequestHeaders() const override;
   HttpResponseHeaders* GetResponseHeaders() const override;
   HostPortPair GetSocketAddress() const override;
   const ProxyServer& ProxyServerUsed() const override;
diff --git a/net/url_request/url_fetcher.h b/net/url_request/url_fetcher.h
index 0606693..5bc635f 100644
--- a/net/url_request/url_fetcher.h
+++ b/net/url_request/url_fetcher.h
@@ -24,7 +24,7 @@
 class SequencedTaskRunner;
 class TaskRunner;
 class TimeDelta;
-}
+}  // namespace base
 
 namespace url {
 class Origin;
@@ -82,9 +82,7 @@
  public:
   // Imposible http response code. Used to signal that no http response code
   // was received.
-  enum ResponseCode {
-    RESPONSE_CODE_INVALID = -1
-  };
+  enum ResponseCode { RESPONSE_CODE_INVALID = -1 };
 
   enum RequestType {
     GET,
@@ -324,6 +322,9 @@
   virtual URLFetcherResponseWriter* GetResponseWriter() const = 0;
 #endif
 
+  // Retrieve the request headers from the request.
+  virtual const HttpRequestHeaders& GetRequestHeaders() const = 0;
+
   // Retrieve the response headers from the request.  Must only be called after
   // the OnURLFetchComplete callback has run.
   virtual HttpResponseHeaders* GetResponseHeaders() const = 0;
diff --git a/net/url_request/url_fetcher_core.cc b/net/url_request/url_fetcher_core.cc
index 6cdcfec..0d77b78 100644
--- a/net/url_request/url_fetcher_core.cc
+++ b/net/url_request/url_fetcher_core.cc
@@ -365,6 +365,10 @@
   response_writer_ = std::move(response_writer);
 }
 
+const HttpRequestHeaders& URLFetcherCore::GetRequestHeaders() const {
+  return extra_request_headers_;
+}
+
 HttpResponseHeaders* URLFetcherCore::GetResponseHeaders() const {
   return response_headers_.get();
 }
diff --git a/net/url_request/url_fetcher_core.h b/net/url_request/url_fetcher_core.h
index 2c4da97..f7ea3ac 100644
--- a/net/url_request/url_fetcher_core.h
+++ b/net/url_request/url_fetcher_core.h
@@ -123,6 +123,7 @@
     return response_writer_.get();
   }
 #endif
+  const HttpRequestHeaders& GetRequestHeaders() const;
   HttpResponseHeaders* GetResponseHeaders() const;
   HostPortPair GetSocketAddress() const;
   const ProxyServer& ProxyServerUsed() const;
diff --git a/net/url_request/url_fetcher_impl.cc b/net/url_request/url_fetcher_impl.cc
index 41e88ec..b651ce4 100644
--- a/net/url_request/url_fetcher_impl.cc
+++ b/net/url_request/url_fetcher_impl.cc
@@ -156,6 +156,10 @@
 }
 #endif
 
+const HttpRequestHeaders& URLFetcherImpl::GetRequestHeaders() const {
+  return core_->GetRequestHeaders();
+}
+
 HttpResponseHeaders* URLFetcherImpl::GetResponseHeaders() const {
   return core_->GetResponseHeaders();
 }
diff --git a/net/url_request/url_fetcher_impl.h b/net/url_request/url_fetcher_impl.h
index 3ad8bea..21aafed 100644
--- a/net/url_request/url_fetcher_impl.h
+++ b/net/url_request/url_fetcher_impl.h
@@ -84,6 +84,7 @@
 #if defined(STARBOARD)
   URLFetcherResponseWriter* GetResponseWriter() const override;
 #endif
+  const HttpRequestHeaders& GetRequestHeaders() const override;
   HttpResponseHeaders* GetResponseHeaders() const override;
   HostPortPair GetSocketAddress() const override;
   const ProxyServer& ProxyServerUsed() const override;
diff --git a/starboard/BUILD.gn b/starboard/BUILD.gn
index cb5ecb1..2193658 100644
--- a/starboard/BUILD.gn
+++ b/starboard/BUILD.gn
@@ -61,9 +61,16 @@
     if (sb_is_evergreen_compatible) {
       deps += [
         "//third_party/crashpad/client",
+
+        # TODO(b/270858365): remove this dependency on the Starboard-based
+        # target (from the target toolchain) once Buildbot has been updated to
+        # use the native target (from native_target toolchain).
         "//third_party/crashpad/handler",
       ]
-      data_deps = [ "//starboard/loader_app" ]
+      data_deps = [
+        "//starboard/loader_app",
+        "//third_party/crashpad/handler:crashpad_handler(//$starboard_path/toolchain:native_target)",
+      ]
     }
   }
 }
diff --git a/starboard/CHANGELOG.md b/starboard/CHANGELOG.md
index 49438ee..b0bb7b1 100644
--- a/starboard/CHANGELOG.md
+++ b/starboard/CHANGELOG.md
@@ -14,6 +14,11 @@
 can be found in the comments of the "Experimental Feature Defines" section of
 [configuration.h](configuration.h).
 
+### Removed version suffixes of SbPlayer functions and structures
+Renamed SbPlayerInfo2 to SbPlayerInfo, SbPlayerSeek2() to SbPlayerSeek(),and
+SbPlayerGetInfo2() to SbPlayerGetInfo(), as the version suffixes are no longer
+necessary.
+
 ### Cobalt extensions are now Starboard extensions
 Previously named Cobalt extensions are now found under `starboard/extensions`.
 The mechanism extends platform-specific functionality of Starboard via runtime
@@ -23,6 +28,37 @@
 For existing uses in Starboard ports, fallback forwarding headers are provided
 in the previous location of the code in `cobalt/extensions`.
 
+### Refined SbMediaAudioSampleInfo and SbMediaVideoSampleInfo
+Moved attributes of `SbMediaAudioSampleInfo` and `SbMedidaVideoSampleInfo` that
+specific to the stream (i.e. don't change per access unit) to
+`SbMediaAudioStreamInfo` and `SbMediaVideoStreamInfo`.
+
+### Add duration discard support to SbMediaAudioSampleInfo
+This allows to discard specific durations from the front and the back of any
+audio access units to improve the accuracy of audio playback.  For example, in
+an AAC stream where each access unit contains 1024 frames, this allows to
+discard the last 300 frames of the 1024 frames when the duration of the stream
+isn't aligned to 1024 frames.
+
+### Renamed SbPlayerWriteSample2() to SbPlayerWriteSamples()
+To better reflect the fact that it may write multiple samples in one call.
+
+### Add IAMF value to SbMediaAudioCodec.
+This makes it possible to support IAMF in the future.
+
+### Added SB_MODULAR_BUILD for supporting modular builds
+This configuration is set for modular builds, which have:
+  1. Application binary built as a shared library.
+  2. Either of a) or b)
+        a. Starboard built at a shared library and a separate loader_app executable.
+        b. A loader_app executable with Starboard built in ( Evergreen ).
+
+### Split C++ and C code from `starboard/atomic.h`
+`starboard/atomic.h` previously included C++ wrapper code for atomic operations.
+This code has been separated out and refactored into `starboard/common/atomic.h`
+to break dependency cycle between Starboard interface and Starboard Common C++
+library.
+
 ## Version 14
 ### Add MP3, FLAC, and PCM values to SbMediaAudioCodec.
 This makes it possible to support these codecs in the future.
diff --git a/starboard/android/apk/app/CMakeLists.txt b/starboard/android/apk/app/CMakeLists.txt
index 5b1cb17..8c7d129 100644
--- a/starboard/android/apk/app/CMakeLists.txt
+++ b/starboard/android/apk/app/CMakeLists.txt
@@ -75,6 +75,22 @@
             ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libcoat.so
 )
 
+if(EVERGREEN_COMPATIBLE)
+  # Follow the pattern used to import libcoat.so, above.
+  add_custom_command(OUTPUT crashpad_handler_lib
+      COMMAND ${CMAKE_CURRENT_LIST_DIR}/cobalt-ninja.sh
+              ${skip_ninja_arg} -C ${COBALT_PRODUCT_DIR} crashpad_handler
+      COMMAND ${CMAKE_COMMAND} -E copy
+              ${COBALT_LIBRARY_DIR}/libcrashpad_handler.so
+              ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libcrashpad_handler.so
+  )
+endif()
+
+set (lib_deps coat_lib)
+if(EVERGREEN_COMPATIBLE)
+  list(APPEND lib_deps crashpad_handler_lib)
+endif()
+
 # Make a symlink to the cobalt static content. We put it in a parent directory
 # of the library output, which corresponds to what the Gradle build sets for
 # android.sourceSets.<build-type>.assets.srcDir. This ends up overwriting the
@@ -82,7 +98,7 @@
 # for a given build config is the same for all architectures.
 # ("cobalt_content" never gets created, so this runs every time.)
 add_custom_command(OUTPUT cobalt_content
-    DEPENDS coat_lib
+    DEPENDS ${lib_deps}
     COMMAND ${CMAKE_COMMAND} -E make_directory
             ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/../../../../${COBALT_CONFIG}
     COMMAND ${CMAKE_COMMAND} -E create_symlink
@@ -100,6 +116,14 @@
     IMPORTED_LOCATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libcoat.so
 )
 
+if(EVERGREEN_COMPATIBLE)
+  # Follow the pattern used to include libcoat.so, above.
+  add_library(crashpadhandler SHARED IMPORTED)
+  set_target_properties(crashpadhandler PROPERTIES
+      IMPORTED_LOCATION ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libcrashpad_handler.so
+  )
+endif()
+
 # Make a phony "native" library, so Android Studio has something to build.
 file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/phony.cpp "void __phony() {}")
 add_library(native SHARED  ${CMAKE_CURRENT_BINARY_DIR}/phony.cpp)
diff --git a/starboard/android/apk/app/build.gradle b/starboard/android/apk/app/build.gradle
index c88e2d3..3a63b5c 100644
--- a/starboard/android/apk/app/build.gradle
+++ b/starboard/android/apk/app/build.gradle
@@ -34,6 +34,8 @@
             project.hasProperty('cobaltLibraryDir') ? new File(cobaltLibraryDir).canonicalPath : ''
     enableVulkan =
             project.hasProperty('enableVulkan') ? enableVulkan : 0
+    evergreenCompatible =
+            project.hasProperty('evergreenCompatible') ? evergreenCompatible : "false"
 
     buildIdFile = rootProject.file('build.id')
     buildId = buildIdFile.exists() ? buildIdFile.text.trim() : '0'
@@ -76,7 +78,10 @@
         targetSdkVersion 31
         versionCode 1
         versionName "${buildId}"
-        manifestPlaceholders = [applicationName: "CoAT: ${cobaltTarget}"]
+        manifestPlaceholders = [
+            applicationName: "CoAT: ${cobaltTarget}",
+            evergreenCompatible: "${evergreenCompatible}"
+        ]
         externalNativeBuild {
             cmake {
                 arguments "-DCOBALT_TARGET=${cobaltTarget}"
@@ -85,6 +90,7 @@
                 arguments "-DCOBALT_LIBRARY_DIR=${cobaltLibraryDir}"
                 arguments "-DCOBALT_PLATFORM_DEPLOY=${project.hasProperty('cobaltDeployApk')}"
                 arguments "-DENABLE_VULKAN=${enableVulkan}"
+                arguments "-DEVERGREEN_COMPATIBLE=${evergreenCompatible}"
             }
         }
     }
@@ -162,6 +168,14 @@
             buildStagingDirectory new File("${buildDir}.cxx").canonicalFile
         }
     }
+    packagingOptions {
+        jniLibs {
+            // extractNativeLibs, which we set, based on evergreenCompatible, in the manifest file,
+            // is deprecated. Gradle asks that useLegacyPackaging be set if extractNativeLibs is
+            // set, and complains if they have different values. To be safe, both are set for now.
+            useLegacyPackaging "${evergreenCompatible}" == "true" ? true : false
+        }
+    }
 }
 
 // When running in the platform deploy action, make tasks that depend on the appropriate Android
diff --git a/starboard/android/apk/app/src/app/AndroidManifest.xml b/starboard/android/apk/app/src/app/AndroidManifest.xml
index 9c4e11a..0bece9b 100644
--- a/starboard/android/apk/app/src/app/AndroidManifest.xml
+++ b/starboard/android/apk/app/src/app/AndroidManifest.xml
@@ -34,12 +34,12 @@
 
   <!-- https://iabtechlab.com/OTT-IFA, AdvertisingIdClient.Info.getId() -->
   <uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
-
   <application
     android:name="dev.cobalt.app.CobaltApplication"
     android:icon="@mipmap/ic_app"
     android:banner="@drawable/app_banner"
-    android:label="${applicationName}">
+    android:label="${applicationName}"
+    android:extractNativeLibs="${evergreenCompatible}">
 
     <activity android:name="dev.cobalt.app.MainActivity"
       android:exported="true"
@@ -49,6 +49,7 @@
       android:theme="@style/CobaltTheme">
       <meta-data android:name="cobalt.APP_URL" android:value="https://www.youtube.com/tv"/>
       <meta-data android:name="cobalt.SPLASH_URL" android:value="h5vcc-embedded://cobalt_splash_screen.html"/>
+      <meta-data android:name="cobalt.EVERGREEN_LITE" android:value="false"/>
       <meta-data android:name="android.app.lib_name" android:value="coat"/>
       <intent-filter>
         <action android:name="android.intent.action.MAIN"/>
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
index eae0f49..0592441 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
@@ -62,6 +62,9 @@
   private static final String META_FORCE_MIGRATION_FOR_STORAGE_PARTITIONING =
       "cobalt.force_migration_for_storage_partitioning";
 
+  private static final String EVERGREEN_LITE = "--evergreen_lite";
+  private static final java.lang.String META_DATA_EVERGREEN_LITE = "cobalt.EVERGREEN_LITE";
+
   @SuppressWarnings("unused")
   private CobaltA11yHelper a11yHelper;
 
@@ -220,7 +223,9 @@
     boolean hasSplashUrlArg = hasArg(args, SPLASH_URL_ARG);
     // If the splash screen topics arg isn't specified, get it from AndroidManifest.xml.
     boolean hasSplashTopicsArg = hasArg(args, SPLASH_TOPICS_ARG);
-    if (!hasUrlArg || !hasSplashUrlArg || !hasSplashTopicsArg) {
+    // If the Evergreen-Lite arg isn't specified, get it from AndroidManifest.xml.
+    boolean hasEvergreenLiteArg = hasArg(args, EVERGREEN_LITE);
+    if (!hasUrlArg || !hasSplashUrlArg || !hasSplashTopicsArg || !hasEvergreenLiteArg) {
       try {
         ActivityInfo ai =
             getPackageManager()
@@ -244,6 +249,9 @@
               args.add(SPLASH_TOPICS_ARG + splashTopics);
             }
           }
+          if (!hasEvergreenLiteArg && ai.metaData.getBoolean(META_DATA_EVERGREEN_LITE)) {
+            args.add(EVERGREEN_LITE);
+          }
           if (ai.metaData.getBoolean(META_FORCE_MIGRATION_FOR_STORAGE_PARTITIONING)) {
             args.add(FORCE_MIGRATION_FOR_STORAGE_PARTITIONING);
           }
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
index b323fcb..e1cf4f7 100644
--- a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
@@ -80,6 +80,7 @@
   private NetworkStatus networkStatus;
   private ResourceOverlay resourceOverlay;
   private AdvertisingId advertisingId;
+  private VolumeStateReceiver volumeStateReceiver;
 
   static {
     // Even though NativeActivity already loads our library from C++,
@@ -136,6 +137,7 @@
     this.networkStatus = new NetworkStatus(appContext);
     this.resourceOverlay = new ResourceOverlay(appContext);
     this.advertisingId = new AdvertisingId(appContext);
+    this.volumeStateReceiver = new VolumeStateReceiver(appContext);
   }
 
   private native boolean nativeInitialize();
diff --git a/starboard/android/apk/app/src/main/java/dev/cobalt/coat/VolumeStateReceiver.java b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/VolumeStateReceiver.java
new file mode 100644
index 0000000..c2b94c3
--- /dev/null
+++ b/starboard/android/apk/app/src/main/java/dev/cobalt/coat/VolumeStateReceiver.java
@@ -0,0 +1,47 @@
+package dev.cobalt.coat;
+
+import static dev.cobalt.util.Log.TAG;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+/** VolumeStateReceiver monitors Android media broadcast to capture volume button events. */
+final class VolumeStateReceiver extends BroadcastReceiver {
+
+  public static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION";
+  public static final String EXTRA_VOLUME_STREAM_VALUE = "android.media.EXTRA_VOLUME_STREAM_VALUE";
+  public static final String EXTRA_PREV_VOLUME_STREAM_VALUE =
+      "android.media.EXTRA_PREV_VOLUME_STREAM_VALUE";
+
+  public static final String STREAM_MUTE_CHANGED_ACTION =
+      "android.media.STREAM_MUTE_CHANGED_ACTION";
+
+  VolumeStateReceiver(Context appContext) {
+    IntentFilter filter = new IntentFilter();
+    filter.addAction(VOLUME_CHANGED_ACTION);
+    filter.addAction(STREAM_MUTE_CHANGED_ACTION);
+    appContext.registerReceiver(this, filter);
+  }
+
+  @Override
+  public void onReceive(Context context, Intent intent) {
+    if (intent.getAction().equals(VOLUME_CHANGED_ACTION)) {
+      int newVolume = intent.getIntExtra(EXTRA_VOLUME_STREAM_VALUE, 0);
+      int oldVolume = intent.getIntExtra(EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
+
+      int volumeDelta = newVolume - oldVolume;
+      Log.d(TAG, "VolumeStateReceiver capture volume changed, volumeDelta:" + volumeDelta);
+      nativeVolumeChanged(volumeDelta);
+    } else if (intent.getAction().equals(STREAM_MUTE_CHANGED_ACTION)) {
+      Log.d(TAG, "VolumeStateReceiver capture mute changed.");
+      nativeMuteChanged();
+    }
+  }
+
+  private native void nativeVolumeChanged(int volumeDelta);
+
+  private native void nativeMuteChanged();
+}
diff --git a/starboard/android/arm/platform_configuration/BUILD.gn b/starboard/android/arm/platform_configuration/BUILD.gn
index 56a3cda..544d4c0 100644
--- a/starboard/android/arm/platform_configuration/BUILD.gn
+++ b/starboard/android/arm/platform_configuration/BUILD.gn
@@ -14,11 +14,9 @@
 
 config("platform_configuration") {
   configs = [ "//starboard/android/shared/platform_configuration" ]
-  if (current_toolchain == default_toolchain) {
-    cflags = [ "-march=armv7-a" ]
-    ldflags = [
-      # Mimic build/cmake/android.toolchain.cmake in the Android NDK.
-      "-Wl,--exclude-libs,libunwind.a",
-    ]
-  }
+  cflags = [ "-march=armv7-a" ]
+  ldflags = [
+    # Mimic build/cmake/android.toolchain.cmake in the Android NDK.
+    "-Wl,--exclude-libs,libunwind.a",
+  ]
 }
diff --git a/starboard/android/arm/toolchain/BUILD.gn b/starboard/android/arm/toolchain/BUILD.gn
index ddc2c46..427eb0b 100644
--- a/starboard/android/arm/toolchain/BUILD.gn
+++ b/starboard/android/arm/toolchain/BUILD.gn
@@ -16,15 +16,6 @@
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/android/shared/toolchain/toolchain.gni")
 
-clang_toolchain("host") {
-  clang_base_path = clang_base_path
-
-  toolchain_args = {
-    current_os = "linux"
-    current_cpu = "x86"
-  }
-}
-
 gcc_toolchain("target") {
   prefix = rebase_path("$android_toolchain_path/bin", root_build_dir)
   cc = "$prefix/armv7a-linux-androideabi${android_ndk_api_level}-clang"
@@ -38,3 +29,19 @@
     is_clang = true
   }
 }
+
+gcc_toolchain("native_target") {
+  prefix = rebase_path("$android_toolchain_path/bin", root_build_dir)
+  cc = "$prefix/armv7a-linux-androideabi${android_ndk_api_level}-clang"
+  cxx = "$prefix/armv7a-linux-androideabi${android_ndk_api_level}-clang++"
+  ld = cxx
+  ar = "$prefix/arm-linux-androideabi-readelf"
+  ar = "$prefix/arm-linux-androideabi-ar"
+  nm = "$prefix/arm-linux-androideabi-nm"
+
+  toolchain_args = {
+    is_starboard = false
+    is_native_target_build = true
+    is_clang = true
+  }
+}
diff --git a/starboard/android/arm64/toolchain/BUILD.gn b/starboard/android/arm64/toolchain/BUILD.gn
index 554b007..6c98a14 100644
--- a/starboard/android/arm64/toolchain/BUILD.gn
+++ b/starboard/android/arm64/toolchain/BUILD.gn
@@ -16,15 +16,6 @@
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/android/shared/toolchain/toolchain.gni")
 
-clang_toolchain("host") {
-  clang_base_path = clang_base_path
-
-  toolchain_args = {
-    current_os = "linux"
-    current_cpu = "x64"
-  }
-}
-
 gcc_toolchain("target") {
   prefix = rebase_path("$android_toolchain_path/bin", root_build_dir)
   cc = "$prefix/aarch64-linux-android${android_ndk_api_level}-clang"
diff --git a/starboard/android/arm64/vulkan/platform_configuration/BUILD.gn b/starboard/android/arm64/vulkan/platform_configuration/BUILD.gn
index 4f5e185..d9a5bdc 100644
--- a/starboard/android/arm64/vulkan/platform_configuration/BUILD.gn
+++ b/starboard/android/arm64/vulkan/platform_configuration/BUILD.gn
@@ -15,19 +15,17 @@
 config("platform_configuration") {
   configs = [ "//starboard/android/shared/platform_configuration" ]
 
-  if (current_toolchain == default_toolchain) {
-    libs = [
-      "c++_shared",
-      "EGL_angle",
-      "GLESv2_angle",
-      "GLESv1_CM_angle",
-      "feature_support_angle",
-    ]
+  libs = [
+    "c++_shared",
+    "EGL_angle",
+    "GLESv2_angle",
+    "GLESv1_CM_angle",
+    "feature_support_angle",
+  ]
 
-    ldflags = [
-      "-Wl",
-      "--verbose",
-      "-Llib",
-    ]
-  }
+  ldflags = [
+    "-Wl",
+    "--verbose",
+    "-Llib",
+  ]
 }
diff --git a/starboard/android/arm64/vulkan/toolchain/BUILD.gn b/starboard/android/arm64/vulkan/toolchain/BUILD.gn
index 554b007..6c98a14 100644
--- a/starboard/android/arm64/vulkan/toolchain/BUILD.gn
+++ b/starboard/android/arm64/vulkan/toolchain/BUILD.gn
@@ -16,15 +16,6 @@
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/android/shared/toolchain/toolchain.gni")
 
-clang_toolchain("host") {
-  clang_base_path = clang_base_path
-
-  toolchain_args = {
-    current_os = "linux"
-    current_cpu = "x64"
-  }
-}
-
 gcc_toolchain("target") {
   prefix = rebase_path("$android_toolchain_path/bin", root_build_dir)
   cc = "$prefix/aarch64-linux-android${android_ndk_api_level}-clang"
diff --git a/starboard/android/shared/BUILD.gn b/starboard/android/shared/BUILD.gn
index c000807..158e0a6 100644
--- a/starboard/android/shared/BUILD.gn
+++ b/starboard/android/shared/BUILD.gn
@@ -253,8 +253,6 @@
     "//starboard/shared/starboard/media/media_get_buffer_storage_type.cc",
     "//starboard/shared/starboard/media/media_get_progressive_buffer_budget.cc",
     "//starboard/shared/starboard/media/media_is_buffer_using_memory_pool.cc",
-    "//starboard/shared/starboard/media/mime_type.cc",
-    "//starboard/shared/starboard/media/mime_type.h",
     "//starboard/shared/starboard/memory.cc",
     "//starboard/shared/starboard/microphone/microphone_close.cc",
     "//starboard/shared/starboard/microphone/microphone_create.cc",
@@ -369,6 +367,8 @@
     "log_is_tty.cc",
     "log_raw.cc",
     "main.cc",
+    "max_output_buffers_lookup_table.cc",
+    "max_output_buffers_lookup_table.h",
     "media_capabilities_cache.cc",
     "media_capabilities_cache.h",
     "media_codec_bridge.cc",
@@ -489,11 +489,23 @@
   }
 
   if (sb_is_evergreen_compatible) {
-    public_deps += [ "//starboard/elf_loader:evergreen_config" ]
+    sources -= [
+      "crash_handler.cc",
+      "crash_handler.h",
+    ]
+    sources += [
+      "//starboard/shared/starboard/crash_handler.cc",
+      "//starboard/shared/starboard/crash_handler.h",
+      "//starboard/shared/starboard/starboard_switches.cc",
+      "//starboard/shared/starboard/starboard_switches.h",
+    ]
 
-    if (!sb_evergreen_compatible_enable_lite) {
-      public_deps += [ "//starboard/loader_app:pending_restart" ]
-    }
+    public_deps += [
+      "//starboard/elf_loader:evergreen_config",
+      "//starboard/loader_app:pending_restart",
+    ]
+
+    deps += [ "//third_party/crashpad/wrapper" ]
   }
 
   if (sb_evergreen_compatible_use_libunwind) {
diff --git a/starboard/android/shared/android_main.cc b/starboard/android/shared/android_main.cc
index 755210d..4c2e911 100644
--- a/starboard/android/shared/android_main.cc
+++ b/starboard/android/shared/android_main.cc
@@ -19,9 +19,18 @@
 #include "starboard/android/shared/log_internal.h"
 #include "starboard/common/semaphore.h"
 #include "starboard/common/string.h"
+#if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/directory.h"
+#include "starboard/file.h"
+#endif
+#include "starboard/event.h"
 #include "starboard/log.h"
 #include "starboard/shared/starboard/command_line.h"
+#include "starboard/shared/starboard/starboard_switches.h"
 #include "starboard/thread.h"
+#if SB_IS(EVERGREEN_COMPATIBLE)
+#include "third_party/crashpad/wrapper/wrapper.h"  // nogncheck
+#endif
 
 namespace starboard {
 namespace android {
@@ -33,6 +42,7 @@
     AndroidCommand;
 
 SbThread g_starboard_thread = kSbThreadInvalid;
+Semaphore* g_app_created_semaphore = nullptr;
 
 // Safeguard to avoid sending AndroidCommands either when there is no instance
 // of the Starboard application, or after the run loop has exited and the
@@ -73,25 +83,165 @@
   return start_url;
 }
 
-void* ThreadEntryPoint(void* context) {
-  Semaphore* app_created_semaphore = static_cast<Semaphore*>(context);
+#if SB_IS(EVERGREEN_COMPATIBLE)
+bool CopyDirContents(const std::string& src_dir_path,
+                     const std::string& dst_dir_path) {
+  SbDirectory src_dir = SbDirectoryOpen(src_dir_path.c_str(), NULL);
+  if (!SbDirectoryIsValid(src_dir)) {
+    SB_LOG(WARNING) << "Failed to open dir=" << src_dir_path;
+    return false;
+  }
 
+  std::vector<char> filename_buffer(kSbFileMaxName);
+  while (SbDirectoryGetNext(src_dir, filename_buffer.data(),
+                            filename_buffer.size())) {
+    std::string filename(filename_buffer.begin(), filename_buffer.end());
+    std::string path_to_src_file = src_dir_path + kSbFileSepString + filename;
+    SbFile src_file =
+        SbFileOpen(path_to_src_file.c_str(), kSbFileOpenOnly | kSbFileRead,
+                   nullptr, nullptr);
+    if (src_file == kSbFileInvalid) {
+      SB_LOG(WARNING) << "Failed to open file=" << path_to_src_file;
+      return false;
+    }
+
+    SbFileInfo info;
+    if (!SbFileGetInfo(src_file, &info)) {
+      SB_LOG(WARNING) << "Failed to get info for file=" << path_to_src_file;
+      SbFileClose(src_file);
+      return false;
+    }
+
+    int file_size = static_cast<int>(info.size);
+
+    // Read in bytes from src file
+    char file_contents_buffer[file_size];
+    int read = SbFileReadAll(src_file, file_contents_buffer, file_size);
+    if (read == -1) {
+      SB_LOG(WARNING) << "SbFileReadAll failed for file=" << path_to_src_file;
+      return false;
+    }
+    const std::string file_contents =
+        std::string(file_contents_buffer, file_size);
+    SbFileClose(src_file);
+
+    // Write bytes out to dst file
+    std::string path_to_dst_file = dst_dir_path;
+    path_to_dst_file.append(kSbFileSepString);
+    path_to_dst_file.append(filename);
+    SbFile dst_file =
+        SbFileOpen(path_to_dst_file.c_str(), kSbFileCreateAlways | kSbFileWrite,
+                   NULL, NULL);
+    if (dst_file == kSbFileInvalid) {
+      SB_LOG(WARNING) << "Failed to open file=" << path_to_dst_file;
+      return false;
+    }
+    int wrote = SbFileWriteAll(dst_file, file_contents.c_str(), file_size);
+    if (wrote == -1) {
+      SB_LOG(WARNING) << "SbFileWriteAll failed for file=" << path_to_dst_file;
+      return false;
+    }
+    SbFileClose(dst_file);
+  }
+
+  SbDirectoryClose(src_dir);
+  return true;
+}
+
+// Extracts CA certificates from the APK to the file system and returns the path
+// to the directory containing the extracted certifictes, or an empty string on
+// error.
+std::string ExtractCertificatesToFileSystem() {
+  std::vector<char> apk_path_buffer(kSbFileMaxPath);
+  if (!SbSystemGetPath(kSbSystemPathContentDirectory, apk_path_buffer.data(),
+                       apk_path_buffer.size())) {
+    SB_LOG(WARNING) << "Failed to get path to content dir in APK";
+    return "";
+  }
+
+  std::string apk_path(apk_path_buffer.data());
+  apk_path.append(std::string(kSbFileSepString) + "app" + kSbFileSepString +
+                  "cobalt" + kSbFileSepString + "content" + kSbFileSepString +
+                  "ssl" + kSbFileSepString + "certs");
+  if (!SbFileExists(apk_path.c_str())) {
+    SB_LOG(WARNING) << "CA certificates directory not found in APK";
+    return "";
+  }
+
+  std::vector<char> file_system_path_buffer(kSbFileMaxPath);
+  if (!SbSystemGetPath(kSbSystemPathCacheDirectory,
+                       file_system_path_buffer.data(),
+                       file_system_path_buffer.size())) {
+    SB_LOG(WARNING) << "Failed to get path to cache dir on file system";
+    return "";
+  }
+
+  std::string file_system_path(file_system_path_buffer.data());
+  file_system_path.append(std::string(kSbFileSepString) + "certs");
+  if (!SbDirectoryCreate(file_system_path.c_str())) {
+    SB_LOG(WARNING) << "Failed to create new dir for CA certificates";
+    return "";
+  }
+
+  if (!CopyDirContents(apk_path, file_system_path)) {
+    SB_LOG(WARNING) << "Failed to copy CA certificates to the file system";
+    return "";
+  }
+
+  return file_system_path;
+}
+
+void InstallCrashpadHandler(const CommandLine& command_line) {
+  if (command_line.HasSwitch(
+          starboard::shared::starboard::kStartHandlerAtLaunch)) {
+    SB_LOG(WARNING) << "--"
+                    << starboard::shared::starboard::kStartHandlerAtLaunch
+                    << " not supported for AOSP Evergreen, not installing "
+                    << "Crashpad handler";
+    return;
+  }
+
+  std::string extracted_ca_certificates_path =
+      ExtractCertificatesToFileSystem();
+  if (extracted_ca_certificates_path.empty()) {
+    SB_LOG(WARNING) << "Failed to extract CA certificates to file system, not "
+                    << "installing Crashpad handler";
+    return;
+  }
+
+  third_party::crashpad::wrapper::InstallCrashpadHandler(
+      /*start_at_crash=*/true, extracted_ca_certificates_path);
+}
+#endif  // SB_IS(EVERGREEN_COMPATIBLE)
+
+void* ThreadEntryPoint(void* context) {
+  g_app_created_semaphore = static_cast<Semaphore*>(context);
+
+#if SB_MODULAR_BUILD
+  int unused_value = -1;
+  int error_level = SbRunStarboardMain(unused_value, nullptr, SbEventHandle);
+#else
   ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
   ApplicationAndroid app(looper);
 
   CommandLine command_line(GetArgs());
   LogInit(command_line);
 
+#if SB_IS(EVERGREEN_COMPATIBLE)
+  InstallCrashpadHandler(command_line);
+#endif  // SB_IS(EVERGREEN_COMPATIBLE)
+
   // Mark the app running before signaling app created so there's no race to
   // allow sending the first AndroidCommand after onCreate() returns.
   g_app_running = true;
 
   // Signal GameActivity_onCreate() that it may proceed.
-  app_created_semaphore->Put();
+  g_app_created_semaphore->Put();
 
   // Enter the Starboard run loop until stopped.
   int error_level =
       app.Run(std::move(command_line), GetStartDeepLink().c_str());
+#endif  // SB_MODULAR_BUILD
 
   // Mark the app not running before informing StarboardBridge that the app is
   // stopped so that we won't send any more AndroidCommands as a result of
@@ -225,6 +375,31 @@
 }
 
 }  // namespace
+
+#if SB_MODULAR_BUILD
+extern "C" int SbRunStarboardMain(int argc,
+                                  char** argv,
+                                  SbEventHandleCallback callback) {
+  ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
+  ApplicationAndroid app(looper, callback);
+
+  CommandLine command_line(GetArgs());
+  LogInit(command_line);
+
+  // Mark the app running before signaling app created so there's no race to
+  // allow sending the first AndroidCommand after onCreate() returns.
+  g_app_running = true;
+
+  // Signal GameActivity_onCreate() that it may proceed.
+  g_app_created_semaphore->Put();
+
+  // Enter the Starboard run loop until stopped.
+  int error_level =
+      app.Run(std::move(command_line), GetStartDeepLink().c_str());
+  return error_level;
+}
+#endif  // SB_MODULAR_BUILD
+
 }  // namespace shared
 }  // namespace android
 }  // namespace starboard
diff --git a/starboard/android/shared/application_android.cc b/starboard/android/shared/application_android.cc
index 37a8077..45c2488 100644
--- a/starboard/android/shared/application_android.cc
+++ b/starboard/android/shared/application_android.cc
@@ -34,6 +34,7 @@
 #include "starboard/common/mutex.h"
 #include "starboard/common/string.h"
 #include "starboard/event.h"
+#include "starboard/key.h"
 #include "starboard/shared/starboard/audio_sink/audio_sink_internal.h"
 
 namespace starboard {
@@ -79,7 +80,13 @@
 // "using" doesn't work with class members, so make a local convenience type.
 typedef ::starboard::shared::starboard::Application::Event Event;
 
+#if SB_MODULAR_BUILD
+ApplicationAndroid::ApplicationAndroid(
+    ALooper* looper,
+    SbEventHandleCallback sb_event_handle_callback)
+#else
 ApplicationAndroid::ApplicationAndroid(ALooper* looper)
+#endif  // SB_MODULAR_BUILD
     : looper_(looper),
       native_window_(NULL),
       android_command_readfd_(-1),
@@ -89,6 +96,9 @@
       android_command_condition_(android_command_mutex_),
       activity_state_(AndroidCommand::kUndefined),
       window_(kSbWindowInvalid),
+#if SB_MODULAR_BUILD
+      QueueApplication(sb_event_handle_callback),
+#endif  // SB_MODULAR_BUILD
       last_is_accessibility_high_contrast_text_enabled_(false) {
   // Initialize Time Zone early so that local time works correctly.
   // Called once here to help SbTimeZoneGet*Name()
@@ -736,6 +746,20 @@
   return value;
 }
 
+extern "C" SB_EXPORT_PLATFORM void
+Java_dev_cobalt_coat_VolumeStateReceiver_nativeVolumeChanged(JNIEnv* env,
+                                                             jobject jcaller,
+                                                             jint volumeDelta) {
+  SbKey key = volumeDelta > 0 ? SbKey::kSbKeyVolumeUp : SbKey::kSbKeyVolumeDown;
+  ApplicationAndroid::Get()->SendKeyboardInject(key);
+}
+
+extern "C" SB_EXPORT_PLATFORM void
+Java_dev_cobalt_coat_VolumeStateReceiver_nativeMuteChanged(JNIEnv* env,
+                                                           jobject jcaller) {
+  ApplicationAndroid::Get()->SendKeyboardInject(SbKey::kSbKeyVolumeMute);
+}
+
 }  // namespace shared
 }  // namespace android
 }  // namespace starboard
diff --git a/starboard/android/shared/application_android.h b/starboard/android/shared/application_android.h
index 6cdb792..60f24bf 100644
--- a/starboard/android/shared/application_android.h
+++ b/starboard/android/shared/application_android.h
@@ -63,7 +63,12 @@
     void* data;
   };
 
+#if SB_MODULAR_BUILD
+  ApplicationAndroid(ALooper* looper,
+                     SbEventHandleCallback sb_event_handle_callback);
+#else
   explicit ApplicationAndroid(ALooper* looper);
+#endif  //  SB_MODULAR_BUILD
   ~ApplicationAndroid() override;
 
   static ApplicationAndroid* Get() {
diff --git a/starboard/android/shared/audio_decoder.cc b/starboard/android/shared/audio_decoder.cc
index fb81100..dfa32dd 100644
--- a/starboard/android/shared/audio_decoder.cc
+++ b/starboard/android/shared/audio_decoder.cc
@@ -54,6 +54,9 @@
 
 namespace {
 
+using std::placeholders::_1;
+using std::placeholders::_2;
+
 SbMediaAudioSampleType GetSupportedSampleType() {
   SB_DCHECK(SbAudioSinkIsAudioSampleTypeSupported(
       kSbMediaAudioSampleTypeInt16Deprecated));
@@ -66,14 +69,12 @@
 
 }  // namespace
 
-AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec,
-                           const SbMediaAudioSampleInfo& audio_sample_info,
+AudioDecoder::AudioDecoder(const AudioStreamInfo& audio_stream_info,
                            SbDrmSystem drm_system)
-    : audio_codec_(audio_codec),
-      audio_sample_info_(audio_sample_info),
+    : audio_stream_info_(audio_stream_info),
       sample_type_(GetSupportedSampleType()),
-      output_sample_rate_(audio_sample_info.samples_per_second),
-      output_channel_count_(audio_sample_info.number_of_channels),
+      output_sample_rate_(audio_stream_info.samples_per_second),
+      output_channel_count_(audio_stream_info.number_of_channels),
       drm_system_(static_cast<DrmSystem*>(drm_system)) {
   if (!InitializeCodec()) {
     SB_LOG(ERROR) << "Failed to initialize audio decoder.";
@@ -94,7 +95,8 @@
   output_cb_ = output_cb;
   error_cb_ = error_cb;
 
-  media_decoder_->Initialize(error_cb_);
+  media_decoder_->Initialize(
+      std::bind(&AudioDecoder::ReportError, this, _1, _2));
 }
 
 void AudioDecoder::Decode(const InputBuffers& input_buffers,
@@ -104,6 +106,8 @@
   SB_DCHECK(output_cb_);
   SB_DCHECK(media_decoder_);
 
+  audio_frame_discarder_.OnInputBuffers(input_buffers);
+
   for (const auto& input_buffer : input_buffers) {
     VERBOSE_MEDIA_LOG() << "T1: timestamp " << input_buffer->timestamp();
   }
@@ -147,7 +151,7 @@
     Schedule(consumed_cb_);
     consumed_cb_ = nullptr;
   }
-  *samples_per_second = audio_sample_info_.samples_per_second;
+  *samples_per_second = audio_stream_info_.samples_per_second;
   return result;
 }
 
@@ -156,6 +160,7 @@
   SB_DCHECK(output_cb_);
 
   media_decoder_.reset();
+  audio_frame_discarder_.Reset();
 
   if (!InitializeCodec()) {
     // TODO: Communicate this failure to our clients somehow.
@@ -173,11 +178,11 @@
 
 bool AudioDecoder::InitializeCodec() {
   SB_DCHECK(!media_decoder_);
-  media_decoder_.reset(
-      new MediaDecoder(this, audio_codec_, audio_sample_info_, drm_system_));
+  media_decoder_.reset(new MediaDecoder(this, audio_stream_info_, drm_system_));
   if (media_decoder_->is_valid()) {
     if (error_cb_) {
-      media_decoder_->Initialize(error_cb_);
+      media_decoder_->Initialize(
+          std::bind(&AudioDecoder::ReportError, this, _1, _2));
     }
     return true;
   }
@@ -195,12 +200,17 @@
   if (dequeue_output_result.num_bytes > 0) {
     ScopedJavaByteBuffer byte_buffer(
         media_codec_bridge->GetOutputBuffer(dequeue_output_result.index));
-    SB_DCHECK(!byte_buffer.IsNull());
+
+    if (byte_buffer.IsNull()) {
+      ReportError(kSbPlayerErrorDecode,
+                  "Failed to process audio output buffer.");
+      return;
+    }
 
     int16_t* data = static_cast<int16_t*>(IncrementPointerByBytes(
         byte_buffer.address(), dequeue_output_result.offset));
     int size = dequeue_output_result.num_bytes;
-    if (2 * audio_sample_info_.samples_per_second == output_sample_rate_) {
+    if (2 * audio_stream_info_.samples_per_second == output_sample_rate_) {
       // The audio is encoded using implicit HE-AAC.  As the audio sink has
       // been created already we try to down-mix the decoded data to half of
       // its channels so the audio sink can play it with the correct pitch.
@@ -212,11 +222,13 @@
     }
 
     scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
-        audio_sample_info_.number_of_channels, sample_type_,
+        audio_stream_info_.number_of_channels, sample_type_,
         kSbMediaAudioFrameStorageTypeInterleaved,
         dequeue_output_result.presentation_time_microseconds, size);
 
-    memcpy(decoded_audio->buffer(), data, size);
+    memcpy(decoded_audio->data(), data, size);
+    audio_frame_discarder_.AdjustForDiscardedDurations(
+        audio_stream_info_.samples_per_second, &decoded_audio);
 
     {
       starboard::ScopedLock lock(decoded_audios_mutex_);
@@ -233,6 +245,7 @@
       starboard::ScopedLock lock(decoded_audios_mutex_);
       decoded_audios_.push(new DecodedAudio());
     }
+    audio_frame_discarder_.OnDecodedAudioEndOfStream();
     Schedule(output_cb_);
   }
 
@@ -250,6 +263,17 @@
   output_channel_count_ = output_format.channel_count;
 }
 
+void AudioDecoder::ReportError(SbPlayerError error,
+                               const std::string& error_message) {
+  SB_DCHECK(error_cb_);
+
+  if (!error_cb_) {
+    return;
+  }
+
+  error_cb_(kSbPlayerErrorDecode, error_message);
+}
+
 }  // namespace shared
 }  // namespace android
 }  // namespace starboard
diff --git a/starboard/android/shared/audio_decoder.h b/starboard/android/shared/audio_decoder.h
index f9968b4..a57f0ff 100644
--- a/starboard/android/shared/audio_decoder.h
+++ b/starboard/android/shared/audio_decoder.h
@@ -18,6 +18,7 @@
 #include <jni.h>
 
 #include <queue>
+#include <string>
 
 #include "starboard/android/shared/drm_system.h"
 #include "starboard/android/shared/media_codec_bridge.h"
@@ -28,6 +29,7 @@
 #include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
+#include "starboard/shared/starboard/player/filter/audio_frame_discarder.h"
 #include "starboard/shared/starboard/player/job_queue.h"
 
 namespace starboard {
@@ -39,11 +41,10 @@
       private ::starboard::shared::starboard::player::JobQueue::JobOwner,
       private MediaDecoder::Host {
  public:
-  typedef ::starboard::shared::starboard::media::AudioSampleInfo
-      AudioSampleInfo;
+  typedef ::starboard::shared::starboard::media::AudioStreamInfo
+      AudioStreamInfo;
 
-  AudioDecoder(SbMediaAudioCodec audio_codec,
-               const SbMediaAudioSampleInfo& audio_sample_info,
+  AudioDecoder(const AudioStreamInfo& audio_stream_info,
                SbDrmSystem drm_system);
   ~AudioDecoder() override;
 
@@ -57,6 +58,9 @@
   bool is_valid() const { return media_decoder_ != NULL; }
 
  private:
+  typedef ::starboard::shared::starboard::player::filter::AudioFrameDiscarder
+      AudioFrameDiscarder;
+
   // The maximum amount of work that can exist in the union of |decoded_audios_|
   // and |media_decoder_->GetNumberOfPendingTasks()|.
   static const int kMaxPendingWorkSize = 64;
@@ -69,9 +73,10 @@
   bool Tick(MediaCodecBridge* media_codec_bridge) override { return false; }
   void OnFlushing() override {}
 
-  SbMediaAudioCodec audio_codec_;
-  AudioSampleInfo audio_sample_info_;
-  SbMediaAudioSampleType sample_type_;
+  void ReportError(SbPlayerError error, const std::string& error_message);
+
+  const AudioStreamInfo audio_stream_info_;
+  const SbMediaAudioSampleType sample_type_;
 
   jint output_sample_rate_;
   jint output_channel_count_;
@@ -85,6 +90,7 @@
   starboard::Mutex decoded_audios_mutex_;
   std::queue<scoped_refptr<DecodedAudio> > decoded_audios_;
 
+  AudioFrameDiscarder audio_frame_discarder_;
   scoped_ptr<MediaDecoder> media_decoder_;
 };
 
diff --git a/starboard/android/shared/audio_decoder_passthrough.h b/starboard/android/shared/audio_decoder_passthrough.h
index 4b5ca80..99e86b3 100644
--- a/starboard/android/shared/audio_decoder_passthrough.h
+++ b/starboard/android/shared/audio_decoder_passthrough.h
@@ -71,8 +71,7 @@
           new DecodedAudio(kChannels, kSbMediaAudioSampleTypeInt16Deprecated,
                            kSbMediaAudioFrameStorageTypePlanar,
                            input_buffer->timestamp(), input_buffer->size());
-      memcpy(decoded_audio->buffer(), input_buffer->data(),
-             input_buffer->size());
+      memcpy(decoded_audio->data(), input_buffer->data(), input_buffer->size());
       decoded_audios_.push(decoded_audio);
       output_cb_();
     }
diff --git a/starboard/android/shared/audio_renderer_passthrough.cc b/starboard/android/shared/audio_renderer_passthrough.cc
index 34f5bed..b0d0acd 100644
--- a/starboard/android/shared/audio_renderer_passthrough.cc
+++ b/starboard/android/shared/audio_renderer_passthrough.cc
@@ -73,24 +73,24 @@
 }  // namespace
 
 AudioRendererPassthrough::AudioRendererPassthrough(
-    const SbMediaAudioSampleInfo& audio_sample_info,
+    const AudioStreamInfo& audio_stream_info,
     SbDrmSystem drm_system,
     bool enable_audio_device_callback)
-    : audio_sample_info_(audio_sample_info),
+    : audio_stream_info_(audio_stream_info),
       enable_audio_device_callback_(enable_audio_device_callback) {
-  SB_DCHECK(audio_sample_info_.codec == kSbMediaAudioCodecAc3 ||
-            audio_sample_info_.codec == kSbMediaAudioCodecEac3);
+  SB_DCHECK(audio_stream_info_.codec == kSbMediaAudioCodecAc3 ||
+            audio_stream_info_.codec == kSbMediaAudioCodecEac3);
   if (SbDrmSystemIsValid(drm_system)) {
     SB_LOG(INFO) << "Creating AudioDecoder as decryptor.";
-    scoped_ptr<AudioDecoder> audio_decoder(new AudioDecoder(
-        audio_sample_info_.codec, audio_sample_info, drm_system));
+    scoped_ptr<AudioDecoder> audio_decoder(
+        new AudioDecoder(audio_stream_info, drm_system));
     if (audio_decoder->is_valid()) {
       decoder_.reset(audio_decoder.release());
     }
   } else {
     SB_LOG(INFO) << "Creating AudioDecoderPassthrough.";
     decoder_.reset(
-        new AudioDecoderPassthrough(audio_sample_info_.samples_per_second));
+        new AudioDecoderPassthrough(audio_stream_info_.samples_per_second));
   }
 }
 
@@ -326,12 +326,12 @@
     SB_DCHECK(now >= stopped_at_);
     auto time_elapsed = now - stopped_at_;
     int64_t frames_played =
-        time_elapsed * audio_sample_info_.samples_per_second / kSbTimeSecond;
+        time_elapsed * audio_stream_info_.samples_per_second / kSbTimeSecond;
     int64_t total_frames_played =
         frames_played + playback_head_position_when_stopped_;
     total_frames_played = std::min(total_frames_played, total_frames_written_);
     return seek_to_time_ + total_frames_played * kSbTimeSecond /
-                               audio_sample_info_.samples_per_second;
+                               audio_stream_info_.samples_per_second;
   }
 
   SbTime updated_at;
@@ -347,7 +347,7 @@
   //       returned on pause, after an adjusted time has been returned.
   SbTime playback_time =
       seek_to_time_ + playback_head_position * kSbTimeSecond /
-                          audio_sample_info_.samples_per_second;
+                          audio_stream_info_.samples_per_second;
 
   // When underlying AudioTrack is paused, we use returned playback time
   // directly. Note that we should not use |paused_| or |playback_rate_| here.
@@ -391,12 +391,12 @@
   }
 
   std::unique_ptr<AudioTrackBridge> audio_track_bridge(new AudioTrackBridge(
-      audio_sample_info_.codec == kSbMediaAudioCodecAc3
+      audio_stream_info_.codec == kSbMediaAudioCodecAc3
           ? kSbMediaAudioCodingTypeAc3
           : kSbMediaAudioCodingTypeDolbyDigitalPlus,
       optional<SbMediaAudioSampleType>(),  // Not required in passthrough mode
-      audio_sample_info_.number_of_channels,
-      audio_sample_info_.samples_per_second, kPreferredBufferSizeInBytes,
+      audio_stream_info_.number_of_channels,
+      audio_stream_info_.samples_per_second, kPreferredBufferSizeInBytes,
       enable_audio_device_callback_, false /* enable_pcm_content_type_movie */,
       kTunnelModeAudioSessionId, false /* is_web_audio */));
 
@@ -515,10 +515,11 @@
                      << playback_head_position_when_stopped_;
       }
     } else {
-      auto sample_buffer = decoded_audio_writing_in_progress_->buffer() +
+      auto sample_buffer = decoded_audio_writing_in_progress_->data() +
                            decoded_audio_writing_offset_;
-      auto samples_to_write = (decoded_audio_writing_in_progress_->size() -
-                               decoded_audio_writing_offset_);
+      auto samples_to_write =
+          (decoded_audio_writing_in_progress_->size_in_bytes() -
+           decoded_audio_writing_offset_);
       // TODO: |sync_time| currently doesn't take partial writes into account.
       //       It is not used in non-tunneled mode so it doesn't matter, but we
       //       should revisit this.
@@ -550,7 +551,7 @@
       decoded_audio_writing_offset_ += samples_written;
 
       if (decoded_audio_writing_offset_ ==
-          decoded_audio_writing_in_progress_->size()) {
+          decoded_audio_writing_in_progress_->size_in_bytes()) {
         total_frames_written_on_audio_track_thread_ += frames_per_input_buffer_;
         decoded_audio_writing_in_progress_ = nullptr;
         decoded_audio_writing_offset_ = 0;
@@ -569,7 +570,7 @@
   if (stop_called_ && !end_of_stream_played_.load()) {
     auto time_elapsed = SbTimeGetMonotonicNow() - stopped_at_;
     auto frames_played =
-        time_elapsed * audio_sample_info_.samples_per_second / kSbTimeSecond;
+        time_elapsed * audio_stream_info_.samples_per_second / kSbTimeSecond;
     if (frames_played + playback_head_position_when_stopped_ >=
         total_frames_written_on_audio_track_thread_) {
       end_of_stream_played_.store(true);
diff --git a/starboard/android/shared/audio_renderer_passthrough.h b/starboard/android/shared/audio_renderer_passthrough.h
index 9e421e0..d4063df 100644
--- a/starboard/android/shared/audio_renderer_passthrough.h
+++ b/starboard/android/shared/audio_renderer_passthrough.h
@@ -28,6 +28,7 @@
 #include "starboard/drm.h"
 #include "starboard/media.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_renderer_internal.h"
 #include "starboard/shared/starboard/player/filter/common.h"
@@ -49,7 +50,10 @@
       public ::starboard::shared::starboard::player::filter::MediaTimeProvider,
       private ::starboard::shared::starboard::player::JobQueue::JobOwner {
  public:
-  AudioRendererPassthrough(const SbMediaAudioSampleInfo& audio_sample_info,
+  typedef ::starboard::shared::starboard::media::AudioStreamInfo
+      AudioStreamInfo;
+
+  AudioRendererPassthrough(const AudioStreamInfo& audio_stream_info,
                            SbDrmSystem drm_system,
                            bool enable_audio_device_callback);
   ~AudioRendererPassthrough() override;
@@ -99,7 +103,7 @@
   void OnDecoderOutput();
 
   // The following three variables are set in the ctor.
-  const SbMediaAudioSampleInfo audio_sample_info_;
+  const AudioStreamInfo audio_stream_info_;
   const bool enable_audio_device_callback_;
   // The AudioDecoder is used as a decryptor when the stream is encrypted.
   // TODO: Revisit to encapsulate the AudioDecoder as a SbDrmSystemPrivate
diff --git a/starboard/android/shared/install_target.gni b/starboard/android/shared/install_target.gni
index 6eae3d3..ad1b07b 100644
--- a/starboard/android/shared/install_target.gni
+++ b/starboard/android/shared/install_target.gni
@@ -90,6 +90,8 @@
       "cobaltTarget=$installable_target_name",
       "-P",
       "enableVulkan=$enable_vulkan",
+      "-P",
+      "evergreenCompatible=$sb_is_evergreen_compatible",
       "assembleCobalt_$gradle_build_type",
       "-Dorg.gradle.workers.max=$num_gradle_workers",
       "-Dorg.gradle.workers.parallel=$use_parallel_build",
diff --git a/starboard/android/shared/jni_env_ext.h b/starboard/android/shared/jni_env_ext.h
index 5a9bde9..3060918 100644
--- a/starboard/android/shared/jni_env_ext.h
+++ b/starboard/android/shared/jni_env_ext.h
@@ -139,7 +139,7 @@
     const jstring charset = NewStringUTF("UTF-8");
     AbortOnException();
     const jbyteArray byte_array = NewByteArrayFromRaw(
-        reinterpret_cast<const jbyte*>(bytes), strlen(bytes));
+        reinterpret_cast<const jbyte*>(bytes), (bytes ? strlen(bytes) : 0U));
     AbortOnException();
     jstring result = static_cast<jstring>(NewObjectOrAbort(
         "java/lang/String", "([BLjava/lang/String;)V", byte_array, charset));
diff --git a/starboard/android/shared/max_output_buffers_lookup_table.cc b/starboard/android/shared/max_output_buffers_lookup_table.cc
new file mode 100644
index 0000000..0d817bd
--- /dev/null
+++ b/starboard/android/shared/max_output_buffers_lookup_table.cc
@@ -0,0 +1,98 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/android/shared/max_output_buffers_lookup_table.h"
+
+#include "starboard/common/log.h"
+#include "starboard/common/string.h"
+#include "starboard/once.h"
+
+namespace starboard {
+namespace android {
+namespace shared {
+
+bool VideoOutputFormat::operator<(const VideoOutputFormat& key) const {
+  if (codec_ != key.codec_) {
+    return codec_ < key.codec_;
+  } else if (output_width_ != key.output_width_) {
+    return output_width_ < key.output_width_;
+  } else if (output_height_ != key.output_height_) {
+    return output_height_ < key.output_height_;
+  } else if (is_hdr_ != key.is_hdr_) {
+    return is_hdr_ < key.is_hdr_;
+  } else {
+    return false;
+  }
+}
+
+std::string VideoOutputFormat::ToString() const {
+  std::string info = FormatString("Video codec  = %d res = [%d x %d]", codec_,
+                                  output_width_, output_height_);
+  if (is_hdr_) {
+    info += "+ HDR";
+  }
+  return info;
+}
+
+SB_ONCE_INITIALIZE_FUNCTION(MaxMediaCodecOutputBuffersLookupTable,
+                            MaxMediaCodecOutputBuffersLookupTable::GetInstance);
+
+std::string MaxMediaCodecOutputBuffersLookupTable::DumpContent() const {
+  std::string table_content =
+      "[Preroll] MaxMediaCodecOutputBuffersLookupTable:\n";
+  for (const auto& item : lookup_table_) {
+    table_content += item.first.ToString();
+    table_content +=
+        FormatString(": max output video frame capacity = %d\n", item.second);
+  }
+  return table_content;
+}
+
+int MaxMediaCodecOutputBuffersLookupTable::GetMaxOutputVideoBuffers(
+    const VideoOutputFormat& format) const {
+  ScopedLock scoped_lock(mutex_);
+
+  auto iter = lookup_table_.find(format);
+  if (iter == lookup_table_.end() || !enable_) {
+    return -1;
+  } else {
+    return iter->second;
+  }
+}
+
+void MaxMediaCodecOutputBuffersLookupTable::SetEnabled(bool enable) {
+  ScopedLock scoped_lock(mutex_);
+
+  enable_ = enable;
+}
+
+void MaxMediaCodecOutputBuffersLookupTable::UpdateMaxOutputBuffers(
+    const VideoOutputFormat& format,
+    int max_num_of_frames) {
+  ScopedLock scoped_lock(mutex_);
+
+  if (!enable_) {
+    return;
+  }
+
+  int max_output_buffer_record = lookup_table_[format];
+  if (max_num_of_frames > max_output_buffer_record) {
+    lookup_table_[format] = max_num_of_frames;
+    SB_DLOG(INFO) << DumpContent();
+  }
+}
+
+}  // namespace shared
+}  // namespace android
+}  // namespace starboard
diff --git a/starboard/android/shared/max_output_buffers_lookup_table.h b/starboard/android/shared/max_output_buffers_lookup_table.h
new file mode 100644
index 0000000..4dddc5d
--- /dev/null
+++ b/starboard/android/shared/max_output_buffers_lookup_table.h
@@ -0,0 +1,75 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_ANDROID_SHARED_MAX_OUTPUT_BUFFERS_LOOKUP_TABLE_H_
+#define STARBOARD_ANDROID_SHARED_MAX_OUTPUT_BUFFERS_LOOKUP_TABLE_H_
+
+#include <functional>
+#include <map>
+#include <string>
+
+#include "starboard/common/mutex.h"
+#include "starboard/media.h"
+
+namespace starboard {
+namespace android {
+namespace shared {
+
+class VideoOutputFormat {
+ public:
+  VideoOutputFormat(SbMediaVideoCodec codec,
+                    int output_width,
+                    int output_height,
+                    bool is_hdr)
+      : codec_(codec),
+        output_width_(output_width),
+        output_height_(output_height),
+        is_hdr_(is_hdr) {}
+
+  bool operator<(const VideoOutputFormat& key) const;
+
+  std::string ToString() const;
+
+ private:
+  SbMediaVideoCodec codec_;
+  int output_width_;
+  int output_height_;
+  bool is_hdr_;
+};
+
+class MaxMediaCodecOutputBuffersLookupTable {
+ public:
+  static MaxMediaCodecOutputBuffersLookupTable* GetInstance();
+
+  int GetMaxOutputVideoBuffers(const VideoOutputFormat& format) const;
+
+  void SetEnabled(bool enable);
+
+  void UpdateMaxOutputBuffers(const VideoOutputFormat& format,
+                              int max_num_of_frames);
+
+ private:
+  std::string DumpContent() const;
+
+  bool enable_ = true;
+
+  Mutex mutex_;
+  std::map<VideoOutputFormat, int> lookup_table_;
+};
+
+}  // namespace shared
+}  // namespace android
+}  // namespace starboard
+
+#endif  // STARBOARD_ANDROID_SHARED_MAX_OUTPUT_BUFFERS_LOOKUP_TABLE_H_
diff --git a/starboard/android/shared/media_codec_bridge.cc b/starboard/android/shared/media_codec_bridge.cc
index 2e2cd1a..b7eac22 100644
--- a/starboard/android/shared/media_codec_bridge.cc
+++ b/starboard/android/shared/media_codec_bridge.cc
@@ -156,15 +156,14 @@
 
 // static
 scoped_ptr<MediaCodecBridge> MediaCodecBridge::CreateAudioMediaCodecBridge(
-    SbMediaAudioCodec audio_codec,
-    const SbMediaAudioSampleInfo& audio_sample_info,
+    const AudioStreamInfo& audio_stream_info,
     Handler* handler,
     jobject j_media_crypto) {
   bool is_passthrough = false;
   const char* mime =
-      SupportedAudioCodecToMimeType(audio_codec, &is_passthrough);
+      SupportedAudioCodecToMimeType(audio_stream_info.codec, &is_passthrough);
   if (!mime) {
-    SB_LOG(ERROR) << "Unsupported codec " << audio_codec << ".";
+    SB_LOG(ERROR) << "Unsupported codec " << audio_stream_info.codec << ".";
     return scoped_ptr<MediaCodecBridge>(NULL);
   }
 
@@ -174,17 +173,19 @@
           false /* must_support_tunnel_mode */);
 
   if (decoder_name.empty()) {
-    SB_LOG(ERROR) << "Failed to find decoder for " << audio_codec << ".";
+    SB_LOG(ERROR) << "Failed to find decoder for " << audio_stream_info.codec
+                  << ".";
     return scoped_ptr<MediaCodecBridge>(NULL);
   }
 
   JniEnvExt* env = JniEnvExt::Get();
   ScopedLocalJavaRef<jbyteArray> configuration_data;
-  if (audio_codec == kSbMediaAudioCodecOpus &&
-      audio_sample_info.audio_specific_config_size != 0) {
+  if (audio_stream_info.codec == kSbMediaAudioCodecOpus &&
+      !audio_stream_info.audio_specific_config.empty()) {
     configuration_data.Reset(env->NewByteArrayFromRaw(
-        static_cast<const jbyte*>(audio_sample_info.audio_specific_config),
-        audio_sample_info.audio_specific_config_size));
+        reinterpret_cast<const jbyte*>(
+            audio_stream_info.audio_specific_config.data()),
+        audio_stream_info.audio_specific_config.size()));
   }
   ScopedLocalJavaRef<jstring> j_mime(env->NewStringStandardUTFOrAbort(mime));
   ScopedLocalJavaRef<jstring> j_decoder_name(
@@ -196,12 +197,13 @@
       "(JLjava/lang/String;Ljava/lang/String;IILandroid/media/MediaCrypto;"
       "[B)Ldev/cobalt/media/MediaCodecBridge;",
       reinterpret_cast<jlong>(native_media_codec_bridge.get()), j_mime.Get(),
-      j_decoder_name.Get(), audio_sample_info.samples_per_second,
-      audio_sample_info.number_of_channels, j_media_crypto,
+      j_decoder_name.Get(), audio_stream_info.samples_per_second,
+      audio_stream_info.number_of_channels, j_media_crypto,
       configuration_data.Get());
 
   if (!j_media_codec_bridge) {
-    SB_LOG(ERROR) << "Failed to create codec bridge for " << audio_codec << ".";
+    SB_LOG(ERROR) << "Failed to create codec bridge for "
+                  << audio_stream_info.codec << ".";
     return scoped_ptr<MediaCodecBridge>(NULL);
   }
 
diff --git a/starboard/android/shared/media_codec_bridge.h b/starboard/android/shared/media_codec_bridge.h
index 77b9c7a..7aa7cfc 100644
--- a/starboard/android/shared/media_codec_bridge.h
+++ b/starboard/android/shared/media_codec_bridge.h
@@ -21,6 +21,7 @@
 #include "starboard/android/shared/jni_utils.h"
 #include "starboard/android/shared/media_common.h"
 #include "starboard/common/scoped_ptr.h"
+#include "starboard/shared/starboard/media/media_util.h"
 
 namespace starboard {
 namespace android {
@@ -74,6 +75,9 @@
 
 class MediaCodecBridge {
  public:
+  typedef ::starboard::shared::starboard::media::AudioStreamInfo
+      AudioStreamInfo;
+
   // The methods are called on the default Looper.  They won't get called after
   // Flush() is returned.
   class Handler {
@@ -96,8 +100,7 @@
   };
 
   static scoped_ptr<MediaCodecBridge> CreateAudioMediaCodecBridge(
-      SbMediaAudioCodec audio_codec,
-      const SbMediaAudioSampleInfo& audio_sample_info,
+      const AudioStreamInfo& audio_stream_info,
       Handler* handler,
       jobject j_media_crypto);
 
diff --git a/starboard/android/shared/media_decoder.cc b/starboard/android/shared/media_decoder.cc
index 90beda3..8365e21 100644
--- a/starboard/android/shared/media_decoder.cc
+++ b/starboard/android/shared/media_decoder.cc
@@ -72,8 +72,7 @@
 }  // namespace
 
 MediaDecoder::MediaDecoder(Host* host,
-                           SbMediaAudioCodec audio_codec,
-                           const SbMediaAudioSampleInfo& audio_sample_info,
+                           const AudioStreamInfo& audio_stream_info,
                            SbDrmSystem drm_system)
     : media_type_(kSbMediaTypeAudio),
       host_(host),
@@ -85,22 +84,19 @@
   jobject j_media_crypto = drm_system_ ? drm_system_->GetMediaCrypto() : NULL;
   SB_DCHECK(!drm_system_ || j_media_crypto);
   media_codec_bridge_ = MediaCodecBridge::CreateAudioMediaCodecBridge(
-      audio_codec, audio_sample_info, this, j_media_crypto);
+      audio_stream_info, this, j_media_crypto);
   if (!media_codec_bridge_) {
     SB_LOG(ERROR) << "Failed to create audio media codec bridge.";
     return;
   }
-  // When |audio_codec| == kSbMediaAudioCodecOpus, we instead send the audio
-  // specific configuration when we create the MediaCodec object.
+  // When |audio_stream_info.codec| == kSbMediaAudioCodecOpus, we instead send
+  // the audio specific configuration when we create the MediaCodec object in
+  // the call to MediaCodecBridge::CreateAudioMediaCodecBridge() above.
   // TODO: Determine if we should send the audio specific configuration here
   // only when |audio_codec| == kSbMediaAudioCodecAac.
-  if (audio_codec != kSbMediaAudioCodecOpus &&
-      audio_sample_info.audio_specific_config_size > 0) {
-    // |audio_sample_info.audio_specific_config| is guaranteed to be outlived
-    // the decoder as it is stored in |FilterBasedPlayerWorkerHandler|.
-    pending_tasks_.push_back(Event(
-        static_cast<const int8_t*>(audio_sample_info.audio_specific_config),
-        audio_sample_info.audio_specific_config_size));
+  if (audio_stream_info.codec != kSbMediaAudioCodecOpus &&
+      !audio_stream_info.audio_specific_config.empty()) {
+    pending_tasks_.push_back(Event(audio_stream_info.audio_specific_config));
     number_of_pending_tasks_.increment();
   }
 }
@@ -425,8 +421,8 @@
   int size = 0;
   if (event.type == Event::kWriteCodecConfig) {
     SB_DCHECK(media_type_ == kSbMediaTypeAudio);
-    data = event.codec_config;
-    size = event.codec_config_size;
+    data = event.codec_config.data();
+    size = event.codec_config.size();
   } else if (event.type == Event::kWriteInputBuffer) {
     data = input_buffer->data();
     size = input_buffer->size();
diff --git a/starboard/android/shared/media_decoder.h b/starboard/android/shared/media_decoder.h
index 913aff6..ed89f0f 100644
--- a/starboard/android/shared/media_decoder.h
+++ b/starboard/android/shared/media_decoder.h
@@ -23,13 +23,14 @@
 
 #include "starboard/android/shared/drm_system.h"
 #include "starboard/android/shared/media_codec_bridge.h"
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/condition_variable.h"
 #include "starboard/common/mutex.h"
 #include "starboard/common/optional.h"
 #include "starboard/common/ref_counted.h"
 #include "starboard/media.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/filter/common.h"
 #include "starboard/shared/starboard/player/input_buffer_internal.h"
 #include "starboard/shared/starboard/player/job_queue.h"
@@ -45,6 +46,8 @@
     : private MediaCodecBridge::Handler,
       protected ::starboard::shared::starboard::player::JobQueue::JobOwner {
  public:
+  typedef ::starboard::shared::starboard::media::AudioStreamInfo
+      AudioStreamInfo;
   typedef ::starboard::shared::starboard::player::filter::ErrorCB ErrorCB;
   typedef ::starboard::shared::starboard::player::InputBuffer InputBuffer;
   typedef ::starboard::shared::starboard::player::InputBuffers InputBuffers;
@@ -75,8 +78,7 @@
   };
 
   MediaDecoder(Host* host,
-               SbMediaAudioCodec audio_codec,
-               const SbMediaAudioSampleInfo& audio_sample_info,
+               const AudioStreamInfo& audio_stream_info,
                SbDrmSystem drm_system);
   MediaDecoder(Host* host,
                SbMediaVideoCodec video_codec,
@@ -118,17 +120,16 @@
     explicit Event(Type type = kInvalid) : type(type) {
       SB_DCHECK(type != kWriteInputBuffer && type != kWriteCodecConfig);
     }
-    Event(const int8_t* codec_config, int16_t codec_config_size)
-        : type(kWriteCodecConfig),
-          codec_config(codec_config),
-          codec_config_size(codec_config_size) {}
+    explicit Event(const std::vector<uint8_t>& codec_config)
+        : type(kWriteCodecConfig), codec_config(codec_config) {
+      SB_DCHECK(!this->codec_config.empty());
+    }
     explicit Event(const scoped_refptr<InputBuffer>& input_buffer)
         : type(kWriteInputBuffer), input_buffer(input_buffer) {}
 
     Type type;
     scoped_refptr<InputBuffer> input_buffer;
-    const int8_t* codec_config;
-    int16_t codec_config_size;
+    std::vector<uint8_t> codec_config;
   };
 
   struct QueueInputBufferTask {
diff --git a/starboard/android/shared/media_is_video_supported.cc b/starboard/android/shared/media_is_video_supported.cc
index 9b8fcf5..8aa3101 100644
--- a/starboard/android/shared/media_is_video_supported.cc
+++ b/starboard/android/shared/media_is_video_supported.cc
@@ -14,12 +14,14 @@
 
 #include "starboard/shared/starboard/media/media_support_internal.h"
 
+#include "starboard/android/shared/max_output_buffers_lookup_table.h"
 #include "starboard/android/shared/media_capabilities_cache.h"
 #include "starboard/android/shared/media_common.h"
 #include "starboard/configuration.h"
 #include "starboard/media.h"
 #include "starboard/shared/starboard/media/media_util.h"
 
+using starboard::android::shared::MaxMediaCodecOutputBuffersLookupTable;
 using starboard::android::shared::MediaCapabilitiesCache;
 using starboard::android::shared::SupportedVideoCodecToMimeType;
 using starboard::shared::starboard::media::IsSDRVideo;
@@ -88,6 +90,14 @@
     if (mime_type->GetParamBoolValue("disablecache", false)) {
       MediaCapabilitiesCache::GetInstance()->SetCacheEnabled(false);
     }
+
+    if (!mime_type->ValidateBoolParameter("disabledynamicprerollframecount")) {
+      return false;
+    }
+    if (mime_type->GetParamBoolValue("disabledynamicprerollframecount",
+                                     false)) {
+      MaxMediaCodecOutputBuffersLookupTable::GetInstance()->SetEnabled(false);
+    }
   }
 
   if (must_support_tunnel_mode && decode_to_texture_required) {
diff --git a/starboard/android/shared/platform_configuration/BUILD.gn b/starboard/android/shared/platform_configuration/BUILD.gn
index a2dbcff..d3f0475 100644
--- a/starboard/android/shared/platform_configuration/BUILD.gn
+++ b/starboard/android/shared/platform_configuration/BUILD.gn
@@ -18,145 +18,141 @@
   configs = [ "//starboard/build/config/sabi" ]
   cflags = []
 
-  if (current_toolchain == host_toolchain) {
+  defines = []
+  ldflags = [
+    # The NDK default "ld" is actually the gold linker for all architectures
+    # except arm64 (aarch64) where it"s the bfd linker. Don"t use either of
+    # those, rather use lld everywhere. See release notes for NDK 19:
+    # https://developer.android.com/ndk/downloads/revision_history
+    "-fuse-ld=lld",
+  ]
+  if (is_debug) {
+    cflags += [ "-O0" ]
+    configs += [ "//build/config/compiler:rtti" ]
+  } else if (is_devel) {
     cflags += [ "-O2" ]
+    configs += [ "//build/config/compiler:rtti" ]
   } else {
-    defines = []
-    ldflags = [
-      # The NDK default "ld" is actually the gold linker for all architectures
-      # except arm64 (aarch64) where it"s the bfd linker. Don"t use either of
-      # those, rather use lld everywhere. See release notes for NDK 19:
-      # https://developer.android.com/ndk/downloads/revision_history
-      "-fuse-ld=lld",
-    ]
-    if (is_debug) {
-      cflags += [ "-O0" ]
-      configs += [ "//build/config/compiler:rtti" ]
-    } else if (is_devel) {
-      cflags += [ "-O2" ]
-      configs += [ "//build/config/compiler:rtti" ]
-    } else {
-      cflags += [ "-gline-tables-only" ]
-    }
+    cflags += [ "-gline-tables-only" ]
+  }
 
-    libs = [
-      "EGL",
-      "GLESv2",
-      "OpenSLES",
-      "android",
-      "log",
-      "mediandk",
-    ]
+  libs = [
+    "EGL",
+    "GLESv2",
+    "OpenSLES",
+    "android",
+    "log",
+    "mediandk",
+  ]
 
-    if (!cobalt_fastbuild && (is_debug || is_devel)) {
-      cflags += [ "-g" ]
-    }
+  if (!cobalt_fastbuild && (is_debug || is_devel)) {
+    cflags += [ "-g" ]
+  }
 
-    if (use_asan) {
-      cflags += [
-        "-fsanitize=address",
-        "-fno-omit-frame-pointer",
-      ]
-      ldflags += [
-        "-fsanitize=address",
-
-        # Force linking of the helpers in sanitizer_options.cc
-        "-Wl,-u_sanitizer_options_link_helper",
-      ]
-      defines += [ "ADDRESS_SANITIZER" ]
-    } else if (use_tsan) {
-      cflags += [
-        "-fsanitize=thread",
-        "-fno-omit-frame-pointer",
-      ]
-      ldflags += [ "-fsanitize=thread" ]
-      defines += [ "THREAD_SANITIZER" ]
-    }
-
-    cflags_cc = [ "-std=c++14" ]
+  if (use_asan) {
     cflags += [
-      # libwebp uses the cpufeatures library to detect ARM NEON support
-      "-I${android_ndk_path}/sources/android/cpufeatures",
-
-      # Mimic build/cmake/android.toolchain.cmake in the Android NDK.
-      "-ffunction-sections",
-      "-funwind-tables",
-      "-fstack-protector-strong",
-      "-no-canonical-prefixes",
-
-      # Other flags
-      "-fsigned-char",
-      "-fno-limit-debug-info",
-      "-fcolor-diagnostics",
-      "-fno-strict-aliasing",  # See http://crbug.com/32204
-
-      # Default visibility is hidden to enable dead stripping.
-      "-fvisibility=hidden",
-
-      # Any warning will stop the build.
-      "-Werror",
-
-      # Disable errors for the warning till the Android NDK r19 is fixed.
-      # The warning is trigger when compiling .c files and complains for
-      # "-stdlib=libc++" which is added by the NDK.
-      "-Wno-error=unused-command-line-argument",
-
-      # Don"t warn about register variables (in base and net)
-      "-Wno-deprecated-register",
-
-      # Don"t warn about deprecated ICU methods (in googleurl and net)
-      "-Wno-deprecated-declarations",
-
-      # Skia doesn"t use overrides.
-      "-Wno-inconsistent-missing-override",
-
-      # shifting a negative signed value is undefined
-      "-Wno-shift-negative-value",
-
-      # Don"t warn for implicit sign conversions. (in v8 and protobuf)
-      "-Wno-sign-conversion",
-    ]
-    defines += [
-      # Enable compile-time decisions based on the ABI
-      "ANDROID_ABI=$android_abi",
-
-      # -DANDROID is an argument to some ifdefs in the NDK"s eglplatform.h
-      "ANDROID",
-
-      # Cobalt on Linux flag
-      "COBALT_LINUX",
-
-      # So that we get the PRI* macros from inttypes.h
-      "__STDC_FORMAT_MACROS",
-
-      # Enable GNU extensions to get prototypes like ffsl.
-      "_GNU_SOURCE=1",
-
-      # Undefining __linux__ causes the system headers to make wrong
-      # assumptions about which C-library is used on the platform.
-      "__BIONIC__",
-
-      # Undefining __linux__ leaves libc++ without a threads implementation.
-      # TODO: See if there"s a way to make libcpp threading use Starboard.
-      "_LIBCPP_HAS_THREAD_API_PTHREAD",
+      "-fsanitize=address",
+      "-fno-omit-frame-pointer",
     ]
     ldflags += [
-      # Use the static LLVM libc++.
-      "-static-libstdc++",
+      "-fsanitize=address",
 
-      # Mimic build/cmake/android.toolchain.cmake in the Android NDK, but
-      # force build-id to sha1, so that older lldb versions can still find
-      # debugsymbols, see https://github.com/android-ndk/ndk/issues/885
-      "-Wl,--build-id=sha1",
-      "-Wl,--warn-shared-textrel",
-      "-Wl,--fatal-warnings",
-      "-Wl,--gc-sections",
-      "-Wl,-z,nocopyreloc",
-
-      # Wrapper synchronizes punch-out video bounds with the UI frame.
-      "-Wl,--wrap=eglSwapBuffers",
+      # Force linking of the helpers in sanitizer_options.cc
+      "-Wl,-u_sanitizer_options_link_helper",
     ]
+    defines += [ "ADDRESS_SANITIZER" ]
+  } else if (use_tsan) {
+    cflags += [
+      "-fsanitize=thread",
+      "-fno-omit-frame-pointer",
+    ]
+    ldflags += [ "-fsanitize=thread" ]
+    defines += [ "THREAD_SANITIZER" ]
   }
+
+  cflags_cc = [ "-std=c++14" ]
+  cflags += [
+    # libwebp uses the cpufeatures library to detect ARM NEON support
+    "-I${android_ndk_path}/sources/android/cpufeatures",
+
+    # Mimic build/cmake/android.toolchain.cmake in the Android NDK.
+    "-ffunction-sections",
+    "-funwind-tables",
+    "-fstack-protector-strong",
+    "-no-canonical-prefixes",
+
+    # Other flags
+    "-fsigned-char",
+    "-fno-limit-debug-info",
+    "-fcolor-diagnostics",
+    "-fno-strict-aliasing",  # See http://crbug.com/32204
+
+    # Default visibility is hidden to enable dead stripping.
+    "-fvisibility=hidden",
+
+    # Any warning will stop the build.
+    "-Werror",
+
+    # Disable errors for the warning till the Android NDK r19 is fixed.
+    # The warning is trigger when compiling .c files and complains for
+    # "-stdlib=libc++" which is added by the NDK.
+    "-Wno-error=unused-command-line-argument",
+
+    # Don"t warn about register variables (in base and net)
+    "-Wno-deprecated-register",
+
+    # Don"t warn about deprecated ICU methods (in googleurl and net)
+    "-Wno-deprecated-declarations",
+
+    # Skia doesn"t use overrides.
+    "-Wno-inconsistent-missing-override",
+
+    # shifting a negative signed value is undefined
+    "-Wno-shift-negative-value",
+
+    # Don"t warn for implicit sign conversions. (in v8 and protobuf)
+    "-Wno-sign-conversion",
+  ]
+  defines += [
+    # Enable compile-time decisions based on the ABI
+    "ANDROID_ABI=$android_abi",
+
+    # -DANDROID is an argument to some ifdefs in the NDK"s eglplatform.h
+    "ANDROID",
+
+    # Cobalt on Linux flag
+    "COBALT_LINUX",
+
+    # So that we get the PRI* macros from inttypes.h
+    "__STDC_FORMAT_MACROS",
+
+    # Enable GNU extensions to get prototypes like ffsl.
+    "_GNU_SOURCE=1",
+
+    # Undefining __linux__ causes the system headers to make wrong
+    # assumptions about which C-library is used on the platform.
+    "__BIONIC__",
+
+    # Undefining __linux__ leaves libc++ without a threads implementation.
+    # TODO: See if there"s a way to make libcpp threading use Starboard.
+    "_LIBCPP_HAS_THREAD_API_PTHREAD",
+  ]
+  ldflags += [
+    # Use the static LLVM libc++.
+    "-static-libstdc++",
+
+    # Mimic build/cmake/android.toolchain.cmake in the Android NDK, but
+    # force build-id to sha1, so that older lldb versions can still find
+    # debugsymbols, see https://github.com/android-ndk/ndk/issues/885
+    "-Wl,--build-id=sha1",
+    "-Wl,--warn-shared-textrel",
+    "-Wl,--fatal-warnings",
+    "-Wl,--gc-sections",
+    "-Wl,-z,nocopyreloc",
+
+    # Wrapper synchronizes punch-out video bounds with the UI frame.
+    "-Wl,--wrap=eglSwapBuffers",
+  ]
 }
 
 config("size") {
diff --git a/starboard/android/shared/player_components_factory.h b/starboard/android/shared/player_components_factory.h
index 6d26909..501a860 100644
--- a/starboard/android/shared/player_components_factory.h
+++ b/starboard/android/shared/player_components_factory.h
@@ -34,6 +34,7 @@
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/media.h"
 #include "starboard/shared/opus/opus_audio_decoder.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/media/mime_type.h"
 #include "starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
@@ -88,7 +89,6 @@
                 SbAudioSinkPrivate::ConsumeFramesFunc consume_frames_func,
                 SbAudioSinkPrivate::ErrorFunc error_func,
                 void* context) {
-
               auto type = static_cast<AudioTrackAudioSinkType*>(
                   SbAudioSinkPrivate::GetPreferredType());
 
@@ -100,15 +100,22 @@
                   tunnel_mode_audio_session_id, enable_audio_device_callback,
                   enable_pcm_content_type_movie, false, /* is_web_audio */
                   context);
-            }) {}
+            }),
+        tunnel_mode_audio_session_id_(tunnel_mode_audio_session_id) {}
 
  private:
   bool IsAudioSampleTypeSupported(
       SbMediaAudioSampleType audio_sample_type) const override {
-    // Currently the implementation only supports tunnel mode with int16 audio
-    // samples.
-    return audio_sample_type == kSbMediaAudioSampleTypeInt16Deprecated;
+    if (tunnel_mode_audio_session_id_ != -1) {
+      // Currently the implementation only supports tunnel mode with int16 audio
+      // samples.
+      return audio_sample_type == kSbMediaAudioSampleTypeInt16Deprecated;
+    }
+
+    return SbAudioSinkIsAudioSampleTypeSupported(audio_sample_type);
   }
+
+  const int tunnel_mode_audio_session_id_;
 };
 
 class AudioRendererSinkCallbackStub
@@ -209,7 +216,7 @@
 
     bool enable_audio_device_callback = true;
 
-    if (strlen(creation_parameters.audio_mime()) > 0) {
+    if (!creation_parameters.audio_mime().empty()) {
       MimeType audio_mime_type(creation_parameters.audio_mime());
       if (!audio_mime_type.is_valid() ||
           !audio_mime_type.ValidateBoolParameter("enableaudiodevicecallback") ||
@@ -233,7 +240,7 @@
     // TODO: Enable tunnel mode for passthrough
     scoped_ptr<AudioRendererPassthrough> audio_renderer;
     audio_renderer.reset(new AudioRendererPassthrough(
-        creation_parameters.audio_sample_info(),
+        creation_parameters.audio_stream_info(),
         GetExtendedDrmSystem(creation_parameters.drm_system()),
         enable_audio_device_callback));
     if (!audio_renderer->is_valid()) {
@@ -248,7 +255,7 @@
 
       bool force_improved_support_check = true;
 
-      if (strlen(creation_parameters.video_mime()) > 0) {
+      if (!creation_parameters.video_mime().empty()) {
         MimeType video_mime_type(creation_parameters.video_mime());
         if (!video_mime_type.is_valid() ||
             !video_mime_type.ValidateBoolParameter(
@@ -294,12 +301,12 @@
       std::string* error_message) override {
     SB_DCHECK(error_message);
 
-    const char* audio_mime =
+    const std::string audio_mime =
         creation_parameters.audio_codec() != kSbMediaAudioCodecNone
             ? creation_parameters.audio_mime()
             : "";
     MimeType audio_mime_type(audio_mime);
-    if (strlen(audio_mime) > 0) {
+    if (!audio_mime.empty()) {
       if (!audio_mime_type.is_valid() ||
           !audio_mime_type.ValidateBoolParameter("tunnelmode") ||
           !audio_mime_type.ValidateBoolParameter("enableaudiodevicecallback") ||
@@ -310,12 +317,12 @@
       }
     }
 
-    const char* video_mime =
+    const std::string video_mime =
         creation_parameters.video_codec() != kSbMediaVideoCodecNone
             ? creation_parameters.video_mime()
             : "";
     MimeType video_mime_type(video_mime);
-    if (strlen(video_mime) > 0) {
+    if (!video_mime.empty()) {
       if (!video_mime_type.is_valid() ||
           !video_mime_type.ValidateBoolParameter("tunnelmode") ||
           !video_mime_type.ValidateBoolParameter("forceimprovedsupportcheck")) {
@@ -386,33 +393,34 @@
       SB_DCHECK(audio_decoder);
       SB_DCHECK(audio_renderer_sink);
 
-      auto decoder_creator = [](const SbMediaAudioSampleInfo& audio_sample_info,
+      using starboard::shared::starboard::media::AudioStreamInfo;
+      auto decoder_creator = [](const AudioStreamInfo& audio_stream_info,
                                 SbDrmSystem drm_system) {
         bool use_libopus_decoder =
-            audio_sample_info.codec == kSbMediaAudioCodecOpus &&
+            audio_stream_info.codec == kSbMediaAudioCodecOpus &&
             !SbDrmSystemIsValid(drm_system) && !kForcePlatformOpusDecoder;
         if (use_libopus_decoder) {
           scoped_ptr<OpusAudioDecoder> audio_decoder_impl(
-              new OpusAudioDecoder(audio_sample_info));
+              new OpusAudioDecoder(audio_stream_info));
           if (audio_decoder_impl->is_valid()) {
             return audio_decoder_impl.PassAs<AudioDecoderBase>();
           }
-        } else if (audio_sample_info.codec == kSbMediaAudioCodecAac ||
-                   audio_sample_info.codec == kSbMediaAudioCodecOpus) {
-          scoped_ptr<AudioDecoder> audio_decoder_impl(new AudioDecoder(
-              audio_sample_info.codec, audio_sample_info, drm_system));
+        } else if (audio_stream_info.codec == kSbMediaAudioCodecAac ||
+                   audio_stream_info.codec == kSbMediaAudioCodecOpus) {
+          scoped_ptr<AudioDecoder> audio_decoder_impl(
+              new AudioDecoder(audio_stream_info, drm_system));
           if (audio_decoder_impl->is_valid()) {
             return audio_decoder_impl.PassAs<AudioDecoderBase>();
           }
         } else {
           SB_LOG(ERROR) << "Unsupported audio codec "
-                        << audio_sample_info.codec;
+                        << audio_stream_info.codec;
         }
         return scoped_ptr<AudioDecoderBase>();
       };
 
       audio_decoder->reset(new AdaptiveAudioDecoder(
-          creation_parameters.audio_sample_info(),
+          creation_parameters.audio_stream_info(),
           GetExtendedDrmSystem(creation_parameters.drm_system()),
           decoder_creator));
 
@@ -480,11 +488,11 @@
     // AudioRenderer prefers to use kSbMediaAudioSampleTypeFloat32 and only uses
     // kSbMediaAudioSampleTypeInt16Deprecated when float32 is not supported.
     int min_frames_required = SbAudioSinkGetMinBufferSizeInFrames(
-        creation_parameters.audio_sample_info().number_of_channels,
+        creation_parameters.audio_stream_info().number_of_channels,
         SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)
             ? kSbMediaAudioSampleTypeFloat32
             : kSbMediaAudioSampleTypeInt16Deprecated,
-        creation_parameters.audio_sample_info().samples_per_second);
+        creation_parameters.audio_stream_info().samples_per_second);
     // On Android 5.0, the size of audio renderer sink buffer need to be two
     // times larger than AudioTrack minBufferSize. Otherwise, AudioTrack may
     // stop working after pause.
@@ -500,7 +508,7 @@
       bool force_improved_support_check,
       std::string* error_message) {
     bool force_big_endian_hdr_metadata = false;
-    if (strlen(creation_parameters.video_mime()) > 0) {
+    if (!creation_parameters.video_mime().empty()) {
       // Use mime param to determine endianness of HDR metadata. If param is
       // missing or invalid it defaults to Little Endian.
       MimeType video_mime_type(creation_parameters.video_mime());
@@ -609,7 +617,7 @@
             "()Ldev/cobalt/media/AudioOutputManager;"));
     int tunnel_mode_audio_session_id = env->CallIntMethodOrAbort(
         j_audio_output_manager.Get(), "generateTunnelModeAudioSessionId",
-        "(I)I", creation_parameters.audio_sample_info().number_of_channels);
+        "(I)I", creation_parameters.audio_stream_info().number_of_channels);
 
     // AudioManager.generateAudioSessionId() return ERROR (-1) to indicate a
     // failure, please see the following url for more details:
@@ -633,11 +641,11 @@
     AudioRendererSinkCallbackStub callback_stub;
     std::vector<uint16_t> frame_buffer(
         max_cached_frames *
-        creation_parameters.audio_sample_info().number_of_channels);
+        creation_parameters.audio_stream_info().number_of_channels);
     uint16_t* frame_buffers[] = {frame_buffer.data()};
     audio_sink->Start(
-        0, creation_parameters.audio_sample_info().number_of_channels,
-        creation_parameters.audio_sample_info().samples_per_second,
+        0, creation_parameters.audio_stream_info().number_of_channels,
+        creation_parameters.audio_stream_info().samples_per_second,
         kSbMediaAudioSampleTypeInt16Deprecated,
         kSbMediaAudioFrameStorageTypeInterleaved,
         reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers),
@@ -648,9 +656,9 @@
     }
     SB_LOG(WARNING)
         << "AudioTrack does not support tunnel mode with sample rate:"
-        << creation_parameters.audio_sample_info().samples_per_second
+        << creation_parameters.audio_stream_info().samples_per_second
         << ", channels:"
-        << creation_parameters.audio_sample_info().number_of_channels
+        << creation_parameters.audio_stream_info().number_of_channels
         << ", audio format:" << creation_parameters.audio_codec()
         << ", and audio buffer frames:" << max_cached_frames;
     return scoped_ptr<AudioRendererSink>();
diff --git a/starboard/android/shared/player_create.cc b/starboard/android/shared/player_create.cc
index a79fa48..5348bee 100644
--- a/starboard/android/shared/player_create.cc
+++ b/starboard/android/shared/player_create.cc
@@ -16,7 +16,6 @@
 
 #include "starboard/player.h"
 
-#include "starboard/android/shared/video_decoder.h"
 #include "starboard/android/shared/video_window.h"
 #include "starboard/common/log.h"
 #include "starboard/common/media.h"
@@ -28,10 +27,9 @@
 #include "starboard/shared/starboard/player/player_worker.h"
 #include "starboard/string.h"
 
+using starboard::shared::starboard::player::PlayerWorker;
 using starboard::shared::starboard::player::filter::
     FilterBasedPlayerWorkerHandler;
-using starboard::shared::starboard::player::PlayerWorker;
-using starboard::android::shared::VideoDecoder;
 
 SbPlayer SbPlayerCreate(SbWindow window,
                         const SbPlayerCreationParam* creation_param,
@@ -53,35 +51,43 @@
     return kSbPlayerInvalid;
   }
 
-  bool has_audio =
-      creation_param->audio_sample_info.codec != kSbMediaAudioCodecNone;
-  bool has_video =
-      creation_param->video_sample_info.codec != kSbMediaVideoCodecNone;
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaAudioStreamInfo& audio_stream_info =
+      creation_param->audio_stream_info;
+  const SbMediaVideoStreamInfo& video_stream_info =
+      creation_param->video_stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaAudioSampleInfo& audio_stream_info =
+      creation_param->audio_sample_info;
+  const SbMediaVideoSampleInfo& video_stream_info =
+      creation_param->video_sample_info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 
-  const char* audio_mime =
-      has_audio ? creation_param->audio_sample_info.mime : "";
-  const char* video_mime =
-      has_video ? creation_param->video_sample_info.mime : "";
+  bool has_audio = audio_stream_info.codec != kSbMediaAudioCodecNone;
+  bool has_video = video_stream_info.codec != kSbMediaVideoCodecNone;
+
+  const char* audio_mime = has_audio ? audio_stream_info.mime : "";
+  const char* video_mime = has_video ? video_stream_info.mime : "";
   const char* max_video_capabilities =
-      has_video ? creation_param->video_sample_info.max_video_capabilities : "";
+      has_video ? video_stream_info.max_video_capabilities : "";
 
   if (!audio_mime) {
-    SB_LOG(ERROR) << "creation_param->audio_sample_info.mime cannot be null.";
+    SB_LOG(ERROR) << "creation_param->audio_stream_info.mime cannot be null.";
     player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
-                      "creation_param->audio_sample_info.mime cannot be null");
+                      "creation_param->audio_stream_info.mime cannot be null");
     return kSbPlayerInvalid;
   }
   if (!video_mime) {
-    SB_LOG(ERROR) << "creation_param->video_sample_info.mime cannot be null.";
+    SB_LOG(ERROR) << "creation_param->video_stream_info.mime cannot be null.";
     player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
-                      "creation_param->video_sample_info.mime cannot be null");
+                      "creation_param->video_stream_info.mime cannot be null");
     return kSbPlayerInvalid;
   }
   if (!max_video_capabilities) {
-    SB_LOG(ERROR) << "creation_param->video_sample_info.max_video_capabilities"
+    SB_LOG(ERROR) << "creation_param->video_stream_info.max_video_capabilities"
                   << " cannot be null.";
     player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
-                      "creation_param->video_sample_info.max_video_"
+                      "creation_param->video_stream_info.max_video_"
                       "capabilities cannot be null");
     return kSbPlayerInvalid;
   }
@@ -112,8 +118,8 @@
     return kSbPlayerInvalid;
   }
 
-  auto audio_codec = creation_param->audio_sample_info.codec;
-  auto video_codec = creation_param->video_sample_info.codec;
+  auto audio_codec = audio_stream_info.codec;
+  auto video_codec = video_stream_info.codec;
 
   if (audio_codec != kSbMediaAudioCodecNone &&
       audio_codec != kSbMediaAudioCodecAac &&
@@ -156,13 +162,12 @@
   }
 
   std::string error_message;
-  if (has_audio && creation_param->audio_sample_info.number_of_channels >
-                       SbAudioSinkGetMaxChannels()) {
+  if (has_audio &&
+      audio_stream_info.number_of_channels > SbAudioSinkGetMaxChannels()) {
     error_message = starboard::FormatString(
         "Number of audio channels (%d) exceeds the maximum number of audio "
         "channels supported by this platform (%d)",
-        creation_param->audio_sample_info.number_of_channels,
-        SbAudioSinkGetMaxChannels());
+        audio_stream_info.number_of_channels, SbAudioSinkGetMaxChannels());
     SB_LOG(ERROR) << error_message << ".";
     player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
                       error_message.c_str());
@@ -179,25 +184,19 @@
     return kSbPlayerInvalid;
   }
 
-  if (strlen(max_video_capabilities) == 0) {
-    // Check the availability of hardware video decoder. Main player must use a
-    // hardware codec, but Android doesn't support multiple concurrent hardware
-    // codecs. Since it's not safe to have multiple hardware codecs, we only
-    // support one main player on Android, which can be either in punch out mode
-    // or decode to target mode.
-    const int kMaxNumberOfHardwareDecoders = 1;
-    auto number_of_hardware_decoders =
-        VideoDecoder::number_of_hardware_decoders();
-    if (number_of_hardware_decoders >= kMaxNumberOfHardwareDecoders) {
-      error_message = starboard::FormatString(
-          "Number of hardware decoders (%d) is equal to or exceeds the max "
-          "number of hardware decoders supported by this platform (%d)",
-          number_of_hardware_decoders, kMaxNumberOfHardwareDecoders);
-      SB_LOG(ERROR) << error_message << ".";
-      player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
-                        error_message.c_str());
-      return kSbPlayerInvalid;
-    }
+  // Android doesn't support multiple concurrent hardware decoders, so we can't
+  // have more than one primary player. And as secondary player is disabled on
+  // android, we simply check the number of active players here.
+  const int kMaxNumberOfPlayers = 1;
+  if (SbPlayerPrivate::number_of_players() >= kMaxNumberOfPlayers) {
+    error_message = starboard::FormatString(
+        "Failed to create a new player. Platform cannot support more than %d "
+        "players.",
+        kMaxNumberOfPlayers);
+    SB_LOG(ERROR) << error_message << ".";
+    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
+                      error_message.c_str());
+    return kSbPlayerInvalid;
   }
 
   if (creation_param->output_mode != kSbPlayerOutputModeDecodeToTexture &&
@@ -219,9 +218,8 @@
   starboard::scoped_ptr<PlayerWorker::Handler> handler(
       new FilterBasedPlayerWorkerHandler(creation_param, provider));
   SbPlayer player = SbPlayerPrivate::CreateInstance(
-      audio_codec, video_codec, &creation_param->audio_sample_info,
-      sample_deallocate_func, decoder_status_func, player_status_func,
-      player_error_func, context, handler.Pass());
+      audio_codec, video_codec, sample_deallocate_func, decoder_status_func,
+      player_status_func, player_error_func, context, handler.Pass());
 
   if (creation_param->output_mode != kSbPlayerOutputModeDecodeToTexture) {
     // TODO: accomplish this through more direct means.
diff --git a/starboard/android/shared/player_get_preferred_output_mode.cc b/starboard/android/shared/player_get_preferred_output_mode.cc
index 66df1e6..2d1880f 100644
--- a/starboard/android/shared/player_get_preferred_output_mode.cc
+++ b/starboard/android/shared/player_get_preferred_output_mode.cc
@@ -29,29 +29,40 @@
     return kSbPlayerOutputModeInvalid;
   }
 
-  if (creation_param->audio_sample_info.codec != kSbMediaAudioCodecNone &&
-      !creation_param->audio_sample_info.mime) {
-    SB_LOG(ERROR) << "creation_param->audio_sample_info.mime cannot be NULL";
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaAudioStreamInfo& audio_stream_info =
+      creation_param->audio_stream_info;
+  const SbMediaVideoStreamInfo& video_stream_info =
+      creation_param->video_stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaAudioSampleInfo& audio_stream_info =
+      creation_param->audio_sample_info;
+  const SbMediaVideoSampleInfo& video_stream_info =
+      creation_param->video_sample_info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+  if (audio_stream_info.codec != kSbMediaAudioCodecNone &&
+      !audio_stream_info.mime) {
+    SB_LOG(ERROR) << "creation_param->audio_stream_info.mime cannot be NULL";
     return kSbPlayerOutputModeInvalid;
   }
 
-  if (creation_param->video_sample_info.codec != kSbMediaVideoCodecNone &&
-      !creation_param->video_sample_info.mime) {
-    SB_LOG(ERROR) << "creation_param->video_sample_info.mime cannot be NULL";
+  if (video_stream_info.codec != kSbMediaVideoCodecNone &&
+      !video_stream_info.mime) {
+    SB_LOG(ERROR) << "creation_param->video_stream_info.mime cannot be NULL";
     return kSbPlayerOutputModeInvalid;
   }
 
-  if (creation_param->video_sample_info.codec != kSbMediaVideoCodecNone &&
-      !creation_param->video_sample_info.max_video_capabilities) {
-    SB_LOG(ERROR) << "creation_param->video_sample_info.max_video_capabilities"
+  if (video_stream_info.codec != kSbMediaVideoCodecNone &&
+      !video_stream_info.max_video_capabilities) {
+    SB_LOG(ERROR) << "creation_param->video_stream_info.max_video_capabilities"
                   << " cannot be NULL";
     return kSbPlayerOutputModeInvalid;
   }
 
-  auto codec = creation_param->video_sample_info.codec;
+  auto codec = video_stream_info.codec;
   auto drm_system = creation_param->drm_system;
-  auto max_video_capabilities =
-      creation_param->video_sample_info.max_video_capabilities;
+  auto max_video_capabilities = video_stream_info.max_video_capabilities;
 
   // Sub players must use decode-to-texture on Android.
   if (max_video_capabilities && strlen(max_video_capabilities) > 0) {
@@ -65,7 +76,8 @@
 
   // The main player may use any output mode.
   SbPlayerOutputMode output_modes_to_check[] = {
-      kSbPlayerOutputModePunchOut, kSbPlayerOutputModeDecodeToTexture,
+      kSbPlayerOutputModePunchOut,
+      kSbPlayerOutputModeDecodeToTexture,
   };
 
   // Check |kSbPlayerOutputModeDecodeToTexture| first if the caller prefers it.
@@ -83,8 +95,7 @@
     return output_modes_to_check[1];
   }
 
-  SB_LOG(WARNING) << "creation_param->video_sample_info.codec ("
-                  << creation_param->video_sample_info.codec
-                  << ") is not supported";
+  SB_LOG(WARNING) << "creation_param->video_stream_info.codec ("
+                  << video_stream_info.codec << ") is not supported";
   return kSbPlayerOutputModeInvalid;
 }
diff --git a/starboard/android/shared/speech_synthesis_speak.cc b/starboard/android/shared/speech_synthesis_speak.cc
index b571951..23865f0 100644
--- a/starboard/android/shared/speech_synthesis_speak.cc
+++ b/starboard/android/shared/speech_synthesis_speak.cc
@@ -21,6 +21,9 @@
 using starboard::android::shared::ScopedLocalJavaRef;
 
 void SbSpeechSynthesisSpeak(const char* text) {
+  if (!text) {
+    return;
+  }
   JniEnvExt* env = JniEnvExt::Get();
   ScopedLocalJavaRef<jobject> j_tts_helper(
       env->CallStarboardObjectMethodOrAbort(
@@ -28,6 +31,6 @@
           "()Ldev/cobalt/coat/CobaltTextToSpeechHelper;"));
   ScopedLocalJavaRef<jstring> j_text_string(
       env->NewStringStandardUTFOrAbort(text));
-  env->CallVoidMethodOrAbort(j_tts_helper.Get(),
-      "speak", "(Ljava/lang/String;)V", j_text_string.Get());
+  env->CallVoidMethodOrAbort(j_tts_helper.Get(), "speak",
+                             "(Ljava/lang/String;)V", j_text_string.Get());
 }
diff --git a/starboard/android/shared/system_get_extensions.cc b/starboard/android/shared/system_get_extensions.cc
index 531f932..ab642b1 100644
--- a/starboard/android/shared/system_get_extensions.cc
+++ b/starboard/android/shared/system_get_extensions.cc
@@ -16,13 +16,15 @@
 
 #include "starboard/android/shared/android_media_session_client.h"
 #include "starboard/android/shared/configuration.h"
-#include "starboard/android/shared/crash_handler.h"
 #include "starboard/android/shared/graphics.h"
 #include "starboard/android/shared/platform_service.h"
 #include "starboard/common/log.h"
 #include "starboard/common/string.h"
 #if SB_IS(EVERGREEN_COMPATIBLE)
 #include "starboard/elf_loader/evergreen_config.h"  // nogncheck
+#include "starboard/shared/starboard/crash_handler.h"
+#else
+#include "starboard/android/shared/crash_handler.h"
 #endif
 #include "starboard/extension/configuration.h"
 #include "starboard/extension/crash_handler.h"
@@ -55,7 +57,11 @@
     return starboard::android::shared::GetGraphicsApi();
   }
   if (strcmp(name, kCobaltExtensionCrashHandlerName) == 0) {
+#if SB_IS(EVERGREEN_COMPATIBLE)
+    return starboard::common::GetCrashHandlerApi();
+#else
     return starboard::android::shared::GetCrashHandlerApi();
+#endif
   }
   return NULL;
 }
diff --git a/starboard/android/shared/video_decoder.cc b/starboard/android/shared/video_decoder.cc
index e84cf2e..e9b980a 100644
--- a/starboard/android/shared/video_decoder.cc
+++ b/starboard/android/shared/video_decoder.cc
@@ -52,21 +52,29 @@
 
 class VideoFrameImpl : public VideoFrame {
  public:
+  typedef std::function<void()> VideoFrameReleaseCallback;
+
   VideoFrameImpl(const DequeueOutputResult& dequeue_output_result,
-                 MediaCodecBridge* media_codec_bridge)
+                 MediaCodecBridge* media_codec_bridge,
+                 const VideoFrameReleaseCallback& release_callback)
       : VideoFrame(dequeue_output_result.flags & BUFFER_FLAG_END_OF_STREAM
                        ? kMediaTimeEndOfStream
                        : dequeue_output_result.presentation_time_microseconds),
         dequeue_output_result_(dequeue_output_result),
         media_codec_bridge_(media_codec_bridge),
-        released_(false) {
+        released_(false),
+        release_callback_(release_callback) {
     SB_DCHECK(media_codec_bridge_);
+    SB_DCHECK(release_callback_);
   }
 
   ~VideoFrameImpl() {
     if (!released_) {
       media_codec_bridge_->ReleaseOutputBuffer(dequeue_output_result_.index,
                                                false);
+      if (!is_end_of_stream()) {
+        release_callback_();
+      }
     }
   }
 
@@ -76,12 +84,14 @@
     released_ = true;
     media_codec_bridge_->ReleaseOutputBufferAtTimestamp(
         dequeue_output_result_.index, release_time_in_nanoseconds);
+    release_callback_();
   }
 
  private:
   DequeueOutputResult dequeue_output_result_;
   MediaCodecBridge* media_codec_bridge_;
   volatile bool released_;
+  const VideoFrameReleaseCallback release_callback_;
 };
 
 const SbTime kInitialPrerollTimeout = 250 * kSbTimeMillisecond;
@@ -174,8 +184,6 @@
   VideoFrameTracker* frame_tracker_;
 };
 
-int VideoDecoder::number_of_hardware_decoders_ = 0;
-
 class VideoDecoder::Sink : public VideoDecoder::VideoRendererSink {
  public:
   bool Render() {
@@ -215,7 +223,7 @@
                            SbPlayerOutputMode output_mode,
                            SbDecodeTargetGraphicsContextProvider*
                                decode_target_graphics_context_provider,
-                           const char* max_video_capabilities,
+                           const std::string& max_video_capabilities,
                            int tunnel_mode_audio_session_id,
                            bool force_secure_pipeline_under_tunnel_mode,
                            bool force_reset_surface_under_tunnel_mode,
@@ -232,10 +240,10 @@
           force_reset_surface_under_tunnel_mode),
       has_new_texture_available_(false),
       surface_condition_variable_(surface_destroy_mutex_),
-      require_software_codec_(max_video_capabilities &&
-                              strlen(max_video_capabilities) > 0),
+      require_software_codec_(!max_video_capabilities.empty()),
       force_big_endian_hdr_metadata_(force_big_endian_hdr_metadata),
-      force_improved_support_check_(force_improved_support_check) {
+      force_improved_support_check_(force_improved_support_check),
+      number_of_preroll_frames_(kInitialPrerollFrameCount) {
   SB_DCHECK(error_message);
 
   if (tunnel_mode_audio_session_id != -1) {
@@ -253,9 +261,6 @@
   if (require_software_codec_) {
     SB_DCHECK(output_mode_ == kSbPlayerOutputModeDecodeToTexture);
   }
-  if (!require_software_codec_) {
-    number_of_hardware_decoders_++;
-  }
 
   if (video_codec_ != kSbMediaVideoCodecAv1) {
     if (!InitializeCodec(error_message)) {
@@ -274,10 +279,6 @@
   } else {
     ClearVideoWindow(false);
   }
-
-  if (!require_software_codec_) {
-    number_of_hardware_decoders_--;
-  }
 }
 
 scoped_refptr<VideoDecoder::VideoRendererSink> VideoDecoder::GetSink() {
@@ -327,7 +328,7 @@
   if (input_buffer_written_ > 0 && first_buffer_timestamp_ != 0) {
     return kNonInitialPrerollFrameCount;
   }
-  return kInitialPrerollFrameCount;
+  return number_of_preroll_frames_;
 }
 
 SbTime VideoDecoder::GetPrerollTimeout() const {
@@ -350,7 +351,7 @@
     // If color metadata is present and is not an identity mapping, then
     // teardown the codec so it can be reinitalized with the new metadata.
     const auto& color_metadata =
-        input_buffers.front()->video_sample_info().color_metadata;
+        input_buffers.front()->video_stream_info().color_metadata;
     if (!IsIdentity(color_metadata)) {
       SB_DCHECK(!color_metadata_) << "Unexpected residual color metadata.";
       SB_LOG(INFO) << "Reinitializing codec with HDR color metadata.";
@@ -458,9 +459,14 @@
   TeardownCodec();
   CancelPendingJobs();
 
+  // After TeardownCodec, buffered_output_frames_ should equal to 0.
+  SB_DCHECK(buffered_output_frames_ == 0);
+
   tunnel_mode_prerolling_.store(true);
   tunnel_mode_frame_rendered_.store(false);
   input_buffer_written_ = 0;
+  decoded_output_frames_ = 0;
+  output_format_ = starboard::nullopt;
   end_of_stream_written_ = false;
   video_fps_ = 0;
   pending_input_buffers_.clear();
@@ -724,9 +730,27 @@
 
   bool is_end_of_stream =
       dequeue_output_result.flags & BUFFER_FLAG_END_OF_STREAM;
+  if (!is_end_of_stream) {
+    ++decoded_output_frames_;
+    if (output_format_) {
+      ++buffered_output_frames_;
+      // We have to wait until we feed enough inputs to the decoder and receive
+      // enough outputs before update |max_buffered_output_frames_|. Otherwise,
+      // |max_buffered_output_frames_| may be updated to a very small number
+      // when we receive the first few outputs.
+      if (decoded_output_frames_ > kInitialPrerollFrameCount &&
+          buffered_output_frames_ > max_buffered_output_frames_) {
+        max_buffered_output_frames_ = buffered_output_frames_;
+        MaxMediaCodecOutputBuffersLookupTable::GetInstance()
+            ->UpdateMaxOutputBuffers(output_format_.value(),
+                                     max_buffered_output_frames_);
+      }
+    }
+  }
   decoder_status_cb_(
       is_end_of_stream ? kBufferFull : kNeedMoreInput,
-      new VideoFrameImpl(dequeue_output_result, media_codec_bridge));
+      new VideoFrameImpl(dequeue_output_result, media_codec_bridge,
+                         std::bind(&VideoDecoder::OnVideoFrameRelease, this)));
 }
 
 void VideoDecoder::RefreshOutputFormat(MediaCodecBridge* media_codec_bridge) {
@@ -739,6 +763,29 @@
       media_codec_bridge->GetOutputDimensions();
   frame_width_ = output_dimensions.width;
   frame_height_ = output_dimensions.height;
+
+  if (tunnel_mode_audio_session_id_ != -1) {
+    return;
+  }
+  if (first_output_format_changed_) {
+    // After resolution changes, the output buffers may have frames of different
+    // resolutions. In that case, it's hard to determine the max supported
+    // output buffers. So, we reset |output_format_| to null here to skip max
+    // output buffers check.
+    output_format_ = starboard::nullopt;
+    return;
+  }
+  output_format_ = VideoOutputFormat(video_codec_, output_dimensions.width,
+                                     output_dimensions.height,
+                                     (color_metadata_ ? true : false));
+  first_output_format_changed_ = true;
+  auto max_output_buffers =
+      MaxMediaCodecOutputBuffersLookupTable::GetInstance()
+          ->GetMaxOutputVideoBuffers(output_format_.value());
+  if (max_output_buffers > 0 &&
+      max_output_buffers < kInitialPrerollFrameCount) {
+    number_of_preroll_frames_ = max_output_buffers;
+  }
 }
 
 bool VideoDecoder::Tick(MediaCodecBridge* media_codec_bridge) {
@@ -928,6 +975,13 @@
            kNeedMoreInputCheckIntervalInTunnelMode);
 }
 
+void VideoDecoder::OnVideoFrameRelease() {
+  if (output_format_) {
+    --buffered_output_frames_;
+    SB_DCHECK(buffered_output_frames_ >= 0);
+  }
+}
+
 void VideoDecoder::OnSurfaceDestroyed() {
   if (!BelongsToCurrentThread()) {
     // Wait until codec is stopped.
diff --git a/starboard/android/shared/video_decoder.h b/starboard/android/shared/video_decoder.h
index 19a8b82..34c7001 100644
--- a/starboard/android/shared/video_decoder.h
+++ b/starboard/android/shared/video_decoder.h
@@ -16,10 +16,12 @@
 #define STARBOARD_ANDROID_SHARED_VIDEO_DECODER_H_
 
 #include <atomic>
+#include <memory>
 #include <string>
 #include <vector>
 
 #include "starboard/android/shared/drm_system.h"
+#include "starboard/android/shared/max_output_buffers_lookup_table.h"
 #include "starboard/android/shared/media_codec_bridge.h"
 #include "starboard/android/shared/media_decoder.h"
 #include "starboard/android/shared/video_frame_tracker.h"
@@ -55,16 +57,12 @@
 
   class Sink;
 
-  static int number_of_hardware_decoders() {
-    return number_of_hardware_decoders_;
-  }
-
   VideoDecoder(SbMediaVideoCodec video_codec,
                SbDrmSystem drm_system,
                SbPlayerOutputMode output_mode,
                SbDecodeTargetGraphicsContextProvider*
                    decode_target_graphics_context_provider,
-               const char* max_video_capabilities,
+               const std::string& max_video_capabilities,
                int tunnel_mode_audio_session_id,
                bool force_secure_pipeline_under_tunnel_mode,
                bool force_reset_surface_under_tunnel_mode,
@@ -117,11 +115,11 @@
   void OnTunnelModePrerollTimeout();
   void OnTunnelModeCheckForNeedMoreInput();
 
+  void OnVideoFrameRelease();
+
   void OnSurfaceDestroyed() override;
   void ReportError(SbPlayerError error, const std::string& error_message);
 
-  static int number_of_hardware_decoders_;
-
   // These variables will be initialized inside ctor or Initialize() and will
   // not be changed during the life time of this class.
   const SbMediaVideoCodec video_codec_;
@@ -197,6 +195,15 @@
 
   std::vector<scoped_refptr<InputBuffer>> pending_input_buffers_;
   int video_fps_ = 0;
+
+  // The variables below are used to calculate platform max supported MediaCodec
+  // output buffers.
+  int decoded_output_frames_ = 0;
+  int buffered_output_frames_ = 0;
+  int max_buffered_output_frames_ = 0;
+  bool first_output_format_changed_ = false;
+  optional<VideoOutputFormat> output_format_;
+  size_t number_of_preroll_frames_;
 };
 
 }  // namespace shared
diff --git a/starboard/android/x86/toolchain/BUILD.gn b/starboard/android/x86/toolchain/BUILD.gn
index 3f6180e..8bf6d29 100644
--- a/starboard/android/x86/toolchain/BUILD.gn
+++ b/starboard/android/x86/toolchain/BUILD.gn
@@ -16,15 +16,6 @@
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/android/shared/toolchain/toolchain.gni")
 
-clang_toolchain("host") {
-  clang_base_path = clang_base_path
-
-  toolchain_args = {
-    current_os = "linux"
-    current_cpu = "x86"
-  }
-}
-
 gcc_toolchain("target") {
   prefix = rebase_path("$android_toolchain_path/bin", root_build_dir)
   cc = "$prefix/i686-linux-android${android_ndk_api_level}-clang"
diff --git a/starboard/atomic.h b/starboard/atomic.h
index 6dc938b..47f0872 100644
--- a/starboard/atomic.h
+++ b/starboard/atomic.h
@@ -252,492 +252,14 @@
 }  // extern "C"
 #endif
 
-// C++ Can choose the right function based on overloaded arguments. This
-// provides overloaded versions that will choose the right function version
-// based on type for C++ callers.
-
-#ifdef __cplusplus
-extern "C++" {
-namespace starboard {
-namespace atomic {
-
-inline SbAtomic8 Release_CompareAndSwap(volatile SbAtomic8* ptr,
-                                        SbAtomic8 old_value,
-                                        SbAtomic8 new_value) {
-  return SbAtomicRelease_CompareAndSwap8(ptr, old_value, new_value);
-}
-
-inline void NoBarrier_Store(volatile SbAtomic8* ptr, SbAtomic8 value) {
-  SbAtomicNoBarrier_Store8(ptr, value);
-}
-
-inline SbAtomic8 NoBarrier_Load(volatile const SbAtomic8* ptr) {
-  return SbAtomicNoBarrier_Load8(ptr);
-}
-
-inline SbAtomic32 NoBarrier_CompareAndSwap(volatile SbAtomic32* ptr,
-                                           SbAtomic32 old_value,
-                                           SbAtomic32 new_value) {
-  return SbAtomicNoBarrier_CompareAndSwap(ptr, old_value, new_value);
-}
-
-inline SbAtomic32 NoBarrier_Exchange(volatile SbAtomic32* ptr,
-                                     SbAtomic32 new_value) {
-  return SbAtomicNoBarrier_Exchange(ptr, new_value);
-}
-
-inline SbAtomic32 NoBarrier_Increment(volatile SbAtomic32* ptr,
-                                      SbAtomic32 increment) {
-  return SbAtomicNoBarrier_Increment(ptr, increment);
-}
-
-inline SbAtomic32 Barrier_Increment(volatile SbAtomic32* ptr,
-                                    SbAtomic32 increment) {
-  return SbAtomicBarrier_Increment(ptr, increment);
-}
-
-inline SbAtomic32 Acquire_CompareAndSwap(volatile SbAtomic32* ptr,
-                                         SbAtomic32 old_value,
-                                         SbAtomic32 new_value) {
-  return SbAtomicAcquire_CompareAndSwap(ptr, old_value, new_value);
-}
-
-inline SbAtomic32 Release_CompareAndSwap(volatile SbAtomic32* ptr,
-                                         SbAtomic32 old_value,
-                                         SbAtomic32 new_value) {
-  return SbAtomicRelease_CompareAndSwap(ptr, old_value, new_value);
-}
-
-inline void NoBarrier_Store(volatile SbAtomic32* ptr, SbAtomic32 value) {
-  SbAtomicNoBarrier_Store(ptr, value);
-}
-
-inline void Acquire_Store(volatile SbAtomic32* ptr, SbAtomic32 value) {
-  SbAtomicAcquire_Store(ptr, value);
-}
-
-inline void Release_Store(volatile SbAtomic32* ptr, SbAtomic32 value) {
-  SbAtomicRelease_Store(ptr, value);
-}
-
-inline SbAtomic32 NoBarrier_Load(volatile const SbAtomic32* ptr) {
-  return SbAtomicNoBarrier_Load(ptr);
-}
-
-inline SbAtomic32 Acquire_Load(volatile const SbAtomic32* ptr) {
-  return SbAtomicAcquire_Load(ptr);
-}
-
-inline SbAtomic32 Release_Load(volatile const SbAtomic32* ptr) {
-  return SbAtomicRelease_Load(ptr);
-}
-
-#if SB_HAS(64_BIT_ATOMICS)
-inline SbAtomic64 NoBarrier_CompareAndSwap(volatile SbAtomic64* ptr,
-                                           SbAtomic64 old_value,
-                                           SbAtomic64 new_value) {
-  return SbAtomicNoBarrier_CompareAndSwap64(ptr, old_value, new_value);
-}
-
-inline SbAtomic64 NoBarrier_Exchange(volatile SbAtomic64* ptr,
-                                     SbAtomic64 new_value) {
-  return SbAtomicNoBarrier_Exchange64(ptr, new_value);
-}
-
-inline SbAtomic64 NoBarrier_Increment(volatile SbAtomic64* ptr,
-                                      SbAtomic64 increment) {
-  return SbAtomicNoBarrier_Increment64(ptr, increment);
-}
-
-inline SbAtomic64 Barrier_Increment(volatile SbAtomic64* ptr,
-                                    SbAtomic64 increment) {
-  return SbAtomicBarrier_Increment64(ptr, increment);
-}
-
-inline SbAtomic64 Acquire_CompareAndSwap(volatile SbAtomic64* ptr,
-                                         SbAtomic64 old_value,
-                                         SbAtomic64 new_value) {
-  return SbAtomicAcquire_CompareAndSwap64(ptr, old_value, new_value);
-}
-
-inline SbAtomic64 Release_CompareAndSwap(volatile SbAtomic64* ptr,
-                                         SbAtomic64 old_value,
-                                         SbAtomic64 new_value) {
-  return SbAtomicRelease_CompareAndSwap64(ptr, old_value, new_value);
-}
-
-inline void NoBarrier_Store(volatile SbAtomic64* ptr, SbAtomic64 value) {
-  SbAtomicNoBarrier_Store64(ptr, value);
-}
-
-inline void Acquire_Store(volatile SbAtomic64* ptr, SbAtomic64 value) {
-  SbAtomicAcquire_Store64(ptr, value);
-}
-
-inline void Release_Store(volatile SbAtomic64* ptr, SbAtomic64 value) {
-  SbAtomicRelease_Store64(ptr, value);
-}
-
-inline SbAtomic64 NoBarrier_Load(volatile const SbAtomic64* ptr) {
-  return SbAtomicNoBarrier_Load64(ptr);
-}
-
-inline SbAtomic64 Acquire_Load(volatile const SbAtomic64* ptr) {
-  return SbAtomicAcquire_Load64(ptr);
-}
-
-inline SbAtomic64 Release_Load(volatile const SbAtomic64* ptr) {
-  return SbAtomicRelease_Load64(ptr);
-}
-#endif  // SB_HAS(64_BIT_ATOMICS)
-
-}  // namespace atomic
-}  // namespace starboard
-}  // extern "C++"
-#endif
-
 // Include the platform definitions of these functions, which should be defined
-// as inlined. This macro is defined on the command line by gyp.
+// as inlined. This macro is defined on the command line by GN.
 #include STARBOARD_ATOMIC_INCLUDE
 
-#ifdef __cplusplus
-
-#include "starboard/common/mutex.h"
-
+#if SB_API_VERSION < SB_ATOMIC_MOVED_API_VERSION && defined(__cplusplus)
 extern "C++" {
-namespace starboard {
-
-// Provides atomic types like integer and float. Some types like atomic_int32_t
-// are likely to be hardware accelerated for your platform.
-//
-// Never use the parent types like atomic_base<T>, atomic_number<T> or
-// atomic_integral<T> and instead use the fully qualified classes like
-// atomic_int32_t, atomic_pointer<T*>, etc.
-//
-// Note on template instantiation, avoid using the parent type and instead
-// use the fully qualified type.
-// BAD:
-//  template<typename T>
-//  void Foo(const atomic_base<T>& value);
-// GOOD:
-//  template<typename atomic_t>
-//  void Foo(const atomic_t& value);
-
-// Atomic Pointer class. Instantiate as atomic_pointer<void*>
-// for void* pointer types.
-template <typename T>
-class atomic_pointer;
-
-// Atomic bool class.
-class atomic_bool;
-
-// Atomic int32 class
-class atomic_int32_t;
-
-// Atomic int64 class.
-class atomic_int64_t;
-
-// Atomic float class.
-class atomic_float;
-
-// Atomic double class.
-class atomic_double;
-
-///////////////////////////////////////////////////////////////////////////////
-// Class hierarchy.
-///////////////////////////////////////////////////////////////////////////////
-
-// Base functionality for atomic types. Defines exchange(), load(),
-// store(), compare_exchange_weak(), compare_exchange_strong()
-template <typename T>
-class atomic_base;
-
-// Subtype of atomic_base<T> for numbers likes float and integer types but not
-// bool.  Adds fetch_add() and fetch_sub().
-template <typename T>
-class atomic_number;
-
-// Subtype of atomic_number<T> for integer types like int32 and int64. Adds
-// increment and decrement.
-template <typename T>
-class atomic_integral;
-
-///////////////////////////////////////////////////////////////////////////////
-// Implementation.
-///////////////////////////////////////////////////////////////////////////////
-
-// Similar to C++11 std::atomic<T>.
-// atomic_base<T> may be instantiated with any TriviallyCopyable type T.
-// atomic_base<T> is neither copyable nor movable.
-template <typename T>
-class atomic_base {
- public:
-  typedef T ValueType;
-
-  // C++11 forbids a copy constructor for std::atomic<T>, it also forbids
-  // a move operation.
-  atomic_base() : value_() {}
-  explicit atomic_base(T v) : value_(v) {}
-
-  // Checks whether the atomic operations on all objects of this type
-  // are lock-free.
-  // Returns true if the atomic operations on the objects of this type
-  // are lock-free, false otherwise.
-  //
-  // All atomic types may be implemented using mutexes or other locking
-  // operations, rather than using the lock-free atomic CPU instructions.
-  // atomic types are also allowed to be sometimes lock-free, e.g. if only
-  // aligned memory accesses are naturally atomic on a given architecture,
-  // misaligned objects of the same type have to use locks.
-  //
-  // See also std::atomic<T>::is_lock_free().
-  bool is_lock_free() const { return false; }
-  bool is_lock_free() const volatile { return false; }
-
-  // Atomically replaces the value of the atomic object and returns the value
-  // held previously.
-  // See also std::atomic<T>::exchange().
-  T exchange(T new_val) {
-    T old_value;
-    {
-      starboard::ScopedLock lock(mutex_);
-      old_value = value_;
-      value_ = new_val;
-    }
-    return old_value;
-  }
-
-  // Atomically obtains the value of the atomic object.
-  // See also std::atomic<T>::load().
-  T load() const {
-    starboard::ScopedLock lock(mutex_);
-    return value_;
-  }
-
-  // Stores the value. See std::atomic<T>::store(...)
-  void store(T val) {
-    starboard::ScopedLock lock(mutex_);
-    value_ = val;
-  }
-
-  // compare_exchange_strong(...) sets the new value if and only if
-  // *expected_value matches what is stored internally.
-  // If this succeeds then true is returned and *expected_value == new_value.
-  // Otherwise If there is a mismatch then false is returned and
-  // *expected_value is set to the internal value.
-  // Inputs:
-  //  new_value: Attempt to set the value to this new value.
-  //  expected_value: A test condition for success. If the actual value
-  //    matches the expected_value then the swap will succeed.
-  //
-  // See also std::atomic<T>::compare_exchange_strong(...).
-  bool compare_exchange_strong(T* expected_value, T new_value) {
-    // Save original value so that its local. This hints to the compiler
-    // that test_val doesn't have aliasing issues and should result in
-    // more optimal code inside of the lock.
-    const T test_val = *expected_value;
-    starboard::ScopedLock lock(mutex_);
-    if (test_val == value_) {
-      value_ = new_value;
-      return true;
-    } else {
-      *expected_value = value_;
-      return false;
-    }
-  }
-
-  // Weak version of this function is documented to be faster, but has allows
-  // weaker memory ordering and therefore will sometimes have a false negative:
-  // The value compared will actually be equal but the return value from this
-  // function indicates otherwise.
-  // By default, the function delegates to compare_exchange_strong(...).
-  //
-  // See also std::atomic<T>::compare_exchange_weak(...).
-  bool compare_exchange_weak(T* expected_value, T new_value) {
-    return compare_exchange_strong(expected_value, new_value);
-  }
-
- protected:
-  T value_;
-  starboard::Mutex mutex_;
-};
-
-// A subclass of atomic_base<T> that adds fetch_add(...) and fetch_sub(...).
-template <typename T>
-class atomic_number : public atomic_base<T> {
- public:
-  typedef atomic_base<T> Super;
-  typedef T ValueType;
-
-  atomic_number() : Super() {}
-  explicit atomic_number(T v) : Super(v) {}
-
-  // Returns the previous value before the input value was added.
-  // See also std::atomic<T>::fetch_add(...).
-  T fetch_add(T val) {
-    T old_val;
-    {
-      starboard::ScopedLock lock(this->mutex_);
-      old_val = this->value_;
-      this->value_ += val;
-    }
-    return old_val;
-  }
-
-  // Returns the value before the operation was applied.
-  // See also std::atomic<T>::fetch_sub(...).
-  T fetch_sub(T val) {
-    T old_val;
-    {
-      starboard::ScopedLock lock(this->mutex_);
-      old_val = this->value_;
-      this->value_ -= val;
-    }
-    return old_val;
-  }
-};
-
-// A subclass to classify Atomic Integers. Adds increment and decrement
-// functions.
-template <typename T>
-class atomic_integral : public atomic_number<T> {
- public:
-  typedef atomic_number<T> Super;
-
-  atomic_integral() : Super() {}
-  explicit atomic_integral(T v) : Super(v) {}
-
-  T increment() { return this->fetch_add(T(1)); }
-  T decrement() { return this->fetch_sub(T(1)); }
-};
-
-// atomic_pointer class.
-template <typename T>
-class atomic_pointer : public atomic_base<T> {
- public:
-  typedef atomic_base<T> Super;
-  atomic_pointer() : Super() {}
-  explicit atomic_pointer(T initial_val) : Super(initial_val) {}
-};
-
-// Simple atomic bool class.
-class atomic_bool {
- public:
-  typedef bool ValueType;
-
-  // C++11 forbids a copy constructor for std::atomic<T>, it also forbids
-  // a move operation.
-  atomic_bool() : value_(false) {}
-  explicit atomic_bool(bool v) : value_(v) {}
-
-  bool is_lock_free() const { return true; }
-  bool is_lock_free() const volatile { return true; }
-
-  bool exchange(bool new_val) {
-    return SbAtomicNoBarrier_Exchange(volatile_ptr(), new_val);
-  }
-
-  bool load() const { return SbAtomicAcquire_Load(volatile_const_ptr()) != 0; }
-
-  void store(bool val) { SbAtomicRelease_Store(volatile_ptr(), val); }
-
- private:
-  volatile int32_t* volatile_ptr() { return &value_; }
-  volatile const int32_t* volatile_const_ptr() const { return &value_; }
-  int32_t value_;
-};
-
-// Lockfree atomic int class.
-class atomic_int32_t {
- public:
-  typedef int32_t ValueType;
-  atomic_int32_t() : value_(0) {}
-  explicit atomic_int32_t(SbAtomic32 value) : value_(value) {}
-
-  bool is_lock_free() const { return true; }
-  bool is_lock_free() const volatile { return true; }
-
-  int32_t increment() { return fetch_add(1); }
-  int32_t decrement() { return fetch_add(-1); }
-
-  int32_t fetch_add(int32_t val) {
-    // fetch_add is a post-increment operation, while SbAtomicBarrier_Increment
-    // is a pre-increment operation. Therefore subtract the value to match
-    // the expected interface.
-    return SbAtomicBarrier_Increment(volatile_ptr(), val) - val;
-  }
-
-  int32_t fetch_sub(int32_t val) { return fetch_add(-val); }
-
-  // Atomically replaces the value of the atomic object
-  // and returns the value held previously.
-  // See also std::atomic<T>::exchange().
-  int32_t exchange(int32_t new_val) {
-    return SbAtomicNoBarrier_Exchange(volatile_ptr(), new_val);
-  }
-
-  // Atomically obtains the value of the atomic object.
-  // See also std::atomic<T>::load().
-  int32_t load() const { return SbAtomicAcquire_Load(volatile_const_ptr()); }
-
-  // Stores the value. See std::atomic<T>::store(...)
-  void store(int32_t val) { SbAtomicRelease_Store(volatile_ptr(), val); }
-
-  bool compare_exchange_strong(int32_t* expected_value, int32_t new_value) {
-    int32_t prev_value = *expected_value;
-    SbAtomicMemoryBarrier();
-    int32_t value_written =
-        SbAtomicRelease_CompareAndSwap(volatile_ptr(), prev_value, new_value);
-    const bool write_ok = (prev_value == value_written);
-    if (!write_ok) {
-      *expected_value = value_written;
-    }
-    return write_ok;
-  }
-
-  // Weak version of this function is documented to be faster, but has allows
-  // weaker memory ordering and therefore will sometimes have a false negative:
-  // The value compared will actually be equal but the return value from this
-  // function indicates otherwise.
-  // By default, the function delegates to compare_exchange_strong(...).
-  //
-  // See also std::atomic<T>::compare_exchange_weak(...).
-  bool compare_exchange_weak(int32_t* expected_value, int32_t new_value) {
-    return compare_exchange_strong(expected_value, new_value);
-  }
-
- private:
-  volatile int32_t* volatile_ptr() { return &value_; }
-  volatile const int32_t* volatile_const_ptr() const { return &value_; }
-  int32_t value_;
-};
-
-// Simple atomic int class. This could be optimized for speed using
-// compiler intrinsics for concurrent integer modification.
-class atomic_int64_t : public atomic_integral<int64_t> {
- public:
-  typedef atomic_integral<int64_t> Super;
-  atomic_int64_t() : Super() {}
-  explicit atomic_int64_t(int64_t initial_val) : Super(initial_val) {}
-};
-
-class atomic_float : public atomic_number<float> {
- public:
-  typedef atomic_number<float> Super;
-  atomic_float() : Super() {}
-  explicit atomic_float(float initial_val) : Super(initial_val) {}
-};
-
-class atomic_double : public atomic_number<double> {
- public:
-  typedef atomic_number<double> Super;
-  atomic_double() : Super() {}
-  explicit atomic_double(double initial_val) : Super(initial_val) {}
-};
-
-}  // namespace starboard
+#include "starboard/common/atomic.h"
 }  // extern "C++"
-
-#endif  // __cplusplus
+#endif  // SB_API_VERSION < SB_ATOMIC_MOVED_API_VERSION  && defined(__cplusplus)
 
 #endif  // STARBOARD_ATOMIC_H_
diff --git a/starboard/build/config/BUILD.gn b/starboard/build/config/BUILD.gn
index f407ee5..bbe652c 100644
--- a/starboard/build/config/BUILD.gn
+++ b/starboard/build/config/BUILD.gn
@@ -15,7 +15,7 @@
 import("//build/config/compiler/compiler.gni")
 
 config("base") {
-  defines = []
+  defines = [ "USE_COBALT_CUSTOMIZATIONS" ]
 
   if (is_debug) {
     defines += [
@@ -104,6 +104,8 @@
 
     if (is_starboard) {
       configs = [ ":starboard" ]
+    } else if (is_native_target_build) {
+      configs = [ ":native_target_build" ]
     }
   }
 }
@@ -156,10 +158,6 @@
       defines += [ "SB_IS_EVERGREEN_COMPATIBLE_LIBUNWIND=1" ]
     }
 
-    if (sb_evergreen_compatible_enable_lite) {
-      defines += [ "SB_IS_EVERGREEN_COMPATIBLE_LITE=1" ]
-    }
-
     defines += [
       "STARBOARD_ATOMIC_INCLUDE=\"$starboard_path/atomic_public.h\"",
       "STARBOARD_CONFIGURATION_INCLUDE=\"$starboard_path/configuration_public.h\"",
@@ -167,6 +165,10 @@
   }
 }
 
+config("native_target_build") {
+  defines = [ "NATIVE_TARGET_BUILD" ]
+}
+
 config("starboard_implementation") {
   # This allows the benchmarks to include internal only header files.
   defines = [ "STARBOARD_IMPLEMENTATION" ]
@@ -197,7 +199,8 @@
 }
 
 config("no_pedantic_warnings") {
-  if (current_toolchain == default_toolchain) {
+  if (current_toolchain == default_toolchain ||
+      current_toolchain == "//$starboard_path/toolchain:native_target") {
     if (defined(no_pedantic_warnings_config_path)) {
       configs = [ no_pedantic_warnings_config_path ]
     }
diff --git a/starboard/build/config/BUILDCONFIG.gn b/starboard/build/config/BUILDCONFIG.gn
index 0d3b11c..3b3be5b 100644
--- a/starboard/build/config/BUILDCONFIG.gn
+++ b/starboard/build/config/BUILDCONFIG.gn
@@ -22,6 +22,10 @@
 
   is_starboard = true
 
+  # Used to guard any customizations to Chromium or third-party code for builds
+  # where the Starboard porting layer is not used.
+  is_native_target_build = false
+
   cobalt_fastbuild = getenv("IS_CI") == "1"
 
   is_internal_build = getenv("COBALT_USE_INTERNAL_BUILD") == "1"
@@ -31,6 +35,19 @@
   using_old_compiler = false
 }
 
+assert(!(is_starboard && is_native_target_build),
+       "Targets should be built for Starboard or natively, but not both")
+
+# Used to guard customizations to Chromium or third-party code. We historically
+# used is_starboard for this, but we developed the need to distinguish between
+# a) customizations that should only be used for Starboard builds and b)
+# those that should only be used for native target builds (see
+# is_native_target_build). use_cobalt_customizations is a convenience in the
+# sense that it is functionally equivalent to
+# (is_starboard || is_native_target_build), but it should be used in lieu of
+# that since the intent is more clear.
+use_cobalt_customizations = true
+
 is_debug = build_type == "debug"
 is_devel = build_type == "devel"
 is_qa = build_type == "qa"
@@ -68,7 +85,13 @@
 # Get the path to the starboard implementation and include its GN
 # configuration.
 import("//starboard/build/platform_path.gni")
-host_toolchain = "//$starboard_path/toolchain:host"
+# TODO(b/272790873): Set host_cpu to x86 for 32 bit builds.
+if (target_cpu == "x86" || target_cpu == "arm") {
+  _host_toolchain_cpu = "x86"
+} else {
+  _host_toolchain_cpu = host_cpu
+}
+host_toolchain = "//starboard/build/toolchain/$host_os:$_host_toolchain_cpu"
 target_toolchain = "//$starboard_path/toolchain:target"
 set_default_toolchain(target_toolchain)
 
@@ -156,8 +179,12 @@
 # We make sure to change the pedantic_warnings configs in a particular order to
 # ensure -Wno-foo compiler flags (usually set in no_pedantic_warnings and some
 # of the platform_configuration) come after -Wfoo flags (including -Wall and
-# -Wextra which are set in pedantic_warnings). It is only certain the the "foo"
+# -Wextra which are set in pedantic_warnings). It is only certain that the "foo"
 # error will be ignored if -Wfoo precedes -Wno-foo in the compilation line.
+declare_args() {
+  toolchain_config_path = "//$starboard_path/platform_configuration"
+}
+
 template("target_with_platform_configs") {
   target(invoker.target_type, target_name) {
     forward_variables_from(invoker, "*", [ "target_type" ])
@@ -165,7 +192,7 @@
     if (has_pedantic_warnings) {
       configs += [ "//starboard/build/config:pedantic_warnings" ]
     }
-    configs += [ "//$starboard_path/platform_configuration" ]
+    configs += [ toolchain_config_path ]
     if (!has_pedantic_warnings) {
       configs += [ "//starboard/build/config:no_pedantic_warnings" ]
     }
diff --git a/starboard/build/config/base_configuration.gni b/starboard/build/config/base_configuration.gni
index eb7f8d8..1fc8b72 100644
--- a/starboard/build/config/base_configuration.gni
+++ b/starboard/build/config/base_configuration.gni
@@ -12,8 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import("//build/config/clang/clang.gni")
 import("//cobalt/content/fonts/font_configuration.gni")
+import("//starboard/build/config/clang.gni")
 import("//starboard/build/config/enable_vr.gni")
 
 # NOTE:
@@ -21,11 +21,11 @@
 # Please follow the formatting in this file when adding new ones.
 
 declare_args() {
-  # Enables the yasm compiler to be used to compile .asm files.
-  yasm_exists = false
+  # Enables the nasm compiler to be used to compile .asm files.
+  nasm_exists = false
 
   # Where yasm can be found on the host device.
-  path_to_yasm = "yasm"
+  path_to_nasm = "nasm"
 
   # The Starboard API version of the current build configuration. The default
   # value is meant to be overridden by a Starboard ABI file.
@@ -48,9 +48,6 @@
   # Whether to use the libunwind library on Evergreen compatible platform.
   sb_evergreen_compatible_use_libunwind = false
 
-  # Whether to adopt Evergreen Lite on the Evergreen compatible platform.
-  sb_evergreen_compatible_enable_lite = false
-
   # Whether to generate the whole package containing both Loader app and Cobalt
   # core on the Evergreen compatible platform.
   sb_evergreen_compatible_package = false
diff --git a/starboard/build/config/build_assertions.gni b/starboard/build/config/build_assertions.gni
index 4162c0e..1b6844f 100644
--- a/starboard/build/config/build_assertions.gni
+++ b/starboard/build/config/build_assertions.gni
@@ -21,6 +21,3 @@
 assert(
     !sb_evergreen_compatible_use_libunwind || sb_is_evergreen_compatible,
     "Can only specify sb_evergreen_compatible_use_libunwind on an evergreen-compatible platform.")
-
-assert(!sb_evergreen_compatible_enable_lite || sb_is_evergreen_compatible,
-       "Can only enable evergreen lite on an evergreen-comptaible platform.")
diff --git a/starboard/build/config/clang.gni b/starboard/build/config/clang.gni
new file mode 100644
index 0000000..a383c72
--- /dev/null
+++ b/starboard/build/config/clang.gni
@@ -0,0 +1,25 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/toolchain/starboard_toolchains.gni")
+
+declare_args() {
+  clang_revision = "365097-f7e52fbd-8"
+}
+
+default_clang_base_path = "$starboard_toolchains_path/x86_64-linux-gnu-clang-chromium-${clang_revision}"
+
+declare_args() {
+  clang_base_path = default_clang_base_path
+}
diff --git a/starboard/build/config/linux/BUILD.gn b/starboard/build/config/linux/BUILD.gn
new file mode 100644
index 0000000..42e5718
--- /dev/null
+++ b/starboard/build/config/linux/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+config("host") {
+  cflags = [ "-O2" ]
+}
diff --git a/starboard/build/config/mac/BUILD.gn b/starboard/build/config/mac/BUILD.gn
new file mode 100644
index 0000000..c968be7
--- /dev/null
+++ b/starboard/build/config/mac/BUILD.gn
@@ -0,0 +1,57 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/config/mac/xcode_tools_path.gni")
+
+config("host") {
+  configs = [ ":common" ]
+
+  sysroot_path =
+      "$xcode_tools_path/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"
+
+  cflags = [ "-O2" ]
+
+  clang_flags = [
+    "-arch",
+    "x86_64",
+    "-mmacosx-version-min=10.12",
+    "-isysroot",
+    sysroot_path,
+  ]
+  cflags += clang_flags
+  ldflags = clang_flags
+  asmflags = clang_flags
+
+  arflags = [
+    "-arch_only",
+    "x86_64",
+    "-syslibroot",
+    sysroot_path,
+  ]
+}
+
+config("common") {
+  arflags = [ "-no_warning_for_no_symbols" ]
+  cflags_cc = [ "-std=gnu++14" ]
+  cflags_objcc = [ "-std=gnu++14" ]
+  cflags = [ "-fno-common" ]
+  asmflags = [ "-fno-common" ]
+  ldflags = [ "-fno-common" ]
+
+  ldflags += [
+    "-stdlib=libc++",
+    "-Xlinker",
+    "-no_deduplicate",
+  ]
+}
diff --git a/starboard/build/config/mac/xcode_tools_path.gni b/starboard/build/config/mac/xcode_tools_path.gni
new file mode 100644
index 0000000..b0d146e
--- /dev/null
+++ b/starboard/build/config/mac/xcode_tools_path.gni
@@ -0,0 +1,22 @@
+# Copyright 2021 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+declare_args() {
+  xcode_tools_path = exec_script("//starboard/build/run_bash.py",
+                                 [
+                                   "xcode-select",
+                                   "-print-path",
+                                 ],
+                                 "trim string")
+}
diff --git a/starboard/build/config/win/BUILD.gn b/starboard/build/config/win/BUILD.gn
new file mode 100644
index 0000000..6082d82
--- /dev/null
+++ b/starboard/build/config/win/BUILD.gn
@@ -0,0 +1,252 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/config/win/visual_studio_version.gni")
+
+# TODO(andrewsavage): No way host builds need all these flags
+config("host") {
+  configs = [ ":common" ]
+}
+
+config("common") {
+  if (!cobalt_fastbuild) {
+    # Containers cannot use PDBs, so we conditionally disable them.
+    # We want to keep them for non-docker builds as they make compilation
+    # significantly faster.
+    configs = [ ":no_pdbs" ]
+  }
+
+  cflags_cc = [ "/TP" ]
+  cflags = []
+  ldflags = [
+    "/NXCOMPAT",
+    "/MANIFEST:NO",
+  ]
+  libs = []
+  include_dirs = []
+  arflags = []
+  defines = [
+    "_UNICODE",
+    "UNICODE",
+  ]
+
+  # msvs_base
+  # OutputDirectory and IntermediateDirectory, maybe CharacterSet
+  include_dirs += [
+    "$wdk_include_path/shared",
+    "$wdk_include_path/ucrt",
+    "$wdk_include_path/um",
+    "$wdk_include_path/winrt",
+    "$msvc_path/include",
+  ]
+  cflags += [
+    "/EHsc",
+    "/std:c++14",
+  ]
+
+  # msvs_debug/_devel/etc
+  ldflags += [ "/INCREMENTAL:NO" ]
+  if (is_debug) {
+    cflags += [
+      "/Od",
+
+      # Check stack frame validity and check for uninitialized variables at run time.
+      "/RTC1",
+    ]
+  } else {
+    cflags += [ "/O2" ]
+  }
+
+  if (!is_gold) {
+    libs += [ "dbghelp.lib" ]
+  }
+
+  if (is_debug || is_devel) {
+    cflags += [
+      # Use debug multithreaded library.
+      "/MDd",
+      "/GS",
+
+      # Unit tests can have huge object files.
+      "/bigobj",
+    ]
+  } else {
+    cflags += [
+      # Use release multithreaded library.
+      "/MD",
+
+      # Unreferenced variable.
+      # Often variables are only referenced in DCHECKs.
+      "/wd4189",
+    ]
+    ldflags += [
+      "/OPT:REF",
+      "/OPT:ICF",
+    ]
+  }
+
+  cflags += [
+    # Check for buffer overruns.
+    "/GS",
+
+    # "for" loop's initializer go out of scope after the for loop.
+    "/Zc:forScope",
+
+    # wchar_t is treated as a built-in type.
+    "/Zc:wchar_t",
+
+    # Don't send error reports to MS.
+    "/errorReport:none",
+  ]
+
+  arflags += [
+    # Linking statically with C++/CX library is not recommended.
+    # TODO: Remove after removing ComponentExtensions
+    "/ignore:4264",
+  ]
+
+  cflags += [
+    # Allow unused function input parameter.
+    "/wd4100",
+
+    # Conditional expression is constant.
+    # Triggers in many legitimate cases, like branching on a constant declared
+    # in type traits.
+    "/wd4127",
+
+    # Disable anonymous union warnings.
+    "/wd4201",
+
+    # 4244 (Level 2) - Implicit conversion from float to int
+    # 4244 (Level 3) - Implicit conversion from int to something smaller
+    # than int.
+    # 4244 (Level 4) - Implicit conversion of types, which may result in
+    # data loss.
+    "/wd4244",
+
+    # Class has virtual functions, but destructor is not virtual.
+    # Far less useful than in GCC because doesn't take into account the fact
+    # that destructor is not public.
+    "/wd4265",
+
+    # Inconsistent DLL linkage
+    "/wd4273",
+
+    # Matching delete operator for `new`. 4291 is also ignored by Chromium.
+    "/wd4291",
+
+    # Double -> float truncation. Not enabled on other compilers.
+    "/wd4305",
+
+    # cast truncates constant value.
+    # We do not care.
+    "/wd4309",
+
+    # casting constant number.
+    "/wd4310",
+
+    # An rvalue cannot be bound to a non-const reference.
+    # In previous versions of Visual C++, it was possible to bind an rvalue
+    # to a non-const reference in a direct initialization. This warning
+    # is useless as it simply describes proper C++ behavior.
+    "/wd4350",
+
+    # layout of class may have changed from a previous version of
+    # the compiler due to better packing of member. We don't care about
+    # binary compatibility with other compiler versions.
+    "/wd4371",
+
+    # relative include path contains '..'.
+    # This occurs in a lot third party libraries and we don't care.
+    "/wd4464",
+
+    # decorated name length exceeded, name was truncated.
+    "/wd4503",
+
+    # assignment operator could not be generated.
+    # This is expected for structs with const members.
+    "/wd4512",
+
+    # Unreferenced inline function has been removed.
+    # While detection of dead code is good, this warning triggers in
+    # third-party libraries which renders it useless.
+    "/wd4514",
+
+    # Expression before comma has no effect.
+    # Cannot be used because Microsoft uses _ASSERTE(("message", 0)) trick
+    # in malloc.h which is included pretty much everywhere.
+    "/wd4548",
+
+    # Use of noexcept in targets compiled without /EHsc triggers a warning
+    # "termination on exception is not guaranteed" which we consider benign
+    # because we don't expect exceptions to cross the boundary of modules
+    # compiled with /EHsc.
+    "/wd4577",
+
+    # Copy constructor could not be generated because a base class copy
+    # constructor is inaccessible.
+    # This is an expected consequence of using DISALLOW_COPY_AND_ASSIGN().
+    "/wd4625",
+
+    # Assignment operator could not be generated because a base class
+    # assignment operator is inaccessible.
+    # This is an expected consequence of using DISALLOW_COPY_AND_ASSIGN().
+    "/wd4626",
+
+    # Digraphs not supported.
+    "/wd4628",
+
+    # Sometimes template definitions and declarations are separate and MSVC
+    # complains when it fails to find the definition on seeing the template.
+    "/wd4661",
+
+    # Symbol is not defined as a preprocessor macro, replacing with '0'.
+    # Seems like common practice, used in Windows SDK and gtest.
+    "/wd4668",
+
+    # Function not inlined.
+    # It's up to the compiler to decide what to inline.
+    "/wd4710",
+
+    # Function selected for inline expansion.
+    # It's up to the compiler to decide what to inline.
+    "/wd4711",
+
+    # The type and order of elements caused the compiler to add padding
+    # to the end of a struct.
+    # Unsurprisingly, most of the structs become larger because of padding
+    # but it's a universally acceptable price for better performance.
+    "/wd4820",
+
+    # Disable static analyzer warning for std::min and std::max with
+    # objects.
+    # https://connect.microsoft.com/VisualStudio/feedback/details/783808/static-analyzer-warning-c28285-for-std-min-and-std-max
+    "/wd28285",
+
+    # Deprecated function warning.
+    "/wd4996",
+  ]
+}
+
+config("no_pdbs") {
+  if (is_docker_build) {
+    cflags = [ "/Z7" ]
+  } else {
+    cflags = [
+      "/Zi",
+      "/FS",
+    ]
+  }
+  ldflags = [ "/DEBUG:FULL" ]
+}
diff --git a/starboard/build/doc/migration_changes.md b/starboard/build/doc/migration_changes.md
index 650d107..c63a3bf 100644
--- a/starboard/build/doc/migration_changes.md
+++ b/starboard/build/doc/migration_changes.md
@@ -20,7 +20,6 @@
 `sb_evergreen` (0/1)                      | `sb_is_evergreen` (true/false)                       | `//starboard/build/config/base_configuration.gni`
 `sb_evergreen_compatible` (0/1)           | `sb_is_evergreen_compatible` (true/false)            | `//starboard/build/config/base_configuration.gni`
 `sb_evergreen_compatible_libunwind` (0/1) | `sb_evergreen_compatible_use_libunwind` (true/false) | `//starboard/build/config/base_configuration.gni`
-`sb_evergreen_compatible_lite` (0/1)      | `sb_evergreen_compatible_enable_lite` (true/false)   | `//starboard/build/config/base_configuration.gni`
 `sb_disable_cpp14_audit`                  | (none)                                               |
 `sb_disable_microphone_idl`               | (none)                                               |
 `starboard_path`                          | (none)                                               |
diff --git a/starboard/build/nasm_assemble.gni b/starboard/build/nasm_assemble.gni
index 9b23cd9..737db6c 100644
--- a/starboard/build/nasm_assemble.gni
+++ b/starboard/build/nasm_assemble.gni
@@ -69,7 +69,7 @@
                            ])
 
     # Flags.
-    args = [ "$path_to_yasm" ]
+    args = [ "$path_to_nasm" ]
     args += _nasm_flags
     if (defined(invoker.nasm_flags)) {
       args += invoker.nasm_flags
@@ -105,9 +105,7 @@
     }
 
     # Output file.
-    outputs = [
-      "$root_out_dir/obj/third_party/libjpeg-turbo/{{source_name_part}}.asm.o",
-    ]
+    outputs = [ "$target_out_dir/$source_set_name/{{source_name_part}}.asm.o" ]
     args += [
       "-o",
       rebase_path(outputs[0], root_build_dir),
diff --git a/starboard/build/toolchain/linux/BUILD.gn b/starboard/build/toolchain/linux/BUILD.gn
new file mode 100644
index 0000000..da6cb5c
--- /dev/null
+++ b/starboard/build/toolchain/linux/BUILD.gn
@@ -0,0 +1,36 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/toolchain/gcc_toolchain.gni")
+import("//starboard/build/platform_path.gni")
+
+clang_toolchain("x64") {
+  clang_base_path = clang_base_path
+
+  toolchain_args = {
+    current_os = "linux"
+    current_cpu = "x64"
+    toolchain_config_path = "//starboard/build/config/linux:host"
+  }
+}
+
+clang_toolchain("x86") {
+  clang_base_path = clang_base_path
+
+  toolchain_args = {
+    current_os = "linux"
+    current_cpu = "x86"
+    toolchain_config_path = "//starboard/build/config/linux:host"
+  }
+}
diff --git a/starboard/build/toolchain/mac/BUILD.gn b/starboard/build/toolchain/mac/BUILD.gn
new file mode 100644
index 0000000..780c648
--- /dev/null
+++ b/starboard/build/toolchain/mac/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/toolchain/apple/toolchain.gni")
+import("//starboard/build/toolchain/mac/variables.gni")
+
+apple_toolchain("x64") {
+  bin_path = apple_clang_base_path
+  toolchain_args = {
+    current_os = "mac"
+    current_cpu = "x64"
+    use_xcode_clang = true
+    toolchain_config_path = "//starboard/build/config/mac:host"
+  }
+}
diff --git a/starboard/build/toolchain/mac/variables.gni b/starboard/build/toolchain/mac/variables.gni
new file mode 100644
index 0000000..9c24322
--- /dev/null
+++ b/starboard/build/toolchain/mac/variables.gni
@@ -0,0 +1,18 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//starboard/build/config/mac/xcode_tools_path.gni")
+
+apple_clang_base_path =
+    "$xcode_tools_path/Toolchains/XcodeDefault.xctoolchain/usr/bin/"
diff --git a/starboard/build/toolchain/win/BUILD.gn b/starboard/build/toolchain/win/BUILD.gn
new file mode 100644
index 0000000..498db6d
--- /dev/null
+++ b/starboard/build/toolchain/win/BUILD.gn
@@ -0,0 +1,31 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/toolchain/win/msvc_toolchain.gni")
+import("//starboard/build/toolchain/win/variables.gni")
+
+msvc_toolchain("x64") {
+  cl = "$tool_base_path/cl.exe"
+  lib = "$tool_base_path/lib.exe"
+  link = "$tool_base_path/link.exe"
+  asm = "$tool_base_path/ml64.exe"
+  sys_lib_flags = sys_libpaths
+
+  toolchain_args = {
+    is_clang = false
+    current_os = "win"
+    current_cpu = "x64"
+    toolchain_config_path = "//starboard/build/config/win:host"
+  }
+}
diff --git a/starboard/build/toolchain/win/variables.gni b/starboard/build/toolchain/win/variables.gni
new file mode 100644
index 0000000..bec21e0
--- /dev/null
+++ b/starboard/build/toolchain/win/variables.gni
@@ -0,0 +1,20 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/config/win/visual_studio_version.gni")
+
+tool_base_path = "$msvc_path/bin/HostX64/x64"
+common_libpaths =
+    "/LIBPATH:\"$wdk_lib_path/ucrt/x64\" /LIBPATH:\"$wdk_lib_path/um/x64\""
+sys_libpaths = "/LIBPATH:\"$msvc_path/lib/x64\" " + common_libpaths
diff --git a/starboard/client_porting/eztime/BUILD.gn b/starboard/client_porting/eztime/BUILD.gn
index df579de..8f19135 100644
--- a/starboard/client_porting/eztime/BUILD.gn
+++ b/starboard/client_porting/eztime/BUILD.gn
@@ -42,5 +42,8 @@
     "//testing/gtest",
   ]
 
-  data_deps = [ "//third_party/icu:icudata" ]
+  data_deps = [
+    "//cobalt/network:copy_ssl_certificates",
+    "//third_party/icu:icudata",
+  ]
 }
diff --git a/starboard/common/BUILD.gn b/starboard/common/BUILD.gn
index 62f7974..4e86288 100644
--- a/starboard/common/BUILD.gn
+++ b/starboard/common/BUILD.gn
@@ -57,6 +57,8 @@
     "new.cc",
     "optional.cc",
     "optional.h",
+    "paths.cc",
+    "paths.h",
     "queue.h",
     "recursive_mutex.cc",
     "recursive_mutex.h",
diff --git a/starboard/common/atomic.h b/starboard/common/atomic.h
new file mode 100644
index 0000000..eb545e4
--- /dev/null
+++ b/starboard/common/atomic.h
@@ -0,0 +1,490 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_COMMON_ATOMIC_H_
+#define STARBOARD_COMMON_ATOMIC_H_
+
+#include "starboard/atomic.h"
+#include "starboard/common/mutex.h"
+
+// C++ Can choose the right function based on overloaded arguments. This
+// provides overloaded versions that will choose the right function version
+// based on type for C++ callers.
+
+namespace starboard {
+namespace atomic {
+
+inline SbAtomic8 Release_CompareAndSwap(volatile SbAtomic8* ptr,
+                                        SbAtomic8 old_value,
+                                        SbAtomic8 new_value) {
+  return SbAtomicRelease_CompareAndSwap8(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile SbAtomic8* ptr, SbAtomic8 value) {
+  SbAtomicNoBarrier_Store8(ptr, value);
+}
+
+inline SbAtomic8 NoBarrier_Load(volatile const SbAtomic8* ptr) {
+  return SbAtomicNoBarrier_Load8(ptr);
+}
+
+inline SbAtomic32 NoBarrier_CompareAndSwap(volatile SbAtomic32* ptr,
+                                           SbAtomic32 old_value,
+                                           SbAtomic32 new_value) {
+  return SbAtomicNoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline SbAtomic32 NoBarrier_Exchange(volatile SbAtomic32* ptr,
+                                     SbAtomic32 new_value) {
+  return SbAtomicNoBarrier_Exchange(ptr, new_value);
+}
+
+inline SbAtomic32 NoBarrier_Increment(volatile SbAtomic32* ptr,
+                                      SbAtomic32 increment) {
+  return SbAtomicNoBarrier_Increment(ptr, increment);
+}
+
+inline SbAtomic32 Barrier_Increment(volatile SbAtomic32* ptr,
+                                    SbAtomic32 increment) {
+  return SbAtomicBarrier_Increment(ptr, increment);
+}
+
+inline SbAtomic32 Acquire_CompareAndSwap(volatile SbAtomic32* ptr,
+                                         SbAtomic32 old_value,
+                                         SbAtomic32 new_value) {
+  return SbAtomicAcquire_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline SbAtomic32 Release_CompareAndSwap(volatile SbAtomic32* ptr,
+                                         SbAtomic32 old_value,
+                                         SbAtomic32 new_value) {
+  return SbAtomicRelease_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile SbAtomic32* ptr, SbAtomic32 value) {
+  SbAtomicNoBarrier_Store(ptr, value);
+}
+
+inline void Acquire_Store(volatile SbAtomic32* ptr, SbAtomic32 value) {
+  SbAtomicAcquire_Store(ptr, value);
+}
+
+inline void Release_Store(volatile SbAtomic32* ptr, SbAtomic32 value) {
+  SbAtomicRelease_Store(ptr, value);
+}
+
+inline SbAtomic32 NoBarrier_Load(volatile const SbAtomic32* ptr) {
+  return SbAtomicNoBarrier_Load(ptr);
+}
+
+inline SbAtomic32 Acquire_Load(volatile const SbAtomic32* ptr) {
+  return SbAtomicAcquire_Load(ptr);
+}
+
+inline SbAtomic32 Release_Load(volatile const SbAtomic32* ptr) {
+  return SbAtomicRelease_Load(ptr);
+}
+
+#if SB_HAS(64_BIT_ATOMICS)
+inline SbAtomic64 NoBarrier_CompareAndSwap(volatile SbAtomic64* ptr,
+                                           SbAtomic64 old_value,
+                                           SbAtomic64 new_value) {
+  return SbAtomicNoBarrier_CompareAndSwap64(ptr, old_value, new_value);
+}
+
+inline SbAtomic64 NoBarrier_Exchange(volatile SbAtomic64* ptr,
+                                     SbAtomic64 new_value) {
+  return SbAtomicNoBarrier_Exchange64(ptr, new_value);
+}
+
+inline SbAtomic64 NoBarrier_Increment(volatile SbAtomic64* ptr,
+                                      SbAtomic64 increment) {
+  return SbAtomicNoBarrier_Increment64(ptr, increment);
+}
+
+inline SbAtomic64 Barrier_Increment(volatile SbAtomic64* ptr,
+                                    SbAtomic64 increment) {
+  return SbAtomicBarrier_Increment64(ptr, increment);
+}
+
+inline SbAtomic64 Acquire_CompareAndSwap(volatile SbAtomic64* ptr,
+                                         SbAtomic64 old_value,
+                                         SbAtomic64 new_value) {
+  return SbAtomicAcquire_CompareAndSwap64(ptr, old_value, new_value);
+}
+
+inline SbAtomic64 Release_CompareAndSwap(volatile SbAtomic64* ptr,
+                                         SbAtomic64 old_value,
+                                         SbAtomic64 new_value) {
+  return SbAtomicRelease_CompareAndSwap64(ptr, old_value, new_value);
+}
+
+inline void NoBarrier_Store(volatile SbAtomic64* ptr, SbAtomic64 value) {
+  SbAtomicNoBarrier_Store64(ptr, value);
+}
+
+inline void Acquire_Store(volatile SbAtomic64* ptr, SbAtomic64 value) {
+  SbAtomicAcquire_Store64(ptr, value);
+}
+
+inline void Release_Store(volatile SbAtomic64* ptr, SbAtomic64 value) {
+  SbAtomicRelease_Store64(ptr, value);
+}
+
+inline SbAtomic64 NoBarrier_Load(volatile const SbAtomic64* ptr) {
+  return SbAtomicNoBarrier_Load64(ptr);
+}
+
+inline SbAtomic64 Acquire_Load(volatile const SbAtomic64* ptr) {
+  return SbAtomicAcquire_Load64(ptr);
+}
+
+inline SbAtomic64 Release_Load(volatile const SbAtomic64* ptr) {
+  return SbAtomicRelease_Load64(ptr);
+}
+#endif  // SB_HAS(64_BIT_ATOMICS)
+
+}  // namespace atomic
+
+// Provides atomic types like integer and float. Some types like atomic_int32_t
+// are likely to be hardware accelerated for your platform.
+//
+// Never use the parent types like atomic_base<T>, atomic_number<T> or
+// atomic_integral<T> and instead use the fully qualified classes like
+// atomic_int32_t, atomic_pointer<T*>, etc.
+//
+// Note on template instantiation, avoid using the parent type and instead
+// use the fully qualified type.
+// BAD:
+//  template<typename T>
+//  void Foo(const atomic_base<T>& value);
+// GOOD:
+//  template<typename atomic_t>
+//  void Foo(const atomic_t& value);
+
+// Atomic Pointer class. Instantiate as atomic_pointer<void*>
+// for void* pointer types.
+template <typename T>
+class atomic_pointer;
+
+// Atomic bool class.
+class atomic_bool;
+
+// Atomic int32 class
+class atomic_int32_t;
+
+// Atomic int64 class.
+class atomic_int64_t;
+
+// Atomic float class.
+class atomic_float;
+
+// Atomic double class.
+class atomic_double;
+
+///////////////////////////////////////////////////////////////////////////////
+// Class hierarchy.
+///////////////////////////////////////////////////////////////////////////////
+
+// Base functionality for atomic types. Defines exchange(), load(),
+// store(), compare_exchange_weak(), compare_exchange_strong()
+template <typename T>
+class atomic_base;
+
+// Subtype of atomic_base<T> for numbers likes float and integer types but not
+// bool.  Adds fetch_add() and fetch_sub().
+template <typename T>
+class atomic_number;
+
+// Subtype of atomic_number<T> for integer types like int32 and int64. Adds
+// increment and decrement.
+template <typename T>
+class atomic_integral;
+
+///////////////////////////////////////////////////////////////////////////////
+// Implementation.
+///////////////////////////////////////////////////////////////////////////////
+
+// Similar to C++11 std::atomic<T>.
+// atomic_base<T> may be instantiated with any TriviallyCopyable type T.
+// atomic_base<T> is neither copyable nor movable.
+template <typename T>
+class atomic_base {
+ public:
+  typedef T ValueType;
+
+  // C++11 forbids a copy constructor for std::atomic<T>, it also forbids
+  // a move operation.
+  atomic_base() : value_() {}
+  explicit atomic_base(T v) : value_(v) {}
+
+  // Checks whether the atomic operations on all objects of this type
+  // are lock-free.
+  // Returns true if the atomic operations on the objects of this type
+  // are lock-free, false otherwise.
+  //
+  // All atomic types may be implemented using mutexes or other locking
+  // operations, rather than using the lock-free atomic CPU instructions.
+  // atomic types are also allowed to be sometimes lock-free, e.g. if only
+  // aligned memory accesses are naturally atomic on a given architecture,
+  // misaligned objects of the same type have to use locks.
+  //
+  // See also std::atomic<T>::is_lock_free().
+  bool is_lock_free() const { return false; }
+  bool is_lock_free() const volatile { return false; }
+
+  // Atomically replaces the value of the atomic object and returns the value
+  // held previously.
+  // See also std::atomic<T>::exchange().
+  T exchange(T new_val) {
+    T old_value;
+    {
+      starboard::ScopedLock lock(mutex_);
+      old_value = value_;
+      value_ = new_val;
+    }
+    return old_value;
+  }
+
+  // Atomically obtains the value of the atomic object.
+  // See also std::atomic<T>::load().
+  T load() const {
+    starboard::ScopedLock lock(mutex_);
+    return value_;
+  }
+
+  // Stores the value. See std::atomic<T>::store(...)
+  void store(T val) {
+    starboard::ScopedLock lock(mutex_);
+    value_ = val;
+  }
+
+  // compare_exchange_strong(...) sets the new value if and only if
+  // *expected_value matches what is stored internally.
+  // If this succeeds then true is returned and *expected_value == new_value.
+  // Otherwise If there is a mismatch then false is returned and
+  // *expected_value is set to the internal value.
+  // Inputs:
+  //  new_value: Attempt to set the value to this new value.
+  //  expected_value: A test condition for success. If the actual value
+  //    matches the expected_value then the swap will succeed.
+  //
+  // See also std::atomic<T>::compare_exchange_strong(...).
+  bool compare_exchange_strong(T* expected_value, T new_value) {
+    // Save original value so that its local. This hints to the compiler
+    // that test_val doesn't have aliasing issues and should result in
+    // more optimal code inside of the lock.
+    const T test_val = *expected_value;
+    starboard::ScopedLock lock(mutex_);
+    if (test_val == value_) {
+      value_ = new_value;
+      return true;
+    } else {
+      *expected_value = value_;
+      return false;
+    }
+  }
+
+  // Weak version of this function is documented to be faster, but has allows
+  // weaker memory ordering and therefore will sometimes have a false negative:
+  // The value compared will actually be equal but the return value from this
+  // function indicates otherwise.
+  // By default, the function delegates to compare_exchange_strong(...).
+  //
+  // See also std::atomic<T>::compare_exchange_weak(...).
+  bool compare_exchange_weak(T* expected_value, T new_value) {
+    return compare_exchange_strong(expected_value, new_value);
+  }
+
+ protected:
+  T value_;
+  starboard::Mutex mutex_;
+};
+
+// A subclass of atomic_base<T> that adds fetch_add(...) and fetch_sub(...).
+template <typename T>
+class atomic_number : public atomic_base<T> {
+ public:
+  typedef atomic_base<T> Super;
+  typedef T ValueType;
+
+  atomic_number() : Super() {}
+  explicit atomic_number(T v) : Super(v) {}
+
+  // Returns the previous value before the input value was added.
+  // See also std::atomic<T>::fetch_add(...).
+  T fetch_add(T val) {
+    T old_val;
+    {
+      starboard::ScopedLock lock(this->mutex_);
+      old_val = this->value_;
+      this->value_ += val;
+    }
+    return old_val;
+  }
+
+  // Returns the value before the operation was applied.
+  // See also std::atomic<T>::fetch_sub(...).
+  T fetch_sub(T val) {
+    T old_val;
+    {
+      starboard::ScopedLock lock(this->mutex_);
+      old_val = this->value_;
+      this->value_ -= val;
+    }
+    return old_val;
+  }
+};
+
+// A subclass to classify Atomic Integers. Adds increment and decrement
+// functions.
+template <typename T>
+class atomic_integral : public atomic_number<T> {
+ public:
+  typedef atomic_number<T> Super;
+
+  atomic_integral() : Super() {}
+  explicit atomic_integral(T v) : Super(v) {}
+
+  T increment() { return this->fetch_add(T(1)); }
+  T decrement() { return this->fetch_sub(T(1)); }
+};
+
+// atomic_pointer class.
+template <typename T>
+class atomic_pointer : public atomic_base<T> {
+ public:
+  typedef atomic_base<T> Super;
+  atomic_pointer() : Super() {}
+  explicit atomic_pointer(T initial_val) : Super(initial_val) {}
+};
+
+// Simple atomic bool class.
+class atomic_bool {
+ public:
+  typedef bool ValueType;
+
+  // C++11 forbids a copy constructor for std::atomic<T>, it also forbids
+  // a move operation.
+  atomic_bool() : value_(false) {}
+  explicit atomic_bool(bool v) : value_(v) {}
+
+  bool is_lock_free() const { return true; }
+  bool is_lock_free() const volatile { return true; }
+
+  bool exchange(bool new_val) {
+    return SbAtomicNoBarrier_Exchange(volatile_ptr(), new_val);
+  }
+
+  bool load() const { return SbAtomicAcquire_Load(volatile_const_ptr()) != 0; }
+
+  void store(bool val) { SbAtomicRelease_Store(volatile_ptr(), val); }
+
+ private:
+  volatile int32_t* volatile_ptr() { return &value_; }
+  volatile const int32_t* volatile_const_ptr() const { return &value_; }
+  int32_t value_;
+};
+
+// Lockfree atomic int class.
+class atomic_int32_t {
+ public:
+  typedef int32_t ValueType;
+  atomic_int32_t() : value_(0) {}
+  explicit atomic_int32_t(SbAtomic32 value) : value_(value) {}
+
+  bool is_lock_free() const { return true; }
+  bool is_lock_free() const volatile { return true; }
+
+  int32_t increment() { return fetch_add(1); }
+  int32_t decrement() { return fetch_add(-1); }
+
+  int32_t fetch_add(int32_t val) {
+    // fetch_add is a post-increment operation, while SbAtomicBarrier_Increment
+    // is a pre-increment operation. Therefore subtract the value to match
+    // the expected interface.
+    return SbAtomicBarrier_Increment(volatile_ptr(), val) - val;
+  }
+
+  int32_t fetch_sub(int32_t val) { return fetch_add(-val); }
+
+  // Atomically replaces the value of the atomic object
+  // and returns the value held previously.
+  // See also std::atomic<T>::exchange().
+  int32_t exchange(int32_t new_val) {
+    return SbAtomicNoBarrier_Exchange(volatile_ptr(), new_val);
+  }
+
+  // Atomically obtains the value of the atomic object.
+  // See also std::atomic<T>::load().
+  int32_t load() const { return SbAtomicAcquire_Load(volatile_const_ptr()); }
+
+  // Stores the value. See std::atomic<T>::store(...)
+  void store(int32_t val) { SbAtomicRelease_Store(volatile_ptr(), val); }
+
+  bool compare_exchange_strong(int32_t* expected_value, int32_t new_value) {
+    int32_t prev_value = *expected_value;
+    SbAtomicMemoryBarrier();
+    int32_t value_written =
+        SbAtomicRelease_CompareAndSwap(volatile_ptr(), prev_value, new_value);
+    const bool write_ok = (prev_value == value_written);
+    if (!write_ok) {
+      *expected_value = value_written;
+    }
+    return write_ok;
+  }
+
+  // Weak version of this function is documented to be faster, but has allows
+  // weaker memory ordering and therefore will sometimes have a false negative:
+  // The value compared will actually be equal but the return value from this
+  // function indicates otherwise.
+  // By default, the function delegates to compare_exchange_strong(...).
+  //
+  // See also std::atomic<T>::compare_exchange_weak(...).
+  bool compare_exchange_weak(int32_t* expected_value, int32_t new_value) {
+    return compare_exchange_strong(expected_value, new_value);
+  }
+
+ private:
+  volatile int32_t* volatile_ptr() { return &value_; }
+  volatile const int32_t* volatile_const_ptr() const { return &value_; }
+  int32_t value_;
+};
+
+// Simple atomic int class. This could be optimized for speed using
+// compiler intrinsics for concurrent integer modification.
+class atomic_int64_t : public atomic_integral<int64_t> {
+ public:
+  typedef atomic_integral<int64_t> Super;
+  atomic_int64_t() : Super() {}
+  explicit atomic_int64_t(int64_t initial_val) : Super(initial_val) {}
+};
+
+class atomic_float : public atomic_number<float> {
+ public:
+  typedef atomic_number<float> Super;
+  atomic_float() : Super() {}
+  explicit atomic_float(float initial_val) : Super(initial_val) {}
+};
+
+class atomic_double : public atomic_number<double> {
+ public:
+  typedef atomic_number<double> Super;
+  atomic_double() : Super() {}
+  explicit atomic_double(double initial_val) : Super(initial_val) {}
+};
+
+}  // namespace starboard
+
+#endif  // STARBOARD_COMMON_ATOMIC_H_
diff --git a/starboard/common/media.cc b/starboard/common/media.cc
index eb74ac8..c51f162 100644
--- a/starboard/common/media.cc
+++ b/starboard/common/media.cc
@@ -609,6 +609,10 @@
     case kSbMediaAudioCodecPcm:
       return "pcm";
 #endif  // SB_API_VERSION >= 14
+#if SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
+    case kSbMediaAudioCodecIamf:
+      return "iamf";
+#endif  // SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
   }
   SB_NOTREACHED();
   return "invalid";
@@ -784,6 +788,29 @@
   return "Invalid";
 }
 
+const char* GetMediaAudioSampleTypeName(SbMediaAudioSampleType sample_type) {
+  switch (sample_type) {
+    case kSbMediaAudioSampleTypeFloat32:
+      return "float32";
+    case kSbMediaAudioSampleTypeInt16Deprecated:
+      return "int16";
+  }
+  SB_NOTREACHED();
+  return "Invalid";
+}
+
+const char* GetMediaAudioStorageTypeName(
+    SbMediaAudioFrameStorageType storage_type) {
+  switch (storage_type) {
+    case kSbMediaAudioFrameStorageTypeInterleaved:
+      return "interleaved";
+    case kSbMediaAudioFrameStorageTypePlanar:
+      return "planar";
+  }
+  SB_NOTREACHED();
+  return "Invalid";
+}
+
 bool ParseVideoCodec(const char* codec_string,
                      SbMediaVideoCodec* codec,
                      int* profile,
@@ -859,10 +886,10 @@
 
 std::ostream& operator<<(std::ostream& os,
                          const SbMediaColorMetadata& metadata) {
-  using starboard::GetMediaPrimaryIdName;
-  using starboard::GetMediaTransferIdName;
   using starboard::GetMediaMatrixIdName;
+  using starboard::GetMediaPrimaryIdName;
   using starboard::GetMediaRangeIdName;
+  using starboard::GetMediaTransferIdName;
 
   os << metadata.bits_per_channel
      << " bits, mastering metadata: " << metadata.mastering_metadata
@@ -886,14 +913,20 @@
                          const SbMediaVideoSampleInfo& sample_info) {
   using starboard::GetMediaVideoCodecName;
 
-  if (sample_info.codec == kSbMediaVideoCodecNone) {
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaVideoStreamInfo& stream_info = sample_info.stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaVideoSampleInfo& stream_info = sample_info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+  if (stream_info.codec == kSbMediaVideoCodecNone) {
     return os;
   }
 
-  os << "codec: " << GetMediaVideoCodecName(sample_info.codec) << ", ";
-  os << "mime: " << (sample_info.mime ? sample_info.mime : "<null>")
+  os << "codec: " << GetMediaVideoCodecName(stream_info.codec) << ", ";
+  os << "mime: " << (stream_info.mime ? stream_info.mime : "<null>")
      << ", max video capabilities: "
-     << (sample_info.max_video_capabilities ? sample_info.max_video_capabilities
+     << (stream_info.max_video_capabilities ? stream_info.max_video_capabilities
                                             : "<null>")
      << ", ";
 
@@ -901,8 +934,8 @@
     os << "key frame, ";
   }
 
-  os << sample_info.frame_width << 'x' << sample_info.frame_height << ' ';
-  os << '(' << sample_info.color_metadata << ')';
+  os << stream_info.frame_width << 'x' << stream_info.frame_height << ' ';
+  os << '(' << stream_info.color_metadata << ')';
 
   return os;
 }
@@ -912,22 +945,77 @@
   using starboard::GetMediaAudioCodecName;
   using starboard::HexEncode;
 
-  if (sample_info.codec == kSbMediaAudioCodecNone) {
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaAudioStreamInfo& stream_info = sample_info.stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaAudioSampleInfo& stream_info = sample_info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+  if (stream_info.codec == kSbMediaAudioCodecNone) {
     return os;
   }
 
-  os << "codec: " << GetMediaAudioCodecName(sample_info.codec) << ", ";
-  os << "mime: " << (sample_info.mime ? sample_info.mime : "<null>");
-  os << "channels: " << sample_info.number_of_channels
-     << ", sample rate: " << sample_info.samples_per_second
-     << ", config: " << sample_info.audio_specific_config_size << " bytes, "
+  os << "codec: " << GetMediaAudioCodecName(stream_info.codec) << ", ";
+  os << "mime: " << (stream_info.mime ? stream_info.mime : "<null>");
+  os << "channels: " << stream_info.number_of_channels
+     << ", sample rate: " << stream_info.samples_per_second
+     << ", config: " << stream_info.audio_specific_config_size << " bytes, "
      << "["
      << HexEncode(
-            sample_info.audio_specific_config,
-            std::min(static_cast<int>(sample_info.audio_specific_config_size),
+            stream_info.audio_specific_config,
+            std::min(static_cast<int>(stream_info.audio_specific_config_size),
                      16),
             " ")
-     << (sample_info.audio_specific_config_size > 16 ? " ...]" : " ]");
+     << (stream_info.audio_specific_config_size > 16 ? " ...]" : " ]");
 
   return os;
 }
+
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+std::ostream& operator<<(std::ostream& os,
+                         const SbMediaVideoStreamInfo& stream_info) {
+  using starboard::GetMediaVideoCodecName;
+
+  if (stream_info.codec == kSbMediaVideoCodecNone) {
+    return os;
+  }
+
+  os << "codec: " << GetMediaVideoCodecName(stream_info.codec) << ", ";
+  os << "mime: " << (stream_info.mime ? stream_info.mime : "<null>")
+     << ", max video capabilities: "
+     << (stream_info.max_video_capabilities ? stream_info.max_video_capabilities
+                                            : "<null>")
+     << ", ";
+
+  os << stream_info.frame_width << 'x' << stream_info.frame_height << ' ';
+  os << '(' << stream_info.color_metadata << ')';
+
+  return os;
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const SbMediaAudioStreamInfo& stream_info) {
+  using starboard::GetMediaAudioCodecName;
+  using starboard::HexEncode;
+
+  if (stream_info.codec == kSbMediaAudioCodecNone) {
+    return os;
+  }
+
+  os << "codec: " << GetMediaAudioCodecName(stream_info.codec) << ", ";
+  os << "mime: " << (stream_info.mime ? stream_info.mime : "<null>");
+  os << "channels: " << stream_info.number_of_channels
+     << ", sample rate: " << stream_info.samples_per_second
+     << ", config: " << stream_info.audio_specific_config_size << " bytes, "
+     << "["
+     << HexEncode(
+            stream_info.audio_specific_config,
+            std::min(static_cast<int>(stream_info.audio_specific_config_size),
+                     16),
+            " ")
+     << (stream_info.audio_specific_config_size > 16 ? " ...]" : " ]");
+
+  return os;
+}
+
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
diff --git a/starboard/common/media.h b/starboard/common/media.h
index a51641e..43ac99c 100644
--- a/starboard/common/media.h
+++ b/starboard/common/media.h
@@ -28,6 +28,10 @@
 const char* GetMediaMatrixIdName(SbMediaMatrixId matrix_id);
 const char* GetMediaRangeIdName(SbMediaRangeId range_id);
 
+const char* GetMediaAudioSampleTypeName(SbMediaAudioSampleType sample_type);
+const char* GetMediaAudioStorageTypeName(
+    SbMediaAudioFrameStorageType storage_type);
+
 // This function parses the video codec string and returns a codec.  All fields
 // will be filled with information parsed from the codec string when possible,
 // otherwise they will have the following default values:
@@ -60,4 +64,11 @@
 std::ostream& operator<<(std::ostream& os,
                          const SbMediaAudioSampleInfo& sample_info);
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+std::ostream& operator<<(std::ostream& os,
+                         const SbMediaVideoStreamInfo& stream_info);
+std::ostream& operator<<(std::ostream& os,
+                         const SbMediaAudioStreamInfo& stream_info);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
 #endif  // STARBOARD_COMMON_MEDIA_H_
diff --git a/starboard/common/paths.cc b/starboard/common/paths.cc
new file mode 100644
index 0000000..3ebbfc5
--- /dev/null
+++ b/starboard/common/paths.cc
@@ -0,0 +1,104 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/common/paths.h"
+
+#include <string>
+#include <vector>
+
+#include "starboard/common/log.h"
+#include "starboard/configuration_constants.h"
+#include "starboard/file.h"
+#include "starboard/system.h"
+
+namespace starboard {
+namespace common {
+
+namespace {
+
+std::string GetCACertificatesPathFromSubdir(const std::string& content_subdir) {
+  std::vector<char> buffer(kSbFileMaxPath);
+  if (!SbSystemGetPath(kSbSystemPathContentDirectory, buffer.data(),
+                       buffer.size())) {
+    SB_LOG(ERROR) << "Failed to get system path content directory";
+    return "";
+  }
+
+  std::string content_base_path(buffer.data());
+  if (!content_subdir.empty()) {
+    content_base_path += std::string(kSbFileSepString) + content_subdir;
+  }
+  return content_base_path + kSbFileSepString + "ssl" + kSbFileSepString +
+         "certs";
+}
+
+}  // namespace
+
+std::string PrependContentPath(const std::string& path) {
+  std::vector<char> content_path(kSbFileMaxPath);
+
+  if (!SbSystemGetPath(kSbSystemPathContentDirectory, content_path.data(),
+                       kSbFileMaxPath)) {
+    SB_LOG(ERROR) << "Failed to get system path content directory";
+    return "";
+  }
+
+  std::string relative_path(content_path.data());
+
+  relative_path.push_back(kSbFileSepChar);
+  relative_path.append(path);
+
+  return relative_path;
+}
+
+// TODO(b/36360851): add unit tests when we have a preferred way to mock
+// Starboard APIs.
+std::string GetCACertificatesPath(const std::string& content_subdir) {
+  if (content_subdir.empty()) {
+    SB_LOG(ERROR) << "content_subdir must be set.";
+    return "";
+  }
+
+  std::string ca_certificates_path =
+      GetCACertificatesPathFromSubdir(content_subdir);
+  if (!SbFileExists(ca_certificates_path.c_str())) {
+    SB_LOG(ERROR) << "Failed to get CA certificates path";
+    return "";
+  }
+
+  return ca_certificates_path;
+}
+
+std::string GetCACertificatesPath() {
+  // Try the Evergreen content path first.
+  std::string content_subdir = std::string("app") + kSbFileSepString +
+                               "cobalt" + kSbFileSepString + "content";
+  std::string ca_certificates_path =
+      GetCACertificatesPathFromSubdir(content_subdir);
+
+  // Then fall back to the regular content path if necessary.
+  if (!SbFileExists(ca_certificates_path.c_str())) {
+    ca_certificates_path = GetCACertificatesPathFromSubdir("");
+  }
+
+  if (!SbFileExists(ca_certificates_path.c_str())) {
+    SB_LOG(ERROR) << "Failed to get CA certificates path";
+    return "";
+  }
+
+  return ca_certificates_path;
+}
+
+}  // namespace common
+}  // namespace starboard
diff --git a/starboard/common/paths.h b/starboard/common/paths.h
new file mode 100644
index 0000000..bdcd254
--- /dev/null
+++ b/starboard/common/paths.h
@@ -0,0 +1,35 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_COMMON_PATHS_H_
+#define STARBOARD_COMMON_PATHS_H_
+
+#include <string>
+
+namespace starboard {
+namespace common {
+
+// Returns an empty string on error.
+std::string PrependContentPath(const std::string& path);
+
+// Returns the absolute path to a directory that contains Cobalt's trusted
+// Certificate Authority (CA) root certificates, or an empty string if it can't
+// be found.
+std::string GetCACertificatesPath();
+std::string GetCACertificatesPath(const std::string& content_subdir);
+
+}  // namespace common
+}  // namespace starboard
+
+#endif  // STARBOARD_COMMON_PATHS_H_
diff --git a/starboard/common/thread.cc b/starboard/common/thread.cc
index 0dc0e4e..44fdcb2 100644
--- a/starboard/common/thread.cc
+++ b/starboard/common/thread.cc
@@ -16,7 +16,7 @@
 
 #include "starboard/common/thread.h"
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/log.h"
 #include "starboard/common/mutex.h"
 #include "starboard/common/optional.h"
@@ -55,13 +55,10 @@
   d_->started_.store(true);
   d_->options_ = options;
 
-  d_->thread_ = SbThreadCreate(options.stack_size,
-                               options.priority_,
-                               kSbThreadNoAffinity,  // default affinity.
-                               options.joinable,
-                               d_->name_.c_str(),
-                               entry_point,
-                               this);
+  d_->thread_ =
+      SbThreadCreate(options.stack_size, options.priority_,
+                     kSbThreadNoAffinity,  // default affinity.
+                     options.joinable, d_->name_.c_str(), entry_point, this);
 
   // SbThreadCreate() above produced an invalid thread handle.
   SB_DCHECK(d_->thread_ != kSbThreadInvalid);
@@ -99,8 +96,7 @@
 
 void Thread::Join() {
   SB_DCHECK(d_->join_called_.load() == false);
-  SB_DCHECK(d_->options_->joinable)
-      << "Detached thread should not be joined.";
+  SB_DCHECK(d_->options_->joinable) << "Detached thread should not be joined.";
 
   d_->join_called_.store(true);
   d_->join_sema_.Put();
diff --git a/starboard/configuration.h b/starboard/configuration.h
index b00d129..fd19697 100644
--- a/starboard/configuration.h
+++ b/starboard/configuration.h
@@ -65,6 +65,32 @@
 //   //   exposes functionality for my new feature.
 //   #define SB_MY_EXPERIMENTAL_FEATURE_VERSION SB_EXPERIMENTAL_API_VERSION
 
+// Improves audio access unit processing.
+//   1.  Abstracted stream specific info from SbMediaAudioSampleInfo and
+//       SbMediaVideoSampleInfo into SbMediaAudioStreamInfo and
+//       SbMediaVideoStreamInfo.
+//   2.  Removed unused info about the audio stream.
+//   3.  Renamed SbPlayerWriteSample2() to SbPlayerWriteSamples().
+#define SB_MEDIA_ENHANCED_AUDIO_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
+// Minimum Starboard version for modular toolchain builds.
+#define SB_MINIMUM_API_VERSION_FOR_SB_MODULAR_BUILD SB_EXPERIMENTAL_API_VERSION
+
+// Support the IAMF audio codec.
+#define SB_MEDIA_IAMF_SUPPORT_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
+// This configuration is set for modular builds, which have:
+//   1. Application binary built as a shared library.
+//   2. Either
+//     - Starboard built at a shared library and a separate loader_app
+//     executable.
+//     - A loader_app executable with Starboard built in ( Evergreen ).
+#define SB_MODULAR_BUILD \
+  (SB_API_VERSION >= SB_MINIMUM_API_VERSION_FOR_SB_MODULAR_BUILD)
+
+// Moved atomic operations C++ wrappers to starboard/common.
+#define SB_ATOMIC_MOVED_API_VERSION SB_EXPERIMENTAL_API_VERSION
+
 // --- Release Candidate Feature Defines -------------------------------------
 
 // --- Common Detected Features ----------------------------------------------
@@ -150,7 +176,7 @@
   void operator=(const TypeName&) = delete
 #else
 #define SB_DISALLOW_COPY_AND_ASSIGN \
-  #error "The SB_DISALLOW_COPY_AND_ASSIGN macro is deprecated."
+#error "The SB_DISALLOW_COPY_AND_ASSIGN macro is deprecated."
 #endif  // SB_API_VERSION < 13
 
 // An enumeration of values for the kSbPreferredByteOrder configuration
@@ -240,7 +266,7 @@
 #endif  // SB_OVERRIDE
 #else
 #define SB_OVERRIDE \
-  #error "The SB_OVERRIDE macro is deprecated. Please use \"override\" instead."
+#error "The SB_OVERRIDE macro is deprecated. Please use \"override\" instead."
 #endif  // SB_API_VERSION < 13
 
 // Declare numeric literals of signed 64-bit type.
diff --git a/starboard/contrib/linux/stadia/main.cc b/starboard/contrib/linux/stadia/main.cc
index 77edff2..d4b79c6 100644
--- a/starboard/contrib/linux/stadia/main.cc
+++ b/starboard/contrib/linux/stadia/main.cc
@@ -16,10 +16,12 @@
 
 #include "starboard/configuration.h"
 #include "starboard/contrib/stadia/x11/application_stadia_x11.h"
+#include "starboard/event.h"
 #include "starboard/shared/signal/crash_signals.h"
 #include "starboard/shared/signal/suspend_signals.h"
 #include "starboard/shared/starboard/link_receiver.h"
 #if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/common/paths.h"
 #include "starboard/shared/starboard/command_line.h"
 #include "starboard/shared/starboard/starboard_switches.h"
 #endif
@@ -32,13 +34,21 @@
   starboard::shared::signal::InstallSuspendSignalHandlers();
 
 #if SB_IS(EVERGREEN_COMPATIBLE)
+  std::string ca_certificates_path = starboard::common::GetCACertificatesPath();
+  if (ca_certificates_path.empty()) {
+    SB_LOG(ERROR) << "Failed to get CA certificates path";
+    return 1;
+  }
+
   if (starboard::shared::starboard::CommandLine(argc, argv)
           .HasSwitch(starboard::shared::starboard::kStartHandlerAtLaunch) &&
       !starboard::shared::starboard::CommandLine(argc, argv)
            .HasSwitch(starboard::shared::starboard::kStartHandlerAtCrash)) {
-    third_party::crashpad::wrapper::InstallCrashpadHandler(false);
+    third_party::crashpad::wrapper::InstallCrashpadHandler(
+        false, ca_certificates_path);
   } else {
-    third_party::crashpad::wrapper::InstallCrashpadHandler(true);
+    third_party::crashpad::wrapper::InstallCrashpadHandler(
+        true, ca_certificates_path);
   }
 #endif
 
@@ -48,6 +58,9 @@
   SbLogRawDumpStack(3);
 #endif
 
+#if SB_MODULAR_BUILD
+  return SbRunStarboardMain(argc, argv, SbEventHandle);
+#else
   starboard::contrib::stadia::x11::ApplicationStadiaX11 application;
   int result = 0;
   {
@@ -57,4 +70,19 @@
   starboard::shared::signal::UninstallSuspendSignalHandlers();
   starboard::shared::signal::UninstallCrashSignalHandlers();
   return result;
+#endif  // SB_MODULAR_BUILD
 }
+
+#if SB_MODULAR_BUILD
+int SbRunStarboardMain(int argc, char** argv, SbEventHandleCallback callback) {
+  starboard::contrib::stadia::x11::ApplicationStadiaX11 application(callback);
+  int result = 0;
+  {
+    starboard::shared::starboard::LinkReceiver receiver(&application);
+    result = application.Run(argc, argv);
+  }
+  starboard::shared::signal::UninstallSuspendSignalHandlers();
+  starboard::shared::signal::UninstallCrashSignalHandlers();
+  return result;
+}
+#endif  // SB_MODULAR_BUILD
diff --git a/starboard/decode_target.h b/starboard/decode_target.h
index b347070..abc2c13 100644
--- a/starboard/decode_target.h
+++ b/starboard/decode_target.h
@@ -90,9 +90,9 @@
 #ifndef STARBOARD_DECODE_TARGET_H_
 #define STARBOARD_DECODE_TARGET_H_
 
-#include "starboard/common/log.h"
 #include "starboard/configuration.h"
 #include "starboard/export.h"
+#include "starboard/log.h"
 #include "starboard/types.h"
 
 #ifdef __cplusplus
@@ -330,7 +330,7 @@
     case kSbDecodeTargetFormat3PlaneYUVI420:
       return 3;
     default:
-      SB_NOTREACHED();
+      SbLog(kSbLogPriorityFatal, "Unhandled SbDecodeTargetFormat");
       return 0;
   }
 }
@@ -342,8 +342,10 @@
 // must be called on a thread with the context
 SB_EXPORT void SbDecodeTargetRelease(SbDecodeTarget decode_target);
 
-// Writes all information about |decode_target| into |out_info|.  Returns false
-// if the provided |out_info| structure is not zero initialized.
+// Writes all information about |decode_target| into |out_info|.
+// The |decode_target| must not be kSbDecodeTargetInvalid.
+// The |out_info| pointer must not be NULL.
+// Returns false if the provided |out_info| structure is not zero initialized.
 SB_EXPORT bool SbDecodeTargetGetInfo(SbDecodeTarget decode_target,
                                      SbDecodeTargetInfo* out_info);
 
@@ -356,7 +358,9 @@
     SbDecodeTargetGraphicsContextProvider* provider,
     SbDecodeTargetGlesContextRunnerTarget target,
     void* target_context) {
-  provider->gles_context_runner(provider, target, target_context);
+  if (provider) {
+    provider->gles_context_runner(provider, target, target_context);
+  }
 }
 
 // This function is just an implementation detail of
diff --git a/starboard/drm.h b/starboard/drm.h
index 6eb68d0..1fcebb0 100644
--- a/starboard/drm.h
+++ b/starboard/drm.h
@@ -228,7 +228,7 @@
 // Creates a new DRM system that can be used when constructing an SbPlayer or an
 // SbDecoder.
 //
-// This function returns kSbDrmSystemInvalid if |key_system| is unsupported.
+// This function returns |kSbDrmSystemInvalid| if |key_system| is unsupported.
 //
 // Also see the documentation of SbDrmGenerateSessionUpdateRequest() and
 // SbDrmUpdateSession() for more details.
@@ -284,7 +284,7 @@
 //
 // |drm_system|: The DRM system that defines the key system used for the session
 // update request payload as well as the callback function that is called as a
-// result of the function being called.
+// result of the function being called. Must not be |kSbDrmSystemInvalid|.
 //
 // |ticket|: The opaque ID that allows to distinguish callbacks from multiple
 // concurrent calls to SbDrmGenerateSessionUpdateRequest(), which will be passed
@@ -293,9 +293,10 @@
 // may result in undefined behavior. The value |kSbDrmTicketInvalid| must not be
 // used.
 //
-// |type|: The case-sensitive type of the session update request payload.
-// |initialization_data|: The data for which the session update request payload
-// is created.
+// |type|: The case-sensitive type of the session update request payload. Must
+//   not be NULL.
+// |initialization_data|: The data for which the session update
+//   request payload is created. Must not be NULL.
 // |initialization_data_size|: The size of the session update request payload.
 SB_EXPORT void SbDrmGenerateSessionUpdateRequest(
     SbDrmSystem drm_system,
@@ -307,8 +308,9 @@
 // Update session with |key|, in |drm_system|'s key system, from the license
 // server response. Calls |session_updated_callback| with |context| and whether
 // the update succeeded. |context| may be used to route callbacks back to an
-// object instance.
+// object instance. The |key| must not be NULL.
 //
+// |drm_system| must not be |kSbDrmSystemInvalid|.
 // |ticket| is the opaque ID that allows to distinguish callbacks from multiple
 // concurrent calls to SbDrmUpdateSession(), which will be passed to
 // |session_updated_callback| as-is. It is the responsibility of the caller to
@@ -320,6 +322,7 @@
 //
 // |drm_system|'s |session_updated_callback| may called either from the current
 // thread before this function returns or from another thread.
+// The |session_id| must not be NULL.
 SB_EXPORT void SbDrmUpdateSession(SbDrmSystem drm_system,
                                   int ticket,
                                   const void* key,
@@ -328,6 +331,9 @@
                                   int session_id_size);
 
 // Clear any internal states/resources related to the specified |session_id|.
+//
+// |drm_system| must not be |kSbDrmSystemInvalid|.
+// |session_id| must not be NULL.
 SB_EXPORT void SbDrmCloseSession(SbDrmSystem drm_system,
                                  const void* session_id,
                                  int session_id_size);
@@ -337,6 +343,7 @@
 // during the life time of |drm_system|.
 //
 // |drm_system|: The DRM system to check if its server certificate is updatable.
+// Must not be |kSbDrmSystemInvalid|.
 SB_EXPORT bool SbDrmIsServerCertificateUpdatable(SbDrmSystem drm_system);
 
 // Update the server certificate of |drm_system|.  The function can be called
@@ -345,14 +352,15 @@
 // Note that this function should only be called after
 // |SbDrmIsServerCertificateUpdatable| is called first and returned true.
 //
-// |drm_system|: The DRM system whose server certificate is being updated.
+// |drm_system|: The DRM system whose server certificate is being updated. Must
+//   not be |kSbDrmSystemInvalid|.
 // |ticket|: The opaque ID that allows to distinguish callbacks from multiple
-// concurrent calls to SbDrmUpdateServerCertificate(), which will be passed to
-// |server_certificate_updated_callback| as-is. It is the responsibility of the
-// caller to establish ticket uniqueness, issuing multiple requests with the
-// same ticket may result in undefined behavior. The value |kSbDrmTicketInvalid|
-// must not be used.
-// |certificate|: Pointer to the server certificate data.
+//   concurrent calls to SbDrmUpdateServerCertificate(), which will be passed to
+//   |server_certificate_updated_callback| as-is. It is the responsibility of
+//   the caller to establish ticket uniqueness, issuing multiple requests with
+//   the same ticket may result in undefined behavior. The value
+//   |kSbDrmTicketInvalid| must not be used.
+// |certificate|: Pointer to the server certificate data. Must not be NULL.
 // |certificate_size|: Size of the server certificate data.
 SB_EXPORT void SbDrmUpdateServerCertificate(SbDrmSystem drm_system,
                                             int ticket,
@@ -379,7 +387,8 @@
 // It should return NULL when there is no metrics support in the underlying drm
 // system, or when the drm system implementation fails to retrieve the metrics.
 //
-// The caller will never set |size| to NULL.
+// |drm_system| must not be |kSbDrmSystemInvalid|.
+// |size| must not be NULL.
 SB_EXPORT const void* SbDrmGetMetrics(SbDrmSystem drm_system, int* size);
 
 // Destroys |drm_system|, which implicitly removes all keys installed in it and
@@ -391,7 +400,8 @@
 // result, if this function is called from a callback that is passed to
 // SbDrmCreateSystem(), a deadlock will occur.
 //
-// |drm_system|: The DRM system to be destroyed.
+// |drm_system|: The DRM system to be destroyed. Must not be
+//   |kSbDrmSystemInvalid|.
 SB_EXPORT void SbDrmDestroySystem(SbDrmSystem drm_system);
 
 #ifdef __cplusplus
diff --git a/starboard/elf_loader/BUILD.gn b/starboard/elf_loader/BUILD.gn
index 07974b0..e255bc5 100644
--- a/starboard/elf_loader/BUILD.gn
+++ b/starboard/elf_loader/BUILD.gn
@@ -21,8 +21,6 @@
   "elf_header.h",
   "elf_loader.cc",
   "elf_loader.h",
-  "elf_loader_constants.cc",
-  "elf_loader_constants.h",
   "exported_symbols.cc",
   "file.h",
   "file_impl.cc",
@@ -53,9 +51,11 @@
   configs += [ ":elf_loader_config" ]
 
   deps = [
+    ":constants",
     ":evergreen_config",
     ":evergreen_info",
     "//starboard",
+    "//starboard/common",
     "//third_party/lz4_lib:lz4",
   ]
 }
@@ -73,9 +73,11 @@
     configs += [ ":elf_loader_config" ]
 
     deps = [
+      ":constants",
       ":evergreen_config",
       ":evergreen_info",
       "//starboard",
+      "//starboard/common",
     ]
 
     if (sb_is_evergreen_compatible) {
@@ -101,6 +103,7 @@
   configs += [ ":elf_loader_config" ]
 
   deps = [
+    ":constants",
     ":elf_loader",
     ":evergreen_info",
     ":sabi_string",
@@ -134,6 +137,7 @@
     ]
 
     deps = [
+      ":constants",
       ":elf_loader_sys",
       ":evergreen_info",
       ":sabi_string",
@@ -185,6 +189,14 @@
   outputs = [ "$sb_static_contents_output_data_dir/test/$subdir/{{source_target_relative}}" ]
 }
 
+static_library("constants") {
+  sources = [
+    "elf_loader_constants.cc",
+    "elf_loader_constants.h",
+  ]
+  deps = [ "//starboard:starboard_headers_only" ]
+}
+
 static_library("evergreen_info") {
   sources = [
     "evergreen_info.cc",
diff --git a/starboard/elf_loader/elf_loader.cc b/starboard/elf_loader/elf_loader.cc
index adebc1b..f8af5f6 100644
--- a/starboard/elf_loader/elf_loader.cc
+++ b/starboard/elf_loader/elf_loader.cc
@@ -18,6 +18,7 @@
 
 #include "starboard/atomic.h"
 #include "starboard/common/log.h"
+#include "starboard/common/paths.h"
 #include "starboard/configuration_constants.h"
 #include "starboard/elf_loader/elf_loader_impl.h"
 #include "starboard/elf_loader/evergreen_config.h"
@@ -63,8 +64,8 @@
                      bool use_compression,
                      bool use_memory_mapped_file) {
   if (is_relative_path) {
-    library_path_ = MakeRelativeToContentPath(library_path);
-    content_path_ = MakeRelativeToContentPath(content_path);
+    library_path_ = common::PrependContentPath(library_path);
+    content_path_ = common::PrependContentPath(content_path);
   } else {
     library_path_ = library_path;
     content_path_ = content_path;
@@ -90,23 +91,5 @@
   return impl_->LookupSymbol(symbol);
 }
 
-std::string ElfLoader::MakeRelativeToContentPath(const std::string& path) {
-  std::vector<char> content_path(kSbFileMaxPath);
-
-  if (!SbSystemGetPath(kSbSystemPathContentDirectory, content_path.data(),
-                       kSbFileMaxPath)) {
-    SB_LOG(ERROR) << "Failed to make '" << path.data()
-                  << "' relative to the ELF Loader content directory.";
-    return "";
-  }
-
-  std::string relative_path(content_path.data());
-
-  relative_path.push_back(kSbFileSepChar);
-  relative_path.append(path);
-
-  return relative_path;
-}
-
 }  // namespace elf_loader
 }  // namespace starboard
diff --git a/starboard/elf_loader/elf_loader.h b/starboard/elf_loader/elf_loader.h
index f8f9e26..36640c0 100644
--- a/starboard/elf_loader/elf_loader.h
+++ b/starboard/elf_loader/elf_loader.h
@@ -56,10 +56,6 @@
   const std::string& GetContentPath() const { return content_path_; }
 
  private:
-  // Adjusts |path| to be relative to the content directory of the ELF Loader.
-  // Returns an empty string on error.
-  std::string MakeRelativeToContentPath(const std::string& path);
-
   // The paths to the loaded shared library and it's content.
   std::string library_path_;
   std::string content_path_;
diff --git a/starboard/elf_loader/evergreen_info.h b/starboard/elf_loader/evergreen_info.h
index 7f500ea..6562e4a 100644
--- a/starboard/elf_loader/evergreen_info.h
+++ b/starboard/elf_loader/evergreen_info.h
@@ -26,6 +26,7 @@
 #define EVERGREEN_FILE_PATH_MAX_SIZE 4096
 #define EVERGREEN_BUILD_ID_MAX_SIZE 128
 
+#ifndef NATIVE_TARGET_BUILD
 #define IS_EVERGREEN_ADDRESS(address, evergreen_info)                    \
   (evergreen_info.base_address != 0 &&                                   \
    reinterpret_cast<uint64_t>(address) >= evergreen_info.base_address && \
@@ -35,6 +36,7 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
+#endif  // !NATIVE_TARGET_BUILD
 
 // Evergreen shared library memory mapping information used for
 // stack unwinding and symbolizing.
@@ -61,6 +63,7 @@
   size_t build_id_length;
 } EvergreenInfo;
 
+#ifndef NATIVE_TARGET_BUILD
 // Set the Evergreen information. Should be called only from the
 // elf_loader module. Passing NULL clears the currently stored
 // information.
@@ -76,5 +79,6 @@
 #ifdef __cplusplus
 }  // extern "C"
 #endif
+#endif  // !NATIVE_TARGET_BUILD
 
 #endif  // STARBOARD_ELF_LOADER_EVERGREEN_INFO_H_
diff --git a/starboard/elf_loader/exported_symbols.cc b/starboard/elf_loader/exported_symbols.cc
index d7c3684..d014f7f 100644
--- a/starboard/elf_loader/exported_symbols.cc
+++ b/starboard/elf_loader/exported_symbols.cc
@@ -42,6 +42,7 @@
 #if SB_API_VERSION < 13
 #include "starboard/speech_recognizer.h"
 #endif
+#include "starboard/common/log.h"
 #include "starboard/speech_synthesis.h"
 #include "starboard/storage.h"
 #include "starboard/string.h"
@@ -239,15 +240,27 @@
   REGISTER_SYMBOL(SbPlayerCreate);
   REGISTER_SYMBOL(SbPlayerDestroy);
   REGISTER_SYMBOL(SbPlayerGetCurrentFrame);
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  REGISTER_SYMBOL(SbPlayerGetInfo);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   REGISTER_SYMBOL(SbPlayerGetInfo2);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   REGISTER_SYMBOL(SbPlayerGetMaximumNumberOfSamplesPerWrite);
   REGISTER_SYMBOL(SbPlayerGetPreferredOutputMode);
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  REGISTER_SYMBOL(SbPlayerSeek);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   REGISTER_SYMBOL(SbPlayerSeek2);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   REGISTER_SYMBOL(SbPlayerSetBounds);
   REGISTER_SYMBOL(SbPlayerSetPlaybackRate);
   REGISTER_SYMBOL(SbPlayerSetVolume);
   REGISTER_SYMBOL(SbPlayerWriteEndOfStream);
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  REGISTER_SYMBOL(SbPlayerWriteSamples);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   REGISTER_SYMBOL(SbPlayerWriteSample2);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   REGISTER_SYMBOL(SbSocketAccept);
   REGISTER_SYMBOL(SbSocketBind);
   REGISTER_SYMBOL(SbSocketClearLastError);
diff --git a/starboard/event.h b/starboard/event.h
index 9d6e367..e0113b9 100644
--- a/starboard/event.h
+++ b/starboard/event.h
@@ -499,6 +499,14 @@
   return handle != kSbEventIdInvalid;
 }
 
+#if SB_MODULAR_BUILD
+typedef void (*SbEventHandleCallback)(const SbEvent* event);
+// Serves as the entry point in the Starboard library for running the Starboard
+// event loop with the application event handler.
+SB_EXPORT int SbRunStarboardMain(int argc,
+                                 char** argv,
+                                 SbEventHandleCallback callback);
+#endif  // SB_MODULAR_BUILD
 // The entry point that Starboard applications MUST implement. Any memory
 // pointed at by |event| or the |data| field inside |event| is owned by the
 // system, and that memory is reclaimed after this function returns, so the
@@ -510,13 +518,17 @@
 // specification about what other work might happen on this thread, so the
 // application should generally do as little work as possible on this thread,
 // and just dispatch it over to another thread.
+#if SB_MODULAR_BUILD
+SB_EXPORT_PLATFORM void SbEventHandle(const SbEvent* event);
+#else
 SB_IMPORT void SbEventHandle(const SbEvent* event);
+#endif  // SB_MODULAR_BUILD
 
 // Schedules an event |callback| into the main Starboard event loop.
 // This function may be called from any thread, but |callback| is always
 // called from the main Starboard thread, queued with other pending events.
 //
-// |callback|: The callback function to be called.
+// |callback|: The callback function to be called. Must not be NULL.
 // |context|: The context that is passed to the |callback| function.
 // |delay|: The minimum number of microseconds to wait before calling the
 // |callback| function. Set |delay| to |0| to call the callback as soon as
diff --git a/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn b/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn
index 1c13575..2dcac4f 100644
--- a/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/arm/hardfp/platform_configuration/BUILD.gn
@@ -21,15 +21,14 @@
 }
 
 config("platform_configuration") {
-  configs = [ "//starboard/build/config/sabi" ]
-
-  if (current_toolchain == default_toolchain) {
-    configs += [
-      "//starboard/evergreen/arm/shared/platform_configuration:sabi_flags",
-      ":sabi_flags",
-      "//starboard/evergreen/arm/shared/platform_configuration",
-    ]
-  } else {
-    cflags = [ "-O2" ]
-  }
+  configs = [
+    "//starboard/build/config/sabi",
+    "//starboard/evergreen/arm/shared/platform_configuration:sabi_flags",
+    ":sabi_flags",
+    "//starboard/evergreen/arm/shared/platform_configuration",
+  ]
+  ldflags = [
+    "-m",
+    "armelf",
+  ]
 }
diff --git a/starboard/evergreen/arm/hardfp/toolchain/BUILD.gn b/starboard/evergreen/arm/hardfp/toolchain/BUILD.gn
index 2673ab2..1565a25 100644
--- a/starboard/evergreen/arm/hardfp/toolchain/BUILD.gn
+++ b/starboard/evergreen/arm/hardfp/toolchain/BUILD.gn
@@ -16,15 +16,6 @@
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/evergreen/shared/toolchain/toolchain.gni")
 
-clang_toolchain("host") {
-  clang_base_path = clang_base_path
-
-  toolchain_args = {
-    current_os = "linux"
-    current_cpu = "x86"
-  }
-}
-
 gcc_toolchain("target") {
   cc = evergreen_target_cc
   cxx = evergreen_target_cxx
@@ -32,7 +23,6 @@
   ar = evergreen_target_ar
   nm = evergreen_target_nm
 
-  evergreen_target_extra_ldflags += [ "-m armelf" ]
   extra_ldflags = string_join(" ", evergreen_target_extra_ldflags)
 
   toolchain_args = {
diff --git a/starboard/evergreen/arm/softfp/platform_configuration/BUILD.gn b/starboard/evergreen/arm/softfp/platform_configuration/BUILD.gn
index a17c539..0de964f 100644
--- a/starboard/evergreen/arm/softfp/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/arm/softfp/platform_configuration/BUILD.gn
@@ -21,15 +21,14 @@
 }
 
 config("platform_configuration") {
-  configs = [ "//starboard/build/config/sabi" ]
-
-  if (current_toolchain == default_toolchain) {
-    configs += [
-      "//starboard/evergreen/arm/shared/platform_configuration:sabi_flags",
-      ":sabi_flags",
-      "//starboard/evergreen/arm/shared/platform_configuration",
-    ]
-  } else {
-    cflags = [ "-O2" ]
-  }
+  configs = [
+    "//starboard/build/config/sabi",
+    "//starboard/evergreen/arm/shared/platform_configuration:sabi_flags",
+    ":sabi_flags",
+    "//starboard/evergreen/arm/shared/platform_configuration",
+  ]
+  ldflags = [
+    "-m",
+    "armelf",
+  ]
 }
diff --git a/starboard/evergreen/arm/softfp/toolchain/BUILD.gn b/starboard/evergreen/arm/softfp/toolchain/BUILD.gn
index 2673ab2..1565a25 100644
--- a/starboard/evergreen/arm/softfp/toolchain/BUILD.gn
+++ b/starboard/evergreen/arm/softfp/toolchain/BUILD.gn
@@ -16,15 +16,6 @@
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/evergreen/shared/toolchain/toolchain.gni")
 
-clang_toolchain("host") {
-  clang_base_path = clang_base_path
-
-  toolchain_args = {
-    current_os = "linux"
-    current_cpu = "x86"
-  }
-}
-
 gcc_toolchain("target") {
   cc = evergreen_target_cc
   cxx = evergreen_target_cxx
@@ -32,7 +23,6 @@
   ar = evergreen_target_ar
   nm = evergreen_target_nm
 
-  evergreen_target_extra_ldflags += [ "-m armelf" ]
   extra_ldflags = string_join(" ", evergreen_target_extra_ldflags)
 
   toolchain_args = {
diff --git a/starboard/evergreen/arm64/platform_configuration/BUILD.gn b/starboard/evergreen/arm64/platform_configuration/BUILD.gn
index 88bd7e6..ce00c13 100644
--- a/starboard/evergreen/arm64/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/arm64/platform_configuration/BUILD.gn
@@ -24,17 +24,16 @@
 }
 
 config("platform_configuration") {
-  configs = [ "//starboard/build/config/sabi" ]
+  configs = [
+    "//starboard/build/config/sabi",
+    ":sabi_flags",
+    "//starboard/evergreen/shared/platform_configuration",
+  ]
 
-  if (current_toolchain == default_toolchain) {
-    configs += [
-      ":sabi_flags",
-      "//starboard/evergreen/shared/platform_configuration",
-    ]
-
-    cflags = [ "-isystem" +
-               rebase_path("//third_party/musl/arch/aarch64", root_build_dir) ]
-  } else {
-    cflags = [ "-O2" ]
-  }
+  ldflags = [
+    "-m",
+    "aarch64elf",
+  ]
+  cflags = [ "-isystem" +
+             rebase_path("//third_party/musl/arch/aarch64", root_build_dir) ]
 }
diff --git a/starboard/evergreen/arm64/toolchain/BUILD.gn b/starboard/evergreen/arm64/toolchain/BUILD.gn
index f571c39..1565a25 100644
--- a/starboard/evergreen/arm64/toolchain/BUILD.gn
+++ b/starboard/evergreen/arm64/toolchain/BUILD.gn
@@ -16,15 +16,6 @@
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/evergreen/shared/toolchain/toolchain.gni")
 
-clang_toolchain("host") {
-  clang_base_path = clang_base_path
-
-  toolchain_args = {
-    current_os = "linux"
-    current_cpu = "x64"
-  }
-}
-
 gcc_toolchain("target") {
   cc = evergreen_target_cc
   cxx = evergreen_target_cxx
@@ -32,7 +23,6 @@
   ar = evergreen_target_ar
   nm = evergreen_target_nm
 
-  evergreen_target_extra_ldflags += [ "-m aarch64elf" ]
   extra_ldflags = string_join(" ", evergreen_target_extra_ldflags)
 
   toolchain_args = {
diff --git a/starboard/evergreen/shared/launcher.py b/starboard/evergreen/shared/launcher.py
index d22f416..956cbb9 100644
--- a/starboard/evergreen/shared/launcher.py
+++ b/starboard/evergreen/shared/launcher.py
@@ -247,7 +247,6 @@
                                        _CRASHPAD_TARGET)
     crashpad_target_dst = os.path.join(staging_directory_loader,
                                        _CRASHPAD_TARGET)
-    os.makedirs(crashpad_target_dst)
     shutil.copy(crashpad_target_src, crashpad_target_dst)
 
     # Copy target content and binary
diff --git a/starboard/evergreen/shared/platform_configuration/configuration.gni b/starboard/evergreen/shared/platform_configuration/configuration.gni
index 5b331dc..a2b90ac 100644
--- a/starboard/evergreen/shared/platform_configuration/configuration.gni
+++ b/starboard/evergreen/shared/platform_configuration/configuration.gni
@@ -18,7 +18,7 @@
 
 cobalt_font_package = "empty"
 
-yasm_exists = true
+nasm_exists = true
 
 # Override that omits the "data" subdirectory.
 # TODO: Remove when omitted for all platforms in base_configuration.gni.
diff --git a/starboard/evergreen/testing/run_all_tests.sh b/starboard/evergreen/testing/run_all_tests.sh
index 1339884..0894d29 100755
--- a/starboard/evergreen/testing/run_all_tests.sh
+++ b/starboard/evergreen/testing/run_all_tests.sh
@@ -23,6 +23,9 @@
 AUTH_METHOD="public-key"
 USE_COMPRESSED_SYSTEM_IMAGE="false"
 SYSTEM_IMAGE_EXTENSION=".so"
+
+DISABLE_TESTS="false"
+
 while getopts "d:a:c" o; do
     case "${o}" in
         d)
@@ -89,9 +92,14 @@
 
     log "info" " [ RUN      ] ${TEST_NAME} attempt ${attempt}"
 
-    run_test
 
-    RESULT=$?
+    if [[ "${DISABLE_TESTS}" == "true" ]]; then
+      # Set the result to skipped.
+      RESULT=2
+    else
+      run_test
+      RESULT=$?
+    fi
 
     stop_cobalt &> /dev/null
 
diff --git a/starboard/evergreen/testing/tests/crashpad_runs_test.sh b/starboard/evergreen/testing/tests/crashpad_runs_test.sh
index ebd40ac..0e61ce1 100644
--- a/starboard/evergreen/testing/tests/crashpad_runs_test.sh
+++ b/starboard/evergreen/testing/tests/crashpad_runs_test.sh
@@ -28,11 +28,6 @@
   LOG="${TEST_NAME}.0.log"
   start_cobalt "file:///tests/${TEST_FILE}?channel=tcrash" "${LOG}" LOADER
 
-  if [[ $(run_command "ps -C crashpad_handler") -ne 0 ]]; then
-    log "error" " Failed to start crashpad_handler"
-    return 1
-  fi
-
   watch_cobalt "${LOG}" "update from tcrash channel was installed"
 
   if [[ $? -ne 0 ]]; then
@@ -54,10 +49,13 @@
     return 1
   fi
 
-  if [[ $(run_command "find ${CACHE_DIR}/crashpad_database/completed/ -mmin -3 | ${WC} -l") -eq 0 ]]; then
+  local num_recently_completed_crash_files=$(eval "run_command \"find ${CACHE_DIR}/crashpad_database/completed/ -mmin -3 | ${WC} -l\"")
+  # The actual numeric value in the variable may be surrounded by newlines and spaces.
+  if [[ $(echo "$num_recently_completed_crash_files" | tr -d '\n' | tr -d '[:space:]') != "0" ]]; then
+    # Some recently completed crash files were found in the local crash database.
+    return 0
+  else
     log "error" " Failed upload crash to crash database"
     return 1
   fi
-
-  return 0
 }
diff --git a/starboard/evergreen/x64/platform_configuration/BUILD.gn b/starboard/evergreen/x64/platform_configuration/BUILD.gn
index 658a9ac..47d7e22 100644
--- a/starboard/evergreen/x64/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/x64/platform_configuration/BUILD.gn
@@ -21,17 +21,16 @@
 }
 
 config("platform_configuration") {
-  configs = [ "//starboard/build/config/sabi" ]
+  configs = [
+    "//starboard/build/config/sabi",
+    ":sabi_flags",
+    "//starboard/evergreen/shared/platform_configuration",
+  ]
 
-  if (current_toolchain == default_toolchain) {
-    configs += [
-      ":sabi_flags",
-      "//starboard/evergreen/shared/platform_configuration",
-    ]
-
-    cflags = [ "-isystem" +
-               rebase_path("//third_party/musl/arch/x86_64", root_build_dir) ]
-  } else {
-    cflags = [ "-O2" ]
-  }
+  ldflags = [
+    "-m",
+    "elf_x86_64",
+  ]
+  cflags = [ "-isystem" +
+             rebase_path("//third_party/musl/arch/x86_64", root_build_dir) ]
 }
diff --git a/starboard/evergreen/x64/test_filters.py b/starboard/evergreen/x64/test_filters.py
index 3f11560..08e4e96 100644
--- a/starboard/evergreen/x64/test_filters.py
+++ b/starboard/evergreen/x64/test_filters.py
@@ -15,8 +15,27 @@
 
 import os
 
-from starboard.tools import paths
 from starboard.evergreen.shared import test_filters as shared_test_filters
+from starboard.tools import paths
+from starboard.tools.testing import test_filter
+
+# pylint: disable=line-too-long
+_FILTERED_TESTS = {
+    'nplb': [
+        'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.WriteSingleBatch/audio_sintel_329_ec3_dmp_video__null__output_DecodeToTexture',
+        'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.WriteSingleBatch/audio_sintel_329_ec3_dmp_video__null__output_Punchout',
+        'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.WriteSingleBatch/audio_sintel_381_ac3_dmp_video__null__output_DecodeToTexture',
+        'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.WriteSingleBatch/audio_sintel_381_ac3_dmp_video__null__output_Punchout',
+        'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.WriteMultipleBatches/audio_sintel_329_ec3_dmp_video__null__output_DecodeToTexture',
+        'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.WriteMultipleBatches/audio_sintel_329_ec3_dmp_video__null__output_Punchout',
+        'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.WriteMultipleBatches/audio_sintel_381_ac3_dmp_video__null__output_DecodeToTexture',
+        'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.WriteMultipleBatches/audio_sintel_381_ac3_dmp_video__null__output_Punchout',
+        'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDayDestination/type_ipv6',
+        'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceForDestination/type_ipv6',
+        'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceNotLoopback/type_ipv6',
+    ],
+}
+# pylint: enable=line-too-long
 
 
 def CreateTestFilters():
@@ -27,11 +46,14 @@
   """Starboard Evergreen X64 Platform Test Filters."""
 
   def GetTestFilters(self):
-    filters = super(EvergreenX64TestFilters, self).GetTestFilters()
+    filters = super().GetTestFilters()
+
+    for target, tests in _FILTERED_TESTS.items():
+      filters.extend(test_filter.TestFilter(target, test) for test in tests)
+
     # Remove the exclusion filter on SbDrmTest.AnySupportedKeySystems.
     # Generally, children of linux/shared do not support widevine, but children
     # of linux/x64x11 do, if the content decryption module is present.
-
     has_cdm = os.path.isfile(
         os.path.join(paths.REPOSITORY_ROOT, 'third_party', 'cdm', 'cdm',
                      'include', 'content_decryption_module.h'))
@@ -39,10 +61,11 @@
     if not has_cdm:
       return filters
 
-    test_filters = []
-    for test_filter in filters:
-      if (test_filter.target_name == 'nplb' and
-          test_filter.test_name == 'SbDrmTest.AnySupportedKeySystems'):
-        continue
-      test_filters.append(test_filter)
-    return test_filters
+    def IsAllowedTest(test_filter_item):
+      return (test_filter_item.target_name == 'nplb' and
+              test_filter_item.test_name == 'SbDrmTest.AnySupportedKeySystems')
+
+    return [
+        test_filter_item for test_filter_item in filters
+        if not IsAllowedTest(test_filter_item)
+    ]
diff --git a/starboard/evergreen/x64/toolchain/BUILD.gn b/starboard/evergreen/x64/toolchain/BUILD.gn
index 6fedf62..9c89ff2 100644
--- a/starboard/evergreen/x64/toolchain/BUILD.gn
+++ b/starboard/evergreen/x64/toolchain/BUILD.gn
@@ -16,10 +16,6 @@
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/evergreen/shared/toolchain/toolchain.gni")
 
-clang_toolchain("host") {
-  clang_base_path = clang_base_path
-}
-
 gcc_toolchain("target") {
   cc = evergreen_target_cc
   cxx = evergreen_target_cxx
@@ -27,7 +23,6 @@
   ar = evergreen_target_ar
   nm = evergreen_target_nm
 
-  evergreen_target_extra_ldflags += [ "-m elf_x86_64" ]
   extra_ldflags = string_join(" ", evergreen_target_extra_ldflags)
 
   toolchain_args = {
diff --git a/starboard/evergreen/x86/platform_configuration/BUILD.gn b/starboard/evergreen/x86/platform_configuration/BUILD.gn
index 91f6dd4..633a354 100644
--- a/starboard/evergreen/x86/platform_configuration/BUILD.gn
+++ b/starboard/evergreen/x86/platform_configuration/BUILD.gn
@@ -21,17 +21,12 @@
 }
 
 config("platform_configuration") {
-  configs = [ "//starboard/build/config/sabi" ]
+  configs = [
+    "//starboard/build/config/sabi",
+    ":sabi_flags",
+    "//starboard/evergreen/shared/platform_configuration",
+  ]
 
-  if (current_toolchain == default_toolchain) {
-    configs += [
-      ":sabi_flags",
-      "//starboard/evergreen/shared/platform_configuration",
-    ]
-
-    cflags = [ "-isystem" +
-               rebase_path("//third_party/musl/arch/i386", root_build_dir) ]
-  } else {
-    cflags = [ "-O2" ]
-  }
+  cflags = [ "-isystem" +
+             rebase_path("//third_party/musl/arch/i386", root_build_dir) ]
 }
diff --git a/starboard/evergreen/x86/toolchain/BUILD.gn b/starboard/evergreen/x86/toolchain/BUILD.gn
index e444b05..1565a25 100644
--- a/starboard/evergreen/x86/toolchain/BUILD.gn
+++ b/starboard/evergreen/x86/toolchain/BUILD.gn
@@ -16,10 +16,6 @@
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/evergreen/shared/toolchain/toolchain.gni")
 
-clang_toolchain("host") {
-  clang_base_path = clang_base_path
-}
-
 gcc_toolchain("target") {
   cc = evergreen_target_cc
   cxx = evergreen_target_cxx
diff --git a/starboard/extension/BUILD.gn b/starboard/extension/BUILD.gn
index fc63fc7..cf61cd9 100644
--- a/starboard/extension/BUILD.gn
+++ b/starboard/extension/BUILD.gn
@@ -15,7 +15,10 @@
 target(gtest_target_type, "extension_test") {
   testonly = true
   has_pedantic_warnings = true
-  sources = [ "extension_test.cc" ]
+  sources = [
+    "enhanced_audio_test.cc",
+    "extension_test.cc",
+  ]
   deps = [
     "//cobalt/base",
     "//cobalt/test:run_all_unittests",
diff --git a/starboard/extension/enhanced_audio.h b/starboard/extension/enhanced_audio.h
new file mode 100644
index 0000000..f352a24
--- /dev/null
+++ b/starboard/extension/enhanced_audio.h
@@ -0,0 +1,122 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_EXTENSION_ENHANCED_AUDIO_H_
+#define STARBOARD_EXTENSION_ENHANCED_AUDIO_H_
+
+#include "starboard/configuration.h"
+#include "starboard/drm.h"
+#include "starboard/media.h"
+#include "starboard/player.h"
+#include "starboard/time.h"
+#include "starboard/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define kCobaltExtensionEnhancedAudioName "dev.cobalt.extension.EnhancedAudio"
+
+// The structure has the same binary layout as `SbMediaAudioStreamInfo` in the
+// most recent Starboard version, and is verified by unit tests.
+// The comment of individual members are explicitly removed, please refer to the
+// comment of `SbMediaAudioStreamInfo` in `media.h` for more details.
+typedef struct CobaltExtensionEnhancedAudioMediaAudioStreamInfo {
+  SbMediaAudioCodec codec;
+  const char* mime;
+  uint16_t number_of_channels;
+  uint32_t samples_per_second;
+  uint16_t bits_per_sample;
+  uint16_t audio_specific_config_size;
+  const void* audio_specific_config;
+} CobaltExtensionEnhancedAudioMediaAudioStreamInfo;
+
+// The structure has the same binary layout as `SbMediaAudioSampleInfo` in the
+// most recent Starboard version, and is verified by unit tests.
+// The comment of individual members are explicitly removed, please refer to the
+// comment of `SbMediaAudioSampleInfo` in `media.h` for more details.
+typedef struct CobaltExtensionEnhancedAudioMediaAudioSampleInfo {
+  CobaltExtensionEnhancedAudioMediaAudioStreamInfo stream_info;
+  SbTime discarded_duration_from_front;
+  SbTime discarded_duration_from_back;
+} CobaltExtensionEnhancedAudioMediaAudioSampleInfo;
+
+// The structure has the same binary layout as `SbMediaVideoStreamInfo` in the
+// most recent Starboard version, and is verified by unit tests.
+// The comment of individual members are explicitly removed, please refer to the
+// comment of `SbMediaVideoStreamInfo` in `media.h` for more details.
+typedef struct CobaltExtensionEnhancedAudioMediaVideoStreamInfo {
+  SbMediaVideoCodec codec;
+  const char* mime;
+  const char* max_video_capabilities;
+  int frame_width;
+  int frame_height;
+  SbMediaColorMetadata color_metadata;
+} CobaltExtensionEnhancedAudioMediaVideoStreamInfo;
+
+// The structure has the same binary layout as `SbMediaVideoSampleInfo` in the
+// most recent Starboard version, and is verified by unit tests.
+// The comment of individual members are explicitly removed, please refer to the
+// comment of `SbMediaVideoSampleInfo` in `media.h` for more details.
+typedef struct CobaltExtensionEnhancedAudioMediaVideoSampleInfo {
+  CobaltExtensionEnhancedAudioMediaVideoStreamInfo stream_info;
+  bool is_key_frame;
+} CobaltExtensionEnhancedAudioMediaVideoSampleInfo;
+
+// The structure has the same binary layout as `SbPlayerSampleInfo` in the
+// most recent Starboard version, and is verified by unit tests.
+// The comment of individual members are explicitly removed, please refer to the
+// comment of `SbPlayerSampleInfo` in `player.h` for more details.
+typedef struct CobaltExtensionEnhancedAudioPlayerSampleInfo {
+  SbMediaType type;
+  const void* buffer;
+  int buffer_size;
+  SbTime timestamp;
+  SbPlayerSampleSideData* side_data;
+  int side_data_count;
+  union {
+    CobaltExtensionEnhancedAudioMediaAudioSampleInfo audio_sample_info;
+    CobaltExtensionEnhancedAudioMediaVideoSampleInfo video_sample_info;
+  };
+  const SbDrmSampleInfo* drm_info;
+} CobaltExtensionEnhancedAudioPlayerSampleInfo;
+
+typedef struct CobaltExtensionEnhancedAudioApi {
+  // Name should be the string kCobaltExtensionEnhancedAudioName.
+  // This helps to validate that the extension API is correct.
+  const char* name;
+
+  // This specifies the version of the API that is implemented.
+  uint32_t version;
+
+  // EnhancedAudio API Wrapper.
+
+  // Writes samples of the given media type to |player|'s input stream.
+  // It has exactly the same interface as `SbPlayerWriteSamples()` defined in
+  // `player.h`, except that |sample_infos| is under type specific to this
+  // extension.
+  // Please refer to the comment of `SbPlayerWriteSamples()` in `player.h` for
+  // more details.
+  void (*PlayerWriteSamples)(
+      SbPlayer player,
+      SbMediaType sample_type,
+      const CobaltExtensionEnhancedAudioPlayerSampleInfo* sample_infos,
+      int number_of_sample_infos);
+} CobaltExtensionEnhancedAudioApi;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // STARBOARD_EXTENSION_ENHANCED_AUDIO_H_
diff --git a/starboard/extension/enhanced_audio_test.cc b/starboard/extension/enhanced_audio_test.cc
new file mode 100644
index 0000000..e215f9e
--- /dev/null
+++ b/starboard/extension/enhanced_audio_test.cc
@@ -0,0 +1,116 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/extension/enhanced_audio.h"
+
+#include <cstddef>
+
+#include "starboard/extension/configuration.h"
+#include "starboard/media.h"
+#include "starboard/player.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace extension {
+
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+TEST(EnhancedAudioTest, VerifyBinaryLayouts) {
+  // Sanity check that the layouts of the extension specific types are the same
+  // as corresponding SbMedia and SbPlayer types.
+  EXPECT_EQ(sizeof(CobaltExtensionEnhancedAudioMediaAudioStreamInfo),
+            sizeof(SbMediaAudioStreamInfo));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioMediaAudioStreamInfo, codec),
+            offsetof(SbMediaAudioStreamInfo, codec));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioMediaAudioStreamInfo, mime),
+            offsetof(SbMediaAudioStreamInfo, mime));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioMediaAudioStreamInfo,
+                     number_of_channels),
+            offsetof(SbMediaAudioStreamInfo, number_of_channels));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioMediaAudioStreamInfo,
+                     samples_per_second),
+            offsetof(SbMediaAudioStreamInfo, samples_per_second));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioMediaAudioStreamInfo,
+                     bits_per_sample),
+            offsetof(SbMediaAudioStreamInfo, bits_per_sample));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioMediaAudioStreamInfo,
+                     audio_specific_config_size),
+            offsetof(SbMediaAudioStreamInfo, audio_specific_config_size));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioMediaAudioStreamInfo,
+                     audio_specific_config),
+            offsetof(SbMediaAudioStreamInfo, audio_specific_config));
+
+  EXPECT_EQ(sizeof(CobaltExtensionEnhancedAudioMediaAudioSampleInfo),
+            sizeof(SbMediaAudioSampleInfo));
+  EXPECT_EQ(
+      offsetof(CobaltExtensionEnhancedAudioMediaAudioSampleInfo, stream_info),
+      offsetof(SbMediaAudioSampleInfo, stream_info));
+
+  EXPECT_EQ(sizeof(CobaltExtensionEnhancedAudioMediaVideoStreamInfo),
+            sizeof(SbMediaVideoStreamInfo));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioMediaVideoStreamInfo, codec),
+            offsetof(SbMediaVideoStreamInfo, codec));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioMediaVideoStreamInfo, mime),
+            offsetof(SbMediaVideoStreamInfo, mime));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioMediaVideoStreamInfo,
+                     max_video_capabilities),
+            offsetof(SbMediaVideoStreamInfo, max_video_capabilities));
+  EXPECT_EQ(
+      offsetof(CobaltExtensionEnhancedAudioMediaVideoStreamInfo, frame_width),
+      offsetof(SbMediaVideoStreamInfo, frame_width));
+  EXPECT_EQ(
+      offsetof(CobaltExtensionEnhancedAudioMediaVideoStreamInfo, frame_height),
+      offsetof(SbMediaVideoStreamInfo, frame_height));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioMediaVideoStreamInfo,
+                     color_metadata),
+            offsetof(SbMediaVideoStreamInfo, color_metadata));
+
+  EXPECT_EQ(sizeof(CobaltExtensionEnhancedAudioMediaVideoSampleInfo),
+            sizeof(SbMediaVideoSampleInfo));
+  EXPECT_EQ(
+      offsetof(CobaltExtensionEnhancedAudioMediaVideoSampleInfo, stream_info),
+      offsetof(SbMediaVideoSampleInfo, stream_info));
+  EXPECT_EQ(
+      offsetof(CobaltExtensionEnhancedAudioMediaVideoSampleInfo, is_key_frame),
+      offsetof(SbMediaVideoSampleInfo, is_key_frame));
+
+  EXPECT_EQ(sizeof(CobaltExtensionEnhancedAudioPlayerSampleInfo),
+            sizeof(SbPlayerSampleInfo));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioPlayerSampleInfo, type),
+            offsetof(SbPlayerSampleInfo, type));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioPlayerSampleInfo, buffer),
+            offsetof(SbPlayerSampleInfo, buffer));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioPlayerSampleInfo, buffer_size),
+            offsetof(SbPlayerSampleInfo, buffer_size));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioPlayerSampleInfo, timestamp),
+            offsetof(SbPlayerSampleInfo, timestamp));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioPlayerSampleInfo, side_data),
+            offsetof(SbPlayerSampleInfo, side_data));
+  EXPECT_EQ(
+      offsetof(CobaltExtensionEnhancedAudioPlayerSampleInfo, side_data_count),
+      offsetof(SbPlayerSampleInfo, side_data_count));
+  EXPECT_EQ(
+      offsetof(CobaltExtensionEnhancedAudioPlayerSampleInfo, audio_sample_info),
+      offsetof(SbPlayerSampleInfo, audio_sample_info));
+  EXPECT_EQ(
+      offsetof(CobaltExtensionEnhancedAudioPlayerSampleInfo, video_sample_info),
+      offsetof(SbPlayerSampleInfo, video_sample_info));
+  EXPECT_EQ(offsetof(CobaltExtensionEnhancedAudioPlayerSampleInfo, drm_info),
+            offsetof(SbPlayerSampleInfo, drm_info));
+}
+
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+}  // namespace extension
+}  // namespace starboard
diff --git a/starboard/extension/extension_test.cc b/starboard/extension/extension_test.cc
index ec716ad..7d516be 100644
--- a/starboard/extension/extension_test.cc
+++ b/starboard/extension/extension_test.cc
@@ -17,6 +17,7 @@
 #include "starboard/extension/configuration.h"
 #include "starboard/extension/crash_handler.h"
 #include "starboard/extension/cwrappers.h"
+#include "starboard/extension/enhanced_audio.h"
 #include "starboard/extension/font.h"
 #include "starboard/extension/free_space.h"
 #include "starboard/extension/graphics.h"
@@ -30,7 +31,7 @@
 #include "starboard/system.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace cobalt {
+namespace starboard {
 namespace extension {
 
 TEST(ExtensionTest, PlatformService) {
@@ -382,5 +383,38 @@
   EXPECT_EQ(second_extension_api, extension_api)
       << "Extension struct should be a singleton";
 }
+
+TEST(ExtensionTest, EnhancedAudio) {
+  typedef CobaltExtensionEnhancedAudioApi ExtensionApi;
+  const char* kExtensionName = kCobaltExtensionEnhancedAudioName;
+
+  const ExtensionApi* extension_api =
+      static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
+  if (!extension_api) {
+    return;
+  }
+
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  ASSERT_FALSE(extension_api)
+      << "EnhancedAudio extension shouldn't be used under SB_API_VERSION "
+      << SB_API_VERSION
+      << ", the features are supported by the current SbPlayer api by default."
+      << "\nTo upgrade the EnhancedAudio extension based implementation from"
+      << " a previous Starboard version, please rename the `PlayerWriteSamples`"
+      << " implementation to `SbPlayerWriteSample()` (as they are compatible"
+      << " at abi level) and disable the EnhancedAudio extension from"
+      << " `SbSystemGetExtension()`.";
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+  EXPECT_STREQ(extension_api->name, kExtensionName);
+  EXPECT_EQ(extension_api->version, 1u);
+  EXPECT_NE(extension_api->PlayerWriteSamples, nullptr);
+
+  const ExtensionApi* second_extension_api =
+      static_cast<const ExtensionApi*>(SbSystemGetExtension(kExtensionName));
+  EXPECT_EQ(second_extension_api, extension_api)
+      << "Extension struct should be a singleton";
+}
+
 }  // namespace extension
-}  // namespace cobalt
+}  // namespace starboard
diff --git a/starboard/image.h b/starboard/image.h
index 80ede0e..6352b6e 100644
--- a/starboard/image.h
+++ b/starboard/image.h
@@ -35,7 +35,7 @@
 //       return;
 //     }
 //
-//     SbDecodeTarget result_target = SbDecodeImage(provider, data, data_size,
+//     SbDecodeTarget result_target = SbImageDecode(provider, data, data_size,
 //                                                  mime_type, format);
 //
 
@@ -52,15 +52,16 @@
 #endif
 
 // Whether the current platform supports hardware accelerated decoding an
-// image of mime type |mime_type| into SbDecodeTargetFormat |format|.  The
-// result of this function must not change over the course of the program,
-// which means that the results of this function may be cached indefinitely.
+// image of mime type |mime_type| into SbDecodeTargetFormat |format|. The
+// |mime_type| must not be NULL. The result of this function must not change
+// over the course of the program, which means that the results of this function
+// may be cached indefinitely.
 SB_EXPORT bool SbImageIsDecodeSupported(const char* mime_type,
                                         SbDecodeTargetFormat format);
 
 // Attempt to decode encoded |mime_type| image data |data| of size |data_size|
 // into an SbDecodeTarget of SbDecodeFormatType |format|, possibly using
-// SbDecodeTargetProvider |provider|, if it is non-null.  Thus, four following
+// SbDecodeTargetProvider |provider|, if it is non-null. Thus, four following
 // scenarios regarding the provider may happen:
 //
 //   1. The provider is required by the |SbImageDecode| implementation and no
@@ -73,7 +74,8 @@
 //      desires.
 //   4. The provider is not required and is not passed in.  The implementation
 //      will proceed forward.
-//
+// The |data| pointer must not be NULL.
+// The |mime_type| string must not be NULL.
 // Thus, it is NOT safe for clients of this API to assume that the |provider|
 // it passes in will be called.  Finally, if the decode succeeds, a new
 // SbDecodeTarget will be allocated. If |mime_type| image decoding for the
diff --git a/starboard/linux/shared/BUILD.gn b/starboard/linux/shared/BUILD.gn
index ec8ce53..c62aa23 100644
--- a/starboard/linux/shared/BUILD.gn
+++ b/starboard/linux/shared/BUILD.gn
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("//starboard/shared/enhanced_audio/enhanced_audio.gni")
 import("//starboard/shared/starboard/media/media_tests.gni")
 import("//starboard/shared/starboard/player/buildfiles.gni")
 import("//starboard/shared/starboard/player/player_tests.gni")
@@ -32,11 +33,11 @@
   ]
 
   if (sb_is_evergreen_compatible) {
-    public_deps += [ "//starboard/elf_loader:evergreen_config" ]
-
-    if (!sb_evergreen_compatible_enable_lite) {
-      public_deps += [ "//starboard/loader_app:pending_restart" ]
-    }
+    public_deps += [
+      "//starboard/elf_loader:constants",
+      "//starboard/elf_loader:evergreen_config",
+      "//starboard/loader_app:pending_restart",
+    ]
   }
 
   if (sb_evergreen_compatible_use_libunwind) {
@@ -341,8 +342,6 @@
     "//starboard/shared/starboard/media/media_get_video_buffer_budget.cc",
     "//starboard/shared/starboard/media/media_is_buffer_pool_allocate_on_demand.cc",
     "//starboard/shared/starboard/media/media_is_buffer_using_memory_pool.cc",
-    "//starboard/shared/starboard/media/mime_type.cc",
-    "//starboard/shared/starboard/media/mime_type.h",
     "//starboard/shared/starboard/memory.cc",
     "//starboard/shared/starboard/queue_application.cc",
     "//starboard/shared/starboard/starboard_switches.cc",
@@ -400,6 +399,11 @@
 
   sources -= [ "//starboard/shared/starboard/player/player_set_bounds.cc" ]
 
+  if (sb_api_version < 15) {
+    sources -= [ "//starboard/shared/starboard/player/player_write_samples.cc" ]
+    sources += enhanced_audio_sources
+  }
+
   configs += [
     "//starboard/build/config:starboard_implementation",
     "//third_party/de265_includes",
diff --git a/starboard/linux/shared/platform_configuration/configuration.gni b/starboard/linux/shared/platform_configuration/configuration.gni
index ef6eba3..e98ebe7 100644
--- a/starboard/linux/shared/platform_configuration/configuration.gni
+++ b/starboard/linux/shared/platform_configuration/configuration.gni
@@ -14,7 +14,7 @@
 
 import("//starboard/build/config/base_configuration.gni")
 
-yasm_exists = true
+nasm_exists = true
 
 sb_static_contents_output_data_dir = "$root_out_dir/content"
 
diff --git a/starboard/linux/shared/player_components_factory.cc b/starboard/linux/shared/player_components_factory.cc
index 7440b40..7bc746c 100644
--- a/starboard/linux/shared/player_components_factory.cc
+++ b/starboard/linux/shared/player_components_factory.cc
@@ -28,6 +28,7 @@
 #include "starboard/shared/openh264/openh264_library_loader.h"
 #include "starboard/shared/openh264/openh264_video_decoder.h"
 #include "starboard/shared/opus/opus_audio_decoder.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_renderer_sink.h"
@@ -70,24 +71,23 @@
       typedef ::starboard::shared::libfdkaac::FdkAacAudioDecoder
           FdkAacAudioDecoder;
 
-      auto decoder_creator = [](const SbMediaAudioSampleInfo& audio_sample_info,
+      auto decoder_creator = [](const media::AudioStreamInfo& audio_stream_info,
                                 SbDrmSystem drm_system) {
-        if (audio_sample_info.codec == kSbMediaAudioCodecOpus) {
+        if (audio_stream_info.codec == kSbMediaAudioCodecOpus) {
           scoped_ptr<OpusAudioDecoder> audio_decoder_impl(
-              new OpusAudioDecoder(audio_sample_info));
+              new OpusAudioDecoder(audio_stream_info));
           if (audio_decoder_impl->is_valid()) {
             return audio_decoder_impl.PassAs<AudioDecoder>();
           }
-        } else if (audio_sample_info.codec == kSbMediaAudioCodecAac &&
-                   audio_sample_info.number_of_channels <=
+        } else if (audio_stream_info.codec == kSbMediaAudioCodecAac &&
+                   audio_stream_info.number_of_channels <=
                        FdkAacAudioDecoder::kMaxChannels &&
                    libfdkaac::LibfdkaacHandle::GetHandle()->IsLoaded()) {
           SB_LOG(INFO) << "Playing audio using FdkAacAudioDecoder.";
           return scoped_ptr<AudioDecoder>(new FdkAacAudioDecoder());
         } else {
           scoped_ptr<FfmpegAudioDecoder> audio_decoder_impl(
-              FfmpegAudioDecoder::Create(audio_sample_info.codec,
-                                         audio_sample_info));
+              FfmpegAudioDecoder::Create(audio_stream_info));
           if (audio_decoder_impl && audio_decoder_impl->is_valid()) {
             SB_LOG(INFO) << "Playing audio using FfmpegAudioDecoder";
             return audio_decoder_impl.PassAs<AudioDecoder>();
@@ -97,7 +97,7 @@
       };
 
       audio_decoder->reset(new AdaptiveAudioDecoder(
-          creation_parameters.audio_sample_info(),
+          creation_parameters.audio_stream_info(),
           creation_parameters.drm_system(), decoder_creator));
       audio_renderer_sink->reset(new AudioRendererSinkImpl);
     }
diff --git a/starboard/linux/shared/system_get_extensions.cc b/starboard/linux/shared/system_get_extensions.cc
index eafacf0..b191ae4 100644
--- a/starboard/linux/shared/system_get_extensions.cc
+++ b/starboard/linux/shared/system_get_extensions.cc
@@ -18,10 +18,12 @@
 #include "starboard/extension/configuration.h"
 #include "starboard/extension/crash_handler.h"
 #include "starboard/extension/demuxer.h"
+#include "starboard/extension/enhanced_audio.h"
 #include "starboard/extension/free_space.h"
 #include "starboard/extension/memory_mapped_file.h"
 #include "starboard/extension/platform_service.h"
 #include "starboard/linux/shared/soft_mic_platform_service.h"
+#include "starboard/shared/enhanced_audio/enhanced_audio.h"
 #include "starboard/shared/ffmpeg/ffmpeg_demuxer.h"
 #include "starboard/shared/posix/free_space.h"
 #include "starboard/shared/posix/memory_mapped_file.h"
@@ -59,6 +61,11 @@
   if (strcmp(name, kCobaltExtensionFreeSpaceName) == 0) {
     return starboard::shared::posix::GetFreeSpaceApi();
   }
+#if SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  if (strcmp(name, kCobaltExtensionEnhancedAudioName) == 0) {
+    return starboard::shared::enhanced_audio::GetEnhancedAudioApi();
+  }
+#endif  // SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   if (strcmp(name, kCobaltExtensionDemuxerApi) == 0) {
     auto command_line =
         starboard::shared::starboard::Application::Get()->GetCommandLine();
diff --git a/starboard/linux/x64x11/clang/3.9/platform_configuration/BUILD.gn b/starboard/linux/x64x11/clang/3.9/platform_configuration/BUILD.gn
index 855eed2..ef3052c 100644
--- a/starboard/linux/x64x11/clang/3.9/platform_configuration/BUILD.gn
+++ b/starboard/linux/x64x11/clang/3.9/platform_configuration/BUILD.gn
@@ -180,14 +180,10 @@
 }
 
 config("platform_configuration") {
-  if (current_toolchain == default_toolchain) {
-    configs = [
-      "//starboard/linux/shared/platform_configuration",
-      "//starboard/linux/x64x11/shared/platform_configuration:libraries",
-      "//starboard/build/config/sabi",
-      ":compiler_flags",
-    ]
-  } else {
-    cflags = [ "-O2" ]
-  }
+  configs = [
+    "//starboard/linux/shared/platform_configuration",
+    "//starboard/linux/x64x11/shared/platform_configuration:libraries",
+    "//starboard/build/config/sabi",
+    ":compiler_flags",
+  ]
 }
diff --git a/starboard/linux/x64x11/clang/3.9/platform_configuration/configuration.gni b/starboard/linux/x64x11/clang/3.9/platform_configuration/configuration.gni
index e3b7857..188d57d 100644
--- a/starboard/linux/x64x11/clang/3.9/platform_configuration/configuration.gni
+++ b/starboard/linux/x64x11/clang/3.9/platform_configuration/configuration.gni
@@ -14,7 +14,7 @@
 
 import("//starboard/build/config/base_configuration.gni")
 
-yasm_exists = true
+nasm_exists = true
 
 sb_static_contents_output_data_dir = "$root_out_dir/content"
 
diff --git a/starboard/linux/x64x11/clang/3.9/toolchain/BUILD.gn b/starboard/linux/x64x11/clang/3.9/toolchain/BUILD.gn
index 420bc6f..79cd0f0 100644
--- a/starboard/linux/x64x11/clang/3.9/toolchain/BUILD.gn
+++ b/starboard/linux/x64x11/clang/3.9/toolchain/BUILD.gn
@@ -17,15 +17,6 @@
 
 _target_llvm_3_9_bin_dir = "/usr/lib/llvm-3.9/bin"
 
-overridable_clang_toolchain("host") {
-  clang_base_path = clang_base_path
-
-  toolchain_args = {
-    target_os = "linux"
-    target_cpu = "x64"
-  }
-}
-
 overridable_gcc_toolchain("target") {
   cc = "${_target_llvm_3_9_bin_dir}/clang"
   cxx = "${_target_llvm_3_9_bin_dir}/clang++"
diff --git a/starboard/linux/x64x11/egl/platform_configuration/BUILD.gn b/starboard/linux/x64x11/egl/platform_configuration/BUILD.gn
index 31f12ca..53e2774 100644
--- a/starboard/linux/x64x11/egl/platform_configuration/BUILD.gn
+++ b/starboard/linux/x64x11/egl/platform_configuration/BUILD.gn
@@ -14,10 +14,8 @@
 
 config("platform_configuration") {
   configs = [ "//starboard/linux/x64x11/platform_configuration" ]
-  if (current_toolchain == default_toolchain) {
-    libs = [
-      "EGL",
-      "GLESv2",
-    ]
-  }
+  libs = [
+    "EGL",
+    "GLESv2",
+  ]
 }
diff --git a/starboard/linux/x64x11/egl/toolchain/BUILD.gn b/starboard/linux/x64x11/egl/toolchain/BUILD.gn
index c1469cb..f705916 100644
--- a/starboard/linux/x64x11/egl/toolchain/BUILD.gn
+++ b/starboard/linux/x64x11/egl/toolchain/BUILD.gn
@@ -15,15 +15,15 @@
 import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
 
-clang_toolchain("host") {
+clang_toolchain("target") {
   clang_base_path = clang_base_path
+}
 
+clang_toolchain("native_target") {
   toolchain_args = {
+    is_starboard = false
+    is_native_target_build = true
     current_os = "linux"
     current_cpu = "x64"
   }
 }
-
-clang_toolchain("target") {
-  clang_base_path = clang_base_path
-}
diff --git a/starboard/linux/x64x11/gcc/6.3/platform_configuration/BUILD.gn b/starboard/linux/x64x11/gcc/6.3/platform_configuration/BUILD.gn
index 45767b2..0ba189c 100644
--- a/starboard/linux/x64x11/gcc/6.3/platform_configuration/BUILD.gn
+++ b/starboard/linux/x64x11/gcc/6.3/platform_configuration/BUILD.gn
@@ -99,14 +99,10 @@
 }
 
 config("platform_configuration") {
-  if (current_toolchain == default_toolchain) {
-    configs = [
-      "//starboard/linux/shared/platform_configuration",
-      "//starboard/linux/x64x11/shared/platform_configuration:libraries",
-      "//starboard/build/config/sabi",
-      ":compiler_flags",
-    ]
-  } else {
-    cflags = [ "-O2" ]
-  }
+  configs = [
+    "//starboard/linux/shared/platform_configuration",
+    "//starboard/linux/x64x11/shared/platform_configuration:libraries",
+    "//starboard/build/config/sabi",
+    ":compiler_flags",
+  ]
 }
diff --git a/starboard/linux/x64x11/gcc/6.3/toolchain/BUILD.gn b/starboard/linux/x64x11/gcc/6.3/toolchain/BUILD.gn
index 52826ea..c962435 100644
--- a/starboard/linux/x64x11/gcc/6.3/toolchain/BUILD.gn
+++ b/starboard/linux/x64x11/gcc/6.3/toolchain/BUILD.gn
@@ -17,21 +17,6 @@
 # Directory for GCC 6.3 if it is installed as a system dependency.
 _default_gcc_6_3_bin_dir = "/usr/bin"
 
-overridable_gcc_toolchain("host") {
-  cc = "${_default_gcc_6_3_bin_dir}/gcc-6"
-  cxx = "${_default_gcc_6_3_bin_dir}/g++-6"
-  ld = cxx
-
-  # We use whatever 'ar' resolves to.
-  ar = "ar"
-
-  toolchain_args = {
-    target_os = "linux"
-    target_cpu = "x64"
-    is_clang = false
-  }
-}
-
 overridable_gcc_toolchain("target") {
   cc = "${_default_gcc_6_3_bin_dir}/gcc-6"
   cxx = "${_default_gcc_6_3_bin_dir}/g++-6"
diff --git a/starboard/linux/x64x11/main.cc b/starboard/linux/x64x11/main.cc
index 56e1caa..e434f37 100644
--- a/starboard/linux/x64x11/main.cc
+++ b/starboard/linux/x64x11/main.cc
@@ -15,10 +15,13 @@
 #include <time.h>
 
 #include "starboard/configuration.h"
+#include "starboard/event.h"
 #include "starboard/shared/signal/crash_signals.h"
 #include "starboard/shared/signal/debug_signals.h"
 #include "starboard/shared/signal/suspend_signals.h"
 #if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/common/paths.h"
+#include "starboard/elf_loader/elf_loader_constants.h"
 #include "starboard/shared/starboard/command_line.h"
 #include "starboard/shared/starboard/starboard_switches.h"
 #endif
@@ -34,14 +37,25 @@
   starboard::shared::signal::InstallSuspendSignalHandlers();
 
 #if SB_IS(EVERGREEN_COMPATIBLE)
-  if (starboard::shared::starboard::CommandLine(argc, argv)
-          .HasSwitch(starboard::shared::starboard::kStartHandlerAtLaunch) &&
-      !starboard::shared::starboard::CommandLine(argc, argv)
-           .HasSwitch(starboard::shared::starboard::kStartHandlerAtCrash)) {
-    third_party::crashpad::wrapper::InstallCrashpadHandler(false);
-  } else {
-    third_party::crashpad::wrapper::InstallCrashpadHandler(true);
+  auto command_line = starboard::shared::starboard::CommandLine(argc, argv);
+  auto evergreen_content_path =
+      command_line.GetSwitchValue(starboard::elf_loader::kEvergreenContent);
+  std::string ca_certificates_path =
+      evergreen_content_path.empty()
+          ? starboard::common::GetCACertificatesPath()
+          : starboard::common::GetCACertificatesPath(evergreen_content_path);
+  if (ca_certificates_path.empty()) {
+    SB_LOG(ERROR) << "Failed to get CA certificates path";
+    return 1;
   }
+
+  bool start_handler_at_crash =
+      command_line.HasSwitch(
+          starboard::shared::starboard::kStartHandlerAtCrash) ||
+      !command_line.HasSwitch(
+          starboard::shared::starboard::kStartHandlerAtLaunch);
+  third_party::crashpad::wrapper::InstallCrashpadHandler(start_handler_at_crash,
+                                                         ca_certificates_path);
 #endif
 
 #if SB_HAS_QUIRK(BACKTRACE_DLOPEN_BUG)
@@ -50,6 +64,9 @@
   SbLogRawDumpStack(3);
 #endif
 
+#if SB_MODULAR_BUILD
+  return SbRunStarboardMain(argc, argv, SbEventHandle);
+#else
   starboard::shared::x11::ApplicationX11 application;
   int result = 0;
   {
@@ -60,4 +77,20 @@
   starboard::shared::signal::UninstallDebugSignalHandlers();
   starboard::shared::signal::UninstallCrashSignalHandlers();
   return result;
+#endif  // SB_MODULAR_BUILD
 }
+
+#if SB_MODULAR_BUILD
+int SbRunStarboardMain(int argc, char** argv, SbEventHandleCallback callback) {
+  starboard::shared::x11::ApplicationX11 application(callback);
+  int result = 0;
+  {
+    starboard::shared::starboard::LinkReceiver receiver(&application);
+    result = application.Run(argc, argv);
+  }
+  starboard::shared::signal::UninstallSuspendSignalHandlers();
+  starboard::shared::signal::UninstallDebugSignalHandlers();
+  starboard::shared::signal::UninstallCrashSignalHandlers();
+  return result;
+}
+#endif  // SB_MODULAR_BUILD
diff --git a/starboard/linux/x64x11/shared/platform_configuration/BUILD.gn b/starboard/linux/x64x11/shared/platform_configuration/BUILD.gn
index d4c0954..7aa81ed 100644
--- a/starboard/linux/x64x11/shared/platform_configuration/BUILD.gn
+++ b/starboard/linux/x64x11/shared/platform_configuration/BUILD.gn
@@ -13,16 +13,12 @@
 # limitations under the License.
 
 config("platform_configuration") {
-  if (current_toolchain == default_toolchain) {
-    configs = [
-      ":libraries",
-      "//starboard/linux/shared/platform_configuration",
-      "//starboard/linux/shared/platform_configuration:compiler_flags",
-      ":linker_flags",
-    ]
-  } else {
-    cflags = [ "-O2" ]
-  }
+  configs = [
+    ":libraries",
+    "//starboard/linux/shared/platform_configuration",
+    "//starboard/linux/shared/platform_configuration:compiler_flags",
+    ":linker_flags",
+  ]
 }
 
 config("libraries") {
diff --git a/starboard/linux/x64x11/skia/toolchain/BUILD.gn b/starboard/linux/x64x11/skia/toolchain/BUILD.gn
index c1469cb..f705916 100644
--- a/starboard/linux/x64x11/skia/toolchain/BUILD.gn
+++ b/starboard/linux/x64x11/skia/toolchain/BUILD.gn
@@ -15,15 +15,15 @@
 import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
 
-clang_toolchain("host") {
+clang_toolchain("target") {
   clang_base_path = clang_base_path
+}
 
+clang_toolchain("native_target") {
   toolchain_args = {
+    is_starboard = false
+    is_native_target_build = true
     current_os = "linux"
     current_cpu = "x64"
   }
 }
-
-clang_toolchain("target") {
-  clang_base_path = clang_base_path
-}
diff --git a/starboard/linux/x64x11/toolchain/BUILD.gn b/starboard/linux/x64x11/toolchain/BUILD.gn
index 9e483e1..304fda6 100644
--- a/starboard/linux/x64x11/toolchain/BUILD.gn
+++ b/starboard/linux/x64x11/toolchain/BUILD.gn
@@ -15,15 +15,15 @@
 import("//build/config/clang/clang.gni")
 import("//starboard/shared/toolchain/overridable_gcc_toolchain.gni")
 
-overridable_clang_toolchain("host") {
+overridable_clang_toolchain("target") {
   clang_base_path = clang_base_path
+}
 
+clang_toolchain("native_target") {
   toolchain_args = {
+    is_starboard = false
+    is_native_target_build = true
     current_os = "linux"
     current_cpu = "x64"
   }
 }
-
-overridable_clang_toolchain("target") {
-  clang_base_path = clang_base_path
-}
diff --git a/starboard/loader_app/BUILD.gn b/starboard/loader_app/BUILD.gn
index 71c898c..62aba14 100644
--- a/starboard/loader_app/BUILD.gn
+++ b/starboard/loader_app/BUILD.gn
@@ -27,6 +27,7 @@
     ":memory_tracker_thread",
     ":slot_management",
     "//starboard",
+    "//starboard/elf_loader:constants",
     "//starboard/elf_loader:evergreen_info",
     "//starboard/elf_loader:sabi_string",
   ]
@@ -63,6 +64,28 @@
     outputs =
         [ "$sb_static_contents_output_data_dir/app/cobalt/lib/libcobalt.so" ]
   }
+  copy("copy_loader_app_manifest") {
+    install_content = true
+    if (target_cpu == "arm" && arm_float_abi == "softfp") {
+      sources = [ "$root_out_dir/../evergreen-$target_cpu-${arm_float_abi}_$build_type/manifest.json" ]
+    } else if (target_cpu == "arm64") {
+      sources =
+          [ "$root_out_dir/../evergreen-$target_cpu_$build_type/manifest.json" ]
+    } else {
+      sources = []
+    }
+    outputs = [ "$sb_static_contents_output_data_dir/app/cobalt/manifest.json" ]
+  }
+
+  # This trick to name the Crashpad handler executable as a shared library, so
+  # that the Package Manager extracts it to the file system on install, is
+  # borrowed from Chrome on Android.
+  copy("copy_crashpad_handler_named_as_so") {
+    install_content = true
+    sources = [ "$root_out_dir/native_target/crashpad_handler" ]
+    outputs = [ "$root_out_dir/libcrashpad_handler.so" ]
+    deps = [ "//third_party/crashpad/handler:crashpad_handler(//starboard/android/arm/toolchain:native_target)" ]
+  }
 }
 
 target(final_executable_type, "loader_app") {
@@ -85,12 +108,16 @@
     ]
     if (sb_is_evergreen_compatible && sb_evergreen_compatible_package) {
       data_deps += [
+        ":copy_crashpad_handler_named_as_so",
         ":copy_loader_app_content",
         ":copy_loader_app_lib",
+        ":copy_loader_app_manifest",
       ]
       deps += [
+        ":copy_crashpad_handler_named_as_so",
         ":copy_loader_app_content",
         ":copy_loader_app_lib",
+        ":copy_loader_app_manifest",
       ]
     }
   }
@@ -209,12 +236,9 @@
 
   deps = [
     ":installation_store_proto",
+    ":pending_restart",
     "//starboard",
   ]
-
-  if (!sb_evergreen_compatible_enable_lite) {
-    deps += [ ":pending_restart" ]
-  }
 }
 
 target(gtest_target_type, "installation_manager_test") {
@@ -222,13 +246,12 @@
   sources = [
     "//starboard/common/test_main.cc",
     "installation_manager_test.cc",
+    "pending_restart_test.cc",
   ]
-  if (sb_evergreen_compatible_enable_lite) {
-    sources += [ "pending_restart_test.cc" ]
-  }
   deps = [
     ":installation_manager",
     ":installation_store_proto",
+    ":pending_restart",
     "//testing/gmock",
     "//testing/gtest",
   ]
@@ -245,6 +268,7 @@
     ":installation_manager",
     "//starboard",
     "//starboard/elf_loader",
+    "//starboard/elf_loader:constants",
     "//starboard/elf_loader:sabi_string",
   ]
 
diff --git a/starboard/log.h b/starboard/log.h
index e31c73c..46b3aeb 100644
--- a/starboard/log.h
+++ b/starboard/log.h
@@ -47,7 +47,8 @@
 //   that passing |kSbLogPriorityFatal| does not terminate the program. Such a
 //   policy must be enforced at the application level. In fact, |priority| may
 //   be completely ignored on many platforms.
-// |message|: The message to be logged. No formatting is required to be done
+// |message|: The message to be logged. Must not be NULL. No formatting is
+// required to be done
 //   on the value, including newline termination. That said, platforms can
 //   adjust the message to be more suitable for their output method by
 //   wrapping the text, stripping unprintable characters, etc.
@@ -57,7 +58,7 @@
 // from an asynchronous signal handler (e.g. a |SIGSEGV| handler). It should not
 // do any additional formatting.
 //
-// |message|: The message to be logged.
+// |message|: The message to be logged. Must not be NULL.
 SB_EXPORT void SbLogRaw(const char* message);
 
 // Dumps the stack of the current thread to the log in an async-signal-safe
@@ -79,7 +80,7 @@
 // Inline wrapper of SbLogFormat to convert from ellipsis to va_args.
 static SB_C_INLINE void SbLogRawFormatF(const char* format, ...)
     SB_PRINTF_FORMAT(1, 2);
-void SbLogRawFormatF(const char* format, ...) {
+static SB_C_INLINE void SbLogRawFormatF(const char* format, ...) {
   va_list args;
   va_start(args, format);
   SbLogRawFormat(format, args);
@@ -94,7 +95,7 @@
 // Inline wrapper of SbLogFormat that converts from ellipsis to va_args.
 static SB_C_INLINE void SbLogFormatF(const char* format, ...)
     SB_PRINTF_FORMAT(1, 2);
-void SbLogFormatF(const char* format, ...) {
+static SB_C_INLINE void SbLogFormatF(const char* format, ...) {
   va_list args;
   va_start(args, format);
   SbLogFormat(format, args);
diff --git a/starboard/media.h b/starboard/media.h
index 17d637d..0a27419 100644
--- a/starboard/media.h
+++ b/starboard/media.h
@@ -68,6 +68,9 @@
   kSbMediaAudioCodecFlac,
   kSbMediaAudioCodecPcm,
 #endif  // SB_API_VERSION >= 14
+#if SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
+  kSbMediaAudioCodecIamf,
+#endif  // SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
 } SbMediaAudioCodec;
 
 // Indicates how confident the device is that it can play media resources of the
@@ -379,6 +382,65 @@
   float custom_primary_matrix[12];
 } SbMediaColorMetadata;
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+// The set of information required by the decoder or player for each video
+// stream.
+typedef struct SbMediaVideoStreamInfo {
+  // The video codec of this sample.
+  SbMediaVideoCodec codec;
+
+  // The mime of the video stream when |codec| isn't kSbMediaVideoCodecNone.  It
+  // may point to an empty string if the mime is not available, and it can only
+  // be set to NULL when |codec| is kSbMediaVideoCodecNone.
+  const char* mime;
+
+  // Indicates the max video capabilities required. The web app will not provide
+  // a video stream exceeding the maximums described by this parameter. Allows
+  // the platform to optimize playback pipeline for low quality video streams if
+  // it knows that it will never adapt to higher quality streams. The string
+  // uses the same format as the string passed in to
+  // SbMediaCanPlayMimeAndKeySystem(), for example, when it is set to
+  // "width=1920; height=1080; framerate=15;", the video will never adapt to
+  // resolution higher than 1920x1080 or frame per second higher than 15 fps.
+  // When the maximums are unknown, this will be set to an empty string.  It can
+  // only be set to NULL when |codec| is kSbMediaVideoCodecNone.
+  const char* max_video_capabilities;
+
+  // The frame width of this sample, in pixels. Also could be parsed from the
+  // Sequence Parameter Set (SPS) NAL Unit. Frame dimensions must only change on
+  // key frames, but may change on any key frame.
+  int frame_width;
+
+  // The frame height of this sample, in pixels. Also could be parsed from the
+  // Sequence Parameter Set (SPS) NAL Unit. Frame dimensions must only change on
+  // key frames, but may change on any key frame.
+  int frame_height;
+
+  // HDR metadata common for HDR10 and WebM/VP9-based HDR formats as
+  // well as the Color Space, and Color elements: MatrixCoefficients,
+  // BitsPerChannel, ChromaSubsamplingHorz, ChromaSubsamplingVert,
+  // CbSubsamplingHorz, CbSubsamplingVert, ChromaSitingHorz,
+  // ChromaSitingVert, Range, TransferCharacteristics, and Primaries
+  // described here: https://matroska.org/technical/specs/index.html .
+  // This will only be specified on frames where the HDR metadata and
+  // color / color space might have changed (e.g. keyframes).
+  SbMediaColorMetadata color_metadata;
+} SbMediaVideoStreamInfo;
+
+// The set of information required by the decoder or player for each video
+// sample.
+typedef struct SbMediaVideoSampleInfo {
+  // The set of information of the video stream associated with this sample.
+  SbMediaVideoStreamInfo stream_info;
+
+  // Indicates whether the associated sample is a key frame (I-frame). Avc video
+  // key frames must always start with SPS and PPS NAL units.
+  bool is_key_frame;
+} SbMediaVideoSampleInfo;
+
+#else  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
 // The set of information required by the decoder or player for each video
 // sample.
 typedef struct SbMediaVideoSampleInfo {
@@ -425,7 +487,9 @@
   // This will only be specified on frames where the HDR metadata and
   // color / color space might have changed (e.g. keyframes).
   SbMediaColorMetadata color_metadata;
-} SbMediaVideoSampleInfo;
+} SbMediaVideoSampleInfo, SbMediaVideoStreamInfo;
+
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 
 // A structure describing the audio configuration parameters of a single audio
 // output.
@@ -450,6 +514,47 @@
   int number_of_channels;
 } SbMediaAudioConfiguration;
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+// The set of information required by the decoder or player for each audio
+// stream.
+typedef struct SbMediaAudioStreamInfo {
+  // The audio codec of this sample.
+  SbMediaAudioCodec codec;
+
+  // The mime of the audio stream when |codec| isn't kSbMediaAudioCodecNone.  It
+  // may point to an empty string if the mime is not available, and it can only
+  // be set to NULL when |codec| is kSbMediaAudioCodecNone.
+  const char* mime;
+
+  // The number of audio channels in this format. |1| for mono, |2| for stereo.
+  uint16_t number_of_channels;
+
+  // The sampling rate.
+  uint32_t samples_per_second;
+
+  // The bit depth for the stream this represents, e.g. |8| or |16|.
+  uint16_t bits_per_sample;
+
+  // The size, in bytes, of the audio_specific_config.
+  uint16_t audio_specific_config_size;
+
+  // The AudioSpecificConfig, as specified in ISO/IEC-14496-3, section 1.6.2.1:
+  // http://read.pudn.com/downloads98/doc/comm/401153/14496/ISO_IEC_14496-3%20Part%203%20Audio/C036083E_SUB1.PDF
+  const void* audio_specific_config;
+} SbMediaAudioStreamInfo;
+
+// The set of information required by the decoder or player for each audio
+// sample.
+typedef struct SbMediaAudioSampleInfo {
+  // The set of information of the video stream associated with this sample.
+  SbMediaAudioStreamInfo stream_info;
+  SbTime discarded_duration_from_front;
+  SbTime discarded_duration_from_back;
+} SbMediaAudioSampleInfo;
+
+#else  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
 // An audio sample info, which is a description of a given audio sample.  This
 // acts as a set of instructions to the audio decoder.
 //
@@ -490,7 +595,9 @@
   // The AudioSpecificConfig, as specified in ISO/IEC-14496-3, section 1.6.2.1:
   // http://read.pudn.com/downloads98/doc/comm/401153/14496/ISO_IEC_14496-3%20Part%203%20Audio/C036083E_SUB1.PDF
   const void* audio_specific_config;
-} SbMediaAudioSampleInfo;
+} SbMediaAudioSampleInfo, SbMediaAudioStreamInfo;
+
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 
 // --- Functions -------------------------------------------------------------
 
diff --git a/starboard/nplb/BUILD.gn b/starboard/nplb/BUILD.gn
index 7b44620..2f6dd33 100644
--- a/starboard/nplb/BUILD.gn
+++ b/starboard/nplb/BUILD.gn
@@ -289,6 +289,7 @@
   }
 
   data_deps = [
+    "//cobalt/network:copy_ssl_certificates",
     "//starboard/nplb/testdata/file_tests:nplb_file_tests_data",
     "//starboard/shared/starboard/player:player_download_test_data",
     "//third_party/icu:icudata",
diff --git a/starboard/nplb/atomic_base_test.cc b/starboard/nplb/atomic_base_test.cc
index 93114f0..2034f91 100644
--- a/starboard/nplb/atomic_base_test.cc
+++ b/starboard/nplb/atomic_base_test.cc
@@ -16,7 +16,7 @@
 #include <numeric>
 #include <vector>
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/mutex.h"
 #include "starboard/configuration.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/starboard/nplb/atomic_test.cc b/starboard/nplb/atomic_test.cc
index 99cdc0b..ad63f9a 100644
--- a/starboard/nplb/atomic_test.cc
+++ b/starboard/nplb/atomic_test.cc
@@ -14,7 +14,7 @@
 
 // Adapted from base's atomicops_unittest.
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/memory.h"
 #include "starboard/thread.h"
 #include "starboard/time.h"
@@ -45,19 +45,19 @@
   virtual ~AdvancedSbAtomicTest() {}
 };
 
-typedef testing::Types<
-    SbAtomic8,
-    SbAtomic32,
+typedef testing::Types<SbAtomic8,
+                       SbAtomic32,
 #if SB_HAS(64_BIT_ATOMICS)
-    SbAtomic64,
+                       SbAtomic64,
 #endif
-    SbAtomicPtr>
+                       SbAtomicPtr>
     BasicSbAtomicTestTypes;
 typedef testing::Types<SbAtomic32,
 #if SB_HAS(64_BIT_ATOMICS)
                        SbAtomic64,
 #endif
-                       SbAtomicPtr> AdvancedSbAtomicTestTypes;
+                       SbAtomicPtr>
+    AdvancedSbAtomicTestTypes;
 
 TYPED_TEST_CASE(BasicSbAtomicTest, BasicSbAtomicTestTypes);
 
@@ -394,8 +394,7 @@
     // Ensure that exactly one thread initialized the data.
     bool match = false;
     for (int i = 0; i < kNumThreads; ++i) {
-      if (memcmp(target_data, data + i * kDataPerThread,
-                 kDataPerThread) == 0) {
+      if (memcmp(target_data, data + i * kDataPerThread, kDataPerThread) == 0) {
         match = true;
         break;
       }
diff --git a/starboard/nplb/drm_create_system_test.cc b/starboard/nplb/drm_create_system_test.cc
index bb548d0..e324fdd 100644
--- a/starboard/nplb/drm_create_system_test.cc
+++ b/starboard/nplb/drm_create_system_test.cc
@@ -35,7 +35,10 @@
                     << " is NOT valid.";
     }
     any_supported_key_systems |= SbDrmSystemIsValid(drm_system);
-    SbDrmDestroySystem(drm_system);
+
+    if (SbDrmSystemIsValid(drm_system)) {
+      SbDrmDestroySystem(drm_system);
+    }
   }
   EXPECT_TRUE(any_supported_key_systems) << " no DRM key systems supported";
 }
@@ -50,7 +53,6 @@
           DummySessionKeyStatusesChangedFunc, DummyServerCertificateUpdatedFunc,
           DummySessionClosedFunc);
       EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
-      SbDrmDestroySystem(drm_system);
     }
     {
       SbDrmSystem drm_system = SbDrmCreateSystem(
@@ -58,7 +60,6 @@
           NULL /*session_updated_func */, DummySessionKeyStatusesChangedFunc,
           DummyServerCertificateUpdatedFunc, DummySessionClosedFunc);
       EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
-      SbDrmDestroySystem(drm_system);
     }
     {
       SbDrmSystem drm_system = SbDrmCreateSystem(
@@ -66,7 +67,6 @@
           DummySessionUpdatedFunc, NULL /* session_key_statuses_changed_func */,
           DummyServerCertificateUpdatedFunc, DummySessionClosedFunc);
       EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
-      SbDrmDestroySystem(drm_system);
     }
     {
       SbDrmSystem drm_system = SbDrmCreateSystem(
@@ -74,7 +74,6 @@
           DummySessionUpdatedFunc, DummySessionKeyStatusesChangedFunc,
           NULL /* server_certificate_updated_func */, DummySessionClosedFunc);
       EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
-      SbDrmDestroySystem(drm_system);
     }
     {
       SbDrmSystem drm_system = SbDrmCreateSystem(
@@ -82,7 +81,6 @@
           DummySessionUpdatedFunc, DummySessionKeyStatusesChangedFunc,
           DummyServerCertificateUpdatedFunc, NULL /* session_closed_func */);
       EXPECT_FALSE(SbDrmSystemIsValid(drm_system));
-      SbDrmDestroySystem(drm_system);
     }
   }
 }
diff --git a/starboard/nplb/drm_get_metrics_test.cc b/starboard/nplb/drm_get_metrics_test.cc
index 484c1bc..7716641 100644
--- a/starboard/nplb/drm_get_metrics_test.cc
+++ b/starboard/nplb/drm_get_metrics_test.cc
@@ -40,20 +40,6 @@
   }
 }
 
-TEST(SbDrmGetMetricsTest, RainyDay) {
-  int size = -1;
-  ASSERT_FALSE(SbDrmGetMetrics(kSbDrmSystemInvalid, &size));
-
-  for (auto key_system : kKeySystems) {
-    SbDrmSystem drm_system = CreateDummyDrmSystem(key_system);
-    if (!SbDrmSystemIsValid(drm_system)) {
-      continue;
-    }
-    EXPECT_EQ(SbDrmGetMetrics(drm_system, nullptr), nullptr);
-    SbDrmDestroySystem(drm_system);
-  }
-}
-
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/starboard/nplb/drm_session_test.cc b/starboard/nplb/drm_session_test.cc
index 0e911a3..9069367 100644
--- a/starboard/nplb/drm_session_test.cc
+++ b/starboard/nplb/drm_session_test.cc
@@ -166,7 +166,9 @@
 }
 
 void SbDrmSessionTest::TearDown() {
-  SbDrmDestroySystem(drm_system_);
+  if (SbDrmSystemIsValid(drm_system_)) {
+    SbDrmDestroySystem(drm_system_);
+  }
 }
 
 void SbDrmSessionTest::CheckSessionUpdateRequestGeneratedCallback(
diff --git a/starboard/nplb/media_set_audio_write_duration_test.cc b/starboard/nplb/media_set_audio_write_duration_test.cc
index 84ff9b2..09490a3 100644
--- a/starboard/nplb/media_set_audio_write_duration_test.cc
+++ b/starboard/nplb/media_set_audio_write_duration_test.cc
@@ -30,8 +30,8 @@
 namespace nplb {
 namespace {
 
-using ::starboard::testing::FakeGraphicsContextProvider;
 using ::shared::starboard::player::video_dmp::VideoDmpReader;
+using ::starboard::testing::FakeGraphicsContextProvider;
 using ::testing::ValuesIn;
 
 const SbTime kDuration = kSbTimeSecond / 2;
@@ -57,24 +57,18 @@
     }
 
     // Check if we're about to input too far beyond the current playback time.
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+    SbPlayerInfo info;
+    SbPlayerGetInfo(pending_decoder_status_->player, &info);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
     SbPlayerInfo2 info;
     SbPlayerGetInfo2(pending_decoder_status_->player, &info);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
     if ((last_input_timestamp_ - info.current_media_timestamp) > kDuration) {
       // Postpone writing samples.
       return;
     }
 
-    SbPlayerSampleInfo player_sample_info =
-        dmp_reader_.GetPlayerSampleInfo(kSbMediaTypeAudio, index_++);
-
-    SbPlayerSampleInfo sample_info = {};
-    sample_info.buffer = player_sample_info.buffer;
-    sample_info.buffer_size = player_sample_info.buffer_size;
-    sample_info.timestamp = player_sample_info.timestamp;
-    sample_info.drm_info = NULL;
-    sample_info.type = kSbMediaTypeAudio;
-    sample_info.audio_sample_info = dmp_reader_.audio_sample_info();
-
     SbPlayer player = pending_decoder_status_->player;
     SbMediaType type = pending_decoder_status_->type;
     int ticket = pending_decoder_status_->ticket;
@@ -83,7 +77,10 @@
       pending_decoder_status_ = nullopt;
     }
 
-    SbPlayerWriteSample2(player, kSbMediaTypeAudio, &sample_info, 1);
+    CallSbPlayerWriteSamples(player, kSbMediaTypeAudio, dmp_reader_.get(),
+                             index_, 1);
+    ++index_;
+
     last_input_timestamp_ = player_sample_info.timestamp;
   }
 
@@ -191,19 +188,30 @@
   WaitForPlayerState(kSbPlayerStateInitialized);
 
   // Seek to preroll.
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerSeek(player, first_input_timestamp_, /* ticket */ 1);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbPlayerSeek2(player, first_input_timestamp_, /* ticket */ 1);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 
   WaitForPlayerState(kSbPlayerStatePresenting);
 
   // Wait until the playback time is > 0.
   const SbTime kMaxWaitTime = 5 * kSbTimeSecond;
   SbTime start_of_wait = SbTimeGetMonotonicNow();
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerInfo info = {};
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbPlayerInfo2 info = {};
-
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   while (SbTimeGetMonotonicNow() - start_of_wait < kMaxWaitTime &&
          info.current_media_timestamp == 0) {
     SbThreadSleep(kSbTimeMillisecond * 500);
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+    SbPlayerGetInfo(player, &info);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
     SbPlayerGetInfo2(player, &info);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   }
 
   EXPECT_GT(info.current_media_timestamp, 0);
@@ -224,8 +232,11 @@
   WaitForPlayerState(kSbPlayerStateInitialized);
 
   // Seek to preroll.
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerSeek(player, first_input_timestamp_, /* ticket */ 1);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbPlayerSeek2(player, first_input_timestamp_, /* ticket */ 1);
-
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   WaitForPlayerState(kSbPlayerStatePresenting);
 
   // Wait for the player to play far enough. It may not play all the way to
@@ -233,11 +244,20 @@
   SbTime min_ending_playback_time = total_duration_ - kDuration;
   SbTime start_of_wait = SbTimeGetMonotonicNow();
   const SbTime kMaxWaitTime = total_duration_ + 5 * kSbTimeSecond;
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerInfo info;
+  SbPlayerGetInfo(player, &info);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbPlayerInfo2 info;
   SbPlayerGetInfo2(player, &info);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   while (info.current_media_timestamp < min_ending_playback_time &&
          (SbTimeGetMonotonicNow() - start_of_wait) < kMaxWaitTime) {
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+    SbPlayerGetInfo(player, &info);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
     SbPlayerGetInfo2(player, &info);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
     SbThreadSleep(kSmallWaitInterval);
     TryToWritePendingSample();
   }
diff --git a/starboard/nplb/memory_reallocate_test.cc b/starboard/nplb/memory_reallocate_test.cc
index 1eac476..e293af5 100644
--- a/starboard/nplb/memory_reallocate_test.cc
+++ b/starboard/nplb/memory_reallocate_test.cc
@@ -107,7 +107,9 @@
   SbMemoryDeallocate(memory);
 }
 
-TEST(SbMemoryReallocateTest, ReallocatestoZero) {
+// Tests unspecified behavior, currently not stable.
+// Should be deleted or fixed.
+TEST(SbMemoryReallocateTest, DISABLED_ReallocatestoZero) {
   void* memory = SbMemoryAllocate(kSize);
   ASSERT_NE(static_cast<void*>(NULL), memory);
   memory = SbMemoryReallocate(memory, 0);
diff --git a/starboard/nplb/mutex_acquire_test.cc b/starboard/nplb/mutex_acquire_test.cc
index 81ad198..f8d78a4 100644
--- a/starboard/nplb/mutex_acquire_test.cc
+++ b/starboard/nplb/mutex_acquire_test.cc
@@ -91,6 +91,15 @@
   EXPECT_TRUE(SbMutexDestroy(&mutex));
 }
 
+TEST(SbMutexAcquireTest, RainyDayAcquireNull) {
+  SbMutexResult result = SbMutexAcquire(NULL);
+  EXPECT_EQ(kSbMutexDestroyed, result);
+}
+
+TEST(SbMutexAcquireTest, RainyDayReleaseNull) {
+  EXPECT_FALSE(SbMutexRelease(NULL));
+}
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/starboard/nplb/mutex_acquire_try_test.cc b/starboard/nplb/mutex_acquire_try_test.cc
index 4c4b471..7d78855 100644
--- a/starboard/nplb/mutex_acquire_try_test.cc
+++ b/starboard/nplb/mutex_acquire_try_test.cc
@@ -74,6 +74,11 @@
   EXPECT_TRUE(SbMutexDestroy(&mutex));
 }
 
+TEST(SbMutexAcquireTest, RainyDayAcquireTryNull) {
+  SbMutexResult result = SbMutexAcquireTry(NULL);
+  EXPECT_EQ(kSbMutexDestroyed, result);
+}
+
 }  // namespace
 }  // namespace nplb
 }  // namespace starboard
diff --git a/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn b/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
index f22104c..a80482b 100644
--- a/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
+++ b/starboard/nplb/nplb_evergreen_compat_tests/BUILD.gn
@@ -21,21 +21,18 @@
     "crashpad_config_test.cc",
     "executable_memory_test.cc",
     "fonts_test.cc",
+    "max_file_name_test.cc",
     "sabi_test.cc",
+    "storage_test.cc",
   ]
 
-  if (!sb_evergreen_compatible_enable_lite) {
-    sources += [
-      "icu_test.cc",
-      "max_file_name_test.cc",
-      "storage_test.cc",
-    ]
-  }
-
   public_deps = [
     "//starboard",
     "//testing/gmock",
   ]
 
-  data_deps = [ "//third_party/icu:icudata" ]
+  data_deps = [
+    "//cobalt/network:copy_ssl_certificates",
+    "//third_party/icu:icudata",
+  ]
 }
diff --git a/starboard/nplb/nplb_evergreen_compat_tests/icu_test.cc b/starboard/nplb/nplb_evergreen_compat_tests/icu_test.cc
deleted file mode 100644
index 53a99f6..0000000
--- a/starboard/nplb/nplb_evergreen_compat_tests/icu_test.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2020 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <string>
-#include <vector>
-
-#include "starboard/common/log.h"
-#include "starboard/configuration.h"
-#include "starboard/file.h"
-#include "starboard/nplb/nplb_evergreen_compat_tests/checks.h"
-#include "starboard/system.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if SB_IS(EVERGREEN_COMPATIBLE)
-
-namespace starboard {
-namespace nplb {
-namespace nplb_evergreen_compat_tests {
-
-namespace {
-
-const char kDirName[] = "icu";
-
-TEST(IcuTest, VerifyIcuDirectory) {
-  std::vector<char> storage_dir(kSbFileMaxPath);
-  ASSERT_TRUE(SbSystemGetPath(kSbSystemPathStorageDirectory, storage_dir.data(),
-                              kSbFileMaxPath));
-
-  std::string icu_path = storage_dir.data();
-  icu_path += kSbFileSepString;
-  icu_path += kDirName;
-  ASSERT_TRUE(SbFileExists(icu_path.c_str()));
-  SbFileInfo info;
-  ASSERT_TRUE(SbFileGetPathInfo(icu_path.c_str(), &info));
-  ASSERT_TRUE(info.is_directory);
-}
-
-}  // namespace
-}  // namespace nplb_evergreen_compat_tests
-}  // namespace nplb
-}  // namespace starboard
-
-#endif  // SB_IS(EVERGREEN_COMPATIBLE)
diff --git a/starboard/nplb/player_create_test.cc b/starboard/nplb/player_create_test.cc
index 061a323..54161d6 100644
--- a/starboard/nplb/player_create_test.cc
+++ b/starboard/nplb/player_create_test.cc
@@ -30,7 +30,7 @@
 using ::testing::Values;
 
 class SbPlayerTest : public ::testing::TestWithParam<SbPlayerOutputMode> {
- public:
+ protected:
   SbPlayerTest() : output_mode_(GetParam()) {}
 
   void GetCurrentFrameIfSupported(SbPlayer player) {
@@ -48,15 +48,13 @@
 #endif  // SB_HAS(GLES2)
   }
 
- protected:
   FakeGraphicsContextProvider fake_graphics_context_provider_;
 
   SbPlayerOutputMode output_mode_;
 };
 
 TEST_P(SbPlayerTest, SunnyDay) {
-  SbMediaAudioSampleInfo audio_sample_info =
-      CreateAudioSampleInfo(kSbMediaAudioCodecAac);
+  auto audio_stream_info = CreateAudioStreamInfo(kSbMediaAudioCodecAac);
 
   if (!IsOutputModeSupported(output_mode_, kSbMediaAudioCodecAac,
                              kSbMediaVideoCodecH264)) {
@@ -65,7 +63,7 @@
 
   SbPlayer player = CallSbPlayerCreate(
       fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
-      kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info,
+      kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_stream_info,
       "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
       DummyDecoderStatusFunc, DummyPlayerStatusFunc, DummyErrorFunc,
       NULL /* context */, output_mode_,
@@ -78,8 +76,7 @@
 }
 
 TEST_P(SbPlayerTest, NullCallbacks) {
-  SbMediaAudioSampleInfo audio_sample_info =
-      CreateAudioSampleInfo(kSbMediaAudioCodecAac);
+  auto audio_stream_info = CreateAudioStreamInfo(kSbMediaAudioCodecAac);
 
   if (!IsOutputModeSupported(output_mode_, kSbMediaAudioCodecAac,
                              kSbMediaVideoCodecH264)) {
@@ -89,7 +86,7 @@
   {
     SbPlayer player = CallSbPlayerCreate(
         fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
-        kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info,
+        kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_stream_info,
         "" /* max_video_capabilities */, NULL /* deallocate_sample_func */,
         DummyDecoderStatusFunc, DummyPlayerStatusFunc, DummyErrorFunc,
         NULL /* context */, output_mode_,
@@ -102,7 +99,7 @@
   {
     SbPlayer player = CallSbPlayerCreate(
         fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
-        kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info,
+        kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_stream_info,
         "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
         NULL /* decoder_status_func */, DummyPlayerStatusFunc, DummyErrorFunc,
         NULL /* context */, output_mode_,
@@ -115,7 +112,7 @@
   {
     SbPlayer player = CallSbPlayerCreate(
         fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
-        kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info,
+        kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_stream_info,
         "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
         DummyDecoderStatusFunc, NULL /*status_func */, DummyErrorFunc,
         NULL /* context */, output_mode_,
@@ -128,7 +125,7 @@
   {
     SbPlayer player = CallSbPlayerCreate(
         fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
-        kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info, "",
+        kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_stream_info, "",
         DummyDeallocateSampleFunc, DummyDecoderStatusFunc,
         DummyPlayerStatusFunc, NULL /* error_func */, NULL /* context */,
         output_mode_,
@@ -147,7 +144,7 @@
 
   SbPlayer player = CallSbPlayerCreate(
       fake_graphics_context_provider_.window(), kSbMediaVideoCodecH264,
-      kSbMediaAudioCodecNone, kSbDrmSystemInvalid, NULL /* audio_sample_info */,
+      kSbMediaAudioCodecNone, kSbDrmSystemInvalid, NULL /* audio_stream_info */,
       "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
       DummyDecoderStatusFunc, DummyPlayerStatusFunc, DummyErrorFunc,
       NULL /* context */, output_mode_,
@@ -160,8 +157,7 @@
 }
 
 TEST_P(SbPlayerTest, AudioOnly) {
-  SbMediaAudioSampleInfo audio_sample_info =
-      CreateAudioSampleInfo(kSbMediaAudioCodecAac);
+  auto audio_stream_info = CreateAudioStreamInfo(kSbMediaAudioCodecAac);
 
   if (!IsOutputModeSupported(output_mode_, kSbMediaAudioCodecAac,
                              kSbMediaVideoCodecH264)) {
@@ -170,7 +166,7 @@
 
   SbPlayer player = CallSbPlayerCreate(
       fake_graphics_context_provider_.window(), kSbMediaVideoCodecNone,
-      kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_sample_info,
+      kSbMediaAudioCodecAac, kSbDrmSystemInvalid, &audio_stream_info,
       "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
       DummyDecoderStatusFunc, DummyPlayerStatusFunc, DummyErrorFunc,
       NULL /* context */, output_mode_,
@@ -183,8 +179,7 @@
 }
 
 TEST_P(SbPlayerTest, MultiPlayer) {
-  SbMediaAudioSampleInfo audio_sample_info =
-      CreateAudioSampleInfo(kSbMediaAudioCodecAac);
+  auto audio_stream_info = CreateAudioStreamInfo(kSbMediaAudioCodecAac);
   SbDrmSystem kDrmSystem = kSbDrmSystemInvalid;
 
   constexpr SbPlayerOutputMode kOutputModes[] = {
@@ -203,6 +198,9 @@
     kSbMediaAudioCodecFlac,
     kSbMediaAudioCodecPcm,
 #endif  // SB_API_VERSION >= 14
+#if SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
+    kSbMediaAudioCodecIamf,
+#endif  // SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
   };
 
   // TODO: turn this into a macro.
@@ -222,6 +220,9 @@
     case kAudioCodecs[7]:
     case kAudioCodecs[8]:
 #endif
+#if SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
+    case kAudioCodecs[9]:
+#endif  // SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
       break;
   }
 
@@ -258,10 +259,10 @@
     for (int j = 0; j < SB_ARRAY_SIZE_INT(kOutputModes); ++j) {
       for (int k = 0; k < SB_ARRAY_SIZE_INT(kAudioCodecs); ++k) {
         for (int l = 0; l < SB_ARRAY_SIZE_INT(kVideoCodecs); ++l) {
-          audio_sample_info.codec = kAudioCodecs[k];
+          audio_stream_info.codec = kAudioCodecs[k];
           created_players.push_back(CallSbPlayerCreate(
               fake_graphics_context_provider_.window(), kVideoCodecs[l],
-              kAudioCodecs[k], kSbDrmSystemInvalid, &audio_sample_info,
+              kAudioCodecs[k], kSbDrmSystemInvalid, &audio_stream_info,
               "" /* max_video_capabilities */, DummyDeallocateSampleFunc,
               DummyDecoderStatusFunc, DummyPlayerStatusFunc, DummyErrorFunc,
               NULL /* context */, kOutputModes[j],
diff --git a/starboard/nplb/player_creation_param_helpers.cc b/starboard/nplb/player_creation_param_helpers.cc
index a2f2090..4be0ad2 100644
--- a/starboard/nplb/player_creation_param_helpers.cc
+++ b/starboard/nplb/player_creation_param_helpers.cc
@@ -18,12 +18,18 @@
 
 namespace starboard {
 namespace nplb {
+namespace {
 
-SbMediaAudioSampleInfo CreateAudioSampleInfo(SbMediaAudioCodec codec) {
-  SbMediaAudioSampleInfo audio_sample_info = {};
+using shared::starboard::media::AudioStreamInfo;
+using shared::starboard::media::VideoStreamInfo;
 
-  audio_sample_info.codec = codec;
-  audio_sample_info.mime = "";
+}  // namespace
+
+AudioStreamInfo CreateAudioStreamInfo(SbMediaAudioCodec codec) {
+  AudioStreamInfo audio_stream_info = {};
+
+  audio_stream_info.codec = codec;
+  audio_stream_info.mime = "";
 
   switch (codec) {
     case kSbMediaAudioCodecNone:
@@ -31,143 +37,97 @@
     case kSbMediaAudioCodecAac: {
       static const uint8_t kAacAudioSpecificConfig[16] = {18, 16};
 
-      audio_sample_info.format_tag = 0xff;
-      audio_sample_info.number_of_channels = 2;
-      audio_sample_info.samples_per_second = 44100;
-      audio_sample_info.block_alignment = 4;
-      audio_sample_info.bits_per_sample = 16;
-      audio_sample_info.audio_specific_config = kAacAudioSpecificConfig;
-      audio_sample_info.audio_specific_config_size =
-          sizeof(kAacAudioSpecificConfig);
-      audio_sample_info.average_bytes_per_second =
-          audio_sample_info.samples_per_second *
-          audio_sample_info.number_of_channels *
-          audio_sample_info.bits_per_sample / 8;
+      audio_stream_info.number_of_channels = 2;
+      audio_stream_info.samples_per_second = 44100;
+      audio_stream_info.bits_per_sample = 16;
+      audio_stream_info.audio_specific_config.assign(
+          kAacAudioSpecificConfig,
+          kAacAudioSpecificConfig + sizeof(kAacAudioSpecificConfig));
       break;
     }
     case kSbMediaAudioCodecAc3:
     case kSbMediaAudioCodecEac3: {
-      audio_sample_info.format_tag = 0xff;
-      audio_sample_info.number_of_channels = 6;
-      audio_sample_info.samples_per_second = 48000;
-      audio_sample_info.block_alignment = 4;
-      audio_sample_info.bits_per_sample = 16;
-      audio_sample_info.audio_specific_config = nullptr;
-      audio_sample_info.audio_specific_config_size = 0;
-      audio_sample_info.average_bytes_per_second =
-          audio_sample_info.samples_per_second *
-          audio_sample_info.number_of_channels *
-          audio_sample_info.bits_per_sample / 8;
+      audio_stream_info.number_of_channels = 6;
+      audio_stream_info.samples_per_second = 48000;
+      audio_stream_info.bits_per_sample = 16;
       break;
     }
     case kSbMediaAudioCodecOpus: {
       static const uint8_t kOpusAudioSpecificConfig[19] = {
           79, 112, 117, 115, 72, 101, 97, 100, 1, 2, 56, 1, 128, 187};
 
-      audio_sample_info.format_tag = 0xff;
-      audio_sample_info.number_of_channels = 2;
-      audio_sample_info.samples_per_second = 48000;
-      audio_sample_info.block_alignment = 4;
-      audio_sample_info.bits_per_sample = 32;
-      audio_sample_info.audio_specific_config = kOpusAudioSpecificConfig;
-      audio_sample_info.audio_specific_config_size =
-          sizeof(kOpusAudioSpecificConfig);
-      audio_sample_info.average_bytes_per_second =
-          audio_sample_info.samples_per_second *
-          audio_sample_info.number_of_channels *
-          audio_sample_info.bits_per_sample / 8;
+      audio_stream_info.number_of_channels = 2;
+      audio_stream_info.samples_per_second = 48000;
+      audio_stream_info.bits_per_sample = 32;
+      audio_stream_info.audio_specific_config.assign(
+          kOpusAudioSpecificConfig,
+          kOpusAudioSpecificConfig + sizeof(kOpusAudioSpecificConfig));
       break;
     }
     case kSbMediaAudioCodecVorbis: {
       // Note that unlike the configuration of the other formats, the following
       // configuration is made up, instead of taking from a real input.
-      audio_sample_info.format_tag = 0xff;
-      audio_sample_info.number_of_channels = 2;
-      audio_sample_info.samples_per_second = 48000;
-      audio_sample_info.block_alignment = 4;
-      audio_sample_info.bits_per_sample = 16;
-      audio_sample_info.audio_specific_config = nullptr;
-      audio_sample_info.audio_specific_config_size = 0;
-      audio_sample_info.average_bytes_per_second =
-          audio_sample_info.samples_per_second *
-          audio_sample_info.number_of_channels *
-          audio_sample_info.bits_per_sample / 8;
+      audio_stream_info.number_of_channels = 2;
+      audio_stream_info.samples_per_second = 48000;
+      audio_stream_info.bits_per_sample = 16;
       break;
     }
 #if SB_API_VERSION >= 14
     case kSbMediaAudioCodecMp3: {
-      audio_sample_info.format_tag = 0xff;
-      audio_sample_info.number_of_channels = 2;
-      audio_sample_info.samples_per_second = 44100;
-      audio_sample_info.block_alignment = 4;
-      audio_sample_info.bits_per_sample = 16;
-      audio_sample_info.audio_specific_config = nullptr;
-      audio_sample_info.audio_specific_config_size = 0;
-      audio_sample_info.average_bytes_per_second =
-          audio_sample_info.samples_per_second *
-          audio_sample_info.number_of_channels *
-          audio_sample_info.bits_per_sample / 8;
+      audio_stream_info.number_of_channels = 2;
+      audio_stream_info.samples_per_second = 44100;
+      audio_stream_info.bits_per_sample = 16;
       break;
     }
     case kSbMediaAudioCodecFlac: {
-      audio_sample_info.format_tag = 0xff;
-      audio_sample_info.number_of_channels = 2;
-      audio_sample_info.samples_per_second = 44100;
-      audio_sample_info.block_alignment = 4;
-      audio_sample_info.bits_per_sample = 16;
-      audio_sample_info.audio_specific_config = nullptr;
-      audio_sample_info.audio_specific_config_size = 0;
-      audio_sample_info.average_bytes_per_second =
-          audio_sample_info.samples_per_second *
-          audio_sample_info.number_of_channels *
-          audio_sample_info.bits_per_sample / 8;
+      audio_stream_info.number_of_channels = 2;
+      audio_stream_info.samples_per_second = 44100;
+      audio_stream_info.bits_per_sample = 16;
       break;
     }
     case kSbMediaAudioCodecPcm: {
-      audio_sample_info.format_tag = 0x01;
-      audio_sample_info.number_of_channels = 2;
-      audio_sample_info.samples_per_second = 44100;
-      audio_sample_info.block_alignment = 4;
-      audio_sample_info.bits_per_sample = 32;
-      audio_sample_info.audio_specific_config = nullptr;
-      audio_sample_info.audio_specific_config_size = 0;
-      audio_sample_info.average_bytes_per_second =
-          audio_sample_info.samples_per_second *
-          audio_sample_info.number_of_channels *
-          audio_sample_info.bits_per_sample / 8;
+      audio_stream_info.number_of_channels = 2;
+      audio_stream_info.samples_per_second = 44100;
+      audio_stream_info.bits_per_sample = 32;
       break;
     }
 #endif  // SB_API_VERSION >= 14
+#if SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
+    case kSbMediaAudioCodecIamf: {
+      audio_stream_info.number_of_channels = 2;
+      audio_stream_info.samples_per_second = 48000;
+      audio_stream_info.bits_per_sample = 32;
+      break;
+    }
+#endif  // SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
   }
-  return audio_sample_info;
+  return audio_stream_info;
 }
 
-SbMediaVideoSampleInfo CreateVideoSampleInfo(SbMediaVideoCodec codec) {
-  SbMediaVideoSampleInfo video_sample_info = {};
+VideoStreamInfo CreateVideoStreamInfo(SbMediaVideoCodec codec) {
+  VideoStreamInfo video_stream_info;
 
-  video_sample_info.codec = codec;
+  video_stream_info.codec = codec;
 
-  video_sample_info.mime = "";
-  video_sample_info.max_video_capabilities = "";
-  video_sample_info.color_metadata.primaries = kSbMediaPrimaryIdBt709;
-  video_sample_info.color_metadata.transfer = kSbMediaTransferIdBt709;
-  video_sample_info.color_metadata.matrix = kSbMediaMatrixIdBt709;
-  video_sample_info.color_metadata.range = kSbMediaRangeIdLimited;
+  video_stream_info.mime = "";
+  video_stream_info.max_video_capabilities = "";
+  video_stream_info.color_metadata.primaries = kSbMediaPrimaryIdBt709;
+  video_stream_info.color_metadata.transfer = kSbMediaTransferIdBt709;
+  video_stream_info.color_metadata.matrix = kSbMediaMatrixIdBt709;
+  video_stream_info.color_metadata.range = kSbMediaRangeIdLimited;
 
-  video_sample_info.frame_width = 1920;
-  video_sample_info.frame_height = 1080;
+  video_stream_info.frame_width = 1920;
+  video_stream_info.frame_height = 1080;
 
-  return video_sample_info;
+  return video_stream_info;
 }
 
-SbPlayerCreationParam CreatePlayerCreationParam(SbMediaAudioCodec audio_codec,
-                                                SbMediaVideoCodec video_codec) {
-  SbPlayerCreationParam creation_param = {};
+PlayerCreationParam CreatePlayerCreationParam(SbMediaAudioCodec audio_codec,
+                                              SbMediaVideoCodec video_codec) {
+  PlayerCreationParam creation_param;
 
-  creation_param.drm_system = kSbDrmSystemInvalid;
-  creation_param.audio_sample_info = CreateAudioSampleInfo(audio_codec);
-  creation_param.video_sample_info = CreateVideoSampleInfo(video_codec);
-  creation_param.output_mode = kSbPlayerOutputModeInvalid;
+  creation_param.audio_stream_info = CreateAudioStreamInfo(audio_codec);
+  creation_param.video_stream_info = CreateVideoStreamInfo(video_codec);
 
   return creation_param;
 }
diff --git a/starboard/nplb/player_creation_param_helpers.h b/starboard/nplb/player_creation_param_helpers.h
index 50755bf..34507d6 100644
--- a/starboard/nplb/player_creation_param_helpers.h
+++ b/starboard/nplb/player_creation_param_helpers.h
@@ -17,16 +17,56 @@
 
 #include "starboard/configuration.h"
 
+#include "starboard/common/log.h"
+#include "starboard/drm.h"
 #include "starboard/media.h"
 #include "starboard/player.h"
+#include "starboard/shared/starboard/media/media_util.h"
 
 namespace starboard {
 namespace nplb {
 
-SbMediaAudioSampleInfo CreateAudioSampleInfo(SbMediaAudioCodec codec);
-SbMediaVideoSampleInfo CreateVideoSampleInfo(SbMediaVideoCodec codec);
-SbPlayerCreationParam CreatePlayerCreationParam(SbMediaAudioCodec audio_codec,
-                                                SbMediaVideoCodec video_codec);
+// Encapsulates all information contained in `SbPlayerCreationParam`.  It
+// doesn't aim to maintain the same binary layout as `SbPlayerCreationParam`,
+// and is intended to be used as a C++ wrapper in unit test code to allow of
+// converting between types more conveniently.
+struct PlayerCreationParam {
+  SbDrmSystem drm_system = kSbDrmSystemInvalid;
+
+  shared::starboard::media::AudioStreamInfo audio_stream_info;
+  shared::starboard::media::VideoStreamInfo video_stream_info;
+
+  SbPlayerOutputMode output_mode = kSbPlayerOutputModeInvalid;
+
+  // Convert to an SbPlayerCreationParam that's only valid during the life time
+  // of this PlayerCreationParam instance, as the member pointers of the
+  // SbPlayerCreationParam point to memory held in this PlayerCreationParam
+  // instance.
+  void ConvertTo(SbPlayerCreationParam* creation_param) const {
+    SB_DCHECK(creation_param);
+
+    *creation_param = {};
+
+    creation_param->drm_system = drm_system;
+
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+    audio_stream_info.ConvertTo(&creation_param->audio_stream_info);
+    video_stream_info.ConvertTo(&creation_param->video_stream_info);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+    audio_stream_info.ConvertTo(&creation_param->audio_sample_info);
+    video_stream_info.ConvertTo(&creation_param->video_sample_info);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+    creation_param->output_mode = output_mode;
+  }
+};
+
+shared::starboard::media::AudioStreamInfo CreateAudioStreamInfo(
+    SbMediaAudioCodec codec);
+shared::starboard::media::VideoStreamInfo CreateVideoStreamInfo(
+    SbMediaVideoCodec codec);
+PlayerCreationParam CreatePlayerCreationParam(SbMediaAudioCodec audio_codec,
+                                              SbMediaVideoCodec video_codec);
 
 }  // namespace nplb
 }  // namespace starboard
diff --git a/starboard/nplb/player_get_preferred_output_mode_test.cc b/starboard/nplb/player_get_preferred_output_mode_test.cc
index a007352..975431c 100644
--- a/starboard/nplb/player_get_preferred_output_mode_test.cc
+++ b/starboard/nplb/player_get_preferred_output_mode_test.cc
@@ -22,24 +22,31 @@
 namespace nplb {
 namespace {
 
+SbPlayerOutputMode GetPreferredOutputMode(
+    const PlayerCreationParam& creation_param) {
+  SbPlayerCreationParam param = {};
+  creation_param.ConvertTo(&param);
+  return SbPlayerGetPreferredOutputMode(&param);
+}
+
 TEST(SbPlayerGetPreferredOutputModeTest, SunnyDay) {
   auto creation_param =
       CreatePlayerCreationParam(kSbMediaAudioCodecAac, kSbMediaVideoCodecH264);
 
   creation_param.output_mode = kSbPlayerOutputModeInvalid;
-  auto output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  auto output_mode = GetPreferredOutputMode(creation_param);
   ASSERT_NE(output_mode, kSbPlayerOutputModeInvalid);
 
   creation_param.output_mode = output_mode;
-  output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  output_mode = GetPreferredOutputMode(creation_param);
   ASSERT_EQ(output_mode, creation_param.output_mode);
 
   creation_param.output_mode = kSbPlayerOutputModeDecodeToTexture;
-  output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  output_mode = GetPreferredOutputMode(creation_param);
   ASSERT_NE(output_mode, kSbPlayerOutputModeInvalid);
 
   creation_param.output_mode = kSbPlayerOutputModePunchOut;
-  output_mode = SbPlayerGetPreferredOutputMode(&creation_param);
+  output_mode = GetPreferredOutputMode(creation_param);
   ASSERT_NE(output_mode, kSbPlayerOutputModeInvalid);
 }
 
@@ -56,6 +63,9 @@
     kSbMediaAudioCodecFlac,
     kSbMediaAudioCodecPcm,
 #endif  // SB_API_VERSION >= 14
+#if SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
+    kSbMediaAudioCodecIamf,
+#endif  // SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
   };
   const SbMediaVideoCodec kVideoCodecs[] = {
       kSbMediaVideoCodecNone,  kSbMediaVideoCodecH264,   kSbMediaVideoCodecH265,
@@ -63,7 +73,8 @@
       kSbMediaVideoCodecAv1,   kSbMediaVideoCodecVp8,    kSbMediaVideoCodecVp9,
   };
   const SbPlayerOutputMode kOutputModes[] = {
-      kSbPlayerOutputModeDecodeToTexture, kSbPlayerOutputModePunchOut,
+      kSbPlayerOutputModeDecodeToTexture,
+      kSbPlayerOutputModePunchOut,
       kSbPlayerOutputModeInvalid,
   };
 
@@ -73,7 +84,7 @@
         auto creation_param =
             CreatePlayerCreationParam(audio_codec, video_codec);
         creation_param.output_mode = output_mode;
-        SbPlayerGetPreferredOutputMode(&creation_param);
+        GetPreferredOutputMode(creation_param);
       }
     }
   }
diff --git a/starboard/nplb/player_test_util.cc b/starboard/nplb/player_test_util.cc
index 8c06675..472dda4 100644
--- a/starboard/nplb/player_test_util.cc
+++ b/starboard/nplb/player_test_util.cc
@@ -17,17 +17,22 @@
 #include <functional>
 
 #include "starboard/audio_sink.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/string.h"
 #include "starboard/directory.h"
+#include "starboard/extension/enhanced_audio.h"
 #include "starboard/nplb/player_creation_param_helpers.h"
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
 #include "starboard/testing/fake_graphics_context_provider.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace starboard {
 namespace nplb {
 
 namespace {
 
+using shared::starboard::media::AudioSampleInfo;
+using shared::starboard::media::VideoSampleInfo;
 using shared::starboard::player::video_dmp::VideoDmpReader;
 using std::placeholders::_1;
 using std::placeholders::_2;
@@ -142,7 +147,7 @@
     SbMediaVideoCodec video_codec,
     SbMediaAudioCodec audio_codec,
     SbDrmSystem drm_system,
-    const SbMediaAudioSampleInfo* audio_sample_info,
+    const shared::starboard::media::AudioStreamInfo* audio_stream_info,
     const char* max_video_capabilities,
     SbPlayerDeallocateSampleFunc sample_deallocate_func,
     SbPlayerDecoderStatusFunc decoder_status_func,
@@ -151,34 +156,111 @@
     void* context,
     SbPlayerOutputMode output_mode,
     SbDecodeTargetGraphicsContextProvider* context_provider) {
-  if (audio_sample_info) {
-    SB_CHECK(audio_sample_info->codec == audio_codec);
+  if (audio_stream_info) {
+    SB_CHECK(audio_stream_info->codec == audio_codec);
   } else {
     SB_CHECK(audio_codec == kSbMediaAudioCodecNone);
   }
 
-  SbPlayerCreationParam creation_param =
-      nplb::CreatePlayerCreationParam(audio_codec, video_codec);
-  if (audio_sample_info) {
-    creation_param.audio_sample_info = *audio_sample_info;
+  PlayerCreationParam creation_param =
+      CreatePlayerCreationParam(audio_codec, video_codec);
+  if (audio_stream_info) {
+    creation_param.audio_stream_info = *audio_stream_info;
   }
   creation_param.drm_system = drm_system;
   creation_param.output_mode = output_mode;
-  creation_param.video_sample_info.max_video_capabilities =
+  creation_param.video_stream_info.max_video_capabilities =
       max_video_capabilities;
 
-  return SbPlayerCreate(window, &creation_param, sample_deallocate_func,
+  SbPlayerCreationParam param = {};
+  creation_param.ConvertTo(&param);
+  return SbPlayerCreate(window, &param, sample_deallocate_func,
                         decoder_status_func, player_status_func,
                         player_error_func, context, context_provider);
 }
 
+void CallSbPlayerWriteSamples(
+    SbPlayer player,
+    SbMediaType sample_type,
+    shared::starboard::player::video_dmp::VideoDmpReader* dmp_reader,
+    int start_index,
+    int number_of_samples_to_write) {
+  SB_DCHECK(start_index >= 0);
+  SB_DCHECK(number_of_samples_to_write > 0);
+
+  static auto const* enhanced_audio_extension =
+      static_cast<const CobaltExtensionEnhancedAudioApi*>(
+          SbSystemGetExtension(kCobaltExtensionEnhancedAudioName));
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  ASSERT_FALSE(enhanced_audio_extension);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+  if (enhanced_audio_extension) {
+    ASSERT_STREQ(enhanced_audio_extension->name,
+                 kCobaltExtensionEnhancedAudioName);
+    ASSERT_EQ(enhanced_audio_extension->version, 1u);
+
+    std::vector<CobaltExtensionEnhancedAudioPlayerSampleInfo> sample_infos;
+    // We have to hold all intermediate sample infos to ensure that their member
+    // variables with allocated memory (like `std::string mime`) won't go out of
+    // scope before the call to `enhanced_audio_extension->PlayerWriteSamples`.
+    std::vector<AudioSampleInfo> audio_sample_infos;
+    std::vector<VideoSampleInfo> video_sample_infos;
+
+    for (int i = 0; i < number_of_samples_to_write; ++i) {
+      SbPlayerSampleInfo source =
+          dmp_reader->GetPlayerSampleInfo(sample_type, start_index++);
+      sample_infos.resize(sample_infos.size() + 1);
+      sample_infos.back().type = source.type;
+      sample_infos.back().buffer = source.buffer;
+      sample_infos.back().buffer_size = source.buffer_size;
+      sample_infos.back().timestamp = source.timestamp;
+      sample_infos.back().side_data = source.side_data;
+      sample_infos.back().side_data_count = source.side_data_count;
+      sample_infos.back().drm_info = source.drm_info;
+
+      if (sample_type == kSbMediaTypeAudio) {
+        audio_sample_infos.emplace_back(source.audio_sample_info);
+        audio_sample_infos.back().ConvertTo(
+            &sample_infos.back().audio_sample_info);
+      } else {
+        SB_DCHECK(sample_type == kSbMediaTypeVideo);
+        video_sample_infos.emplace_back(source.video_sample_info);
+        video_sample_infos.back().ConvertTo(
+            &sample_infos.back().video_sample_info);
+      }
+    }
+
+    enhanced_audio_extension->PlayerWriteSamples(
+        player, sample_type, sample_infos.data(), number_of_samples_to_write);
+
+    return;
+  }
+
+  std::vector<SbPlayerSampleInfo> sample_infos;
+  for (int i = 0; i < number_of_samples_to_write; ++i) {
+    sample_infos.push_back(
+        dmp_reader->GetPlayerSampleInfo(sample_type, start_index++));
+  }
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerWriteSamples(player, sample_type, sample_infos.data(),
+                       number_of_samples_to_write);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerWriteSample2(player, sample_type, sample_infos.data(),
+                       number_of_samples_to_write);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+}
+
 bool IsOutputModeSupported(SbPlayerOutputMode output_mode,
                            SbMediaAudioCodec audio_codec,
                            SbMediaVideoCodec video_codec) {
-  SbPlayerCreationParam creation_param =
+  PlayerCreationParam creation_param =
       CreatePlayerCreationParam(audio_codec, video_codec);
   creation_param.output_mode = output_mode;
-  return SbPlayerGetPreferredOutputMode(&creation_param) == output_mode;
+
+  SbPlayerCreationParam param = {};
+  creation_param.ConvertTo(&param);
+  return SbPlayerGetPreferredOutputMode(&param) == output_mode;
 }
 
 }  // namespace nplb
diff --git a/starboard/nplb/player_test_util.h b/starboard/nplb/player_test_util.h
index 53503ee..e85909b 100644
--- a/starboard/nplb/player_test_util.h
+++ b/starboard/nplb/player_test_util.h
@@ -21,6 +21,8 @@
 
 #include "starboard/configuration_constants.h"
 #include "starboard/player.h"
+#include "starboard/shared/starboard/media/media_util.h"
+#include "starboard/shared/starboard/player/video_dmp_reader.h"
 
 namespace starboard {
 namespace nplb {
@@ -59,7 +61,7 @@
     SbMediaVideoCodec video_codec,
     SbMediaAudioCodec audio_codec,
     SbDrmSystem drm_system,
-    const SbMediaAudioSampleInfo* audio_sample_info,
+    const shared::starboard::media::AudioStreamInfo* audio_stream_info,
     const char* max_video_capabilities,
     SbPlayerDeallocateSampleFunc sample_deallocate_func,
     SbPlayerDecoderStatusFunc decoder_status_func,
@@ -69,6 +71,13 @@
     SbPlayerOutputMode output_mode,
     SbDecodeTargetGraphicsContextProvider* context_provider);
 
+void CallSbPlayerWriteSamples(
+    SbPlayer player,
+    SbMediaType sample_type,
+    shared::starboard::player::video_dmp::VideoDmpReader* dmp_reader,
+    int start_index,
+    int number_of_samples_to_write);
+
 bool IsOutputModeSupported(SbPlayerOutputMode output_mode,
                            SbMediaAudioCodec audio_codec,
                            SbMediaVideoCodec video_codec);
diff --git a/starboard/nplb/player_write_sample_test.cc b/starboard/nplb/player_write_sample_test.cc
index 65b3b19..5402105 100644
--- a/starboard/nplb/player_write_sample_test.cc
+++ b/starboard/nplb/player_write_sample_test.cc
@@ -16,12 +16,13 @@
 #include <deque>
 #include <functional>
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/optional.h"
 #include "starboard/common/queue.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/common/string.h"
 #include "starboard/nplb/player_test_util.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
 #include "starboard/string.h"
 #include "starboard/testing/fake_graphics_context_provider.h"
@@ -43,13 +44,12 @@
 
 class SbPlayerWriteSampleTest
     : public ::testing::TestWithParam<SbPlayerTestConfig> {
- public:
+ protected:
   SbPlayerWriteSampleTest();
 
   void SetUp() override;
   void TearDown() override;
 
- protected:
   struct CallbackEvent {
     CallbackEvent() {}
 
@@ -211,12 +211,12 @@
 void SbPlayerWriteSampleTest::SetUp() {
   SbMediaVideoCodec video_codec = dmp_reader_->video_codec();
   SbMediaAudioCodec audio_codec = dmp_reader_->audio_codec();
-  const SbMediaAudioSampleInfo* audio_sample_info = NULL;
+  const shared::starboard::media::AudioStreamInfo* audio_stream_info = NULL;
 
   if (test_media_type_ == kSbMediaTypeAudio) {
     SB_DCHECK(audio_codec != kSbMediaAudioCodecNone);
 
-    audio_sample_info = &dmp_reader_->audio_sample_info();
+    audio_stream_info = &dmp_reader_->audio_stream_info();
     video_codec = kSbMediaVideoCodecNone;
   } else {
     SB_DCHECK(video_codec != kSbMediaVideoCodecNone);
@@ -226,7 +226,7 @@
 
   player_ = CallSbPlayerCreate(
       fake_graphics_context_provider_.window(), video_codec, audio_codec,
-      kSbDrmSystemInvalid, audio_sample_info, "", DummyDeallocateSampleFunc,
+      kSbDrmSystemInvalid, audio_stream_info, "", DummyDeallocateSampleFunc,
       DecoderStatusCallback, PlayerStatusCallback, ErrorCallback, this,
       output_mode_, fake_graphics_context_provider_.decoder_target_provider());
 
@@ -299,7 +299,11 @@
 
 void SbPlayerWriteSampleTest::Seek(const SbTime time) {
   PrepareForSeek();
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerSeek(player_, time, ticket_);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbPlayerSeek2(player_, time, ticket_);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   ASSERT_NO_FATAL_FAILURE(WaitForDecoderStateNeedsData());
   ASSERT_TRUE(decoder_state_queue_.empty());
 }
@@ -402,14 +406,9 @@
   samples_to_write = std::min(samples_to_write, max_batch_size);
 
   SB_DCHECK(start_index + samples_to_write <= GetNumBuffers());
-  // Prepare a batch writing.
-  std::vector<SbPlayerSampleInfo> sample_infos;
-  for (int i = 0; i < samples_to_write; ++i) {
-    sample_infos.push_back(
-        dmp_reader_->GetPlayerSampleInfo(test_media_type_, start_index++));
-  }
-  SbPlayerWriteSample2(player_, test_media_type_, sample_infos.data(),
-                       samples_to_write);
+
+  CallSbPlayerWriteSamples(player_, test_media_type_, dmp_reader_.get(),
+                           start_index, samples_to_write);
 }
 
 void SbPlayerWriteSampleTest::WriteEndOfStream() {
@@ -520,7 +519,11 @@
 
 TEST_P(SbPlayerWriteSampleTest, SeekAndDestroy) {
   PrepareForSeek();
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbPlayerSeek(player_, 1 * kSbTimeSecond, ticket_);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SbPlayerSeek2(player_, 1 * kSbTimeSecond, ticket_);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 }
 
 TEST_P(SbPlayerWriteSampleTest, NoInput) {
diff --git a/starboard/nplb/socket_accept_test.cc b/starboard/nplb/socket_accept_test.cc
index 35195bf..be933db 100644
--- a/starboard/nplb/socket_accept_test.cc
+++ b/starboard/nplb/socket_accept_test.cc
@@ -72,6 +72,10 @@
   EXPECT_TRUE(SbSocketDestroy(server_socket));
 }
 
+TEST_P(SbSocketAcceptTest, RainyDayInvalidSocket) {
+  EXPECT_EQ(kSbSocketInvalid, SbSocketAccept(kSbSocketInvalid));
+}
+
 #if SB_HAS(IPV6)
 INSTANTIATE_TEST_CASE_P(SbSocketAddressTypes,
                         SbSocketAcceptTest,
diff --git a/starboard/nplb/socket_set_options_test.cc b/starboard/nplb/socket_set_options_test.cc
index 60e0c17..b871de6 100644
--- a/starboard/nplb/socket_set_options_test.cc
+++ b/starboard/nplb/socket_set_options_test.cc
@@ -54,6 +54,16 @@
   EXPECT_TRUE(SbSocketDestroy(socket));
 }
 
+TEST_P(SbSocketSetOptionsTest, RainyDayInvalidSocket) {
+  EXPECT_FALSE(SbSocketSetReuseAddress(kSbSocketInvalid, true));
+  EXPECT_FALSE(SbSocketSetReceiveBufferSize(kSbSocketInvalid, 16 * 1024));
+  EXPECT_FALSE(SbSocketSetSendBufferSize(kSbSocketInvalid, 16 * 1024));
+  EXPECT_FALSE(
+      SbSocketSetTcpKeepAlive(kSbSocketInvalid, true, 30 * kSbTimeSecond));
+  EXPECT_FALSE(SbSocketSetTcpNoDelay(kSbSocketInvalid, true));
+  EXPECT_FALSE(SbSocketSetTcpWindowScaling(kSbSocketInvalid, true));
+}
+
 // TODO: Come up with some way to test the effects of these options.
 
 #if SB_HAS(IPV6)
diff --git a/starboard/nplb/thread_sampler_test.cc b/starboard/nplb/thread_sampler_test.cc
index d2dbd2e..2a4aa7a 100644
--- a/starboard/nplb/thread_sampler_test.cc
+++ b/starboard/nplb/thread_sampler_test.cc
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/nplb/thread_helpers.h"
 #include "starboard/thread.h"
 #include "starboard/time.h"
@@ -44,7 +44,8 @@
     int32_t end_count = GetCount() + 2;
     SbTime end_time = SbTimeGetNow() + timeout;
     while (SbTimeGetNow() < end_time) {
-      if (GetCount() >= end_count) return true;
+      if (GetCount() >= end_count)
+        return true;
       SbThreadYield();
     }
     return false;
diff --git a/starboard/nplb/thread_test.cc b/starboard/nplb/thread_test.cc
index 488f7b4..dc4f8de 100644
--- a/starboard/nplb/thread_test.cc
+++ b/starboard/nplb/thread_test.cc
@@ -14,7 +14,7 @@
 
 #include <functional>
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/semaphore.h"
 #include "starboard/common/thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/starboard/player.h b/starboard/player.h
index cde4c7a..8f06aa4 100644
--- a/starboard/player.h
+++ b/starboard/player.h
@@ -25,6 +25,7 @@
 #include "starboard/drm.h"
 #include "starboard/export.h"
 #include "starboard/media.h"
+#include "starboard/time.h"
 #include "starboard/types.h"
 #include "starboard/window.h"
 
@@ -104,6 +105,17 @@
   // portions.  It will be |kSbDrmSystemInvalid| if the stream does not have
   // encrypted portions.
   SbDrmSystem drm_system;
+
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  // Contains a populated SbMediaAudioStreamInfo if |audio_stream_info.codec|
+  // isn't |kSbMediaAudioCodecNone|.  When |audio_stream_info.codec| is
+  // |kSbMediaAudioCodecNone|, the video doesn't have an audio track.
+  SbMediaAudioStreamInfo audio_stream_info;
+  // Contains a populated SbMediaVideoStreamInfo if |video_stream_info.codec|
+  // isn't |kSbMediaVideoCodecNone|.  When |video_stream_info.codec| is
+  // |kSbMediaVideoCodecNone|, the video is audio only.
+  SbMediaVideoStreamInfo video_stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   // Contains a populated SbMediaAudioSampleInfo if |audio_sample_info.codec|
   // isn't |kSbMediaAudioCodecNone|.  When |audio_sample_info.codec| is
   // |kSbMediaAudioCodecNone|, the video doesn't have an audio track.
@@ -112,6 +124,8 @@
   // isn't |kSbMediaVideoCodecNone|.  When |video_sample_info.codec| is
   // |kSbMediaVideoCodecNone|, the video is audio only.
   SbMediaVideoSampleInfo video_sample_info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
   // Selects how the decoded video frames will be output.  For example,
   // |kSbPlayerOutputModePunchOut| indicates that the decoded video frames will
   // be output to a background video layer by the platform, and
@@ -144,7 +158,7 @@
   size_t size;
 } SbPlayerSampleSideData;
 
-// Information about the samples to be written into SbPlayerWriteSample2.
+// Information about the samples to be written into SbPlayerWriteSamples().
 typedef struct SbPlayerSampleInfo {
   SbMediaType type;
   // Points to the buffer containing the sample data.
@@ -175,7 +189,11 @@
 } SbPlayerSampleInfo;
 
 // Information about the current media playback state.
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+typedef struct SbPlayerInfo {
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 typedef struct SbPlayerInfo2 {
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   // The position of the playback head, as precisely as possible, in
   // microseconds.
   SbTime current_media_timestamp;
@@ -220,7 +238,11 @@
   // is played in a slower than normal speed.  Negative speeds are not
   // supported.
   double playback_rate;
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+} SbPlayerInfo;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 } SbPlayerInfo2;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 
 // An opaque handle to an implementation-private structure representing a
 // player.
@@ -413,7 +435,7 @@
 // Note that it is not the responsibility of this function to verify whether the
 // video described by |creation_param| can be played on the platform, and the
 // implementation should try its best effort to return a valid output mode.
-// |creation_param| will never be NULL.
+// |creation_param| must not be NULL.
 SB_EXPORT SbPlayerOutputMode
 SbPlayerGetPreferredOutputMode(const SbPlayerCreationParam* creation_param);
 
@@ -425,12 +447,9 @@
 //  * No more other callbacks should be issued after this function returns.
 //  * It is not allowed to pass |player| into any other |SbPlayer| function
 //    once SbPlayerDestroy has been called on that player.
-// |player|: The player to be destroyed.
+// |player|: The player to be destroyed. Must not be |kSbPlayerInvalid|.
 SB_EXPORT void SbPlayerDestroy(SbPlayer player);
 
-// SbPlayerSeek2 is like the deprecated SbPlayerSeek, but accepts SbTime
-// |seek_to_timestamp| instead of SbMediaTime |seek_to_pts|.
-
 // Tells the player to freeze playback (if playback has already started),
 // reset or flush the decoder pipeline, and go back to the Prerolling state.
 // The player should restart playback once it can display the frame at
@@ -448,43 +467,57 @@
 //   that was specified when the player was created (SbPlayerCreate).
 //
 // |player|: The SbPlayer in which the seek operation is being performed.
+//   Must not be |kSbPlayerInvalid|.
+
 // |seek_to_timestamp|: The frame at which playback should begin.
 // |ticket|: A user-supplied unique ID that is be passed to all subsequent
 //   |SbPlayerDecoderStatusFunc| calls. (That is the |decoder_status_func|
 //   callback function specified when calling SbPlayerCreate.)
 //
 //   The |ticket| value is used to filter calls that may have been in flight
-//   when SbPlayerSeek2 was called. To be very specific, once SbPlayerSeek2 has
+//   when SbPlayerSeek was called. To be very specific, once SbPlayerSeek has
 //   been called with ticket X, a client should ignore all
 //   |SbPlayerDecoderStatusFunc| calls that do not pass in ticket X.
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+SB_EXPORT void SbPlayerSeek(SbPlayer player,
+                            SbTime seek_to_timestamp,
+                            int ticket);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 SB_EXPORT void SbPlayerSeek2(SbPlayer player,
                              SbTime seek_to_timestamp,
                              int ticket);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 
 // Writes samples of the given media type to |player|'s input stream. The
 // lifetime of |sample_infos|, and the members of its elements like |buffer|,
 // |video_sample_info|, and |drm_info| (as well as member |subsample_mapping|
 // contained inside it) are not guaranteed past the call to
-// SbPlayerWriteSample2. That means that before returning, the implementation
+// SbPlayerWriteSamples(). That means that before returning, the implementation
 // must synchronously copy any information it wants to retain from those
 // structures.
 //
-// SbPlayerWriteSample2 allows writing of multiple samples in one call.
+// SbPlayerWriteSamples() allows writing of multiple samples in one call.
 //
-// |player|: The player to which the sample is written.
+// |player|: The player to which the sample is written. Must not be
+//   |kSbPlayerInvalid|.
+
 // |sample_type|: The type of sample being written. See the |SbMediaType|
 //   enum in media.h.
 // |sample_infos|: A pointer to an array of SbPlayerSampleInfo with
 //   |number_of_sample_infos| elements, each holds the data for an sample, i.e.
 //   a sequence of whole NAL Units for video, or a complete audio frame.
 //   |sample_infos| cannot be assumed to live past the call into
-//   SbPlayerWriteSample2(), so it must be copied if its content will be used
-//   after SbPlayerWriteSample2() returns.
+//   SbPlayerWriteSamples(), so it must be copied if its content will be used
+//   after SbPlayerWriteSamples() returns.
 // |number_of_sample_infos|: Specify the number of samples contained inside
 //   |sample_infos|.  It has to be at least one, and less than the return value
 //   of SbPlayerGetMaximumNumberOfSamplesPerWrite().
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+SB_EXPORT void SbPlayerWriteSamples(SbPlayer player,
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 SB_EXPORT void SbPlayerWriteSample2(SbPlayer player,
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
                                     SbMediaType sample_type,
                                     const SbPlayerSampleInfo* sample_infos,
                                     int number_of_sample_infos);
@@ -526,7 +559,7 @@
 // frame, implementors should take care to avoid related performance concerns
 // with such frequent calls.
 //
-// |player|: The player that is being resized.
+// |player|: The player that is being resized. Must not be |kSbPlayerInvalid|.
 // |z_index|: The z-index of the player.  When the bounds of multiple players
 //            are overlapped, the one with larger z-index will be rendered on
 //            top of the ones with smaller z-index.
@@ -551,27 +584,33 @@
 // to a rate that is close to |playback_rate| which the implementation supports.
 // It returns false when the playback rate is unchanged, this can happen when
 // |playback_rate| is negative or if it is too high to support.
+//
+// |player| must not be |kSbPlayerInvalid|.
 SB_EXPORT bool SbPlayerSetPlaybackRate(SbPlayer player, double playback_rate);
 
 // Sets the player's volume.
 //
-// |player|: The player in which the volume is being adjusted.
+// |player|: The player in which the volume is being adjusted. Must not be
+//   |kSbPlayerInvalid|.
 // |volume|: The new player volume. The value must be between |0.0| and |1.0|,
 //   inclusive. A value of |0.0| means that the audio should be muted, and a
 //   value of |1.0| means that it should be played at full volume.
 SB_EXPORT void SbPlayerSetVolume(SbPlayer player, double volume);
 
-// SbPlayerGetInfo2 is like the deprecated SbPlayerGetInfo, but accepts
-// SbPlayerInfo2* |out_player_info2| instead of SbPlayerInfo |out_player_info|.
-
 // Gets a snapshot of the current player state and writes it to
 // |out_player_info|. This function may be called very frequently and is
 // expected to be inexpensive.
 //
-// |player|: The player about which information is being retrieved.
+// |player|: The player about which information is being retrieved. Must not be
+//   |kSbPlayerInvalid|.
 // |out_player_info|: The information retrieved for the player.
+
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+SB_EXPORT void SbPlayerGetInfo(SbPlayer player, SbPlayerInfo* out_player_info);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 SB_EXPORT void SbPlayerGetInfo2(SbPlayer player,
                                 SbPlayerInfo2* out_player_info2);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VER
 
 // Given a player created with the kSbPlayerOutputModeDecodeToTexture
 // output mode, it will return a SbDecodeTarget representing the current frame
@@ -580,6 +619,8 @@
 // be used to eventually render the frame.  If this function is called with a
 // |player| object that was created with an output mode other than
 // kSbPlayerOutputModeDecodeToTexture, kSbDecodeTargetInvalid is returned.
+//
+// |player| must not be |kSbPlayerInvalid|.
 SB_EXPORT SbDecodeTarget SbPlayerGetCurrentFrame(SbPlayer player);
 
 #ifdef __cplusplus
diff --git a/starboard/raspi/2/platform_configuration/BUILD.gn b/starboard/raspi/2/platform_configuration/BUILD.gn
index 30d143e..ef03b60 100644
--- a/starboard/raspi/2/platform_configuration/BUILD.gn
+++ b/starboard/raspi/2/platform_configuration/BUILD.gn
@@ -13,18 +13,15 @@
 # limitations under the License.
 
 config("platform_configuration") {
-  configs = [ "//starboard/build/config/sabi" ]
-
-  if (current_toolchain == default_toolchain) {
-    configs += [ "//starboard/raspi/shared/platform_configuration" ]
-    cflags = [
-      "-march=armv7-a",
-      "-mfpu=neon-vfpv4",
-      "-mfloat-abi=hard",
-      "-mcpu=cortex-a8",
-      "-mtune=cortex-a8",
-    ]
-  } else {
-    cflags = [ "-O2" ]
-  }
+  configs = [
+    "//starboard/build/config/sabi",
+    "//starboard/raspi/shared/platform_configuration",
+  ]
+  cflags = [
+    "-march=armv7-a",
+    "-mfpu=neon-vfpv4",
+    "-mfloat-abi=hard",
+    "-mcpu=cortex-a8",
+    "-mtune=cortex-a8",
+  ]
 }
diff --git a/starboard/raspi/2/skia/toolchain/BUILD.gn b/starboard/raspi/2/skia/toolchain/BUILD.gn
index 0d9a282..569a254 100644
--- a/starboard/raspi/2/skia/toolchain/BUILD.gn
+++ b/starboard/raspi/2/skia/toolchain/BUILD.gn
@@ -16,15 +16,6 @@
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni")
 
-clang_toolchain("host") {
-  clang_base_path = clang_base_path
-
-  toolchain_args = {
-    current_os = "linux"
-    current_cpu = "x86"
-  }
-}
-
 gcc_toolchain("target") {
   cc = gcc_toolchain_cc
   cxx = gcc_toolchain_cxx
diff --git a/starboard/raspi/2/toolchain/BUILD.gn b/starboard/raspi/2/toolchain/BUILD.gn
index 9056b43..0993e86 100644
--- a/starboard/raspi/2/toolchain/BUILD.gn
+++ b/starboard/raspi/2/toolchain/BUILD.gn
@@ -15,15 +15,6 @@
 import("//build/toolchain/gcc_toolchain.gni")
 import("//starboard/raspi/shared/toolchain/raspi_shared_toolchain.gni")
 
-clang_toolchain("host") {
-  clang_base_path = clang_base_path
-
-  toolchain_args = {
-    current_os = "linux"
-    current_cpu = "x86"
-  }
-}
-
 gcc_toolchain("target") {
   cc = gcc_toolchain_cc
   cxx = gcc_toolchain_cxx
@@ -38,3 +29,20 @@
     is_clang = false
   }
 }
+
+gcc_toolchain("native_target") {
+  cc = gcc_toolchain_cc
+  cxx = gcc_toolchain_cxx
+  ld = cxx
+
+  # We use whatever 'ar' resolves to.
+  ar = gcc_toolchain_ar
+
+  tail_lib_dependencies = "-l:libpthread.so.0"
+
+  toolchain_args = {
+    is_starboard = false
+    is_native_target_build = true
+    is_clang = false
+  }
+}
diff --git a/starboard/raspi/shared/BUILD.gn b/starboard/raspi/shared/BUILD.gn
index a5653f3..0522a91 100644
--- a/starboard/raspi/shared/BUILD.gn
+++ b/starboard/raspi/shared/BUILD.gn
@@ -310,8 +310,6 @@
     "//starboard/shared/starboard/media/media_is_audio_supported_aac_and_opus.cc",
     "//starboard/shared/starboard/media/media_is_buffer_pool_allocate_on_demand.cc",
     "//starboard/shared/starboard/media/media_is_buffer_using_memory_pool.cc",
-    "//starboard/shared/starboard/media/mime_type.cc",
-    "//starboard/shared/starboard/media/mime_type.h",
     "//starboard/shared/starboard/memory.cc",
     "//starboard/shared/starboard/new.cc",
     "//starboard/shared/starboard/queue_application.cc",
@@ -399,10 +397,6 @@
     "//starboard/shared/starboard/player/filter:filter_based_player_sources",
   ]
 
-  if (sb_is_evergreen_compatible && !sb_evergreen_compatible_enable_lite) {
-    public_deps += [ "//starboard/loader_app:pending_restart" ]
-  }
-
   deps = [
     "//third_party/libevent",
     "//third_party/opus",
@@ -413,7 +407,11 @@
   }
 
   if (sb_is_evergreen_compatible) {
-    public_deps += [ "//starboard/elf_loader:evergreen_config" ]
+    public_deps += [
+      "//starboard/elf_loader:constants",
+      "//starboard/elf_loader:evergreen_config",
+      "//starboard/loader_app:pending_restart",
+    ]
     deps += [ "//third_party/crashpad/wrapper" ]
   } else {
     deps += [ "//third_party/crashpad/wrapper:wrapper_stub" ]
diff --git a/starboard/raspi/shared/application_dispmanx.h b/starboard/raspi/shared/application_dispmanx.h
index a23ff53..0892c85 100644
--- a/starboard/raspi/shared/application_dispmanx.h
+++ b/starboard/raspi/shared/application_dispmanx.h
@@ -35,7 +35,12 @@
 class ApplicationDispmanx
     : public ::starboard::shared::starboard::QueueApplication {
  public:
+#if SB_MODULAR_BUILD
+  explicit ApplicationDispmanx(SbEventHandleCallback sb_event_handle_callback)
+      : window_(kSbWindowInvalid), QueueApplication(sb_event_handle_callback) {}
+#else
   ApplicationDispmanx() : window_(kSbWindowInvalid) {}
+#endif  // SB_MODULAR_BUILD
   ~ApplicationDispmanx() override {}
 
   static ApplicationDispmanx* Get() {
diff --git a/starboard/raspi/shared/main.cc b/starboard/raspi/shared/main.cc
index ee0d66c..b874bb1 100644
--- a/starboard/raspi/shared/main.cc
+++ b/starboard/raspi/shared/main.cc
@@ -16,11 +16,14 @@
 #include <time.h>
 
 #include "starboard/configuration.h"
+#include "starboard/event.h"
 #include "starboard/raspi/shared/application_dispmanx.h"
 #include "starboard/shared/signal/crash_signals.h"
 #include "starboard/shared/signal/debug_signals.h"
 #include "starboard/shared/signal/suspend_signals.h"
 #if SB_IS(EVERGREEN_COMPATIBLE)
+#include "starboard/common/paths.h"
+#include "starboard/elf_loader/elf_loader_constants.h"
 #include "starboard/shared/starboard/command_line.h"
 #include "starboard/shared/starboard/starboard_switches.h"
 #endif
@@ -37,19 +40,46 @@
   starboard::shared::signal::InstallSuspendSignalHandlers();
 
 #if SB_IS(EVERGREEN_COMPATIBLE)
-  if (starboard::shared::starboard::CommandLine(argc, argv)
-          .HasSwitch(starboard::shared::starboard::kStartHandlerAtLaunch) &&
-      !starboard::shared::starboard::CommandLine(argc, argv)
-           .HasSwitch(starboard::shared::starboard::kStartHandlerAtCrash)) {
-    third_party::crashpad::wrapper::InstallCrashpadHandler(false);
-  } else {
-    third_party::crashpad::wrapper::InstallCrashpadHandler(true);
+  auto command_line = starboard::shared::starboard::CommandLine(argc, argv);
+  auto evergreen_content_path =
+      command_line.GetSwitchValue(starboard::elf_loader::kEvergreenContent);
+  std::string ca_certificates_path =
+      evergreen_content_path.empty()
+          ? starboard::common::GetCACertificatesPath()
+          : starboard::common::GetCACertificatesPath(evergreen_content_path);
+  if (ca_certificates_path.empty()) {
+    SB_LOG(ERROR) << "Failed to get CA certificates path";
+    return 1;
   }
-#endif
+
+  bool start_handler_at_crash =
+      command_line.HasSwitch(
+          starboard::shared::starboard::kStartHandlerAtCrash) ||
+      !command_line.HasSwitch(
+          starboard::shared::starboard::kStartHandlerAtLaunch);
+  third_party::crashpad::wrapper::InstallCrashpadHandler(start_handler_at_crash,
+                                                         ca_certificates_path);
+#endif  // SB_IS(EVERGREEN_COMPATIBLE)
+
+#if SB_MODULAR_BUILD
+  return SbRunStarboardMain(argc, argv, SbEventHandle);
+#else
   starboard::raspi::shared::ApplicationDispmanx application;
   int result = application.Run(argc, argv);
   starboard::shared::signal::UninstallSuspendSignalHandlers();
   starboard::shared::signal::UninstallDebugSignalHandlers();
   starboard::shared::signal::UninstallCrashSignalHandlers();
   return result;
+#endif  // SB_MODULAR_BUILD
 }
+
+#if SB_MODULAR_BUILD
+int SbRunStarboardMain(int argc, char** argv, SbEventHandleCallback callback) {
+  starboard::raspi::shared::ApplicationDispmanx application(callback);
+  int result = application.Run(argc, argv);
+  starboard::shared::signal::UninstallSuspendSignalHandlers();
+  starboard::shared::signal::UninstallDebugSignalHandlers();
+  starboard::shared::signal::UninstallCrashSignalHandlers();
+  return result;
+}
+#endif  // SB_MODULAR_BUILD
diff --git a/starboard/raspi/shared/player_components_factory.cc b/starboard/raspi/shared/player_components_factory.cc
index 0c488a9..b1498ff 100644
--- a/starboard/raspi/shared/player_components_factory.cc
+++ b/starboard/raspi/shared/player_components_factory.cc
@@ -18,6 +18,7 @@
 #include "starboard/raspi/shared/video_renderer_sink_impl.h"
 #include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.h"
 #include "starboard/shared/opus/opus_audio_decoder.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h"
@@ -50,21 +51,21 @@
       SB_DCHECK(audio_decoder);
       SB_DCHECK(audio_renderer_sink);
 
-      auto decoder_creator = [](const SbMediaAudioSampleInfo& audio_sample_info,
+      auto decoder_creator = [](const media::AudioStreamInfo& audio_stream_info,
                                 SbDrmSystem drm_system) {
         typedef ::starboard::shared::ffmpeg::AudioDecoder AudioDecoderImpl;
-        typedef ::starboard::shared::opus::OpusAudioDecoder OpusAudioDecoderImpl;
+        typedef ::starboard::shared::opus::OpusAudioDecoder
+            OpusAudioDecoderImpl;
 
-        if(audio_sample_info.codec == kSbMediaAudioCodecOpus) {
+        if (audio_stream_info.codec == kSbMediaAudioCodecOpus) {
           scoped_ptr<OpusAudioDecoderImpl> opus_audio_decoder_impl(
-              new OpusAudioDecoderImpl(audio_sample_info));
+              new OpusAudioDecoderImpl(audio_stream_info));
           if (opus_audio_decoder_impl && opus_audio_decoder_impl->is_valid()) {
             return opus_audio_decoder_impl.PassAs<AudioDecoder>();
           }
         } else {
           scoped_ptr<AudioDecoderImpl> audio_decoder_impl(
-              AudioDecoderImpl::Create(audio_sample_info.codec,
-                                      audio_sample_info));
+              AudioDecoderImpl::Create(audio_stream_info));
           if (audio_decoder_impl && audio_decoder_impl->is_valid()) {
             return audio_decoder_impl.PassAs<AudioDecoder>();
           }
@@ -73,7 +74,7 @@
       };
 
       audio_decoder->reset(new AdaptiveAudioDecoder(
-          creation_parameters.audio_sample_info(),
+          creation_parameters.audio_stream_info(),
           creation_parameters.drm_system(), decoder_creator));
       audio_renderer_sink->reset(new AudioRendererSinkImpl);
     }
diff --git a/starboard/shared/enhanced_audio/enhanced_audio.cc b/starboard/shared/enhanced_audio/enhanced_audio.cc
new file mode 100644
index 0000000..304f5cd
--- /dev/null
+++ b/starboard/shared/enhanced_audio/enhanced_audio.cc
@@ -0,0 +1,46 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/enhanced_audio/enhanced_audio.h"
+
+#include "starboard/common/log.h"
+#include "starboard/extension/enhanced_audio.h"
+#include "starboard/shared/enhanced_audio/enhanced_audio_player_write_samples.h"
+
+namespace starboard {
+namespace shared {
+namespace enhanced_audio {
+
+#if SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+namespace {
+
+const CobaltExtensionEnhancedAudioApi kEnhancedAudioApi = {
+    kCobaltExtensionEnhancedAudioName,
+    1,
+    &EnhancedAudioPlayerWriteSamples,
+};
+
+}  // namespace
+
+const void* GetEnhancedAudioApi() {
+  SB_LOG(INFO) << "EnhancedAudio extension enabled.";
+  return &kEnhancedAudioApi;
+}
+
+#endif  // SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+}  // namespace enhanced_audio
+}  // namespace shared
+}  // namespace starboard
diff --git a/starboard/shared/enhanced_audio/enhanced_audio.gni b/starboard/shared/enhanced_audio/enhanced_audio.gni
new file mode 100644
index 0000000..03588b8
--- /dev/null
+++ b/starboard/shared/enhanced_audio/enhanced_audio.gni
@@ -0,0 +1,21 @@
+# Copyright 2023 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+enhanced_audio_sources = [
+  "//starboard/shared/enhanced_audio/enhanced_audio.cc",
+  "//starboard/shared/enhanced_audio/enhanced_audio.h",
+  "//starboard/shared/enhanced_audio/enhanced_audio_player_write_samples.cc",
+  "//starboard/shared/enhanced_audio/enhanced_audio_player_write_samples.h",
+  "//starboard/shared/enhanced_audio/player_write_samples_checked.cc",
+]
diff --git a/starboard/shared/enhanced_audio/enhanced_audio.h b/starboard/shared/enhanced_audio/enhanced_audio.h
new file mode 100644
index 0000000..19f0729
--- /dev/null
+++ b/starboard/shared/enhanced_audio/enhanced_audio.h
@@ -0,0 +1,28 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_ENHANCED_AUDIO_ENHANCED_AUDIO_H_
+#define STARBOARD_SHARED_ENHANCED_AUDIO_ENHANCED_AUDIO_H_
+
+namespace starboard {
+namespace shared {
+namespace enhanced_audio {
+
+const void* GetEnhancedAudioApi();
+
+}  // namespace enhanced_audio
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_ENHANCED_AUDIO_ENHANCED_AUDIO_H_
diff --git a/starboard/shared/starboard/player/player_write_sample2.cc b/starboard/shared/enhanced_audio/enhanced_audio_player_write_samples.cc
similarity index 70%
copy from starboard/shared/starboard/player/player_write_sample2.cc
copy to starboard/shared/enhanced_audio/enhanced_audio_player_write_samples.cc
index d839aff..eb4794a 100644
--- a/starboard/shared/starboard/player/player_write_sample2.cc
+++ b/starboard/shared/enhanced_audio/enhanced_audio_player_write_samples.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,15 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "starboard/player.h"
+#include "starboard/shared/enhanced_audio/enhanced_audio_player_write_samples.h"
 
 #include "starboard/common/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
-void SbPlayerWriteSample2(SbPlayer player,
-                          SbMediaType sample_type,
-                          const SbPlayerSampleInfo* sample_infos,
-                          int number_of_sample_infos) {
+#if SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+namespace starboard {
+namespace shared {
+namespace enhanced_audio {
+
+void EnhancedAudioPlayerWriteSamples(
+    SbPlayer player,
+    SbMediaType sample_type,
+    const CobaltExtensionEnhancedAudioPlayerSampleInfo* sample_infos,
+    int number_of_sample_infos) {
   if (!SbPlayerIsValid(player)) {
     SB_LOG(WARNING) << "player is invalid.";
     return;
@@ -44,3 +51,9 @@
 
   player->WriteSamples(sample_infos, number_of_sample_infos);
 }
+
+}  // namespace enhanced_audio
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
diff --git a/starboard/shared/enhanced_audio/enhanced_audio_player_write_samples.h b/starboard/shared/enhanced_audio/enhanced_audio_player_write_samples.h
new file mode 100644
index 0000000..5dd3045
--- /dev/null
+++ b/starboard/shared/enhanced_audio/enhanced_audio_player_write_samples.h
@@ -0,0 +1,41 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_ENHANCED_AUDIO_ENHANCED_AUDIO_PLAYER_WRITE_SAMPLES_H_
+#define STARBOARD_SHARED_ENHANCED_AUDIO_ENHANCED_AUDIO_PLAYER_WRITE_SAMPLES_H_
+
+#include "starboard/extension/enhanced_audio.h"
+
+#include "starboard/media.h"
+#include "starboard/player.h"
+
+#if SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+namespace starboard {
+namespace shared {
+namespace enhanced_audio {
+
+void EnhancedAudioPlayerWriteSamples(
+    SbPlayer player,
+    SbMediaType sample_type,
+    const CobaltExtensionEnhancedAudioPlayerSampleInfo* sample_infos,
+    int number_of_sample_infos);
+
+}  // namespace enhanced_audio
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+#endif  // STARBOARD_SHARED_ENHANCED_AUDIO_ENHANCED_AUDIO_PLAYER_WRITE_SAMPLES_H_
diff --git a/starboard/shared/stub/player_write_sample2.cc b/starboard/shared/enhanced_audio/player_write_samples_checked.cc
similarity index 60%
copy from starboard/shared/stub/player_write_sample2.cc
copy to starboard/shared/enhanced_audio/player_write_samples_checked.cc
index 50ec297..1570cd3 100644
--- a/starboard/shared/stub/player_write_sample2.cc
+++ b/starboard/shared/enhanced_audio/player_write_samples_checked.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,7 +14,18 @@
 
 #include "starboard/player.h"
 
+#include "starboard/common/log.h"
+
+// Special implementation of `SbPlayerWriteSample2()` for EnhancedAudio, where
+// all sample writes should use the `PlayerWriteSamples()` function provided by
+// the EnhancedAudio extension.
+#if SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
 void SbPlayerWriteSample2(SbPlayer player,
                           SbMediaType sample_type,
                           const SbPlayerSampleInfo* sample_infos,
-                          int number_of_sample_infos) {}
+                          int number_of_sample_infos) {
+  SB_NOTREACHED();
+}
+
+#endif  // SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
diff --git a/starboard/shared/ffmpeg/BUILD.gn b/starboard/shared/ffmpeg/BUILD.gn
index 2482113..ec30571 100644
--- a/starboard/shared/ffmpeg/BUILD.gn
+++ b/starboard/shared/ffmpeg/BUILD.gn
@@ -23,6 +23,7 @@
 ]
 
 static_library("ffmpeg_dynamic_load") {
+  check_includes = false
   sources = [
     "ffmpeg_dynamic_load_audio_decoder_impl.cc",
     "ffmpeg_dynamic_load_demuxer_impl.cc",
diff --git a/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h b/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
index f2f9231..4535cce 100644
--- a/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
+++ b/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -17,6 +17,7 @@
 
 #include "starboard/media.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 
 namespace starboard {
@@ -25,9 +26,10 @@
 
 class AudioDecoder : public starboard::player::filter::AudioDecoder {
  public:
+  typedef starboard::media::AudioStreamInfo AudioStreamInfo;
+
   // Create an audio decoder for the currently loaded ffmpeg library.
-  static AudioDecoder* Create(SbMediaAudioCodec audio_codec,
-                              const SbMediaAudioSampleInfo& audio_sample_info);
+  static AudioDecoder* Create(const AudioStreamInfo& audio_stream_info);
   // Returns true if the audio decoder is initialized successfully.
   virtual bool is_valid() const = 0;
 };
diff --git a/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc b/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
index 4b21032..746fbad 100644
--- a/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
+++ b/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.cc
@@ -66,16 +66,15 @@
 }  // namespace
 
 AudioDecoderImpl<FFMPEG>::AudioDecoderImpl(
-    SbMediaAudioCodec audio_codec,
-    const SbMediaAudioSampleInfo& audio_sample_info)
-    : audio_codec_(audio_codec),
-      codec_context_(NULL),
+    const AudioStreamInfo& audio_stream_info)
+    : codec_context_(NULL),
       av_frame_(NULL),
       stream_ended_(false),
-      audio_sample_info_(audio_sample_info) {
+      audio_stream_info_(audio_stream_info) {
   SB_DCHECK(g_registered) << "Decoder Specialization registration failed.";
-  SB_DCHECK(GetFfmpegCodecIdByMediaCodec(audio_codec) != AV_CODEC_ID_NONE)
-      << "Unsupported audio codec " << audio_codec;
+  SB_DCHECK(GetFfmpegCodecIdByMediaCodec(audio_stream_info_.codec) !=
+            AV_CODEC_ID_NONE)
+      << "Unsupported audio codec " << audio_stream_info_.codec;
   ffmpeg_ = FFMPEGDispatch::GetInstance();
   SB_DCHECK(ffmpeg_);
   if ((ffmpeg_->specialization_version()) == FFMPEG) {
@@ -89,9 +88,8 @@
 
 // static
 AudioDecoder* AudioDecoderImpl<FFMPEG>::Create(
-    SbMediaAudioCodec audio_codec,
-    const SbMediaAudioSampleInfo& audio_sample_info) {
-  return new AudioDecoderImpl<FFMPEG>(audio_codec, audio_sample_info);
+    const AudioStreamInfo& audio_stream_info) {
+  return new AudioDecoderImpl<FFMPEG>(audio_stream_info);
 }
 
 void AudioDecoderImpl<FFMPEG>::Initialize(const OutputCB& output_cb,
@@ -155,7 +153,7 @@
   int decoded_audio_size = ffmpeg_->av_samples_get_buffer_size(
       NULL, codec_context_->channels, av_frame_->nb_samples,
       codec_context_->sample_fmt, 1);
-  audio_sample_info_.samples_per_second = codec_context_->sample_rate;
+  audio_stream_info_.samples_per_second = codec_context_->sample_rate;
 
   if (decoded_audio_size > 0) {
     scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
@@ -164,17 +162,23 @@
         codec_context_->channels * av_frame_->nb_samples *
             starboard::media::GetBytesPerSample(GetSampleType()));
     if (GetStorageType() == kSbMediaAudioFrameStorageTypeInterleaved) {
-      memcpy(decoded_audio->buffer(), *av_frame_->extended_data,
-             decoded_audio->size());
+      memcpy(decoded_audio->data(), *av_frame_->extended_data,
+             decoded_audio->size_in_bytes());
     } else {
       SB_DCHECK(GetStorageType() == kSbMediaAudioFrameStorageTypePlanar);
       const int per_channel_size_in_bytes =
-          decoded_audio->size() / decoded_audio->channels();
+          decoded_audio->size_in_bytes() / decoded_audio->channels();
       for (int i = 0; i < decoded_audio->channels(); ++i) {
-        memcpy(decoded_audio->buffer() + per_channel_size_in_bytes * i,
+        memcpy(decoded_audio->data() + per_channel_size_in_bytes * i,
                av_frame_->extended_data[i], per_channel_size_in_bytes);
       }
+      decoded_audio = decoded_audio->SwitchFormatTo(
+          GetSampleType(), kSbMediaAudioFrameStorageTypeInterleaved);
     }
+    decoded_audio->AdjustForDiscardedDurations(
+        audio_stream_info_.samples_per_second,
+        input_buffer->audio_sample_info().discarded_duration_from_front,
+        input_buffer->audio_sample_info().discarded_duration_from_back);
     decoded_audios_.push(decoded_audio);
     Schedule(output_cb_);
   } else {
@@ -207,7 +211,7 @@
     result = decoded_audios_.front();
     decoded_audios_.pop();
   }
-  *samples_per_second = audio_sample_info_.samples_per_second;
+  *samples_per_second = audio_stream_info_.samples_per_second;
   return result;
 }
 
@@ -267,7 +271,8 @@
   }
 
   codec_context_->codec_type = AVMEDIA_TYPE_AUDIO;
-  codec_context_->codec_id = GetFfmpegCodecIdByMediaCodec(audio_codec_);
+  codec_context_->codec_id =
+      GetFfmpegCodecIdByMediaCodec(audio_stream_info_.codec);
   // Request_sample_fmt is set by us, but sample_fmt is set by the decoder.
   if (GetSupportedSampleType() == kSbMediaAudioSampleTypeInt16Deprecated) {
     codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16;
@@ -275,23 +280,24 @@
     codec_context_->request_sample_fmt = AV_SAMPLE_FMT_FLT;
   }
 
-  codec_context_->channels = audio_sample_info_.number_of_channels;
-  codec_context_->sample_rate = audio_sample_info_.samples_per_second;
+  codec_context_->channels = audio_stream_info_.number_of_channels;
+  codec_context_->sample_rate = audio_stream_info_.samples_per_second;
   codec_context_->extradata = NULL;
   codec_context_->extradata_size = 0;
 
   if ((codec_context_->codec_id == AV_CODEC_ID_OPUS ||
        codec_context_->codec_id == AV_CODEC_ID_VORBIS) &&
-      audio_sample_info_.audio_specific_config_size > 0) {
+      !audio_stream_info_.audio_specific_config.empty()) {
     // AV_INPUT_BUFFER_PADDING_SIZE is not defined in ancient avcodec.h.  Use a
     // large enough padding here explicitly.
     const int kAvInputBufferPaddingSize = 256;
     codec_context_->extradata_size =
-        audio_sample_info_.audio_specific_config_size;
+        audio_stream_info_.audio_specific_config.size();
     codec_context_->extradata = static_cast<uint8_t*>(ffmpeg_->av_malloc(
         codec_context_->extradata_size + kAvInputBufferPaddingSize));
     SB_DCHECK(codec_context_->extradata);
-    memcpy(codec_context_->extradata, audio_sample_info_.audio_specific_config,
+    memcpy(codec_context_->extradata,
+           audio_stream_info_.audio_specific_config.data(),
            codec_context_->extradata_size);
     memset(codec_context_->extradata + codec_context_->extradata_size, 0,
            kAvInputBufferPaddingSize);
diff --git a/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h b/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h
index 9c4cc69..bb430cc 100644
--- a/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h
+++ b/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h
@@ -42,13 +42,11 @@
 class AudioDecoderImpl<FFMPEG> : public AudioDecoder,
                                  private starboard::player::JobQueue::JobOwner {
  public:
-  AudioDecoderImpl(SbMediaAudioCodec audio_codec,
-                   const SbMediaAudioSampleInfo& audio_sample_info);
+  explicit AudioDecoderImpl(const AudioStreamInfo& audio_stream_info);
   ~AudioDecoderImpl() override;
 
   // From: AudioDecoder
-  static AudioDecoder* Create(SbMediaAudioCodec audio_codec,
-                              const SbMediaAudioSampleInfo& audio_sample_info);
+  static AudioDecoder* Create(const AudioStreamInfo& audio_stream_info);
   bool is_valid() const override;
 
   // From: starboard::player::filter::AudioDecoder
@@ -71,13 +69,12 @@
   FFMPEGDispatch* ffmpeg_;
   OutputCB output_cb_;
   ErrorCB error_cb_;
-  SbMediaAudioCodec audio_codec_;
   AVCodecContext* codec_context_;
   AVFrame* av_frame_;
 
   bool stream_ended_;
   std::queue<scoped_refptr<DecodedAudio> > decoded_audios_;
-  starboard::media::AudioSampleInfo audio_sample_info_;
+  AudioStreamInfo audio_stream_info_;
 };
 
 }  // namespace ffmpeg
diff --git a/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl_interface.h b/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl_interface.h
index c780bac..37c9265 100644
--- a/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl_interface.h
+++ b/starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl_interface.h
@@ -18,6 +18,7 @@
 #include "starboard/media.h"
 #include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/media_util.h"
 
 namespace starboard {
 namespace shared {
@@ -28,8 +29,7 @@
 template <int V>
 class AudioDecoderImpl : public AudioDecoder {
  public:
-  static AudioDecoder* Create(SbMediaAudioCodec audio_codec,
-                              const SbMediaAudioSampleInfo& audio_sample_info);
+  static AudioDecoder* Create(const AudioStreamInfo& audio_stream_info);
 };
 
 }  // namespace ffmpeg
diff --git a/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc b/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc
index 74e4a8e..e277d11 100644
--- a/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc
+++ b/starboard/shared/ffmpeg/ffmpeg_dynamic_load_audio_decoder_impl.cc
@@ -21,15 +21,14 @@
 #include "starboard/player.h"
 #include "starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl_interface.h"
 #include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
+#include "starboard/shared/starboard/media/media_util.h"
 
 namespace starboard {
 namespace shared {
 namespace ffmpeg {
 
 // static
-AudioDecoder* AudioDecoder::Create(
-    SbMediaAudioCodec audio_codec,
-    const SbMediaAudioSampleInfo& audio_sample_info) {
+AudioDecoder* AudioDecoder::Create(const AudioStreamInfo& audio_stream_info) {
   FFMPEGDispatch* ffmpeg = FFMPEGDispatch::GetInstance();
   if (!ffmpeg || !ffmpeg->is_valid()) {
     return NULL;
@@ -38,21 +37,17 @@
   AudioDecoder* audio_decoder = NULL;
   switch (ffmpeg->specialization_version()) {
     case 540:
-      audio_decoder =
-          AudioDecoderImpl<540>::Create(audio_codec, audio_sample_info);
+      audio_decoder = AudioDecoderImpl<540>::Create(audio_stream_info);
       break;
     case 550:
     case 560:
-      audio_decoder =
-          AudioDecoderImpl<560>::Create(audio_codec, audio_sample_info);
+      audio_decoder = AudioDecoderImpl<560>::Create(audio_stream_info);
       break;
     case 571:
-      audio_decoder =
-          AudioDecoderImpl<571>::Create(audio_codec, audio_sample_info);
+      audio_decoder = AudioDecoderImpl<571>::Create(audio_stream_info);
       break;
     case 581:
-      audio_decoder =
-          AudioDecoderImpl<581>::Create(audio_codec, audio_sample_info);
+      audio_decoder = AudioDecoderImpl<581>::Create(audio_stream_info);
       break;
     default:
       SB_LOG(WARNING) << "Unsupported FFMPEG specialization "
diff --git a/starboard/shared/ffmpeg/ffmpeg_linked_audio_decoder_impl.cc b/starboard/shared/ffmpeg/ffmpeg_linked_audio_decoder_impl.cc
index 54c6c73..ef17071 100644
--- a/starboard/shared/ffmpeg/ffmpeg_linked_audio_decoder_impl.cc
+++ b/starboard/shared/ffmpeg/ffmpeg_linked_audio_decoder_impl.cc
@@ -22,20 +22,19 @@
 #include "starboard/shared/ffmpeg/ffmpeg_audio_decoder_impl.h"
 #include "starboard/shared/ffmpeg/ffmpeg_common.h"
 #include "starboard/shared/ffmpeg/ffmpeg_dispatch.h"
+#include "starboard/shared/starboard/media/media_util.h"
 
 namespace starboard {
 namespace shared {
 namespace ffmpeg {
 
 // static
-AudioDecoder* AudioDecoder::Create(
-    SbMediaAudioCodec audio_codec,
-    const SbMediaAudioSampleInfo& audio_sample_info) {
+AudioDecoder* AudioDecoder::Create(const AudioStreamInfo& audio_stream_info) {
   FFMPEGDispatch* ffmpeg = FFMPEGDispatch::GetInstance();
   SB_DCHECK(ffmpeg && ffmpeg->is_valid());
   SB_DCHECK(FFMPEG == ffmpeg->specialization_version());
 
-  return AudioDecoderImpl<FFMPEG>::Create(audio_codec, audio_sample_info);
+  return AudioDecoderImpl<FFMPEG>::Create(audio_stream_info);
 }
 
 }  // namespace ffmpeg
diff --git a/starboard/shared/libaom/aom_video_decoder.cc b/starboard/shared/libaom/aom_video_decoder.cc
index 2170661..2f51115 100644
--- a/starboard/shared/libaom/aom_video_decoder.cc
+++ b/starboard/shared/libaom/aom_video_decoder.cc
@@ -184,11 +184,11 @@
   SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
   SB_DCHECK(input_buffer);
 
-  const SbMediaVideoSampleInfo& sample_info = input_buffer->video_sample_info();
-  if (!context_ || sample_info.frame_width != current_frame_width_ ||
-      sample_info.frame_height != current_frame_height_) {
-    current_frame_width_ = sample_info.frame_width;
-    current_frame_height_ = sample_info.frame_height;
+  const auto& stream_info = input_buffer->video_stream_info();
+  if (!context_ || stream_info.frame_width != current_frame_width_ ||
+      stream_info.frame_height != current_frame_height_) {
+    current_frame_width_ = stream_info.frame_width;
+    current_frame_height_ = stream_info.frame_height;
     TeardownCodec();
     InitializeCodec();
   }
diff --git a/starboard/shared/libdav1d/dav1d_video_decoder.cc b/starboard/shared/libdav1d/dav1d_video_decoder.cc
index 95a1c08..d584643 100644
--- a/starboard/shared/libdav1d/dav1d_video_decoder.cc
+++ b/starboard/shared/libdav1d/dav1d_video_decoder.cc
@@ -212,11 +212,11 @@
   SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
   SB_DCHECK(input_buffer);
 
-  const SbMediaVideoSampleInfo& sample_info = input_buffer->video_sample_info();
-  if (!dav1d_context_ || sample_info.frame_width != current_frame_width_ ||
-      sample_info.frame_height != current_frame_height_) {
-    current_frame_width_ = sample_info.frame_width;
-    current_frame_height_ = sample_info.frame_height;
+  const auto& stream_info = input_buffer->video_stream_info();
+  if (!dav1d_context_ || stream_info.frame_width != current_frame_width_ ||
+      stream_info.frame_height != current_frame_height_) {
+    current_frame_width_ = stream_info.frame_width;
+    current_frame_height_ = stream_info.frame_height;
     TeardownCodec();
     InitializeCodec();
   }
diff --git a/starboard/shared/libfdkaac/fdk_aac_audio_decoder.cc b/starboard/shared/libfdkaac/fdk_aac_audio_decoder.cc
index 59ffd16..693c133 100644
--- a/starboard/shared/libfdkaac/fdk_aac_audio_decoder.cc
+++ b/starboard/shared/libfdkaac/fdk_aac_audio_decoder.cc
@@ -61,7 +61,7 @@
     return;
   }
 
-  timestamp_queue_.push(input_buffer->timestamp());
+  decoding_input_buffers_.push(input_buffer);
   Schedule(consumed_cb);
   ReadFromFdkDecoder(kDecodeModeDoNotFlush);
 }
@@ -92,7 +92,7 @@
   decoded_audios_ = std::queue<scoped_refptr<DecodedAudio>>();  // clear
   partially_decoded_audio_ = nullptr;
   partially_decoded_audio_data_in_bytes_ = 0;
-  timestamp_queue_ = std::queue<SbTime>();  // clear
+  decoding_input_buffers_ = decltype(decoding_input_buffers_)();  // clear
   // Clean up stream information and deduced results.
   has_stream_info_ = false;
   num_channels_ = 0;
@@ -107,7 +107,7 @@
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(output_cb_);
 
-  while (!timestamp_queue_.empty()) {
+  while (!decoding_input_buffers_.empty()) {
     if (!ReadFromFdkDecoder(kDecodeModeFlush)) {
       return;
     }
@@ -212,32 +212,40 @@
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(has_stream_info_);
 
-  while (size_in_bytes > 0 && !timestamp_queue_.empty()) {
+  while (size_in_bytes > 0 && !decoding_input_buffers_.empty()) {
     if (!partially_decoded_audio_) {
       SB_DCHECK(partially_decoded_audio_data_in_bytes_ == 0);
       partially_decoded_audio_ = new DecodedAudio(
           num_channels_, kSbMediaAudioSampleTypeInt16Deprecated,
-          kSbMediaAudioFrameStorageTypeInterleaved, timestamp_queue_.front(),
+          kSbMediaAudioFrameStorageTypeInterleaved,
+          decoding_input_buffers_.front()->timestamp(),
           decoded_audio_size_in_bytes_);
     }
-    int freespace = static_cast<int>(partially_decoded_audio_->size()) -
-                    partially_decoded_audio_data_in_bytes_;
+    int freespace =
+        static_cast<int>(partially_decoded_audio_->size_in_bytes()) -
+        partially_decoded_audio_data_in_bytes_;
     if (size_in_bytes >= freespace) {
-      memcpy(partially_decoded_audio_->buffer() +
+      memcpy(partially_decoded_audio_->data() +
                  partially_decoded_audio_data_in_bytes_,
              data, freespace);
       data += freespace;
       size_in_bytes -= freespace;
-      SB_DCHECK(timestamp_queue_.front() ==
+      SB_DCHECK(decoding_input_buffers_.front()->timestamp() ==
                 partially_decoded_audio_->timestamp());
-      timestamp_queue_.pop();
+
+      const auto& sample_info =
+          decoding_input_buffers_.front()->audio_sample_info();
+      partially_decoded_audio_->AdjustForDiscardedDurations(
+          samples_per_second_, sample_info.discarded_duration_from_front,
+          sample_info.discarded_duration_from_back);
+      decoding_input_buffers_.pop();
       decoded_audios_.push(partially_decoded_audio_);
       Schedule(output_cb_);
       partially_decoded_audio_ = nullptr;
       partially_decoded_audio_data_in_bytes_ = 0;
       continue;
     }
-    memcpy(partially_decoded_audio_->buffer() +
+    memcpy(partially_decoded_audio_->data() +
                partially_decoded_audio_data_in_bytes_,
            data, size_in_bytes);
     partially_decoded_audio_data_in_bytes_ += size_in_bytes;
diff --git a/starboard/shared/libfdkaac/fdk_aac_audio_decoder.h b/starboard/shared/libfdkaac/fdk_aac_audio_decoder.h
index 98a90c2..d25a3b9 100644
--- a/starboard/shared/libfdkaac/fdk_aac_audio_decoder.h
+++ b/starboard/shared/libfdkaac/fdk_aac_audio_decoder.h
@@ -76,8 +76,8 @@
   // once it's fully filled (and |output_cb_| will be called).
   scoped_refptr<DecodedAudio> partially_decoded_audio_;
   int partially_decoded_audio_data_in_bytes_ = 0;
-  // Keep timestamps for inputs, which will be used to create DecodedAudio.
-  std::queue<SbTime> timestamp_queue_;
+  // The input buffers being decoded.
+  std::queue<scoped_refptr<InputBuffer>> decoding_input_buffers_;
   // libfdkaac related parameters are listed below.
   HANDLE_AACDECODER decoder_ = nullptr;
   // There are two quirks of the fdk aac decoder:
diff --git a/starboard/shared/libvpx/vpx_video_decoder.cc b/starboard/shared/libvpx/vpx_video_decoder.cc
index d88a762..76d2d71 100644
--- a/starboard/shared/libvpx/vpx_video_decoder.cc
+++ b/starboard/shared/libvpx/vpx_video_decoder.cc
@@ -183,11 +183,11 @@
   SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
   SB_DCHECK(input_buffer);
 
-  const SbMediaVideoSampleInfo& sample_info = input_buffer->video_sample_info();
-  if (!context_ || sample_info.frame_width != current_frame_width_ ||
-      sample_info.frame_height != current_frame_height_) {
-    current_frame_width_ = sample_info.frame_width;
-    current_frame_height_ = sample_info.frame_height;
+  const auto& stream_info = input_buffer->video_stream_info();
+  if (!context_ || stream_info.frame_width != current_frame_width_ ||
+      stream_info.frame_height != current_frame_height_) {
+    current_frame_width_ = stream_info.frame_width;
+    current_frame_height_ = stream_info.frame_height;
     TeardownCodec();
     InitializeCodec();
   }
diff --git a/starboard/shared/openh264/openh264_video_decoder.cc b/starboard/shared/openh264/openh264_video_decoder.cc
index 7538611..55dc299 100644
--- a/starboard/shared/openh264/openh264_video_decoder.cc
+++ b/starboard/shared/openh264/openh264_video_decoder.cc
@@ -153,10 +153,9 @@
   SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
   SB_DCHECK(input_buffer);
 
-  const SbMediaVideoSampleInfo& sample_info = input_buffer->video_sample_info();
-  if (sample_info.is_key_frame) {
-    VideoConfig new_config(sample_info, input_buffer->data(),
-                           input_buffer->size());
+  if (input_buffer->video_sample_info().is_key_frame) {
+    VideoConfig new_config(input_buffer->video_stream_info(),
+                           input_buffer->data(), input_buffer->size());
     if (!video_config_ || video_config_.value() != new_config) {
       video_config_ = new_config;
       if (decoder_) {
diff --git a/starboard/shared/opus/opus_audio_decoder.cc b/starboard/shared/opus/opus_audio_decoder.cc
index 1a97dc2..4833964 100644
--- a/starboard/shared/opus/opus_audio_decoder.cc
+++ b/starboard/shared/opus/opus_audio_decoder.cc
@@ -14,6 +14,8 @@
 
 #include "starboard/shared/opus/opus_audio_decoder.h"
 
+#include <algorithm>
+
 #include "starboard/common/log.h"
 #include "starboard/common/string.h"
 
@@ -43,18 +45,17 @@
 
 }  // namespace
 
-OpusAudioDecoder::OpusAudioDecoder(
-    const SbMediaAudioSampleInfo& audio_sample_info)
-    : audio_sample_info_(audio_sample_info) {
+OpusAudioDecoder::OpusAudioDecoder(const AudioStreamInfo& audio_stream_info)
+    : audio_stream_info_(audio_stream_info) {
   int error;
-  int channels = audio_sample_info_.number_of_channels;
+  int channels = audio_stream_info_.number_of_channels;
   if (channels > 8 || channels < 1) {
     SB_LOG(ERROR) << "Can't create decoder with " << channels << " channels";
     return;
   }
 
   decoder_ = opus_multistream_decoder_create(
-      audio_sample_info_.samples_per_second, channels,
+      audio_stream_info_.samples_per_second, channels,
       vorbis_mappings[channels - 1].nb_streams,
       vorbis_mappings[channels - 1].nb_coupled_streams,
       vorbis_mappings[channels - 1].mapping, &error);
@@ -89,19 +90,51 @@
                               const ConsumedCB& consumed_cb) {
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(!input_buffers.empty());
+  SB_DCHECK(pending_audio_buffers_.empty());
   SB_DCHECK(output_cb_);
 
   if (stream_ended_) {
     SB_LOG(ERROR) << "Decode() is called after WriteEndOfStream() is called.";
     return;
   }
+  if (input_buffers.size() > kMinimumBuffersToDecode) {
+    std::copy(std::begin(input_buffers), std::end(input_buffers),
+              std::back_inserter(pending_audio_buffers_));
+    consumed_cb_ = consumed_cb;
+    DecodePendingBuffers();
+  } else {
+    for (const auto& input_buffer : input_buffers) {
+      if (!DecodeInternal(input_buffer)) {
+        return;
+      }
+    }
+    Schedule(consumed_cb);
+  }
+}
 
-  for (const auto& input_buffer : input_buffers) {
-    if (!DecodeInternal(input_buffer)) {
+void OpusAudioDecoder::DecodePendingBuffers() {
+  SB_DCHECK(BelongsToCurrentThread());
+  SB_DCHECK(!pending_audio_buffers_.empty());
+  SB_DCHECK(consumed_cb_);
+
+  for (int i = 0; i < kMinimumBuffersToDecode; ++i) {
+    if (!DecodeInternal(pending_audio_buffers_.front())) {
+      return;
+    }
+    pending_audio_buffers_.pop_front();
+    if (pending_audio_buffers_.empty()) {
+      Schedule(consumed_cb_);
+      consumed_cb_ = nullptr;
+      if (stream_ended_) {
+        Schedule(std::bind(&OpusAudioDecoder::WriteEndOfStream, this));
+        stream_ended_ = false;
+      }
       return;
     }
   }
-  Schedule(consumed_cb);
+
+  SB_DCHECK(!pending_audio_buffers_.empty());
+  Schedule(std::bind(&OpusAudioDecoder::DecodePendingBuffers, this));
 }
 
 bool OpusAudioDecoder::DecodeInternal(
@@ -109,12 +142,12 @@
   SB_DCHECK(BelongsToCurrentThread());
   SB_DCHECK(input_buffer);
   SB_DCHECK(output_cb_);
-  SB_DCHECK(!stream_ended_);
+  SB_DCHECK(!stream_ended_ || !pending_audio_buffers_.empty());
 
   scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
-      audio_sample_info_.number_of_channels, GetSampleType(),
+      audio_stream_info_.number_of_channels, GetSampleType(),
       kSbMediaAudioFrameStorageTypeInterleaved, input_buffer->timestamp(),
-      audio_sample_info_.number_of_channels * frames_per_au_ *
+      audio_stream_info_.number_of_channels * frames_per_au_ *
           starboard::media::GetBytesPerSample(GetSampleType()));
 
 #if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
@@ -122,13 +155,12 @@
   int decoded_frames = opus_multistream_decode(
       decoder_, static_cast<const unsigned char*>(input_buffer->data()),
       input_buffer->size(),
-      reinterpret_cast<opus_int16*>(decoded_audio->buffer()), frames_per_au_,
-      0);
+      reinterpret_cast<opus_int16*>(decoded_audio->data()), frames_per_au_, 0);
 #else   // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
   const char kDecodeFunctionName[] = "opus_multistream_decode_float";
   int decoded_frames = opus_multistream_decode_float(
       decoder_, static_cast<const unsigned char*>(input_buffer->data()),
-      input_buffer->size(), reinterpret_cast<float*>(decoded_audio->buffer()),
+      input_buffer->size(), reinterpret_cast<float*>(decoded_audio->data()),
       frames_per_au_, 0);
 #endif  // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
   if (decoded_frames == OPUS_BUFFER_TOO_SMALL &&
@@ -153,10 +185,14 @@
   }
 
   frames_per_au_ = decoded_frames;
-  decoded_audio->ShrinkTo(audio_sample_info_.number_of_channels *
+  decoded_audio->ShrinkTo(audio_stream_info_.number_of_channels *
                           frames_per_au_ *
                           starboard::media::GetBytesPerSample(GetSampleType()));
-
+  const auto& sample_info = input_buffer->audio_sample_info();
+  decoded_audio->AdjustForDiscardedDurations(
+      audio_stream_info_.samples_per_second,
+      sample_info.discarded_duration_from_front,
+      sample_info.discarded_duration_from_back);
   decoded_audios_.push(decoded_audio);
   output_cb_();
   return true;
@@ -169,6 +205,10 @@
   // Opus has no dependent frames so we needn't flush the decoder.  Set the
   // flag to ensure that Decode() is not called when the stream is ended.
   stream_ended_ = true;
+  if (!pending_audio_buffers_.empty()) {
+    return;
+  }
+
   // Put EOS into the queue.
   decoded_audios_.push(new DecodedAudio);
 
@@ -186,7 +226,7 @@
     result = decoded_audios_.front();
     decoded_audios_.pop();
   }
-  *samples_per_second = audio_sample_info_.samples_per_second;
+  *samples_per_second = audio_stream_info_.samples_per_second;
   return result;
 }
 
@@ -197,6 +237,8 @@
   while (!decoded_audios_.empty()) {
     decoded_audios_.pop();
   }
+  pending_audio_buffers_.clear();
+  consumed_cb_ = nullptr;
 
   CancelPendingJobs();
 }
diff --git a/starboard/shared/opus/opus_audio_decoder.h b/starboard/shared/opus/opus_audio_decoder.h
index 09687f3..ab3e947 100644
--- a/starboard/shared/opus/opus_audio_decoder.h
+++ b/starboard/shared/opus/opus_audio_decoder.h
@@ -15,6 +15,7 @@
 #ifndef STARBOARD_SHARED_OPUS_OPUS_AUDIO_DECODER_H_
 #define STARBOARD_SHARED_OPUS_OPUS_AUDIO_DECODER_H_
 
+#include <deque>
 #include <queue>
 #include <vector>
 
@@ -35,9 +36,9 @@
     : public ::starboard::shared::starboard::player::filter::AudioDecoder,
       private starboard::player::JobQueue::JobOwner {
  public:
-  typedef shared::starboard::media::AudioSampleInfo AudioSampleInfo;
+  typedef starboard::media::AudioStreamInfo AudioStreamInfo;
 
-  explicit OpusAudioDecoder(const SbMediaAudioSampleInfo& audio_sample_info);
+  explicit OpusAudioDecoder(const AudioStreamInfo& audio_stream_info);
   ~OpusAudioDecoder() override;
 
   bool is_valid() const;
@@ -51,6 +52,9 @@
   void Reset() override;
 
  private:
+  static constexpr int kMinimumBuffersToDecode = 2;
+
+  void DecodePendingBuffers();
   bool DecodeInternal(const scoped_refptr<InputBuffer>& input_buffer);
   static const int kMaxOpusFramesPerAU = 9600;
 
@@ -61,9 +65,12 @@
 
   OpusMSDecoder* decoder_ = NULL;
   bool stream_ended_ = false;
-  std::queue<scoped_refptr<DecodedAudio> > decoded_audios_;
-  AudioSampleInfo audio_sample_info_;
+  std::queue<scoped_refptr<DecodedAudio>> decoded_audios_;
+  AudioStreamInfo audio_stream_info_;
   int frames_per_au_ = kMaxOpusFramesPerAU;
+
+  std::deque<scoped_refptr<InputBuffer>> pending_audio_buffers_;
+  ConsumedCB consumed_cb_;
 };
 
 }  // namespace opus
diff --git a/starboard/shared/pulse/pulse_audio_sink_type.cc b/starboard/shared/pulse/pulse_audio_sink_type.cc
index 1b51c18..ab75c85 100644
--- a/starboard/shared/pulse/pulse_audio_sink_type.cc
+++ b/starboard/shared/pulse/pulse_audio_sink_type.cc
@@ -20,8 +20,8 @@
 #include <memory>
 #include <vector>
 
-#include "starboard/atomic.h"
 #include "starboard/audio_sink.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/log.h"
 #include "starboard/common/mutex.h"
 #include "starboard/shared/pulse/pulse_dynamic_load_dispatcher.h"
diff --git a/starboard/shared/speechd/speech_synthesis_speak.cc b/starboard/shared/speechd/speech_synthesis_speak.cc
index 2ea21f3..f574f16 100644
--- a/starboard/shared/speechd/speech_synthesis_speak.cc
+++ b/starboard/shared/speechd/speech_synthesis_speak.cc
@@ -19,6 +19,9 @@
 using starboard::shared::speechd::SpeechDispatcher;
 
 void SbSpeechSynthesisSpeak(const char* text) {
+  if (!text) {
+    return;
+  }
   SpeechDispatcher* speech_dispatcher = SpeechDispatcher::Get();
   if (speech_dispatcher) {
     speech_dispatcher->Speak(text);
diff --git a/starboard/shared/starboard/application.cc b/starboard/shared/starboard/application.cc
index e5f670e..c70e433 100644
--- a/starboard/shared/starboard/application.cc
+++ b/starboard/shared/starboard/application.cc
@@ -44,7 +44,11 @@
   SbEvent event;
   event.type = type;
   event.data = data;
+#if SB_MODULAR_BUILD
+  Application::Get()->sb_event_handle_callback_(&event);
+#else
   SbEventHandle(&event);
+#endif  // SB_MODULAR_BUILD
   if (destructor) {
     destructor(event.data);
   }
@@ -65,11 +69,22 @@
 
 Application* Application::g_instance = NULL;
 
+#if SB_MODULAR_BUILD
+Application::Application(SbEventHandleCallback sb_event_handle_callback)
+    : error_level_(0),
+      thread_(SbThreadGetCurrent()),
+      start_link_(NULL),
+      state_(kStateUnstarted),
+      sb_event_handle_callback_(sb_event_handle_callback) {
+  SB_CHECK(sb_event_handle_callback_)
+      << "sb_event_handle_callback_ has not been set.";
+#else
 Application::Application()
     : error_level_(0),
       thread_(SbThreadGetCurrent()),
       start_link_(NULL),
       state_(kStateUnstarted) {
+#endif  // SB_MODULAR_BUILD
   Application* old_instance =
       reinterpret_cast<Application*>(SbAtomicAcquire_CompareAndSwapPtr(
           reinterpret_cast<SbAtomicPtr*>(&g_instance),
@@ -274,10 +289,10 @@
   // Ensure the event is deleted unless it is released.
   scoped_ptr<Event> scoped_event(event);
 
-// Ensure that we go through the the appropriate lifecycle events based on
-// the current state. If intermediate events need to be processed, use
-// HandleEventAndUpdateState() rather than Inject() for the intermediate events
-// because there may already be other lifecycle events in the queue.
+  // Ensure that we go through the the appropriate lifecycle events based on
+  // the current state. If intermediate events need to be processed, use
+  // HandleEventAndUpdateState() rather than Inject() for the intermediate
+  // events because there may already be other lifecycle events in the queue.
 
 #if SB_API_VERSION >= 13
   SbTimeMonotonic timestamp = scoped_event->event->timestamp;
@@ -539,7 +554,11 @@
 // OnSuspend() is called after SbEventHandle(kSbEventTypeSuspend).
 #endif  // SB_API_VERSION >= 13
 
+#if SB_MODULAR_BUILD
+  sb_event_handle_callback_(scoped_event->event);
+#else
   SbEventHandle(scoped_event->event);
+#endif  // SB_MODULAR_BUILD
 
 #if SB_API_VERSION >= 13
   switch (scoped_event->event->type) {
@@ -650,7 +669,7 @@
 
 #if SB_API_VERSION >= 13
   return new Event(type, timestamp, start_data, &DeleteStartData);
-#else  // SB_API_VERSION >= 13
+#else   // SB_API_VERSION >= 13
   return new Event(type, start_data, &DeleteStartData);
 #endif  // SB_API_VERSION >= 13
 }
diff --git a/starboard/shared/starboard/application.h b/starboard/shared/starboard/application.h
index 04bc51c..7720654 100644
--- a/starboard/shared/starboard/application.h
+++ b/starboard/shared/starboard/application.h
@@ -45,6 +45,11 @@
  public:
   typedef player::filter::VideoFrame VideoFrame;
 
+#if SB_MODULAR_BUILD
+  // Executes a SbEventHandle method callback.
+  SbEventHandleCallback sb_event_handle_callback_ = NULL;
+#endif  // SB_MODULAR_BUILD
+
   // You can use a void(void *) function to signal that a state-transition event
   // has completed.
   typedef SbEventDataDestructor EventHandledCallback;
@@ -198,7 +203,11 @@
     int error_level;
   };
 
+#if SB_MODULAR_BUILD
+  explicit Application(SbEventHandleCallback sb_event_handle_callback);
+#else
   Application();
+#endif  // SB_MODULAR_BUILD
   virtual ~Application();
 
   // Gets the current instance of the Application. DCHECKS if called before the
diff --git a/starboard/shared/starboard/link_receiver.cc b/starboard/shared/starboard/link_receiver.cc
index befd32c..d67735a 100644
--- a/starboard/shared/starboard/link_receiver.cc
+++ b/starboard/shared/starboard/link_receiver.cc
@@ -17,7 +17,7 @@
 #include <string>
 #include <unordered_map>
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/file.h"
 #include "starboard/common/log.h"
 #include "starboard/common/scoped_ptr.h"
@@ -270,8 +270,8 @@
   listen_socket_ =
       CreateListeningSocket(kSbSocketAddressTypeIpv4, specified_port_);
   if (!listen_socket_ || !listen_socket_->IsValid()) {
-      listen_socket_ = CreateListeningSocket(kSbSocketAddressTypeIpv6,
-                                             specified_port_);
+    listen_socket_ =
+        CreateListeningSocket(kSbSocketAddressTypeIpv6, specified_port_);
   }
   if (!listen_socket_ || !listen_socket_->IsValid()) {
     SB_LOG(WARNING) << "Unable to start LinkReceiver on port "
@@ -294,8 +294,7 @@
 
   char port_string[32] = {0};
   SbStringFormatF(port_string, SB_ARRAY_SIZE(port_string), "%d", actual_port_);
-  CreateTemporaryFile("link_receiver_port", port_string,
-                      strlen(port_string));
+  CreateTemporaryFile("link_receiver_port", port_string, strlen(port_string));
 
   if (!AddForAccept(listen_socket_.get())) {
     quit_.store(true);
diff --git a/starboard/shared/starboard/media/BUILD.gn b/starboard/shared/starboard/media/BUILD.gn
index 7baaf99..20cb3bf 100644
--- a/starboard/shared/starboard/media/BUILD.gn
+++ b/starboard/shared/starboard/media/BUILD.gn
@@ -25,6 +25,8 @@
     "//starboard/shared/starboard/media/media_util.h",
     "//starboard/shared/starboard/media/mime_supportability_cache.cc",
     "//starboard/shared/starboard/media/mime_supportability_cache.h",
+    "//starboard/shared/starboard/media/mime_type.cc",
+    "//starboard/shared/starboard/media/mime_type.h",
     "//starboard/shared/starboard/media/mime_util.cc",
     "//starboard/shared/starboard/media/mime_util.h",
     "//starboard/shared/starboard/media/parsed_mime_info.cc",
diff --git a/starboard/shared/starboard/media/codec_util.cc b/starboard/shared/starboard/media/codec_util.cc
index fa1c5bf..dcc0747 100644
--- a/starboard/shared/starboard/media/codec_util.cc
+++ b/starboard/shared/starboard/media/codec_util.cc
@@ -49,16 +49,14 @@
   }
 }
 
-VideoConfig::VideoConfig(const SbMediaVideoSampleInfo& video_sample_info,
+VideoConfig::VideoConfig(const VideoStreamInfo& video_stream_info,
                          const uint8_t* data,
                          size_t size)
-    : VideoConfig(video_sample_info.codec,
-                  video_sample_info.frame_width,
-                  video_sample_info.frame_height,
+    : VideoConfig(video_stream_info.codec,
+                  video_stream_info.frame_width,
+                  video_stream_info.frame_height,
                   data,
-                  size) {
-  SB_DCHECK(video_sample_info.is_key_frame);
-}
+                  size) {}
 
 bool VideoConfig::operator==(const VideoConfig& that) const {
   if (video_codec_ == kSbMediaVideoCodecNone &&
@@ -101,6 +99,11 @@
     return kSbMediaAudioCodecFlac;
   }
 #endif  // SB_API_VERSION >= 14
+#if SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
+  if (strncmp(codec, "iamf.", 5) == 0) {
+    return kSbMediaAudioCodecIamf;
+  }
+#endif  // SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
   return kSbMediaAudioCodecNone;
 }
 
diff --git a/starboard/shared/starboard/media/codec_util.h b/starboard/shared/starboard/media/codec_util.h
index fed5710..11f3102 100644
--- a/starboard/shared/starboard/media/codec_util.h
+++ b/starboard/shared/starboard/media/codec_util.h
@@ -21,6 +21,7 @@
 #include "starboard/media.h"
 #include "starboard/shared/internal_only.h"
 #include "starboard/shared/starboard/media/avc_util.h"
+#include "starboard/shared/starboard/media/media_util.h"
 
 namespace starboard {
 namespace shared {
@@ -39,7 +40,7 @@
               const uint8_t* data,
               size_t size);
 
-  VideoConfig(const SbMediaVideoSampleInfo& video_sample_info,
+  VideoConfig(const VideoStreamInfo& video_stream_info,
               const uint8_t* data,
               size_t size);
 
diff --git a/starboard/shared/starboard/media/codec_util_test.cc b/starboard/shared/starboard/media/codec_util_test.cc
index a319697..2a334be 100644
--- a/starboard/shared/starboard/media/codec_util_test.cc
+++ b/starboard/shared/starboard/media/codec_util_test.cc
@@ -42,19 +42,19 @@
   return result;
 }
 
-TEST(VideoConfigTest, CtorWithSbMediaVideoSampleInfo) {
-  SbMediaVideoSampleInfo video_sample_info = {kSbMediaVideoCodecH264};
-  video_sample_info.is_key_frame = true;
-  video_sample_info.frame_width = 1920;
-  video_sample_info.frame_height = 1080;
+TEST(VideoConfigTest, CtorWithVideoStreamInfo) {
+  VideoStreamInfo video_stream_info;
+  video_stream_info.codec = kSbMediaVideoCodecH264;
+  video_stream_info.frame_width = 1920;
+  video_stream_info.frame_height = 1080;
 
   std::vector<uint8_t> nalus_in_annex_b =
       kSpsInAnnexB + kPpsInAnnexB + kIdrInAnnexB;
 
-  VideoConfig config_1(video_sample_info.codec, video_sample_info.frame_width,
-                       video_sample_info.frame_height, nalus_in_annex_b.data(),
+  VideoConfig config_1(video_stream_info.codec, video_stream_info.frame_width,
+                       video_stream_info.frame_height, nalus_in_annex_b.data(),
                        nalus_in_annex_b.size());
-  VideoConfig config_2(video_sample_info, nalus_in_annex_b.data(),
+  VideoConfig config_2(video_stream_info, nalus_in_annex_b.data(),
                        nalus_in_annex_b.size());
   ASSERT_TRUE(config_1 == config_2);
 }
diff --git a/starboard/shared/starboard/media/media_util.cc b/starboard/shared/starboard/media/media_util.cc
index 8ee17fc..9731003 100644
--- a/starboard/shared/starboard/media/media_util.cc
+++ b/starboard/shared/starboard/media/media_util.cc
@@ -14,6 +14,7 @@
 
 #include "starboard/shared/starboard/media/media_util.h"
 
+#include <algorithm>
 #include <cctype>
 
 #include "starboard/character.h"
@@ -34,65 +35,289 @@
 const int64_t kDefaultBitRate = 0;
 const int64_t kDefaultAudioChannels = 2;
 
-}  // namespace
+template <typename StreamInfo>
+void Assign(const StreamInfo& source, AudioStreamInfo* dest) {
+  SB_DCHECK(dest);
 
-AudioSampleInfo::AudioSampleInfo() {
-  memset(this, 0, sizeof(SbMediaAudioSampleInfo));
-  codec = kSbMediaAudioCodecNone;
+  if (source.audio_specific_config_size > 0) {
+    SB_DCHECK(source.audio_specific_config);
+  }
+
+  dest->codec = source.codec;
+
+  if (dest->codec == kSbMediaAudioCodecNone) {
+    dest->mime.clear();
+    dest->audio_specific_config.clear();
+    return;
+  }
+
+  SB_DCHECK(source.mime);
+
+  dest->mime = source.mime;
+  dest->number_of_channels = source.number_of_channels;
+  dest->samples_per_second = source.samples_per_second;
+  dest->bits_per_sample = source.bits_per_sample;
+
+  auto config = static_cast<const uint8_t*>(source.audio_specific_config);
+  dest->audio_specific_config.assign(
+      config, config + source.audio_specific_config_size);
 }
 
-AudioSampleInfo::AudioSampleInfo(const SbMediaAudioSampleInfo& that) {
-  *this = that;
+template <typename StreamInfo>
+void Assign(const StreamInfo& source, VideoStreamInfo* dest) {
+  SB_DCHECK(dest);
+
+  dest->codec = source.codec;
+
+  if (dest->codec == kSbMediaVideoCodecNone) {
+    dest->mime.clear();
+    dest->max_video_capabilities.clear();
+    return;
+  }
+
+  SB_DCHECK(source.mime);
+  SB_DCHECK(source.max_video_capabilities);
+
+  dest->mime = source.mime;
+  dest->max_video_capabilities = source.max_video_capabilities;
+  dest->frame_width = source.frame_width;
+  dest->frame_height = source.frame_height;
+  dest->color_metadata = source.color_metadata;
+}
+
+template <typename StreamInfo>
+void Assign(const AudioStreamInfo& source, StreamInfo* dest) {
+  SB_DCHECK(dest);
+
+  *dest = {};
+
+  dest->codec = source.codec;
+  dest->mime = source.mime.c_str();
+  dest->number_of_channels = source.number_of_channels;
+  dest->samples_per_second = source.samples_per_second;
+  dest->bits_per_sample = source.bits_per_sample;
+  if (!source.audio_specific_config.empty()) {
+    dest->audio_specific_config = source.audio_specific_config.data();
+    dest->audio_specific_config_size =
+        static_cast<uint16_t>(source.audio_specific_config.size());
+  }
+}
+
+template <typename StreamInfo>
+void Assign(const VideoStreamInfo& source, StreamInfo* dest) {
+  SB_DCHECK(dest);
+
+  *dest = {};
+
+  dest->codec = source.codec;
+  dest->mime = source.mime.c_str();
+  dest->max_video_capabilities = source.max_video_capabilities.c_str();
+  dest->frame_width = source.frame_width;
+  dest->frame_height = source.frame_height;
+  dest->color_metadata = source.color_metadata;
+}
+
+}  // namespace
+
+AudioStreamInfo& AudioStreamInfo::operator=(
+    const SbMediaAudioStreamInfo& that) {
+  Assign(that, this);
+  return *this;
+}
+
+AudioStreamInfo& AudioStreamInfo::operator=(
+    const CobaltExtensionEnhancedAudioMediaAudioStreamInfo& that) {
+  Assign(that, this);
+  return *this;
+}
+
+void AudioStreamInfo::ConvertTo(
+    SbMediaAudioStreamInfo* audio_stream_info) const {
+  Assign(*this, audio_stream_info);
+
+#if SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SB_DCHECK(audio_stream_info);
+  audio_stream_info->format_tag = 0xff;
+  audio_stream_info->block_alignment = 4;
+  audio_stream_info->average_bytes_per_second =
+      audio_stream_info->samples_per_second *
+      audio_stream_info->number_of_channels *
+      audio_stream_info->bits_per_sample / 8;
+#endif  // SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+}
+
+void AudioStreamInfo::ConvertTo(
+    CobaltExtensionEnhancedAudioMediaAudioStreamInfo* audio_stream_info) const {
+  Assign(*this, audio_stream_info);
+}
+
+bool operator==(const AudioStreamInfo& left, const AudioStreamInfo& right) {
+  if (left.codec == kSbMediaAudioCodecNone &&
+      right.codec == kSbMediaAudioCodecNone) {
+    return true;
+  }
+
+  return left.codec == right.codec && left.mime == right.mime &&
+         left.number_of_channels == right.number_of_channels &&
+         left.samples_per_second == right.samples_per_second &&
+         left.bits_per_sample == right.bits_per_sample &&
+         left.audio_specific_config == right.audio_specific_config;
+}
+
+bool operator!=(const AudioStreamInfo& left, const AudioStreamInfo& right) {
+  return !(left == right);
 }
 
 AudioSampleInfo& AudioSampleInfo::operator=(
     const SbMediaAudioSampleInfo& that) {
-  *static_cast<SbMediaAudioSampleInfo*>(this) = that;
-  if (audio_specific_config_size > 0) {
-    audio_specific_config_storage.resize(audio_specific_config_size);
-    memcpy(audio_specific_config_storage.data(), audio_specific_config,
-           audio_specific_config_size);
-    audio_specific_config = audio_specific_config_storage.data();
-  }
-
-  if (codec == kSbMediaAudioCodecNone) {
-    mime_storage.clear();
-  } else {
-    SB_DCHECK(that.mime);
-    mime_storage = that.mime;
-  }
-  mime = mime_storage.c_str();
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  stream_info = that.stream_info;
+  discarded_duration_from_front = that.discarded_duration_from_front;
+  discarded_duration_from_back = that.discarded_duration_from_back;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  stream_info = that;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 
   return *this;
 }
 
-VideoSampleInfo::VideoSampleInfo() {
-  memset(this, 0, sizeof(SbMediaVideoSampleInfo));
-  codec = kSbMediaVideoCodecNone;
+AudioSampleInfo& AudioSampleInfo::operator=(
+    const CobaltExtensionEnhancedAudioMediaAudioSampleInfo& that) {
+  stream_info = that.stream_info;
+  discarded_duration_from_front = that.discarded_duration_from_front;
+  discarded_duration_from_back = that.discarded_duration_from_back;
+  return *this;
 }
 
-VideoSampleInfo::VideoSampleInfo(const SbMediaVideoSampleInfo& that) {
-  *this = that;
+void AudioSampleInfo::ConvertTo(
+    SbMediaAudioSampleInfo* audio_sample_info) const {
+  SB_DCHECK(audio_sample_info);
+
+  *audio_sample_info = {};
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  stream_info.ConvertTo(&audio_sample_info->stream_info);
+  audio_sample_info->discarded_duration_from_front =
+      discarded_duration_from_front;
+  audio_sample_info->discarded_duration_from_back =
+      discarded_duration_from_back;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  stream_info.ConvertTo(audio_sample_info);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+}
+
+void AudioSampleInfo::ConvertTo(
+    CobaltExtensionEnhancedAudioMediaAudioSampleInfo* audio_sample_info) const {
+  SB_DCHECK(audio_sample_info);
+
+  *audio_sample_info = {};
+  stream_info.ConvertTo(&audio_sample_info->stream_info);
+  audio_sample_info->discarded_duration_from_front =
+      discarded_duration_from_front;
+  audio_sample_info->discarded_duration_from_back =
+      discarded_duration_from_back;
+}
+
+VideoStreamInfo& VideoStreamInfo::operator=(
+    const SbMediaVideoStreamInfo& that) {
+  Assign(that, this);
+  return *this;
+}
+
+VideoStreamInfo& VideoStreamInfo::operator=(
+    const CobaltExtensionEnhancedAudioMediaVideoStreamInfo& that) {
+  Assign(that, this);
+  return *this;
+}
+
+void VideoStreamInfo::ConvertTo(
+    SbMediaVideoStreamInfo* video_stream_info) const {
+  Assign(*this, video_stream_info);
+}
+
+void VideoStreamInfo::ConvertTo(
+    CobaltExtensionEnhancedAudioMediaVideoStreamInfo* video_stream_info) const {
+  Assign(*this, video_stream_info);
+}
+
+bool operator==(const VideoStreamInfo& left, const VideoStreamInfo& right) {
+  if (left.codec == kSbMediaVideoCodecNone &&
+      right.codec == kSbMediaVideoCodecNone) {
+    return true;
+  }
+
+  return left.codec == right.codec && left.mime == right.mime &&
+         left.max_video_capabilities == right.max_video_capabilities &&
+         left.frame_width == right.frame_width &&
+         left.frame_height == right.frame_height &&
+         left.color_metadata == right.color_metadata;
+}
+
+bool operator!=(const VideoStreamInfo& left, const VideoStreamInfo& right) {
+  return !(left == right);
 }
 
 VideoSampleInfo& VideoSampleInfo::operator=(
     const SbMediaVideoSampleInfo& that) {
-  *static_cast<SbMediaVideoSampleInfo*>(this) = that;
-
-  if (codec == kSbMediaVideoCodecNone) {
-    mime_storage.clear();
-    max_video_capabilities_storage.clear();
-  } else {
-    SB_DCHECK(that.mime);
-    mime_storage = that.mime;
-    max_video_capabilities_storage = that.max_video_capabilities;
-  }
-  mime = mime_storage.c_str();
-  max_video_capabilities = max_video_capabilities_storage.c_str();
-
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  stream_info = that.stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  stream_info = that;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  is_key_frame = that.is_key_frame;
   return *this;
 }
 
+VideoSampleInfo& VideoSampleInfo::operator=(
+    const CobaltExtensionEnhancedAudioMediaVideoSampleInfo& that) {
+  stream_info = that.stream_info;
+  is_key_frame = that.is_key_frame;
+  return *this;
+}
+
+void VideoSampleInfo::ConvertTo(
+    SbMediaVideoSampleInfo* video_sample_info) const {
+  SB_DCHECK(video_sample_info);
+
+  *video_sample_info = {};
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  stream_info.ConvertTo(&video_sample_info->stream_info);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  stream_info.ConvertTo(video_sample_info);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  video_sample_info->is_key_frame = is_key_frame;
+}
+
+void VideoSampleInfo::ConvertTo(
+    CobaltExtensionEnhancedAudioMediaVideoSampleInfo* video_sample_info) const {
+  SB_DCHECK(video_sample_info);
+
+  *video_sample_info = {};
+  stream_info.ConvertTo(&video_sample_info->stream_info);
+  video_sample_info->is_key_frame = is_key_frame;
+}
+
+std::ostream& operator<<(std::ostream& os, const VideoSampleInfo& sample_info) {
+  const auto& stream_info = sample_info.stream_info;
+
+  if (stream_info.codec == kSbMediaVideoCodecNone) {
+    return os << "codec: " << GetMediaVideoCodecName(stream_info.codec);
+  }
+
+  os << "codec: " << GetMediaVideoCodecName(stream_info.codec) << ", ";
+  os << "mime: " << stream_info.mime
+     << ", max video capabilities: " << stream_info.max_video_capabilities
+     << ", ";
+
+  if (sample_info.is_key_frame) {
+    os << "key frame, ";
+  }
+
+  os << stream_info.frame_width << 'x' << stream_info.frame_height << ' ';
+  os << '(' << stream_info.color_metadata << ')';
+
+  return os;
+}
+
 bool IsSDRVideo(int bit_depth,
                 SbMediaPrimaryId primary_id,
                 SbMediaTransferId transfer_id,
@@ -220,15 +445,26 @@
   return result;
 }
 
-bool IsAudioSampleInfoSubstantiallyDifferent(
-    const SbMediaAudioSampleInfo& left,
-    const SbMediaAudioSampleInfo& right) {
+bool IsAudioSampleInfoSubstantiallyDifferent(const AudioStreamInfo& left,
+                                             const AudioStreamInfo& right) {
   return left.codec != right.codec ||
          left.samples_per_second != right.samples_per_second ||
          left.number_of_channels != right.number_of_channels ||
-         left.audio_specific_config_size != right.audio_specific_config_size ||
-         memcmp(left.audio_specific_config, right.audio_specific_config,
-                left.audio_specific_config_size) != 0;
+         left.audio_specific_config != right.audio_specific_config;
+}
+
+int AudioDurationToFrames(SbTime duration, int samples_per_second) {
+  SB_DCHECK(samples_per_second > 0)
+      << "samples_per_second has to be greater than 0";
+  // The same as `frames = (duration / kSbTimeSecond) * samples_per_second`,
+  // switch order to avoid precision loss due to integer division.
+  return duration * samples_per_second / kSbTimeSecond;
+}
+
+SbTime AudioFramesToDuration(int frames, int samples_per_second) {
+  SB_DCHECK(samples_per_second > 0)
+      << "samples_per_second has to be greater than 0";
+  return frames * kSbTimeSecond / std::max(samples_per_second, 1);
 }
 
 }  // namespace media
@@ -243,33 +479,70 @@
 
 bool operator==(const SbMediaVideoSampleInfo& sample_info_1,
                 const SbMediaVideoSampleInfo& sample_info_2) {
-  if (sample_info_1.codec != sample_info_2.codec) {
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaVideoStreamInfo& stream_info_1 = sample_info_1.stream_info;
+  const SbMediaVideoStreamInfo& stream_info_2 = sample_info_2.stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaVideoStreamInfo& stream_info_1 = sample_info_1;
+  const SbMediaVideoStreamInfo& stream_info_2 = sample_info_2;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+  if (stream_info_1.codec != stream_info_2.codec) {
     return false;
   }
-  if (sample_info_1.codec == kSbMediaVideoCodecNone) {
+  if (stream_info_1.codec == kSbMediaVideoCodecNone) {
     return true;
   }
 
-  if (strcmp(sample_info_1.mime, sample_info_2.mime) != 0) {
+  if (strcmp(stream_info_1.mime, stream_info_2.mime) != 0) {
     return false;
   }
-  if (strcmp(sample_info_1.max_video_capabilities,
-             sample_info_2.max_video_capabilities) != 0) {
+  if (strcmp(stream_info_1.max_video_capabilities,
+             stream_info_2.max_video_capabilities) != 0) {
     return false;
   }
 
   if (sample_info_1.is_key_frame != sample_info_2.is_key_frame) {
     return false;
   }
-  if (sample_info_1.frame_width != sample_info_2.frame_width) {
+  if (stream_info_1.frame_width != stream_info_2.frame_width) {
     return false;
   }
-  if (sample_info_1.frame_height != sample_info_2.frame_height) {
+  if (stream_info_1.frame_height != stream_info_2.frame_height) {
     return false;
   }
-  return sample_info_1.color_metadata == sample_info_2.color_metadata;
+  return stream_info_1.color_metadata == stream_info_2.color_metadata;
 }
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+bool operator==(const SbMediaVideoStreamInfo& stream_info_1,
+                const SbMediaVideoStreamInfo& stream_info_2) {
+  if (stream_info_1.codec != stream_info_2.codec) {
+    return false;
+  }
+  if (stream_info_1.codec == kSbMediaVideoCodecNone) {
+    return true;
+  }
+
+  if (strcmp(stream_info_1.mime, stream_info_2.mime) != 0) {
+    return false;
+  }
+  if (strcmp(stream_info_1.max_video_capabilities,
+             stream_info_2.max_video_capabilities) != 0) {
+    return false;
+  }
+  if (stream_info_1.frame_width != stream_info_2.frame_width) {
+    return false;
+  }
+  if (stream_info_1.frame_height != stream_info_2.frame_height) {
+    return false;
+  }
+  return stream_info_1.color_metadata == stream_info_2.color_metadata;
+}
+
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
 bool operator!=(const SbMediaColorMetadata& metadata_1,
                 const SbMediaColorMetadata& metadata_2) {
   return !(metadata_1 == metadata_2);
@@ -279,3 +552,10 @@
                 const SbMediaVideoSampleInfo& sample_info_2) {
   return !(sample_info_1 == sample_info_2);
 }
+
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+bool operator!=(const SbMediaVideoStreamInfo& stream_info_1,
+                const SbMediaVideoStreamInfo& stream_info_2) {
+  return !(stream_info_1 == stream_info_2);
+}
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
diff --git a/starboard/shared/starboard/media/media_util.h b/starboard/shared/starboard/media/media_util.h
index 8a97986..02e178b 100644
--- a/starboard/shared/starboard/media/media_util.h
+++ b/starboard/shared/starboard/media/media_util.h
@@ -19,7 +19,9 @@
 #include <string>
 #include <vector>
 
+#include "starboard/extension/enhanced_audio.h"
 #include "starboard/media.h"
+#include "starboard/player.h"
 #include "starboard/shared/internal_only.h"
 
 namespace starboard {
@@ -27,26 +29,123 @@
 namespace starboard {
 namespace media {
 
-struct AudioSampleInfo : SbMediaAudioSampleInfo {
-  AudioSampleInfo();
-  explicit AudioSampleInfo(const SbMediaAudioSampleInfo& that);
+// Encapsulates all information contained in `SbMediaAudioStreamInfo`.  It
+// doesn't maintain the same binary layout as `SbMediaAudioStreamInfo`, and is
+// intended to be used across the codebase as a C++ wrapper that owns the memory
+// of all its pointer members.
+struct AudioStreamInfo {
+  AudioStreamInfo() = default;
+  template <typename StreamInfo>
+  explicit AudioStreamInfo(const StreamInfo& that) {
+    *this = that;
+  }
+  AudioStreamInfo& operator=(const SbMediaAudioStreamInfo& that);
+  AudioStreamInfo& operator=(
+      const CobaltExtensionEnhancedAudioMediaAudioStreamInfo& that);
+
+  void ConvertTo(SbMediaAudioStreamInfo* audio_stream_info) const;
+  void ConvertTo(CobaltExtensionEnhancedAudioMediaAudioStreamInfo*
+                     audio_stream_info) const;
+
+  // The member variables are the C++ mappings of the members of
+  // `SbMediaAudioStreamInfo` defined in `media.h`.  Please refer to the comment
+  // of `SbMediaAudioStreamInfo` for more details.
+  SbMediaAudioCodec codec = kSbMediaAudioCodecNone;
+  std::string mime;
+  uint16_t number_of_channels;
+  uint32_t samples_per_second;
+  uint16_t bits_per_sample;
+  std::vector<uint8_t> audio_specific_config;
+};
+
+bool operator==(const AudioStreamInfo& left, const AudioStreamInfo& right);
+bool operator!=(const AudioStreamInfo& left, const AudioStreamInfo& right);
+
+// Encapsulates all information contained in `SbMediaAudioSampleInfo`.  It
+// doesn't maintain the same binary layout as `SbMediaAudioSampleInfo`, and is
+// intended to be used across the codebase as a C++ wrapper that owns the memory
+// of all its pointer members.
+struct AudioSampleInfo {
+  AudioSampleInfo() = default;
+  template <typename SampleInfo>
+  explicit AudioSampleInfo(const SampleInfo& that) {
+    *this = that;
+  }
   AudioSampleInfo& operator=(const SbMediaAudioSampleInfo& that);
+  AudioSampleInfo& operator=(
+      const CobaltExtensionEnhancedAudioMediaAudioSampleInfo& that);
 
- private:
-  std::string mime_storage;
-  std::vector<char> audio_specific_config_storage;
+  void ConvertTo(SbMediaAudioSampleInfo* audio_sample_info) const;
+  void ConvertTo(CobaltExtensionEnhancedAudioMediaAudioSampleInfo*
+                     audio_sample_info) const;
+
+  // The member variables are the C++ mappings of the members of
+  // `SbMediaAudioSampleInfo` defined in `media.h`.  Please refer to the comment
+  // of `SbMediaAudioSampleInfo` for more details.
+  AudioStreamInfo stream_info;
+  SbTime discarded_duration_from_front = 0;
+  SbTime discarded_duration_from_back = 0;
 };
 
-struct VideoSampleInfo : SbMediaVideoSampleInfo {
-  VideoSampleInfo();
-  explicit VideoSampleInfo(const SbMediaVideoSampleInfo& that);
+// Encapsulates all information contained in `SbMediaVideoStreamInfo`.  It
+// doesn't maintain the same binary layout as `SbMediaVideoStreamInfo`, and is
+// intended to be used across the codebase as a C++ wrapper that owns the memory
+// of all its pointer members.
+struct VideoStreamInfo {
+  VideoStreamInfo() = default;
+  template <typename StreamInfo>
+  explicit VideoStreamInfo(const StreamInfo& that) {
+    *this = that;
+  }
+  VideoStreamInfo& operator=(const SbMediaVideoStreamInfo& that);
+  VideoStreamInfo& operator=(
+      const CobaltExtensionEnhancedAudioMediaVideoStreamInfo& that);
+
+  void ConvertTo(SbMediaVideoStreamInfo* video_stream_info) const;
+  void ConvertTo(CobaltExtensionEnhancedAudioMediaVideoStreamInfo*
+                     video_stream_info) const;
+
+  // The member variables are the C++ mappings of the members of
+  // `SbMediaVideoStreamInfo` defined in `media.h`.  Please refer to the comment
+  // of `SbMediaVideoStreamInfo` for more details.
+  SbMediaVideoCodec codec = kSbMediaVideoCodecNone;
+  std::string mime;
+  std::string max_video_capabilities;
+  int frame_width;
+  int frame_height;
+  SbMediaColorMetadata color_metadata;
+};
+
+bool operator==(const VideoStreamInfo& left, const VideoStreamInfo& right);
+bool operator!=(const VideoStreamInfo& left, const VideoStreamInfo& right);
+
+// Encapsulates all information contained in `SbMediaVideoSampleInfo`.  It
+// doesn't maintain the same binary layout as `SbMediaVideoSampleInfo`, and is
+// intended to be used across the codebase as a C++ wrapper that owns the memory
+// of all its pointer members.
+struct VideoSampleInfo {
+  VideoSampleInfo() = default;
+  template <typename SampleInfo>
+  explicit VideoSampleInfo(const SampleInfo& that) {
+    *this = that;
+  }
   VideoSampleInfo& operator=(const SbMediaVideoSampleInfo& that);
+  VideoSampleInfo& operator=(
+      const CobaltExtensionEnhancedAudioMediaVideoSampleInfo& that);
 
- private:
-  std::string mime_storage;
-  std::string max_video_capabilities_storage;
+  void ConvertTo(SbMediaVideoSampleInfo* video_sample_info) const;
+  void ConvertTo(CobaltExtensionEnhancedAudioMediaVideoSampleInfo*
+                     video_sample_info) const;
+
+  // The member variables are the C++ mappings of the members of
+  // `SbMediaVideoSampleInfo` defined in `media.h`.  Please refer to the comment
+  // of `SbMediaVideoSampleInfo` for more details.
+  VideoStreamInfo stream_info;
+  bool is_key_frame;
 };
 
+std::ostream& operator<<(std::ostream& os, const VideoSampleInfo& stream_info);
+
 bool IsSDRVideo(int bit_depth,
                 SbMediaPrimaryId primary_id,
                 SbMediaTransferId transfer_id,
@@ -62,9 +161,11 @@
 
 //  When this function returns true, usually indicates that the two sample info
 //  cannot be processed by the same audio decoder.
-bool IsAudioSampleInfoSubstantiallyDifferent(
-    const SbMediaAudioSampleInfo& left,
-    const SbMediaAudioSampleInfo& right);
+bool IsAudioSampleInfoSubstantiallyDifferent(const AudioStreamInfo& left,
+                                             const AudioStreamInfo& right);
+
+int AudioDurationToFrames(SbTime duration, int samples_per_second);
+SbTime AudioFramesToDuration(int frames, int samples_per_second);
 
 }  // namespace media
 }  // namespace starboard
@@ -76,9 +177,19 @@
 bool operator==(const SbMediaVideoSampleInfo& sample_info_1,
                 const SbMediaVideoSampleInfo& sample_info_2);
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+bool operator==(const SbMediaVideoStreamInfo& stream_info_1,
+                const SbMediaVideoStreamInfo& stream_info_2);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
 bool operator!=(const SbMediaColorMetadata& metadata_1,
                 const SbMediaColorMetadata& metadata_2);
 bool operator!=(const SbMediaVideoSampleInfo& sample_info_1,
                 const SbMediaVideoSampleInfo& sample_info_2);
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+bool operator!=(const SbMediaVideoStreamInfo& stream_info_1,
+                const SbMediaVideoStreamInfo& stream_info_2);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
 #endif  // STARBOARD_SHARED_STARBOARD_MEDIA_MEDIA_UTIL_H_
diff --git a/starboard/shared/starboard/media/media_util_test.cc b/starboard/shared/starboard/media/media_util_test.cc
index 5c048fd..c6a2a9d 100644
--- a/starboard/shared/starboard/media/media_util_test.cc
+++ b/starboard/shared/starboard/media/media_util_test.cc
@@ -14,6 +14,9 @@
 
 #include "starboard/shared/starboard/media/media_util.h"
 
+#include <vector>
+
+#include "starboard/extension/enhanced_audio.h"
 #include "starboard/media.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -23,14 +26,289 @@
 namespace media {
 namespace {
 
+std::vector<uint8_t> ToVector(const void* data, int size) {
+  if (size == 0) {
+    return std::vector<uint8_t>();
+  }
+  SB_CHECK(data);
+  const uint8_t* data_as_uint8 = static_cast<const uint8_t*>(data);
+  return std::vector<uint8_t>(data_as_uint8, data_as_uint8 + size);
+}
+
+TEST(AudioStreamInfoTest, DefaultCtor) {
+  AudioStreamInfo audio_stream_info;
+
+  EXPECT_EQ(audio_stream_info.codec, kSbMediaAudioCodecNone);
+  // No other members should be accessed if `codec` is `kSbMediaAudioCodecNone`,
+  // however we still want to make sure that the following members are empty.
+  EXPECT_TRUE(audio_stream_info.mime.empty());
+  EXPECT_TRUE(audio_stream_info.audio_specific_config.empty());
+}
+
+TEST(AudioStreamInfoTest, CodecNone) {
+  SbMediaAudioStreamInfo sb_media_audio_stream_info = {kSbMediaAudioCodecNone};
+
+  AudioStreamInfo audio_stream_info;
+  audio_stream_info = sb_media_audio_stream_info;
+  EXPECT_EQ(audio_stream_info.codec, kSbMediaAudioCodecNone);
+
+  audio_stream_info.ConvertTo(&sb_media_audio_stream_info);
+  EXPECT_EQ(sb_media_audio_stream_info.codec, kSbMediaAudioCodecNone);
+}
+
+TEST(AudioStreamInfoTest, SbMediaAudioStreamInfo) {
+  SbMediaAudioStreamInfo original = {};
+
+  original.codec = kSbMediaAudioCodecOpus;
+  original.mime = "audio/webm";
+  original.number_of_channels = 2;
+  original.samples_per_second = 24000;
+  original.bits_per_sample = 16;
+  original.audio_specific_config_size = 6;
+  original.audio_specific_config = "config";
+
+  AudioStreamInfo audio_stream_info(original);
+
+  EXPECT_EQ(original.codec, audio_stream_info.codec);
+  EXPECT_EQ(original.mime, audio_stream_info.mime);
+  EXPECT_EQ(original.number_of_channels, audio_stream_info.number_of_channels);
+  EXPECT_EQ(original.samples_per_second, audio_stream_info.samples_per_second);
+  EXPECT_EQ(original.bits_per_sample, audio_stream_info.bits_per_sample);
+  EXPECT_EQ(ToVector(original.audio_specific_config,
+                     original.audio_specific_config_size),
+            audio_stream_info.audio_specific_config);
+}
+
+TEST(AudioStreamInfoTest, CobaltExtensionEnhancedAudioMediaAudioStreamInfo) {
+  CobaltExtensionEnhancedAudioMediaAudioStreamInfo original = {};
+
+  original.codec = kSbMediaAudioCodecOpus;
+  original.mime = "audio/webm";
+  original.number_of_channels = 2;
+  original.samples_per_second = 24000;
+  original.bits_per_sample = 16;
+  original.audio_specific_config_size = 6;
+  original.audio_specific_config = "config";
+
+  AudioStreamInfo audio_stream_info(original);
+
+  EXPECT_EQ(original.codec, audio_stream_info.codec);
+  EXPECT_EQ(original.mime, audio_stream_info.mime);
+  EXPECT_EQ(original.number_of_channels, audio_stream_info.number_of_channels);
+  EXPECT_EQ(original.samples_per_second, audio_stream_info.samples_per_second);
+  EXPECT_EQ(original.bits_per_sample, audio_stream_info.bits_per_sample);
+  EXPECT_EQ(ToVector(original.audio_specific_config,
+                     original.audio_specific_config_size),
+            audio_stream_info.audio_specific_config);
+}
+
+TEST(VideoStreamInfoTest, DefaultCtor) {
+  VideoStreamInfo video_stream_info;
+
+  EXPECT_EQ(video_stream_info.codec, kSbMediaVideoCodecNone);
+  // No other members should be accessed if `codec` is `kSbMediaVideoCodecNone`,
+  // however we still want to make sure that the following members are empty.
+  EXPECT_TRUE(video_stream_info.mime.empty());
+  EXPECT_TRUE(video_stream_info.max_video_capabilities.empty());
+}
+
+TEST(VideoStreamInfoTest, CodecNone) {
+  SbMediaVideoStreamInfo sb_media_video_stream_info = {kSbMediaVideoCodecNone};
+
+  VideoStreamInfo video_stream_info;
+  video_stream_info = sb_media_video_stream_info;
+  EXPECT_EQ(video_stream_info.codec, kSbMediaVideoCodecNone);
+
+  video_stream_info.ConvertTo(&sb_media_video_stream_info);
+  EXPECT_EQ(sb_media_video_stream_info.codec, kSbMediaVideoCodecNone);
+}
+
+TEST(VideoStreamInfoTest, SbMediaVideoStreamInfo) {
+  SbMediaVideoStreamInfo original = {};
+
+  original.codec = kSbMediaVideoCodecAv1;
+  original.mime = "video/mp4";
+  original.max_video_capabilities = "width=3840";
+  original.frame_width = 1080;
+  original.frame_height = 1920;
+
+  VideoStreamInfo video_stream_info(original);
+
+  EXPECT_EQ(original.codec, video_stream_info.codec);
+  EXPECT_EQ(original.mime, video_stream_info.mime);
+  EXPECT_EQ(original.max_video_capabilities,
+            video_stream_info.max_video_capabilities);
+  EXPECT_EQ(original.frame_width, video_stream_info.frame_width);
+  EXPECT_EQ(original.frame_height, video_stream_info.frame_height);
+  EXPECT_EQ(original.color_metadata, video_stream_info.color_metadata);
+}
+
+TEST(VideoStreamInfoTest, CobaltExtensionEnhancedAudioMediaVideoStreamInfo) {
+  CobaltExtensionEnhancedAudioMediaVideoStreamInfo original = {};
+
+  original.codec = kSbMediaVideoCodecAv1;
+  original.mime = "video/mp4";
+  original.max_video_capabilities = "width=3840";
+  original.frame_width = 1080;
+  original.frame_height = 1920;
+
+  VideoStreamInfo video_stream_info(original);
+
+  EXPECT_EQ(original.codec, video_stream_info.codec);
+  EXPECT_EQ(original.mime, video_stream_info.mime);
+  EXPECT_EQ(original.max_video_capabilities,
+            video_stream_info.max_video_capabilities);
+  EXPECT_EQ(original.frame_width, video_stream_info.frame_width);
+  EXPECT_EQ(original.frame_height, video_stream_info.frame_height);
+  EXPECT_EQ(original.color_metadata, video_stream_info.color_metadata);
+}
+
+TEST(AudioSampleInfoTest, DefaultCtor) {
+  AudioSampleInfo audio_sample_info;
+
+  EXPECT_EQ(audio_sample_info.stream_info.codec, kSbMediaAudioCodecNone);
+  // No other members should be accessed if `codec` is `kSbMediaAudioCodecNone`,
+  // however we still want to make sure that the following members are empty.
+  EXPECT_TRUE(audio_sample_info.stream_info.mime.empty());
+  EXPECT_TRUE(audio_sample_info.stream_info.audio_specific_config.empty());
+}
+
+TEST(AudioSampleInfoTest, SbMediaAudioSampleInfo) {
+  SbMediaAudioSampleInfo original = {};
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbMediaAudioStreamInfo& stream_info = original.stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbMediaAudioStreamInfo& stream_info = original;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+  stream_info.codec = kSbMediaAudioCodecOpus;
+  stream_info.mime = "audio/webm";
+  stream_info.number_of_channels = 2;
+  stream_info.samples_per_second = 24000;
+  stream_info.bits_per_sample = 16;
+  stream_info.audio_specific_config_size = 6;
+  stream_info.audio_specific_config = "config";
+
+  AudioSampleInfo audio_sample_info(original);
+
+  EXPECT_EQ(stream_info.codec, audio_sample_info.stream_info.codec);
+  EXPECT_EQ(stream_info.mime, audio_sample_info.stream_info.mime);
+  EXPECT_EQ(stream_info.number_of_channels,
+            audio_sample_info.stream_info.number_of_channels);
+  EXPECT_EQ(stream_info.samples_per_second,
+            audio_sample_info.stream_info.samples_per_second);
+  EXPECT_EQ(stream_info.bits_per_sample,
+            audio_sample_info.stream_info.bits_per_sample);
+  EXPECT_EQ(ToVector(stream_info.audio_specific_config,
+                     stream_info.audio_specific_config_size),
+            audio_sample_info.stream_info.audio_specific_config);
+}
+
+TEST(AudioSampleInfoTest, CobaltExtensionEnhancedAudioMediaAudioSampleInfo) {
+  CobaltExtensionEnhancedAudioMediaAudioSampleInfo original = {};
+  CobaltExtensionEnhancedAudioMediaAudioStreamInfo& stream_info =
+      original.stream_info;
+
+  stream_info.codec = kSbMediaAudioCodecOpus;
+  stream_info.mime = "audio/webm";
+  stream_info.number_of_channels = 2;
+  stream_info.samples_per_second = 24000;
+  stream_info.bits_per_sample = 16;
+  stream_info.audio_specific_config_size = 6;
+  stream_info.audio_specific_config = "config";
+
+  AudioSampleInfo audio_sample_info(original);
+
+  EXPECT_EQ(stream_info.codec, audio_sample_info.stream_info.codec);
+  EXPECT_EQ(stream_info.mime, audio_sample_info.stream_info.mime);
+  EXPECT_EQ(stream_info.number_of_channels,
+            audio_sample_info.stream_info.number_of_channels);
+  EXPECT_EQ(stream_info.samples_per_second,
+            audio_sample_info.stream_info.samples_per_second);
+  EXPECT_EQ(stream_info.bits_per_sample,
+            audio_sample_info.stream_info.bits_per_sample);
+  EXPECT_EQ(ToVector(stream_info.audio_specific_config,
+                     stream_info.audio_specific_config_size),
+            audio_sample_info.stream_info.audio_specific_config);
+}
+
 TEST(VideoSampleInfoTest, DefaultCtor) {
   VideoSampleInfo video_sample_info;
-  EXPECT_EQ(video_sample_info.codec, kSbMediaVideoCodecNone);
+
+  EXPECT_EQ(video_sample_info.stream_info.codec, kSbMediaVideoCodecNone);
   // No other members should be accessed if `codec` is `kSbMediaVideoCodecNone`,
-  // however we still want to make sure that the pointer members are set to
-  // nullptr.
-  EXPECT_EQ(video_sample_info.mime, nullptr);
-  EXPECT_EQ(video_sample_info.max_video_capabilities, nullptr);
+  // however we still want to make sure that the following members are empty.
+  EXPECT_TRUE(video_sample_info.stream_info.mime.empty());
+  EXPECT_TRUE(video_sample_info.stream_info.max_video_capabilities.empty());
+}
+
+TEST(VideoSampleInfoTest, SbMediaVideoSampleInfo) {
+  SbMediaVideoSampleInfo original = {};
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbMediaVideoStreamInfo& stream_info = original.stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  SbMediaVideoStreamInfo& stream_info = original;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+  original.is_key_frame = true;
+  stream_info.codec = kSbMediaVideoCodecAv1;
+  stream_info.mime = "video/mp4";
+  stream_info.max_video_capabilities = "width=3840";
+  stream_info.frame_width = 1080;
+  stream_info.frame_height = 1920;
+
+  VideoSampleInfo video_sample_info(original);
+
+  EXPECT_EQ(original.is_key_frame, video_sample_info.is_key_frame);
+  EXPECT_EQ(stream_info.codec, video_sample_info.stream_info.codec);
+  EXPECT_EQ(stream_info.mime, video_sample_info.stream_info.mime);
+  EXPECT_EQ(stream_info.max_video_capabilities,
+            video_sample_info.stream_info.max_video_capabilities);
+  EXPECT_EQ(stream_info.frame_width, video_sample_info.stream_info.frame_width);
+  EXPECT_EQ(stream_info.frame_height,
+            video_sample_info.stream_info.frame_height);
+  EXPECT_EQ(stream_info.color_metadata,
+            video_sample_info.stream_info.color_metadata);
+}
+
+TEST(VideoSampleInfoTest, CobaltExtensionEnhancedAudioMediaVideoSampleInfo) {
+  CobaltExtensionEnhancedAudioMediaVideoSampleInfo original = {};
+  CobaltExtensionEnhancedAudioMediaVideoStreamInfo& stream_info =
+      original.stream_info;
+
+  original.is_key_frame = true;
+  stream_info.codec = kSbMediaVideoCodecAv1;
+  stream_info.mime = "video/mp4";
+  stream_info.max_video_capabilities = "width=3840";
+  stream_info.frame_width = 1080;
+  stream_info.frame_height = 1920;
+
+  VideoSampleInfo video_sample_info(original);
+
+  EXPECT_EQ(original.is_key_frame, video_sample_info.is_key_frame);
+  EXPECT_EQ(stream_info.codec, video_sample_info.stream_info.codec);
+  EXPECT_EQ(stream_info.mime, video_sample_info.stream_info.mime);
+  EXPECT_EQ(stream_info.max_video_capabilities,
+            video_sample_info.stream_info.max_video_capabilities);
+  EXPECT_EQ(stream_info.frame_width, video_sample_info.stream_info.frame_width);
+  EXPECT_EQ(stream_info.frame_height,
+            video_sample_info.stream_info.frame_height);
+  EXPECT_EQ(stream_info.color_metadata,
+            video_sample_info.stream_info.color_metadata);
+}
+
+TEST(MediaUtilTest, AudioDurationToFrames) {
+  EXPECT_EQ(AudioDurationToFrames(0, 48000), 0);
+  EXPECT_EQ(AudioDurationToFrames(kSbTimeSecond / 2, 48000), 48000 / 2);
+  EXPECT_EQ(AudioDurationToFrames(kSbTimeSecond, 48000), 48000);
+  EXPECT_EQ(AudioDurationToFrames(kSbTimeSecond * 2, 48000), 48000 * 2);
+}
+
+TEST(MediaUtilTest, AudioFramesToDuration) {
+  EXPECT_EQ(AudioFramesToDuration(0, 48000), 0);
+  EXPECT_EQ(AudioFramesToDuration(48000 / 2, 48000), kSbTimeSecond / 2);
+  EXPECT_EQ(AudioFramesToDuration(48000, 48000), kSbTimeSecond);
+  EXPECT_EQ(AudioFramesToDuration(48000 * 2, 48000), kSbTimeSecond * 2);
 }
 
 }  // namespace
diff --git a/starboard/shared/starboard/media/mime_util.cc b/starboard/shared/starboard/media/mime_util.cc
index c8656b1..2951340 100644
--- a/starboard/shared/starboard/media/mime_util.cc
+++ b/starboard/shared/starboard/media/mime_util.cc
@@ -103,6 +103,13 @@
     case kSbMediaAudioCodecPcm:
       return false;
 #endif  // SB_API_VERSION >= 14
+#if SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
+    case kSbMediaAudioCodecIamf:
+      if (mime_type.subtype() != "mp4") {
+        return false;
+      }
+      break;
+#endif  // SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
   }
 
   if (!IsAudioOutputSupported(kSbMediaAudioCodingTypePcm,
diff --git a/starboard/shared/starboard/memory.cc b/starboard/shared/starboard/memory.cc
index 8be6cc9..919e828 100644
--- a/starboard/shared/starboard/memory.cc
+++ b/starboard/shared/starboard/memory.cc
@@ -54,12 +54,14 @@
   // These are straight forward error messages. We use the build settings to
   // predict whether the MemoryReporter is likely to fail.
   if (!StarboardAllowsMemoryTracking()) {
-    SbLogRaw("\nMemory Reporting is disabled because this build does "
-             "not support it. Try a QA, devel or debug build.\n");
+    SbLogRaw(
+        "\nMemory Reporting is disabled because this build does "
+        "not support it. Try a QA, devel or debug build.\n");
     return false;
   } else if (LeakTraceEnabled()) {
-    SbLogRaw("\nMemory Reporting might be disabled because leak trace "
-             "(from address sanitizer?) is active.\n");
+    SbLogRaw(
+        "\nMemory Reporting might be disabled because leak trace "
+        "(from address sanitizer?) is active.\n");
     return false;
   }
   return true;
@@ -83,6 +85,11 @@
 }
 
 void* SbMemoryReallocate(void* memory, size_t size) {
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+  SB_CHECK((size != 0) || (memory == nullptr))
+      << "Calling SbMemoryReallocate with a non-null pointer and size 0 is not "
+         "guaranteed to release the memory, and therefore may leak memory.";
+#endif
   SbReportDeallocation(memory);
   void* new_memory = SbMemoryReallocateImpl(memory, size);
   SbReportAllocation(new_memory, size);
@@ -136,10 +143,7 @@
   if (SB_LIKELY(!s_memory_reporter)) {
     return;
   }
-  s_memory_reporter->on_mapmem_cb(
-      s_memory_reporter->context,
-      memory,
-      size);
+  s_memory_reporter->on_mapmem_cb(s_memory_reporter->context, memory, size);
 #endif  // STARBOARD_ALLOWS_MEMORY_TRACKING
 }
 
@@ -150,10 +154,7 @@
   if (SB_LIKELY(!s_memory_reporter)) {
     return;
   }
-  s_memory_reporter->on_unmapmem_cb(
-      s_memory_reporter->context,
-      memory,
-      size);
+  s_memory_reporter->on_unmapmem_cb(s_memory_reporter->context, memory, size);
 #endif  // STARBOARD_ALLOWS_MEMORY_TRACKING
 }
 
@@ -166,10 +167,7 @@
   if (SB_LIKELY(!s_memory_reporter)) {
     return;
   }
-  s_memory_reporter->on_alloc_cb(
-      s_memory_reporter->context,
-      memory,
-      size);
+  s_memory_reporter->on_alloc_cb(s_memory_reporter->context, memory, size);
 #endif  // STARBOARD_ALLOWS_MEMORY_TRACKING
 }
 
@@ -180,9 +178,7 @@
   if (SB_LIKELY(!s_memory_reporter)) {
     return;
   }
-  s_memory_reporter->on_dealloc_cb(
-      s_memory_reporter->context,
-      memory);
+  s_memory_reporter->on_dealloc_cb(s_memory_reporter->context, memory);
 #endif  // STARBOARD_ALLOWS_MEMORY_TRACKING
 }
 
diff --git a/starboard/shared/starboard/net_log.cc b/starboard/shared/starboard/net_log.cc
index f71427a..9ca4192 100644
--- a/starboard/shared/starboard/net_log.cc
+++ b/starboard/shared/starboard/net_log.cc
@@ -22,7 +22,7 @@
 #include <map>
 #include <string>
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/log.h"
 #include "starboard/common/mutex.h"
 #include "starboard/common/semaphore.h"
diff --git a/starboard/shared/starboard/player/buffer_internal.h b/starboard/shared/starboard/player/buffer_internal.h
new file mode 100644
index 0000000..fc3bed6
--- /dev/null
+++ b/starboard/shared/starboard/player/buffer_internal.h
@@ -0,0 +1,96 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_PLAYER_BUFFER_INTERNAL_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_BUFFER_INTERNAL_H_
+
+#include <cstring>
+#include <utility>
+
+#include "starboard/common/log.h"
+#include "starboard/configuration.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/types.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+
+// A buffer containing arbitrary binary data, with life time and size managed.
+// It performs better than std::vector<> as it doesn't fill the buffer with 0s.
+class Buffer {
+ public:
+  static constexpr uint8_t kPadding = 0x78;
+#if defined(NDEBUG)
+  static constexpr int kPaddingSize = 0;
+#else   // defined(NDEBUG)
+  static constexpr int kPaddingSize = 32;
+#endif  // defined(NDEBUG)
+
+  Buffer() = default;
+  explicit Buffer(int size)
+      : size_(size), data_(new uint8_t[size + kPaddingSize * 2]) {
+#if !defined(NDEBUG)
+    memset(data_, kPadding, kPaddingSize);
+    memset(data_ + kPaddingSize + size_, kPadding, kPaddingSize);
+#endif  // !defined(NDEBUG)
+  }
+
+  Buffer(const Buffer& that)
+      : size_(that.size_), data_(new uint8_t[that.size_ + kPaddingSize * 2]) {
+    memcpy(data_, that.data_, size_ + kPaddingSize * 2);
+  }
+  Buffer(Buffer&& that) : size_(that.size_), data_(that.data_) {
+    that.size_ = 0;
+    that.data_ = nullptr;
+  }
+  ~Buffer() {
+#if !defined(NDEBUG)
+    if (data_) {
+      uint8_t buffer[kPaddingSize];
+      memset(buffer, kPadding, kPaddingSize);
+      SB_CHECK(memcmp(data_, buffer, kPaddingSize) == 0);
+      SB_CHECK(memcmp(data_ + kPaddingSize + size_, buffer, kPaddingSize) == 0);
+    }
+#endif  // !defined(NDEBUG)
+    delete[] data_;
+  }
+
+  Buffer& operator=(Buffer&& that) {
+    std::swap(this->size_, that.size_);
+    std::swap(this->data_, that.data_);
+    return *this;
+  }
+
+  int size() const { return size_; }
+
+  uint8_t* data() { return size_ == 0 ? nullptr : data_ + kPaddingSize; }
+  const uint8_t* data() const {
+    return size_ == 0 ? nullptr : data_ + kPaddingSize;
+  }
+
+ private:
+  int size_ = 0;
+  uint8_t* data_ = nullptr;
+
+  Buffer& operator=(const Buffer& that) = delete;
+};
+
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_BUFFER_INTERNAL_H_
diff --git a/starboard/shared/starboard/player/buffer_test_internal.cc b/starboard/shared/starboard/player/buffer_test_internal.cc
new file mode 100644
index 0000000..b51f8d0
--- /dev/null
+++ b/starboard/shared/starboard/player/buffer_test_internal.cc
@@ -0,0 +1,98 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/player/buffer_internal.h"
+
+#include <utility>
+
+#include "starboard/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace {
+
+TEST(BufferTest, DefaultCtor) {
+  Buffer buffer;
+  EXPECT_EQ(buffer.size(), 0);
+}
+
+TEST(BufferTest, Allocate) {
+  Buffer buffer(128);
+  ASSERT_EQ(buffer.size(), 128);
+  ASSERT_NE(buffer.data(), nullptr);
+  memset(buffer.data(), 'x', 128);
+}
+
+TEST(BufferTest, CopyCtor) {
+  Buffer original(128);
+  memset(original.data(), 'x', 128);
+
+  Buffer copy(original);
+  ASSERT_EQ(copy.size(), original.size());
+  ASSERT_NE(copy.data(), nullptr);
+
+  for (int i = 0; i < original.size(); ++i) {
+    ASSERT_EQ(original.data()[i], copy.data()[i]);
+  }
+
+  memset(copy.data(), 'y', 128);
+
+  for (int i = 0; i < original.size(); ++i) {
+    ASSERT_EQ(original.data()[i], 'x');
+    ASSERT_EQ(copy.data()[i], 'y');
+  }
+}
+
+TEST(BufferTest, MoveCtor) {
+  Buffer original(128);
+  memset(original.data(), 'x', 128);
+
+  const uint8_t* original_data_pointer = original.data();
+
+  Buffer copy(std::move(original));
+  ASSERT_EQ(copy.size(), 128);
+  ASSERT_NE(copy.data(), nullptr);
+  ASSERT_EQ(copy.data(), original_data_pointer);
+
+  for (int i = 0; i < copy.size(); ++i) {
+    ASSERT_EQ(copy.data()[i], 'x');
+  }
+}
+
+TEST(BufferTest, MoveAssignmentOperator) {
+  Buffer original(128);
+  memset(original.data(), 'x', 128);
+
+  const uint8_t* original_data_pointer = original.data();
+
+  Buffer copy;
+
+  copy = std::move(original);
+  ASSERT_EQ(copy.size(), 128);
+  ASSERT_NE(copy.data(), nullptr);
+  ASSERT_EQ(copy.data(), original_data_pointer);
+
+  for (int i = 0; i < copy.size(); ++i) {
+    ASSERT_EQ(copy.data()[i], 'x');
+  }
+}
+
+}  // namespace
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/starboard/shared/starboard/player/buildfiles.gni b/starboard/shared/starboard/player/buildfiles.gni
index 7ce4e60..2fa42bc 100644
--- a/starboard/shared/starboard/player/buildfiles.gni
+++ b/starboard/shared/starboard/player/buildfiles.gni
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 common_player_sources = [
+  "//starboard/shared/starboard/player/buffer_internal.h",
   "//starboard/shared/starboard/player/decoded_audio_internal.cc",
   "//starboard/shared/starboard/player/decoded_audio_internal.h",
   "//starboard/shared/starboard/player/input_buffer_internal.cc",
@@ -24,17 +25,17 @@
   "//starboard/shared/starboard/player/player_create.cc",
   "//starboard/shared/starboard/player/player_destroy.cc",
   "//starboard/shared/starboard/player/player_get_current_frame.cc",
-  "//starboard/shared/starboard/player/player_get_info2.cc",
+  "//starboard/shared/starboard/player/player_get_info.cc",
   "//starboard/shared/starboard/player/player_get_maximum_number_of_samples_per_write.cc",
   "//starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc",
   "//starboard/shared/starboard/player/player_internal.cc",
   "//starboard/shared/starboard/player/player_internal.h",
-  "//starboard/shared/starboard/player/player_seek2.cc",
+  "//starboard/shared/starboard/player/player_seek.cc",
   "//starboard/shared/starboard/player/player_set_bounds.cc",
   "//starboard/shared/starboard/player/player_set_playback_rate.cc",
   "//starboard/shared/starboard/player/player_set_volume.cc",
   "//starboard/shared/starboard/player/player_worker.cc",
   "//starboard/shared/starboard/player/player_worker.h",
   "//starboard/shared/starboard/player/player_write_end_of_stream.cc",
-  "//starboard/shared/starboard/player/player_write_sample2.cc",
+  "//starboard/shared/starboard/player/player_write_samples.cc",
 ]
diff --git a/starboard/shared/starboard/player/decoded_audio_internal.cc b/starboard/shared/starboard/player/decoded_audio_internal.cc
index b8d0192..79952d7 100644
--- a/starboard/shared/starboard/player/decoded_audio_internal.cc
+++ b/starboard/shared/starboard/player/decoded_audio_internal.cc
@@ -15,13 +15,14 @@
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 
 #include <algorithm>
+#include <cstring>
+#include <utility>
 
 #include "starboard/common/log.h"
+#include "starboard/common/media.h"
 #include "starboard/memory.h"
 #include "starboard/shared/starboard/media/media_util.h"
 
-#include <cstring>
-
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -29,6 +30,8 @@
 
 namespace {
 
+using ::starboard::shared::starboard::media::AudioDurationToFrames;
+using ::starboard::shared::starboard::media::AudioFramesToDuration;
 using ::starboard::shared::starboard::media::GetBytesPerSample;
 
 void ConvertSample(const int16_t* source, float* destination) {
@@ -48,91 +51,134 @@
       sample_type_(kSbMediaAudioSampleTypeInt16Deprecated),
       storage_type_(kSbMediaAudioFrameStorageTypeInterleaved),
       timestamp_(0),
-      size_(0) {}
+      offset_in_bytes_(0),
+      size_in_bytes_(0) {}
 
 DecodedAudio::DecodedAudio(int channels,
                            SbMediaAudioSampleType sample_type,
                            SbMediaAudioFrameStorageType storage_type,
                            SbTime timestamp,
-                           size_t size)
+                           int size_in_bytes)
     : channels_(channels),
       sample_type_(sample_type),
       storage_type_(storage_type),
       timestamp_(timestamp),
-      buffer_(new uint8_t[size]),
-      size_(size) {}
+      storage_(size_in_bytes),
+      offset_in_bytes_(0),
+      size_in_bytes_(size_in_bytes) {
+  SB_DCHECK(channels_ > 0);
+  SB_DCHECK(size_in_bytes_ >= 0);
+  // TODO(b/275199195): Enable the SB_DCHECK below.
+  // SB_DCHECK(size_in_bytes_ % (GetBytesPerSample(sample_type_) * channels_) ==
+  //           0);
+}
 
 int DecodedAudio::frames() const {
   int bytes_per_sample = GetBytesPerSample(sample_type_);
-  SB_DCHECK(size_ % (bytes_per_sample * channels_) == 0);
-  return static_cast<int>(size_ / bytes_per_sample / channels_);
+  SB_DCHECK(size_in_bytes_ % (bytes_per_sample * channels_) == 0);
+  return static_cast<int>(size_in_bytes_ / bytes_per_sample / channels_);
 }
 
-void DecodedAudio::ShrinkTo(size_t new_size) {
-  SB_DCHECK(new_size <= size_);
-  size_ = new_size;
+bool DecodedAudio::IsFormat(SbMediaAudioSampleType sample_type,
+                            SbMediaAudioFrameStorageType storage_type) const {
+  return sample_type_ == sample_type && storage_type_ == storage_type;
 }
 
-void DecodedAudio::AdjustForSeekTime(int samples_per_second,
-                                     SbTime seeking_to_time) {
+void DecodedAudio::ShrinkTo(int new_size_in_bytes) {
+  SB_DCHECK(new_size_in_bytes <= size_in_bytes_);
+  size_in_bytes_ = new_size_in_bytes;
+}
+
+void DecodedAudio::AdjustForSeekTime(int sample_rate, SbTime seeking_to_time) {
   SB_DCHECK(!is_end_of_stream());
-  SB_DCHECK(samples_per_second != 0);
+  SB_DCHECK(sample_rate != 0);
 
-  int frames_to_remove =
-      (seeking_to_time - timestamp()) * samples_per_second / kSbTimeSecond;
+  int frames_to_skip =
+      media::AudioDurationToFrames(seeking_to_time - timestamp(), sample_rate);
 
-  if (samples_per_second == 0 || frames_to_remove < 0 ||
-      frames_to_remove >= frames()) {
+  if (sample_rate == 0 || frames_to_skip < 0 || frames_to_skip >= frames()) {
     SB_LOG(WARNING) << "AdjustForSeekTime failed for seeking_to_time: "
-                    << seeking_to_time
-                    << ", samples_per_second: " << samples_per_second
+                    << seeking_to_time << ", sample_rate: " << sample_rate
                     << ", timestamp: " << timestamp() << ", and there are "
                     << frames() << " frames in the DecodedAudio object.";
     return;
   }
 
-  auto bytes_per_sample = GetBytesPerSample(sample_type_);
-  auto bytes_per_frame = bytes_per_sample * channels();
+  const auto bytes_per_sample = GetBytesPerSample(sample_type_);
+  const auto bytes_per_frame = bytes_per_sample * channels();
 
   if (storage_type_ == kSbMediaAudioFrameStorageTypeInterleaved) {
-    memmove(buffer(), buffer() + bytes_per_frame * frames_to_remove,
-                 (frames() - frames_to_remove) * bytes_per_frame);
-  } else {
-    SB_DCHECK(storage_type_ == kSbMediaAudioFrameStorageTypePlanar);
-    const uint8_t* source_addr = buffer();
-    uint8_t* dest_addr = buffer();
-    for (int channel = 0; channel < channels(); ++channel) {
-      memmove(dest_addr, source_addr + bytes_per_sample * frames_to_remove,
-                   (frames() - frames_to_remove) * bytes_per_sample);
-      source_addr += frames() * bytes_per_sample;
-      dest_addr += (frames() - frames_to_remove) * bytes_per_sample;
-    }
+    offset_in_bytes_ += frames_to_skip * bytes_per_frame;
+    size_in_bytes_ -= frames_to_skip * bytes_per_frame;
+    timestamp_ += media::AudioFramesToDuration(frames_to_skip, sample_rate);
+    return;
   }
-  size_ = (frames() - frames_to_remove) * bytes_per_frame;
-  timestamp_ += frames_to_remove * kSbTimeSecond / samples_per_second;
+
+  SB_DCHECK(storage_type_ == kSbMediaAudioFrameStorageTypePlanar);
+
+  Buffer new_storage(size_in_bytes_ - frames_to_skip * bytes_per_frame);
+  const auto new_frames = frames() - frames_to_skip;
+
+  const uint8_t* source_addr = data();
+  uint8_t* dest_addr = new_storage.data();
+  for (int channel = 0; channel < channels(); ++channel) {
+    memcpy(dest_addr, source_addr + bytes_per_sample * frames_to_skip,
+           new_frames * bytes_per_frame);
+    source_addr += frames() * bytes_per_sample;
+    dest_addr += new_frames * bytes_per_sample;
+  }
+
+  storage_ = std::move(new_storage);
+  timestamp_ += media::AudioFramesToDuration(frames_to_skip, sample_rate);
+  offset_in_bytes_ = 0;
+  size_in_bytes_ = new_frames * bytes_per_frame;
 }
 
-void DecodedAudio::SwitchFormatTo(
+void DecodedAudio::AdjustForDiscardedDurations(
+    int sample_rate,
+    SbTime discarded_duration_from_front,
+    SbTime discarded_duration_from_back) {
+  SB_DCHECK(discarded_duration_from_front >= 0);
+  SB_DCHECK(discarded_duration_from_back >= 0);
+  SB_DCHECK(storage_type() == kSbMediaAudioFrameStorageTypeInterleaved);
+
+  const auto bytes_per_frame = GetBytesPerSample(sample_type()) * channels_;
+  auto discarded_frames_from_front =
+      AudioDurationToFrames(discarded_duration_from_front, sample_rate);
+
+  discarded_frames_from_front = std::min(discarded_frames_from_front, frames());
+  offset_in_bytes_ += bytes_per_frame * discarded_frames_from_front;
+  size_in_bytes_ -= bytes_per_frame * discarded_frames_from_front;
+  timestamp_ +=
+      media::AudioFramesToDuration(discarded_frames_from_front, sample_rate);
+
+  auto discarded_frames_from_back =
+      AudioDurationToFrames(discarded_duration_from_back, sample_rate);
+  discarded_frames_from_back = std::min(discarded_frames_from_back, frames());
+  size_in_bytes_ -= bytes_per_frame * discarded_frames_from_back;
+}
+
+scoped_refptr<DecodedAudio> DecodedAudio::SwitchFormatTo(
     SbMediaAudioSampleType new_sample_type,
-    SbMediaAudioFrameStorageType new_storage_type) {
-  if (new_sample_type == sample_type_ && new_storage_type == storage_type_) {
-    return;
-  }
+    SbMediaAudioFrameStorageType new_storage_type) const {
+  // The caller should call IsFormat() to check before calling SwitchFormatTo(),
+  // as SwitchFormatTo() always copies the whole buffer and is not optimal.
+  SB_DCHECK(new_sample_type != sample_type_ ||
+            new_storage_type != storage_type_);
 
   if (new_storage_type == storage_type_) {
-    SwitchSampleTypeTo(new_sample_type);
-    return;
+    return SwitchSampleTypeTo(new_sample_type);
   }
 
   if (new_sample_type == sample_type_) {
-    SwitchStorageTypeTo(new_storage_type);
-    return;
+    return SwitchStorageTypeTo(new_storage_type);
   }
 
   // Both sample types and storage types are different, use the slowest way.
-  size_t new_size =
+  int new_size =
       media::GetBytesPerSample(new_sample_type) * frames() * channels();
-  scoped_array<uint8_t> new_buffer(new uint8_t[new_size]);
+  scoped_refptr<DecodedAudio> new_decoded_audio = new DecodedAudio(
+      channels(), new_sample_type, new_storage_type, timestamp(), new_size);
 
 #define InterleavedSampleAddr(start_addr, channel, frame) \
   (start_addr + (frame * channels() + channel))
@@ -143,9 +189,9 @@
 #define SwitchTo(OldSampleType, OldStorageType, NewSampleType, NewStorageType) \
   do {                                                                         \
     const OldSampleType* old_samples =                                         \
-        reinterpret_cast<OldSampleType*>(buffer_.get());                       \
+        reinterpret_cast<const OldSampleType*>(this->data());                  \
     NewSampleType* new_samples =                                               \
-        reinterpret_cast<NewSampleType*>(new_buffer.get());                    \
+        reinterpret_cast<NewSampleType*>(new_decoded_audio->data());           \
                                                                                \
     for (int channel = 0; channel < channels(); ++channel) {                   \
       for (int frame = 0; frame < frames(); ++frame) {                         \
@@ -182,52 +228,61 @@
     SB_NOTREACHED();
   }
 
-  buffer_.swap(new_buffer);
-  sample_type_ = new_sample_type;
-  storage_type_ = new_storage_type;
-  size_ = new_size;
+  return new_decoded_audio;
 }
 
-void DecodedAudio::SwitchSampleTypeTo(SbMediaAudioSampleType new_sample_type) {
-  size_t new_size =
+scoped_refptr<DecodedAudio> DecodedAudio::Clone() const {
+  scoped_refptr<DecodedAudio> copy = new DecodedAudio(
+      channels(), sample_type(), storage_type(), timestamp(), size_in_bytes());
+
+  memcpy(copy->data(), data(), size_in_bytes());
+
+  return copy;
+}
+
+scoped_refptr<DecodedAudio> DecodedAudio::SwitchSampleTypeTo(
+    SbMediaAudioSampleType new_sample_type) const {
+  int new_size =
       media::GetBytesPerSample(new_sample_type) * frames() * channels();
-  scoped_array<uint8_t> new_buffer(new uint8_t[new_size]);
+  scoped_refptr<DecodedAudio> new_decoded_audio = new DecodedAudio(
+      channels(), new_sample_type, storage_type(), timestamp(), new_size);
 
   if (sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated &&
       new_sample_type == kSbMediaAudioSampleTypeFloat32) {
-    const int16_t* old_samples = reinterpret_cast<int16_t*>(buffer_.get());
-    float* new_samples = reinterpret_cast<float*>(new_buffer.get());
+    const int16_t* old_samples = reinterpret_cast<const int16_t*>(this->data());
+    float* new_samples = reinterpret_cast<float*>(new_decoded_audio->data());
 
     for (int i = 0; i < frames() * channels(); ++i) {
       ConvertSample(old_samples + i, new_samples + i);
     }
   } else if (sample_type_ == kSbMediaAudioSampleTypeFloat32 &&
              new_sample_type == kSbMediaAudioSampleTypeInt16Deprecated) {
-    const float* old_samples = reinterpret_cast<float*>(buffer_.get());
-    int16_t* new_samples = reinterpret_cast<int16_t*>(new_buffer.get());
+    const float* old_samples = reinterpret_cast<const float*>(this->data());
+    int16_t* new_samples =
+        reinterpret_cast<int16_t*>(new_decoded_audio->data());
 
     for (int i = 0; i < frames() * channels(); ++i) {
       ConvertSample(old_samples + i, new_samples + i);
     }
   }
 
-  buffer_.swap(new_buffer);
-  sample_type_ = new_sample_type;
-  size_ = new_size;
+  return new_decoded_audio;
 }
 
-void DecodedAudio::SwitchStorageTypeTo(
-    SbMediaAudioFrameStorageType new_storage_type) {
-  scoped_array<uint8_t> new_buffer(new uint8_t[size_]);
-  int bytes_per_sample = media::GetBytesPerSample(sample_type_);
-  uint8_t* old_samples = buffer_.get();
-  uint8_t* new_samples = new_buffer.get();
+scoped_refptr<DecodedAudio> DecodedAudio::SwitchStorageTypeTo(
+    SbMediaAudioFrameStorageType new_storage_type) const {
+  scoped_refptr<DecodedAudio> new_decoded_audio =
+      new DecodedAudio(channels(), sample_type(), new_storage_type, timestamp(),
+                       size_in_bytes());
+  int bytes_per_sample = media::GetBytesPerSample(sample_type());
+  const uint8_t* old_samples = this->data();
+  uint8_t* new_samples = new_decoded_audio->data();
 
   if (storage_type_ == kSbMediaAudioFrameStorageTypeInterleaved &&
       new_storage_type == kSbMediaAudioFrameStorageTypePlanar) {
     for (int channel = 0; channel < channels(); ++channel) {
       for (int frame = 0; frame < frames(); ++frame) {
-        uint8_t* old_sample =
+        const uint8_t* old_sample =
             old_samples + (frame * channels() + channel) * bytes_per_sample;
         uint8_t* new_sample =
             new_samples + (channel * frames() + frame) * bytes_per_sample;
@@ -238,7 +293,7 @@
              new_storage_type == kSbMediaAudioFrameStorageTypeInterleaved) {
     for (int channel = 0; channel < channels(); ++channel) {
       for (int frame = 0; frame < frames(); ++frame) {
-        uint8_t* old_sample =
+        const uint8_t* old_sample =
             old_samples + (channel * frames() + frame) * bytes_per_sample;
         uint8_t* new_sample =
             new_samples + (frame * channels() + channel) * bytes_per_sample;
@@ -247,8 +302,39 @@
     }
   }
 
-  buffer_.swap(new_buffer);
-  storage_type_ = new_storage_type;
+  return new_decoded_audio;
+}
+
+bool operator==(const DecodedAudio& left, const DecodedAudio& right) {
+  if (left.is_end_of_stream() && right.is_end_of_stream()) {
+    return true;
+  }
+  if (left.is_end_of_stream() || right.is_end_of_stream()) {
+    return false;
+  }
+
+  return left.timestamp() == right.timestamp() &&
+         left.channels() == right.channels() &&
+         left.sample_type() == right.sample_type() &&
+         left.storage_type() == right.storage_type() &&
+         left.size_in_bytes() == right.size_in_bytes() &&
+         memcmp(left.data(), right.data(), right.size_in_bytes()) == 0;
+}
+
+bool operator!=(const DecodedAudio& left, const DecodedAudio& right) {
+  return !(left == right);
+}
+
+std::ostream& operator<<(std::ostream& os, const DecodedAudio& decoded_audio) {
+  if (decoded_audio.is_end_of_stream()) {
+    return os << "(eos)";
+  }
+  return os << "timestamp: " << decoded_audio.timestamp()
+            << ", channels: " << decoded_audio.channels() << ", sample type: "
+            << GetMediaAudioSampleTypeName(decoded_audio.sample_type())
+            << ", storage type: "
+            << GetMediaAudioStorageTypeName(decoded_audio.storage_type())
+            << ", frames: " << decoded_audio.frames();
 }
 
 }  // namespace player
diff --git a/starboard/shared/starboard/player/decoded_audio_internal.h b/starboard/shared/starboard/player/decoded_audio_internal.h
index b1eea20..8aff071 100644
--- a/starboard/shared/starboard/player/decoded_audio_internal.h
+++ b/starboard/shared/starboard/player/decoded_audio_internal.h
@@ -15,10 +15,13 @@
 #ifndef STARBOARD_SHARED_STARBOARD_PLAYER_DECODED_AUDIO_INTERNAL_H_
 #define STARBOARD_SHARED_STARBOARD_PLAYER_DECODED_AUDIO_INTERNAL_H_
 
+#include <iostream>
+
 #include "starboard/common/ref_counted.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/media.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/buffer_internal.h"
 
 namespace starboard {
 namespace shared {
@@ -27,56 +30,88 @@
 
 // Decoded audio frames produced by an audio decoder.  It can contain multiple
 // frames with continuous timestamps.
-// It doesn't have a specific storage type and sample type.  The decoder and the
-// renderer will determine the proper storage type and sample type in their own
-// way.
 class DecodedAudio : public RefCountedThreadSafe<DecodedAudio> {
  public:
   DecodedAudio();  // Signal an EOS.
+  // TODO(b/272837615): Remove `storage_type` support and always store data in
+  // interleaved. Refactor the places that store sample in planar to convert the
+  // samples to interleaved on creation.
   DecodedAudio(int channels,
                SbMediaAudioSampleType sample_type,
                SbMediaAudioFrameStorageType storage_type,
                SbTime timestamp,
-               size_t size);
+               int size_in_bytes);
 
   int channels() const { return channels_; }
   SbMediaAudioSampleType sample_type() const { return sample_type_; }
   SbMediaAudioFrameStorageType storage_type() const { return storage_type_; }
 
-  bool is_end_of_stream() const { return buffer_ == NULL; }
+  bool is_end_of_stream() const { return channels_ == 0; }
   SbTime timestamp() const { return timestamp_; }
-  const uint8_t* buffer() const { return buffer_.get(); }
-  size_t size() const { return size_; }
+  const uint8_t* data() const { return storage_.data() + offset_in_bytes_; }
+  const int16_t* data_as_int16() const {
+    return reinterpret_cast<const int16_t*>(storage_.data() + offset_in_bytes_);
+  }
+  const float* data_as_float32() const {
+    return reinterpret_cast<const float*>(storage_.data() + offset_in_bytes_);
+  }
+  int size_in_bytes() const { return size_in_bytes_; }
 
-  uint8_t* buffer() { return buffer_.get(); }
+  uint8_t* data() { return storage_.data() + offset_in_bytes_; }
+  int16_t* data_as_int16() {
+    return reinterpret_cast<int16_t*>(storage_.data() + offset_in_bytes_);
+  }
+  float* data_as_float32() {
+    return reinterpret_cast<float*>(storage_.data() + offset_in_bytes_);
+  }
   int frames() const;
 
-  void SwitchFormatTo(SbMediaAudioSampleType new_sample_type,
-                      SbMediaAudioFrameStorageType new_storage_type);
-  void ShrinkTo(size_t new_size);
+  void ShrinkTo(int new_size_in_bytes);
+
   // During seeking, the target time can be in the middle of the DecodedAudio
   // object.  This function will adjust the object to the seek target time by
   // removing the frames in the beginning that are before the seek target time.
-  void AdjustForSeekTime(int samples_per_second, SbTime seeking_to_time);
+  void AdjustForSeekTime(int sample_rate, SbTime seeking_to_time);
+  void AdjustForDiscardedDurations(int sample_rate,
+                                   SbTime discarded_duration_from_front,
+                                   SbTime discarded_duration_from_back);
+
+  bool IsFormat(SbMediaAudioSampleType sample_type,
+                SbMediaAudioFrameStorageType storage_type) const;
+  scoped_refptr<DecodedAudio> SwitchFormatTo(
+      SbMediaAudioSampleType new_sample_type,
+      SbMediaAudioFrameStorageType new_storage_type) const
+      SB_WARN_UNUSED_RESULT;
+
+  scoped_refptr<DecodedAudio> Clone() const;
 
  private:
-  void SwitchSampleTypeTo(SbMediaAudioSampleType new_sample_type);
-  void SwitchStorageTypeTo(SbMediaAudioFrameStorageType new_storage_type);
+  scoped_refptr<DecodedAudio> SwitchSampleTypeTo(
+      SbMediaAudioSampleType new_sample_type) const;
+  scoped_refptr<DecodedAudio> SwitchStorageTypeTo(
+      SbMediaAudioFrameStorageType new_storage_type) const;
 
-  int channels_;
-  SbMediaAudioSampleType sample_type_;
-  SbMediaAudioFrameStorageType storage_type_;
+  const int channels_;
+  const SbMediaAudioSampleType sample_type_;
+  const SbMediaAudioFrameStorageType storage_type_;
   // The timestamp of the first audio frame.
   SbTime timestamp_;
-  // Use scoped_array<uint8_t> instead of std::vector<uint8_t> to avoid wasting
-  // time on setting content to 0.
-  scoped_array<uint8_t> buffer_;
-  size_t size_;
+  Buffer storage_;
+  // The audio samples to be played are stored in the memory region starts from
+  // `storage_.data() + offset_in_bytes_`, `size_in_bytes_` bytes in total.
+  int offset_in_bytes_ = 0;
+  int size_in_bytes_ = 0;
 
   DecodedAudio(const DecodedAudio&) = delete;
   void operator=(const DecodedAudio&) = delete;
 };
 
+bool operator==(const DecodedAudio& left, const DecodedAudio& right);
+bool operator!=(const DecodedAudio& left, const DecodedAudio& right);
+
+// For debugging or testing only.
+std::ostream& operator<<(std::ostream& os, const DecodedAudio& decoded_audio);
+
 }  // namespace player
 }  // namespace starboard
 }  // namespace shared
diff --git a/starboard/shared/starboard/player/decoded_audio_test_internal.cc b/starboard/shared/starboard/player/decoded_audio_test_internal.cc
new file mode 100644
index 0000000..a4eae16
--- /dev/null
+++ b/starboard/shared/starboard/player/decoded_audio_test_internal.cc
@@ -0,0 +1,367 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+
+#include <cmath>
+#include <utility>
+
+#include "starboard/common/log.h"
+#include "starboard/shared/starboard/media/media_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace {
+
+using ::starboard::shared::starboard::media::GetBytesPerSample;
+
+constexpr int kChannels = 2;
+constexpr SbTime kTimestamp = kSbTimeSecond;
+constexpr int kSizeInBytes = 4192;
+constexpr int kSampleRate = 22050;
+
+constexpr double kPi = 3.1415926535897932384626;
+
+constexpr SbMediaAudioSampleType kSampleTypes[] = {
+    kSbMediaAudioSampleTypeInt16Deprecated, kSbMediaAudioSampleTypeFloat32};
+constexpr SbMediaAudioFrameStorageType kStorageTypes[] = {
+    kSbMediaAudioFrameStorageTypeInterleaved,
+    kSbMediaAudioFrameStorageTypePlanar};
+
+// The following two functions fill `data` with audio samples of a sine wave
+// starting from `initial_angle`.  `stride` is the number of samples per group.
+// For example:
+// 1. When `stride` is one, all samples filled with be continuous.
+// 2. When `stride` is 2, filling `data` and `data + 1` fills an interleaved
+//    stereo audio buffer with samples.
+void Fill(int16_t* data,
+          double initial_angle,
+          double angle_step,
+          int count,
+          int stride) {
+  SB_DCHECK(data);
+
+  auto current_angel = initial_angle;
+  for (int i = 0; i < count; ++i) {
+    *data = static_cast<int16_t>(sin(current_angel) * 32767);
+    current_angel += angle_step;
+    data += stride;
+  }
+}
+
+void Fill(float* data,
+          double initial_angle,
+          double angle_step,
+          int count,
+          int stride) {
+  SB_DCHECK(data);
+
+  auto current_angel = initial_angle;
+  for (int i = 0; i < count; ++i) {
+    *data = static_cast<float>(sin(current_angel));
+    current_angel += angle_step;
+    data += stride;
+  }
+}
+
+// The following two functions verify `data` with audio samples against a sine
+// wave starting from `initial_angle`.  `stride` is the number of samples per
+// group.  For example:
+// 1. When `stride` is one, all samples will be treated as continuous.
+// 2. When `stride` is 2, verifying against `data` and `data + 1` verifies an
+//    interleaved stereo audio buffer.
+void Verify(const int16_t* data,
+            double initial_angle,
+            double angle_step,
+            int count,
+            int stride) {
+  SB_DCHECK(data);
+
+  auto current_angel = initial_angle;
+  for (int i = 0; i < count; ++i) {
+    const auto current_value = static_cast<int16_t>(sin(current_angel) * 32767);
+    // Using `ASSERT_NEAR()` to allow for small value drifting due to
+    // conversions between sample types.
+    ASSERT_NEAR(static_cast<double>(*data), static_cast<double>(current_value),
+                2.0 / 32767);
+    current_angel += angle_step;
+    data += stride;
+  }
+}
+
+void Verify(const float* data,
+            double initial_angle,
+            double angle_step,
+            int count,
+            int stride) {
+  SB_DCHECK(data);
+
+  auto current_angel = initial_angle;
+  for (int i = 0; i < count; ++i) {
+    const auto current_value = static_cast<float>(sin(current_angel));
+    // Using `ASSERT_NEAR()` to allow for small value drifting due to
+    // conversions between sample types.
+    ASSERT_NEAR(static_cast<double>(*data), static_cast<double>(current_value),
+                2.0 / 32767);
+    current_angel += angle_step;
+    data += stride;
+  }
+}
+
+// Fill `decoded_audio` with sine wave samples, with phase shift of Pi/2 on each
+// channel.
+void Fill(scoped_refptr<DecodedAudio>* decoded_audio) {
+  SB_DCHECK(decoded_audio);
+  SB_DCHECK(*decoded_audio);
+
+  bool is_int16 =
+      (*decoded_audio)->sample_type() == kSbMediaAudioSampleTypeInt16Deprecated;
+  bool is_interleaved = (*decoded_audio)->storage_type() ==
+                        kSbMediaAudioFrameStorageTypeInterleaved;
+
+  for (int i = 0; i < (*decoded_audio)->channels(); ++i) {
+    if (is_int16 && is_interleaved) {
+      Fill((*decoded_audio)->data_as_int16() + i, kPi / 2 * i, kPi / 180,
+           (*decoded_audio)->frames(), (*decoded_audio)->channels());
+    } else if (!is_int16 && is_interleaved) {
+      Fill((*decoded_audio)->data_as_float32() + i, kPi / 2 * i, kPi / 180,
+           (*decoded_audio)->frames(), (*decoded_audio)->channels());
+    } else if (is_int16 && !is_interleaved) {
+      Fill((*decoded_audio)->data_as_int16() + (*decoded_audio)->frames() * i,
+           kPi / 2 * i, kPi / 180, (*decoded_audio)->frames(), 1);
+    } else if (!is_int16 && !is_interleaved) {
+      Fill((*decoded_audio)->data_as_float32() + (*decoded_audio)->frames() * i,
+           kPi / 2 * i, kPi / 180, (*decoded_audio)->frames(), 1);
+    }
+  }
+}
+
+// verify `decoded_audio` against sine wave samples, with phase shift of Pi/2 on
+// each channel.
+void Verify(const scoped_refptr<DecodedAudio>& decoded_audio) {
+  SB_DCHECK(decoded_audio);
+
+  bool is_int16 =
+      decoded_audio->sample_type() == kSbMediaAudioSampleTypeInt16Deprecated;
+  bool is_interleaved =
+      decoded_audio->storage_type() == kSbMediaAudioFrameStorageTypeInterleaved;
+
+  for (int i = 0; i < decoded_audio->channels(); ++i) {
+    if (is_int16 && is_interleaved) {
+      ASSERT_NO_FATAL_FAILURE(
+          Verify(decoded_audio->data_as_int16() + i, kPi / 2 * i, kPi / 180,
+                 decoded_audio->frames(), decoded_audio->channels()));
+    } else if (!is_int16 && is_interleaved) {
+      ASSERT_NO_FATAL_FAILURE(
+          Verify(decoded_audio->data_as_float32() + i, kPi / 2 * i, kPi / 180,
+                 decoded_audio->frames(), decoded_audio->channels()));
+    } else if (is_int16 && !is_interleaved) {
+      ASSERT_NO_FATAL_FAILURE(
+          Verify(decoded_audio->data_as_int16() + decoded_audio->frames() * i,
+                 kPi / 2 * i, kPi / 180, decoded_audio->frames(), 1));
+    } else if (!is_int16 && !is_interleaved) {
+      ASSERT_NO_FATAL_FAILURE(
+          Verify(decoded_audio->data_as_float32() + decoded_audio->frames() * i,
+                 kPi / 2 * i, kPi / 180, decoded_audio->frames(), 1));
+    }
+  }
+}
+
+TEST(DecodedAudioTest, DefaultCtor) {
+  scoped_refptr<DecodedAudio> decoded_audio(new DecodedAudio);
+  EXPECT_TRUE(decoded_audio->is_end_of_stream());
+}
+
+TEST(DecodedAudioTest, CtorWithSize) {
+  for (auto sample_type : kSampleTypes) {
+    for (auto storage_type : kStorageTypes) {
+      scoped_refptr<DecodedAudio> decoded_audio(new DecodedAudio(
+          kChannels, sample_type, storage_type, kTimestamp, kSizeInBytes));
+
+      EXPECT_FALSE(decoded_audio->is_end_of_stream());
+      EXPECT_EQ(decoded_audio->channels(), kChannels);
+      EXPECT_EQ(decoded_audio->sample_type(), sample_type);
+      EXPECT_EQ(decoded_audio->storage_type(), storage_type);
+      EXPECT_EQ(decoded_audio->size_in_bytes(), kSizeInBytes);
+      EXPECT_EQ(decoded_audio->frames(),
+                kSizeInBytes / GetBytesPerSample(decoded_audio->sample_type()) /
+                    kChannels);
+
+      Fill(&decoded_audio);
+      Verify(decoded_audio);
+    }
+  }
+}
+
+TEST(DecodedAudioTest, AdjustForSeekTime) {
+  for (int channels = 1; channels <= 6; ++channels) {
+    for (auto sample_type : kSampleTypes) {
+      scoped_refptr<DecodedAudio> original_decoded_audio(new DecodedAudio(
+          kChannels, sample_type, kSbMediaAudioFrameStorageTypeInterleaved,
+          kTimestamp, kSizeInBytes));
+      Fill(&original_decoded_audio);
+
+      scoped_refptr<DecodedAudio> adjusted_decoded_audio =
+          original_decoded_audio->Clone();
+
+      // Adjust to the beginning of `adjusted_decoded_audio` should be a no-op.
+      adjusted_decoded_audio->AdjustForSeekTime(
+          kSampleRate, adjusted_decoded_audio->timestamp());
+      ASSERT_EQ(*original_decoded_audio, *adjusted_decoded_audio);
+
+      // Adjust to an invalid timestamp before the time range of
+      // `adjusted_decoded_audio`, it's a no-op.
+      adjusted_decoded_audio->AdjustForSeekTime(
+          kSampleRate, adjusted_decoded_audio->timestamp() - kSbTimeSecond / 2);
+      ASSERT_EQ(*original_decoded_audio, *adjusted_decoded_audio);
+
+      // Adjust to an invalid timestamp after the time range of
+      // `adjusted_decoded_audio`, it's also a no-op.
+      adjusted_decoded_audio->AdjustForSeekTime(
+          kSampleRate,
+          adjusted_decoded_audio->timestamp() + kSbTimeSecond * 100);
+      ASSERT_EQ(*original_decoded_audio, *adjusted_decoded_audio);
+
+      const SbTime duration = media::AudioFramesToDuration(
+          adjusted_decoded_audio->frames(), kSampleRate);
+      const SbTime duration_of_one_frame =
+          media::AudioFramesToDuration(1, kSampleRate) + 1;
+      for (int i = 1; i < 10; ++i) {
+        adjusted_decoded_audio = original_decoded_audio->Clone();
+        // Adjust to the middle of `adjusted_decoded_audio`.
+        SbTime seek_time =
+            adjusted_decoded_audio->timestamp() + duration * i / 10;
+        adjusted_decoded_audio->AdjustForSeekTime(kSampleRate, seek_time);
+        ASSERT_NEAR(adjusted_decoded_audio->frames(),
+                    original_decoded_audio->frames() * (10 - i) / 10, 1);
+
+        ASSERT_LE(adjusted_decoded_audio->timestamp(), seek_time);
+        ASSERT_NEAR(adjusted_decoded_audio->timestamp(), seek_time,
+                    duration_of_one_frame);
+
+        auto offset_in_bytes = original_decoded_audio->size_in_bytes() -
+                               adjusted_decoded_audio->size_in_bytes();
+        ASSERT_TRUE(memcmp(adjusted_decoded_audio->data(),
+                           original_decoded_audio->data() + offset_in_bytes,
+                           adjusted_decoded_audio->size_in_bytes()) == 0);
+      }
+    }
+  }
+}
+
+TEST(DecodedAudioTest, AdjustForDiscardedDurations) {
+  for (int channels = 1; channels <= 6; ++channels) {
+    for (auto sample_type : kSampleTypes) {
+      scoped_refptr<DecodedAudio> original_decoded_audio(new DecodedAudio(
+          kChannels, sample_type, kSbMediaAudioFrameStorageTypeInterleaved,
+          kTimestamp, kSizeInBytes));
+      Fill(&original_decoded_audio);
+
+      scoped_refptr<DecodedAudio> adjusted_decoded_audio =
+          original_decoded_audio->Clone();
+
+      adjusted_decoded_audio->AdjustForDiscardedDurations(kSampleRate, 0, 0);
+      ASSERT_EQ(*original_decoded_audio, *adjusted_decoded_audio);
+
+      auto duration_of_decoded_audio = media::AudioFramesToDuration(
+          original_decoded_audio->frames(), kSampleRate);
+      auto quarter_duration = duration_of_decoded_audio / 4;
+      auto duration_of_one_frame =
+          media::AudioFramesToDuration(1, kSampleRate) + 1;
+      adjusted_decoded_audio->AdjustForDiscardedDurations(
+          kSampleRate, quarter_duration, quarter_duration);
+      ASSERT_NEAR(adjusted_decoded_audio->frames(),
+                  original_decoded_audio->frames() / 2, 2);
+      ASSERT_NEAR(adjusted_decoded_audio->timestamp(),
+                  original_decoded_audio->timestamp() + quarter_duration,
+                  duration_of_one_frame * 2);
+
+      adjusted_decoded_audio = original_decoded_audio->Clone();
+      // Adjust more frames than it has from front
+      adjusted_decoded_audio->AdjustForDiscardedDurations(
+          kSampleRate, duration_of_decoded_audio * 2, 0);
+      ASSERT_EQ(adjusted_decoded_audio->frames(), 0);
+      ASSERT_NEAR(
+          adjusted_decoded_audio->timestamp(),
+          original_decoded_audio->timestamp() + duration_of_decoded_audio,
+          duration_of_one_frame * 2);
+
+      adjusted_decoded_audio = original_decoded_audio->Clone();
+      // Adjust more frames than it has from back
+      adjusted_decoded_audio->AdjustForDiscardedDurations(
+          kSampleRate, 0, duration_of_decoded_audio * 2);
+      ASSERT_EQ(adjusted_decoded_audio->frames(), 0);
+      ASSERT_EQ(adjusted_decoded_audio->timestamp(),
+                original_decoded_audio->timestamp());
+    }
+  }
+}
+
+TEST(DecodedAudioTest, SwitchFormatTo) {
+  for (auto original_sample_type : kSampleTypes) {
+    for (auto original_storage_type : kStorageTypes) {
+      scoped_refptr<DecodedAudio> original_decoded_audio(
+          new DecodedAudio(kChannels, original_sample_type,
+                           original_storage_type, kTimestamp, kSizeInBytes));
+
+      Fill(&original_decoded_audio);
+
+      for (auto new_sample_type : kSampleTypes) {
+        for (auto new_storage_type : kStorageTypes) {
+          if (!original_decoded_audio->IsFormat(new_sample_type,
+                                                new_storage_type)) {
+            scoped_refptr<DecodedAudio> new_decoded_audio =
+                original_decoded_audio->SwitchFormatTo(new_sample_type,
+                                                       new_storage_type);
+
+            EXPECT_FALSE(new_decoded_audio->is_end_of_stream());
+            EXPECT_EQ(new_decoded_audio->channels(),
+                      original_decoded_audio->channels());
+            EXPECT_EQ(new_decoded_audio->timestamp(),
+                      original_decoded_audio->timestamp());
+            EXPECT_EQ(new_decoded_audio->frames(),
+                      original_decoded_audio->frames());
+            EXPECT_TRUE(
+                new_decoded_audio->IsFormat(new_sample_type, new_storage_type));
+
+            ASSERT_NO_FATAL_FAILURE(Verify(new_decoded_audio));
+          }
+        }
+      }
+    }
+  }
+}
+
+TEST(DecodedAudioTest, Clone) {
+  for (auto sample_type : kSampleTypes) {
+    scoped_refptr<DecodedAudio> decoded_audio(new DecodedAudio(
+        kChannels, sample_type, kSbMediaAudioFrameStorageTypeInterleaved,
+        kTimestamp, kSizeInBytes));
+    Fill(&decoded_audio);
+    auto copy = decoded_audio->Clone();
+    ASSERT_EQ(*copy, *decoded_audio);
+    ASSERT_GT(decoded_audio->size_in_bytes(), 0);
+    decoded_audio->data()[0] = ~decoded_audio->data()[0];
+    ASSERT_NE(*copy, *decoded_audio);
+  }
+}
+
+}  // namespace
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/starboard/shared/starboard/player/filter/BUILD.gn b/starboard/shared/starboard/player/filter/BUILD.gn
index f5f9d4f..9adf95f 100644
--- a/starboard/shared/starboard/player/filter/BUILD.gn
+++ b/starboard/shared/starboard/player/filter/BUILD.gn
@@ -20,6 +20,8 @@
     "//starboard/shared/starboard/player/filter/audio_channel_layout_mixer.h",
     "//starboard/shared/starboard/player/filter/audio_channel_layout_mixer_impl.cc",
     "//starboard/shared/starboard/player/filter/audio_decoder_internal.h",
+    "//starboard/shared/starboard/player/filter/audio_frame_discarder.cc",
+    "//starboard/shared/starboard/player/filter/audio_frame_discarder.h",
     "//starboard/shared/starboard/player/filter/audio_frame_tracker.cc",
     "//starboard/shared/starboard/player/filter/audio_frame_tracker.h",
     "//starboard/shared/starboard/player/filter/audio_renderer_internal.h",
diff --git a/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc b/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc
index 664544c..7f87b5d 100644
--- a/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc
+++ b/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.cc
@@ -31,16 +31,16 @@
 using common::ResetAndReturn;
 
 AdaptiveAudioDecoder::AdaptiveAudioDecoder(
-    const SbMediaAudioSampleInfo& audio_sample_info,
+    const media::AudioStreamInfo& audio_stream_info,
     SbDrmSystem drm_system,
     const AudioDecoderCreator& audio_decoder_creator,
     const OutputFormatAdjustmentCallback& output_adjustment_callback)
-    : initial_audio_sample_info_(audio_sample_info),
+    : initial_samples_per_second_(audio_stream_info.samples_per_second),
       drm_system_(drm_system),
       audio_decoder_creator_(audio_decoder_creator),
       output_adjustment_callback_(output_adjustment_callback),
-      output_number_of_channels_(audio_sample_info.number_of_channels) {
-  SB_DCHECK(audio_sample_info.codec != kSbMediaAudioCodecNone);
+      output_number_of_channels_(audio_stream_info.number_of_channels) {
+  SB_DCHECK(audio_stream_info.codec != kSbMediaAudioCodecNone);
 }
 
 AdaptiveAudioDecoder::~AdaptiveAudioDecoder() {
@@ -72,19 +72,19 @@
   SB_DCHECK(!pending_consumed_cb_);
   SB_DCHECK(!input_buffers.empty());
   SB_DCHECK(input_buffers.front()->sample_type() == kSbMediaTypeAudio);
-  SB_DCHECK(input_buffers.front()->audio_sample_info().codec !=
+  SB_DCHECK(input_buffers.front()->audio_stream_info().codec !=
             kSbMediaAudioCodecNone);
 
   if (!audio_decoder_) {
-    InitializeAudioDecoder(input_buffers.front()->audio_sample_info());
+    InitializeAudioDecoder(input_buffers.front()->audio_stream_info());
     if (audio_decoder_) {
       audio_decoder_->Decode(input_buffers, consumed_cb);
     }
     return;
   }
   if (starboard::media::IsAudioSampleInfoSubstantiallyDifferent(
-          input_audio_sample_info_,
-          input_buffers.front()->audio_sample_info())) {
+          input_audio_stream_info_,
+          input_buffers.front()->audio_stream_info())) {
     flushing_ = true;
     pending_input_buffers_ = input_buffers;
     pending_consumed_cb_ = consumed_cb;
@@ -94,7 +94,7 @@
 #if !defined(COBALT_BUILD_TYPE_GOLD)
   for (int i = 1; i < input_buffers.size(); i++) {
     if (starboard::media::IsAudioSampleInfoSubstantiallyDifferent(
-            input_audio_sample_info_, input_buffers[i]->audio_sample_info())) {
+            input_audio_stream_info_, input_buffers[i]->audio_stream_info())) {
       error_cb_(kSbPlayerErrorDecode,
                 "Configuration switches should NOT happen within a batch.");
       return;
@@ -137,9 +137,8 @@
             ret->channels() == output_number_of_channels_);
 
   SB_DCHECK(first_output_received_ || ret->is_end_of_stream());
-  *samples_per_second = first_output_received_
-                            ? output_samples_per_second_
-                            : initial_audio_sample_info_.samples_per_second;
+  *samples_per_second = first_output_received_ ? output_samples_per_second_
+                                               : initial_samples_per_second_;
   return ret;
 }
 
@@ -161,17 +160,17 @@
 }
 
 void AdaptiveAudioDecoder::InitializeAudioDecoder(
-    const SbMediaAudioSampleInfo& audio_sample_info) {
+    const media::AudioStreamInfo& audio_stream_info) {
   SB_DCHECK(!audio_decoder_);
   SB_DCHECK(output_cb_);
   SB_DCHECK(error_cb_);
   SB_DCHECK(!resampler_);
   SB_DCHECK(!channel_mixer_);
 
-  input_audio_sample_info_ = audio_sample_info;
+  input_audio_stream_info_ = audio_stream_info;
   output_format_checked_ = false;
   audio_decoder_ =
-      audio_decoder_creator_(input_audio_sample_info_, drm_system_);
+      audio_decoder_creator_(input_audio_stream_info_, drm_system_);
 
   if (!audio_decoder_) {
     error_cb_(kSbPlayerErrorDecode, "Decoder adapter cannot create decoder.");
@@ -215,7 +214,7 @@
     if (resampler_) {
       scoped_refptr<DecodedAudio> resampler_output =
           resampler_->WriteEndOfStream();
-      if (resampler_output && resampler_output->size() > 0) {
+      if (resampler_output && resampler_output->size_in_bytes() > 0) {
         if (channel_mixer_) {
           resampler_output = channel_mixer_->Mix(resampler_output);
         }
@@ -237,7 +236,7 @@
     return;
   }
 
-  SB_DCHECK(input_audio_sample_info_.number_of_channels ==
+  SB_DCHECK(input_audio_stream_info_.number_of_channels ==
             decoded_audio->channels());
   if (!output_format_checked_) {
     SB_DCHECK(!resampler_);
@@ -250,9 +249,9 @@
           decoded_audio->sample_type(), decoded_audio->storage_type(),
           decoded_sample_rate, output_sample_type_, output_storage_type_,
           output_samples_per_second_,
-          input_audio_sample_info_.number_of_channels);
+          input_audio_stream_info_.number_of_channels);
     }
-    if (input_audio_sample_info_.number_of_channels !=
+    if (input_audio_stream_info_.number_of_channels !=
         output_number_of_channels_) {
       channel_mixer_ = AudioChannelLayoutMixer::Create(
           output_sample_type_, output_storage_type_,
@@ -262,7 +261,7 @@
   if (resampler_) {
     decoded_audio = resampler_->Resample(decoded_audio);
   }
-  if (decoded_audio && decoded_audio->size() > 0) {
+  if (decoded_audio && decoded_audio->size_in_bytes() > 0) {
     if (channel_mixer_) {
       decoded_audio = channel_mixer_->Mix(decoded_audio);
     }
diff --git a/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h b/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h
index 0932965..2fc9964 100644
--- a/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h
+++ b/starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h
@@ -37,7 +37,7 @@
 class AdaptiveAudioDecoder : public AudioDecoder, private JobQueue::JobOwner {
  public:
   typedef std::function<scoped_ptr<filter::AudioDecoder>(
-      const SbMediaAudioSampleInfo& audio_sample_info,
+      const media::AudioStreamInfo& audio_stream_info,
       SbDrmSystem drm_system)>
       AudioDecoderCreator;
 
@@ -47,7 +47,7 @@
                              int* output_number_of_channels)>
       OutputFormatAdjustmentCallback;
 
-  AdaptiveAudioDecoder(const SbMediaAudioSampleInfo& audio_sample_info,
+  AdaptiveAudioDecoder(const media::AudioStreamInfo& audio_stream_info,
                        SbDrmSystem drm_system,
                        const AudioDecoderCreator& audio_decoder_creator,
                        const OutputFormatAdjustmentCallback&
@@ -62,11 +62,11 @@
   void Reset() override;
 
  private:
-  void InitializeAudioDecoder(const SbMediaAudioSampleInfo& audio_sample_info);
+  void InitializeAudioDecoder(const media::AudioStreamInfo& audio_stream_info);
   void TeardownAudioDecoder();
   void OnDecoderOutput();
 
-  const SbMediaAudioSampleInfo initial_audio_sample_info_;
+  const uint32_t initial_samples_per_second_;
   const SbDrmSystem drm_system_;
   const AudioDecoderCreator audio_decoder_creator_;
   const OutputFormatAdjustmentCallback output_adjustment_callback_;
@@ -74,7 +74,7 @@
   SbMediaAudioFrameStorageType output_storage_type_;
   int output_samples_per_second_;
   int output_number_of_channels_;
-  media::AudioSampleInfo input_audio_sample_info_;
+  media::AudioStreamInfo input_audio_stream_info_;
 
   OutputCB output_cb_ = nullptr;
   ErrorCB error_cb_ = nullptr;
diff --git a/starboard/shared/starboard/player/filter/audio_channel_layout_mixer_impl.cc b/starboard/shared/starboard/player/filter/audio_channel_layout_mixer_impl.cc
index 6a7bd14..f377720 100644
--- a/starboard/shared/starboard/player/filter/audio_channel_layout_mixer_impl.cc
+++ b/starboard/shared/starboard/player/filter/audio_channel_layout_mixer_impl.cc
@@ -89,7 +89,10 @@
 // 4 -> 1
 const float kQuadToMonoMatrix[] = {
     // output = 0.25 * (input.L + input.R + input.SL + input.SR)
-    0.25f, 0.25f, 0.25f, 0.25f,
+    0.25f,
+    0.25f,
+    0.25f,
+    0.25f,
 };
 
 // 5.1 -> 1
@@ -108,21 +111,51 @@
 // 5.1 -> 2
 const float kFivePointOneToStereoMatrix[] = {
     // output.L = L + sqrt(0.5) * (input.C + input.SL)
-    1.0f, 0.0f, 0.7071f, 0.0f, 0.7071f, 0.0f,
+    1.0f,
+    0.0f,
+    0.7071f,
+    0.0f,
+    0.7071f,
+    0.0f,
     // output.R = R + sqrt(0.5) * (input.C + input.SR)
-    0.0f, 1.0f, 0.7071f, 0.0f, 0.0f, 0.7071f,
+    0.0f,
+    1.0f,
+    0.7071f,
+    0.0f,
+    0.0f,
+    0.7071f,
 };
 
 // 5.1 -> 4
 const float kFivePointOneToQuadMatrix[] = {
     // output.L = L + sqrt(0.5) * input.C
-    1.0f, 0.0f, 0.7071f, 0.0f, 0.0f, 0.0f,
+    1.0f,
+    0.0f,
+    0.7071f,
+    0.0f,
+    0.0f,
+    0.0f,
     // output.R = R + sqrt(0.5) * input.C
-    0.0f, 1.0f, 0.7071f, 0.0f, 0.0f, 0.0f,
+    0.0f,
+    1.0f,
+    0.7071f,
+    0.0f,
+    0.0f,
+    0.0f,
     // output.SL = input.SL
-    0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
+    0.0f,
+    0.0f,
+    0.0f,
+    0.0f,
+    1.0f,
+    0.0f,
     // output.SR = input.SR
-    0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
+    0.0f,
+    0.0f,
+    0.0f,
+    0.0f,
+    0.0f,
+    1.0f,
 };
 
 // Get the samples of frames at |frame_index|.  If |input| is already
@@ -136,7 +169,7 @@
     int frame_index,
     SampleType* aux_buffer) {
   const SampleType* input_buffer =
-      reinterpret_cast<const SampleType*>(input->buffer());
+      reinterpret_cast<const SampleType*>(input->data());
   if (input->storage_type() == kSbMediaAudioFrameStorageTypeInterleaved) {
     return input_buffer + frame_index * input->channels();
   }
@@ -154,7 +187,7 @@
                                     scoped_refptr<DecodedAudio>* destination,
                                     int frame_index) {
   SampleType* dest_buffer =
-      reinterpret_cast<SampleType*>((*destination)->buffer());
+      reinterpret_cast<SampleType*>((*destination)->data());
   for (size_t channel_index = 0; channel_index < (*destination)->channels();
        channel_index++) {
     if ((*destination)->storage_type() ==
@@ -329,12 +362,12 @@
 
   scoped_refptr<DecodedAudio> output(
       new DecodedAudio(output_channels_, sample_type_, storage_type_,
-                       input->timestamp(), input->size() * 2));
+                       input->timestamp(), input->size_in_bytes() * 2));
   if (storage_type_ == kSbMediaAudioFrameStorageTypeInterleaved) {
     size_t frames_left = input->frames();
     size_t bytes_per_sample = GetBytesPerSample(sample_type_);
-    const uint8_t* src_buffer_ptr = input->buffer();
-    uint8_t* dest_buffer_ptr = output->buffer();
+    const uint8_t* src_buffer_ptr = input->data();
+    uint8_t* dest_buffer_ptr = output->data();
     while (frames_left > 0) {
       memcpy(dest_buffer_ptr, src_buffer_ptr, bytes_per_sample);
       dest_buffer_ptr += bytes_per_sample;
@@ -345,9 +378,9 @@
     }
   } else {
     SB_DCHECK(storage_type_ == kSbMediaAudioFrameStorageTypePlanar);
-    memcpy(output->buffer(), input->buffer(), input->size());
-    memcpy(output->buffer() + input->size(), input->buffer(),
-                 input->size());
+    memcpy(output->data(), input->data(), input->size_in_bytes());
+    memcpy(output->data() + input->size_in_bytes(), input->data(),
+           input->size_in_bytes());
   }
   return output;
 }
diff --git a/starboard/shared/starboard/player/filter/audio_frame_discarder.cc b/starboard/shared/starboard/player/filter/audio_frame_discarder.cc
new file mode 100644
index 0000000..2afc3fc
--- /dev/null
+++ b/starboard/shared/starboard/player/filter/audio_frame_discarder.cc
@@ -0,0 +1,89 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/player/filter/audio_frame_discarder.h"
+
+#include "starboard/common/log.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+void AudioFrameDiscarder::OnInputBuffers(const InputBuffers& input_buffers) {
+  if (input_buffer_infos_.size() >= kMaxNumberOfPendingInputBufferInfos) {
+    // This shouldn't happen as it's DCHECKed at the end of this function. Add
+    // an extra check here to ensure that |input_buffer_infos_| won't grow
+    // without bound, which can lead to OOM in production.
+    return;
+  }
+
+  for (auto&& input_buffer : input_buffers) {
+    SB_DCHECK(input_buffer);
+    SB_DCHECK(input_buffer->sample_type() == kSbMediaTypeAudio);
+
+    input_buffer_infos_.push({
+        input_buffer->timestamp(),
+        input_buffer->audio_sample_info().discarded_duration_from_front,
+        input_buffer->audio_sample_info().discarded_duration_from_back,
+    });
+  }
+
+  SB_DCHECK(input_buffer_infos_.size() < kMaxNumberOfPendingInputBufferInfos);
+}
+
+void AudioFrameDiscarder::AdjustForDiscardedDurations(
+    int sample_rate,
+    scoped_refptr<DecodedAudio>* decoded_audio) {
+  SB_DCHECK(decoded_audio);
+  SB_DCHECK(*decoded_audio);
+  SB_DCHECK(!input_buffer_infos_.empty());
+
+  if (input_buffer_infos_.empty()) {
+    return;
+  }
+
+  auto info = input_buffer_infos_.front();
+  SB_LOG_IF(WARNING, info.timestamp != (*decoded_audio)->timestamp())
+      << "Inconsistent timestamps between InputBuffer (@" << info.timestamp
+      << ") and DecodedAudio (@" << (*decoded_audio)->timestamp() << ").";
+  input_buffer_infos_.pop();
+
+  (*decoded_audio)
+      ->AdjustForDiscardedDurations(sample_rate,
+                                    info.discarded_duration_from_front,
+                                    info.discarded_duration_from_back);
+  // `(*decoded_audio)->frames()` might be 0 here.  We don't set it to nullptr
+  // in this case so the DecodedAudio instance is always valid (but might be
+  // empty).
+}
+
+void AudioFrameDiscarder::OnDecodedAudioEndOfStream() {
+  // |input_buffer_infos_| can have extra elements when the decoder skip outputs
+  // due to errors (like invalid inputs).
+  SB_LOG_IF(INFO, !input_buffer_infos_.empty())
+      << "OnDecodedAudioEndOfStream() is called with "
+      << input_buffer_infos_.size() << " input buffer infos pending.";
+}
+
+void AudioFrameDiscarder::Reset() {
+  input_buffer_infos_ = std::queue<InputBufferInfo>();
+}
+
+}  // namespace filter
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/starboard/shared/starboard/player/filter/audio_frame_discarder.h b/starboard/shared/starboard/player/filter/audio_frame_discarder.h
new file mode 100644
index 0000000..a1f4f37
--- /dev/null
+++ b/starboard/shared/starboard/player/filter/audio_frame_discarder.h
@@ -0,0 +1,67 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_FRAME_DISCARDER_H_
+#define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_FRAME_DISCARDER_H_
+
+#include <queue>
+
+#include "starboard/common/ref_counted.h"
+#include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/time.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+
+// The class helps tracking the information contained in the InputBuffer objects
+// required to adjust discarded duration on the DecodedAudio object.  It should
+// be used when the DecodedAudio object is produced asynchronously, and the
+// corresponding InputBuffer object isn't available at the time.
+// This class assumes that there is exact one DecodedAudio object produced for
+// one InputBuffer object, which may not always be the case.
+// TODO(b/274021285): Ensure that the class works when there isn't a 1:1
+//                    relationship between DecodedAudio and InputBuffer.
+class AudioFrameDiscarder {
+ public:
+  void OnInputBuffers(const InputBuffers& input_buffers);
+  void AdjustForDiscardedDurations(int sample_rate,
+                                   scoped_refptr<DecodedAudio>* decoded_audio);
+  void OnDecodedAudioEndOfStream();
+
+  void Reset();
+
+ private:
+  struct InputBufferInfo {
+    SbTime timestamp;
+    SbTime discarded_duration_from_front;
+    SbTime discarded_duration_from_back;
+  };
+
+  static constexpr size_t kMaxNumberOfPendingInputBufferInfos = 128;
+
+  std::queue<InputBufferInfo> input_buffer_infos_;
+};
+
+}  // namespace filter
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
+
+#endif  // STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_AUDIO_FRAME_DISCARDER_H_
diff --git a/starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.cc b/starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.cc
index 3e09554..107d2ae 100644
--- a/starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.cc
+++ b/starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.cc
@@ -35,7 +35,7 @@
  public:
   IdentityAudioResampler() : eos_reached_(false) {}
   scoped_refptr<DecodedAudio> Resample(
-      const scoped_refptr<DecodedAudio>& audio_data) override {
+      scoped_refptr<DecodedAudio> audio_data) override {
     SB_DCHECK(!eos_reached_);
 
     return audio_data;
@@ -68,12 +68,12 @@
 AudioRendererPcm::AudioRendererPcm(
     scoped_ptr<AudioDecoder> decoder,
     scoped_ptr<AudioRendererSink> audio_renderer_sink,
-    const SbMediaAudioSampleInfo& audio_sample_info,
+    const media::AudioStreamInfo& audio_stream_info,
     int max_cached_frames,
     int min_frames_per_append)
     : max_cached_frames_(max_cached_frames),
       min_frames_per_append_(min_frames_per_append),
-      channels_(audio_sample_info.number_of_channels),
+      channels_(audio_stream_info.number_of_channels),
       sink_sample_type_(GetSinkAudioSampleType(audio_renderer_sink.get())),
       bytes_per_frame_(media::GetBytesPerSample(sink_sample_type_) * channels_),
       frame_buffer_(max_cached_frames_ * bytes_per_frame_),
@@ -637,11 +637,16 @@
       resampled_audio = resampler_->Resample(decoded_audio);
     }
 
-    if (resampled_audio && resampled_audio->size() > 0) {
+    if (resampled_audio && resampled_audio->size_in_bytes() > 0) {
       // |time_stretcher_| only support kSbMediaAudioSampleTypeFloat32 and
       // kSbMediaAudioFrameStorageTypeInterleaved.
-      resampled_audio->SwitchFormatTo(kSbMediaAudioSampleTypeFloat32,
-                                      kSbMediaAudioFrameStorageTypeInterleaved);
+      if (!resampled_audio->IsFormat(
+              kSbMediaAudioSampleTypeFloat32,
+              kSbMediaAudioFrameStorageTypeInterleaved)) {
+        resampled_audio = resampled_audio->SwitchFormatTo(
+            kSbMediaAudioSampleTypeFloat32,
+            kSbMediaAudioFrameStorageTypeInterleaved);
+      }
       time_stretcher_.EnqueueBuffer(resampled_audio);
     }
 
@@ -713,9 +718,12 @@
 
   // |time_stretcher_| only support kSbMediaAudioSampleTypeFloat32 and
   // kSbMediaAudioFrameStorageTypeInterleaved.
-  decoded_audio->SwitchFormatTo(sink_sample_type_,
-                                kSbMediaAudioFrameStorageTypeInterleaved);
-  const uint8_t* source_buffer = decoded_audio->buffer();
+  if (!decoded_audio->IsFormat(sink_sample_type_,
+                               kSbMediaAudioFrameStorageTypeInterleaved)) {
+    decoded_audio = decoded_audio->SwitchFormatTo(
+        sink_sample_type_, kSbMediaAudioFrameStorageTypeInterleaved);
+  }
+  const uint8_t* source_buffer = decoded_audio->data();
   int frames_to_append = decoded_audio->frames();
   int frames_appended = 0;
 
diff --git a/starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.h b/starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.h
index ed3b734..fd7ce93 100644
--- a/starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.h
+++ b/starboard/shared/starboard/player/filter/audio_renderer_internal_pcm.h
@@ -19,13 +19,14 @@
 #include <string>
 #include <vector>
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/log.h"
 #include "starboard/common/mutex.h"
 #include "starboard/common/optional.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/media.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_frame_tracker.h"
@@ -68,7 +69,7 @@
   // tries to append to the sink buffer at once.
   AudioRendererPcm(scoped_ptr<AudioDecoder> decoder,
                    scoped_ptr<AudioRendererSink> audio_renderer_sink,
-                   const SbMediaAudioSampleInfo& audio_sample_info,
+                   const media::AudioStreamInfo& audio_stream_info,
                    int max_cached_frames,
                    int min_frames_per_append);
   ~AudioRendererPcm() override;
diff --git a/starboard/shared/starboard/player/filter/audio_resampler.h b/starboard/shared/starboard/player/filter/audio_resampler.h
index 91239b6..3aeaa4f 100644
--- a/starboard/shared/starboard/player/filter/audio_resampler.h
+++ b/starboard/shared/starboard/player/filter/audio_resampler.h
@@ -43,7 +43,7 @@
   // Write frames to the AudioResampler.  The format of the frames is determined
   // by the input formats passed to Create().
   virtual scoped_refptr<DecodedAudio> Resample(
-      const scoped_refptr<DecodedAudio>& audio_data) = 0;
+      scoped_refptr<DecodedAudio> audio_data) = 0;
 
   // Signal that the last audio input frame has been written.  The resampler
   // should allow for reading of any audio data inside its internal cache.  The
diff --git a/starboard/shared/starboard/player/filter/audio_resampler_impl.cc b/starboard/shared/starboard/player/filter/audio_resampler_impl.cc
index 892c2de..c07f29e 100644
--- a/starboard/shared/starboard/player/filter/audio_resampler_impl.cc
+++ b/starboard/shared/starboard/player/filter/audio_resampler_impl.cc
@@ -46,7 +46,7 @@
                                channels) {}
 
   scoped_refptr<DecodedAudio> Resample(
-      const scoped_refptr<DecodedAudio>& buffer) override;
+      scoped_refptr<DecodedAudio> buffer) override;
 
   scoped_refptr<DecodedAudio> WriteEndOfStream() override;
 
@@ -91,11 +91,14 @@
         channels, kSbMediaAudioSampleTypeFloat32,
         kSbMediaAudioFrameStorageTypeInterleaved, 0, resampled_audio_size);
 
-    float* dst = reinterpret_cast<float*>(resampled_audio->buffer());
+    float* dst = reinterpret_cast<float*>(resampled_audio->data());
     interleaved_resampler_.Resample(dst, out_num_of_frames);
 
-    resampled_audio->SwitchFormatTo(destination_sample_type_,
-                                    destination_storage_type_);
+    if (!resampled_audio->IsFormat(destination_sample_type_,
+                                   destination_storage_type_)) {
+      resampled_audio = resampled_audio->SwitchFormatTo(
+          destination_sample_type_, destination_storage_type_);
+    }
     return resampled_audio;
   }
 
@@ -103,13 +106,17 @@
 }
 
 scoped_refptr<DecodedAudio> AudioResamplerImpl::Resample(
-    const scoped_refptr<DecodedAudio>& audio_data) {
+    scoped_refptr<DecodedAudio> audio_data) {
   SB_DCHECK(audio_data->channels() == interleaved_resampler_.channels());
 
   // It does nothing if source sample type is float and source storage type is
   // interleaved.
-  audio_data->SwitchFormatTo(kSbMediaAudioSampleTypeFloat32,
-                             kSbMediaAudioFrameStorageTypeInterleaved);
+  if (!audio_data->IsFormat(kSbMediaAudioSampleTypeFloat32,
+                            kSbMediaAudioFrameStorageTypeInterleaved)) {
+    audio_data =
+        audio_data->SwitchFormatTo(kSbMediaAudioSampleTypeFloat32,
+                                   kSbMediaAudioFrameStorageTypeInterleaved);
+  }
 
   int num_of_frames = audio_data->frames();
   frames_to_resample_ += num_of_frames;
@@ -121,7 +128,7 @@
 
   scoped_refptr<DecodedAudio> resampled_audio = nullptr;
 
-  float* samples = reinterpret_cast<float*>(audio_data->buffer());
+  float* samples = reinterpret_cast<float*>(audio_data->data());
   interleaved_resampler_.QueueBuffer(samples, num_of_frames);
 
   if (interleaved_resampler_.HasEnoughData(out_num_of_frames)) {
@@ -129,12 +136,15 @@
         new DecodedAudio(channels, kSbMediaAudioSampleTypeFloat32,
                          kSbMediaAudioFrameStorageTypeInterleaved,
                          audio_data->timestamp(), resampled_audio_size);
-    float* dst = reinterpret_cast<float*>(resampled_audio->buffer());
+    float* dst = reinterpret_cast<float*>(resampled_audio->data());
     interleaved_resampler_.Resample(dst, out_num_of_frames);
     frames_resampled_ += out_num_of_frames;
 
-    resampled_audio->SwitchFormatTo(destination_sample_type_,
-                                    destination_storage_type_);
+    if (!resampled_audio->IsFormat(destination_sample_type_,
+                                   destination_storage_type_)) {
+      resampled_audio = resampled_audio->SwitchFormatTo(
+          destination_sample_type_, destination_storage_type_);
+    }
   }
 
   return resampled_audio;
diff --git a/starboard/shared/starboard/player/filter/audio_time_stretcher.cc b/starboard/shared/starboard/player/filter/audio_time_stretcher.cc
index f451f5b..7d0f6fb 100644
--- a/starboard/shared/starboard/player/filter/audio_time_stretcher.cc
+++ b/starboard/shared/starboard/player/filter/audio_time_stretcher.cc
@@ -20,14 +20,14 @@
 
 #include <algorithm>
 #include <cmath>
+#include <cstring>
+#include <utility>
 
 #include "starboard/common/log.h"
 #include "starboard/memory.h"
 #include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/filter/wsola_internal.h"
 
-#include <cstring>
-
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -157,7 +157,7 @@
       channels_, sample_type_, kSbMediaAudioFrameStorageTypeInterleaved, 0,
       (ola_window_size_ + ola_hop_size_) * bytes_per_frame_);
   // Initialize for overlap-and-add of the first block.
-  memset(wsola_output_->buffer(), 0, wsola_output_->size());
+  memset(wsola_output_->data(), 0, wsola_output_->size_in_bytes());
 
   // Auxiliary containers.
   optimal_block_ = new DecodedAudio(channels_, sample_type_,
@@ -201,7 +201,7 @@
     // audio_buffer_.frames()+1.
     int seek_frames = std::min(static_cast<int>(muted_partial_frame_),
                                audio_buffer_.frames());
-    memset(dest->buffer(), 0, frames_to_render * bytes_per_frame_);
+    memset(dest->data(), 0, frames_to_render * bytes_per_frame_);
     audio_buffer_.SeekFrames(seek_frames);
 
     // Determine the partial frame that remains to be skipped for next call. If
@@ -244,7 +244,7 @@
   output_time_ = 0.0;
   search_block_index_ = 0;
   target_block_index_ = 0;
-  memset(wsola_output_->buffer(), 0, wsola_output_->size());
+  memset(wsola_output_->data(), 0, wsola_output_->size_in_bytes());
   num_complete_frames_ = 0;
 
   // Reset |capacity_| so growth triggered by underflows doesn't penalize seek
@@ -287,8 +287,8 @@
   // Overlap-and-add.
   for (int k = 0; k < channels_; ++k) {
     const float* const ch_opt_frame =
-        reinterpret_cast<const float*>(optimal_block_->buffer()) + k;
-    float* ch_output = reinterpret_cast<float*>(wsola_output_->buffer()) + k +
+        reinterpret_cast<const float*>(optimal_block_->data()) + k;
+    float* ch_output = reinterpret_cast<float*>(wsola_output_->data()) + k +
                        num_complete_frames_ * sizeof(float);
     for (int n = 0; n < ola_hop_size_; ++n) {
       ch_output[n * channels_] =
@@ -298,12 +298,12 @@
   }
   // Copy the second half to the output.
   const float* const ch_opt_frame =
-      reinterpret_cast<const float*>(optimal_block_->buffer());
-  float* ch_output = reinterpret_cast<float*>(wsola_output_->buffer()) +
+      reinterpret_cast<const float*>(optimal_block_->data());
+  float* ch_output = reinterpret_cast<float*>(wsola_output_->data()) +
                      num_complete_frames_ * sizeof(float);
   memcpy(&ch_output[ola_hop_size_ * channels_],
-               &ch_opt_frame[ola_hop_size_ * channels_],
-               sizeof(*ch_opt_frame) * ola_hop_size_ * channels_);
+         &ch_opt_frame[ola_hop_size_ * channels_],
+         sizeof(*ch_opt_frame) * ola_hop_size_ * channels_);
 
   num_complete_frames_ += ola_hop_size_;
   UpdateOutputTime(playback_rate, ola_hop_size_);
@@ -347,14 +347,14 @@
   if (rendered_frames == 0)
     return 0;  // There is nothing to read from |wsola_output_|, return.
 
-  memcpy(dest->buffer() + bytes_per_frame_ * dest_offset,
-               wsola_output_->buffer(), rendered_frames * bytes_per_frame_);
+  memcpy(dest->data() + bytes_per_frame_ * dest_offset, wsola_output_->data(),
+         rendered_frames * bytes_per_frame_);
 
   // Remove the frames which are read.
   int frames_to_move = wsola_output_->frames() - rendered_frames;
-  memmove(wsola_output_->buffer(),
-               wsola_output_->buffer() + rendered_frames * bytes_per_frame_,
-               frames_to_move * bytes_per_frame_);
+  memmove(wsola_output_->data(),
+          wsola_output_->data() + rendered_frames * bytes_per_frame_,
+          frames_to_move * bytes_per_frame_);
   num_complete_frames_ -= rendered_frames;
   return rendered_frames;
 }
@@ -406,9 +406,9 @@
     // where target-block has higher weight close to zero (weight of 1 at index
     // 0) and lower weight close the end.
     for (int k = 0; k < channels_; ++k) {
-      float* ch_opt = reinterpret_cast<float*>(optimal_block_->buffer()) + k;
+      float* ch_opt = reinterpret_cast<float*>(optimal_block_->data()) + k;
       const float* const ch_target =
-          reinterpret_cast<float*>(target_block_->buffer()) + k;
+          reinterpret_cast<float*>(target_block_->data()) + k;
       for (int n = 0; n < ola_window_size_; ++n) {
         ch_opt[n * channels_] =
             ch_opt[n * channels_] * transition_window_[n] +
@@ -434,7 +434,7 @@
     read_offset_frames = 0;
     num_frames_to_read -= num_zero_frames_appended;
     write_offset = num_zero_frames_appended;
-    memset(dest->buffer(), 0, num_zero_frames_appended * bytes_per_frame_);
+    memset(dest->data(), 0, num_zero_frames_appended * bytes_per_frame_);
   }
   audio_buffer_.PeekFrames(num_frames_to_read, read_offset_frames, write_offset,
                            dest);
diff --git a/starboard/shared/starboard/player/filter/decoded_audio_queue.cc b/starboard/shared/starboard/player/filter/decoded_audio_queue.cc
index 9508cf6..b8a87da 100644
--- a/starboard/shared/starboard/player/filter/decoded_audio_queue.cc
+++ b/starboard/shared/starboard/player/filter/decoded_audio_queue.cc
@@ -116,17 +116,16 @@
       if (dest) {
         SB_DCHECK(buffer->channels() == dest->channels());
         if (dest->sample_type() == kSbMediaAudioSampleTypeFloat32) {
-          const float* source =
-              reinterpret_cast<const float*>(buffer->buffer()) +
-              buffer->channels() * current_buffer_offset;
-          float* destination = reinterpret_cast<float*>(dest->buffer()) +
+          const float* source = reinterpret_cast<const float*>(buffer->data()) +
+                                buffer->channels() * current_buffer_offset;
+          float* destination = reinterpret_cast<float*>(dest->data()) +
                                dest->channels() * (dest_frame_offset + taken);
           memcpy(destination, source, copied * dest->channels() * 4);
         } else {
           const int16_t* source =
-              reinterpret_cast<const int16_t*>(buffer->buffer()) +
+              reinterpret_cast<const int16_t*>(buffer->data()) +
               buffer->channels() * current_buffer_offset;
-          int16_t* destination = reinterpret_cast<int16_t*>(dest->buffer()) +
+          int16_t* destination = reinterpret_cast<int16_t*>(dest->data()) +
                                  dest->channels() * (dest_frame_offset + taken);
           memcpy(destination, source, copied * dest->channels() * 2);
         }
diff --git a/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc b/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
index a632764..3dce606 100644
--- a/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
+++ b/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc
@@ -73,13 +73,19 @@
     const SbPlayerCreationParam* creation_param,
     SbDecodeTargetGraphicsContextProvider* provider)
     : JobOwner(kDetached),
-      video_codec_(creation_param->video_sample_info.codec),
-      audio_codec_(creation_param->audio_sample_info.codec),
       drm_system_(creation_param->drm_system),
-      audio_sample_info_(creation_param->audio_sample_info),
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+      audio_stream_info_(creation_param->audio_stream_info),
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+      audio_stream_info_(creation_param->audio_sample_info),
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
       output_mode_(creation_param->output_mode),
       decode_target_graphics_context_provider_(provider),
-      video_sample_info_(creation_param->video_sample_info) {
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+      video_stream_info_(creation_param->video_stream_info) {
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+      video_stream_info_(creation_param->video_sample_info) {
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   update_job_ = std::bind(&FilterBasedPlayerWorkerHandler::Update, this);
 }
 
@@ -110,11 +116,11 @@
       PlayerComponents::Factory::Create();
   SB_DCHECK(factory);
 
-  if (audio_codec_ != kSbMediaAudioCodecNone) {
+  if (audio_stream_info_.codec != kSbMediaAudioCodecNone) {
     // TODO: This is not ideal as we should really handle the creation failure
     // of audio sink inside the audio renderer to give the renderer a chance to
     // resample the decoded audio.
-    const int required_audio_channels = audio_sample_info_.number_of_channels;
+    const int required_audio_channels = audio_stream_info_.number_of_channels;
     const int supported_audio_channels = SbAudioSinkGetMaxChannels();
     if (required_audio_channels > supported_audio_channels) {
       SB_LOG(ERROR) << "Audio channels requested " << required_audio_channels
@@ -129,9 +135,8 @@
   }
 
   PlayerComponents::Factory::CreationParameters creation_parameters(
-      audio_codec_, audio_sample_info_, video_codec_, video_sample_info_,
-      player_, output_mode_, decode_target_graphics_context_provider_,
-      drm_system_);
+      audio_stream_info_, video_stream_info_, player_, output_mode_,
+      decode_target_graphics_context_provider_, drm_system_);
 
   {
     ::starboard::ScopedLock lock(player_components_existence_mutex_);
@@ -148,10 +153,10 @@
     audio_renderer_ = player_components_->GetAudioRenderer();
     video_renderer_ = player_components_->GetVideoRenderer();
   }
-  if (audio_codec_ != kSbMediaAudioCodecNone) {
+  if (audio_stream_info_.codec != kSbMediaAudioCodecNone) {
     SB_DCHECK(audio_renderer_);
   }
-  if (video_codec_ != kSbMediaVideoCodecNone) {
+  if (video_stream_info_.codec != kSbMediaVideoCodecNone) {
     SB_DCHECK(video_renderer_);
   }
   SB_DCHECK(media_time_provider_);
diff --git a/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h b/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
index 17df66d..6f3ed97 100644
--- a/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
+++ b/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h
@@ -76,11 +76,9 @@
   UpdatePlayerStateCB update_player_state_cb_;
   UpdatePlayerErrorCB update_player_error_cb_;
 
-  SbMediaVideoCodec video_codec_;
-  SbMediaAudioCodec audio_codec_;
   SbDrmSystem drm_system_;
 
-  media::AudioSampleInfo audio_sample_info_;
+  const media::AudioStreamInfo audio_stream_info_;
 
   // A mutex guarding changes to the existence (e.g. creation/destruction)
   // of the |player_components_| object.  This is necessary because calls to
@@ -115,7 +113,7 @@
   SbPlayerOutputMode output_mode_;
   SbDecodeTargetGraphicsContextProvider*
       decode_target_graphics_context_provider_;
-  media::VideoSampleInfo video_sample_info_;
+  const media::VideoStreamInfo video_stream_info_;
 };
 
 }  // namespace filter
diff --git a/starboard/shared/starboard/player/filter/player_components.cc b/starboard/shared/starboard/player/filter/player_components.cc
index 94d71e0..624efd6 100644
--- a/starboard/shared/starboard/player/filter/player_components.cc
+++ b/starboard/shared/starboard/player/filter/player_components.cc
@@ -83,85 +83,58 @@
 }  // namespace
 
 PlayerComponents::Factory::CreationParameters::CreationParameters(
-    SbMediaAudioCodec audio_codec,
-    const SbMediaAudioSampleInfo& audio_sample_info,
+    const media::AudioStreamInfo& audio_stream_info,
     SbDrmSystem drm_system)
-    : audio_codec_(audio_codec),
-      audio_sample_info_(audio_sample_info),
-      drm_system_(drm_system) {
-  SB_DCHECK(audio_codec_ != kSbMediaAudioCodecNone);
-  TryToCopyAudioSpecificConfig();
+    : audio_stream_info_(audio_stream_info), drm_system_(drm_system) {
+  SB_DCHECK(audio_stream_info_.codec != kSbMediaAudioCodecNone);
 }
 
 PlayerComponents::Factory::CreationParameters::CreationParameters(
-    SbMediaVideoCodec video_codec,
-    const SbMediaVideoSampleInfo& video_sample_info,
+    const media::VideoStreamInfo& video_stream_info,
     SbPlayer player,
     SbPlayerOutputMode output_mode,
     SbDecodeTargetGraphicsContextProvider*
         decode_target_graphics_context_provider,
     SbDrmSystem drm_system)
-    : video_codec_(video_codec),
-      video_sample_info_(video_sample_info),
+    : video_stream_info_(video_stream_info),
       player_(player),
       output_mode_(output_mode),
       decode_target_graphics_context_provider_(
           decode_target_graphics_context_provider),
       drm_system_(drm_system) {
-  SB_DCHECK(video_codec_ != kSbMediaVideoCodecNone);
+  SB_DCHECK(video_stream_info_.codec != kSbMediaVideoCodecNone);
   SB_DCHECK(SbPlayerIsValid(player_));
   SB_DCHECK(output_mode_ != kSbPlayerOutputModeInvalid);
 }
 
 PlayerComponents::Factory::CreationParameters::CreationParameters(
-    SbMediaAudioCodec audio_codec,
-    const SbMediaAudioSampleInfo& audio_sample_info,
-    SbMediaVideoCodec video_codec,
-    const SbMediaVideoSampleInfo& video_sample_info,
+    const media::AudioStreamInfo& audio_stream_info,
+    const media::VideoStreamInfo& video_stream_info,
     SbPlayer player,
     SbPlayerOutputMode output_mode,
     SbDecodeTargetGraphicsContextProvider*
         decode_target_graphics_context_provider,
     SbDrmSystem drm_system)
-    : audio_codec_(audio_codec),
-      audio_sample_info_(audio_sample_info),
-      video_codec_(video_codec),
-      video_sample_info_(video_sample_info),
+    : audio_stream_info_(audio_stream_info),
+      video_stream_info_(video_stream_info),
       player_(player),
       output_mode_(output_mode),
       decode_target_graphics_context_provider_(
           decode_target_graphics_context_provider),
       drm_system_(drm_system) {
-  SB_DCHECK(audio_codec_ != kSbMediaAudioCodecNone ||
-            video_codec_ != kSbMediaVideoCodecNone);
-  TryToCopyAudioSpecificConfig();
+  SB_DCHECK(audio_stream_info_.codec != kSbMediaAudioCodecNone ||
+            video_stream_info_.codec != kSbMediaVideoCodecNone);
 }
 
 PlayerComponents::Factory::CreationParameters::CreationParameters(
     const CreationParameters& that) {
-  this->audio_codec_ = that.audio_codec_;
-  this->audio_sample_info_ = that.audio_sample_info_;
-  this->video_codec_ = that.video_codec_;
-  this->video_sample_info_ = that.video_sample_info_;
+  this->audio_stream_info_ = that.audio_stream_info_;
+  this->video_stream_info_ = that.video_stream_info_;
   this->player_ = that.player_;
   this->output_mode_ = that.output_mode_;
   this->decode_target_graphics_context_provider_ =
       that.decode_target_graphics_context_provider_;
   this->drm_system_ = that.drm_system_;
-
-  TryToCopyAudioSpecificConfig();
-}
-
-void PlayerComponents::Factory::CreationParameters::
-    TryToCopyAudioSpecificConfig() {
-  if (audio_sample_info_.audio_specific_config_size > 0) {
-    auto audio_specific_config = reinterpret_cast<const uint8_t*>(
-        audio_sample_info_.audio_specific_config);
-    audio_specific_config_.assign(
-        audio_specific_config,
-        audio_specific_config + audio_sample_info_.audio_specific_config_size);
-    audio_sample_info_.audio_specific_config = audio_specific_config_.data();
-  }
 }
 
 scoped_ptr<PlayerComponents> PlayerComponents::Factory::CreateComponents(
@@ -229,7 +202,7 @@
 
     audio_renderer.reset(
         new AudioRendererPcm(audio_decoder.Pass(), audio_renderer_sink.Pass(),
-                             creation_parameters.audio_sample_info(),
+                             creation_parameters.audio_stream_info(),
                              max_cached_frames, min_frames_per_append));
   }
 
@@ -264,13 +237,12 @@
   SB_DCHECK(audio_decoder);
   SB_DCHECK(audio_renderer_sink);
 
-  auto decoder_creator = [](const SbMediaAudioSampleInfo& audio_sample_info,
+  auto decoder_creator = [](const media::AudioStreamInfo& audio_stream_info,
                             SbDrmSystem drm_system) {
-    return scoped_ptr<AudioDecoder>(
-        new StubAudioDecoder(audio_sample_info.codec, audio_sample_info));
+    return scoped_ptr<AudioDecoder>(new StubAudioDecoder(audio_stream_info));
   };
   audio_decoder->reset(new AdaptiveAudioDecoder(
-      creation_parameters.audio_sample_info(), creation_parameters.drm_system(),
+      creation_parameters.audio_stream_info(), creation_parameters.drm_system(),
       decoder_creator));
   audio_renderer_sink->reset(new AudioRendererSinkImpl);
 }
@@ -304,11 +276,11 @@
   // AudioRenderer prefers to use kSbMediaAudioSampleTypeFloat32 and only uses
   // kSbMediaAudioSampleTypeInt16Deprecated when float32 is not supported.
   int min_frames_required = SbAudioSinkGetMinBufferSizeInFrames(
-      creation_parameters.audio_sample_info().number_of_channels,
+      creation_parameters.audio_stream_info().number_of_channels,
       SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)
           ? kSbMediaAudioSampleTypeFloat32
           : kSbMediaAudioSampleTypeInt16Deprecated,
-      creation_parameters.audio_sample_info().samples_per_second);
+      creation_parameters.audio_stream_info().samples_per_second);
   // Audio renderer would sleep for a while if it thinks there're enough
   // frames in the sink. The sleeping time is 1/4 of |max_cached_frames|. So, to
   // maintain required min buffer size of audio sink, the |max_cached_frames|
diff --git a/starboard/shared/starboard/player/filter/player_components.h b/starboard/shared/starboard/player/filter/player_components.h
index 0948845..b96c506 100644
--- a/starboard/shared/starboard/player/filter/player_components.h
+++ b/starboard/shared/starboard/player/filter/player_components.h
@@ -60,20 +60,17 @@
    public:
     class CreationParameters {
      public:
-      CreationParameters(SbMediaAudioCodec audio_codec,
-                         const SbMediaAudioSampleInfo& audio_sample_info,
-                         SbDrmSystem drm_system = kSbDrmSystemInvalid);
-      CreationParameters(SbMediaVideoCodec video_codec,
-                         const SbMediaVideoSampleInfo& video_sample_info,
+      explicit CreationParameters(
+          const media::AudioStreamInfo& audio_stream_info,
+          SbDrmSystem drm_system = kSbDrmSystemInvalid);
+      CreationParameters(const media::VideoStreamInfo& video_stream_info,
                          SbPlayer player,
                          SbPlayerOutputMode output_mode,
                          SbDecodeTargetGraphicsContextProvider*
                              decode_target_graphics_context_provider,
                          SbDrmSystem drm_system = kSbDrmSystemInvalid);
-      CreationParameters(SbMediaAudioCodec audio_codec,
-                         const SbMediaAudioSampleInfo& audio_sample_info,
-                         SbMediaVideoCodec video_codec,
-                         const SbMediaVideoSampleInfo& video_sample_info,
+      CreationParameters(const media::AudioStreamInfo& audio_stream_info,
+                         const media::VideoStreamInfo& video_stream_info,
                          SbPlayer player,
                          SbPlayerOutputMode output_mode,
                          SbDecodeTargetGraphicsContextProvider*
@@ -82,63 +79,62 @@
       CreationParameters(const CreationParameters& that);
       void operator=(const CreationParameters& that) = delete;
 
-      void reset_audio_codec() { audio_codec_ = kSbMediaAudioCodecNone; }
-      void reset_video_codec() { video_codec_ = kSbMediaVideoCodecNone; }
-
-      SbMediaAudioCodec audio_codec() const { return audio_codec_; }
-
-      const SbMediaAudioSampleInfo& audio_sample_info() const {
-        SB_DCHECK(audio_codec_ != kSbMediaAudioCodecNone);
-        return audio_sample_info_;
+      void reset_audio_codec() {
+        audio_stream_info_.codec = kSbMediaAudioCodecNone;
+      }
+      void reset_video_codec() {
+        video_stream_info_.codec = kSbMediaVideoCodecNone;
       }
 
-      SbMediaVideoCodec video_codec() const { return video_codec_; }
+      SbMediaAudioCodec audio_codec() const { return audio_stream_info_.codec; }
 
-      const char* audio_mime() const {
-        SB_DCHECK(audio_codec_ != kSbMediaAudioCodecNone);
-        return audio_sample_info_.mime;
+      const media::AudioStreamInfo& audio_stream_info() const {
+        SB_DCHECK(audio_stream_info_.codec != kSbMediaAudioCodecNone);
+        return audio_stream_info_;
       }
 
-      const SbMediaVideoSampleInfo& video_sample_info() const {
-        SB_DCHECK(video_codec_ != kSbMediaVideoCodecNone);
-        return video_sample_info_;
+      SbMediaVideoCodec video_codec() const { return video_stream_info_.codec; }
+
+      const std::string& audio_mime() const {
+        SB_DCHECK(audio_stream_info_.codec != kSbMediaAudioCodecNone);
+        return audio_stream_info_.mime;
       }
 
-      const char* video_mime() const {
-        SB_DCHECK(video_codec_ != kSbMediaVideoCodecNone);
-        return video_sample_info_.mime;
+      const media::VideoStreamInfo& video_stream_info() const {
+        SB_DCHECK(video_stream_info_.codec != kSbMediaVideoCodecNone);
+        return video_stream_info_;
       }
 
-      const char* max_video_capabilities() const {
-        SB_DCHECK(video_codec_ != kSbMediaVideoCodecNone);
-        return video_sample_info_.max_video_capabilities;
+      const std::string& video_mime() const {
+        SB_DCHECK(video_stream_info_.codec != kSbMediaVideoCodecNone);
+        return video_stream_info_.mime;
+      }
+
+      const std::string& max_video_capabilities() const {
+        SB_DCHECK(video_stream_info_.codec != kSbMediaVideoCodecNone);
+        return video_stream_info_.max_video_capabilities;
       }
 
       SbPlayer player() const { return player_; }
       SbPlayerOutputMode output_mode() const { return output_mode_; }
       SbDecodeTargetGraphicsContextProvider*
       decode_target_graphics_context_provider() const {
-        SB_DCHECK(video_codec_ != kSbMediaVideoCodecNone);
+        SB_DCHECK(video_stream_info_.codec != kSbMediaVideoCodecNone);
         return decode_target_graphics_context_provider_;
       }
 
       SbDrmSystem drm_system() const { return drm_system_; }
 
      private:
-      void TryToCopyAudioSpecificConfig();
-
-      // The following members are only used by the audio stream, and only need
-      // to be set when |audio_codec| isn't kSbMediaAudioCodecNone.
-      // |audio_codec| can be set to kSbMediaAudioCodecNone for audioless video.
-      SbMediaAudioCodec audio_codec_ = kSbMediaAudioCodecNone;
-      media::AudioSampleInfo audio_sample_info_;
+      // |audio_stream_info_.codec| can be set to kSbMediaAudioCodecNone for
+      // audioless video.
+      media::AudioStreamInfo audio_stream_info_;
 
       // The following members are only used by the video stream, and only need
-      // to be set when |video_codec| isn't kSbMediaVideoCodecNone.
-      // |video_codec| can be set to kSbMediaVideoCodecNone for audio only
-      // video.
-      SbMediaVideoCodec video_codec_ = kSbMediaVideoCodecNone;
-      media::VideoSampleInfo video_sample_info_;
+      // to be set when |video_stream_info_.codec| isn't kSbMediaVideoCodecNone.
+      // |video_stream_info_.codec| can be set to kSbMediaVideoCodecNone for
+      // audio only video.
+      media::VideoStreamInfo video_stream_info_;
       SbPlayer player_ = kSbPlayerInvalid;
       SbPlayerOutputMode output_mode_ = kSbPlayerOutputModeInvalid;
       SbDecodeTargetGraphicsContextProvider*
@@ -147,8 +143,6 @@
       // The following member are used by both the audio stream and the video
       // stream, when they are encrypted.
       SbDrmSystem drm_system_ = kSbDrmSystemInvalid;
-
-      std::vector<uint8_t> audio_specific_config_;
     };
 
     virtual ~Factory() {}
diff --git a/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.h b/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.h
index e3f4581..4caf66e 100644
--- a/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.h
+++ b/starboard/shared/starboard/player/filter/punchout_video_renderer_sink.h
@@ -15,7 +15,7 @@
 #ifndef STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_PUNCHOUT_VIDEO_RENDERER_SINK_H_
 #define STARBOARD_SHARED_STARBOARD_PLAYER_FILTER_PUNCHOUT_VIDEO_RENDERER_SINK_H_
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/mutex.h"
 #include "starboard/media.h"
 #include "starboard/player.h"
diff --git a/starboard/shared/starboard/player/filter/stub_audio_decoder.cc b/starboard/shared/starboard/player/filter/stub_audio_decoder.cc
index b406e40..2f4a7b4 100644
--- a/starboard/shared/starboard/player/filter/stub_audio_decoder.cc
+++ b/starboard/shared/starboard/player/filter/stub_audio_decoder.cc
@@ -14,6 +14,8 @@
 
 #include "starboard/shared/starboard/player/filter/stub_audio_decoder.h"
 
+#include <algorithm>
+
 #include "starboard/audio_sink.h"
 #include "starboard/common/log.h"
 
@@ -25,6 +27,9 @@
 
 namespace {
 
+using ::starboard::shared::starboard::media::AudioDurationToFrames;
+using ::starboard::shared::starboard::media::GetBytesPerSample;
+
 SbMediaAudioSampleType GetSupportedSampleType() {
   if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) {
     return kSbMediaAudioSampleTypeFloat32;
@@ -34,14 +39,59 @@
   return kSbMediaAudioSampleTypeInt16Deprecated;
 }
 
+// Calculate the frames per InputBuffer based on two consecutive audio samples.
+int CalculateFramesPerInputBuffer(int sample_rate,
+                                  const scoped_refptr<InputBuffer>& first,
+                                  const scoped_refptr<InputBuffer>& second) {
+  SB_DCHECK(first);
+  SB_DCHECK(second);
+
+  SbTime duration = second->timestamp() - first->timestamp();
+  if (duration <= 0) {
+    SB_LOG(ERROR) << "Duration (" << duration << ") for InputBuffer@ "
+                  << first->timestamp() << " is invalid.";
+    return -1;
+  }
+
+  return AudioDurationToFrames(duration, sample_rate);
+}
+
+scoped_refptr<DecodedAudio> CreateDecodedAudio(
+    SbTime timestamp,
+    SbMediaAudioSampleType sample_type,
+    int number_of_channels,
+    int frames) {
+  int sample_size = GetBytesPerSample(sample_type);
+  scoped_refptr<DecodedAudio> decoded_audio(new DecodedAudio(
+      number_of_channels, sample_type, kSbMediaAudioFrameStorageTypeInterleaved,
+      timestamp, sample_size * number_of_channels * frames));
+
+  for (int j = 0; j < decoded_audio->size_in_bytes() / sample_size; ++j) {
+    if (sample_size == 2) {
+      *(reinterpret_cast<int16_t*>(decoded_audio->data()) + j) = j;
+    } else {
+      SB_DCHECK(sample_size == 4);
+      *(reinterpret_cast<float*>(decoded_audio->data()) + j) =
+          ((j % 1024) - 512) / 512.0f;
+    }
+  }
+
+  return decoded_audio;
+}
+
 }  // namespace
 
 StubAudioDecoder::StubAudioDecoder(
-    SbMediaAudioCodec audio_codec,
-    const SbMediaAudioSampleInfo& audio_sample_info)
-    : sample_type_(GetSupportedSampleType()),
-      audio_codec_(audio_codec),
-      audio_sample_info_(audio_sample_info) {}
+    const media::AudioStreamInfo& audio_stream_info)
+    : codec_(audio_stream_info.codec),
+      number_of_channels_(audio_stream_info.number_of_channels),
+      samples_per_second_(audio_stream_info.samples_per_second),
+      sample_type_(GetSupportedSampleType()) {
+  if (codec_ == kSbMediaAudioCodecAac) {
+    // Assume the frames per input buffer is always 1024 for aac.
+    frames_per_input_ = 1024;
+  }
+}
 
 void StubAudioDecoder::Initialize(const OutputCB& output_cb,
                                   const ErrorCB& error_cb) {
@@ -80,7 +130,7 @@
 scoped_refptr<DecodedAudio> StubAudioDecoder::Read(int* samples_per_second) {
   SB_DCHECK(BelongsToCurrentThread());
 
-  *samples_per_second = audio_sample_info_.samples_per_second;
+  *samples_per_second = samples_per_second_;
   ScopedLock lock(decoded_audios_mutex_);
   if (decoded_audios_.empty()) {
     return scoped_refptr<DecodedAudio>();
@@ -113,77 +163,75 @@
 
 void StubAudioDecoder::DecodeOneBuffer(
     const scoped_refptr<InputBuffer>& input_buffer) {
-  // Values to represent what kind of dummy audio to fill the decoded audio
-  // we produce with.
-  enum FillType {
-    kSilence,
-    kWave,
-  };
-  // Can be set locally to fill with different types.
-  const FillType fill_type = kSilence;
   const int kMaxInputBeforeMultipleDecodedAudios = 4;
 
   if (last_input_buffer_) {
-    SbTime total_output_duration =
-        input_buffer->timestamp() - last_input_buffer_->timestamp();
-    if (total_output_duration < 0) {
-      SB_DLOG(ERROR) << "Total output duration " << total_output_duration
-                     << " is invalid.";
-      error_cb_(kSbPlayerErrorDecode, "Total output duration is less than 0.");
-      return;
-    }
-    size_t sample_size =
-        sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated ? 2 : 4;
-    size_t total_frame_size = total_output_duration *
-                              audio_sample_info_.samples_per_second /
-                              kSbTimeSecond;
-    if (audio_codec_ == kSbMediaAudioCodecAac) {
-      // Frame size for AAC is fixed at 1024 samples.
-      total_frame_size = 1024;
+    if (!frames_per_input_) {
+      auto frames_per_input = CalculateFramesPerInputBuffer(
+          samples_per_second_, last_input_buffer_, input_buffer);
+      if (frames_per_input <= 0) {
+        error_cb_(kSbPlayerErrorDecode,
+                  "Failed to calculate frames per input.");
+        return;
+      }
+      frames_per_input_ = frames_per_input;
     }
 
-    // Send 3 decoded audio objects on every 5th call to DecodeOneBuffer().
-    int num_decoded_audio_objects =
-        total_input_count_ % kMaxInputBeforeMultipleDecodedAudios == 0 ? 3 : 1;
-    size_t output_frame_size = total_frame_size / num_decoded_audio_objects;
-    size_t output_byte_size =
-        output_frame_size * sample_size * audio_sample_info_.number_of_channels;
+    scoped_refptr<DecodedAudio> decoded_audio =
+        CreateDecodedAudio(last_input_buffer_->timestamp(), sample_type_,
+                           number_of_channels_, *frames_per_input_);
 
-    for (int i = 0; i < num_decoded_audio_objects; ++i) {
-      SbTime timestamp =
-          last_input_buffer_->timestamp() +
-          ((total_output_duration / num_decoded_audio_objects) * i);
-      // Calculate the output frame size of the last decoded audio object, which
-      // may be larger than the rest.
-      if (i == num_decoded_audio_objects - 1 && num_decoded_audio_objects > 1) {
-        output_frame_size = total_frame_size -
-                            (num_decoded_audio_objects - 1) * output_frame_size;
-        output_byte_size = output_frame_size * sample_size *
-                           audio_sample_info_.number_of_channels;
-      }
-      scoped_refptr<DecodedAudio> decoded_audio(
-          new DecodedAudio(audio_sample_info_.number_of_channels, sample_type_,
-                           kSbMediaAudioFrameStorageTypeInterleaved, timestamp,
-                           output_byte_size));
+    decoded_audio->AdjustForDiscardedDurations(
+        samples_per_second_,
+        last_input_buffer_->audio_sample_info().discarded_duration_from_front,
+        last_input_buffer_->audio_sample_info().discarded_duration_from_back);
 
-      if (fill_type == kSilence) {
-        memset(decoded_audio.get()->buffer(), 0, output_byte_size);
-      } else {
-        SB_DCHECK(fill_type == kWave);
-        for (int j = 0; j < output_byte_size / sample_size; ++j) {
-          if (sample_size == 2) {
-            *(reinterpret_cast<int16_t*>(decoded_audio.get()->buffer()) + j) =
-                j;
-          } else {
-            SB_DCHECK(sample_size == 4);
-            *(reinterpret_cast<float*>(decoded_audio.get()->buffer()) + j) =
-                ((j % 1024) - 512) / 512.0f;
-          }
-        }
-      }
+    if (total_input_count_ % kMaxInputBeforeMultipleDecodedAudios != 0) {
       ScopedLock lock(decoded_audios_mutex_);
       decoded_audios_.push(decoded_audio);
       decoder_thread_->job_queue()->Schedule(output_cb_);
+    } else {
+      // Divide the content of `decoded_audio` as multiple DecodedAudio objects
+      // to ensure that the user of AudioDecoders works with output in
+      // arbitrary frames.
+      const int kNumDecodedAudioObjects = 3;
+      const int sample_size = GetBytesPerSample(sample_type_);
+      int offset_in_bytes = 0;
+
+      for (int i = 0; i < kNumDecodedAudioObjects; ++i) {
+        size_t frames_of_output =
+            decoded_audio->frames() / kNumDecodedAudioObjects;
+        size_t size_in_bytes_of_output =
+            frames_of_output * sample_size * number_of_channels_;
+
+        if (i == kNumDecodedAudioObjects - 1) {
+          // It's the last one, send out all remaining data.
+          size_in_bytes_of_output =
+              decoded_audio->size_in_bytes() - offset_in_bytes;
+          SB_DCHECK(size_in_bytes_of_output %
+                        (sample_size * number_of_channels_) ==
+                    0);
+        }
+
+        auto offset_in_frames =
+            offset_in_bytes / (sample_size * number_of_channels_);
+        SbTime timestamp =
+            decoded_audio->timestamp() +
+            AudioDurationToFrames(offset_in_frames, samples_per_second_);
+
+        scoped_refptr<DecodedAudio> current_decoded_audio(
+            new DecodedAudio(number_of_channels_, sample_type_,
+                             kSbMediaAudioFrameStorageTypeInterleaved,
+                             timestamp, size_in_bytes_of_output));
+        memcpy(current_decoded_audio->data(),
+               decoded_audio->data() + offset_in_bytes,
+               size_in_bytes_of_output);
+        offset_in_bytes += size_in_bytes_of_output;
+
+        ScopedLock lock(decoded_audios_mutex_);
+        decoded_audios_.push(current_decoded_audio);
+        decoder_thread_->job_queue()->Schedule(output_cb_);
+      }
     }
   }
   last_input_buffer_ = input_buffer;
@@ -194,23 +242,38 @@
   SB_DCHECK(decoder_thread_->job_queue()->BelongsToCurrentThread());
 
   if (last_input_buffer_) {
-    // There won't be a next pts, so just guess that the decoded size is
-    // 4 times the encoded size.
-    size_t fake_size = 4 * last_input_buffer_->size();
-    size_t sample_size =
-        sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated ? 2 : 4;
-    fake_size -=
-        fake_size % (sample_size * audio_sample_info_.number_of_channels);
-    if (audio_codec_ == kSbMediaAudioCodecAac) {
-      // Frame size for AAC is fixed at 1024, so fake the output size such that
-      // number of frames matches up.
-      fake_size = sample_size * audio_sample_info_.number_of_channels * 1024;
+    if (!frames_per_input_) {
+      if (codec_ == kSbMediaAudioCodecOpus) {
+        frames_per_input_ = 960;
+      } else if (codec_ == kSbMediaAudioCodecAc3 ||
+                 codec_ == kSbMediaAudioCodecEac3) {
+        frames_per_input_ = 1536;
+      } else {
+        SB_NOTREACHED() << "Unsupported audio codec " << codec_;
+      }
     }
+
+    SB_DCHECK(frames_per_input_);
+
+    scoped_refptr<DecodedAudio> decoded_audio =
+        CreateDecodedAudio(last_input_buffer_->timestamp(), sample_type_,
+                           number_of_channels_, *frames_per_input_);
+
+    auto discarded_duration_from_front =
+        last_input_buffer_->audio_sample_info().discarded_duration_from_front;
+    auto discarded_duration_from_back =
+        last_input_buffer_->audio_sample_info().discarded_duration_from_back;
+    SB_DCHECK(AudioDurationToFrames(
+                  discarded_duration_from_front + discarded_duration_from_back,
+                  last_input_buffer_->audio_stream_info().samples_per_second) <=
+              decoded_audio->frames());
+
+    decoded_audio->AdjustForDiscardedDurations(samples_per_second_,
+                                               discarded_duration_from_front,
+                                               discarded_duration_from_back);
+
     ScopedLock lock(decoded_audios_mutex_);
-    decoded_audios_.push(
-        new DecodedAudio(audio_sample_info_.number_of_channels, sample_type_,
-                         kSbMediaAudioFrameStorageTypeInterleaved,
-                         last_input_buffer_->timestamp(), fake_size));
+    decoded_audios_.push(decoded_audio);
     decoder_thread_->job_queue()->Schedule(output_cb_);
   }
   ScopedLock lock(decoded_audios_mutex_);
diff --git a/starboard/shared/starboard/player/filter/stub_audio_decoder.h b/starboard/shared/starboard/player/filter/stub_audio_decoder.h
index 887a67e..347c23e 100644
--- a/starboard/shared/starboard/player/filter/stub_audio_decoder.h
+++ b/starboard/shared/starboard/player/filter/stub_audio_decoder.h
@@ -17,6 +17,7 @@
 
 #include <queue>
 
+#include "starboard/common/optional.h"
 #include "starboard/common/ref_counted.h"
 #include "starboard/shared/internal_only.h"
 #include "starboard/shared/starboard/media/media_util.h"
@@ -32,8 +33,7 @@
 
 class StubAudioDecoder : public AudioDecoder, private JobQueue::JobOwner {
  public:
-  StubAudioDecoder(SbMediaAudioCodec audio_codec,
-                   const SbMediaAudioSampleInfo& audio_sample_info);
+  explicit StubAudioDecoder(const media::AudioStreamInfo& audio_stream_info);
   ~StubAudioDecoder() { Reset(); }
 
   void Initialize(const OutputCB& output_cb, const ErrorCB& error_cb) override;
@@ -50,16 +50,19 @@
   void DecodeOneBuffer(const scoped_refptr<InputBuffer>& input_buffer);
   void DecodeEndOfStream();
 
+  const SbMediaAudioCodec codec_;
+  const int number_of_channels_;
+  const int samples_per_second_;
+  const SbMediaAudioSampleType sample_type_;
+
   OutputCB output_cb_;
   ErrorCB error_cb_;
-  SbMediaAudioSampleType sample_type_;
-  SbMediaAudioCodec audio_codec_;
-  starboard::media::AudioSampleInfo audio_sample_info_;
 
   scoped_ptr<starboard::player::JobThread> decoder_thread_;
   Mutex decoded_audios_mutex_;
   std::queue<scoped_refptr<DecodedAudio> > decoded_audios_;
   scoped_refptr<InputBuffer> last_input_buffer_;
+  optional<int> frames_per_input_;
   // Used to determine when to send multiple DecodedAudios in DecodeOneBuffer().
   int total_input_count_ = 0;
 };
diff --git a/starboard/shared/starboard/player/filter/stub_video_decoder.cc b/starboard/shared/starboard/player/filter/stub_video_decoder.cc
index 7e3fc13..681b676 100644
--- a/starboard/shared/starboard/player/filter/stub_video_decoder.cc
+++ b/starboard/shared/starboard/player/filter/stub_video_decoder.cc
@@ -70,7 +70,7 @@
 void StubVideoDecoder::Reset() {
   SB_DCHECK(BelongsToCurrentThread());
 
-  video_sample_info_ = media::VideoSampleInfo();
+  video_stream_info_ = media::VideoStreamInfo();
   decoder_thread_.reset();
   output_frame_timestamps_.clear();
   total_input_count_ = 0;
@@ -86,9 +86,9 @@
   for (const auto& input_buffer : input_buffers) {
     auto& video_sample_info = input_buffer->video_sample_info();
     if (video_sample_info.is_key_frame) {
-      if (video_sample_info_ != video_sample_info) {
+      if (video_stream_info_ != input_buffer->video_stream_info()) {
         SB_LOG(INFO) << "New video sample info: " << video_sample_info;
-        video_sample_info_ = video_sample_info;
+        video_stream_info_ = input_buffer->video_stream_info();
       }
     }
 
@@ -133,19 +133,19 @@
 
 scoped_refptr<VideoFrame> StubVideoDecoder::CreateOutputFrame(
     SbTime timestamp) const {
-  int bits_per_channel = video_sample_info_.color_metadata.bits_per_channel;
+  int bits_per_channel = video_stream_info_.color_metadata.bits_per_channel;
   if (bits_per_channel == 0) {
     // Assume 8 bits when |bits_per_channel| is unknown (0).
     bits_per_channel = 8;
   }
-  int uv_stride = bits_per_channel > 8 ? video_sample_info_.frame_width
-                                       : video_sample_info_.frame_width / 2;
+  int uv_stride = bits_per_channel > 8 ? video_stream_info_.frame_width
+                                       : video_stream_info_.frame_width / 2;
   int y_stride = uv_stride * 2;
-  std::string data(y_stride * video_sample_info_.frame_height, 0);
+  std::string data(y_stride * video_stream_info_.frame_height, 0);
 
   return CpuVideoFrame::CreateYV12Frame(
-      bits_per_channel, video_sample_info_.frame_width,
-      video_sample_info_.frame_height, y_stride, uv_stride, timestamp,
+      bits_per_channel, video_stream_info_.frame_width,
+      video_stream_info_.frame_height, y_stride, uv_stride, timestamp,
       reinterpret_cast<const uint8_t*>(data.data()),
       reinterpret_cast<const uint8_t*>(data.data()),
       reinterpret_cast<const uint8_t*>(data.data()));
diff --git a/starboard/shared/starboard/player/filter/stub_video_decoder.h b/starboard/shared/starboard/player/filter/stub_video_decoder.h
index fc66d7f..f6c9668 100644
--- a/starboard/shared/starboard/player/filter/stub_video_decoder.h
+++ b/starboard/shared/starboard/player/filter/stub_video_decoder.h
@@ -54,7 +54,7 @@
   scoped_refptr<VideoFrame> CreateOutputFrame(SbTime timestamp) const;
 
   DecoderStatusCB decoder_status_cb_;
-  media::VideoSampleInfo video_sample_info_;
+  media::VideoStreamInfo video_stream_info_;
 
   scoped_ptr<starboard::player::JobThread> decoder_thread_;
   // std::set<> keeps frame timestamps sorted in ascending order.
diff --git a/starboard/shared/starboard/player/filter/testing/BUILD.gn b/starboard/shared/starboard/player/filter/testing/BUILD.gn
index a126ef0..176c1ed 100644
--- a/starboard/shared/starboard/player/filter/testing/BUILD.gn
+++ b/starboard/shared/starboard/player/filter/testing/BUILD.gn
@@ -22,6 +22,7 @@
     "adaptive_audio_decoder_test.cc",
     "audio_channel_layout_mixer_test.cc",
     "audio_decoder_test.cc",
+    "audio_frame_discarder_test.cc",
     "audio_renderer_internal_test.cc",
     "file_cache_reader_test.cc",
     "media_time_provider_impl_test.cc",
@@ -75,6 +76,7 @@
 
   public_deps = [
     "//starboard",
+    "//starboard/shared/starboard/media:media_util",
     "//starboard/shared/starboard/player:player_download_test_data",
     "//starboard/shared/starboard/player:video_dmp",
     "//testing/gtest",
diff --git a/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc b/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
index 551a0ad..6d41cb2 100644
--- a/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
+++ b/starboard/shared/starboard/player/filter/testing/adaptive_audio_decoder_test.cc
@@ -119,8 +119,7 @@
 
     scoped_ptr<AudioRendererSink> audio_renderer_sink;
     ASSERT_TRUE(CreateAudioComponents(using_stub_decoder_,
-                                      dmp_readers_[0]->audio_codec(),
-                                      dmp_readers_[0]->audio_sample_info(),
+                                      dmp_readers_[0]->audio_stream_info(),
                                       &audio_decoder_, &audio_renderer_sink));
     ASSERT_TRUE(audio_decoder_);
     audio_decoder_->Initialize(
diff --git a/starboard/shared/starboard/player/filter/testing/audio_channel_layout_mixer_test.cc b/starboard/shared/starboard/player/filter/testing/audio_channel_layout_mixer_test.cc
index 56a8985..16c9537 100644
--- a/starboard/shared/starboard/player/filter/testing/audio_channel_layout_mixer_test.cc
+++ b/starboard/shared/starboard/player/filter/testing/audio_channel_layout_mixer_test.cc
@@ -90,7 +90,7 @@
             (sample_type_ == kSbMediaAudioSampleTypeFloat32 ? 4 : 2)));
 
     if (sample_type_ == kSbMediaAudioSampleTypeFloat32) {
-      float* dest_buffer = reinterpret_cast<float*>(decoded_audio->buffer());
+      float* dest_buffer = reinterpret_cast<float*>(decoded_audio->data());
       for (size_t i = 0; i < num_of_channels * kInputFrames; i++) {
         int src_index = i;
         if (storage_type_ == kSbMediaAudioFrameStorageTypePlanar) {
@@ -99,8 +99,7 @@
         dest_buffer[i] = data_buffer[src_index];
       }
     } else {
-      int16_t* dest_buffer =
-          reinterpret_cast<int16_t*>(decoded_audio->buffer());
+      int16_t* dest_buffer = reinterpret_cast<int16_t*>(decoded_audio->data());
       for (size_t i = 0; i < num_of_channels * kInputFrames; i++) {
         int src_index = i;
         if (storage_type_ == kSbMediaAudioFrameStorageTypePlanar) {
@@ -120,11 +119,11 @@
     ASSERT_EQ(input->sample_type(), output->sample_type());
     ASSERT_EQ(input->storage_type(), output->storage_type());
     ASSERT_EQ(input->timestamp(), output->timestamp());
-    ASSERT_EQ(input->size() * output->channels(),
-              output->size() * input->channels());
+    ASSERT_EQ(input->size_in_bytes() * output->channels(),
+              output->size_in_bytes() * input->channels());
 
     if (sample_type_ == kSbMediaAudioSampleTypeFloat32) {
-      float* output_buffer = reinterpret_cast<float*>(output->buffer());
+      float* output_buffer = reinterpret_cast<float*>(output->data());
       for (size_t i = 0; i < output->frames() * output_num_of_channels; i++) {
         int src_index = i;
         if (storage_type_ == kSbMediaAudioFrameStorageTypePlanar) {
@@ -141,7 +140,7 @@
         }
       }
     } else {
-      int16_t* output_buffer = reinterpret_cast<int16_t*>(output->buffer());
+      int16_t* output_buffer = reinterpret_cast<int16_t*>(output->data());
       for (size_t i = 0; i < output->frames() * output_num_of_channels; i++) {
         int src_index = i;
         if (storage_type_ == kSbMediaAudioFrameStorageTypePlanar) {
diff --git a/starboard/shared/starboard/player/filter/testing/audio_decoder_benchmark.cc b/starboard/shared/starboard/player/filter/testing/audio_decoder_benchmark.cc
index ab08076..5cb1616 100644
--- a/starboard/shared/starboard/player/filter/testing/audio_decoder_benchmark.cc
+++ b/starboard/shared/starboard/player/filter/testing/audio_decoder_benchmark.cc
@@ -46,8 +46,8 @@
                                    kMaxNumberOfInputs)) {
     const bool kUseStubDecoder = false;
     SB_CHECK(number_of_inputs_ > 0);
-    SB_CHECK(CreateAudioComponents(kUseStubDecoder, dmp_reader_.audio_codec(),
-                                   dmp_reader_.audio_sample_info(),
+    SB_CHECK(CreateAudioComponents(kUseStubDecoder,
+                                   dmp_reader_.audio_stream_info(),
                                    &audio_decoder_, &audio_renderer_sink_));
     SB_CHECK(audio_decoder_);
     audio_decoder_->Initialize(std::bind(&AudioDecoderHelper::OnOutput, this),
diff --git a/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc b/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
index 2904b6a..601e6f5 100644
--- a/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
+++ b/starboard/shared/starboard/player/filter/testing/audio_decoder_test.cc
@@ -25,17 +25,21 @@
 #include "starboard/common/condition_variable.h"
 #include "starboard/common/media.h"
 #include "starboard/common/mutex.h"
+#include "starboard/common/ref_counted.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/configuration_constants.h"
 #include "starboard/media.h"
 #include "starboard/memory.h"
 #include "starboard/shared/starboard/media/media_support_internal.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/starboard/player/filter/player_components.h"
 #include "starboard/shared/starboard/player/filter/stub_player_components_factory.h"
 #include "starboard/shared/starboard/player/filter/testing/test_util.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
 #include "starboard/shared/starboard/player/job_queue.h"
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
+#include "starboard/system.h"
 #include "starboard/thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -54,6 +58,48 @@
 
 const SbTimeMonotonic kWaitForNextEventTimeOut = 5 * kSbTimeSecond;
 
+scoped_refptr<DecodedAudio> ConsolidateDecodedAudios(
+    const std::vector<scoped_refptr<DecodedAudio>>& decoded_audios) {
+  if (decoded_audios.empty()) {
+    return new DecodedAudio(2, kSbMediaAudioSampleTypeFloat32,
+                            kSbMediaAudioFrameStorageTypeInterleaved, 0, 0);
+  }
+
+  int total_size_in_bytes = 0;
+  int channels = decoded_audios.front()->channels();
+  auto sample_type = decoded_audios.front()->sample_type();
+
+  for (auto decoded_audio : decoded_audios) {
+    SB_DCHECK(decoded_audio->channels() == channels);
+    SB_DCHECK(decoded_audio->sample_type() == sample_type);
+    SB_DCHECK(decoded_audio->storage_type() ==
+              kSbMediaAudioFrameStorageTypeInterleaved);
+    total_size_in_bytes += decoded_audio->size_in_bytes();
+  }
+
+  scoped_refptr<DecodedAudio> consolidated = new DecodedAudio(
+      channels, sample_type, kSbMediaAudioFrameStorageTypeInterleaved,
+      decoded_audios.front()->timestamp(), total_size_in_bytes);
+
+  int offset_in_bytes = 0;
+  for (auto decoded_audio : decoded_audios) {
+    memcpy(consolidated->data() + offset_in_bytes, decoded_audio->data(),
+           decoded_audio->size_in_bytes());
+    offset_in_bytes += decoded_audio->size_in_bytes();
+  }
+
+  return consolidated;
+}
+
+int GetTotalFrames(
+    const std::vector<scoped_refptr<DecodedAudio>>& decoded_audios) {
+  int total_frames = 0;
+  for (auto decoded_audio : decoded_audios) {
+    total_frames += decoded_audio->frames();
+  }
+  return total_frames;
+}
+
 class AudioDecoderTest
     : public ::testing::TestWithParam<std::tuple<const char*, bool>> {
  public:
@@ -66,21 +112,20 @@
                  << (using_stub_decoder_ ? " with stub audio decoder." : ".");
   }
   void SetUp() override {
-    ASSERT_NE(dmp_reader_.audio_codec(), kSbMediaAudioCodecNone);
+    ASSERT_NE(dmp_reader_.audio_stream_info().codec, kSbMediaAudioCodecNone);
     ASSERT_GT(dmp_reader_.number_of_audio_buffers(), 0);
 
-    CreateComponents(dmp_reader_.audio_codec(), dmp_reader_.audio_sample_info(),
-                     &audio_decoder_, &audio_renderer_sink_);
+    CreateComponents(dmp_reader_.audio_stream_info(), &audio_decoder_,
+                     &audio_renderer_sink_);
   }
 
  protected:
   enum Event { kConsumed, kOutput, kError };
 
-  void CreateComponents(SbMediaAudioCodec codec,
-                        const SbMediaAudioSampleInfo& audio_sample_info,
+  void CreateComponents(const media::AudioStreamInfo& audio_stream_info,
                         scoped_ptr<AudioDecoder>* audio_decoder,
                         scoped_ptr<AudioRendererSink>* audio_renderer_sink) {
-    if (CreateAudioComponents(using_stub_decoder_, codec, audio_sample_info,
+    if (CreateAudioComponents(using_stub_decoder_, audio_stream_info,
                               audio_decoder, audio_renderer_sink)) {
       SB_CHECK(*audio_decoder);
       (*audio_decoder)
@@ -143,6 +188,21 @@
     audio_decoder_->Decode({last_input_buffer_}, consumed_cb());
   }
 
+  void WriteSingleInput(size_t index,
+                        SbTime discarded_duration_from_front,
+                        SbTime discarded_duration_from_back) {
+    SB_DCHECK(IsPartialAudioSupported());
+
+    ASSERT_TRUE(can_accept_more_input_);
+    ASSERT_LT(index, dmp_reader_.number_of_audio_buffers());
+
+    can_accept_more_input_ = false;
+
+    last_input_buffer_ = GetAudioInputBuffer(
+        index, discarded_duration_from_front, discarded_duration_from_back);
+    audio_decoder_->Decode({last_input_buffer_}, consumed_cb());
+  }
+
   // This has to be called when OnOutput() is called.
   void ReadFromDecoder(scoped_refptr<DecodedAudio>* decoded_audio) {
     ASSERT_TRUE(decoded_audio);
@@ -154,27 +214,25 @@
     if (!first_output_received_) {
       first_output_received_ = true;
       decoded_audio_sample_type_ = local_decoded_audio->sample_type();
-      decoded_audio_storage_type_ = local_decoded_audio->storage_type();
-      decoded_audio_samples_per_second_ = decoded_sample_rate;
+      decoded_audio_sample_rate_ = decoded_sample_rate;
     }
 
     if (local_decoded_audio->is_end_of_stream()) {
       *decoded_audio = local_decoded_audio;
       return;
     }
-    ASSERT_EQ(decoded_audio_sample_type_, local_decoded_audio->sample_type());
-    ASSERT_EQ(decoded_audio_storage_type_, local_decoded_audio->storage_type());
-    ASSERT_EQ(decoded_audio_samples_per_second_, decoded_sample_rate);
 
-    // TODO: Adaptive audio decoder outputs may don't have timestamp info.
-    // Currently, we skip timestamp check if the outputs don't have timestamp
-    // info. Enable it after we fix timestamp issues.
-    if (local_decoded_audio->timestamp() && last_decoded_audio_) {
-      ASSERT_LT(last_decoded_audio_->timestamp(),
+    ASSERT_EQ(decoded_audio_sample_type_, local_decoded_audio->sample_type());
+    ASSERT_EQ(decoded_audio_sample_rate_, decoded_sample_rate);
+
+    // TODO: Adaptive audio decoder may set output timestamp to 0, so we don't
+    //       verify audio timestamp if it's 0.  Consider enabling it after we
+    //       fix timestamp issues.
+    if (local_decoded_audio->timestamp() != 0 && !decoded_audios_.empty()) {
+      ASSERT_LT(decoded_audios_.back()->timestamp(),
                 local_decoded_audio->timestamp());
     }
-    last_decoded_audio_ = local_decoded_audio;
-    num_of_output_frames_ += last_decoded_audio_->frames();
+    decoded_audios_.push_back(local_decoded_audio);
     *decoded_audio = local_decoded_audio;
   }
 
@@ -279,15 +337,15 @@
     audio_decoder_->Reset();
     can_accept_more_input_ = true;
     last_input_buffer_ = nullptr;
-    last_decoded_audio_ = nullptr;
+    decoded_audios_.clear();
     eos_written_ = false;
-    decoded_audio_samples_per_second_ = 0;
+    decoded_audio_sample_rate_ = 0;
     first_output_received_ = false;
   }
 
   void WaitForDecodedAudio() {
     Event event;
-    while (!last_decoded_audio_) {
+    while (decoded_audios_.empty()) {
       ASSERT_NO_FATAL_FAILURE(WaitForNextEvent(&event));
       if (event == kConsumed) {
         continue;
@@ -301,10 +359,25 @@
   }
 
   scoped_refptr<InputBuffer> GetAudioInputBuffer(size_t index) {
-    auto player_sample_info =
-        dmp_reader_.GetPlayerSampleInfo(kSbMediaTypeAudio, index);
-    auto input_buffer = new InputBuffer(StubDeallocateSampleFunc, nullptr,
-                                        nullptr, player_sample_info);
+    auto input_buffer = testing::GetAudioInputBuffer(&dmp_reader_, index);
+    auto iter = invalid_inputs_.find(index);
+    if (iter != invalid_inputs_.end()) {
+      std::vector<uint8_t> content(input_buffer->size(), iter->second);
+      // Replace the content with invalid data.
+      input_buffer->SetDecryptedContent(std::move(content));
+    }
+    return input_buffer;
+  }
+
+  scoped_refptr<InputBuffer> GetAudioInputBuffer(
+      size_t index,
+      SbTime discarded_duration_from_front,
+      SbTime discarded_duration_from_back) {
+    SB_DCHECK(IsPartialAudioSupported());
+
+    auto input_buffer = testing::GetAudioInputBuffer(
+        &dmp_reader_, index, discarded_duration_from_front,
+        discarded_duration_from_back);
     auto iter = invalid_inputs_.find(index);
     if (iter != invalid_inputs_.end()) {
       std::vector<uint8_t> content(input_buffer->size(), iter->second);
@@ -324,18 +397,12 @@
     eos_written_ = true;
   }
 
-  void AssertInvalidOutputFormat() {
+  void AssertOutputFormatValid() {
     ASSERT_TRUE(decoded_audio_sample_type_ == kSbMediaAudioSampleTypeFloat32 ||
                 decoded_audio_sample_type_ ==
                     kSbMediaAudioSampleTypeInt16Deprecated);
-
-    ASSERT_TRUE(decoded_audio_storage_type_ ==
-                    kSbMediaAudioFrameStorageTypeInterleaved ||
-                decoded_audio_storage_type_ ==
-                    kSbMediaAudioFrameStorageTypePlanar);
-
-    ASSERT_TRUE(decoded_audio_samples_per_second_ > 0 &&
-                decoded_audio_samples_per_second_ <= 480000);
+    ASSERT_TRUE(decoded_audio_sample_rate_ > 0 &&
+                decoded_audio_sample_rate_ <= 480000);
   }
 
   void AssertExpectedAndOutputFramesMatch(int expected_output_frames) {
@@ -344,7 +411,8 @@
       // StubAudioDecoder, because it is not actually doing any decoding work.
       return;
     }
-    ASSERT_LE(abs(expected_output_frames - num_of_output_frames_), 1);
+
+    ASSERT_LE(abs(expected_output_frames - GetTotalFrames(decoded_audios_)), 1);
   }
 
   Mutex event_queue_mutex_;
@@ -364,19 +432,15 @@
 
   bool can_accept_more_input_ = true;
   scoped_refptr<InputBuffer> last_input_buffer_;
-  scoped_refptr<DecodedAudio> last_decoded_audio_;
+  std::vector<scoped_refptr<DecodedAudio>> decoded_audios_;
 
   bool eos_written_ = false;
 
   std::map<size_t, uint8_t> invalid_inputs_;
 
-  int num_of_output_frames_ = 0;
-
   SbMediaAudioSampleType decoded_audio_sample_type_ =
       kSbMediaAudioSampleTypeInt16Deprecated;
-  SbMediaAudioFrameStorageType decoded_audio_storage_type_ =
-      kSbMediaAudioFrameStorageTypeInterleaved;
-  int decoded_audio_samples_per_second_ = 0;
+  int decoded_audio_sample_rate_ = 0;
 
   bool first_output_received_ = false;
 };
@@ -399,8 +463,8 @@
   scoped_ptr<AudioRendererSink> audio_renderer_sinks[kDecodersToCreate];
 
   for (int i = 0; i < kDecodersToCreate; ++i) {
-    CreateComponents(dmp_reader_.audio_codec(), dmp_reader_.audio_sample_info(),
-                     &audio_decoders[i], &audio_renderer_sinks[i]);
+    CreateComponents(dmp_reader_.audio_stream_info(), &audio_decoders[i],
+                     &audio_renderer_sinks[i]);
     if (!audio_decoders[i]) {
       ASSERT_GE(i, kMinimumNumberOfExtraDecodersRequired);
     }
@@ -412,8 +476,8 @@
   WriteEndOfStream();
 
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
-  ASSERT_TRUE(last_decoded_audio_);
-  ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
+  ASSERT_FALSE(decoded_audios_.empty());
+  ASSERT_NO_FATAL_FAILURE(AssertOutputFormatValid());
 }
 
 TEST_P(AudioDecoderTest, SingleInputHEAAC) {
@@ -427,14 +491,14 @@
   WriteEndOfStream();
 
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
-  ASSERT_TRUE(last_decoded_audio_);
-  ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
+  ASSERT_FALSE(decoded_audios_.empty());
+  ASSERT_NO_FATAL_FAILURE(AssertOutputFormatValid());
 
   int input_sample_rate =
-      last_input_buffer_->audio_sample_info().samples_per_second;
-  ASSERT_NE(0, decoded_audio_samples_per_second_);
+      last_input_buffer_->audio_stream_info().samples_per_second;
+  ASSERT_NE(0, decoded_audio_sample_rate_);
   int expected_output_frames =
-      kAacFrameSize * decoded_audio_samples_per_second_ / input_sample_rate;
+      kAacFrameSize * decoded_audio_sample_rate_ / input_sample_rate;
   AssertExpectedAndOutputFramesMatch(expected_output_frames);
 }
 
@@ -442,12 +506,11 @@
   auto invalid_codec = dmp_reader_.audio_codec() == kSbMediaAudioCodecAac
                            ? kSbMediaAudioCodecOpus
                            : kSbMediaAudioCodecAac;
-  auto audio_sample_info = dmp_reader_.audio_sample_info();
+  auto audio_stream_info = dmp_reader_.audio_stream_info();
 
-  audio_sample_info.codec = invalid_codec;
+  audio_stream_info.codec = invalid_codec;
 
-  CreateComponents(invalid_codec, audio_sample_info, &audio_decoder_,
-                   &audio_renderer_sink_);
+  CreateComponents(audio_stream_info, &audio_decoder_, &audio_renderer_sink_);
   if (!audio_decoder_) {
     return;
   }
@@ -460,20 +523,16 @@
 }
 
 TEST_P(AudioDecoderTest, InvalidConfig) {
-  auto original_audio_sample_info = dmp_reader_.audio_sample_info();
+  auto original_audio_stream_info = dmp_reader_.audio_stream_info();
 
-  for (uint16_t i = 0;
-       i < original_audio_sample_info.audio_specific_config_size; ++i) {
-    std::vector<uint8_t> config(
-        original_audio_sample_info.audio_specific_config_size);
-    memcpy(config.data(), original_audio_sample_info.audio_specific_config,
-           original_audio_sample_info.audio_specific_config_size);
-    auto audio_sample_info = original_audio_sample_info;
-    config[i] = ~config[i];
-    audio_sample_info.audio_specific_config = config.data();
+  for (size_t i = 0;
+       i < original_audio_stream_info.audio_specific_config.size(); ++i) {
+    auto audio_stream_info = original_audio_stream_info;
 
-    CreateComponents(dmp_reader_.audio_codec(), audio_sample_info,
-                     &audio_decoder_, &audio_renderer_sink_);
+    audio_stream_info.audio_specific_config[i] =
+        ~audio_stream_info.audio_specific_config[i];
+
+    CreateComponents(audio_stream_info, &audio_decoder_, &audio_renderer_sink_);
     if (!audio_decoder_) {
       return;
     }
@@ -486,16 +545,13 @@
     ResetDecoder();
   }
 
-  for (uint16_t i = 0;
-       i < original_audio_sample_info.audio_specific_config_size; ++i) {
-    std::vector<uint8_t> config(i);
-    memcpy(config.data(), original_audio_sample_info.audio_specific_config, i);
-    auto audio_sample_info = original_audio_sample_info;
-    audio_sample_info.audio_specific_config = config.data();
-    audio_sample_info.audio_specific_config_size = i;
+  for (size_t i = 0;
+       i < original_audio_stream_info.audio_specific_config.size(); ++i) {
+    auto audio_stream_info = original_audio_stream_info;
 
-    CreateComponents(dmp_reader_.audio_codec(), audio_sample_info,
-                     &audio_decoder_, &audio_renderer_sink_);
+    audio_stream_info.audio_specific_config.resize(i);
+
+    CreateComponents(audio_stream_info, &audio_decoder_, &audio_renderer_sink_);
     if (!audio_decoder_) {
       return;
     }
@@ -559,8 +615,8 @@
   WriteEndOfStream();
 
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
-  ASSERT_FALSE(last_decoded_audio_);
-  ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
+  ASSERT_TRUE(decoded_audios_.empty());
+  ASSERT_NO_FATAL_FAILURE(AssertOutputFormatValid());
 }
 
 TEST_P(AudioDecoderTest, ResetBeforeInput) {
@@ -570,8 +626,8 @@
   WriteEndOfStream();
 
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
-  ASSERT_TRUE(last_decoded_audio_);
-  ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
+  ASSERT_FALSE(decoded_audios_.empty());
+  ASSERT_NO_FATAL_FAILURE(AssertOutputFormatValid());
 }
 
 TEST_P(AudioDecoderTest, MultipleInputs) {
@@ -584,15 +640,15 @@
   WriteEndOfStream();
 
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
-  ASSERT_TRUE(last_decoded_audio_);
-  ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
+  ASSERT_FALSE(decoded_audios_.empty());
+  ASSERT_NO_FATAL_FAILURE(AssertOutputFormatValid());
 }
 
 TEST_P(AudioDecoderTest, LimitedInput) {
   SbTime duration = kSbTimeSecond / 2;
   SbMediaSetAudioWriteDuration(duration);
 
-  ASSERT_FALSE(last_decoded_audio_);
+  ASSERT_TRUE(decoded_audios_.empty());
   int start_index = 0;
   ASSERT_NO_FATAL_FAILURE(WriteTimeLimitedInputs(&start_index, duration));
 
@@ -605,6 +661,7 @@
 }
 
 TEST_P(AudioDecoderTest, ContinuedLimitedInput) {
+  constexpr int kMaxAccessUnitsToDecode = 256;
   SbTime duration = kSbTimeSecond / 2;
   SbMediaSetAudioWriteDuration(duration);
 
@@ -613,14 +670,15 @@
   Event event;
   while (true) {
     ASSERT_NO_FATAL_FAILURE(WriteTimeLimitedInputs(&start_index, duration));
-    if (start_index >= dmp_reader_.number_of_audio_buffers()) {
+    if (start_index >= std::min<int>(dmp_reader_.number_of_audio_buffers(),
+                                     kMaxAccessUnitsToDecode)) {
       break;
     }
     SB_DCHECK(last_input_buffer_);
     WaitForDecodedAudio();
-    ASSERT_TRUE(last_decoded_audio_);
+    ASSERT_FALSE(decoded_audios_.empty());
     while ((last_input_buffer_->timestamp() -
-            last_decoded_audio_->timestamp()) > duration ||
+            decoded_audios_.back()->timestamp()) > duration ||
            !can_accept_more_input_) {
       ASSERT_NO_FATAL_FAILURE(WaitForNextEvent(&event));
       if (event == kConsumed) {
@@ -636,12 +694,134 @@
   WriteEndOfStream();
   ASSERT_NO_FATAL_FAILURE(DrainOutputs());
   SbTime elapsed = SbTimeGetMonotonicNow() - start;
-  SB_LOG(INFO) << "Decoding " << dmp_reader_.number_of_audio_buffers() << " au "
-               << " of " << GetMediaAudioCodecName(dmp_reader_.audio_codec())
-               << " takes " << elapsed << " microseconds.";
+  SB_LOG(INFO) << "Decoding " << dmp_reader_.number_of_audio_buffers()
+               << " access units of "
+               << GetMediaAudioCodecName(dmp_reader_.audio_codec()) << " takes "
+               << elapsed << " microseconds.";
 
-  ASSERT_TRUE(last_decoded_audio_);
-  ASSERT_NO_FATAL_FAILURE(AssertInvalidOutputFormat());
+  ASSERT_FALSE(decoded_audios_.empty());
+  ASSERT_NO_FATAL_FAILURE(AssertOutputFormatValid());
+}
+
+TEST_P(AudioDecoderTest, PartialAudio) {
+  if (!IsPartialAudioSupported()) {
+    return;
+  }
+
+  const int max_number_of_input_to_write =
+      std::min(7, static_cast<int>(dmp_reader_.number_of_audio_buffers()));
+
+  for (int number_of_input_to_write = 1;
+       number_of_input_to_write < max_number_of_input_to_write;
+       ++number_of_input_to_write) {
+    SB_LOG(INFO) << "Testing " << number_of_input_to_write
+                 << " access units for partial audio.";
+    ResetDecoder();
+
+    // Decode InputBuffers without partial audio and use the output as reference
+    for (int i = 0; i < number_of_input_to_write; ++i) {
+      ASSERT_NO_FATAL_FAILURE(WriteSingleInput(i));
+      if (i == number_of_input_to_write - 1) {
+        WriteEndOfStream();
+        break;
+      }
+
+      for (;;) {
+        Event event = kError;
+        ASSERT_NO_FATAL_FAILURE(WaitForNextEvent(&event));
+        ASSERT_NE(event, kError);
+        if (event == kConsumed) {
+          break;
+        }
+        ASSERT_EQ(kOutput, event);
+        scoped_refptr<DecodedAudio> decoded_audio;
+        ASSERT_NO_FATAL_FAILURE(ReadFromDecoder(&decoded_audio));
+        ASSERT_TRUE(decoded_audio);
+      }
+    }
+
+    ASSERT_NO_FATAL_FAILURE(DrainOutputs());
+    ASSERT_FALSE(decoded_audios_.empty());
+    ASSERT_NO_FATAL_FAILURE(AssertOutputFormatValid());
+
+    auto reference_decoded_audio = ConsolidateDecodedAudios(decoded_audios_);
+    ASSERT_GT(reference_decoded_audio->frames(), 1);
+
+    // Discard 1/4 of the duration from front, and back.  The resulting audio
+    // will keep 1/2 of the frames in the middle. This has to be called before
+    // `ResetDecoder()` as it resets `decoded_audio_sample_rate_`.
+    ASSERT_GT(decoded_audio_sample_rate_, 0);
+    auto frames_per_access_unit =
+        reference_decoded_audio->frames() / number_of_input_to_write;
+    SbTime duration_to_discard =
+        media::AudioFramesToDuration(frames_per_access_unit,
+                                     decoded_audio_sample_rate_) /
+        4;
+
+    ResetDecoder();
+
+    for (int i = 0; i < number_of_input_to_write; ++i) {
+      SbTime duration_to_discard_from_front = i == 0 ? duration_to_discard : 0;
+      SbTime duration_to_discard_from_back =
+          i == number_of_input_to_write - 1 ? duration_to_discard : 0;
+      ASSERT_NO_FATAL_FAILURE(WriteSingleInput(
+          i, duration_to_discard_from_front, duration_to_discard_from_back));
+
+      if (i == number_of_input_to_write - 1) {
+        WriteEndOfStream();
+        break;
+      }
+      for (;;) {
+        Event event = kError;
+        ASSERT_NO_FATAL_FAILURE(WaitForNextEvent(&event));
+        ASSERT_NE(event, kError);
+        if (event == kConsumed) {
+          break;
+        }
+        ASSERT_EQ(kOutput, event);
+        scoped_refptr<DecodedAudio> decoded_audio;
+        ASSERT_NO_FATAL_FAILURE(ReadFromDecoder(&decoded_audio));
+        ASSERT_TRUE(decoded_audio);
+      }
+    }
+
+    ASSERT_NO_FATAL_FAILURE(DrainOutputs());
+    ASSERT_FALSE(decoded_audios_.empty());
+    ASSERT_NO_FATAL_FAILURE(AssertOutputFormatValid());
+
+    auto partial_decoded_audio = ConsolidateDecodedAudios(decoded_audios_);
+
+    ASSERT_EQ(reference_decoded_audio->sample_type(),
+              partial_decoded_audio->sample_type());
+    ASSERT_EQ(reference_decoded_audio->storage_type(),
+              partial_decoded_audio->storage_type());
+    ASSERT_GT(reference_decoded_audio->frames(),
+              partial_decoded_audio->frames());
+
+    auto bytes_per_frame = reference_decoded_audio->size_in_bytes() /
+                           reference_decoded_audio->frames();
+    // |partial_decoded_audio| should contain exactly the same data as
+    // |reference_decoded_audio|, begin from about 1/4 of an access unit.  We
+    // search from (1/4 access unit - 1) in |reference_decoded_audio| to allow
+    // for up to one frame of error during calculation.
+    auto reference_search_begin =
+        reference_decoded_audio->data() +
+        (frames_per_access_unit / 4 - 1) * bytes_per_frame;
+    auto reference_search_end = reference_decoded_audio->data() +
+                                reference_decoded_audio->size_in_bytes();
+    auto offset_in_bytes =
+        std::search(reference_search_begin, reference_search_end,
+                    partial_decoded_audio->data(),
+                    partial_decoded_audio->data() +
+                        partial_decoded_audio->size_in_bytes()) -
+        reference_search_begin;
+    auto offset_in_frames = offset_in_bytes / bytes_per_frame;
+
+    constexpr int kEpsilonInFrames = 2;
+    EXPECT_LE(offset_in_frames, kEpsilonInFrames);
+    EXPECT_NEAR(reference_decoded_audio->frames() - frames_per_access_unit / 2,
+                partial_decoded_audio->frames(), kEpsilonInFrames);
+  }
 }
 
 INSTANTIATE_TEST_CASE_P(
diff --git a/starboard/shared/starboard/player/filter/testing/audio_frame_discarder_test.cc b/starboard/shared/starboard/player/filter/testing/audio_frame_discarder_test.cc
new file mode 100644
index 0000000..a1ca871
--- /dev/null
+++ b/starboard/shared/starboard/player/filter/testing/audio_frame_discarder_test.cc
@@ -0,0 +1,175 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/starboard/player/filter/audio_frame_discarder.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "starboard/shared/starboard/player/decoded_audio_internal.h"
+#include "starboard/shared/starboard/player/filter/testing/test_util.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/video_dmp_reader.h"
+#include "starboard/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace shared {
+namespace starboard {
+namespace player {
+namespace filter {
+namespace testing {
+namespace {
+
+using ::testing::ValuesIn;
+using video_dmp::VideoDmpReader;
+
+class AudioFrameDiscarderTest : public ::testing::TestWithParam<const char*> {
+ public:
+  AudioFrameDiscarderTest()
+      : dmp_reader_(ResolveTestFileName(GetParam()).c_str(),
+                    VideoDmpReader::kEnableReadOnDemand) {}
+
+ protected:
+  VideoDmpReader dmp_reader_;
+};
+
+scoped_refptr<DecodedAudio> MakeDecodedAudio(int channels, SbTime timestamp) {
+  scoped_refptr<DecodedAudio> decoded_audio = new DecodedAudio(
+      channels, kSbMediaAudioSampleTypeFloat32,
+      kSbMediaAudioFrameStorageTypeInterleaved, timestamp,
+      media::GetBytesPerSample(kSbMediaAudioSampleTypeFloat32) * channels *
+          1536);
+
+  memset(decoded_audio->data(), timestamp % 256,
+         decoded_audio->size_in_bytes());
+
+  return decoded_audio;
+}
+
+TEST_P(AudioFrameDiscarderTest, Empty) {
+  AudioFrameDiscarder discarder;
+  discarder.Reset();
+  discarder.OnDecodedAudioEndOfStream();
+  discarder.Reset();
+}
+
+TEST_P(AudioFrameDiscarderTest, NonPartialAudio) {
+  InputBuffers input_buffers;
+  std::vector<scoped_refptr<DecodedAudio>> decoded_audios;
+  const auto& audio_stream_info = dmp_reader_.audio_stream_info();
+
+  for (int i = 0; i < std::min<int>(32, dmp_reader_.number_of_audio_buffers());
+       ++i) {
+    input_buffers.push_back(GetAudioInputBuffer(&dmp_reader_, i));
+    decoded_audios.push_back(
+        MakeDecodedAudio(audio_stream_info.number_of_channels,
+                         input_buffers.back()->timestamp()));
+  }
+  ASSERT_EQ(input_buffers.size(), decoded_audios.size());
+
+  AudioFrameDiscarder discarder;
+
+  for (size_t i = 0; i < input_buffers.size(); ++i) {
+    discarder.OnInputBuffers({input_buffers[i]});
+    auto copy = decoded_audios[i]->Clone();
+    discarder.AdjustForDiscardedDurations(audio_stream_info.samples_per_second,
+                                          &copy);
+    ASSERT_EQ(*copy, *decoded_audios[i]);
+  }
+
+  discarder.Reset();
+
+  discarder.OnInputBuffers(input_buffers);
+
+  for (auto decoded_audio : decoded_audios) {
+    auto copy = decoded_audio->Clone();
+    discarder.AdjustForDiscardedDurations(audio_stream_info.samples_per_second,
+                                          &copy);
+    ASSERT_EQ(*copy, *decoded_audio);
+  }
+}
+
+TEST_P(AudioFrameDiscarderTest, PartialAudio) {
+  InputBuffers input_buffers;
+  std::vector<scoped_refptr<DecodedAudio>> decoded_audios;
+  const auto& audio_stream_info = dmp_reader_.audio_stream_info();
+  const SbTime duration = media::AudioFramesToDuration(
+      MakeDecodedAudio(audio_stream_info.number_of_channels, 0)->frames(),
+      audio_stream_info.samples_per_second);
+
+  for (int i = 0; i < std::min<int>(32, dmp_reader_.number_of_audio_buffers());
+       ++i) {
+    if (i % 2 == 0) {
+      // Skip 1/4 of the duration from both front and back on even buffers.
+      input_buffers.push_back(
+          GetAudioInputBuffer(&dmp_reader_, i, duration / 4, duration / 4));
+    } else {
+      input_buffers.push_back(GetAudioInputBuffer(&dmp_reader_, i));
+    }
+    decoded_audios.push_back(
+        MakeDecodedAudio(dmp_reader_.audio_stream_info().number_of_channels,
+                         input_buffers.back()->timestamp()));
+  }
+  ASSERT_EQ(input_buffers.size(), decoded_audios.size());
+
+  AudioFrameDiscarder discarder;
+
+  for (size_t i = 0; i < input_buffers.size(); ++i) {
+    discarder.OnInputBuffers({input_buffers[i]});
+    auto copy = decoded_audios[i]->Clone();
+    discarder.AdjustForDiscardedDurations(audio_stream_info.samples_per_second,
+                                          &copy);
+    if (i % 2 == 0) {
+      ASSERT_NEAR(copy->frames(), decoded_audios[i]->frames() / 2, 2);
+    } else {
+      ASSERT_EQ(*copy, *decoded_audios[i]);
+    }
+  }
+
+  discarder.Reset();
+
+  discarder.OnInputBuffers(input_buffers);
+
+  for (size_t i = 0; i < decoded_audios.size(); ++i) {
+    auto copy = decoded_audios[i]->Clone();
+    discarder.AdjustForDiscardedDurations(audio_stream_info.samples_per_second,
+                                          &copy);
+    if (i % 2 == 0) {
+      ASSERT_NEAR(copy->frames(), decoded_audios[i]->frames() / 2, 2);
+    } else {
+      ASSERT_EQ(*copy, *decoded_audios[i]);
+    }
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(
+    AudioFrameDiscarderTests,
+    AudioFrameDiscarderTest,
+    ValuesIn(
+        GetSupportedAudioTestFiles(kIncludeHeaac, 6, "audiopassthrough=false")),
+    [](::testing::TestParamInfo<const char*> info) {
+      std::string filename(info.param);
+      std::replace(filename.begin(), filename.end(), '.', '_');
+      return filename;
+    });
+
+}  // namespace
+}  // namespace testing
+}  // namespace filter
+}  // namespace player
+}  // namespace starboard
+}  // namespace shared
+}  // namespace starboard
diff --git a/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc b/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
index bfeb7a7..14295ba 100644
--- a/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
+++ b/starboard/shared/starboard/player/filter/testing/audio_renderer_internal_test.cc
@@ -117,7 +117,7 @@
     audio_renderer_.reset(new AudioRendererPcm(
         make_scoped_ptr<AudioDecoder>(audio_decoder_),
         make_scoped_ptr<AudioRendererSink>(audio_renderer_sink_),
-        GetDefaultAudioSampleInfo(), kMaxCachedFrames, kMaxFramesPerAppend));
+        GetDefaultAudioStreamInfo(), kMaxCachedFrames, kMaxFramesPerAppend));
     audio_renderer_->Initialize(
         std::bind(&AudioRendererTest::OnError, this),
         std::bind(&AudioRendererTest::OnPrerolled, this),
@@ -203,7 +203,7 @@
     sample_info.timestamp = timestamp;
     sample_info.drm_info = NULL;
     sample_info.type = kSbMediaTypeAudio;
-    sample_info.audio_sample_info = GetDefaultAudioSampleInfo();
+    GetDefaultAudioSampleInfo().ConvertTo(&sample_info.audio_sample_info);
     return new InputBuffer(DeallocateSampleCB, NULL, this, sample_info);
   }
 
@@ -212,7 +212,7 @@
         kDefaultNumberOfChannels, sample_type_, storage_type_, timestamp,
         frames * kDefaultNumberOfChannels *
             media::GetBytesPerSample(sample_type_));
-    memset(decoded_audio->buffer(), 0, decoded_audio->size());
+    memset(decoded_audio->data(), 0, decoded_audio->size_in_bytes());
     return decoded_audio;
   }
 
@@ -245,20 +245,22 @@
     SbMemoryDeallocate(const_cast<void*>(sample_buffer));
   }
 
-  static const SbMediaAudioSampleInfo& GetDefaultAudioSampleInfo() {
-    static starboard::media::AudioSampleInfo audio_sample_info = {};
+  static const media::AudioStreamInfo& GetDefaultAudioStreamInfo() {
+    static starboard::media::AudioStreamInfo audio_stream_info;
 
-    audio_sample_info.codec = kSbMediaAudioCodecAac;
-    audio_sample_info.mime = "";
-    audio_sample_info.number_of_channels = kDefaultNumberOfChannels;
-    audio_sample_info.samples_per_second = kDefaultSamplesPerSecond;
-    audio_sample_info.bits_per_sample = 32;
-    audio_sample_info.average_bytes_per_second =
-        audio_sample_info.samples_per_second *
-        audio_sample_info.number_of_channels *
-        audio_sample_info.bits_per_sample / 8;
-    audio_sample_info.block_alignment = 4;
-    audio_sample_info.audio_specific_config_size = 0;
+    audio_stream_info.codec = kSbMediaAudioCodecAac;
+    audio_stream_info.mime = "";
+    audio_stream_info.number_of_channels = kDefaultNumberOfChannels;
+    audio_stream_info.samples_per_second = kDefaultSamplesPerSecond;
+    audio_stream_info.bits_per_sample = 32;
+
+    return audio_stream_info;
+  }
+
+  static const media::AudioSampleInfo& GetDefaultAudioSampleInfo() {
+    static starboard::media::AudioSampleInfo audio_sample_info;
+
+    audio_sample_info.stream_info = GetDefaultAudioStreamInfo();
 
     return audio_sample_info;
   }
diff --git a/starboard/shared/starboard/player/filter/testing/player_components_test.cc b/starboard/shared/starboard/player/filter/testing/player_components_test.cc
index b7287bb..88e585e 100644
--- a/starboard/shared/starboard/player/filter/testing/player_components_test.cc
+++ b/starboard/shared/starboard/player/filter/testing/player_components_test.cc
@@ -29,6 +29,7 @@
 #include "starboard/shared/starboard/player/video_dmp_reader.h"
 #include "starboard/testing/fake_graphics_context_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
+
 namespace starboard {
 namespace shared {
 namespace starboard {
@@ -89,28 +90,22 @@
     string error_message;
     if (audio_reader_ && video_reader_) {
       CreationParameters creation_parameters(
-          audio_reader_->audio_codec(), audio_reader_->audio_sample_info(),
-          video_reader_->video_codec(),
-          video_reader_->GetPlayerSampleInfo(kSbMediaTypeVideo, 0)
-              .video_sample_info,
-          kDummyPlayer, output_mode_,
+          audio_reader_->audio_stream_info(),
+          video_reader_->video_stream_info(), kDummyPlayer, output_mode_,
           fake_graphics_context_provider_.decoder_target_provider());
       player_components_ =
           factory->CreateComponents(creation_parameters, &error_message);
     } else if (audio_reader_) {
       // Audio only
       CreationParameters creation_parameters(
-          audio_reader_->audio_codec(), audio_reader_->audio_sample_info());
+          audio_reader_->audio_stream_info());
       player_components_ =
           factory->CreateComponents(creation_parameters, &error_message);
     } else {
       // Video only
       ASSERT_TRUE(video_reader_);
       CreationParameters creation_parameters(
-          video_reader_->video_codec(),
-          video_reader_->GetPlayerSampleInfo(kSbMediaTypeVideo, 0)
-              .video_sample_info,
-          kDummyPlayer, output_mode_,
+          video_reader_->video_stream_info(), kDummyPlayer, output_mode_,
           fake_graphics_context_provider_.decoder_target_provider());
       player_components_ =
           factory->CreateComponents(creation_parameters, &error_message);
diff --git a/starboard/shared/starboard/player/filter/testing/test_util.cc b/starboard/shared/starboard/player/filter/testing/test_util.cc
index d87f71d..0739df4 100644
--- a/starboard/shared/starboard/player/filter/testing/test_util.cc
+++ b/starboard/shared/starboard/player/filter/testing/test_util.cc
@@ -17,6 +17,7 @@
 #include "starboard/audio_sink.h"
 #include "starboard/common/log.h"
 #include "starboard/directory.h"
+#include "starboard/extension/enhanced_audio.h"
 #include "starboard/shared/starboard/media/media_support_internal.h"
 #include "starboard/shared/starboard/media/mime_type.h"
 #include "starboard/shared/starboard/player/filter/player_components.h"
@@ -140,7 +141,7 @@
 
       audio_file_info_cache.push_back(
           {filename, dmp_reader.audio_codec(),
-           dmp_reader.audio_sample_info().number_of_channels,
+           dmp_reader.audio_stream_info().number_of_channels,
            dmp_reader.audio_bitrate(), strstr(filename, "heaac") != nullptr});
     }
   }
@@ -201,17 +202,15 @@
         continue;
       }
 
-      const auto& video_sample_info =
-          dmp_reader.GetPlayerSampleInfo(kSbMediaTypeVideo, 0)
-              .video_sample_info;
+      const auto& video_stream_info = dmp_reader.video_stream_info();
       const std::string video_mime = dmp_reader.video_mime_type();
       const MimeType video_mime_type(video_mime.c_str());
       if (SbMediaIsVideoSupported(
               dmp_reader.video_codec(),
               video_mime.size() > 0 ? &video_mime_type : nullptr, -1, -1, 8,
               kSbMediaPrimaryIdUnspecified, kSbMediaTransferIdUnspecified,
-              kSbMediaMatrixIdUnspecified, video_sample_info.frame_width,
-              video_sample_info.frame_height, dmp_reader.video_bitrate(),
+              kSbMediaMatrixIdUnspecified, video_stream_info.frame_width,
+              video_stream_info.frame_height, dmp_reader.video_bitrate(),
               dmp_reader.video_fps(), false)) {
         test_params.push_back(std::make_tuple(filename, output_mode));
       }
@@ -223,8 +222,7 @@
 }
 
 bool CreateAudioComponents(bool using_stub_decoder,
-                           SbMediaAudioCodec codec,
-                           const SbMediaAudioSampleInfo& audio_sample_info,
+                           const media::AudioStreamInfo& audio_stream_info,
                            scoped_ptr<AudioDecoder>* audio_decoder,
                            scoped_ptr<AudioRendererSink>* audio_renderer_sink) {
   SB_CHECK(audio_decoder);
@@ -234,7 +232,7 @@
   audio_decoder->reset();
 
   PlayerComponents::Factory::CreationParameters creation_parameters(
-      codec, audio_sample_info);
+      audio_stream_info);
 
   scoped_ptr<PlayerComponents::Factory> factory;
   if (using_stub_decoder) {
@@ -264,22 +262,80 @@
          << "time " << time1 << " doesn't match with time " << time2;
 }
 
-media::VideoSampleInfo CreateVideoSampleInfo(SbMediaVideoCodec codec) {
-  shared::starboard::media::VideoSampleInfo video_sample_info = {};
+media::VideoStreamInfo CreateVideoStreamInfo(SbMediaVideoCodec codec) {
+  shared::starboard::media::VideoStreamInfo video_stream_info = {};
 
-  video_sample_info.codec = codec;
-  video_sample_info.mime = "";
-  video_sample_info.max_video_capabilities = "";
+  video_stream_info.codec = codec;
+  video_stream_info.mime = "";
+  video_stream_info.max_video_capabilities = "";
 
-  video_sample_info.color_metadata.primaries = kSbMediaPrimaryIdBt709;
-  video_sample_info.color_metadata.transfer = kSbMediaTransferIdBt709;
-  video_sample_info.color_metadata.matrix = kSbMediaMatrixIdBt709;
-  video_sample_info.color_metadata.range = kSbMediaRangeIdLimited;
+  video_stream_info.color_metadata.primaries = kSbMediaPrimaryIdBt709;
+  video_stream_info.color_metadata.transfer = kSbMediaTransferIdBt709;
+  video_stream_info.color_metadata.matrix = kSbMediaMatrixIdBt709;
+  video_stream_info.color_metadata.range = kSbMediaRangeIdLimited;
 
-  video_sample_info.frame_width = 1920;
-  video_sample_info.frame_height = 1080;
+  video_stream_info.frame_width = 1920;
+  video_stream_info.frame_height = 1080;
 
-  return video_sample_info;
+  return video_stream_info;
+}
+
+bool IsPartialAudioSupported() {
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  return true;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  return SbSystemGetExtension(kCobaltExtensionEnhancedAudioName) != nullptr;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+}
+
+scoped_refptr<InputBuffer> GetAudioInputBuffer(
+    video_dmp::VideoDmpReader* dmp_reader,
+    size_t index) {
+  SB_DCHECK(dmp_reader);
+
+  auto player_sample_info =
+      dmp_reader->GetPlayerSampleInfo(kSbMediaTypeAudio, index);
+  return new InputBuffer(StubDeallocateSampleFunc, nullptr, nullptr,
+                         player_sample_info);
+}
+
+scoped_refptr<InputBuffer> GetAudioInputBuffer(
+    video_dmp::VideoDmpReader* dmp_reader,
+    size_t index,
+    SbTime discarded_duration_from_front,
+    SbTime discarded_duration_from_back) {
+  SB_DCHECK(dmp_reader);
+  auto player_sample_info =
+      dmp_reader->GetPlayerSampleInfo(kSbMediaTypeAudio, index);
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  player_sample_info.audio_sample_info.discarded_duration_from_front =
+      discarded_duration_from_front;
+  player_sample_info.audio_sample_info.discarded_duration_from_back =
+      discarded_duration_from_back;
+  auto input_buffer = new InputBuffer(StubDeallocateSampleFunc, nullptr,
+                                      nullptr, player_sample_info);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  media::AudioSampleInfo audio_sample_info(
+      player_sample_info.audio_sample_info);
+  audio_sample_info.discarded_duration_from_front =
+      discarded_duration_from_front;
+  audio_sample_info.discarded_duration_from_back = discarded_duration_from_back;
+
+  CobaltExtensionEnhancedAudioPlayerSampleInfo enhanced_audio_sample_info;
+  enhanced_audio_sample_info.type = player_sample_info.type;
+  enhanced_audio_sample_info.buffer = player_sample_info.buffer;
+  enhanced_audio_sample_info.buffer_size = player_sample_info.buffer_size;
+  enhanced_audio_sample_info.timestamp = player_sample_info.timestamp;
+  enhanced_audio_sample_info.side_data = player_sample_info.side_data;
+  enhanced_audio_sample_info.side_data_count =
+      player_sample_info.side_data_count;
+  audio_sample_info.ConvertTo(&enhanced_audio_sample_info.audio_sample_info);
+  enhanced_audio_sample_info.drm_info = player_sample_info.drm_info;
+
+  auto input_buffer = new InputBuffer(StubDeallocateSampleFunc, nullptr,
+                                      nullptr, enhanced_audio_sample_info);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  return input_buffer;
 }
 
 }  // namespace testing
diff --git a/starboard/shared/starboard/player/filter/testing/test_util.h b/starboard/shared/starboard/player/filter/testing/test_util.h
index 625ccba..3a84465 100644
--- a/starboard/shared/starboard/player/filter/testing/test_util.h
+++ b/starboard/shared/starboard/player/filter/testing/test_util.h
@@ -20,10 +20,14 @@
 #include <utility>
 #include <vector>
 
+#include "starboard/common/ref_counted.h"
 #include "starboard/player.h"
-
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_renderer_sink.h"
+#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/video_dmp_reader.h"
+#include "starboard/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace starboard {
@@ -55,14 +59,25 @@
 std::vector<VideoTestParam> GetSupportedVideoTests();
 
 bool CreateAudioComponents(bool using_stub_decoder,
-                           SbMediaAudioCodec codec,
-                           const SbMediaAudioSampleInfo& audio_sample_info,
+                           const media::AudioStreamInfo& audio_stream_info,
                            scoped_ptr<AudioDecoder>* audio_decoder,
                            scoped_ptr<AudioRendererSink>* audio_renderer_sink);
 
 ::testing::AssertionResult AlmostEqualTime(SbTime time1, SbTime time2);
 
-media::VideoSampleInfo CreateVideoSampleInfo(SbMediaVideoCodec codec);
+media::VideoStreamInfo CreateVideoStreamInfo(SbMediaVideoCodec codec);
+
+bool IsPartialAudioSupported();
+
+scoped_refptr<InputBuffer> GetAudioInputBuffer(
+    video_dmp::VideoDmpReader* dmp_reader,
+    size_t index);
+
+scoped_refptr<InputBuffer> GetAudioInputBuffer(
+    video_dmp::VideoDmpReader* dmp_reader,
+    size_t index,
+    SbTime discarded_duration_from_front,
+    SbTime discarded_duration_from_back);
 
 }  // namespace testing
 }  // namespace filter
diff --git a/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc b/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
index 8d38b52..c4ce8e1 100644
--- a/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
+++ b/starboard/shared/starboard/player/filter/testing/video_decoder_test.cc
@@ -158,8 +158,7 @@
           SbMediaAudioSampleInfo dummy_audio_sample_info = {
               kSbMediaAudioCodecNone};
           PlayerComponents::Factory::CreationParameters creation_parameters(
-              fixture_.dmp_reader().video_codec(),
-              CreateVideoSampleInfo(fixture_.dmp_reader().video_codec()),
+              CreateVideoStreamInfo(fixture_.dmp_reader().video_codec()),
               &players[i], output_mode,
               fake_graphics_context_provider_.decoder_target_provider(),
               nullptr);
diff --git a/starboard/shared/starboard/player/filter/testing/video_decoder_test_fixture.cc b/starboard/shared/starboard/player/filter/testing/video_decoder_test_fixture.cc
index 618359c..ca9c3cf 100644
--- a/starboard/shared/starboard/player/filter/testing/video_decoder_test_fixture.cc
+++ b/starboard/shared/starboard/player/filter/testing/video_decoder_test_fixture.cc
@@ -87,8 +87,7 @@
       output_mode, dmp_reader_.video_codec(), kSbDrmSystemInvalid));
 
   PlayerComponents::Factory::CreationParameters creation_parameters(
-      dmp_reader_.video_codec(), GetVideoInputBuffer(0)->video_sample_info(),
-      &player_, output_mode,
+      GetVideoInputBuffer(0)->video_stream_info(), &player_, output_mode,
       fake_graphics_context_provider_->decoder_target_provider(), nullptr);
 
   scoped_ptr<PlayerComponents::Factory> factory;
diff --git a/starboard/shared/starboard/player/filter/tools/BUILD.gn b/starboard/shared/starboard/player/filter/tools/BUILD.gn
index 4e59d2f..99d78f3 100644
--- a/starboard/shared/starboard/player/filter/tools/BUILD.gn
+++ b/starboard/shared/starboard/player/filter/tools/BUILD.gn
@@ -25,9 +25,11 @@
   configs += [ "//starboard/build/config:starboard_implementation" ]
   public_deps = [
     "//starboard",
+    "//starboard/shared/starboard/media:media_util",
     "//starboard/shared/starboard/player:player_download_test_data",
   ]
   data_deps = [
+    "//cobalt/network:copy_ssl_certificates",
     "//starboard/shared/starboard/player:player_download_test_data",
     "//third_party/icu:icudata",
   ]
diff --git a/starboard/shared/starboard/player/filter/tools/audio_dmp_player.cc b/starboard/shared/starboard/player/filter/tools/audio_dmp_player.cc
index 3f860b6..8124ad1 100644
--- a/starboard/shared/starboard/player/filter/tools/audio_dmp_player.cc
+++ b/starboard/shared/starboard/player/filter/tools/audio_dmp_player.cc
@@ -129,8 +129,7 @@
   scoped_ptr<PlayerComponents::Factory> factory =
       PlayerComponents::Factory::Create();
   PlayerComponents::Factory::CreationParameters creation_parameters(
-      s_video_dmp_reader->audio_codec(),
-      s_video_dmp_reader->audio_sample_info());
+      s_video_dmp_reader->audio_stream_info());
   std::string error_message;
   s_player_components =
       factory->CreateComponents(creation_parameters, &error_message);
diff --git a/starboard/shared/starboard/player/filter/video_renderer_internal_impl.h b/starboard/shared/starboard/player/filter/video_renderer_internal_impl.h
index e456eab..6b10524 100644
--- a/starboard/shared/starboard/player/filter/video_renderer_internal_impl.h
+++ b/starboard/shared/starboard/player/filter/video_renderer_internal_impl.h
@@ -17,7 +17,7 @@
 
 #include <list>
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/log.h"
 #include "starboard/common/mutex.h"
 #include "starboard/common/optional.h"
diff --git a/starboard/shared/starboard/player/filter/wsola_internal.cc b/starboard/shared/starboard/player/filter/wsola_internal.cc
index a62517c..deb2928 100644
--- a/starboard/shared/starboard/player/filter/wsola_internal.cc
+++ b/starboard/shared/starboard/player/filter/wsola_internal.cc
@@ -25,6 +25,7 @@
 
 #include <algorithm>
 #include <cmath>
+#include <cstring>
 #include <limits>
 #include <memory>
 
@@ -32,8 +33,6 @@
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/memory.h"
 
-#include <cstring>
-
 // TODO: Detect Neon on ARM platform and enable SIMD.
 #if SB_IS(ARCH_X86) || SB_IS(ARCH_X64)
 #define USE_SIMD 1
@@ -49,11 +48,14 @@
 
 namespace {
 
-bool InInterval(int n, Interval q) { return n >= q.first && n <= q.second; }
+bool InInterval(int n, Interval q) {
+  return n >= q.first && n <= q.second;
+}
 
 float MultiChannelSimilarityMeasure(const float* dot_prod_a_b,
                                     const float* energy_a,
-                                    const float* energy_b, int channels) {
+                                    const float* energy_b,
+                                    int channels) {
   const float kEpsilon = 1e-12f;
   float similarity_measure = 0.0f;
   for (int n = 0; n < channels; ++n) {
@@ -75,8 +77,8 @@
   SB_DCHECK(frame_offset_a + num_frames <= a->frames());
   SB_DCHECK(frame_offset_b + num_frames <= b->frames());
 
-  const float* a_frames = reinterpret_cast<const float*>(a->buffer());
-  const float* b_frames = reinterpret_cast<const float*>(b->buffer());
+  const float* a_frames = reinterpret_cast<const float*>(a->data());
+  const float* b_frames = reinterpret_cast<const float*>(b->data());
 
 // SIMD optimized variants can provide a massive speedup to this operation.
 #if defined(USE_SIMD)
@@ -139,7 +141,7 @@
   int num_blocks = input->frames() - (frames_per_block - 1);
   int channels = input->channels();
 
-  const float* input_frames = reinterpret_cast<const float*>(input->buffer());
+  const float* input_frames = reinterpret_cast<const float*>(input->data());
   for (int k = 0; k < channels; ++k) {
     const float* input_channel = input_frames + k;
 
@@ -166,7 +168,8 @@
 //   f(0) = y[1]
 //   f(1) = y[2]
 // and return the maximum, assuming that y[0] <= y[1] >= y[2].
-void QuadraticInterpolation(const float* y_values, float* extremum,
+void QuadraticInterpolation(const float* y_values,
+                            float* extremum,
                             float* extremum_value) {
   float a = 0.5f * (y_values[2] + y_values[0]) - y_values[1];
   float b = 0.5f * (y_values[2] - y_values[0]);
diff --git a/starboard/shared/starboard/player/input_buffer_internal.cc b/starboard/shared/starboard/player/input_buffer_internal.cc
index f3ec61e..b4f7b7a 100644
--- a/starboard/shared/starboard/player/input_buffer_internal.cc
+++ b/starboard/shared/starboard/player/input_buffer_internal.cc
@@ -29,39 +29,6 @@
 namespace starboard {
 namespace player {
 
-InputBuffer::InputBuffer(SbPlayerDeallocateSampleFunc deallocate_sample_func,
-                         SbPlayer player,
-                         void* context,
-                         const SbPlayerSampleInfo& sample_info)
-    : deallocate_sample_func_(deallocate_sample_func),
-      player_(player),
-      context_(context),
-      sample_type_(sample_info.type),
-      data_(static_cast<const uint8_t*>(sample_info.buffer)),
-      size_(sample_info.buffer_size),
-      timestamp_(sample_info.timestamp) {
-  SB_DCHECK(deallocate_sample_func);
-
-  if (sample_type_ == kSbMediaTypeAudio) {
-    audio_sample_info_ = sample_info.audio_sample_info;
-  } else {
-    SB_DCHECK(sample_type_ == kSbMediaTypeVideo);
-    video_sample_info_ = sample_info.video_sample_info;
-  }
-  TryToAssignDrmSampleInfo(sample_info.drm_info);
-  if (sample_info.side_data_count > 0) {
-    SB_DCHECK(sample_info.side_data_count == 1);
-    SB_DCHECK(sample_info.side_data);
-    SB_DCHECK(sample_info.side_data->type == kMatroskaBlockAdditional);
-    SB_DCHECK(sample_info.side_data->data);
-    // Make a copy anyway as it is possible to release |data_| earlier in
-    // SetDecryptedContent().
-    side_data_.assign(
-        sample_info.side_data->data,
-        sample_info.side_data->data + sample_info.side_data->size);
-  }
-}
-
 InputBuffer::~InputBuffer() {
   DeallocateSampleBuffer(data_);
 }
@@ -87,18 +54,18 @@
      << " sample @ timestamp: " << timestamp() << " in " << size()
      << " bytes ==========\n";
   if (sample_type() == kSbMediaTypeAudio) {
-    ss << "codec: " << audio_sample_info().codec << ", mime: '"
-       << audio_sample_info().mime << "'\n";
-    ss << audio_sample_info().samples_per_second << '\n';
+    ss << "codec: " << audio_stream_info().codec << ", mime: '"
+       << audio_stream_info().mime << "'\n";
+    ss << audio_stream_info().samples_per_second << '\n';
   } else {
     SB_DCHECK(sample_type() == kSbMediaTypeVideo);
 
-    ss << "codec: " << video_sample_info().codec << ", mime: '"
-       << video_sample_info().mime << "'"
+    ss << "codec: " << video_stream_info().codec << ", mime: '"
+       << video_stream_info().mime << "'"
        << ", max_video_capabilities: '"
-       << video_sample_info().max_video_capabilities << "'\n";
-    ss << video_sample_info().frame_width << " x "
-       << video_sample_info().frame_height << '\n';
+       << video_stream_info().max_video_capabilities << "'\n";
+    ss << video_stream_info().frame_width << " x "
+       << video_stream_info().frame_height << '\n';
   }
   if (has_drm_info_) {
     ss << "iv: "
diff --git a/starboard/shared/starboard/player/input_buffer_internal.h b/starboard/shared/starboard/player/input_buffer_internal.h
index 3da8d19..0ae44e1 100644
--- a/starboard/shared/starboard/player/input_buffer_internal.h
+++ b/starboard/shared/starboard/player/input_buffer_internal.h
@@ -20,6 +20,7 @@
 
 #include "starboard/common/ref_counted.h"
 #include "starboard/drm.h"
+#include "starboard/extension/enhanced_audio.h"
 #include "starboard/media.h"
 #include "starboard/player.h"
 #include "starboard/shared/internal_only.h"
@@ -33,10 +34,11 @@
 // This class encapsulate a media buffer.
 class InputBuffer : public RefCountedThreadSafe<InputBuffer> {
  public:
+  template <typename SampleInfo>
   InputBuffer(SbPlayerDeallocateSampleFunc deallocate_sample_func,
               SbPlayer player,
               void* context,
-              const SbPlayerSampleInfo& sample_info);
+              const SampleInfo& sample_info);
 
   ~InputBuffer();
 
@@ -47,14 +49,21 @@
   const std::vector<uint8_t>& side_data() const { return side_data_; }
 
   SbTime timestamp() const { return timestamp_; }
-  const SbMediaAudioSampleInfo& audio_sample_info() const {
+  const media::AudioSampleInfo& audio_sample_info() const {
     SB_DCHECK(sample_type_ == kSbMediaTypeAudio);
     return audio_sample_info_;
   }
-  const SbMediaVideoSampleInfo& video_sample_info() const {
+  const media::VideoSampleInfo& video_sample_info() const {
     SB_DCHECK(sample_type_ == kSbMediaTypeVideo);
     return video_sample_info_;
   }
+  const media::AudioStreamInfo& audio_stream_info() const {
+    return audio_sample_info().stream_info;
+  }
+  const media::VideoStreamInfo& video_stream_info() const {
+    return video_sample_info().stream_info;
+  }
+
   const SbDrmSampleInfo* drm_info() const {
     return has_drm_info_ ? &drm_info_ : NULL;
   }
@@ -90,6 +99,40 @@
 
 typedef std::vector<scoped_refptr<InputBuffer>> InputBuffers;
 
+template <typename SampleInfo>
+InputBuffer::InputBuffer(SbPlayerDeallocateSampleFunc deallocate_sample_func,
+                         SbPlayer player,
+                         void* context,
+                         const SampleInfo& sample_info)
+    : deallocate_sample_func_(deallocate_sample_func),
+      player_(player),
+      context_(context),
+      sample_type_(sample_info.type),
+      data_(static_cast<const uint8_t*>(sample_info.buffer)),
+      size_(sample_info.buffer_size),
+      timestamp_(sample_info.timestamp) {
+  SB_DCHECK(deallocate_sample_func);
+
+  if (sample_type_ == kSbMediaTypeAudio) {
+    audio_sample_info_ = sample_info.audio_sample_info;
+  } else {
+    SB_DCHECK(sample_type_ == kSbMediaTypeVideo);
+    video_sample_info_ = sample_info.video_sample_info;
+  }
+  TryToAssignDrmSampleInfo(sample_info.drm_info);
+  if (sample_info.side_data_count > 0) {
+    SB_DCHECK(sample_info.side_data_count == 1);
+    SB_DCHECK(sample_info.side_data);
+    SB_DCHECK(sample_info.side_data->type == kMatroskaBlockAdditional);
+    SB_DCHECK(sample_info.side_data->data);
+    // Make a copy anyway as it is possible to release |data_| earlier in
+    // SetDecryptedContent().
+    side_data_.assign(
+        sample_info.side_data->data,
+        sample_info.side_data->data + sample_info.side_data->size);
+  }
+}
+
 }  // namespace player
 }  // namespace starboard
 }  // namespace shared
diff --git a/starboard/shared/starboard/player/player_create.cc b/starboard/shared/starboard/player/player_create.cc
index 7ffb717..6917f47 100644
--- a/starboard/shared/starboard/player/player_create.cc
+++ b/starboard/shared/starboard/player/player_create.cc
@@ -30,13 +30,13 @@
 #include "starboard/shared/starboard/player/video_dmp_writer.h"
 #endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
 
+using ::starboard::shared::media_session::kPlaying;
 using ::starboard::shared::media_session::
     UpdateActiveSessionPlatformPlaybackState;
-using ::starboard::shared::media_session::kPlaying;
 using ::starboard::shared::starboard::media::MimeType;
+using ::starboard::shared::starboard::player::PlayerWorker;
 using ::starboard::shared::starboard::player::filter::
     FilterBasedPlayerWorkerHandler;
-using ::starboard::shared::starboard::player::PlayerWorker;
 
 SbPlayer SbPlayerCreate(SbWindow window,
                         const SbPlayerCreationParam* creation_param,
@@ -58,35 +58,43 @@
     return kSbPlayerInvalid;
   }
 
-  bool has_audio =
-      creation_param->audio_sample_info.codec != kSbMediaAudioCodecNone;
-  bool has_video =
-      creation_param->video_sample_info.codec != kSbMediaVideoCodecNone;
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaAudioStreamInfo& audio_stream_info =
+      creation_param->audio_stream_info;
+  const SbMediaVideoStreamInfo& video_stream_info =
+      creation_param->video_stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaAudioSampleInfo& audio_stream_info =
+      creation_param->audio_sample_info;
+  const SbMediaVideoSampleInfo& video_stream_info =
+      creation_param->video_sample_info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 
-  const char* audio_mime =
-      has_audio ? creation_param->audio_sample_info.mime : "";
-  const char* video_mime =
-      has_video ? creation_param->video_sample_info.mime : "";
+  bool has_audio = audio_stream_info.codec != kSbMediaAudioCodecNone;
+  bool has_video = video_stream_info.codec != kSbMediaVideoCodecNone;
+
+  const char* audio_mime = has_audio ? audio_stream_info.mime : "";
+  const char* video_mime = has_video ? video_stream_info.mime : "";
   const char* max_video_capabilities =
-      has_video ? creation_param->video_sample_info.max_video_capabilities : "";
+      has_video ? video_stream_info.max_video_capabilities : "";
 
   if (!audio_mime) {
-    SB_LOG(ERROR) << "creation_param->audio_sample_info.mime cannot be null.";
+    SB_LOG(ERROR) << "creation_param->audio_stream_info.mime cannot be null.";
     player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
-                      "creation_param->video_sample_info.mime cannot be null");
+                      "creation_param->audio_stream_info.mime cannot be null");
     return kSbPlayerInvalid;
   }
   if (!video_mime) {
-    SB_LOG(ERROR) << "creation_param->video_sample_info.mime cannot be null.";
+    SB_LOG(ERROR) << "creation_param->video_stream_info.mime cannot be null.";
     player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
-                      "creation_param->video_sample_info.mime cannot be null");
+                      "creation_param->video_stream_info.mime cannot be null");
     return kSbPlayerInvalid;
   }
   if (!max_video_capabilities) {
-    SB_LOG(ERROR) << "creation_param->video_sample_info.max_video_capabilities"
+    SB_LOG(ERROR) << "creation_param->video_stream_info.max_video_capabilities"
                   << " cannot be null.";
     player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
-                      "creation_param->video_sample_info.max_video_"
+                      "creation_param->video_stream_info.max_video_"
                       "capabilities cannot be null");
     return kSbPlayerInvalid;
   }
@@ -96,24 +104,13 @@
                << "\", and max video capabilities \"" << max_video_capabilities
                << "\".";
 
-  SbMediaAudioCodec audio_codec = creation_param->audio_sample_info.codec;
-  SbMediaVideoCodec video_codec = creation_param->video_sample_info.codec;
+  SbMediaAudioCodec audio_codec = audio_stream_info.codec;
+  SbMediaVideoCodec video_codec = video_stream_info.codec;
 #if SB_PLAYER_ENABLE_VIDEO_DUMPER
   SbDrmSystem drm_system = creation_param->drm_system;
 #endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
-  const SbMediaAudioSampleInfo* audio_sample_info =
-      &creation_param->audio_sample_info;
   const auto output_mode = creation_param->output_mode;
 
-  if (!player_error_func) {
-    SB_LOG(ERROR) << "|player_error_func| cannot be null.";
-    return kSbPlayerInvalid;
-  }
-
-  if (audio_sample_info) {
-    SB_DCHECK(audio_sample_info->codec == audio_codec);
-  }
-
   if (!sample_deallocate_func) {
     SB_LOG(ERROR) << "|sample_deallocate_func| cannot be null.";
     player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
@@ -178,17 +175,6 @@
     }
   }
 
-  if (audio_codec != kSbMediaAudioCodecNone && !audio_sample_info) {
-    SB_LOG(ERROR)
-        << "SbPlayerCreate() requires a non-NULL SbMediaAudioSampleInfo "
-        << "when |audio_codec| is not kSbMediaAudioCodecNone.";
-    player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
-                      "SbPlayerCreate() requires a non-NULL "
-                      "SbMediaAudioSampleInfo when |audio_codec| is not "
-                      "kSbMediaAudioCodecNone");
-    return kSbPlayerInvalid;
-  }
-
   if (audio_codec == kSbMediaAudioCodecNone &&
       video_codec == kSbMediaVideoCodecNone) {
     SB_LOG(ERROR) << "SbPlayerCreate() requires at least one audio track or"
@@ -200,12 +186,12 @@
   }
 
   std::string error_message;
-  if (audio_sample_info &&
-      audio_sample_info->number_of_channels > SbAudioSinkGetMaxChannels()) {
+  if (audio_stream_info.codec != kSbMediaAudioCodecNone &&
+      audio_stream_info.number_of_channels > SbAudioSinkGetMaxChannels()) {
     error_message = starboard::FormatString(
         "Number of audio channels (%d) exceeds the maximum number of audio "
         "channels supported by this platform (%d)",
-        audio_sample_info->number_of_channels, SbAudioSinkGetMaxChannels());
+        audio_stream_info.number_of_channels, SbAudioSinkGetMaxChannels());
     SB_LOG(ERROR) << error_message << ".";
     player_error_func(kSbPlayerInvalid, context, kSbPlayerErrorDecode,
                       error_message.c_str());
@@ -227,14 +213,13 @@
       new FilterBasedPlayerWorkerHandler(creation_param, provider));
 
   SbPlayer player = SbPlayerPrivate::CreateInstance(
-      audio_codec, video_codec, audio_sample_info, sample_deallocate_func,
-      decoder_status_func, player_status_func, player_error_func, context,
-      handler.Pass());
+      audio_codec, video_codec, sample_deallocate_func, decoder_status_func,
+      player_status_func, player_error_func, context, handler.Pass());
 
 #if SB_PLAYER_ENABLE_VIDEO_DUMPER
   using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
   VideoDmpWriter::OnPlayerCreate(player, audio_codec, video_codec, drm_system,
-                                 audio_sample_info);
+                                 audio_stream_info);
 #endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
 
   return player;
diff --git a/starboard/shared/starboard/player/player_get_info2.cc b/starboard/shared/starboard/player/player_get_info.cc
similarity index 80%
rename from starboard/shared/starboard/player/player_get_info2.cc
rename to starboard/shared/starboard/player/player_get_info.cc
index 528b124..0c2eb19 100644
--- a/starboard/shared/starboard/player/player_get_info2.cc
+++ b/starboard/shared/starboard/player/player_get_info.cc
@@ -17,7 +17,11 @@
 #include "starboard/common/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+void SbPlayerGetInfo(SbPlayer player, SbPlayerInfo* out_player_info) {
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 void SbPlayerGetInfo2(SbPlayer player, SbPlayerInfo2* out_player_info) {
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   if (!SbPlayerIsValid(player)) {
     SB_DLOG(WARNING) << "player is invalid.";
     return;
diff --git a/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc b/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc
index 356dccb..e592e18 100644
--- a/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc
+++ b/starboard/shared/starboard/player/player_get_preferred_output_mode_prefer_punchout.cc
@@ -28,30 +28,43 @@
     return kSbPlayerOutputModeInvalid;
   }
 
-  if (creation_param->audio_sample_info.codec != kSbMediaAudioCodecNone &&
-      !creation_param->audio_sample_info.mime) {
-    SB_LOG(ERROR) << "creation_param->audio_sample_info.mime cannot be NULL";
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaAudioStreamInfo& audio_stream_info =
+      creation_param->audio_stream_info;
+  const SbMediaVideoStreamInfo& video_stream_info =
+      creation_param->video_stream_info;
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  const SbMediaAudioSampleInfo& audio_stream_info =
+      creation_param->audio_sample_info;
+  const SbMediaVideoSampleInfo& video_stream_info =
+      creation_param->video_sample_info;
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+
+  if (audio_stream_info.codec != kSbMediaAudioCodecNone &&
+      !audio_stream_info.mime) {
+    SB_LOG(ERROR) << "creation_param->audio_stream_info.mime cannot be NULL";
     return kSbPlayerOutputModeInvalid;
   }
 
-  if (creation_param->video_sample_info.codec != kSbMediaVideoCodecNone &&
-      !creation_param->video_sample_info.mime) {
-    SB_LOG(ERROR) << "creation_param->video_sample_info.mime cannot be NULL";
+  if (video_stream_info.codec != kSbMediaVideoCodecNone &&
+      !video_stream_info.mime) {
+    SB_LOG(ERROR) << "creation_param->video_stream_info.mime cannot be NULL";
     return kSbPlayerOutputModeInvalid;
   }
 
-  if (creation_param->video_sample_info.codec != kSbMediaVideoCodecNone &&
-      !creation_param->video_sample_info.max_video_capabilities) {
-    SB_LOG(ERROR) << "creation_param->video_sample_info.max_video_capabilities"
+  if (video_stream_info.codec != kSbMediaVideoCodecNone &&
+      !video_stream_info.max_video_capabilities) {
+    SB_LOG(ERROR) << "creation_param->video_stream_info.max_video_capabilities"
                   << " cannot be NULL";
     return kSbPlayerOutputModeInvalid;
   }
 
-  auto codec = creation_param->video_sample_info.codec;
+  auto codec = video_stream_info.codec;
   auto drm_system = creation_param->drm_system;
 
   SbPlayerOutputMode output_modes_to_check[] = {
-      kSbPlayerOutputModePunchOut, kSbPlayerOutputModeDecodeToTexture,
+      kSbPlayerOutputModePunchOut,
+      kSbPlayerOutputModeDecodeToTexture,
   };
 
   // Check |kSbPlayerOutputModeDecodeToTexture| first if the caller prefers it.
@@ -69,8 +82,7 @@
     return output_modes_to_check[1];
   }
 
-  SB_LOG(WARNING) << "creation_param->video_sample_info.codec ("
-                  << creation_param->video_sample_info.codec
-                  << ") is not supported";
+  SB_LOG(WARNING) << "creation_param->video_stream_info.codec ("
+                  << video_stream_info.codec << ") is not supported";
   return kSbPlayerOutputModeInvalid;
 }
diff --git a/starboard/shared/starboard/player/player_internal.cc b/starboard/shared/starboard/player/player_internal.cc
index a25f74d..df02e81 100644
--- a/starboard/shared/starboard/player/player_internal.cc
+++ b/starboard/shared/starboard/player/player_internal.cc
@@ -24,8 +24,6 @@
 
 namespace {
 
-using starboard::shared::starboard::player::InputBuffer;
-using starboard::shared::starboard::player::InputBuffers;
 using std::placeholders::_1;
 using std::placeholders::_2;
 using std::placeholders::_3;
@@ -45,7 +43,6 @@
 SbPlayerPrivate::SbPlayerPrivate(
     SbMediaAudioCodec audio_codec,
     SbMediaVideoCodec video_codec,
-    const SbMediaAudioSampleInfo* audio_sample_info,
     SbPlayerDeallocateSampleFunc sample_deallocate_func,
     SbPlayerDecoderStatusFunc decoder_status_func,
     SbPlayerStatusFunc player_status_func,
@@ -70,7 +67,6 @@
 SbPlayerPrivate* SbPlayerPrivate::CreateInstance(
     SbMediaAudioCodec audio_codec,
     SbMediaVideoCodec video_codec,
-    const SbMediaAudioSampleInfo* audio_sample_info,
     SbPlayerDeallocateSampleFunc sample_deallocate_func,
     SbPlayerDecoderStatusFunc decoder_status_func,
     SbPlayerStatusFunc player_status_func,
@@ -78,8 +74,8 @@
     void* context,
     starboard::scoped_ptr<PlayerWorker::Handler> player_worker_handler) {
   SbPlayerPrivate* ret = new SbPlayerPrivate(
-      audio_codec, video_codec, audio_sample_info, sample_deallocate_func,
-      decoder_status_func, player_status_func, player_error_func, context,
+      audio_codec, video_codec, sample_deallocate_func, decoder_status_func,
+      player_status_func, player_error_func, context,
       player_worker_handler.Pass());
 
   if (ret && ret->worker_) {
@@ -102,31 +98,6 @@
   worker_->Seek(seek_to_time, ticket);
 }
 
-void SbPlayerPrivate::WriteSamples(const SbPlayerSampleInfo* sample_infos,
-                                   int number_of_sample_infos) {
-  SB_DCHECK(sample_infos);
-  SB_DCHECK(number_of_sample_infos > 0);
-
-  if (sample_infos[0].type == kSbMediaTypeVideo) {
-    const auto& last_sample_info = sample_infos[number_of_sample_infos - 1];
-    total_video_frames_ += number_of_sample_infos;
-    frame_width_ = last_sample_info.video_sample_info.frame_width;
-    frame_height_ = last_sample_info.video_sample_info.frame_height;
-  }
-
-  InputBuffers input_buffers;
-  input_buffers.reserve(number_of_sample_infos);
-  for (int i = 0; i < number_of_sample_infos; i++) {
-    input_buffers.push_back(new InputBuffer(sample_deallocate_func_, this,
-                                            context_, sample_infos[i]));
-#if SB_PLAYER_ENABLE_VIDEO_DUMPER
-    using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
-    VideoDmpWriter::OnPlayerWriteSample(this, input_buffers.back());
-#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
-  }
-  worker_->WriteSamples(std::move(input_buffers));
-}
-
 void SbPlayerPrivate::WriteEndOfStream(SbMediaType stream_type) {
   worker_->WriteEndOfStream(stream_type);
 }
@@ -141,7 +112,11 @@
   // TODO: Wait until a frame is rendered with the updated bounds.
 }
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+void SbPlayerPrivate::GetInfo(SbPlayerInfo* out_player_info) {
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 void SbPlayerPrivate::GetInfo(SbPlayerInfo2* out_player_info) {
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   SB_DCHECK(out_player_info != NULL);
 
   starboard::ScopedLock lock(mutex_);
diff --git a/starboard/shared/starboard/player/player_internal.h b/starboard/shared/starboard/player/player_internal.h
index a6ffecf..0981380 100644
--- a/starboard/shared/starboard/player/player_internal.h
+++ b/starboard/shared/starboard/player/player_internal.h
@@ -15,8 +15,11 @@
 #ifndef STARBOARD_SHARED_STARBOARD_PLAYER_PLAYER_INTERNAL_H_
 #define STARBOARD_SHARED_STARBOARD_PLAYER_PLAYER_INTERNAL_H_
 
+#include <utility>
+
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/decode_target.h"
+#include "starboard/extension/enhanced_audio.h"
 #include "starboard/media.h"
 #include "starboard/player.h"
 #include "starboard/shared/internal_only.h"
@@ -33,7 +36,6 @@
   static SbPlayerPrivate* CreateInstance(
       SbMediaAudioCodec audio_codec,
       SbMediaVideoCodec video_codec,
-      const SbMediaAudioSampleInfo* audio_sample_info,
       SbPlayerDeallocateSampleFunc sample_deallocate_func,
       SbPlayerDecoderStatusFunc decoder_status_func,
       SbPlayerStatusFunc player_status_func,
@@ -41,13 +43,20 @@
       void* context,
       starboard::scoped_ptr<PlayerWorker::Handler> player_worker_handler);
 
+  static int number_of_players() { return number_of_players_; }
+
   void Seek(SbTime seek_to_time, int ticket);
-  void WriteSamples(const SbPlayerSampleInfo* sample_infos,
+  template <typename PlayerSampleInfo>
+  void WriteSamples(const PlayerSampleInfo* sample_infos,
                     int number_of_sample_infos);
   void WriteEndOfStream(SbMediaType stream_type);
   void SetBounds(int z_index, int x, int y, int width, int height);
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  void GetInfo(SbPlayerInfo* out_player_info);
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   void GetInfo(SbPlayerInfo2* out_player_info);
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   void SetPause(bool pause);
   void SetPlaybackRate(double playback_rate);
   void SetVolume(double volume);
@@ -64,7 +73,6 @@
   SbPlayerPrivate(
       SbMediaAudioCodec audio_codec,
       SbMediaVideoCodec video_codec,
-      const SbMediaAudioSampleInfo* audio_sample_info,
       SbPlayerDeallocateSampleFunc sample_deallocate_func,
       SbPlayerDecoderStatusFunc decoder_status_func,
       SbPlayerStatusFunc player_status_func,
@@ -102,4 +110,34 @@
   static int number_of_players_;
 };
 
+template <typename SampleInfo>
+void SbPlayerPrivate::WriteSamples(const SampleInfo* sample_infos,
+                                   int number_of_sample_infos) {
+  using starboard::shared::starboard::player::InputBuffer;
+  using starboard::shared::starboard::player::InputBuffers;
+
+  SB_DCHECK(sample_infos);
+  SB_DCHECK(number_of_sample_infos > 0);
+
+  InputBuffers input_buffers;
+  input_buffers.reserve(number_of_sample_infos);
+  for (int i = 0; i < number_of_sample_infos; i++) {
+    input_buffers.push_back(new InputBuffer(sample_deallocate_func_, this,
+                                            context_, sample_infos[i]));
+#if SB_PLAYER_ENABLE_VIDEO_DUMPER
+    using ::starboard::shared::starboard::player::video_dmp::VideoDmpWriter;
+    VideoDmpWriter::OnPlayerWriteSample(this, input_buffers.back());
+#endif  // SB_PLAYER_ENABLE_VIDEO_DUMPER
+  }
+
+  const auto& last_input_buffer = input_buffers.back();
+  if (last_input_buffer->sample_type() == kSbMediaTypeVideo) {
+    total_video_frames_ += number_of_sample_infos;
+    frame_width_ = last_input_buffer->video_stream_info().frame_width;
+    frame_height_ = last_input_buffer->video_stream_info().frame_height;
+  }
+
+  worker_->WriteSamples(std::move(input_buffers));
+}
+
 #endif  // STARBOARD_SHARED_STARBOARD_PLAYER_PLAYER_INTERNAL_H_
diff --git a/starboard/shared/starboard/player/player_seek2.cc b/starboard/shared/starboard/player/player_seek.cc
similarity index 78%
rename from starboard/shared/starboard/player/player_seek2.cc
rename to starboard/shared/starboard/player/player_seek.cc
index f34ae55..29a6e73 100644
--- a/starboard/shared/starboard/player/player_seek2.cc
+++ b/starboard/shared/starboard/player/player_seek.cc
@@ -17,7 +17,11 @@
 #include "starboard/common/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+void SbPlayerSeek(SbPlayer player, SbTime seek_to_timestamp, int ticket) {
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 void SbPlayerSeek2(SbPlayer player, SbTime seek_to_timestamp, int ticket) {
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
   if (!SbPlayerIsValid(player)) {
     SB_DLOG(WARNING) << "player is invalid.";
     return;
diff --git a/starboard/shared/starboard/player/player_tests.gni b/starboard/shared/starboard/player/player_tests.gni
index e9ea0ac..00bcb52 100644
--- a/starboard/shared/starboard/player/player_tests.gni
+++ b/starboard/shared/starboard/player/player_tests.gni
@@ -13,6 +13,8 @@
 # limitations under the License.
 
 player_tests_sources = [
+  "//starboard/shared/starboard/player/buffer_test_internal.cc",
+  "//starboard/shared/starboard/player/decoded_audio_test_internal.cc",
   "//starboard/shared/starboard/player/job_queue_test.cc",
   "//starboard/shared/starboard/player/job_thread_test.cc",
 ]
diff --git a/starboard/shared/starboard/player/player_write_sample2.cc b/starboard/shared/starboard/player/player_write_samples.cc
similarity index 88%
rename from starboard/shared/starboard/player/player_write_sample2.cc
rename to starboard/shared/starboard/player/player_write_samples.cc
index d839aff..92708f8 100644
--- a/starboard/shared/starboard/player/player_write_sample2.cc
+++ b/starboard/shared/starboard/player/player_write_samples.cc
@@ -17,7 +17,11 @@
 #include "starboard/common/log.h"
 #include "starboard/shared/starboard/player/player_internal.h"
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+void SbPlayerWriteSamples(SbPlayer player,
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 void SbPlayerWriteSample2(SbPlayer player,
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
                           SbMediaType sample_type,
                           const SbPlayerSampleInfo* sample_infos,
                           int number_of_sample_infos) {
diff --git a/starboard/shared/starboard/player/video_dmp_common.cc b/starboard/shared/starboard/player/video_dmp_common.cc
index a5440c1..1fe8274 100644
--- a/starboard/shared/starboard/player/video_dmp_common.cc
+++ b/starboard/shared/starboard/player/video_dmp_common.cc
@@ -89,41 +89,68 @@
 
 void Read(const ReadCB& read_cb,
           bool reverse_byte_order,
-          SbMediaAudioSampleInfoWithConfig* audio_sample_info) {
-  Read(read_cb, reverse_byte_order, &audio_sample_info->codec);
+          media::AudioSampleInfo* audio_sample_info) {
+  SB_DCHECK(audio_sample_info);
 
-  audio_sample_info->mime = "";
+  *audio_sample_info = media::AudioSampleInfo();
 
-  Read(read_cb, reverse_byte_order, &audio_sample_info->format_tag);
-  Read(read_cb, reverse_byte_order, &audio_sample_info->number_of_channels);
-  Read(read_cb, reverse_byte_order, &audio_sample_info->samples_per_second);
-  Read(read_cb, reverse_byte_order,
-       &audio_sample_info->average_bytes_per_second);
-  Read(read_cb, reverse_byte_order, &audio_sample_info->block_alignment);
-  Read(read_cb, reverse_byte_order, &audio_sample_info->bits_per_sample);
-  Read(read_cb, reverse_byte_order,
-       &audio_sample_info->audio_specific_config_size);
-  audio_sample_info->stored_audio_specific_config.resize(
-      audio_sample_info->audio_specific_config_size);
-  audio_sample_info->audio_specific_config =
-      audio_sample_info->stored_audio_specific_config.data();
-  Read(read_cb, audio_sample_info->stored_audio_specific_config.data(),
-       audio_sample_info->audio_specific_config_size);
+  media::AudioStreamInfo* audio_stream_info = &audio_sample_info->stream_info;
+
+  Read(read_cb, reverse_byte_order, &audio_stream_info->codec);
+
+  audio_stream_info->mime = "";
+
+  // Skip `format_tag` as it's removed from `SbMediaAudioStreamInfo`.
+  uint16_t format_tag_to_skip;
+  Read(read_cb, reverse_byte_order, &format_tag_to_skip);
+
+  Read(read_cb, reverse_byte_order, &audio_stream_info->number_of_channels);
+  Read(read_cb, reverse_byte_order, &audio_stream_info->samples_per_second);
+
+  // Skip `average_bytes` and `block_alignment` as they're removed from
+  // `SbMediaAudioStreamInfo`.
+  uint32_t average_bytes_per_second_to_skip;
+  Read(read_cb, reverse_byte_order, &average_bytes_per_second_to_skip);
+  uint16_t block_alignment_to_skip;
+  Read(read_cb, reverse_byte_order, &block_alignment_to_skip);
+
+  Read(read_cb, reverse_byte_order, &audio_stream_info->bits_per_sample);
+
+  uint16_t audio_specific_config_size;
+  Read(read_cb, reverse_byte_order, &audio_specific_config_size);
+  audio_stream_info->audio_specific_config.resize(audio_specific_config_size);
+  Read(read_cb, audio_stream_info->audio_specific_config.data(),
+       audio_specific_config_size);
 }
 
 void Write(const WriteCB& write_cb,
            SbMediaAudioCodec audio_codec,
-           const SbMediaAudioSampleInfo& audio_sample_info) {
+           const media::AudioSampleInfo& audio_sample_info) {
+  const auto& audio_stream_info = audio_sample_info.stream_info;
+
   Write(write_cb, audio_codec);
-  Write(write_cb, audio_sample_info.format_tag);
-  Write(write_cb, audio_sample_info.number_of_channels);
-  Write(write_cb, audio_sample_info.samples_per_second);
-  Write(write_cb, audio_sample_info.average_bytes_per_second);
-  Write(write_cb, audio_sample_info.block_alignment);
-  Write(write_cb, audio_sample_info.bits_per_sample);
-  Write(write_cb, audio_sample_info.audio_specific_config_size);
-  Write(write_cb, audio_sample_info.audio_specific_config,
-        audio_sample_info.audio_specific_config_size);
+
+  uint16_t format_tag = 0x00ff;
+  // Write a dummy one to be compatible with the existing format.
+  Write(write_cb, format_tag);
+
+  Write(write_cb, audio_stream_info.number_of_channels);
+  Write(write_cb, audio_stream_info.samples_per_second);
+
+  uint32_t average_bytes_per_second = 1;
+  // Write a dummy one to be compatible with the existing format.
+  Write(write_cb, average_bytes_per_second);
+  uint16_t block_alignment = 4;
+  // Write a dummy one to be compatible with the existing format.
+  Write(write_cb, block_alignment);
+
+  Write(write_cb, audio_stream_info.bits_per_sample);
+
+  uint16_t audio_specific_config_size =
+      static_cast<uint16_t>(audio_stream_info.audio_specific_config.size());
+  Write(write_cb, audio_specific_config_size);
+  Write(write_cb, audio_stream_info.audio_specific_config.data(),
+        audio_stream_info.audio_specific_config.size());
 }
 
 void Read(const ReadCB& read_cb,
@@ -165,17 +192,19 @@
 
 void Read(const ReadCB& read_cb,
           bool reverse_byte_order,
-          SbMediaVideoSampleInfoWithOptionalColorMetadata* video_sample_info) {
-  Read(read_cb, reverse_byte_order, &video_sample_info->codec);
+          media::VideoSampleInfo* video_sample_info) {
+  SB_DCHECK(video_sample_info);
 
-  video_sample_info->mime = "";
-  video_sample_info->max_video_capabilities = "";
+  *video_sample_info = media::VideoSampleInfo();
+  media::VideoStreamInfo* video_stream_info = &video_sample_info->stream_info;
+
+  Read(read_cb, reverse_byte_order, &video_stream_info->codec);
 
   Read(read_cb, reverse_byte_order, &video_sample_info->is_key_frame);
-  Read(read_cb, reverse_byte_order, &video_sample_info->frame_width);
-  Read(read_cb, reverse_byte_order, &video_sample_info->frame_height);
+  Read(read_cb, reverse_byte_order, &video_stream_info->frame_width);
+  Read(read_cb, reverse_byte_order, &video_stream_info->frame_height);
 
-  auto& color_metadata = video_sample_info->color_metadata;
+  auto& color_metadata = video_stream_info->color_metadata;
 
   Read(read_cb, reverse_byte_order, &color_metadata.bits_per_channel);
   Read(read_cb, reverse_byte_order,
@@ -220,13 +249,15 @@
 
 void Write(const WriteCB& write_cb,
            SbMediaVideoCodec video_codec,
-           const SbMediaVideoSampleInfo& video_sample_info) {
+           const media::VideoSampleInfo& video_sample_info) {
+  const auto& video_stream_info = video_sample_info.stream_info;
+
   Write(write_cb, video_codec);
   Write(write_cb, video_sample_info.is_key_frame);
-  Write(write_cb, video_sample_info.frame_width);
-  Write(write_cb, video_sample_info.frame_height);
+  Write(write_cb, video_stream_info.frame_width);
+  Write(write_cb, video_stream_info.frame_height);
 
-  auto& color_metadata = video_sample_info.color_metadata;
+  const auto& color_metadata = video_stream_info.color_metadata;
 
   Write(write_cb, color_metadata.bits_per_channel);
   Write(write_cb, color_metadata.chroma_subsampling_horizontal);
diff --git a/starboard/shared/starboard/player/video_dmp_common.h b/starboard/shared/starboard/player/video_dmp_common.h
index 8d357e0..1a350ba 100644
--- a/starboard/shared/starboard/player/video_dmp_common.h
+++ b/starboard/shared/starboard/player/video_dmp_common.h
@@ -17,12 +17,14 @@
 
 #include <algorithm>
 #include <functional>
+#include <string>
 #include <vector>
 
 #include "starboard/common/log.h"
 #include "starboard/media.h"
 #include "starboard/memory.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/media_util.h"
 
 namespace starboard {
 namespace shared {
@@ -68,25 +70,6 @@
   kRecordTypeVideoAccessUnit = 'vdat',
 };
 
-// Helper structures to allow returning structs containing pointers without
-// explicit memory management.  Use our own structure instead of the one defined
-// in media_util.* to ensure that video_dmp has minimum dependency.
-struct SbMediaAudioSampleInfoWithConfig : public SbMediaAudioSampleInfo {
-  SbMediaAudioSampleInfoWithConfig() {}
-  SbMediaAudioSampleInfoWithConfig(const SbMediaAudioSampleInfoWithConfig& that)
-      : SbMediaAudioSampleInfo(that),
-        stored_audio_specific_config(that.stored_audio_specific_config) {
-    audio_specific_config = stored_audio_specific_config.data();
-  }
-  void operator=(const SbMediaAudioSampleInfoWithConfig& that) {
-    memcpy(this, &that, sizeof(SbMediaAudioSampleInfo));
-    stored_audio_specific_config = that.stored_audio_specific_config;
-    audio_specific_config = stored_audio_specific_config.data();
-  }
-
-  std::vector<uint8_t> stored_audio_specific_config;
-};
-
 struct SbDrmSampleInfoWithSubSampleMapping : public SbDrmSampleInfo {
   SbDrmSampleInfoWithSubSampleMapping() {}
   SbDrmSampleInfoWithSubSampleMapping(
@@ -100,21 +83,6 @@
   std::vector<SbDrmSubSampleMapping> stored_subsample_mapping;
 };
 
-struct SbMediaVideoSampleInfoWithOptionalColorMetadata
-    : public SbMediaVideoSampleInfo {
-  SbMediaVideoSampleInfoWithOptionalColorMetadata() {}
-  SbMediaVideoSampleInfoWithOptionalColorMetadata(
-      const SbMediaVideoSampleInfoWithOptionalColorMetadata& that)
-      : SbMediaVideoSampleInfo(that),
-        stored_color_metadata(that.stored_color_metadata) {
-  }
-  void operator=(const SbMediaVideoSampleInfoWithOptionalColorMetadata& that) {
-    memcpy(this, &that, sizeof(SbMediaVideoSampleInfo));
-  }
-
-  SbMediaColorMetadata stored_color_metadata;
-};
-
 const uint32_t kByteOrderMark = 0x76543210;
 const uint32_t kSupportedWriterVersion = 0x00001000;
 
@@ -150,10 +118,10 @@
 
 void Read(const ReadCB& read_cb,
           bool reverse_byte_order,
-          SbMediaAudioSampleInfoWithConfig* audio_sample_info);
+          media::AudioSampleInfo* audio_sample_info);
 void Write(const WriteCB& write_cb,
            SbMediaAudioCodec audio_codec,
-           const SbMediaAudioSampleInfo& audio_sample_info);
+           const media::AudioSampleInfo& audio_sample_info);
 
 void Read(const ReadCB& read_cb,
           bool reverse_byte_order,
@@ -162,10 +130,10 @@
 
 void Read(const ReadCB& read_cb,
           bool reverse_byte_order,
-          SbMediaVideoSampleInfoWithOptionalColorMetadata* video_sample_info);
+          media::VideoSampleInfo* video_sample_info);
 void Write(const WriteCB& write_cb,
            SbMediaVideoCodec video_codec,
-           const SbMediaVideoSampleInfo& video_sample_info);
+           const media::VideoSampleInfo& video_sample_info);
 
 }  // namespace video_dmp
 }  // namespace player
diff --git a/starboard/shared/starboard/player/video_dmp_reader.cc b/starboard/shared/starboard/player/video_dmp_reader.cc
index 07f748d..aac8eb0 100644
--- a/starboard/shared/starboard/player/video_dmp_reader.cc
+++ b/starboard/shared/starboard/player/video_dmp_reader.cc
@@ -61,7 +61,7 @@
   sample_info.timestamp = audio_unit.timestamp();
   sample_info.drm_info = audio_unit.drm_sample_info();
   sample_info.type = kSbMediaTypeAudio;
-  sample_info.audio_sample_info = audio_unit.audio_sample_info();
+  audio_unit.audio_sample_info().ConvertTo(&sample_info.audio_sample_info);
   return sample_info;
 }
 
@@ -73,7 +73,7 @@
   sample_info.timestamp = video_unit.timestamp();
   sample_info.drm_info = video_unit.drm_sample_info();
   sample_info.type = kSbMediaTypeVideo;
-  sample_info.video_sample_info = video_unit.video_sample_info();
+  video_unit.video_sample_info().ConvertTo(&sample_info.video_sample_info);
   return sample_info;
 }
 
@@ -152,7 +152,9 @@
     default:
       SB_NOTREACHED();
   }
-  ss << " channels=" << dmp_info_.audio_sample_info.number_of_channels;
+
+  ss << " channels="
+     << dmp_info_.audio_sample_info.stream_info.number_of_channels;
   return ss.str();
 }
 
@@ -176,10 +178,9 @@
       SB_NOTREACHED();
   }
   if (number_of_video_buffers() > 0) {
-    const auto& video_sample_info =
-        GetPlayerSampleInfo(kSbMediaTypeVideo, 0).video_sample_info;
-    ss << "width=" << video_sample_info.frame_width
-       << "; height=" << video_sample_info.frame_height << ";";
+    const auto& video_stream_info = this->video_stream_info();
+    ss << "width=" << video_stream_info.frame_width
+       << "; height=" << video_stream_info.frame_height << ";";
   }
   ss << " framerate=" << dmp_info_.video_fps;
   return ss.str();
@@ -205,7 +206,7 @@
   return SbPlayerSampleInfo();
 }
 
-const SbMediaAudioSampleInfo& VideoDmpReader::GetAudioSampleInfo(size_t index) {
+const media::AudioSampleInfo& VideoDmpReader::GetAudioSampleInfo(size_t index) {
   EnsureSampleLoaded(kSbMediaTypeAudio, index);
 
   SB_DCHECK(index < audio_access_units_.size());
@@ -251,6 +252,8 @@
       if (dmp_info_.audio_codec != kSbMediaAudioCodecNone) {
         Read(read_cb_, reverse_byte_order_.value(),
              &dmp_info_.audio_sample_info);
+        SB_DCHECK(dmp_info_.audio_codec ==
+                  dmp_info_.audio_sample_info.stream_info.codec);
       }
       break;
     case kRecordTypeVideoConfig:
@@ -363,12 +366,12 @@
   std::vector<uint8_t> data(size);
   Read(read_cb_, data.data(), size);
 
-  SbMediaAudioSampleInfoWithConfig audio_sample_info;
+  media::AudioSampleInfo audio_sample_info;
   Read(read_cb_, reverse_byte_order_.value(), &audio_sample_info);
 
   return AudioAccessUnit(timestamp,
                          drm_sample_info_present ? &drm_sample_info : NULL,
-                         std::move(data), audio_sample_info);
+                         std::move(data), std::move(audio_sample_info));
 }
 
 VideoDmpReader::VideoAccessUnit VideoDmpReader::ReadVideoAccessUnit() {
@@ -388,12 +391,12 @@
   std::vector<uint8_t> data(size);
   Read(read_cb_, data.data(), size);
 
-  SbMediaVideoSampleInfoWithOptionalColorMetadata video_sample_info;
+  media::VideoSampleInfo video_sample_info;
   Read(read_cb_, reverse_byte_order_.value(), &video_sample_info);
 
   return VideoAccessUnit(timestamp,
                          drm_sample_info_present ? &drm_sample_info : NULL,
-                         std::move(data), video_sample_info);
+                         std::move(data), std::move(video_sample_info));
 }
 
 // static
diff --git a/starboard/shared/starboard/player/video_dmp_reader.h b/starboard/shared/starboard/player/video_dmp_reader.h
index eb99fbc..7c2a2e2 100644
--- a/starboard/shared/starboard/player/video_dmp_reader.h
+++ b/starboard/shared/starboard/player/video_dmp_reader.h
@@ -28,6 +28,7 @@
 #include "starboard/media.h"
 #include "starboard/player.h"
 #include "starboard/shared/internal_only.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/file_cache_reader.h"
 #include "starboard/shared/starboard/player/video_dmp_common.h"
 
@@ -73,15 +74,15 @@
     AudioAccessUnit(SbTime timestamp,
                     const SbDrmSampleInfoWithSubSampleMapping* drm_sample_info,
                     std::vector<uint8_t> data,
-                    const SbMediaAudioSampleInfoWithConfig& audio_sample_info)
+                    media::AudioSampleInfo audio_sample_info)
         : AccessUnit(timestamp, drm_sample_info, std::move(data)),
-          audio_sample_info_(audio_sample_info) {}
-    const SbMediaAudioSampleInfo& audio_sample_info() const {
+          audio_sample_info_(std::move(audio_sample_info)) {}
+    const media::AudioSampleInfo& audio_sample_info() const {
       return audio_sample_info_;
     }
 
    private:
-    SbMediaAudioSampleInfoWithConfig audio_sample_info_;
+    media::AudioSampleInfo audio_sample_info_;
   };
 
   class VideoAccessUnit : public AccessUnit {
@@ -89,16 +90,15 @@
     VideoAccessUnit(SbTime timestamp,
                     const SbDrmSampleInfoWithSubSampleMapping* drm_sample_info,
                     std::vector<uint8_t> data,
-                    const SbMediaVideoSampleInfoWithOptionalColorMetadata&
-                        video_sample_info)
+                    media::VideoSampleInfo video_sample_info)
         : AccessUnit(timestamp, drm_sample_info, std::move(data)),
-          video_sample_info_(video_sample_info) {}
-    const SbMediaVideoSampleInfo& video_sample_info() const {
+          video_sample_info_(std::move(video_sample_info)) {}
+    const media::VideoSampleInfo& video_sample_info() const {
       return video_sample_info_;
     }
 
    private:
-    SbMediaVideoSampleInfoWithOptionalColorMetadata video_sample_info_;
+    media::VideoSampleInfo video_sample_info_;
   };
 
   explicit VideoDmpReader(
@@ -107,8 +107,13 @@
   ~VideoDmpReader();
 
   SbMediaAudioCodec audio_codec() const { return dmp_info_.audio_codec; }
-  const SbMediaAudioSampleInfo& audio_sample_info() const {
-    return dmp_info_.audio_sample_info;
+  const media::AudioStreamInfo& audio_stream_info() const {
+    return dmp_info_.audio_sample_info.stream_info;
+  }
+  const media::VideoStreamInfo& video_stream_info() {
+    EnsureSampleLoaded(kSbMediaTypeVideo, 0);
+    SB_DCHECK(!video_access_units_.empty());
+    return video_access_units_[0].video_sample_info().stream_info;
   }
   int64_t audio_bitrate() const { return dmp_info_.audio_bitrate; }
   std::string audio_mime_type() const;
@@ -128,12 +133,12 @@
   }
 
   SbPlayerSampleInfo GetPlayerSampleInfo(SbMediaType type, size_t index);
-  const SbMediaAudioSampleInfo& GetAudioSampleInfo(size_t index);
+  const media::AudioSampleInfo& GetAudioSampleInfo(size_t index);
 
  private:
   struct DmpInfo {
     SbMediaAudioCodec audio_codec = kSbMediaAudioCodecNone;
-    SbMediaAudioSampleInfoWithConfig audio_sample_info;
+    media::AudioSampleInfo audio_sample_info;
     size_t audio_access_units_size = 0;
     int64_t audio_bitrate = 0;
     int audio_duration = 0;
diff --git a/starboard/shared/starboard/player/video_dmp_writer.cc b/starboard/shared/starboard/player/video_dmp_writer.cc
index 68f62a5..11d53ed 100644
--- a/starboard/shared/starboard/player/video_dmp_writer.cc
+++ b/starboard/shared/starboard/player/video_dmp_writer.cc
@@ -141,7 +141,7 @@
   Write(write_cb_, audio_codec);
   if (audio_codec != kSbMediaAudioCodecNone) {
     SB_DCHECK(audio_sample_info);
-    Write(write_cb_, audio_codec, *audio_sample_info);
+    Write(write_cb_, audio_codec, media::AudioSampleInfo(*audio_sample_info));
   }
 
   Write(write_cb_, kRecordTypeVideoConfig);
@@ -177,14 +177,12 @@
   Write(write_cb_, sample_buffer, static_cast<size_t>(sample_buffer_size));
 
   if (sample_type == kSbMediaTypeAudio) {
-    const SbMediaAudioSampleInfo& audio_sample_info =
-        input_buffer->audio_sample_info();
-    Write(write_cb_, audio_sample_info.codec, audio_sample_info);
+    Write(write_cb_, input_buffer->audio_stream_info().codec,
+          input_buffer->audio_sample_info());
   } else {
     SB_DCHECK(sample_type == kSbMediaTypeVideo);
-    const SbMediaVideoSampleInfo& video_sample_info =
-        input_buffer->video_sample_info();
-    Write(write_cb_, video_sample_info.codec, video_sample_info);
+    Write(write_cb_, input_buffer->video_stream_info().codec,
+          input_buffer->video_sample_info());
   }
 }
 
diff --git a/starboard/shared/starboard/queue_application.h b/starboard/shared/starboard/queue_application.h
index 8f9ffb7..2e99c35 100644
--- a/starboard/shared/starboard/queue_application.h
+++ b/starboard/shared/starboard/queue_application.h
@@ -35,7 +35,12 @@
 // manage event dispatching.
 class QueueApplication : public Application {
  public:
+#if SB_MODULAR_BUILD
+  explicit QueueApplication(SbEventHandleCallback sb_event_handle_callback)
+      : Application(sb_event_handle_callback) {}
+#else
   QueueApplication() {}
+#endif  // SB_MODULAR_BUILD
   ~QueueApplication() override {}
 
  protected:
diff --git a/starboard/shared/starboard/speech_recognizer/speech_recognizer_cancel.cc b/starboard/shared/starboard/speech_recognizer/speech_recognizer_cancel.cc
deleted file mode 100644
index f87ee9a..0000000
--- a/starboard/shared/starboard/speech_recognizer/speech_recognizer_cancel.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/speech_recognizer.h"
-
-#include "starboard/common/log.h"
-#include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h"
-
-void SbSpeechRecognizerCancel(SbSpeechRecognizer recognizer) {
-  if (SbSpeechRecognizerIsValid(recognizer)) {
-    recognizer->Cancel();
-  }
-}
diff --git a/starboard/shared/starboard/speech_recognizer/speech_recognizer_create.cc b/starboard/shared/starboard/speech_recognizer/speech_recognizer_create.cc
deleted file mode 100644
index 217d166..0000000
--- a/starboard/shared/starboard/speech_recognizer/speech_recognizer_create.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/speech_recognizer.h"
-
-#if SB_API_VERSION >= 13
-#error Speech Recognizer is deprecated. Implement full Microphone instead.
-#endif
-
-#include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h"
-
-SbSpeechRecognizer SbSpeechRecognizerCreate(
-    const SbSpeechRecognizerHandler* handler) {
-  return SbSpeechRecognizerPrivate::CreateSpeechRecognizer(handler);
-}
diff --git a/starboard/shared/starboard/speech_recognizer/speech_recognizer_destroy.cc b/starboard/shared/starboard/speech_recognizer/speech_recognizer_destroy.cc
deleted file mode 100644
index a8f3b03..0000000
--- a/starboard/shared/starboard/speech_recognizer/speech_recognizer_destroy.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/speech_recognizer.h"
-
-#include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h"
-
-void SbSpeechRecognizerDestroy(SbSpeechRecognizer recognizer) {
-  if (SbSpeechRecognizerIsValid(recognizer)) {
-    SbSpeechRecognizerPrivate::DestroySpeechRecognizer(recognizer);
-  }
-}
diff --git a/starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h b/starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h
deleted file mode 100644
index 04624fb..0000000
--- a/starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef STARBOARD_SHARED_STARBOARD_SPEECH_RECOGNIZER_SPEECH_RECOGNIZER_INTERNAL_H_
-#define STARBOARD_SHARED_STARBOARD_SPEECH_RECOGNIZER_SPEECH_RECOGNIZER_INTERNAL_H_
-
-#include "starboard/shared/internal_only.h"
-#include "starboard/speech_recognizer.h"
-
-struct SbSpeechRecognizerPrivate {
-  virtual ~SbSpeechRecognizerPrivate() {}
-  virtual bool Start(const SbSpeechConfiguration* configuration) = 0;
-  virtual void Stop() = 0;
-  virtual void Cancel() = 0;
-  static SbSpeechRecognizer CreateSpeechRecognizer(
-      const SbSpeechRecognizerHandler* handler);
-  static void DestroySpeechRecognizer(SbSpeechRecognizer speech_recognizer);
-};
-
-#endif  // STARBOARD_SHARED_STARBOARD_SPEECH_RECOGNIZER_SPEECH_RECOGNIZER_INTERNAL_H_
diff --git a/starboard/shared/starboard/speech_recognizer/speech_recognizer_is_supported.cc b/starboard/shared/starboard/speech_recognizer/speech_recognizer_is_supported.cc
deleted file mode 100644
index a2f82c1..0000000
--- a/starboard/shared/starboard/speech_recognizer/speech_recognizer_is_supported.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2019 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/speech_recognizer.h"
-
-bool SbSpeechRecognizerIsSupported() {
-  return true;
-}
diff --git a/starboard/shared/starboard/speech_recognizer/speech_recognizer_start.cc b/starboard/shared/starboard/speech_recognizer/speech_recognizer_start.cc
deleted file mode 100644
index 7282267..0000000
--- a/starboard/shared/starboard/speech_recognizer/speech_recognizer_start.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/speech_recognizer.h"
-
-#include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h"
-
-bool SbSpeechRecognizerStart(SbSpeechRecognizer recognizer,
-                             const SbSpeechConfiguration* configuration) {
-  return SbSpeechRecognizerIsValid(recognizer)
-             ? recognizer->Start(configuration)
-             : false;
-}
diff --git a/starboard/shared/starboard/speech_recognizer/speech_recognizer_stop.cc b/starboard/shared/starboard/speech_recognizer/speech_recognizer_stop.cc
deleted file mode 100644
index f03c234..0000000
--- a/starboard/shared/starboard/speech_recognizer/speech_recognizer_stop.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/speech_recognizer.h"
-
-#include "starboard/common/log.h"
-#include "starboard/shared/starboard/speech_recognizer/speech_recognizer_internal.h"
-
-void SbSpeechRecognizerStop(SbSpeechRecognizer recognizer) {
-  if (SbSpeechRecognizerIsValid(recognizer)) {
-    recognizer->Stop();
-  }
-}
diff --git a/starboard/shared/stub/player_get_info2.cc b/starboard/shared/stub/player_get_info.cc
similarity index 73%
rename from starboard/shared/stub/player_get_info2.cc
rename to starboard/shared/stub/player_get_info.cc
index 4f5dae8..cba06b8 100644
--- a/starboard/shared/stub/player_get_info2.cc
+++ b/starboard/shared/stub/player_get_info.cc
@@ -14,4 +14,8 @@
 
 #include "starboard/player.h"
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+void SbPlayerGetInfo(SbPlayer player, SbPlayerInfo* out_player_info) {}
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 void SbPlayerGetInfo2(SbPlayer player, SbPlayerInfo2* out_player_info) {}
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
diff --git a/starboard/shared/stub/player_seek2.cc b/starboard/shared/stub/player_seek.cc
similarity index 73%
rename from starboard/shared/stub/player_seek2.cc
rename to starboard/shared/stub/player_seek.cc
index b931802..9733f86 100644
--- a/starboard/shared/stub/player_seek2.cc
+++ b/starboard/shared/stub/player_seek.cc
@@ -14,4 +14,8 @@
 
 #include "starboard/player.h"
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+void SbPlayerSeek(SbPlayer player, SbTime seek_to_timestamp, int ticket) {}
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 void SbPlayerSeek2(SbPlayer player, SbTime seek_to_timestamp, int ticket) {}
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
diff --git a/starboard/shared/stub/player_write_sample2.cc b/starboard/shared/stub/player_write_samples.cc
similarity index 73%
rename from starboard/shared/stub/player_write_sample2.cc
rename to starboard/shared/stub/player_write_samples.cc
index 50ec297..773ca88 100644
--- a/starboard/shared/stub/player_write_sample2.cc
+++ b/starboard/shared/stub/player_write_samples.cc
@@ -14,7 +14,12 @@
 
 #include "starboard/player.h"
 
+#if SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+void SbPlayerWriteSamples(SbPlayer player,
+#else   // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
 void SbPlayerWriteSample2(SbPlayer player,
+#endif  // SB_API_VERSION >= SB_MEDIA_ENHANCED_AUDIO_API_VERSION
                           SbMediaType sample_type,
                           const SbPlayerSampleInfo* sample_infos,
-                          int number_of_sample_infos) {}
+                          int number_of_sample_infos) {
+}
diff --git a/starboard/shared/widevine/drm_system_widevine.h b/starboard/shared/widevine/drm_system_widevine.h
index e67ca30..673c7c4 100644
--- a/starboard/shared/widevine/drm_system_widevine.h
+++ b/starboard/shared/widevine/drm_system_widevine.h
@@ -20,7 +20,7 @@
 #include <string>
 #include <vector>
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/optional.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/mutex.h"
diff --git a/starboard/shared/win32/application_win32.cc b/starboard/shared/win32/application_win32.cc
index b816897..cb2d4bb 100644
--- a/starboard/shared/win32/application_win32.cc
+++ b/starboard/shared/win32/application_win32.cc
@@ -119,8 +119,15 @@
 namespace shared {
 namespace win32 {
 
+#if SB_MODULAR_BUILD
+ApplicationWin32::ApplicationWin32(
+    SbEventHandleCallback sb_event_handle_callback)
+    : localized_strings_(SbSystemGetLocaleId()),
+      QueueApplication(sb_event_handle_callback) {}
+#else
 ApplicationWin32::ApplicationWin32()
     : localized_strings_(SbSystemGetLocaleId()) {}
+#endif  // SB_MODULAR_BUILD
 ApplicationWin32::~ApplicationWin32() {}
 
 SbWindow ApplicationWin32::CreateWindowForWin32(
diff --git a/starboard/shared/win32/application_win32.h b/starboard/shared/win32/application_win32.h
index 380c9f0..63eca74 100644
--- a/starboard/shared/win32/application_win32.h
+++ b/starboard/shared/win32/application_win32.h
@@ -36,7 +36,11 @@
 
 class ApplicationWin32 : public starboard::QueueApplication {
  public:
+#if SB_MODULAR_BUILD
+  explicit ApplicationWin32(SbEventHandleCallback sb_event_handle_callback);
+#else
   ApplicationWin32();
+#endif  // SB_MODULAR_BUILD
   ~ApplicationWin32() override;
 
   static ApplicationWin32* Get() {
@@ -52,10 +56,8 @@
   void DispatchStart(SbTimeMonotonic timestamp) {
     shared::starboard::Application::DispatchStart(timestamp);
   }
-#else  // SB_API_VERSION >= 13
-void DispatchStart() {
-    shared::starboard::Application::DispatchStart();
-  }
+#else   // SB_API_VERSION >= 13
+  void DispatchStart() { shared::starboard::Application::DispatchStart(); }
 #endif  // SB_API_VERSION >= 13
 
   SbWindow GetCoreWindow() {
diff --git a/starboard/shared/win32/audio_decoder.cc b/starboard/shared/win32/audio_decoder.cc
index fbcfe44..27d84e1 100644
--- a/starboard/shared/win32/audio_decoder.cc
+++ b/starboard/shared/win32/audio_decoder.cc
@@ -55,14 +55,12 @@
   bool callback_signaled_;
 };
 
-AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec,
-                           const SbMediaAudioSampleInfo& audio_sample_info,
+AudioDecoder::AudioDecoder(const AudioStreamInfo& audio_stream_info,
                            SbDrmSystem drm_system)
-    : audio_codec_(audio_codec),
-      audio_sample_info_(audio_sample_info),
+    : audio_stream_info_(audio_stream_info),
       drm_system_(drm_system),
-      sample_type_((audio_codec == kSbMediaAudioCodecAc3 ||
-                    audio_codec == kSbMediaAudioCodecEac3)
+      sample_type_((audio_stream_info.codec == kSbMediaAudioCodecAc3 ||
+                    audio_stream_info.codec == kSbMediaAudioCodecEac3)
                        ?
 #if SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
                        kSbMediaAudioSampleTypeInt16
@@ -71,9 +69,9 @@
 #endif  // SB_HAS_QUIRK(SUPPORT_INT16_AUDIO_SAMPLES)
                        : kSbMediaAudioSampleTypeFloat32),
       stream_ended_(false) {
-  SB_DCHECK(audio_codec == kSbMediaAudioCodecAac ||
-            audio_codec == kSbMediaAudioCodecAc3 ||
-            audio_codec == kSbMediaAudioCodecEac3);
+  SB_DCHECK(audio_stream_info.codec == kSbMediaAudioCodecAac ||
+            audio_stream_info.codec == kSbMediaAudioCodecAc3 ||
+            audio_stream_info.codec == kSbMediaAudioCodecEac3);
 }
 
 AudioDecoder::~AudioDecoder() {
@@ -92,8 +90,8 @@
   SB_DCHECK(!output_cb_);
   output_cb_ = output_cb;
   decoder_impl_ = AbstractWin32AudioDecoder::Create(
-      audio_codec_, kSbMediaAudioFrameStorageTypeInterleaved, sample_type_,
-      audio_sample_info_, drm_system_);
+      kSbMediaAudioFrameStorageTypeInterleaved, sample_type_,
+      audio_stream_info_, drm_system_);
   decoder_thread_.reset(new AudioDecoderThread(decoder_impl_.get(), this));
   callback_scheduler_.reset(new CallbackScheduler());
 }
diff --git a/starboard/shared/win32/audio_decoder.h b/starboard/shared/win32/audio_decoder.h
index 0467b47..e354e6c 100644
--- a/starboard/shared/win32/audio_decoder.h
+++ b/starboard/shared/win32/audio_decoder.h
@@ -39,8 +39,9 @@
       private ::starboard::shared::starboard::player::JobQueue::JobOwner,
       private AudioDecodedCallback {
  public:
-  AudioDecoder(SbMediaAudioCodec audio_codec,
-               const SbMediaAudioSampleInfo& audio_sample_info,
+  typedef starboard::media::AudioStreamInfo AudioStreamInfo;
+
+  AudioDecoder(const AudioStreamInfo& audio_stream_info,
                SbDrmSystem drm_system);
   ~AudioDecoder() override;
 
@@ -57,8 +58,7 @@
 
   ::starboard::shared::starboard::ThreadChecker thread_checker_;
 
-  const SbMediaAudioCodec audio_codec_;
-  const starboard::media::AudioSampleInfo audio_sample_info_;
+  const AudioStreamInfo audio_stream_info_;
   SbDrmSystem const drm_system_;
   const SbMediaAudioSampleType sample_type_;
   bool stream_ended_;
diff --git a/starboard/shared/win32/audio_decoder_thread.h b/starboard/shared/win32/audio_decoder_thread.h
index 45f0d42..44e4fcd 100644
--- a/starboard/shared/win32/audio_decoder_thread.h
+++ b/starboard/shared/win32/audio_decoder_thread.h
@@ -18,6 +18,7 @@
 #include <deque>
 #include <queue>
 
+#include "starboard/common/atomic.h"
 #include "starboard/common/ref_counted.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/common/semaphore.h"
diff --git a/starboard/shared/win32/audio_transform.cc b/starboard/shared/win32/audio_transform.cc
index 6e1edbb..39c9de8 100644
--- a/starboard/shared/win32/audio_transform.cc
+++ b/starboard/shared/win32/audio_transform.cc
@@ -29,6 +29,7 @@
 namespace win32 {
 
 using Microsoft::WRL::ComPtr;
+using starboard::media::AudioStreamInfo;
 
 namespace {
 
@@ -94,69 +95,69 @@
 
 class WinAudioFormat {
  public:
-  explicit WinAudioFormat(const SbMediaAudioSampleInfo& audio_sample_info) {
-    if (audio_sample_info.codec == kSbMediaAudioCodecAac) {
-      CreateAacAudioFormat(audio_sample_info);
+  explicit WinAudioFormat(const AudioStreamInfo& audio_stream_info) {
+    if (audio_stream_info.codec == kSbMediaAudioCodecAac) {
+      CreateAacAudioFormat(audio_stream_info);
     } else {
-      SB_DCHECK(audio_sample_info.codec == kSbMediaAudioCodecAc3 ||
-                audio_sample_info.codec == kSbMediaAudioCodecEac3);
-      CreateAc3AudioFormat(audio_sample_info);
+      SB_DCHECK(audio_stream_info.codec == kSbMediaAudioCodecAc3 ||
+                audio_stream_info.codec == kSbMediaAudioCodecEac3);
+      CreateAc3AudioFormat(audio_stream_info);
     }
   }
 
-  void CreateAacAudioFormat(const SbMediaAudioSampleInfo& audio_sample_info) {
+  void CreateAacAudioFormat(const AudioStreamInfo& audio_stream_info) {
     // The HEAACWAVEFORMAT structure has many specializations with varying data
     // appended at the end.
     // The "-1" is used to account for pbAudioSpecificConfig[1] at the end of
     // HEAACWAVEFORMAT.
     format_buffer_.resize(sizeof(HEAACWAVEFORMAT) +
-                          audio_sample_info.audio_specific_config_size - 1);
+                          audio_stream_info.audio_specific_config.size() - 1);
     HEAACWAVEFORMAT* wave_format =
         reinterpret_cast<HEAACWAVEFORMAT*>(format_buffer_.data());
 
     wave_format->wfInfo.wfx.nAvgBytesPerSec = 0;
-    wave_format->wfInfo.wfx.nBlockAlign = audio_sample_info.block_alignment;
-    wave_format->wfInfo.wfx.nChannels = audio_sample_info.number_of_channels;
+    wave_format->wfInfo.wfx.nBlockAlign = 4;
+    wave_format->wfInfo.wfx.nChannels = audio_stream_info.number_of_channels;
     wave_format->wfInfo.wfx.nSamplesPerSec =
-        audio_sample_info.samples_per_second;
-    wave_format->wfInfo.wfx.wBitsPerSample = audio_sample_info.bits_per_sample;
+        audio_stream_info.samples_per_second;
+    wave_format->wfInfo.wfx.wBitsPerSample = audio_stream_info.bits_per_sample;
     wave_format->wfInfo.wfx.wFormatTag = WAVE_FORMAT_MPEG_HEAAC;
     // The "-1" is used to account for pbAudioSpecificConfig[1] at the end of
     // HEAACWAVEFORMAT.
     wave_format->wfInfo.wfx.cbSize =
-        sizeof(HEAACWAVEFORMAT) - sizeof(WAVEFORMATEX) +
-        audio_sample_info.audio_specific_config_size - 1;
+        static_cast<WORD>(sizeof(HEAACWAVEFORMAT) - sizeof(WAVEFORMATEX) +
+                          audio_stream_info.audio_specific_config.size() - 1);
 
     wave_format->wfInfo.wPayloadType = 0;                     // RAW
     wave_format->wfInfo.wAudioProfileLevelIndication = 0xfe;  // Unknown Profile
     wave_format->wfInfo.wStructType = 0;  // AudioSpecificConfig()
 
-    if (audio_sample_info.audio_specific_config_size > 0) {
+    if (!audio_stream_info.audio_specific_config.empty()) {
       memcpy(wave_format->pbAudioSpecificConfig,
-             audio_sample_info.audio_specific_config,
-             audio_sample_info.audio_specific_config_size);
+             audio_stream_info.audio_specific_config.data(),
+             audio_stream_info.audio_specific_config.size());
     }
   }
 
-  void CreateAc3AudioFormat(const SbMediaAudioSampleInfo& audio_sample_info) {
+  void CreateAc3AudioFormat(const AudioStreamInfo& audio_stream_info) {
     format_buffer_.resize(sizeof(WAVEFORMATEXTENSIBLE));
     WAVEFORMATEXTENSIBLE* wave_format =
         reinterpret_cast<WAVEFORMATEXTENSIBLE*>(format_buffer_.data());
 
     wave_format->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
-    wave_format->Format.nChannels = audio_sample_info.number_of_channels;
-    wave_format->Format.wBitsPerSample = audio_sample_info.bits_per_sample;
-    wave_format->Format.nSamplesPerSec = audio_sample_info.samples_per_second;
-    wave_format->Format.nBlockAlign = audio_sample_info.block_alignment;
+    wave_format->Format.nChannels = audio_stream_info.number_of_channels;
+    wave_format->Format.wBitsPerSample = audio_stream_info.bits_per_sample;
+    wave_format->Format.nSamplesPerSec = audio_stream_info.samples_per_second;
+    wave_format->Format.nBlockAlign = 4;
     wave_format->Format.nAvgBytesPerSec = 0;
     wave_format->Format.cbSize =
         sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
     wave_format->Samples.wValidBitsPerSample =
         wave_format->Format.wBitsPerSample;
-    wave_format->dwChannelMask = audio_sample_info.number_of_channels > 2
+    wave_format->dwChannelMask = audio_stream_info.number_of_channels > 2
                                      ? KSAUDIO_SPEAKER_5POINT1
                                      : KSAUDIO_SPEAKER_STEREO;
-    wave_format->SubFormat = ConvertToWin32AudioCodec(audio_sample_info.codec);
+    wave_format->SubFormat = ConvertToWin32AudioCodec(audio_stream_info.codec);
   }
 
   WAVEFORMATEX* WaveFormatData() {
@@ -171,13 +172,13 @@
 }  // namespace.
 
 scoped_ptr<MediaTransform> CreateAudioTransform(
-    const SbMediaAudioSampleInfo& audio,
-    SbMediaAudioCodec codec) {
-  SB_DCHECK(codec == kSbMediaAudioCodecAac || codec == kSbMediaAudioCodecAc3 ||
-            codec == kSbMediaAudioCodecEac3);
+    const AudioStreamInfo& audio_stream_info) {
+  SB_DCHECK(audio_stream_info.codec == kSbMediaAudioCodecAac ||
+            audio_stream_info.codec == kSbMediaAudioCodecAc3 ||
+            audio_stream_info.codec == kSbMediaAudioCodecEac3);
   ComPtr<IMFTransform> transform;
-  HRESULT hr =
-      CreateDecoderTransform(ConvertToWin32TransformType(codec), &transform);
+  HRESULT hr = CreateDecoderTransform(
+      ConvertToWin32TransformType(audio_stream_info.codec), &transform);
 
   CheckResult(hr);
 
@@ -185,12 +186,12 @@
   hr = MFCreateMediaType(&input_type);
   CheckResult(hr);
 
-  WinAudioFormat audio_fmt(audio);
+  WinAudioFormat audio_fmt(audio_stream_info);
   hr = MFInitMediaTypeFromWaveFormatEx(
       input_type.Get(), audio_fmt.WaveFormatData(), audio_fmt.Size());
   CheckResult(hr);
 
-  GUID win32_audio_type = ConvertToWin32AudioCodec(codec);
+  GUID win32_audio_type = ConvertToWin32AudioCodec(audio_stream_info.codec);
 
   std::vector<ComPtr<IMFMediaType>> available_types =
       GetAllInputMediaTypes(MediaTransform::kStreamId, transform.Get());
@@ -203,7 +204,8 @@
 
   scoped_ptr<MediaTransform> output(new MediaTransform(transform));
   output->SetInputType(selected);
-  output->SetOutputTypeBySubType(ConvertToWin32OutputFormat(codec));
+  output->SetOutputTypeBySubType(
+      ConvertToWin32OutputFormat(audio_stream_info.codec));
 
   return output.Pass();
 }
diff --git a/starboard/shared/win32/audio_transform.h b/starboard/shared/win32/audio_transform.h
index 9087d96..7ce27a5 100644
--- a/starboard/shared/win32/audio_transform.h
+++ b/starboard/shared/win32/audio_transform.h
@@ -17,6 +17,7 @@
 
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/media.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/win32/media_transform.h"
 
 namespace starboard {
@@ -24,8 +25,7 @@
 namespace win32 {
 
 scoped_ptr<MediaTransform> CreateAudioTransform(
-    const SbMediaAudioSampleInfo& audio,
-    SbMediaAudioCodec codec);
+    const starboard::media::AudioStreamInfo& audio_stream_info);
 
 }  // namespace win32
 }  // namespace shared
diff --git a/starboard/shared/win32/dx_context_video_decoder.cc b/starboard/shared/win32/dx_context_video_decoder.cc
index 9f935c8..1ca7403 100644
--- a/starboard/shared/win32/dx_context_video_decoder.cc
+++ b/starboard/shared/win32/dx_context_video_decoder.cc
@@ -47,23 +47,21 @@
 
   intptr_t device;
   query_device(reinterpret_cast<EGLDeviceEXT>(egl_device),
-      EGL_D3D11_DEVICE_ANGLE, &device);
+               EGL_D3D11_DEVICE_ANGLE, &device);
 
-  ID3D11Device* output_dx_device = reinterpret_cast<ID3D11Device*>(device);
-  IMFDXGIDeviceManager* dxgi_device_mgr = nullptr;
+  Microsoft::WRL::ComPtr<ID3D11Device> output_dx_device =
+      reinterpret_cast<ID3D11Device*>(device);
+  Microsoft::WRL::ComPtr<IMFDXGIDeviceManager> dxgi_device_mgr;
 
   UINT token = 0;
   result = MFCreateDXGIDeviceManager(&token, &dxgi_device_mgr);
   SB_DCHECK(result == S_OK);
   SB_DCHECK(dxgi_device_mgr);
 
-  result = dxgi_device_mgr->ResetDevice(output_dx_device, token);
+  result = dxgi_device_mgr->ResetDevice(output_dx_device.Get(), token);
   SB_DCHECK(SUCCEEDED(result));
 
-  HardwareDecoderContext output = {
-    output_dx_device,
-    dxgi_device_mgr
-  };
+  HardwareDecoderContext output = {output_dx_device, dxgi_device_mgr};
   return output;
 }
 
diff --git a/starboard/shared/win32/mutex_acquire.cc b/starboard/shared/win32/mutex_acquire.cc
index b67d6d6..4e4238f 100644
--- a/starboard/shared/win32/mutex_acquire.cc
+++ b/starboard/shared/win32/mutex_acquire.cc
@@ -19,6 +19,9 @@
 #include "starboard/shared/win32/types_internal.h"
 
 SbMutexResult SbMutexAcquire(SbMutex* mutex) {
+  if (!mutex) {
+    return kSbMutexDestroyed;
+  }
   AcquireSRWLockExclusive(SB_WIN32_INTERNAL_MUTEX(mutex));
   return kSbMutexAcquired;
 }
diff --git a/starboard/shared/win32/mutex_acquire_try.cc b/starboard/shared/win32/mutex_acquire_try.cc
index dda6942..391b6cf 100644
--- a/starboard/shared/win32/mutex_acquire_try.cc
+++ b/starboard/shared/win32/mutex_acquire_try.cc
@@ -19,6 +19,9 @@
 #include "starboard/shared/win32/types_internal.h"
 
 SbMutexResult SbMutexAcquireTry(SbMutex* mutex) {
+  if (!mutex) {
+    return kSbMutexDestroyed;
+  }
   bool result = TryAcquireSRWLockExclusive(SB_WIN32_INTERNAL_MUTEX(mutex));
   return result ? kSbMutexAcquired : kSbMutexBusy;
 }
diff --git a/starboard/shared/win32/mutex_release.cc b/starboard/shared/win32/mutex_release.cc
index 34d35e3..72fbe31 100644
--- a/starboard/shared/win32/mutex_release.cc
+++ b/starboard/shared/win32/mutex_release.cc
@@ -19,6 +19,9 @@
 #include "starboard/shared/win32/types_internal.h"
 
 bool SbMutexRelease(SbMutex* mutex) {
+  if (!mutex) {
+    return false;
+  }
   ReleaseSRWLockExclusive(SB_WIN32_INTERNAL_MUTEX(mutex));
   return true;
 }
diff --git a/starboard/shared/win32/player_components_factory.cc b/starboard/shared/win32/player_components_factory.cc
index be024bd..124d6ff 100644
--- a/starboard/shared/win32/player_components_factory.cc
+++ b/starboard/shared/win32/player_components_factory.cc
@@ -18,6 +18,7 @@
 #include "starboard/common/ref_counted.h"
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/shared/opus/opus_audio_decoder.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/filter/adaptive_audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h"
 #include "starboard/shared/starboard/player/filter/audio_renderer_sink_impl.h"
@@ -51,18 +52,18 @@
       SB_DCHECK(audio_decoder);
       SB_DCHECK(audio_renderer_sink);
 
-      auto decoder_creator = [](const SbMediaAudioSampleInfo& audio_sample_info,
+      auto decoder_creator = [](const media::AudioStreamInfo& audio_stream_info,
                                 SbDrmSystem drm_system) {
         using AacAudioDecoderImpl = ::starboard::shared::win32::AudioDecoder;
         using OpusAudioDecoderImpl =
             ::starboard::shared::opus::OpusAudioDecoder;
 
-        if (audio_sample_info.codec == kSbMediaAudioCodecAac) {
-          return scoped_ptr<AudioDecoder>(new AacAudioDecoderImpl(
-              audio_sample_info.codec, audio_sample_info, drm_system));
-        } else if (audio_sample_info.codec == kSbMediaAudioCodecOpus) {
+        if (audio_stream_info.codec == kSbMediaAudioCodecAac) {
           return scoped_ptr<AudioDecoder>(
-              new OpusAudioDecoderImpl(audio_sample_info));
+              new AacAudioDecoderImpl(audio_stream_info, drm_system));
+        } else if (audio_stream_info.codec == kSbMediaAudioCodecOpus) {
+          return scoped_ptr<AudioDecoder>(
+              new OpusAudioDecoderImpl(audio_stream_info));
         } else {
           SB_NOTREACHED();
         }
@@ -70,7 +71,7 @@
       };
 
       audio_decoder->reset(new AdaptiveAudioDecoder(
-          creation_parameters.audio_sample_info(),
+          creation_parameters.audio_stream_info(),
           creation_parameters.drm_system(), decoder_creator));
       audio_renderer_sink->reset(new AudioRendererSinkImpl);
     }
diff --git a/starboard/shared/win32/socket_internal.h b/starboard/shared/win32/socket_internal.h
index 3030ac8..c51eed0 100644
--- a/starboard/shared/win32/socket_internal.h
+++ b/starboard/shared/win32/socket_internal.h
@@ -18,7 +18,7 @@
 #include <WS2tcpip.h>
 #include <winsock2.h>
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/socket.h"
 #include "starboard/shared/internal_only.h"
 #include "starboard/shared/win32/auto_event_handle.h"
diff --git a/starboard/shared/win32/starboard_main.cc b/starboard/shared/win32/starboard_main.cc
index d03f5a8..8c4fdc0 100644
--- a/starboard/shared/win32/starboard_main.cc
+++ b/starboard/shared/win32/starboard_main.cc
@@ -73,6 +73,9 @@
   SB_DCHECK(SUCCEEDED(hr));
 
   WaitForNetLogIfNecessary(CommandLine(argc, argv));
+#if SB_MODULAR_BUILD
+  return SbRunStarboardMain(argc, argv, SbEventHandle);
+#else
   ApplicationWin32 application;
   // This will run the message loop.
   const int main_return_value = application.Run(argc, argv);
@@ -81,4 +84,18 @@
   MFShutdown();
   WSACleanup();
   return main_return_value;
+#endif  // SB_MODULAR_BUILD
 }
+
+#if SB_MODULAR_BUILD
+int SbRunStarboardMain(int argc, char** argv, SbEventHandleCallback callback) {
+  ApplicationWin32 application(callback);
+  // This will run the message loop.
+  const int main_return_value = application.Run(argc, argv);
+  NetLogFlushThenClose();
+
+  MFShutdown();
+  WSACleanup();
+  return main_return_value;
+}
+#endif  // SB_MODULAR_BUILD
diff --git a/starboard/shared/win32/video_decoder.cc b/starboard/shared/win32/video_decoder.cc
index 5b506bd..f53d6b9 100644
--- a/starboard/shared/win32/video_decoder.cc
+++ b/starboard/shared/win32/video_decoder.cc
@@ -286,7 +286,7 @@
   EnsureDecoderThreadRunning();
 
   const auto& input_buffer = input_buffers[0];
-  if (TryUpdateOutputForHdrVideo(input_buffer->video_sample_info())) {
+  if (TryUpdateOutputForHdrVideo(input_buffer->video_stream_info())) {
     ScopedLock lock(thread_lock_);
     thread_events_.emplace_back(
         new Event{Event::kWriteInputBuffer, input_buffer});
diff --git a/starboard/shared/win32/video_decoder.h b/starboard/shared/win32/video_decoder.h
index c8a38e0..0708b43 100644
--- a/starboard/shared/win32/video_decoder.h
+++ b/starboard/shared/win32/video_decoder.h
@@ -26,6 +26,7 @@
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/configuration.h"
 #include "starboard/decode_target.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
 #include "starboard/shared/starboard/thread_checker.h"
 #include "starboard/shared/win32/decrypting_decoder.h"
@@ -65,6 +66,9 @@
   SbDecodeTarget GetCurrentDecodeTarget() override;
 
  private:
+  typedef ::starboard::shared::starboard::media::VideoStreamInfo
+      VideoStreamInfo;
+
   template <typename T>
   using ComPtr = Microsoft::WRL::ComPtr<T>;
 
@@ -91,7 +95,7 @@
   // lost. Otherwise it returns true. For inherited class it also updates HDMI
   // color metadata and sets HDR mode for Angle, if the video stream is HDR
   // stream.
-  virtual bool TryUpdateOutputForHdrVideo(const SbMediaVideoSampleInfo&) {
+  virtual bool TryUpdateOutputForHdrVideo(const VideoStreamInfo& stream_info) {
     return true;
   }
 
diff --git a/starboard/shared/win32/win32_audio_decoder.cc b/starboard/shared/win32/win32_audio_decoder.cc
index c5dcb35..76cc479 100644
--- a/starboard/shared/win32/win32_audio_decoder.cc
+++ b/starboard/shared/win32/win32_audio_decoder.cc
@@ -18,6 +18,7 @@
 #include <queue>
 
 #include "starboard/atomic.h"
+#include "starboard/shared/starboard/player/filter/audio_frame_discarder.h"
 #include "starboard/shared/starboard/thread_checker.h"
 #include "starboard/shared/win32/atomic_queue.h"
 #include "starboard/shared/win32/audio_decoder.h"
@@ -33,6 +34,7 @@
 
 using Microsoft::WRL::ComPtr;
 using ::starboard::shared::starboard::ThreadChecker;
+using ::starboard::shared::starboard::media::AudioStreamInfo;
 using ::starboard::shared::win32::CreateAudioTransform;
 
 const size_t kAacSamplesPerFrame = 1024;
@@ -56,25 +58,24 @@
 
 class AbstractWin32AudioDecoderImpl : public AbstractWin32AudioDecoder {
  public:
-  AbstractWin32AudioDecoderImpl(SbMediaAudioCodec codec,
-                                SbMediaAudioFrameStorageType audio_frame_fmt,
+  AbstractWin32AudioDecoderImpl(SbMediaAudioFrameStorageType audio_frame_fmt,
                                 SbMediaAudioSampleType sample_type,
-                                const SbMediaAudioSampleInfo& audio_sample_info,
+                                const AudioStreamInfo& audio_stream_info,
                                 SbDrmSystem drm_system)
       : thread_checker_(ThreadChecker::kSetThreadIdOnFirstCheck),
-        codec_(codec),
+        codec_(audio_stream_info.codec),
         audio_frame_fmt_(audio_frame_fmt),
         sample_type_(sample_type),
-        number_of_channels_(audio_sample_info.number_of_channels),
+        number_of_channels_(audio_stream_info.number_of_channels),
         heaac_detected_(false),
         expected_buffer_size_(
-            GetExpectedBufferSize(codec,
-                                  audio_sample_info.number_of_channels)) {
+            GetExpectedBufferSize(codec_,
+                                  audio_stream_info.number_of_channels)) {
     scoped_ptr<MediaTransform> audio_decoder =
-        CreateAudioTransform(audio_sample_info, codec_);
+        CreateAudioTransform(audio_stream_info);
     impl_.reset(
         new DecryptingDecoder("audio", audio_decoder.Pass(), drm_system));
-    switch (codec) {
+    switch (codec_) {
       case kSbMediaAudioCodecAc3:
         samples_per_second_ = kAc3SamplesPerSecond;
         number_of_channels_ = kIec60958Channels;
@@ -85,8 +86,8 @@
         break;
       default:
         samples_per_second_ =
-            static_cast<int>(audio_sample_info.samples_per_second);
-        number_of_channels_ = audio_sample_info.number_of_channels;
+            static_cast<int>(audio_stream_info.samples_per_second);
+        number_of_channels_ = audio_stream_info.number_of_channels;
     }
   }
 
@@ -128,14 +129,17 @@
       SB_DCHECK(decoded_data_size == kEac3BufferSize);
     }
 
-    DecodedAudioPtr data_ptr(
-        new DecodedAudio(number_of_channels_, sample_type_, audio_frame_fmt_,
-                         ConvertToSbTime(win32_timestamp), decoded_data_size));
+    DecodedAudioPtr data_ptr(new DecodedAudio(
+        number_of_channels_, sample_type_, audio_frame_fmt_,
+        ConvertToSbTime(win32_timestamp), static_cast<int>(decoded_data_size)));
 
-    std::copy(data, data + data_size, data_ptr->buffer());
-    std::memset(data_ptr->buffer() + data_size, 0,
-                decoded_data_size - data_size);
+    std::copy(data, data + data_size, data_ptr->data());
+    std::memset(data_ptr->data() + data_size, 0, decoded_data_size - data_size);
 
+    if (codec_ == kSbMediaAudioCodecAac) {
+      audio_frame_discarder_.AdjustForDiscardedDurations(GetSamplesPerSecond(),
+                                                         &data_ptr);
+    }
     output_queue_.push(data_ptr);
     media_buffer->Unlock();
   }
@@ -148,7 +152,11 @@
       // the audio decoder is configured to accept raw AAC.  So we have to
       // adjust the data, size, and subsample mapping to skip the ADTS header.
       const int kADTSHeaderSize = 7;
-      return impl_->TryWriteInputBuffer(buff, kADTSHeaderSize);
+      if (impl_->TryWriteInputBuffer(buff, kADTSHeaderSize)) {
+        audio_frame_discarder_.OnInputBuffers({buff});
+        return true;
+      }
+      return false;
     }
     return impl_->TryWriteInputBuffer(buff, 0);
   }
@@ -165,6 +173,9 @@
       }
     }
     output_queue_.push(new DecodedAudio);
+    if (codec_ == kSbMediaAudioCodecAac) {
+      audio_frame_discarder_.OnDecodedAudioEndOfStream();
+    }
   }
 
   scoped_refptr<DecodedAudio> ProcessAndRead() override {
@@ -187,6 +198,7 @@
 
   void Reset() override {
     impl_->Reset();
+    audio_frame_discarder_.Reset();
     std::queue<DecodedAudioPtr> empty;
     output_queue_.swap(empty);
     thread_checker_.Detach();
@@ -213,6 +225,8 @@
   const SbMediaAudioCodec codec_;
   const SbMediaAudioSampleType sample_type_;
   const SbMediaAudioFrameStorageType audio_frame_fmt_;
+
+  starboard::player::filter::AudioFrameDiscarder audio_frame_discarder_;
   scoped_ptr<DecryptingDecoder> impl_;
   std::queue<DecodedAudioPtr> output_queue_;
   uint16_t number_of_channels_;
@@ -224,14 +238,13 @@
 }  // anonymous namespace.
 
 scoped_ptr<AbstractWin32AudioDecoder> AbstractWin32AudioDecoder::Create(
-    SbMediaAudioCodec code,
     SbMediaAudioFrameStorageType audio_frame_fmt,
     SbMediaAudioSampleType sample_type,
-    const SbMediaAudioSampleInfo& audio_sample_info,
+    const AudioStreamInfo& audio_stream_info,
     SbDrmSystem drm_system) {
   return scoped_ptr<AbstractWin32AudioDecoder>(
-      new AbstractWin32AudioDecoderImpl(code, audio_frame_fmt, sample_type,
-                                        audio_sample_info, drm_system));
+      new AbstractWin32AudioDecoderImpl(audio_frame_fmt, sample_type,
+                                        audio_stream_info, drm_system));
 }
 
 }  // namespace win32
diff --git a/starboard/shared/win32/win32_audio_decoder.h b/starboard/shared/win32/win32_audio_decoder.h
index c174310..697e9a0 100644
--- a/starboard/shared/win32/win32_audio_decoder.h
+++ b/starboard/shared/win32/win32_audio_decoder.h
@@ -21,6 +21,7 @@
 #include "starboard/common/scoped_ptr.h"
 #include "starboard/drm.h"
 #include "starboard/media.h"
+#include "starboard/shared/starboard/media/media_util.h"
 #include "starboard/shared/starboard/player/decoded_audio_internal.h"
 #include "starboard/shared/win32/media_common.h"
 #include "starboard/types.h"
@@ -33,10 +34,9 @@
 class AbstractWin32AudioDecoder {
  public:
   static scoped_ptr<AbstractWin32AudioDecoder> Create(
-      SbMediaAudioCodec codec,
       SbMediaAudioFrameStorageType audio_frame_fmt,
       SbMediaAudioSampleType sample_type,
-      const SbMediaAudioSampleInfo& audio_sample_info,
+      const starboard::media::AudioStreamInfo& audio_stream_info,
       SbDrmSystem drm_system);
   virtual ~AbstractWin32AudioDecoder() {}
 
diff --git a/starboard/shared/x11/application_x11.cc b/starboard/shared/x11/application_x11.cc
index a650884..181882f 100644
--- a/starboard/shared/x11/application_x11.cc
+++ b/starboard/shared/x11/application_x11.cc
@@ -696,12 +696,19 @@
 
 using shared::starboard::player::filter::CpuVideoFrame;
 
+#if SB_MODULAR_BUILD
+ApplicationX11::ApplicationX11(SbEventHandleCallback sb_event_handle_callback)
+#else
 ApplicationX11::ApplicationX11()
+#endif  // SB_MODULAR_BUILD
     : wake_up_atom_(None),
       wm_delete_atom_(None),
 #if SB_API_VERSION >= 13
       wm_change_state_atom_(None),
 #endif  // SB_API_VERSION >= 13
+#if SB_MODULAR_BUILD
+      QueueApplication(sb_event_handle_callback),
+#endif  // SB_MODULAR_BUILD
       composite_event_id_(kSbEventIdInvalid),
       display_(NULL),
       paste_buffer_key_release_pending_(false) {
diff --git a/starboard/shared/x11/application_x11.h b/starboard/shared/x11/application_x11.h
index 79b698c..bd2e5c2 100644
--- a/starboard/shared/x11/application_x11.h
+++ b/starboard/shared/x11/application_x11.h
@@ -38,7 +38,11 @@
 // This application engine combines the generic queue with the X11 event queue.
 class ApplicationX11 : public shared::starboard::QueueApplication {
  public:
+#if SB_MODULAR_BUILD
+  explicit ApplicationX11(SbEventHandleCallback sb_event_handle_callback);
+#else
   ApplicationX11();
+#endif  // SB_MODULAR_BUILD
   ~ApplicationX11() override;
 
   static ApplicationX11* Get() {
diff --git a/starboard/socket.h b/starboard/socket.h
index 0398e75..adcd32b 100644
--- a/starboard/socket.h
+++ b/starboard/socket.h
@@ -283,7 +283,7 @@
 // persistently, so the address is unnecessary, but allowed.
 //
 // |socket|: The SbSocket from which data is read.
-// |out_data|: The data read from the socket.
+// |out_data|: The data read from the socket. Must not be NULL.
 // |data_size|: The number of bytes to read.
 // |out_source|: The source address of the packet.
 SB_EXPORT int SbSocketReceiveFrom(SbSocket socket,
@@ -304,7 +304,7 @@
 // spin).
 //
 // |socket|: The SbSocket to use to write data.
-// |data|: The data read from the socket.
+// |data|: The data written to the socket. Must not be NULL.
 // |data_size|: The number of bytes of |data| to write.
 // |destination|: The location to which data is written. This value must be
 //   |NULL| for TCP connections, which can only have a single endpoint.
diff --git a/starboard/stub/BUILD.gn b/starboard/stub/BUILD.gn
index 2e713bd..7ba6ae2 100644
--- a/starboard/stub/BUILD.gn
+++ b/starboard/stub/BUILD.gn
@@ -164,15 +164,15 @@
     "//starboard/shared/stub/player_create.cc",
     "//starboard/shared/stub/player_destroy.cc",
     "//starboard/shared/stub/player_get_current_frame.cc",
-    "//starboard/shared/stub/player_get_info2.cc",
+    "//starboard/shared/stub/player_get_info.cc",
     "//starboard/shared/stub/player_get_maximum_number_of_samples_per_write.cc",
     "//starboard/shared/stub/player_get_preferred_output_mode.cc",
-    "//starboard/shared/stub/player_seek2.cc",
+    "//starboard/shared/stub/player_seek.cc",
     "//starboard/shared/stub/player_set_bounds.cc",
     "//starboard/shared/stub/player_set_playback_rate.cc",
     "//starboard/shared/stub/player_set_volume.cc",
     "//starboard/shared/stub/player_write_end_of_stream.cc",
-    "//starboard/shared/stub/player_write_sample2.cc",
+    "//starboard/shared/stub/player_write_samples.cc",
     "//starboard/shared/stub/socket_accept.cc",
     "//starboard/shared/stub/socket_bind.cc",
     "//starboard/shared/stub/socket_clear_last_error.cc",
diff --git a/starboard/stub/application_stub.cc b/starboard/stub/application_stub.cc
index 5b8dee5..c1fc67b 100644
--- a/starboard/stub/application_stub.cc
+++ b/starboard/stub/application_stub.cc
@@ -20,17 +20,18 @@
 namespace starboard {
 namespace stub {
 
-ApplicationStub::ApplicationStub() {
-}
+#if SB_MODULAR_BUILD
+ApplicationStub::ApplicationStub(SbEventHandleCallback sb_event_handle_callback)
+    : QueueApplication(sb_event_handle_callback) {}
+#else
+ApplicationStub::ApplicationStub() {}
+#endif  // SB_MODULAR_BUILD
 
-ApplicationStub::~ApplicationStub() {
-}
+ApplicationStub::~ApplicationStub() {}
 
-void ApplicationStub::Initialize() {
-}
+void ApplicationStub::Initialize() {}
 
-void ApplicationStub::Teardown() {
-}
+void ApplicationStub::Teardown() {}
 
 bool ApplicationStub::MayHaveSystemEvents() {
   return false;
@@ -45,8 +46,7 @@
   return NULL;
 }
 
-void ApplicationStub::WakeSystemEventWait() {
-}
+void ApplicationStub::WakeSystemEventWait() {}
 
 }  // namespace stub
 }  // namespace starboard
diff --git a/starboard/stub/application_stub.h b/starboard/stub/application_stub.h
index 8e8ea2f..9e04f4a 100644
--- a/starboard/stub/application_stub.h
+++ b/starboard/stub/application_stub.h
@@ -27,7 +27,12 @@
 // Stub application engine using the generic queue and a stub implementation.
 class ApplicationStub : public shared::starboard::QueueApplication {
  public:
+#if SB_MODULAR_BUILD
+  explicit ApplicationStub(SbEventHandleCallback sb_event_handle_callback);
+#else
   ApplicationStub();
+#endif  // SB_MODULAR_BUILD
+
   ~ApplicationStub() override;
 
   static ApplicationStub* Get() {
diff --git a/starboard/stub/configuration_public.h b/starboard/stub/configuration_public.h
index 0700fcb..b9657c4 100644
--- a/starboard/stub/configuration_public.h
+++ b/starboard/stub/configuration_public.h
@@ -163,15 +163,6 @@
 
 // --- Media Configuration ---------------------------------------------------
 
-// After a seek is triggered, the default behavior is to append video frames
-// from the last key frame before the seek time and append audio frames from the
-// seek time because usually all audio frames are key frames.  On platforms that
-// cannot decode video frames without displaying them, this will cause the video
-// being played without audio for several seconds after seeking.  When the
-// following macro is defined, the app will append audio frames start from the
-// timestamp that is before the timestamp of the video key frame being appended.
-//
-
 // The implementation is allowed to support kSbMediaAudioSampleTypeInt16 only
 // when this macro is defined.
 #undef SB_HAS_QUIRK_SUPPORT_INT16_AUDIO_SAMPLES
diff --git a/starboard/stub/main.cc b/starboard/stub/main.cc
index d0c4edc..1cc7d53 100644
--- a/starboard/stub/main.cc
+++ b/starboard/stub/main.cc
@@ -16,6 +16,17 @@
 #include "starboard/stub/application_stub.h"
 
 int main(int argc, char** argv) {
+#if SB_MODULAR_BUILD
+  return SbRunStarboardMain(argc, argv, SbEventHandle);
+#else
   starboard::stub::ApplicationStub application;
   return application.Run(argc, argv);
+#endif  // SB_MODULAR_BUILD
 }
+
+#if SB_MODULAR_BUILD
+int SbRunStarboardMain(int argc, char** argv, SbEventHandleCallback callback) {
+  starboard::stub::ApplicationStub application(callback);
+  return application.Run(argc, argv);
+}
+#endif  // SB_MODULAR_BUILD
diff --git a/starboard/stub/platform_configuration/BUILD.gn b/starboard/stub/platform_configuration/BUILD.gn
index ab1385e..aae1163 100644
--- a/starboard/stub/platform_configuration/BUILD.gn
+++ b/starboard/stub/platform_configuration/BUILD.gn
@@ -16,72 +16,65 @@
   configs = [ "//starboard/build/config/sabi" ]
   cflags = []
 
-  if (current_toolchain == host_toolchain) {
+  cflags_cc = [ "-std=gnu++14" ]
+  ldflags = [ "-static-libstdc++" ]
+
+  if (is_debug) {
+    cflags += [ "-O0" ]
+    configs += [ "//build/config/compiler:rtti" ]
+  } else if (is_devel) {
+    cflags += [ "-O2" ]
+    configs += [ "//build/config/compiler:rtti" ]
+  } else {
     cflags += [
       "-O2",
+      "-gline-tables-only",
+    ]
+  }
+
+  # if (!cobalt_fastbuild && (is_debug || is_devel)) {
+  #   cflags += ["-g"]
+  # }
+
+  if (is_clang) {
+    cflags += [
+      "-Werror",
+      "-fcolor-diagnostics",
+
+      # Default visibility to hidden, to enable dead stripping.
+      "-fvisibility=hidden",
+
+      # Warn for implicit type conversions that may change a value.
+      "-Wconversion",
+
+      # Warns on switches on enums that cover all enum values but
+      # also contain a default: branch. Chrome is full of that.
+      "-Wno-covered-switch-default",
+
+      # protobuf uses hash_map.
+      "-Wno-deprecated",
+
+      # Don"t warn about the "struct foo f = {0};" initialization pattern.
+      "-Wno-missing-field-initializers",
+
+      # Do not warn for implicit sign conversions.
+      "-Wno-sign-conversion",
+      "-fno-strict-aliasing",  # See http://crbug.com/32204
+
+      # Triggered by the COMPILE_ASSERT macro.
+      "-Wno-unused-local-typedef",
+
+      # Do not warn if a function or variable cannot be implicitly
+      # instantiated.
+      "-Wno-undefined-var-template",
+
+      # Do not warn about unused function params.
       "-Wno-unused-parameter",
     ]
-  } else {
-    cflags_cc = [ "-std=gnu++14" ]
-    ldflags = [ "-static-libstdc++" ]
-
-    if (is_debug) {
-      cflags += [ "-O0" ]
-      configs += [ "//build/config/compiler:rtti" ]
-    } else if (is_devel) {
-      cflags += [ "-O2" ]
-      configs += [ "//build/config/compiler:rtti" ]
-    } else {
-      cflags += [
-        "-O2",
-        "-gline-tables-only",
-      ]
-    }
-
-    # if (!cobalt_fastbuild && (is_debug || is_devel)) {
-    #   cflags += ["-g"]
-    # }
-
-    if (is_clang) {
-      cflags += [
-        "-Werror",
-        "-fcolor-diagnostics",
-
-        # Default visibility to hidden, to enable dead stripping.
-        "-fvisibility=hidden",
-
-        # Warn for implicit type conversions that may change a value.
-        "-Wconversion",
-
-        # Warns on switches on enums that cover all enum values but
-        # also contain a default: branch. Chrome is full of that.
-        "-Wno-covered-switch-default",
-
-        # protobuf uses hash_map.
-        "-Wno-deprecated",
-
-        # Don"t warn about the "struct foo f = {0};" initialization pattern.
-        "-Wno-missing-field-initializers",
-
-        # Do not warn for implicit sign conversions.
-        "-Wno-sign-conversion",
-        "-fno-strict-aliasing",  # See http://crbug.com/32204
-
-        # Triggered by the COMPILE_ASSERT macro.
-        "-Wno-unused-local-typedef",
-
-        # Do not warn if a function or variable cannot be implicitly
-        # instantiated.
-        "-Wno-undefined-var-template",
-
-        # Do not warn about unused function params.
-        "-Wno-unused-parameter",
-      ]
-    }
-
-    # Defined to get format macro constants from <inttypes.h>.
-    defines = [ "__STDC_FORMAT_MACROS" ]
   }
+
+  # Defined to get format macro constants from <inttypes.h>.
+  defines = [ "__STDC_FORMAT_MACROS" ]
 }
 
 config("pedantic_warnings") {
diff --git a/starboard/stub/toolchain/BUILD.gn b/starboard/stub/toolchain/BUILD.gn
index c1469cb..743018a 100644
--- a/starboard/stub/toolchain/BUILD.gn
+++ b/starboard/stub/toolchain/BUILD.gn
@@ -15,15 +15,6 @@
 import("//build/config/clang/clang.gni")
 import("//build/toolchain/gcc_toolchain.gni")
 
-clang_toolchain("host") {
-  clang_base_path = clang_base_path
-
-  toolchain_args = {
-    current_os = "linux"
-    current_cpu = "x64"
-  }
-}
-
 clang_toolchain("target") {
   clang_base_path = clang_base_path
 }
diff --git a/starboard/system.h b/starboard/system.h
index 233cb4d..371a15a 100644
--- a/starboard/system.h
+++ b/starboard/system.h
@@ -620,7 +620,8 @@
 SB_EXPORT bool SbSystemSupportsResume();
 
 // Returns pointer to a constant global struct implementing the extension named
-// |name|, if it is implemented. Otherwise return NULL.
+// |name|, if it is implemented. Otherwise return NULL. The |name| string must
+// not be NULL.
 //
 // Extensions are used to implement behavior which is specific to the
 // combination of application & platform. An extension relies on a header file
@@ -641,7 +642,8 @@
 SB_EXPORT const void* SbSystemGetExtension(const char* name);
 
 // Computes a HMAC-SHA256 digest of |message| into |digest| using the
-// application's certification secret.
+// application's certification secret. The |message| and the |digest|
+// pointers must not be NULL.
 //
 // The output will be written into |digest|.  |digest_size_in_bytes| must be 32
 // (or greater), since 32-bytes will be written into it.
diff --git a/starboard/testing/fake_graphics_context_provider.cc b/starboard/testing/fake_graphics_context_provider.cc
index 237d32e..b5716d8 100644
--- a/starboard/testing/fake_graphics_context_provider.cc
+++ b/starboard/testing/fake_graphics_context_provider.cc
@@ -15,6 +15,7 @@
 #include "starboard/testing/fake_graphics_context_provider.h"
 
 #include "starboard/common/condition_variable.h"
+#include "starboard/common/log.h"
 #include "starboard/common/mutex.h"
 
 #if defined(ADDRESS_SANITIZER)
@@ -229,7 +230,9 @@
 
   // Create the GLES2 or GLEX3 Context.
   EGLint context_attrib_list[] = {
-      EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE,
+      EGL_CONTEXT_CLIENT_VERSION,
+      3,
+      EGL_NONE,
   };
   if (context_ == EGL_NO_CONTEXT) {
     // Create an OpenGL ES 2.0 context.
diff --git a/starboard/tools/testing/test_runner.py b/starboard/tools/testing/test_runner.py
index 78eeed9..92ad8cd 100755
--- a/starboard/tools/testing/test_runner.py
+++ b/starboard/tools/testing/test_runner.py
@@ -679,7 +679,7 @@
       test_status = "SUCCEEDED"
 
       all_flaky_tests_succeeded = initial_flaky_failed_count == len(
-          flaky_passed_tests)
+          flaky_passed_tests) and initial_flaky_failed_count != 0
 
       # Always mark as FAILED if we have a non-zero return code, or failing
       # test.
diff --git a/starboard/ui_navigation.h b/starboard/ui_navigation.h
index 01754a9..4f8e55f 100644
--- a/starboard/ui_navigation.h
+++ b/starboard/ui_navigation.h
@@ -94,10 +94,14 @@
 // @verbatim
 //   | a b tx |
 //   | c d ty | @endverbatim
-typedef struct SbUiNavMatrix2x3 { float m[6]; } SbUiNavMatrix2x3;
+typedef struct SbUiNavMatrix2x3 {
+  float m[6];
+} SbUiNavMatrix2x3;
 
 // This represents a 4x4 transform matrix in row-major order.
-typedef struct SbUiNavMatrix4 { float m[16]; } SbUiNavMatrix4;
+typedef struct SbUiNavMatrix4 {
+  float m[16];
+} SbUiNavMatrix4;
 
 // This structure specifies all the callbacks which the platform UI engine
 // should invoke for various interaction events on navigation items. These
@@ -309,6 +313,7 @@
 // Retrieve the platform's UI navigation implementation. If the platform does
 // not provide one, then return false without modifying |out_interface|.
 // Otherwise, initialize all members of |out_interface| and return true.
+// The |out_interface| pointer must not be NULL.
 SB_EXPORT bool SbUiNavGetInterface(SbUiNavInterface* out_interface);
 
 #ifdef __cplusplus
diff --git a/starboard/win/shared/BUILD.gn b/starboard/win/shared/BUILD.gn
index c968e66..aed688f 100644
--- a/starboard/win/shared/BUILD.gn
+++ b/starboard/win/shared/BUILD.gn
@@ -130,8 +130,6 @@
     "//starboard/shared/starboard/media/media_get_video_buffer_budget.cc",
     "//starboard/shared/starboard/media/media_is_buffer_pool_allocate_on_demand.cc",
     "//starboard/shared/starboard/media/media_is_buffer_using_memory_pool.cc",
-    "//starboard/shared/starboard/media/mime_type.cc",
-    "//starboard/shared/starboard/media/mime_type.h",
     "//starboard/shared/starboard/memory.cc",
     "//starboard/shared/starboard/net_args.cc",
     "//starboard/shared/starboard/net_args.h",
diff --git a/starboard/win/shared/platform_configuration/BUILD.gn b/starboard/win/shared/platform_configuration/BUILD.gn
index 7d01219..1a66c0e 100644
--- a/starboard/win/shared/platform_configuration/BUILD.gn
+++ b/starboard/win/shared/platform_configuration/BUILD.gn
@@ -15,257 +15,30 @@
 import("//build/config/win/visual_studio_version.gni")
 
 config("platform_configuration") {
-  # TODO(b/206809341): Migrate Detect64BitPortabilityProblems
-
-  configs = [ "//starboard/build/config/sabi" ]
-  cflags_cc = [ "/TP" ]
-  cflags = []
-  ldflags = [
-    "/NXCOMPAT",
-    "/MANIFEST:NO",
+  configs = [
+    "//starboard/build/config/sabi",
+    "//starboard/build/config/win:common",
   ]
-  libs = []
-  include_dirs = []
-  arflags = []
+
   defines = [
-    "_UNICODE",
-    "UNICODE",
-  ]
+    # Disable warnings.  These options were inherited from Chromium.
+    "_CRT_SECURE_NO_DEPRECATE",
+    "_CRT_NONSTDC_NO_WARNINGS",
+    "_CRT_NONSTDC_NO_DEPRECATE",
+    "_SCL_SECURE_NO_DEPRECATE",
 
-  if (!cobalt_fastbuild) {
-    # Containers cannot use PDBs, so we conditionally disable them.
-    # We want to keep them for non-docker builds as they make compilation
-    # significantly faster.
-    if (is_docker_build) {
-      cflags += [ "/Z7" ]
-    } else {
-      cflags += [
-        "/Zi",
-        "/FS",
-      ]
-    }
-    ldflags += [ "/DEBUG:FULL" ]
-  }
+    # Disable suggestions to switch to Microsoft-specific secure CRT.
+    "_CRT_SECURE_NO_WARNINGS",
+    "__STDC_FORMAT_MACROS",  # so that we get PRI*
 
-  # msvs_base
-  # OutputDirectory and IntermediateDirectory, maybe CharacterSet
-  include_dirs += [
-    "$wdk_include_path/shared",
-    "$wdk_include_path/ucrt",
-    "$wdk_include_path/um",
-    "$wdk_include_path/winrt",
-    "$msvc_path/include",
-  ]
-  cflags += [
-    "/EHsc",
-    "/std:c++14",
-  ]
+    # By defining this, M_PI will get #defined.
+    "_USE_MATH_DEFINES",
 
-  # msvs_debug/_devel/etc
-  ldflags += [ "/INCREMENTAL:NO" ]
-  if (is_debug) {
-    cflags += [
-      "/Od",
+    # min and max collide with std::min and std::max
+    "NOMINMAX",
 
-      # Check stack frame validity and check for uninitialized variables at run time.
-      "/RTC1",
-    ]
-  } else {
-    cflags += [ "/O2" ]
-  }
-
-  if (!is_gold) {
-    libs += [ "dbghelp.lib" ]
-  }
-
-  if (is_debug || is_devel) {
-    cflags += [
-      # Use debug multithreaded library.
-      "/MDd",
-      "/GS",
-
-      # Unit tests can have huge object files.
-      "/bigobj",
-    ]
-  } else {
-    cflags += [
-      # Use release multithreaded library.
-      "/MD",
-
-      # Unreferenced variable.
-      # Often variables are only referenced in DCHECKs.
-      "/wd4189",
-    ]
-    ldflags += [
-      "/OPT:REF",
-      "/OPT:ICF",
-    ]
-  }
-
-  # target defaults
-  if (current_toolchain == default_toolchain) {
-    defines += [
-      # Disable warnings.  These options were inherited from Chromium.
-      "_CRT_SECURE_NO_DEPRECATE",
-      "_CRT_NONSTDC_NO_WARNINGS",
-      "_CRT_NONSTDC_NO_DEPRECATE",
-      "_SCL_SECURE_NO_DEPRECATE",
-
-      # Disable suggestions to switch to Microsoft-specific secure CRT.
-      "_CRT_SECURE_NO_WARNINGS",
-      "__STDC_FORMAT_MACROS",  # so that we get PRI*
-
-      # By defining this, M_PI will get #defined.
-      "_USE_MATH_DEFINES",
-
-      # min and max collide with std::min and std::max
-      "NOMINMAX",
-
-      # Conform with C99 spec.
-      "_CRT_STDIO_ISO_WIDE_SPECIFIERS",
-    ]
-  }
-
-  cflags += [
-    # Check for buffer overruns.
-    "/GS",
-
-    # "for" loop's initializer go out of scope after the for loop.
-    "/Zc:forScope",
-
-    # wchar_t is treated as a built-in type.
-    "/Zc:wchar_t",
-
-    # Don't send error reports to MS.
-    "/errorReport:none",
-  ]
-
-  arflags += [
-    # Linking statically with C++/CX library is not recommended.
-    # TODO: Remove after removing ComponentExtensions
-    "/ignore:4264",
-  ]
-
-  cflags += [
-    # Allow unused function input parameter.
-    "/wd4100",
-
-    # Conditional expression is constant.
-    # Triggers in many legitimate cases, like branching on a constant declared
-    # in type traits.
-    "/wd4127",
-
-    # Disable anonymous union warnings.
-    "/wd4201",
-
-    # 4244 (Level 2) - Implicit conversion from float to int
-    # 4244 (Level 3) - Implicit conversion from int to something smaller
-    # than int.
-    # 4244 (Level 4) - Implicit conversion of types, which may result in
-    # data loss.
-    "/wd4244",
-
-    # Class has virtual functions, but destructor is not virtual.
-    # Far less useful than in GCC because doesn't take into account the fact
-    # that destructor is not public.
-    "/wd4265",
-
-    # Inconsistent DLL linkage
-    "/wd4273",
-
-    # Matching delete operator for `new`. 4291 is also ignored by Chromium.
-    "/wd4291",
-
-    # Double -> float truncation. Not enabled on other compilers.
-    "/wd4305",
-
-    # cast truncates constant value.
-    # We do not care.
-    "/wd4309",
-
-    # casting constant number.
-    "/wd4310",
-
-    # An rvalue cannot be bound to a non-const reference.
-    # In previous versions of Visual C++, it was possible to bind an rvalue
-    # to a non-const reference in a direct initialization. This warning
-    # is useless as it simply describes proper C++ behavior.
-    "/wd4350",
-
-    # layout of class may have changed from a previous version of
-    # the compiler due to better packing of member. We don't care about
-    # binary compatibility with other compiler versions.
-    "/wd4371",
-
-    # relative include path contains '..'.
-    # This occurs in a lot third party libraries and we don't care.
-    "/wd4464",
-
-    # decorated name length exceeded, name was truncated.
-    "/wd4503",
-
-    # assignment operator could not be generated.
-    # This is expected for structs with const members.
-    "/wd4512",
-
-    # Unreferenced inline function has been removed.
-    # While detection of dead code is good, this warning triggers in
-    # third-party libraries which renders it useless.
-    "/wd4514",
-
-    # Expression before comma has no effect.
-    # Cannot be used because Microsoft uses _ASSERTE(("message", 0)) trick
-    # in malloc.h which is included pretty much everywhere.
-    "/wd4548",
-
-    # Use of noexcept in targets compiled without /EHsc triggers a warning
-    # "termination on exception is not guaranteed" which we consider benign
-    # because we don't expect exceptions to cross the boundary of modules
-    # compiled with /EHsc.
-    "/wd4577",
-
-    # Copy constructor could not be generated because a base class copy
-    # constructor is inaccessible.
-    # This is an expected consequence of using DISALLOW_COPY_AND_ASSIGN().
-    "/wd4625",
-
-    # Assignment operator could not be generated because a base class
-    # assignment operator is inaccessible.
-    # This is an expected consequence of using DISALLOW_COPY_AND_ASSIGN().
-    "/wd4626",
-
-    # Digraphs not supported.
-    "/wd4628",
-
-    # Sometimes template definitions and declarations are separate and MSVC
-    # complains when it fails to find the definition on seeing the template.
-    "/wd4661",
-
-    # Symbol is not defined as a preprocessor macro, replacing with '0'.
-    # Seems like common practice, used in Windows SDK and gtest.
-    "/wd4668",
-
-    # Function not inlined.
-    # It's up to the compiler to decide what to inline.
-    "/wd4710",
-
-    # Function selected for inline expansion.
-    # It's up to the compiler to decide what to inline.
-    "/wd4711",
-
-    # The type and order of elements caused the compiler to add padding
-    # to the end of a struct.
-    # Unsurprisingly, most of the structs become larger because of padding
-    # but it's a universally acceptable price for better performance.
-    "/wd4820",
-
-    # Disable static analyzer warning for std::min and std::max with
-    # objects.
-    # https://connect.microsoft.com/VisualStudio/feedback/details/783808/static-analyzer-warning-c28285-for-std-min-and-std-max
-    "/wd28285",
-
-    # Deprecated function warning.
-    "/wd4996",
+    # Conform with C99 spec.
+    "_CRT_STDIO_ISO_WIDE_SPECIFIERS",
   ]
 }
 
diff --git a/starboard/win/win32/cobalt/configuration.py b/starboard/win/win32/cobalt/configuration.py
index e342987..0a5eaa6 100644
--- a/starboard/win/win32/cobalt/configuration.py
+++ b/starboard/win/win32/cobalt/configuration.py
@@ -24,14 +24,13 @@
     return False
 
   def GetTestFilters(self):
-    filters = super(CobaltWinWin32Configuration, self).GetTestFilters()
+    filters = super().GetTestFilters()
     for target, tests in self.__FILTERED_TESTS.items():
       filters.extend(test_filter.TestFilter(target, test) for test in tests)
     return filters
 
   def GetWebPlatformTestFilters(self):
-    filters = super(CobaltWinWin32Configuration,
-                    self).GetWebPlatformTestFilters()
+    filters = super().GetWebPlatformTestFilters()
     filters += [
         '*WebPlatformTest.Run*',
     ]
@@ -52,4 +51,8 @@
            'RasterizerSubmitCalledAtExpectedFrequencyAfterSinglePipelineSubmit'
            ),
       ],
+      'net_unittests': [
+          # Flaky test to be re-enabled after b/271006511 is fixed.
+          'CookieMonsterTest.PredicateSeesAllCookies',
+      ],
   }
diff --git a/starboard/win/win32/platform_configuration/BUILD.gn b/starboard/win/win32/platform_configuration/BUILD.gn
index 6b48048..3bf87d0 100644
--- a/starboard/win/win32/platform_configuration/BUILD.gn
+++ b/starboard/win/win32/platform_configuration/BUILD.gn
@@ -64,17 +64,12 @@
     "/wd4838",
   ]
 
-  if (current_toolchain == default_toolchain) {
-    defines = [
-      "_WIN32",
-      "WIN32",
-      "WINDOWS",
+  defines = [
+    "_WIN32",
+    "WIN32",
+    "WINDOWS",
 
-      # Enable GNU extensions to get prototypes like ffsl.
-      "_GNU_SOURCE=1",
-    ]
-  } else {
-    # Increase compiler heap limit.
-    cflags += [ "/Zm10" ]
-  }
+    # Enable GNU extensions to get prototypes like ffsl.
+    "_GNU_SOURCE=1",
+  ]
 }
diff --git a/starboard/win/win32/test_filters.py b/starboard/win/win32/test_filters.py
index 8146601..c6cbcf9 100644
--- a/starboard/win/win32/test_filters.py
+++ b/starboard/win/win32/test_filters.py
@@ -41,6 +41,8 @@
         'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceForDestination/type_ipv6',
         'SbSocketAddressTypes/SbSocketGetInterfaceAddressTest.SunnyDaySourceNotLoopback/type_ipv6',
         'SbSocketAddressTypes/SbSocketResolveTest.SunnyDayFiltered/filter_ipv6_type_ipv6',
+        'SbSocketAddressTypes/SbSocketSetOptionsTest.RainyDayInvalidSocket/type_ipv4',
+        'SbSocketAddressTypes/SbSocketSetOptionsTest.RainyDayInvalidSocket/type_ipv6',
     ],
     'player_filter_tests': [
         # These tests fail on our VMs for win-win32 builds due to missing
diff --git a/starboard/win/win32/toolchain/BUILD.gn b/starboard/win/win32/toolchain/BUILD.gn
index 4e82dcf..ae9ace2 100644
--- a/starboard/win/win32/toolchain/BUILD.gn
+++ b/starboard/win/win32/toolchain/BUILD.gn
@@ -14,30 +14,14 @@
 
 import("//build/config/win/visual_studio_version.gni")
 import("//build/toolchain/win/msvc_toolchain.gni")
-
-_base_path = "$msvc_path/bin/HostX64/x64"
-_libpaths = "/LIBPATH:\"$wdk_lib_path/ucrt/x64\" /LIBPATH:\"$wdk_lib_path/um/x64\" /LIBPATH:\"$msvc_path/lib/x64\""
-
-msvc_toolchain("host") {
-  cl = "$_base_path/cl.exe"
-  lib = "$_base_path/lib.exe"
-  link = "$_base_path/link.exe"
-  asm = "$_base_path/ml64.exe"
-  sys_lib_flags = _libpaths
-
-  toolchain_args = {
-    is_clang = false
-    current_os = "win"
-    current_cpu = "x64"
-  }
-}
+import("//starboard/build/toolchain/win/variables.gni")
 
 msvc_toolchain("target") {
-  cl = "$_base_path/cl.exe"
-  lib = "$_base_path/lib.exe"
-  link = "$_base_path/link.exe"
-  asm = "$_base_path/ml64.exe"
-  sys_lib_flags = _libpaths
+  cl = "$tool_base_path/cl.exe"
+  lib = "$tool_base_path/lib.exe"
+  link = "$tool_base_path/link.exe"
+  asm = "$tool_base_path/ml64.exe"
+  sys_lib_flags = sys_libpaths
 
   toolchain_args = {
     is_clang = false
diff --git a/third_party/angle/src/common/angle_hdr.cpp b/third_party/angle/src/common/angle_hdr.cpp
index f975cb5..7387e91 100644
--- a/third_party/angle/src/common/angle_hdr.cpp
+++ b/third_party/angle/src/common/angle_hdr.cpp
@@ -15,7 +15,7 @@
 #if defined(STARBOARD)
 #include "angle_hdr.h"
 
-#include "starboard/atomic.h"
+#include "starboard/common/atomic.h"
 #include "starboard/common/log.h"
 
 namespace angle
diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn
index 1006144..c652096 100644
--- a/third_party/boringssl/BUILD.gn
+++ b/third_party/boringssl/BUILD.gn
@@ -9,11 +9,15 @@
   declare_args() {
     openssl_config_path = "$boringssl_root/config/starboard"
   }
+} else if (is_native_target_build) {
+  declare_args() {
+    openssl_config_path = "$boringssl_root/config/native_target"
+  }
 }
 
 config("external_config") {
   include_dirs = [ "$boringssl_root/include" ]
-  if (is_starboard) {
+  if (use_cobalt_customizations) {
     include_dirs += [ openssl_config_path ]
   }
 }
@@ -148,6 +152,18 @@
         }
       }
     }
+  } else if (is_native_target_build)  {
+    if (is_linux || is_android) {
+      if (asm_target_arch == "x64") {
+        sources += boringssl_linux_x86_64_files
+      } else if (asm_target_arch == "arm") {
+        sources += boringssl_linux_arm_files
+      } else {
+        assert(false, "Unsupported Linux or Android arch for native build")
+      }
+    } else {
+        assert(false, "Unsupported OS for native build")
+    }
   }
 }
 
@@ -178,8 +194,8 @@
   ]
   include_dirs = [ "src/include" ]
   defines = [ "OPENSSL_NO_SOCK" ]
-  deps = [
-    ":crypto",
-    "//starboard",
-  ]
+  deps = [ ":crypto" ]
+  if (is_starboard) {
+    deps += [ "//starboard" ]
+  }
 }
diff --git a/third_party/boringssl/buildfiles.gni b/third_party/boringssl/buildfiles.gni
index ce0c077..1363a96 100644
--- a/third_party/boringssl/buildfiles.gni
+++ b/third_party/boringssl/buildfiles.gni
@@ -170,7 +170,6 @@
   "$boringssl_root/crypto/hkdf/hkdf.c",
   "$boringssl_root/crypto/internal.h",
   "$boringssl_root/crypto/lhash/lhash.c",
-  "$boringssl_root/crypto/mem_starboard.c",
   "$boringssl_root/crypto/obj/obj.c",
   "$boringssl_root/crypto/obj/obj_dat.h",
   "$boringssl_root/crypto/obj/obj_xref.c",
@@ -367,6 +366,15 @@
   "$boringssl_root/include/openssl/x509_vfy.h",
   "$boringssl_root/include/openssl/x509v3.h",
 ]
+if (is_starboard) {
+  boringssl_crypto_files += [ "$boringssl_root/crypto/mem_starboard.c" ]
+} else if (is_native_target_build) {
+  boringssl_crypto_files += [
+    "$boringssl_root/crypto/mem.c",
+    "$boringssl_root/crypto/mem_native_target.c",
+  ]
+}
+
 boringssl_ios_aarch64_files = [
   "ios-aarch64/crypto/chacha/chacha-armv8.S",
   "ios-aarch64/crypto/fipsmodule/aesv8-armx64.S",
diff --git a/third_party/boringssl/src/config/native_target/openssl/opensslconf.h b/third_party/boringssl/src/config/native_target/openssl/opensslconf.h
new file mode 100644
index 0000000..aee2d4b
--- /dev/null
+++ b/third_party/boringssl/src/config/native_target/openssl/opensslconf.h
@@ -0,0 +1,347 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/* opensslconf.h */
+/* WARNING: Edited heavily by hand, based on piii config. Meant for building
+ * from source in the Cobalt repo in cases where the Starboard porting layer
+ * should not be used. Customizations vs. the piii config this was branched from
+ * are guarded by the NATIVE_TARGET_BUILD preprocessor macro. */
+
+#ifdef NATIVE_TARGET_BUILD
+// Resolve shim types as POSIX types.
+#define OPENSSL_port_time_t time_t
+#define OPENSSL_port_timeval struct timeval
+
+// Resolve shim system calls as Linux system calls.
+#define OPENSSL_port_time time
+#define OPENSSL_port_gettimeofday gettimeofday
+#endif  // NATIVE_TARGET_BUILD
+
+/* OpenSSL was configured with the following options: */
+#ifndef OPENSSL_DOING_MAKEDEPEND
+
+
+#ifndef OPENSSL_NO_CAMELLIA
+# define OPENSSL_NO_CAMELLIA
+#endif
+#ifndef OPENSSL_NO_CAST
+# define OPENSSL_NO_CAST
+#endif
+#ifndef OPENSSL_NO_CAPIENG
+# define OPENSSL_NO_CAPIENG
+#endif
+#ifndef OPENSSL_NO_CMS
+# define OPENSSL_NO_CMS
+#endif
+#ifndef OPENSSL_NO_EC_NISTP_64_GCC_128
+# define OPENSSL_NO_EC_NISTP_64_GCC_128
+#endif
+#ifndef OPENSSL_NO_FIPS
+# define OPENSSL_NO_FIPS
+#endif
+#ifndef OPENSSL_NO_GMP
+# define OPENSSL_NO_GMP
+#endif
+#ifndef OPENSSL_NO_IDEA
+# define OPENSSL_NO_IDEA
+#endif
+#ifndef OPENSSL_NO_JPAKE
+# define OPENSSL_NO_JPAKE
+#endif
+#ifndef OPENSSL_NO_KRB5
+# define OPENSSL_NO_KRB5
+#endif
+#ifndef OPENSSL_NO_MDC2
+# define OPENSSL_NO_MDC2
+#endif
+#ifndef OPENSSL_NO_RC5
+# define OPENSSL_NO_RC5
+#endif
+#ifndef OPENSSL_NO_RFC3779
+# define OPENSSL_NO_RFC3779
+#endif
+#ifndef OPENSSL_NO_SCTP
+# define OPENSSL_NO_SCTP
+#endif
+#ifndef OPENSSL_NO_SEED
+# define OPENSSL_NO_SEED
+#endif
+#ifndef OPENSSL_NO_SHA0
+# define OPENSSL_NO_SHA0
+#endif
+#ifndef OPENSSL_NO_WHIRLPOOL
+# define OPENSSL_NO_WHIRLPOOL
+#endif
+
+#endif /* OPENSSL_DOING_MAKEDEPEND */
+
+#ifndef OPENSSL_THREADS
+# define OPENSSL_THREADS
+#endif
+#ifndef OPENSSL_NO_DYNAMIC_ENGINE
+# define OPENSSL_NO_DYNAMIC_ENGINE
+#endif
+
+/* Use x86 assembler functions - taken from Android/x86 config */
+# define OPENSSL_BN_ASM_GF2m
+# define OPENSSL_BN_ASM_MONT
+# define OPENSSL_BN_ASM_PART_WORDS
+# define AES_ASM
+# define GHASH_ASM
+# define SHA1_ASM
+# define SHA256_ASM
+# define SHA512_ASM
+# define MD5_ASM
+# define DES_PTR
+# define DES_RISC1
+# define DES_UNROLL
+
+/* The OPENSSL_NO_* macros are also defined as NO_* if the application
+   asks for it.  This is a transient feature that is provided for those
+   who haven't had the time to do the appropriate changes in their
+   applications.  */
+#ifdef OPENSSL_ALGORITHM_DEFINES
+# if defined(OPENSSL_NO_CAMELLIA) && !defined(NO_CAMELLIA)
+#  define NO_CAMELLIA
+# endif
+# if defined(OPENSSL_NO_CAPIENG) && !defined(NO_CAPIENG)
+#  define NO_CAPIENG
+# endif
+# if defined(OPENSSL_NO_CMS) && !defined(NO_CMS)
+#  define NO_CMS
+# endif
+# if defined(OPENSSL_NO_FIPS) && !defined(NO_FIPS)
+#  define NO_FIPS
+# endif
+# if defined(OPENSSL_NO_GMP) && !defined(NO_GMP)
+#  define NO_GMP
+# endif
+# if defined(OPENSSL_NO_IDEA) && !defined(NO_IDEA)
+#  define NO_IDEA
+# endif
+# if defined(OPENSSL_NO_JPAKE) && !defined(NO_JPAKE)
+#  define NO_JPAKE
+# endif
+# if defined(OPENSSL_NO_KRB5) && !defined(NO_KRB5)
+#  define NO_KRB5
+# endif
+# if defined(OPENSSL_NO_MDC2) && !defined(NO_MDC2)
+#  define NO_MDC2
+# endif
+# if defined(OPENSSL_NO_RC5) && !defined(NO_RC5)
+#  define NO_RC5
+# endif
+# if defined(OPENSSL_NO_RFC3779) && !defined(NO_RFC3779)
+#  define NO_RFC3779
+# endif
+# if defined(OPENSSL_NO_SEED) && !defined(NO_SEED)
+#  define NO_SEED
+# endif
+#endif
+
+/* crypto/opensslconf.h.in */
+
+#ifdef OPENSSL_DOING_MAKEDEPEND
+
+/* Include any symbols here that have to be explicitly set to enable a feature
+ * that should be visible to makedepend.
+ *
+ * [Our "make depend" doesn't actually look at this, we use actual build settings
+ * instead; we want to make it easy to remove subdirectories with disabled algorithms.]
+ */
+
+#ifndef OPENSSL_FIPS
+#define OPENSSL_FIPS
+#endif
+
+#endif
+
+/* Generate 80386 code? */
+#undef I386_ONLY
+
+#if !(defined(VMS) || defined(__VMS)) /* VMS uses logical names instead */
+#if defined(HEADER_CRYPTLIB_H) && !defined(OPENSSLDIR)
+#define ENGINESDIR "/usr/local/ssl/lib/engines"
+#define OPENSSLDIR "/usr/local/ssl"
+#endif
+#endif
+
+#undef OPENSSL_UNISTD
+#define OPENSSL_UNISTD <unistd.h>
+#if !defined(SWIG)
+#include <unistd.h>
+#endif
+
+#undef OPENSSL_EXPORT_VAR_AS_FUNCTION
+
+#if defined(HEADER_IDEA_H) && !defined(IDEA_INT)
+#define IDEA_INT unsigned int
+#endif
+
+#if defined(HEADER_MD2_H) && !defined(MD2_INT)
+#define MD2_INT unsigned int
+#endif
+
+#if defined(HEADER_RC2_H) && !defined(RC2_INT)
+/* I need to put in a mod for the alpha - eay */
+#define RC2_INT unsigned int
+#endif
+
+#if defined(HEADER_RC4_H)
+#if !defined(RC4_INT)
+/* using int types make the structure larger but make the code faster
+ * on most boxes I have tested - up to %20 faster. */
+/*
+ * I don't know what does "most" mean, but declaring "int" is a must on:
+ * - Intel P6 because partial register stalls are very expensive;
+ * - elder Alpha because it lacks byte load/store instructions;
+ */
+#define RC4_INT unsigned int
+#endif
+#if !defined(RC4_CHUNK)
+/*
+ * This enables code handling data aligned at natural CPU word
+ * boundary. See crypto/rc4/rc4_enc.c for further details.
+ */
+#undef RC4_CHUNK
+#endif
+#endif
+
+#if (defined(HEADER_NEW_DES_H) || defined(HEADER_DES_H)) && !defined(DES_LONG)
+/* If this is set to 'unsigned int' on a DEC Alpha, this gives about a
+ * %20 speed up (longs are 8 bytes, int's are 4). */
+#ifndef DES_LONG
+#define DES_LONG unsigned long
+#endif
+#endif
+
+#if defined(HEADER_BN_H) && !defined(CONFIG_HEADER_BN_H)
+#define CONFIG_HEADER_BN_H
+#define BN_LLONG
+
+/* Should we define BN_DIV2W here? */
+
+/* Only one for the following should be defined */
+/* The prime number generation stuff may not work when
+ * EIGHT_BIT but I don't care since I've only used this mode
+ * for debuging the bignum libraries */
+#undef SIXTY_FOUR_BIT_LONG
+#undef SIXTY_FOUR_BIT
+#define THIRTY_TWO_BIT
+#undef SIXTEEN_BIT
+#undef EIGHT_BIT
+#endif
+
+#if defined(HEADER_RC4_LOCL_H) && !defined(CONFIG_HEADER_RC4_LOCL_H)
+#define CONFIG_HEADER_RC4_LOCL_H
+/* if this is defined data[i] is used instead of *data, this is a %20
+ * speedup on x86 */
+#define RC4_INDEX
+#endif
+
+#if defined(HEADER_BF_LOCL_H) && !defined(CONFIG_HEADER_BF_LOCL_H)
+#define CONFIG_HEADER_BF_LOCL_H
+#undef BF_PTR
+#endif /* HEADER_BF_LOCL_H */
+
+#if defined(HEADER_DES_LOCL_H) && !defined(CONFIG_HEADER_DES_LOCL_H)
+#define CONFIG_HEADER_DES_LOCL_H
+#ifndef DES_DEFAULT_OPTIONS
+/* the following is tweaked from a config script, that is why it is a
+ * protected undef/define */
+#ifndef DES_PTR
+#define DES_PTR
+#endif
+
+/* This helps C compiler generate the correct code for multiple functional
+ * units.  It reduces register dependancies at the expense of 2 more
+ * registers */
+#ifndef DES_RISC1
+#define DES_RISC1
+#endif
+
+#ifndef DES_RISC2
+#undef DES_RISC2
+#endif
+
+#if defined(DES_RISC1) && defined(DES_RISC2)
+YOU SHOULD NOT HAVE BOTH DES_RISC1 AND DES_RISC2 DEFINED!!!!!
+#endif
+
+/* Unroll the inner loop, this sometimes helps, sometimes hinders.
+ * Very mucy CPU dependant */
+#ifndef DES_UNROLL
+#define DES_UNROLL
+#endif
+
+/* These default values were supplied by
+ * Peter Gutman <pgut001@cs.auckland.ac.nz>
+ * They are only used if nothing else has been defined */
+#if !defined(DES_PTR) && !defined(DES_RISC1) && !defined(DES_RISC2) && !defined(DES_UNROLL)
+/* Special defines which change the way the code is built depending on the
+   CPU and OS.  For SGI machines you can use _MIPS_SZLONG (32 or 64) to find
+   even newer MIPS CPU's, but at the moment one size fits all for
+   optimization options.  Older Sparc's work better with only UNROLL, but
+   there's no way to tell at compile time what it is you're running on */
+
+#if defined( sun )		/* Newer Sparc's */
+#  define DES_PTR
+#  define DES_RISC1
+#  define DES_UNROLL
+#elif defined( __ultrix )	/* Older MIPS */
+#  define DES_PTR
+#  define DES_RISC2
+#  define DES_UNROLL
+#elif defined( __osf1__ )	/* Alpha */
+#  define DES_PTR
+#  define DES_RISC2
+#elif defined ( _AIX )		/* RS6000 */
+  /* Unknown */
+#elif defined( __hpux )		/* HP-PA */
+  /* Unknown */
+#elif defined( __aux )		/* 68K */
+  /* Unknown */
+#elif defined( __dgux )		/* 88K (but P6 in latest boxes) */
+#  define DES_UNROLL
+#elif defined( __sgi )		/* Newer MIPS */
+#  define DES_PTR
+#  define DES_RISC2
+#  define DES_UNROLL
+#elif defined(i386) || defined(__i386__)	/* x86 boxes, should be gcc */
+#  define DES_PTR
+#  define DES_RISC1
+#  define DES_UNROLL
+#endif /* Systems-specific speed defines */
+#endif
+
+#endif /* DES_DEFAULT_OPTIONS */
+#endif /* HEADER_DES_LOCL_H */
+
+#ifdef NATIVE_TARGET_BUILD
+#define OPENSSL_port_abort abort
+#define OPENSSL_port_assert(x) assert(x)
+#define OPENSSL_port_free free
+#define OPENSSL_port_getenv(x) getenv(x)
+#define OPENSSL_port_gettimeofday gettimeofday
+#define OPENSSL_port_gmtime_r gmtime_r
+#define OPENSSL_port_malloc malloc
+#define OPENSSL_port_printf print
+#define OPENSSL_port_printferr printferr
+#define OPENSSL_port_realloc realloc
+#define OPENSSL_port_sscanf sscanf
+#define OPENSSL_port_strcasecmp strcasecmp
+#define OPENSSL_port_strdup strdup
+#define OPENSSL_port_strerror(x) strerror(x)
+#define OPENSSL_port_strncasecmp strncasecmp
+#endif  // NATIVE_TARGET_BUILD
diff --git a/third_party/boringssl/src/crypto/asn1/time_support.c b/third_party/boringssl/src/crypto/asn1/time_support.c
index 3b27808..c185739 100644
--- a/third_party/boringssl/src/crypto/asn1/time_support.c
+++ b/third_party/boringssl/src/crypto/asn1/time_support.c
@@ -57,11 +57,17 @@
 
 #include <openssl/opensslconf.h>
 
+#if defined(STARBOARD)
 #if !SB_HAS_QUIRK(NO_GMTIME_R)
 #if !defined(_POSIX_C_SOURCE)
 #define _POSIX_C_SOURCE 201410L  /* for gmtime_r */
 #endif
 #endif  // !SB_HAS_QUIRK(NO_GMTIME_R)
+#else  // STARBOARD
+#if !defined(_POSIX_C_SOURCE)
+#define _POSIX_C_SOURCE 201410L  /* for gmtime_r */
+#endif  // !defined(_POSIX_C_SOURCE)
+#endif  // STARBOARD
 
 #include "asn1_locl.h"
 #include "asn1_internal.h"
diff --git a/third_party/boringssl/src/crypto/bio/file.c b/third_party/boringssl/src/crypto/bio/file.c
index 3c7c08d..20cfb3c 100644
--- a/third_party/boringssl/src/crypto/bio/file.c
+++ b/third_party/boringssl/src/crypto/bio/file.c
@@ -212,6 +212,18 @@
   FILE *file = file_fopen(filename, mode);
 
   if (file == NULL) {
+#ifdef NATIVE_TARGET_BUILD
+    // This block was restored from revision 5470252 of this file.
+    OPENSSL_PUT_SYSTEM_ERROR();
+
+    ERR_add_error_data(5, "fopen('", filename, "','", mode, "')");
+    if (errno == ENOENT) {
+      OPENSSL_PUT_ERROR(BIO, BIO_R_NO_SUCH_FILE);
+    } else {
+      OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
+    }
+    return NULL;
+#else  // NATIVE_TARGET_BUILD
     SYSerr(SYS_F_FOPEN, get_last_sys_error());
     ERR_add_error_data(5, "fopen('", filename, "','", mode, "')");
     if (OPENSSL_errno == ENOENT)
@@ -219,6 +231,7 @@
     else
       BIOerr(BIO_F_BIO_NEW_FILE, ERR_R_SYS_LIB);
     return (NULL);
+#endif  // NATIVE_TARGET_BUILD
   }
   if ((ret = BIO_new(BIO_s_file())) == NULL) {
     fclose(file);
@@ -227,6 +240,7 @@
 
   BIO_clear_flags(ret, BIO_FLAGS_UPLINK); /* we did fopen -> we disengage
                                            * UPLINK */
+
   BIO_set_fp(ret, file, BIO_CLOSE);
 #endif  // defined(OPENSSL_SYS_STARBOARD)
   return (ret);
@@ -264,9 +278,17 @@
 #if defined(OPENSSL_SYS_STARBOARD)
       SbFileClose((SbFile)a->ptr);
 #else   // defined(OPENSSL_SYS_STARBOARD)
+// When this file was Starboardized, uplink support was added to the
+// non-Starboard code paths. But at this point it's not clear where to find
+// definitions for the uplink functions. The latest upstream version of
+// BoringSSL, which we will soon upgrade to, does not support uplink. It should
+// therefore be ok to just remove uplink support for native target builds while
+// we are still on this version of BoringSSL.
+#ifndef NATIVE_TARGET_BUILD
       if (a->flags & BIO_FLAGS_UPLINK)
         UP_fclose(a->ptr);
       else
+#endif  // !NATIVE_TARGET_BUILD
         fclose(a->ptr);
 #endif  // defined(OPENSSL_SYS_STARBOARD)
       a->ptr = NULL;
@@ -289,14 +311,27 @@
       OPENSSL_PUT_ERROR(BIO, ERR_R_SYS_LIB);
     }
 #else   // defined(OPENSSL_SYS_STARBOARD)
+#ifndef NATIVE_TARGET_BUILD
     if (b->flags & BIO_FLAGS_UPLINK)
       ret = UP_fread(out, 1, (int)outl, b->ptr);
     else
+#endif  // !NATIVE_TARGET_BUILD
       ret = fread(out, 1, (int)outl, (FILE *)b->ptr);
+#ifndef NATIVE_TARGET_BUILD
     if (ret == 0 && (b->flags & BIO_FLAGS_UPLINK) ? UP_ferror((FILE *)b->ptr)
                                                   : ferror((FILE *)b->ptr)) {
+#else  // !NATIVE_TARGET_BUILD
+    if (ret == 0 && ferror((FILE *)b->ptr)) {
+#endif  // !NATIVE_TARGET_BUILD
+#ifdef NATIVE_TARGET_BUILD
+      // TODO(b/270861949): it would be good to verify that error reporting
+      // works correctly throughout this file in native builds.
+      OPENSSL_PUT_SYSTEM_ERROR();
+      OPENSSL_PUT_ERROR(BIO, ERR_R_SYS_LIB);
+#else  // NATIVE_TARGET_BUILD
       SYSerr(SYS_F_FREAD, get_last_sys_error());
       BIOerr(BIO_F_FILE_READ, ERR_R_SYS_LIB);
+#endif  // NATIVE_TARGET_BUILD
       ret = -1;
     }
 #endif  // defined(OPENSSL_SYS_STARBOARD)
@@ -311,10 +346,13 @@
 #if defined(OPENSSL_SYS_STARBOARD)
     ret = SbFileWrite((SbFile)b->ptr, in, inl);
 #else   // defined(OPENSSL_SYS_STARBOARD)
+#ifndef NATIVE_TARGET_BUILD
     if (b->flags & BIO_FLAGS_UPLINK)
       ret = UP_fwrite(in, (int)inl, 1, b->ptr);
     else
+#else  // !NATIVE_TARGET_BUILD
       ret = fwrite(in, (int)inl, 1, (FILE *)b->ptr);
+#endif  // !NATIVE_TARGET_BUILD
     if (ret)
       ret = inl;
       /* ret=fwrite(in,1,(int)inl,(FILE *)b->ptr); */
@@ -345,10 +383,13 @@
 #if defined(OPENSSL_SYS_STARBOARD)
       ret = (long)SbFileSeek((SbFile)b->ptr, num, kSbFileFromBegin);
 #else   // defined(OPENSSL_SYS_STARBOARD)
+#ifndef NATIVE_TARGET_BUILD
       if (b->flags & BIO_FLAGS_UPLINK)
         ret = (long)UP_fseek(b->ptr, num, 0);
       else
+#else  // !NATIVE_TARGET_BUILD
         ret = (long)fseek(fp, num, 0);
+#endif  // !NATIVE_TARGET_BUILD
 #endif  // defined(OPENSSL_SYS_STARBOARD)
       break;
     case BIO_CTRL_EOF:
@@ -358,10 +399,13 @@
                  ? 1
                  : 0);
 #else   // defined(OPENSSL_SYS_STARBOARD)
+#ifndef NATIVE_TARGET_BUILD
       if (b->flags & BIO_FLAGS_UPLINK)
         ret = (long)UP_feof(fp);
       else
+#else  // !NATIVE_TARGET_BUILD
         ret = (long)feof(fp);
+#endif  // !NATIVE_TARGET_BUILD
 #endif  // defined(OPENSSL_SYS_STARBOARD)
       break;
     case BIO_C_FILE_TELL:
@@ -369,10 +413,13 @@
 #if defined(OPENSSL_SYS_STARBOARD)
       ret = SbFileSeek((SbFile)b->ptr, 0, kSbFileFromCurrent);
 #else   // defined(OPENSSL_SYS_STARBOARD)
+#ifndef NATIVE_TARGET_BUILD
       if (b->flags & BIO_FLAGS_UPLINK)
         ret = UP_ftell(b->ptr);
       else
+#else  // !NATIVE_TARGET_BUILD
         ret = ftell(fp);
+#endif  // !NATIVE_TARGET_BUILD
 #endif  // defined(OPENSSL_SYS_STARBOARD)
       break;
     case BIO_C_SET_FILE_PTR:
@@ -485,9 +532,15 @@
 #else   // defined(OPENSSL_SYS_STARBOARD)
       fp = fopen(ptr, p);
       if (fp == NULL) {
+#ifdef NATIVE_TARGET_BUILD
+        OPENSSL_PUT_SYSTEM_ERROR();
+        ERR_add_error_data(5, "fopen('", ptr, "','", p, "')");
+        OPENSSL_PUT_ERROR(BIO, ERR_R_SYS_LIB);
+#else  // NATIVE_TARGET_BUILD
         SYSerr(SYS_F_FOPEN, get_last_sys_error());
         ERR_add_error_data(5, "fopen('", ptr, "','", p, "')");
         BIOerr(BIO_F_FILE_CTRL, ERR_R_SYS_LIB);
+#endif  // NATIVE_TARGET_BUILD
         ret = 0;
         break;
       }
@@ -522,10 +575,13 @@
 #if defined(OPENSSL_SYS_STARBOARD)
       SbFileFlush((SbFile)b->ptr);
 #else   // defined(OPENSSL_SYS_STARBOARD)
+#ifndef NATIVE_TARGET_BUILD
       if (b->flags & BIO_FLAGS_UPLINK)
         UP_fflush(b->ptr);
       else
+#else  // !NATIVE_TARGET_BUILD
         fflush((FILE *)b->ptr);
+#endif  // !NATIVE_TARGET_BUILD
 #endif  // defined(OPENSSL_SYS_STARBOARD)
       break;
     case BIO_CTRL_DUP:
@@ -574,6 +630,7 @@
   buf[index] = '\0';
   ret = 0;
 #else   // defined(OPENSSL_SYS_STARBOARD)
+#ifndef NATIVE_TARGET_BUILD
   if (bp->flags & BIO_FLAGS_UPLINK) {
     if (!UP_fgets(buf, size, bp->ptr))
       goto err;
@@ -581,6 +638,10 @@
     if (!fgets(buf, size, (FILE *)bp->ptr))
       goto err;
   }
+#else  // !NATIVE_TARGET_BUILD
+   if (!fgets(buf, size, (FILE *)bp->ptr))
+     goto err;
+#endif  // !NATIVE_TARGET_BUILD
 #endif  // defined(OPENSSL_SYS_STARBOARD)
   if (buf[0] != '\0')
     ret = strlen(buf);
@@ -598,4 +659,35 @@
 
 #endif /* OPENSSL_NO_STDIO */
 
+#ifdef NATIVE_TARGET_BUILD
+// These definitions were restored from revision 5470252 of this file.
+int BIO_get_fp(BIO *bio, FILE **out_file) {
+  return BIO_ctrl(bio, BIO_C_GET_FILE_PTR, 0, (char*) out_file);
+}
+
+int BIO_set_fp(BIO *bio, FILE *file, int close_flag) {
+  return BIO_ctrl(bio, BIO_C_SET_FILE_PTR, close_flag, (char *) file);
+}
+
+int BIO_read_filename(BIO *bio, const char *filename) {
+  return BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_READ,
+                  (char *)filename);
+}
+
+int BIO_write_filename(BIO *bio, const char *filename) {
+  return BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_WRITE,
+                  (char *)filename);
+}
+
+int BIO_append_filename(BIO *bio, const char *filename) {
+  return BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_APPEND,
+                  (char *)filename);
+}
+
+int BIO_rw_filename(BIO *bio, const char *filename) {
+  return BIO_ctrl(bio, BIO_C_SET_FILENAME,
+                  BIO_CLOSE | BIO_FP_READ | BIO_FP_WRITE, (char *)filename);
+}
+#endif  // NATIVE_TARGET_BUILD
+
 #endif /* HEADER_BSS_FILE_C */
diff --git a/third_party/boringssl/src/crypto/internal.h b/third_party/boringssl/src/crypto/internal.h
index 41bbe33..7d25ec3 100644
--- a/third_party/boringssl/src/crypto/internal.h
+++ b/third_party/boringssl/src/crypto/internal.h
@@ -116,9 +116,11 @@
 #include <assert.h>
 #include <string.h>
 
+#ifdef STARBOARD
 #include "starboard/atomic.h"
 #include "starboard/once.h"
 #include "starboard/thread.h"
+#endif
 
 #if !defined(__cplusplus)
 #if defined(__GNUC__) && \
diff --git a/third_party/boringssl/src/crypto/mem_native_target.c b/third_party/boringssl/src/crypto/mem_native_target.c
new file mode 100644
index 0000000..984b413
--- /dev/null
+++ b/third_party/boringssl/src/crypto/mem_native_target.c
@@ -0,0 +1,77 @@
+// Copyright 2023 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifdef NATIVE_TARGET_BUILD
+// Note: the definitions below are mostly copied from crypto/internal.h,
+// which was removed in an ealier change.
+
+#include <openssl/mem.h>
+
+#include <string.h>
+
+// Language bug workarounds.
+//
+// Most C standard library functions are undefined if passed NULL, even when the
+// corresponding length is zero. This gives them (and, in turn, all functions
+// which call them) surprising behavior on empty arrays. Some compilers will
+// miscompile code due to this rule. See also
+// https://www.imperialviolet.org/2016/06/26/nonnull.html
+//
+// These wrapper functions behave the same as the corresponding C standard
+// functions, but behave as expected when passed NULL if the length is zero.
+//
+// Note |OPENSSL_memcmp| is a different function from |CRYPTO_memcmp|.
+
+// C++ defines |memchr| as a const-correct overload.
+
+const void *OPENSSL_memchr(const void *s, int c, size_t n) {
+  if (n == 0) {
+    return NULL;
+  }
+
+  return memchr(s, c, n);
+}
+
+int OPENSSL_memcmp(const void *s1, const void *s2, size_t n) {
+  if (n == 0) {
+    return 0;
+  }
+
+  return memcmp(s1, s2, n);
+}
+
+void *OPENSSL_memcpy(void *dst, const void *src, size_t n) {
+  if (n == 0) {
+    return dst;
+  }
+
+  return memcpy(dst, src, n);
+}
+
+void *OPENSSL_memmove(void *dst, const void *src, size_t n) {
+  if (n == 0) {
+    return dst;
+  }
+
+  return memmove(dst, src, n);
+}
+
+void *OPENSSL_memset(void *dst, int c, size_t n) {
+  if (n == 0) {
+    return dst;
+  }
+
+  return memset(dst, c, n);
+}
+#endif  // NATIVE_TARGET_BUILD
diff --git a/third_party/boringssl/src/crypto/pem/pem_lib.c b/third_party/boringssl/src/crypto/pem/pem_lib.c
index 13c9fb4..fa2d952 100644
--- a/third_party/boringssl/src/crypto/pem/pem_lib.c
+++ b/third_party/boringssl/src/crypto/pem/pem_lib.c
@@ -55,13 +55,13 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
-#include <openssl/opensslconf.h>

-#if !defined(OPENSSL_SYS_STARBOARD)

-#include <assert.h>

-#include <ctypes.h>

-#include <stdio.h>

-#include <string.h>

-#endif  // !defined(OPENSSL_SYS_STARBOARD)

+#include <openssl/opensslconf.h>
+#if !defined(OPENSSL_SYS_STARBOARD)
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#endif  // !defined(OPENSSL_SYS_STARBOARD)
 
 #include <openssl/base64.h>
 #include <openssl/buf.h>
diff --git a/third_party/boringssl/src/crypto/x509/by_dir.c b/third_party/boringssl/src/crypto/x509/by_dir.c
index 59f879c..e8f7ba0 100644
--- a/third_party/boringssl/src/crypto/x509/by_dir.c
+++ b/third_party/boringssl/src/crypto/x509/by_dir.c
@@ -61,7 +61,13 @@
 #include <string.h>
 #endif  // !defined(OPENSSL_SYS_STARBOARD)
 
+#ifdef OPENSSL_SYS_STARBOARD
 #include "e_os.h"
+#endif
+
+#ifdef NATIVE_TARGET_BUILD
+#define LIST_SEPARATOR_CHAR ':'
+#endif
 
 #ifndef NO_SYS_TYPES_H
 #include <sys/types.h>
diff --git a/third_party/boringssl/src/include/openssl/thread.h b/third_party/boringssl/src/include/openssl/thread.h
index da3cdfa..7533f0d 100644
--- a/third_party/boringssl/src/include/openssl/thread.h
+++ b/third_party/boringssl/src/include/openssl/thread.h
@@ -61,9 +61,11 @@
 
 #include <openssl/base.h>
 
+#ifdef STARBOARD
 #include "starboard/atomic.h"
 #include "starboard/condition_variable.h"
 #include "starboard/mutex.h"
+#endif
 
 #if defined(__cplusplus)
 extern "C" {
diff --git a/third_party/boringssl/src/ssl/ssl_buffer.cc b/third_party/boringssl/src/ssl/ssl_buffer.cc
index 8eba518..e51ee92 100644
--- a/third_party/boringssl/src/ssl/ssl_buffer.cc
+++ b/third_party/boringssl/src/ssl/ssl_buffer.cc
@@ -37,7 +37,14 @@
               "SSL3_ALIGN_PAYLOAD must be a power of 2");
 
 void SSLBuffer::Clear() {
+
+// TODO(b/270864119): clean up direct references to Starboard APIs and use
+// OPENSSL_port_* shims everywhere instead.
+#ifdef STARBOARD
   SbMemoryDeallocate(buf_);  // Allocated with malloc().
+#else
+  OPENSSL_port_free(buf_);  // Allocated with malloc().
+#endif
   buf_ = nullptr;
   offset_ = 0;
   size_ = 0;
@@ -62,7 +69,11 @@
   //
   // We need to use SbMemoryAllocate in starboard.
   uint8_t *new_buf =
+#ifdef STARBOARD
       (uint8_t *)SbMemoryAllocate(new_cap + SSL3_ALIGN_PAYLOAD - 1);
+#else
+      (uint8_t *)OPENSSL_port_malloc(new_cap + SSL3_ALIGN_PAYLOAD - 1);
+#endif
   if (new_buf == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return false;
@@ -74,7 +85,11 @@
 
   if (buf_ != NULL) {
     OPENSSL_memcpy(new_buf + new_offset, buf_ + offset_, size_);
+#ifdef STARBOARD
     SbMemoryDeallocate(buf_);  // Allocated with malloc().
+#else
+    OPENSSL_port_free(buf_);  // Allocated with malloc().
+#endif
   }
 
   buf_ = new_buf;
diff --git a/third_party/boringssl/src/ssl/ssl_session.cc b/third_party/boringssl/src/ssl/ssl_session.cc
index 6ebd29e..7c55a13 100644
--- a/third_party/boringssl/src/ssl/ssl_session.cc
+++ b/third_party/boringssl/src/ssl/ssl_session.cc
@@ -850,7 +850,13 @@
       ticket_age_add_valid(false),
       is_server(false) {
   CRYPTO_new_ex_data(&ex_data);
+#ifdef STARBOARD
   time = OPENSSL_port_time(nullptr);
+#else
+  // OPENSSL_port_time can't be used here because the name conflict between
+  // the variable and system call needs to be resolved.
+  time = ::time(nullptr);
+#endif
 }
 
 ssl_session_st::~ssl_session_st() {
diff --git a/third_party/chromium/media/base/starboard_utils.cc b/third_party/chromium/media/base/starboard_utils.cc
index 96fb524..251934f 100644
--- a/third_party/chromium/media/base/starboard_utils.cc
+++ b/third_party/chromium/media/base/starboard_utils.cc
@@ -83,6 +83,11 @@
     case AudioCodec::kPCM:
       return kSbMediaAudioCodecPcm;
 #endif  // SB_API_VERSION >= 14
+#if SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
+    // TODO(b/271301103): Enable this once IAMF is added to Chromium.
+    // case AudioCodec::kIAMF:
+    //  return kSbMediaAudioCodecPcm;
+#endif  // SB_API_VERSION >= SB_MEDIA_IAMF_SUPPORT_API_VERSION
     default:
       // Cobalt only supports a subset of audio codecs defined by Chromium.
       DLOG(ERROR) << "Unsupported audio codec " << GetCodecName(codec);
@@ -119,38 +124,42 @@
   return kSbMediaVideoCodecNone;
 }
 
-SbMediaAudioSampleInfo MediaAudioConfigToSbMediaAudioSampleInfo(
+SbMediaAudioStreamInfo MediaAudioConfigToSbMediaAudioStreamInfo(
     const AudioDecoderConfig& audio_decoder_config,
     const char* mime_type) {
   DCHECK(audio_decoder_config.IsValidConfig());
 
-  SbMediaAudioSampleInfo audio_sample_info;
+  SbMediaAudioStreamInfo audio_stream_info;
 
-  audio_sample_info.codec =
+  audio_stream_info.codec =
       MediaAudioCodecToSbMediaAudioCodec(audio_decoder_config.codec());
-  audio_sample_info.mime = mime_type;
-  // TODO: Make this work with non AAC audio.
-  audio_sample_info.format_tag = 0x00ff;
-  audio_sample_info.number_of_channels =
-      ChannelLayoutToChannelCount(audio_decoder_config.channel_layout());
-  audio_sample_info.samples_per_second =
-      audio_decoder_config.samples_per_second();
-  audio_sample_info.average_bytes_per_second = 1;
-  audio_sample_info.block_alignment = 4;
-  audio_sample_info.bits_per_sample = audio_decoder_config.bits_per_channel();
+  audio_stream_info.mime = mime_type;
 
-  const auto& extra_data = audio_sample_info.codec == kSbMediaAudioCodecAac
+#if SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  audio_stream_info.format_tag = 0x00ff;
+#endif // SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  audio_stream_info.number_of_channels =
+      ChannelLayoutToChannelCount(audio_decoder_config.channel_layout());
+  audio_stream_info.samples_per_second =
+      audio_decoder_config.samples_per_second();
+#if SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  audio_stream_info.average_bytes_per_second = 1;
+  audio_stream_info.block_alignment = 4;
+#endif // SB_API_VERSION < SB_MEDIA_ENHANCED_AUDIO_API_VERSION
+  audio_stream_info.bits_per_sample = audio_decoder_config.bits_per_channel();
+
+  const auto& extra_data = audio_stream_info.codec == kSbMediaAudioCodecAac
                                ? audio_decoder_config.aac_extra_data()
                                : audio_decoder_config.extra_data();
-  audio_sample_info.audio_specific_config_size =
+  audio_stream_info.audio_specific_config_size =
       static_cast<uint16_t>(extra_data.size());
-  if (audio_sample_info.audio_specific_config_size == 0) {
-    audio_sample_info.audio_specific_config = NULL;
+  if (audio_stream_info.audio_specific_config_size == 0) {
+    audio_stream_info.audio_specific_config = NULL;
   } else {
-    audio_sample_info.audio_specific_config = extra_data.data();
+    audio_stream_info.audio_specific_config = extra_data.data();
   }
 
-  return audio_sample_info;
+  return audio_stream_info;
 }
 
 DemuxerStream::Type SbMediaTypeToDemuxerStreamType(SbMediaType type) {
diff --git a/third_party/chromium/media/base/starboard_utils.h b/third_party/chromium/media/base/starboard_utils.h
index fe9febc..50920bd 100644
--- a/third_party/chromium/media/base/starboard_utils.h
+++ b/third_party/chromium/media/base/starboard_utils.h
@@ -31,7 +31,7 @@
 SbMediaAudioCodec MediaAudioCodecToSbMediaAudioCodec(AudioCodec codec);
 SbMediaVideoCodec MediaVideoCodecToSbMediaVideoCodec(VideoCodec codec);
 
-SbMediaAudioSampleInfo MediaAudioConfigToSbMediaAudioSampleInfo(
+SbMediaAudioStreamInfo MediaAudioConfigToSbMediaAudioStreamInfo(
     const AudioDecoderConfig& audio_decoder_config,
     const char* mime_type);
 
diff --git a/third_party/crashpad/build/crashpad_buildconfig.gni b/third_party/crashpad/build/crashpad_buildconfig.gni
index 7be9eaf..46d6aee 100644
--- a/third_party/crashpad/build/crashpad_buildconfig.gni
+++ b/third_party/crashpad/build/crashpad_buildconfig.gni
@@ -25,15 +25,21 @@
   if (defined(is_starboard) && is_starboard) {
     crashpad_dependencies = "starboard"
   }
+
+  if (defined(is_native_target_build) && is_native_target_build) {
+    crashpad_dependencies = "native_target_build"
+  }
 }
 
 assert(
     crashpad_dependencies == "chromium" || crashpad_dependencies == "starboard" ||
+    crashpad_dependencies == "native_target_build" ||
     crashpad_dependencies == "fuchsia" || crashpad_dependencies == "standalone" ||
     crashpad_dependencies == "external" || crashpad_dependencies == "dart")
 
 crashpad_is_in_chromium = crashpad_dependencies == "chromium"
 crashpad_is_in_starboard = crashpad_dependencies == "starboard"
+crashpad_is_in_native_target_build = crashpad_dependencies == "native_target_build"
 crashpad_is_in_fuchsia = crashpad_dependencies == "fuchsia"
 crashpad_is_in_dart = crashpad_dependencies == "dart"
 crashpad_is_external = crashpad_dependencies == "external"
@@ -52,7 +58,7 @@
   crashpad_is_clang = is_clang
 } else {
   # External and Dart SDK builds assume crashpad and mini_chromium are peers.
-  if (is_starboard) {
+  if (crashpad_is_in_starboard || crashpad_is_in_native_target_build) {
     import("../../mini_chromium/build/compiler.gni")
     import("../../mini_chromium/build/platform.gni")
   } else if (crashpad_is_external || crashpad_is_in_dart) {
diff --git a/third_party/crashpad/client/BUILD.gn b/third_party/crashpad/client/BUILD.gn
index 6ecd5c5..9ac4137 100644
--- a/third_party/crashpad/client/BUILD.gn
+++ b/third_party/crashpad/client/BUILD.gn
@@ -94,6 +94,8 @@
 
   if (crashpad_is_in_starboard) {
     public_deps += [ "//starboard/elf_loader:evergreen_info" ]
+  }
+  if (crashpad_is_in_starboard || crashpad_is_in_native_target_build) {
     deps += [ "../wrapper/proto:crashpad_annotations_proto" ]
   }
 
@@ -122,7 +124,7 @@
   }
 }
 
-if (!crashpad_is_in_starboard) {
+if (!crashpad_is_in_starboard && !crashpad_is_in_native_target_build) {
   source_set("client_test") {
     testonly = true
 
diff --git a/third_party/crashpad/client/client_argv_handling.cc b/third_party/crashpad/client/client_argv_handling.cc
index 742a000..de8dd12 100644
--- a/third_party/crashpad/client/client_argv_handling.cc
+++ b/third_party/crashpad/client/client_argv_handling.cc
@@ -32,6 +32,9 @@
     const base::FilePath& database,
     const base::FilePath& metrics_dir,
     const std::string& url,
+#if defined(STARBOARD)
+    const std::string& ca_certificates_path,
+#endif  // STARBOARD
     const std::map<std::string, std::string>& annotations,
     const std::vector<std::string>& arguments) {
   std::vector<std::string> argv_strings(1, handler.value());
@@ -53,6 +56,14 @@
     argv_strings.push_back(FormatArgumentString("url", url));
   }
 
+
+#if defined(STARBOARD)
+  if (!ca_certificates_path.empty()) {
+    argv_strings.push_back(FormatArgumentString("ca-certificates-path",
+                                                ca_certificates_path));
+  }
+#endif  // STARBOARD
+
   for (const auto& kv : annotations) {
     argv_strings.push_back(
         FormatArgumentString("annotation", kv.first + '=' + kv.second));
diff --git a/third_party/crashpad/client/client_argv_handling.h b/third_party/crashpad/client/client_argv_handling.h
index 04c66d3..7fe86a8 100644
--- a/third_party/crashpad/client/client_argv_handling.h
+++ b/third_party/crashpad/client/client_argv_handling.h
@@ -34,6 +34,9 @@
     const base::FilePath& database,
     const base::FilePath& metrics_dir,
     const std::string& url,
+#if defined(STARBOARD)
+    const std::string& ca_certificates_path,
+#endif  // STARBOARD
     const std::map<std::string, std::string>& annotations,
     const std::vector<std::string>& arguments);
 
diff --git a/third_party/crashpad/client/crashpad_client.h b/third_party/crashpad/client/crashpad_client.h
index 4cf5a0e..44dd6b6 100644
--- a/third_party/crashpad/client/crashpad_client.h
+++ b/third_party/crashpad/client/crashpad_client.h
@@ -97,6 +97,12 @@
   //!     path as its `--metrics-dir` argument.
   //! \param[in] url The URL of an upload server. The handler will be started
   //!     with this URL as its `--url` argument.
+#if defined(STARBOARD)
+  //! \param[in] ca_certificates_path The absolute path to a directory
+  //!     containing trusted Certificate Authority (CA) root certificates. The
+  //!     handler will be started with this path as its `--ca-certificates-path`
+  //!     argument.
+#endif  // STARBOARD
   //! \param[in] annotations Process annotations to set in each crash report.
   //!     The handler will be started with an `--annotation` argument for each
   //!     element in this map.
@@ -118,6 +124,9 @@
                     const base::FilePath& database,
                     const base::FilePath& metrics_dir,
                     const std::string& url,
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+                    const std::string& ca_certificates_path,
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
                     const std::map<std::string, std::string>& annotations,
                     const std::vector<std::string>& arguments,
                     bool restartable,
@@ -147,7 +156,7 @@
   bool SetHandlerSocket(ScopedFileHandle sock, pid_t pid);
 #endif  // OS_ANDROID || OS_LINUX || DOXYGEN
 
-#if defined(OS_ANDROID) || DOXYGEN
+#if (defined(OS_ANDROID) || DOXYGEN) && !defined(STARBOARD)
   //! \brief Installs a signal handler to execute `/system/bin/app_process` and
   //!     load a Java class in response to a crash.
   //!
@@ -314,7 +323,7 @@
       const std::map<std::string, std::string>& annotations,
       const std::vector<std::string>& arguments,
       int socket);
-#endif  // OS_ANDROID || DOXYGEN
+#endif  // (defined(OS_ANDROID) || DOXYGEN) && !defined(STARBOARD)
 
 #if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN
   //! \brief Installs a signal handler to launch a handler process in reponse to
@@ -330,6 +339,12 @@
   //!     path as its `--metrics-dir` argument.
   //! \param[in] url The URL of an upload server. The handler will be started
   //!     with this URL as its `--url` argument.
+#if defined(STARBOARD)
+  //! \param[in] ca_certificates_path The absolute path to a directory
+  //!     containing trusted Certificate Authority (CA) root certificates. The
+  //!     handler will be started with this path as its `--ca-certificates-path`
+  //!     argument.
+#endif  // STARBOARD
   //! \param[in] annotations Process annotations to set in each crash report.
   //!     The handler will be started with an `--annotation` argument for each
   //!     element in this map.
@@ -344,9 +359,13 @@
       const base::FilePath& database,
       const base::FilePath& metrics_dir,
       const std::string& url,
+  #if defined(STARBOARD)
+      const std::string& ca_certificates_path,
+  #endif  // STARBOARD
       const std::map<std::string, std::string>& annotations,
       const std::vector<std::string>& arguments);
 
+#if !defined(STARBOARD)
   //! \brief Starts a handler process with an initial client.
   //!
   //! This method allows a process to launch the handler process on behalf of
@@ -379,6 +398,7 @@
       const std::map<std::string, std::string>& annotations,
       const std::vector<std::string>& arguments,
       int socket);
+#endif  // !defined(STARBOARD)
 
 #if defined(STARBOARD)
   //! \brief Sends mapping info to the handler
diff --git a/third_party/crashpad/client/crashpad_client_linux.cc b/third_party/crashpad/client/crashpad_client_linux.cc
index 1b579bf..a1a9078 100644
--- a/third_party/crashpad/client/crashpad_client_linux.cc
+++ b/third_party/crashpad/client/crashpad_client_linux.cc
@@ -26,7 +26,9 @@
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
 #include "client/client_argv_handling.h"
+#ifdef STARBOARD
 #include "starboard/common/mutex.h"
+#endif
 #include "third_party/lss/lss.h"
 #include "util/file/file_io.h"
 #include "util/file/filesystem.h"
@@ -95,7 +97,7 @@
 }
 #endif
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) && !defined(STARBOARD)
 
 std::vector<std::string> BuildAppProcessArgs(
     const std::string& class_name,
@@ -163,7 +165,7 @@
   return argv;
 }
 
-#endif  // OS_ANDROID
+#endif  // defined(OS_ANDROID) && !defined(STARBOARD)
 
 // A base class for Crashpad signal handler implementations.
 class SignalHandler {
@@ -565,6 +567,9 @@
     const base::FilePath& database,
     const base::FilePath& metrics_dir,
     const std::string& url,
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+    const std::string& ca_certificates_path,
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
     const std::map<std::string, std::string>& annotations,
     const std::vector<std::string>& arguments,
     bool restartable,
@@ -578,7 +583,15 @@
   }
 
   std::vector<std::string> argv = BuildHandlerArgvStrings(
-      handler, database, metrics_dir, url, annotations, arguments);
+      handler,
+      database,
+      metrics_dir,
+      url,
+#if defined(STARBOARD)
+      ca_certificates_path,
+#endif  // STARBOARD
+      annotations,
+      arguments);
 
   argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get()));
   argv.push_back("--shared-client-connection");
@@ -609,7 +622,7 @@
 }
 #endif  // OS_ANDROID || OS_LINUX
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) && !defined(STARBOARD)
 
 bool CrashpadClient::StartJavaHandlerAtCrash(
     const std::string& class_name,
@@ -695,22 +708,34 @@
   return DoubleForkAndExec(argv, env, socket, false, nullptr);
 }
 
-#endif
+#endif  // defined(OS_ANDROID) && !defined(STARBOARD)
 
 bool CrashpadClient::StartHandlerAtCrash(
     const base::FilePath& handler,
     const base::FilePath& database,
     const base::FilePath& metrics_dir,
     const std::string& url,
+#if defined(STARBOARD)
+    const std::string& ca_certificates_path,
+#endif  // STARBOARD
     const std::map<std::string, std::string>& annotations,
     const std::vector<std::string>& arguments) {
   std::vector<std::string> argv = BuildHandlerArgvStrings(
-      handler, database, metrics_dir, url, annotations, arguments);
+      handler,
+      database,
+      metrics_dir,
+      url,
+#if defined(STARBOARD)
+      ca_certificates_path,
+#endif  // STARBOARD
+      annotations,
+      arguments);
 
   auto signal_handler = LaunchAtCrashHandler::Get();
   return signal_handler->Initialize(&argv, nullptr, &unhandled_signals_);
 }
 
+#if !defined(STARBOARD)
 // static
 bool CrashpadClient::StartHandlerForClient(
     const base::FilePath& handler,
@@ -727,6 +752,7 @@
 
   return DoubleForkAndExec(argv, nullptr, socket, true, nullptr);
 }
+#endif  // !defined(STARBOARD)
 
 #if defined(STARBOARD)
 // static
diff --git a/third_party/crashpad/compat/BUILD.gn b/third_party/crashpad/compat/BUILD.gn
index 56a675a..c22447a 100644
--- a/third_party/crashpad/compat/BUILD.gn
+++ b/third_party/crashpad/compat/BUILD.gn
@@ -57,7 +57,7 @@
 }
 
 compat_target("compat") {
-  if (crashpad_is_in_starboard) {
+  if (crashpad_is_in_starboard || crashpad_is_in_native_target_build) {
     check_includes = false
   }
 
@@ -90,7 +90,7 @@
     ]
   }
 
-  if (!crashpad_is_in_starboard && crashpad_is_android) {
+  if (!crashpad_is_in_starboard && !crashpad_is_in_native_target_build && crashpad_is_android) {
     sources += [
       "android/android/api-level.cc",
       "android/android/api-level.h",
diff --git a/third_party/crashpad/handler/BUILD.gn b/third_party/crashpad/handler/BUILD.gn
index c91280d..b8ce718 100644
--- a/third_party/crashpad/handler/BUILD.gn
+++ b/third_party/crashpad/handler/BUILD.gn
@@ -52,7 +52,8 @@
     ]
   }
 
-  if (crashpad_is_linux && !crashpad_is_in_starboard) {
+  if (crashpad_is_linux && !crashpad_is_in_starboard &&
+      !crashpad_is_in_native_target_build) {
     sources += [
       "linux/cros_crash_report_exception_handler.cc",
       "linux/cros_crash_report_exception_handler.h",
@@ -89,7 +90,7 @@
     "../tools:tool_support",
   ]
 
-  if (crashpad_is_in_starboard) {
+  if (crashpad_is_in_starboard || crashpad_is_in_native_target_build) {
     sources += [ "../util/net/http_transport_socket.cc" ]
     defines = [ "CRASHPAD_USE_BORINGSSL" ]
     deps += [ "//third_party/boringssl:crypto_full" ]
@@ -118,7 +119,7 @@
   }
 }
 
-if (!crashpad_is_in_starboard) {
+if (!crashpad_is_in_starboard && !crashpad_is_in_native_target_build) {
   source_set("handler_test") {
     testonly = true
 
@@ -153,10 +154,9 @@
   }
 }
 
-# TODO: b/256660693 Disabled crashpad_handler executables due to build issues on Android
-if (!crashpad_is_ios && !crashpad_is_android) {
-  if (crashpad_is_in_starboard) {
-    config("crashpad_handler_starboard_config") {
+if (!crashpad_is_ios) {
+  if (crashpad_is_in_starboard || crashpad_is_in_native_target_build) {
+    config("customized_for_cobalt_crashpad_handler_config") {
       cflags = [
         "-ffunction-sections",
         "-fdata-sections",
@@ -169,10 +169,18 @@
     }
   }
 
+  if (crashpad_is_in_native_target_build) {
+    config("crashpad_handler_native_target_config") {
+      # This is to undo the "main=StarboardMain" define added for all targets
+      # when final_executable_type == "shared_library", in
+      # starboard/build/config/BUILD.gn, which itself is admittedly a hack.
+      defines = [ "StarboardMain=main" ]
+    }
+  }
+
   crashpad_executable("crashpad_handler") {
     if (crashpad_is_in_starboard) {
       install_target = !crashpad_is_android
-      check_includes = false
       data_deps = [ "//third_party/icu:icudata" ]
     }
 
@@ -198,8 +206,13 @@
       }
     }
 
-    if (crashpad_is_in_starboard) {
-      configs += [ ":crashpad_handler_starboard_config" ]
+    if (crashpad_is_in_starboard || crashpad_is_in_native_target_build) {
+      check_includes = false
+      configs += [ ":customized_for_cobalt_crashpad_handler_config" ]
+    }
+
+    if (crashpad_is_in_native_target_build) {
+      configs += [ ":crashpad_handler_native_target_config" ]
     }
   }
 }
@@ -209,29 +222,31 @@
 # installer will ignore files not named like a shared object, so give the
 # handler executable an acceptable name.
 # TODO: b/256660693 Disabled crashpad_handler executables due to build issues on Android
-# if (crashpad_is_android) {
-#   copy("crashpad_handler_named_as_so") {
-#     deps = [ ":crashpad_handler" ]
-# 
-#     sources = [ "$root_out_dir/crashpad_handler" ]
-# 
-#     outputs = [ "$root_out_dir/libcrashpad_handler.so" ]
-#   }
-# 
-#   crashpad_executable("crashpad_handler_trampoline") {
-#     output_name = "libcrashpad_handler_trampoline.so"
-# 
-#     sources = [ "linux/handler_trampoline.cc" ]
-# 
-#     ldflags = [ "-llog" ]
-# 
-#     if (crashpad_is_in_chromium) {
-#       no_default_deps = true
-#     }
-#   }
-# }
+if (crashpad_is_android &&
+    !(crashpad_is_in_starboard || crashpad_is_in_native_target_build)) {
+  copy("crashpad_handler_named_as_so") {
+    deps = [ ":crashpad_handler" ]
 
-if (!crashpad_is_ios && !crashpad_is_in_starboard) {
+    sources = [ "$root_out_dir/crashpad_handler" ]
+
+    outputs = [ "$root_out_dir/libcrashpad_handler.so" ]
+  }
+
+  crashpad_executable("crashpad_handler_trampoline") {
+    output_name = "libcrashpad_handler_trampoline.so"
+
+    sources = [ "linux/handler_trampoline.cc" ]
+
+    ldflags = [ "-llog" ]
+
+    if (crashpad_is_in_chromium) {
+      no_default_deps = true
+    }
+  }
+}
+
+if (!crashpad_is_ios && !crashpad_is_in_starboard &&
+    !crashpad_is_in_native_target_build) {
   crashpad_executable("crashpad_handler_test_extended_handler") {
     testonly = true
 
diff --git a/third_party/crashpad/handler/crash_report_upload_thread.cc b/third_party/crashpad/handler/crash_report_upload_thread.cc
index db960b1..8d1ff3c 100644
--- a/third_party/crashpad/handler/crash_report_upload_thread.cc
+++ b/third_party/crashpad/handler/crash_report_upload_thread.cc
@@ -45,11 +45,18 @@
 
 namespace crashpad {
 
-CrashReportUploadThread::CrashReportUploadThread(CrashReportDatabase* database,
-                                                 const std::string& url,
-                                                 const Options& options)
+CrashReportUploadThread::CrashReportUploadThread(
+    CrashReportDatabase* database,
+    const std::string& url,
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+    const std::string& ca_certificates_path,
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+    const Options& options)
     : options_(options),
       url_(url),
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+      ca_certificates_path_(ca_certificates_path),
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
       // When watching for pending reports, check every 15 minutes, even in the
       // absence of a signal from the handler thread. This allows for failed
       // uploads to be retried periodically, and for pending reports written by
@@ -241,7 +248,7 @@
                                   Metrics::CrashSkippedReason::kUploadFailed);
       break;
   }
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   database_->RemoveOldReports(/*num_reports_to_save=*/2);
 #endif
 }
@@ -329,6 +336,9 @@
     }
   }
   http_transport->SetURL(url);
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+  http_transport->SetRootCACertificatesDirectoryPath(ca_certificates_path_);
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 
   if (!http_transport->ExecuteSynchronously(response_body)) {
     return UploadResult::kRetry;
diff --git a/third_party/crashpad/handler/crash_report_upload_thread.h b/third_party/crashpad/handler/crash_report_upload_thread.h
index 2ec1147..095b995 100644
--- a/third_party/crashpad/handler/crash_report_upload_thread.h
+++ b/third_party/crashpad/handler/crash_report_upload_thread.h
@@ -66,9 +66,16 @@
   //!
   //! \param[in] database The database to upload crash reports from.
   //! \param[in] url The URL of the server to upload crash reports to.
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+  //! \param[in] ca_certificates_path The absolute path to a directory
+  //!   containing CA root certificates.
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   //! \param[in] options Options for the report uploads.
   CrashReportUploadThread(CrashReportDatabase* database,
                           const std::string& url,
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+                          const std::string& ca_certificates_path,
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
                           const Options& options);
   ~CrashReportUploadThread();
 
@@ -169,6 +176,9 @@
 
   const Options options_;
   const std::string url_;
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+  const std::string ca_certificates_path_;
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   WorkerThread thread_;
   ThreadSafeVector<UUID> known_pending_report_uuids_;
   CrashReportDatabase* database_;  // weak
diff --git a/third_party/crashpad/handler/handler_main.cc b/third_party/crashpad/handler/handler_main.cc
index cbf9bb5..d42289b 100644
--- a/third_party/crashpad/handler/handler_main.cc
+++ b/third_party/crashpad/handler/handler_main.cc
@@ -169,7 +169,7 @@
 "      --trace-parent-with-exception=EXCEPTION_INFORMATION_ADDRESS\n"
 "                              request a dump for the handler's parent process\n"
 #endif  // OS_LINUX || OS_ANDROID
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 "      --evergreen-information=EVERGREEN_INFORMATION_ADDRESS\n"
 "                              the address of a EvegreenInfo struct.\n"
 "      --handler-started-at-crash\n"
@@ -215,10 +215,11 @@
   VMAddress sanitization_information_address;
   int initial_client_fd;
   bool shared_client_connection;
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   VMAddress evergreen_information_address;
   bool handler_started_at_crash = false;
-#endif  // defined(STARBOARD)
+  std::string ca_certificates_path;
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 #if defined(OS_ANDROID)
   bool write_minidump_to_log;
   bool write_minidump_to_database;
@@ -503,6 +504,9 @@
                                     options.database,
                                     base::FilePath(),
                                     options.url,
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+                                    options.ca_certificates_path,
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
                                     options.annotations,
                                     extra_arguments,
                                     true,
@@ -592,10 +596,11 @@
     kOptionSanitizationInformation,
     kOptionSharedClientConnection,
     kOptionTraceParentWithException,
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
     kOptionEvergreenInformaton,
     kOptionHandlerStartedAtCrash,
-#endif  // defined(STARBOARD)
+    kOptionCACertificatesPath,
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 #endif
     kOptionURL,
 #if defined(OS_CHROMEOS)
@@ -676,7 +681,7 @@
      nullptr,
      kOptionTraceParentWithException},
 #endif  // OS_LINUX || OS_ANDROID
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
     {"evergreen-information",
      required_argument,
      nullptr,
@@ -685,6 +690,10 @@
      no_argument,
      nullptr,
      kOptionHandlerStartedAtCrash},
+    {"ca-certificates-path",
+     required_argument,
+     nullptr,
+     kOptionCACertificatesPath},
 #endif
     {"url", required_argument, nullptr, kOptionURL},
 #if defined(OS_CHROMEOS)
@@ -849,7 +858,7 @@
         }
         break;
       }
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
       case kOptionEvergreenInformaton: {
         if (!StringToNumber(optarg,
                             &options.evergreen_information_address)) {
@@ -863,7 +872,11 @@
         options.handler_started_at_crash = true;
         break;
       }
-#endif   // defined(STARBOARD)
+      case kOptionCACertificatesPath: {
+        options.ca_certificates_path = optarg;
+        break;
+      }
+#endif   // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 #endif  // OS_LINUX || OS_ANDROID
       case kOptionURL: {
         options.url = optarg;
@@ -1019,7 +1032,12 @@
     upload_thread_options.watch_pending_reports = options.periodic_tasks;
 
     upload_thread.Reset(new CrashReportUploadThread(
-        database.get(), options.url, upload_thread_options));
+        database.get(),
+        options.url,
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+        options.ca_certificates_path,
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+        upload_thread_options));
     upload_thread.Get()->Start();
   }
 
@@ -1080,13 +1098,13 @@
     info.exception_information_address = options.exception_information_address;
     info.sanitization_information_address =
         options.sanitization_information_address;
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
     info.evergreen_information_address =
         options.evergreen_information_address;
     if (options.handler_started_at_crash) {
       info.handler_start_type = ExceptionHandlerProtocol::kStartAtCrash;
     }
-#endif   // defined(STARBOARD)
+#endif   // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
     return exception_handler->HandleException(getppid(), geteuid(), info)
                ? EXIT_SUCCESS
                : ExitFailure();
diff --git a/third_party/crashpad/handler/linux/capture_snapshot.cc b/third_party/crashpad/handler/linux/capture_snapshot.cc
index 59350fd..e9182fa 100644
--- a/third_party/crashpad/handler/linux/capture_snapshot.cc
+++ b/third_party/crashpad/handler/linux/capture_snapshot.cc
@@ -34,7 +34,7 @@
     std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot) {
   std::unique_ptr<ProcessSnapshotLinux> process_snapshot(
       new ProcessSnapshotLinux());
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   if (!process_snapshot->Initialize(connection,
                                     info.evergreen_information_address,
                                     info.serialized_annotations_address,
diff --git a/third_party/crashpad/handler/linux/capture_snapshot.h b/third_party/crashpad/handler/linux/capture_snapshot.h
index b01e316..cc40cfe 100644
--- a/third_party/crashpad/handler/linux/capture_snapshot.h
+++ b/third_party/crashpad/handler/linux/capture_snapshot.h
@@ -27,7 +27,7 @@
 #include "util/linux/ptrace_connection.h"
 #include "util/misc/address_types.h"
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 #include "starboard/elf_loader/evergreen_info.h"
 #endif
 
diff --git a/third_party/crashpad/handler/linux/crash_report_exception_handler.cc b/third_party/crashpad/handler/linux/crash_report_exception_handler.cc
index 9a86073..59b6695 100644
--- a/third_party/crashpad/handler/linux/crash_report_exception_handler.cc
+++ b/third_party/crashpad/handler/linux/crash_report_exception_handler.cc
@@ -34,7 +34,7 @@
 #include "util/stream/log_output_stream.h"
 #include "util/stream/zlib_output_stream.h"
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 #include "starboard/elf_loader/evergreen_info.h"
 #endif
 
@@ -80,7 +80,7 @@
 
 CrashReportExceptionHandler::~CrashReportExceptionHandler() = default;
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 bool CrashReportExceptionHandler::AddEvergreenInfo(
     const ExceptionHandlerProtocol::ClientInformation& info) {
   evergreen_info_ = info.evergreen_information_address;
diff --git a/third_party/crashpad/handler/linux/crash_report_exception_handler.h b/third_party/crashpad/handler/linux/crash_report_exception_handler.h
index 7a32ac0..9538565 100644
--- a/third_party/crashpad/handler/linux/crash_report_exception_handler.h
+++ b/third_party/crashpad/handler/linux/crash_report_exception_handler.h
@@ -28,7 +28,7 @@
 #include "util/misc/address_types.h"
 #include "util/misc/uuid.h"
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 #include "starboard/elf_loader/evergreen_info.h"
 #endif
 
@@ -84,7 +84,7 @@
                        pid_t* requesting_thread_id = nullptr,
                        UUID* local_report_id = nullptr) override;
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   bool AddEvergreenInfo(
       const ExceptionHandlerProtocol::ClientInformation& info) override;
   bool AddAnnotations(
@@ -120,7 +120,7 @@
   bool write_minidump_to_database_;
   bool write_minidump_to_log_;
   const UserStreamDataSources* user_stream_data_sources_;  // weak
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   VMAddress evergreen_info_;
   VMAddress serialized_annotations_address_;
   int serialized_annotations_size_;
diff --git a/third_party/crashpad/handler/linux/exception_handler_server.cc b/third_party/crashpad/handler/linux/exception_handler_server.cc
index 3c62e40..bcfbafc 100644
--- a/third_party/crashpad/handler/linux/exception_handler_server.cc
+++ b/third_party/crashpad/handler/linux/exception_handler_server.cc
@@ -36,7 +36,7 @@
 #include "util/linux/socket.h"
 #include "util/misc/as_underlying_type.h"
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 #include "starboard/elf_loader/evergreen_info.h"
 #endif
 
@@ -438,7 +438,7 @@
           event->fd.get(),
           event->type == Event::Type::kSharedSocketMessage);
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
     case ExceptionHandlerProtocol::ClientToServerMessage::kTypeAddEvergreenInfo:
       return HandleAddEvergreenInfoRequest(creds, message.client_info);
     case ExceptionHandlerProtocol::ClientToServerMessage::kTypeAddAnnotations:
@@ -451,7 +451,7 @@
   return false;
 }
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 bool ExceptionHandlerServer::HandleAddEvergreenInfoRequest(
     const ucred& creds,
     const ExceptionHandlerProtocol::ClientInformation& client_info) {
diff --git a/third_party/crashpad/handler/linux/exception_handler_server.h b/third_party/crashpad/handler/linux/exception_handler_server.h
index 1e87157..344e9ae 100644
--- a/third_party/crashpad/handler/linux/exception_handler_server.h
+++ b/third_party/crashpad/handler/linux/exception_handler_server.h
@@ -95,7 +95,7 @@
         pid_t* requesting_thread_id = nullptr,
         UUID* local_report_id = nullptr) = 0;
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
     //! \brief Called on receipt of a request to add Evergreen mapping info.
     //!
     //! \param[in] info Information on the client.
@@ -196,7 +196,7 @@
       int client_sock,
       bool multiple_clients);
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   bool HandleAddEvergreenInfoRequest(
       const ucred& creds,
       const ExceptionHandlerProtocol::ClientInformation& client_info);
diff --git a/third_party/crashpad/minidump/BUILD.gn b/third_party/crashpad/minidump/BUILD.gn
index 83da927..bd77072 100644
--- a/third_party/crashpad/minidump/BUILD.gn
+++ b/third_party/crashpad/minidump/BUILD.gn
@@ -87,12 +87,16 @@
       "/wd4324",  # 'struct' : structure was padded due to __declspec(align())
     ]
   }
+
+  if (crashpad_is_in_native_target_build) {
+    configs -= [ "//build/config/compiler:default_include_dirs" ]
+  }
 }
 
 static_library("test_support") {
   testonly = true
 
-  if (crashpad_is_in_starboard) {
+  if (crashpad_is_in_starboard || crashpad_is_in_native_target_build) {
     check_includes = false
   }
 
diff --git a/third_party/crashpad/snapshot/BUILD.gn b/third_party/crashpad/snapshot/BUILD.gn
index f47b3f2..0ec180d 100644
--- a/third_party/crashpad/snapshot/BUILD.gn
+++ b/third_party/crashpad/snapshot/BUILD.gn
@@ -20,7 +20,7 @@
 }
 
 static_library("snapshot") {
-  if (crashpad_is_in_starboard) {
+  if (crashpad_is_in_starboard || crashpad_is_in_native_target_build) {
     check_includes = false
   }
 
@@ -327,7 +327,7 @@
   }
 }
 
-if (!crashpad_is_in_starboard) {
+if (!crashpad_is_in_starboard && !crashpad_is_in_native_target_build) {
   source_set("snapshot_test") {
     testonly = true
 
diff --git a/third_party/crashpad/snapshot/linux/process_snapshot_linux.cc b/third_party/crashpad/snapshot/linux/process_snapshot_linux.cc
index 48fdb11..fea7eb2 100644
--- a/third_party/crashpad/snapshot/linux/process_snapshot_linux.cc
+++ b/third_party/crashpad/snapshot/linux/process_snapshot_linux.cc
@@ -19,7 +19,7 @@
 #include "base/logging.h"
 #include "util/linux/exception_information.h"
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 #include "third_party/crashpad/util/linux/exception_handler_protocol.h"
 // TODO(b/201538792): resolve conflict between mini_chromium and base functions.
 #ifdef LogMessage
@@ -62,7 +62,7 @@
   return true;
 }
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection,
                                       VMAddress evergreen_information_address,
                                       VMAddress serialized_annotations_address,
@@ -298,7 +298,7 @@
   for (const auto& module : modules_) {
     modules.push_back(module.get());
   }
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   if (evergreen_module_) {
     modules.push_back(evergreen_module_.get());
   }
@@ -367,7 +367,7 @@
   }
 }
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 void ProcessSnapshotLinux::InitializeModules(
     VMAddress evergreen_information_address) {
   for (const ProcessReaderLinux::Module& reader_module :
diff --git a/third_party/crashpad/snapshot/linux/process_snapshot_linux.h b/third_party/crashpad/snapshot/linux/process_snapshot_linux.h
index 0d20528..6b95c8e 100644
--- a/third_party/crashpad/snapshot/linux/process_snapshot_linux.h
+++ b/third_party/crashpad/snapshot/linux/process_snapshot_linux.h
@@ -42,7 +42,7 @@
 #include "util/process/process_id.h"
 #include "util/process/process_memory_range.h"
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 #include "snapshot/module_snapshot_evergreen.h"
 #include "starboard/elf_loader/evergreen_info.h"
 #include "third_party/crashpad/util/linux/exception_handler_protocol.h"
@@ -65,7 +65,7 @@
   //!     an appropriate message logged.
   bool Initialize(PtraceConnection* connection);
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   //! \brief Initializes the object with Evergreen information.
   //!
   //! \param[in] connection A connection to the process to snapshot.
@@ -159,7 +159,7 @@
  private:
   void InitializeThreads();
   void InitializeModules();
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   void InitializeModules(VMAddress evergreen_information_address);
   void AddAnnotations(VMAddress serialized_annotations_address,
                       int serialized_annotations_size);
@@ -172,7 +172,7 @@
   UUID client_id_;
   std::vector<std::unique_ptr<internal::ThreadSnapshotLinux>> threads_;
   std::vector<std::unique_ptr<internal::ModuleSnapshotElf>> modules_;
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   std::unique_ptr<internal::ModuleSnapshotEvergreen> evergreen_module_;
 #endif
   std::unique_ptr<internal::ExceptionSnapshotLinux> exception_;
diff --git a/third_party/crashpad/test/BUILD.gn b/third_party/crashpad/test/BUILD.gn
index 4f2f683..8ebc92c 100644
--- a/third_party/crashpad/test/BUILD.gn
+++ b/third_party/crashpad/test/BUILD.gn
@@ -166,7 +166,7 @@
   }
 }
 
-if (!crashpad_is_in_starboard) {
+if (!crashpad_is_in_starboard && !crashpad_is_in_native_target_build) {
   source_set("test_test") {
     testonly = true
 
diff --git a/third_party/crashpad/third_party/gtest/BUILD.gn b/third_party/crashpad/third_party/gtest/BUILD.gn
index 15f5001..85cbc64 100644
--- a/third_party/crashpad/third_party/gtest/BUILD.gn
+++ b/third_party/crashpad/third_party/gtest/BUILD.gn
@@ -15,7 +15,7 @@
 import("../../build/crashpad_buildconfig.gni")
 import("../../build/test.gni")
 
-if (crashpad_is_in_chromium || crashpad_is_in_starboard) {
+if (crashpad_is_in_chromium || crashpad_is_in_starboard || crashpad_is_in_native_target_build) {
   group("gtest") {
     testonly = true
     public_deps = [
diff --git a/third_party/crashpad/third_party/lss/lss.h b/third_party/crashpad/third_party/lss/lss.h
index 8cd9293..a60eec6 100644
--- a/third_party/crashpad/third_party/lss/lss.h
+++ b/third_party/crashpad/third_party/lss/lss.h
@@ -15,8 +15,8 @@
 #ifndef CRASHPAD_THIRD_PARTY_LSS_LSS_H_
 #define CRASHPAD_THIRD_PARTY_LSS_LSS_H_
 
-#if defined(STARBOARD)
-#include "third_party/linux-syscall-support/linux_syscall_support.h"
+#if defined(STARBOARD) || NATIVE_TARGET_BUILD
+#include "../../third_party/linux-syscall-support/linux_syscall_support.h"
 #elif defined(CRASHPAD_LSS_SOURCE_EXTERNAL)
 #include "third_party/lss/linux_syscall_support.h"
 #elif defined(CRASHPAD_LSS_SOURCE_EMBEDDED)
diff --git a/third_party/crashpad/third_party/mini_chromium/BUILD.gn b/third_party/crashpad/third_party/mini_chromium/BUILD.gn
index f7ec49d..96c4a1e 100644
--- a/third_party/crashpad/third_party/mini_chromium/BUILD.gn
+++ b/third_party/crashpad/third_party/mini_chromium/BUILD.gn
@@ -15,7 +15,7 @@
 import("../../build/crashpad_buildconfig.gni")
 
 group("base") {
-  if (crashpad_is_in_starboard) {
+  if (crashpad_is_in_starboard || crashpad_is_in_native_target_build) {
     public_deps = [ "//third_party/mini_chromium/base" ]
   } else if (crashpad_is_in_chromium) {
     public_deps = [ "//base" ]
diff --git a/third_party/crashpad/third_party/zlib/BUILD.gn b/third_party/crashpad/third_party/zlib/BUILD.gn
index 70657f2..f4e949a 100644
--- a/third_party/crashpad/third_party/zlib/BUILD.gn
+++ b/third_party/crashpad/third_party/zlib/BUILD.gn
@@ -15,7 +15,7 @@
 import("../../build/crashpad_buildconfig.gni")
 
 if (crashpad_is_in_chromium || crashpad_is_in_fuchsia || crashpad_is_in_dart ||
-    crashpad_is_in_starboard) {
+    crashpad_is_in_starboard || crashpad_is_in_native_target_build) {
   zlib_source = "external"
 } else if (!crashpad_is_win && !crashpad_is_fuchsia) {
   zlib_source = "system"
diff --git a/third_party/crashpad/tools/BUILD.gn b/third_party/crashpad/tools/BUILD.gn
index a002795..402f04a 100644
--- a/third_party/crashpad/tools/BUILD.gn
+++ b/third_party/crashpad/tools/BUILD.gn
@@ -25,13 +25,17 @@
   public_configs = [ "..:crashpad_config" ]
 
   deps = [ "../third_party/mini_chromium:base" ]
+
+  if (crashpad_is_in_native_target_build) {
+    configs -= [ "//build/config/compiler:default_include_dirs" ]
+  }
 }
 
 # TODO(b/251521595): resolve GN error and enable this target to be built for
 # android.
 if (!crashpad_is_ios && !crashpad_is_android) {
   crashpad_executable("crashpad_database_util") {
-    check_includes = !crashpad_is_in_starboard
+    check_includes = !crashpad_is_in_starboard && !crashpad_is_in_native_target_build
 
     sources = [ "crashpad_database_util.cc" ]
 
@@ -51,7 +55,7 @@
     }
   }
 
-  if (!crashpad_is_in_starboard) {
+  if (!crashpad_is_in_starboard && !crashpad_is_in_native_target_build) {
     crashpad_executable("crashpad_http_upload") {
       sources = [ "crashpad_http_upload.cc" ]
 
@@ -66,7 +70,7 @@
   }
 }
 
-if (!crashpad_is_in_starboard) {
+if (!crashpad_is_in_starboard && !crashpad_is_in_native_target_build) {
   crashpad_executable("base94_encoder") {
     sources = [ "base94_encoder.cc" ]
     deps = [
@@ -78,7 +82,8 @@
   }
 }
 
-if (!crashpad_is_fuchsia && !crashpad_is_ios && !crashpad_is_in_starboard) {
+if (!crashpad_is_fuchsia && !crashpad_is_ios && !crashpad_is_in_starboard
+    && !crashpad_is_in_native_target_build) {
   crashpad_executable("generate_dump") {
     sources = [ "generate_dump.cc" ]
 
diff --git a/third_party/crashpad/util/BUILD.gn b/third_party/crashpad/util/BUILD.gn
index cb10dfb..20a75f2 100644
--- a/third_party/crashpad/util/BUILD.gn
+++ b/third_party/crashpad/util/BUILD.gn
@@ -306,7 +306,8 @@
 
   deps = []
 
-  if (!crashpad_is_in_starboard && (crashpad_is_linux || crashpad_is_fuchsia || crashpad_is_android)) {
+  if (!crashpad_is_in_starboard && !crashpad_is_in_native_target_build &&
+      (crashpad_is_linux || crashpad_is_fuchsia || crashpad_is_android)) {
     sources += [ "net/http_transport_socket.cc" ]
     if (crashpad_use_boringssl_for_http_transport_socket) {
       defines += [ "CRASHPAD_USE_BORINGSSL" ]
@@ -543,7 +544,8 @@
   configs += [ "..:disable_ubsan" ]
 }
 
-if (!crashpad_is_android && !crashpad_is_in_starboard) {
+if (!crashpad_is_android && !crashpad_is_in_starboard &&
+    !crashpad_is_in_native_target_build) {
   crashpad_executable("http_transport_test_server") {
     testonly = true
     sources = [ "net/http_transport_test_server.cc" ]
@@ -579,7 +581,7 @@
   }
 }
 
-if (!crashpad_is_in_starboard) {
+if (!crashpad_is_in_starboard && !crashpad_is_in_native_target_build) {
   source_set("util_test") {
     testonly = true
 
@@ -784,4 +786,4 @@
     include_dirs = [ "//third_party/mini_chromium" ]
     cflags = [ "-isystem" + rebase_path("../../..", root_build_dir) ]
   }
-}
+}
\ No newline at end of file
diff --git a/third_party/crashpad/util/file/file_io_posix.cc b/third_party/crashpad/util/file/file_io_posix.cc
index e0d4eb3..8062a2c 100644
--- a/third_party/crashpad/util/file/file_io_posix.cc
+++ b/third_party/crashpad/util/file/file_io_posix.cc
@@ -157,7 +157,7 @@
   DCHECK(name.value().find('/') == std::string::npos);
 
   int result;
-#if !defined(STARBOARD)
+#if !defined(STARBOARD) && !defined(NATIVE_TARGET_BUILD)
   result = HANDLE_EINTR(memfd_create(name.value().c_str(), 0));
   if (result >= 0 || errno != ENOSYS) {
     PLOG_IF(ERROR, result < 0) << "memfd_create";
diff --git a/third_party/crashpad/util/linux/exception_handler_client.cc b/third_party/crashpad/util/linux/exception_handler_client.cc
index de27275..4d2316b 100644
--- a/third_party/crashpad/util/linux/exception_handler_client.cc
+++ b/third_party/crashpad/util/linux/exception_handler_client.cc
@@ -84,7 +84,7 @@
       server_sock_, &response, sizeof(response), creds);
 }
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || NATIVE_TARGET_BUILD
 bool ExceptionHandlerClient::SendEvergreenInfo(
     const ExceptionHandlerProtocol::ClientInformation& info) {
   return SendEvergreenInfoRequest(info);
@@ -155,7 +155,7 @@
   return 0;
 }
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || NATIVE_TARGET_BUILD
 bool ExceptionHandlerClient::SendEvergreenInfoRequest(
     const ExceptionHandlerProtocol::ClientInformation& info) {
   ExceptionHandlerProtocol::ClientToServerMessage message;
diff --git a/third_party/crashpad/util/linux/exception_handler_client.h b/third_party/crashpad/util/linux/exception_handler_client.h
index b11e845..32c5719 100644
--- a/third_party/crashpad/util/linux/exception_handler_client.h
+++ b/third_party/crashpad/util/linux/exception_handler_client.h
@@ -21,7 +21,7 @@
 #include "base/macros.h"
 #include "util/linux/exception_handler_protocol.h"
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 #include "starboard/elf_loader/evergreen_info.h"
 #endif
 
@@ -50,7 +50,7 @@
   //! \return `true` on success. Otherwise, `false` with a message logged.
   bool GetHandlerCredentials(ucred* creds);
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   //! \brief Sends EvergreenInfo to the ExceptionHandlerServer.
   //!
   //! \param[in] info Information to about this client.
@@ -86,7 +86,7 @@
   void SetCanSetPtracer(bool can_set_ptracer);
 
  private:
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   bool SendEvergreenInfoRequest(
       const ExceptionHandlerProtocol::ClientInformation& info);
 
diff --git a/third_party/crashpad/util/linux/exception_handler_protocol.cc b/third_party/crashpad/util/linux/exception_handler_protocol.cc
index 5f58721..0e2b7f7 100644
--- a/third_party/crashpad/util/linux/exception_handler_protocol.cc
+++ b/third_party/crashpad/util/linux/exception_handler_protocol.cc
@@ -23,7 +23,7 @@
       ,
       crash_loop_before_time(0)
 #endif  // OS_LINUX
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
       ,
       evergreen_information_address(0),
       serialized_annotations_address(0),
diff --git a/third_party/crashpad/util/linux/exception_handler_protocol.h b/third_party/crashpad/util/linux/exception_handler_protocol.h
index 4f94bd9..46782cd 100644
--- a/third_party/crashpad/util/linux/exception_handler_protocol.h
+++ b/third_party/crashpad/util/linux/exception_handler_protocol.h
@@ -38,7 +38,7 @@
   //! \brief A boolean status suitable for communication between processes.
   enum Bool : char { kBoolFalse, kBoolTrue };
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   //! \brief Describes when, in the client process lifecycle, the Crashpad
   //!     handler was or will be started.
   enum HandlerStartType : char { kStartAtCrash, kStartAtLaunch };
@@ -58,7 +58,7 @@
     //!     SanitizationInformation struct, or 0 if there is no such struct.
     VMAddress sanitization_information_address;
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
     //! \brief The address in the client's address space of an EvergreenInfo
     //!     struct, or 0 if there is no such struct.
     VMAddress evergreen_information_address;
@@ -106,7 +106,7 @@
       //! \brief Used to request a crash dump for the sending client.
       kTypeCrashDumpRequest,
 
-#if defined(STARBOARD)
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
       //! \brief Used to store Evergreen mapping info in the handler for use at
       //!     time of crash.
       kTypeAddEvergreenInfo,
diff --git a/third_party/crashpad/util/net/http_transport.cc b/third_party/crashpad/util/net/http_transport.cc
index 7ee381b..3aea6d1 100644
--- a/third_party/crashpad/util/net/http_transport.cc
+++ b/third_party/crashpad/util/net/http_transport.cc
@@ -52,8 +52,16 @@
   timeout_ = timeout;
 }
 
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+void HTTPTransport::SetRootCACertificatesDirectoryPath(
+    const std::string& path) {
+  root_ca_certificates_directory_path_ = path;
+}
+#else
 void HTTPTransport::SetRootCACertificatePath(const base::FilePath& cert) {
   root_ca_certificate_path_ = cert;
 }
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+
 
 }  // namespace crashpad
diff --git a/third_party/crashpad/util/net/http_transport.h b/third_party/crashpad/util/net/http_transport.h
index acd4e44..82333c6 100644
--- a/third_party/crashpad/util/net/http_transport.h
+++ b/third_party/crashpad/util/net/http_transport.h
@@ -72,6 +72,15 @@
   //! \param[in] timeout The request timeout, in seconds.
   void SetTimeout(double timeout);
 
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+  //! \brief Sets the absolute path to a directory containing certificates in
+  //!     lieu of the system CA cert bundle.
+  //!
+  //! \param[in] path The path to a directory containing cert files in PEM
+  //!     format to be used for TLS connections.
+  void SetRootCACertificatesDirectoryPath(const std::string& path);
+#else
+
   //! \brief Sets a certificate file to be used in lieu of the system CA cert
   //!     bundle.
   //!
@@ -81,6 +90,7 @@
   //! \param[in] cert The filename of a file in PEM format containing the CA
   //!     cert to be used for TLS connections.
   void SetRootCACertificatePath(const base::FilePath& cert);
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 
   //! \brief Performs the HTTP request with the configured parameters and waits
   //!     for the execution to complete.
@@ -101,14 +111,24 @@
   const HTTPHeaders& headers() const { return headers_; }
   HTTPBodyStream* body_stream() const { return body_stream_.get(); }
   double timeout() const { return timeout_; }
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+  const std::string& root_ca_certificates_directory_path() const {
+    return root_ca_certificates_directory_path_;
+  }
+#else
   const base::FilePath& root_ca_certificate_path() const {
     return root_ca_certificate_path_;
   }
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 
  private:
   std::string url_;
   std::string method_;
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+  std::string root_ca_certificates_directory_path_;
+#else
   base::FilePath root_ca_certificate_path_;
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
   HTTPHeaders headers_;
   std::unique_ptr<HTTPBodyStream> body_stream_;
   double timeout_;
diff --git a/third_party/crashpad/util/net/http_transport_socket.cc b/third_party/crashpad/util/net/http_transport_socket.cc
index a2870cc..c63023f 100644
--- a/third_party/crashpad/util/net/http_transport_socket.cc
+++ b/third_party/crashpad/util/net/http_transport_socket.cc
@@ -39,10 +39,6 @@
 #include <openssl/ssl.h>
 #endif
 
-#if defined(STARBOARD)
-#include "starboard/configuration_constants.h"
-#include "starboard/system.h"
-#endif
 
 namespace crashpad {
 
@@ -103,7 +99,11 @@
  public:
   SSLStream() = default;
 
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+  bool Initialize(const std::string& root_cert_directory_path,
+#else
   bool Initialize(const base::FilePath& root_cert_path,
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
                   int sock,
                   const std::string& hostname) {
     SSL_library_init();
@@ -122,6 +122,13 @@
     SSL_CTX_set_verify(ctx_.get(), SSL_VERIFY_PEER, nullptr);
     SSL_CTX_set_verify_depth(ctx_.get(), 5);
 
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+    if (SSL_CTX_load_verify_locations(
+            ctx_.get(), nullptr, root_cert_directory_path.c_str()) <= 0) {
+      LOG(ERROR) << "SSL_CTX_load_verify_locations";
+      return false;
+    }
+#else  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
     if (!root_cert_path.empty()) {
       if (SSL_CTX_load_verify_locations(
               ctx_.get(), root_cert_path.value().c_str(), nullptr) <= 0) {
@@ -129,32 +136,7 @@
         return false;
       }
     } else {
-#if defined(STARBOARD)
-      std::vector<char> buffer(kSbFileMaxPath);
-      bool result = SbSystemGetPath(
-          kSbSystemPathContentDirectory, buffer.data(), buffer.size());
-      if (!result) {
-        LOG(ERROR) << "SSL_CTX_load_verify_locations";
-        return false;
-      }
-
-      std::string cert_location(buffer.data());
-      cert_location.append(std::string(kSbFileSepString) + "app" +
-                           kSbFileSepString + "cobalt" + kSbFileSepString +
-                           "content" + kSbFileSepString + "ssl" +
-                           kSbFileSepString + "certs");
-      // If this is not Cobalt Evergreen setup use the regular content path.
-      if (!SbFileExists(cert_location.c_str())) {
-        cert_location = buffer.data();
-        cert_location.append(std::string(kSbFileSepString) + "ssl" +
-                             kSbFileSepString + "certs");
-      }
-      if (SSL_CTX_load_verify_locations(
-              ctx_.get(), nullptr, cert_location.c_str()) <= 0) {
-        LOG(ERROR) << "SSL_CTX_load_verify_locations";
-        return false;
-      }
-#elif defined(OS_LINUX)
+#if defined(OS_LINUX)
       if (SSL_CTX_load_verify_locations(
               ctx_.get(), nullptr, "/etc/ssl/certs") <= 0) {
         LOG(ERROR) << "SSL_CTX_load_verify_locations";
@@ -166,10 +148,11 @@
         LOG(ERROR) << "SSL_CTX_load_verify_locations";
         return false;
       }
-#else
+#else  // OS_LINUX
 #error cert store location
-#endif
+#endif  // OS_LINUX
     }
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
 
     ssl_.reset(SSL_new(ctx_.get()));
     if (!ssl_.is_valid()) {
@@ -593,7 +576,12 @@
   if (scheme == "https") {
     auto ssl_stream = std::make_unique<SSLStream>();
     if (!ssl_stream->Initialize(
-            root_ca_certificate_path(), sock.get(), hostname)) {
+#if defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+            root_ca_certificates_directory_path(),
+#else
+            root_ca_certificate_path(),
+#endif  // defined(STARBOARD) || defined(NATIVE_TARGET_BUILD)
+            sock.get(), hostname)) {
       LOG(ERROR) << "SSLStream Initialize";
       return false;
     }
diff --git a/third_party/crashpad/util/net/tls.gni b/third_party/crashpad/util/net/tls.gni
index f5ad805..cc3e1f2 100644
--- a/third_party/crashpad/util/net/tls.gni
+++ b/third_party/crashpad/util/net/tls.gni
@@ -19,5 +19,5 @@
   # was removed from the Fuchsia SDK. Re-enable it when we have a way to acquire
   # a BoringSSL lib again.
   crashpad_use_boringssl_for_http_transport_socket =
-      crashpad_is_in_fuchsia || (crashpad_is_linux && crashpad_is_in_chromium) || crashpad_is_in_starboard
+      crashpad_is_in_fuchsia || (crashpad_is_linux && crashpad_is_in_chromium) || crashpad_is_in_starboard || crashpad_is_in_native_target_build
 }
diff --git a/third_party/crashpad/wrapper/proto/crashpad_annotations.proto b/third_party/crashpad/wrapper/proto/crashpad_annotations.proto
index 8eb6205..f8b9879 100644
--- a/third_party/crashpad/wrapper/proto/crashpad_annotations.proto
+++ b/third_party/crashpad/wrapper/proto/crashpad_annotations.proto
@@ -19,7 +19,7 @@
 package crashpad.wrapper;
 
 // Annotations that can be shared between Cobalt and Crashpad handler processes.
-// Next id: 5
+// Next id: 6
 message CrashpadAnnotations {
   // The product name.
   string prod = 1;
@@ -30,6 +30,9 @@
   // The User-Agent string that identifies brand, model, etc.
   string user_agent_string = 3;
 
+  // The device series identifier that is used for device authentication.
+  string cert_scope = 5;
+
   // Annotations with keys that are unknown at compile time.
   map<string, string> runtime_annotations = 4;
 }
diff --git a/third_party/crashpad/wrapper/wrapper.cc b/third_party/crashpad/wrapper/wrapper.cc
index 700f9b7..4922932 100644
--- a/third_party/crashpad/wrapper/wrapper.cc
+++ b/third_party/crashpad/wrapper/wrapper.cc
@@ -36,6 +36,7 @@
 const char kCrashpadVersionKey[]  = "ver";
 const char kCrashpadProductKey[]  = "prod";
 const char kCrashpadUserAgentStringKey[]  = "user_agent_string";
+const char kCrashpadCertScopeKey[] = "cert_scope";
 
 namespace {
 // TODO: Get evergreen information from installation.
@@ -61,7 +62,12 @@
   base::FilePath exe_dir_path = base::FilePath(exe_path.data()).DirName();
   std::string handler_path(exe_dir_path.value());
   handler_path.push_back(kSbFileSepChar);
+#if defined(OS_ANDROID)
+  // Path to the extracted native library.
+  handler_path.append("arm/libcrashpad_handler.so");
+#else
   handler_path.append("crashpad_handler");
+#endif  // defined(OS_ANDROID)
   return base::FilePath(handler_path.c_str());
 }
 
@@ -164,12 +170,20 @@
     platform_info.insert({"model", value.data()});
   }
 
+  result = SbSystemGetProperty(kSbSystemPropertyCertificationScope,
+                               value.data(),
+                               kSystemPropertyMaxLength);
+  if (result) {
+    platform_info.insert({kCrashpadCertScopeKey, value.data()});
+  }
+
   return platform_info;
 }
 
 }  // namespace
 
-void InstallCrashpadHandler(bool start_at_crash) {
+void InstallCrashpadHandler(bool start_at_crash,
+                            const std::string& ca_certificates_path) {
   ::crashpad::CrashpadClient* client = GetCrashpadClient();
 
   const base::FilePath handler_path = GetPathToCrashpadHandlerBinary();
@@ -183,7 +197,9 @@
   const base::FilePath default_metrics_dir;
   const std::string product_name = GetProductName();
   std::map<std::string, std::string> default_annotations = {
-      {"ver", kCrashpadVersion}, {"prod", product_name}};
+      {kCrashpadVersionKey, kCrashpadVersion},
+      {kCrashpadProductKey, product_name}
+  };
   const std::vector<std::string> default_arguments = {};
 
   const std::map<std::string, std::string> platform_info = GetPlatformInfo();
@@ -197,6 +213,7 @@
                                 database_directory_path,
                                 default_metrics_dir,
                                 kUploadUrl,
+                                ca_certificates_path,
                                 default_annotations,
                                 default_arguments);
   else
@@ -204,6 +221,7 @@
                          database_directory_path,
                          default_metrics_dir,
                          kUploadUrl,
+                         ca_certificates_path,
                          default_annotations,
                          default_arguments,
                          false,
diff --git a/third_party/crashpad/wrapper/wrapper.h b/third_party/crashpad/wrapper/wrapper.h
index be60fec..3a384ec 100644
--- a/third_party/crashpad/wrapper/wrapper.h
+++ b/third_party/crashpad/wrapper/wrapper.h
@@ -15,6 +15,8 @@
 #ifndef THIRD_PARTY_CRASHPAD_WRAPPER_WRAPPER_H_
 #define THIRD_PARTY_CRASHPAD_WRAPPER_WRAPPER_H_
 
+#include <string>
+
 #include "starboard/elf_loader/evergreen_info.h" // nogncheck
 
 namespace third_party {
@@ -24,13 +26,24 @@
 // The key name used in Crashpad for the version annotation.
 extern const char kCrashpadVersionKey[];
 
-// The key name used in Crashpad for the version annotation.
+// The key name used in Crashpad for the product annotation.
 extern const char kCrashpadProductKey[];
 
 // The key name used in Crashpad for the user_agent_string annotation.
 extern const char kCrashpadUserAgentStringKey[];
 
-void InstallCrashpadHandler(bool start_at_crash);
+// The key name used in Crashpad for the cert_scope annotation.
+extern const char kCrashpadCertScopeKey[];
+
+// Installs a signal handler to handle a crash. The signal handler will launch a
+// Crashpad handler process in response to a crash when |start_at_crash| is
+// true, otherwise a Crashpad handler process will be started immediately.
+// |ca_certificates_path| is the absolute path to a directory containing
+// Cobalt's trusted Certificate Authority (CA) root certificates, and must be
+// passed so that the certificates can be accessed by the handler process during
+// upload.
+void InstallCrashpadHandler(bool start_at_crash,
+                            const std::string& ca_certificates_path);
 
 bool AddEvergreenInfoToCrashpad(EvergreenInfo evergreen_info);
 
diff --git a/third_party/crashpad/wrapper/wrapper_stub.cc b/third_party/crashpad/wrapper/wrapper_stub.cc
index f96dd94..46bc3fb 100644
--- a/third_party/crashpad/wrapper/wrapper_stub.cc
+++ b/third_party/crashpad/wrapper/wrapper_stub.cc
@@ -21,8 +21,10 @@
 const char kCrashpadVersionKey[]  = "";
 const char kCrashpadProductKey[]  = "";
 const char kCrashpadUserAgentStringKey[]  = "";
+const char kCrashpadCertScopeKey[] = "";
 
-void InstallCrashpadHandler(bool start_at_crash) {}
+void InstallCrashpadHandler(bool start_at_crash,
+                            const std::string& ca_certificates_path) {}
 
 bool AddEvergreenInfoToCrashpad(EvergreenInfo evergreen_info) {
   return false;
diff --git a/third_party/libjpeg-turbo/BUILD.gn b/third_party/libjpeg-turbo/BUILD.gn
index f1ff3f3..0630c11 100644
--- a/third_party/libjpeg-turbo/BUILD.gn
+++ b/third_party/libjpeg-turbo/BUILD.gn
@@ -29,7 +29,7 @@
   }
 }
 
-if (yasm_exists && (current_cpu == "x86" || current_cpu == "x64")) {
+if (nasm_exists && (current_cpu == "x86" || current_cpu == "x64")) {
   if (is_starboard) {
     import("//starboard/build/nasm_assemble.gni")
   } else {
@@ -162,10 +162,10 @@
     defines = []
   }
 
-  if (current_cpu == "x86" && yasm_exists) {
+  if (current_cpu == "x86" && nasm_exists) {
     deps += [ ":simd_asm" ]
     sources = [ "simd/i386/jsimd.c" ]
-  } else if (current_cpu == "x64" && yasm_exists) {
+  } else if (current_cpu == "x64" && nasm_exists) {
     deps += [ ":simd_asm" ]
     sources = [ "simd/x86_64/jsimd.c" ]
   } else if ((current_cpu == "arm" || current_cpu == "arm64") && arm_use_neon) {
diff --git a/third_party/mini_chromium/base/BUILD.gn b/third_party/mini_chromium/base/BUILD.gn
index ac91b33..eaefb49 100644
--- a/third_party/mini_chromium/base/BUILD.gn
+++ b/third_party/mini_chromium/base/BUILD.gn
@@ -6,7 +6,7 @@
 
 config("base_public_config") {
   include_dirs = [ ".." ]
-  if (is_starboard) {
+  if (use_cobalt_customizations) {
     cflags = [ "-isystem" + rebase_path("../../..", root_build_dir) ]
   }
 }
@@ -164,7 +164,7 @@
   }
 
   public_configs = [ ":base_public_config" ]
-  if (is_starboard) {
+  if (use_cobalt_customizations) {
     configs -= [ "//build/config/compiler:default_include_dirs" ]
   }
 }
diff --git a/third_party/mini_chromium/base/logging.cc b/third_party/mini_chromium/base/logging.cc
index ab63538..49f0fbe 100644
--- a/third_party/mini_chromium/base/logging.cc
+++ b/third_party/mini_chromium/base/logging.cc
@@ -52,6 +52,10 @@
 #include <zircon/syscalls.h>
 #endif
 
+#if defined(NATIVE_TARGET_BUILD) && defined(OS_ANDROID)
+#include <android/log.h>
+#endif  // defined(NATIVE_TARGET_BUILD) && defined(OS_ANDROID)
+
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -163,6 +167,32 @@
     return;
   }
 
+#if defined(NATIVE_TARGET_BUILD) && defined(OS_ANDROID)
+  // This code block is mostly taken from base/logging.cc and was added here to
+  // enable logging in crashpad_handler for the AOSP Evergreen port.
+  android_LogPriority priority =
+      (severity_ < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN;
+  switch (severity_) {
+    case LOG_INFO:
+      priority = ANDROID_LOG_INFO;
+      break;
+    case LOG_WARNING:
+      priority = ANDROID_LOG_WARN;
+      break;
+    case LOG_ERROR:
+      priority = ANDROID_LOG_ERROR;
+      break;
+    case LOG_FATAL:
+      priority = ANDROID_LOG_FATAL;
+      break;
+  }
+
+  // Even though this is a native build and not a Starboard build, the
+  // "starboard" tag is used to group these logs with the logs from the Cobalt
+  // process.
+  __android_log_write(priority, "starboard", str_newline.c_str());
+#endif  // defined(NATIVE_TARGET_BUILD) && defined(OS_ANDROID)
+
   fprintf(stderr, "%s", str_newline.c_str());
   fflush(stderr);
 
diff --git a/third_party/protobuf/BUILD.gn b/third_party/protobuf/BUILD.gn
index 1493afc..ca02063 100644
--- a/third_party/protobuf/BUILD.gn
+++ b/third_party/protobuf/BUILD.gn
@@ -156,7 +156,7 @@
 
   if (is_starboard) {
     deps = [ "//starboard/common" ]
-  } else {
+  } else if (!is_native_target_build) {
     configs -= [ "//build/config/compiler:chromium_code" ]
   }
   configs += [
@@ -178,7 +178,7 @@
     "//build/config/compiler:no_size_t_to_int_warning",
   ]
 
-  if (is_starboard) {
+  if (use_cobalt_customizations) {
     public_configs -= [ ":protobuf_config" ]
     all_dependent_configs = [ ":protobuf_config" ]
   } else {
@@ -334,9 +334,7 @@
       "src/google/protobuf/wrappers.pb.h",
     ]
 
-    if (is_starboard) {
-      deps = [ "//starboard/common" ]
-    } else {
+    if (!is_starboard) {
       deps = [
         "//build/config/sanitizers:deps",
       ]
diff --git a/third_party/protobuf/proto_library.gni b/third_party/protobuf/proto_library.gni
index f120a3a..fdfc16b 100644
--- a/third_party/protobuf/proto_library.gni
+++ b/third_party/protobuf/proto_library.gni
@@ -101,7 +101,7 @@
   # Platform files should have gotten filtered out in the sources assignment
   # when this template was invoked. If they weren't, it was on purpose and
   # this template shouldn't re-apply the filter.
-  if (!is_starboard) {
+  if (!use_cobalt_customizations) {
     set_sources_assignment_filter([])
   }
 
@@ -226,7 +226,7 @@
   # Generate protobuf stubs.
   action(action_name) {
     visibility = [ ":$source_set_name" ]
-    if (is_starboard) {
+    if (use_cobalt_customizations) {
       script = "//tools/protoc_wrapper/gn_protoc_wrapper.py"
     } else {
       script = "//tools/protoc_wrapper/protoc_wrapper.py"
diff --git a/third_party/protobuf/src/google/protobuf/io/coded_stream.cc b/third_party/protobuf/src/google/protobuf/io/coded_stream.cc
index deea55a..24d28cc 100644
--- a/third_party/protobuf/src/google/protobuf/io/coded_stream.cc
+++ b/third_party/protobuf/src/google/protobuf/io/coded_stream.cc
@@ -38,7 +38,9 @@
 // will not cross the end of the buffer, since we can avoid a lot
 // of branching in this case.
 
+#ifdef STARBOARD
 #include "starboard/client_porting/poem/string_poem.h"
+#endif
 
 #include <google/protobuf/io/coded_stream_inl.h>
 #include <algorithm>
diff --git a/third_party/protobuf/src/google/protobuf/repeated_field.cc b/third_party/protobuf/src/google/protobuf/repeated_field.cc
index a699b29..4b7d498 100644
--- a/third_party/protobuf/src/google/protobuf/repeated_field.cc
+++ b/third_party/protobuf/src/google/protobuf/repeated_field.cc
@@ -32,7 +32,9 @@
 //  Based on original Protocol Buffers design by
 //  Sanjay Ghemawat, Jeff Dean, and others.
 
+#ifdef STARBOARD
 #include "starboard/client_porting/poem/string_poem.h"
+#endif
 
 #include <algorithm>
 
diff --git a/third_party/protobuf/src/google/protobuf/stubs/bytestream.cc b/third_party/protobuf/src/google/protobuf/stubs/bytestream.cc
index fd3af8f..9509af5 100644
--- a/third_party/protobuf/src/google/protobuf/stubs/bytestream.cc
+++ b/third_party/protobuf/src/google/protobuf/stubs/bytestream.cc
@@ -28,7 +28,9 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#ifdef STARBOARD
 #include "starboard/client_porting/poem/string_poem.h"
+#endif
 
 #include <google/protobuf/stubs/bytestream.h>
 
diff --git a/third_party/protobuf/src/google/protobuf/stubs/starboard_poem.h b/third_party/protobuf/src/google/protobuf/stubs/starboard_poem.h
index fc50d12..0f1d6a7 100644
--- a/third_party/protobuf/src/google/protobuf/stubs/starboard_poem.h
+++ b/third_party/protobuf/src/google/protobuf/stubs/starboard_poem.h
@@ -12,4 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#ifdef STARBOARD
 #include "starboard/memory.h"
+#endif
diff --git a/third_party/protobuf/src/google/protobuf/stubs/stringpiece.cc b/third_party/protobuf/src/google/protobuf/stubs/stringpiece.cc
index 0a5d67d..c807c23 100644
--- a/third_party/protobuf/src/google/protobuf/stubs/stringpiece.cc
+++ b/third_party/protobuf/src/google/protobuf/stubs/stringpiece.cc
@@ -32,7 +32,9 @@
 
 #include <string.h>
 
+#ifdef STARBOARD
 #include "starboard/client_porting/poem/string_poem.h"
+#endif
 
 #include <algorithm>
 #include <climits>
diff --git a/third_party/protobuf/src/google/protobuf/stubs/stringprintf.cc b/third_party/protobuf/src/google/protobuf/stubs/stringprintf.cc
index 9374d4b..bfd7700 100644
--- a/third_party/protobuf/src/google/protobuf/stubs/stringprintf.cc
+++ b/third_party/protobuf/src/google/protobuf/stubs/stringprintf.cc
@@ -41,7 +41,9 @@
 #include <vector>
 #include <google/protobuf/stubs/common.h>
 
+#ifdef STARBOARD
 #include "starboard/client_porting/poem/stdio_poem.h"
+#endif
 
 namespace google {
 namespace protobuf {
diff --git a/third_party/protobuf/src/google/protobuf/stubs/structurally_valid.cc b/third_party/protobuf/src/google/protobuf/stubs/structurally_valid.cc
index ab51ae6..2b0d1ef 100644
--- a/third_party/protobuf/src/google/protobuf/stubs/structurally_valid.cc
+++ b/third_party/protobuf/src/google/protobuf/stubs/structurally_valid.cc
@@ -1,7 +1,9 @@
 // Copyright 2005-2008 Google Inc. All Rights Reserved.
 // Author: jrm@google.com (Jim Meehan)
 
+#ifdef STARBOARD
 #include "starboard/client_porting/poem/string_poem.h"
+#endif
 
 #include <google/protobuf/stubs/common.h>
 
diff --git a/third_party/skia/include/private/SkTDArray.h b/third_party/skia/include/private/SkTDArray.h
index 59c180b..c6a0797 100644
--- a/third_party/skia/include/private/SkTDArray.h
+++ b/third_party/skia/include/private/SkTDArray.h
@@ -320,7 +320,12 @@
 
     void shrinkToFit() {
         fReserve = fCount;
-        fArray = (T*)sk_realloc_throw(fArray, fReserve * sizeof(T));
+        if (fReserve) {
+            fArray = (T*)sk_realloc_throw(fArray, fReserve * sizeof(T));
+        } else {
+            sk_free(fArray);
+            fArray = nullptr;
+        }
     }
 
 private:
diff --git a/third_party/v8/BUILD.gn b/third_party/v8/BUILD.gn
index e798d17..fb91bf8 100644
--- a/third_party/v8/BUILD.gn
+++ b/third_party/v8/BUILD.gn
@@ -419,10 +419,6 @@
       "DISABLE_GRAPHS_STARBOARD",
       "DISABLE_UNWIND_STARBOARD",
 
-      # On host build, V8_OS_STARBOARD or STARBOARD is not defined. We need a
-      # macro to distinguish Cobalt-only configuraitons.
-      "COMPILE_FOR_STARBOARD",
-
       # "DISABLE_WASM_STARBOARD",
       "DISABLE_WASM_COMPILER_ISSUE_STARBOARD",
       "NO_ARRAY_MOVE_STARBOARD",
@@ -926,7 +922,8 @@
   } else if (target_os == "mac") {
     defines += [ "V8_HAVE_TARGET_OS" ]
     defines += [ "V8_TARGET_OS_MACOSX" ]
-  } else if (target_os == "win") {
+  } else if (target_os == "win" ||
+             (use_cobalt_customizations && target_os == "winuwp")) {
     defines += [ "V8_HAVE_TARGET_OS" ]
     defines += [ "V8_TARGET_OS_WIN" ]
   }
diff --git a/third_party/v8/include/v8config.h b/third_party/v8/include/v8config.h
index d32e13a..f5db79f 100644
--- a/third_party/v8/include/v8config.h
+++ b/third_party/v8/include/v8config.h
@@ -181,16 +181,13 @@
 # define V8_TARGET_OS_MACOSX
 #endif
 
-#if defined(COMPILE_FOR_STARBOARD)
-// Cobalt
-#if defined(SB_HAS_WINDOWS_CALLING)
-#define V8_TARGET_OS_WIN 1
-#endif
-#else
+// Playstation and Nintendo Switch build on Windows but that is not their
+// target OS.
+#ifndef USE_COBALT_CUSTOMIZATIONS
 #ifdef V8_OS_WIN
 # define V8_TARGET_OS_WIN
 #endif
-#endif  // COMPILE_FOR_STARBOARD
+#endif  // USE_COBALT_CUSTOMIZATIONS
 
 #endif  // V8_HAVE_TARGET_OS
 
diff --git a/third_party/v8/src/base/platform/platform-win32.cc b/third_party/v8/src/base/platform/platform-win32.cc
index afacf15..f1fe683 100644
--- a/third_party/v8/src/base/platform/platform-win32.cc
+++ b/third_party/v8/src/base/platform/platform-win32.cc
@@ -1405,7 +1405,7 @@
 Stack::StackSlot Stack::GetStackStart() {
 // Cobalt sometimes compile on windows x64 for linux arm64 targets. To make
 // host build compile we need the extra check.
-#if defined(V8_TARGET_ARCH_X64) || defined(COMPILE_FOR_STARBOARD)
+#if defined(V8_TARGET_ARCH_X64) || defined(USE_COBALT_CUSTOMIZATIONS)
   return reinterpret_cast<void*>(
       reinterpret_cast<NT_TIB64*>(NtCurrentTeb())->StackBase);
 #elif defined(V8_TARGET_ARCH_32_BIT)
diff --git a/third_party/v8/src/codegen/arm64/macro-assembler-arm64-inl.h b/third_party/v8/src/codegen/arm64/macro-assembler-arm64-inl.h
index 0d3b22f..5ad43f3 100644
--- a/third_party/v8/src/codegen/arm64/macro-assembler-arm64-inl.h
+++ b/third_party/v8/src/codegen/arm64/macro-assembler-arm64-inl.h
@@ -1302,7 +1302,7 @@
     return;
   }
   DCHECK_EQ(size % 16, 0);
-#if V8_TARGET_OS_WIN
+#ifdef V8_TARGET_OS_WIN
   while (size > kStackPageSize) {
     Sub(sp, sp, kStackPageSize);
     Str(xzr, MemOperand(sp));
@@ -1324,7 +1324,7 @@
   }
   AssertPositiveOrZero(count);
 
-#if V8_TARGET_OS_WIN
+#ifdef V8_TARGET_OS_WIN
   // "Functions that allocate 4k or more worth of stack must ensure that each
   // page prior to the final page is touched in order." Source:
   // https://docs.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=vs-2019#stack
diff --git a/third_party/v8/src/wasm/baseline/arm64/liftoff-assembler-arm64.h b/third_party/v8/src/wasm/baseline/arm64/liftoff-assembler-arm64.h
index eb3c74a..b1bbc50 100644
--- a/third_party/v8/src/wasm/baseline/arm64/liftoff-assembler-arm64.h
+++ b/third_party/v8/src/wasm/baseline/arm64/liftoff-assembler-arm64.h
@@ -247,7 +247,7 @@
 #endif
   PatchingAssembler patching_assembler(AssemblerOptions{},
                                        buffer_start_ + offset, 1);
-#if V8_TARGET_OS_WIN
+#ifdef V8_TARGET_OS_WIN
   if (frame_size > kStackPageSize) {
     // Generate OOL code (at the end of the function, where the current
     // assembler is pointing) to do the explicit stack limit check (see
diff --git a/third_party/v8/src/wasm/baseline/x64/liftoff-assembler-x64.h b/third_party/v8/src/wasm/baseline/x64/liftoff-assembler-x64.h
index e985e1f..ac38d33 100644
--- a/third_party/v8/src/wasm/baseline/x64/liftoff-assembler-x64.h
+++ b/third_party/v8/src/wasm/baseline/x64/liftoff-assembler-x64.h
@@ -166,7 +166,7 @@
   Assembler patching_assembler(
       AssemblerOptions{},
       ExternalAssemblerBuffer(buffer_start_ + offset, kAvailableSpace));
-#if V8_TARGET_OS_WIN
+#ifdef V8_TARGET_OS_WIN
   if (frame_size > kStackPageSize) {
     // Generate OOL code (at the end of the function, where the current
     // assembler is pointing) to do the explicit stack limit check (see
diff --git a/third_party/web_platform_tests/fetch/api/request/request-error.html b/third_party/web_platform_tests/fetch/api/request/request-error.html
index d4d8b01..07ac86e 100644
--- a/third_party/web_platform_tests/fetch/api/request/request-error.html
+++ b/third_party/web_platform_tests/fetch/api/request/request-error.html
@@ -26,10 +26,12 @@
           "Expect TypeError exception");
       },"Input URL has credentials");
 
-      test(function() {
-        assert_throws(new TypeError() , function() { new Request("", {"mode" : "navigate"}); },
-          "Expect TypeError exception");
-      },"RequestInit's mode is navigate");
+      // Since request is polyfilled, it needs to be possible to create a Request with the mode
+      // 'navigate'.
+      // test(function() {
+      //   assert_throws(new TypeError() , function() { new Request("", {"mode" : "navigate"}); },
+      //     "Expect TypeError exception");
+      // },"RequestInit's mode is navigate");
 
       // Referrer is not supported due to privacy concerns.
       /*
diff --git a/third_party/web_platform_tests/fetch/api/request/request-init-001.sub.html b/third_party/web_platform_tests/fetch/api/request/request-init-001.sub.html
index 4b2cfad..53a14bd 100644
--- a/third_party/web_platform_tests/fetch/api/request/request-init-001.sub.html
+++ b/third_party/web_platform_tests/fetch/api/request/request-init-001.sub.html
@@ -49,8 +49,11 @@
                                                   "strict-origin-when-cross-origin"
                                                   ]
       };
-      var modes = {"givenValues" : ["same-origin", "no-cors", "cors"],
-                   "expectedValues" : ["same-origin", "no-cors", "cors"]
+      // Allow 'navigate' to be passed in as a valid mode to requestInit. This is
+      // necessary since Request is a polyfill and there is no other way to set
+      // mode to 'navigate'.
+      var modes = {"givenValues" : ["same-origin", "no-cors", "cors", "navigate"],
+                   "expectedValues" : ["same-origin", "no-cors", "cors", "navigate"]
       };
       var credentials = {"givenValues" : ["omit", "same-origin", "include"],
                           "expectedValues" : ["omit", "same-origin", "include"]
diff --git a/third_party/web_platform_tests/service-workers/service-worker/import-scripts-mime-types.https.html b/third_party/web_platform_tests/service-workers/service-worker/import-scripts-mime-types.https.html
index 1679831..86a7e1f 100644
--- a/third_party/web_platform_tests/service-workers/service-worker/import-scripts-mime-types.https.html
+++ b/third_party/web_platform_tests/service-workers/service-worker/import-scripts-mime-types.https.html
@@ -20,7 +20,15 @@
 
   await wait_for_state(t, registration.installing, 'activated');
 
-  serviceWorker = registration.active;
+  // TODO(b/234788479) Implement waiting for update worker state tasks in
+  // Install algorithm, otherwise the worker is activated too early
+  if (registration.active) {
+    serviceWorker = registration.active;
+  } else if (registration.waiting) {
+    serviceWorker = registration.waiting;
+  } else {
+    serviceWorker = registration.installing;
+  }
 }, 'Global setup');
 
 promise_test(async t => {
diff --git a/third_party/web_platform_tests/service-workers/service-worker/import-scripts-updated-flag.https.html b/third_party/web_platform_tests/service-workers/service-worker/import-scripts-updated-flag.https.html
index 09b4496..a658d07 100644
--- a/third_party/web_platform_tests/service-workers/service-worker/import-scripts-updated-flag.https.html
+++ b/third_party/web_platform_tests/service-workers/service-worker/import-scripts-updated-flag.https.html
@@ -38,10 +38,18 @@
           // other sub-tests in this file are declared synchronously, this test
           // will be the final test executed.
           promise_test(function(t) {
-              return registration.unregister();
-            });
-
-          return registration.active;
+            return registration.unregister();
+          });
+          // TODO(b/234788479) Implement waiting for update worker state tasks in
+          // Install algorithm, otherwise the worker is activated too early
+          if (registration.active) {
+            serviceWorker = registration.active;
+          } else if (registration.waiting) {
+            serviceWorker = registration.waiting;
+          } else {
+            serviceWorker = registration.installing;
+          }
+          return serviceWorker;
         });
 
     return register;
diff --git a/third_party/web_platform_tests/service-workers/service-worker/resources/fetch-access-control.py b/third_party/web_platform_tests/service-workers/service-worker/resources/fetch-access-control.py
index 446af87..a158cf5 100644
--- a/third_party/web_platform_tests/service-workers/service-worker/resources/fetch-access-control.py
+++ b/third_party/web_platform_tests/service-workers/service-worker/resources/fetch-access-control.py
@@ -1,109 +1,107 @@
+import base64
 import json
 import os
-from base64 import decodebytes
-
-from wptserve.utils import isomorphic_decode, isomorphic_encode
 
 def main(request, response):
     headers = []
-    headers.append((b'X-ServiceWorker-ServerHeader', b'SetInTheServer'))
+    headers.append(('X-ServiceWorker-ServerHeader', 'SetInTheServer'))
 
-    if b"ACAOrigin" in request.GET:
-        for item in request.GET[b"ACAOrigin"].split(b","):
-            headers.append((b"Access-Control-Allow-Origin", item))
+    if "ACAOrigin" in request.GET:
+        for item in request.GET["ACAOrigin"].split(","):
+            headers.append(("Access-Control-Allow-Origin", item))
 
-    for suffix in [b"Headers", b"Methods", b"Credentials"]:
-        query = b"ACA%s" % suffix
-        header = b"Access-Control-Allow-%s" % suffix
+    for suffix in ["Headers", "Methods", "Credentials"]:
+        query = "ACA%s" % suffix
+        header = "Access-Control-Allow-%s" % suffix
         if query in request.GET:
             headers.append((header, request.GET[query]))
 
-    if b"ACEHeaders" in request.GET:
-        headers.append((b"Access-Control-Expose-Headers", request.GET[b"ACEHeaders"]))
+    if "ACEHeaders" in request.GET:
+        headers.append(("Access-Control-Expose-Headers", request.GET["ACEHeaders"]))
 
-    if (b"Auth" in request.GET and not request.auth.username) or b"AuthFail" in request.GET:
+    if ("Auth" in request.GET and not request.auth.username) or "AuthFail" in request.GET:
         status = 401
-        headers.append((b'WWW-Authenticate', b'Basic realm="Restricted"'))
-        body = b'Authentication canceled'
+        headers.append(('WWW-Authenticate', 'Basic realm="Restricted"'))
+        body = 'Authentication canceled'
         return status, headers, body
 
-    if b"PNGIMAGE" in request.GET:
-        headers.append((b"Content-Type", b"image/png"))
-        body = decodebytes(b"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1B"
-                           b"AACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAhSURBVDhPY3wro/KfgQLABKXJBqMG"
-                           b"jBoAAqMGDLwBDAwAEsoCTFWunmQAAAAASUVORK5CYII=")
+    if "PNGIMAGE" in request.GET:
+        headers.append(("Content-Type", "image/png"))
+        body = base64.decodestring("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1B"
+                                   "AACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAhSURBVDhPY3wro/KfgQLABKXJBqMG"
+                                   "jBoAAqMGDLwBDAwAEsoCTFWunmQAAAAASUVORK5CYII=")
         return headers, body
 
-    if b"VIDEO" in request.GET:
-        headers.append((b"Content-Type", b"video/ogg"))
-        body = open(os.path.join(request.doc_root, u"media", u"movie_5.ogv"), "rb").read()
+    if "VIDEO" in request.GET:
+        headers.append(("Content-Type", "video/webm"))
+        body = open(os.path.join(request.doc_root, "media", "movie_5.ogv"), "rb").read()
         length = len(body)
         # If "PartialContent" is specified, the requestor wants to test range
         # requests. For the initial request, respond with "206 Partial Content"
         # and don't send the entire content. Then expect subsequent requests to
         # have a "Range" header with a byte range. Respond with that range.
-        if b"PartialContent" in request.GET:
+        if "PartialContent" in request.GET:
           if length < 1:
-            return 500, headers, b"file is too small for range requests"
+            return 500, headers, "file is too small for range requests"
           start = 0
           end = length - 1
-          if b"Range" in request.headers:
-            range_header = request.headers[b"Range"]
-            prefix = b"bytes="
-            split_header = range_header[len(prefix):].split(b"-")
+          if "Range" in request.headers:
+            range_header = request.headers["Range"]
+            prefix = "bytes="
+            split_header = range_header[len(prefix):].split("-")
             # The first request might be "bytes=0-". We want to force a range
             # request, so just return the first byte.
-            if split_header[0] == b"0" and split_header[1] == b"":
+            if split_header[0] == "0" and split_header[1] == "":
               end = start
             # Otherwise, it is a range request. Respect the values sent.
-            if split_header[0] != b"":
+            if split_header[0] != "":
               start = int(split_header[0])
-            if split_header[1] != b"":
+            if split_header[1] != "":
               end = int(split_header[1])
           else:
             # The request doesn't have a range. Force a range request by
             # returning the first byte.
             end = start
 
-          headers.append((b"Accept-Ranges", b"bytes"))
-          headers.append((b"Content-Length", isomorphic_encode(str(end -start + 1))))
-          headers.append((b"Content-Range", b"bytes %d-%d/%d" % (start, end, length)))
+          headers.append(("Accept-Ranges", "bytes"))
+          headers.append(("Content-Length", str(end -start + 1)))
+          headers.append(("Content-Range", "bytes %d-%d/%d" % (start, end, length)))
           chunk = body[start:(end + 1)]
           return 206, headers, chunk
         return headers, body
 
-    username = request.auth.username if request.auth.username else b"undefined"
-    password = request.auth.password if request.auth.username else b"undefined"
-    cookie = request.cookies[b'cookie'].value if b'cookie' in request.cookies else b"undefined"
+    username = request.auth.username if request.auth.username else "undefined"
+    password = request.auth.password if request.auth.username else "undefined"
+    cookie = request.cookies['cookie'].value if 'cookie' in request.cookies else "undefined"
 
     files = []
     for key, values in request.POST.items():
         assert len(values) == 1
         value = values[0]
-        if not hasattr(value, u"file"):
+        if not hasattr(value, "file"):
             continue
         data = value.file.read()
-        files.append({u"key": isomorphic_decode(key),
-                      u"name": value.file.name,
-                      u"type": value.type,
-                      u"error": 0, #TODO,
-                      u"size": len(data),
-                      u"content": data})
+        files.append({"key": key,
+                      "name": value.file.name,
+                      "type": value.type,
+                      "error": 0, #TODO,
+                      "size": len(data),
+                      "content": data})
 
-    get_data = {isomorphic_decode(key):isomorphic_decode(request.GET[key]) for key, value in request.GET.items()}
-    post_data = {isomorphic_decode(key):isomorphic_decode(request.POST[key]) for key, value in request.POST.items()
-                 if not hasattr(request.POST[key], u"file")}
-    headers_data = {isomorphic_decode(key):isomorphic_decode(request.headers[key]) for key, value in request.headers.items()}
+    get_data = {key:request.GET[key] for key,value in request.GET.items()}
+    post_data = {key:request.POST[key] for key,value in request.POST.items()
+                 if not hasattr(request.POST[key], "file")}
+    headers_data = {key:request.headers[key] for key,value in request.headers.items()}
 
-    data = {u"jsonpResult": u"success",
-            u"method": request.method,
-            u"headers": headers_data,
-            u"body": isomorphic_decode(request.body),
-            u"files": files,
-            u"GET": get_data,
-            u"POST": post_data,
-            u"username": isomorphic_decode(username),
-            u"password": isomorphic_decode(password),
-            u"cookie": isomorphic_decode(cookie)}
+    data = {"jsonpResult": "success",
+            "method": request.method,
+            "headers": headers_data,
+            "body": request.body,
+            "files": files,
+            "GET": get_data,
+            "POST": post_data,
+            "username": username,
+            "password": password,
+            "cookie": cookie}
 
-    return headers, u"report( %s )" % json.dumps(data)
+    return headers, "report( %s )" % json.dumps(data)
\ No newline at end of file
diff --git a/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-echo.py b/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-echo.py
index d38d660..7d92794 100644
--- a/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-echo.py
+++ b/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-echo.py
@@ -1,6 +1,6 @@
 def main(req, res):
     return ([
-        (b'Cache-Control', b'no-cache, must-revalidate'),
-        (b'Pragma', b'no-cache'),
-        (b'Content-Type', b'application/javascript')],
-      b'echo_output = "%s";\n' % req.GET[b'msg'])
+        ('Cache-Control', 'no-cache, must-revalidate'),
+        ('Pragma', 'no-cache'),
+        ('Content-Type', 'application/javascript')],
+      'echo_output = "%s";\n' % req.GET['msg'])
diff --git a/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-get.py b/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-get.py
index ab7b84e..9e376bc 100644
--- a/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-get.py
+++ b/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-get.py
@@ -1,6 +1,6 @@
 def main(req, res):
     return ([
-        (b'Cache-Control', b'no-cache, must-revalidate'),
-        (b'Pragma', b'no-cache'),
-        (b'Content-Type', b'application/javascript')],
-        b'%s = "%s";\n' % (req.GET[b'output'], req.GET[b'msg']))
+        ('Cache-Control', 'no-cache, must-revalidate'),
+        ('Pragma', 'no-cache'),
+        ('Content-Type', 'application/javascript')],
+        '%s = "%s";\n' % (req.GET['output'], req.GET['msg']))
diff --git a/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js b/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js
index d4f1f3e..2c585ac 100644
--- a/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js
+++ b/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js
@@ -30,7 +30,7 @@
 
 for (const mimeType of badMimeTypes) {
   test(() => {
-    assert_throws_dom(
+    assert_throws(
       'NetworkError',
       () => { importScriptsWithMimeType(mimeType); },
       `importScripts with ${mimeType ? 'bad' : 'no'} MIME type ${mimeType || ''} throws NetworkError`,
diff --git a/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-version.py b/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-version.py
index cde2854..fcac3b0 100644
--- a/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-version.py
+++ b/third_party/web_platform_tests/service-workers/service-worker/resources/import-scripts-version.py
@@ -11,7 +11,7 @@
     now = (datetime.datetime.now() - epoch).total_seconds()
 
     return ([
-        (b'Cache-Control', b'no-cache, must-revalidate'),
-        (b'Pragma', b'no-cache'),
-        (b'Content-Type', b'application/javascript')],
-       u'version = "%s";\n' % now)
+        ('Cache-Control', 'no-cache, must-revalidate'),
+        ('Pragma', 'no-cache'),
+        ('Content-Type', 'application/javascript')],
+       'version = "%s";\n' % now)
diff --git a/third_party/web_platform_tests/service-workers/service-worker/resources/mime-type-worker.py b/third_party/web_platform_tests/service-workers/service-worker/resources/mime-type-worker.py
index 92a602e..a16684d 100644
--- a/third_party/web_platform_tests/service-workers/service-worker/resources/mime-type-worker.py
+++ b/third_party/web_platform_tests/service-workers/service-worker/resources/mime-type-worker.py
@@ -1,4 +1,4 @@
 def main(request, response):
-    if b'mime' in request.GET:
-        return [(b'Content-Type', request.GET[b'mime'])], b""
-    return [], b""
+    if 'mime' in request.GET:
+        return [('Content-Type', request.GET['mime'])], ""
+    return [], ""
diff --git a/third_party/web_platform_tests/service-workers/service-worker/resources/redirect.py b/third_party/web_platform_tests/service-workers/service-worker/resources/redirect.py
index bd559d5..20521b0 100644
--- a/third_party/web_platform_tests/service-workers/service-worker/resources/redirect.py
+++ b/third_party/web_platform_tests/service-workers/service-worker/resources/redirect.py
@@ -1,27 +1,25 @@
-from wptserve.utils import isomorphic_decode
-
 def main(request, response):
-    if b'Status' in request.GET:
-        status = int(request.GET[b"Status"])
+    if 'Status' in request.GET:
+        status = int(request.GET["Status"])
     else:
         status = 302
 
     headers = []
 
-    url = isomorphic_decode(request.GET[b'Redirect'])
-    headers.append((b"Location", url))
+    url = request.GET['Redirect']
+    headers.append(("Location", url))
 
-    if b"ACAOrigin" in request.GET:
-        for item in request.GET[b"ACAOrigin"].split(b","):
-            headers.append((b"Access-Control-Allow-Origin", item))
+    if "ACAOrigin" in request.GET:
+        for item in request.GET["ACAOrigin"].split(","):
+            headers.append(("Access-Control-Allow-Origin", item))
 
-    for suffix in [b"Headers", b"Methods", b"Credentials"]:
-        query = b"ACA%s" % suffix
-        header = b"Access-Control-Allow-%s" % suffix
+    for suffix in ["Headers", "Methods", "Credentials"]:
+        query = "ACA%s" % suffix
+        header = "Access-Control-Allow-%s" % suffix
         if query in request.GET:
             headers.append((header, request.GET[query]))
 
-    if b"ACEHeaders" in request.GET:
-        headers.append((b"Access-Control-Expose-Headers", request.GET[b"ACEHeaders"]))
+    if "ACEHeaders" in request.GET:
+        headers.append(("Access-Control-Expose-Headers", request.GET["ACEHeaders"]))
 
-    return status, headers, b""
+    return status, headers, ""
diff --git a/third_party/web_platform_tests/service-workers/service-worker/resources/service-worker-csp-worker.py b/third_party/web_platform_tests/service-workers/service-worker/resources/service-worker-csp-worker.py
index 62c945f..20bc85a 100644
--- a/third_party/web_platform_tests/service-workers/service-worker/resources/service-worker-csp-worker.py
+++ b/third_party/web_platform_tests/service-workers/service-worker/resources/service-worker-csp-worker.py
@@ -17,6 +17,7 @@
                 'Importing the other origins script should fail.');
   }, 'importScripts test for default-src');
 
+/* b/114053979 Cobalt eval() allowed when missing csp
 test(function() {
     assert_throws_js(EvalError,
                      function() { eval('1 + 1'); },
@@ -24,7 +25,7 @@
     assert_throws_js(EvalError,
                      function() { new Function('1 + 1'); },
                      'new Function() should throw EvalError.')
-  }, 'eval test for default-src');
+  }, 'eval test for default-src');*/
 
 async_test(function(t) {
     fetch(host_info.HTTPS_REMOTE_ORIGIN +
@@ -43,7 +44,8 @@
       base_path() + 'redirect.py?Redirect=';
     var OTHER_BASE_URL = host_info.HTTPS_REMOTE_ORIGIN +
       base_path() + 'fetch-access-control.py?'
-    fetch(REDIRECT_URL + encodeURIComponent(OTHER_BASE_URL + 'ACAOrigin=*'),
+    fetch(REDIRECT_URL + encodeURIComponent(OTHER_BASE_URL + 'ACAOrigin=*') +
+          '&ACAOrigin=*',
           {mode: 'cors'})
       .then(function(response){
           assert_unreached('Redirected fetch should fail.');
@@ -72,6 +74,7 @@
                 'Importing the other origins script should fail.');
   }, 'importScripts test for script-src');
 
+/* b/114053979 Cobalt eval() allowed when missing csp
 test(function() {
     assert_throws_js(EvalError,
                      function() { eval('1 + 1'); },
@@ -79,7 +82,7 @@
     assert_throws_js(EvalError,
                      function() { new Function('1 + 1'); },
                      'new Function() should throw EvalError.')
-  }, 'eval test for script-src');
+  }, 'eval test for script-src');*/
 
 async_test(function(t) {
     fetch(host_info.HTTPS_REMOTE_ORIGIN +
@@ -98,7 +101,8 @@
       base_path() + 'redirect.py?Redirect=';
     var OTHER_BASE_URL = host_info.HTTPS_REMOTE_ORIGIN +
       base_path() + 'fetch-access-control.py?'
-    fetch(REDIRECT_URL + encodeURIComponent(OTHER_BASE_URL + 'ACAOrigin=*'),
+    fetch(REDIRECT_URL + encodeURIComponent(OTHER_BASE_URL + 'ACAOrigin=*') +
+          '&ACAOrigin=*',
           {mode: 'cors'})
       .then(function(response){
           t.done();
@@ -127,6 +131,7 @@
                  'Importing the other origins script should not fail.');
   }, 'importScripts test for connect-src');
 
+/* b/114053979 Cobalt eval() allowed when missing csp
 test(function() {
     var eval_failed = false;
     try {
@@ -137,7 +142,7 @@
     }
     assert_false(eval_failed,
                  'connect-src without unsafe-eval should not block eval().');
-  }, 'eval test for connect-src');
+  }, 'eval test for connect-src');*/
 
 async_test(function(t) {
     fetch(host_info.HTTPS_REMOTE_ORIGIN +
@@ -156,7 +161,8 @@
       base_path() + 'redirect.py?Redirect=';
     var OTHER_BASE_URL = host_info.HTTPS_REMOTE_ORIGIN +
       base_path() + 'fetch-access-control.py?'
-    fetch(REDIRECT_URL + encodeURIComponent(OTHER_BASE_URL + 'ACAOrigin=*'),
+    fetch(REDIRECT_URL + encodeURIComponent(OTHER_BASE_URL + 'ACAOrigin=*') +
+          '&ACAOrigin=*',
           {mode: 'cors'})
       .then(function(response){
           assert_unreached('Redirected fetch should fail.');
diff --git a/third_party/web_platform_tests/service-workers/service-worker/resources/test-helpers.sub.js b/third_party/web_platform_tests/service-workers/service-worker/resources/test-helpers.sub.js
index 7430152..28b657a 100644
--- a/third_party/web_platform_tests/service-workers/service-worker/resources/test-helpers.sub.js
+++ b/third_party/web_platform_tests/service-workers/service-worker/resources/test-helpers.sub.js
@@ -86,7 +86,15 @@
   return new Promise(test.step_func(function(resolve) {
       var handler = test.step_func(function() {
         registration.removeEventListener('updatefound', handler);
-        resolve(registration.installing);
+        // b/234788479 Implement waiting for update worker state tasks in
+        // Install algorithm, otherwise the worker is activated too early
+        if (registration.installing) {
+          resolve(registration.installing);
+        } else if (registration.waiting) {
+          resolve(registration.waiting);
+        } else {
+          resolve(registration.active);
+        }
       });
       registration.addEventListener('updatefound', handler);
     }));
diff --git a/third_party/web_platform_tests/service-workers/service-worker/resources/update-missing-import-scripts-imported-worker.py b/third_party/web_platform_tests/service-workers/service-worker/resources/update-missing-import-scripts-imported-worker.py
index 2d95387..6c3d445 100644
--- a/third_party/web_platform_tests/service-workers/service-worker/resources/update-missing-import-scripts-imported-worker.py
+++ b/third_party/web_platform_tests/service-workers/service-worker/resources/update-missing-import-scripts-imported-worker.py
@@ -4,6 +4,6 @@
 
     if already_requested is None:
         request.server.stash.put(key, True)
-        return [('Content-Type', 'application/javascript')], '// initial script'
+        return [('Content-Type', 'application/javascript')], 'self.oninstall = () => {};// initial script'
 
     response.status = (404, 'Not found: should not have been able to import this script twice!')
diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn
index 132c555..87edaaf 100644
--- a/third_party/zlib/BUILD.gn
+++ b/third_party/zlib/BUILD.gn
@@ -67,7 +67,7 @@
 
     if (!is_debug) {
       # Use optimize_speed (-O3) to output the _smallest_ code.
-      if(!is_starboard) {
+      if(!use_cobalt_customizations) {
         configs -= [ "//build/config/compiler:default_optimization" ]
       }
       configs += [ "//build/config/compiler:optimize_speed" ]
@@ -115,7 +115,7 @@
     if (!is_ios) {
       include_dirs = [ "." ]
 
-      if (!is_starboard && is_android) {
+      if (!use_cobalt_customizations && is_android) {
         import("//build/config/android/config.gni")
         if (defined(android_ndk_root) && android_ndk_root != "") {
           deps = [
@@ -145,7 +145,7 @@
       }
 
       if (!is_debug) {
-        if(!is_starboard) {
+        if(!use_cobalt_customizations) {
           configs -= [ "//build/config/compiler:default_optimization" ]
         }
         configs += [ "//build/config/compiler:optimize_speed" ]
@@ -204,21 +204,21 @@
       "contrib/optimizations/inflate.c",
     ]
 
-    if (!is_win && is_starboard) {
+    if (!is_win && use_cobalt_customizations) {
       cflags_c = [ "-Wno-unused-function" ]
     }
 
     if (use_arm_neon_optimizations && !is_debug) {
       # Here we trade better performance on newer/bigger ARMv8 cores
       # for less perf on ARMv7, per crbug.com/772870#c40
-      if(!is_starboard) {
+      if(!use_cobalt_customizations) {
         configs -= [ "//build/config/compiler:default_optimization" ]
       }
       configs += [ "//build/config/compiler:optimize_speed" ]
     }
   }
 
-  if (!is_starboard) {
+  if (!use_cobalt_customizations) {
     configs -= [ "//build/config/compiler:chromium_code" ]
   }
   configs += [
@@ -287,7 +287,7 @@
     ]
   }
 
-  if (!is_starboard) {
+  if (!use_cobalt_customizations) {
     configs -= [ "//build/config/compiler:chromium_code" ]
   }
   configs += [
@@ -385,7 +385,7 @@
       "gzread.c",
       "gzwrite.c",
     ]
-  } else {
+  } else if (!is_native_target_build) {
     configs -= [ "//build/config/compiler:chromium_code" ]
   }
   configs += [
@@ -402,7 +402,7 @@
   allow_circular_includes_from = deps
 }
 
-if (is_starboard) {
+if (use_cobalt_customizations) {
   config("zlib_all_dependent_config") {
     # To prevent Zlib ever from redefining const keyword in zconf.h
     defines = [ "STDC" ]
@@ -458,7 +458,7 @@
       "contrib/minizip/iostarboard.h",
     ]
     deps += [ "//starboard:starboard_headers_only" ]
-  } else {
+  } else if (!is_native_target_build) {
     configs -= [ "//build/config/compiler:chromium_code" ]
   }
   configs += [
@@ -480,11 +480,13 @@
     ]
 
     if (!is_debug) {
-      configs -= [ "//build/config/compiler:default_optimization" ]
+      if (!is_native_target_build) {
+        configs -= [ "//build/config/compiler:default_optimization" ]
+      }
       configs += [ "//build/config/compiler:optimize_speed" ]
     }
 
-    if (!is_starboard) {
+    if (!use_cobalt_customizations) {
       configs -= [ "//build/config/compiler:chromium_code" ]
     }
     configs += [ "//build/config/compiler:no_chromium_code" ]
diff --git a/third_party/zlib/arm_features.c b/third_party/zlib/arm_features.c
index a29d6cc..22bcefc 100644
--- a/third_party/zlib/arm_features.c
+++ b/third_party/zlib/arm_features.c
@@ -37,6 +37,8 @@
 #include <zircon/types.h>
 #elif defined(ARMV8_OS_WINDOWS)
 #include <windows.h>
+#elif defined(NATIVE_TARGET_BUILD)
+// TODO(b/270864124): see if there's anything we can/should do here.
 #else
 #error arm_features.c ARM feature detection in not defined for your platform
 #endif
diff --git a/third_party/zlib/google/zip_reader.cc b/third_party/zlib/google/zip_reader.cc
index f3e9d0f..c013b2a 100644
--- a/third_party/zlib/google/zip_reader.cc
+++ b/third_party/zlib/google/zip_reader.cc
@@ -50,6 +50,10 @@
 
   void SetTimeModified(const base::Time& time) override;
 
+#if defined(STARBOARD)
+  bool Flush() override;
+#endif
+
  private:
   size_t max_read_bytes_;
   std::string* output_;
@@ -77,6 +81,13 @@
   return true;
 }
 
+#if defined(STARBOARD)
+bool StringWriterDelegate::Flush() {
+  // Do nothing.
+  return true;
+}
+#endif
+
 void StringWriterDelegate::SetTimeModified(const base::Time& time) {
   // Do nothing.
 }
@@ -289,6 +300,12 @@
     delegate->SetTimeModified(current_entry_info()->last_modified());
   }
 
+#if defined(STARBOARD)
+  if (!delegate->Flush()) {
+    return false;
+  }
+#endif
+
   return entire_file_extracted;
 }
 
@@ -478,14 +495,17 @@
   file_->SetTimes(base::Time::Now(), time);
 }
 
+#if defined(STARBOARD)
+bool FileWriterDelegate::Flush() {
+  return file_->Flush();
+}
+#endif
 // FilePathWriterDelegate ------------------------------------------------------
 
 FilePathWriterDelegate::FilePathWriterDelegate(
     const base::FilePath& output_file_path)
     : output_file_path_(output_file_path) {}
-
 FilePathWriterDelegate::~FilePathWriterDelegate() {}
-
 bool FilePathWriterDelegate::PrepareOutput() {
   // We can't rely on parent directory entries being specified in the
   // zip, so we make sure they are created.
@@ -500,12 +520,19 @@
 bool FilePathWriterDelegate::WriteBytes(const char* data, int num_bytes) {
   return num_bytes == file_.WriteAtCurrentPos(data, num_bytes);
 }
+#if defined(STARBOARD)
+bool FilePathWriterDelegate::Flush() {
+  return file_.Flush();
+}
+#endif
 
 void FilePathWriterDelegate::SetTimeModified(const base::Time& time) {
-  file_.Close();
-  // TODO: use a workaround for base::TouchFile, because Starboard has not
-  // enabled the functions to update file modified/access time.
-  // base::TouchFile(output_file_path_, base::Time::Now(), time);
+#if defined(STARBOARD)
+  // Starboard doesn't support setting file access or modification times.
+#else
+  base::TouchFile(output_file_path_, base::Time::Now(), time);
+#endif
+
 }
 
 }  // namespace zip
diff --git a/third_party/zlib/google/zip_reader.h b/third_party/zlib/google/zip_reader.h
index d442d42..c4bcc1c 100644
--- a/third_party/zlib/google/zip_reader.h
+++ b/third_party/zlib/google/zip_reader.h
@@ -42,6 +42,12 @@
 
   // Sets the last-modified time of the data.
   virtual void SetTimeModified(const base::Time& time) = 0;
+
+#if defined(STARBOARD)
+  // Invoked at the end of the entry extraction to flush to persistent storage.
+  // Returns failure on failure to flush.
+  virtual bool Flush() = 0;
+#endif
 };
 
 // This class is used for reading zip files. A typical use case of this
@@ -255,6 +261,10 @@
   // Sets the last-modified time of the data.
   void SetTimeModified(const base::Time& time) override;
 
+#if defined(STARBOARD)
+  bool Flush() override;
+#endif
+
   // Return the actual size of the file.
   int64_t file_length() { return file_length_; }
 
@@ -289,6 +299,10 @@
   // Sets the last-modified time of the data.
   void SetTimeModified(const base::Time& time) override;
 
+#if defined(STARBOARD)
+  bool Flush() override;
+#endif
+
  private:
   base::FilePath output_file_path_;
   base::File file_;
diff --git a/third_party/zlib/google/zip_reader_unittest.cc b/third_party/zlib/google/zip_reader_unittest.cc
index a595102..b6ab67e 100644
--- a/third_party/zlib/google/zip_reader_unittest.cc
+++ b/third_party/zlib/google/zip_reader_unittest.cc
@@ -117,6 +117,7 @@
   MOCK_METHOD0(PrepareOutput, bool());
   MOCK_METHOD2(WriteBytes, bool(const char*, int));
   MOCK_METHOD1(SetTimeModified, void(const base::Time&));
+  MOCK_METHOD0(Flush, bool());
 };
 
 bool ExtractCurrentEntryToFilePath(zip::ZipReader* reader,
@@ -623,6 +624,8 @@
 
   EXPECT_CALL(mock_writer, PrepareOutput())
       .WillOnce(Return(true));
+  EXPECT_CALL(mock_writer, Flush())
+      .WillOnce(Return(true));
   EXPECT_CALL(mock_writer, WriteBytes(_, _))
       .WillOnce(Return(false));
 
@@ -635,6 +638,29 @@
       &mock_writer, std::numeric_limits<uint64_t>::max()));
 }
 
+#if defined(STARBOARD)
+// Test that when WriterDelegate::Flush returns false the extraction fails.
+TEST_F(ZipReaderTest, ExtractCurrentEntryFlushFailure) {
+  testing::StrictMock<MockWriterDelegate> mock_writer;
+
+  EXPECT_CALL(mock_writer, PrepareOutput())
+      .WillOnce(Return(true));
+  EXPECT_CALL(mock_writer, WriteBytes(_, _))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(mock_writer, SetTimeModified(_));
+  EXPECT_CALL(mock_writer, Flush())
+      .WillOnce(Return(false));
+
+  base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
+  ZipReader reader;
+
+  ASSERT_TRUE(reader.Open(test_zip_file_));
+  ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path));
+  ASSERT_FALSE(reader.ExtractCurrentEntry(
+      &mock_writer, std::numeric_limits<uint64_t>::max()));
+}
+#endif
+
 // Test that extraction succeeds when the writer delegate reports all is well.
 TEST_F(ZipReaderTest, ExtractCurrentEntrySuccess) {
   testing::StrictMock<MockWriterDelegate> mock_writer;
@@ -645,6 +671,8 @@
       .WillRepeatedly(Return(true));
   EXPECT_CALL(mock_writer, SetTimeModified(_));
 
+  EXPECT_CALL(mock_writer, Flush()).WillOnce(Return(true));
+
   base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
   ZipReader reader;
 
diff --git a/tools/create_archive.py b/tools/create_archive.py
index 0c69440..16ccb74 100755
--- a/tools/create_archive.py
+++ b/tools/create_archive.py
@@ -262,7 +262,7 @@
       if glob.glob(os.path.join(source_path, pattern))
   ]
   files_to_tar = ' '.join(contents)
-  return (f'"{_7Z_PATH}" a {excludes} -bsp1 -snl -ttar'
+  return (f'"{_7Z_PATH}" a {excludes} -bsp1 -snl -ttar '
           f'{intermediate_tar_path} {files_to_tar}')
 
 
@@ -282,7 +282,7 @@
       if glob.glob(os.path.join(source_path, pattern))
   ]
   files_to_tar = ' '.join(contents)
-  return (f'tar -{mode}vf {intermediate_tar_path} --format=posix'
+  return (f'tar -{mode}vf {intermediate_tar_path} --format=posix '
           f'{excludes} {files_to_tar}')
 
 
@@ -295,7 +295,7 @@
 
 def _CreateZipCommand(intermediate_tar_path, dest_path, is_parallel=False):
   if _IsWindows():
-    return (f'"{_7Z_PATH}" a -bsp1 -mx={_COMPRESSION_LEVEL}'
+    return (f'"{_7Z_PATH}" a -bsp1 -mx={_COMPRESSION_LEVEL} '
             f'-mmt=on {dest_path} {intermediate_tar_path}')
   else:
     zip_program = (